diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c45ae3376bb76..daefdadaed77c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,7 @@ process](docs/contributing/). This page contains information about reporting issues as well as some tips and guidelines useful to experienced open source contributors. Finally, make sure -you read our [community guidelines](#docker-community-guidelines) before you +you read our [community guidelines](#moby-community-guidelines) before you start participating. ## Topics @@ -17,7 +17,7 @@ start participating. * [Design and Cleanup Proposals](#design-and-cleanup-proposals) * [Reporting Issues](#reporting-other-issues) * [Quick Contribution Tips and Guidelines](#quick-contribution-tips-and-guidelines) -* [Community Guidelines](#docker-community-guidelines) +* [Community Guidelines](#moby-community-guidelines) ## Reporting security issues diff --git a/Dockerfile b/Dockerfile index 4db75a67226d6..9d3fc6116db3c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,10 +24,10 @@ # the case. Therefore, you don't have to disable it anymore. # -FROM golang:1.10.2 AS base +FROM golang:1.10.3 AS base # FIXME(vdemeester) this is kept for other script depending on it to not fail right away # Remove this once the other scripts uses something else to detect the version -ENV GO_VERSION 1.10.2 +ENV GO_VERSION 1.10.3 # allow replacing httpredir or deb mirror ARG APT_MIRROR=deb.debian.org RUN sed -ri "s/(httpredir|deb).debian.org/$APT_MIRROR/g" /etc/apt/sources.list @@ -170,6 +170,8 @@ RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME FROM runtime-dev AS dev RUN groupadd -r docker RUN useradd --create-home --gid docker unprivilegeduser +# Let us use a .bashrc file +RUN ln -sfv /go/src/github.com/docker/docker/.bashrc ~/.bashrc # Activate bash completion and include Docker's completion if mounted with DOCKER_BASH_COMPLETION_PATH RUN echo "source /usr/share/bash-completion/bash_completion" >> /etc/bash.bashrc RUN ln -s /usr/local/completion/bash/docker /etc/bash_completion.d/docker diff --git a/Dockerfile.e2e b/Dockerfile.e2e index 77c9205d458cd..663a58af33075 100644 --- a/Dockerfile.e2e +++ b/Dockerfile.e2e @@ -1,5 +1,5 @@ ## Step 1: Build tests -FROM golang:1.10.2-alpine3.7 as builder +FROM golang:1.10.3-alpine3.7 as builder RUN apk add --update \ bash \ diff --git a/Dockerfile.simple b/Dockerfile.simple index 67a78003e1a0a..b0338ac0c872f 100644 --- a/Dockerfile.simple +++ b/Dockerfile.simple @@ -42,7 +42,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ # will need updating, to avoid errors. Ping #docker-maintainers on IRC # with a heads-up. # IMPORTANT: When updating this please note that stdlib archive/tar pkg is vendored -ENV GO_VERSION 1.10.2 +ENV GO_VERSION 1.10.3 RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" \ | tar -xzC /usr/local ENV PATH /go/bin:/usr/local/go/bin:$PATH diff --git a/Dockerfile.windows b/Dockerfile.windows index 0cc7aa4f5b11a..1b2c1f3c8ae0e 100644 --- a/Dockerfile.windows +++ b/Dockerfile.windows @@ -161,7 +161,7 @@ SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPref # Environment variable notes: # - GO_VERSION must be consistent with 'Dockerfile' used by Linux. # - FROM_DOCKERFILE is used for detection of building within a container. -ENV GO_VERSION=1.10.2 ` +ENV GO_VERSION=1.10.3 ` GIT_VERSION=2.11.1 ` GOPATH=C:\go ` FROM_DOCKERFILE=1 diff --git a/Makefile b/Makefile index ebca5b1b027f1..fc5b7d9964666 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,7 @@ DOCKER_ENVS := \ -e DOCKER_BUILD_ARGS \ -e DOCKER_BUILD_GOGC \ -e DOCKER_BUILD_PKGS \ + -e DOCKER_BUILDKIT \ -e DOCKER_BASH_COMPLETION_PATH \ -e DOCKER_CLI_PATH \ -e DOCKER_DEBUG \ @@ -55,7 +56,8 @@ DOCKER_ENVS := \ -e https_proxy \ -e no_proxy \ -e VERSION \ - -e PLATFORM + -e PLATFORM \ + -e PRODUCT # note: we _cannot_ add "-e DOCKER_BUILDTAGS" here because even if it's unset in the shell, that would shadow the "ENV DOCKER_BUILDTAGS" set in our Dockerfile, which is very important for our official builds # to allow `make BIND_DIR=. shell` or `make BIND_DIR= test` diff --git a/TESTING.md b/TESTING.md index 1231e1c5f4d91..b2c53769ca052 100644 --- a/TESTING.md +++ b/TESTING.md @@ -8,11 +8,11 @@ questions you may have as an aspiring Moby contributor. Moby has two test suites (and one legacy test suite): * Unit tests - use standard `go test` and - [gotestyourself/assert](https://godoc.org/github.com/gotestyourself/gotestyourself/assert) assertions. They are located in + [gotest.tools/assert](https://godoc.org/gotest.tools/assert) assertions. They are located in the package they test. Unit tests should be fast and test only their own package. * API integration tests - use standard `go test` and - [gotestyourself/assert](https://godoc.org/github.com/gotestyourself/gotestyourself/assert) assertions. They are located in + [gotest.tools/assert](https://godoc.org/gotest.tools/assert) assertions. They are located in `./integration/` directories, where `component` is: container, image, volume, etc. These tests perform HTTP requests to an API endpoint and check the HTTP response and daemon state after the call. @@ -47,6 +47,24 @@ Bugs fixes should include a unit test case which exercises the bug. A bug fix may also include new assertions in an existing integration tests for the API endpoint. +### Integration tests environment considerations + +When adding new tests or modifying existing test under `integration/`, testing +environment should be properly considered. `skip.If` from +[gotest.tools/skip](https://godoc.org/gotest.tools/skip) can be used to make the +test run conditionally. Full testing environment conditions can be found at +[environment.go](https://github.com/moby/moby/blob/cb37987ee11655ed6bbef663d245e55922354c68/internal/test/environment/environment.go) + +Here is a quick example. If the test needs to interact with a docker daemon on +the same host, the following condition should be checked within the test code + +```go +skip.If(t, testEnv.IsRemoteDaemon()) +// your integration test code +``` + +If a remote daemon is detected, the test will be skipped. + ## Running tests To run the unit test suite: diff --git a/api/server/backend/build/backend.go b/api/server/backend/build/backend.go index 22ce9cef7cb0c..5e04e837a1bde 100644 --- a/api/server/backend/build/backend.go +++ b/api/server/backend/build/backend.go @@ -8,10 +8,12 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/backend" "github.com/docker/docker/builder" + buildkit "github.com/docker/docker/builder/builder-next" "github.com/docker/docker/builder/fscache" "github.com/docker/docker/image" "github.com/docker/docker/pkg/stringid" "github.com/pkg/errors" + "golang.org/x/sync/errgroup" ) // ImageComponent provides an interface for working with images @@ -30,24 +32,39 @@ type Backend struct { builder Builder fsCache *fscache.FSCache imageComponent ImageComponent + buildkit *buildkit.Builder } // NewBackend creates a new build backend from components -func NewBackend(components ImageComponent, builder Builder, fsCache *fscache.FSCache) (*Backend, error) { - return &Backend{imageComponent: components, builder: builder, fsCache: fsCache}, nil +func NewBackend(components ImageComponent, builder Builder, fsCache *fscache.FSCache, buildkit *buildkit.Builder) (*Backend, error) { + return &Backend{imageComponent: components, builder: builder, fsCache: fsCache, buildkit: buildkit}, nil } // Build builds an image from a Source func (b *Backend) Build(ctx context.Context, config backend.BuildConfig) (string, error) { options := config.Options + useBuildKit := options.Version == types.BuilderBuildKit + tagger, err := NewTagger(b.imageComponent, config.ProgressWriter.StdoutFormatter, options.Tags) if err != nil { return "", err } - build, err := b.builder.Build(ctx, config) - if err != nil { - return "", err + var build *builder.Result + if useBuildKit { + build, err = b.buildkit.Build(ctx, config) + if err != nil { + return "", err + } + } else { + build, err = b.builder.Build(ctx, config) + if err != nil { + return "", err + } + } + + if build == nil { + return "", nil } var imageID = build.ImageID @@ -56,25 +73,54 @@ 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 } } } - stdout := config.ProgressWriter.StdoutFormatter - fmt.Fprintf(stdout, "Successfully built %s\n", stringid.TruncateID(imageID)) - err = tagger.TagImages(image.ID(imageID)) + if !useBuildKit { + stdout := config.ProgressWriter.StdoutFormatter + fmt.Fprintf(stdout, "Successfully built %s\n", stringid.TruncateID(imageID)) + err = tagger.TagImages(image.ID(imageID)) + } return imageID, err } // PruneCache removes all cached build sources func (b *Backend) PruneCache(ctx context.Context) (*types.BuildCachePruneReport, error) { - size, err := b.fsCache.Prune(ctx) - if err != nil { - return nil, errors.Wrap(err, "failed to prune build cache") + eg, ctx := errgroup.WithContext(ctx) + + var fsCacheSize uint64 + eg.Go(func() error { + var err error + fsCacheSize, err = b.fsCache.Prune(ctx) + if err != nil { + return errors.Wrap(err, "failed to prune fscache") + } + return nil + }) + + var buildCacheSize int64 + eg.Go(func() error { + var err error + buildCacheSize, err = b.buildkit.Prune(ctx) + if err != nil { + return errors.Wrap(err, "failed to prune build cache") + } + return nil + }) + + if err := eg.Wait(); err != nil { + return nil, err } - return &types.BuildCachePruneReport{SpaceReclaimed: size}, nil + + return &types.BuildCachePruneReport{SpaceReclaimed: fsCacheSize + uint64(buildCacheSize)}, nil +} + +// Cancel cancels the build by ID +func (b *Backend) Cancel(ctx context.Context, id string) error { + return b.buildkit.Cancel(ctx, id) } func squashBuild(build *builder.Result, imageComponent ImageComponent) (string, error) { diff --git a/api/server/middleware/debug_test.go b/api/server/middleware/debug_test.go index cc227b324844f..a64b73e0d718b 100644 --- a/api/server/middleware/debug_test.go +++ b/api/server/middleware/debug_test.go @@ -3,8 +3,8 @@ package middleware // import "github.com/docker/docker/api/server/middleware" import ( "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestMaskSecretKeys(t *testing.T) { diff --git a/api/server/middleware/version_test.go b/api/server/middleware/version_test.go index c439c8abfc1ea..edbc0bcaa589f 100644 --- a/api/server/middleware/version_test.go +++ b/api/server/middleware/version_test.go @@ -8,8 +8,8 @@ import ( "testing" "github.com/docker/docker/api/server/httputils" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestVersionMiddlewareVersion(t *testing.T) { diff --git a/api/server/router/build/backend.go b/api/server/router/build/backend.go index d82ef63af1558..2ceae9d946c67 100644 --- a/api/server/router/build/backend.go +++ b/api/server/router/build/backend.go @@ -15,6 +15,8 @@ type Backend interface { // Prune build cache PruneCache(context.Context) (*types.BuildCachePruneReport, error) + + Cancel(context.Context, string) error } type experimentalProvider interface { diff --git a/api/server/router/build/build.go b/api/server/router/build/build.go index dc13a1060295a..811cd39181744 100644 --- a/api/server/router/build/build.go +++ b/api/server/router/build/build.go @@ -25,5 +25,6 @@ func (r *buildRouter) initRoutes() { r.routes = []router.Route{ router.NewPostRoute("/build", r.postBuild, router.WithCancel), router.NewPostRoute("/build/prune", r.postPrune, router.WithCancel), + router.NewPostRoute("/build/cancel", r.postCancel), } } diff --git a/api/server/router/build/build_routes.go b/api/server/router/build/build_routes.go index 3e3668c42bb0a..3073479ccf2d3 100644 --- a/api/server/router/build/build_routes.go +++ b/api/server/router/build/build_routes.go @@ -1,6 +1,7 @@ package build // import "github.com/docker/docker/api/server/router/build" import ( + "bufio" "bytes" "context" "encoding/base64" @@ -22,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" ) @@ -70,12 +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") - p := system.ParsePlatform(apiPlatform) - if err := system.ValidatePlatform(p); err != nil { - return nil, errdefs.InvalidParameter(errors.Errorf("invalid platform: %s", err)) - } - options.Platform = p.OS + options.Platform = r.FormValue("platform") } if r.Form.Get("shmsize") != "" { @@ -145,10 +140,26 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui options.CacheFrom = cacheFrom } options.SessionID = r.FormValue("session") + options.BuildID = r.FormValue("buildid") + builderVersion, err := parseVersion(r.FormValue("version")) + if err != nil { + return nil, err + } + options.Version = builderVersion return options, nil } +func parseVersion(s string) (types.BuilderVersion, error) { + if s == "" || s == string(types.BuilderV1) { + return types.BuilderV1, nil + } + if s == string(types.BuilderBuildKit) { + return types.BuilderBuildKit, nil + } + return "", errors.Errorf("invalid version %s", s) +} + func (br *buildRouter) postPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { report, err := br.backend.PruneCache(ctx) if err != nil { @@ -157,6 +168,17 @@ func (br *buildRouter) postPrune(ctx context.Context, w http.ResponseWriter, r * return httputils.WriteJSON(w, http.StatusOK, report) } +func (br *buildRouter) postCancel(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { + w.Header().Set("Content-Type", "application/json") + + id := r.FormValue("id") + if id == "" { + return errors.Errorf("build ID not provided") + } + + return br.backend.Cancel(ctx, id) +} + func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { var ( notVerboseBuffer = bytes.NewBuffer(nil) @@ -165,18 +187,33 @@ func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r * w.Header().Set("Content-Type", "application/json") - output := ioutils.NewWriteFlusher(w) + body := r.Body + var ww io.Writer = w + if body != nil { + // there is a possibility that output is written before request body + // has been fully read so we need to protect against it. + // this can be removed when + // https://github.com/golang/go/issues/15527 + // https://github.com/golang/go/issues/22209 + // has been fixed + body, ww = wrapOutputBufferedUntilRequestRead(body, ww) + } + + output := ioutils.NewWriteFlusher(ww) defer output.Close() + errf := func(err error) error { + if httputils.BoolValue(r, "q") && notVerboseBuffer.Len() > 0 { output.Write(notVerboseBuffer.Bytes()) } + // Do not write the error in the http output if it's still empty. // This prevents from writing a 200(OK) when there is an internal error. if !output.Flushed() { return err } - _, err = w.Write(streamformatter.FormatError(err)) + _, err = output.Write(streamformatter.FormatError(err)) if err != nil { logrus.Warnf("could not write error response: %v", err) } @@ -193,6 +230,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 @@ -208,7 +249,7 @@ func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r * wantAux := versions.GreaterThanOrEqualTo(version, "1.30") imgID, err := br.backend.Build(ctx, backend.BuildConfig{ - Source: r.Body, + Source: body, Options: buildOptions, ProgressWriter: buildProgressWriter(out, wantAux, createProgressReader), }) @@ -267,3 +308,102 @@ func buildProgressWriter(out io.Writer, wantAux bool, createProgressReader func( ProgressReaderFunc: createProgressReader, } } + +type flusher interface { + Flush() +} + +func wrapOutputBufferedUntilRequestRead(rc io.ReadCloser, out io.Writer) (io.ReadCloser, io.Writer) { + var fl flusher = &ioutils.NopFlusher{} + if f, ok := out.(flusher); ok { + fl = f + } + + w := &wcf{ + buf: bytes.NewBuffer(nil), + Writer: out, + flusher: fl, + } + r := bufio.NewReader(rc) + _, err := r.Peek(1) + if err != nil { + return rc, out + } + rc = &rcNotifier{ + Reader: r, + Closer: rc, + notify: w.notify, + } + return rc, w +} + +type rcNotifier struct { + io.Reader + io.Closer + notify func() +} + +func (r *rcNotifier) Read(b []byte) (int, error) { + n, err := r.Reader.Read(b) + if err != nil { + r.notify() + } + return n, err +} + +func (r *rcNotifier) Close() error { + r.notify() + return r.Closer.Close() +} + +type wcf struct { + io.Writer + flusher + mu sync.Mutex + ready bool + buf *bytes.Buffer + flushed bool +} + +func (w *wcf) Flush() { + w.mu.Lock() + w.flushed = true + if !w.ready { + w.mu.Unlock() + return + } + w.mu.Unlock() + w.flusher.Flush() +} + +func (w *wcf) Flushed() bool { + w.mu.Lock() + b := w.flushed + w.mu.Unlock() + return b +} + +func (w *wcf) Write(b []byte) (int, error) { + w.mu.Lock() + if !w.ready { + n, err := w.buf.Write(b) + w.mu.Unlock() + return n, err + } + w.mu.Unlock() + return w.Writer.Write(b) +} + +func (w *wcf) notify() { + w.mu.Lock() + if !w.ready { + if w.buf.Len() > 0 { + io.Copy(w.Writer, w.buf) + } + if w.flushed { + w.flusher.Flush() + } + w.ready = true + } + w.mu.Unlock() +} diff --git a/api/server/router/image/backend.go b/api/server/router/image/backend.go index 93c47cf638987..5837f9a9bcdfd 100644 --- a/api/server/router/image/backend.go +++ b/api/server/router/image/backend.go @@ -8,6 +8,7 @@ import ( "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/image" "github.com/docker/docker/api/types/registry" + specs "github.com/opencontainers/image-spec/specs-go/v1" ) // Backend is all the methods that need to be implemented @@ -34,7 +35,7 @@ type importExportBackend interface { } type registryBackend interface { - PullImage(ctx context.Context, image, tag, platform string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error + PullImage(ctx context.Context, image, tag string, platform *specs.Platform, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error PushImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error SearchRegistryForImages(ctx context.Context, filtersArgs string, term string, limit int, authConfig *types.AuthConfig, metaHeaders map[string][]string) (*registry.SearchResults, error) } diff --git a/api/server/router/image/image_routes.go b/api/server/router/image/image_routes.go index 8e32d0292ed8e..85707c06d2ce3 100644 --- a/api/server/router/image/image_routes.go +++ b/api/server/router/image/image_routes.go @@ -4,11 +4,11 @@ import ( "context" "encoding/base64" "encoding/json" - "fmt" "net/http" "strconv" "strings" + "github.com/containerd/containerd/platforms" "github.com/docker/docker/api/server/httputils" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" @@ -36,7 +36,7 @@ func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrite message = r.Form.Get("message") err error output = ioutils.NewWriteFlusher(w) - platform = &specs.Platform{} + platform *specs.Platform ) defer output.Close() @@ -45,9 +45,15 @@ func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrite version := httputils.VersionFromContext(ctx) if versions.GreaterThanOrEqualTo(version, "1.32") { apiPlatform := r.FormValue("platform") - platform = system.ParsePlatform(apiPlatform) - if err = system.ValidatePlatform(platform); err != nil { - err = fmt.Errorf("invalid platform: %s", err) + if apiPlatform != "" { + sp, err := platforms.Parse(apiPlatform) + if err != nil { + return err + } + if err := system.ValidatePlatform(sp); err != nil { + return err + } + platform = &sp } } @@ -70,13 +76,17 @@ func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrite authConfig = &types.AuthConfig{} } } - err = s.backend.PullImage(ctx, image, tag, platform.OS, metaHeaders, authConfig, output) + err = s.backend.PullImage(ctx, image, tag, platform, metaHeaders, authConfig, output) } else { //import src := r.Form.Get("fromSrc") // 'err' MUST NOT be defined within this block, we need any error // generated from the download to be available to the output // stream processing below - err = s.backend.ImportImage(src, repo, platform.OS, tag, message, r.Body, output, r.Form["changes"]) + os := "" + if platform != nil { + os = platform.OS + } + err = s.backend.ImportImage(src, repo, os, tag, message, r.Body, output, r.Form["changes"]) } } if err != nil { diff --git a/api/server/router/network/backend.go b/api/server/router/network/backend.go index 1bab353a5a452..42eb950ed83e8 100644 --- a/api/server/router/network/backend.go +++ b/api/server/router/network/backend.go @@ -13,7 +13,7 @@ import ( // to provide network specific functionality. type Backend interface { FindNetwork(idName string) (libnetwork.Network, error) - GetNetworks() []libnetwork.Network + GetNetworks(filters.Args, types.NetworkListConfig) ([]types.NetworkResource, error) CreateNetwork(nc types.NetworkCreateRequest) (*types.NetworkCreateResponse, error) ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error DisconnectContainerFromNetwork(containerName string, networkName string, force bool) error @@ -24,7 +24,7 @@ type Backend interface { // ClusterBackend is all the methods that need to be implemented // to provide cluster network specific functionality. type ClusterBackend interface { - GetNetworks() ([]types.NetworkResource, error) + GetNetworks(filters.Args) ([]types.NetworkResource, error) GetNetwork(name string) (types.NetworkResource, error) GetNetworksByName(name string) ([]types.NetworkResource, error) CreateNetwork(nc types.NetworkCreateRequest) (string, error) diff --git a/api/server/router/network/filter.go b/api/server/router/network/filter.go index 02683e8005994..804be8024ac2c 100644 --- a/api/server/router/network/filter.go +++ b/api/server/router/network/filter.go @@ -1,93 +1 @@ package network // import "github.com/docker/docker/api/server/router/network" - -import ( - "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/filters" - "github.com/docker/docker/runconfig" -) - -func filterNetworkByType(nws []types.NetworkResource, netType string) ([]types.NetworkResource, error) { - retNws := []types.NetworkResource{} - switch netType { - case "builtin": - for _, nw := range nws { - if runconfig.IsPreDefinedNetwork(nw.Name) { - retNws = append(retNws, nw) - } - } - case "custom": - for _, nw := range nws { - if !runconfig.IsPreDefinedNetwork(nw.Name) { - retNws = append(retNws, nw) - } - } - default: - return nil, invalidFilter(netType) - } - return retNws, nil -} - -type invalidFilter string - -func (e invalidFilter) Error() string { - return "Invalid filter: 'type'='" + string(e) + "'" -} - -func (e invalidFilter) InvalidParameter() {} - -// filterNetworks filters network list according to user specified filter -// and returns user chosen networks -func filterNetworks(nws []types.NetworkResource, filter filters.Args) ([]types.NetworkResource, error) { - // if filter is empty, return original network list - if filter.Len() == 0 { - return nws, nil - } - - displayNet := []types.NetworkResource{} - for _, nw := range nws { - if filter.Contains("driver") { - if !filter.ExactMatch("driver", nw.Driver) { - continue - } - } - if filter.Contains("name") { - if !filter.Match("name", nw.Name) { - continue - } - } - if filter.Contains("id") { - if !filter.Match("id", nw.ID) { - continue - } - } - if filter.Contains("label") { - if !filter.MatchKVList("label", nw.Labels) { - continue - } - } - if filter.Contains("scope") { - if !filter.ExactMatch("scope", nw.Scope) { - continue - } - } - displayNet = append(displayNet, nw) - } - - if filter.Contains("type") { - typeNet := []types.NetworkResource{} - errFilter := filter.WalkValues("type", func(fval string) error { - passList, err := filterNetworkByType(displayNet, fval) - if err != nil { - return err - } - typeNet = append(typeNet, passList...) - return nil - }) - if errFilter != nil { - return nil, errFilter - } - displayNet = typeNet - } - - return displayNet, nil -} diff --git a/api/server/router/network/network_routes.go b/api/server/router/network/network_routes.go index 0248662a4969b..8b9382cb4523c 100644 --- a/api/server/router/network/network_routes.go +++ b/api/server/router/network/network_routes.go @@ -15,70 +15,54 @@ import ( "github.com/docker/docker/errdefs" "github.com/docker/libnetwork" netconst "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/networkdb" "github.com/pkg/errors" ) -var ( - // acceptedNetworkFilters is a list of acceptable filters - acceptedNetworkFilters = map[string]bool{ - "driver": true, - "type": true, - "name": true, - "id": true, - "label": true, - "scope": true, - } -) - func (n *networkRouter) getNetworksList(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } - filter := r.Form.Get("filters") - netFilters, err := filters.FromJSON(filter) + filter, err := filters.FromJSON(r.Form.Get("filters")) if err != nil { return err } - if err := netFilters.Validate(acceptedNetworkFilters); err != nil { + if err := network.ValidateFilters(filter); err != nil { return err } - list := []types.NetworkResource{} - - if nr, err := n.cluster.GetNetworks(); err == nil { - list = append(list, nr...) + var list []types.NetworkResource + nr, err := n.cluster.GetNetworks(filter) + if err == nil { + list = nr } // Combine the network list returned by Docker daemon if it is not already // returned by the cluster manager -SKIP: - for _, nw := range n.backend.GetNetworks() { - for _, nl := range list { - if nl.ID == nw.ID() { - continue SKIP - } - } + localNetworks, err := n.backend.GetNetworks(filter, types.NetworkListConfig{Detailed: versions.LessThan(httputils.VersionFromContext(ctx), "1.28")}) + if err != nil { + return err + } - var nr *types.NetworkResource - // Versions < 1.28 fetches all the containers attached to a network - // in a network list api call. It is a heavy weight operation when - // run across all the networks. Starting API version 1.28, this detailed - // info is available for network specific GET API (equivalent to inspect) - if versions.LessThan(httputils.VersionFromContext(ctx), "1.28") { - nr = n.buildDetailedNetworkResources(nw, false) - } else { - nr = n.buildNetworkResource(nw) + var idx map[string]bool + if len(list) > 0 { + idx = make(map[string]bool, len(list)) + for _, n := range list { + idx[n.ID] = true } - list = append(list, *nr) + } + for _, n := range localNetworks { + if idx[n.ID] { + continue + } + list = append(list, n) } - list, err = filterNetworks(list, netFilters) - if err != nil { - return err + if list == nil { + list = []types.NetworkResource{} } + return httputils.WriteJSON(w, http.StatusOK, list) } @@ -121,13 +105,6 @@ func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r } scope := r.URL.Query().Get("scope") - isMatchingScope := func(scope, term string) bool { - if term != "" { - return scope == term - } - return true - } - // In case multiple networks have duplicate names, return error. // TODO (yongtang): should we wrap with version here for backward compatibility? @@ -139,20 +116,26 @@ func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r listByFullName := map[string]types.NetworkResource{} listByPartialID := map[string]types.NetworkResource{} - nw := n.backend.GetNetworks() + // TODO(@cpuguy83): All this logic for figuring out which network to return does not belong here + // Instead there should be a backend function to just get one network. + filter := filters.NewArgs(filters.Arg("idOrName", term)) + if scope != "" { + filter.Add("scope", scope) + } + nw, _ := n.backend.GetNetworks(filter, types.NetworkListConfig{Detailed: true, Verbose: verbose}) for _, network := range nw { - if network.ID() == term && isMatchingScope(network.Info().Scope(), scope) { - return httputils.WriteJSON(w, http.StatusOK, *n.buildDetailedNetworkResources(network, verbose)) + if network.ID == term { + return httputils.WriteJSON(w, http.StatusOK, network) } - if network.Name() == term && isMatchingScope(network.Info().Scope(), scope) { + if network.Name == term { // No need to check the ID collision here as we are still in // local scope and the network ID is unique in this scope. - listByFullName[network.ID()] = *n.buildDetailedNetworkResources(network, verbose) + listByFullName[network.ID] = network } - if strings.HasPrefix(network.ID(), term) && isMatchingScope(network.Info().Scope(), scope) { + if strings.HasPrefix(network.ID, term) { // No need to check the ID collision here as we are still in // local scope and the network ID is unique in this scope. - listByPartialID[network.ID()] = *n.buildDetailedNetworkResources(network, verbose) + listByPartialID[network.ID] = network } } @@ -174,12 +157,12 @@ func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r } } - nr, _ := n.cluster.GetNetworks() + nr, _ := n.cluster.GetNetworks(filter) for _, network := range nr { - if network.ID == term && isMatchingScope(network.Scope, scope) { + if network.ID == term { return httputils.WriteJSON(w, http.StatusOK, network) } - if network.Name == term && isMatchingScope(network.Scope, scope) { + if network.Name == term { // Check the ID collision as we are in swarm scope here, and // the map (of the listByFullName) may have already had a // network with the same ID (from local scope previously) @@ -187,7 +170,7 @@ func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r listByFullName[network.ID] = network } } - if strings.HasPrefix(network.ID, term) && isMatchingScope(network.Scope, scope) { + if strings.HasPrefix(network.ID, term) { // Check the ID collision as we are in swarm scope here, and // the map (of the listByPartialID) may have already had a // network with the same ID (from local scope previously) @@ -327,182 +310,6 @@ func (n *networkRouter) deleteNetwork(ctx context.Context, w http.ResponseWriter return nil } -func (n *networkRouter) buildNetworkResource(nw libnetwork.Network) *types.NetworkResource { - r := &types.NetworkResource{} - if nw == nil { - return r - } - - info := nw.Info() - r.Name = nw.Name() - r.ID = nw.ID() - r.Created = info.Created() - r.Scope = info.Scope() - r.Driver = nw.Type() - r.EnableIPv6 = info.IPv6Enabled() - r.Internal = info.Internal() - r.Attachable = info.Attachable() - r.Ingress = info.Ingress() - r.Options = info.DriverOptions() - r.Containers = make(map[string]types.EndpointResource) - buildIpamResources(r, info) - r.Labels = info.Labels() - r.ConfigOnly = info.ConfigOnly() - - if cn := info.ConfigFrom(); cn != "" { - r.ConfigFrom = network.ConfigReference{Network: cn} - } - - peers := info.Peers() - if len(peers) != 0 { - r.Peers = buildPeerInfoResources(peers) - } - - return r -} - -func (n *networkRouter) buildDetailedNetworkResources(nw libnetwork.Network, verbose bool) *types.NetworkResource { - if nw == nil { - return &types.NetworkResource{} - } - - r := n.buildNetworkResource(nw) - epl := nw.Endpoints() - for _, e := range epl { - ei := e.Info() - if ei == nil { - continue - } - sb := ei.Sandbox() - tmpID := e.ID() - key := "ep-" + tmpID - if sb != nil { - key = sb.ContainerID() - } - - r.Containers[key] = buildEndpointResource(tmpID, e.Name(), ei) - } - if !verbose { - return r - } - services := nw.Info().Services() - r.Services = make(map[string]network.ServiceInfo) - for name, service := range services { - tasks := []network.Task{} - for _, t := range service.Tasks { - tasks = append(tasks, network.Task{ - Name: t.Name, - EndpointID: t.EndpointID, - EndpointIP: t.EndpointIP, - Info: t.Info, - }) - } - r.Services[name] = network.ServiceInfo{ - VIP: service.VIP, - Ports: service.Ports, - Tasks: tasks, - LocalLBIndex: service.LocalLBIndex, - } - } - return r -} - -func buildPeerInfoResources(peers []networkdb.PeerInfo) []network.PeerInfo { - peerInfo := make([]network.PeerInfo, 0, len(peers)) - for _, peer := range peers { - peerInfo = append(peerInfo, network.PeerInfo{ - Name: peer.Name, - IP: peer.IP, - }) - } - return peerInfo -} - -func buildIpamResources(r *types.NetworkResource, nwInfo libnetwork.NetworkInfo) { - id, opts, ipv4conf, ipv6conf := nwInfo.IpamConfig() - - ipv4Info, ipv6Info := nwInfo.IpamInfo() - - r.IPAM.Driver = id - - r.IPAM.Options = opts - - r.IPAM.Config = []network.IPAMConfig{} - for _, ip4 := range ipv4conf { - if ip4.PreferredPool == "" { - continue - } - iData := network.IPAMConfig{} - iData.Subnet = ip4.PreferredPool - iData.IPRange = ip4.SubPool - iData.Gateway = ip4.Gateway - iData.AuxAddress = ip4.AuxAddresses - r.IPAM.Config = append(r.IPAM.Config, iData) - } - - if len(r.IPAM.Config) == 0 { - for _, ip4Info := range ipv4Info { - iData := network.IPAMConfig{} - iData.Subnet = ip4Info.IPAMData.Pool.String() - if ip4Info.IPAMData.Gateway != nil { - iData.Gateway = ip4Info.IPAMData.Gateway.IP.String() - } - r.IPAM.Config = append(r.IPAM.Config, iData) - } - } - - hasIpv6Conf := false - for _, ip6 := range ipv6conf { - if ip6.PreferredPool == "" { - continue - } - hasIpv6Conf = true - iData := network.IPAMConfig{} - iData.Subnet = ip6.PreferredPool - iData.IPRange = ip6.SubPool - iData.Gateway = ip6.Gateway - iData.AuxAddress = ip6.AuxAddresses - r.IPAM.Config = append(r.IPAM.Config, iData) - } - - if !hasIpv6Conf { - for _, ip6Info := range ipv6Info { - if ip6Info.IPAMData.Pool == nil { - continue - } - iData := network.IPAMConfig{} - iData.Subnet = ip6Info.IPAMData.Pool.String() - iData.Gateway = ip6Info.IPAMData.Gateway.String() - r.IPAM.Config = append(r.IPAM.Config, iData) - } - } -} - -func buildEndpointResource(id string, name string, info libnetwork.EndpointInfo) types.EndpointResource { - er := types.EndpointResource{} - - er.EndpointID = id - er.Name = name - ei := info - if ei == nil { - return er - } - - if iface := ei.Iface(); iface != nil { - if mac := iface.MacAddress(); mac != nil { - er.MacAddress = mac.String() - } - if ip := iface.Address(); ip != nil && len(ip.IP) > 0 { - er.IPv4Address = ip.String() - } - - if ipv6 := iface.AddressIPv6(); ipv6 != nil && len(ipv6.IP) > 0 { - er.IPv6Address = ipv6.String() - } - } - return er -} - func (n *networkRouter) postNetworksPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err @@ -532,25 +339,25 @@ func (n *networkRouter) findUniqueNetwork(term string) (types.NetworkResource, e listByFullName := map[string]types.NetworkResource{} listByPartialID := map[string]types.NetworkResource{} - nw := n.backend.GetNetworks() + filter := filters.NewArgs(filters.Arg("idOrName", term)) + nw, _ := n.backend.GetNetworks(filter, types.NetworkListConfig{Detailed: true}) for _, network := range nw { - if network.ID() == term { - return *n.buildDetailedNetworkResources(network, false), nil - + if network.ID == term { + return network, nil } - if network.Name() == term && !network.Info().Ingress() { + if network.Name == term && !network.Ingress { // No need to check the ID collision here as we are still in // local scope and the network ID is unique in this scope. - listByFullName[network.ID()] = *n.buildDetailedNetworkResources(network, false) + listByFullName[network.ID] = network } - if strings.HasPrefix(network.ID(), term) { + if strings.HasPrefix(network.ID, term) { // No need to check the ID collision here as we are still in // local scope and the network ID is unique in this scope. - listByPartialID[network.ID()] = *n.buildDetailedNetworkResources(network, false) + listByPartialID[network.ID] = network } } - nr, _ := n.cluster.GetNetworks() + nr, _ := n.cluster.GetNetworks(filter) for _, network := range nr { if network.ID == term { return network, nil diff --git a/api/server/router/system/system.go b/api/server/router/system/system.go index ebb840a899f60..11370584efad8 100644 --- a/api/server/router/system/system.go +++ b/api/server/router/system/system.go @@ -2,6 +2,7 @@ package system // import "github.com/docker/docker/api/server/router/system" import ( "github.com/docker/docker/api/server/router" + buildkit "github.com/docker/docker/builder/builder-next" "github.com/docker/docker/builder/fscache" ) @@ -11,15 +12,17 @@ type systemRouter struct { backend Backend cluster ClusterBackend routes []router.Route - builder *fscache.FSCache + fscache *fscache.FSCache // legacy + builder *buildkit.Builder } // NewRouter initializes a new system router -func NewRouter(b Backend, c ClusterBackend, fscache *fscache.FSCache) router.Router { +func NewRouter(b Backend, c ClusterBackend, fscache *fscache.FSCache, builder *buildkit.Builder) router.Router { r := &systemRouter{ backend: b, cluster: c, - builder: fscache, + fscache: fscache, + builder: builder, } r.routes = []router.Route{ diff --git a/api/server/router/system/system_routes.go b/api/server/router/system/system_routes.go index 573496886cc15..82b649e98f9d8 100644 --- a/api/server/router/system/system_routes.go +++ b/api/server/router/system/system_routes.go @@ -17,6 +17,7 @@ import ( "github.com/docker/docker/pkg/ioutils" pkgerrors "github.com/pkg/errors" "github.com/sirupsen/logrus" + "golang.org/x/sync/errgroup" ) func optionsHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { @@ -69,15 +70,45 @@ func (s *systemRouter) getVersion(ctx context.Context, w http.ResponseWriter, r } func (s *systemRouter) getDiskUsage(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - du, err := s.backend.SystemDiskUsage(ctx) - if err != nil { + eg, ctx := errgroup.WithContext(ctx) + + var du *types.DiskUsage + eg.Go(func() error { + var err error + du, err = s.backend.SystemDiskUsage(ctx) + return err + }) + + var builderSize int64 // legacy + eg.Go(func() error { + var err error + builderSize, err = s.fscache.DiskUsage(ctx) + if err != nil { + return pkgerrors.Wrap(err, "error getting fscache build cache usage") + } + return nil + }) + + var buildCache []*types.BuildCache + eg.Go(func() error { + var err error + buildCache, err = s.builder.DiskUsage(ctx) + if err != nil { + return pkgerrors.Wrap(err, "error getting build cache usage") + } + return nil + }) + + if err := eg.Wait(); err != nil { return err } - builderSize, err := s.builder.DiskUsage(ctx) - if err != nil { - return pkgerrors.Wrap(err, "error getting build cache usage") + + for _, b := range buildCache { + builderSize += b.Size } + du.BuilderSize = builderSize + du.BuildCache = buildCache return httputils.WriteJSON(w, http.StatusOK, du) } diff --git a/api/server/server.go b/api/server/server.go index 3874a56ce5775..c3640082862ca 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -149,7 +149,7 @@ func (s *Server) makeHTTPHandler(handler httputils.APIFunc) http.HandlerFunc { } // InitRouter initializes the list of routers for the server. -// This method also enables the Go profiler if enableProfiler is true. +// This method also enables the Go profiler. func (s *Server) InitRouter(routers ...router.Router) { s.routers = append(s.routers, routers...) diff --git a/api/swagger.yaml b/api/swagger.yaml index 3cdfe2988ca74..0e7e69977e523 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -2721,6 +2721,10 @@ definitions: - "default" - "process" - "hyperv" + Init: + description: "Run an init inside the container that forwards signals and reaps processes. This field is omitted if empty, and the default (as configured on the daemon) is used." + type: "boolean" + x-nullable: true NetworkAttachmentSpec: description: | Read-only spec type for non-swarm containers attached to swarm overlay @@ -5918,7 +5922,7 @@ paths: headers: X-Docker-Container-Path-Stat: type: "string" - description: "TODO" + description: "A base64 - encoded JSON object with some filesystem header information about the path" 400: description: "Bad parameter" schema: @@ -7585,6 +7589,7 @@ paths: schema: type: "object" title: "VolumeListResponse" + description: "Volume list response" required: [Volumes, Warnings] properties: Volumes: @@ -7661,6 +7666,8 @@ paths: description: "Volume configuration" schema: type: "object" + description: "Volume configuration" + title: "VolumeConfig" properties: Name: description: "The new volume's name. If not specified, Docker generates a name." diff --git a/api/templates/server/operation.gotmpl b/api/templates/server/operation.gotmpl index 8bed59d92080e..cf24aacc5f6a1 100644 --- a/api/templates/server/operation.gotmpl +++ b/api/templates/server/operation.gotmpl @@ -8,10 +8,8 @@ package {{ .Package }} // ---------------------------------------------------------------------------- import ( + "context" "net/http" - - context "golang.org/x/net/context" - {{ range .DefaultImports }}{{ printf "%q" . }} {{ end }} {{ range $key, $value := .Imports }}{{ $key }} {{ printf "%q" $value }} diff --git a/api/types/backend/build.go b/api/types/backend/build.go index 31e00ec6ceb9b..1a2e59f2f7c88 100644 --- a/api/types/backend/build.go +++ b/api/types/backend/build.go @@ -5,6 +5,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/pkg/streamformatter" + specs "github.com/opencontainers/image-spec/specs-go/v1" ) // PullOption defines different modes for accessing images @@ -40,5 +41,5 @@ type GetImageAndLayerOptions struct { PullOption PullOption AuthConfig map[string]types.AuthConfig Output io.Writer - OS string + Platform *specs.Platform } diff --git a/api/types/client.go b/api/types/client.go index 3d2e057c9aac1..3b698c2c240d0 100644 --- a/api/types/client.go +++ b/api/types/client.go @@ -7,7 +7,7 @@ import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/filters" - "github.com/docker/go-units" + units "github.com/docker/go-units" ) // CheckpointCreateOptions holds parameters to create a checkpoint from a container @@ -181,8 +181,24 @@ type ImageBuildOptions struct { Target string SessionID string 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 + // build request. The same identifier can be used to gracefully cancel the + // build with the cancel request. + BuildID string } +// BuilderVersion sets the version of underlying builder to use +type BuilderVersion string + +const ( + // BuilderV1 is the first generation builder in docker daemon + BuilderV1 BuilderVersion = "1" + // BuilderBuildKit is builder based on moby/buildkit project + BuilderBuildKit = "2" +) + // ImageBuildResponse holds information // returned by a server after building // an image. diff --git a/api/types/configs.go b/api/types/configs.go index f6537a27f21ef..178e911a7afc2 100644 --- a/api/types/configs.go +++ b/api/types/configs.go @@ -55,3 +55,10 @@ type PluginEnableConfig struct { type PluginDisableConfig struct { ForceDisable bool } + +// NetworkListConfig stores the options available for listing networks +type NetworkListConfig struct { + // TODO(@cpuguy83): naming is hard, this is pulled from what was being used in the router before moving here + Detailed bool + Verbose bool +} diff --git a/api/types/filters/parse_test.go b/api/types/filters/parse_test.go index fbd9ae4fb174a..e8345a1d5d524 100644 --- a/api/types/filters/parse_test.go +++ b/api/types/filters/parse_test.go @@ -4,8 +4,8 @@ import ( "errors" "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestParseArgs(t *testing.T) { diff --git a/api/types/network/network.go b/api/types/network/network.go index 761d0b34f2f16..ccb448f23a214 100644 --- a/api/types/network/network.go +++ b/api/types/network/network.go @@ -1,4 +1,8 @@ package network // import "github.com/docker/docker/api/types/network" +import ( + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/errdefs" +) // Address represents an IP address type Address struct { @@ -106,3 +110,17 @@ type NetworkingConfig struct { type ConfigReference struct { Network string } + +var acceptedFilters = map[string]bool{ + "driver": true, + "type": true, + "name": true, + "id": true, + "label": true, + "scope": true, +} + +// ValidateFilters validates the list of filter args with the available filters. +func ValidateFilters(filter filters.Args) error { + return errdefs.InvalidParameter(filter.Validate(acceptedFilters)) +} diff --git a/api/types/swarm/container.go b/api/types/swarm/container.go index 0041653c9d1a6..151211ff5a496 100644 --- a/api/types/swarm/container.go +++ b/api/types/swarm/container.go @@ -55,6 +55,7 @@ type ContainerSpec struct { User string `json:",omitempty"` Groups []string `json:",omitempty"` Privileges *Privileges `json:",omitempty"` + Init *bool `json:",omitempty"` StopSignal string `json:",omitempty"` TTY bool `json:",omitempty"` OpenStdin bool `json:",omitempty"` diff --git a/api/types/types.go b/api/types/types.go index 729f4eb6c4912..06c0ca3a69f50 100644 --- a/api/types/types.go +++ b/api/types/types.go @@ -512,7 +512,8 @@ type DiskUsage struct { Images []*ImageSummary Containers []*Container Volumes []*Volume - BuilderSize int64 + BuildCache []*BuildCache + BuilderSize int64 // deprecated } // ContainersPruneReport contains the response for Engine API: @@ -585,3 +586,17 @@ type PushResult struct { type BuildResult struct { ID string } + +// BuildCache contains information about a build cache record +type BuildCache struct { + ID string + Mutable bool + InUse bool + Size int64 + + CreatedAt time.Time + LastUsedAt *time.Time + UsageCount int + Parent string + Description string +} diff --git a/api/types/volume/volume_create.go b/api/types/volume/volume_create.go index 539e9b97d9047..f12e48612c56c 100644 --- a/api/types/volume/volume_create.go +++ b/api/types/volume/volume_create.go @@ -7,7 +7,7 @@ package volume // See hack/generate-swagger-api.sh // ---------------------------------------------------------------------------- -// VolumeCreateBody +// VolumeCreateBody Volume configuration // swagger:model VolumeCreateBody type VolumeCreateBody struct { diff --git a/api/types/volume/volume_list.go b/api/types/volume/volume_list.go index 1bb279dbb3f37..020198f7371bc 100644 --- a/api/types/volume/volume_list.go +++ b/api/types/volume/volume_list.go @@ -9,7 +9,7 @@ package volume import "github.com/docker/docker/api/types" -// VolumeListOKBody +// VolumeListOKBody Volume list response // swagger:model VolumeListOKBody type VolumeListOKBody struct { diff --git a/builder/builder-next/adapters/containerimage/pull.go b/builder/builder-next/adapters/containerimage/pull.go new file mode 100644 index 0000000000000..2b6c214b8b8ed --- /dev/null +++ b/builder/builder-next/adapters/containerimage/pull.go @@ -0,0 +1,733 @@ +package containerimage + +import ( + "context" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "runtime" + "sync" + "time" + + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/images" + "github.com/containerd/containerd/platforms" + ctdreference "github.com/containerd/containerd/reference" + "github.com/containerd/containerd/remotes" + "github.com/containerd/containerd/remotes/docker" + "github.com/containerd/containerd/remotes/docker/schema1" + distreference "github.com/docker/distribution/reference" + "github.com/docker/docker/distribution" + "github.com/docker/docker/distribution/metadata" + "github.com/docker/docker/distribution/xfer" + "github.com/docker/docker/image" + "github.com/docker/docker/layer" + pkgprogress "github.com/docker/docker/pkg/progress" + "github.com/docker/docker/reference" + "github.com/moby/buildkit/cache" + "github.com/moby/buildkit/session" + "github.com/moby/buildkit/session/auth" + "github.com/moby/buildkit/source" + "github.com/moby/buildkit/util/flightcontrol" + "github.com/moby/buildkit/util/imageutil" + "github.com/moby/buildkit/util/progress" + "github.com/moby/buildkit/util/tracing" + digest "github.com/opencontainers/go-digest" + "github.com/opencontainers/image-spec/identity" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" + "golang.org/x/time/rate" +) + +const preferLocal = true // FIXME: make this optional from the op + +// SourceOpt is options for creating the image source +type SourceOpt struct { + SessionManager *session.Manager + ContentStore content.Store + CacheAccessor cache.Accessor + ReferenceStore reference.Store + DownloadManager distribution.RootFSDownloadManager + MetadataStore metadata.V2MetadataService + ImageStore image.Store +} + +type imageSource struct { + SourceOpt + g flightcontrol.Group +} + +// NewSource creates a new image source +func NewSource(opt SourceOpt) (source.Source, error) { + is := &imageSource{ + SourceOpt: opt, + } + + return is, nil +} + +func (is *imageSource) ID() string { + return source.DockerImageScheme +} + +func (is *imageSource) getResolver(ctx context.Context) remotes.Resolver { + return docker.NewResolver(docker.ResolverOptions{ + Client: tracing.DefaultClient, + Credentials: is.getCredentialsFromSession(ctx), + }) +} + +func (is *imageSource) getCredentialsFromSession(ctx context.Context) func(string) (string, string, error) { + id := session.FromContext(ctx) + if id == "" { + return nil + } + return func(host string) (string, string, error) { + timeoutCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + caller, err := is.SessionManager.Get(timeoutCtx, id) + if err != nil { + return "", "", err + } + + return auth.CredentialsFunc(tracing.ContextWithSpanFromContext(context.TODO(), ctx), caller)(host) + } +} + +func (is *imageSource) resolveLocal(refStr string) ([]byte, error) { + ref, err := distreference.ParseNormalizedNamed(refStr) + if err != nil { + return nil, err + } + dgst, err := is.ReferenceStore.Get(ref) + if err != nil { + return nil, err + } + img, err := is.ImageStore.Get(image.ID(dgst)) + if err != nil { + return nil, err + } + return img.RawJSON(), nil +} + +func (is *imageSource) ResolveImageConfig(ctx context.Context, ref string, platform *ocispec.Platform) (digest.Digest, []byte, error) { + if preferLocal { + dt, err := is.resolveLocal(ref) + if err == nil { + return "", dt, nil + } + } + + type t struct { + dgst digest.Digest + dt []byte + } + res, err := is.g.Do(ctx, ref, func(ctx context.Context) (interface{}, error) { + dgst, dt, err := imageutil.Config(ctx, ref, is.getResolver(ctx), is.ContentStore, platform) + if err != nil { + return nil, err + } + return &t{dgst: dgst, dt: dt}, nil + }) + if err != nil { + return "", nil, err + } + typed := res.(*t) + return typed.dgst, typed.dt, nil +} + +func (is *imageSource) Resolve(ctx context.Context, id source.Identifier) (source.SourceInstance, error) { + imageIdentifier, ok := id.(*source.ImageIdentifier) + if !ok { + return nil, errors.Errorf("invalid image identifier %v", id) + } + + platform := platforms.DefaultSpec() + if imageIdentifier.Platform != nil { + platform = *imageIdentifier.Platform + } + + p := &puller{ + src: imageIdentifier, + is: is, + resolver: is.getResolver(ctx), + platform: platform, + } + return p, nil +} + +type puller struct { + is *imageSource + resolveOnce sync.Once + resolveLocalOnce sync.Once + src *source.ImageIdentifier + desc ocispec.Descriptor + ref string + resolveErr error + resolver remotes.Resolver + config []byte + platform ocispec.Platform +} + +func (p *puller) mainManifestKey(dgst digest.Digest, platform ocispec.Platform) (digest.Digest, error) { + dt, err := json.Marshal(struct { + Digest digest.Digest + OS string + Arch string + Variant string `json:",omitempty"` + }{ + Digest: p.desc.Digest, + OS: platform.OS, + Arch: platform.Architecture, + Variant: platform.Variant, + }) + if err != nil { + return "", err + } + return digest.FromBytes(dt), nil +} + +func (p *puller) resolveLocal() { + p.resolveLocalOnce.Do(func() { + dgst := p.src.Reference.Digest() + if dgst != "" { + info, err := p.is.ContentStore.Info(context.TODO(), dgst) + if err == nil { + p.ref = p.src.Reference.String() + desc := ocispec.Descriptor{ + Size: info.Size, + Digest: dgst, + } + ra, err := p.is.ContentStore.ReaderAt(context.TODO(), desc) + if err == nil { + mt, err := imageutil.DetectManifestMediaType(ra) + if err == nil { + desc.MediaType = mt + p.desc = desc + } + } + } + } + + if preferLocal { + dt, err := p.is.resolveLocal(p.src.Reference.String()) + if err == nil { + p.config = dt + } + } + }) +} + +func (p *puller) resolve(ctx context.Context) error { + p.resolveOnce.Do(func() { + resolveProgressDone := oneOffProgress(ctx, "resolve "+p.src.Reference.String()) + + ref, err := distreference.ParseNormalizedNamed(p.src.Reference.String()) + if err != nil { + p.resolveErr = err + resolveProgressDone(err) + return + } + + if p.desc.Digest == "" && p.config == nil { + origRef, desc, err := p.resolver.Resolve(ctx, ref.String()) + if err != nil { + p.resolveErr = err + resolveProgressDone(err) + return + } + + p.desc = desc + p.ref = origRef + } + + // Schema 1 manifests cannot be resolved to an image config + // since the conversion must take place after all the content + // has been read. + // It may be possible to have a mapping between schema 1 manifests + // and the schema 2 manifests they are converted to. + if p.config == nil && p.desc.MediaType != images.MediaTypeDockerSchema1Manifest { + ref, err := distreference.WithDigest(ref, p.desc.Digest) + if err != nil { + p.resolveErr = err + resolveProgressDone(err) + return + } + + _, dt, err := p.is.ResolveImageConfig(ctx, ref.String(), &p.platform) + if err != nil { + p.resolveErr = err + resolveProgressDone(err) + return + } + + p.config = dt + } + resolveProgressDone(nil) + }) + return p.resolveErr +} + +func (p *puller) CacheKey(ctx context.Context, index int) (string, bool, error) { + p.resolveLocal() + + if p.desc.Digest != "" && index == 0 { + dgst, err := p.mainManifestKey(p.desc.Digest, p.platform) + if err != nil { + return "", false, err + } + return dgst.String(), false, nil + } + + if p.config != nil { + return cacheKeyFromConfig(p.config).String(), true, nil + } + + if err := p.resolve(ctx); err != nil { + return "", false, err + } + + if p.desc.Digest != "" && index == 0 { + dgst, err := p.mainManifestKey(p.desc.Digest, p.platform) + if err != nil { + return "", false, err + } + return dgst.String(), false, nil + } + + return cacheKeyFromConfig(p.config).String(), true, nil +} + +func (p *puller) Snapshot(ctx context.Context) (cache.ImmutableRef, error) { + p.resolveLocal() + if err := p.resolve(ctx); err != nil { + return nil, err + } + + if p.config != nil { + img, err := p.is.ImageStore.Get(image.ID(digest.FromBytes(p.config))) + if err == nil { + if len(img.RootFS.DiffIDs) == 0 { + return nil, nil + } + ref, err := p.is.CacheAccessor.GetFromSnapshotter(ctx, string(img.RootFS.ChainID()), cache.WithDescription(fmt.Sprintf("from local %s", p.ref))) + if err != nil { + return nil, err + } + return ref, nil + } + } + + ongoing := newJobs(p.ref) + + pctx, stopProgress := context.WithCancel(ctx) + + pw, _, ctx := progress.FromContext(ctx) + defer pw.Close() + + progressDone := make(chan struct{}) + go func() { + showProgress(pctx, ongoing, p.is.ContentStore, pw) + close(progressDone) + }() + defer func() { + <-progressDone + }() + + fetcher, err := p.resolver.Fetcher(ctx, p.ref) + if err != nil { + stopProgress() + return nil, err + } + + var ( + schema1Converter *schema1.Converter + handlers []images.Handler + ) + if p.desc.MediaType == images.MediaTypeDockerSchema1Manifest { + schema1Converter = schema1.NewConverter(p.is.ContentStore, fetcher) + handlers = append(handlers, schema1Converter) + + // TODO: Optimize to do dispatch and integrate pulling with download manager, + // leverage existing blob mapping and layer storage + } else { + + // TODO: need a wrapper snapshot interface that combines content + // and snapshots as 1) buildkit shouldn't have a dependency on contentstore + // or 2) cachemanager should manage the contentstore + handlers = append(handlers, images.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { + switch desc.MediaType { + case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest, + images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex, + images.MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig: + default: + return nil, images.ErrSkipDesc + } + ongoing.add(desc) + return nil, nil + })) + + // Get all the children for a descriptor + childrenHandler := images.ChildrenHandler(p.is.ContentStore) + // Set any children labels for that content + childrenHandler = images.SetChildrenLabels(p.is.ContentStore, childrenHandler) + // Filter the childen by the platform + childrenHandler = images.FilterPlatforms(childrenHandler, platforms.Default()) + + handlers = append(handlers, + remotes.FetchHandler(p.is.ContentStore, fetcher), + childrenHandler, + ) + } + + if err := images.Dispatch(ctx, images.Handlers(handlers...), p.desc); err != nil { + stopProgress() + return nil, err + } + defer stopProgress() + + if schema1Converter != nil { + p.desc, err = schema1Converter.Convert(ctx) + if err != nil { + return nil, err + } + } + + mfst, err := images.Manifest(ctx, p.is.ContentStore, p.desc, platforms.Default()) + if err != nil { + return nil, err + } + + config, err := images.Config(ctx, p.is.ContentStore, p.desc, platforms.Default()) + if err != nil { + return nil, err + } + + dt, err := content.ReadBlob(ctx, p.is.ContentStore, config) + if err != nil { + return nil, err + } + + var img ocispec.Image + if err := json.Unmarshal(dt, &img); err != nil { + return nil, err + } + + if len(mfst.Layers) != len(img.RootFS.DiffIDs) { + return nil, errors.Errorf("invalid config for manifest") + } + + pchan := make(chan pkgprogress.Progress, 10) + defer close(pchan) + + go func() { + m := map[string]struct { + st time.Time + limiter *rate.Limiter + }{} + for p := range pchan { + if p.Action == "Extracting" { + st, ok := m[p.ID] + if !ok { + st.st = time.Now() + st.limiter = rate.NewLimiter(rate.Every(100*time.Millisecond), 1) + m[p.ID] = st + } + var end *time.Time + if p.LastUpdate || st.limiter.Allow() { + if p.LastUpdate { + tm := time.Now() + end = &tm + } + pw.Write("extracting "+p.ID, progress.Status{ + Action: "extract", + Started: &st.st, + Completed: end, + }) + } + } + } + }() + + if len(mfst.Layers) == 0 { + return nil, nil + } + + layers := make([]xfer.DownloadDescriptor, 0, len(mfst.Layers)) + + for i, desc := range mfst.Layers { + ongoing.add(desc) + layers = append(layers, &layerDescriptor{ + desc: desc, + diffID: layer.DiffID(img.RootFS.DiffIDs[i]), + fetcher: fetcher, + ref: p.src.Reference, + is: p.is, + }) + } + + defer func() { + <-progressDone + for _, desc := range mfst.Layers { + p.is.ContentStore.Delete(context.TODO(), desc.Digest) + } + }() + + r := image.NewRootFS() + rootFS, release, err := p.is.DownloadManager.Download(ctx, *r, runtime.GOOS, layers, pkgprogress.ChanOutput(pchan)) + if err != nil { + return nil, err + } + stopProgress() + + ref, err := p.is.CacheAccessor.GetFromSnapshotter(ctx, string(rootFS.ChainID()), cache.WithDescription(fmt.Sprintf("pulled from %s", p.ref))) + release() + if err != nil { + return nil, err + } + + return ref, nil +} + +// Fetch(ctx context.Context, desc ocispec.Descriptor) (io.ReadCloser, error) +type layerDescriptor struct { + is *imageSource + fetcher remotes.Fetcher + desc ocispec.Descriptor + diffID layer.DiffID + ref ctdreference.Spec +} + +func (ld *layerDescriptor) Key() string { + return "v2:" + ld.desc.Digest.String() +} + +func (ld *layerDescriptor) ID() string { + return ld.desc.Digest.String() +} + +func (ld *layerDescriptor) DiffID() (layer.DiffID, error) { + return ld.diffID, nil +} + +func (ld *layerDescriptor) Download(ctx context.Context, progressOutput pkgprogress.Output) (io.ReadCloser, int64, error) { + rc, err := ld.fetcher.Fetch(ctx, ld.desc) + if err != nil { + return nil, 0, err + } + defer rc.Close() + + refKey := remotes.MakeRefKey(ctx, ld.desc) + + ld.is.ContentStore.Abort(ctx, refKey) + + if err := content.WriteBlob(ctx, ld.is.ContentStore, refKey, rc, ld.desc); err != nil { + ld.is.ContentStore.Abort(ctx, refKey) + return nil, 0, err + } + + ra, err := ld.is.ContentStore.ReaderAt(ctx, ld.desc) + if err != nil { + return nil, 0, err + } + + return ioutil.NopCloser(content.NewReader(ra)), ld.desc.Size, nil +} + +func (ld *layerDescriptor) Close() { + // ld.is.ContentStore.Delete(context.TODO(), ld.desc.Digest)) +} + +func (ld *layerDescriptor) Registered(diffID layer.DiffID) { + // Cache mapping from this layer's DiffID to the blobsum + ld.is.MetadataStore.Add(diffID, metadata.V2Metadata{Digest: ld.desc.Digest, SourceRepository: ld.ref.Locator}) +} + +func showProgress(ctx context.Context, ongoing *jobs, cs content.Store, pw progress.Writer) { + var ( + ticker = time.NewTicker(100 * time.Millisecond) + statuses = map[string]statusInfo{} + done bool + ) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + case <-ctx.Done(): + done = true + } + + resolved := "resolved" + if !ongoing.isResolved() { + resolved = "resolving" + } + statuses[ongoing.name] = statusInfo{ + Ref: ongoing.name, + Status: resolved, + } + + actives := make(map[string]statusInfo) + + if !done { + active, err := cs.ListStatuses(ctx) + if err != nil { + // log.G(ctx).WithError(err).Error("active check failed") + continue + } + // update status of active entries! + for _, active := range active { + actives[active.Ref] = statusInfo{ + Ref: active.Ref, + Status: "downloading", + Offset: active.Offset, + Total: active.Total, + StartedAt: active.StartedAt, + UpdatedAt: active.UpdatedAt, + } + } + } + + // now, update the items in jobs that are not in active + for _, j := range ongoing.jobs() { + refKey := remotes.MakeRefKey(ctx, j.Descriptor) + if a, ok := actives[refKey]; ok { + started := j.started + pw.Write(j.Digest.String(), progress.Status{ + Action: a.Status, + Total: int(a.Total), + Current: int(a.Offset), + Started: &started, + }) + continue + } + + if !j.done { + info, err := cs.Info(context.TODO(), j.Digest) + if err != nil { + if errdefs.IsNotFound(err) { + // pw.Write(j.Digest.String(), progress.Status{ + // Action: "waiting", + // }) + continue + } + } else { + j.done = true + } + + if done || j.done { + started := j.started + createdAt := info.CreatedAt + pw.Write(j.Digest.String(), progress.Status{ + Action: "done", + Current: int(info.Size), + Total: int(info.Size), + Completed: &createdAt, + Started: &started, + }) + } + } + } + if done { + return + } + } +} + +// jobs provides a way of identifying the download keys for a particular task +// encountering during the pull walk. +// +// This is very minimal and will probably be replaced with something more +// featured. +type jobs struct { + name string + added map[digest.Digest]*job + mu sync.Mutex + resolved bool +} + +type job struct { + ocispec.Descriptor + done bool + started time.Time +} + +func newJobs(name string) *jobs { + return &jobs{ + name: name, + added: make(map[digest.Digest]*job), + } +} + +func (j *jobs) add(desc ocispec.Descriptor) { + j.mu.Lock() + defer j.mu.Unlock() + + if _, ok := j.added[desc.Digest]; ok { + return + } + j.added[desc.Digest] = &job{ + Descriptor: desc, + started: time.Now(), + } +} + +func (j *jobs) jobs() []*job { + j.mu.Lock() + defer j.mu.Unlock() + + descs := make([]*job, 0, len(j.added)) + for _, j := range j.added { + descs = append(descs, j) + } + return descs +} + +func (j *jobs) isResolved() bool { + j.mu.Lock() + defer j.mu.Unlock() + return j.resolved +} + +type statusInfo struct { + Ref string + Status string + Offset int64 + Total int64 + StartedAt time.Time + UpdatedAt time.Time +} + +func oneOffProgress(ctx context.Context, id string) func(err error) error { + pw, _, _ := progress.FromContext(ctx) + now := time.Now() + st := progress.Status{ + Started: &now, + } + pw.Write(id, st) + return func(err error) error { + // TODO: set error on status + now := time.Now() + st.Completed = &now + pw.Write(id, st) + pw.Close() + return err + } +} + +// cacheKeyFromConfig returns a stable digest from image config. If image config +// is a known oci image we will use chainID of layers. +func cacheKeyFromConfig(dt []byte) digest.Digest { + var img ocispec.Image + err := json.Unmarshal(dt, &img) + if err != nil { + return digest.FromBytes(dt) + } + if img.RootFS.Type != "layers" { + return digest.FromBytes(dt) + } + return identity.ChainID(img.RootFS.DiffIDs) +} diff --git a/builder/builder-next/adapters/snapshot/layer.go b/builder/builder-next/adapters/snapshot/layer.go new file mode 100644 index 0000000000000..d0aa6f28fae8c --- /dev/null +++ b/builder/builder-next/adapters/snapshot/layer.go @@ -0,0 +1,113 @@ +package snapshot + +import ( + "context" + "os" + "path/filepath" + + "github.com/boltdb/bolt" + "github.com/docker/docker/layer" + "github.com/docker/docker/pkg/ioutils" + "github.com/pkg/errors" + "golang.org/x/sync/errgroup" +) + +func (s *snapshotter) EnsureLayer(ctx context.Context, key string) ([]layer.DiffID, error) { + if l, err := s.getLayer(key, true); err != nil { + return nil, err + } else if l != nil { + return getDiffChain(l), nil + } + + id, committed := s.getGraphDriverID(key) + if !committed { + return nil, errors.Errorf("can not convert active %s to layer", key) + } + + info, err := s.Stat(ctx, key) + if err != nil { + return nil, err + } + + eg, gctx := errgroup.WithContext(ctx) + + // TODO: add flightcontrol + + var parentChainID layer.ChainID + if info.Parent != "" { + eg.Go(func() error { + diffIDs, err := s.EnsureLayer(gctx, info.Parent) + if err != nil { + return err + } + parentChainID = layer.CreateChainID(diffIDs) + return nil + }) + } + + tmpDir, err := ioutils.TempDir("", "docker-tarsplit") + if err != nil { + return nil, err + } + defer os.RemoveAll(tmpDir) + tarSplitPath := filepath.Join(tmpDir, "tar-split") + + var diffID layer.DiffID + var size int64 + eg.Go(func() error { + parent := "" + if p := info.Parent; p != "" { + if l, err := s.getLayer(p, true); err != nil { + return err + } else if l != nil { + parent, err = getGraphID(l) + if err != nil { + return err + } + } else { + parent, _ = s.getGraphDriverID(info.Parent) + } + } + diffID, size, err = s.reg.ChecksumForGraphID(id, parent, "", tarSplitPath) + return err + }) + + if err := eg.Wait(); err != nil { + return nil, err + } + + l, err := s.reg.RegisterByGraphID(id, parentChainID, diffID, tarSplitPath, size) + if err != nil { + return nil, err + } + + if err := s.db.Update(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(key)) + b.Put(keyChainID, []byte(l.ChainID())) + return nil + }); err != nil { + return nil, err + } + + s.mu.Lock() + s.refs[key] = l + s.mu.Unlock() + + return getDiffChain(l), nil +} + +func getDiffChain(l layer.Layer) []layer.DiffID { + if p := l.Parent(); p != nil { + return append(getDiffChain(p), l.DiffID()) + } + return []layer.DiffID{l.DiffID()} +} + +func getGraphID(l layer.Layer) (string, error) { + if l, ok := l.(interface { + CacheID() string + }); ok { + return l.CacheID(), nil + } + return "", errors.Errorf("couldn't access cacheID for %s", l.ChainID()) +} diff --git a/builder/builder-next/adapters/snapshot/snapshot.go b/builder/builder-next/adapters/snapshot/snapshot.go new file mode 100644 index 0000000000000..e1a12a4563c4b --- /dev/null +++ b/builder/builder-next/adapters/snapshot/snapshot.go @@ -0,0 +1,457 @@ +package snapshot + +import ( + "context" + "path/filepath" + "strconv" + "strings" + "sync" + + "github.com/boltdb/bolt" + "github.com/containerd/containerd/mount" + "github.com/containerd/containerd/snapshots" + "github.com/docker/docker/daemon/graphdriver" + "github.com/docker/docker/layer" + "github.com/moby/buildkit/identity" + "github.com/moby/buildkit/snapshot" + digest "github.com/opencontainers/go-digest" + "github.com/pkg/errors" +) + +var keyParent = []byte("parent") +var keyCommitted = []byte("committed") +var keyChainID = []byte("chainid") +var keySize = []byte("size") + +// Opt defines options for creating the snapshotter +type Opt struct { + GraphDriver graphdriver.Driver + LayerStore layer.Store + Root string +} + +type graphIDRegistrar interface { + RegisterByGraphID(string, layer.ChainID, layer.DiffID, string, int64) (layer.Layer, error) + Release(layer.Layer) ([]layer.Metadata, error) + checksumCalculator +} + +type checksumCalculator interface { + ChecksumForGraphID(id, parent, oldTarDataPath, newTarDataPath string) (diffID layer.DiffID, size int64, err error) +} + +type snapshotter struct { + opt Opt + + refs map[string]layer.Layer + db *bolt.DB + mu sync.Mutex + reg graphIDRegistrar +} + +var _ snapshot.SnapshotterBase = &snapshotter{} + +// NewSnapshotter creates a new snapshotter +func NewSnapshotter(opt Opt) (snapshot.SnapshotterBase, error) { + dbPath := filepath.Join(opt.Root, "snapshots.db") + db, err := bolt.Open(dbPath, 0600, nil) + if err != nil { + return nil, errors.Wrapf(err, "failed to open database file %s", dbPath) + } + + reg, ok := opt.LayerStore.(graphIDRegistrar) + if !ok { + return nil, errors.Errorf("layerstore doesn't support graphID registration") + } + + s := &snapshotter{ + opt: opt, + db: db, + refs: map[string]layer.Layer{}, + reg: reg, + } + return s, nil +} + +func (s *snapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) error { + origParent := parent + if parent != "" { + if l, err := s.getLayer(parent, false); err != nil { + return err + } else if l != nil { + parent, err = getGraphID(l) + if err != nil { + return err + } + } else { + parent, _ = s.getGraphDriverID(parent) + } + } + if err := s.opt.GraphDriver.Create(key, parent, nil); err != nil { + return err + } + return s.db.Update(func(tx *bolt.Tx) error { + b, err := tx.CreateBucketIfNotExists([]byte(key)) + if err != nil { + return err + } + return b.Put(keyParent, []byte(origParent)) + }) +} + +func (s *snapshotter) chainID(key string) (layer.ChainID, bool) { + if strings.HasPrefix(key, "sha256:") { + dgst, err := digest.Parse(key) + if err != nil { + return "", false + } + return layer.ChainID(dgst), true + } + return "", false +} + +func (s *snapshotter) getLayer(key string, withCommitted bool) (layer.Layer, error) { + s.mu.Lock() + l, ok := s.refs[key] + if !ok { + id, ok := s.chainID(key) + if !ok { + if !withCommitted { + s.mu.Unlock() + return nil, nil + } + if err := s.db.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(key)) + if b == nil { + return nil + } + v := b.Get(keyChainID) + if v != nil { + id = layer.ChainID(v) + } + return nil + }); err != nil { + s.mu.Unlock() + return nil, err + } + if id == "" { + s.mu.Unlock() + return nil, nil + } + } + var err error + l, err = s.opt.LayerStore.Get(id) + if err != nil { + s.mu.Unlock() + return nil, err + } + s.refs[key] = l + if err := s.db.Update(func(tx *bolt.Tx) error { + _, err := tx.CreateBucketIfNotExists([]byte(key)) + return err + }); err != nil { + s.mu.Unlock() + return nil, err + } + } + s.mu.Unlock() + + return l, nil +} + +func (s *snapshotter) getGraphDriverID(key string) (string, bool) { + var gdID string + if err := s.db.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(key)) + if b == nil { + return errors.Errorf("not found") // TODO: typed + } + v := b.Get(keyCommitted) + if v != nil { + gdID = string(v) + } + return nil + }); err != nil || gdID == "" { + return key, false + } + return gdID, true +} + +func (s *snapshotter) Stat(ctx context.Context, key string) (snapshots.Info, error) { + inf := snapshots.Info{ + Kind: snapshots.KindActive, + } + + l, err := s.getLayer(key, false) + if err != nil { + return snapshots.Info{}, err + } + if l != nil { + if p := l.Parent(); p != nil { + inf.Parent = p.ChainID().String() + } + inf.Kind = snapshots.KindCommitted + inf.Name = key + return inf, nil + } + + l, err = s.getLayer(key, true) + if err != nil { + return snapshots.Info{}, err + } + + id, committed := s.getGraphDriverID(key) + if committed { + inf.Kind = snapshots.KindCommitted + } + + if err := s.db.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(id)) + if b == nil && l == nil { + return errors.Errorf("snapshot %s not found", id) // TODO: typed + } + inf.Name = key + if b != nil { + v := b.Get(keyParent) + if v != nil { + inf.Parent = string(v) + return nil + } + } + if l != nil { + if p := l.Parent(); p != nil { + inf.Parent = p.ChainID().String() + } + inf.Kind = snapshots.KindCommitted + } + return nil + }); err != nil { + return snapshots.Info{}, err + } + return inf, nil +} + +func (s *snapshotter) Mounts(ctx context.Context, key string) (snapshot.Mountable, error) { + l, err := s.getLayer(key, true) + if err != nil { + return nil, err + } + if l != nil { + id := identity.NewID() + var rwlayer layer.RWLayer + return &mountable{ + acquire: func() ([]mount.Mount, error) { + rwlayer, err = s.opt.LayerStore.CreateRWLayer(id, l.ChainID(), nil) + if err != nil { + return nil, err + } + rootfs, err := rwlayer.Mount("") + if err != nil { + return nil, err + } + return []mount.Mount{{ + Source: rootfs.Path(), + Type: "bind", + Options: []string{"rbind"}, + }}, nil + }, + release: func() error { + _, err := s.opt.LayerStore.ReleaseRWLayer(rwlayer) + return err + }, + }, nil + } + + id, _ := s.getGraphDriverID(key) + + return &mountable{ + acquire: func() ([]mount.Mount, error) { + rootfs, err := s.opt.GraphDriver.Get(id, "") + if err != nil { + return nil, err + } + return []mount.Mount{{ + Source: rootfs.Path(), + Type: "bind", + Options: []string{"rbind"}, + }}, nil + }, + release: func() error { + return s.opt.GraphDriver.Put(id) + }, + }, nil +} + +func (s *snapshotter) Remove(ctx context.Context, key string) error { + l, err := s.getLayer(key, true) + if err != nil { + return err + } + + id, _ := s.getGraphDriverID(key) + + var found bool + if err := s.db.Update(func(tx *bolt.Tx) error { + found = tx.Bucket([]byte(key)) != nil + if found { + tx.DeleteBucket([]byte(key)) + if id != key { + tx.DeleteBucket([]byte(id)) + } + } + return nil + }); err != nil { + return err + } + + if l != nil { + s.mu.Lock() + delete(s.refs, key) + s.mu.Unlock() + _, err := s.opt.LayerStore.Release(l) + return err + } + + if !found { // this happens when removing views + return nil + } + + return s.opt.GraphDriver.Remove(id) +} + +func (s *snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) error { + return s.db.Update(func(tx *bolt.Tx) error { + b, err := tx.CreateBucketIfNotExists([]byte(name)) + if err != nil { + return err + } + return b.Put(keyCommitted, []byte(key)) + }) +} + +func (s *snapshotter) View(ctx context.Context, key, parent string, opts ...snapshots.Opt) (snapshot.Mountable, error) { + return s.Mounts(ctx, parent) +} + +func (s *snapshotter) Walk(ctx context.Context, fn func(context.Context, snapshots.Info) error) error { + return errors.Errorf("not-implemented") +} + +func (s *snapshotter) Update(ctx context.Context, info snapshots.Info, fieldpaths ...string) (snapshots.Info, error) { + // not implemented + return s.Stat(ctx, info.Name) +} + +func (s *snapshotter) Usage(ctx context.Context, key string) (us snapshots.Usage, retErr error) { + usage := snapshots.Usage{} + if l, err := s.getLayer(key, true); err != nil { + return usage, err + } else if l != nil { + s, err := l.DiffSize() + if err != nil { + return usage, err + } + usage.Size = s + return usage, nil + } + + size := int64(-1) + if err := s.db.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(key)) + if b == nil { + return nil + } + v := b.Get(keySize) + if v != nil { + s, err := strconv.Atoi(string(v)) + if err != nil { + return err + } + size = int64(s) + } + return nil + }); err != nil { + return usage, err + } + + if size != -1 { + usage.Size = size + return usage, nil + } + + id, _ := s.getGraphDriverID(key) + + info, err := s.Stat(ctx, key) + if err != nil { + return usage, err + } + var parent string + if info.Parent != "" { + if l, err := s.getLayer(info.Parent, false); err != nil { + return usage, err + } else if l != nil { + parent, err = getGraphID(l) + if err != nil { + return usage, err + } + } else { + parent, _ = s.getGraphDriverID(info.Parent) + } + } + + diffSize, err := s.opt.GraphDriver.DiffSize(id, parent) + if err != nil { + return usage, err + } + + if err := s.db.Update(func(tx *bolt.Tx) error { + b, err := tx.CreateBucketIfNotExists([]byte(key)) + if err != nil { + return err + } + return b.Put(keySize, []byte(strconv.Itoa(int(diffSize)))) + }); err != nil { + return usage, err + } + usage.Size = diffSize + return usage, nil +} + +func (s *snapshotter) Close() error { + return s.db.Close() +} + +type mountable struct { + mu sync.Mutex + mounts []mount.Mount + acquire func() ([]mount.Mount, error) + release func() error +} + +func (m *mountable) Mount() ([]mount.Mount, error) { + m.mu.Lock() + defer m.mu.Unlock() + + if m.mounts != nil { + return m.mounts, nil + } + + mounts, err := m.acquire() + if err != nil { + return nil, err + } + m.mounts = mounts + + return m.mounts, nil +} + +func (m *mountable) Release() error { + m.mu.Lock() + defer m.mu.Unlock() + if m.release == nil { + return nil + } + + m.mounts = nil + return m.release() +} diff --git a/builder/builder-next/builder.go b/builder/builder-next/builder.go new file mode 100644 index 0000000000000..dacec0af67398 --- /dev/null +++ b/builder/builder-next/builder.go @@ -0,0 +1,419 @@ +package buildkit + +import ( + "context" + "io" + "strings" + "sync" + "time" + + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/platforms" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/backend" + "github.com/docker/docker/builder" + "github.com/docker/docker/daemon/images" + "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" + "github.com/moby/buildkit/session" + "github.com/moby/buildkit/util/tracing" + "github.com/pkg/errors" + "golang.org/x/sync/errgroup" + grpcmetadata "google.golang.org/grpc/metadata" +) + +// Opt is option struct required for creating the builder +type Opt struct { + SessionManager *session.Manager + Root string + Dist images.DistributionServices +} + +// Builder can build using BuildKit backend +type Builder struct { + controller *control.Controller + reqBodyHandler *reqBodyHandler + + mu sync.Mutex + jobs map[string]*buildJob +} + +// New creates a new builder +func New(opt Opt) (*Builder, error) { + reqHandler := newReqBodyHandler(tracing.DefaultTransport) + + c, err := newController(reqHandler, opt) + if err != nil { + return nil, err + } + b := &Builder{ + controller: c, + reqBodyHandler: reqHandler, + jobs: map[string]*buildJob{}, + } + return b, nil +} + +// Cancel cancels a build using ID +func (b *Builder) Cancel(ctx context.Context, id string) error { + b.mu.Lock() + if j, ok := b.jobs[id]; ok && j.cancel != nil { + j.cancel() + } + b.mu.Unlock() + return nil +} + +// DiskUsage returns a report about space used by build cache +func (b *Builder) DiskUsage(ctx context.Context) ([]*types.BuildCache, error) { + duResp, err := b.controller.DiskUsage(ctx, &controlapi.DiskUsageRequest{}) + if err != nil { + return nil, err + } + + var items []*types.BuildCache + for _, r := range duResp.Record { + items = append(items, &types.BuildCache{ + ID: r.ID, + Mutable: r.Mutable, + InUse: r.InUse, + Size: r.Size_, + + CreatedAt: r.CreatedAt, + LastUsedAt: r.LastUsedAt, + UsageCount: int(r.UsageCount), + Parent: r.Parent, + Description: r.Description, + }) + } + return items, nil +} + +// Prune clears all reclaimable build cache +func (b *Builder) Prune(ctx context.Context) (int64, error) { + ch := make(chan *controlapi.UsageRecord) + + eg, ctx := errgroup.WithContext(ctx) + + eg.Go(func() error { + defer close(ch) + return b.controller.Prune(&controlapi.PruneRequest{}, &pruneProxy{ + streamProxy: streamProxy{ctx: ctx}, + ch: ch, + }) + }) + + var size int64 + eg.Go(func() error { + for r := range ch { + size += r.Size_ + } + return nil + }) + + if err := eg.Wait(); err != nil { + return 0, err + } + + return size, nil +} + +// Build executes a build request +func (b *Builder) Build(ctx context.Context, opt backend.BuildConfig) (*builder.Result, error) { + var rc = opt.Source + + if buildID := opt.Options.BuildID; buildID != "" { + b.mu.Lock() + + upload := false + if strings.HasPrefix(buildID, "upload-request:") { + upload = true + buildID = strings.TrimPrefix(buildID, "upload-request:") + } + + if _, ok := b.jobs[buildID]; !ok { + b.jobs[buildID] = newBuildJob() + } + j := b.jobs[buildID] + var cancel func() + ctx, cancel = context.WithCancel(ctx) + j.cancel = cancel + b.mu.Unlock() + + if upload { + ctx2, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + err := j.SetUpload(ctx2, rc) + return nil, err + } + + if remoteContext := opt.Options.RemoteContext; remoteContext == "upload-request" { + ctx2, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + var err error + rc, err = j.WaitUpload(ctx2) + if err != nil { + return nil, err + } + opt.Options.RemoteContext = "" + } + + defer func() { + delete(b.jobs, buildID) + }() + } + + var out builder.Result + + id := identity.NewID() + + frontendAttrs := map[string]string{} + + if opt.Options.Target != "" { + frontendAttrs["target"] = opt.Options.Target + } + + if opt.Options.Dockerfile != "" && opt.Options.Dockerfile != "." { + frontendAttrs["filename"] = opt.Options.Dockerfile + } + + if opt.Options.RemoteContext != "" { + if opt.Options.RemoteContext != "client-session" { + frontendAttrs["context"] = opt.Options.RemoteContext + } + } else { + url, cancel := b.reqBodyHandler.newRequest(rc) + defer cancel() + frontendAttrs["context"] = url + } + + cacheFrom := append([]string{}, opt.Options.CacheFrom...) + + frontendAttrs["cache-from"] = strings.Join(cacheFrom, ",") + + for k, v := range opt.Options.BuildArgs { + if v == nil { + continue + } + frontendAttrs["build-arg:"+k] = *v + } + + for k, v := range opt.Options.Labels { + frontendAttrs["label:"+k] = v + } + + if opt.Options.NoCache { + frontendAttrs["no-cache"] = "" + } + + 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{} + + if len(opt.Options.Tags) > 0 { + exporterAttrs["name"] = strings.Join(opt.Options.Tags, ",") + } + + req := &controlapi.SolveRequest{ + Ref: id, + Exporter: "moby", + ExporterAttrs: exporterAttrs, + Frontend: "dockerfile.v0", + FrontendAttrs: frontendAttrs, + Session: opt.Options.SessionID, + } + + aux := streamformatter.AuxFormatter{Writer: opt.ProgressWriter.Output} + + eg, ctx := errgroup.WithContext(ctx) + + eg.Go(func() error { + resp, err := b.controller.Solve(ctx, req) + if err != nil { + return err + } + id, ok := resp.ExporterResponse["containerimage.digest"] + if !ok { + return errors.Errorf("missing image id") + } + out.ImageID = id + return aux.Emit("moby.image.id", types.BuildResult{ID: id}) + }) + + ch := make(chan *controlapi.StatusResponse) + + eg.Go(func() error { + defer close(ch) + return b.controller.Status(&controlapi.StatusRequest{ + Ref: id, + }, &statusProxy{streamProxy: streamProxy{ctx: ctx}, ch: ch}) + }) + + eg.Go(func() error { + for sr := range ch { + dt, err := sr.Marshal() + if err != nil { + return err + } + if err := aux.Emit("moby.buildkit.trace", dt); err != nil { + return err + } + } + return nil + }) + + if err := eg.Wait(); err != nil { + return nil, err + } + + return &out, nil +} + +type streamProxy struct { + ctx context.Context +} + +func (sp *streamProxy) SetHeader(_ grpcmetadata.MD) error { + return nil +} + +func (sp *streamProxy) SendHeader(_ grpcmetadata.MD) error { + return nil +} + +func (sp *streamProxy) SetTrailer(_ grpcmetadata.MD) { +} + +func (sp *streamProxy) Context() context.Context { + return sp.ctx +} +func (sp *streamProxy) RecvMsg(m interface{}) error { + return io.EOF +} + +type statusProxy struct { + streamProxy + ch chan *controlapi.StatusResponse +} + +func (sp *statusProxy) Send(resp *controlapi.StatusResponse) error { + return sp.SendMsg(resp) +} +func (sp *statusProxy) SendMsg(m interface{}) error { + if sr, ok := m.(*controlapi.StatusResponse); ok { + sp.ch <- sr + } + return nil +} + +type pruneProxy struct { + streamProxy + ch chan *controlapi.UsageRecord +} + +func (sp *pruneProxy) Send(resp *controlapi.UsageRecord) error { + return sp.SendMsg(resp) +} +func (sp *pruneProxy) SendMsg(m interface{}) error { + if sr, ok := m.(*controlapi.UsageRecord); ok { + sp.ch <- sr + } + return nil +} + +type contentStoreNoLabels struct { + content.Store +} + +func (c *contentStoreNoLabels) Update(ctx context.Context, info content.Info, fieldpaths ...string) (content.Info, error) { + return content.Info{}, nil +} + +type wrapRC struct { + io.ReadCloser + once sync.Once + err error + waitCh chan struct{} +} + +func (w *wrapRC) Read(b []byte) (int, error) { + n, err := w.ReadCloser.Read(b) + if err != nil { + e := err + if e == io.EOF { + e = nil + } + w.close(e) + } + return n, err +} + +func (w *wrapRC) Close() error { + err := w.ReadCloser.Close() + w.close(err) + return err +} + +func (w *wrapRC) close(err error) { + w.once.Do(func() { + w.err = err + close(w.waitCh) + }) +} + +func (w *wrapRC) wait() error { + <-w.waitCh + return w.err +} + +type buildJob struct { + cancel func() + waitCh chan func(io.ReadCloser) error +} + +func newBuildJob() *buildJob { + return &buildJob{waitCh: make(chan func(io.ReadCloser) error)} +} + +func (j *buildJob) WaitUpload(ctx context.Context) (io.ReadCloser, error) { + done := make(chan struct{}) + + var upload io.ReadCloser + fn := func(rc io.ReadCloser) error { + w := &wrapRC{ReadCloser: rc, waitCh: make(chan struct{})} + upload = w + close(done) + return w.wait() + } + + select { + case <-ctx.Done(): + return nil, ctx.Err() + case j.waitCh <- fn: + <-done + return upload, nil + } +} + +func (j *buildJob) SetUpload(ctx context.Context, rc io.ReadCloser) error { + select { + case <-ctx.Done(): + return ctx.Err() + case fn := <-j.waitCh: + return fn(rc) + } +} diff --git a/builder/builder-next/controller.go b/builder/builder-next/controller.go new file mode 100644 index 0000000000000..c97f941bada81 --- /dev/null +++ b/builder/builder-next/controller.go @@ -0,0 +1,154 @@ +package buildkit + +import ( + "net/http" + "os" + "path/filepath" + + "github.com/containerd/containerd/content/local" + "github.com/docker/docker/builder/builder-next/adapters/containerimage" + "github.com/docker/docker/builder/builder-next/adapters/snapshot" + containerimageexp "github.com/docker/docker/builder/builder-next/exporter" + mobyworker "github.com/docker/docker/builder/builder-next/worker" + "github.com/docker/docker/daemon/graphdriver" + "github.com/moby/buildkit/cache" + "github.com/moby/buildkit/cache/metadata" + registryremotecache "github.com/moby/buildkit/cache/remotecache/registry" + "github.com/moby/buildkit/control" + "github.com/moby/buildkit/exporter" + "github.com/moby/buildkit/frontend" + dockerfile "github.com/moby/buildkit/frontend/dockerfile/builder" + "github.com/moby/buildkit/frontend/gateway" + "github.com/moby/buildkit/frontend/gateway/forwarder" + "github.com/moby/buildkit/snapshot/blobmapping" + "github.com/moby/buildkit/solver/boltdbcachestorage" + "github.com/moby/buildkit/worker" + "github.com/pkg/errors" +) + +func newController(rt http.RoundTripper, opt Opt) (*control.Controller, error) { + if err := os.MkdirAll(opt.Root, 0700); err != nil { + return nil, err + } + + dist := opt.Dist + root := opt.Root + + var driver graphdriver.Driver + if ls, ok := dist.LayerStore.(interface { + Driver() graphdriver.Driver + }); ok { + driver = ls.Driver() + } else { + return nil, errors.Errorf("could not access graphdriver") + } + + sbase, err := snapshot.NewSnapshotter(snapshot.Opt{ + GraphDriver: driver, + LayerStore: dist.LayerStore, + Root: root, + }) + if err != nil { + return nil, err + } + + store, err := local.NewStore(filepath.Join(root, "content")) + if err != nil { + return nil, err + } + store = &contentStoreNoLabels{store} + + md, err := metadata.NewStore(filepath.Join(root, "metadata.db")) + if err != nil { + return nil, err + } + + snapshotter := blobmapping.NewSnapshotter(blobmapping.Opt{ + Content: store, + Snapshotter: sbase, + MetadataStore: md, + }) + + cm, err := cache.NewManager(cache.ManagerOpt{ + Snapshotter: snapshotter, + MetadataStore: md, + }) + if err != nil { + return nil, err + } + + src, err := containerimage.NewSource(containerimage.SourceOpt{ + SessionManager: opt.SessionManager, + CacheAccessor: cm, + ContentStore: store, + DownloadManager: dist.DownloadManager, + MetadataStore: dist.V2MetadataService, + ImageStore: dist.ImageStore, + ReferenceStore: dist.ReferenceStore, + }) + if err != nil { + return nil, err + } + + exec, err := newExecutor(root) + if err != nil { + return nil, err + } + + differ, ok := sbase.(containerimageexp.Differ) + if !ok { + return nil, errors.Errorf("snapshotter doesn't support differ") + } + + exp, err := containerimageexp.New(containerimageexp.Opt{ + ImageStore: dist.ImageStore, + ReferenceStore: dist.ReferenceStore, + Differ: differ, + }) + if err != nil { + return nil, err + } + + cacheStorage, err := boltdbcachestorage.NewStore(filepath.Join(opt.Root, "cache.db")) + if err != nil { + return nil, err + } + + wopt := mobyworker.Opt{ + ID: "moby", + SessionManager: opt.SessionManager, + MetadataStore: md, + ContentStore: store, + CacheManager: cm, + Snapshotter: snapshotter, + Executor: exec, + ImageSource: src, + DownloadManager: dist.DownloadManager, + V2MetadataService: dist.V2MetadataService, + Exporters: map[string]exporter.Exporter{ + "moby": exp, + }, + Transport: rt, + } + + wc := &worker.Controller{} + w, err := mobyworker.NewWorker(wopt) + if err != nil { + return nil, err + } + wc.Add(w) + + frontends := map[string]frontend.Frontend{ + "dockerfile.v0": forwarder.NewGatewayForwarder(wc, dockerfile.Build), + "gateway.v0": gateway.NewGatewayFrontend(wc), + } + + return control.NewController(control.Opt{ + SessionManager: opt.SessionManager, + WorkerController: wc, + Frontends: frontends, + CacheKeyStorage: cacheStorage, + ResolveCacheImporterFunc: registryremotecache.ResolveCacheImporterFunc(opt.SessionManager), + // TODO: set ResolveCacheExporterFunc for exporting cache + }) +} diff --git a/builder/builder-next/executor_unix.go b/builder/builder-next/executor_unix.go new file mode 100644 index 0000000000000..da54473dd15b9 --- /dev/null +++ b/builder/builder-next/executor_unix.go @@ -0,0 +1,17 @@ +// +build !windows + +package buildkit + +import ( + "path/filepath" + + "github.com/moby/buildkit/executor" + "github.com/moby/buildkit/executor/runcexecutor" +) + +func newExecutor(root string) (executor.Executor, error) { + return runcexecutor.New(runcexecutor.Opt{ + Root: filepath.Join(root, "executor"), + CommandCandidates: []string{"docker-runc", "runc"}, + }) +} diff --git a/builder/builder-next/executor_windows.go b/builder/builder-next/executor_windows.go new file mode 100644 index 0000000000000..7b0c6e64c89c0 --- /dev/null +++ b/builder/builder-next/executor_windows.go @@ -0,0 +1,21 @@ +package buildkit + +import ( + "context" + "errors" + "io" + + "github.com/moby/buildkit/cache" + "github.com/moby/buildkit/executor" +) + +func newExecutor(_ string) (executor.Executor, error) { + return &winExecutor{}, nil +} + +type winExecutor struct { +} + +func (e *winExecutor) Exec(ctx context.Context, meta executor.Meta, rootfs cache.Mountable, mounts []executor.Mount, stdin io.ReadCloser, stdout, stderr io.WriteCloser) error { + return errors.New("buildkit executor not implemented for windows") +} diff --git a/builder/builder-next/exporter/export.go b/builder/builder-next/exporter/export.go new file mode 100644 index 0000000000000..70e7d6ece10c7 --- /dev/null +++ b/builder/builder-next/exporter/export.go @@ -0,0 +1,146 @@ +package containerimage + +import ( + "context" + "fmt" + "strings" + + distref "github.com/docker/distribution/reference" + "github.com/docker/docker/image" + "github.com/docker/docker/layer" + "github.com/docker/docker/reference" + "github.com/moby/buildkit/cache" + "github.com/moby/buildkit/exporter" + digest "github.com/opencontainers/go-digest" + "github.com/sirupsen/logrus" +) + +const ( + keyImageName = "name" + exporterImageConfig = "containerimage.config" +) + +// Differ can make a moby layer from a snapshot +type Differ interface { + EnsureLayer(ctx context.Context, key string) ([]layer.DiffID, error) +} + +// Opt defines a struct for creating new exporter +type Opt struct { + ImageStore image.Store + ReferenceStore reference.Store + Differ Differ +} + +type imageExporter struct { + opt Opt +} + +// New creates a new moby imagestore exporter +func New(opt Opt) (exporter.Exporter, error) { + im := &imageExporter{opt: opt} + return im, nil +} + +func (e *imageExporter) Resolve(ctx context.Context, opt map[string]string) (exporter.ExporterInstance, error) { + i := &imageExporterInstance{imageExporter: e} + for k, v := range opt { + switch k { + case keyImageName: + for _, v := range strings.Split(v, ",") { + ref, err := distref.ParseNormalizedNamed(v) + if err != nil { + return nil, err + } + i.targetNames = append(i.targetNames, ref) + } + case exporterImageConfig: + i.config = []byte(v) + default: + logrus.Warnf("image exporter: unknown option %s", k) + } + } + return i, nil +} + +type imageExporterInstance struct { + *imageExporter + targetNames []distref.Named + config []byte +} + +func (e *imageExporterInstance) Name() string { + return "exporting to image" +} + +func (e *imageExporterInstance) Export(ctx context.Context, ref cache.ImmutableRef, opt map[string][]byte) (map[string]string, error) { + if config, ok := opt[exporterImageConfig]; ok { + e.config = config + } + config := e.config + + var diffs []digest.Digest + if ref != nil { + layersDone := oneOffProgress(ctx, "exporting layers") + + if err := ref.Finalize(ctx, true); err != nil { + return nil, err + } + + diffIDs, err := e.opt.Differ.EnsureLayer(ctx, ref.ID()) + if err != nil { + return nil, err + } + + diffs = make([]digest.Digest, len(diffIDs)) + for i := range diffIDs { + diffs[i] = digest.Digest(diffIDs[i]) + } + + layersDone(nil) + } + + if len(config) == 0 { + var err error + config, err = emptyImageConfig() + if err != nil { + return nil, err + } + } + + history, err := parseHistoryFromConfig(config) + if err != nil { + return nil, err + } + + diffs, history = normalizeLayersAndHistory(diffs, history, ref) + + config, err = patchImageConfig(config, diffs, history) + if err != nil { + return nil, err + } + + configDigest := digest.FromBytes(config) + + configDone := oneOffProgress(ctx, fmt.Sprintf("writing image %s", configDigest)) + id, err := e.opt.ImageStore.Create(config) + if err != nil { + return nil, configDone(err) + } + configDone(nil) + + if e.opt.ReferenceStore != nil { + for _, targetName := range e.targetNames { + tagDone := oneOffProgress(ctx, "naming to "+targetName.String()) + + if err := e.opt.ReferenceStore.AddTag(targetName, digest.Digest(id), true); err != nil { + return nil, tagDone(err) + } + tagDone(nil) + } + } + + return map[string]string{ + "containerimage.digest": id.String(), + }, nil +} diff --git a/builder/builder-next/exporter/writer.go b/builder/builder-next/exporter/writer.go new file mode 100644 index 0000000000000..e8fa143fc628b --- /dev/null +++ b/builder/builder-next/exporter/writer.go @@ -0,0 +1,177 @@ +package containerimage + +import ( + "context" + "encoding/json" + "runtime" + "time" + + "github.com/moby/buildkit/cache" + "github.com/moby/buildkit/util/progress" + "github.com/moby/buildkit/util/system" + digest "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// const ( +// emptyGZLayer = digest.Digest("sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1") +// ) + +func emptyImageConfig() ([]byte, error) { + img := ocispec.Image{ + Architecture: runtime.GOARCH, + OS: runtime.GOOS, + } + img.RootFS.Type = "layers" + img.Config.WorkingDir = "/" + img.Config.Env = []string{"PATH=" + system.DefaultPathEnv} + dt, err := json.Marshal(img) + return dt, errors.Wrap(err, "failed to create empty image config") +} + +func parseHistoryFromConfig(dt []byte) ([]ocispec.History, error) { + var config struct { + History []ocispec.History + } + if err := json.Unmarshal(dt, &config); err != nil { + return nil, errors.Wrap(err, "failed to unmarshal history from config") + } + return config.History, nil +} + +func patchImageConfig(dt []byte, dps []digest.Digest, history []ocispec.History) ([]byte, error) { + m := map[string]json.RawMessage{} + if err := json.Unmarshal(dt, &m); err != nil { + return nil, errors.Wrap(err, "failed to parse image config for patch") + } + + var rootFS ocispec.RootFS + rootFS.Type = "layers" + rootFS.DiffIDs = append(rootFS.DiffIDs, dps...) + + dt, err := json.Marshal(rootFS) + if err != nil { + return nil, errors.Wrap(err, "failed to marshal rootfs") + } + m["rootfs"] = dt + + dt, err = json.Marshal(history) + if err != nil { + return nil, errors.Wrap(err, "failed to marshal history") + } + m["history"] = dt + + if _, ok := m["created"]; !ok { + var tm *time.Time + for _, h := range history { + if h.Created != nil { + tm = h.Created + } + } + dt, err = json.Marshal(&tm) + if err != nil { + return nil, errors.Wrap(err, "failed to marshal creation time") + } + m["created"] = dt + } + + dt, err = json.Marshal(m) + return dt, errors.Wrap(err, "failed to marshal config after patch") +} + +func normalizeLayersAndHistory(diffs []digest.Digest, history []ocispec.History, ref cache.ImmutableRef) ([]digest.Digest, []ocispec.History) { + refMeta := getRefMetadata(ref, len(diffs)) + var historyLayers int + for _, h := range history { + if !h.EmptyLayer { + historyLayers++ + } + } + if historyLayers > len(diffs) { + // this case shouldn't happen but if it does force set history layers empty + // from the bottom + logrus.Warn("invalid image config with unaccounted layers") + historyCopy := make([]ocispec.History, 0, len(history)) + var l int + for _, h := range history { + if l >= len(diffs) { + h.EmptyLayer = true + } + if !h.EmptyLayer { + l++ + } + historyCopy = append(historyCopy, h) + } + history = historyCopy + } + + if len(diffs) > historyLayers { + // some history items are missing. add them based on the ref metadata + for _, md := range refMeta[historyLayers:] { + history = append(history, ocispec.History{ + Created: &md.createdAt, + CreatedBy: md.description, + Comment: "buildkit.exporter.image.v0", + }) + } + } + + var layerIndex int + for i, h := range history { + if !h.EmptyLayer { + if h.Created == nil { + h.Created = &refMeta[layerIndex].createdAt + } + layerIndex++ + } + history[i] = h + } + + return diffs, history +} + +type refMetadata struct { + description string + createdAt time.Time +} + +func getRefMetadata(ref cache.ImmutableRef, limit int) []refMetadata { + if limit <= 0 { + return nil + } + meta := refMetadata{ + description: "created by buildkit", // shouldn't be shown but don't fail build + createdAt: time.Now(), + } + if ref == nil { + return append(getRefMetadata(nil, limit-1), meta) + } + if descr := cache.GetDescription(ref.Metadata()); descr != "" { + meta.description = descr + } + meta.createdAt = cache.GetCreatedAt(ref.Metadata()) + p := ref.Parent() + if p != nil { + defer p.Release(context.TODO()) + } + return append(getRefMetadata(p, limit-1), meta) +} + +func oneOffProgress(ctx context.Context, id string) func(err error) error { + pw, _, _ := progress.FromContext(ctx) + now := time.Now() + st := progress.Status{ + Started: &now, + } + pw.Write(id, st) + return func(err error) error { + // TODO: set error on status + now := time.Now() + st.Completed = &now + pw.Write(id, st) + pw.Close() + return err + } +} diff --git a/builder/builder-next/reqbodyhandler.go b/builder/builder-next/reqbodyhandler.go new file mode 100644 index 0000000000000..48433908fb30f --- /dev/null +++ b/builder/builder-next/reqbodyhandler.go @@ -0,0 +1,67 @@ +package buildkit + +import ( + "io" + "net/http" + "strings" + "sync" + + "github.com/moby/buildkit/identity" + "github.com/pkg/errors" +) + +const urlPrefix = "build-context-" + +type reqBodyHandler struct { + mu sync.Mutex + rt http.RoundTripper + + requests map[string]io.ReadCloser +} + +func newReqBodyHandler(rt http.RoundTripper) *reqBodyHandler { + return &reqBodyHandler{ + rt: rt, + requests: map[string]io.ReadCloser{}, + } +} + +func (h *reqBodyHandler) newRequest(rc io.ReadCloser) (string, func()) { + id := identity.NewID() + h.mu.Lock() + h.requests[id] = rc + h.mu.Unlock() + return "http://" + urlPrefix + id, func() { + h.mu.Lock() + delete(h.requests, id) + h.mu.Unlock() + } +} + +func (h *reqBodyHandler) RoundTrip(req *http.Request) (*http.Response, error) { + host := req.URL.Host + if strings.HasPrefix(host, urlPrefix) { + if req.Method != "GET" { + return nil, errors.Errorf("invalid request") + } + id := strings.TrimPrefix(host, urlPrefix) + h.mu.Lock() + rc, ok := h.requests[id] + delete(h.requests, id) + h.mu.Unlock() + + if !ok { + return nil, errors.Errorf("context not found") + } + + resp := &http.Response{ + Status: "200 OK", + StatusCode: 200, + Body: rc, + ContentLength: -1, + } + + return resp, nil + } + return h.rt.RoundTrip(req) +} diff --git a/builder/builder-next/worker/worker.go b/builder/builder-next/worker/worker.go new file mode 100644 index 0000000000000..29e8095c328db --- /dev/null +++ b/builder/builder-next/worker/worker.go @@ -0,0 +1,331 @@ +package worker + +import ( + "context" + "fmt" + "io" + "io/ioutil" + nethttp "net/http" + "runtime" + "time" + + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/platforms" + "github.com/containerd/containerd/rootfs" + "github.com/docker/docker/distribution" + distmetadata "github.com/docker/docker/distribution/metadata" + "github.com/docker/docker/distribution/xfer" + "github.com/docker/docker/image" + "github.com/docker/docker/layer" + pkgprogress "github.com/docker/docker/pkg/progress" + "github.com/moby/buildkit/cache" + "github.com/moby/buildkit/cache/metadata" + "github.com/moby/buildkit/client" + "github.com/moby/buildkit/executor" + "github.com/moby/buildkit/exporter" + "github.com/moby/buildkit/frontend" + "github.com/moby/buildkit/session" + "github.com/moby/buildkit/snapshot" + "github.com/moby/buildkit/solver" + "github.com/moby/buildkit/solver/llbsolver/ops" + "github.com/moby/buildkit/solver/pb" + "github.com/moby/buildkit/source" + "github.com/moby/buildkit/source/git" + "github.com/moby/buildkit/source/http" + "github.com/moby/buildkit/source/local" + "github.com/moby/buildkit/util/contentutil" + "github.com/moby/buildkit/util/progress" + digest "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// Opt defines a structure for creating a worker. +type Opt struct { + ID string + Labels map[string]string + SessionManager *session.Manager + MetadataStore *metadata.Store + Executor executor.Executor + Snapshotter snapshot.Snapshotter + ContentStore content.Store + CacheManager cache.Manager + ImageSource source.Source + Exporters map[string]exporter.Exporter + DownloadManager distribution.RootFSDownloadManager + V2MetadataService distmetadata.V2MetadataService + Transport nethttp.RoundTripper +} + +// Worker is a local worker instance with dedicated snapshotter, cache, and so on. +// TODO: s/Worker/OpWorker/g ? +type Worker struct { + Opt + SourceManager *source.Manager +} + +// NewWorker instantiates a local worker +func NewWorker(opt Opt) (*Worker, error) { + sm, err := source.NewManager() + if err != nil { + return nil, err + } + + cm := opt.CacheManager + sm.Register(opt.ImageSource) + + gs, err := git.NewSource(git.Opt{ + CacheAccessor: cm, + MetadataStore: opt.MetadataStore, + }) + if err == nil { + sm.Register(gs) + } else { + logrus.Warnf("Could not register builder git source: %s", err) + } + + hs, err := http.NewSource(http.Opt{ + CacheAccessor: cm, + MetadataStore: opt.MetadataStore, + Transport: opt.Transport, + }) + if err == nil { + sm.Register(hs) + } else { + logrus.Warnf("Could not register builder http source: %s", err) + } + + ss, err := local.NewSource(local.Opt{ + SessionManager: opt.SessionManager, + CacheAccessor: cm, + MetadataStore: opt.MetadataStore, + }) + if err == nil { + sm.Register(ss) + } else { + logrus.Warnf("Could not register builder local source: %s", err) + } + + return &Worker{ + Opt: opt, + SourceManager: sm, + }, nil +} + +// ID returns worker ID +func (w *Worker) ID() string { + return w.Opt.ID +} + +// Labels returns map of all worker labels +func (w *Worker) Labels() map[string]string { + return w.Opt.Labels +} + +// Platforms returns one or more platforms supported by the image. +func (w *Worker) Platforms() []ocispec.Platform { + // does not handle lcow + return []ocispec.Platform{platforms.DefaultSpec()} +} + +// LoadRef loads a reference by ID +func (w *Worker) LoadRef(id string) (cache.ImmutableRef, error) { + return w.CacheManager.Get(context.TODO(), id) +} + +// ResolveOp converts a LLB vertex into a LLB operation +func (w *Worker) ResolveOp(v solver.Vertex, s frontend.FrontendLLBBridge) (solver.Op, error) { + if baseOp, ok := v.Sys().(*pb.Op); ok { + switch op := baseOp.Op.(type) { + case *pb.Op_Source: + return ops.NewSourceOp(v, op, baseOp.Platform, w.SourceManager, w) + case *pb.Op_Exec: + return ops.NewExecOp(v, op, w.CacheManager, w.MetadataStore, w.Executor, w) + case *pb.Op_Build: + return ops.NewBuildOp(v, op, s, w) + } + } + return nil, errors.Errorf("could not resolve %v", v) +} + +// ResolveImageConfig returns image config for an image +func (w *Worker) ResolveImageConfig(ctx context.Context, ref string, platform *ocispec.Platform) (digest.Digest, []byte, error) { + // ImageSource is typically source/containerimage + resolveImageConfig, ok := w.ImageSource.(resolveImageConfig) + if !ok { + return "", nil, errors.Errorf("worker %q does not implement ResolveImageConfig", w.ID()) + } + return resolveImageConfig.ResolveImageConfig(ctx, ref, platform) +} + +// Exec executes a process directly on a worker +func (w *Worker) Exec(ctx context.Context, meta executor.Meta, rootFS cache.ImmutableRef, stdin io.ReadCloser, stdout, stderr io.WriteCloser) error { + active, err := w.CacheManager.New(ctx, rootFS) + if err != nil { + return err + } + defer active.Release(context.TODO()) + return w.Executor.Exec(ctx, meta, active, nil, stdin, stdout, stderr) +} + +// DiskUsage returns disk usage report +func (w *Worker) DiskUsage(ctx context.Context, opt client.DiskUsageInfo) ([]*client.UsageInfo, error) { + return w.CacheManager.DiskUsage(ctx, opt) +} + +// Prune deletes reclaimable build cache +func (w *Worker) Prune(ctx context.Context, ch chan client.UsageInfo) error { + return w.CacheManager.Prune(ctx, ch) +} + +// Exporter returns exporter by name +func (w *Worker) Exporter(name string) (exporter.Exporter, error) { + exp, ok := w.Exporters[name] + if !ok { + return nil, errors.Errorf("exporter %q could not be found", name) + } + return exp, nil +} + +// GetRemote returns a remote snapshot reference for a local one +func (w *Worker) GetRemote(ctx context.Context, ref cache.ImmutableRef, createIfNeeded bool) (*solver.Remote, error) { + return nil, errors.Errorf("getremote not implemented") +} + +// FromRemote converts a remote snapshot reference to a local one +func (w *Worker) FromRemote(ctx context.Context, remote *solver.Remote) (cache.ImmutableRef, error) { + rootfs, err := getLayers(ctx, remote.Descriptors) + if err != nil { + return nil, err + } + + layers := make([]xfer.DownloadDescriptor, 0, len(rootfs)) + + for _, l := range rootfs { + // ongoing.add(desc) + layers = append(layers, &layerDescriptor{ + desc: l.Blob, + diffID: layer.DiffID(l.Diff.Digest), + provider: remote.Provider, + w: w, + pctx: ctx, + }) + } + + defer func() { + for _, l := range rootfs { + w.ContentStore.Delete(context.TODO(), l.Blob.Digest) + } + }() + + r := image.NewRootFS() + rootFS, release, err := w.DownloadManager.Download(ctx, *r, runtime.GOOS, layers, &discardProgress{}) + if err != nil { + return nil, err + } + defer release() + + ref, err := w.CacheManager.GetFromSnapshotter(ctx, string(rootFS.ChainID()), cache.WithDescription(fmt.Sprintf("imported %s", remote.Descriptors[len(remote.Descriptors)-1].Digest))) + if err != nil { + return nil, err + } + return ref, nil +} + +type discardProgress struct{} + +func (*discardProgress) WriteProgress(_ pkgprogress.Progress) error { + return nil +} + +// Fetch(ctx context.Context, desc ocispec.Descriptor) (io.ReadCloser, error) +type layerDescriptor struct { + provider content.Provider + desc ocispec.Descriptor + diffID layer.DiffID + // ref ctdreference.Spec + w *Worker + pctx context.Context +} + +func (ld *layerDescriptor) Key() string { + return "v2:" + ld.desc.Digest.String() +} + +func (ld *layerDescriptor) ID() string { + return ld.desc.Digest.String() +} + +func (ld *layerDescriptor) DiffID() (layer.DiffID, error) { + return ld.diffID, nil +} + +func (ld *layerDescriptor) Download(ctx context.Context, progressOutput pkgprogress.Output) (io.ReadCloser, int64, error) { + done := oneOffProgress(ld.pctx, fmt.Sprintf("pulling %s", ld.desc.Digest)) + if err := contentutil.Copy(ctx, ld.w.ContentStore, ld.provider, ld.desc); err != nil { + return nil, 0, done(err) + } + done(nil) + + ra, err := ld.w.ContentStore.ReaderAt(ctx, ld.desc) + if err != nil { + return nil, 0, err + } + + return ioutil.NopCloser(content.NewReader(ra)), ld.desc.Size, nil +} + +func (ld *layerDescriptor) Close() { + // ld.is.ContentStore.Delete(context.TODO(), ld.desc.Digest) +} + +func (ld *layerDescriptor) Registered(diffID layer.DiffID) { + // Cache mapping from this layer's DiffID to the blobsum + ld.w.V2MetadataService.Add(diffID, distmetadata.V2Metadata{Digest: ld.desc.Digest}) +} + +func getLayers(ctx context.Context, descs []ocispec.Descriptor) ([]rootfs.Layer, error) { + layers := make([]rootfs.Layer, len(descs)) + for i, desc := range descs { + diffIDStr := desc.Annotations["containerd.io/uncompressed"] + if diffIDStr == "" { + return nil, errors.Errorf("%s missing uncompressed digest", desc.Digest) + } + diffID, err := digest.Parse(diffIDStr) + if err != nil { + return nil, err + } + layers[i].Diff = ocispec.Descriptor{ + MediaType: ocispec.MediaTypeImageLayer, + Digest: diffID, + } + layers[i].Blob = ocispec.Descriptor{ + MediaType: desc.MediaType, + Digest: desc.Digest, + Size: desc.Size, + } + } + return layers, nil +} + +func oneOffProgress(ctx context.Context, id string) func(err error) error { + pw, _, _ := progress.FromContext(ctx) + now := time.Now() + st := progress.Status{ + Started: &now, + } + pw.Write(id, st) + return func(err error) error { + // TODO: set error on status + now := time.Now() + st.Completed = &now + pw.Write(id, st) + pw.Close() + return err + } +} + +type resolveImageConfig interface { + ResolveImageConfig(ctx context.Context, ref string, platform *ocispec.Platform) (digest.Digest, []byte, error) +} diff --git a/builder/dockerfile/buildargs_test.go b/builder/dockerfile/buildargs_test.go index c3f61048626f0..c3b82c83f4b60 100644 --- a/builder/dockerfile/buildargs_test.go +++ b/builder/dockerfile/buildargs_test.go @@ -5,8 +5,8 @@ import ( "strings" "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func strPtr(source string) *string { diff --git a/builder/dockerfile/builder.go b/builder/dockerfile/builder.go index d5d2de818024c..ee95f236892fe 100644 --- a/builder/dockerfile/builder.go +++ b/builder/dockerfile/builder.go @@ -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" @@ -104,13 +106,6 @@ func (bm *BuildManager) Build(ctx context.Context, config backend.BuildConfig) ( source = src } - os := "" - apiPlatform := system.ParsePlatform(config.Options.Platform) - if apiPlatform.OS != "" { - os = apiPlatform.OS - } - config.Options.Platform = os - builderOptions := builderOptions{ Options: config.Options, ProgressWriter: config.ProgressWriter, @@ -118,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) { @@ -182,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) @@ -206,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 @@ -264,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 { @@ -372,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 { diff --git a/builder/dockerfile/copy.go b/builder/dockerfile/copy.go index 43f40b62f99ca..74e245bdc40b9 100644 --- a/builder/dockerfile/copy.go +++ b/builder/dockerfile/copy.go @@ -24,6 +24,7 @@ import ( "github.com/docker/docker/pkg/streamformatter" "github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/urlutil" + specs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" ) @@ -72,7 +73,7 @@ type copier struct { source builder.Source pathCache pathCache download sourceDownloader - platform string + platform *specs.Platform // for cleanup. TODO: having copier.cleanup() is error prone and hard to // follow. Code calling performCopy should manage the lifecycle of its params. // Copier should take override source as input, not imageMount. @@ -86,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, } } @@ -95,8 +96,14 @@ func (o *copier) createCopyInstruction(args []string, cmdName string) (copyInstr last := len(args) - 1 // Work in platform-specific filepath semantics - inst.dest = fromSlash(args[last], o.platform) - separator := string(separator(o.platform)) + // TODO: This OS switch for paths is NOT correct and should not be supported. + // Maintained for backwards compatibility + pathOS := runtime.GOOS + if o.platform != nil { + pathOS = o.platform.OS + } + inst.dest = fromSlash(args[last], pathOS) + separator := string(separator(pathOS)) infos, err := o.getCopyInfosForSourcePaths(args[0:last], inst.dest) if err != nil { return inst, errors.Wrapf(err, "%s failed", cmdName) diff --git a/builder/dockerfile/copy_test.go b/builder/dockerfile/copy_test.go index f2f895387ce78..f559ff4fd8213 100644 --- a/builder/dockerfile/copy_test.go +++ b/builder/dockerfile/copy_test.go @@ -5,9 +5,9 @@ import ( "testing" "github.com/docker/docker/pkg/containerfs" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/fs" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/fs" ) func TestIsExistingDirectory(t *testing.T) { diff --git a/builder/dockerfile/dispatchers.go b/builder/dockerfile/dispatchers.go index 6ff65292b58a5..f2da08ed4d6e8 100644 --- a/builder/dockerfile/dispatchers.go +++ b/builder/dockerfile/dispatchers.go @@ -14,6 +14,7 @@ import ( "sort" "strings" + "github.com/containerd/containerd/platforms" "github.com/docker/docker/api" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/strslice" @@ -27,8 +28,8 @@ import ( "github.com/moby/buildkit/frontend/dockerfile/instructions" "github.com/moby/buildkit/frontend/dockerfile/parser" "github.com/moby/buildkit/frontend/dockerfile/shell" + specs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" - "github.com/sirupsen/logrus" ) // ENV foo bar @@ -88,7 +89,7 @@ func dispatchLabel(d dispatchRequest, c *instructions.LabelCommand) error { // 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. // func dispatchAdd(d dispatchRequest, c *instructions.AddCommand) error { @@ -103,7 +104,7 @@ func dispatchAdd(d dispatchRequest, c *instructions.AddCommand) error { copyInstruction.chownStr = c.Chown copyInstruction.allowLocalDecompression = true - return d.builder.performCopy(d.state, copyInstruction) + return d.builder.performCopy(d, copyInstruction) } // COPY foo /path @@ -127,7 +128,7 @@ func dispatchCopy(d dispatchRequest, c *instructions.CopyCommand) error { } copyInstruction.chownStr = c.Chown - return d.builder.performCopy(d.state, copyInstruction) + return d.builder.performCopy(d, copyInstruction) } func (d *dispatchRequest) getImageMount(imageRefOrID string) (*imageMount, error) { @@ -145,17 +146,32 @@ func (d *dispatchRequest) getImageMount(imageRefOrID string) (*imageMount, error imageRefOrID = stage.Image localOnly = true } - return d.builder.imageSources.Get(imageRefOrID, localOnly, d.state.operatingSystem) + return d.builder.imageSources.Get(imageRefOrID, localOnly, d.builder.platform) } // FROM [--platform=platform] imagename[:tag | @digest] [AS build-stage-name] // func initializeStage(d dispatchRequest, cmd *instructions.Stage) error { d.builder.imageProber.Reset() - if err := system.ValidatePlatform(&cmd.Platform); err != nil { - return err + + var platform *specs.Platform + if v := cmd.Platform; v != "" { + v, err := d.getExpandedString(d.shlex, v) + if err != nil { + return errors.Wrapf(err, "failed to process arguments for platform %s", v) + } + + p, err := platforms.Parse(v) + if err != nil { + return errors.Wrapf(err, "failed to parse platform %s", v) + } + if err := system.ValidatePlatform(p); err != nil { + return err + } + platform = &p } - image, err := d.getFromImage(d.shlex, cmd.BaseName, cmd.Platform.OS) + + image, err := d.getFromImage(d.shlex, cmd.BaseName, platform) if err != nil { return err } @@ -201,82 +217,78 @@ func dispatchTriggeredOnBuild(d dispatchRequest, triggers []string) error { return nil } -func (d *dispatchRequest) getExpandedImageName(shlex *shell.Lex, name string) (string, error) { +func (d *dispatchRequest) getExpandedString(shlex *shell.Lex, str string) (string, error) { substitutionArgs := []string{} for key, value := range d.state.buildArgs.GetAllMeta() { substitutionArgs = append(substitutionArgs, key+"="+value) } - name, err := shlex.ProcessWord(name, substitutionArgs) + name, err := shlex.ProcessWord(str, substitutionArgs) if err != nil { return "", err } return name, nil } -// getOsFromFlagsAndStage calculates the operating system if we need to pull an image. -// stagePlatform contains the value supplied by optional `--platform=` on -// a current FROM statement. b.builder.options.Platform contains the operating -// system part of the optional flag passed in the API call (or CLI flag -// through `docker build --platform=...`). Precedence is for an explicit -// platform indication in the FROM statement. -func (d *dispatchRequest) getOsFromFlagsAndStage(stageOS string) string { - switch { - case stageOS != "": - return stageOS - case d.builder.options.Platform != "": - // Note this is API "platform", but by this point, as the daemon is not - // multi-arch aware yet, it is guaranteed to only hold the OS part here. - return d.builder.options.Platform - default: - return runtime.GOOS - } -} - -func (d *dispatchRequest) getImageOrStage(name string, stageOS string) (builder.Image, error) { +func (d *dispatchRequest) getImageOrStage(name string, platform *specs.Platform) (builder.Image, error) { var localOnly bool if im, ok := d.stages.getByName(name); ok { name = im.Image localOnly = true } - os := d.getOsFromFlagsAndStage(stageOS) + if platform == nil { + platform = d.builder.platform + } // Windows cannot support a container with no base image unless it is LCOW. if name == api.NoBaseImageSpecifier { + p := platforms.DefaultSpec() + if platform != nil { + p = *platform + } imageImage := &image.Image{} - imageImage.OS = runtime.GOOS + imageImage.OS = p.OS + + // old windows scratch handling + // TODO: scratch should not have an os. It should be nil image. + // Windows supports scratch. What is not supported is running containers + // from it. if runtime.GOOS == "windows" { - switch os { - case "windows", "": - return nil, errors.New("Windows does not support FROM scratch") - case "linux": + if platform == nil || platform.OS == "linux" { if !system.LCOWSupported() { return nil, errors.New("Linux containers are not supported on this system") } imageImage.OS = "linux" - default: - return nil, errors.Errorf("operating system %q is not supported", os) + } else if platform.OS == "windows" { + return nil, errors.New("Windows does not support FROM scratch") + } else { + return nil, errors.Errorf("platform %s is not supported", platforms.Format(p)) } } return builder.Image(imageImage), nil } - imageMount, err := d.builder.imageSources.Get(name, localOnly, os) + imageMount, err := d.builder.imageSources.Get(name, localOnly, platform) if err != nil { return nil, err } return imageMount.Image(), nil } -func (d *dispatchRequest) getFromImage(shlex *shell.Lex, name string, stageOS string) (builder.Image, error) { - name, err := d.getExpandedImageName(shlex, name) +func (d *dispatchRequest) getFromImage(shlex *shell.Lex, basename string, platform *specs.Platform) (builder.Image, error) { + name, err := d.getExpandedString(shlex, basename) if err != nil { return nil, err } - return d.getImageOrStage(name, stageOS) + // Empty string is interpreted to FROM scratch by images.GetImageAndReleasableLayer, + // so validate expanded result is not empty. + if name == "" { + return nil, errors.Errorf("base name (%s) should not be blank", basename) + } + + return d.getImageOrStage(name, platform) } func dispatchOnbuild(d dispatchRequest, c *instructions.OnbuildCommand) error { - d.state.runConfig.OnBuild = append(d.state.runConfig.OnBuild, c.Expression) return d.builder.commit(d.state, "ONBUILD "+c.Expression) } @@ -305,10 +317,12 @@ func dispatchWorkdir(d dispatchRequest, c *instructions.WorkdirCommand) error { comment := "WORKDIR " + runConfig.WorkingDir runConfigWithCommentCmd := copyRunConfig(runConfig, withCmdCommentString(comment, d.state.operatingSystem)) + containerID, err := d.builder.probeAndCreate(d.state, runConfigWithCommentCmd) if err != nil || containerID == "" { return err } + if err := d.builder.docker.ContainerCreateWorkdir(containerID); err != nil { return err } @@ -350,24 +364,24 @@ func dispatchRun(d dispatchRequest, c *instructions.RunCommand) error { runConfigForCacheProbe := copyRunConfig(stateRunConfig, withCmd(saveCmd), withEntrypointOverride(saveCmd, nil)) - hit, err := d.builder.probeCache(d.state, runConfigForCacheProbe) - if err != nil || hit { + if hit, err := d.builder.probeCache(d.state, runConfigForCacheProbe); err != nil || hit { return err } runConfig := copyRunConfig(stateRunConfig, withCmd(cmdFromArgs), withEnv(append(stateRunConfig.Env, buildArgs...)), - withEntrypointOverride(saveCmd, strslice.StrSlice{""})) + withEntrypointOverride(saveCmd, strslice.StrSlice{""}), + withoutHealthcheck()) // set config as already being escaped, this prevents double escaping on windows runConfig.ArgsEscaped = true - logrus.Debugf("[BUILDER] Command to be executed: %v", runConfig.Cmd) cID, err := d.builder.create(runConfig) if err != nil { return err } + if err := d.builder.containerManager.Run(d.builder.clientCtx, cID, d.builder.Stdout, d.builder.Stderr); err != nil { if err, ok := err.(*statusCodeError); ok { // TODO: change error type, because jsonmessage.JSONError assumes HTTP diff --git a/builder/dockerfile/dispatchers_test.go b/builder/dockerfile/dispatchers_test.go index a5474d6db8e77..d767d318fac28 100644 --- a/builder/dockerfile/dispatchers_test.go +++ b/builder/dockerfile/dispatchers_test.go @@ -14,23 +14,24 @@ import ( "github.com/docker/docker/image" "github.com/docker/docker/pkg/system" "github.com/docker/go-connections/nat" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/moby/buildkit/frontend/dockerfile/instructions" "github.com/moby/buildkit/frontend/dockerfile/shell" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func newBuilderWithMockBackend() *Builder { mockBackend := &MockBackend{} + opts := &types.ImageBuildOptions{} ctx := context.Background() b := &Builder{ - options: &types.ImageBuildOptions{Platform: runtime.GOOS}, + options: opts, docker: mockBackend, Stdout: new(bytes.Buffer), clientCtx: ctx, disableCommit: true, imageSources: newImageSources(ctx, builderOptions{ - Options: &types.ImageBuildOptions{Platform: runtime.GOOS}, + Options: opts, Backend: mockBackend, }), imageProber: newImageProber(mockBackend, nil, false), @@ -113,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 } @@ -136,10 +137,10 @@ func TestFromWithArg(t *testing.T) { args := NewBuildArgs(make(map[string]*string)) val := "sometag" - metaArg := instructions.ArgCommand{ + metaArg := instructions.ArgCommand{KeyValuePairOptional: instructions.KeyValuePairOptional{ Key: "THETAG", Value: &val, - } + }} cmd := &instructions.Stage{ BaseName: "alpine:${THETAG}", } @@ -156,6 +157,22 @@ func TestFromWithArg(t *testing.T) { assert.Check(t, is.Len(sb.state.buildArgs.GetAllMeta(), 1)) } +func TestFromWithArgButBuildArgsNotGiven(t *testing.T) { + b := newBuilderWithMockBackend() + args := NewBuildArgs(make(map[string]*string)) + + metaArg := instructions.ArgCommand{} + cmd := &instructions.Stage{ + BaseName: "${THETAG}", + } + err := processMetaArg(metaArg, shell.NewLex('\\'), args) + + sb := newDispatchRequest(b, '\\', nil, args, newStagesBuildResults()) + assert.NilError(t, err) + err = initializeStage(sb, cmd) + assert.Error(t, err, "base name (${THETAG}) should not be blank") +} + func TestFromWithUndefinedArg(t *testing.T) { tag, expected := "sometag", "expectedthisid" @@ -376,7 +393,7 @@ func TestArg(t *testing.T) { argName := "foo" argVal := "bar" - cmd := &instructions.ArgCommand{Key: argName, Value: &argVal} + cmd := &instructions.ArgCommand{KeyValuePairOptional: instructions.KeyValuePairOptional{Key: argName, Value: &argVal}} err := dispatch(sb, cmd) assert.NilError(t, err) @@ -472,3 +489,64 @@ func TestRunWithBuildArgs(t *testing.T) { // Check that runConfig.Cmd has not been modified by run assert.Check(t, is.DeepEqual(origCmd, sb.state.runConfig.Cmd)) } + +func TestRunIgnoresHealthcheck(t *testing.T) { + b := newBuilderWithMockBackend() + args := NewBuildArgs(make(map[string]*string)) + sb := newDispatchRequest(b, '`', nil, args, newStagesBuildResults()) + b.disableCommit = false + + origCmd := strslice.StrSlice([]string{"cmd", "in", "from", "image"}) + + imageCache := &mockImageCache{ + getCacheFunc: func(parentID string, cfg *container.Config) (string, error) { + return "", nil + }, + } + + mockBackend := b.docker.(*MockBackend) + mockBackend.makeImageCacheFunc = func(_ []string) builder.ImageCache { + return imageCache + } + b.imageProber = newImageProber(mockBackend, nil, false) + mockBackend.getImageFunc = func(_ string) (builder.Image, builder.ROLayer, error) { + return &mockImage{ + id: "abcdef", + config: &container.Config{Cmd: origCmd}, + }, nil, nil + } + mockBackend.containerCreateFunc = func(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error) { + return container.ContainerCreateCreatedBody{ID: "12345"}, nil + } + mockBackend.commitFunc = func(cfg backend.CommitConfig) (image.ID, error) { + return "", nil + } + from := &instructions.Stage{BaseName: "abcdef"} + err := initializeStage(sb, from) + assert.NilError(t, err) + + expectedTest := []string{"CMD-SHELL", "curl -f http://localhost/ || exit 1"} + cmd := &instructions.HealthCheckCommand{ + Health: &container.HealthConfig{ + Test: expectedTest, + }, + } + assert.NilError(t, dispatch(sb, cmd)) + assert.Assert(t, sb.state.runConfig.Healthcheck != nil) + + mockBackend.containerCreateFunc = func(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error) { + // Check the Healthcheck is disabled. + assert.Check(t, is.DeepEqual([]string{"NONE"}, config.Config.Healthcheck.Test)) + return container.ContainerCreateCreatedBody{ID: "123456"}, nil + } + + sb.state.buildArgs.AddArg("one", strPtr("two")) + run := &instructions.RunCommand{ + ShellDependantCmdLine: instructions.ShellDependantCmdLine{ + CmdLine: strslice.StrSlice{"echo foo"}, + PrependShell: true, + }, + } + assert.NilError(t, dispatch(sb, run)) + assert.Check(t, is.DeepEqual(expectedTest, sb.state.runConfig.Healthcheck.Test)) +} diff --git a/builder/dockerfile/evaluator_test.go b/builder/dockerfile/evaluator_test.go index 4657b1c58073f..fb79b238e8219 100644 --- a/builder/dockerfile/evaluator_test.go +++ b/builder/dockerfile/evaluator_test.go @@ -7,10 +7,10 @@ import ( "github.com/docker/docker/builder/remotecontext" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/reexec" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/skip" "github.com/moby/buildkit/frontend/dockerfile/instructions" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/skip" ) type dispatchTestCase struct { @@ -97,7 +97,7 @@ func initDispatchTestCases() []dispatchTestCase { } func TestDispatch(t *testing.T) { - skip.IfCondition(t, os.Getuid() != 0, "skipping test that requires root") + skip.If(t, os.Getuid() != 0, "skipping test that requires root") testCases := initDispatchTestCases() for _, testCase := range testCases { diff --git a/builder/dockerfile/imagecontext.go b/builder/dockerfile/imagecontext.go index 53a4b9774b5bd..08cb396a2bdfb 100644 --- a/builder/dockerfile/imagecontext.go +++ b/builder/dockerfile/imagecontext.go @@ -7,11 +7,12 @@ import ( "github.com/docker/docker/api/types/backend" "github.com/docker/docker/builder" dockerimage "github.com/docker/docker/image" + specs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) -type getAndMountFunc func(string, bool, string) (builder.Image, builder.ROLayer, error) +type getAndMountFunc func(string, bool, *specs.Platform) (builder.Image, builder.ROLayer, error) // imageSources mounts images and provides a cache for mounted images. It tracks // all images so they can be unmounted at the end of the build. @@ -22,7 +23,7 @@ type imageSources struct { } func newImageSources(ctx context.Context, options builderOptions) *imageSources { - getAndMount := func(idOrRef string, localOnly bool, osForPull string) (builder.Image, builder.ROLayer, error) { + getAndMount := func(idOrRef string, localOnly bool, platform *specs.Platform) (builder.Image, builder.ROLayer, error) { pullOption := backend.PullOptionNoPull if !localOnly { if options.Options.PullParent { @@ -35,7 +36,7 @@ func newImageSources(ctx context.Context, options builderOptions) *imageSources PullOption: pullOption, AuthConfig: options.Options.AuthConfigs, Output: options.ProgressWriter.Output, - OS: osForPull, + Platform: platform, }) } @@ -45,12 +46,12 @@ func newImageSources(ctx context.Context, options builderOptions) *imageSources } } -func (m *imageSources) Get(idOrRef string, localOnly bool, osForPull string) (*imageMount, error) { +func (m *imageSources) Get(idOrRef string, localOnly bool, platform *specs.Platform) (*imageMount, error) { if im, ok := m.byImageID[idOrRef]; ok { return im, nil } - image, layer, err := m.getImage(idOrRef, localOnly, osForPull) + image, layer, err := m.getImage(idOrRef, localOnly, platform) if err != nil { return nil, err } diff --git a/builder/dockerfile/internals.go b/builder/dockerfile/internals.go index 53748f0619f3f..d892285a1c899 100644 --- a/builder/dockerfile/internals.go +++ b/builder/dockerfile/internals.go @@ -27,6 +27,7 @@ import ( "github.com/docker/docker/pkg/system" "github.com/docker/go-connections/nat" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) // Archiver defines an interface for copying files from one destination to @@ -84,12 +85,8 @@ func (b *Builder) commit(dispatchState *dispatchState, comment string) error { } runConfigWithCommentCmd := copyRunConfig(dispatchState.runConfig, withCmdComment(comment, dispatchState.operatingSystem)) - hit, err := b.probeCache(dispatchState, runConfigWithCommentCmd) - if err != nil || hit { - return err - } - id, err := b.create(runConfigWithCommentCmd) - if err != nil { + id, err := b.probeAndCreate(dispatchState, runConfigWithCommentCmd) + if err != nil || id == "" { return err } @@ -153,7 +150,8 @@ func (b *Builder) exportImage(state *dispatchState, layer builder.RWLayer, paren return nil } -func (b *Builder) performCopy(state *dispatchState, inst copyInstruction) error { +func (b *Builder) performCopy(req dispatchRequest, inst copyInstruction) error { + state := req.state srcHash := getSourceHashFromInfos(inst.infos) var chownComment string @@ -171,7 +169,7 @@ func (b *Builder) performCopy(state *dispatchState, inst copyInstruction) error return err } - imageMount, err := b.imageSources.Get(state.imageID, true, state.operatingSystem) + 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) } @@ -345,6 +343,18 @@ func withEntrypointOverride(cmd []string, entrypoint []string) runConfigModifier } } +// withoutHealthcheck disables healthcheck. +// +// The dockerfile RUN instruction expect to run without healthcheck +// so the runConfig Healthcheck needs to be disabled. +func withoutHealthcheck() runConfigModifier { + return func(runConfig *container.Config) { + runConfig.Healthcheck = &container.HealthConfig{ + Test: []string{"NONE"}, + } + } +} + func copyRunConfig(runConfig *container.Config, modifiers ...runConfigModifier) *container.Config { copy := *runConfig copy.Cmd = copyStringSlice(runConfig.Cmd) @@ -413,14 +423,14 @@ func (b *Builder) probeAndCreate(dispatchState *dispatchState, runConfig *contai if hit, err := b.probeCache(dispatchState, runConfig); err != nil || hit { return "", err } - // Set a log config to override any default value set on the daemon - hostConfig := &container.HostConfig{LogConfig: defaultLogConfig} - container, err := b.containerManager.Create(runConfig, hostConfig) - return container.ID, err + return b.create(runConfig) } func (b *Builder) create(runConfig *container.Config) (string, error) { - hostConfig := hostConfigFromOptions(b.options) + logrus.Debugf("[BUILDER] Command to be executed: %v", runConfig.Cmd) + + 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 @@ -433,7 +443,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, @@ -461,7 +471,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 == "windows" { + if isWCOW { hc.StorageOpt = make(map[string]string) hc.StorageOpt["size"] = "127GB" } diff --git a/builder/dockerfile/internals_linux_test.go b/builder/dockerfile/internals_linux_test.go index c244ddfe3febe..1b3a99893a72b 100644 --- a/builder/dockerfile/internals_linux_test.go +++ b/builder/dockerfile/internals_linux_test.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/docker/docker/pkg/idtools" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestChownFlagParsing(t *testing.T) { diff --git a/builder/dockerfile/internals_test.go b/builder/dockerfile/internals_test.go index afd4a45f88b43..1c34fd3871c96 100644 --- a/builder/dockerfile/internals_test.go +++ b/builder/dockerfile/internals_test.go @@ -13,9 +13,9 @@ import ( "github.com/docker/docker/builder/remotecontext" "github.com/docker/docker/pkg/archive" "github.com/docker/go-connections/nat" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/skip" ) func TestEmptyDockerfile(t *testing.T) { @@ -61,7 +61,7 @@ func TestNonExistingDockerfile(t *testing.T) { } func readAndCheckDockerfile(t *testing.T, testName, contextDir, dockerfilePath, expectedError string) { - skip.IfCondition(t, os.Getuid() != 0, "skipping test that requires root") + skip.If(t, os.Getuid() != 0, "skipping test that requires root") tarStream, err := archive.Tar(contextDir, archive.Uncompressed) assert.NilError(t, err) diff --git a/builder/dockerfile/internals_windows_test.go b/builder/dockerfile/internals_windows_test.go index ffe52fb132623..4f00623404dbb 100644 --- a/builder/dockerfile/internals_windows_test.go +++ b/builder/dockerfile/internals_windows_test.go @@ -6,8 +6,8 @@ import ( "fmt" "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestNormalizeDest(t *testing.T) { diff --git a/builder/fscache/fscache_test.go b/builder/fscache/fscache_test.go index 16712ff743894..5108d65df1576 100644 --- a/builder/fscache/fscache_test.go +++ b/builder/fscache/fscache_test.go @@ -8,9 +8,9 @@ import ( "testing" "time" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/moby/buildkit/session/filesync" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestFSCache(t *testing.T) { diff --git a/builder/remotecontext/detect.go b/builder/remotecontext/detect.go index aaace269e93d0..49b196ed7bd2d 100644 --- a/builder/remotecontext/detect.go +++ b/builder/remotecontext/detect.go @@ -22,8 +22,7 @@ import ( const ClientSessionRemote = "client-session" // Detect returns a context and dockerfile from remote location or local -// archive. progressReader is only used if remoteURL is actually a URL -// (not empty, and not a Git endpoint). +// archive. func Detect(config backend.BuildConfig) (remote builder.Source, dockerfile *parser.Result, err error) { remoteURL := config.Options.RemoteContext dockerfilePath := config.Options.Dockerfile diff --git a/builder/remotecontext/git/gitutils_test.go b/builder/remotecontext/git/gitutils_test.go index a46675b22b6c2..8c39679081f1e 100644 --- a/builder/remotecontext/git/gitutils_test.go +++ b/builder/remotecontext/git/gitutils_test.go @@ -14,8 +14,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestParseRemoteURL(t *testing.T) { diff --git a/builder/remotecontext/lazycontext.go b/builder/remotecontext/lazycontext.go index 442cecad85e3a..96435f2585ce9 100644 --- a/builder/remotecontext/lazycontext.go +++ b/builder/remotecontext/lazycontext.go @@ -45,7 +45,7 @@ func (c *lazySource) Hash(path string) (string, error) { return "", errors.WithStack(convertPathError(err, cleanPath)) } - fi, err := os.Lstat(fullPath) + fi, err := c.root.Lstat(fullPath) if err != nil { // Backwards compatibility: a missing file returns a path as hash. // This is reached in the case of a broken symlink. diff --git a/builder/remotecontext/mimetype_test.go b/builder/remotecontext/mimetype_test.go index b13429cfa8a67..df9c37877091f 100644 --- a/builder/remotecontext/mimetype_test.go +++ b/builder/remotecontext/mimetype_test.go @@ -3,8 +3,8 @@ package remotecontext // import "github.com/docker/docker/builder/remotecontext" import ( "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestDetectContentType(t *testing.T) { diff --git a/builder/remotecontext/remote_test.go b/builder/remotecontext/remote_test.go index 64442d5107623..a0101f7493853 100644 --- a/builder/remotecontext/remote_test.go +++ b/builder/remotecontext/remote_test.go @@ -10,9 +10,9 @@ import ( "testing" "github.com/docker/docker/builder" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/fs" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/fs" ) var binaryContext = []byte{0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00} //xz magic diff --git a/builder/remotecontext/tarsum_test.go b/builder/remotecontext/tarsum_test.go index b05e4449ff15a..46f128d9f0608 100644 --- a/builder/remotecontext/tarsum_test.go +++ b/builder/remotecontext/tarsum_test.go @@ -9,8 +9,8 @@ import ( "github.com/docker/docker/builder" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/reexec" - "github.com/gotestyourself/gotestyourself/skip" "github.com/pkg/errors" + "gotest.tools/skip" ) const ( @@ -137,7 +137,7 @@ func TestRemoveDirectory(t *testing.T) { } func makeTestArchiveContext(t *testing.T, dir string) builder.Source { - skip.IfCondition(t, os.Getuid() != 0, "skipping test that requires root") + skip.If(t, os.Getuid() != 0, "skipping test that requires root") tarStream, err := archive.Tar(dir, archive.Uncompressed) if err != nil { t.Fatalf("error: %s", err) diff --git a/client/build_cancel.go b/client/build_cancel.go new file mode 100644 index 0000000000000..74df495089178 --- /dev/null +++ b/client/build_cancel.go @@ -0,0 +1,20 @@ +package client // import "github.com/docker/docker/client" + +import ( + "context" + "net/url" +) + +// BuildCancel requests the daemon to cancel ongoing build request +func (cli *Client) BuildCancel(ctx context.Context, id string) error { + query := url.Values{} + query.Set("id", id) + + serverResp, err := cli.post(ctx, "/build/cancel", query, nil, nil) + if err != nil { + return err + } + defer ensureReaderClosed(serverResp) + + return nil +} diff --git a/client/client_test.go b/client/client_test.go index fbf29d56392d9..58bccaa311508 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -10,10 +10,10 @@ import ( "github.com/docker/docker/api" "github.com/docker/docker/api/types" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/env" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/env" + "gotest.tools/skip" ) func TestNewEnvClient(t *testing.T) { @@ -193,7 +193,7 @@ func TestNewEnvClientSetsDefaultVersion(t *testing.T) { // TestNegotiateAPIVersionEmpty asserts that client.Client can // negotiate a compatible APIVersion when omitted func TestNegotiateAPIVersionEmpty(t *testing.T) { - defer env.PatchAll(t, map[string]string{"DOCKER_API_VERSION": ""}) + defer env.PatchAll(t, map[string]string{"DOCKER_API_VERSION": ""})() client, err := NewEnvClient() assert.NilError(t, err) diff --git a/client/config_create_test.go b/client/config_create_test.go index 8675e7f541a14..a6408792db931 100644 --- a/client/config_create_test.go +++ b/client/config_create_test.go @@ -12,8 +12,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestConfigCreateUnsupported(t *testing.T) { diff --git a/client/config_inspect_test.go b/client/config_inspect_test.go index b0c30fa27942f..76a5dae9e5ebc 100644 --- a/client/config_inspect_test.go +++ b/client/config_inspect_test.go @@ -11,9 +11,9 @@ import ( "testing" "github.com/docker/docker/api/types/swarm" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestConfigInspectNotFound(t *testing.T) { diff --git a/client/config_list_test.go b/client/config_list_test.go index e01d3d4c9df5c..b35a5929531ef 100644 --- a/client/config_list_test.go +++ b/client/config_list_test.go @@ -13,8 +13,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/swarm" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestConfigListUnsupported(t *testing.T) { diff --git a/client/config_remove_test.go b/client/config_remove_test.go index d574c5be0cfe2..9c0c0f9337e2c 100644 --- a/client/config_remove_test.go +++ b/client/config_remove_test.go @@ -9,8 +9,8 @@ import ( "strings" "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestConfigRemoveUnsupported(t *testing.T) { diff --git a/client/config_update_test.go b/client/config_update_test.go index 8b82d42eda9fa..1299f8278ca7c 100644 --- a/client/config_update_test.go +++ b/client/config_update_test.go @@ -10,8 +10,8 @@ import ( "testing" "github.com/docker/docker/api/types/swarm" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestConfigUpdateUnsupported(t *testing.T) { diff --git a/client/container_logs_test.go b/client/container_logs_test.go index f4ba0e76b475f..6d6e34e101272 100644 --- a/client/container_logs_test.go +++ b/client/container_logs_test.go @@ -14,8 +14,8 @@ import ( "time" "github.com/docker/docker/api/types" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestContainerLogsNotFoundError(t *testing.T) { diff --git a/client/container_prune_test.go b/client/container_prune_test.go index 9efb7b5854771..6a830d01dcacb 100644 --- a/client/container_prune_test.go +++ b/client/container_prune_test.go @@ -12,8 +12,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestContainersPruneError(t *testing.T) { diff --git a/client/container_remove_test.go b/client/container_remove_test.go index 8a2d01b1882cd..d94d8313041e8 100644 --- a/client/container_remove_test.go +++ b/client/container_remove_test.go @@ -10,8 +10,8 @@ import ( "testing" "github.com/docker/docker/api/types" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestContainerRemoveError(t *testing.T) { diff --git a/client/distribution_inspect_test.go b/client/distribution_inspect_test.go index d3c6828df13ae..a23d5f55d59fb 100644 --- a/client/distribution_inspect_test.go +++ b/client/distribution_inspect_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestDistributionInspectUnsupported(t *testing.T) { diff --git a/client/hijack.go b/client/hijack.go index 2b14831fd2237..35f5dd86dc1e5 100644 --- a/client/hijack.go +++ b/client/hijack.go @@ -9,7 +9,6 @@ import ( "net/http" "net/http/httputil" "net/url" - "strings" "time" "github.com/docker/docker/api/types" @@ -17,21 +16,6 @@ import ( "github.com/pkg/errors" ) -// tlsClientCon holds tls information and a dialed connection. -type tlsClientCon struct { - *tls.Conn - rawConn net.Conn -} - -func (c *tlsClientCon) CloseWrite() error { - // Go standard tls.Conn doesn't provide the CloseWrite() method so we do it - // on its underlying connection. - if conn, ok := c.rawConn.(types.CloseWriter); ok { - return conn.CloseWrite() - } - return nil -} - // postHijacked sends a POST request and hijacks the connection. func (cli *Client) postHijacked(ctx context.Context, path string, query url.Values, body interface{}, headers map[string][]string) (types.HijackedResponse, error) { bodyEncoded, err := encodeData(body) @@ -54,96 +38,9 @@ func (cli *Client) postHijacked(ctx context.Context, path string, query url.Valu return types.HijackedResponse{Conn: conn, Reader: bufio.NewReader(conn)}, err } -func tlsDial(network, addr string, config *tls.Config) (net.Conn, error) { - return tlsDialWithDialer(new(net.Dialer), network, addr, config) -} - -// We need to copy Go's implementation of tls.Dial (pkg/cryptor/tls/tls.go) in -// order to return our custom tlsClientCon struct which holds both the tls.Conn -// object _and_ its underlying raw connection. The rationale for this is that -// we need to be able to close the write end of the connection when attaching, -// which tls.Conn does not provide. -func tlsDialWithDialer(dialer *net.Dialer, network, addr string, config *tls.Config) (net.Conn, error) { - // We want the Timeout and Deadline values from dialer to cover the - // whole process: TCP connection and TLS handshake. This means that we - // also need to start our own timers now. - timeout := dialer.Timeout - - if !dialer.Deadline.IsZero() { - deadlineTimeout := time.Until(dialer.Deadline) - if timeout == 0 || deadlineTimeout < timeout { - timeout = deadlineTimeout - } - } - - var errChannel chan error - - if timeout != 0 { - errChannel = make(chan error, 2) - time.AfterFunc(timeout, func() { - errChannel <- errors.New("") - }) - } - - proxyDialer, err := sockets.DialerFromEnvironment(dialer) - if err != nil { - return nil, err - } - - rawConn, err := proxyDialer.Dial(network, addr) - if err != nil { - return nil, err - } - // When we set up a TCP connection for hijack, there could be long periods - // of inactivity (a long running command with no output) that in certain - // network setups may cause ECONNTIMEOUT, leaving the client in an unknown - // state. Setting TCP KeepAlive on the socket connection will prohibit - // ECONNTIMEOUT unless the socket connection truly is broken - if tcpConn, ok := rawConn.(*net.TCPConn); ok { - tcpConn.SetKeepAlive(true) - tcpConn.SetKeepAlivePeriod(30 * time.Second) - } - - colonPos := strings.LastIndex(addr, ":") - if colonPos == -1 { - colonPos = len(addr) - } - hostname := addr[:colonPos] - - // If no ServerName is set, infer the ServerName - // from the hostname we're connecting to. - if config.ServerName == "" { - // Make a copy to avoid polluting argument or default. - config = tlsConfigClone(config) - config.ServerName = hostname - } - - conn := tls.Client(rawConn, config) - - if timeout == 0 { - err = conn.Handshake() - } else { - go func() { - errChannel <- conn.Handshake() - }() - - err = <-errChannel - } - - if err != nil { - rawConn.Close() - return nil, err - } - - // This is Docker difference with standard's crypto/tls package: returned a - // wrapper which holds both the TLS and raw connections. - return &tlsClientCon{conn, rawConn}, nil -} - func dial(proto, addr string, tlsConfig *tls.Config) (net.Conn, error) { if tlsConfig != nil && proto != "unix" && proto != "npipe" { - // Notice this isn't Go standard's tls.Dial function - return tlsDial(proto, addr, tlsConfig) + return tls.Dial(proto, addr, tlsConfig) } if proto == "npipe" { return sockets.DialPipe(addr, 32*time.Second) diff --git a/client/hijack_test.go b/client/hijack_test.go new file mode 100644 index 0000000000000..d71dc9ea38cc8 --- /dev/null +++ b/client/hijack_test.go @@ -0,0 +1,103 @@ +package client + +import ( + "context" + "fmt" + "io/ioutil" + "net" + "net/http" + "net/http/httptest" + "net/url" + "testing" + + "github.com/docker/docker/api/server/httputils" + "github.com/docker/docker/api/types" + "github.com/pkg/errors" + "gotest.tools/assert" +) + +func TestTLSCloseWriter(t *testing.T) { + t.Parallel() + + var chErr chan error + ts := &httptest.Server{Config: &http.Server{Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + chErr = make(chan error, 1) + defer close(chErr) + if err := httputils.ParseForm(req); err != nil { + chErr <- errors.Wrap(err, "error parsing form") + http.Error(w, err.Error(), 500) + return + } + r, rw, err := httputils.HijackConnection(w) + if err != nil { + chErr <- errors.Wrap(err, "error hijacking connection") + http.Error(w, err.Error(), 500) + return + } + defer r.Close() + + fmt.Fprint(rw, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\n") + + buf := make([]byte, 5) + _, err = r.Read(buf) + if err != nil { + chErr <- errors.Wrap(err, "error reading from client") + return + } + _, err = rw.Write(buf) + if err != nil { + chErr <- errors.Wrap(err, "error writing to client") + return + } + })}} + + var ( + l net.Listener + err error + ) + for i := 1024; i < 10000; i++ { + l, err = net.Listen("tcp4", fmt.Sprintf("127.0.0.1:%d", i)) + if err == nil { + break + } + } + assert.Assert(t, err) + + ts.Listener = l + defer l.Close() + + defer func() { + if chErr != nil { + assert.Assert(t, <-chErr) + } + }() + + ts.StartTLS() + defer ts.Close() + + serverURL, err := url.Parse(ts.URL) + assert.Assert(t, err) + + client, err := NewClient("tcp://"+serverURL.Host, "", ts.Client(), nil) + assert.Assert(t, err) + + resp, err := client.postHijacked(context.Background(), "/asdf", url.Values{}, nil, map[string][]string{"Content-Type": {"text/plain"}}) + assert.Assert(t, err) + defer resp.Close() + + if _, ok := resp.Conn.(types.CloseWriter); !ok { + t.Fatal("tls conn did not implement the CloseWrite interface") + } + + _, err = resp.Conn.Write([]byte("hello")) + assert.Assert(t, err) + + b, err := ioutil.ReadAll(resp.Reader) + assert.Assert(t, err) + assert.Assert(t, string(b) == "hello") + assert.Assert(t, resp.CloseWrite()) + + // This should error since writes are closed + _, err = resp.Conn.Write([]byte("no")) + assert.Assert(t, err != nil) +} diff --git a/client/image_build.go b/client/image_build.go index 6721460316ade..9add3c10b348b 100644 --- a/client/image_build.go +++ b/client/image_build.go @@ -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 != "" { - if err := cli.NewVersionError("1.32", "platform"); err != nil { - return types.ImageBuildResponse{}, err - } - query.Set("platform", options.Platform) - } headers.Set("Content-Type", "application/x-tar") serverResp, err := cli.postRaw(ctx, "/build", query, buildContext, headers) @@ -131,7 +125,14 @@ func (cli *Client) imageBuildOptionsToQuery(options types.ImageBuildOptions) (ur query.Set("session", options.SessionID) } 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) + } + query.Set("version", string(options.Version)) return query, nil } diff --git a/client/image_prune_test.go b/client/image_prune_test.go index 3de3226f83b17..9b0839bb6c12b 100644 --- a/client/image_prune_test.go +++ b/client/image_prune_test.go @@ -12,8 +12,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestImagesPruneError(t *testing.T) { diff --git a/client/image_remove_test.go b/client/image_remove_test.go index 274d450ac49b2..acc6bc9177602 100644 --- a/client/image_remove_test.go +++ b/client/image_remove_test.go @@ -11,8 +11,8 @@ import ( "testing" "github.com/docker/docker/api/types" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestImageRemoveError(t *testing.T) { diff --git a/client/interface.go b/client/interface.go index 0487a0b9f35c8..9250c468a6e19 100644 --- a/client/interface.go +++ b/client/interface.go @@ -86,6 +86,7 @@ type DistributionAPIClient interface { type ImageAPIClient interface { ImageBuild(ctx context.Context, context io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) BuildCachePrune(ctx context.Context) (*types.BuildCachePruneReport, error) + BuildCancel(ctx context.Context, id string) error ImageCreate(ctx context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) ImageHistory(ctx context.Context, image string) ([]image.HistoryResponseItem, error) ImageImport(ctx context.Context, source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) diff --git a/client/network_inspect_test.go b/client/network_inspect_test.go index 4f4919e3054e3..699bccba675a0 100644 --- a/client/network_inspect_test.go +++ b/client/network_inspect_test.go @@ -12,9 +12,9 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/network" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestNetworkInspectError(t *testing.T) { diff --git a/client/network_prune_test.go b/client/network_prune_test.go index f67b6ab4d37b9..7a5d340e51953 100644 --- a/client/network_prune_test.go +++ b/client/network_prune_test.go @@ -12,8 +12,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestNetworksPruneError(t *testing.T) { diff --git a/client/ping_test.go b/client/ping_test.go index a26a136e3e54a..10bbbe811de62 100644 --- a/client/ping_test.go +++ b/client/ping_test.go @@ -8,8 +8,8 @@ import ( "strings" "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) // TestPingFail tests that when a server sends a non-successful response that we diff --git a/client/request_test.go b/client/request_test.go index e45a8651a09a1..fda4d88aa1425 100644 --- a/client/request_test.go +++ b/client/request_test.go @@ -10,7 +10,7 @@ import ( "testing" "github.com/docker/docker/api/types" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) // TestSetHostHeader should set fake host for local communications, set real host diff --git a/client/secret_create_test.go b/client/secret_create_test.go index 83f99cd09ccc5..419bdbcbc6213 100644 --- a/client/secret_create_test.go +++ b/client/secret_create_test.go @@ -12,8 +12,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestSecretCreateUnsupported(t *testing.T) { diff --git a/client/secret_inspect_test.go b/client/secret_inspect_test.go index 75782324a29b0..6c84799b1767a 100644 --- a/client/secret_inspect_test.go +++ b/client/secret_inspect_test.go @@ -11,9 +11,9 @@ import ( "testing" "github.com/docker/docker/api/types/swarm" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestSecretInspectUnsupported(t *testing.T) { diff --git a/client/secret_list_test.go b/client/secret_list_test.go index 4fc3235f3d9ec..72323b055faa0 100644 --- a/client/secret_list_test.go +++ b/client/secret_list_test.go @@ -13,8 +13,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/swarm" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestSecretListUnsupported(t *testing.T) { diff --git a/client/secret_remove_test.go b/client/secret_remove_test.go index 03e0316cab28e..bdfccf6be86d6 100644 --- a/client/secret_remove_test.go +++ b/client/secret_remove_test.go @@ -9,8 +9,8 @@ import ( "strings" "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestSecretRemoveUnsupported(t *testing.T) { diff --git a/client/secret_update_test.go b/client/secret_update_test.go index d0ce89cda2c82..c7670b440c37b 100644 --- a/client/secret_update_test.go +++ b/client/secret_update_test.go @@ -10,8 +10,8 @@ import ( "testing" "github.com/docker/docker/api/types/swarm" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestSecretUpdateUnsupported(t *testing.T) { diff --git a/client/service_create_test.go b/client/service_create_test.go index 1ef2f422cd734..9f51c18223b8a 100644 --- a/client/service_create_test.go +++ b/client/service_create_test.go @@ -13,10 +13,10 @@ import ( "github.com/docker/docker/api/types" registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/api/types/swarm" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/opencontainers/go-digest" "github.com/opencontainers/image-spec/specs-go/v1" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestServiceCreateError(t *testing.T) { diff --git a/client/service_logs_test.go b/client/service_logs_test.go index c3d624e212450..28f3ab5c6b669 100644 --- a/client/service_logs_test.go +++ b/client/service_logs_test.go @@ -14,8 +14,8 @@ import ( "time" "github.com/docker/docker/api/types" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestServiceLogsError(t *testing.T) { diff --git a/client/service_remove_test.go b/client/service_remove_test.go index 45892ceb2f28b..d2379a1366392 100644 --- a/client/service_remove_test.go +++ b/client/service_remove_test.go @@ -9,8 +9,8 @@ import ( "strings" "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestServiceRemoveError(t *testing.T) { diff --git a/client/swarm_get_unlock_key_test.go b/client/swarm_get_unlock_key_test.go index be822cb8aa94a..a1e460c1dc51d 100644 --- a/client/swarm_get_unlock_key_test.go +++ b/client/swarm_get_unlock_key_test.go @@ -11,8 +11,8 @@ import ( "testing" "github.com/docker/docker/api/types" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestSwarmGetUnlockKeyError(t *testing.T) { diff --git a/client/tlsconfig_clone.go b/client/tlsconfig_clone.go deleted file mode 100644 index 88200e92c3d91..0000000000000 --- a/client/tlsconfig_clone.go +++ /dev/null @@ -1,11 +0,0 @@ -// +build go1.8 - -package client // import "github.com/docker/docker/client" - -import "crypto/tls" - -// tlsConfigClone returns a clone of tls.Config. This function is provided for -// compatibility for go1.7 that doesn't include this method in stdlib. -func tlsConfigClone(c *tls.Config) *tls.Config { - return c.Clone() -} diff --git a/client/tlsconfig_clone_go17.go b/client/tlsconfig_clone_go17.go deleted file mode 100644 index e298542367eed..0000000000000 --- a/client/tlsconfig_clone_go17.go +++ /dev/null @@ -1,33 +0,0 @@ -// +build go1.7,!go1.8 - -package client // import "github.com/docker/docker/client" - -import "crypto/tls" - -// tlsConfigClone returns a clone of tls.Config. This function is provided for -// compatibility for go1.7 that doesn't include this method in stdlib. -func tlsConfigClone(c *tls.Config) *tls.Config { - return &tls.Config{ - Rand: c.Rand, - Time: c.Time, - Certificates: c.Certificates, - NameToCertificate: c.NameToCertificate, - GetCertificate: c.GetCertificate, - RootCAs: c.RootCAs, - NextProtos: c.NextProtos, - ServerName: c.ServerName, - ClientAuth: c.ClientAuth, - ClientCAs: c.ClientCAs, - InsecureSkipVerify: c.InsecureSkipVerify, - CipherSuites: c.CipherSuites, - PreferServerCipherSuites: c.PreferServerCipherSuites, - SessionTicketsDisabled: c.SessionTicketsDisabled, - SessionTicketKey: c.SessionTicketKey, - ClientSessionCache: c.ClientSessionCache, - MinVersion: c.MinVersion, - MaxVersion: c.MaxVersion, - CurvePreferences: c.CurvePreferences, - DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled, - Renegotiation: c.Renegotiation, - } -} diff --git a/client/volume_inspect_test.go b/client/volume_inspect_test.go index d0a3246689cce..04f00129b7142 100644 --- a/client/volume_inspect_test.go +++ b/client/volume_inspect_test.go @@ -11,9 +11,9 @@ import ( "testing" "github.com/docker/docker/api/types" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestVolumeInspectError(t *testing.T) { diff --git a/cmd/dockerd/config_unix_test.go b/cmd/dockerd/config_unix_test.go index eaa53d8f6fec8..d7dbf4b4cc5d7 100644 --- a/cmd/dockerd/config_unix_test.go +++ b/cmd/dockerd/config_unix_test.go @@ -6,9 +6,9 @@ import ( "testing" "github.com/docker/docker/daemon/config" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/spf13/pflag" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestDaemonParseShmSize(t *testing.T) { diff --git a/cmd/dockerd/daemon.go b/cmd/dockerd/daemon.go index 6b0be5f7f75b8..efefaa1ac38c7 100644 --- a/cmd/dockerd/daemon.go +++ b/cmd/dockerd/daemon.go @@ -27,6 +27,7 @@ import ( swarmrouter "github.com/docker/docker/api/server/router/swarm" systemrouter "github.com/docker/docker/api/server/router/system" "github.com/docker/docker/api/server/router/volume" + buildkit "github.com/docker/docker/builder/builder-next" "github.com/docker/docker/builder/dockerfile" "github.com/docker/docker/builder/fscache" "github.com/docker/docker/cli/debug" @@ -238,7 +239,8 @@ func (cli *DaemonCli) start(opts *daemonOptions) (err error) { type routerOptions struct { sessionManager *session.Manager buildBackend *buildbackend.Backend - buildCache *fscache.FSCache + buildCache *fscache.FSCache // legacy + buildkit *buildkit.Builder daemon *daemon.Daemon api *apiserver.Server cluster *cluster.Cluster @@ -270,7 +272,16 @@ func newRouterOptions(config *config.Config, daemon *daemon.Daemon) (routerOptio return opts, err } - bb, err := buildbackend.NewBackend(daemon.ImageService(), manager, buildCache) + buildkit, err := buildkit.New(buildkit.Opt{ + SessionManager: sm, + Root: filepath.Join(config.Root, "buildkit"), + Dist: daemon.DistributionServices(), + }) + if err != nil { + return opts, err + } + + bb, err := buildbackend.NewBackend(daemon.ImageService(), manager, buildCache, buildkit) if err != nil { return opts, errors.Wrap(err, "failed to create buildmanager") } @@ -279,6 +290,7 @@ func newRouterOptions(config *config.Config, daemon *daemon.Daemon) (routerOptio sessionManager: sm, buildBackend: bb, buildCache: buildCache, + buildkit: buildkit, daemon: daemon, }, nil } @@ -452,7 +464,7 @@ func initRouter(opts routerOptions) { checkpointrouter.NewRouter(opts.daemon, decoder), container.NewRouter(opts.daemon, decoder), image.NewRouter(opts.daemon.ImageService()), - systemrouter.NewRouter(opts.daemon, opts.cluster, opts.buildCache), + systemrouter.NewRouter(opts.daemon, opts.cluster, opts.buildCache, opts.buildkit), volume.NewRouter(opts.daemon.VolumesService()), build.NewRouter(opts.buildBackend, opts.daemon), sessionrouter.NewRouter(opts.sessionManager), diff --git a/cmd/dockerd/daemon_test.go b/cmd/dockerd/daemon_test.go index 539b442cbd7dc..ad447e3b90b5d 100644 --- a/cmd/dockerd/daemon_test.go +++ b/cmd/dockerd/daemon_test.go @@ -4,11 +4,11 @@ import ( "testing" "github.com/docker/docker/daemon/config" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/fs" "github.com/sirupsen/logrus" "github.com/spf13/pflag" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/fs" ) func defaultOptions(configFile string) *daemonOptions { diff --git a/cmd/dockerd/daemon_unix.go b/cmd/dockerd/daemon_unix.go index af16fa4e1b7fc..a88bcccc0c36a 100644 --- a/cmd/dockerd/daemon_unix.go +++ b/cmd/dockerd/daemon_unix.go @@ -10,7 +10,7 @@ import ( "path/filepath" "strconv" - "github.com/containerd/containerd/linux" + "github.com/containerd/containerd/runtime/linux" "github.com/docker/docker/cmd/dockerd/hack" "github.com/docker/docker/daemon" "github.com/docker/docker/libcontainerd" @@ -48,6 +48,8 @@ func (cli *DaemonCli) getPlatformRemoteOptions() ([]libcontainerd.RemoteOption, } if cli.Config.Debug { opts = append(opts, libcontainerd.WithLogLevel("debug")) + } else if cli.Config.LogLevel != "" { + opts = append(opts, libcontainerd.WithLogLevel(cli.Config.LogLevel)) } if cli.Config.ContainerdAddr != "" { opts = append(opts, libcontainerd.WithRemoteAddr(cli.Config.ContainerdAddr)) diff --git a/cmd/dockerd/daemon_unix_test.go b/cmd/dockerd/daemon_unix_test.go index 39ff1e682269a..692d0328c4167 100644 --- a/cmd/dockerd/daemon_unix_test.go +++ b/cmd/dockerd/daemon_unix_test.go @@ -6,9 +6,9 @@ import ( "testing" "github.com/docker/docker/daemon/config" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/fs" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/fs" ) func TestLoadDaemonCliConfigWithDaemonFlags(t *testing.T) { diff --git a/cmd/dockerd/docker.go b/cmd/dockerd/docker.go index 463482e938289..197bb49c92e12 100644 --- a/cmd/dockerd/docker.go +++ b/cmd/dockerd/docker.go @@ -10,6 +10,7 @@ import ( "github.com/docker/docker/dockerversion" "github.com/docker/docker/pkg/reexec" "github.com/docker/docker/pkg/term" + "github.com/moby/buildkit/util/apicaps" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -42,6 +43,12 @@ func newDaemonCommand() *cobra.Command { return cmd } +func init() { + if dockerversion.ProductName != "" { + apicaps.ExportedProduct = dockerversion.ProductName + } +} + func main() { if reexec.Init() { return diff --git a/cmd/dockerd/options_test.go b/cmd/dockerd/options_test.go index 2a4e63b6b624f..691118f08fbe1 100644 --- a/cmd/dockerd/options_test.go +++ b/cmd/dockerd/options_test.go @@ -6,9 +6,9 @@ import ( cliconfig "github.com/docker/docker/cli/config" "github.com/docker/docker/daemon/config" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/spf13/pflag" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestCommonOptionsInstallFlags(t *testing.T) { diff --git a/container/container_unit_test.go b/container/container_unit_test.go index fbee6e5eb0567..82b5864760fca 100644 --- a/container/container_unit_test.go +++ b/container/container_unit_test.go @@ -11,7 +11,7 @@ import ( swarmtypes "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/daemon/logger/jsonfilelog" "github.com/docker/docker/pkg/signal" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) func TestContainerStopSignal(t *testing.T) { diff --git a/container/stream/attach.go b/container/stream/attach.go index cee90b88a1ed4..1366dcb4994c4 100644 --- a/container/stream/attach.go +++ b/container/stream/attach.go @@ -3,11 +3,12 @@ package stream // import "github.com/docker/docker/container/stream" import ( "context" "io" - "sync" "github.com/docker/docker/pkg/pools" "github.com/docker/docker/pkg/term" + "github.com/pkg/errors" "github.com/sirupsen/logrus" + "golang.org/x/sync/errgroup" ) var defaultEscapeSequence = []byte{16, 17} // ctrl-p, ctrl-q @@ -57,117 +58,107 @@ func (c *Config) AttachStreams(cfg *AttachConfig) { // CopyStreams starts goroutines to copy data in and out to/from the container func (c *Config) CopyStreams(ctx context.Context, cfg *AttachConfig) <-chan error { - var ( - wg sync.WaitGroup - errors = make(chan error, 3) - ) + var group errgroup.Group + // Connect stdin of container to the attach stdin stream. if cfg.Stdin != nil { - wg.Add(1) - } - - if cfg.Stdout != nil { - wg.Add(1) - } - - if cfg.Stderr != nil { - wg.Add(1) - } + group.Go(func() error { + logrus.Debug("attach: stdin: begin") + defer logrus.Debug("attach: stdin: end") - // Connect stdin of container to the attach stdin stream. - go func() { - if cfg.Stdin == nil { - return - } - logrus.Debug("attach: stdin: begin") + defer func() { + if cfg.CloseStdin && !cfg.TTY { + cfg.CStdin.Close() + } else { + // No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr + if cfg.CStdout != nil { + cfg.CStdout.Close() + } + if cfg.CStderr != nil { + cfg.CStderr.Close() + } + } + }() - var err error - if cfg.TTY { - _, err = copyEscapable(cfg.CStdin, cfg.Stdin, cfg.DetachKeys) - } else { - _, err = pools.Copy(cfg.CStdin, cfg.Stdin) - } - if err == io.ErrClosedPipe { - err = nil - } - if err != nil { - logrus.Errorf("attach: stdin: %s", err) - errors <- err - } - if cfg.CloseStdin && !cfg.TTY { - cfg.CStdin.Close() - } else { - // No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr - if cfg.CStdout != nil { - cfg.CStdout.Close() + var err error + if cfg.TTY { + _, err = copyEscapable(cfg.CStdin, cfg.Stdin, cfg.DetachKeys) + } else { + _, err = pools.Copy(cfg.CStdin, cfg.Stdin) } - if cfg.CStderr != nil { - cfg.CStderr.Close() + if err == io.ErrClosedPipe { + err = nil } - } - logrus.Debug("attach: stdin: end") - wg.Done() - }() - - attachStream := func(name string, stream io.Writer, streamPipe io.ReadCloser) { - if stream == nil { - return - } + if err != nil { + logrus.WithError(err).Debug("error on attach stdin") + return errors.Wrap(err, "error on attach stdin") + } + return nil + }) + } + attachStream := func(name string, stream io.Writer, streamPipe io.ReadCloser) error { logrus.Debugf("attach: %s: begin", name) + defer logrus.Debugf("attach: %s: end", name) + defer func() { + // Make sure stdin gets closed + if cfg.Stdin != nil { + cfg.Stdin.Close() + } + streamPipe.Close() + }() + _, err := pools.Copy(stream, streamPipe) if err == io.ErrClosedPipe { err = nil } if err != nil { - logrus.Errorf("attach: %s: %v", name, err) - errors <- err - } - // Make sure stdin gets closed - if cfg.Stdin != nil { - cfg.Stdin.Close() + logrus.WithError(err).Debugf("attach: %s", name) + return errors.Wrapf(err, "error attaching %s stream", name) } - streamPipe.Close() - logrus.Debugf("attach: %s: end", name) - wg.Done() + return nil } - go attachStream("stdout", cfg.Stdout, cfg.CStdout) - go attachStream("stderr", cfg.Stderr, cfg.CStderr) + if cfg.Stdout != nil { + group.Go(func() error { + return attachStream("stdout", cfg.Stdout, cfg.CStdout) + }) + } + if cfg.Stderr != nil { + group.Go(func() error { + return attachStream("stderr", cfg.Stderr, cfg.CStderr) + }) + } errs := make(chan error, 1) - go func() { - defer close(errs) - errs <- func() error { - done := make(chan struct{}) - go func() { - wg.Wait() - close(done) - }() - select { - case <-done: - case <-ctx.Done(): - // close all pipes - if cfg.CStdin != nil { - cfg.CStdin.Close() - } - if cfg.CStdout != nil { - cfg.CStdout.Close() - } - if cfg.CStderr != nil { - cfg.CStderr.Close() - } - <-done + defer logrus.Debug("attach done") + groupErr := make(chan error, 1) + go func() { + groupErr <- group.Wait() + }() + select { + case <-ctx.Done(): + // close all pipes + if cfg.CStdin != nil { + cfg.CStdin.Close() } - close(errors) - for err := range errors { - if err != nil { - return err - } + if cfg.CStdout != nil { + cfg.CStdout.Close() } - return nil - }() + if cfg.CStderr != nil { + cfg.CStderr.Close() + } + + // Now with these closed, wait should return. + if err := group.Wait(); err != nil { + errs <- err + return + } + errs <- ctx.Err() + case err := <-groupErr: + errs <- err + } }() return errs diff --git a/container/view_test.go b/container/view_test.go index a872dffea617e..434b7c618d53b 100644 --- a/container/view_test.go +++ b/container/view_test.go @@ -8,9 +8,9 @@ import ( "github.com/docker/docker/api/types" containertypes "github.com/docker/docker/api/types/container" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pborman/uuid" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) var root string diff --git a/contrib/mkimage-alpine.sh b/contrib/mkimage-alpine.sh index 03180e435abf3..16791a758d375 100755 --- a/contrib/mkimage-alpine.sh +++ b/contrib/mkimage-alpine.sh @@ -29,7 +29,7 @@ getapk() { } mkbase() { - $TMP/sbin/apk.static --repository $MAINREPO --update-cache --allow-untrusted \ + $TMP/sbin/apk.static --repository $MAINREPO --no-cache --allow-untrusted \ --root $ROOTFS --initdb add alpine-base } @@ -43,7 +43,7 @@ pack() { id=$(tar --numeric-owner -C $ROOTFS -c . | docker import - alpine:$REL) docker tag $id alpine:latest - docker run -i -t --rm alpine printf 'alpine:%s with id=%s created!\n' $REL $id + docker run --rm alpine printf 'alpine:%s with id=%s created!\n' $REL $id } save() { diff --git a/contrib/syntax/vim/README.md b/contrib/syntax/vim/README.md index 5aa9bd825dfa0..b73a30bea328a 100644 --- a/contrib/syntax/vim/README.md +++ b/contrib/syntax/vim/README.md @@ -11,6 +11,10 @@ With [Vundle](https://github.com/gmarik/Vundle.vim) Plugin 'docker/docker' , {'rtp': '/contrib/syntax/vim/'} +With [vim-plug](https://github.com/junegunn/vim-plug) + + Plug 'docker/docker' , {'rtp': '/contrib/syntax/vim/'} + Features -------- diff --git a/contrib/syntax/vim/ftdetect/dockerfile.vim b/contrib/syntax/vim/ftdetect/dockerfile.vim index f7a962e073f05..a21dd14095ccf 100644 --- a/contrib/syntax/vim/ftdetect/dockerfile.vim +++ b/contrib/syntax/vim/ftdetect/dockerfile.vim @@ -1 +1 @@ -au BufNewFile,BufRead [Dd]ockerfile,Dockerfile.*,*.Dockerfile set filetype=dockerfile +au BufNewFile,BufRead [Dd]ockerfile,[Dd]ockerfile.*,*.[Dd]ockerfile set filetype=dockerfile diff --git a/daemon/attach.go b/daemon/attach.go index 21ed9a06b7c19..fb14691d242af 100644 --- a/daemon/attach.go +++ b/daemon/attach.go @@ -176,7 +176,7 @@ func (daemon *Daemon) containerAttach(c *container.Container, cfg *stream.Attach ctx := c.InitAttachContext() err := <-c.StreamConfig.CopyStreams(ctx, cfg) if err != nil { - if _, ok := err.(term.EscapeError); ok { + if _, ok := errors.Cause(err).(term.EscapeError); ok || err == context.Canceled { daemon.LogContainerEvent(c, "detach") } else { logrus.Errorf("attach failed with error: %v", err) diff --git a/daemon/caps/utils_unix.go b/daemon/caps/utils.go similarity index 99% rename from daemon/caps/utils_unix.go rename to daemon/caps/utils.go index 4c18b28be56d5..c5ded542eff7a 100644 --- a/daemon/caps/utils_unix.go +++ b/daemon/caps/utils.go @@ -1,5 +1,3 @@ -// +build !windows - package caps // import "github.com/docker/docker/daemon/caps" import ( diff --git a/daemon/cluster.go b/daemon/cluster.go index b5ac6c4856e17..62eb2a2e5957d 100644 --- a/daemon/cluster.go +++ b/daemon/cluster.go @@ -2,6 +2,7 @@ package daemon // import "github.com/docker/docker/daemon" import ( apitypes "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" lncluster "github.com/docker/libnetwork/cluster" ) @@ -21,6 +22,6 @@ type ClusterStatus interface { // NetworkManager provides methods to manage networks type NetworkManager interface { GetNetwork(input string) (apitypes.NetworkResource, error) - GetNetworks() ([]apitypes.NetworkResource, error) + GetNetworks(filters.Args) ([]apitypes.NetworkResource, error) RemoveNetwork(input string) error } diff --git a/daemon/cluster/controllers/plugin/controller.go b/daemon/cluster/controllers/plugin/controller.go index 6d7606aa84355..371f918d39964 100644 --- a/daemon/cluster/controllers/plugin/controller.go +++ b/daemon/cluster/controllers/plugin/controller.go @@ -180,7 +180,7 @@ func (p *Controller) Wait(ctx context.Context) error { case <-ctx.Done(): return ctx.Err() case e := <-events: - p.logger.Debugf("got event %#T", e) + p.logger.Debugf("got event %T", e) switch e.(type) { case plugin.EventEnable: diff --git a/daemon/cluster/convert/container.go b/daemon/cluster/convert/container.go index 0a34fc73e4757..d889b4004ccc7 100644 --- a/daemon/cluster/convert/container.go +++ b/daemon/cluster/convert/container.go @@ -35,6 +35,7 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) *types.ContainerSpec { Secrets: secretReferencesFromGRPC(c.Secrets), Configs: configReferencesFromGRPC(c.Configs), Isolation: IsolationFromGRPC(c.Isolation), + Init: initFromGRPC(c.Init), } if c.DNSConfig != nil { @@ -119,6 +120,21 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) *types.ContainerSpec { return containerSpec } +func initFromGRPC(v *gogotypes.BoolValue) *bool { + if v == nil { + return nil + } + value := v.GetValue() + return &value +} + +func initToGRPC(v *bool) *gogotypes.BoolValue { + if v == nil { + return nil + } + return &gogotypes.BoolValue{Value: *v} +} + func secretReferencesToGRPC(sr []*types.SecretReference) []*swarmapi.SecretReference { refs := make([]*swarmapi.SecretReference, 0, len(sr)) for _, s := range sr { @@ -234,6 +250,7 @@ func containerToGRPC(c *types.ContainerSpec) (*swarmapi.ContainerSpec, error) { Secrets: secretReferencesToGRPC(c.Secrets), Configs: configReferencesToGRPC(c.Configs), Isolation: isolationToGRPC(c.Isolation), + Init: initToGRPC(c.Init), } if c.DNSConfig != nil { diff --git a/daemon/cluster/convert/service_test.go b/daemon/cluster/convert/service_test.go index 826cf6fbefe00..ad5f0d4494246 100644 --- a/daemon/cluster/convert/service_test.go +++ b/daemon/cluster/convert/service_test.go @@ -8,7 +8,7 @@ import ( "github.com/docker/docker/api/types/swarm/runtime" swarmapi "github.com/docker/swarmkit/api" google_protobuf3 "github.com/gogo/protobuf/types" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) func TestServiceConvertFromGRPCRuntimeContainer(t *testing.T) { diff --git a/daemon/cluster/executor/backend.go b/daemon/cluster/executor/backend.go index 1f2312ab4011c..cfbc86ce366da 100644 --- a/daemon/cluster/executor/backend.go +++ b/daemon/cluster/executor/backend.go @@ -23,6 +23,7 @@ import ( "github.com/docker/libnetwork/cluster" networktypes "github.com/docker/libnetwork/types" "github.com/docker/swarmkit/agent/exec" + specs "github.com/opencontainers/image-spec/specs-go/v1" ) // Backend defines the executor component for a swarm agent. @@ -69,7 +70,7 @@ type VolumeBackend interface { // ImageBackend is used by an executor to perform image operations type ImageBackend interface { - PullImage(ctx context.Context, image, tag, platform string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error + PullImage(ctx context.Context, image, tag string, platform *specs.Platform, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error GetRepository(context.Context, reference.Named, *types.AuthConfig) (distribution.Repository, bool, error) LookupImage(name string) (*types.ImageInspect, error) } diff --git a/daemon/cluster/executor/container/adapter.go b/daemon/cluster/executor/container/adapter.go index fdf1ee2ec75bb..f4b97ce6609f3 100644 --- a/daemon/cluster/executor/container/adapter.go +++ b/daemon/cluster/executor/container/adapter.go @@ -4,11 +4,9 @@ import ( "context" "encoding/base64" "encoding/json" - "errors" "fmt" "io" "os" - "runtime" "strings" "syscall" "time" @@ -29,6 +27,7 @@ import ( "github.com/docker/swarmkit/log" gogotypes "github.com/gogo/protobuf/types" "github.com/opencontainers/go-digest" + "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/time/rate" ) @@ -97,8 +96,7 @@ func (c *containerAdapter) pullImage(ctx context.Context) error { go func() { // TODO @jhowardmsft LCOW Support: This will need revisiting as // the stack is built up to include LCOW support for swarm. - platform := runtime.GOOS - err := c.imageBackend.PullImage(ctx, c.container.image(), "", platform, metaHeaders, authConfig, pw) + err := c.imageBackend.PullImage(ctx, c.container.image(), "", nil, metaHeaders, authConfig, pw) pw.CloseWithError(err) }() @@ -174,7 +172,7 @@ func (c *containerAdapter) createNetworks(ctx context.Context) error { func (c *containerAdapter) removeNetworks(ctx context.Context) error { for name, v := range c.container.networksAttachments { if err := c.backend.DeleteManagedNetwork(v.Network.ID); err != nil { - switch err.(type) { + switch errors.Cause(err).(type) { case *libnetwork.ActiveEndpointsError: continue case libnetwork.ErrNoSuchNetwork: diff --git a/daemon/cluster/executor/container/container.go b/daemon/cluster/executor/container/container.go index 69d673bd304ca..77d21d2c1f32b 100644 --- a/daemon/cluster/executor/container/container.go +++ b/daemon/cluster/executor/container/container.go @@ -172,6 +172,14 @@ func (c *containerConfig) isolation() enginecontainer.Isolation { return convert.IsolationFromGRPC(c.spec().Isolation) } +func (c *containerConfig) init() *bool { + if c.spec().Init == nil { + return nil + } + init := c.spec().Init.GetValue() + return &init +} + func (c *containerConfig) exposedPorts() map[nat.Port]struct{} { exposedPorts := make(map[nat.Port]struct{}) if c.task.Endpoint == nil { @@ -355,6 +363,7 @@ func (c *containerConfig) hostConfig() *enginecontainer.HostConfig { Mounts: c.mounts(), ReadonlyRootfs: c.spec().ReadOnly, Isolation: c.isolation(), + Init: c.init(), } if c.spec().DNSConfig != nil { diff --git a/daemon/cluster/executor/container/container_test.go b/daemon/cluster/executor/container/container_test.go index f9e8c8a92e5b6..1bf6f6cf02648 100644 --- a/daemon/cluster/executor/container/container_test.go +++ b/daemon/cluster/executor/container/container_test.go @@ -5,7 +5,7 @@ import ( "github.com/docker/docker/api/types/container" swarmapi "github.com/docker/swarmkit/api" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) func TestIsolationConversion(t *testing.T) { diff --git a/daemon/cluster/networks.go b/daemon/cluster/networks.go index b8e31baa11b4c..bcda316ca23ab 100644 --- a/daemon/cluster/networks.go +++ b/daemon/cluster/networks.go @@ -5,9 +5,11 @@ import ( "fmt" apitypes "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/network" types "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/daemon/cluster/convert" + internalnetwork "github.com/docker/docker/daemon/network" "github.com/docker/docker/errdefs" "github.com/docker/docker/runconfig" swarmapi "github.com/docker/swarmkit/api" @@ -16,16 +18,32 @@ import ( ) // GetNetworks returns all current cluster managed networks. -func (c *Cluster) GetNetworks() ([]apitypes.NetworkResource, error) { - list, err := c.getNetworks(nil) +func (c *Cluster) GetNetworks(filter filters.Args) ([]apitypes.NetworkResource, error) { + var f *swarmapi.ListNetworksRequest_Filters + + if filter.Len() > 0 { + f = &swarmapi.ListNetworksRequest_Filters{} + + if filter.Contains("name") { + f.Names = filter.Get("name") + f.NamePrefixes = filter.Get("name") + } + + if filter.Contains("id") { + f.IDPrefixes = filter.Get("id") + } + } + + list, err := c.getNetworks(f) if err != nil { return nil, err } - removePredefinedNetworks(&list) - return list, nil + filterPredefinedNetworks(&list) + + return internalnetwork.FilterNetworks(list, filter) } -func removePredefinedNetworks(networks *[]apitypes.NetworkResource) { +func filterPredefinedNetworks(networks *[]apitypes.NetworkResource) { if networks == nil { return } diff --git a/daemon/config/config_test.go b/daemon/config/config_test.go index cb7aa740710e1..6998ed3312342 100644 --- a/daemon/config/config_test.go +++ b/daemon/config/config_test.go @@ -8,11 +8,11 @@ import ( "github.com/docker/docker/daemon/discovery" "github.com/docker/docker/opts" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/fs" - "github.com/gotestyourself/gotestyourself/skip" "github.com/spf13/pflag" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/fs" + "gotest.tools/skip" ) func TestDaemonConfigurationNotFound(t *testing.T) { @@ -461,7 +461,7 @@ func TestReloadSetConfigFileNotExist(t *testing.T) { // TestReloadDefaultConfigNotExist tests that if the default configuration file // doesn't exist the daemon still will be reloaded. func TestReloadDefaultConfigNotExist(t *testing.T) { - skip.IfCondition(t, os.Getuid() != 0, "skipping test that requires root") + skip.If(t, os.Getuid() != 0, "skipping test that requires root") reloaded := false configFile := "/etc/docker/daemon.json" flags := pflag.NewFlagSet("test", pflag.ContinueOnError) diff --git a/daemon/config/config_unix_test.go b/daemon/config/config_unix_test.go index d9bb9476ac73a..529b6777050e7 100644 --- a/daemon/config/config_unix_test.go +++ b/daemon/config/config_unix_test.go @@ -7,10 +7,10 @@ import ( "github.com/docker/docker/opts" "github.com/docker/go-units" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/fs" "github.com/spf13/pflag" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/fs" ) func TestGetConflictFreeConfiguration(t *testing.T) { diff --git a/daemon/config/config_windows_test.go b/daemon/config/config_windows_test.go index fff98014f9823..09417ee388307 100644 --- a/daemon/config/config_windows_test.go +++ b/daemon/config/config_windows_test.go @@ -7,9 +7,9 @@ import ( "testing" "github.com/docker/docker/opts" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/spf13/pflag" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestDaemonConfigurationMerge(t *testing.T) { diff --git a/daemon/container_unix_test.go b/daemon/container_unix_test.go index 12075f8b8df05..b4c5f84c7e794 100644 --- a/daemon/container_unix_test.go +++ b/daemon/container_unix_test.go @@ -9,7 +9,7 @@ import ( containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/daemon/config" "github.com/docker/go-connections/nat" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) // TestContainerWarningHostAndPublishPorts that a warning is returned when setting network mode to host and specifying published ports. diff --git a/daemon/create_test.go b/daemon/create_test.go index 7ef49d7623360..3dba847d46b3d 100644 --- a/daemon/create_test.go +++ b/daemon/create_test.go @@ -5,7 +5,7 @@ import ( "github.com/docker/docker/api/types/network" "github.com/docker/docker/errdefs" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) // Test case for 35752 diff --git a/daemon/daemon.go b/daemon/daemon.go index 43b7731a3ea9d..119fac2a0a4fd 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -538,7 +538,7 @@ func (daemon *Daemon) DaemonLeavesCluster() { select { case <-done: case <-time.After(5 * time.Second): - logrus.Warnf("timeout while waiting for ingress network removal") + logrus.Warn("timeout while waiting for ingress network removal") } } else { logrus.Warnf("failed to initiate ingress network removal: %v", err) @@ -922,6 +922,11 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe return d, nil } +// DistributionServices returns services controlling daemon storage +func (daemon *Daemon) DistributionServices() images.DistributionServices { + return daemon.imageService.DistributionServices() +} + func (daemon *Daemon) waitForStartupDone() { <-daemon.startupDone } diff --git a/daemon/daemon_linux_test.go b/daemon/daemon_linux_test.go index 1635b08535fad..767925e2fb2b0 100644 --- a/daemon/daemon_linux_test.go +++ b/daemon/daemon_linux_test.go @@ -15,8 +15,8 @@ import ( "github.com/docker/docker/oci" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/mount" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) const mountsFixture = `142 78 0:38 / / rw,relatime - aufs none rw,si=573b861da0b3a05b,dio diff --git a/daemon/daemon_test.go b/daemon/daemon_test.go index 4e068194f0692..43f4f504b6f98 100644 --- a/daemon/daemon_test.go +++ b/daemon/daemon_test.go @@ -16,9 +16,9 @@ import ( volumesservice "github.com/docker/docker/volume/service" "github.com/docker/go-connections/nat" "github.com/docker/libnetwork" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) // diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go index e2c77610d4e85..3c11f9d582637 100644 --- a/daemon/daemon_unix.go +++ b/daemon/daemon_unix.go @@ -646,13 +646,13 @@ func (daemon *Daemon) initRuntimes(runtimes map[string]types.Runtime) (err error os.RemoveAll(runtimeDir + "-old") tmpDir, err := ioutils.TempDir(daemon.configStore.Root, "gen-runtimes") if err != nil { - return errors.Wrapf(err, "failed to get temp dir to generate runtime scripts") + return errors.Wrap(err, "failed to get temp dir to generate runtime scripts") } defer func() { if err != nil { if err1 := os.RemoveAll(tmpDir); err1 != nil { logrus.WithError(err1).WithField("dir", tmpDir). - Warnf("failed to remove tmp dir") + Warn("failed to remove tmp dir") } return } @@ -661,12 +661,12 @@ func (daemon *Daemon) initRuntimes(runtimes map[string]types.Runtime) (err error return } if err = os.Rename(tmpDir, runtimeDir); err != nil { - err = errors.Wrapf(err, "failed to setup runtimes dir, new containers may not start") + err = errors.Wrap(err, "failed to setup runtimes dir, new containers may not start") return } if err = os.RemoveAll(runtimeDir + "-old"); err != nil { logrus.WithError(err).WithField("dir", tmpDir). - Warnf("failed to remove old runtimes dir") + Warn("failed to remove old runtimes dir") } }() @@ -1126,7 +1126,7 @@ func setupRemappedRoot(config *config.Config) (*idtools.IDMappings, error) { mappings, err := idtools.NewIDMappings(username, groupname) if err != nil { - return nil, errors.Wrapf(err, "Can't create ID mappings: %v") + return nil, errors.Wrap(err, "Can't create ID mappings") } return mappings, nil } diff --git a/daemon/daemon_windows.go b/daemon/daemon_windows.go index 1f801032df3b7..eb1c3fc4fdba5 100644 --- a/daemon/daemon_windows.go +++ b/daemon/daemon_windows.go @@ -195,11 +195,20 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes. warnings := []string{} hyperv := daemon.runAsHyperVContainer(hostConfig) - if !hyperv && system.IsWindowsClient() && !system.IsIoTCore() { - // @engine maintainers. This block should not be removed. It partially enforces licensing - // restrictions on Windows. Ping @jhowardmsft if there are concerns or PRs to change this. - return warnings, fmt.Errorf("Windows client operating systems only support Hyper-V containers") - } + if !hyperv { + switch system.GetProcessIsolationPolicy() { + default: + // system.PROC_ISOLATION_DEFAULT: + if system.IsWindowsClient() && !system.IsIoTCore() { + // @engine maintainers. This block should not be removed. It partially enforces licensing + // restrictions on Windows. Ping @jhowardmsft if there are concerns or PRs to change this. + return warnings, fmt.Errorf("Windows client operating systems only support Hyper-V containers") + } + case system.PROC_ISOLATION_DENY: + return warnings, fmt.Errorf("Process isolation not allowed by policy") + case system.PROC_ISOLATION_ALLOW: + } + } w, err := verifyContainerResources(&hostConfig.Resources, hyperv) warnings = append(warnings, w...) @@ -612,15 +621,22 @@ func (daemon *Daemon) setDefaultIsolation() error { daemon.defaultIsolation = containertypes.Isolation("hyperv") } if containertypes.Isolation(val).IsProcess() { - if system.IsWindowsClient() && !system.IsIoTCore() { - // @engine maintainers. This block should not be removed. It partially enforces licensing - // restrictions on Windows. Ping @jhowardmsft if there are concerns or PRs to change this. - return fmt.Errorf("Windows client operating systems only support Hyper-V containers") + switch system.GetProcessIsolationPolicy() { + default: + // system.PROC_ISOLATION_DEFAULT: + if system.IsWindowsClient() && !system.IsIoTCore() { + // @engine maintainers. This block should not be removed. It partially enforces licensing + // restrictions on Windows. Ping @jhowardmsft if there are concerns or PRs to change this. + return fmt.Errorf("Windows client operating systems only support Hyper-V containers") + } + case system.PROC_ISOLATION_DENY: + return fmt.Errorf("Process isolation not allowed by policy") + case system.PROC_ISOLATION_ALLOW: } daemon.defaultIsolation = containertypes.Isolation("process") } default: - return fmt.Errorf("Unrecognised exec-opt '%s'\n", key) + return fmt.Errorf("Unrecognized exec-opt '%s'\n", key) } } diff --git a/daemon/delete_test.go b/daemon/delete_test.go index 4af206d9c6591..d600917b0c188 100644 --- a/daemon/delete_test.go +++ b/daemon/delete_test.go @@ -9,8 +9,8 @@ import ( "github.com/docker/docker/api/types" containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/container" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func newDaemonWithTmpRoot(t *testing.T) (*Daemon, func()) { diff --git a/daemon/discovery/discovery_test.go b/daemon/discovery/discovery_test.go index d00e02e10b2cd..c354a2918d646 100644 --- a/daemon/discovery/discovery_test.go +++ b/daemon/discovery/discovery_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestDiscoveryOptsErrors(t *testing.T) { diff --git a/daemon/exec.go b/daemon/exec.go index 289c6bfb30e1b..f0b43d725352c 100644 --- a/daemon/exec.go +++ b/daemon/exec.go @@ -16,7 +16,7 @@ import ( "github.com/docker/docker/pkg/pools" "github.com/docker/docker/pkg/signal" "github.com/docker/docker/pkg/term" - "github.com/opencontainers/runtime-spec/specs-go" + specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -249,6 +249,9 @@ func (d *Daemon) ContainerExecStart(ctx context.Context, name string, stdin io.R ec.Lock() c.ExecCommands.Lock() systemPid, err := d.containerd.Exec(ctx, c.ID, ec.ID, p, cStdin != nil, ec.InitializeStdio) + // the exec context should be ready, or error happened. + // close the chan to notify readiness + close(ec.Started) if err != nil { c.ExecCommands.Unlock() ec.Unlock() diff --git a/daemon/exec/exec.go b/daemon/exec/exec.go index ca4e1aeb4995c..c036c46a0c8eb 100644 --- a/daemon/exec/exec.go +++ b/daemon/exec/exec.go @@ -15,6 +15,7 @@ import ( // examined both during and after completion. type Config struct { sync.Mutex + Started chan struct{} StreamConfig *stream.Config ID string Running bool @@ -40,6 +41,7 @@ func NewConfig() *Config { return &Config{ ID: stringid.GenerateNonCryptoID(), StreamConfig: stream.NewConfig(), + Started: make(chan struct{}), } } diff --git a/daemon/exec_linux_test.go b/daemon/exec_linux_test.go index 9e5496ae4b6e3..0db7f080db106 100644 --- a/daemon/exec_linux_test.go +++ b/daemon/exec_linux_test.go @@ -8,9 +8,9 @@ import ( containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/container" "github.com/docker/docker/daemon/exec" - "github.com/gotestyourself/gotestyourself/assert" "github.com/opencontainers/runc/libcontainer/apparmor" "github.com/opencontainers/runtime-spec/specs-go" + "gotest.tools/assert" ) func TestExecSetPlatformOpt(t *testing.T) { diff --git a/daemon/graphdriver/aufs/aufs.go b/daemon/graphdriver/aufs/aufs.go index 9152252770174..ac2fe26ecd5c7 100644 --- a/daemon/graphdriver/aufs/aufs.go +++ b/daemon/graphdriver/aufs/aufs.go @@ -373,7 +373,7 @@ func atomicRemove(source string) error { case os.IsExist(err): // Got error saying the target dir already exists, maybe the source doesn't exist due to a previous (failed) remove if _, e := os.Stat(source); !os.IsNotExist(e) { - return errors.Wrapf(err, "target rename dir '%s' exists but should not, this needs to be manually cleaned up") + return errors.Wrapf(err, "target rename dir %q exists but should not, this needs to be manually cleaned up", target) } default: return errors.Wrapf(err, "error preparing atomic delete") diff --git a/daemon/graphdriver/aufs/aufs_test.go b/daemon/graphdriver/aufs/aufs_test.go index 2338ad320d72e..fdc502ba65341 100644 --- a/daemon/graphdriver/aufs/aufs_test.go +++ b/daemon/graphdriver/aufs/aufs_test.go @@ -17,8 +17,8 @@ import ( "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/reexec" "github.com/docker/docker/pkg/stringid" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) var ( diff --git a/daemon/graphdriver/copy/copy_test.go b/daemon/graphdriver/copy/copy_test.go index b41348827d694..0f3b1670f741a 100644 --- a/daemon/graphdriver/copy/copy_test.go +++ b/daemon/graphdriver/copy/copy_test.go @@ -13,9 +13,9 @@ import ( "time" "github.com/docker/docker/pkg/system" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" "golang.org/x/sys/unix" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestCopy(t *testing.T) { diff --git a/daemon/graphdriver/driver_test.go b/daemon/graphdriver/driver_test.go index 4a29465f15285..e6f973c397e87 100644 --- a/daemon/graphdriver/driver_test.go +++ b/daemon/graphdriver/driver_test.go @@ -6,7 +6,7 @@ import ( "path/filepath" "testing" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) func TestIsEmptyDir(t *testing.T) { diff --git a/daemon/graphdriver/graphtest/graphbench_unix.go b/daemon/graphdriver/graphtest/graphbench_unix.go index 1b221dabeffa5..22de8d17813ad 100644 --- a/daemon/graphdriver/graphtest/graphbench_unix.go +++ b/daemon/graphdriver/graphtest/graphbench_unix.go @@ -9,7 +9,7 @@ import ( contdriver "github.com/containerd/continuity/driver" "github.com/docker/docker/pkg/stringid" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) // DriverBenchExists benchmarks calls to exist diff --git a/daemon/graphdriver/graphtest/graphtest_unix.go b/daemon/graphdriver/graphtest/graphtest_unix.go index 5ac3979752a1a..e83d0bb2ad77e 100644 --- a/daemon/graphdriver/graphtest/graphtest_unix.go +++ b/daemon/graphdriver/graphtest/graphtest_unix.go @@ -16,9 +16,9 @@ import ( "github.com/docker/docker/daemon/graphdriver/quota" "github.com/docker/docker/pkg/stringid" "github.com/docker/go-units" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" "golang.org/x/sys/unix" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) var ( diff --git a/daemon/graphdriver/graphtest/testutil_unix.go b/daemon/graphdriver/graphtest/testutil_unix.go index 3103df150d204..6871dca09a690 100644 --- a/daemon/graphdriver/graphtest/testutil_unix.go +++ b/daemon/graphdriver/graphtest/testutil_unix.go @@ -9,9 +9,9 @@ import ( contdriver "github.com/containerd/continuity/driver" "github.com/docker/docker/daemon/graphdriver" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" "golang.org/x/sys/unix" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func verifyFile(t testing.TB, path string, mode os.FileMode, uid, gid uint32) { diff --git a/daemon/graphdriver/lcow/lcow.go b/daemon/graphdriver/lcow/lcow.go index 649beccdc634c..242aa8016e8c8 100644 --- a/daemon/graphdriver/lcow/lcow.go +++ b/daemon/graphdriver/lcow/lcow.go @@ -219,21 +219,21 @@ func (d *Driver) startServiceVMIfNotRunning(id string, mvdToAdd []hcsshim.Mapped // Use the global ID if in global mode id = d.getVMID(id) - title := fmt.Sprintf("lcowdriver: startservicevmifnotrunning %s:", id) + title := "lcowdriver: startServiceVMIfNotRunning " + id // Attempt to add ID to the service vm map - logrus.Debugf("%s: Adding entry to service vm map", title) + logrus.Debugf("%s: adding entry to service vm map", title) svm, exists, err := d.serviceVms.add(id) if err != nil && err == errVMisTerminating { // VM is in the process of terminating. Wait until it's done and and then try again - logrus.Debugf("%s: VM with current ID still in the process of terminating: %s", title, id) + logrus.Debugf("%s: VM with current ID still in the process of terminating", title) if err := svm.getStopError(); err != nil { - logrus.Debugf("%s: VM %s did not stop successfully: %s", title, id, err) + logrus.Debugf("%s: VM did not stop successfully: %s", title, err) return nil, err } return d.startServiceVMIfNotRunning(id, mvdToAdd, context) } else if err != nil { - logrus.Debugf("%s: failed to add service vm to map: %s", err) + logrus.Debugf("%s: failed to add service vm to map: %s", title, err) return nil, fmt.Errorf("%s: failed to add to service vm map: %s", title, err) } @@ -248,7 +248,7 @@ func (d *Driver) startServiceVMIfNotRunning(id string, mvdToAdd []hcsshim.Mapped } // We are the first service for this id, so we need to start it - logrus.Debugf("%s: service vm doesn't exist. Now starting it up: %s", title, id) + logrus.Debugf("%s: service vm doesn't exist. Now starting it up", title) defer func() { // Signal that start has finished, passing in the error if any. @@ -261,7 +261,7 @@ func (d *Driver) startServiceVMIfNotRunning(id string, mvdToAdd []hcsshim.Mapped // Generate a default configuration if err := svm.config.GenerateDefault(d.options); err != nil { - return nil, fmt.Errorf("%s failed to generate default gogcs configuration for global svm (%s): %s", title, context, err) + return nil, fmt.Errorf("%s: failed to generate default gogcs configuration for global svm (%s): %s", title, context, err) } // For the name, we deliberately suffix if safe-mode to ensure that it doesn't @@ -277,19 +277,19 @@ func (d *Driver) startServiceVMIfNotRunning(id string, mvdToAdd []hcsshim.Mapped // and not in the process of being created by another thread. scratchTargetFile := filepath.Join(d.dataRoot, scratchDirectory, fmt.Sprintf("%s.vhdx", id)) - logrus.Debugf("%s locking cachedScratchMutex", title) + logrus.Debugf("%s: locking cachedScratchMutex", title) d.cachedScratchMutex.Lock() if _, err := os.Stat(d.cachedScratchFile); err == nil { // Make a copy of cached scratch to the scratch directory - logrus.Debugf("lcowdriver: startServiceVmIfNotRunning: (%s) cloning cached scratch for mvd", context) + logrus.Debugf("%s: (%s) cloning cached scratch for mvd", title, context) if err := client.CopyFile(d.cachedScratchFile, scratchTargetFile, true); err != nil { - logrus.Debugf("%s releasing cachedScratchMutex on err: %s", title, err) + logrus.Debugf("%s: releasing cachedScratchMutex on err: %s", title, err) d.cachedScratchMutex.Unlock() return nil, err } // Add the cached clone as a mapped virtual disk - logrus.Debugf("lcowdriver: startServiceVmIfNotRunning: (%s) adding cloned scratch as mvd", context) + logrus.Debugf("%s: (%s) adding cloned scratch as mvd", title, context) mvd := hcsshim.MappedVirtualDisk{ HostPath: scratchTargetFile, ContainerPath: toolsScratchPath, @@ -299,7 +299,7 @@ func (d *Driver) startServiceVMIfNotRunning(id string, mvdToAdd []hcsshim.Mapped svm.scratchAttached = true } - logrus.Debugf("%s releasing cachedScratchMutex", title) + logrus.Debugf("%s: releasing cachedScratchMutex", title) d.cachedScratchMutex.Unlock() // If requested to start it with a mapped virtual disk, add it now. @@ -309,7 +309,7 @@ func (d *Driver) startServiceVMIfNotRunning(id string, mvdToAdd []hcsshim.Mapped } // Start it. - logrus.Debugf("lcowdriver: startServiceVmIfNotRunning: (%s) starting %s", context, svm.config.Name) + logrus.Debugf("%s: (%s) starting %s", title, context, svm.config.Name) if err := svm.config.StartUtilityVM(); err != nil { return nil, fmt.Errorf("failed to start service utility VM (%s): %s", context, err) } @@ -317,31 +317,31 @@ func (d *Driver) startServiceVMIfNotRunning(id string, mvdToAdd []hcsshim.Mapped // defer function to terminate the VM if the next steps fail defer func() { if err != nil { - waitTerminate(svm, fmt.Sprintf("startServiceVmIfNotRunning: %s (%s)", id, context)) + waitTerminate(svm, fmt.Sprintf("%s: (%s)", title, context)) } }() // Now we have a running service VM, we can create the cached scratch file if it doesn't exist. - logrus.Debugf("%s locking cachedScratchMutex", title) + logrus.Debugf("%s: locking cachedScratchMutex", title) d.cachedScratchMutex.Lock() if _, err := os.Stat(d.cachedScratchFile); err != nil { - logrus.Debugf("%s (%s): creating an SVM scratch", title, context) + logrus.Debugf("%s: (%s) creating an SVM scratch", title, context) // Don't use svm.CreateExt4Vhdx since that only works when the service vm is setup, // but we're still in that process right now. if err := svm.config.CreateExt4Vhdx(scratchTargetFile, client.DefaultVhdxSizeGB, d.cachedScratchFile); err != nil { - logrus.Debugf("%s (%s): releasing cachedScratchMutex on error path", title, context) + logrus.Debugf("%s: (%s) releasing cachedScratchMutex on error path", title, context) d.cachedScratchMutex.Unlock() logrus.Debugf("%s: failed to create vm scratch %s: %s", title, scratchTargetFile, err) return nil, fmt.Errorf("failed to create SVM scratch VHDX (%s): %s", context, err) } } - logrus.Debugf("%s (%s): releasing cachedScratchMutex", title, context) + logrus.Debugf("%s: (%s) releasing cachedScratchMutex", title, context) d.cachedScratchMutex.Unlock() // Hot-add the scratch-space if not already attached if !svm.scratchAttached { - logrus.Debugf("lcowdriver: startServiceVmIfNotRunning: (%s) hot-adding scratch %s", context, scratchTargetFile) + logrus.Debugf("%s: (%s) hot-adding scratch %s", title, context, scratchTargetFile) if err := svm.hotAddVHDsAtStart(hcsshim.MappedVirtualDisk{ HostPath: scratchTargetFile, ContainerPath: toolsScratchPath, @@ -353,7 +353,7 @@ func (d *Driver) startServiceVMIfNotRunning(id string, mvdToAdd []hcsshim.Mapped svm.scratchAttached = true } - logrus.Debugf("lcowdriver: startServiceVmIfNotRunning: (%s) success", context) + logrus.Debugf("%s: (%s) success", title, context) return svm, nil } diff --git a/daemon/graphdriver/overlay/overlay.go b/daemon/graphdriver/overlay/overlay.go index 62dc26a87189e..0c2167f083d4f 100644 --- a/daemon/graphdriver/overlay/overlay.go +++ b/daemon/graphdriver/overlay/overlay.go @@ -366,6 +366,9 @@ func (d *Driver) dir(id string) string { // Remove cleans the directories that are created for this id. func (d *Driver) Remove(id string) error { + if id == "" { + return fmt.Errorf("refusing to remove the directories: id is empty") + } d.locker.Lock(id) defer d.locker.Unlock(id) return system.EnsureRemoveAll(d.dir(id)) diff --git a/daemon/graphdriver/overlay2/overlay.go b/daemon/graphdriver/overlay2/overlay.go index 2b52adb858de7..49b055cc03c18 100644 --- a/daemon/graphdriver/overlay2/overlay.go +++ b/daemon/graphdriver/overlay2/overlay.go @@ -513,12 +513,17 @@ func (d *Driver) getLowerDirs(id string) ([]string, error) { // Remove cleans the directories that are created for this id. func (d *Driver) Remove(id string) error { + if id == "" { + return fmt.Errorf("refusing to remove the directories: id is empty") + } d.locker.Lock(id) defer d.locker.Unlock(id) dir := d.dir(id) lid, err := ioutil.ReadFile(path.Join(dir, "link")) if err == nil { - if err := os.RemoveAll(path.Join(d.home, linkDir, string(lid))); err != nil { + if len(lid) == 0 { + logrus.WithField("storage-driver", "overlay2").Errorf("refusing to remove empty link for layer %v", id) + } else if err := os.RemoveAll(path.Join(d.home, linkDir, string(lid))); err != nil { logrus.WithField("storage-driver", "overlay2").Debugf("Failed to remove link: %v", err) } } @@ -749,16 +754,5 @@ func (d *Driver) Diff(id, parent string) (io.ReadCloser, error) { // Changes produces a list of changes between the specified layer and its // parent layer. If parent is "", then all changes will be ADD changes. func (d *Driver) Changes(id, parent string) ([]archive.Change, error) { - if useNaiveDiff(d.home) || !d.isParent(id, parent) { - return d.naiveDiff.Changes(id, parent) - } - // Overlay doesn't have snapshots, so we need to get changes from all parent - // layers. - diffPath := d.getDiffPath(id) - layers, err := d.getLowerDirs(id) - if err != nil { - return nil, err - } - - return archive.OverlayChanges(layers, diffPath) + return d.naiveDiff.Changes(id, parent) } diff --git a/daemon/graphdriver/overlay2/overlay_test.go b/daemon/graphdriver/overlay2/overlay_test.go index 4a07137ed8291..6befa7db57d4e 100644 --- a/daemon/graphdriver/overlay2/overlay_test.go +++ b/daemon/graphdriver/overlay2/overlay_test.go @@ -62,7 +62,7 @@ func TestOverlayDiffApply10Files(t *testing.T) { } func TestOverlayChanges(t *testing.T) { - skipIfNaive(t) + t.Skipf("Cannot run test with naive change algorithm") graphtest.DriverTestChanges(t, driverName) } diff --git a/daemon/graphdriver/quota/projectquota_test.go b/daemon/graphdriver/quota/projectquota_test.go index 2f1bf593debcf..aa164cc4198a1 100644 --- a/daemon/graphdriver/quota/projectquota_test.go +++ b/daemon/graphdriver/quota/projectquota_test.go @@ -10,10 +10,10 @@ import ( "path/filepath" "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/fs" "golang.org/x/sys/unix" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/fs" ) // 10MB diff --git a/daemon/images/image_builder.go b/daemon/images/image_builder.go index ca7d0fda4ace9..cdf951c6f5649 100644 --- a/daemon/images/image_builder.go +++ b/daemon/images/image_builder.go @@ -3,6 +3,7 @@ package images // import "github.com/docker/docker/daemon/images" import ( "context" "io" + "runtime" "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" @@ -14,6 +15,7 @@ import ( "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/system" "github.com/docker/docker/registry" + specs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" ) @@ -137,7 +139,7 @@ func newROLayerForImage(img *image.Image, layerStore layer.Store) (builder.ROLay } // TODO: could this use the regular daemon PullImage ? -func (i *ImageService) pullForBuilder(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer, os string) (*image.Image, error) { +func (i *ImageService) pullForBuilder(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer, platform *specs.Platform) (*image.Image, error) { ref, err := reference.ParseNormalizedNamed(name) if err != nil { return nil, err @@ -156,7 +158,7 @@ func (i *ImageService) pullForBuilder(ctx context.Context, name string, authConf pullRegistryAuth = &resolvedConfig } - if err := i.pullImageWithReference(ctx, ref, os, nil, pullRegistryAuth, output); err != nil { + if err := i.pullImageWithReference(ctx, ref, platform, nil, pullRegistryAuth, output); err != nil { return nil, err } return i.GetImage(name) @@ -166,11 +168,15 @@ func (i *ImageService) pullForBuilder(ctx context.Context, name string, authConf // Every call to GetImageAndReleasableLayer MUST call releasableLayer.Release() to prevent // leaking of layers. func (i *ImageService) GetImageAndReleasableLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (builder.Image, builder.ROLayer, error) { - if refOrID == "" { - if !system.IsOSSupported(opts.OS) { + if refOrID == "" { // ie FROM scratch + os := runtime.GOOS + if opts.Platform != nil { + os = opts.Platform.OS + } + if !system.IsOSSupported(os) { return nil, nil, system.ErrNotSupportedOperatingSystem } - layer, err := newROLayerForImage(nil, i.layerStores[opts.OS]) + layer, err := newROLayerForImage(nil, i.layerStores[os]) return nil, layer, err } @@ -189,7 +195,7 @@ func (i *ImageService) GetImageAndReleasableLayer(ctx context.Context, refOrID s } } - image, err := i.pullForBuilder(ctx, refOrID, opts.AuthConfig, opts.Output, opts.OS) + image, err := i.pullForBuilder(ctx, refOrID, opts.AuthConfig, opts.Output, opts.Platform) if err != nil { return nil, nil, err } diff --git a/daemon/images/image_pull.go b/daemon/images/image_pull.go index e25e1d0631504..3e6b4330373b9 100644 --- a/daemon/images/image_pull.go +++ b/daemon/images/image_pull.go @@ -3,8 +3,8 @@ package images // import "github.com/docker/docker/daemon/images" import ( "context" "io" - "runtime" "strings" + "time" dist "github.com/docker/distribution" "github.com/docker/distribution/reference" @@ -15,11 +15,13 @@ import ( "github.com/docker/docker/pkg/progress" "github.com/docker/docker/registry" "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/image-spec/specs-go/v1" ) // PullImage initiates a pull operation. image is the repository name to pull, and // tag may be either empty, or indicate a specific tag to pull. -func (i *ImageService) PullImage(ctx context.Context, image, tag, os string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error { +func (i *ImageService) PullImage(ctx context.Context, image, tag string, platform *specs.Platform, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error { + start := time.Now() // Special case: "pull -a" may send an image name with a // trailing :. This is ugly, but let's not break API // compatibility. @@ -44,10 +46,12 @@ func (i *ImageService) PullImage(ctx context.Context, image, tag, os string, met } } - return i.pullImageWithReference(ctx, ref, os, metaHeaders, authConfig, outStream) + err = i.pullImageWithReference(ctx, ref, platform, metaHeaders, authConfig, outStream) + imageActions.WithValues("pull").UpdateSince(start) + return err } -func (i *ImageService) pullImageWithReference(ctx context.Context, ref reference.Named, os string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error { +func (i *ImageService) pullImageWithReference(ctx context.Context, ref reference.Named, platform *specs.Platform, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error { // Include a buffer so that slow client connections don't affect // transfer performance. progressChan := make(chan progress.Progress, 100) @@ -61,11 +65,6 @@ func (i *ImageService) pullImageWithReference(ctx context.Context, ref reference close(writesDone) }() - // Default to the host OS platform in case it hasn't been populated with an explicit value. - if os == "" { - os = runtime.GOOS - } - imagePullConfig := &distribution.ImagePullConfig{ Config: distribution.Config{ MetaHeaders: metaHeaders, @@ -79,7 +78,7 @@ func (i *ImageService) pullImageWithReference(ctx context.Context, ref reference }, DownloadManager: i.downloadManager, Schema2Types: distribution.ImageTypes, - OS: os, + Platform: platform, } err := distribution.Pull(ctx, ref, imagePullConfig) diff --git a/daemon/images/image_push.go b/daemon/images/image_push.go index a1a6d7f6d1414..4c7be8d2e9749 100644 --- a/daemon/images/image_push.go +++ b/daemon/images/image_push.go @@ -3,6 +3,7 @@ package images // import "github.com/docker/docker/daemon/images" import ( "context" "io" + "time" "github.com/docker/distribution/manifest/schema2" "github.com/docker/distribution/reference" @@ -14,6 +15,7 @@ import ( // PushImage initiates a push operation on the repository named localName. func (i *ImageService) PushImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error { + start := time.Now() ref, err := reference.ParseNormalizedNamed(image) if err != nil { return err @@ -59,5 +61,6 @@ func (i *ImageService) PushImage(ctx context.Context, image, tag string, metaHea err = distribution.Push(ctx, ref, imagePushConfig) close(progressChan) <-writesDone + imageActions.WithValues("push").UpdateSince(start) return err } diff --git a/daemon/images/service.go b/daemon/images/service.go index 4af48959bffa3..263217dccd060 100644 --- a/daemon/images/service.go +++ b/daemon/images/service.go @@ -3,9 +3,11 @@ package images // import "github.com/docker/docker/daemon/images" import ( "context" "os" + "runtime" "github.com/docker/docker/container" daemonevents "github.com/docker/docker/daemon/events" + "github.com/docker/docker/distribution" "github.com/docker/docker/distribution/metadata" "github.com/docker/docker/distribution/xfer" "github.com/docker/docker/image" @@ -74,6 +76,26 @@ type ImageService struct { uploadManager *xfer.LayerUploadManager } +// DistributionServices provides daemon image storage services +type DistributionServices struct { + DownloadManager distribution.RootFSDownloadManager + V2MetadataService metadata.V2MetadataService + LayerStore layer.Store // TODO: lcow + ImageStore image.Store + ReferenceStore dockerreference.Store +} + +// DistributionServices return services controlling daemon image storage +func (i *ImageService) DistributionServices() DistributionServices { + return DistributionServices{ + DownloadManager: i.downloadManager, + V2MetadataService: metadata.NewV2MetadataService(i.distributionMetadataStore), + LayerStore: i.layerStores[runtime.GOOS], + ImageStore: i.imageStore, + ReferenceStore: i.referenceStore, + } +} + // CountImages returns the number of images stored by ImageService // called from info.go func (i *ImageService) CountImages() int { diff --git a/daemon/info.go b/daemon/info.go index 7b011fe324caf..0ed81c3b5eaeb 100644 --- a/daemon/info.go +++ b/daemon/info.go @@ -25,70 +25,9 @@ import ( // SystemInfo returns information about the host server the daemon is running on. func (daemon *Daemon) SystemInfo() (*types.Info, error) { - kernelVersion := "" - if kv, err := kernel.GetKernelVersion(); err != nil { - logrus.Warnf("Could not get kernel version: %v", err) - } else { - kernelVersion = kv.String() - } - - operatingSystem := "" - if s, err := operatingsystem.GetOperatingSystem(); err != nil { - logrus.Warnf("Could not get operating system name: %v", err) - } else { - operatingSystem = s - } - - // Don't do containerized check on Windows - if runtime.GOOS != "windows" { - if inContainer, err := operatingsystem.IsContainerized(); err != nil { - logrus.Errorf("Could not determine if daemon is containerized: %v", err) - operatingSystem += " (error determining if containerized)" - } else if inContainer { - operatingSystem += " (containerized)" - } - } - - meminfo, err := system.ReadMemInfo() - if err != nil { - logrus.Errorf("Could not read system memory info: %v", err) - meminfo = &system.MemInfo{} - } - sysInfo := sysinfo.New(true) cRunning, cPaused, cStopped := stateCtr.get() - securityOptions := []string{} - if sysInfo.AppArmor { - securityOptions = append(securityOptions, "name=apparmor") - } - if sysInfo.Seccomp && supportsSeccomp { - profile := daemon.seccompProfilePath - if profile == "" { - profile = "default" - } - securityOptions = append(securityOptions, fmt.Sprintf("name=seccomp,profile=%s", profile)) - } - if selinuxEnabled() { - securityOptions = append(securityOptions, "name=selinux") - } - rootIDs := daemon.idMappings.RootPair() - if rootIDs.UID != 0 || rootIDs.GID != 0 { - securityOptions = append(securityOptions, "name=userns") - } - - var ds [][2]string - drivers := "" - statuses := daemon.imageService.LayerStoreStatus() - for os, gd := range daemon.graphDrivers { - ds = append(ds, statuses[os]...) - drivers += gd - if len(daemon.graphDrivers) > 1 { - drivers += fmt.Sprintf(" (%s) ", os) - } - } - drivers = strings.TrimSpace(drivers) - v := &types.Info{ ID: daemon.ID, Containers: cRunning + cPaused + cStopped, @@ -96,27 +35,25 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) { ContainersPaused: cPaused, ContainersStopped: cStopped, Images: daemon.imageService.CountImages(), - Driver: drivers, - DriverStatus: ds, - Plugins: daemon.showPluginsInfo(), IPv4Forwarding: !sysInfo.IPv4ForwardingDisabled, BridgeNfIptables: !sysInfo.BridgeNFCallIPTablesDisabled, BridgeNfIP6tables: !sysInfo.BridgeNFCallIP6TablesDisabled, Debug: debug.IsEnabled(), + Name: hostName(), NFd: fileutils.GetTotalUsedFds(), NGoroutines: runtime.NumGoroutine(), SystemTime: time.Now().Format(time.RFC3339Nano), LoggingDriver: daemon.defaultLogConfig.Type, CgroupDriver: daemon.getCgroupDriver(), NEventsListener: daemon.EventsService.SubscribersCount(), - KernelVersion: kernelVersion, - OperatingSystem: operatingSystem, + KernelVersion: kernelVersion(), + OperatingSystem: operatingSystem(), IndexServerAddress: registry.IndexServer, OSType: platform.OSType, Architecture: platform.Architecture, RegistryConfig: daemon.RegistryService.ServiceConfig(), NCPU: sysinfo.NumCPU(), - MemTotal: meminfo.MemTotal, + MemTotal: memInfo().MemTotal, GenericResources: daemon.genericResources, DockerRootDir: daemon.configStore.Root, Labels: daemon.configStore.Labels, @@ -128,32 +65,21 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) { HTTPSProxy: sockets.GetProxyEnv("https_proxy"), NoProxy: sockets.GetProxyEnv("no_proxy"), LiveRestoreEnabled: daemon.configStore.LiveRestoreEnabled, - SecurityOptions: securityOptions, Isolation: daemon.defaultIsolation, } // Retrieve platform specific info - daemon.FillPlatformInfo(v, sysInfo) - - hostname := "" - if hn, err := os.Hostname(); err != nil { - logrus.Warnf("Could not get hostname: %v", err) - } else { - hostname = hn - } - v.Name = hostname + daemon.fillPlatformInfo(v, sysInfo) + daemon.fillDriverInfo(v) + daemon.fillPluginsInfo(v) + daemon.fillSecurityOptions(v, sysInfo) return v, nil } // SystemVersion returns version information about the daemon. func (daemon *Daemon) SystemVersion() types.Version { - kernelVersion := "" - if kv, err := kernel.GetKernelVersion(); err != nil { - logrus.Warnf("Could not get kernel version: %v", err) - } else { - kernelVersion = kv.String() - } + kernelVersion := kernelVersion() v := types.Version{ Components: []types.ComponentVersion{ @@ -192,15 +118,100 @@ func (daemon *Daemon) SystemVersion() types.Version { return v } -func (daemon *Daemon) showPluginsInfo() types.PluginsInfo { - var pluginsInfo types.PluginsInfo +func (daemon *Daemon) fillDriverInfo(v *types.Info) { + var ds [][2]string + drivers := "" + statuses := daemon.imageService.LayerStoreStatus() + for os, gd := range daemon.graphDrivers { + ds = append(ds, statuses[os]...) + drivers += gd + if len(daemon.graphDrivers) > 1 { + drivers += fmt.Sprintf(" (%s) ", os) + } + } + drivers = strings.TrimSpace(drivers) - pluginsInfo.Volume = daemon.volumes.GetDriverList() - pluginsInfo.Network = daemon.GetNetworkDriverList() - // The authorization plugins are returned in the order they are - // used as they constitute a request/response modification chain. - pluginsInfo.Authorization = daemon.configStore.AuthorizationPlugins - pluginsInfo.Log = logger.ListDrivers() + v.Driver = drivers + v.DriverStatus = ds +} - return pluginsInfo +func (daemon *Daemon) fillPluginsInfo(v *types.Info) { + v.Plugins = types.PluginsInfo{ + Volume: daemon.volumes.GetDriverList(), + Network: daemon.GetNetworkDriverList(), + + // The authorization plugins are returned in the order they are + // used as they constitute a request/response modification chain. + Authorization: daemon.configStore.AuthorizationPlugins, + Log: logger.ListDrivers(), + } +} + +func (daemon *Daemon) fillSecurityOptions(v *types.Info, sysInfo *sysinfo.SysInfo) { + var securityOptions []string + if sysInfo.AppArmor { + securityOptions = append(securityOptions, "name=apparmor") + } + if sysInfo.Seccomp && supportsSeccomp { + profile := daemon.seccompProfilePath + if profile == "" { + profile = "default" + } + securityOptions = append(securityOptions, fmt.Sprintf("name=seccomp,profile=%s", profile)) + } + if selinuxEnabled() { + securityOptions = append(securityOptions, "name=selinux") + } + if rootIDs := daemon.idMappings.RootPair(); rootIDs.UID != 0 || rootIDs.GID != 0 { + securityOptions = append(securityOptions, "name=userns") + } + v.SecurityOptions = securityOptions +} + +func hostName() string { + hostname := "" + if hn, err := os.Hostname(); err != nil { + logrus.Warnf("Could not get hostname: %v", err) + } else { + hostname = hn + } + return hostname +} + +func kernelVersion() string { + kernelVersion := "" + if kv, err := kernel.GetKernelVersion(); err != nil { + logrus.Warnf("Could not get kernel version: %v", err) + } else { + kernelVersion = kv.String() + } + return kernelVersion +} + +func memInfo() *system.MemInfo { + memInfo, err := system.ReadMemInfo() + if err != nil { + logrus.Errorf("Could not read system memory info: %v", err) + memInfo = &system.MemInfo{} + } + return memInfo +} + +func operatingSystem() string { + operatingSystem := "" + if s, err := operatingsystem.GetOperatingSystem(); err != nil { + logrus.Warnf("Could not get operating system name: %v", err) + } else { + operatingSystem = s + } + // Don't do containerized check on Windows + if runtime.GOOS != "windows" { + if inContainer, err := operatingsystem.IsContainerized(); err != nil { + logrus.Errorf("Could not determine if daemon is containerized: %v", err) + operatingSystem += " (error determining if containerized)" + } else if inContainer { + operatingSystem += " (containerized)" + } + } + return operatingSystem } diff --git a/daemon/info_unix.go b/daemon/info_unix.go index 56be9c06fb81a..864e8816a9c95 100644 --- a/daemon/info_unix.go +++ b/daemon/info_unix.go @@ -14,8 +14,8 @@ import ( "github.com/sirupsen/logrus" ) -// FillPlatformInfo fills the platform related info. -func (daemon *Daemon) FillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo) { +// fillPlatformInfo fills the platform related info. +func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo) { v.MemoryLimit = sysInfo.MemoryLimit v.SwapLimit = sysInfo.SwapLimit v.KernelMemory = sysInfo.KernelMemory diff --git a/daemon/info_unix_test.go b/daemon/info_unix_test.go index 7ff1009326513..a5a4e06f98451 100644 --- a/daemon/info_unix_test.go +++ b/daemon/info_unix_test.go @@ -7,8 +7,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/dockerversion" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestParseInitVersion(t *testing.T) { diff --git a/daemon/info_windows.go b/daemon/info_windows.go index e452369fc8e34..bf979714793a6 100644 --- a/daemon/info_windows.go +++ b/daemon/info_windows.go @@ -5,6 +5,6 @@ import ( "github.com/docker/docker/pkg/sysinfo" ) -// FillPlatformInfo fills the platform related info. -func (daemon *Daemon) FillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo) { +// fillPlatformInfo fills the platform related info. +func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo) { } diff --git a/daemon/inspect_test.go b/daemon/inspect_test.go index d1ad5b0e0a45c..f402a7af99b6e 100644 --- a/daemon/inspect_test.go +++ b/daemon/inspect_test.go @@ -7,8 +7,8 @@ import ( "github.com/docker/docker/container" "github.com/docker/docker/daemon/config" "github.com/docker/docker/daemon/exec" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestGetInspectData(t *testing.T) { diff --git a/daemon/kill.go b/daemon/kill.go index 5034c4df393ca..3e6457e9522dd 100644 --- a/daemon/kill.go +++ b/daemon/kill.go @@ -108,7 +108,7 @@ func (daemon *Daemon) killWithSignal(container *containerpkg.Container, sig int) if unpause { // above kill signal will be sent once resume is finished if err := daemon.containerd.Resume(context.Background(), container.ID); err != nil { - logrus.Warn("Cannot unpause container %s: %s", container.ID, err) + logrus.Warnf("Cannot unpause container %s: %s", container.ID, err) } } diff --git a/daemon/list_test.go b/daemon/list_test.go index d35eac3cd6970..3be510d13d940 100644 --- a/daemon/list_test.go +++ b/daemon/list_test.go @@ -6,8 +6,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/container" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestListInvalidFilter(t *testing.T) { diff --git a/daemon/logger/adapter_test.go b/daemon/logger/adapter_test.go index e9143928b4a11..f47e711c892ea 100644 --- a/daemon/logger/adapter_test.go +++ b/daemon/logger/adapter_test.go @@ -9,8 +9,8 @@ import ( "github.com/docker/docker/api/types/plugins/logdriver" protoio "github.com/gogo/protobuf/io" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) // mockLoggingPlugin implements the loggingPlugin interface for testing purposes diff --git a/daemon/logger/awslogs/cloudwatchlogs.go b/daemon/logger/awslogs/cloudwatchlogs.go index 3d6466f09d099..0f989af379838 100644 --- a/daemon/logger/awslogs/cloudwatchlogs.go +++ b/daemon/logger/awslogs/cloudwatchlogs.go @@ -29,6 +29,7 @@ import ( const ( name = "awslogs" regionKey = "awslogs-region" + endpointKey = "awslogs-endpoint" regionEnvKey = "AWS_REGION" logGroupKey = "awslogs-group" logStreamKey = "awslogs-stream" @@ -111,11 +112,11 @@ type eventBatch struct { // New creates an awslogs logger using the configuration passed in on the // context. Supported context configuration variables are awslogs-region, -// awslogs-group, awslogs-stream, awslogs-create-group, awslogs-multiline-pattern -// and awslogs-datetime-format. When available, configuration is -// also taken from environment variables AWS_REGION, AWS_ACCESS_KEY_ID, -// AWS_SECRET_ACCESS_KEY, the shared credentials file (~/.aws/credentials), and -// the EC2 Instance Metadata Service. +// awslogs-endpoint, awslogs-group, awslogs-stream, awslogs-create-group, +// awslogs-multiline-pattern and awslogs-datetime-format. +// When available, configuration is also taken from environment variables +// AWS_REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, the shared credentials +// file (~/.aws/credentials), and the EC2 Instance Metadata Service. func New(info logger.Info) (logger.Logger, error) { logGroupName := info.Config[logGroupKey] logStreamName, err := loggerutils.ParseLogTag(info, "{{.FullID}}") @@ -262,13 +263,16 @@ var newSDKEndpoint = credentialsEndpoint // User-Agent string and automatic region detection using the EC2 Instance // Metadata Service when region is otherwise unspecified. func newAWSLogsClient(info logger.Info) (api, error) { - var region *string + var region, endpoint *string if os.Getenv(regionEnvKey) != "" { region = aws.String(os.Getenv(regionEnvKey)) } if info.Config[regionKey] != "" { region = aws.String(info.Config[regionKey]) } + if info.Config[endpointKey] != "" { + endpoint = aws.String(info.Config[endpointKey]) + } if region == nil || *region == "" { logrus.Info("Trying to get region from EC2 Metadata") ec2MetadataClient := newRegionFinder() @@ -290,6 +294,11 @@ func newAWSLogsClient(info logger.Info) (api, error) { // attach region to cloudwatchlogs config sess.Config.Region = region + // attach endpoint to cloudwatchlogs config + if endpoint != nil { + sess.Config.Endpoint = endpoint + } + if uri, ok := info.Config[credentialsEndpointKey]; ok { logrus.Debugf("Trying to get credentials from awslogs-credentials-endpoint") @@ -606,7 +615,7 @@ func (l *logStream) putLogEvents(events []*cloudwatchlogs.InputLogEvent, sequenc return resp.NextSequenceToken, nil } -// ValidateLogOpt looks for awslogs-specific log options awslogs-region, +// ValidateLogOpt looks for awslogs-specific log options awslogs-region, awslogs-endpoint // awslogs-group, awslogs-stream, awslogs-create-group, awslogs-datetime-format, // awslogs-multiline-pattern func ValidateLogOpt(cfg map[string]string) error { @@ -616,6 +625,7 @@ func ValidateLogOpt(cfg map[string]string) error { case logStreamKey: case logCreateGroupKey: case regionKey: + case endpointKey: case tagKey: case datetimeFormatKey: case multilinePatternKey: diff --git a/daemon/logger/awslogs/cloudwatchlogs_test.go b/daemon/logger/awslogs/cloudwatchlogs_test.go index b8e49055e8332..172be51a307f5 100644 --- a/daemon/logger/awslogs/cloudwatchlogs_test.go +++ b/daemon/logger/awslogs/cloudwatchlogs_test.go @@ -21,8 +21,8 @@ import ( "github.com/docker/docker/daemon/logger" "github.com/docker/docker/daemon/logger/loggerutils" "github.com/docker/docker/dockerversion" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) const ( @@ -67,13 +67,11 @@ func TestNewAWSLogsClientUserAgentHandler(t *testing.T) { } client, err := newAWSLogsClient(info) - if err != nil { - t.Fatal(err) - } + assert.NilError(t, err) + realClient, ok := client.(*cloudwatchlogs.CloudWatchLogs) - if !ok { - t.Fatal("Could not cast client to cloudwatchlogs.CloudWatchLogs") - } + assert.Check(t, ok, "Could not cast client to cloudwatchlogs.CloudWatchLogs") + buildHandlerList := realClient.Handlers.Build request := &request.Request{ HTTPRequest: &http.Request{ @@ -90,6 +88,26 @@ func TestNewAWSLogsClientUserAgentHandler(t *testing.T) { } } +func TestNewAWSLogsClientAWSLogsEndpoint(t *testing.T) { + endpoint := "mock-endpoint" + info := logger.Info{ + Config: map[string]string{ + regionKey: "us-east-1", + endpointKey: endpoint, + }, + } + + client, err := newAWSLogsClient(info) + assert.NilError(t, err) + + realClient, ok := client.(*cloudwatchlogs.CloudWatchLogs) + assert.Check(t, ok, "Could not cast client to cloudwatchlogs.CloudWatchLogs") + + endpointWithScheme := realClient.Endpoint + expectedEndpointWithScheme := "https://" + endpoint + assert.Equal(t, endpointWithScheme, expectedEndpointWithScheme, "Wrong endpoint") +} + func TestNewAWSLogsClientRegionDetect(t *testing.T) { info := logger.Info{ Config: map[string]string{}, @@ -104,9 +122,7 @@ func TestNewAWSLogsClientRegionDetect(t *testing.T) { } _, err := newAWSLogsClient(info) - if err != nil { - t.Fatal(err) - } + assert.NilError(t, err) } func TestCreateSuccess(t *testing.T) { @@ -196,9 +212,7 @@ func TestCreateAlreadyExists(t *testing.T) { err := stream.create() - if err != nil { - t.Fatal("Expected nil err") - } + assert.NilError(t, err) } func TestLogClosed(t *testing.T) { @@ -242,9 +256,8 @@ func TestLogBlocking(t *testing.T) { } select { case err := <-errorCh: - if err != nil { - t.Fatal(err) - } + assert.NilError(t, err) + case <-time.After(30 * time.Second): t.Fatal("timed out waiting for read") } @@ -258,9 +271,7 @@ func TestLogNonBlockingBufferEmpty(t *testing.T) { logNonBlocking: true, } err := stream.Log(&logger.Message{}) - if err != nil { - t.Fatal(err) - } + assert.NilError(t, err) } func TestLogNonBlockingBufferFull(t *testing.T) { @@ -1246,9 +1257,7 @@ func TestCreateTagSuccess(t *testing.T) { err := stream.create() - if err != nil { - t.Errorf("Received unexpected err: %v\n", err) - } + assert.NilError(t, err) argument := <-mockClient.createLogStreamArgument if *argument.LogStreamName != "test-container/container-abcdefghijklmnopqrstuvwxyz01234567890" { @@ -1340,7 +1349,6 @@ func TestNewAWSLogsClientCredentialEnvironmentVariable(t *testing.T) { assert.Check(t, is.Equal(expectedAccessKeyID, creds.AccessKeyID)) assert.Check(t, is.Equal(expectedSecretAccessKey, creds.SecretAccessKey)) - } func TestNewAWSLogsClientCredentialSharedFile(t *testing.T) { diff --git a/daemon/logger/jsonfilelog/jsonfilelog_test.go b/daemon/logger/jsonfilelog/jsonfilelog_test.go index 2becd694b0d2f..22bbcf2eb7a16 100644 --- a/daemon/logger/jsonfilelog/jsonfilelog_test.go +++ b/daemon/logger/jsonfilelog/jsonfilelog_test.go @@ -14,9 +14,9 @@ import ( "github.com/docker/docker/daemon/logger" "github.com/docker/docker/daemon/logger/jsonfilelog/jsonlog" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/fs" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/fs" ) func TestJSONFileLogger(t *testing.T) { diff --git a/daemon/logger/jsonfilelog/jsonlog/jsonlogbytes_test.go b/daemon/logger/jsonfilelog/jsonlog/jsonlogbytes_test.go index b3bfe6b18c853..d268db4df0c97 100644 --- a/daemon/logger/jsonfilelog/jsonlog/jsonlogbytes_test.go +++ b/daemon/logger/jsonfilelog/jsonlog/jsonlogbytes_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) func TestJSONLogsMarshalJSONBuf(t *testing.T) { diff --git a/daemon/logger/jsonfilelog/jsonlog/time_marshalling_test.go b/daemon/logger/jsonfilelog/jsonlog/time_marshalling_test.go index 3cfdcc33db9bd..b3959b0467a50 100644 --- a/daemon/logger/jsonfilelog/jsonlog/time_marshalling_test.go +++ b/daemon/logger/jsonfilelog/jsonlog/time_marshalling_test.go @@ -4,8 +4,8 @@ import ( "testing" "time" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestFastTimeMarshalJSONWithInvalidYear(t *testing.T) { diff --git a/daemon/logger/jsonfilelog/read_test.go b/daemon/logger/jsonfilelog/read_test.go index f89fabfe1c682..cad8003e5efd3 100644 --- a/daemon/logger/jsonfilelog/read_test.go +++ b/daemon/logger/jsonfilelog/read_test.go @@ -6,8 +6,8 @@ import ( "time" "github.com/docker/docker/daemon/logger" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/fs" + "gotest.tools/assert" + "gotest.tools/fs" ) func BenchmarkJSONFileLoggerReadLogs(b *testing.B) { diff --git a/daemon/logger/loggerutils/logfile.go b/daemon/logger/loggerutils/logfile.go index 6e3cda8648d9e..d13db5ec48881 100644 --- a/daemon/logger/loggerutils/logfile.go +++ b/daemon/logger/loggerutils/logfile.go @@ -332,7 +332,7 @@ func (w *LogFile) ReadLogs(config logger.ReadConfig, watcher *logger.LogWatcher) if strings.HasSuffix(fileName, tmpLogfileSuffix) { err := w.filesRefCounter.Dereference(fileName) if err != nil { - logrus.Errorf("Failed to dereference the log file %q: %v", fileName, err) + logrus.Errorf("Failed to dereference log file %q: %v", fileName, err) } } } @@ -364,7 +364,7 @@ func (w *LogFile) openRotatedFiles(config logger.ReadConfig) (files []*os.File, if strings.HasSuffix(f.Name(), tmpLogfileSuffix) { err := os.Remove(f.Name()) if err != nil && !os.IsNotExist(err) { - logrus.Warningf("Failed to remove the logfile %q: %v", f.Name, err) + logrus.Warnf("Failed to remove logfile: %v", err) } } } @@ -436,7 +436,7 @@ func decompressfile(fileName, destFileName string, since time.Time) (*os.File, e rs.Close() rErr := os.Remove(rs.Name()) if rErr != nil && !os.IsNotExist(rErr) { - logrus.Errorf("Failed to remove the logfile %q: %v", rs.Name(), rErr) + logrus.Errorf("Failed to remove logfile: %v", rErr) } return nil, errors.Wrap(err, "error while copying decompressed log stream to file") } @@ -561,7 +561,7 @@ func followLogs(f *os.File, logWatcher *logger.LogWatcher, notifyRotate chan int } return errRetry case err := <-fileWatcher.Errors(): - logrus.Debug("logger got error watching file: %v", err) + logrus.Debugf("logger got error watching file: %v", err) // Something happened, let's try and stay alive and create a new watcher if retries <= 5 { fileWatcher.Close() diff --git a/daemon/logger/splunk/splunk_test.go b/daemon/logger/splunk/splunk_test.go index 62895a6dd68cf..cfb83e80d1c55 100644 --- a/daemon/logger/splunk/splunk_test.go +++ b/daemon/logger/splunk/splunk_test.go @@ -11,8 +11,8 @@ import ( "time" "github.com/docker/docker/daemon/logger" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/env" + "gotest.tools/assert" + "gotest.tools/env" ) // Validate options diff --git a/daemon/logger/templates/templates_test.go b/daemon/logger/templates/templates_test.go index b767037477a09..25e7c88750781 100644 --- a/daemon/logger/templates/templates_test.go +++ b/daemon/logger/templates/templates_test.go @@ -4,8 +4,8 @@ import ( "bytes" "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestNewParse(t *testing.T) { diff --git a/daemon/monitor.go b/daemon/monitor.go index 5e740dd4feee3..9b4452d7efc0b 100644 --- a/daemon/monitor.go +++ b/daemon/monitor.go @@ -138,7 +138,7 @@ func (daemon *Daemon) ProcessEvent(id string, e libcontainerd.EventType, ei libc "container": c.ID, "exec-id": ei.ProcessID, "exec-pid": ei.Pid, - }).Warnf("Ignoring Exit Event, no such exec command found") + }).Warn("Ignoring Exit Event, no such exec command found") } case libcontainerd.EventStart: c.Lock() diff --git a/daemon/network.go b/daemon/network.go index 4263409be825b..c524f01e7fd37 100644 --- a/daemon/network.go +++ b/daemon/network.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "net" - "runtime" "sort" "strconv" "strings" @@ -12,6 +11,7 @@ import ( "github.com/docker/docker/api/types" containertypes "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/network" "github.com/docker/docker/container" clustertypes "github.com/docker/docker/daemon/cluster/provider" @@ -26,6 +26,7 @@ import ( "github.com/docker/libnetwork/driverapi" "github.com/docker/libnetwork/ipamapi" "github.com/docker/libnetwork/netlabel" + "github.com/docker/libnetwork/networkdb" "github.com/docker/libnetwork/options" networktypes "github.com/docker/libnetwork/types" "github.com/pkg/errors" @@ -89,7 +90,7 @@ func (daemon *Daemon) FindNetwork(term string) (libnetwork.Network, error) { func (daemon *Daemon) GetNetworkByID(id string) (libnetwork.Network, error) { c := daemon.netController if c == nil { - return nil, libnetwork.ErrNoSuchNetwork(id) + return nil, errors.Wrap(libnetwork.ErrNoSuchNetwork(id), "netcontroller is nil") } return c.NetworkByID(id) } @@ -230,9 +231,7 @@ func (daemon *Daemon) releaseIngress(id string) { return } - daemon.deleteLoadBalancerSandbox(n) - - if err := n.Delete(); err != nil { + if err := n.Delete(libnetwork.NetworkDeleteOptionRemoveLB); err != nil { logrus.Errorf("Failed to delete ingress network %s: %v", n.ID(), err) return } @@ -349,7 +348,7 @@ func (daemon *Daemon) createNetwork(create types.NetworkCreateRequest, id string nwOptions = append(nwOptions, libnetwork.NetworkOptionConfigFrom(create.ConfigFrom.Network)) } - if agent && driver == "overlay" && (create.Ingress || runtime.GOOS == "windows") { + if agent && driver == "overlay" { nodeIP, exists := daemon.GetAttachmentStore().GetIPForNetwork(id) if !exists { return nil, fmt.Errorf("Failed to find a load balancer IP to use for network: %v", id) @@ -507,42 +506,11 @@ func (daemon *Daemon) DeleteManagedNetwork(networkID string) error { func (daemon *Daemon) DeleteNetwork(networkID string) error { n, err := daemon.GetNetworkByID(networkID) if err != nil { - return err + return errors.Wrap(err, "could not find network by ID") } return daemon.deleteNetwork(n, false) } -func (daemon *Daemon) deleteLoadBalancerSandbox(n libnetwork.Network) { - controller := daemon.netController - - //The only endpoint left should be the LB endpoint (nw.Name() + "-endpoint") - endpoints := n.Endpoints() - if len(endpoints) == 1 { - sandboxName := n.Name() + "-sbox" - - info := endpoints[0].Info() - if info != nil { - sb := info.Sandbox() - if sb != nil { - if err := sb.DisableService(); err != nil { - logrus.Warnf("Failed to disable service on sandbox %s: %v", sandboxName, err) - //Ignore error and attempt to delete the load balancer endpoint - } - } - } - - if err := endpoints[0].Delete(true); err != nil { - logrus.Warnf("Failed to delete endpoint %s (%s) in %s: %v", endpoints[0].Name(), endpoints[0].ID(), sandboxName, err) - //Ignore error and attempt to delete the sandbox. - } - - if err := controller.SandboxDestroy(sandboxName); err != nil { - logrus.Warnf("Failed to delete %s sandbox: %v", sandboxName, err) - //Ignore error and attempt to delete the network. - } - } -} - func (daemon *Daemon) deleteNetwork(nw libnetwork.Network, dynamic bool) error { if runconfig.IsPreDefinedNetwork(nw.Name()) && !dynamic { err := fmt.Errorf("%s is a pre-defined network and cannot be removed", nw.Name()) @@ -560,7 +528,7 @@ func (daemon *Daemon) deleteNetwork(nw libnetwork.Network, dynamic bool) error { } if err := nw.Delete(); err != nil { - return err + return errors.Wrap(err, "error while removing network") } // If this is not a configuration only network, we need to @@ -576,8 +544,212 @@ func (daemon *Daemon) deleteNetwork(nw libnetwork.Network, dynamic bool) error { } // GetNetworks returns a list of all networks -func (daemon *Daemon) GetNetworks() []libnetwork.Network { - return daemon.getAllNetworks() +func (daemon *Daemon) GetNetworks(filter filters.Args, config types.NetworkListConfig) ([]types.NetworkResource, error) { + networks := daemon.getAllNetworks() + + list := make([]types.NetworkResource, 0, len(networks)) + var idx map[string]libnetwork.Network + if config.Detailed { + idx = make(map[string]libnetwork.Network) + } + + for _, n := range networks { + nr := buildNetworkResource(n) + list = append(list, nr) + if config.Detailed { + idx[nr.ID] = n + } + } + + var err error + list, err = internalnetwork.FilterNetworks(list, filter) + if err != nil { + return nil, err + } + + if config.Detailed { + for i, n := range list { + np := &n + buildDetailedNetworkResources(np, idx[n.ID], config.Verbose) + list[i] = *np + } + } + + return list, nil +} + +func buildNetworkResource(nw libnetwork.Network) types.NetworkResource { + r := types.NetworkResource{} + if nw == nil { + return r + } + + info := nw.Info() + r.Name = nw.Name() + r.ID = nw.ID() + r.Created = info.Created() + r.Scope = info.Scope() + r.Driver = nw.Type() + r.EnableIPv6 = info.IPv6Enabled() + r.Internal = info.Internal() + r.Attachable = info.Attachable() + r.Ingress = info.Ingress() + r.Options = info.DriverOptions() + r.Containers = make(map[string]types.EndpointResource) + buildIpamResources(&r, info) + r.Labels = info.Labels() + r.ConfigOnly = info.ConfigOnly() + + if cn := info.ConfigFrom(); cn != "" { + r.ConfigFrom = network.ConfigReference{Network: cn} + } + + peers := info.Peers() + if len(peers) != 0 { + r.Peers = buildPeerInfoResources(peers) + } + + return r +} + +func buildDetailedNetworkResources(r *types.NetworkResource, nw libnetwork.Network, verbose bool) { + if nw == nil { + return + } + + epl := nw.Endpoints() + for _, e := range epl { + ei := e.Info() + if ei == nil { + continue + } + sb := ei.Sandbox() + tmpID := e.ID() + key := "ep-" + tmpID + if sb != nil { + key = sb.ContainerID() + } + + r.Containers[key] = buildEndpointResource(tmpID, e.Name(), ei) + } + if !verbose { + return + } + services := nw.Info().Services() + r.Services = make(map[string]network.ServiceInfo) + for name, service := range services { + tasks := []network.Task{} + for _, t := range service.Tasks { + tasks = append(tasks, network.Task{ + Name: t.Name, + EndpointID: t.EndpointID, + EndpointIP: t.EndpointIP, + Info: t.Info, + }) + } + r.Services[name] = network.ServiceInfo{ + VIP: service.VIP, + Ports: service.Ports, + Tasks: tasks, + LocalLBIndex: service.LocalLBIndex, + } + } +} + +func buildPeerInfoResources(peers []networkdb.PeerInfo) []network.PeerInfo { + peerInfo := make([]network.PeerInfo, 0, len(peers)) + for _, peer := range peers { + peerInfo = append(peerInfo, network.PeerInfo{ + Name: peer.Name, + IP: peer.IP, + }) + } + return peerInfo +} + +func buildIpamResources(r *types.NetworkResource, nwInfo libnetwork.NetworkInfo) { + id, opts, ipv4conf, ipv6conf := nwInfo.IpamConfig() + + ipv4Info, ipv6Info := nwInfo.IpamInfo() + + r.IPAM.Driver = id + + r.IPAM.Options = opts + + r.IPAM.Config = []network.IPAMConfig{} + for _, ip4 := range ipv4conf { + if ip4.PreferredPool == "" { + continue + } + iData := network.IPAMConfig{} + iData.Subnet = ip4.PreferredPool + iData.IPRange = ip4.SubPool + iData.Gateway = ip4.Gateway + iData.AuxAddress = ip4.AuxAddresses + r.IPAM.Config = append(r.IPAM.Config, iData) + } + + if len(r.IPAM.Config) == 0 { + for _, ip4Info := range ipv4Info { + iData := network.IPAMConfig{} + iData.Subnet = ip4Info.IPAMData.Pool.String() + if ip4Info.IPAMData.Gateway != nil { + iData.Gateway = ip4Info.IPAMData.Gateway.IP.String() + } + r.IPAM.Config = append(r.IPAM.Config, iData) + } + } + + hasIpv6Conf := false + for _, ip6 := range ipv6conf { + if ip6.PreferredPool == "" { + continue + } + hasIpv6Conf = true + iData := network.IPAMConfig{} + iData.Subnet = ip6.PreferredPool + iData.IPRange = ip6.SubPool + iData.Gateway = ip6.Gateway + iData.AuxAddress = ip6.AuxAddresses + r.IPAM.Config = append(r.IPAM.Config, iData) + } + + if !hasIpv6Conf { + for _, ip6Info := range ipv6Info { + if ip6Info.IPAMData.Pool == nil { + continue + } + iData := network.IPAMConfig{} + iData.Subnet = ip6Info.IPAMData.Pool.String() + iData.Gateway = ip6Info.IPAMData.Gateway.String() + r.IPAM.Config = append(r.IPAM.Config, iData) + } + } +} + +func buildEndpointResource(id string, name string, info libnetwork.EndpointInfo) types.EndpointResource { + er := types.EndpointResource{} + + er.EndpointID = id + er.Name = name + ei := info + if ei == nil { + return er + } + + if iface := ei.Iface(); iface != nil { + if mac := iface.MacAddress(); mac != nil { + er.MacAddress = mac.String() + } + if ip := iface.Address(); ip != nil && len(ip.IP) > 0 { + er.IPv4Address = ip.String() + } + + if ipv6 := iface.AddressIPv6(); ipv6 != nil && len(ipv6.IP) > 0 { + er.IPv6Address = ipv6.String() + } + } + return er } // clearAttachableNetworks removes the attachable networks diff --git a/daemon/network/filter.go b/daemon/network/filter.go new file mode 100644 index 0000000000000..01a7d2dd81451 --- /dev/null +++ b/daemon/network/filter.go @@ -0,0 +1,92 @@ +package network // import "github.com/docker/docker/daemon/network" + +import ( + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/runconfig" + "github.com/pkg/errors" +) + +// FilterNetworks filters network list according to user specified filter +// and returns user chosen networks +func FilterNetworks(nws []types.NetworkResource, filter filters.Args) ([]types.NetworkResource, error) { + // if filter is empty, return original network list + if filter.Len() == 0 { + return nws, nil + } + + displayNet := nws[:0] + for _, nw := range nws { + if filter.Contains("driver") { + if !filter.ExactMatch("driver", nw.Driver) { + continue + } + } + if filter.Contains("name") { + if !filter.Match("name", nw.Name) { + continue + } + } + if filter.Contains("id") { + if !filter.Match("id", nw.ID) { + continue + } + } + if filter.Contains("label") { + if !filter.MatchKVList("label", nw.Labels) { + continue + } + } + if filter.Contains("scope") { + if !filter.ExactMatch("scope", nw.Scope) { + continue + } + } + + if filter.Contains("idOrName") { + if !filter.Match("name", nw.Name) && !filter.Match("id", nw.Name) { + continue + } + } + displayNet = append(displayNet, nw) + } + + if filter.Contains("type") { + typeNet := []types.NetworkResource{} + errFilter := filter.WalkValues("type", func(fval string) error { + passList, err := filterNetworkByType(displayNet, fval) + if err != nil { + return err + } + typeNet = append(typeNet, passList...) + return nil + }) + if errFilter != nil { + return nil, errFilter + } + displayNet = typeNet + } + + return displayNet, nil +} + +func filterNetworkByType(nws []types.NetworkResource, netType string) ([]types.NetworkResource, error) { + retNws := []types.NetworkResource{} + switch netType { + case "builtin": + for _, nw := range nws { + if runconfig.IsPreDefinedNetwork(nw.Name) { + retNws = append(retNws, nw) + } + } + case "custom": + for _, nw := range nws { + if !runconfig.IsPreDefinedNetwork(nw.Name) { + retNws = append(retNws, nw) + } + } + default: + return nil, errors.Errorf("invalid filter: 'type'='%s'", netType) + } + return retNws, nil +} diff --git a/api/server/router/network/filter_test.go b/daemon/network/filter_test.go similarity index 64% rename from api/server/router/network/filter_test.go rename to daemon/network/filter_test.go index 8a84fd16feb78..d3180a05a93f8 100644 --- a/api/server/router/network/filter_test.go +++ b/daemon/network/filter_test.go @@ -1,6 +1,6 @@ // +build !windows -package network // import "github.com/docker/docker/api/server/router/network" +package network // import "github.com/docker/docker/daemon/network" import ( "strings" @@ -75,75 +75,89 @@ func TestFilterNetworks(t *testing.T) { filter filters.Args resultCount int err string + name string }{ { filter: bridgeDriverFilters, resultCount: 1, err: "", + name: "bridge driver filters", }, { filter: overlayDriverFilters, resultCount: 1, err: "", + name: "overlay driver filters", }, { filter: nonameDriverFilters, resultCount: 0, err: "", + name: "no name driver filters", }, { filter: customDriverFilters, resultCount: 3, err: "", + name: "custom driver filters", }, { filter: builtinDriverFilters, resultCount: 3, err: "", + name: "builtin driver filters", }, { filter: invalidDriverFilters, resultCount: 0, - err: "Invalid filter: 'type'='invalid'", + err: "invalid filter: 'type'='invalid'", + name: "invalid driver filters", }, { filter: localScopeFilters, resultCount: 4, err: "", + name: "local scope filters", }, { filter: swarmScopeFilters, resultCount: 1, err: "", + name: "swarm scope filters", }, { filter: globalScopeFilters, resultCount: 1, err: "", + name: "global scope filters", }, } for _, testCase := range testCases { - result, err := filterNetworks(networks, testCase.filter) - if testCase.err != "" { - if err == nil { - t.Fatalf("expect error '%s', got no error", testCase.err) - - } else if !strings.Contains(err.Error(), testCase.err) { - t.Fatalf("expect error '%s', got '%s'", testCase.err, err) - } - } else { - if err != nil { - t.Fatalf("expect no error, got error '%s'", err) - } - // Make sure result is not nil - if result == nil { - t.Fatal("filterNetworks should not return nil") - } - - if len(result) != testCase.resultCount { - t.Fatalf("expect '%d' networks, got '%d' networks", testCase.resultCount, len(result)) + t.Run(testCase.name, func(t *testing.T) { + ls := make([]types.NetworkResource, 0, len(networks)) + ls = append(ls, networks...) + result, err := FilterNetworks(ls, testCase.filter) + if testCase.err != "" { + if err == nil { + t.Fatalf("expect error '%s', got no error", testCase.err) + + } else if !strings.Contains(err.Error(), testCase.err) { + t.Fatalf("expect error '%s', got '%s'", testCase.err, err) + } + } else { + if err != nil { + t.Fatalf("expect no error, got error '%s'", err) + } + // Make sure result is not nil + if result == nil { + t.Fatal("filterNetworks should not return nil") + } + + if len(result) != testCase.resultCount { + t.Fatalf("expect '%d' networks, got '%d' networks", testCase.resultCount, len(result)) + } } - } + }) } } diff --git a/daemon/oci.go b/daemon/oci.go new file mode 100644 index 0000000000000..52050e24faee7 --- /dev/null +++ b/daemon/oci.go @@ -0,0 +1,78 @@ +package daemon // import "github.com/docker/docker/daemon" + +import ( + "fmt" + "regexp" + "strconv" + + "github.com/docker/docker/container" + "github.com/docker/docker/daemon/caps" + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +// nolint: gosimple +var ( + deviceCgroupRuleRegex = regexp.MustCompile("^([acb]) ([0-9]+|\\*):([0-9]+|\\*) ([rwm]{1,3})$") +) + +func setCapabilities(s *specs.Spec, c *container.Container) error { + var caplist []string + var err error + if c.HostConfig.Privileged { + caplist = caps.GetAllCapabilities() + } else { + caplist, err = caps.TweakCapabilities(s.Process.Capabilities.Bounding, c.HostConfig.CapAdd, c.HostConfig.CapDrop) + if err != nil { + return err + } + } + s.Process.Capabilities.Effective = caplist + s.Process.Capabilities.Bounding = caplist + s.Process.Capabilities.Permitted = caplist + s.Process.Capabilities.Inheritable = caplist + // setUser has already been executed here + // if non root drop capabilities in the way execve does + if s.Process.User.UID != 0 { + s.Process.Capabilities.Effective = []string{} + s.Process.Capabilities.Permitted = []string{} + } + return nil +} + +func appendDevicePermissionsFromCgroupRules(devPermissions []specs.LinuxDeviceCgroup, rules []string) ([]specs.LinuxDeviceCgroup, error) { + for _, deviceCgroupRule := range rules { + ss := deviceCgroupRuleRegex.FindAllStringSubmatch(deviceCgroupRule, -1) + if len(ss[0]) != 5 { + return nil, fmt.Errorf("invalid device cgroup rule format: '%s'", deviceCgroupRule) + } + matches := ss[0] + + dPermissions := specs.LinuxDeviceCgroup{ + Allow: true, + Type: matches[1], + Access: matches[4], + } + if matches[2] == "*" { + major := int64(-1) + dPermissions.Major = &major + } else { + major, err := strconv.ParseInt(matches[2], 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid major value in device cgroup rule format: '%s'", deviceCgroupRule) + } + dPermissions.Major = &major + } + if matches[3] == "*" { + minor := int64(-1) + dPermissions.Minor = &minor + } else { + minor, err := strconv.ParseInt(matches[3], 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid minor value in device cgroup rule format: '%s'", deviceCgroupRule) + } + dPermissions.Minor = &minor + } + devPermissions = append(devPermissions, dPermissions) + } + return devPermissions, nil +} diff --git a/daemon/oci_linux.go b/daemon/oci_linux.go index 9b39a64ee73d6..6fb7a26dcb318 100644 --- a/daemon/oci_linux.go +++ b/daemon/oci_linux.go @@ -6,14 +6,12 @@ import ( "os" "os/exec" "path/filepath" - "regexp" "sort" "strconv" "strings" containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/container" - "github.com/docker/docker/daemon/caps" daemonconfig "github.com/docker/docker/daemon/config" "github.com/docker/docker/oci" "github.com/docker/docker/pkg/idtools" @@ -29,11 +27,6 @@ import ( "golang.org/x/sys/unix" ) -// nolint: gosimple -var ( - deviceCgroupRuleRegex = regexp.MustCompile("^([acb]) ([0-9]+|\\*):([0-9]+|\\*) ([rwm]{1,3})$") -) - func setResources(s *specs.Spec, r containertypes.Resources) error { weightDevices, err := getBlkioWeightDevices(r) if err != nil { @@ -115,39 +108,10 @@ func setDevices(s *specs.Spec, c *container.Container) error { devPermissions = append(devPermissions, dPermissions...) } - for _, deviceCgroupRule := range c.HostConfig.DeviceCgroupRules { - ss := deviceCgroupRuleRegex.FindAllStringSubmatch(deviceCgroupRule, -1) - if len(ss[0]) != 5 { - return fmt.Errorf("invalid device cgroup rule format: '%s'", deviceCgroupRule) - } - matches := ss[0] - - dPermissions := specs.LinuxDeviceCgroup{ - Allow: true, - Type: matches[1], - Access: matches[4], - } - if matches[2] == "*" { - major := int64(-1) - dPermissions.Major = &major - } else { - major, err := strconv.ParseInt(matches[2], 10, 64) - if err != nil { - return fmt.Errorf("invalid major value in device cgroup rule format: '%s'", deviceCgroupRule) - } - dPermissions.Major = &major - } - if matches[3] == "*" { - minor := int64(-1) - dPermissions.Minor = &minor - } else { - minor, err := strconv.ParseInt(matches[3], 10, 64) - if err != nil { - return fmt.Errorf("invalid minor value in device cgroup rule format: '%s'", deviceCgroupRule) - } - dPermissions.Minor = &minor - } - devPermissions = append(devPermissions, dPermissions) + var err error + devPermissions, err = appendDevicePermissionsFromCgroupRules(devPermissions, c.HostConfig.DeviceCgroupRules) + if err != nil { + return err } } @@ -249,30 +213,6 @@ func setNamespace(s *specs.Spec, ns specs.LinuxNamespace) { s.Linux.Namespaces = append(s.Linux.Namespaces, ns) } -func setCapabilities(s *specs.Spec, c *container.Container) error { - var caplist []string - var err error - if c.HostConfig.Privileged { - caplist = caps.GetAllCapabilities() - } else { - caplist, err = caps.TweakCapabilities(s.Process.Capabilities.Bounding, c.HostConfig.CapAdd, c.HostConfig.CapDrop) - if err != nil { - return err - } - } - s.Process.Capabilities.Effective = caplist - s.Process.Capabilities.Bounding = caplist - s.Process.Capabilities.Permitted = caplist - s.Process.Capabilities.Inheritable = caplist - // setUser has already been executed here - // if non root drop capabilities in the way execve does - if s.Process.User.UID != 0 { - s.Process.Capabilities.Effective = []string{} - s.Process.Capabilities.Permitted = []string{} - } - return nil -} - func setNamespaces(daemon *Daemon, s *specs.Spec, c *container.Container) error { userNS := false // user diff --git a/daemon/oci_linux_test.go b/daemon/oci_linux_test.go index e28fac004efdf..e618951ef95ea 100644 --- a/daemon/oci_linux_test.go +++ b/daemon/oci_linux_test.go @@ -9,8 +9,8 @@ import ( "github.com/docker/docker/daemon/config" "github.com/docker/docker/oci" "github.com/docker/docker/pkg/idtools" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) // TestTmpfsDevShmNoDupMount checks that a user-specified /dev/shm tmpfs diff --git a/daemon/oci_windows.go b/daemon/oci_windows.go index f00ab3363d7e5..a7543229bb21e 100644 --- a/daemon/oci_windows.go +++ b/daemon/oci_windows.go @@ -211,7 +211,9 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) { if !system.LCOWSupported() { return nil, fmt.Errorf("Linux containers on Windows are not supported") } - daemon.createSpecLinuxFields(c, &s) + if err := daemon.createSpecLinuxFields(c, &s); err != nil { + return nil, err + } default: return nil, fmt.Errorf("Unsupported platform %q", img.OS) } @@ -330,18 +332,51 @@ func (daemon *Daemon) createSpecWindowsFields(c *container.Container, s *specs.S s.Windows.CredentialSpec = cs } + // Do we have any assigned devices? + if len(c.HostConfig.Devices) > 0 { + if isHyperV { + return errors.New("device assignment not support for HyperV containers") + } + if system.GetOSVersion().Build < 17717 { + return errors.New("device assignment not support on builds older than 17717") + } + for _, deviceMapping := range c.HostConfig.Devices { + srcParts := strings.SplitN(deviceMapping.PathOnHost, "/", 2) + if len(srcParts) != 2 { + return errors.New("invalid device assignment path") + } + if srcParts[0] != "class" { + return errors.Errorf("invalid device assignment type: '%s' should be 'class'", srcParts[0]) + } + wd := specs.WindowsDevice{ + ID: srcParts[1], + IDType: srcParts[0], + } + s.Windows.Devices = append(s.Windows.Devices, wd) + } + } + return nil } // Sets the Linux-specific fields of the OCI spec // TODO: @jhowardmsft LCOW Support. We need to do a lot more pulling in what can // be pulled in from oci_linux.go. -func (daemon *Daemon) createSpecLinuxFields(c *container.Container, s *specs.Spec) { +func (daemon *Daemon) createSpecLinuxFields(c *container.Container, s *specs.Spec) error { if len(s.Process.Cwd) == 0 { s.Process.Cwd = `/` } s.Root.Path = "rootfs" s.Root.Readonly = c.HostConfig.ReadonlyRootfs + if err := setCapabilities(s, c); err != nil { + return fmt.Errorf("linux spec capabilities: %v", err) + } + devPermissions, err := appendDevicePermissionsFromCgroupRules(nil, c.HostConfig.DeviceCgroupRules) + if err != nil { + return fmt.Errorf("linux runtime spec devices: %v", err) + } + s.Linux.Resources.Devices = devPermissions + return nil } func escapeArgs(args []string) []string { diff --git a/daemon/prune.go b/daemon/prune.go index b690f2e552073..b1bcd5b79c048 100644 --- a/daemon/prune.go +++ b/daemon/prune.go @@ -141,7 +141,7 @@ func (daemon *Daemon) clusterNetworksPrune(ctx context.Context, pruneFilters fil return rep, nil } - networks, err := cluster.GetNetworks() + networks, err := cluster.GetNetworks(pruneFilters) if err != nil { return rep, err } diff --git a/daemon/reload.go b/daemon/reload.go index 210864ff879da..452615c3eefd5 100644 --- a/daemon/reload.go +++ b/daemon/reload.go @@ -186,7 +186,7 @@ func (daemon *Daemon) reloadClusterDiscovery(conf *config.Config, attributes map } netOptions, err := daemon.networkOptions(daemon.configStore, daemon.PluginStore, nil) if err != nil { - logrus.WithError(err).Warnf("failed to get options with network controller") + logrus.WithError(err).Warn("failed to get options with network controller") return nil } err = daemon.netController.ReloadConfiguration(netOptions...) diff --git a/daemon/reload_test.go b/daemon/reload_test.go index c4d15d93e5569..ffad297f71b74 100644 --- a/daemon/reload_test.go +++ b/daemon/reload_test.go @@ -13,8 +13,8 @@ import ( _ "github.com/docker/docker/pkg/discovery/memory" "github.com/docker/docker/registry" "github.com/docker/libnetwork" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestDaemonReloadLabels(t *testing.T) { diff --git a/daemon/resize.go b/daemon/resize.go index 369b8fdb3dc2a..21240650f85a8 100644 --- a/daemon/resize.go +++ b/daemon/resize.go @@ -3,6 +3,7 @@ package daemon // import "github.com/docker/docker/daemon" import ( "context" "fmt" + "time" "github.com/docker/docker/libcontainerd" ) @@ -37,5 +38,13 @@ func (daemon *Daemon) ContainerExecResize(name string, height, width int) error if err != nil { return err } - return daemon.containerd.ResizeTerminal(context.Background(), ec.ContainerID, ec.ID, width, height) + // TODO: the timeout is hardcoded here, it would be more flexible to make it + // a parameter in resize request context, which would need API changes. + timeout := 10 * time.Second + select { + case <-ec.Started: + return daemon.containerd.ResizeTerminal(context.Background(), ec.ContainerID, ec.ID, width, height) + case <-time.After(timeout): + return fmt.Errorf("timeout waiting for exec session ready") + } } diff --git a/daemon/resize_test.go b/daemon/resize_test.go new file mode 100644 index 0000000000000..edfe9d3ed1771 --- /dev/null +++ b/daemon/resize_test.go @@ -0,0 +1,103 @@ +// +build linux + +package daemon + +import ( + "context" + "testing" + + "github.com/docker/docker/container" + "github.com/docker/docker/daemon/exec" + "gotest.tools/assert" +) + +// This test simply verify that when a wrong ID used, a specific error should be returned for exec resize. +func TestExecResizeNoSuchExec(t *testing.T) { + n := "TestExecResize" + d := &Daemon{ + execCommands: exec.NewStore(), + } + c := &container.Container{ + ExecCommands: exec.NewStore(), + } + ec := &exec.Config{ + ID: n, + } + d.registerExecCommand(c, ec) + err := d.ContainerExecResize("nil", 24, 8) + assert.ErrorContains(t, err, "No such exec instance") +} + +type execResizeMockContainerdClient struct { + MockContainerdClient + ProcessID string + ContainerID string + Width int + Height int +} + +func (c *execResizeMockContainerdClient) ResizeTerminal(ctx context.Context, containerID, processID string, width, height int) error { + c.ProcessID = processID + c.ContainerID = containerID + c.Width = width + c.Height = height + return nil +} + +// This test is to make sure that when exec context is ready, resize should call ResizeTerminal via containerd client. +func TestExecResize(t *testing.T) { + n := "TestExecResize" + width := 24 + height := 8 + ec := &exec.Config{ + ID: n, + ContainerID: n, + Started: make(chan struct{}), + } + close(ec.Started) + mc := &execResizeMockContainerdClient{} + d := &Daemon{ + execCommands: exec.NewStore(), + containerd: mc, + containers: container.NewMemoryStore(), + } + c := &container.Container{ + ExecCommands: exec.NewStore(), + State: &container.State{Running: true}, + } + d.containers.Add(n, c) + d.registerExecCommand(c, ec) + err := d.ContainerExecResize(n, height, width) + assert.NilError(t, err) + assert.Equal(t, mc.Width, width) + assert.Equal(t, mc.Height, height) + assert.Equal(t, mc.ProcessID, n) + assert.Equal(t, mc.ContainerID, n) +} + +// This test is to make sure that when exec context is not ready, a timeout error should happen. +// TODO: the expect running time for this test is 10s, which would be too long for unit test. +func TestExecResizeTimeout(t *testing.T) { + n := "TestExecResize" + width := 24 + height := 8 + ec := &exec.Config{ + ID: n, + ContainerID: n, + Started: make(chan struct{}), + } + mc := &execResizeMockContainerdClient{} + d := &Daemon{ + execCommands: exec.NewStore(), + containerd: mc, + containers: container.NewMemoryStore(), + } + c := &container.Container{ + ExecCommands: exec.NewStore(), + State: &container.State{Running: true}, + } + d.containers.Add(n, c) + d.registerExecCommand(c, ec) + err := d.ContainerExecResize(n, height, width) + assert.ErrorContains(t, err, "timeout waiting for exec session ready") +} diff --git a/daemon/start_unix.go b/daemon/start_unix.go index 52a34a9ac914c..e680b95f42149 100644 --- a/daemon/start_unix.go +++ b/daemon/start_unix.go @@ -7,7 +7,7 @@ import ( "os/exec" "path/filepath" - "github.com/containerd/containerd/linux/runctypes" + "github.com/containerd/containerd/runtime/linux/runctypes" "github.com/docker/docker/container" "github.com/docker/docker/errdefs" "github.com/pkg/errors" diff --git a/daemon/trustkey_test.go b/daemon/trustkey_test.go index e13129e46743f..e49e76aa3eeec 100644 --- a/daemon/trustkey_test.go +++ b/daemon/trustkey_test.go @@ -6,9 +6,9 @@ import ( "path/filepath" "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/fs" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/fs" ) // LoadOrCreateTrustKey diff --git a/daemon/unpause.go b/daemon/unpause.go index 9061d50a168e4..27648ae72e3c8 100644 --- a/daemon/unpause.go +++ b/daemon/unpause.go @@ -37,7 +37,7 @@ func (daemon *Daemon) containerUnpause(container *container.Container) error { daemon.LogContainerEvent(container, "unpause") if err := container.CheckpointTo(daemon.containersReplica); err != nil { - logrus.WithError(err).Warnf("could not save container to disk") + logrus.WithError(err).Warn("could not save container to disk") } return nil diff --git a/daemon/util_test.go b/daemon/util_test.go new file mode 100644 index 0000000000000..b2c464f737fac --- /dev/null +++ b/daemon/util_test.go @@ -0,0 +1,65 @@ +// +build linux + +package daemon + +import ( + "context" + "time" + + "github.com/containerd/containerd" + "github.com/docker/docker/libcontainerd" + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +// Mock containerd client implementation, for unit tests. +type MockContainerdClient struct { +} + +func (c *MockContainerdClient) Version(ctx context.Context) (containerd.Version, error) { + return containerd.Version{}, nil +} +func (c *MockContainerdClient) Restore(ctx context.Context, containerID string, attachStdio libcontainerd.StdioCallback) (alive bool, pid int, err error) { + return false, 0, nil +} +func (c *MockContainerdClient) Create(ctx context.Context, containerID string, spec *specs.Spec, runtimeOptions interface{}) error { + return nil +} +func (c *MockContainerdClient) Start(ctx context.Context, containerID, checkpointDir string, withStdin bool, attachStdio libcontainerd.StdioCallback) (pid int, err error) { + return 0, nil +} +func (c *MockContainerdClient) SignalProcess(ctx context.Context, containerID, processID string, signal int) error { + return nil +} +func (c *MockContainerdClient) Exec(ctx context.Context, containerID, processID string, spec *specs.Process, withStdin bool, attachStdio libcontainerd.StdioCallback) (int, error) { + return 0, nil +} +func (c *MockContainerdClient) ResizeTerminal(ctx context.Context, containerID, processID string, width, height int) error { + return nil +} +func (c *MockContainerdClient) CloseStdin(ctx context.Context, containerID, processID string) error { + return nil +} +func (c *MockContainerdClient) Pause(ctx context.Context, containerID string) error { return nil } +func (c *MockContainerdClient) Resume(ctx context.Context, containerID string) error { return nil } +func (c *MockContainerdClient) Stats(ctx context.Context, containerID string) (*libcontainerd.Stats, error) { + return nil, nil +} +func (c *MockContainerdClient) ListPids(ctx context.Context, containerID string) ([]uint32, error) { + return nil, nil +} +func (c *MockContainerdClient) Summary(ctx context.Context, containerID string) ([]libcontainerd.Summary, error) { + return nil, nil +} +func (c *MockContainerdClient) DeleteTask(ctx context.Context, containerID string) (uint32, time.Time, error) { + return 0, time.Time{}, nil +} +func (c *MockContainerdClient) Delete(ctx context.Context, containerID string) error { return nil } +func (c *MockContainerdClient) Status(ctx context.Context, containerID string) (libcontainerd.Status, error) { + return "null", nil +} +func (c *MockContainerdClient) UpdateResources(ctx context.Context, containerID string, resources *libcontainerd.Resources) error { + return nil +} +func (c *MockContainerdClient) CreateCheckpoint(ctx context.Context, containerID, checkpointDir string, exit bool) error { + return nil +} diff --git a/daemon/volumes.go b/daemon/volumes.go index a20ff1fbf53cf..d1c98d0a4fae1 100644 --- a/daemon/volumes.go +++ b/daemon/volumes.go @@ -215,6 +215,10 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo } } + if mp.Type == mounttypes.TypeBind { + mp.SkipMountpointCreation = true + } + binds[mp.Destination] = true dereferenceIfExists(mp.Destination) mountPoints[mp.Destination] = mp diff --git a/distribution/config.go b/distribution/config.go index 55f1f8c2df3ba..438051c296e27 100644 --- a/distribution/config.go +++ b/distribution/config.go @@ -60,9 +60,8 @@ type ImagePullConfig struct { // Schema2Types is the valid schema2 configuration types allowed // by the pull operation. Schema2Types []string - // OS is the requested operating system of the image being pulled to ensure it can be validated - // when the host OS supports multiple image operating systems. - OS string + // Platform is the requested platform of the image being pulled + Platform *specs.Platform } // ImagePushConfig stores push configuration. @@ -171,7 +170,7 @@ func (s *imageConfigStore) PlatformFromConfig(c []byte) (*specs.Platform, error) if !system.IsOSSupported(os) { return nil, system.ErrNotSupportedOperatingSystem } - return &specs.Platform{OS: os, OSVersion: unmarshalledConfig.OSVersion}, nil + return &specs.Platform{OS: os, Architecture: unmarshalledConfig.Architecture, OSVersion: unmarshalledConfig.OSVersion}, nil } type storeLayerProvider struct { diff --git a/distribution/metadata/v1_id_service_test.go b/distribution/metadata/v1_id_service_test.go index 7bac8e821230c..5003897cbb92d 100644 --- a/distribution/metadata/v1_id_service_test.go +++ b/distribution/metadata/v1_id_service_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/docker/docker/layer" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) func TestV1IDService(t *testing.T) { diff --git a/distribution/oci.go b/distribution/oci.go new file mode 100644 index 0000000000000..7366546d09611 --- /dev/null +++ b/distribution/oci.go @@ -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)) + } +} diff --git a/distribution/pull.go b/distribution/pull.go index 0240eb05f7b5c..5de73ae99ac39 100644 --- a/distribution/pull.go +++ b/distribution/pull.go @@ -3,7 +3,6 @@ package distribution // import "github.com/docker/docker/distribution" import ( "context" "fmt" - "runtime" "github.com/docker/distribution/reference" "github.com/docker/docker/api" @@ -12,6 +11,7 @@ import ( refstore "github.com/docker/docker/reference" "github.com/docker/docker/registry" "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -21,7 +21,7 @@ type Puller interface { // Pull tries to pull the image referenced by `tag` // Pull returns an error if any, as well as a boolean that determines whether to retry Pull on the next configured endpoint. // - Pull(ctx context.Context, ref reference.Named, os string) error + Pull(ctx context.Context, ref reference.Named, platform *specs.Platform) error } // newPuller returns a Puller interface that will pull from either a v1 or v2 @@ -115,12 +115,7 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo continue } - // Make sure we default the OS if it hasn't been supplied - if imagePullConfig.OS == "" { - imagePullConfig.OS = runtime.GOOS - } - - if err := puller.Pull(ctx, ref, imagePullConfig.OS); err != nil { + if err := puller.Pull(ctx, ref, imagePullConfig.Platform); err != nil { // Was this pull cancelled? If so, don't try to fall // back. fallback := false diff --git a/distribution/pull_v1.go b/distribution/pull_v1.go index c26d881223f0a..c2c897dc1c31c 100644 --- a/distribution/pull_v1.go +++ b/distribution/pull_v1.go @@ -25,6 +25,7 @@ import ( "github.com/docker/docker/pkg/progress" "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/registry" + specs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/sirupsen/logrus" ) @@ -36,7 +37,7 @@ type v1Puller struct { session *registry.Session } -func (p *v1Puller) Pull(ctx context.Context, ref reference.Named, os string) error { +func (p *v1Puller) Pull(ctx context.Context, ref reference.Named, _ *specs.Platform) error { if _, isCanonical := ref.(reference.Canonical); isCanonical { // Allowing fallback, because HTTPS v1 is before HTTP v2 return fallbackError{err: ErrNoSupport{Err: errors.New("Cannot pull by digest with v1 registry")}} diff --git a/distribution/pull_v2.go b/distribution/pull_v2.go index 60a894b1c3a8b..8f05cfa0b289b 100644 --- a/distribution/pull_v2.go +++ b/distribution/pull_v2.go @@ -11,6 +11,7 @@ import ( "runtime" "strings" + "github.com/containerd/containerd/platforms" "github.com/docker/distribution" "github.com/docker/distribution/manifest/manifestlist" "github.com/docker/distribution/manifest/schema1" @@ -63,7 +64,7 @@ type v2Puller struct { confirmedV2 bool } -func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, os string) (err error) { +func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, platform *specs.Platform) (err error) { // TODO(tiborvass): was ReceiveTimeout p.repo, p.confirmedV2, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull") if err != nil { @@ -71,7 +72,7 @@ func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, os string) (er return err } - if err = p.pullV2Repository(ctx, ref, os); err != nil { + if err = p.pullV2Repository(ctx, ref, platform); err != nil { if _, ok := err.(fallbackError); ok { return err } @@ -86,10 +87,10 @@ func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, os string) (er return err } -func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named, os string) (err error) { +func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named, platform *specs.Platform) (err error) { var layersDownloaded bool if !reference.IsNameOnly(ref) { - layersDownloaded, err = p.pullV2Tag(ctx, ref, os) + layersDownloaded, err = p.pullV2Tag(ctx, ref, platform) if err != nil { return err } @@ -111,7 +112,7 @@ func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named, os if err != nil { return err } - pulledNew, err := p.pullV2Tag(ctx, tagRef, os) + pulledNew, err := p.pullV2Tag(ctx, tagRef, platform) if err != nil { // Since this is the pull-all-tags case, don't // allow an error pulling a particular tag to @@ -327,7 +328,7 @@ func (ld *v2LayerDescriptor) Registered(diffID layer.DiffID) { ld.V2MetadataService.Add(diffID, metadata.V2Metadata{Digest: ld.digest, SourceRepository: ld.repoInfo.Name.Name()}) } -func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, os string) (tagUpdated bool, err error) { +func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, platform *specs.Platform) (tagUpdated bool, err error) { manSvc, err := p.repo.Manifests(ctx) if err != nil { return false, err @@ -391,17 +392,17 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, os string if p.config.RequireSchema2 { return false, fmt.Errorf("invalid manifest: not schema2") } - id, manifestDigest, err = p.pullSchema1(ctx, ref, v, os) + id, manifestDigest, err = p.pullSchema1(ctx, ref, v, platform) if err != nil { return false, err } case *schema2.DeserializedManifest: - id, manifestDigest, err = p.pullSchema2(ctx, ref, v, os) + id, manifestDigest, err = p.pullSchema2(ctx, ref, v, platform) if err != nil { return false, err } case *manifestlist.DeserializedManifestList: - id, manifestDigest, err = p.pullManifestList(ctx, ref, v, os) + id, manifestDigest, err = p.pullManifestList(ctx, ref, v, platform) if err != nil { return false, err } @@ -437,7 +438,7 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, os string return true, nil } -func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unverifiedManifest *schema1.SignedManifest, requestedOS string) (id digest.Digest, manifestDigest digest.Digest, err error) { +func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unverifiedManifest *schema1.SignedManifest, platform *specs.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) { var verifiedManifest *schema1.Manifest verifiedManifest, err = verifySchema1Manifest(unverifiedManifest, ref) if err != nil { @@ -509,6 +510,17 @@ func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unv } } + // In the situation that the API call didn't specify an OS explicitly, but + // we support the operating system, switch to that operating system. + // eg FROM supertest2014/nyan with no platform specifier, and docker build + // with no --platform= flag under LCOW. + requestedOS := "" + if platform != nil { + requestedOS = platform.OS + } else if system.IsOSSupported(configOS) { + requestedOS = configOS + } + // Early bath if the requested OS doesn't match that of the configuration. // This avoids doing the download, only to potentially fail later. if !strings.EqualFold(configOS, requestedOS) { @@ -536,7 +548,7 @@ func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unv return imageID, manifestDigest, nil } -func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *schema2.DeserializedManifest, requestedOS string) (id digest.Digest, manifestDigest digest.Digest, err error) { +func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *schema2.DeserializedManifest, platform *specs.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) { manifestDigest, err = schema2ManifestDigest(ref, mfst) if err != nil { return "", "", err @@ -592,6 +604,11 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s configPlatform *specs.Platform // for LCOW when registering downloaded layers ) + layerStoreOS := runtime.GOOS + if platform != nil { + layerStoreOS = platform.OS + } + // https://github.com/docker/docker/issues/24766 - Err on the side of caution, // explicitly blocking images intended for linux from the Windows daemon. On // Windows, we do this before the attempt to download, effectively serialising @@ -615,11 +632,13 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s if len(descriptors) != len(configRootFS.DiffIDs) { return "", "", errRootFSMismatch } - - // Early bath if the requested OS doesn't match that of the configuration. - // This avoids doing the download, only to potentially fail later. - if !strings.EqualFold(configPlatform.OS, requestedOS) { - return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configPlatform.OS, requestedOS) + if platform == nil { + // Early bath if the requested OS doesn't match that of the configuration. + // This avoids doing the download, only to potentially fail later. + if !system.IsOSSupported(configPlatform.OS) { + return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configPlatform.OS, layerStoreOS) + } + layerStoreOS = configPlatform.OS } // Populate diff ids in descriptors to avoid downloading foreign layers @@ -636,7 +655,7 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s rootFS image.RootFS ) downloadRootFS := *image.NewRootFS() - rootFS, release, err = p.config.DownloadManager.Download(ctx, downloadRootFS, requestedOS, descriptors, p.config.ProgressOutput) + rootFS, release, err = p.config.DownloadManager.Download(ctx, downloadRootFS, layerStoreOS, descriptors, p.config.ProgressOutput) if err != nil { // Intentionally do not cancel the config download here // as the error from config download (if there is one) @@ -722,18 +741,22 @@ func receiveConfig(s ImageConfigStore, configChan <-chan []byte, errChan <-chan // pullManifestList handles "manifest lists" which point to various // platform-specific manifests. -func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mfstList *manifestlist.DeserializedManifestList, os string) (id digest.Digest, manifestListDigest digest.Digest, err error) { +func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mfstList *manifestlist.DeserializedManifestList, pp *specs.Platform) (id digest.Digest, manifestListDigest digest.Digest, err error) { manifestListDigest, err = schema2ManifestDigest(ref, mfstList) if err != nil { return "", "", err } - logrus.Debugf("%s resolved to a manifestList object with %d entries; looking for a %s/%s match", ref, len(mfstList.Manifests), os, runtime.GOARCH) + var platform specs.Platform + if pp != nil { + platform = *pp + } + logrus.Debugf("%s resolved to a manifestList object with %d entries; looking for a %s/%s match", ref, len(mfstList.Manifests), platforms.Format(platform), runtime.GOARCH) - manifestMatches := filterManifests(mfstList.Manifests, os) + manifestMatches := filterManifests(mfstList.Manifests, platform) if len(manifestMatches) == 0 { - errMsg := fmt.Sprintf("no matching manifest for %s/%s in the manifest list entries", os, runtime.GOARCH) + errMsg := fmt.Sprintf("no matching manifest for %s in the manifest list entries", platforms.Format(platform)) logrus.Debugf(errMsg) return "", "", errors.New(errMsg) } @@ -764,12 +787,14 @@ func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mf switch v := manifest.(type) { case *schema1.SignedManifest: - id, _, err = p.pullSchema1(ctx, manifestRef, v, os) + platform := toOCIPlatform(manifestMatches[0].Platform) + id, _, err = p.pullSchema1(ctx, manifestRef, v, &platform) if err != nil { return "", "", err } case *schema2.DeserializedManifest: - id, _, err = p.pullSchema2(ctx, manifestRef, v, os) + platform := toOCIPlatform(manifestMatches[0].Platform) + id, _, err = p.pullSchema2(ctx, manifestRef, v, &platform) if err != nil { return "", "", err } @@ -939,3 +964,13 @@ func fixManifestLayers(m *schema1.Manifest) error { func createDownloadFile() (*os.File, error) { return ioutil.TempFile("", "GetImageBlob") } + +func toOCIPlatform(p manifestlist.PlatformSpec) specs.Platform { + return specs.Platform{ + OS: p.OS, + Architecture: p.Architecture, + Variant: p.Variant, + OSFeatures: p.OSFeatures, + OSVersion: p.OSVersion, + } +} diff --git a/distribution/pull_v2_test.go b/distribution/pull_v2_test.go index 1079b5fe53843..ca3470c8cfe8d 100644 --- a/distribution/pull_v2_test.go +++ b/distribution/pull_v2_test.go @@ -10,9 +10,9 @@ import ( "github.com/docker/distribution/manifest/schema1" "github.com/docker/distribution/reference" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/opencontainers/go-digest" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) // TestFixManifestLayers checks that fixManifestLayers removes a duplicate diff --git a/distribution/pull_v2_unix.go b/distribution/pull_v2_unix.go index 0be8a032428e5..adbaf41451280 100644 --- a/distribution/pull_v2_unix.go +++ b/distribution/pull_v2_unix.go @@ -4,10 +4,11 @@ package distribution // import "github.com/docker/docker/distribution" import ( "context" - "runtime" + "github.com/containerd/containerd/platforms" "github.com/docker/distribution" "github.com/docker/distribution/manifest/manifestlist" + specs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/sirupsen/logrus" ) @@ -16,15 +17,28 @@ func (ld *v2LayerDescriptor) open(ctx context.Context) (distribution.ReadSeekClo return blobs.Open(ctx, ld.digest) } -func filterManifests(manifests []manifestlist.ManifestDescriptor, os string) []manifestlist.ManifestDescriptor { +func filterManifests(manifests []manifestlist.ManifestDescriptor, p specs.Platform) []manifestlist.ManifestDescriptor { + p = platforms.Normalize(withDefault(p)) + m := platforms.NewMatcher(p) var matches []manifestlist.ManifestDescriptor - for _, manifestDescriptor := range manifests { - if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == os { - matches = append(matches, manifestDescriptor) + for _, desc := range manifests { + if m.Match(toOCIPlatform(desc.Platform)) { + matches = append(matches, desc) + logrus.Debugf("found match for %s with media type %s, digest %s", platforms.Format(p), desc.MediaType, desc.Digest.String()) + } + } - logrus.Debugf("found match for %s/%s with media type %s, digest %s", os, runtime.GOARCH, manifestDescriptor.MediaType, manifestDescriptor.Digest.String()) + // deprecated: backwards compatibility with older versions that didn't compare variant + if len(matches) == 0 && p.Architecture == "arm" { + p = platforms.Normalize(p) + for _, desc := range manifests { + if desc.Platform.OS == p.OS && desc.Platform.Architecture == p.Architecture { + matches = append(matches, desc) + logrus.Debugf("found deprecated partial match for %s with media type %s, digest %s", platforms.Format(p), desc.MediaType, desc.Digest.String()) + } } } + return matches } @@ -32,3 +46,15 @@ func filterManifests(manifests []manifestlist.ManifestDescriptor, os string) []m func checkImageCompatibility(imageOS, imageOSVersion string) error { return nil } + +func withDefault(p specs.Platform) specs.Platform { + def := platforms.DefaultSpec() + if p.OS == "" { + p.OS = def.OS + } + if p.Architecture == "" { + p.Architecture = def.Architecture + p.Variant = def.Variant + } + return p +} diff --git a/distribution/pull_v2_windows.go b/distribution/pull_v2_windows.go index 432a36119d4c9..1ae167ef777d0 100644 --- a/distribution/pull_v2_windows.go +++ b/distribution/pull_v2_windows.go @@ -16,6 +16,7 @@ import ( "github.com/docker/distribution/manifest/schema2" "github.com/docker/distribution/registry/client/transport" "github.com/docker/docker/pkg/system" + specs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/sirupsen/logrus" ) @@ -62,24 +63,30 @@ func (ld *v2LayerDescriptor) open(ctx context.Context) (distribution.ReadSeekClo return rsc, err } -func filterManifests(manifests []manifestlist.ManifestDescriptor, os string) []manifestlist.ManifestDescriptor { - osVersion := "" - if os == "windows" { - version := system.GetOSVersion() - osVersion = fmt.Sprintf("%d.%d.%d", version.MajorVersion, version.MinorVersion, version.Build) - logrus.Debugf("will prefer entries with version %s", osVersion) - } +func filterManifests(manifests []manifestlist.ManifestDescriptor, p specs.Platform) []manifestlist.ManifestDescriptor { + version := system.GetOSVersion() + osVersion := fmt.Sprintf("%d.%d.%d", version.MajorVersion, version.MinorVersion, version.Build) + logrus.Debugf("will prefer Windows entries with version %s", osVersion) var matches []manifestlist.ManifestDescriptor + foundWindowsMatch := false for _, manifestDescriptor := range manifests { - if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == os { + 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 + 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 for %s/%s %s with media type %s, digest %s", os, runtime.GOARCH, manifestDescriptor.Platform.OSVersion, manifestDescriptor.MediaType, manifestDescriptor.Digest.String()) + 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", os, runtime.GOARCH, manifestDescriptor.Platform.OSVersion, manifestDescriptor.MediaType, manifestDescriptor.Digest.String()) + 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()) } } - if os == "windows" { + if foundWindowsMatch { sort.Stable(manifestsByVersion{osVersion, matches}) } return matches @@ -99,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 { diff --git a/distribution/registry.go b/distribution/registry.go index 8b46aaad6d118..d81530b75cbbf 100644 --- a/distribution/registry.go +++ b/distribution/registry.go @@ -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) diff --git a/distribution/registry_unit_test.go b/distribution/registry_unit_test.go index 5ae529d23dab4..3651c4688bde5 100644 --- a/distribution/registry_unit_test.go +++ b/distribution/registry_unit_test.go @@ -5,7 +5,6 @@ import ( "net/http" "net/http/httptest" "net/url" - "runtime" "strings" "testing" @@ -84,7 +83,7 @@ func testTokenPassThru(t *testing.T, ts *httptest.Server) { logrus.Debug("About to pull") // We expect it to fail, since we haven't mock'd the full registry exchange in our handler above tag, _ := reference.WithTag(n, "tag_goes_here") - _ = p.pullV2Repository(ctx, tag, runtime.GOOS) + _ = p.pullV2Repository(ctx, tag, nil) } func TestTokenPassThru(t *testing.T) { diff --git a/dockerversion/version_lib.go b/dockerversion/version_lib.go index 0897c0728eb56..ff1816503be6c 100644 --- a/dockerversion/version_lib.go +++ b/dockerversion/version_lib.go @@ -14,4 +14,5 @@ const ( RuncCommitID = "library-import" InitCommitID = "library-import" PlatformName = "" + ProductName = "" ) diff --git a/docs/contributing/set-up-dev-env.md b/docs/contributing/set-up-dev-env.md index 311edf89514ec..3d56c0b8c7d15 100644 --- a/docs/contributing/set-up-dev-env.md +++ b/docs/contributing/set-up-dev-env.md @@ -93,7 +93,7 @@ can take over 15 minutes to complete. 1. Open a terminal. - For [Docker Toolbox](../../toolbox/overview.md) users, use `docker-machine status your_vm_name` to make sure your VM is running. You + For [Docker Toolbox](https://github.com/docker/toolbox) users, use `docker-machine status your_vm_name` to make sure your VM is running. You may need to run `eval "$(docker-machine env your_vm_name)"` to initialize your shell environment. If you use Docker for Mac or Docker for Windows, you do not need to use Docker Machine. diff --git a/docs/contributing/software-req-win.md b/docs/contributing/software-req-win.md index d51861cbe56cf..3070d34e8381e 100644 --- a/docs/contributing/software-req-win.md +++ b/docs/contributing/software-req-win.md @@ -100,8 +100,8 @@ To build Moby, run: Copy out the resulting Windows Moby Engine binary to `dockerd.exe` in the current directory: - docker cp binaries:C:\go\src\github.com\moby\moby\bundles\docker.exe docker.exe - docker cp binaries:C:\go\src\github.com\moby\moby\bundles\dockerd.exe dockerd.exe + docker cp binaries:C:\go\src\github.com\docker\docker\bundles\docker.exe docker.exe + docker cp binaries:C:\go\src\github.com\docker\docker\bundles\dockerd.exe dockerd.exe To test it, stop the system Docker daemon and start the one you just built: diff --git a/docs/contributing/test.md b/docs/contributing/test.md index fdcee328a99ee..8a50b0678646a 100644 --- a/docs/contributing/test.md +++ b/docs/contributing/test.md @@ -153,7 +153,7 @@ $ TESTDIRS='opts' make test-unit You can also use the `TESTFLAGS` environment variable to run a single test. The flag's value is passed as arguments to the `go test` command. For example, from -your local host you can run the `TestBuild` test with this command: +your local host you can run the `TestValidateIPAddress` test with this command: ```bash $ TESTFLAGS='-test.run ^TestValidateIPAddress$' make test-unit diff --git a/hack/dockerfile/install/containerd.installer b/hack/dockerfile/install/containerd.installer index 2c0502e0987d4..4be15a6abfb8b 100755 --- a/hack/dockerfile/install/containerd.installer +++ b/hack/dockerfile/install/containerd.installer @@ -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=395068d2b7256518259816ae19e45824b15da071 # v1.1.1-rc.0 +CONTAINERD_COMMIT=468a545b9edcd5932818eb9de8e72413e616e86e # v1.1.2 install_containerd() { echo "Install containerd version $CONTAINERD_COMMIT" @@ -14,7 +14,7 @@ install_containerd() { ( - export BUILDTAGS='static_build netgo' + export BUILDTAGS='netgo osusergo static_build' export EXTRA_FLAGS='-buildmode=pie' export EXTRA_LDFLAGS='-extldflags "-fno-PIC -static"' diff --git a/hack/dockerfile/install/gometalinter.installer b/hack/dockerfile/install/gometalinter.installer index 13500e1c899b5..d921fd739add3 100755 --- a/hack/dockerfile/install/gometalinter.installer +++ b/hack/dockerfile/install/gometalinter.installer @@ -1,6 +1,6 @@ #!/bin/sh -GOMETALINTER_COMMIT=bfcc1d6942136fd86eb6f1a6fb328de8398fbd80 +GOMETALINTER_COMMIT=v2.0.6 install_gometalinter() { echo "Installing gometalinter version $GOMETALINTER_COMMIT" diff --git a/hack/dockerfile/install/proxy.installer b/hack/dockerfile/install/proxy.installer index c10ce330ef3c4..4983d909b363a 100755 --- a/hack/dockerfile/install/proxy.installer +++ b/hack/dockerfile/install/proxy.installer @@ -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=3931ba4d815e385ab97093c64477b82f14dadefb +LIBNETWORK_COMMIT=3ac297bc7fd0afec9051bbb47024c9bc1d75bf5b install_proxy() { case "$1" in diff --git a/hack/make.ps1 b/hack/make.ps1 index 70b9a4772692e..e1e91e52c2845 100644 --- a/hack/make.ps1 +++ b/hack/make.ps1 @@ -365,7 +365,7 @@ Try { # Run autogen if building binaries or running unit tests. if ($Client -or $Daemon -or $TestUnit) { Write-Host "INFO: Invoking autogen..." - Try { .\hack\make\.go-autogen.ps1 -CommitString $gitCommit -DockerVersion $dockerVersion -Platform "$env:PLATFORM" } + Try { .\hack\make\.go-autogen.ps1 -CommitString $gitCommit -DockerVersion $dockerVersion -Platform "$env:PLATFORM" -Product "$env:PRODUCT" } Catch [Exception] { Throw $_ } } diff --git a/hack/make.sh b/hack/make.sh index cd9232a4a5c40..bc0c2a39d0d06 100755 --- a/hack/make.sh +++ b/hack/make.sh @@ -145,7 +145,7 @@ LDFLAGS_STATIC='' EXTLDFLAGS_STATIC='-static' # ORIG_BUILDFLAGS is necessary for the cross target which cannot always build # with options like -race. -ORIG_BUILDFLAGS=( -tags "autogen netgo static_build $DOCKER_BUILDTAGS" -installsuffix netgo ) +ORIG_BUILDFLAGS=( -tags "autogen netgo osusergo static_build $DOCKER_BUILDTAGS" -installsuffix netgo ) # see https://github.com/golang/go/issues/9369#issuecomment-69864440 for why -installsuffix is necessary here # When $DOCKER_INCREMENTAL_BINARY is set in the environment, enable incremental diff --git a/hack/make/.go-autogen b/hack/make/.go-autogen index ba001895d8a20..342f5ec95b8c1 100644 --- a/hack/make/.go-autogen +++ b/hack/make/.go-autogen @@ -21,6 +21,7 @@ const ( IAmStatic string = "${IAMSTATIC:-true}" ContainerdCommitID string = "${CONTAINERD_COMMIT}" PlatformName string = "${PLATFORM}" + ProductName string = "${PRODUCT}" ) // AUTOGENERATED FILE; see /go/src/github.com/docker/docker/hack/make/.go-autogen diff --git a/hack/make/.go-autogen.ps1 b/hack/make/.go-autogen.ps1 index cc14e9ee9d81c..98686e1362f59 100644 --- a/hack/make/.go-autogen.ps1 +++ b/hack/make/.go-autogen.ps1 @@ -15,7 +15,8 @@ param( [Parameter(Mandatory=$true)][string]$CommitString, [Parameter(Mandatory=$true)][string]$DockerVersion, - [Parameter(Mandatory=$false)][string]$Platform + [Parameter(Mandatory=$false)][string]$Platform, + [Parameter(Mandatory=$false)][string]$Product ) $ErrorActionPreference = "Stop" @@ -45,6 +46,7 @@ const ( Version string = "'+$DockerVersion+'" BuildTime string = "'+$buildDateTime+'" PlatformName string = "'+$Platform+'" + ProductName string = "'+$Product+'" ) // AUTOGENERATED FILE; see hack\make\.go-autogen.ps1 diff --git a/hack/make/.integration-test-helpers b/hack/make/.integration-test-helpers index bb34d4588737b..da2bb7cad2e36 100644 --- a/hack/make/.integration-test-helpers +++ b/hack/make/.integration-test-helpers @@ -84,6 +84,7 @@ test_env() { env -i \ DEST="$ABS_DEST" \ DOCKER_API_VERSION="$DOCKER_API_VERSION" \ + DOCKER_BUILDKIT="$DOCKER_BUILDKIT" \ DOCKER_INTEGRATION_DAEMON_DEST="$DOCKER_INTEGRATION_DAEMON_DEST" \ DOCKER_TLS_VERIFY="$DOCKER_TEST_TLS_VERIFY" \ DOCKER_CERT_PATH="$DOCKER_TEST_CERT_PATH" \ diff --git a/hack/make/dynbinary-daemon b/hack/make/dynbinary-daemon index d1c0070e62efe..c712bfefc281b 100644 --- a/hack/make/dynbinary-daemon +++ b/hack/make/dynbinary-daemon @@ -5,6 +5,7 @@ set -e export IAMSTATIC='false' export LDFLAGS_STATIC_DOCKER='' export BUILDFLAGS=( "${BUILDFLAGS[@]/netgo /}" ) # disable netgo, since we don't need it for a dynamic binary + export BUILDFLAGS=( "${BUILDFLAGS[@]/osusergo /}" ) # ditto for osusergo export BUILDFLAGS=( "${BUILDFLAGS[@]/static_build /}" ) # we're not building a "static" binary here source "${MAKEDIR}/.binary" ) diff --git a/image/fs_test.go b/image/fs_test.go index e8c120a0032cd..6290c2b66e841 100644 --- a/image/fs_test.go +++ b/image/fs_test.go @@ -10,9 +10,9 @@ import ( "path/filepath" "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/opencontainers/go-digest" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func defaultFSStoreBackend(t *testing.T) (StoreBackend, func()) { diff --git a/image/image_test.go b/image/image_test.go index dfb438b4d316f..981be0b68c698 100644 --- a/image/image_test.go +++ b/image/image_test.go @@ -10,8 +10,8 @@ import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/layer" "github.com/google/go-cmp/cmp" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) const sampleImageJSON = `{ diff --git a/image/store.go b/image/store.go index 9fd7d7dcf3737..1a8a8a24516c2 100644 --- a/image/store.go +++ b/image/store.go @@ -76,7 +76,8 @@ func (is *store) restore() error { var l layer.Layer if chainID := img.RootFS.ChainID(); chainID != "" { if !system.IsOSSupported(img.OperatingSystem()) { - return system.ErrNotSupportedOperatingSystem + logrus.Errorf("not restoring image with unsupported operating system %v, %v, %s", dgst, chainID, img.OperatingSystem()) + return nil } l, err = is.lss[img.OperatingSystem()].Get(chainID) if err != nil { diff --git a/image/store_test.go b/image/store_test.go index d59cde9195f8d..0edf3282af471 100644 --- a/image/store_test.go +++ b/image/store_test.go @@ -6,9 +6,9 @@ import ( "testing" "github.com/docker/docker/layer" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/opencontainers/go-digest" + "gotest.tools/assert" + "gotest.tools/assert/cmp" ) func TestRestore(t *testing.T) { diff --git a/image/tarexport/load.go b/image/tarexport/load.go index c89dd08f93ca4..786214383eaf3 100644 --- a/image/tarexport/load.go +++ b/image/tarexport/load.go @@ -11,6 +11,7 @@ import ( "reflect" "runtime" + "github.com/containerd/containerd/platforms" "github.com/docker/distribution" "github.com/docker/distribution/reference" "github.com/docker/docker/image" @@ -421,9 +422,11 @@ func checkCompatibleOS(imageOS string) error { if runtime.GOOS != "windows" && imageOS == "windows" { return fmt.Errorf("cannot load %s image on %s", imageOS, runtime.GOOS) } - // Finally, check the image OS is supported for the platform. - if err := system.ValidatePlatform(system.ParsePlatform(imageOS)); err != nil { - return fmt.Errorf("cannot load %s image on %s: %s", imageOS, runtime.GOOS, err) + + p, err := platforms.Parse(imageOS) + if err != nil { + return err } - return nil + + return system.ValidatePlatform(p) } diff --git a/integration-cli/cli/build/build.go b/integration-cli/cli/build/build.go index 71048d0d6ddac..0b10ea79f81e5 100644 --- a/integration-cli/cli/build/build.go +++ b/integration-cli/cli/build/build.go @@ -5,7 +5,7 @@ import ( "strings" "github.com/docker/docker/internal/test/fakecontext" - "github.com/gotestyourself/gotestyourself/icmd" + "gotest.tools/icmd" ) type testingT interface { diff --git a/integration-cli/cli/cli.go b/integration-cli/cli/cli.go index 17f3fd52caf06..bc3f3c194e52e 100644 --- a/integration-cli/cli/cli.go +++ b/integration-cli/cli/cli.go @@ -8,9 +8,9 @@ import ( "github.com/docker/docker/integration-cli/daemon" "github.com/docker/docker/integration-cli/environment" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/icmd" "github.com/pkg/errors" + "gotest.tools/assert" + "gotest.tools/icmd" ) var testEnv *environment.Execution diff --git a/integration-cli/daemon/daemon.go b/integration-cli/daemon/daemon.go index fcbbfdfb0ce39..3d1fa38d5dde3 100644 --- a/integration-cli/daemon/daemon.go +++ b/integration-cli/daemon/daemon.go @@ -8,9 +8,9 @@ import ( "github.com/docker/docker/integration-cli/checker" "github.com/docker/docker/internal/test/daemon" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/icmd" "github.com/pkg/errors" + "gotest.tools/assert" + "gotest.tools/icmd" ) type testingT interface { diff --git a/integration-cli/daemon/daemon_swarm.go b/integration-cli/daemon/daemon_swarm.go index c38f5a69e067b..4a6ce8a5c5142 100644 --- a/integration-cli/daemon/daemon_swarm.go +++ b/integration-cli/daemon/daemon_swarm.go @@ -11,7 +11,7 @@ import ( "github.com/docker/docker/client" "github.com/docker/docker/integration-cli/checker" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) // CheckServiceTasksInState returns the number of tasks with a matching state, diff --git a/integration-cli/docker_api_build_test.go b/integration-cli/docker_api_build_test.go index 581df8d5c709e..144acbd046e4e 100644 --- a/integration-cli/docker_api_build_test.go +++ b/integration-cli/docker_api_build_test.go @@ -19,8 +19,8 @@ import ( "github.com/docker/docker/internal/test/fakestorage" "github.com/docker/docker/internal/test/request" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func (s *DockerSuite) TestBuildAPIDockerFileRemote(c *check.C) { diff --git a/integration-cli/docker_api_build_windows_test.go b/integration-cli/docker_api_build_windows_test.go index 92d3f5e13459f..a605c5be39225 100644 --- a/integration-cli/docker_api_build_windows_test.go +++ b/integration-cli/docker_api_build_windows_test.go @@ -9,8 +9,8 @@ import ( "github.com/docker/docker/internal/test/fakecontext" "github.com/docker/docker/internal/test/request" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func (s *DockerSuite) TestBuildWithRecycleBin(c *check.C) { diff --git a/integration-cli/docker_api_containers_test.go b/integration-cli/docker_api_containers_test.go index 31d6077880d13..ca90e152f6f12 100644 --- a/integration-cli/docker_api_containers_test.go +++ b/integration-cli/docker_api_containers_test.go @@ -33,9 +33,9 @@ import ( "github.com/docker/docker/volume" "github.com/docker/go-connections/nat" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/poll" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/poll" ) func (s *DockerSuite) TestContainerAPIGetAll(c *check.C) { @@ -1265,6 +1265,7 @@ func (s *DockerSuite) TestContainerAPIDeleteRemoveVolume(c *check.C) { c.Assert(waitRun(id), checker.IsNil) source, err := inspectMountSourceField(id, vol) + c.Assert(err, checker.IsNil) _, err = os.Stat(source) c.Assert(err, checker.IsNil) @@ -2201,6 +2202,7 @@ func (s *DockerSuite) TestContainerKillCustomStopSignal(c *check.C) { defer res.Body.Close() b, err := ioutil.ReadAll(res.Body) + c.Assert(err, checker.IsNil) c.Assert(res.StatusCode, checker.Equals, http.StatusNoContent, check.Commentf(string(b))) err = waitInspect(id, "{{.State.Running}} {{.State.Restarting}}", "false false", 30*time.Second) c.Assert(err, checker.IsNil) diff --git a/integration-cli/docker_api_containers_windows_test.go b/integration-cli/docker_api_containers_windows_test.go index 8b71fc607746b..c569574de5350 100644 --- a/integration-cli/docker_api_containers_windows_test.go +++ b/integration-cli/docker_api_containers_windows_test.go @@ -14,8 +14,8 @@ import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/mount" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func (s *DockerSuite) TestContainersAPICreateMountsBindNamedPipe(c *check.C) { diff --git a/integration-cli/docker_api_exec_resize_test.go b/integration-cli/docker_api_exec_resize_test.go index c561d60f56f64..2db3d3e3170f1 100644 --- a/integration-cli/docker_api_exec_resize_test.go +++ b/integration-cli/docker_api_exec_resize_test.go @@ -71,16 +71,17 @@ func (s *DockerSuite) TestExecResizeImmediatelyAfterExecStart(c *check.C) { defer conn.Close() _, rc, err := request.Post(fmt.Sprintf("/exec/%s/resize?h=24&w=80", execID), request.ContentType("text/plain")) - // It's probably a panic of the daemon if io.ErrUnexpectedEOF is returned. - if err == io.ErrUnexpectedEOF { - return fmt.Errorf("The daemon might have crashed.") + if err != nil { + // It's probably a panic of the daemon if io.ErrUnexpectedEOF is returned. + if err == io.ErrUnexpectedEOF { + return fmt.Errorf("The daemon might have crashed.") + } + // Other error happened, should be reported. + return fmt.Errorf("Fail to exec resize immediately after start. Error: %q", err.Error()) } - if err == nil { - rc.Close() - } + rc.Close() - // We only interested in the io.ErrUnexpectedEOF error, so we return nil otherwise. return nil } diff --git a/integration-cli/docker_api_inspect_test.go b/integration-cli/docker_api_inspect_test.go index 5d7aa5590380e..68055b6c145fa 100644 --- a/integration-cli/docker_api_inspect_test.go +++ b/integration-cli/docker_api_inspect_test.go @@ -10,8 +10,8 @@ import ( "github.com/docker/docker/client" "github.com/docker/docker/integration-cli/checker" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func (s *DockerSuite) TestInspectAPIContainerResponse(c *check.C) { diff --git a/integration-cli/docker_api_ipcmode_test.go b/integration-cli/docker_api_ipcmode_test.go index 886ff88d201b5..e37a875e8edef 100644 --- a/integration-cli/docker_api_ipcmode_test.go +++ b/integration-cli/docker_api_ipcmode_test.go @@ -46,72 +46,6 @@ func testIpcCheckDevExists(mm string) (bool, error) { return false, s.Err() } -// testIpcNonePrivateShareable is a helper function to test "none", -// "private" and "shareable" modes. -func testIpcNonePrivateShareable(c *check.C, mode string, mustBeMounted bool, mustBeShared bool) { - cfg := container.Config{ - Image: "busybox", - Cmd: []string{"top"}, - } - hostCfg := container.HostConfig{ - IpcMode: container.IpcMode(mode), - } - ctx := context.Background() - - client := testEnv.APIClient() - - resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "") - c.Assert(err, checker.IsNil) - c.Assert(len(resp.Warnings), checker.Equals, 0) - - err = client.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}) - c.Assert(err, checker.IsNil) - - // get major:minor pair for /dev/shm from container's /proc/self/mountinfo - cmd := "awk '($5 == \"/dev/shm\") {printf $3}' /proc/self/mountinfo" - mm := cli.DockerCmd(c, "exec", "-i", resp.ID, "sh", "-c", cmd).Combined() - if !mustBeMounted { - c.Assert(mm, checker.Equals, "") - // no more checks to perform - return - } - c.Assert(mm, checker.Matches, "^[0-9]+:[0-9]+$") - - shared, err := testIpcCheckDevExists(mm) - c.Assert(err, checker.IsNil) - c.Logf("[testIpcPrivateShareable] ipcmode: %v, ipcdev: %v, shared: %v, mustBeShared: %v\n", mode, mm, shared, mustBeShared) - c.Assert(shared, checker.Equals, mustBeShared) -} - -/* TestAPIIpcModeNone checks the container "none" IPC mode - * (--ipc none) works as expected. It makes sure there is no - * /dev/shm mount inside the container. - */ -func (s *DockerSuite) TestAPIIpcModeNone(c *check.C) { - testRequires(c, DaemonIsLinux, MinimumAPIVersion("1.32")) - testIpcNonePrivateShareable(c, "none", false, false) -} - -/* TestAPIIpcModePrivate checks the container private IPC mode - * (--ipc private) works as expected. It gets the minor:major pair - * of /dev/shm mount from the container, and makes sure there is no - * such pair on the host. - */ -func (s *DockerSuite) TestAPIIpcModePrivate(c *check.C) { - testRequires(c, DaemonIsLinux, SameHostDaemon) - testIpcNonePrivateShareable(c, "private", true, false) -} - -/* TestAPIIpcModeShareable checks the container shareable IPC mode - * (--ipc shareable) works as expected. It gets the minor:major pair - * of /dev/shm mount from the container, and makes sure such pair - * also exists on the host. - */ -func (s *DockerSuite) TestAPIIpcModeShareable(c *check.C) { - testRequires(c, DaemonIsLinux, SameHostDaemon) - testIpcNonePrivateShareable(c, "shareable", true, true) -} - // testIpcContainer is a helper function to test --ipc container:NNN mode in various scenarios func testIpcContainer(s *DockerSuite, c *check.C, donorMode string, mustWork bool) { cfg := container.Config{ diff --git a/integration-cli/docker_api_network_test.go b/integration-cli/docker_api_network_test.go index 9c22cb7e3ae5e..9ec2ba90e870f 100644 --- a/integration-cli/docker_api_network_test.go +++ b/integration-cli/docker_api_network_test.go @@ -26,24 +26,6 @@ func (s *DockerSuite) TestAPINetworkGetDefaults(c *check.C) { } } -func (s *DockerSuite) TestAPINetworkCreateDelete(c *check.C) { - testRequires(c, DaemonIsLinux) - // Create a network - name := "testnetwork" - config := types.NetworkCreateRequest{ - Name: name, - NetworkCreate: types.NetworkCreate{ - CheckDuplicate: true, - }, - } - id := createNetwork(c, config, http.StatusCreated) - c.Assert(isNetworkAvailable(c, name), checker.Equals, true) - - // delete the network and make sure it is deleted - deleteNetwork(c, id, true) - c.Assert(isNetworkAvailable(c, name), checker.Equals, false) -} - func (s *DockerSuite) TestAPINetworkCreateCheckDuplicate(c *check.C) { testRequires(c, DaemonIsLinux) name := "testcheckduplicate" diff --git a/integration-cli/docker_api_swarm_service_test.go b/integration-cli/docker_api_swarm_service_test.go index 2e27c7e93dfa4..1a826c99c6bd3 100644 --- a/integration-cli/docker_api_swarm_service_test.go +++ b/integration-cli/docker_api_swarm_service_test.go @@ -17,8 +17,8 @@ import ( "github.com/docker/docker/integration-cli/daemon" testdaemon "github.com/docker/docker/internal/test/daemon" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" "golang.org/x/sys/unix" + "gotest.tools/icmd" ) func setPortConfig(portConfig []swarm.PortConfig) testdaemon.ServiceConstructor { diff --git a/integration-cli/docker_api_swarm_test.go b/integration-cli/docker_api_swarm_test.go index 11cdd89215e0c..6a31dd209f01f 100644 --- a/integration-cli/docker_api_swarm_test.go +++ b/integration-cli/docker_api_swarm_test.go @@ -26,8 +26,8 @@ import ( "github.com/docker/docker/internal/test/request" "github.com/docker/swarmkit/ca" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) var defaultReconciliationTimeout = 30 * time.Second diff --git a/integration-cli/docker_cli_attach_test.go b/integration-cli/docker_cli_attach_test.go index 353cb65e5d177..ef2c708bbe63a 100644 --- a/integration-cli/docker_cli_attach_test.go +++ b/integration-cli/docker_cli_attach_test.go @@ -12,7 +12,7 @@ import ( "github.com/docker/docker/integration-cli/cli" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" + "gotest.tools/icmd" ) const attachWait = 5 * time.Second diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index 0aae7c4575453..f6d5fe23f3faf 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -16,7 +16,6 @@ import ( "text/template" "time" - "github.com/moby/buildkit/frontend/dockerfile/command" "github.com/docker/docker/integration-cli/checker" "github.com/docker/docker/integration-cli/cli" "github.com/docker/docker/integration-cli/cli/build" @@ -26,8 +25,9 @@ import ( "github.com/docker/docker/internal/testutil" "github.com/docker/docker/pkg/archive" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" + "github.com/moby/buildkit/frontend/dockerfile/command" "github.com/opencontainers/go-digest" + "gotest.tools/icmd" ) func (s *DockerSuite) TestBuildJSONEmptyRun(c *check.C) { @@ -1012,10 +1012,6 @@ func (s *DockerSuite) TestBuildAddBadLinksVolume(c *check.C) { ADD foo.txt /x/` targetFile = "foo.txt" ) - var ( - name = "test-link-absolute-volume" - dockerfile = "" - ) tempDir, err := ioutil.TempDir("", "test-link-absolute-volume-temp-") if err != nil { @@ -1023,7 +1019,7 @@ func (s *DockerSuite) TestBuildAddBadLinksVolume(c *check.C) { } defer os.RemoveAll(tempDir) - dockerfile = fmt.Sprintf(dockerfileTemplate, tempDir) + dockerfile := fmt.Sprintf(dockerfileTemplate, tempDir) nonExistingFile := filepath.Join(tempDir, targetFile) ctx := fakecontext.New(c, "", fakecontext.WithDockerfile(dockerfile)) @@ -1040,7 +1036,7 @@ func (s *DockerSuite) TestBuildAddBadLinksVolume(c *check.C) { c.Fatal(err) } - buildImageSuccessfully(c, name, build.WithExternalBuildContext(ctx)) + buildImageSuccessfully(c, "test-link-absolute-volume", build.WithExternalBuildContext(ctx)) if _, err := os.Stat(nonExistingFile); err == nil || err != nil && !os.IsNotExist(err) { c.Fatalf("%s shouldn't have been written and it shouldn't exist", nonExistingFile) } @@ -2534,7 +2530,7 @@ func (s *DockerSuite) TestBuildDockerignoringBadExclusion(c *check.C) { build.WithFile(".dockerignore", "!\n"), )).Assert(c, icmd.Expected{ ExitCode: 1, - Err: "error checking context: 'illegal exclusion pattern: \"!\"", + Err: `illegal exclusion pattern: "!"`, }) } diff --git a/integration-cli/docker_cli_build_unix_test.go b/integration-cli/docker_cli_build_unix_test.go index d6c43700641ad..8cad28f457371 100644 --- a/integration-cli/docker_cli_build_unix_test.go +++ b/integration-cli/docker_cli_build_unix_test.go @@ -21,7 +21,7 @@ import ( "github.com/docker/docker/internal/test/fakecontext" "github.com/docker/go-units" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" + "gotest.tools/icmd" ) func (s *DockerSuite) TestBuildResourceConstraintsAreUsed(c *check.C) { @@ -126,8 +126,12 @@ func (s *DockerSuite) TestBuildAddChangeOwnership(c *check.C) { // * Run a 1-year-long sleep from a docker build. // * When docker events sees container start, close the "docker build" command // * Wait for docker events to emit a dying event. +// +// TODO(buildkit): this test needs to be rewritten for buildkit. +// It has been manually tested positive. Confirmed issue: docker build output parsing. +// Potential issue: newEventObserver uses docker events, which is not hooked up to buildkit. func (s *DockerSuite) TestBuildCancellationKillsSleep(c *check.C) { - testRequires(c, DaemonIsLinux) + testRequires(c, DaemonIsLinux, TODOBuildkit) name := "testbuildcancellation" observer, err := newEventObserver(c) diff --git a/integration-cli/docker_cli_by_digest_test.go b/integration-cli/docker_cli_by_digest_test.go index ac97e0aecc5f3..006cf11e1a634 100644 --- a/integration-cli/docker_cli_by_digest_test.go +++ b/integration-cli/docker_cli_by_digest_test.go @@ -15,9 +15,9 @@ import ( "github.com/docker/docker/integration-cli/cli" "github.com/docker/docker/integration-cli/cli/build" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/opencontainers/go-digest" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) var ( diff --git a/integration-cli/docker_cli_commit_test.go b/integration-cli/docker_cli_commit_test.go index 79c5f7315646f..d9f6e6875cee0 100644 --- a/integration-cli/docker_cli_commit_test.go +++ b/integration-cli/docker_cli_commit_test.go @@ -46,8 +46,7 @@ func (s *DockerSuite) TestCommitPausedContainer(c *check.C) { cleanedContainerID := strings.TrimSpace(out) dockerCmd(c, "pause", cleanedContainerID) - - out, _ = dockerCmd(c, "commit", cleanedContainerID) + dockerCmd(c, "commit", cleanedContainerID) out = inspectField(c, cleanedContainerID, "State.Paused") // commit should not unpause a paused container diff --git a/integration-cli/docker_cli_config_create_test.go b/integration-cli/docker_cli_config_create_test.go index b82325487420f..c499a2c9cf487 100644 --- a/integration-cli/docker_cli_config_create_test.go +++ b/integration-cli/docker_cli_config_create_test.go @@ -7,108 +7,10 @@ import ( "os" "strings" - "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/integration-cli/checker" "github.com/go-check/check" ) -func (s *DockerSwarmSuite) TestConfigCreate(c *check.C) { - d := s.AddDaemon(c, true, true) - - testName := "test_config" - id := d.CreateConfig(c, swarm.ConfigSpec{ - Annotations: swarm.Annotations{ - Name: testName, - }, - Data: []byte("TESTINGDATA"), - }) - c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id)) - - config := d.GetConfig(c, id) - c.Assert(config.Spec.Name, checker.Equals, testName) -} - -func (s *DockerSwarmSuite) TestConfigCreateWithLabels(c *check.C) { - d := s.AddDaemon(c, true, true) - - testName := "test_config" - id := d.CreateConfig(c, swarm.ConfigSpec{ - Annotations: swarm.Annotations{ - Name: testName, - Labels: map[string]string{ - "key1": "value1", - "key2": "value2", - }, - }, - Data: []byte("TESTINGDATA"), - }) - c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id)) - - config := d.GetConfig(c, id) - c.Assert(config.Spec.Name, checker.Equals, testName) - c.Assert(len(config.Spec.Labels), checker.Equals, 2) - c.Assert(config.Spec.Labels["key1"], checker.Equals, "value1") - c.Assert(config.Spec.Labels["key2"], checker.Equals, "value2") -} - -// Test case for 28884 -func (s *DockerSwarmSuite) TestConfigCreateResolve(c *check.C) { - d := s.AddDaemon(c, true, true) - - name := "test_config" - id := d.CreateConfig(c, swarm.ConfigSpec{ - Annotations: swarm.Annotations{ - Name: name, - }, - Data: []byte("foo"), - }) - c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id)) - - fake := d.CreateConfig(c, swarm.ConfigSpec{ - Annotations: swarm.Annotations{ - Name: id, - }, - Data: []byte("fake foo"), - }) - c.Assert(fake, checker.Not(checker.Equals), "", check.Commentf("configs: %s", fake)) - - out, err := d.Cmd("config", "ls") - c.Assert(err, checker.IsNil) - c.Assert(out, checker.Contains, name) - c.Assert(out, checker.Contains, fake) - - out, err = d.Cmd("config", "rm", id) - c.Assert(out, checker.Contains, id) - - // Fake one will remain - out, err = d.Cmd("config", "ls") - c.Assert(err, checker.IsNil) - c.Assert(out, checker.Not(checker.Contains), name) - c.Assert(out, checker.Contains, fake) - - // Remove based on name prefix of the fake one - // (which is the same as the ID of foo one) should not work - // as search is only done based on: - // - Full ID - // - Full Name - // - Partial ID (prefix) - out, err = d.Cmd("config", "rm", id[:5]) - c.Assert(out, checker.Not(checker.Contains), id) - out, err = d.Cmd("config", "ls") - c.Assert(err, checker.IsNil) - c.Assert(out, checker.Not(checker.Contains), name) - c.Assert(out, checker.Contains, fake) - - // Remove based on ID prefix of the fake one should succeed - out, err = d.Cmd("config", "rm", fake[:5]) - c.Assert(out, checker.Contains, fake[:5]) - out, err = d.Cmd("config", "ls") - c.Assert(err, checker.IsNil) - c.Assert(out, checker.Not(checker.Contains), name) - c.Assert(out, checker.Not(checker.Contains), id) - c.Assert(out, checker.Not(checker.Contains), fake) -} - func (s *DockerSwarmSuite) TestConfigCreateWithFile(c *check.C) { d := s.AddDaemon(c, true, true) diff --git a/integration-cli/docker_cli_cp_test.go b/integration-cli/docker_cli_cp_test.go index b33be6b2099f9..a1997335b10ba 100644 --- a/integration-cli/docker_cli_cp_test.go +++ b/integration-cli/docker_cli_cp_test.go @@ -12,7 +12,7 @@ import ( "github.com/docker/docker/integration-cli/checker" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" + "gotest.tools/icmd" ) const ( @@ -423,6 +423,7 @@ func (s *DockerSuite) TestCpSpecialFiles(c *check.C) { expected := readContainerFile(c, containerID, "resolv.conf") actual, err := ioutil.ReadFile(outDir + "/resolv.conf") + c.Assert(err, checker.IsNil) // Expected copied file to be duplicate of the container resolvconf c.Assert(bytes.Equal(actual, expected), checker.True) @@ -432,6 +433,7 @@ func (s *DockerSuite) TestCpSpecialFiles(c *check.C) { expected = readContainerFile(c, containerID, "hosts") actual, err = ioutil.ReadFile(outDir + "/hosts") + c.Assert(err, checker.IsNil) // Expected copied file to be duplicate of the container hosts c.Assert(bytes.Equal(actual, expected), checker.True) @@ -639,6 +641,7 @@ func (s *DockerSuite) TestCpSymlinkFromConToHostFollowSymlink(c *check.C) { expected := []byte(cpContainerContents) actual, err := ioutil.ReadFile(expectedPath) + c.Assert(err, checker.IsNil) if !bytes.Equal(actual, expected) { c.Fatalf("Expected copied file to be duplicate of the container symbol link target") diff --git a/integration-cli/docker_cli_daemon_plugins_test.go b/integration-cli/docker_cli_daemon_plugins_test.go index 2f66bc05e20ef..69e190c30dd5c 100644 --- a/integration-cli/docker_cli_daemon_plugins_test.go +++ b/integration-cli/docker_cli_daemon_plugins_test.go @@ -8,8 +8,8 @@ import ( "github.com/docker/docker/integration-cli/checker" "github.com/docker/docker/pkg/mount" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" "golang.org/x/sys/unix" + "gotest.tools/icmd" ) // TestDaemonRestartWithPluginEnabled tests state restore for an enabled plugin diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go index 347696e8a4428..9fc96c56aec02 100644 --- a/integration-cli/docker_cli_daemon_test.go +++ b/integration-cli/docker_cli_daemon_test.go @@ -39,9 +39,9 @@ import ( "github.com/docker/libnetwork/iptables" "github.com/docker/libtrust" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" "github.com/kr/pty" "golang.org/x/sys/unix" + "gotest.tools/icmd" ) // TestLegacyDaemonCommand test starting docker daemon using "deprecated" docker daemon @@ -448,10 +448,10 @@ func (s *DockerDaemonSuite) TestDaemonIPv6FixedCIDRAndMac(c *check.C) { s.d.StartWithBusybox(c, "--ipv6", "--fixed-cidr-v6=2001:db8:1::/64") - out, err := s.d.Cmd("run", "-itd", "--name=ipv6test", "--mac-address", "AA:BB:CC:DD:EE:FF", "busybox") + _, err := s.d.Cmd("run", "-itd", "--name=ipv6test", "--mac-address", "AA:BB:CC:DD:EE:FF", "busybox") c.Assert(err, checker.IsNil) - out, err = s.d.Cmd("inspect", "--format", "{{.NetworkSettings.Networks.bridge.GlobalIPv6Address}}", "ipv6test") + out, err := s.d.Cmd("inspect", "--format", "{{.NetworkSettings.Networks.bridge.GlobalIPv6Address}}", "ipv6test") c.Assert(err, checker.IsNil) c.Assert(strings.Trim(out, " \r\n'"), checker.Equals, "2001:db8:1::aabb:ccdd:eeff") } @@ -742,6 +742,7 @@ func (s *DockerDaemonSuite) TestDaemonBridgeFixedCidr2(c *check.C) { defer d.Cmd("stop", "bb") out, err = d.Cmd("exec", "bb", "/bin/sh", "-c", "ifconfig eth0 | awk '/inet addr/{print substr($2,6)}'") + c.Assert(err, check.IsNil) c.Assert(out, checker.Equals, "10.2.2.0\n") out, err = d.Cmd("run", "--rm", "busybox", "/bin/sh", "-c", "ifconfig eth0 | awk '/inet addr/{print substr($2,6)}'") @@ -1811,10 +1812,10 @@ func (s *DockerDaemonSuite) TestDaemonNoSpaceLeftOnDeviceError(c *check.C) { dockerCmd(c, "run", "--rm", "-v", testDir+":/test", "busybox", "sh", "-c", "dd of=/test/testfs.img bs=1M seek=3 count=0") icmd.RunCommand("mkfs.ext4", "-F", filepath.Join(testDir, "testfs.img")).Assert(c, icmd.Success) - dockerCmd(c, "run", "--privileged", "--rm", "-v", testDir+":/test:shared", "busybox", "sh", "-c", "mkdir -p /test/test-mount && mount -n /test/testfs.img /test/test-mount") + dockerCmd(c, "run", "--privileged", "--rm", "-v", testDir+":/test:shared", "busybox", "sh", "-c", "mkdir -p /test/test-mount/vfs && mount -n /test/testfs.img /test/test-mount/vfs") defer mount.Unmount(filepath.Join(testDir, "test-mount")) - s.d.Start(c, "--data-root", filepath.Join(testDir, "test-mount")) + s.d.Start(c, "--storage-driver", "vfs", "--data-root", filepath.Join(testDir, "test-mount")) defer s.d.Stop(c) // pull a repository large enough to overfill the mounted filesystem @@ -1944,11 +1945,12 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithNames(c *check.C) { test2ID := strings.TrimSpace(out) out, err = s.d.Cmd("run", "-d", "--name=test3", "--link", "test2:abc", "busybox", "top") + c.Assert(err, check.IsNil) test3ID := strings.TrimSpace(out) s.d.Restart(c) - out, err = s.d.Cmd("create", "--name=test", "busybox") + _, err = s.d.Cmd("create", "--name=test", "busybox") c.Assert(err, check.NotNil, check.Commentf("expected error trying to create container with duplicate name")) // this one is no longer needed, removing simplifies the remainder of the test out, err = s.d.Cmd("rm", "-f", "test") @@ -2615,6 +2617,7 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithAutoRemoveContainer(c *check.C) c.Assert(err, checker.IsNil, check.Commentf("run top2: %v", out)) out, err = s.d.Cmd("ps") + c.Assert(err, checker.IsNil) c.Assert(out, checker.Contains, "top1", check.Commentf("top1 should be running")) c.Assert(out, checker.Contains, "top2", check.Commentf("top2 should be running")) @@ -2639,11 +2642,11 @@ func (s *DockerDaemonSuite) TestDaemonRestartSaveContainerExitCode(c *check.C) { // captured, so `.State.Error` is empty. // See the discussion on https://github.com/docker/docker/pull/30227#issuecomment-274161426, // and https://github.com/docker/docker/pull/26061#r78054578 for more information. - out, err := s.d.Cmd("run", "--name", containerName, "--init=false", "busybox", "toto") + _, err := s.d.Cmd("run", "--name", containerName, "--init=false", "busybox", "toto") c.Assert(err, checker.NotNil) // Check that those values were saved on disk - out, err = s.d.Cmd("inspect", "-f", "{{.State.ExitCode}}", containerName) + out, err := s.d.Cmd("inspect", "-f", "{{.State.ExitCode}}", containerName) out = strings.TrimSpace(out) c.Assert(err, checker.IsNil) c.Assert(out, checker.Equals, "127") diff --git a/integration-cli/docker_cli_events_test.go b/integration-cli/docker_cli_events_test.go index 28e418ea3b477..db1e34020f58e 100644 --- a/integration-cli/docker_cli_events_test.go +++ b/integration-cli/docker_cli_events_test.go @@ -20,7 +20,7 @@ import ( "github.com/docker/docker/integration-cli/cli" "github.com/docker/docker/integration-cli/cli/build" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" + "gotest.tools/icmd" ) func (s *DockerSuite) TestEventsTimestampFormats(c *check.C) { diff --git a/integration-cli/docker_cli_events_unix_test.go b/integration-cli/docker_cli_events_unix_test.go index ad47b49c076bc..343b900342efd 100644 --- a/integration-cli/docker_cli_events_unix_test.go +++ b/integration-cli/docker_cli_events_unix_test.go @@ -436,7 +436,7 @@ func (s *DockerDaemonSuite) TestDaemonEvents(c *check.C) { "(allow-nondistributable-artifacts=[", " cluster-advertise=, ", " cluster-store=, ", - " cluster-store-opts={", + " cluster-store-opts=", " debug=true, ", " default-ipc-mode=", " default-runtime=", diff --git a/integration-cli/docker_cli_exec_test.go b/integration-cli/docker_cli_exec_test.go index d0557c56a0a4b..e97fb851402df 100644 --- a/integration-cli/docker_cli_exec_test.go +++ b/integration-cli/docker_cli_exec_test.go @@ -20,7 +20,7 @@ import ( "github.com/docker/docker/integration-cli/cli" "github.com/docker/docker/integration-cli/cli/build" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" + "gotest.tools/icmd" ) func (s *DockerSuite) TestExec(c *check.C) { diff --git a/integration-cli/docker_cli_export_import_test.go b/integration-cli/docker_cli_export_import_test.go index 6405c1bb5e508..d0dac97367665 100644 --- a/integration-cli/docker_cli_export_import_test.go +++ b/integration-cli/docker_cli_export_import_test.go @@ -6,7 +6,7 @@ import ( "github.com/docker/docker/integration-cli/checker" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" + "gotest.tools/icmd" ) // TODO: Move this test to docker/cli, as it is essentially the same test diff --git a/integration-cli/docker_cli_health_test.go b/integration-cli/docker_cli_health_test.go index 632830c60b26e..4fb63994bb699 100644 --- a/integration-cli/docker_cli_health_test.go +++ b/integration-cli/docker_cli_health_test.go @@ -81,7 +81,7 @@ func (s *DockerSuite) TestHealth(c *check.C) { dockerCmd(c, "rm", "-f", name) // Disable the check from the CLI - out, _ = dockerCmd(c, "create", "--name=noh", "--no-healthcheck", imageName) + dockerCmd(c, "create", "--name=noh", "--no-healthcheck", imageName) out, _ = dockerCmd(c, "inspect", "--format={{.Config.Healthcheck.Test}}", "noh") c.Check(out, checker.Equals, "[NONE]\n") dockerCmd(c, "rm", "noh") @@ -90,7 +90,7 @@ func (s *DockerSuite) TestHealth(c *check.C) { buildImageSuccessfully(c, "no_healthcheck", build.WithDockerfile(`FROM testhealth HEALTHCHECK NONE`)) - out, _ = dockerCmd(c, "inspect", "--format={{.ContainerConfig.Healthcheck.Test}}", "no_healthcheck") + out, _ = dockerCmd(c, "inspect", "--format={{.Config.Healthcheck.Test}}", "no_healthcheck") c.Check(out, checker.Equals, "[NONE]\n") // Enable the checks from the CLI diff --git a/integration-cli/docker_cli_images_test.go b/integration-cli/docker_cli_images_test.go index 2a1152eb5044f..0dd319fbc9ac5 100644 --- a/integration-cli/docker_cli_images_test.go +++ b/integration-cli/docker_cli_images_test.go @@ -14,7 +14,7 @@ import ( "github.com/docker/docker/integration-cli/cli/build" "github.com/docker/docker/pkg/stringid" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" + "gotest.tools/icmd" ) func (s *DockerSuite) TestImagesEnsureImageIsListed(c *check.C) { diff --git a/integration-cli/docker_cli_import_test.go b/integration-cli/docker_cli_import_test.go index eb0fe2cf8cb7b..9f8e9158036c9 100644 --- a/integration-cli/docker_cli_import_test.go +++ b/integration-cli/docker_cli_import_test.go @@ -12,7 +12,7 @@ import ( "github.com/docker/docker/integration-cli/checker" "github.com/docker/docker/integration-cli/cli" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" + "gotest.tools/icmd" ) func (s *DockerSuite) TestImportDisplay(c *check.C) { diff --git a/integration-cli/docker_cli_inspect_test.go b/integration-cli/docker_cli_inspect_test.go index a1130aebf5a7e..d027c447750d1 100644 --- a/integration-cli/docker_cli_inspect_test.go +++ b/integration-cli/docker_cli_inspect_test.go @@ -12,7 +12,7 @@ import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/integration-cli/checker" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" + "gotest.tools/icmd" ) func checkValidGraphDriver(c *check.C, name string) { diff --git a/integration-cli/docker_cli_logs_test.go b/integration-cli/docker_cli_logs_test.go index fb99b2807d53c..60ea333815deb 100644 --- a/integration-cli/docker_cli_logs_test.go +++ b/integration-cli/docker_cli_logs_test.go @@ -12,7 +12,7 @@ import ( "github.com/docker/docker/integration-cli/cli" "github.com/docker/docker/pkg/jsonmessage" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" + "gotest.tools/icmd" ) // This used to work, it test a log of PageSize-1 (gh#4851) @@ -181,14 +181,14 @@ func (s *DockerSuite) TestLogsSinceFutureFollow(c *check.C) { // TODO Windows TP5 - Figure out why this test is so flakey. Disabled for now. testRequires(c, DaemonIsLinux) name := "testlogssincefuturefollow" - out, _ := dockerCmd(c, "run", "-d", "--name", name, "busybox", "/bin/sh", "-c", `for i in $(seq 1 5); do echo log$i; sleep 1; done`) + dockerCmd(c, "run", "-d", "--name", name, "busybox", "/bin/sh", "-c", `for i in $(seq 1 5); do echo log$i; sleep 1; done`) // Extract one timestamp from the log file to give us a starting point for // our `--since` argument. Because the log producer runs in the background, // we need to check repeatedly for some output to be produced. var timestamp string for i := 0; i != 100 && timestamp == ""; i++ { - if out, _ = dockerCmd(c, "logs", "-t", name); out == "" { + if out, _ := dockerCmd(c, "logs", "-t", name); out == "" { time.Sleep(time.Millisecond * 100) // Retry } else { timestamp = strings.Split(strings.Split(out, "\n")[0], " ")[0] @@ -200,7 +200,7 @@ func (s *DockerSuite) TestLogsSinceFutureFollow(c *check.C) { c.Assert(err, check.IsNil) since := t.Unix() + 2 - out, _ = dockerCmd(c, "logs", "-t", "-f", fmt.Sprintf("--since=%v", since), name) + out, _ := dockerCmd(c, "logs", "-t", "-f", fmt.Sprintf("--since=%v", since), name) c.Assert(out, checker.Not(checker.HasLen), 0, check.Commentf("cannot read from empty log")) lines := strings.Split(strings.TrimSpace(out), "\n") for _, v := range lines { diff --git a/integration-cli/docker_cli_network_unix_test.go b/integration-cli/docker_cli_network_unix_test.go index 1087d0045dad0..60f211cda8717 100644 --- a/integration-cli/docker_cli_network_unix_test.go +++ b/integration-cli/docker_cli_network_unix_test.go @@ -27,9 +27,9 @@ import ( remoteipam "github.com/docker/libnetwork/ipams/remote/api" "github.com/docker/libnetwork/netlabel" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" "github.com/vishvananda/netlink" "golang.org/x/sys/unix" + "gotest.tools/icmd" ) const dummyNetworkDriver = "dummy-network-driver" @@ -348,7 +348,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkLsFilter(c *check.C) { out, _ = dockerCmd(c, "network", "ls", "-f", "type=custom", "-f", "type=builtin") assertNwList(c, out, []string{"bridge", "dev", "host", "none"}) - out, _ = dockerCmd(c, "network", "create", "--label", testLabel+"="+testValue, testNet) + dockerCmd(c, "network", "create", "--label", testLabel+"="+testValue, testNet) assertNwIsAvailable(c, testNet) out, _ = dockerCmd(c, "network", "ls", "-f", "label="+testLabel) @@ -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") @@ -880,8 +880,6 @@ func (s *DockerNetworkSuite) TestDockerNetworkAnonymousEndpoint(c *check.C) { out, _ = dockerCmd(c, "run", "-d", "--net", cstmBridgeNw, "busybox", "top") cid2 := strings.TrimSpace(out) - hosts2 := readContainerFileWithExec(c, cid2, hostsFile) - // verify first container etc/hosts file has not changed hosts1post := readContainerFileWithExec(c, cid1, hostsFile) c.Assert(string(hosts1), checker.Equals, string(hosts1post), @@ -894,7 +892,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkAnonymousEndpoint(c *check.C) { dockerCmd(c, "network", "connect", cstmBridgeNw1, cid2) - hosts2 = readContainerFileWithExec(c, cid2, hostsFile) + hosts2 := readContainerFileWithExec(c, cid2, hostsFile) hosts1post = readContainerFileWithExec(c, cid1, hostsFile) c.Assert(string(hosts1), checker.Equals, string(hosts1post), check.Commentf("Unexpected %s change on container connect", hostsFile)) @@ -1646,9 +1644,9 @@ func (s *DockerSuite) TestDockerNetworkInternalMode(c *check.C) { c.Assert(waitRun("first"), check.IsNil) dockerCmd(c, "run", "-d", "--net=internal", "--name=second", "busybox:glibc", "top") c.Assert(waitRun("second"), check.IsNil) - out, _, err := dockerCmdWithError("exec", "first", "ping", "-W", "4", "-c", "1", "www.google.com") + out, _, err := dockerCmdWithError("exec", "first", "ping", "-W", "4", "-c", "1", "8.8.8.8") c.Assert(err, check.NotNil) - c.Assert(out, checker.Contains, "ping: bad address") + c.Assert(out, checker.Contains, "100% packet loss") _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first") c.Assert(err, check.IsNil) } diff --git a/integration-cli/docker_cli_plugins_test.go b/integration-cli/docker_cli_plugins_test.go index 391c74aa5d219..3692b6cb7097e 100644 --- a/integration-cli/docker_cli_plugins_test.go +++ b/integration-cli/docker_cli_plugins_test.go @@ -62,10 +62,10 @@ func (ps *DockerPluginSuite) TestPluginBasicOps(c *check.C) { func (ps *DockerPluginSuite) TestPluginForceRemove(c *check.C) { pNameWithTag := ps.getPluginRepoWithTag() - out, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", pNameWithTag) + _, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", pNameWithTag) c.Assert(err, checker.IsNil) - out, _, err = dockerCmdWithError("plugin", "remove", pNameWithTag) + out, _, _ := dockerCmdWithError("plugin", "remove", pNameWithTag) c.Assert(out, checker.Contains, "is enabled") out, _, err = dockerCmdWithError("plugin", "remove", "--force", pNameWithTag) @@ -82,7 +82,7 @@ func (s *DockerSuite) TestPluginActive(c *check.C) { _, _, err = dockerCmdWithError("volume", "create", "-d", pNameWithTag, "--name", "testvol1") c.Assert(err, checker.IsNil) - out, _, err := dockerCmdWithError("plugin", "disable", pNameWithTag) + out, _, _ := dockerCmdWithError("plugin", "disable", pNameWithTag) c.Assert(out, checker.Contains, "in use") _, _, err = dockerCmdWithError("volume", "rm", "testvol1") @@ -98,21 +98,21 @@ func (s *DockerSuite) TestPluginActive(c *check.C) { func (s *DockerSuite) TestPluginActiveNetwork(c *check.C) { testRequires(c, DaemonIsLinux, IsAmd64, Network) - out, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", npNameWithTag) + _, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", npNameWithTag) c.Assert(err, checker.IsNil) - out, _, err = dockerCmdWithError("network", "create", "-d", npNameWithTag, "test") + out, _, err := dockerCmdWithError("network", "create", "-d", npNameWithTag, "test") c.Assert(err, checker.IsNil) nID := strings.TrimSpace(out) - out, _, err = dockerCmdWithError("plugin", "remove", npNameWithTag) + out, _, _ = dockerCmdWithError("plugin", "remove", npNameWithTag) c.Assert(out, checker.Contains, "is in use") _, _, err = dockerCmdWithError("network", "rm", nID) c.Assert(err, checker.IsNil) - out, _, err = dockerCmdWithError("plugin", "remove", npNameWithTag) + out, _, _ = dockerCmdWithError("plugin", "remove", npNameWithTag) c.Assert(out, checker.Contains, "is enabled") _, _, err = dockerCmdWithError("plugin", "disable", npNameWithTag) @@ -400,7 +400,7 @@ func (ps *DockerPluginSuite) TestPluginIDPrefix(c *check.C) { c.Assert(out, checker.Contains, "false") // Remove - out, _, err = dockerCmdWithError("plugin", "remove", id[:5]) + _, _, err = dockerCmdWithError("plugin", "remove", id[:5]) c.Assert(err, checker.IsNil) // List returns none out, _, err = dockerCmdWithError("plugin", "ls") diff --git a/integration-cli/docker_cli_proxy_test.go b/integration-cli/docker_cli_proxy_test.go index bdb4772592925..52159aa9c538e 100644 --- a/integration-cli/docker_cli_proxy_test.go +++ b/integration-cli/docker_cli_proxy_test.go @@ -6,7 +6,7 @@ import ( "github.com/docker/docker/integration-cli/checker" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" + "gotest.tools/icmd" ) func (s *DockerSuite) TestCLIProxyDisableProxyUnixSock(c *check.C) { diff --git a/integration-cli/docker_cli_prune_unix_test.go b/integration-cli/docker_cli_prune_unix_test.go index 259b48676644e..d60420b591481 100644 --- a/integration-cli/docker_cli_prune_unix_test.go +++ b/integration-cli/docker_cli_prune_unix_test.go @@ -15,7 +15,7 @@ import ( "github.com/docker/docker/integration-cli/cli/build" "github.com/docker/docker/integration-cli/daemon" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" + "gotest.tools/icmd" ) func pruneNetworkAndVerify(c *check.C, d *daemon.Daemon, kept, pruned []string) { diff --git a/integration-cli/docker_cli_ps_test.go b/integration-cli/docker_cli_ps_test.go index 771c9d70d5d80..824ca683ba5cc 100644 --- a/integration-cli/docker_cli_ps_test.go +++ b/integration-cli/docker_cli_ps_test.go @@ -13,7 +13,7 @@ import ( "github.com/docker/docker/integration-cli/cli/build" "github.com/docker/docker/pkg/stringid" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" + "gotest.tools/icmd" ) func (s *DockerSuite) TestPsListContainersBase(c *check.C) { @@ -792,19 +792,14 @@ func (s *DockerSuite) TestPsListContainersFilterNetwork(c *check.C) { } func (s *DockerSuite) TestPsByOrder(c *check.C) { - name1 := "xyz-abc" - out := runSleepingContainer(c, "--name", name1) + out := runSleepingContainer(c, "--name", "xyz-abc") container1 := strings.TrimSpace(out) - name2 := "xyz-123" - out = runSleepingContainer(c, "--name", name2) + out = runSleepingContainer(c, "--name", "xyz-123") container2 := strings.TrimSpace(out) - name3 := "789-abc" - out = runSleepingContainer(c, "--name", name3) - - name4 := "789-123" - out = runSleepingContainer(c, "--name", name4) + runSleepingContainer(c, "--name", "789-abc") + runSleepingContainer(c, "--name", "789-123") // Run multiple time should have the same result out = cli.DockerCmd(c, "ps", "--no-trunc", "-q", "-f", "name=xyz").Combined() diff --git a/integration-cli/docker_cli_pull_local_test.go b/integration-cli/docker_cli_pull_local_test.go index 31afdfb539602..33d4ae5e7c612 100644 --- a/integration-cli/docker_cli_pull_local_test.go +++ b/integration-cli/docker_cli_pull_local_test.go @@ -16,8 +16,8 @@ import ( "github.com/docker/docker/integration-cli/checker" "github.com/docker/docker/integration-cli/cli/build" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" "github.com/opencontainers/go-digest" + "gotest.tools/icmd" ) // testPullImageWithAliases pulls a specific image tag and verifies that any aliases (i.e., other diff --git a/integration-cli/docker_cli_push_test.go b/integration-cli/docker_cli_push_test.go index 382260a5c75b6..01ad829192b19 100644 --- a/integration-cli/docker_cli_push_test.go +++ b/integration-cli/docker_cli_push_test.go @@ -14,7 +14,7 @@ import ( "github.com/docker/docker/integration-cli/checker" "github.com/docker/docker/integration-cli/cli/build" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" + "gotest.tools/icmd" ) // Pushing an image to a private registry. diff --git a/integration-cli/docker_cli_restart_test.go b/integration-cli/docker_cli_restart_test.go index 1b4c928b9a056..02d4254c35356 100644 --- a/integration-cli/docker_cli_restart_test.go +++ b/integration-cli/docker_cli_restart_test.go @@ -224,6 +224,7 @@ func (s *DockerSuite) TestRestartWithPolicyUserDefinedNetwork(c *check.C) { c.Assert(err, check.IsNil) err = waitInspect("second", "{{.State.Status}}", "running", 5*time.Second) + c.Assert(err, check.IsNil) // ping to first and its alias foo must still succeed _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first") diff --git a/integration-cli/docker_cli_rmi_test.go b/integration-cli/docker_cli_rmi_test.go index aedfa13a814e9..662285682327d 100644 --- a/integration-cli/docker_cli_rmi_test.go +++ b/integration-cli/docker_cli_rmi_test.go @@ -10,7 +10,7 @@ import ( "github.com/docker/docker/integration-cli/cli/build" "github.com/docker/docker/pkg/stringid" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" + "gotest.tools/icmd" ) func (s *DockerSuite) TestRmiWithContainerFails(c *check.C) { diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index 7e0efdc08f64a..53abc21c62c04 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -36,7 +36,7 @@ import ( "github.com/docker/libnetwork/resolvconf" "github.com/docker/libnetwork/types" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" + "gotest.tools/icmd" ) // "test123" should be printed by docker run @@ -623,6 +623,7 @@ func (s *DockerSuite) TestRunCreateVolumeWithSymlink(c *check.C) { // Cannot run on Windows as relies on Linux-specific functionality (sh -c mount...) testRequires(c, DaemonIsLinux) workingDirectory, err := ioutil.TempDir("", "TestRunCreateVolumeWithSymlink") + c.Assert(err, checker.IsNil) image := "docker-test-createvolumewithsymlink" buildCmd := exec.Command(dockerBinary, "build", "-t", image, "-") @@ -3948,6 +3949,7 @@ func (s *DockerSuite) TestRunAttachFailedNoLeak(c *check.C) { // TODO Windows Post TP5. Fix the error message string c.Assert(strings.Contains(string(out), "port is already allocated") || strings.Contains(string(out), "were not connected because a duplicate name exists") || + strings.Contains(string(out), "The specified port already exists") || strings.Contains(string(out), "HNS failed with error : Failed to create endpoint") || strings.Contains(string(out), "HNS failed with error : The object already exists"), checker.Equals, true, check.Commentf("Output: %s", out)) dockerCmd(c, "rm", "-f", "test") diff --git a/integration-cli/docker_cli_run_unix_test.go b/integration-cli/docker_cli_run_unix_test.go index 70acbb37f3db7..3444d22bfd953 100644 --- a/integration-cli/docker_cli_run_unix_test.go +++ b/integration-cli/docker_cli_run_unix_test.go @@ -26,8 +26,8 @@ import ( "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/sysinfo" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" "github.com/kr/pty" + "gotest.tools/icmd" ) // #6509 diff --git a/integration-cli/docker_cli_save_load_test.go b/integration-cli/docker_cli_save_load_test.go index d370b6cc5ef0b..688eac684ea83 100644 --- a/integration-cli/docker_cli_save_load_test.go +++ b/integration-cli/docker_cli_save_load_test.go @@ -18,8 +18,8 @@ import ( "github.com/docker/docker/integration-cli/checker" "github.com/docker/docker/integration-cli/cli/build" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" "github.com/opencontainers/go-digest" + "gotest.tools/icmd" ) // save a repo using gz compression and try to load it using stdout diff --git a/integration-cli/docker_cli_save_load_unix_test.go b/integration-cli/docker_cli_save_load_unix_test.go index fcbfd7e62752f..da520e41c0cb6 100644 --- a/integration-cli/docker_cli_save_load_unix_test.go +++ b/integration-cli/docker_cli_save_load_unix_test.go @@ -14,8 +14,8 @@ import ( "github.com/docker/docker/integration-cli/checker" "github.com/docker/docker/integration-cli/cli/build" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" "github.com/kr/pty" + "gotest.tools/icmd" ) // save a repo and try to load it using stdout diff --git a/integration-cli/docker_cli_secret_create_test.go b/integration-cli/docker_cli_secret_create_test.go index a807e4e7e73c8..738db3b9e584e 100644 --- a/integration-cli/docker_cli_secret_create_test.go +++ b/integration-cli/docker_cli_secret_create_test.go @@ -39,6 +39,7 @@ func (s *DockerSwarmSuite) TestSecretCreateResolve(c *check.C) { c.Assert(out, checker.Contains, fake) out, err = d.Cmd("secret", "rm", id) + c.Assert(err, checker.IsNil) c.Assert(out, checker.Contains, id) // Fake one will remain @@ -54,6 +55,7 @@ func (s *DockerSwarmSuite) TestSecretCreateResolve(c *check.C) { // - Full Name // - Partial ID (prefix) out, err = d.Cmd("secret", "rm", id[:5]) + c.Assert(err, checker.Not(checker.IsNil)) c.Assert(out, checker.Not(checker.Contains), id) out, err = d.Cmd("secret", "ls") c.Assert(err, checker.IsNil) @@ -62,6 +64,7 @@ func (s *DockerSwarmSuite) TestSecretCreateResolve(c *check.C) { // Remove based on ID prefix of the fake one should succeed out, err = d.Cmd("secret", "rm", fake[:5]) + c.Assert(err, checker.IsNil) c.Assert(out, checker.Contains, fake[:5]) out, err = d.Cmd("secret", "ls") c.Assert(err, checker.IsNil) diff --git a/integration-cli/docker_cli_service_health_test.go b/integration-cli/docker_cli_service_health_test.go index ac525d08ea849..ae9e7868bb2cd 100644 --- a/integration-cli/docker_cli_service_health_test.go +++ b/integration-cli/docker_cli_service_health_test.go @@ -12,7 +12,7 @@ import ( "github.com/docker/docker/integration-cli/cli" "github.com/docker/docker/integration-cli/cli/build" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" + "gotest.tools/icmd" ) // start a service, and then make its task unhealthy during running diff --git a/integration-cli/docker_cli_service_logs_test.go b/integration-cli/docker_cli_service_logs_test.go index c26a7455a51a2..ba337491b1440 100644 --- a/integration-cli/docker_cli_service_logs_test.go +++ b/integration-cli/docker_cli_service_logs_test.go @@ -13,7 +13,7 @@ import ( "github.com/docker/docker/integration-cli/checker" "github.com/docker/docker/integration-cli/daemon" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" + "gotest.tools/icmd" ) type logMessage struct { diff --git a/integration-cli/docker_cli_service_scale_test.go b/integration-cli/docker_cli_service_scale_test.go index 41b49d64aa58b..ad045ee570bd9 100644 --- a/integration-cli/docker_cli_service_scale_test.go +++ b/integration-cli/docker_cli_service_scale_test.go @@ -21,16 +21,16 @@ func (s *DockerSwarmSuite) TestServiceScale(c *check.C) { service2Args := append([]string{"service", "create", "--detach", "--no-resolve-image", "--name", service2Name, "--mode=global", defaultSleepImage}, sleepCommandForDaemonPlatform()...) // Create services - out, err := d.Cmd(service1Args...) + _, err := d.Cmd(service1Args...) c.Assert(err, checker.IsNil) - out, err = d.Cmd(service2Args...) + _, err = d.Cmd(service2Args...) c.Assert(err, checker.IsNil) - out, err = d.Cmd("service", "scale", "TestService1=2") + _, err = d.Cmd("service", "scale", "TestService1=2") c.Assert(err, checker.IsNil) - out, err = d.Cmd("service", "scale", "TestService1=foobar") + out, err := d.Cmd("service", "scale", "TestService1=foobar") c.Assert(err, checker.NotNil) str := fmt.Sprintf("%s: invalid replicas value %s", service1Name, "foobar") diff --git a/integration-cli/docker_cli_start_test.go b/integration-cli/docker_cli_start_test.go index 98f7beaea90e3..cbe917bf4f805 100644 --- a/integration-cli/docker_cli_start_test.go +++ b/integration-cli/docker_cli_start_test.go @@ -8,7 +8,7 @@ import ( "github.com/docker/docker/integration-cli/checker" "github.com/docker/docker/integration-cli/cli" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" + "gotest.tools/icmd" ) // Regression test for https://github.com/docker/docker/issues/7843 diff --git a/integration-cli/docker_cli_swarm_test.go b/integration-cli/docker_cli_swarm_test.go index 94013d82b39f5..ae9fb3e8dd37f 100644 --- a/integration-cli/docker_cli_swarm_test.go +++ b/integration-cli/docker_cli_swarm_test.go @@ -27,9 +27,9 @@ import ( remoteipam "github.com/docker/libnetwork/ipams/remote/api" "github.com/docker/swarmkit/ca/keyutils" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/fs" - "github.com/gotestyourself/gotestyourself/icmd" "github.com/vishvananda/netlink" + "gotest.tools/fs" + "gotest.tools/icmd" ) func (s *DockerSwarmSuite) TestSwarmUpdate(c *check.C) { @@ -279,13 +279,13 @@ func (s *DockerSwarmSuite) TestSwarmPublishAdd(c *check.C) { c.Assert(err, checker.IsNil) c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") - out, err = d.Cmd("service", "update", "--detach", "--publish-add", "80:80", name) + _, err = d.Cmd("service", "update", "--detach", "--publish-add", "80:80", name) c.Assert(err, checker.IsNil) - out, err = d.Cmd("service", "update", "--detach", "--publish-add", "80:80", name) + _, err = d.Cmd("service", "update", "--detach", "--publish-add", "80:80", name) c.Assert(err, checker.IsNil) - out, err = d.Cmd("service", "update", "--detach", "--publish-add", "80:80", "--publish-add", "80:20", name) + _, err = d.Cmd("service", "update", "--detach", "--publish-add", "80:80", "--publish-add", "80:20", name) c.Assert(err, checker.NotNil) out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.EndpointSpec.Ports }}", name) @@ -841,14 +841,14 @@ func (s *DockerSwarmSuite) TestSwarmServiceTTY(c *check.C) { // Without --tty expectedOutput := "none" - out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "busybox", "sh", "-c", ttyCheck) + _, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "busybox", "sh", "-c", ttyCheck) c.Assert(err, checker.IsNil) // Make sure task has been deployed. waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1) // We need to get the container id. - out, err = d.Cmd("ps", "-q", "--no-trunc") + out, err := d.Cmd("ps", "-q", "--no-trunc") c.Assert(err, checker.IsNil) id := strings.TrimSpace(out) @@ -857,14 +857,14 @@ func (s *DockerSwarmSuite) TestSwarmServiceTTY(c *check.C) { c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out)) // Remove service - out, err = d.Cmd("service", "rm", name) + _, err = d.Cmd("service", "rm", name) c.Assert(err, checker.IsNil) // Make sure container has been destroyed. waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 0) // With --tty expectedOutput = "TTY" - out, err = d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "--tty", "busybox", "sh", "-c", ttyCheck) + _, err = d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "--tty", "busybox", "sh", "-c", ttyCheck) c.Assert(err, checker.IsNil) // Make sure task has been deployed. @@ -1069,6 +1069,7 @@ func (s *DockerSwarmSuite) TestSwarmInitLocked(c *check.C) { c.Assert(unlockKey, checker.Not(checker.Equals), "") outs, err = d.Cmd("swarm", "unlock-key", "-q") + c.Assert(err, checker.IsNil) c.Assert(outs, checker.Equals, unlockKey+"\n") c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive) @@ -1168,6 +1169,7 @@ func (s *DockerSwarmSuite) TestSwarmLockUnlockCluster(c *check.C) { c.Assert(unlockKey, checker.Not(checker.Equals), "") outs, err = d1.Cmd("swarm", "unlock-key", "-q") + c.Assert(err, checker.IsNil) c.Assert(outs, checker.Equals, unlockKey+"\n") // The ones that got the cluster update should be set to locked @@ -1234,6 +1236,7 @@ func (s *DockerSwarmSuite) TestSwarmJoinPromoteLocked(c *check.C) { c.Assert(unlockKey, checker.Not(checker.Equals), "") outs, err = d1.Cmd("swarm", "unlock-key", "-q") + c.Assert(err, checker.IsNil) c.Assert(outs, checker.Equals, unlockKey+"\n") // joined workers start off unlocked @@ -1306,6 +1309,7 @@ func (s *DockerSwarmSuite) TestSwarmRotateUnlockKey(c *check.C) { c.Assert(unlockKey, checker.Not(checker.Equals), "") outs, err = d.Cmd("swarm", "unlock-key", "-q") + c.Assert(err, checker.IsNil) c.Assert(outs, checker.Equals, unlockKey+"\n") // Rotate multiple times @@ -1390,6 +1394,7 @@ func (s *DockerSwarmSuite) TestSwarmClusterRotateUnlockKey(c *check.C) { c.Assert(unlockKey, checker.Not(checker.Equals), "") outs, err = d1.Cmd("swarm", "unlock-key", "-q") + c.Assert(err, checker.IsNil) c.Assert(outs, checker.Equals, unlockKey+"\n") // Rotate multiple times diff --git a/integration-cli/docker_cli_swarm_unix_test.go b/integration-cli/docker_cli_swarm_unix_test.go index 3b890bcc6946f..f491a0da608f1 100644 --- a/integration-cli/docker_cli_swarm_unix_test.go +++ b/integration-cli/docker_cli_swarm_unix_test.go @@ -26,6 +26,7 @@ func (s *DockerSwarmSuite) TestSwarmVolumePlugin(c *check.C) { // create a dummy volume to trigger lazy loading of the plugin out, err = d.Cmd("volume", "create", "-d", "customvolumedriver", "hello") + c.Assert(err, checker.IsNil, check.Commentf(out)) // TODO(aaronl): It will take about 15 seconds for swarm to realize the // plugin was loaded. Switching the test over to plugin v2 would avoid diff --git a/integration-cli/docker_cli_top_test.go b/integration-cli/docker_cli_top_test.go index b8924d3b50623..50744b0111187 100644 --- a/integration-cli/docker_cli_top_test.go +++ b/integration-cli/docker_cli_top_test.go @@ -5,7 +5,7 @@ import ( "github.com/docker/docker/integration-cli/checker" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" + "gotest.tools/icmd" ) func (s *DockerSuite) TestTopMultipleArgs(c *check.C) { diff --git a/integration-cli/docker_cli_update_unix_test.go b/integration-cli/docker_cli_update_unix_test.go index 564c50f32fbc5..1fb30f04d6df3 100644 --- a/integration-cli/docker_cli_update_unix_test.go +++ b/integration-cli/docker_cli_update_unix_test.go @@ -324,7 +324,7 @@ func (s *DockerSuite) TestUpdateWithNanoCPUs(c *check.C) { c.Assert(err, checker.NotNil) c.Assert(out, checker.Contains, "Conflicting options: CPU Quota cannot be updated as NanoCPUs has already been set") - out, _ = dockerCmd(c, "update", "--cpus", "0.8", "top") + dockerCmd(c, "update", "--cpus", "0.8", "top") inspect, err = clt.ContainerInspect(context.Background(), "top") c.Assert(err, checker.IsNil) c.Assert(inspect.HostConfig.NanoCPUs, checker.Equals, int64(800000000)) diff --git a/integration-cli/docker_cli_userns_test.go b/integration-cli/docker_cli_userns_test.go index 54cfdd179cd07..13d1f56dc63d7 100644 --- a/integration-cli/docker_cli_userns_test.go +++ b/integration-cli/docker_cli_userns_test.go @@ -61,12 +61,12 @@ func (s *DockerDaemonSuite) TestDaemonUserNamespaceRootSetting(c *check.C) { c.Assert(err, checker.IsNil, check.Commentf("Could not inspect running container: out: %q", pid)) // check the uid and gid maps for the PID to ensure root is remapped // (cmd = cat /proc//uid_map | grep -E '0\s+9999\s+1') - out, err = RunCommandPipelineWithOutput( + _, err = RunCommandPipelineWithOutput( exec.Command("cat", "/proc/"+strings.TrimSpace(pid)+"/uid_map"), exec.Command("grep", "-E", fmt.Sprintf("0[[:space:]]+%d[[:space:]]+", uid))) c.Assert(err, check.IsNil) - out, err = RunCommandPipelineWithOutput( + _, err = RunCommandPipelineWithOutput( exec.Command("cat", "/proc/"+strings.TrimSpace(pid)+"/gid_map"), exec.Command("grep", "-E", fmt.Sprintf("0[[:space:]]+%d[[:space:]]+", gid))) c.Assert(err, check.IsNil) diff --git a/integration-cli/docker_cli_volume_test.go b/integration-cli/docker_cli_volume_test.go index ad1e96577c2b5..257ac89701d5e 100644 --- a/integration-cli/docker_cli_volume_test.go +++ b/integration-cli/docker_cli_volume_test.go @@ -16,7 +16,7 @@ import ( "github.com/docker/docker/integration-cli/checker" "github.com/docker/docker/integration-cli/cli/build" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" + "gotest.tools/icmd" ) func (s *DockerSuite) TestVolumeCLICreate(c *check.C) { @@ -300,10 +300,10 @@ func (s *DockerSuite) TestVolumeCLICreateLabel(c *check.C) { testLabel := "foo" testValue := "bar" - out, _, err := dockerCmdWithError("volume", "create", "--label", testLabel+"="+testValue, testVol) + _, _, err := dockerCmdWithError("volume", "create", "--label", testLabel+"="+testValue, testVol) c.Assert(err, check.IsNil) - out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Labels."+testLabel+" }}", testVol) + out, _ := dockerCmd(c, "volume", "inspect", "--format={{ .Labels."+testLabel+" }}", testVol) c.Assert(strings.TrimSpace(out), check.Equals, testValue) } @@ -325,25 +325,25 @@ func (s *DockerSuite) TestVolumeCLICreateLabelMultiple(c *check.C) { args = append(args, "--label", k+"="+v) } - out, _, err := dockerCmdWithError(args...) + _, _, err := dockerCmdWithError(args...) c.Assert(err, check.IsNil) for k, v := range testLabels { - out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Labels."+k+" }}", testVol) + out, _ := dockerCmd(c, "volume", "inspect", "--format={{ .Labels."+k+" }}", testVol) c.Assert(strings.TrimSpace(out), check.Equals, v) } } func (s *DockerSuite) TestVolumeCLILsFilterLabels(c *check.C) { testVol1 := "testvolcreatelabel-1" - out, _, err := dockerCmdWithError("volume", "create", "--label", "foo=bar1", testVol1) + _, _, err := dockerCmdWithError("volume", "create", "--label", "foo=bar1", testVol1) c.Assert(err, check.IsNil) testVol2 := "testvolcreatelabel-2" - out, _, err = dockerCmdWithError("volume", "create", "--label", "foo=bar2", testVol2) + _, _, err = dockerCmdWithError("volume", "create", "--label", "foo=bar2", testVol2) c.Assert(err, check.IsNil) - out, _ = dockerCmd(c, "volume", "ls", "--filter", "label=foo") + out, _ := dockerCmd(c, "volume", "ls", "--filter", "label=foo") // filter with label=key c.Assert(out, checker.Contains, "testvolcreatelabel-1\n", check.Commentf("expected volume 'testvolcreatelabel-1' in output")) @@ -367,15 +367,15 @@ func (s *DockerSuite) TestVolumeCLILsFilterLabels(c *check.C) { func (s *DockerSuite) TestVolumeCLILsFilterDrivers(c *check.C) { // using default volume driver local to create volumes testVol1 := "testvol-1" - out, _, err := dockerCmdWithError("volume", "create", testVol1) + _, _, err := dockerCmdWithError("volume", "create", testVol1) c.Assert(err, check.IsNil) testVol2 := "testvol-2" - out, _, err = dockerCmdWithError("volume", "create", testVol2) + _, _, err = dockerCmdWithError("volume", "create", testVol2) c.Assert(err, check.IsNil) // filter with driver=local - out, _ = dockerCmd(c, "volume", "ls", "--filter", "driver=local") + out, _ := dockerCmd(c, "volume", "ls", "--filter", "driver=local") c.Assert(out, checker.Contains, "testvol-1\n", check.Commentf("expected volume 'testvol-1' in output")) c.Assert(out, checker.Contains, "testvol-2\n", check.Commentf("expected volume 'testvol-2' in output")) @@ -434,7 +434,7 @@ func (s *DockerSuite) TestVolumeCLIRmForceInUse(c *check.C) { c.Assert(id, checker.Equals, name) prefix, slash := getPrefixAndSlashFromDaemonPlatform() - out, e := dockerCmd(c, "create", "-v", "testvolume:"+prefix+slash+"foo", "busybox") + out, _ = dockerCmd(c, "create", "-v", "testvolume:"+prefix+slash+"foo", "busybox") cid := strings.TrimSpace(out) _, _, err := dockerCmdWithError("volume", "rm", "-f", name) @@ -454,7 +454,7 @@ func (s *DockerSuite) TestVolumeCLIRmForceInUse(c *check.C) { c.Assert(out, checker.Contains, name) // Verify removing the volume after the container is removed works - _, e = dockerCmd(c, "rm", cid) + _, e := dockerCmd(c, "rm", cid) c.Assert(e, check.Equals, 0) _, e = dockerCmd(c, "volume", "rm", "-f", name) diff --git a/integration-cli/docker_cli_wait_test.go b/integration-cli/docker_cli_wait_test.go index e8047042d002e..669e54f1ae095 100644 --- a/integration-cli/docker_cli_wait_test.go +++ b/integration-cli/docker_cli_wait_test.go @@ -8,7 +8,7 @@ import ( "github.com/docker/docker/integration-cli/checker" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" + "gotest.tools/icmd" ) // non-blocking wait with 0 exit code diff --git a/integration-cli/docker_utils_test.go b/integration-cli/docker_utils_test.go index 19fdcff19a134..1c05bf5d04ac4 100644 --- a/integration-cli/docker_utils_test.go +++ b/integration-cli/docker_utils_test.go @@ -21,7 +21,7 @@ import ( "github.com/docker/docker/integration-cli/daemon" "github.com/docker/docker/internal/test/request" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" + "gotest.tools/icmd" ) // Deprecated diff --git a/integration-cli/requirements_test.go b/integration-cli/requirements_test.go index 9b08a63b0ea5f..28be59cd2ca8b 100644 --- a/integration-cli/requirements_test.go +++ b/integration-cli/requirements_test.go @@ -60,7 +60,7 @@ func OnlyDefaultNetworks() bool { return true } -// Deprecated: use skip.IfCondition(t, !testEnv.DaemonInfo.ExperimentalBuild) +// Deprecated: use skip.If(t, !testEnv.DaemonInfo.ExperimentalBuild) func ExperimentalDaemon() bool { return testEnv.DaemonInfo.ExperimentalBuild } @@ -208,6 +208,10 @@ func SwarmInactive() bool { return testEnv.DaemonInfo.Swarm.LocalNodeState == swarm.LocalNodeStateInactive } +func TODOBuildkit() bool { + return os.Getenv("DOCKER_BUILDKIT") == "" +} + // testRequires checks if the environment satisfies the requirements // for the test to run or skips the tests. func testRequires(c requirement.SkipT, requirements ...requirement.Test) { diff --git a/integration-cli/utils_test.go b/integration-cli/utils_test.go index 33913c3922096..fd083681f266b 100644 --- a/integration-cli/utils_test.go +++ b/integration-cli/utils_test.go @@ -9,8 +9,8 @@ import ( "github.com/docker/docker/internal/testutil" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" "github.com/pkg/errors" + "gotest.tools/icmd" ) func getPrefixAndSlashFromDaemonPlatform() (prefix, slash string) { @@ -21,7 +21,7 @@ func getPrefixAndSlashFromDaemonPlatform() (prefix, slash string) { } // TODO: update code to call cmd.RunCmd directly, and remove this function -// Deprecated: use gotestyourself/gotestyourself/icmd +// Deprecated: use gotest.tools/icmd func runCommandWithOutput(execCmd *exec.Cmd) (string, int, error) { result := icmd.RunCmd(transformCmd(execCmd)) return result.Combined(), result.ExitCode, result.Error diff --git a/integration/build/build_session_test.go b/integration/build/build_session_test.go index c2f93629440f0..dde4b427b4974 100644 --- a/integration/build/build_session_test.go +++ b/integration/build/build_session_test.go @@ -10,12 +10,12 @@ import ( dclient "github.com/docker/docker/client" "github.com/docker/docker/internal/test/fakecontext" "github.com/docker/docker/internal/test/request" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/skip" "github.com/moby/buildkit/session" "github.com/moby/buildkit/session/filesync" "golang.org/x/sync/errgroup" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/skip" ) func TestBuildWithSession(t *testing.T) { diff --git a/integration/build/build_squash_test.go b/integration/build/build_squash_test.go index 9110604e4fd69..4cd282a976030 100644 --- a/integration/build/build_squash_test.go +++ b/integration/build/build_squash_test.go @@ -12,9 +12,9 @@ import ( "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/fakecontext" "github.com/docker/docker/pkg/stdcopy" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/skip" ) func TestBuildSquashParent(t *testing.T) { diff --git a/integration/build/build_test.go b/integration/build/build_test.go index ee9631d3ff4a4..25c5e635bdfdf 100644 --- a/integration/build/build_test.go +++ b/integration/build/build_test.go @@ -16,9 +16,9 @@ import ( "github.com/docker/docker/internal/test/fakecontext" "github.com/docker/docker/internal/test/request" "github.com/docker/docker/pkg/jsonmessage" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/skip" ) func TestBuildWithRemoveAndForceRemove(t *testing.T) { @@ -385,7 +385,7 @@ COPY bar /` // docker/for-linux#135 // #35641 func TestBuildMultiStageLayerLeak(t *testing.T) { - skip.IfCondition(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.37"), "broken in earlier versions") + skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.37"), "broken in earlier versions") ctx := context.TODO() defer setupTest(t)() diff --git a/integration/config/config_test.go b/integration/config/config_test.go index 1c002423eabd0..536a645b01ae6 100644 --- a/integration/config/config_test.go +++ b/integration/config/config_test.go @@ -14,9 +14,9 @@ import ( "github.com/docker/docker/client" "github.com/docker/docker/integration/internal/swarm" "github.com/docker/docker/pkg/stdcopy" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/skip" ) func TestConfigList(t *testing.T) { @@ -45,19 +45,10 @@ func TestConfigList(t *testing.T) { config1ID := createConfig(ctx, t, client, testName1, []byte("TESTINGDATA1"), map[string]string{"type": "production"}) - names := func(entries []swarmtypes.Config) []string { - var values []string - for _, entry := range entries { - values = append(values, entry.Spec.Name) - } - sort.Strings(values) - return values - } - // test by `config ls` entries, err := client.ConfigList(ctx, types.ConfigListOptions{}) assert.NilError(t, err) - assert.Check(t, is.DeepEqual(names(entries), testNames)) + assert.Check(t, is.DeepEqual(configNamesFromList(entries), testNames)) testCases := []struct { filters filters.Args @@ -92,7 +83,7 @@ func TestConfigList(t *testing.T) { Filters: tc.filters, }) assert.NilError(t, err) - assert.Check(t, is.DeepEqual(names(entries), tc.expected)) + assert.Check(t, is.DeepEqual(configNamesFromList(entries), tc.expected)) } } @@ -354,3 +345,89 @@ func TestConfigInspect(t *testing.T) { assert.NilError(t, err) assert.Check(t, is.DeepEqual(config, insp)) } + +func TestConfigCreateWithLabels(t *testing.T) { + skip.If(t, testEnv.DaemonInfo.OSType != "linux") + + defer setupTest(t)() + d := swarm.NewSwarm(t, testEnv) + defer d.Stop(t) + client := d.NewClientT(t) + defer client.Close() + + ctx := context.Background() + + labels := map[string]string{ + "key1": "value1", + "key2": "value2", + } + testName := t.Name() + configID := createConfig(ctx, t, client, testName, []byte("TESTINGDATA"), labels) + + insp, _, err := client.ConfigInspectWithRaw(ctx, configID) + assert.NilError(t, err) + assert.Check(t, is.Equal(insp.Spec.Name, testName)) + assert.Check(t, is.Equal(2, len(insp.Spec.Labels))) + assert.Check(t, is.Equal("value1", insp.Spec.Labels["key1"])) + assert.Check(t, is.Equal("value2", insp.Spec.Labels["key2"])) +} + +// Test case for 28884 +func TestConfigCreateResolve(t *testing.T) { + skip.If(t, testEnv.DaemonInfo.OSType != "linux") + + defer setupTest(t)() + d := swarm.NewSwarm(t, testEnv) + defer d.Stop(t) + client := d.NewClientT(t) + defer client.Close() + + ctx := context.Background() + + configName := "test_config_" + t.Name() + + configID := createConfig(ctx, t, client, configName, []byte("foo"), nil) + fakeName := configID + fakeID := createConfig(ctx, t, client, fakeName, []byte("fake foo"), nil) + + entries, err := client.ConfigList(ctx, types.ConfigListOptions{}) + assert.NilError(t, err) + assert.Assert(t, is.Contains(configNamesFromList(entries), configName)) + assert.Assert(t, is.Contains(configNamesFromList(entries), fakeName)) + + err = client.ConfigRemove(ctx, configID) + assert.NilError(t, err) + + // Fake one will remain + entries, err = client.ConfigList(ctx, types.ConfigListOptions{}) + assert.NilError(t, err) + assert.Assert(t, is.DeepEqual(configNamesFromList(entries), []string{fakeName})) + + // Remove based on name prefix of the fake one + // (which is the same as the ID of foo one) should not work + // as search is only done based on: + // - Full ID + // - Full Name + // - Partial ID (prefix) + err = client.ConfigRemove(ctx, configID[:5]) + assert.Assert(t, nil != err) + entries, err = client.ConfigList(ctx, types.ConfigListOptions{}) + assert.NilError(t, err) + assert.Assert(t, is.DeepEqual(configNamesFromList(entries), []string{fakeName})) + + // Remove based on ID prefix of the fake one should succeed + err = client.ConfigRemove(ctx, fakeID[:5]) + assert.NilError(t, err) + entries, err = client.ConfigList(ctx, types.ConfigListOptions{}) + assert.NilError(t, err) + assert.Assert(t, is.Equal(0, len(entries))) +} + +func configNamesFromList(entries []swarmtypes.Config) []string { + var values []string + for _, entry := range entries { + values = append(values, entry.Spec.Name) + } + sort.Strings(values) + return values +} diff --git a/integration/container/copy_test.go b/integration/container/copy_test.go index 6794f9855523d..241b719eb70e3 100644 --- a/integration/container/copy_test.go +++ b/integration/container/copy_test.go @@ -8,9 +8,9 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/client" "github.com/docker/docker/integration/internal/container" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/skip" ) func TestCopyFromContainerPathDoesNotExist(t *testing.T) { diff --git a/integration/container/create_test.go b/integration/container/create_test.go index 9c466448903a0..f94eb4a3fb8e9 100644 --- a/integration/container/create_test.go +++ b/integration/container/create_test.go @@ -14,10 +14,10 @@ import ( ctr "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" "github.com/docker/docker/oci" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/poll" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/poll" + "gotest.tools/skip" ) func TestCreateFailsWhenIdentifierDoesNotExist(t *testing.T) { diff --git a/integration/container/daemon_linux_test.go b/integration/container/daemon_linux_test.go index df6aa683abac6..bc5c5076b8e65 100644 --- a/integration/container/daemon_linux_test.go +++ b/integration/container/daemon_linux_test.go @@ -11,9 +11,9 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/daemon" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/skip" "golang.org/x/sys/unix" + "gotest.tools/assert" + "gotest.tools/skip" ) // This is a regression test for #36145 diff --git a/integration/container/diff_test.go b/integration/container/diff_test.go index fee58391e9903..b4219c3627e94 100644 --- a/integration/container/diff_test.go +++ b/integration/container/diff_test.go @@ -9,8 +9,8 @@ import ( "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" "github.com/docker/docker/pkg/archive" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/poll" + "gotest.tools/assert" + "gotest.tools/poll" ) func TestDiff(t *testing.T) { diff --git a/integration/container/exec_test.go b/integration/container/exec_test.go index e1750d9414382..85f9e0591571e 100644 --- a/integration/container/exec_test.go +++ b/integration/container/exec_test.go @@ -10,9 +10,9 @@ import ( "github.com/docker/docker/api/types/versions" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/skip" ) func TestExec(t *testing.T) { diff --git a/integration/container/export_test.go b/integration/container/export_test.go index dbeea341cdd2c..7a9ed0aa99fbd 100644 --- a/integration/container/export_test.go +++ b/integration/container/export_test.go @@ -12,10 +12,10 @@ import ( "github.com/docker/docker/internal/test/daemon" "github.com/docker/docker/internal/test/request" "github.com/docker/docker/pkg/jsonmessage" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/poll" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/poll" + "gotest.tools/skip" ) // export an image and try to import it into a new one diff --git a/integration/container/health_test.go b/integration/container/health_test.go index e13885002d31f..7cc196e46d740 100644 --- a/integration/container/health_test.go +++ b/integration/container/health_test.go @@ -10,7 +10,7 @@ import ( "github.com/docker/docker/client" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" - "github.com/gotestyourself/gotestyourself/poll" + "gotest.tools/poll" ) // TestHealthCheckWorkdir verifies that health-checks inherit the containers' diff --git a/integration/container/inspect_test.go b/integration/container/inspect_test.go index 8ed590443355d..d034c536500e6 100644 --- a/integration/container/inspect_test.go +++ b/integration/container/inspect_test.go @@ -9,10 +9,10 @@ import ( "github.com/docker/docker/client" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/poll" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/poll" + "gotest.tools/skip" ) func TestInspectCpusetInConfigPre120(t *testing.T) { diff --git a/integration/container/ipcmode_test.go b/integration/container/ipcmode_test.go new file mode 100644 index 0000000000000..3011748cd0ffd --- /dev/null +++ b/integration/container/ipcmode_test.go @@ -0,0 +1,116 @@ +package container // import "github.com/docker/docker/integration/container" + +import ( + "bufio" + "context" + "os" + "regexp" + "strings" + "testing" + + "github.com/docker/docker/api/types" + containertypes "github.com/docker/docker/api/types/container" + "github.com/docker/docker/integration/internal/container" + "github.com/docker/docker/internal/test/request" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/skip" +) + +// testIpcCheckDevExists checks whether a given mount (identified by its +// major:minor pair from /proc/self/mountinfo) exists on the host system. +// +// The format of /proc/self/mountinfo is like: +// +// 29 23 0:24 / /dev/shm rw,nosuid,nodev shared:4 - tmpfs tmpfs rw +// ^^^^\ +// - this is the minor:major we look for +func testIpcCheckDevExists(mm string) (bool, error) { + f, err := os.Open("/proc/self/mountinfo") + if err != nil { + return false, err + } + defer f.Close() + + s := bufio.NewScanner(f) + for s.Scan() { + fields := strings.Fields(s.Text()) + if len(fields) < 7 { + continue + } + if fields[2] == mm { + return true, nil + } + } + + return false, s.Err() +} + +// testIpcNonePrivateShareable is a helper function to test "none", +// "private" and "shareable" modes. +func testIpcNonePrivateShareable(t *testing.T, mode string, mustBeMounted bool, mustBeShared bool) { + defer setupTest(t)() + + cfg := containertypes.Config{ + Image: "busybox", + Cmd: []string{"top"}, + } + hostCfg := containertypes.HostConfig{ + IpcMode: containertypes.IpcMode(mode), + } + client := request.NewAPIClient(t) + ctx := context.Background() + + resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "") + assert.NilError(t, err) + assert.Check(t, is.Equal(len(resp.Warnings), 0)) + + err = client.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}) + assert.NilError(t, err) + + // get major:minor pair for /dev/shm from container's /proc/self/mountinfo + cmd := "awk '($5 == \"/dev/shm\") {printf $3}' /proc/self/mountinfo" + result, err := container.Exec(ctx, client, resp.ID, []string{"sh", "-c", cmd}) + assert.NilError(t, err) + mm := result.Combined() + if !mustBeMounted { + assert.Check(t, is.Equal(mm, "")) + // no more checks to perform + return + } + assert.Check(t, is.Equal(true, regexp.MustCompile("^[0-9]+:[0-9]+$").MatchString(mm))) + + shared, err := testIpcCheckDevExists(mm) + assert.NilError(t, err) + t.Logf("[testIpcPrivateShareable] ipcmode: %v, ipcdev: %v, shared: %v, mustBeShared: %v\n", mode, mm, shared, mustBeShared) + assert.Check(t, is.Equal(shared, mustBeShared)) +} + +// TestIpcModeNone checks the container "none" IPC mode +// (--ipc none) works as expected. It makes sure there is no +// /dev/shm mount inside the container. +func TestIpcModeNone(t *testing.T) { + skip.If(t, testEnv.DaemonInfo.OSType != "linux" || testEnv.IsRemoteDaemon()) + + testIpcNonePrivateShareable(t, "none", false, false) +} + +// TestAPIIpcModePrivate checks the container private IPC mode +// (--ipc private) works as expected. It gets the minor:major pair +// of /dev/shm mount from the container, and makes sure there is no +// such pair on the host. +func TestIpcModePrivate(t *testing.T) { + skip.If(t, testEnv.DaemonInfo.OSType != "linux" || testEnv.IsRemoteDaemon()) + + testIpcNonePrivateShareable(t, "private", true, false) +} + +// TestAPIIpcModeShareable checks the container shareable IPC mode +// (--ipc shareable) works as expected. It gets the minor:major pair +// of /dev/shm mount from the container, and makes sure such pair +// also exists on the host. +func TestIpcModeShareable(t *testing.T) { + skip.If(t, testEnv.DaemonInfo.OSType != "linux" || testEnv.IsRemoteDaemon()) + + testIpcNonePrivateShareable(t, "shareable", true, true) +} diff --git a/integration/container/kill_test.go b/integration/container/kill_test.go index 8cef5bb897acd..12a9083cf3a63 100644 --- a/integration/container/kill_test.go +++ b/integration/container/kill_test.go @@ -9,10 +9,10 @@ import ( "github.com/docker/docker/client" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/poll" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/poll" + "gotest.tools/skip" ) func TestKillContainerInvalidSignal(t *testing.T) { diff --git a/integration/container/links_linux_test.go b/integration/container/links_linux_test.go index 9baa32728dd9c..f9f3cbe5eddf8 100644 --- a/integration/container/links_linux_test.go +++ b/integration/container/links_linux_test.go @@ -10,9 +10,9 @@ import ( "github.com/docker/docker/api/types/filters" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/skip" ) func TestLinksEtcHostsContentMatch(t *testing.T) { diff --git a/integration/container/logs_test.go b/integration/container/logs_test.go index 80e3d9a6c6a29..68fbe13a73709 100644 --- a/integration/container/logs_test.go +++ b/integration/container/logs_test.go @@ -9,15 +9,15 @@ import ( "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" "github.com/docker/docker/pkg/stdcopy" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + "gotest.tools/skip" ) // Regression test for #35370 // Makes sure that when following we don't get an EOF error when there are no logs func TestLogsFollowTailEmpty(t *testing.T) { // FIXME(vdemeester) fails on a e2e run on linux... - skip.IfCondition(t, testEnv.IsRemoteDaemon()) + skip.If(t, testEnv.IsRemoteDaemon()) defer setupTest(t)() client := request.NewAPIClient(t) ctx := context.Background() diff --git a/integration/container/mounts_linux_test.go b/integration/container/mounts_linux_test.go index 884eebb1b4f35..a0a8836c5171b 100644 --- a/integration/container/mounts_linux_test.go +++ b/integration/container/mounts_linux_test.go @@ -13,10 +13,10 @@ import ( "github.com/docker/docker/client" "github.com/docker/docker/internal/test/request" "github.com/docker/docker/pkg/system" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/fs" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/fs" + "gotest.tools/skip" ) func TestContainerNetworkMountsNoChown(t *testing.T) { diff --git a/integration/container/nat_test.go b/integration/container/nat_test.go index 12541b6a0a8ed..0dbed897dbd77 100644 --- a/integration/container/nat_test.go +++ b/integration/container/nat_test.go @@ -15,10 +15,10 @@ import ( "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" "github.com/docker/go-connections/nat" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/poll" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/poll" + "gotest.tools/skip" ) func TestNetworkNat(t *testing.T) { diff --git a/integration/container/pause_test.go b/integration/container/pause_test.go index 8f856bcb3c6f4..8dd2d784b70c2 100644 --- a/integration/container/pause_test.go +++ b/integration/container/pause_test.go @@ -12,10 +12,10 @@ import ( "github.com/docker/docker/api/types/versions" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/poll" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/poll" + "gotest.tools/skip" ) func TestPause(t *testing.T) { diff --git a/integration/container/ps_test.go b/integration/container/ps_test.go index 1080cd2f572cc..4ae07043ab9a0 100644 --- a/integration/container/ps_test.go +++ b/integration/container/ps_test.go @@ -8,8 +8,8 @@ import ( "github.com/docker/docker/api/types/filters" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestPsFilter(t *testing.T) { diff --git a/integration/container/remove_test.go b/integration/container/remove_test.go index 185f90cc00d95..5de13f22adcef 100644 --- a/integration/container/remove_test.go +++ b/integration/container/remove_test.go @@ -10,11 +10,11 @@ import ( "github.com/docker/docker/api/types/filters" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/fs" - "github.com/gotestyourself/gotestyourself/poll" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/fs" + "gotest.tools/poll" + "gotest.tools/skip" ) func getPrefixAndSlashFromDaemonPlatform() (prefix, slash string) { diff --git a/integration/container/rename_test.go b/integration/container/rename_test.go index fd270052a5d8e..c3f46e10c2d01 100644 --- a/integration/container/rename_test.go +++ b/integration/container/rename_test.go @@ -12,10 +12,10 @@ import ( "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" "github.com/docker/docker/pkg/stringid" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/poll" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/poll" + "gotest.tools/skip" ) // This test simulates the scenario mentioned in #31392: diff --git a/integration/container/resize_test.go b/integration/container/resize_test.go index c6c4e81751917..5961af0a4729c 100644 --- a/integration/container/resize_test.go +++ b/integration/container/resize_test.go @@ -11,10 +11,10 @@ import ( "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" req "github.com/docker/docker/internal/test/request" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/poll" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/poll" + "gotest.tools/skip" ) func TestResize(t *testing.T) { diff --git a/integration/container/restart_test.go b/integration/container/restart_test.go index c63faba3a7433..69007218f1a4c 100644 --- a/integration/container/restart_test.go +++ b/integration/container/restart_test.go @@ -9,8 +9,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/internal/test/daemon" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + "gotest.tools/skip" ) func TestDaemonRestartKillContainers(t *testing.T) { diff --git a/integration/container/stats_test.go b/integration/container/stats_test.go index dbececa0e5c84..6493a30573e21 100644 --- a/integration/container/stats_test.go +++ b/integration/container/stats_test.go @@ -10,10 +10,10 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/poll" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/poll" + "gotest.tools/skip" ) func TestStats(t *testing.T) { diff --git a/integration/container/stop_test.go b/integration/container/stop_test.go index 2932ae0bf924b..7a2fa20188a6c 100644 --- a/integration/container/stop_test.go +++ b/integration/container/stop_test.go @@ -11,10 +11,10 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/icmd" - "github.com/gotestyourself/gotestyourself/poll" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + "gotest.tools/icmd" + "gotest.tools/poll" + "gotest.tools/skip" ) func TestStopContainerWithRestartPolicyAlways(t *testing.T) { diff --git a/integration/container/update_linux_test.go b/integration/container/update_linux_test.go index 6e4fb637ff95e..0e410a14616ab 100644 --- a/integration/container/update_linux_test.go +++ b/integration/container/update_linux_test.go @@ -10,10 +10,10 @@ import ( containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/poll" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/poll" + "gotest.tools/skip" ) func TestUpdateMemory(t *testing.T) { diff --git a/integration/container/update_test.go b/integration/container/update_test.go index 2ac122aea7188..0e32184d27f10 100644 --- a/integration/container/update_test.go +++ b/integration/container/update_test.go @@ -8,9 +8,9 @@ import ( containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/poll" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/poll" ) func TestUpdateRestartPolicy(t *testing.T) { diff --git a/integration/image/commit_test.go b/integration/image/commit_test.go index eb0b4e6b5d684..4555391262ec4 100644 --- a/integration/image/commit_test.go +++ b/integration/image/commit_test.go @@ -8,9 +8,9 @@ import ( "github.com/docker/docker/api/types/versions" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/skip" ) func TestCommitInheritsEnv(t *testing.T) { diff --git a/integration/image/remove_test.go b/integration/image/remove_test.go index 172c27f54dfa9..4f9122a5e37b4 100644 --- a/integration/image/remove_test.go +++ b/integration/image/remove_test.go @@ -7,8 +7,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestRemoveImageOrphaning(t *testing.T) { diff --git a/integration/image/tag_test.go b/integration/image/tag_test.go index 06431cd8a29b6..55c3ff7b2b0b2 100644 --- a/integration/image/tag_test.go +++ b/integration/image/tag_test.go @@ -7,8 +7,8 @@ import ( "github.com/docker/docker/internal/test/request" "github.com/docker/docker/internal/testutil" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) // tagging a named image in a new unprefixed repo should work diff --git a/integration/internal/container/container.go b/integration/internal/container/container.go index 0c76571769bbd..20ad774242199 100644 --- a/integration/internal/container/container.go +++ b/integration/internal/container/container.go @@ -8,7 +8,7 @@ import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/network" "github.com/docker/docker/client" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) // TestContainerConfig holds container configuration struct that @@ -21,6 +21,7 @@ type TestContainerConfig struct { } // Create creates a container with the specified options +// nolint: golint func Create(t *testing.T, ctx context.Context, client client.APIClient, ops ...func(*TestContainerConfig)) string { // nolint: golint t.Helper() config := &TestContainerConfig{ @@ -43,6 +44,7 @@ func Create(t *testing.T, ctx context.Context, client client.APIClient, ops ...f } // Run creates and start a container with the specified options +// nolint: golint func Run(t *testing.T, ctx context.Context, client client.APIClient, ops ...func(*TestContainerConfig)) string { // nolint: golint t.Helper() id := Create(t, ctx, client, ops...) diff --git a/integration/internal/container/states.go b/integration/internal/container/states.go index 1ee73e01a6b72..088407deb84eb 100644 --- a/integration/internal/container/states.go +++ b/integration/internal/container/states.go @@ -5,7 +5,7 @@ import ( "strings" "github.com/docker/docker/client" - "github.com/gotestyourself/gotestyourself/poll" + "gotest.tools/poll" ) // IsStopped verifies the container is in stopped state. diff --git a/integration/internal/network/network.go b/integration/internal/network/network.go new file mode 100644 index 0000000000000..07d366433ba1d --- /dev/null +++ b/integration/internal/network/network.go @@ -0,0 +1,36 @@ +package network + +import ( + "context" + "testing" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/client" + "gotest.tools/assert" +) + +func createNetwork(ctx context.Context, client client.APIClient, name string, ops ...func(*types.NetworkCreate)) (string, error) { + config := types.NetworkCreate{} + + for _, op := range ops { + op(&config) + } + + n, err := client.NetworkCreate(ctx, name, config) + return n.ID, err +} + +// Create creates a network with the specified options +func Create(ctx context.Context, client client.APIClient, name string, ops ...func(*types.NetworkCreate)) (string, error) { + return createNetwork(ctx, client, name, ops...) +} + +// CreateNoError creates a network with the specified options and verifies there were no errors +// nolint: golint +func CreateNoError(t *testing.T, ctx context.Context, client client.APIClient, name string, ops ...func(*types.NetworkCreate)) string { // nolint: golint + t.Helper() + + name, err := createNetwork(ctx, client, name, ops...) + assert.NilError(t, err) + return name +} diff --git a/integration/internal/network/ops.go b/integration/internal/network/ops.go new file mode 100644 index 0000000000000..33bd3d05f335f --- /dev/null +++ b/integration/internal/network/ops.go @@ -0,0 +1,94 @@ +package network + +import ( + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/network" +) + +// WithDriver sets the driver of the network +func WithDriver(driver string) func(*types.NetworkCreate) { + return func(n *types.NetworkCreate) { + n.Driver = driver + } +} + +// WithIPv6 Enables IPv6 on the network +func WithIPv6() func(*types.NetworkCreate) { + return func(n *types.NetworkCreate) { + n.EnableIPv6 = true + } +} + +// WithCheckDuplicate sets the CheckDuplicate field on create network request +func WithCheckDuplicate() func(*types.NetworkCreate) { + return func(n *types.NetworkCreate) { + n.CheckDuplicate = true + } +} + +// WithInternal enables Internal flag on the create network request +func WithInternal() func(*types.NetworkCreate) { + return func(n *types.NetworkCreate) { + n.Internal = true + } +} + +// WithAttachable sets Attachable flag on the create network request +func WithAttachable() func(*types.NetworkCreate) { + return func(n *types.NetworkCreate) { + n.Attachable = true + } +} + +// WithMacvlan sets the network as macvlan with the specified parent +func WithMacvlan(parent string) func(*types.NetworkCreate) { + return func(n *types.NetworkCreate) { + n.Driver = "macvlan" + if parent != "" { + n.Options = map[string]string{ + "parent": parent, + } + } + } +} + +// WithIPvlan sets the network as ipvlan with the specified parent and mode +func WithIPvlan(parent, mode string) func(*types.NetworkCreate) { + return func(n *types.NetworkCreate) { + n.Driver = "ipvlan" + if n.Options == nil { + n.Options = map[string]string{} + } + if parent != "" { + n.Options["parent"] = parent + } + if mode != "" { + n.Options["ipvlan_mode"] = mode + } + } +} + +// WithOption adds the specified key/value pair to network's options +func WithOption(key, value string) func(*types.NetworkCreate) { + return func(n *types.NetworkCreate) { + if n.Options == nil { + n.Options = map[string]string{} + } + n.Options[key] = value + } +} + +// WithIPAM adds an IPAM with the specified Subnet and Gateway to the network +func WithIPAM(subnet, gateway string) func(*types.NetworkCreate) { + return func(n *types.NetworkCreate) { + if n.IPAM == nil { + n.IPAM = &network.IPAM{} + } + + n.IPAM.Config = append(n.IPAM.Config, network.IPAMConfig{ + Subnet: subnet, + Gateway: gateway, + AuxAddress: map[string]string{}, + }) + } +} diff --git a/integration/internal/requirement/requirement.go b/integration/internal/requirement/requirement.go index cd498ab87d71e..004383bd055d5 100644 --- a/integration/internal/requirement/requirement.go +++ b/integration/internal/requirement/requirement.go @@ -7,7 +7,7 @@ import ( "time" "github.com/docker/docker/pkg/parsers/kernel" - "github.com/gotestyourself/gotestyourself/icmd" + "gotest.tools/icmd" ) // HasHubConnectivity checks to see if https://hub.docker.com is diff --git a/integration/internal/swarm/service.go b/integration/internal/swarm/service.go index e6a1bfcdd096a..d8b16224fb61e 100644 --- a/integration/internal/swarm/service.go +++ b/integration/internal/swarm/service.go @@ -11,9 +11,9 @@ import ( swarmtypes "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/internal/test/daemon" "github.com/docker/docker/internal/test/environment" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/poll" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + "gotest.tools/poll" + "gotest.tools/skip" ) // ServicePoll tweaks the pollSettings for `service` @@ -86,6 +86,14 @@ func defaultServiceSpec() swarmtypes.ServiceSpec { return spec } +// ServiceWithInit sets whether the service should use init or not +func ServiceWithInit(b *bool) func(*swarmtypes.ServiceSpec) { + return func(spec *swarmtypes.ServiceSpec) { + ensureContainerSpec(spec) + spec.TaskTemplate.ContainerSpec.Init = b + } +} + // ServiceWithImage sets the image to use for the service func ServiceWithImage(image string) func(*swarmtypes.ServiceSpec) { return func(spec *swarmtypes.ServiceSpec) { diff --git a/integration/network/delete_test.go b/integration/network/delete_test.go index f937da52749c9..c9099f4816a4f 100644 --- a/integration/network/delete_test.go +++ b/integration/network/delete_test.go @@ -6,15 +6,16 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/versions" + "github.com/docker/docker/integration/internal/network" "github.com/docker/docker/internal/test/request" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/skip" ) -func containsNetwork(nws []types.NetworkResource, nw types.NetworkCreateResponse) bool { +func containsNetwork(nws []types.NetworkResource, networkID string) bool { for _, n := range nws { - if n.ID == nw.ID { + if n.ID == networkID { return true } } @@ -26,16 +27,13 @@ func containsNetwork(nws []types.NetworkResource, nw types.NetworkCreateResponse // first network's ID as name. // // After successful creation, properties of all three networks is returned -func createAmbiguousNetworks(t *testing.T) (types.NetworkCreateResponse, types.NetworkCreateResponse, types.NetworkCreateResponse) { +func createAmbiguousNetworks(t *testing.T) (string, string, string) { client := request.NewAPIClient(t) ctx := context.Background() - testNet, err := client.NetworkCreate(ctx, "testNet", types.NetworkCreate{}) - assert.NilError(t, err) - idPrefixNet, err := client.NetworkCreate(ctx, testNet.ID[:12], types.NetworkCreate{}) - assert.NilError(t, err) - fullIDNet, err := client.NetworkCreate(ctx, testNet.ID, types.NetworkCreate{}) - assert.NilError(t, err) + testNet := network.CreateNoError(t, ctx, client, "testNet") + idPrefixNet := network.CreateNoError(t, ctx, client, testNet[:12]) + fullIDNet := network.CreateNoError(t, ctx, client, testNet) nws, err := client.NetworkList(ctx, types.NetworkListOptions{}) assert.NilError(t, err) @@ -46,6 +44,24 @@ func createAmbiguousNetworks(t *testing.T) (types.NetworkCreateResponse, types.N return testNet, idPrefixNet, fullIDNet } +func TestNetworkCreateDelete(t *testing.T) { + skip.If(t, testEnv.DaemonInfo.OSType != "linux") + defer setupTest(t)() + client := request.NewAPIClient(t) + ctx := context.Background() + + netName := "testnetwork_" + t.Name() + network.CreateNoError(t, ctx, client, netName, + network.WithCheckDuplicate(), + ) + assert.Check(t, IsNetworkAvailable(client, netName)) + + // delete the network and make sure it is deleted + err := client.NetworkRemove(ctx, netName) + assert.NilError(t, err) + assert.Check(t, IsNetworkNotAvailable(client, netName)) +} + // TestDockerNetworkDeletePreferID tests that if a network with a name // equal to another network's ID exists, the Network with the given // ID is removed, and not the network with the given name. @@ -58,12 +74,12 @@ func TestDockerNetworkDeletePreferID(t *testing.T) { // Delete the network using a prefix of the first network's ID as name. // This should the network name with the id-prefix, not the original network. - err := client.NetworkRemove(ctx, testNet.ID[:12]) + err := client.NetworkRemove(ctx, testNet[:12]) assert.NilError(t, err) // Delete the network using networkID. This should remove the original // network, not the network with the name equal to the networkID - err = client.NetworkRemove(ctx, testNet.ID) + err = client.NetworkRemove(ctx, testNet) assert.NilError(t, err) // networks "testNet" and "idPrefixNet" should be removed, but "fullIDNet" should still exist diff --git a/integration/network/helpers.go b/integration/network/helpers.go index df609dd4104f7..c0d70a168e785 100644 --- a/integration/network/helpers.go +++ b/integration/network/helpers.go @@ -8,8 +8,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/client" "github.com/docker/docker/pkg/parsers/kernel" - "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/icmd" + "gotest.tools/assert/cmp" + "gotest.tools/icmd" ) // CreateMasterDummy creates a dummy network interface diff --git a/integration/network/inspect_test.go b/integration/network/inspect_test.go index 052642f9808e6..815ffb4c337d9 100644 --- a/integration/network/inspect_test.go +++ b/integration/network/inspect_test.go @@ -9,9 +9,10 @@ import ( "github.com/docker/docker/api/types/filters" swarmtypes "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/client" + "github.com/docker/docker/integration/internal/network" "github.com/docker/docker/integration/internal/swarm" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/poll" + "gotest.tools/assert" + "gotest.tools/poll" ) const defaultSwarmPort = 2477 @@ -24,14 +25,10 @@ func TestInspectNetwork(t *testing.T) { defer client.Close() overlayName := "overlay1" - networkCreate := types.NetworkCreate{ - CheckDuplicate: true, - Driver: "overlay", - } - - netResp, err := client.NetworkCreate(context.Background(), overlayName, networkCreate) - assert.NilError(t, err) - overlayID := netResp.ID + overlayID := network.CreateNoError(t, context.Background(), client, overlayName, + network.WithDriver("overlay"), + network.WithCheckDuplicate(), + ) var instances uint64 = 4 serviceName := "TestService" + t.Name() @@ -44,7 +41,7 @@ func TestInspectNetwork(t *testing.T) { poll.WaitOn(t, serviceRunningTasksCount(client, serviceID, instances), swarm.ServicePoll) - _, _, err = client.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{}) + _, _, err := client.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{}) assert.NilError(t, err) // Test inspect verbose with full NetworkID diff --git a/integration/network/ipvlan/ipvlan_test.go b/integration/network/ipvlan/ipvlan_test.go index 001565eefd46d..3da255e747373 100644 --- a/integration/network/ipvlan/ipvlan_test.go +++ b/integration/network/ipvlan/ipvlan_test.go @@ -6,14 +6,13 @@ import ( "testing" "time" - "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/network" dclient "github.com/docker/docker/client" "github.com/docker/docker/integration/internal/container" + net "github.com/docker/docker/integration/internal/network" n "github.com/docker/docker/integration/network" "github.com/docker/docker/internal/test/daemon" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + "gotest.tools/skip" ) func TestDockerNetworkIpvlanPersistance(t *testing.T) { @@ -35,17 +34,15 @@ func TestDockerNetworkIpvlanPersistance(t *testing.T) { assert.NilError(t, err) // create a network specifying the desired sub-interface name - _, err = client.NetworkCreate(context.Background(), "di-persist", types.NetworkCreate{ - Driver: "ipvlan", - Options: map[string]string{ - "parent": "di-dummy0.70", - }, - }) - assert.NilError(t, err) - assert.Check(t, n.IsNetworkAvailable(client, "di-persist")) + netName := "di-persist" + net.CreateNoError(t, context.Background(), client, netName, + net.WithIPvlan("di-dummy0.70", ""), + ) + + assert.Check(t, n.IsNetworkAvailable(client, netName)) // Restart docker daemon to test the config has persisted to disk d.Restart(t) - assert.Check(t, n.IsNetworkAvailable(client, "di-persist")) + assert.Check(t, n.IsNetworkAvailable(client, netName)) } func TestDockerNetworkIpvlan(t *testing.T) { @@ -105,20 +102,17 @@ func testIpvlanSubinterface(client dclient.APIClient) func(*testing.T) { n.CreateMasterDummy(t, master) defer n.DeleteInterface(t, master) - _, err := client.NetworkCreate(context.Background(), "di-subinterface", types.NetworkCreate{ - Driver: "ipvlan", - Options: map[string]string{ - "parent": "di-dummy0.60", - }, - }) - assert.NilError(t, err) - assert.Check(t, n.IsNetworkAvailable(client, "di-subinterface")) + netName := "di-subinterface" + net.CreateNoError(t, context.Background(), client, netName, + net.WithIPvlan("di-dummy0.60", ""), + ) + assert.Check(t, n.IsNetworkAvailable(client, netName)) // delete the network while preserving the parent link - err = client.NetworkRemove(context.Background(), "di-subinterface") + err := client.NetworkRemove(context.Background(), netName) assert.NilError(t, err) - assert.Check(t, n.IsNetworkNotAvailable(client, "di-subinterface")) + assert.Check(t, n.IsNetworkNotAvailable(client, netName)) // verify the network delete did not delete the predefined link n.LinkExists(t, "di-dummy0") } @@ -128,25 +122,20 @@ func testIpvlanOverlapParent(client dclient.APIClient) func(*testing.T) { return func(t *testing.T) { // verify the same parent interface cannot be used if already in use by an existing network master := "di-dummy0" + parent := master + ".30" n.CreateMasterDummy(t, master) defer n.DeleteInterface(t, master) - n.CreateVlanInterface(t, master, "di-dummy0.30", "30") - - _, err := client.NetworkCreate(context.Background(), "di-subinterface", types.NetworkCreate{ - Driver: "ipvlan", - Options: map[string]string{ - "parent": "di-dummy0.30", - }, - }) - assert.NilError(t, err) - assert.Check(t, n.IsNetworkAvailable(client, "di-subinterface")) - - _, err = client.NetworkCreate(context.Background(), "di-subinterface", types.NetworkCreate{ - Driver: "ipvlan", - Options: map[string]string{ - "parent": "di-dummy0.30", - }, - }) + n.CreateVlanInterface(t, master, parent, "30") + + netName := "di-subinterface" + net.CreateNoError(t, context.Background(), client, netName, + net.WithIPvlan(parent, ""), + ) + assert.Check(t, n.IsNetworkAvailable(client, netName)) + + _, err := net.Create(context.Background(), client, netName, + net.WithIPvlan(parent, ""), + ) // verify that the overlap returns an error assert.Check(t, err != nil) } @@ -155,37 +144,37 @@ func testIpvlanOverlapParent(client dclient.APIClient) func(*testing.T) { func testIpvlanL2NilParent(client dclient.APIClient) func(*testing.T) { return func(t *testing.T) { // ipvlan l2 mode - dummy parent interface is provisioned dynamically - _, err := client.NetworkCreate(context.Background(), "di-nil-parent", types.NetworkCreate{ - Driver: "ipvlan", - }) - assert.NilError(t, err) - assert.Check(t, n.IsNetworkAvailable(client, "di-nil-parent")) + netName := "di-nil-parent" + net.CreateNoError(t, context.Background(), client, netName, + net.WithIPvlan("", ""), + ) + assert.Check(t, n.IsNetworkAvailable(client, netName)) ctx := context.Background() - id1 := container.Run(t, ctx, client, container.WithNetworkMode("di-nil-parent")) - id2 := container.Run(t, ctx, client, container.WithNetworkMode("di-nil-parent")) + id1 := container.Run(t, ctx, client, container.WithNetworkMode(netName)) + id2 := container.Run(t, ctx, client, container.WithNetworkMode(netName)) - _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1}) + _, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1}) assert.NilError(t, err) } } func testIpvlanL2InternalMode(client dclient.APIClient) func(*testing.T) { return func(t *testing.T) { - _, err := client.NetworkCreate(context.Background(), "di-internal", types.NetworkCreate{ - Driver: "ipvlan", - Internal: true, - }) - assert.NilError(t, err) - assert.Check(t, n.IsNetworkAvailable(client, "di-internal")) + netName := "di-internal" + net.CreateNoError(t, context.Background(), client, netName, + net.WithIPvlan("", ""), + net.WithInternal(), + ) + assert.Check(t, n.IsNetworkAvailable(client, netName)) ctx := context.Background() - id1 := container.Run(t, ctx, client, container.WithNetworkMode("di-internal")) - id2 := container.Run(t, ctx, client, container.WithNetworkMode("di-internal")) + id1 := container.Run(t, ctx, client, container.WithNetworkMode(netName)) + id2 := container.Run(t, ctx, client, container.WithNetworkMode(netName)) timeoutCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() - _, err = container.Exec(timeoutCtx, client, id1, []string{"ping", "-c", "1", "-w", "1", "8.8.8.8"}) + _, err := container.Exec(timeoutCtx, client, id1, []string{"ping", "-c", "1", "-w", "1", "8.8.8.8"}) // FIXME(vdemeester) check the time of error ? assert.Check(t, err != nil) assert.Check(t, timeoutCtx.Err() == context.DeadlineExceeded) @@ -197,79 +186,53 @@ func testIpvlanL2InternalMode(client dclient.APIClient) func(*testing.T) { func testIpvlanL3NilParent(client dclient.APIClient) func(*testing.T) { return func(t *testing.T) { - _, err := client.NetworkCreate(context.Background(), "di-nil-parent-l3", types.NetworkCreate{ - Driver: "ipvlan", - Options: map[string]string{ - "ipvlan_mode": "l3", - }, - IPAM: &network.IPAM{ - Config: []network.IPAMConfig{ - { - Subnet: "172.28.230.0/24", - AuxAddress: map[string]string{}, - }, - { - Subnet: "172.28.220.0/24", - AuxAddress: map[string]string{}, - }, - }, - }, - }) - assert.NilError(t, err) - assert.Check(t, n.IsNetworkAvailable(client, "di-nil-parent-l3")) + netName := "di-nil-parent-l3" + net.CreateNoError(t, context.Background(), client, netName, + net.WithIPvlan("", "l3"), + net.WithIPAM("172.28.230.0/24", ""), + net.WithIPAM("172.28.220.0/24", ""), + ) + assert.Check(t, n.IsNetworkAvailable(client, netName)) ctx := context.Background() id1 := container.Run(t, ctx, client, - container.WithNetworkMode("di-nil-parent-l3"), - container.WithIPv4("di-nil-parent-l3", "172.28.220.10"), + container.WithNetworkMode(netName), + container.WithIPv4(netName, "172.28.220.10"), ) id2 := container.Run(t, ctx, client, - container.WithNetworkMode("di-nil-parent-l3"), - container.WithIPv4("di-nil-parent-l3", "172.28.230.10"), + container.WithNetworkMode(netName), + container.WithIPv4(netName, "172.28.230.10"), ) - _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1}) + _, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1}) assert.NilError(t, err) } } func testIpvlanL3InternalMode(client dclient.APIClient) func(*testing.T) { return func(t *testing.T) { - _, err := client.NetworkCreate(context.Background(), "di-internal-l3", types.NetworkCreate{ - Driver: "ipvlan", - Internal: true, - Options: map[string]string{ - "ipvlan_mode": "l3", - }, - IPAM: &network.IPAM{ - Config: []network.IPAMConfig{ - { - Subnet: "172.28.230.0/24", - AuxAddress: map[string]string{}, - }, - { - Subnet: "172.28.220.0/24", - AuxAddress: map[string]string{}, - }, - }, - }, - }) - assert.NilError(t, err) - assert.Check(t, n.IsNetworkAvailable(client, "di-internal-l3")) + netName := "di-internal-l3" + net.CreateNoError(t, context.Background(), client, netName, + net.WithIPvlan("", "l3"), + net.WithInternal(), + net.WithIPAM("172.28.230.0/24", ""), + net.WithIPAM("172.28.220.0/24", ""), + ) + assert.Check(t, n.IsNetworkAvailable(client, netName)) ctx := context.Background() id1 := container.Run(t, ctx, client, - container.WithNetworkMode("di-internal-l3"), - container.WithIPv4("di-internal-l3", "172.28.220.10"), + container.WithNetworkMode(netName), + container.WithIPv4(netName, "172.28.220.10"), ) id2 := container.Run(t, ctx, client, - container.WithNetworkMode("di-internal-l3"), - container.WithIPv4("di-internal-l3", "172.28.230.10"), + container.WithNetworkMode(netName), + container.WithIPv4(netName, "172.28.230.10"), ) timeoutCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() - _, err = container.Exec(timeoutCtx, client, id1, []string{"ping", "-c", "1", "-w", "1", "8.8.8.8"}) + _, err := container.Exec(timeoutCtx, client, id1, []string{"ping", "-c", "1", "-w", "1", "8.8.8.8"}) // FIXME(vdemeester) check the time of error ? assert.Check(t, err != nil) assert.Check(t, timeoutCtx.Err() == context.DeadlineExceeded) @@ -281,174 +244,135 @@ func testIpvlanL3InternalMode(client dclient.APIClient) func(*testing.T) { func testIpvlanL2MultiSubnet(client dclient.APIClient) func(*testing.T) { return func(t *testing.T) { - _, err := client.NetworkCreate(context.Background(), "dualstackl2", types.NetworkCreate{ - Driver: "ipvlan", - EnableIPv6: true, - IPAM: &network.IPAM{ - Config: []network.IPAMConfig{ - { - Subnet: "172.28.200.0/24", - AuxAddress: map[string]string{}, - }, - { - Subnet: "172.28.202.0/24", - Gateway: "172.28.202.254", - AuxAddress: map[string]string{}, - }, - { - Subnet: "2001:db8:abc8::/64", - AuxAddress: map[string]string{}, - }, - { - Subnet: "2001:db8:abc6::/64", - Gateway: "2001:db8:abc6::254", - AuxAddress: map[string]string{}, - }, - }, - }, - }) - assert.NilError(t, err) - assert.Check(t, n.IsNetworkAvailable(client, "dualstackl2")) + netName := "dualstackl2" + net.CreateNoError(t, context.Background(), client, netName, + net.WithIPvlan("", ""), + net.WithIPv6(), + net.WithIPAM("172.28.200.0/24", ""), + net.WithIPAM("172.28.202.0/24", "172.28.202.254"), + net.WithIPAM("2001:db8:abc8::/64", ""), + net.WithIPAM("2001:db8:abc6::/64", "2001:db8:abc6::254"), + ) + assert.Check(t, n.IsNetworkAvailable(client, netName)) // start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.100.0/24 and 2001:db8:abc2::/64 ctx := context.Background() id1 := container.Run(t, ctx, client, - container.WithNetworkMode("dualstackl2"), - container.WithIPv4("dualstackl2", "172.28.200.20"), - container.WithIPv6("dualstackl2", "2001:db8:abc8::20"), + container.WithNetworkMode(netName), + container.WithIPv4(netName, "172.28.200.20"), + container.WithIPv6(netName, "2001:db8:abc8::20"), ) id2 := container.Run(t, ctx, client, - container.WithNetworkMode("dualstackl2"), - container.WithIPv4("dualstackl2", "172.28.200.21"), - container.WithIPv6("dualstackl2", "2001:db8:abc8::21"), + container.WithNetworkMode(netName), + container.WithIPv4(netName, "172.28.200.21"), + container.WithIPv6(netName, "2001:db8:abc8::21"), ) c1, err := client.ContainerInspect(ctx, id1) assert.NilError(t, err) // verify ipv4 connectivity to the explicit --ipv address second to first - _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks["dualstackl2"].IPAddress}) + _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks[netName].IPAddress}) assert.NilError(t, err) // verify ipv6 connectivity to the explicit --ipv6 address second to first - _, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks["dualstackl2"].GlobalIPv6Address}) + _, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks[netName].GlobalIPv6Address}) assert.NilError(t, err) // start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.102.0/24 and 2001:db8:abc4::/64 id3 := container.Run(t, ctx, client, - container.WithNetworkMode("dualstackl2"), - container.WithIPv4("dualstackl2", "172.28.202.20"), - container.WithIPv6("dualstackl2", "2001:db8:abc6::20"), + container.WithNetworkMode(netName), + container.WithIPv4(netName, "172.28.202.20"), + container.WithIPv6(netName, "2001:db8:abc6::20"), ) id4 := container.Run(t, ctx, client, - container.WithNetworkMode("dualstackl2"), - container.WithIPv4("dualstackl2", "172.28.202.21"), - container.WithIPv6("dualstackl2", "2001:db8:abc6::21"), + container.WithNetworkMode(netName), + container.WithIPv4(netName, "172.28.202.21"), + container.WithIPv6(netName, "2001:db8:abc6::21"), ) c3, err := client.ContainerInspect(ctx, id3) assert.NilError(t, err) // verify ipv4 connectivity to the explicit --ipv address from third to fourth - _, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks["dualstackl2"].IPAddress}) + _, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks[netName].IPAddress}) assert.NilError(t, err) // verify ipv6 connectivity to the explicit --ipv6 address from third to fourth - _, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks["dualstackl2"].GlobalIPv6Address}) + _, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks[netName].GlobalIPv6Address}) assert.NilError(t, err) // Inspect the v4 gateway to ensure the proper default GW was assigned - assert.Equal(t, c1.NetworkSettings.Networks["dualstackl2"].Gateway, "172.28.200.1") + assert.Equal(t, c1.NetworkSettings.Networks[netName].Gateway, "172.28.200.1") // Inspect the v6 gateway to ensure the proper default GW was assigned - assert.Equal(t, c1.NetworkSettings.Networks["dualstackl2"].IPv6Gateway, "2001:db8:abc8::1") + assert.Equal(t, c1.NetworkSettings.Networks[netName].IPv6Gateway, "2001:db8:abc8::1") // Inspect the v4 gateway to ensure the proper explicitly assigned default GW was assigned - assert.Equal(t, c3.NetworkSettings.Networks["dualstackl2"].Gateway, "172.28.202.254") + assert.Equal(t, c3.NetworkSettings.Networks[netName].Gateway, "172.28.202.254") // Inspect the v6 gateway to ensure the proper explicitly assigned default GW was assigned - assert.Equal(t, c3.NetworkSettings.Networks["dualstackl2"].IPv6Gateway, "2001:db8:abc6::254") + assert.Equal(t, c3.NetworkSettings.Networks[netName].IPv6Gateway, "2001:db8:abc6::254") } } func testIpvlanL3MultiSubnet(client dclient.APIClient) func(*testing.T) { return func(t *testing.T) { - _, err := client.NetworkCreate(context.Background(), "dualstackl3", types.NetworkCreate{ - Driver: "ipvlan", - EnableIPv6: true, - Options: map[string]string{ - "ipvlan_mode": "l3", - }, - IPAM: &network.IPAM{ - Config: []network.IPAMConfig{ - { - Subnet: "172.28.10.0/24", - AuxAddress: map[string]string{}, - }, - { - Subnet: "172.28.12.0/24", - Gateway: "172.28.12.254", - AuxAddress: map[string]string{}, - }, - { - Subnet: "2001:db8:abc9::/64", - AuxAddress: map[string]string{}, - }, - { - Subnet: "2001:db8:abc7::/64", - Gateway: "2001:db8:abc7::254", - AuxAddress: map[string]string{}, - }, - }, - }, - }) - assert.NilError(t, err) - assert.Check(t, n.IsNetworkAvailable(client, "dualstackl3")) + netName := "dualstackl3" + net.CreateNoError(t, context.Background(), client, netName, + net.WithIPvlan("", "l3"), + net.WithIPv6(), + net.WithIPAM("172.28.10.0/24", ""), + net.WithIPAM("172.28.12.0/24", "172.28.12.254"), + net.WithIPAM("2001:db8:abc9::/64", ""), + net.WithIPAM("2001:db8:abc7::/64", "2001:db8:abc7::254"), + ) + assert.Check(t, n.IsNetworkAvailable(client, netName)) // start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.100.0/24 and 2001:db8:abc2::/64 ctx := context.Background() id1 := container.Run(t, ctx, client, - container.WithNetworkMode("dualstackl3"), - container.WithIPv4("dualstackl3", "172.28.10.20"), - container.WithIPv6("dualstackl3", "2001:db8:abc9::20"), + container.WithNetworkMode(netName), + container.WithIPv4(netName, "172.28.10.20"), + container.WithIPv6(netName, "2001:db8:abc9::20"), ) id2 := container.Run(t, ctx, client, - container.WithNetworkMode("dualstackl3"), - container.WithIPv4("dualstackl3", "172.28.10.21"), - container.WithIPv6("dualstackl3", "2001:db8:abc9::21"), + container.WithNetworkMode(netName), + container.WithIPv4(netName, "172.28.10.21"), + container.WithIPv6(netName, "2001:db8:abc9::21"), ) c1, err := client.ContainerInspect(ctx, id1) assert.NilError(t, err) // verify ipv4 connectivity to the explicit --ipv address second to first - _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks["dualstackl3"].IPAddress}) + _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks[netName].IPAddress}) assert.NilError(t, err) // verify ipv6 connectivity to the explicit --ipv6 address second to first - _, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks["dualstackl3"].GlobalIPv6Address}) + _, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks[netName].GlobalIPv6Address}) assert.NilError(t, err) // start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.102.0/24 and 2001:db8:abc4::/64 id3 := container.Run(t, ctx, client, - container.WithNetworkMode("dualstackl3"), - container.WithIPv4("dualstackl3", "172.28.12.20"), - container.WithIPv6("dualstackl3", "2001:db8:abc7::20"), + container.WithNetworkMode(netName), + container.WithIPv4(netName, "172.28.12.20"), + container.WithIPv6(netName, "2001:db8:abc7::20"), ) id4 := container.Run(t, ctx, client, - container.WithNetworkMode("dualstackl3"), - container.WithIPv4("dualstackl3", "172.28.12.21"), - container.WithIPv6("dualstackl3", "2001:db8:abc7::21"), + container.WithNetworkMode(netName), + container.WithIPv4(netName, "172.28.12.21"), + container.WithIPv6(netName, "2001:db8:abc7::21"), ) c3, err := client.ContainerInspect(ctx, id3) assert.NilError(t, err) // verify ipv4 connectivity to the explicit --ipv address from third to fourth - _, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks["dualstackl3"].IPAddress}) + _, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks[netName].IPAddress}) assert.NilError(t, err) // verify ipv6 connectivity to the explicit --ipv6 address from third to fourth - _, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks["dualstackl3"].GlobalIPv6Address}) + _, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks[netName].GlobalIPv6Address}) assert.NilError(t, err) // Inspect the v4 gateway to ensure no next hop is assigned in L3 mode - assert.Equal(t, c1.NetworkSettings.Networks["dualstackl3"].Gateway, "") + assert.Equal(t, c1.NetworkSettings.Networks[netName].Gateway, "") // Inspect the v6 gateway to ensure the explicitly specified default GW is ignored per L3 mode enabled - assert.Equal(t, c1.NetworkSettings.Networks["dualstackl3"].IPv6Gateway, "") + assert.Equal(t, c1.NetworkSettings.Networks[netName].IPv6Gateway, "") // Inspect the v4 gateway to ensure no next hop is assigned in L3 mode - assert.Equal(t, c3.NetworkSettings.Networks["dualstackl3"].Gateway, "") + assert.Equal(t, c3.NetworkSettings.Networks[netName].Gateway, "") // Inspect the v6 gateway to ensure the explicitly specified default GW is ignored per L3 mode enabled - assert.Equal(t, c3.NetworkSettings.Networks["dualstackl3"].IPv6Gateway, "") + assert.Equal(t, c3.NetworkSettings.Networks[netName].IPv6Gateway, "") } } @@ -456,32 +380,18 @@ func testIpvlanAddressing(client dclient.APIClient) func(*testing.T) { return func(t *testing.T) { // Verify ipvlan l2 mode sets the proper default gateway routes via netlink // for either an explicitly set route by the user or inferred via default IPAM - _, err := client.NetworkCreate(context.Background(), "dualstackl2", types.NetworkCreate{ - Driver: "ipvlan", - EnableIPv6: true, - Options: map[string]string{ - "ipvlan_mode": "l2", - }, - IPAM: &network.IPAM{ - Config: []network.IPAMConfig{ - { - Subnet: "172.28.140.0/24", - Gateway: "172.28.140.254", - AuxAddress: map[string]string{}, - }, - { - Subnet: "2001:db8:abcb::/64", - AuxAddress: map[string]string{}, - }, - }, - }, - }) - assert.NilError(t, err) - assert.Check(t, n.IsNetworkAvailable(client, "dualstackl2")) + netNameL2 := "dualstackl2" + net.CreateNoError(t, context.Background(), client, netNameL2, + net.WithIPvlan("", "l2"), + net.WithIPv6(), + net.WithIPAM("172.28.140.0/24", "172.28.140.254"), + net.WithIPAM("2001:db8:abcb::/64", ""), + ) + assert.Check(t, n.IsNetworkAvailable(client, netNameL2)) ctx := context.Background() id1 := container.Run(t, ctx, client, - container.WithNetworkMode("dualstackl2"), + container.WithNetworkMode(netNameL2), ) // Validate ipvlan l2 mode defaults gateway sets the default IPAM next-hop inferred from the subnet result, err := container.Exec(ctx, client, id1, []string{"ip", "route"}) @@ -493,32 +403,17 @@ func testIpvlanAddressing(client dclient.APIClient) func(*testing.T) { assert.Check(t, strings.Contains(result.Combined(), "default via 2001:db8:abcb::1 dev eth0")) // Validate ipvlan l3 mode sets the v4 gateway to dev eth0 and disregards any explicit or inferred next-hops - _, err = client.NetworkCreate(context.Background(), "dualstackl3", types.NetworkCreate{ - Driver: "ipvlan", - EnableIPv6: true, - Options: map[string]string{ - "ipvlan_mode": "l3", - }, - IPAM: &network.IPAM{ - Config: []network.IPAMConfig{ - { - Subnet: "172.28.160.0/24", - Gateway: "172.28.160.254", - AuxAddress: map[string]string{}, - }, - { - Subnet: "2001:db8:abcd::/64", - Gateway: "2001:db8:abcd::254", - AuxAddress: map[string]string{}, - }, - }, - }, - }) - assert.NilError(t, err) - assert.Check(t, n.IsNetworkAvailable(client, "dualstackl3")) + netNameL3 := "dualstackl3" + net.CreateNoError(t, context.Background(), client, netNameL3, + net.WithIPvlan("", "l3"), + net.WithIPv6(), + net.WithIPAM("172.28.160.0/24", "172.28.160.254"), + net.WithIPAM("2001:db8:abcd::/64", "2001:db8:abcd::254"), + ) + assert.Check(t, n.IsNetworkAvailable(client, netNameL3)) id2 := container.Run(t, ctx, client, - container.WithNetworkMode("dualstackl3"), + container.WithNetworkMode(netNameL3), ) // Validate ipvlan l3 mode sets the v4 gateway to dev eth0 and disregards any explicit or inferred next-hops result, err = container.Exec(ctx, client, id2, []string{"ip", "route"}) diff --git a/integration/network/macvlan/macvlan_test.go b/integration/network/macvlan/macvlan_test.go index 7358af873b445..9c0fd407a1253 100644 --- a/integration/network/macvlan/macvlan_test.go +++ b/integration/network/macvlan/macvlan_test.go @@ -6,14 +6,13 @@ import ( "testing" "time" - "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/network" "github.com/docker/docker/client" "github.com/docker/docker/integration/internal/container" + net "github.com/docker/docker/integration/internal/network" n "github.com/docker/docker/integration/network" "github.com/docker/docker/internal/test/daemon" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + "gotest.tools/skip" ) func TestDockerNetworkMacvlanPersistance(t *testing.T) { @@ -33,16 +32,13 @@ func TestDockerNetworkMacvlanPersistance(t *testing.T) { client, err := d.NewClient() assert.NilError(t, err) - _, err = client.NetworkCreate(context.Background(), "dm-persist", types.NetworkCreate{ - Driver: "macvlan", - Options: map[string]string{ - "parent": "dm-dummy0.60", - }, - }) - assert.NilError(t, err) - assert.Check(t, n.IsNetworkAvailable(client, "dm-persist")) + netName := "dm-persist" + net.CreateNoError(t, context.Background(), client, netName, + net.WithMacvlan("dm-dummy0.60"), + ) + assert.Check(t, n.IsNetworkAvailable(client, netName)) d.Restart(t) - assert.Check(t, n.IsNetworkAvailable(client, "dm-persist")) + assert.Check(t, n.IsNetworkAvailable(client, netName)) } func TestDockerNetworkMacvlan(t *testing.T) { @@ -91,29 +87,25 @@ func testMacvlanOverlapParent(client client.APIClient) func(*testing.T) { n.CreateMasterDummy(t, master) defer n.DeleteInterface(t, master) - _, err := client.NetworkCreate(context.Background(), "dm-subinterface", types.NetworkCreate{ - Driver: "macvlan", - Options: map[string]string{ - "parent": "dm-dummy0.40", - }, - }) - assert.NilError(t, err) - assert.Check(t, n.IsNetworkAvailable(client, "dm-subinterface")) - - _, err = client.NetworkCreate(context.Background(), "dm-parent-net-overlap", types.NetworkCreate{ - Driver: "macvlan", - Options: map[string]string{ - "parent": "dm-dummy0.40", - }, - }) + netName := "dm-subinterface" + parentName := "dm-dummy0.40" + net.CreateNoError(t, context.Background(), client, netName, + net.WithMacvlan(parentName), + ) + assert.Check(t, n.IsNetworkAvailable(client, netName)) + + _, err := net.Create(context.Background(), client, "dm-parent-net-overlap", + net.WithMacvlan(parentName), + ) assert.Check(t, err != nil) + // delete the network while preserving the parent link - err = client.NetworkRemove(context.Background(), "dm-subinterface") + err = client.NetworkRemove(context.Background(), netName) assert.NilError(t, err) - assert.Check(t, n.IsNetworkNotAvailable(client, "dm-subinterface")) + assert.Check(t, n.IsNetworkNotAvailable(client, netName)) // verify the network delete did not delete the predefined link - n.LinkExists(t, "dm-dummy0") + n.LinkExists(t, master) } } @@ -121,43 +113,41 @@ func testMacvlanSubinterface(client client.APIClient) func(*testing.T) { return func(t *testing.T) { // verify the same parent interface cannot be used if already in use by an existing network master := "dm-dummy0" + parentName := "dm-dummy0.20" n.CreateMasterDummy(t, master) defer n.DeleteInterface(t, master) - n.CreateVlanInterface(t, master, "dm-dummy0.20", "20") - - _, err := client.NetworkCreate(context.Background(), "dm-subinterface", types.NetworkCreate{ - Driver: "macvlan", - Options: map[string]string{ - "parent": "dm-dummy0.20", - }, - }) - assert.NilError(t, err) - assert.Check(t, n.IsNetworkAvailable(client, "dm-subinterface")) + n.CreateVlanInterface(t, master, parentName, "20") + + netName := "dm-subinterface" + net.CreateNoError(t, context.Background(), client, netName, + net.WithMacvlan(parentName), + ) + assert.Check(t, n.IsNetworkAvailable(client, netName)) // delete the network while preserving the parent link - err = client.NetworkRemove(context.Background(), "dm-subinterface") + err := client.NetworkRemove(context.Background(), netName) assert.NilError(t, err) - assert.Check(t, n.IsNetworkNotAvailable(client, "dm-subinterface")) + assert.Check(t, n.IsNetworkNotAvailable(client, netName)) // verify the network delete did not delete the predefined link - n.LinkExists(t, "dm-dummy0.20") + n.LinkExists(t, parentName) } } func testMacvlanNilParent(client client.APIClient) func(*testing.T) { return func(t *testing.T) { // macvlan bridge mode - dummy parent interface is provisioned dynamically - _, err := client.NetworkCreate(context.Background(), "dm-nil-parent", types.NetworkCreate{ - Driver: "macvlan", - }) - assert.NilError(t, err) - assert.Check(t, n.IsNetworkAvailable(client, "dm-nil-parent")) + netName := "dm-nil-parent" + net.CreateNoError(t, context.Background(), client, netName, + net.WithMacvlan(""), + ) + assert.Check(t, n.IsNetworkAvailable(client, netName)) ctx := context.Background() - id1 := container.Run(t, ctx, client, container.WithNetworkMode("dm-nil-parent")) - id2 := container.Run(t, ctx, client, container.WithNetworkMode("dm-nil-parent")) + id1 := container.Run(t, ctx, client, container.WithNetworkMode(netName)) + id2 := container.Run(t, ctx, client, container.WithNetworkMode(netName)) - _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1}) + _, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1}) assert.Check(t, err == nil) } } @@ -165,20 +155,20 @@ func testMacvlanNilParent(client client.APIClient) func(*testing.T) { func testMacvlanInternalMode(client client.APIClient) func(*testing.T) { return func(t *testing.T) { // macvlan bridge mode - dummy parent interface is provisioned dynamically - _, err := client.NetworkCreate(context.Background(), "dm-internal", types.NetworkCreate{ - Driver: "macvlan", - Internal: true, - }) - assert.NilError(t, err) - assert.Check(t, n.IsNetworkAvailable(client, "dm-internal")) + netName := "dm-internal" + net.CreateNoError(t, context.Background(), client, netName, + net.WithMacvlan(""), + net.WithInternal(), + ) + assert.Check(t, n.IsNetworkAvailable(client, netName)) ctx := context.Background() - id1 := container.Run(t, ctx, client, container.WithNetworkMode("dm-internal")) - id2 := container.Run(t, ctx, client, container.WithNetworkMode("dm-internal")) + id1 := container.Run(t, ctx, client, container.WithNetworkMode(netName)) + id2 := container.Run(t, ctx, client, container.WithNetworkMode(netName)) timeoutCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() - _, err = container.Exec(timeoutCtx, client, id1, []string{"ping", "-c", "1", "-w", "1", "8.8.8.8"}) + _, err := container.Exec(timeoutCtx, client, id1, []string{"ping", "-c", "1", "-w", "1", "8.8.8.8"}) // FIXME(vdemeester) check the time of error ? assert.Check(t, err != nil) assert.Check(t, timeoutCtx.Err() == context.DeadlineExceeded) @@ -190,34 +180,17 @@ func testMacvlanInternalMode(client client.APIClient) func(*testing.T) { func testMacvlanMultiSubnet(client client.APIClient) func(*testing.T) { return func(t *testing.T) { - _, err := client.NetworkCreate(context.Background(), "dualstackbridge", types.NetworkCreate{ - Driver: "macvlan", - EnableIPv6: true, - IPAM: &network.IPAM{ - Config: []network.IPAMConfig{ - { - Subnet: "172.28.100.0/24", - AuxAddress: map[string]string{}, - }, - { - Subnet: "172.28.102.0/24", - Gateway: "172.28.102.254", - AuxAddress: map[string]string{}, - }, - { - Subnet: "2001:db8:abc2::/64", - AuxAddress: map[string]string{}, - }, - { - Subnet: "2001:db8:abc4::/64", - Gateway: "2001:db8:abc4::254", - AuxAddress: map[string]string{}, - }, - }, - }, - }) - assert.NilError(t, err) - assert.Check(t, n.IsNetworkAvailable(client, "dualstackbridge")) + netName := "dualstackbridge" + net.CreateNoError(t, context.Background(), client, netName, + net.WithMacvlan(""), + net.WithIPv6(), + net.WithIPAM("172.28.100.0/24", ""), + net.WithIPAM("172.28.102.0/24", "172.28.102.254"), + net.WithIPAM("2001:db8:abc2::/64", ""), + net.WithIPAM("2001:db8:abc4::/64", "2001:db8:abc4::254"), + ) + + assert.Check(t, n.IsNetworkAvailable(client, netName)) // start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.100.0/24 and 2001:db8:abc2::/64 ctx := context.Background() @@ -276,28 +249,15 @@ func testMacvlanMultiSubnet(client client.APIClient) func(*testing.T) { func testMacvlanAddressing(client client.APIClient) func(*testing.T) { return func(t *testing.T) { // Ensure the default gateways, next-hops and default dev devices are properly set - _, err := client.NetworkCreate(context.Background(), "dualstackbridge", types.NetworkCreate{ - Driver: "macvlan", - EnableIPv6: true, - Options: map[string]string{ - "macvlan_mode": "bridge", - }, - IPAM: &network.IPAM{ - Config: []network.IPAMConfig{ - { - Subnet: "172.28.130.0/24", - AuxAddress: map[string]string{}, - }, - { - Subnet: "2001:db8:abca::/64", - Gateway: "2001:db8:abca::254", - AuxAddress: map[string]string{}, - }, - }, - }, - }) - assert.NilError(t, err) - assert.Check(t, n.IsNetworkAvailable(client, "dualstackbridge")) + netName := "dualstackbridge" + net.CreateNoError(t, context.Background(), client, netName, + net.WithMacvlan(""), + net.WithIPv6(), + net.WithOption("macvlan_mode", "bridge"), + net.WithIPAM("172.28.130.0/24", ""), + net.WithIPAM("2001:db8:abca::/64", "2001:db8:abca::254"), + ) + assert.Check(t, n.IsNetworkAvailable(client, netName)) ctx := context.Background() id1 := container.Run(t, ctx, client, diff --git a/integration/network/service_test.go b/integration/network/service_test.go index 77ef870911acb..b276c510cd37b 100644 --- a/integration/network/service_test.go +++ b/integration/network/service_test.go @@ -9,12 +9,13 @@ import ( swarmtypes "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/api/types/versions" "github.com/docker/docker/client" + "github.com/docker/docker/integration/internal/network" "github.com/docker/docker/integration/internal/swarm" "github.com/docker/docker/internal/test/daemon" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/icmd" - "github.com/gotestyourself/gotestyourself/poll" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + "gotest.tools/icmd" + "gotest.tools/poll" + "gotest.tools/skip" ) // delInterface removes given network interface @@ -65,25 +66,19 @@ func TestDaemonDefaultNetworkPools(t *testing.T) { assert.Equal(t, out.IPAM.Config[0].Subnet, "175.30.0.0/16") // Create a bridge network and verify its subnet is the second default pool - name := "elango" - networkCreate := types.NetworkCreate{ - CheckDuplicate: false, - } - networkCreate.Driver = "bridge" - _, err = cli.NetworkCreate(context.Background(), name, networkCreate) - assert.NilError(t, err) + name := "elango" + t.Name() + network.CreateNoError(t, context.Background(), cli, name, + network.WithDriver("bridge"), + ) out, err = cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{}) assert.NilError(t, err) assert.Equal(t, out.IPAM.Config[0].Subnet, "175.33.0.0/24") // Create a bridge network and verify its subnet is the third default pool - name = "saanvi" - networkCreate = types.NetworkCreate{ - CheckDuplicate: false, - } - networkCreate.Driver = "bridge" - _, err = cli.NetworkCreate(context.Background(), name, networkCreate) - assert.NilError(t, err) + name = "saanvi" + t.Name() + network.CreateNoError(t, context.Background(), cli, name, + network.WithDriver("bridge"), + ) out, err = cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{}) assert.NilError(t, err) assert.Equal(t, out.IPAM.Config[0].Subnet, "175.33.1.0/24") @@ -104,13 +99,10 @@ func TestDaemonRestartWithExistingNetwork(t *testing.T) { defer cli.Close() // Create a bridge network - name := "elango" - networkCreate := types.NetworkCreate{ - CheckDuplicate: false, - } - networkCreate.Driver = "bridge" - _, err = cli.NetworkCreate(context.Background(), name, networkCreate) - assert.NilError(t, err) + name := "elango" + t.Name() + network.CreateNoError(t, context.Background(), cli, name, + network.WithDriver("bridge"), + ) out, err := cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{}) assert.NilError(t, err) networkip := out.IPAM.Config[0].Subnet @@ -139,25 +131,19 @@ func TestDaemonRestartWithExistingNetworkWithDefaultPoolRange(t *testing.T) { defer cli.Close() // Create a bridge network - name := "elango" - networkCreate := types.NetworkCreate{ - CheckDuplicate: false, - } - networkCreate.Driver = "bridge" - _, err = cli.NetworkCreate(context.Background(), name, networkCreate) - assert.NilError(t, err) + name := "elango" + t.Name() + network.CreateNoError(t, context.Background(), cli, name, + network.WithDriver("bridge"), + ) out, err := cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{}) assert.NilError(t, err) networkip := out.IPAM.Config[0].Subnet // Create a bridge network - name = "sthira" - networkCreate = types.NetworkCreate{ - CheckDuplicate: false, - } - networkCreate.Driver = "bridge" - _, err = cli.NetworkCreate(context.Background(), name, networkCreate) - assert.NilError(t, err) + name = "sthira" + t.Name() + network.CreateNoError(t, context.Background(), cli, name, + network.WithDriver("bridge"), + ) out, err = cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{}) assert.NilError(t, err) networkip2 := out.IPAM.Config[0].Subnet @@ -168,13 +154,10 @@ func TestDaemonRestartWithExistingNetworkWithDefaultPoolRange(t *testing.T) { "--default-address-pool", "base=175.19.0.0/16,size=24") // Create a bridge network - name = "saanvi" - networkCreate = types.NetworkCreate{ - CheckDuplicate: false, - } - networkCreate.Driver = "bridge" - _, err = cli.NetworkCreate(context.Background(), name, networkCreate) - assert.NilError(t, err) + name = "saanvi" + t.Name() + network.CreateNoError(t, context.Background(), cli, name, + network.WithDriver("bridge"), + ) out1, err := cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{}) assert.NilError(t, err) diff --git a/integration/plugin/authz/authz_plugin_test.go b/integration/plugin/authz/authz_plugin_test.go index 5585d93024c77..105affc1af077 100644 --- a/integration/plugin/authz/authz_plugin_test.go +++ b/integration/plugin/authz/authz_plugin_test.go @@ -25,8 +25,8 @@ import ( "github.com/docker/docker/internal/test/environment" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/authorization" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + "gotest.tools/skip" ) const ( @@ -211,7 +211,7 @@ func TestAuthZPluginDenyResponse(t *testing.T) { // TestAuthZPluginAllowEventStream verifies event stream propagates // correctly after request pass through by the authorization plugin func TestAuthZPluginAllowEventStream(t *testing.T) { - skip.IfCondition(t, testEnv.DaemonInfo.OSType != "linux") + skip.If(t, testEnv.DaemonInfo.OSType != "linux") defer setupTestV1(t)() ctrl.reqRes.Allow = true diff --git a/integration/plugin/authz/authz_plugin_v2_test.go b/integration/plugin/authz/authz_plugin_v2_test.go index 93e36d76b9179..5ebaca41c626a 100644 --- a/integration/plugin/authz/authz_plugin_v2_test.go +++ b/integration/plugin/authz/authz_plugin_v2_test.go @@ -16,8 +16,8 @@ import ( "github.com/docker/docker/client" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/integration/internal/requirement" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + "gotest.tools/skip" ) var ( @@ -29,8 +29,8 @@ var ( ) func setupTestV2(t *testing.T) func() { - skip.IfCondition(t, testEnv.DaemonInfo.OSType != "linux") - skip.IfCondition(t, !requirement.HasHubConnectivity(t)) + skip.If(t, testEnv.DaemonInfo.OSType != "linux") + skip.If(t, !requirement.HasHubConnectivity(t)) teardown := setupTest(t) @@ -40,7 +40,7 @@ func setupTestV2(t *testing.T) func() { } func TestAuthZPluginV2AllowNonVolumeRequest(t *testing.T) { - skip.IfCondition(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64") + skip.If(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64") defer setupTestV2(t)() client, err := d.NewClient() @@ -64,7 +64,7 @@ func TestAuthZPluginV2AllowNonVolumeRequest(t *testing.T) { } func TestAuthZPluginV2Disable(t *testing.T) { - skip.IfCondition(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64") + skip.If(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64") defer setupTestV2(t)() client, err := d.NewClient() @@ -91,7 +91,7 @@ func TestAuthZPluginV2Disable(t *testing.T) { } func TestAuthZPluginV2RejectVolumeRequests(t *testing.T) { - skip.IfCondition(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64") + skip.If(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64") defer setupTestV2(t)() client, err := d.NewClient() @@ -127,7 +127,7 @@ func TestAuthZPluginV2RejectVolumeRequests(t *testing.T) { } func TestAuthZPluginV2BadManifestFailsDaemonStart(t *testing.T) { - skip.IfCondition(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64") + skip.If(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64") defer setupTestV2(t)() client, err := d.NewClient() diff --git a/integration/plugin/authz/main_test.go b/integration/plugin/authz/main_test.go index 54bf259e95ff1..75555dc96fc3f 100644 --- a/integration/plugin/authz/main_test.go +++ b/integration/plugin/authz/main_test.go @@ -16,7 +16,7 @@ import ( "github.com/docker/docker/internal/test/environment" "github.com/docker/docker/pkg/authorization" "github.com/docker/docker/pkg/plugins" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/skip" ) var ( diff --git a/integration/plugin/graphdriver/external_test.go b/integration/plugin/graphdriver/external_test.go index 11e24aec70338..99ce60ceef0e1 100644 --- a/integration/plugin/graphdriver/external_test.go +++ b/integration/plugin/graphdriver/external_test.go @@ -22,9 +22,9 @@ import ( "github.com/docker/docker/internal/test/daemon" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/plugins" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/skip" ) type graphEventsCounter struct { @@ -394,12 +394,12 @@ func testGraphDriverPull(c client.APIClient, d *daemon.Daemon) func(*testing.T) defer d.Stop(t) ctx := context.Background() - r, err := c.ImagePull(ctx, "busybox:latest", types.ImagePullOptions{}) + r, err := c.ImagePull(ctx, "busybox:latest@sha256:bbc3a03235220b170ba48a157dd097dd1379299370e1ed99ce976df0355d24f0", types.ImagePullOptions{}) assert.NilError(t, err) _, err = io.Copy(ioutil.Discard, r) assert.NilError(t, err) - container.Run(t, ctx, c, container.WithImage("busybox:latest")) + container.Run(t, ctx, c, container.WithImage("busybox:latest@sha256:bbc3a03235220b170ba48a157dd097dd1379299370e1ed99ce976df0355d24f0")) } } @@ -437,6 +437,7 @@ func TestGraphdriverPluginV2(t *testing.T) { testGraphDriver(t, client, ctx, plugin, nil) } +// nolint: golint func testGraphDriver(t *testing.T, c client.APIClient, ctx context.Context, driverName string, afterContainerRunFn func(*testing.T)) { //nolint: golint id := container.Run(t, ctx, c, container.WithCmd("sh", "-c", "echo hello > /hello")) diff --git a/integration/plugin/logging/logging_test.go b/integration/plugin/logging/logging_test.go index 1b6f2962bb4df..3921fa6e6985c 100644 --- a/integration/plugin/logging/logging_test.go +++ b/integration/plugin/logging/logging_test.go @@ -11,8 +11,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/daemon" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + "gotest.tools/skip" ) func TestContinueAfterPluginCrash(t *testing.T) { diff --git a/integration/plugin/logging/validation_test.go b/integration/plugin/logging/validation_test.go index b0398cc6ab90b..0d9b15efbfe4a 100644 --- a/integration/plugin/logging/validation_test.go +++ b/integration/plugin/logging/validation_test.go @@ -6,8 +6,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/internal/test/daemon" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + "gotest.tools/skip" ) // Regression test for #35553 diff --git a/integration/plugin/volumes/mounts_test.go b/integration/plugin/volumes/mounts_test.go index 97e822279759b..4b422ee5c39f5 100644 --- a/integration/plugin/volumes/mounts_test.go +++ b/integration/plugin/volumes/mounts_test.go @@ -9,8 +9,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/internal/test/daemon" "github.com/docker/docker/internal/test/fixtures/plugin" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + "gotest.tools/skip" ) // TestPluginWithDevMounts tests very specific regression caused by mounts ordering diff --git a/integration/secret/secret_test.go b/integration/secret/secret_test.go index 939dad60de2ad..ecc3108f65774 100644 --- a/integration/secret/secret_test.go +++ b/integration/secret/secret_test.go @@ -13,9 +13,9 @@ import ( "github.com/docker/docker/client" "github.com/docker/docker/integration/internal/swarm" "github.com/docker/docker/pkg/stdcopy" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/skip" ) func TestSecretInspect(t *testing.T) { diff --git a/integration/service/create_test.go b/integration/service/create_test.go index 68af6e825aa56..a89ae0a172cc3 100644 --- a/integration/service/create_test.go +++ b/integration/service/create_test.go @@ -2,6 +2,7 @@ package service // import "github.com/docker/docker/integration/service" import ( "context" + "fmt" "io/ioutil" "testing" "time" @@ -10,12 +11,66 @@ import ( "github.com/docker/docker/api/types/filters" swarmtypes "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/client" + "github.com/docker/docker/integration/internal/network" "github.com/docker/docker/integration/internal/swarm" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/poll" + "github.com/docker/docker/internal/test/daemon" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/poll" ) +func TestServiceCreateInit(t *testing.T) { + defer setupTest(t)() + t.Run("daemonInitDisabled", testServiceCreateInit(false)) + t.Run("daemonInitEnabled", testServiceCreateInit(true)) +} + +func testServiceCreateInit(daemonEnabled bool) func(t *testing.T) { + return func(t *testing.T) { + var ops = []func(*daemon.Daemon){} + + if daemonEnabled { + ops = append(ops, daemon.WithInit) + } + d := swarm.NewSwarm(t, testEnv, ops...) + defer d.Stop(t) + client := d.NewClientT(t) + defer client.Close() + + booleanTrue := true + booleanFalse := false + + serviceID := swarm.CreateService(t, d) + poll.WaitOn(t, serviceRunningTasksCount(client, serviceID, 1), swarm.ServicePoll) + i := inspectServiceContainer(t, client, serviceID) + // HostConfig.Init == nil means that it delegates to daemon configuration + assert.Check(t, i.HostConfig.Init == nil) + + serviceID = swarm.CreateService(t, d, swarm.ServiceWithInit(&booleanTrue)) + poll.WaitOn(t, serviceRunningTasksCount(client, serviceID, 1), swarm.ServicePoll) + i = inspectServiceContainer(t, client, serviceID) + assert.Check(t, is.Equal(true, *i.HostConfig.Init)) + + serviceID = swarm.CreateService(t, d, swarm.ServiceWithInit(&booleanFalse)) + poll.WaitOn(t, serviceRunningTasksCount(client, serviceID, 1), swarm.ServicePoll) + i = inspectServiceContainer(t, client, serviceID) + assert.Check(t, is.Equal(false, *i.HostConfig.Init)) + } +} + +func inspectServiceContainer(t *testing.T, client client.APIClient, serviceID string) types.ContainerJSON { + t.Helper() + filter := filters.NewArgs() + filter.Add("label", fmt.Sprintf("com.docker.swarm.service.id=%s", serviceID)) + containers, err := client.ContainerList(context.Background(), types.ContainerListOptions{Filters: filter}) + assert.NilError(t, err) + assert.Check(t, is.Len(containers, 1)) + + i, err := client.ContainerInspect(context.Background(), containers[0].ID) + assert.NilError(t, err) + return i +} + func TestCreateServiceMultipleTimes(t *testing.T) { defer setupTest(t)() d := swarm.NewSwarm(t, testEnv) @@ -23,28 +78,25 @@ func TestCreateServiceMultipleTimes(t *testing.T) { client := d.NewClientT(t) defer client.Close() - overlayName := "overlay1" - networkCreate := types.NetworkCreate{ - CheckDuplicate: true, - Driver: "overlay", - } - - netResp, err := client.NetworkCreate(context.Background(), overlayName, networkCreate) - assert.NilError(t, err) - overlayID := netResp.ID + overlayName := "overlay1_" + t.Name() + overlayID := network.CreateNoError(t, context.Background(), client, overlayName, + network.WithCheckDuplicate(), + network.WithDriver("overlay"), + ) var instances uint64 = 4 + serviceName := "TestService_" + t.Name() serviceSpec := []swarm.ServiceSpecOpt{ swarm.ServiceWithReplicas(instances), - swarm.ServiceWithName("TestService"), + swarm.ServiceWithName(serviceName), swarm.ServiceWithNetwork(overlayName), } serviceID := swarm.CreateService(t, d, serviceSpec...) poll.WaitOn(t, serviceRunningTasksCount(client, serviceID, instances), swarm.ServicePoll) - _, _, err = client.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{}) + _, _, err := client.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{}) assert.NilError(t, err) err = client.ServiceRemove(context.Background(), serviceID) @@ -75,29 +127,26 @@ func TestCreateWithDuplicateNetworkNames(t *testing.T) { client := d.NewClientT(t) defer client.Close() - name := "foo" - networkCreate := types.NetworkCreate{ - CheckDuplicate: false, - Driver: "bridge", - } - - n1, err := client.NetworkCreate(context.Background(), name, networkCreate) - assert.NilError(t, err) - - n2, err := client.NetworkCreate(context.Background(), name, networkCreate) - assert.NilError(t, err) + name := "foo_" + t.Name() + n1 := network.CreateNoError(t, context.Background(), client, name, + network.WithDriver("bridge"), + ) + n2 := network.CreateNoError(t, context.Background(), client, name, + network.WithDriver("bridge"), + ) // Dupliates with name but with different driver - networkCreate.Driver = "overlay" - n3, err := client.NetworkCreate(context.Background(), name, networkCreate) - assert.NilError(t, err) + n3 := network.CreateNoError(t, context.Background(), client, name, + network.WithDriver("overlay"), + ) // Create Service with the same name var instances uint64 = 1 + serviceName := "top_" + t.Name() serviceID := swarm.CreateService(t, d, swarm.ServiceWithReplicas(instances), - swarm.ServiceWithName("top"), + swarm.ServiceWithName(serviceName), swarm.ServiceWithNetwork(name), ) @@ -105,7 +154,7 @@ func TestCreateWithDuplicateNetworkNames(t *testing.T) { resp, _, err := client.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{}) assert.NilError(t, err) - assert.Check(t, is.Equal(n3.ID, resp.Spec.TaskTemplate.Networks[0].Target)) + assert.Check(t, is.Equal(n3, resp.Spec.TaskTemplate.Networks[0].Target)) // Remove Service err = client.ServiceRemove(context.Background(), serviceID) @@ -115,19 +164,19 @@ func TestCreateWithDuplicateNetworkNames(t *testing.T) { poll.WaitOn(t, serviceIsRemoved(client, serviceID), swarm.ServicePoll) // Remove networks - err = client.NetworkRemove(context.Background(), n3.ID) + err = client.NetworkRemove(context.Background(), n3) assert.NilError(t, err) - err = client.NetworkRemove(context.Background(), n2.ID) + err = client.NetworkRemove(context.Background(), n2) assert.NilError(t, err) - err = client.NetworkRemove(context.Background(), n1.ID) + err = client.NetworkRemove(context.Background(), n1) assert.NilError(t, err) // Make sure networks have been destroyed. - poll.WaitOn(t, networkIsRemoved(client, n3.ID), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second)) - poll.WaitOn(t, networkIsRemoved(client, n2.ID), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second)) - poll.WaitOn(t, networkIsRemoved(client, n1.ID), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second)) + poll.WaitOn(t, networkIsRemoved(client, n3), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second)) + poll.WaitOn(t, networkIsRemoved(client, n2), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second)) + poll.WaitOn(t, networkIsRemoved(client, n1), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second)) } func TestCreateServiceSecretFileMode(t *testing.T) { @@ -138,18 +187,20 @@ func TestCreateServiceSecretFileMode(t *testing.T) { defer client.Close() ctx := context.Background() + secretName := "TestSecret_" + t.Name() secretResp, err := client.SecretCreate(ctx, swarmtypes.SecretSpec{ Annotations: swarmtypes.Annotations{ - Name: "TestSecret", + Name: secretName, }, Data: []byte("TESTSECRET"), }) assert.NilError(t, err) var instances uint64 = 1 + serviceName := "TestService_" + t.Name() serviceID := swarm.CreateService(t, d, swarm.ServiceWithReplicas(instances), - swarm.ServiceWithName("TestService"), + swarm.ServiceWithName(serviceName), swarm.ServiceWithCommand([]string{"/bin/sh", "-c", "ls -l /etc/secret || /bin/top"}), swarm.ServiceWithSecret(&swarmtypes.SecretReference{ File: &swarmtypes.SecretReferenceFileTarget{ @@ -159,7 +210,7 @@ func TestCreateServiceSecretFileMode(t *testing.T) { Mode: 0777, }, SecretID: secretResp.ID, - SecretName: "TestSecret", + SecretName: secretName, }), ) @@ -189,7 +240,7 @@ func TestCreateServiceSecretFileMode(t *testing.T) { poll.WaitOn(t, serviceIsRemoved(client, serviceID), swarm.ServicePoll) poll.WaitOn(t, noTasks(client), swarm.ServicePoll) - err = client.SecretRemove(ctx, "TestSecret") + err = client.SecretRemove(ctx, secretName) assert.NilError(t, err) } @@ -201,17 +252,19 @@ func TestCreateServiceConfigFileMode(t *testing.T) { defer client.Close() ctx := context.Background() + configName := "TestConfig_" + t.Name() configResp, err := client.ConfigCreate(ctx, swarmtypes.ConfigSpec{ Annotations: swarmtypes.Annotations{ - Name: "TestConfig", + Name: configName, }, Data: []byte("TESTCONFIG"), }) assert.NilError(t, err) var instances uint64 = 1 + serviceName := "TestService_" + t.Name() serviceID := swarm.CreateService(t, d, - swarm.ServiceWithName("TestService"), + swarm.ServiceWithName(serviceName), swarm.ServiceWithCommand([]string{"/bin/sh", "-c", "ls -l /etc/config || /bin/top"}), swarm.ServiceWithReplicas(instances), swarm.ServiceWithConfig(&swarmtypes.ConfigReference{ @@ -222,7 +275,7 @@ func TestCreateServiceConfigFileMode(t *testing.T) { Mode: 0777, }, ConfigID: configResp.ID, - ConfigName: "TestConfig", + ConfigName: configName, }), ) @@ -252,7 +305,7 @@ func TestCreateServiceConfigFileMode(t *testing.T) { poll.WaitOn(t, serviceIsRemoved(client, serviceID)) poll.WaitOn(t, noTasks(client)) - err = client.ConfigRemove(ctx, "TestConfig") + err = client.ConfigRemove(ctx, configName) assert.NilError(t, err) } diff --git a/integration/service/inspect_test.go b/integration/service/inspect_test.go index 7492a4fc021d6..daeabcfe1294c 100644 --- a/integration/service/inspect_test.go +++ b/integration/service/inspect_test.go @@ -12,10 +12,10 @@ import ( "github.com/docker/docker/client" "github.com/docker/docker/integration/internal/swarm" "github.com/google/go-cmp/cmp" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/poll" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/poll" + "gotest.tools/skip" ) func TestInspect(t *testing.T) { @@ -54,7 +54,7 @@ func TestInspect(t *testing.T) { assert.Check(t, is.DeepEqual(service, expected, cmpServiceOpts())) } -// TODO: use helpers from gotestyourself/assert/opt when available +// TODO: use helpers from gotest.tools/assert/opt when available func cmpServiceOpts() cmp.Option { const threshold = 20 * time.Second diff --git a/integration/service/network_test.go b/integration/service/network_test.go index d69aeb91f6cff..ba170fce0ecd9 100644 --- a/integration/service/network_test.go +++ b/integration/service/network_test.go @@ -7,9 +7,10 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/network" "github.com/docker/docker/integration/internal/container" + net "github.com/docker/docker/integration/internal/network" "github.com/docker/docker/integration/internal/swarm" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestDockerNetworkConnectAlias(t *testing.T) { @@ -21,11 +22,10 @@ func TestDockerNetworkConnectAlias(t *testing.T) { ctx := context.Background() name := t.Name() + "test-alias" - _, err := client.NetworkCreate(ctx, name, types.NetworkCreate{ - Driver: "overlay", - Attachable: true, - }) - assert.NilError(t, err) + net.CreateNoError(t, ctx, client, name, + net.WithDriver("overlay"), + net.WithAttachable(), + ) cID1 := container.Create(t, ctx, client, func(c *container.TestContainerConfig) { c.NetworkingConfig = &network.NetworkingConfig{ @@ -35,7 +35,7 @@ func TestDockerNetworkConnectAlias(t *testing.T) { } }) - err = client.NetworkConnect(ctx, name, cID1, &network.EndpointSettings{ + err := client.NetworkConnect(ctx, name, cID1, &network.EndpointSettings{ Aliases: []string{ "aaa", }, diff --git a/integration/service/plugin_test.go b/integration/service/plugin_test.go index 29a0398323166..e476c2f1f4707 100644 --- a/integration/service/plugin_test.go +++ b/integration/service/plugin_test.go @@ -15,9 +15,9 @@ import ( "github.com/docker/docker/internal/test/daemon" "github.com/docker/docker/internal/test/fixtures/plugin" "github.com/docker/docker/internal/test/registry" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/poll" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + "gotest.tools/poll" + "gotest.tools/skip" ) func TestServicePlugin(t *testing.T) { @@ -107,7 +107,7 @@ func TestServicePlugin(t *testing.T) { func makePlugin(repo, name string, constraints []string) func(*swarmtypes.Service) { return func(s *swarmtypes.Service) { - s.Spec.TaskTemplate.Runtime = "plugin" + s.Spec.TaskTemplate.Runtime = swarmtypes.RuntimePlugin s.Spec.TaskTemplate.PluginSpec = &runtime.PluginSpec{ Name: name, Remote: repo, diff --git a/integration/session/session_test.go b/integration/session/session_test.go index 893682fabaaa3..67a3773abdb96 100644 --- a/integration/session/session_test.go +++ b/integration/session/session_test.go @@ -5,9 +5,9 @@ import ( "testing" req "github.com/docker/docker/internal/test/request" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/skip" ) func TestSessionCreate(t *testing.T) { diff --git a/integration/system/cgroupdriver_systemd_test.go b/integration/system/cgroupdriver_systemd_test.go index 0b4e7eeeeb1cb..449c83fdab6e7 100644 --- a/integration/system/cgroupdriver_systemd_test.go +++ b/integration/system/cgroupdriver_systemd_test.go @@ -9,7 +9,7 @@ import ( "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/daemon" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) // hasSystemd checks whether the host was booted with systemd as its init diff --git a/integration/system/event_test.go b/integration/system/event_test.go index b1966fe6cef05..6e86f4ad95654 100644 --- a/integration/system/event_test.go +++ b/integration/system/event_test.go @@ -18,9 +18,9 @@ import ( "github.com/docker/docker/internal/test/request" req "github.com/docker/docker/internal/test/request" "github.com/docker/docker/pkg/jsonmessage" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/skip" ) func TestEventsExecDie(t *testing.T) { diff --git a/integration/system/info_linux_test.go b/integration/system/info_linux_test.go index 15bd69fdf6117..50fa9874b4f93 100644 --- a/integration/system/info_linux_test.go +++ b/integration/system/info_linux_test.go @@ -9,8 +9,8 @@ import ( "github.com/docker/docker/internal/test/request" req "github.com/docker/docker/internal/test/request" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestInfoBinaryCommits(t *testing.T) { diff --git a/integration/system/info_test.go b/integration/system/info_test.go index 0906eea336325..2a05dfbb74993 100644 --- a/integration/system/info_test.go +++ b/integration/system/info_test.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/docker/docker/internal/test/request" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestInfoAPI(t *testing.T) { diff --git a/integration/system/login_test.go b/integration/system/login_test.go index 5d512ca343205..ad1a8756dc173 100644 --- a/integration/system/login_test.go +++ b/integration/system/login_test.go @@ -7,14 +7,14 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/integration/internal/requirement" "github.com/docker/docker/internal/test/request" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/skip" ) // Test case for GitHub 22244 func TestLoginFailsWithBadCredentials(t *testing.T) { - skip.IfCondition(t, !requirement.HasHubConnectivity(t)) + skip.If(t, !requirement.HasHubConnectivity(t)) client := request.NewAPIClient(t) diff --git a/integration/system/version_test.go b/integration/system/version_test.go index 24618053604b4..8904c09b26c6f 100644 --- a/integration/system/version_test.go +++ b/integration/system/version_test.go @@ -5,8 +5,8 @@ import ( "testing" "github.com/docker/docker/internal/test/request" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestVersion(t *testing.T) { diff --git a/integration/volume/volume_test.go b/integration/volume/volume_test.go index 4acc6e8b1ae99..ce42bb3040b87 100644 --- a/integration/volume/volume_test.go +++ b/integration/volume/volume_test.go @@ -13,9 +13,9 @@ import ( "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/internal/test/request" "github.com/google/go-cmp/cmp/cmpopts" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/skip" ) func TestVolumesCreateAndList(t *testing.T) { diff --git a/internal/test/daemon/config.go b/internal/test/daemon/config.go index c57010db90bf4..ce99222b37eac 100644 --- a/internal/test/daemon/config.go +++ b/internal/test/daemon/config.go @@ -6,7 +6,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/internal/test" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) // ConfigConstructor defines a swarm config constructor diff --git a/internal/test/daemon/container.go b/internal/test/daemon/container.go index 6a0ced9447279..3aa69e195cdc9 100644 --- a/internal/test/daemon/container.go +++ b/internal/test/daemon/container.go @@ -5,7 +5,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/internal/test" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) // ActiveContainers returns the list of ids of the currently running containers diff --git a/internal/test/daemon/daemon.go b/internal/test/daemon/daemon.go index 9ba13edc0ae26..98f1ee1b08b0d 100644 --- a/internal/test/daemon/daemon.go +++ b/internal/test/daemon/daemon.go @@ -23,8 +23,8 @@ import ( "github.com/docker/docker/pkg/stringid" "github.com/docker/go-connections/sockets" "github.com/docker/go-connections/tlsconfig" - "github.com/gotestyourself/gotestyourself/assert" "github.com/pkg/errors" + "gotest.tools/assert" ) type testingT interface { @@ -66,6 +66,7 @@ type Daemon struct { userlandProxy bool execRoot string experimental bool + init bool dockerdBinary string log logT @@ -229,7 +230,10 @@ func (d *Daemon) StartWithLogFile(out *os.File, providedArgs ...string) error { fmt.Sprintf("--userland-proxy=%t", d.userlandProxy), ) if d.experimental { - args = append(args, "--experimental", "--init") + args = append(args, "--experimental") + } + if d.init { + args = append(args, "--init") } if !(d.UseDefaultHost || d.UseDefaultTLSHost) { args = append(args, []string{"--host", d.Sock()}...) diff --git a/internal/test/daemon/node.go b/internal/test/daemon/node.go index 5015c75eb1d7e..d9263a7f29dc8 100644 --- a/internal/test/daemon/node.go +++ b/internal/test/daemon/node.go @@ -8,7 +8,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/internal/test" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) // NodeConstructor defines a swarm node constructor diff --git a/internal/test/daemon/ops.go b/internal/test/daemon/ops.go index 288fe8807050e..34db073b57bd1 100644 --- a/internal/test/daemon/ops.go +++ b/internal/test/daemon/ops.go @@ -5,6 +5,12 @@ import "github.com/docker/docker/internal/test/environment" // WithExperimental sets the daemon in experimental mode func WithExperimental(d *Daemon) { d.experimental = true + d.init = true +} + +// WithInit sets the daemon init +func WithInit(d *Daemon) { + d.init = true } // WithDockerdBinary sets the dockerd binary to the specified one diff --git a/internal/test/daemon/plugin.go b/internal/test/daemon/plugin.go index 9a7cc345ea74a..63bbeed219327 100644 --- a/internal/test/daemon/plugin.go +++ b/internal/test/daemon/plugin.go @@ -5,7 +5,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/client" - "github.com/gotestyourself/gotestyourself/poll" + "gotest.tools/poll" ) // PluginIsRunning provides a poller to check if the specified plugin is running diff --git a/internal/test/daemon/secret.go b/internal/test/daemon/secret.go index 615489bfd0a51..f3db7a42601da 100644 --- a/internal/test/daemon/secret.go +++ b/internal/test/daemon/secret.go @@ -6,7 +6,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/internal/test" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) // SecretConstructor defines a swarm secret constructor diff --git a/internal/test/daemon/service.go b/internal/test/daemon/service.go index 77614d0daafc1..0f88ca786b32d 100644 --- a/internal/test/daemon/service.go +++ b/internal/test/daemon/service.go @@ -8,7 +8,7 @@ import ( "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/internal/test" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) // ServiceConstructor defines a swarm service constructor function diff --git a/internal/test/daemon/swarm.go b/internal/test/daemon/swarm.go index 3e803eeeb87a2..e8e8b945dee3d 100644 --- a/internal/test/daemon/swarm.go +++ b/internal/test/daemon/swarm.go @@ -6,8 +6,8 @@ import ( "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/internal/test" - "github.com/gotestyourself/gotestyourself/assert" "github.com/pkg/errors" + "gotest.tools/assert" ) const ( diff --git a/internal/test/environment/clean.go b/internal/test/environment/clean.go index e92006fc46cf0..93dee593f2817 100644 --- a/internal/test/environment/clean.go +++ b/internal/test/environment/clean.go @@ -9,7 +9,7 @@ import ( "github.com/docker/docker/api/types/filters" "github.com/docker/docker/client" "github.com/docker/docker/internal/test" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) type testingT interface { diff --git a/internal/test/environment/protect.go b/internal/test/environment/protect.go index 6d57dedb1ccde..b5b27d2dd4f4c 100644 --- a/internal/test/environment/protect.go +++ b/internal/test/environment/protect.go @@ -7,7 +7,7 @@ import ( "github.com/docker/docker/api/types/filters" dclient "github.com/docker/docker/client" "github.com/docker/docker/internal/test" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) var frozenImages = []string{"busybox:latest", "busybox:glibc", "hello-world:frozen", "debian:jessie"} diff --git a/internal/test/fakegit/fakegit.go b/internal/test/fakegit/fakegit.go index 59f4bcb056017..605d1baaa8337 100644 --- a/internal/test/fakegit/fakegit.go +++ b/internal/test/fakegit/fakegit.go @@ -12,7 +12,7 @@ import ( "github.com/docker/docker/internal/test" "github.com/docker/docker/internal/test/fakecontext" "github.com/docker/docker/internal/test/fakestorage" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) type testingT interface { diff --git a/internal/test/fakestorage/fixtures.go b/internal/test/fakestorage/fixtures.go index a694834f7d895..ad8f76314338a 100644 --- a/internal/test/fakestorage/fixtures.go +++ b/internal/test/fakestorage/fixtures.go @@ -12,7 +12,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/internal/test" "github.com/docker/docker/pkg/archive" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) var ensureHTTPServerOnce sync.Once diff --git a/internal/test/fakestorage/storage.go b/internal/test/fakestorage/storage.go index adce3512c12f8..b091cbc3f1c85 100644 --- a/internal/test/fakestorage/storage.go +++ b/internal/test/fakestorage/storage.go @@ -20,7 +20,7 @@ import ( "github.com/docker/docker/internal/test/request" "github.com/docker/docker/internal/testutil" "github.com/docker/go-connections/nat" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) var testEnv *environment.Execution diff --git a/internal/test/registry/registry.go b/internal/test/registry/registry.go index 2e89c32e57d6d..b6128d3ba4d3c 100644 --- a/internal/test/registry/registry.go +++ b/internal/test/registry/registry.go @@ -10,8 +10,8 @@ import ( "time" "github.com/docker/docker/internal/test" - "github.com/gotestyourself/gotestyourself/assert" "github.com/opencontainers/go-digest" + "gotest.tools/assert" ) const ( diff --git a/internal/test/request/request.go b/internal/test/request/request.go index 00450d94a2229..1986d370f152f 100644 --- a/internal/test/request/request.go +++ b/internal/test/request/request.go @@ -20,8 +20,8 @@ import ( "github.com/docker/docker/pkg/ioutils" "github.com/docker/go-connections/sockets" "github.com/docker/go-connections/tlsconfig" - "github.com/gotestyourself/gotestyourself/assert" "github.com/pkg/errors" + "gotest.tools/assert" ) // NewAPIClient returns a docker API client configured from environment variables diff --git a/internal/testutil/stringutils_test.go b/internal/testutil/stringutils_test.go index 1dd09af95f359..753aac966dc2e 100644 --- a/internal/testutil/stringutils_test.go +++ b/internal/testutil/stringutils_test.go @@ -3,8 +3,8 @@ package testutil // import "github.com/docker/docker/internal/testutil" import ( "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func testLengthHelper(generator func(int) string, t *testing.T) { diff --git a/layer/filestore.go b/layer/filestore.go index b1cbb80166376..208a0c3a859a4 100644 --- a/layer/filestore.go +++ b/layer/filestore.go @@ -3,7 +3,6 @@ package layer // import "github.com/docker/docker/layer" import ( "compress/gzip" "encoding/json" - "errors" "fmt" "io" "io/ioutil" @@ -16,6 +15,7 @@ import ( "github.com/docker/distribution" "github.com/docker/docker/pkg/ioutils" "github.com/opencontainers/go-digest" + "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -194,8 +194,8 @@ func (fms *fileMetadataStore) GetCacheID(layer ChainID) (string, error) { } content := strings.TrimSpace(string(contentBytes)) - if !stringIDRegexp.MatchString(content) { - return "", errors.New("invalid cache id value") + if content == "" { + return "", errors.Errorf("invalid cache id value") } return content, nil diff --git a/layer/layer_store.go b/layer/layer_store.go index bf0705afc5c16..c1fbf85091730 100644 --- a/layer/layer_store.go +++ b/layer/layer_store.go @@ -121,6 +121,10 @@ func newStoreFromGraphDriver(root string, driver graphdriver.Driver, os string) return ls, nil } +func (ls *layerStore) Driver() graphdriver.Driver { + return ls.driver +} + func (ls *layerStore) loadLayer(layer ChainID) (*roLayer, error) { cl, ok := ls.layerMap[layer] if ok { diff --git a/layer/ro_layer.go b/layer/ro_layer.go index bc0fe1dddf33f..3555e8b027994 100644 --- a/layer/ro_layer.go +++ b/layer/ro_layer.go @@ -54,6 +54,10 @@ func (rl *roLayer) TarStreamFrom(parent ChainID) (io.ReadCloser, error) { return rl.layerStore.driver.Diff(rl.cacheID, parentCacheID) } +func (rl *roLayer) CacheID() string { + return rl.cacheID +} + func (rl *roLayer) ChainID() ChainID { return rl.chainID } diff --git a/libcontainerd/client_daemon.go b/libcontainerd/client_daemon.go index a59dfbcd5d28c..0706fa4daaa4e 100644 --- a/libcontainerd/client_daemon.go +++ b/libcontainerd/client_daemon.go @@ -16,9 +16,6 @@ import ( "syscall" "time" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "github.com/containerd/containerd" apievents "github.com/containerd/containerd/api/events" "github.com/containerd/containerd/api/types" @@ -28,14 +25,16 @@ import ( containerderrors "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/events" "github.com/containerd/containerd/images" - "github.com/containerd/containerd/linux/runctypes" + "github.com/containerd/containerd/runtime/linux/runctypes" "github.com/containerd/typeurl" "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/ioutils" "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/opencontainers/runtime-spec/specs-go" + specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) // InitProcessName is the name given to the first process of a @@ -295,7 +294,7 @@ func (c *client) Start(ctx context.Context, id, checkpointDir string, withStdin func(id string) (cio.IO, error) { fifos := newFIFOSet(ctr.bundleDir, InitProcessName, withStdin, spec.Process.Terminal) - rio, err = c.createIO(fifos, id, InitProcessName, stdinCloseSync, attachStdio, spec.Process.Terminal) + rio, err = c.createIO(fifos, id, InitProcessName, stdinCloseSync, attachStdio) return rio, err }, func(_ context.Context, _ *containerd.Client, info *containerd.TaskInfo) error { @@ -366,7 +365,7 @@ func (c *client) Exec(ctx context.Context, containerID, processID string, spec * }() p, err = t.Exec(ctx, processID, spec, func(id string) (cio.IO, error) { - rio, err = c.createIO(fifos, containerID, processID, stdinCloseSync, attachStdio, spec.Terminal) + rio, err = c.createIO(fifos, containerID, processID, stdinCloseSync, attachStdio) return rio, err }) if err != nil { @@ -575,7 +574,7 @@ func (c *client) CreateCheckpoint(ctx context.Context, containerID, checkpointDi } }() - b, err := content.ReadBlob(ctx, c.getRemote().ContentStore(), img.Target().Digest) + b, err := content.ReadBlob(ctx, c.getRemote().ContentStore(), img.Target()) if err != nil { return errdefs.System(errors.Wrapf(err, "failed to retrieve checkpoint data")) } @@ -595,7 +594,7 @@ func (c *client) CreateCheckpoint(ctx context.Context, containerID, checkpointDi return errdefs.System(errors.Wrapf(err, "invalid checkpoint")) } - rat, err := c.getRemote().ContentStore().ReaderAt(ctx, cpDesc.Digest) + rat, err := c.getRemote().ContentStore().ReaderAt(ctx, *cpDesc) if err != nil { return errdefs.System(errors.Wrapf(err, "failed to get checkpoint reader")) } @@ -645,16 +644,13 @@ func (c *client) getProcess(containerID, processID string) (containerd.Process, // createIO creates the io to be used by a process // This needs to get a pointer to interface as upon closure the process may not have yet been registered -func (c *client) createIO(fifos *cio.FIFOSet, containerID, processID string, stdinCloseSync chan struct{}, attachStdio StdioCallback, terminal bool) (cio.IO, error) { +func (c *client) createIO(fifos *cio.FIFOSet, containerID, processID string, stdinCloseSync chan struct{}, attachStdio StdioCallback) (cio.IO, error) { var ( io *cio.DirectIO err error ) - if terminal { - io, err = cio.NewDirectIOWithTerminal(context.Background(), fifos) - } else { - io, err = cio.NewDirectIO(context.Background(), fifos) - } + + io, err = cio.NewDirectIO(context.Background(), fifos) if err != nil { return nil, err } @@ -858,7 +854,7 @@ func (c *client) processEventStream(ctx context.Context) { } func (c *client) writeContent(ctx context.Context, mediaType, ref string, r io.Reader) (*types.Descriptor, error) { - writer, err := c.getRemote().ContentStore().Writer(ctx, ref, 0, "") + writer, err := c.getRemote().ContentStore().Writer(ctx, content.WithRef(ref)) if err != nil { return nil, err } diff --git a/libcontainerd/client_local_windows.go b/libcontainerd/client_local_windows.go index 6e3454e514525..0236fecaac972 100644 --- a/libcontainerd/client_local_windows.go +++ b/libcontainerd/client_local_windows.go @@ -305,6 +305,13 @@ func (c *client) createWindows(id string, spec *specs.Spec, runtimeOptions inter } configuration.MappedPipes = mps + // Add any device assignments + if len(spec.Windows.Devices) > 0 { + for _, d := range spec.Windows.Devices { + configuration.AssignedDevices = append(configuration.AssignedDevices, hcsshim.AssignedDevice{InterfaceClassGUID: d.ID}) + } + } + hcsContainer, err := hcsshim.CreateContainer(id, configuration) if err != nil { return err @@ -494,6 +501,10 @@ func (c *client) createLinux(id string, spec *specs.Spec, runtimeOptions interfa CreateInUtilityVM: true, ReadOnly: readonly, } + // If we are 1803/RS4+ enable LinuxMetadata support by default + if system.GetOSVersion().Build >= 17134 { + md.LinuxMetadata = true + } mds = append(mds, md) specMount.Source = path.Join(uvmPath, mount.Destination) } diff --git a/libcontainerd/queue_test.go b/libcontainerd/queue_test.go index df5332c128b34..e13afca89a25a 100644 --- a/libcontainerd/queue_test.go +++ b/libcontainerd/queue_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) func TestSerialization(t *testing.T) { diff --git a/libcontainerd/remote_daemon.go b/libcontainerd/remote_daemon.go index cd2ac1ce4d4c5..07c0ea6bc6daf 100644 --- a/libcontainerd/remote_daemon.go +++ b/libcontainerd/remote_daemon.go @@ -221,6 +221,11 @@ func (r *remote) startContainerd() error { } args := []string{"--config", configFile} + + if r.Debug.Level != "" { + args = append(args, "--log-level", r.Debug.Level) + } + cmd := exec.Command(binaryName, args...) // redirect containerd logs to docker logs cmd.Stdout = os.Stdout diff --git a/libcontainerd/remote_daemon_linux.go b/libcontainerd/remote_daemon_linux.go index dc59eb8c14f0e..5a46afb89da13 100644 --- a/libcontainerd/remote_daemon_linux.go +++ b/libcontainerd/remote_daemon_linux.go @@ -28,9 +28,6 @@ func (r *remote) setDefaults() { if r.Debug.Address == "" { r.Debug.Address = filepath.Join(r.stateDir, debugSockFile) } - if r.Debug.Level == "" { - r.Debug.Level = "info" - } if r.OOMScore == 0 { r.OOMScore = -999 } diff --git a/libcontainerd/remote_daemon_windows.go b/libcontainerd/remote_daemon_windows.go index 89342d7395820..69cd1ac30c134 100644 --- a/libcontainerd/remote_daemon_windows.go +++ b/libcontainerd/remote_daemon_windows.go @@ -18,9 +18,6 @@ func (r *remote) setDefaults() { if r.Debug.Address == "" { r.Debug.Address = debugPipeName } - if r.Debug.Level == "" { - r.Debug.Level = "info" - } if r.snapshotter == "" { r.snapshotter = "naive" // TODO(mlaventure): switch to "windows" once implemented } diff --git a/oci/defaults.go b/oci/defaults.go index 4145412dd493b..992157b0f5771 100644 --- a/oci/defaults.go +++ b/oci/defaults.go @@ -114,6 +114,7 @@ func DefaultLinuxSpec() specs.Spec { s.Linux = &specs.Linux{ MaskedPaths: []string{ + "/proc/acpi", "/proc/kcore", "/proc/keys", "/proc/latency_stats", diff --git a/opts/quotedstring_test.go b/opts/quotedstring_test.go index 21e6e4c85ae6e..89fed6cfa6ac3 100644 --- a/opts/quotedstring_test.go +++ b/opts/quotedstring_test.go @@ -3,8 +3,8 @@ package opts // import "github.com/docker/docker/opts" import ( "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestQuotedStringSetWithQuotes(t *testing.T) { diff --git a/pkg/archive/archive.go b/pkg/archive/archive.go index 5fb3995e9b036..86922ddcac0d9 100644 --- a/pkg/archive/archive.go +++ b/pkg/archive/archive.go @@ -127,6 +127,7 @@ func IsArchivePath(path string) bool { if err != nil { return false } + defer rdr.Close() r := tar.NewReader(rdr) _, err = r.Next() return err == nil @@ -366,11 +367,7 @@ func FileInfoHeader(name string, fi os.FileInfo, link string) (*tar.Header, erro hdr.AccessTime = time.Time{} hdr.ChangeTime = time.Time{} hdr.Mode = fillGo18FileTypeBits(int64(chmodTarEntry(os.FileMode(hdr.Mode))), fi) - name, err = canonicalTarName(name, fi.IsDir()) - if err != nil { - return nil, fmt.Errorf("tar: cannot canonicalize path: %v", err) - } - hdr.Name = name + hdr.Name = canonicalTarName(name, fi.IsDir()) if err := setHeaderForSpecialDevice(hdr, name, fi.Sys()); err != nil { return nil, err } @@ -446,17 +443,14 @@ func newTarAppender(idMapping *idtools.IDMappings, writer io.Writer, chownOpts * // canonicalTarName provides a platform-independent and consistent posix-style //path for files and directories to be archived regardless of the platform. -func canonicalTarName(name string, isDir bool) (string, error) { - name, err := CanonicalTarNameForPath(name) - if err != nil { - return "", err - } +func canonicalTarName(name string, isDir bool) string { + name = CanonicalTarNameForPath(name) // suffix with '/' for directories if isDir && !strings.HasSuffix(name, "/") { name += "/" } - return name, nil + return name } // addTarFile adds to the tar archive a file from `path` as `name` diff --git a/pkg/archive/archive_linux_test.go b/pkg/archive/archive_linux_test.go index b06cabecb330a..9422269dffe71 100644 --- a/pkg/archive/archive_linux_test.go +++ b/pkg/archive/archive_linux_test.go @@ -8,9 +8,9 @@ import ( "testing" "github.com/docker/docker/pkg/system" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/skip" "golang.org/x/sys/unix" + "gotest.tools/assert" + "gotest.tools/skip" ) // setupOverlayTestDir creates files in a directory with overlay whiteouts diff --git a/pkg/archive/archive_test.go b/pkg/archive/archive_test.go index d801f2724a17f..9f06af9969c95 100644 --- a/pkg/archive/archive_test.go +++ b/pkg/archive/archive_test.go @@ -18,9 +18,9 @@ import ( "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/ioutils" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/skip" ) var tmp string diff --git a/pkg/archive/archive_unix.go b/pkg/archive/archive_unix.go index e81076c170105..fda3f50c6a786 100644 --- a/pkg/archive/archive_unix.go +++ b/pkg/archive/archive_unix.go @@ -32,8 +32,8 @@ func getWalkRoot(srcPath string, include string) string { // CanonicalTarNameForPath returns platform-specific filepath // to canonical posix-style path for tar archival. p is relative // path. -func CanonicalTarNameForPath(p string) (string, error) { - return p, nil // already unix-style +func CanonicalTarNameForPath(p string) string { + return p // already unix-style } // chmodTarEntry is used to adjust the file permissions used in tar header based diff --git a/pkg/archive/archive_unix_test.go b/pkg/archive/archive_unix_test.go index 034490b5731fa..83deab0840725 100644 --- a/pkg/archive/archive_unix_test.go +++ b/pkg/archive/archive_unix_test.go @@ -13,10 +13,10 @@ import ( "testing" "github.com/docker/docker/pkg/system" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/skip" "golang.org/x/sys/unix" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/skip" ) func TestCanonicalTarNameForPath(t *testing.T) { @@ -26,10 +26,8 @@ func TestCanonicalTarNameForPath(t *testing.T) { {"foo/dir/", "foo/dir/"}, } for _, v := range cases { - if out, err := CanonicalTarNameForPath(v.in); err != nil { - t.Fatalf("cannot get canonical name for path: %s: %v", v.in, err) - } else if out != v.expected { - t.Fatalf("wrong canonical tar name. expected:%s got:%s", v.expected, out) + if CanonicalTarNameForPath(v.in) != v.expected { + t.Fatalf("wrong canonical tar name. expected:%s got:%s", v.expected, CanonicalTarNameForPath(v.in)) } } } @@ -46,10 +44,8 @@ func TestCanonicalTarName(t *testing.T) { {"foo/bar", true, "foo/bar/"}, } for _, v := range cases { - if out, err := canonicalTarName(v.in, v.isDir); err != nil { - t.Fatalf("cannot get canonical name for path: %s: %v", v.in, err) - } else if out != v.expected { - t.Fatalf("wrong canonical tar name. expected:%s got:%s", v.expected, out) + if canonicalTarName(v.in, v.isDir) != v.expected { + t.Fatalf("wrong canonical tar name. expected:%s got:%s", v.expected, canonicalTarName(v.in, v.isDir)) } } } diff --git a/pkg/archive/archive_windows.go b/pkg/archive/archive_windows.go index 69aadd823c8aa..3160c5a8fdb27 100644 --- a/pkg/archive/archive_windows.go +++ b/pkg/archive/archive_windows.go @@ -2,10 +2,8 @@ package archive // import "github.com/docker/docker/pkg/archive" import ( "archive/tar" - "fmt" "os" "path/filepath" - "strings" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/longpath" @@ -26,16 +24,8 @@ func getWalkRoot(srcPath string, include string) string { // CanonicalTarNameForPath returns platform-specific filepath // to canonical posix-style path for tar archival. p is relative // path. -func CanonicalTarNameForPath(p string) (string, error) { - // windows: convert windows style relative path with backslashes - // into forward slashes. Since windows does not allow '/' or '\' - // in file names, it is mostly safe to replace however we must - // check just in case - if strings.Contains(p, "/") { - return "", fmt.Errorf("Windows path contains forward slash: %s", p) - } - return strings.Replace(p, string(os.PathSeparator), "/", -1), nil - +func CanonicalTarNameForPath(p string) string { + return filepath.ToSlash(p) } // chmodTarEntry is used to adjust the file permissions used in tar header based diff --git a/pkg/archive/archive_windows_test.go b/pkg/archive/archive_windows_test.go index b3dbb32754f88..6f0e25ccac534 100644 --- a/pkg/archive/archive_windows_test.go +++ b/pkg/archive/archive_windows_test.go @@ -36,19 +36,14 @@ func TestCopyFileWithInvalidDest(t *testing.T) { func TestCanonicalTarNameForPath(t *testing.T) { cases := []struct { in, expected string - shouldFail bool }{ - {"foo", "foo", false}, - {"foo/bar", "___", true}, // unix-styled windows path must fail - {`foo\bar`, "foo/bar", false}, + {"foo", "foo"}, + {"foo/bar", "foo/bar"}, + {`foo\bar`, "foo/bar"}, } for _, v := range cases { - if out, err := CanonicalTarNameForPath(v.in); err != nil && !v.shouldFail { - t.Fatalf("cannot get canonical name for path: %s: %v", v.in, err) - } else if v.shouldFail && err == nil { - t.Fatalf("canonical path call should have failed with error. in=%s out=%s", v.in, out) - } else if !v.shouldFail && out != v.expected { - t.Fatalf("wrong canonical tar name. expected:%s got:%s", v.expected, out) + if CanonicalTarNameForPath(v.in) != v.expected { + t.Fatalf("wrong canonical tar name. expected:%s got:%s", v.expected, CanonicalTarNameForPath(v.in)) } } } @@ -65,10 +60,8 @@ func TestCanonicalTarName(t *testing.T) { {`foo\bar`, true, "foo/bar/"}, } for _, v := range cases { - if out, err := canonicalTarName(v.in, v.isDir); err != nil { - t.Fatalf("cannot get canonical name for path: %s: %v", v.in, err) - } else if out != v.expected { - t.Fatalf("wrong canonical tar name. expected:%s got:%s", v.expected, out) + if canonicalTarName(v.in, v.isDir) != v.expected { + t.Fatalf("wrong canonical tar name. expected:%s got:%s", v.expected, canonicalTarName(v.in, v.isDir)) } } } diff --git a/pkg/archive/changes_linux.go b/pkg/archive/changes_linux.go index 78a5393c8ea54..f8792b3d4e5e3 100644 --- a/pkg/archive/changes_linux.go +++ b/pkg/archive/changes_linux.go @@ -284,30 +284,3 @@ func clen(n []byte) int { } return len(n) } - -// OverlayChanges walks the path rw and determines changes for the files in the path, -// with respect to the parent layers -func OverlayChanges(layers []string, rw string) ([]Change, error) { - return changes(layers, rw, overlayDeletedFile, nil) -} - -func overlayDeletedFile(root, path string, fi os.FileInfo) (string, error) { - if fi.Mode()&os.ModeCharDevice != 0 { - s := fi.Sys().(*syscall.Stat_t) - if unix.Major(uint64(s.Rdev)) == 0 && unix.Minor(uint64(s.Rdev)) == 0 { // nolint: unconvert - return path, nil - } - } - if fi.Mode()&os.ModeDir != 0 { - opaque, err := system.Lgetxattr(filepath.Join(root, path), "trusted.overlay.opaque") - if err != nil { - return "", err - } - if len(opaque) == 1 && opaque[0] == 'y' { - return path, nil - } - } - - return "", nil - -} diff --git a/pkg/archive/changes_test.go b/pkg/archive/changes_test.go index 83ad5be605ef3..f2527cd93650f 100644 --- a/pkg/archive/changes_test.go +++ b/pkg/archive/changes_test.go @@ -11,8 +11,8 @@ import ( "time" "github.com/docker/docker/pkg/system" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + "gotest.tools/skip" ) func max(x, y int) int { diff --git a/pkg/archive/copy_unix_test.go b/pkg/archive/copy_unix_test.go index 08b1702cf1c25..739ad0e3eff4e 100644 --- a/pkg/archive/copy_unix_test.go +++ b/pkg/archive/copy_unix_test.go @@ -16,7 +16,7 @@ import ( "strings" "testing" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) func removeAllPaths(paths ...string) { diff --git a/pkg/archive/diff.go b/pkg/archive/diff.go index d0cff98ffc245..fae4b9de02c8b 100644 --- a/pkg/archive/diff.go +++ b/pkg/archive/diff.go @@ -247,10 +247,12 @@ func applyLayerHandler(dest string, layer io.Reader, options *TarOptions, decomp defer system.Umask(oldmask) // ignore err, ErrNotSupportedPlatform if decompress { - layer, err = DecompressStream(layer) + decompLayer, err := DecompressStream(layer) if err != nil { return 0, err } + defer decompLayer.Close() + layer = decompLayer } return UnpackLayer(dest, layer, options) } diff --git a/pkg/archive/wrap_test.go b/pkg/archive/wrap_test.go index 979536777ff35..1faa7aed75393 100644 --- a/pkg/archive/wrap_test.go +++ b/pkg/archive/wrap_test.go @@ -6,7 +6,7 @@ import ( "io" "testing" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) func TestGenerateEmptyFile(t *testing.T) { diff --git a/pkg/authorization/api_test.go b/pkg/authorization/api_test.go index 84964d2c5e866..ff364fd0bcfd0 100644 --- a/pkg/authorization/api_test.go +++ b/pkg/authorization/api_test.go @@ -11,8 +11,8 @@ import ( "testing" "time" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestPeerCertificateMarshalJSON(t *testing.T) { diff --git a/pkg/authorization/middleware_test.go b/pkg/authorization/middleware_test.go index e32e4bf4279d7..6afafe082d3ba 100644 --- a/pkg/authorization/middleware_test.go +++ b/pkg/authorization/middleware_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/docker/docker/pkg/plugingetter" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) func TestMiddleware(t *testing.T) { diff --git a/pkg/authorization/middleware_unix_test.go b/pkg/authorization/middleware_unix_test.go index 6d1a4284602b9..450e7fbbb7375 100644 --- a/pkg/authorization/middleware_unix_test.go +++ b/pkg/authorization/middleware_unix_test.go @@ -9,8 +9,8 @@ import ( "testing" "github.com/docker/docker/pkg/plugingetter" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestMiddlewareWrapHandler(t *testing.T) { diff --git a/pkg/chrootarchive/archive_test.go b/pkg/chrootarchive/archive_test.go index 3851d7224e5b8..5911a36158bf6 100644 --- a/pkg/chrootarchive/archive_test.go +++ b/pkg/chrootarchive/archive_test.go @@ -16,7 +16,7 @@ import ( "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/reexec" "github.com/docker/docker/pkg/system" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/skip" ) func init() { diff --git a/pkg/fileutils/fileutils_test.go b/pkg/fileutils/fileutils_test.go index b167538d5a121..4b5f129a50afd 100644 --- a/pkg/fileutils/fileutils_test.go +++ b/pkg/fileutils/fileutils_test.go @@ -10,8 +10,8 @@ import ( "strings" "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) // CopyFile with invalid src diff --git a/pkg/idtools/idtools_unix_test.go b/pkg/idtools/idtools_unix_test.go index 7d8c9715e238a..608000a66b207 100644 --- a/pkg/idtools/idtools_unix_test.go +++ b/pkg/idtools/idtools_unix_test.go @@ -10,10 +10,10 @@ import ( "path/filepath" "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/skip" "golang.org/x/sys/unix" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/skip" ) const ( diff --git a/pkg/ioutils/readers_test.go b/pkg/ioutils/readers_test.go index e424054aa76c9..e645c78d83b1c 100644 --- a/pkg/ioutils/readers_test.go +++ b/pkg/ioutils/readers_test.go @@ -8,8 +8,8 @@ import ( "testing" "time" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) // Implement io.Reader diff --git a/pkg/jsonmessage/jsonmessage_test.go b/pkg/jsonmessage/jsonmessage_test.go index f9ead207cab46..223d9c7f5a821 100644 --- a/pkg/jsonmessage/jsonmessage_test.go +++ b/pkg/jsonmessage/jsonmessage_test.go @@ -9,8 +9,8 @@ import ( "time" "github.com/docker/docker/pkg/term" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestError(t *testing.T) { diff --git a/pkg/mount/mountinfo_linux_test.go b/pkg/mount/mountinfo_linux_test.go index f8acb9eeae702..64411ccaef1df 100644 --- a/pkg/mount/mountinfo_linux_test.go +++ b/pkg/mount/mountinfo_linux_test.go @@ -6,7 +6,7 @@ import ( "bytes" "testing" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) const ( diff --git a/pkg/namesgenerator/names-generator.go b/pkg/namesgenerator/names-generator.go index 5c3395aaaa446..9eb02e14ec337 100644 --- a/pkg/namesgenerator/names-generator.go +++ b/pkg/namesgenerator/names-generator.go @@ -159,6 +159,9 @@ var ( // Bhaskara II - Ancient Indian mathematician-astronomer whose work on calculus predates Newton and Leibniz by over half a millennium - https://en.wikipedia.org/wiki/Bh%C4%81skara_II#Calculus "bhaskara", + // Elizabeth Helen Blackburn - Australian-American Nobel laureate; best known for co-discovering telomerase. https://en.wikipedia.org/wiki/Elizabeth_Blackburn + "blackburn", + // Elizabeth Blackwell - American doctor and first American woman to receive a medical degree - https://en.wikipedia.org/wiki/Elizabeth_Blackwell "blackwell", @@ -186,23 +189,41 @@ var ( // Emmett Brown invented time travel. https://en.wikipedia.org/wiki/Emmett_Brown (thanks Brian Goff) "brown", + // Linda Brown Buck - American biologist and Nobel laureate best known for her genetic and molecular analyses of the mechanisms of smell. https://en.wikipedia.org/wiki/Linda_B._Buck + "buck", + + // Dame Susan Jocelyn Bell Burnell - Northern Irish astrophysicist who discovered radio pulsars and was the first to analyse them. https://en.wikipedia.org/wiki/Jocelyn_Bell_Burnell + "burnell", + // Rachel Carson - American marine biologist and conservationist, her book Silent Spring and other writings are credited with advancing the global environmental movement. https://en.wikipedia.org/wiki/Rachel_Carson "carson", + // Dame Mary Lucy Cartwright - British mathematician who was one of the first to study what is now known as chaos theory. Also known for Cartwright's theorem which finds applications in signal processing. https://en.wikipedia.org/wiki/Mary_Cartwright + "cartwright", + // Subrahmanyan Chandrasekhar - Astrophysicist known for his mathematical theory on different stages and evolution in structures of the stars. He has won nobel prize for physics - https://en.wikipedia.org/wiki/Subrahmanyan_Chandrasekhar "chandrasekhar", - //Sergey Alexeyevich Chaplygin (Russian: Серге́й Алексе́евич Чаплы́гин; April 5, 1869 – October 8, 1942) was a Russian and Soviet physicist, mathematician, and mechanical engineer. He is known for mathematical formulas such as Chaplygin's equation and for a hypothetical substance in cosmology called Chaplygin gas, named after him. https://en.wikipedia.org/wiki/Sergey_Chaplygin + // Sergey Alexeyevich Chaplygin (Russian: Серге́й Алексе́евич Чаплы́гин; April 5, 1869 – October 8, 1942) was a Russian and Soviet physicist, mathematician, and mechanical engineer. He is known for mathematical formulas such as Chaplygin's equation and for a hypothetical substance in cosmology called Chaplygin gas, named after him. https://en.wikipedia.org/wiki/Sergey_Chaplygin "chaplygin", - // Asima Chatterjee was an indian organic chemist noted for her research on vinca alkaloids, development of drugs for treatment of epilepsy and malaria - https://en.wikipedia.org/wiki/Asima_Chatterjee + // Émilie du Châtelet - French natural philosopher, mathematician, physicist, and author during the early 1730s, known for her translation of and commentary on Isaac Newton's book Principia containing basic laws of physics. https://en.wikipedia.org/wiki/%C3%89milie_du_Ch%C3%A2telet + "chatelet", + + // Asima Chatterjee was an Indian organic chemist noted for her research on vinca alkaloids, development of drugs for treatment of epilepsy and malaria - https://en.wikipedia.org/wiki/Asima_Chatterjee "chatterjee", // Pafnuty Chebyshev - Russian mathematician. He is known fo his works on probability, statistics, mechanics, analytical geometry and number theory https://en.wikipedia.org/wiki/Pafnuty_Chebyshev "chebyshev", - //Claude Shannon - The father of information theory and founder of digital circuit design theory. (https://en.wikipedia.org/wiki/Claude_Shannon) - "shannon", + // Clifford Christopher Cocks - British mathematician and cryptographer employed by the GCHQ. Invented in 1973 an equivalent of what is now known as the RSA public-key cryptosystem (Rivest, Shamir and Adleman first publicly described RSA in 1978). https://en.wikipedia.org/wiki/Clifford_Cocks + "cocks", + + // Bram Cohen - American computer programmer and author of the BitTorrent peer-to-peer protocol. https://en.wikipedia.org/wiki/Bram_Cohen + "cohen", + + // David Lee Chaum - American computer scientist and cryptographer. Known for his seminal contributions in the field of anonymous communication. https://en.wikipedia.org/wiki/David_Chaum + "chaum", // Joan Clarke - Bletchley Park code breaker during the Second World War who pioneered techniques that remained top secret for decades. Also an accomplished numismatist https://en.wikipedia.org/wiki/Joan_Clarke "clarke", @@ -230,9 +251,21 @@ var ( // Leonardo Da Vinci invented too many things to list here. https://en.wikipedia.org/wiki/Leonardo_da_Vinci. "davinci", + // Satish Dhawan - Indian mathematician and aerospace engineer, known for leading the successful and indigenous development of the Indian space programme. https://en.wikipedia.org/wiki/Satish_Dhawan + "dhawan", + + // Bailey Whitfield Diffie - American cryptographer and one of the pioneers of public-key cryptography. https://en.wikipedia.org/wiki/Whitfield_Diffie + "diffie", + // Edsger Wybe Dijkstra was a Dutch computer scientist and mathematical scientist. https://en.wikipedia.org/wiki/Edsger_W._Dijkstra. "dijkstra", + // Paul Adrien Maurice Dirac - English theoretical physicist who made fundamental contributions to the early development of both quantum mechanics and quantum electrodynamics. https://en.wikipedia.org/wiki/Paul_Dirac + "dirac", + + // Agnes Meyer Driscoll - American cryptanalyst during World Wars I and II who successfully cryptanalysed a number of Japanese ciphers. She was also the co-developer of one of the cipher machines of the US Navy, the CM. https://en.wikipedia.org/wiki/Agnes_Meyer_Driscoll + "driscoll", + // Donna Dubinsky - played an integral role in the development of personal digital assistants (PDAs) serving as CEO of Palm, Inc. and co-founding Handspring. https://en.wikipedia.org/wiki/Donna_Dubinsky "dubinsky", @@ -245,11 +278,17 @@ var ( // Albert Einstein invented the general theory of relativity. https://en.wikipedia.org/wiki/Albert_Einstein "einstein", + // Alexandra Asanovna Elbakyan (Russian: Алекса́ндра Аса́новна Элбакя́н) is a Kazakhstani graduate student, computer programmer, internet pirate in hiding, and the creator of the site Sci-Hub. Nature has listed her in 2016 in the top ten people that mattered in science, and Ars Technica has compared her to Aaron Swartz. - https://en.wikipedia.org/wiki/Alexandra_Elbakyan + "elbakyan", + + // Taher A. ElGamal - Egyptian cryptographer best known for the ElGamal discrete log cryptosystem and the ElGamal digital signature scheme. https://en.wikipedia.org/wiki/Taher_Elgamal + "elgamal", + // Gertrude Elion - American biochemist, pharmacologist and the 1988 recipient of the Nobel Prize in Medicine - https://en.wikipedia.org/wiki/Gertrude_Elion "elion", - // Alexandra Asanovna Elbakyan (Russian: Алекса́ндра Аса́новна Элбакя́н) is a Kazakhstani graduate student, computer programmer, internet pirate in hiding, and the creator of the site Sci-Hub. Nature has listed her in 2016 in the top ten people that mattered in science, and Ars Technica has compared her to Aaron Swartz. - https://en.wikipedia.org/wiki/Alexandra_Elbakyan - "elbakyan", + // James Henry Ellis - British engineer and cryptographer employed by the GCHQ. Best known for conceiving for the first time, the idea of public-key cryptography. https://en.wikipedia.org/wiki/James_H._Ellis + "ellis", // Douglas Engelbart gave the mother of all demos: https://en.wikipedia.org/wiki/Douglas_Engelbart "engelbart", @@ -260,6 +299,9 @@ var ( // Leonhard Euler invented large parts of modern mathematics. https://de.wikipedia.org/wiki/Leonhard_Euler "euler", + // Horst Feistel - German-born American cryptographer who was one of the earliest non-government researchers to study the design and theory of block ciphers. Co-developer of DES and Lucifer. Feistel networks, a symmetric structure used in the construction of block ciphers are named after him. https://en.wikipedia.org/wiki/Horst_Feistel + "feistel", + // Pierre de Fermat pioneered several aspects of modern mathematics. https://en.wikipedia.org/wiki/Pierre_de_Fermat "fermat", @@ -272,12 +314,27 @@ var ( // Benjamin Franklin is famous for his experiments in electricity and the invention of the lightning rod. "franklin", + // Yuri Alekseyevich Gagarin - Soviet pilot and cosmonaut, best known as the first human to journey into outer space. https://en.wikipedia.org/wiki/Yuri_Gagarin + "gagarin", + // Galileo was a founding father of modern astronomy, and faced politics and obscurantism to establish scientific truth. https://en.wikipedia.org/wiki/Galileo_Galilei "galileo", + // Évariste Galois - French mathematician whose work laid the foundations of Galois theory and group theory, two major branches of abstract algebra, and the subfield of Galois connections, all while still in his late teens. https://en.wikipedia.org/wiki/%C3%89variste_Galois + "galois", + + // Kadambini Ganguly - Indian physician, known for being the first South Asian female physician, trained in western medicine, to graduate in South Asia. https://en.wikipedia.org/wiki/Kadambini_Ganguly + "ganguly", + // William Henry "Bill" Gates III is an American business magnate, philanthropist, investor, computer programmer, and inventor. https://en.wikipedia.org/wiki/Bill_Gates "gates", + // Johann Carl Friedrich Gauss - German mathematician who made significant contributions to many fields, including number theory, algebra, statistics, analysis, differential geometry, geodesy, geophysics, mechanics, electrostatics, magnetic fields, astronomy, matrix theory, and optics. https://en.wikipedia.org/wiki/Carl_Friedrich_Gauss + "gauss", + + // Marie-Sophie Germain - French mathematician, physicist and philosopher. Known for her work on elasticity theory, number theory and philosophy. https://en.wikipedia.org/wiki/Sophie_Germain + "germain", + // Adele Goldberg, was one of the designers and developers of the Smalltalk language. https://en.wikipedia.org/wiki/Adele_Goldberg_(computer_scientist) "goldberg", @@ -293,21 +350,39 @@ var ( // Jane Goodall - British primatologist, ethologist, and anthropologist who is considered to be the world's foremost expert on chimpanzees - https://en.wikipedia.org/wiki/Jane_Goodall "goodall", + // Carolyn Widney Greider - American molecular biologist and joint winner of the 2009 Nobel Prize for Physiology or Medicine for the discovery of telomerase. https://en.wikipedia.org/wiki/Carol_W._Greider + "greider", + + // Alexander Grothendieck - German-born French mathematician who became a leading figure in the creation of modern algebraic geometry. https://en.wikipedia.org/wiki/Alexander_Grothendieck + "grothendieck", + // Lois Haibt - American computer scientist, part of the team at IBM that developed FORTRAN - https://en.wikipedia.org/wiki/Lois_Haibt "haibt", // Margaret Hamilton - Director of the Software Engineering Division of the MIT Instrumentation Laboratory, which developed on-board flight software for the Apollo space program. https://en.wikipedia.org/wiki/Margaret_Hamilton_(scientist) "hamilton", + // Caroline Harriet Haslett - English electrical engineer, electricity industry administrator and champion of women's rights. Co-author of British Standard 1363 that specifies AC power plugs and sockets used across the United Kingdom (which is widely considered as one of the safest designs). https://en.wikipedia.org/wiki/Caroline_Haslett + "haslett", + // Stephen Hawking pioneered the field of cosmology by combining general relativity and quantum mechanics. https://en.wikipedia.org/wiki/Stephen_Hawking "hawking", + // Martin Edward Hellman - American cryptologist, best known for his invention of public-key cryptography in co-operation with Whitfield Diffie and Ralph Merkle. https://en.wikipedia.org/wiki/Martin_Hellman + "hellman", + // Werner Heisenberg was a founding father of quantum mechanics. https://en.wikipedia.org/wiki/Werner_Heisenberg "heisenberg", // Grete Hermann was a German philosopher noted for her philosophical work on the foundations of quantum mechanics. https://en.wikipedia.org/wiki/Grete_Hermann "hermann", + // Caroline Lucretia Herschel - German astronomer and discoverer of several comets. https://en.wikipedia.org/wiki/Caroline_Herschel + "herschel", + + // Heinrich Rudolf Hertz - German physicist who first conclusively proved the existence of the electromagnetic waves. https://en.wikipedia.org/wiki/Heinrich_Hertz + "hertz", + // Jaroslav Heyrovský was the inventor of the polarographic method, father of the electroanalytical method, and recipient of the Nobel Prize in 1959. His main field of work was polarography. https://en.wikipedia.org/wiki/Jaroslav_Heyrovsk%C3%BD "heyrovsky", @@ -326,6 +401,9 @@ var ( // Hypatia - Greek Alexandrine Neoplatonist philosopher in Egypt who was one of the earliest mothers of mathematics - https://en.wikipedia.org/wiki/Hypatia "hypatia", + // Teruko Ishizaka - Japanese scientist and immunologist who co-discovered the antibody class Immunoglobulin E. https://en.wikipedia.org/wiki/Teruko_Ishizaka + "ishizaka", + // Mary Jackson, American mathematician and aerospace engineer who earned the highest title within NASA's engineering department - https://en.wikipedia.org/wiki/Mary_Jackson_(engineer) "jackson", @@ -365,6 +443,9 @@ var ( // Johannes Kepler, German astronomer known for his three laws of planetary motion - https://en.wikipedia.org/wiki/Johannes_Kepler "kepler", + // Omar Khayyam - Persian mathematician, astronomer and poet. Known for his work on the classification and solution of cubic equations, for his contribution to the understanding of Euclid's fifth postulate and for computing the length of a year very accurately. https://en.wikipedia.org/wiki/Omar_Khayyam + "khayyam", + // Har Gobind Khorana - Indian-American biochemist who shared the 1968 Nobel Prize for Physiology - https://en.wikipedia.org/wiki/Har_Gobind_Khorana "khorana", @@ -392,10 +473,16 @@ var ( // Mary Leakey - British paleoanthropologist who discovered the first fossilized Proconsul skull - https://en.wikipedia.org/wiki/Mary_Leakey "leakey", - // Henrietta Swan Leavitt - she was an American astronomer who discovered the relation between the luminosity and the period of Cepheid variable stars. https://en.wikipedia.org/wiki/Henrietta_Swan_Leavitt + // Henrietta Swan Leavitt - she was an American astronomer who discovered the relation between the luminosity and the period of Cepheid variable stars. https://en.wikipedia.org/wiki/Henrietta_Swan_Leavitt "leavitt", - //Daniel Lewin - Mathematician, Akamai co-founder, soldier, 9/11 victim-- Developed optimization techniques for routing traffic on the internet. Died attempting to stop the 9-11 hijackers. https://en.wikipedia.org/wiki/Daniel_Lewin + // Esther Miriam Zimmer Lederberg - American microbiologist and a pioneer of bacterial genetics. https://en.wikipedia.org/wiki/Esther_Lederberg + "lederberg", + + // Inge Lehmann - Danish seismologist and geophysicist. Known for discovering in 1936 that the Earth has a solid inner core inside a molten outer core. https://en.wikipedia.org/wiki/Inge_Lehmann + "lehmann", + + // Daniel Lewin - Mathematician, Akamai co-founder, soldier, 9/11 victim-- Developed optimization techniques for routing traffic on the internet. Died attempting to stop the 9-11 hijackers. https://en.wikipedia.org/wiki/Daniel_Lewin "lewin", // Ruth Lichterman - one of the original programmers of the ENIAC. https://en.wikipedia.org/wiki/ENIAC - https://en.wikipedia.org/wiki/Ruth_Teitelbaum @@ -413,6 +500,9 @@ var ( // Mahavira - Ancient Indian mathematician during 9th century AD who discovered basic algebraic identities - https://en.wikipedia.org/wiki/Mah%C4%81v%C4%ABra_(mathematician) "mahavira", + // Yukihiro Matsumoto - Japanese computer scientist and software programmer best known as the chief designer of the Ruby programming language. https://en.wikipedia.org/wiki/Yukihiro_Matsumoto + "matsumoto", + // Maria Mayer - American theoretical physicist and Nobel laureate in Physics for proposing the nuclear shell model of the atomic nucleus - https://en.wikipedia.org/wiki/Maria_Mayer "mayer", @@ -422,12 +512,18 @@ var ( // Barbara McClintock - a distinguished American cytogeneticist, 1983 Nobel Laureate in Physiology or Medicine for discovering transposons. https://en.wikipedia.org/wiki/Barbara_McClintock "mcclintock", + // Anne Laura Dorinthea McLaren - British developmental biologist whose work helped lead to human in-vitro fertilisation. https://en.wikipedia.org/wiki/Anne_McLaren + "mclaren", + // Malcolm McLean invented the modern shipping container: https://en.wikipedia.org/wiki/Malcom_McLean "mclean", // Kay McNulty - one of the original programmers of the ENIAC. https://en.wikipedia.org/wiki/ENIAC - https://en.wikipedia.org/wiki/Kathleen_Antonelli "mcnulty", + // Gregor Johann Mendel - Czech scientist and founder of genetics. https://en.wikipedia.org/wiki/Gregor_Mendel + "mendel", + // Dmitri Mendeleev - a chemist and inventor. He formulated the Periodic Law, created a farsighted version of the periodic table of elements, and used it to correct the properties of some already discovered elements and also to predict the properties of eight elements yet to be discovered. https://en.wikipedia.org/wiki/Dmitri_Mendeleev "mendeleev", @@ -437,6 +533,9 @@ var ( // Carla Meninsky, was the game designer and programmer for Atari 2600 games Dodge 'Em and Warlords. https://en.wikipedia.org/wiki/Carla_Meninsky "meninsky", + // Ralph C. Merkle - American computer scientist, known for devising Merkle's puzzles - one of the very first schemes for public-key cryptography. Also, inventor of Merkle trees and co-inventor of the Merkle-Damgård construction for building collision-resistant cryptographic hash functions and the Merkle-Hellman knapsack cryptosystem. https://en.wikipedia.org/wiki/Ralph_Merkle + "merkle", + // Johanna Mestorf - German prehistoric archaeologist and first female museum director in Germany - https://en.wikipedia.org/wiki/Johanna_Mestorf "mestorf", @@ -452,6 +551,12 @@ var ( // Ian Murdock - founder of the Debian project - https://en.wikipedia.org/wiki/Ian_Murdock "murdock", + // John Napier of Merchiston - Scottish landowner known as an astronomer, mathematician and physicist. Best known for his discovery of logarithms. https://en.wikipedia.org/wiki/John_Napier + "napier", + + // John Forbes Nash, Jr. - American mathematician who made fundamental contributions to game theory, differential geometry, and the study of partial differential equations. https://en.wikipedia.org/wiki/John_Forbes_Nash_Jr. + "nash", + // John von Neumann - todays computer architectures are based on the von Neumann architecture. https://en.wikipedia.org/wiki/Von_Neumann_architecture "neumann", @@ -518,18 +623,30 @@ var ( // Dennis Ritchie - co-creator of UNIX and the C programming language. - https://en.wikipedia.org/wiki/Dennis_Ritchie "ritchie", + // Julia Hall Bowman Robinson - American mathematician renowned for her contributions to the fields of computability theory and computational complexity theory. https://en.wikipedia.org/wiki/Julia_Robinson + "robinson", + // Wilhelm Conrad Röntgen - German physicist who was awarded the first Nobel Prize in Physics in 1901 for the discovery of X-rays (Röntgen rays). https://en.wikipedia.org/wiki/Wilhelm_R%C3%B6ntgen "roentgen", // Rosalind Franklin - British biophysicist and X-ray crystallographer whose research was critical to the understanding of DNA - https://en.wikipedia.org/wiki/Rosalind_Franklin "rosalind", + // Vera Rubin - American astronomer who pioneered work on galaxy rotation rates. https://en.wikipedia.org/wiki/Vera_Rubin + "rubin", + // Meghnad Saha - Indian astrophysicist best known for his development of the Saha equation, used to describe chemical and physical conditions in stars - https://en.wikipedia.org/wiki/Meghnad_Saha "saha", // Jean E. Sammet developed FORMAC, the first widely used computer language for symbolic manipulation of mathematical formulas. https://en.wikipedia.org/wiki/Jean_E._Sammet "sammet", + // Mildred Sanderson - American mathematician best known for Sanderson's theorem concerning modular invariants. https://en.wikipedia.org/wiki/Mildred_Sanderson + "sanderson", + + // Claude Shannon - The father of information theory and founder of digital circuit design theory. (https://en.wikipedia.org/wiki/Claude_Shannon) + "shannon", + // Carol Shaw - Originally an Atari employee, Carol Shaw is said to be the first female video game designer. https://en.wikipedia.org/wiki/Carol_Shaw_(video_game_designer) "shaw", @@ -539,6 +656,9 @@ var ( // William Shockley co-invented the transistor - https://en.wikipedia.org/wiki/William_Shockley "shockley", + // Lina Solomonovna Stern (or Shtern; Russian: Лина Соломоновна Штерн; 26 August 1878 – 7 March 1968) was a Soviet biochemist, physiologist and humanist whose medical discoveries saved thousands of lives at the fronts of World War II. She is best known for her pioneering work on blood–brain barrier, which she described as hemato-encephalic barrier in 1921. https://en.wikipedia.org/wiki/Lina_Stern + "shtern", + // Françoise Barré-Sinoussi - French virologist and Nobel Prize Laureate in Physiology or Medicine; her work was fundamental in identifying HIV as the cause of AIDS. https://en.wikipedia.org/wiki/Fran%C3%A7oise_Barr%C3%A9-Sinoussi "sinoussi", @@ -551,9 +671,6 @@ var ( // Richard Matthew Stallman - the founder of the Free Software movement, the GNU project, the Free Software Foundation, and the League for Programming Freedom. He also invented the concept of copyleft to protect the ideals of this movement, and enshrined this concept in the widely-used GPL (General Public License) for software. https://en.wikiquote.org/wiki/Richard_Stallman "stallman", - // Lina Solomonovna Stern (or Shtern; Russian: Лина Соломоновна Штерн; 26 August 1878 – 7 March 1968) was a Soviet biochemist, physiologist and humanist whose medical discoveries saved thousands of lives at the fronts of World War II. She is best known for her pioneering work on blood–brain barrier, which she described as hemato-encephalic barrier in 1921. https://en.wikipedia.org/wiki/Lina_Stern - "shtern", - // Michael Stonebraker is a database research pioneer and architect of Ingres, Postgres, VoltDB and SciDB. Winner of 2014 ACM Turing Award. https://en.wikipedia.org/wiki/Michael_Stonebraker "stonebraker", @@ -566,18 +683,27 @@ var ( // Bertha Swirles was a theoretical physicist who made a number of contributions to early quantum theory. https://en.wikipedia.org/wiki/Bertha_Swirles "swirles", - // Valentina Tereshkova is a russian engineer, cosmonaut and politician. She was the first woman flying to space in 1963. In 2013, at the age of 76, she offered to go on a one-way mission to mars. https://en.wikipedia.org/wiki/Valentina_Tereshkova + // Helen Brooke Taussig - American cardiologist and founder of the field of paediatric cardiology. https://en.wikipedia.org/wiki/Helen_B._Taussig + "taussig", + + // Valentina Tereshkova is a Russian engineer, cosmonaut and politician. She was the first woman to fly to space in 1963. In 2013, at the age of 76, she offered to go on a one-way mission to Mars. https://en.wikipedia.org/wiki/Valentina_Tereshkova "tereshkova", // Nikola Tesla invented the AC electric system and every gadget ever used by a James Bond villain. https://en.wikipedia.org/wiki/Nikola_Tesla "tesla", + // Marie Tharp - American geologist and oceanic cartographer who co-created the first scientific map of the Atlantic Ocean floor. Her work led to the acceptance of the theories of plate tectonics and continental drift. https://en.wikipedia.org/wiki/Marie_Tharp + "tharp", + // Ken Thompson - co-creator of UNIX and the C programming language - https://en.wikipedia.org/wiki/Ken_Thompson "thompson", // Linus Torvalds invented Linux and Git. https://en.wikipedia.org/wiki/Linus_Torvalds "torvalds", + // Youyou Tu - Chinese pharmaceutical chemist and educator known for discovering artemisinin and dihydroartemisinin, used to treat malaria, which has saved millions of lives. Joint winner of the 2015 Nobel Prize in Physiology or Medicine. https://en.wikipedia.org/wiki/Tu_Youyou + "tu", + // Alan Turing was a founding father of computer science. https://en.wikipedia.org/wiki/Alan_Turing. "turing", @@ -605,6 +731,9 @@ var ( // Roberta Williams, did pioneering work in graphical adventure games for personal computers, particularly the King's Quest series. https://en.wikipedia.org/wiki/Roberta_Williams "williams", + // Malcolm John Williamson - British mathematician and cryptographer employed by the GCHQ. Developed in 1974 what is now known as Diffie-Hellman key exchange (Diffie and Hellman first published the scheme in 1976). https://en.wikipedia.org/wiki/Malcolm_J._Williamson + "williamson", + // Sophie Wilson designed the first Acorn Micro-Computer and the instruction set for ARM processors. https://en.wikipedia.org/wiki/Sophie_Wilson "wilson", @@ -617,6 +746,9 @@ var ( // The Wright brothers, Orville and Wilbur - credited with inventing and building the world's first successful airplane and making the first controlled, powered and sustained heavier-than-air human flight - https://en.wikipedia.org/wiki/Wright_brothers "wright", + // Chien-Shiung Wu - Chinese-American experimental physicist who made significant contributions to nuclear physics. https://en.wikipedia.org/wiki/Chien-Shiung_Wu + "wu", + // Rosalyn Sussman Yalow - Rosalyn Sussman Yalow was an American medical physicist, and a co-winner of the 1977 Nobel Prize in Physiology or Medicine for development of the radioimmunoassay technique. https://en.wikipedia.org/wiki/Rosalyn_Sussman_Yalow "yalow", diff --git a/pkg/plugins/client_test.go b/pkg/plugins/client_test.go index d420010f1a1a5..c3a4892272cd0 100644 --- a/pkg/plugins/client_test.go +++ b/pkg/plugins/client_test.go @@ -14,9 +14,9 @@ import ( "github.com/docker/docker/pkg/plugins/transport" "github.com/docker/go-connections/tlsconfig" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) var ( diff --git a/pkg/plugins/discovery_unix_test.go b/pkg/plugins/discovery_unix_test.go index 2c718d8beedad..b4aefc83e488a 100644 --- a/pkg/plugins/discovery_unix_test.go +++ b/pkg/plugins/discovery_unix_test.go @@ -11,7 +11,7 @@ import ( "reflect" "testing" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) func TestLocalSocket(t *testing.T) { diff --git a/pkg/plugins/plugin_test.go b/pkg/plugins/plugin_test.go index ca8d5984007cd..ce98078f87c17 100644 --- a/pkg/plugins/plugin_test.go +++ b/pkg/plugins/plugin_test.go @@ -14,8 +14,8 @@ import ( "github.com/docker/docker/pkg/plugins/transport" "github.com/docker/go-connections/tlsconfig" - "github.com/gotestyourself/gotestyourself/assert" "github.com/pkg/errors" + "gotest.tools/assert" ) const ( diff --git a/pkg/plugins/transport/http_test.go b/pkg/plugins/transport/http_test.go index 081f60424d68f..78ab23724b88d 100644 --- a/pkg/plugins/transport/http_test.go +++ b/pkg/plugins/transport/http_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestHTTPTransport(t *testing.T) { diff --git a/pkg/pools/pools_test.go b/pkg/pools/pools_test.go index 76015169d42b2..7ff01ce3d5c85 100644 --- a/pkg/pools/pools_test.go +++ b/pkg/pools/pools_test.go @@ -7,8 +7,8 @@ import ( "strings" "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestBufioReaderPoolGetWithNoReaderShouldCreateOne(t *testing.T) { diff --git a/pkg/reexec/command_unsupported.go b/pkg/reexec/command_unsupported.go index 09fb4b2d2930f..e7eed24240c63 100644 --- a/pkg/reexec/command_unsupported.go +++ b/pkg/reexec/command_unsupported.go @@ -6,6 +6,10 @@ import ( "os/exec" ) +func Self() string { + return "" +} + // Command is unsupported on operating systems apart from Linux, Windows, and Darwin. func Command(args ...string) *exec.Cmd { return nil diff --git a/pkg/reexec/reexec_test.go b/pkg/reexec/reexec_test.go index 90aa01a390fd5..44675e7b6378b 100644 --- a/pkg/reexec/reexec_test.go +++ b/pkg/reexec/reexec_test.go @@ -5,7 +5,7 @@ import ( "os/exec" "testing" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) func init() { diff --git a/pkg/signal/signal_linux_test.go b/pkg/signal/signal_linux_test.go index 71c577ed64aaa..9a021e2164319 100644 --- a/pkg/signal/signal_linux_test.go +++ b/pkg/signal/signal_linux_test.go @@ -8,8 +8,8 @@ import ( "testing" "time" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestCatchAll(t *testing.T) { diff --git a/pkg/signal/signal_test.go b/pkg/signal/signal_test.go index bbf3736fc8c19..0bfcf6ce448dd 100644 --- a/pkg/signal/signal_test.go +++ b/pkg/signal/signal_test.go @@ -4,8 +4,8 @@ import ( "syscall" "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestParseSignal(t *testing.T) { diff --git a/pkg/signal/trap_linux_test.go b/pkg/signal/trap_linux_test.go index a3afe7a7b6895..14d1543117b15 100644 --- a/pkg/signal/trap_linux_test.go +++ b/pkg/signal/trap_linux_test.go @@ -10,8 +10,8 @@ import ( "syscall" "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func buildTestBinary(t *testing.T, tmpdir string, prefix string) (string, string) { diff --git a/pkg/streamformatter/streamformatter.go b/pkg/streamformatter/streamformatter.go index 2b5e713040eb4..04917d49ab29d 100644 --- a/pkg/streamformatter/streamformatter.go +++ b/pkg/streamformatter/streamformatter.go @@ -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 } diff --git a/pkg/streamformatter/streamformatter_test.go b/pkg/streamformatter/streamformatter_test.go index 172d568bda0c9..f630699d73e11 100644 --- a/pkg/streamformatter/streamformatter_test.go +++ b/pkg/streamformatter/streamformatter_test.go @@ -10,8 +10,8 @@ import ( "github.com/docker/docker/pkg/jsonmessage" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestRawProgressFormatterFormatStatus(t *testing.T) { @@ -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())) } diff --git a/pkg/streamformatter/streamwriter_test.go b/pkg/streamformatter/streamwriter_test.go index b74d6fb2d3435..5b679f2cf4822 100644 --- a/pkg/streamformatter/streamwriter_test.go +++ b/pkg/streamformatter/streamwriter_test.go @@ -4,8 +4,8 @@ import ( "bytes" "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestStreamWriterStdout(t *testing.T) { diff --git a/pkg/sysinfo/sysinfo_linux_test.go b/pkg/sysinfo/sysinfo_linux_test.go index e8a12a35c9a44..13a07fbce93cf 100644 --- a/pkg/sysinfo/sysinfo_linux_test.go +++ b/pkg/sysinfo/sysinfo_linux_test.go @@ -7,8 +7,8 @@ import ( "path/filepath" "testing" - "github.com/gotestyourself/gotestyourself/assert" "golang.org/x/sys/unix" + "gotest.tools/assert" ) func TestReadProcBool(t *testing.T) { diff --git a/pkg/system/lcow.go b/pkg/system/lcow.go index 5c3fbfe6f4a76..5be3e2182ba08 100644 --- a/pkg/system/lcow.go +++ b/pkg/system/lcow.go @@ -1,69 +1,32 @@ package system // import "github.com/docker/docker/pkg/system" import ( - "fmt" "runtime" "strings" specs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" ) -// ValidatePlatform determines if a platform structure is valid. -// TODO This is a temporary function - can be replaced by parsing from -// https://github.com/containerd/containerd/pull/1403/files at a later date. -// @jhowardmsft -func ValidatePlatform(platform *specs.Platform) error { - platform.Architecture = strings.ToLower(platform.Architecture) - platform.OS = strings.ToLower(platform.OS) - // Based on https://github.com/moby/moby/pull/34642#issuecomment-330375350, do - // not support anything except operating system. - if platform.Architecture != "" { - return fmt.Errorf("invalid platform architecture %q", platform.Architecture) - } - if platform.OS != "" { - if !(platform.OS == runtime.GOOS || (LCOWSupported() && platform.OS == "linux")) { - return fmt.Errorf("invalid platform os %q", platform.OS) - } - } - if len(platform.OSFeatures) != 0 { - return fmt.Errorf("invalid platform osfeatures %q", platform.OSFeatures) - } - if platform.OSVersion != "" { - return fmt.Errorf("invalid platform osversion %q", platform.OSVersion) - } - if platform.Variant != "" { - return fmt.Errorf("invalid platform variant %q", platform.Variant) - } - return nil -} - -// ParsePlatform parses a platform string in the format os[/arch[/variant] -// into an OCI image-spec platform structure. -// TODO This is a temporary function - can be replaced by parsing from -// https://github.com/containerd/containerd/pull/1403/files at a later date. -// @jhowardmsft -func ParsePlatform(in string) *specs.Platform { - p := &specs.Platform{} - elements := strings.SplitN(strings.ToLower(in), "/", 3) - if len(elements) == 3 { - p.Variant = elements[2] - } - if len(elements) >= 2 { - p.Architecture = elements[1] - } - if len(elements) >= 1 { - p.OS = elements[0] - } - return p -} - // IsOSSupported determines if an operating system is supported by the host func IsOSSupported(os string) bool { - if runtime.GOOS == os { + if strings.EqualFold(runtime.GOOS, os) { return true } - if LCOWSupported() && os == "linux" { + if LCOWSupported() && strings.EqualFold(os, "linux") { return true } return false } + +// ValidatePlatform determines if a platform structure is valid. +// TODO This is a temporary windows-only function, should be replaced by +// comparison of worker capabilities +func ValidatePlatform(platform specs.Platform) error { + if runtime.GOOS == "windows" { + if !(platform.OS == runtime.GOOS || (LCOWSupported() && platform.OS == "linux")) { + return errors.Errorf("unsupported os %s", platform.OS) + } + } + return nil +} diff --git a/pkg/system/rm_test.go b/pkg/system/rm_test.go index 19dc42cba9496..0448aac619de1 100644 --- a/pkg/system/rm_test.go +++ b/pkg/system/rm_test.go @@ -9,7 +9,7 @@ import ( "time" "github.com/docker/docker/pkg/mount" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/skip" ) func TestEnsureRemoveAllNotExist(t *testing.T) { diff --git a/pkg/system/stat_unix_test.go b/pkg/system/stat_unix_test.go index fd68a9665652d..44e048f2a7230 100644 --- a/pkg/system/stat_unix_test.go +++ b/pkg/system/stat_unix_test.go @@ -7,7 +7,7 @@ import ( "syscall" "testing" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) // TestFromStatT tests fromStatT for a tempfile diff --git a/pkg/system/syscall_windows.go b/pkg/system/syscall_windows.go index ee7e0256f333b..7bfd5f5cb618f 100644 --- a/pkg/system/syscall_windows.go +++ b/pkg/system/syscall_windows.go @@ -6,6 +6,20 @@ import ( "github.com/sirupsen/logrus" "golang.org/x/sys/windows" + "golang.org/x/sys/windows/registry" +) + +type procisolationpolicy int32 + +const ( + PROC_ISOLATION_DEFAULT procisolationpolicy = iota + PROC_ISOLATION_ALLOW + PROC_ISOLATION_DENY +) + +const ( + hcsPolicyRegPath = `SOFTWARE\Microsoft\Windows NT\CurrentVersion\HostComputeService\Policy` + isProcessIsolationAllowedRegValName = `ProcessIsolationIsAllowed` ) var ( @@ -88,6 +102,35 @@ func IsIoTCore() bool { return returnedProductType == productIoTUAP || returnedProductType == productIoTUAPCommercial } +// GetProcessIsolationPolicy returns the process based container policy +func GetProcessIsolationPolicy() procisolationpolicy { + var ( + k registry.Key + err error + val uint64 + ) + defer func() { + if k != 0 { + k.Close() + } + }() + + if k, err = registry.OpenKey(registry.LOCAL_MACHINE, hcsPolicyRegPath, registry.QUERY_VALUE); err != nil { + return PROC_ISOLATION_DEFAULT + } + if val, _, err = k.GetIntegerValue(isProcessIsolationAllowedRegValName); err != nil { + return PROC_ISOLATION_DEFAULT + } + switch val { + default: + return PROC_ISOLATION_DEFAULT + case 0: + return PROC_ISOLATION_DENY + case 1: + return PROC_ISOLATION_ALLOW + } +} + // Unmount is a platform-specific helper function to call // the unmount syscall. Not supported on Windows func Unmount(dest string) error { diff --git a/pkg/tarsum/tarsum_test.go b/pkg/tarsum/tarsum_test.go index e1b38a6a1bc07..97ece8506cb24 100644 --- a/pkg/tarsum/tarsum_test.go +++ b/pkg/tarsum/tarsum_test.go @@ -17,8 +17,8 @@ import ( "strings" "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) type testLayer struct { @@ -65,7 +65,7 @@ var testLayers = []testLayer{ tarsum: "tarsum+sha256:c66bd5ec9f87b8f4c6135ca37684618f486a3dd1d113b138d0a177bfa39c2571"}, { options: &sizedOptions{1, 1024 * 1024, false, false}, // a 1mb file (in memory) - tarsum: "tarsum+sha256:8bf12d7e67c51ee2e8306cba569398b1b9f419969521a12ffb9d8875e8836738"}, + tarsum: "tarsum+sha256:75258b2c5dcd9adfe24ce71eeca5fc5019c7e669912f15703ede92b1a60cb11f"}, { // this tar has two files with the same path filename: "testdata/collision/collision-0.tar", @@ -84,27 +84,27 @@ var testLayers = []testLayer{ tarsum: "tarsum+sha256:cbe4dee79fe979d69c16c2bccd032e3205716a562f4a3c1ca1cbeed7b256eb19"}, { options: &sizedOptions{1, 1024 * 1024, false, false}, // a 1mb file (in memory) - tarsum: "tarsum+md5:0d7529ec7a8360155b48134b8e599f53", + tarsum: "tarsum+md5:3a6cdb475d90459ac0d3280703d17be2", hash: md5THash, }, { options: &sizedOptions{1, 1024 * 1024, false, false}, // a 1mb file (in memory) - tarsum: "tarsum+sha1:f1fee39c5925807ff75ef1925e7a23be444ba4df", + tarsum: "tarsum+sha1:14b5e0d12a0c50a4281e86e92153fa06d55d00c6", hash: sha1Hash, }, { options: &sizedOptions{1, 1024 * 1024, false, false}, // a 1mb file (in memory) - tarsum: "tarsum+sha224:6319390c0b061d639085d8748b14cd55f697cf9313805218b21cf61c", + tarsum: "tarsum+sha224:dd8925b7a4c71b13f3a68a0f9428a757c76b93752c398f272a9062d5", hash: sha224Hash, }, { options: &sizedOptions{1, 1024 * 1024, false, false}, // a 1mb file (in memory) - tarsum: "tarsum+sha384:a578ce3ce29a2ae03b8ed7c26f47d0f75b4fc849557c62454be4b5ffd66ba021e713b48ce71e947b43aab57afd5a7636", + tarsum: "tarsum+sha384:e39e82f40005134bed13fb632d1a5f2aa4675c9ddb4a136fbcec202797e68d2f635e1200dee2e3a8d7f69d54d3f2fd27", hash: sha384Hash, }, { options: &sizedOptions{1, 1024 * 1024, false, false}, // a 1mb file (in memory) - tarsum: "tarsum+sha512:e9bfb90ca5a4dfc93c46ee061a5cf9837de6d2fdf82544d6460d3147290aecfabf7b5e415b9b6e72db9b8941f149d5d69fb17a394cbfaf2eac523bd9eae21855", + tarsum: "tarsum+sha512:7c56de40b2d1ed3863ff25d83b59cdc8f53e67d1c01c3ee8f201f8e4dec3107da976d0c0ec9109c962a152b32699fe329b2dab13966020e400c32878a0761a7e", hash: sha512Hash, }, } @@ -138,11 +138,12 @@ func sizedTar(opts sizedOptions) io.Reader { defer tarW.Close() for i := int64(0); i < opts.num; i++ { err := tarW.WriteHeader(&tar.Header{ - Name: fmt.Sprintf("/testdata%d", i), - Mode: 0755, - Uid: 0, - Gid: 0, - Size: opts.size, + Name: fmt.Sprintf("/testdata%d", i), + Mode: 0755, + Uid: 0, + Gid: 0, + Size: opts.size, + Typeflag: tar.TypeReg, }) if err != nil { return nil diff --git a/pkg/term/ascii_test.go b/pkg/term/ascii_test.go index 321d1b87de3ba..665ab1552ff04 100644 --- a/pkg/term/ascii_test.go +++ b/pkg/term/ascii_test.go @@ -3,8 +3,8 @@ package term // import "github.com/docker/docker/pkg/term" import ( "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestToBytes(t *testing.T) { diff --git a/pkg/term/proxy_test.go b/pkg/term/proxy_test.go index 72f53e58d15c1..df588fe15bb00 100644 --- a/pkg/term/proxy_test.go +++ b/pkg/term/proxy_test.go @@ -5,8 +5,8 @@ import ( "fmt" "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestEscapeProxyRead(t *testing.T) { diff --git a/pkg/term/term_linux_test.go b/pkg/term/term_linux_test.go index 4f1d67586f1ec..272395a10ea46 100644 --- a/pkg/term/term_linux_test.go +++ b/pkg/term/term_linux_test.go @@ -8,7 +8,7 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) // RequiresRoot skips tests that require root, unless the test.root flag has diff --git a/plugin/blobstore.go b/plugin/blobstore.go index fd7f040efcd8b..a24e7bdf4f8e4 100644 --- a/plugin/blobstore.go +++ b/plugin/blobstore.go @@ -145,6 +145,7 @@ func (dm *downloadManager) Download(ctx context.Context, initialRootFS image.Roo if err != nil { return initialRootFS, nil, err } + defer inflatedLayerData.Close() digester := digest.Canonical.Digester() if _, err := chrootarchive.ApplyLayer(dm.tmpDir, io.TeeReader(inflatedLayerData, digester.Hash())); err != nil { return initialRootFS, nil, err diff --git a/plugin/executor/containerd/containerd.go b/plugin/executor/containerd/containerd.go index e490ef0a9ebe8..8f1c8a4a192c6 100644 --- a/plugin/executor/containerd/containerd.go +++ b/plugin/executor/containerd/containerd.go @@ -8,7 +8,7 @@ import ( "time" "github.com/containerd/containerd/cio" - "github.com/containerd/containerd/linux/runctypes" + "github.com/containerd/containerd/runtime/linux/runctypes" "github.com/docker/docker/errdefs" "github.com/docker/docker/libcontainerd" "github.com/opencontainers/runtime-spec/specs-go" @@ -58,6 +58,19 @@ type Executor struct { exitHandler ExitHandler } +// deleteTaskAndContainer deletes plugin task and then plugin container from containerd +func deleteTaskAndContainer(ctx context.Context, cli Client, id string) { + _, _, err := cli.DeleteTask(ctx, id) + if err != nil && !errdefs.IsNotFound(err) { + logrus.WithError(err).WithField("id", id).Error("failed to delete plugin task from containerd") + } + + err = cli.Delete(ctx, id) + if err != nil && !errdefs.IsNotFound(err) { + logrus.WithError(err).WithField("id", id).Error("failed to delete plugin container from containerd") + } +} + // Create creates a new container func (e *Executor) Create(id string, spec specs.Spec, stdout, stderr io.WriteCloser) error { opts := runctypes.RuncOptions{ @@ -87,34 +100,21 @@ func (e *Executor) Create(id string, spec specs.Spec, stdout, stderr io.WriteClo _, err = e.client.Start(ctx, id, "", false, attachStreamsFunc(stdout, stderr)) if err != nil { - if _, _, err2 := e.client.DeleteTask(ctx, id); err2 != nil && !errdefs.IsNotFound(err2) { - logrus.WithError(err2).WithField("id", id).Warn("Received an error while attempting to clean up containerd plugin task after failed start") - } - if err2 := e.client.Delete(ctx, id); err2 != nil && !errdefs.IsNotFound(err2) { - logrus.WithError(err2).WithField("id", id).Warn("Received an error while attempting to clean up containerd plugin container after failed start") - } + deleteTaskAndContainer(ctx, e.client, id) } return err } // Restore restores a container -func (e *Executor) Restore(id string, stdout, stderr io.WriteCloser) error { +func (e *Executor) Restore(id string, stdout, stderr io.WriteCloser) (bool, error) { alive, _, err := e.client.Restore(context.Background(), id, attachStreamsFunc(stdout, stderr)) if err != nil && !errdefs.IsNotFound(err) { - return err + return false, err } if !alive { - _, _, err = e.client.DeleteTask(context.Background(), id) - if err != nil && !errdefs.IsNotFound(err) { - logrus.WithError(err).Errorf("failed to delete container plugin %s task from containerd", id) - } - - err = e.client.Delete(context.Background(), id) - if err != nil && !errdefs.IsNotFound(err) { - logrus.WithError(err).Errorf("failed to delete container plugin %s from containerd", id) - } + deleteTaskAndContainer(context.Background(), e.client, id) } - return nil + return alive, nil } // IsRunning returns if the container with the given id is running @@ -133,14 +133,7 @@ func (e *Executor) Signal(id string, signal int) error { func (e *Executor) ProcessEvent(id string, et libcontainerd.EventType, ei libcontainerd.EventInfo) error { switch et { case libcontainerd.EventExit: - // delete task and container - if _, _, err := e.client.DeleteTask(context.Background(), id); err != nil { - logrus.WithError(err).Errorf("failed to delete container plugin %s task from containerd", id) - } - - if err := e.client.Delete(context.Background(), id); err != nil { - logrus.WithError(err).Errorf("failed to delete container plugin %s from containerd", id) - } + deleteTaskAndContainer(context.Background(), e.client, id) return e.exitHandler.HandleExitEvent(ei.ContainerID) } return nil diff --git a/plugin/executor/containerd/containerd_test.go b/plugin/executor/containerd/containerd_test.go index d9185a05e1c54..e27063b1d81f9 100644 --- a/plugin/executor/containerd/containerd_test.go +++ b/plugin/executor/containerd/containerd_test.go @@ -9,9 +9,9 @@ import ( "time" "github.com/docker/docker/libcontainerd" - "github.com/gotestyourself/gotestyourself/assert" "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" + "gotest.tools/assert" ) func TestLifeCycle(t *testing.T) { diff --git a/plugin/manager.go b/plugin/manager.go index 9c674f9545bee..c6f896129b9b4 100644 --- a/plugin/manager.go +++ b/plugin/manager.go @@ -37,14 +37,14 @@ var validFullID = regexp.MustCompile(`^([a-f0-9]{64})$`) // Executor is the interface that the plugin manager uses to interact with for starting/stopping plugins type Executor interface { Create(id string, spec specs.Spec, stdout, stderr io.WriteCloser) error - Restore(id string, stdout, stderr io.WriteCloser) error IsRunning(id string) (bool, error) + Restore(id string, stdout, stderr io.WriteCloser) (alive bool, err error) Signal(id string, signal int) error } -func (pm *Manager) restorePlugin(p *v2.Plugin) error { +func (pm *Manager) restorePlugin(p *v2.Plugin, c *controller) error { if p.IsEnabled() { - return pm.restore(p) + return pm.restore(p, c) } return nil } @@ -143,12 +143,15 @@ func (pm *Manager) HandleExitEvent(id string) error { return err } - os.RemoveAll(filepath.Join(pm.config.ExecRoot, id)) + if err := os.RemoveAll(filepath.Join(pm.config.ExecRoot, id)); err != nil && !os.IsNotExist(err) { + logrus.WithError(err).WithField("id", id).Error("Could not remove plugin bundle dir") + } pm.mu.RLock() c := pm.cMap[p] if c.exitChan != nil { close(c.exitChan) + c.exitChan = nil // ignore duplicate events (containerd issue #2299) } restart := c.restart pm.mu.RUnlock() @@ -205,12 +208,15 @@ func (pm *Manager) reload() error { // todo: restore var wg sync.WaitGroup wg.Add(len(plugins)) for _, p := range plugins { - c := &controller{} // todo: remove this + c := &controller{exitChan: make(chan bool)} + pm.mu.Lock() pm.cMap[p] = c + pm.mu.Unlock() + go func(p *v2.Plugin) { defer wg.Done() - if err := pm.restorePlugin(p); err != nil { - logrus.Errorf("failed to restore plugin '%s': %s", p.Name(), err) + if err := pm.restorePlugin(p, c); err != nil { + logrus.WithError(err).WithField("id", p.GetID()).Error("Failed to restore plugin") return } @@ -248,7 +254,7 @@ func (pm *Manager) reload() error { // todo: restore if requiresManualRestore { // if liveRestore is not enabled, the plugin will be stopped now so we should enable it if err := pm.enable(p, c, true); err != nil { - logrus.Errorf("failed to enable plugin '%s': %s", p.Name(), err) + logrus.WithError(err).WithField("id", p.GetID()).Error("failed to enable plugin") } } }(p) diff --git a/plugin/manager_linux.go b/plugin/manager_linux.go index 0029ff7868d4b..3c6f9c553a965 100644 --- a/plugin/manager_linux.go +++ b/plugin/manager_linux.go @@ -79,7 +79,7 @@ func (pm *Manager) pluginPostStart(p *v2.Plugin, c *controller) error { client, err := plugins.NewClientWithTimeout(addr.Network()+"://"+addr.String(), nil, p.Timeout()) if err != nil { c.restart = false - shutdownPlugin(p, c, pm.executor) + shutdownPlugin(p, c.exitChan, pm.executor) return errors.WithStack(err) } @@ -106,7 +106,7 @@ func (pm *Manager) pluginPostStart(p *v2.Plugin, c *controller) error { c.restart = false // While restoring plugins, we need to explicitly set the state to disabled pm.config.Store.SetState(p, false) - shutdownPlugin(p, c, pm.executor) + shutdownPlugin(p, c.exitChan, pm.executor) return err } @@ -117,16 +117,15 @@ func (pm *Manager) pluginPostStart(p *v2.Plugin, c *controller) error { return pm.save(p) } -func (pm *Manager) restore(p *v2.Plugin) error { +func (pm *Manager) restore(p *v2.Plugin, c *controller) error { stdout, stderr := makeLoggerStreams(p.GetID()) - if err := pm.executor.Restore(p.GetID(), stdout, stderr); err != nil { + alive, err := pm.executor.Restore(p.GetID(), stdout, stderr) + if err != nil { return err } if pm.config.LiveRestoreEnabled { - c := &controller{} - if isRunning, _ := pm.executor.IsRunning(p.GetID()); !isRunning { - // plugin is not running, so follow normal startup procedure + if !alive { return pm.enable(p, c, true) } @@ -138,10 +137,16 @@ func (pm *Manager) restore(p *v2.Plugin) error { return pm.pluginPostStart(p, c) } + if alive { + // TODO(@cpuguy83): Should we always just re-attach to the running plugin instead of doing this? + c.restart = false + shutdownPlugin(p, c.exitChan, pm.executor) + } + return nil } -func shutdownPlugin(p *v2.Plugin, c *controller, executor Executor) { +func shutdownPlugin(p *v2.Plugin, ec chan bool, executor Executor) { pluginID := p.GetID() err := executor.Signal(pluginID, int(unix.SIGTERM)) @@ -149,7 +154,7 @@ func shutdownPlugin(p *v2.Plugin, c *controller, executor Executor) { logrus.Errorf("Sending SIGTERM to plugin failed with error: %v", err) } else { select { - case <-c.exitChan: + case <-ec: logrus.Debug("Clean shutdown of plugin") case <-time.After(time.Second * 10): logrus.Debug("Force shutdown plugin") @@ -157,7 +162,7 @@ func shutdownPlugin(p *v2.Plugin, c *controller, executor Executor) { logrus.Errorf("Sending SIGKILL to plugin failed with error: %v", err) } select { - case <-c.exitChan: + case <-ec: logrus.Debug("SIGKILL plugin shutdown") case <-time.After(time.Second * 10): logrus.Debug("Force shutdown plugin FAILED") @@ -172,7 +177,7 @@ func (pm *Manager) disable(p *v2.Plugin, c *controller) error { } c.restart = false - shutdownPlugin(p, c, pm.executor) + shutdownPlugin(p, c.exitChan, pm.executor) pm.config.Store.SetState(p, false) return pm.save(p) } @@ -191,7 +196,7 @@ func (pm *Manager) Shutdown() { } if pm.executor != nil && p.IsEnabled() { c.restart = false - shutdownPlugin(p, c, pm.executor) + shutdownPlugin(p, c.exitChan, pm.executor) } } if err := mount.RecursiveUnmount(pm.config.Root); err != nil { diff --git a/plugin/manager_linux_test.go b/plugin/manager_linux_test.go index d4199c80da719..fd8fa8523c23a 100644 --- a/plugin/manager_linux_test.go +++ b/plugin/manager_linux_test.go @@ -3,17 +3,19 @@ package plugin // import "github.com/docker/docker/plugin" import ( "io" "io/ioutil" + "net" "os" "path/filepath" "testing" "github.com/docker/docker/api/types" "github.com/docker/docker/pkg/mount" + "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/system" "github.com/docker/docker/plugin/v2" - "github.com/gotestyourself/gotestyourself/skip" "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" + "gotest.tools/skip" ) func TestManagerWithPluginMounts(t *testing.T) { @@ -59,7 +61,7 @@ func TestManagerWithPluginMounts(t *testing.T) { t.Fatal(err) } - if err := m.Remove(p1.Name(), &types.PluginRmConfig{ForceRemove: true}); err != nil { + if err := m.Remove(p1.GetID(), &types.PluginRmConfig{ForceRemove: true}); err != nil { t.Fatal(err) } if mounted, err := mount.Mounted(p2Mount); !mounted || err != nil { @@ -68,17 +70,18 @@ func TestManagerWithPluginMounts(t *testing.T) { } func newTestPlugin(t *testing.T, name, cap, root string) *v2.Plugin { - rootfs := filepath.Join(root, name) + id := stringid.GenerateNonCryptoID() + rootfs := filepath.Join(root, id) if err := os.MkdirAll(rootfs, 0755); err != nil { t.Fatal(err) } - p := v2.Plugin{PluginObj: types.Plugin{Name: name}} + p := v2.Plugin{PluginObj: types.Plugin{ID: id, Name: name}} p.Rootfs = rootfs iType := types.PluginInterfaceType{Capability: cap, Prefix: "docker", Version: "1.0"} - i := types.PluginConfigInterface{Socket: "plugins.sock", Types: []types.PluginInterfaceType{iType}} + i := types.PluginConfigInterface{Socket: "plugin.sock", Types: []types.PluginInterfaceType{iType}} p.PluginObj.Config.Interface = i - p.PluginObj.ID = name + p.PluginObj.ID = id return &p } @@ -90,8 +93,8 @@ func (e *simpleExecutor) Create(id string, spec specs.Spec, stdout, stderr io.Wr return errors.New("Create failed") } -func (e *simpleExecutor) Restore(id string, stdout, stderr io.WriteCloser) error { - return nil +func (e *simpleExecutor) Restore(id string, stdout, stderr io.WriteCloser) (bool, error) { + return false, nil } func (e *simpleExecutor) IsRunning(id string) (bool, error) { @@ -133,7 +136,144 @@ func TestCreateFailed(t *testing.T) { t.Fatalf("expected Create failed error, got %v", err) } - if err := m.Remove(p.Name(), &types.PluginRmConfig{ForceRemove: true}); err != nil { + if err := m.Remove(p.GetID(), &types.PluginRmConfig{ForceRemove: true}); err != nil { + t.Fatal(err) + } +} + +type executorWithRunning struct { + m *Manager + root string + exitChans map[string]chan struct{} +} + +func (e *executorWithRunning) Create(id string, spec specs.Spec, stdout, stderr io.WriteCloser) error { + sockAddr := filepath.Join(e.root, id, "plugin.sock") + ch := make(chan struct{}) + if e.exitChans == nil { + e.exitChans = make(map[string]chan struct{}) + } + e.exitChans[id] = ch + listenTestPlugin(sockAddr, ch) + return nil +} + +func (e *executorWithRunning) IsRunning(id string) (bool, error) { + return true, nil +} +func (e *executorWithRunning) Restore(id string, stdout, stderr io.WriteCloser) (bool, error) { + return true, nil +} + +func (e *executorWithRunning) Signal(id string, signal int) error { + ch := e.exitChans[id] + ch <- struct{}{} + <-ch + e.m.HandleExitEvent(id) + return nil +} + +func TestPluginAlreadyRunningOnStartup(t *testing.T) { + t.Parallel() + + root, err := ioutil.TempDir("", t.Name()) + if err != nil { t.Fatal(err) } + defer system.EnsureRemoveAll(root) + + for _, test := range []struct { + desc string + config ManagerConfig + }{ + { + desc: "live-restore-disabled", + config: ManagerConfig{ + LogPluginEvent: func(_, _, _ string) {}, + }, + }, + { + desc: "live-restore-enabled", + config: ManagerConfig{ + LogPluginEvent: func(_, _, _ string) {}, + LiveRestoreEnabled: true, + }, + }, + } { + t.Run(test.desc, func(t *testing.T) { + config := test.config + desc := test.desc + t.Parallel() + + p := newTestPlugin(t, desc, desc, config.Root) + p.PluginObj.Enabled = true + + // Need a short-ish path here so we don't run into unix socket path length issues. + config.ExecRoot, err = ioutil.TempDir("", "plugintest") + + executor := &executorWithRunning{root: config.ExecRoot} + config.CreateExecutor = func(m *Manager) (Executor, error) { executor.m = m; return executor, nil } + + if err := executor.Create(p.GetID(), specs.Spec{}, nil, nil); err != nil { + t.Fatal(err) + } + + root := filepath.Join(root, desc) + config.Root = filepath.Join(root, "manager") + if err := os.MkdirAll(filepath.Join(config.Root, p.GetID()), 0755); err != nil { + t.Fatal(err) + } + + if !p.IsEnabled() { + t.Fatal("plugin should be enabled") + } + if err := (&Manager{config: config}).save(p); err != nil { + t.Fatal(err) + } + + s := NewStore() + config.Store = s + if err != nil { + t.Fatal(err) + } + defer system.EnsureRemoveAll(config.ExecRoot) + + m, err := NewManager(config) + if err != nil { + t.Fatal(err) + } + defer m.Shutdown() + + p = s.GetAll()[p.GetID()] // refresh `p` with what the manager knows + if p.Client() == nil { + t.Fatal("plugin client should not be nil") + } + }) + } +} + +func listenTestPlugin(sockAddr string, exit chan struct{}) (net.Listener, error) { + if err := os.MkdirAll(filepath.Dir(sockAddr), 0755); err != nil { + return nil, err + } + l, err := net.Listen("unix", sockAddr) + if err != nil { + return nil, err + } + go func() { + for { + conn, err := l.Accept() + if err != nil { + return + } + conn.Close() + } + }() + go func() { + <-exit + l.Close() + os.Remove(sockAddr) + exit <- struct{}{} + }() + return l, nil } diff --git a/plugin/manager_windows.go b/plugin/manager_windows.go index 9fafea5c2249a..90cc52c99253c 100644 --- a/plugin/manager_windows.go +++ b/plugin/manager_windows.go @@ -19,7 +19,7 @@ func (pm *Manager) disable(p *v2.Plugin, c *controller) error { return fmt.Errorf("Not implemented") } -func (pm *Manager) restore(p *v2.Plugin) error { +func (pm *Manager) restore(p *v2.Plugin, c *controller) error { return fmt.Errorf("Not implemented") } diff --git a/profiles/seccomp/default.json b/profiles/seccomp/default.json index 5717c00cdef96..c0f140524e55d 100755 --- a/profiles/seccomp/default.json +++ b/profiles/seccomp/default.json @@ -746,6 +746,22 @@ ] }, "excludes": {} + }, + { + "names": [ + "get_mempolicy", + "mbind", + "set_mempolicy" + ], + "action": "SCMP_ACT_ALLOW", + "args": [], + "comment": "", + "includes": { + "caps": [ + "CAP_SYS_NICE" + ] + }, + "excludes": {} } ] } \ No newline at end of file diff --git a/profiles/seccomp/seccomp_default.go b/profiles/seccomp/seccomp_default.go index be29aa4f70a4c..25360a1277d62 100644 --- a/profiles/seccomp/seccomp_default.go +++ b/profiles/seccomp/seccomp_default.go @@ -630,6 +630,18 @@ func DefaultProfile() *types.Seccomp { Caps: []string{"CAP_SYS_TTY_CONFIG"}, }, }, + { + Names: []string{ + "get_mempolicy", + "mbind", + "set_mempolicy", + }, + Action: types.ActAllow, + Args: []*types.Arg{}, + Includes: types.Filter{ + Caps: []string{"CAP_SYS_NICE"}, + }, + }, } return &types.Seccomp{ diff --git a/reference/store_test.go b/reference/store_test.go index 71f1d96e59e32..1ce674cbfbbd6 100644 --- a/reference/store_test.go +++ b/reference/store_test.go @@ -9,9 +9,9 @@ import ( "testing" "github.com/docker/distribution/reference" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/opencontainers/go-digest" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) var ( diff --git a/registry/config_test.go b/registry/config_test.go index 2f9c9548eaf0b..30a257e325563 100644 --- a/registry/config_test.go +++ b/registry/config_test.go @@ -6,8 +6,8 @@ import ( "strings" "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestLoadAllowNondistributableArtifacts(t *testing.T) { diff --git a/registry/registry_test.go b/registry/registry_test.go index ec112523949c4..b7459471b3f64 100644 --- a/registry/registry_test.go +++ b/registry/registry_test.go @@ -13,8 +13,8 @@ import ( "github.com/docker/distribution/registry/client/transport" "github.com/docker/docker/api/types" registrytypes "github.com/docker/docker/api/types/registry" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/assert" + "gotest.tools/skip" ) var ( diff --git a/registry/resumable/resumablerequestreader_test.go b/registry/resumable/resumablerequestreader_test.go index bd3d55885fb00..c72c210e776e2 100644 --- a/registry/resumable/resumablerequestreader_test.go +++ b/registry/resumable/resumablerequestreader_test.go @@ -10,8 +10,8 @@ import ( "testing" "time" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestResumableRequestHeaderSimpleErrors(t *testing.T) { diff --git a/registry/service_v1_test.go b/registry/service_v1_test.go index 590a653c3666b..11861f7c05296 100644 --- a/registry/service_v1_test.go +++ b/registry/service_v1_test.go @@ -4,7 +4,7 @@ import ( "os" "testing" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/skip" ) func TestLookupV1Endpoints(t *testing.T) { diff --git a/runconfig/config_test.go b/runconfig/config_test.go index 58e3a9f788474..67d386969f4da 100644 --- a/runconfig/config_test.go +++ b/runconfig/config_test.go @@ -12,8 +12,8 @@ import ( "github.com/docker/docker/api/types/container" networktypes "github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/strslice" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) type f struct { diff --git a/runconfig/hostconfig_test.go b/runconfig/hostconfig_test.go index d2482fbe7b87c..b04cbc6bc3e65 100644 --- a/runconfig/hostconfig_test.go +++ b/runconfig/hostconfig_test.go @@ -10,8 +10,8 @@ import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/pkg/sysinfo" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) // TODO Windows: This will need addressing for a Windows daemon. diff --git a/vendor.conf b/vendor.conf index 858182b841b2e..ee7123c4c747f 100644 --- a/vendor.conf +++ b/vendor.conf @@ -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/hcsshim v0.6.12 +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 @@ -18,31 +18,33 @@ golang.org/x/sys 37707fdb30a5b38865cfb95e5aab41707daec7fd github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1 github.com/docker/go-connections 7beb39f0b969b075d1325fecb092faf27fd357b6 golang.org/x/text f72d8390a633d5dfb0cc84043294db9f6c935756 -github.com/pmezard/go-difflib v1.0.0 -github.com/gotestyourself/gotestyourself cf3a5ab914a2efa8bc838d09f5918c1d44d029 +gotest.tools v2.1.0 github.com/google/go-cmp v0.2.0 github.com/RackSec/srslog 456df3a81436d29ba874f3590eeeee25d666f8a5 -github.com/imdario/mergo 0.2.1 +github.com/imdario/mergo v0.3.5 golang.org/x/sync fd80eb99c8f653c847d294a001bdf2a3a6f768f5 # buildkit -github.com/moby/buildkit 43e758232a0ac7d50c6a11413186e16684fc1e4f -github.com/tonistiigi/fsutil dc68c74458923f357474a9178bd198aa3ed11a5f +github.com/moby/buildkit 98f1604134f945d48538ffca0e18662337b4a850 +github.com/tonistiigi/fsutil 8abad97ee3969cdf5e9c367f46adba2c212b3ddb github.com/grpc-ecosystem/grpc-opentracing 8e809c8a86450a29b90dcc9efbf062d0fe6d9746 github.com/opentracing/opentracing-go 1361b9cd60be79c4c3a7fa9841b3c132e40066a7 +github.com/google/shlex 6f45313302b9c56850fc17f99e40caebce98c716 +github.com/opentracing-contrib/go-stdlib b1a47cfbdd7543e70e9ef3e73d0802ad306cc1cc +github.com/mitchellh/hashstructure 2bca23e0e452137f789efbc8610126fd8b94f73b #get libnetwork packages # When updating, also update LIBNETWORK_COMMIT in hack/dockerfile/install/proxy accordingly -github.com/docker/libnetwork 3931ba4d815e385ab97093c64477b82f14dadefb +github.com/docker/libnetwork d00ceed44cc447c77f25cdf5d59e83163bdcb4c9 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 @@ -72,9 +74,9 @@ github.com/pborman/uuid v1.0 google.golang.org/grpc v1.12.0 -# When updating, also update RUNC_COMMIT in hack/dockerfile/install/runc accordingly -github.com/opencontainers/runc 69663f0bd4b60df09991c08812a60108003fa340 -github.com/opencontainers/runtime-spec v1.0.1 +# This does not need to match RUNC_COMMIT as it is used for helper packages but should be newer or equal +github.com/opencontainers/runc ad0f5255060d36872be04de22f8731f38ef2d7b1 +github.com/opencontainers/runtime-spec d810dbc60d8c5aeeb3d054bd1132fab2121968ce github.com/opencontainers/image-spec v1.0.1 github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0 @@ -93,7 +95,7 @@ github.com/philhofer/fwd 98c11a7a6ec829d672b03833c3d69a7fae1ca972 github.com/tinylib/msgp 3b556c64540842d4f82967be066a7f7fffc3adad # fsnotify -github.com/fsnotify/fsnotify 4da3e2cfbabc9f751898f250b49f2439785783a1 +github.com/fsnotify/fsnotify v1.4.7 # awslogs deps github.com/aws/aws-sdk-go v1.12.66 @@ -112,26 +114,26 @@ github.com/googleapis/gax-go v2.0.0 google.golang.org/genproto 694d95ba50e67b2e363f3483057db5d4910c18f9 # containerd -github.com/containerd/containerd c7083eed5d8633d54c25fe81aa609010a4f2e495 +github.com/containerd/containerd b41633746ed4833f52c3c071e8edcfa2713e5677 github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c -github.com/containerd/continuity d3c23511c1bf5851696cba83143d9cbcd666869b +github.com/containerd/continuity 0377f7d767206f3a9e8881d0f02267b0d89c7a62 github.com/containerd/cgroups fe281dd265766145e943a034aa41086474ea6130 -github.com/containerd/console cb7008ab3d8359b78c5f464cb7cf160107ad5925 +github.com/containerd/console 5d1b48d6114b8c9666f0c8b916f871af97b0a761 github.com/containerd/go-runc f271fa2021de855d4d918dbef83c5fe19db1bdd -github.com/containerd/typeurl f6943554a7e7e88b3c14aad190bf05932da84788 -github.com/stevvooe/ttrpc d4528379866b0ce7e9d71f3eb96f0582fc374577 +github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40 +github.com/containerd/ttrpc 94dde388801693c54f88a6596f713b51a8b30b2d github.com/gogo/googleapis 08a7655d27152912db7aaf4f983275eaf8d128ef # cluster -github.com/docker/swarmkit edd5641391926a50bc5f7040e20b7efc05003c26 +github.com/docker/swarmkit 68266392a176434d282760d2d6d0ab4c68edcae6 github.com/gogo/protobuf v1.0.0 -github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a +github.com/cloudflare/cfssl 1.3.2 github.com/fernet/fernet-go 1b2437bc582b3cfbb341ee5a29f8ef5b42912ff2 -github.com/google/certificate-transparency d90e65c3a07988180c5b1ece71791c0b6506826e +github.com/google/certificate-transparency-go v1.0.20 golang.org/x/crypto 1a580b3eff7814fc9b40602fd35256c63b50f491 golang.org/x/time a4bde12657593d5e90d0533a3e4fd95e635124cb github.com/hashicorp/go-memdb cb9a474f84cc5e41b273b20c6927680b2a8776ad -github.com/hashicorp/go-immutable-radix 8e8ed81f8f0bf1bdd829593fdd5c29922c1ea990 +github.com/hashicorp/go-immutable-radix 826af9ccf0feeee615d546d69b11f8e98da8c8f1 git://github.com/tonistiigi/go-immutable-radix.git github.com/hashicorp/golang-lru a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4 github.com/coreos/pkg fa29b1d70f0beaddd4c7021607cc3c3be8ce94b8 github.com/pivotal-golang/clock 3fd3c1944c59d9742e1cd333672181cd1a6f9fa0 diff --git a/vendor/github.com/Microsoft/go-winio/pipe.go b/vendor/github.com/Microsoft/go-winio/pipe.go index 82cbe7af45e65..806fd34252539 100644 --- a/vendor/github.com/Microsoft/go-winio/pipe.go +++ b/vendor/github.com/Microsoft/go-winio/pipe.go @@ -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) diff --git a/vendor/github.com/Microsoft/hcsshim/interface.go b/vendor/github.com/Microsoft/hcsshim/interface.go index e21f30025aebb..b8fa736447bf4 100644 --- a/vendor/github.com/Microsoft/hcsshim/interface.go +++ b/vendor/github.com/Microsoft/hcsshim/interface.go @@ -36,6 +36,8 @@ type MappedDir struct { BandwidthMaximum uint64 IOPSMaximum uint64 CreateInUtilityVM bool + // LinuxMetadata - Support added in 1803/RS4+. + LinuxMetadata bool `json:",omitempty"` } type MappedPipe struct { @@ -62,6 +64,14 @@ type MappedVirtualDisk struct { AttachOnly bool `json:",omitempty:` } +// AssignedDevice represents a device that has been directly assigned to a container +// +// NOTE: Support added in RS5 +type AssignedDevice struct { + // InterfaceClassGUID of the device to assign to container. + InterfaceClassGUID string `json:"InterfaceClassGuid,omitempty"` +} + // ContainerConfig is used as both the input of CreateContainer // and to convert the parameters to JSON for passing onto the HCS type ContainerConfig struct { @@ -93,6 +103,7 @@ type ContainerConfig struct { ContainerType string `json:",omitempty"` // "Linux" for Linux containers on Windows. Omitted otherwise. TerminateOnLastHandleClosed bool `json:",omitempty"` // Should HCS terminate the container once all handles have been closed MappedVirtualDisks []MappedVirtualDisk `json:",omitempty"` // Array of virtual disks to mount at start + AssignedDevices []AssignedDevice `json:",omitempty"` // Array of devices to assign. NOTE: Support added in RS5 } type ComputeSystemQuery struct { diff --git a/vendor/github.com/cloudflare/cfssl/README.md b/vendor/github.com/cloudflare/cfssl/README.md index 7285eee896ffd..a7f34698c6b53 100644 --- a/vendor/github.com/cloudflare/cfssl/README.md +++ b/vendor/github.com/cloudflare/cfssl/README.md @@ -8,7 +8,7 @@ CFSSL is CloudFlare's PKI/TLS swiss army knife. It is both a command line tool and an HTTP API server for signing, verifying, and bundling TLS -certificates. It requires Go 1.5+ to build. +certificates. It requires Go 1.8+ to build. Note that certain linux distributions have certain algorithms removed (RHEL-based distributions in particular), so the golang from the @@ -34,7 +34,7 @@ See [BUILDING](BUILDING.md) ### Installation Installation requires a -[working Go 1.5+ installation](http://golang.org/doc/install) and a +[working Go 1.8+ installation](http://golang.org/doc/install) and a properly set `GOPATH`. ``` @@ -42,33 +42,28 @@ $ go get -u github.com/cloudflare/cfssl/cmd/cfssl ``` will download and build the CFSSL tool, installing it in -`$GOPATH/bin/cfssl`. To install the other utility programs that are in -this repo: +`$GOPATH/bin/cfssl`. -``` -$ go get -u github.com/cloudflare/cfssl/cmd/... -``` - -This will download, build, and install `cfssl`, `cfssljson`, and -`mkbundle` into `$GOPATH/bin/`. - -Note that CFSSL makes use of vendored packages; in Go 1.5, the -`GO15VENDOREXPERIMENT` environment variable will need to be set, e.g. +To install any of the other utility programs that are +in this repo (for instance `cffsljson` in this case): ``` -export GO15VENDOREXPERIMENT=1 +$ go get -u github.com/cloudflare/cfssl/cmd/cfssljson ``` -In Go 1.6, this works out of the box. +This will download and build the CFSSLJSON tool, installing it in +`$GOPATH/bin/`. -#### Installing pre-Go 1.5 -With a Go 1.4 or earlier installation, you won't be able to install the latest version of CFSSL. However, you can checkout the `1.1.0` release and build that. +And to simply install __all__ of the programs in this repo: ``` -git clone -b 1.1.0 https://github.com/cloudflare/cfssl.git $GOPATH/src/github.com/cloudflare/cfssl -go get github.com/cloudflare/cfssl/cmd/cfssl +$ go get -u github.com/cloudflare/cfssl/cmd/... ``` +This will download, build, and install all of the utility programs +(including `cfssl`, `cfssljson`, and `mkbundle` among others) into the +`$GOPATH/bin/` directory. + ### Using the Command Line Tool The `cfssl` command line tool takes a command to specify what @@ -81,10 +76,10 @@ operation it should carry out: serve start the API server version prints out the current version selfsign generates a self-signed certificate - print-defaults print default configurations + print-defaults print default configurations -Use "cfssl [command] -help" to find out more about a command. -The version command takes no arguments. +Use `cfssl [command] -help` to find out more about a command. +The `version` command takes no arguments. #### Signing @@ -92,9 +87,9 @@ The version command takes no arguments. cfssl sign [-ca cert] [-ca-key key] [-hostname comma,separated,hostnames] csr [subject] ``` -The csr is the client's certificate request. The `-ca` and `-ca-key` +The `csr` is the client's certificate request. The `-ca` and `-ca-key` flags are the CA's certificate and private key, respectively. By -default, they are "ca.pem" and "ca_key.pem". The `-hostname` is +default, they are `ca.pem` and `ca_key.pem`. The `-hostname` is a comma separated hostname list that overrides the DNS names and IP address in the certificate SAN extension. For example, assuming the CA's private key is in @@ -103,26 +98,27 @@ For example, assuming the CA's private key is in for cloudflare.com: ``` -cfssl sign -ca /etc/ssl/certs/cfssl.pem \ +cfssl sign -ca /etc/ssl/certs/cfssl.pem \ -ca-key /etc/ssl/private/cfssl_key.pem \ - -hostname cloudflare.com ./cloudflare.pem + -hostname cloudflare.com \ + ./cloudflare.pem ``` -It is also possible to specify csr through '-csr' flag. By doing so, +It is also possible to specify CSR with the `-csr` flag. By doing so, flag values take precedence and will overwrite the argument. The subject is an optional file that contains subject information that should be used in place of the information from the CSR. It should be -a JSON file with the type: +a JSON file as follows: ```json { "CN": "example.com", "names": [ { - "C": "US", - "L": "San Francisco", - "O": "Internet Widgets, Inc.", + "C": "US", + "L": "San Francisco", + "O": "Internet Widgets, Inc.", "OU": "WWW", "ST": "California" } @@ -130,6 +126,9 @@ a JSON file with the type: } ``` +**N.B.** As of Go 1.7, self-signed certificates will not include +[the AKI](https://go.googlesource.com/go/+/b623b71509b2d24df915d5bc68602e1c6edf38ca). + #### Bundling ``` @@ -139,28 +138,29 @@ cfssl bundle [-ca-bundle bundle] [-int-bundle bundle] \ ``` The bundles are used for the root and intermediate certificate -pools. In addition, platform metadata is specified through '-metadata' +pools. In addition, platform metadata is specified through `-metadata`. The bundle files, metadata file (and auxiliary files) can be -found at [cfssl_trust](https://github.com/cloudflare/cfssl_trust) +found at: + https://github.com/cloudflare/cfssl_trust -Specify PEM-encoded client certificate and key through '-cert' and -'-key' respectively. If key is specified, the bundle will be built +Specify PEM-encoded client certificate and key through `-cert` and +`-key` respectively. If key is specified, the bundle will be built and verified with the key. Otherwise the bundle will be built -without a private key. Instead of file path, use '-' for reading -certificate PEM from stdin. It is also acceptable the certificate -file contains a (partial) certificate bundle. +without a private key. Instead of file path, use `-` for reading +certificate PEM from stdin. It is also acceptable that the certificate +file should contain a (partial) certificate bundle. -Specify bundling flavor through '-flavor'. There are three flavors: -'optimal' to generate a bundle of shortest chain and most advanced -cryptographic algorithms, 'ubiquitous' to generate a bundle of most +Specify bundling flavor through `-flavor`. There are three flavors: +`optimal` to generate a bundle of shortest chain and most advanced +cryptographic algorithms, `ubiquitous` to generate a bundle of most widely acceptance across different browsers and OS platforms, and -'force' to find an acceptable bundle which is identical to the +`force` to find an acceptable bundle which is identical to the content of the input certificate file. Alternatively, the client certificate can be pulled directly from a domain. It is also possible to connect to the remote address -through '-ip'. +through `-ip`. ``` cfssl bundle [-ca-bundle bundle] [-int-bundle bundle] \ @@ -168,7 +168,7 @@ cfssl bundle [-ca-bundle bundle] [-int-bundle bundle] \ -domain domain_name [-ip ip_address] ``` -The bundle output form should follow the example +The bundle output form should follow the example: ```json { @@ -204,7 +204,7 @@ cfssl genkey csr.json ``` To generate a private key and corresponding certificate request, specify -the key request as a JSON file. This file should follow the form +the key request as a JSON file. This file should follow the form: ```json { @@ -218,9 +218,9 @@ the key request as a JSON file. This file should follow the form }, "names": [ { - "C": "US", - "L": "San Francisco", - "O": "Internet Widgets, Inc.", + "C": "US", + "L": "San Francisco", + "O": "Internet Widgets, Inc.", "OU": "WWW", "ST": "California" } @@ -235,7 +235,7 @@ cfssl genkey -initca csr.json | cfssljson -bare ca ``` To generate a self-signed root CA certificate, specify the key request as -the JSON file in the same format as in 'genkey'. Three PEM-encoded entities +a JSON file in the same format as in 'genkey'. Three PEM-encoded entities will appear in the output: the private key, the csr, and the self-signed certificate. @@ -245,8 +245,8 @@ certificate. cfssl gencert -remote=remote_server [-hostname=comma,separated,hostnames] csr.json ``` -This is calls genkey, but has a remote CFSSL server sign and issue -a certificate. You may use `-hostname` to override certificate SANs. +This calls `genkey` but has a remote CFSSL server sign and issue +the certificate. You may use `-hostname` to override certificate SANs. #### Generating a local-issued certificate and private key. @@ -254,25 +254,25 @@ a certificate. You may use `-hostname` to override certificate SANs. cfssl gencert -ca cert -ca-key key [-hostname=comma,separated,hostnames] csr.json ``` -This is generates and issues a certificate and private key from a local CA +This generates and issues a certificate and private key from a local CA via a JSON request. You may use `-hostname` to override certificate SANs. -#### Updating a OCSP responses file with a newly issued certificate +#### Updating an OCSP responses file with a newly issued certificate ``` cfssl ocspsign -ca cert -responder key -responder-key key -cert cert \ | cfssljson -bare -stdout >> responses ``` -This will generate a OCSP response for the `cert` and add it to the -`responses` file. You can then pass `responses` to `ocspserve` to start a +This will generate an OCSP response for the `cert` and add it to the +`responses` file. You can then pass `responses` to `ocspserve` to start an OCSP server. ### Starting the API Server CFSSL comes with an HTTP-based API server; the endpoints are -documented in `doc/api.txt`. The server is started with the "serve" +documented in `doc/api/intro.txt`. The server is started with the `serve` command: ``` @@ -284,18 +284,19 @@ cfssl serve [-address address] [-ca cert] [-ca-bundle bundle] \ Address and port default to "127.0.0.1:8888". The `-ca` and `-ca-key` arguments should be the PEM-encoded certificate and private key to use -for signing; by default, they are "ca.pem" and "ca_key.pem". The +for signing; by default, they are `ca.pem` and `ca_key.pem`. The `-ca-bundle` and `-int-bundle` should be the certificate bundles used for the root and intermediate certificate pools, respectively. These -default to "ca-bundle.crt" and "int-bundle." If the "remote" option is -provided, all signature operations will be forwarded to the remote CFSSL. +default to `ca-bundle.crt` and `int-bundle.crt` respectively. If the +`-remote` option is specified, all signature operations will be forwarded +to the remote CFSSL. -'-int-dir' specifies intermediates directory. '-metadata' is a file for +`-int-dir` specifies an intermediates directory. `-metadata` is a file for root certificate presence. The content of the file is a json dictionary -(k,v): each key k is SHA-1 digest of a root certificate while value v -is a list of key store filenames. '-config' specifies path to configuration -file. '-responder' and '-responder-key' are Certificate for OCSP responder -and private key for OCSP responder certificate, respectively. +(k,v) such that each key k is an SHA-1 digest of a root certificate while value v +is a list of key store filenames. `-config` specifies a path to a configuration +file. `-responder` and `-responder-key` are the certificate and the +private key for the OCSP responder, respectively. The amount of logging can be controlled with the `-loglevel` option. This comes *after* the serve command: @@ -306,18 +307,18 @@ cfssl serve -loglevel 2 The levels are: -* 0. DEBUG -* 1. INFO (this is the default level) -* 2. WARNING -* 3. ERROR -* 4. CRITICAL +* 0 - DEBUG +* 1 - INFO (this is the default level) +* 2 - WARNING +* 3 - ERROR +* 4 - CRITICAL ### The multirootca The `cfssl` program can act as an online certificate authority, but it only uses a single key. If multiple signing keys are needed, the -`multirootca` program can be used. It only provides the sign, -authsign, and info endpoints. The documentation contains instructions +`multirootca` program can be used. It only provides the `sign`, +`authsign` and `info` endpoints. The documentation contains instructions for configuring and running the CA. ### The mkbundle Utility @@ -334,49 +335,44 @@ support is planned for the next release) and expired certificates, and bundles them into one file. It takes directories of certificates and certificate files (which may contain multiple certificates). For example, if the directory `intermediates` contains a number of intermediate -certificates, +certificates: ``` mkbundle -f int-bundle.crt intermediates ``` -will check those certificates and combine valid ones into a single +will check those certificates and combine valid certificates into a single `int-bundle.crt` file. The `-f` flag specifies an output name; `-loglevel` specifies the verbosity -of the logging (using the same loglevels above), and `-nw` controls the +of the logging (using the same loglevels as above), and `-nw` controls the number of revocation-checking workers. ### The cfssljson Utility -Most of the output from `cfssl` is in JSON. The `cfssljson` will take -this output and split it out into separate key, certificate, CSR, and -bundle files as appropriate. The tool takes a single flag, `-f`, that +Most of the output from `cfssl` is in JSON. The `cfssljson` utility can take +this output and split it out into separate `key`, `certificate`, `CSR`, and +`bundle` files as appropriate. The tool takes a single flag, `-f`, that specifies the input file, and an argument that specifies the base name for -the files produced. If the input filename is "-" (which is the default), -`cfssljson` reads from standard input. It maps keys in the JSON file to +the files produced. If the input filename is `-` (which is the default), +cfssljson reads from standard input. It maps keys in the JSON file to filenames in the following way: -* if there is a "cert" (or if not, if there's a "certificate") field, the - file "basename.pem" will be produced. -* if there is a "key" (or if not, if there's a "private_key") field, the - file "basename-key.pem" will be produced. -* if there is a "csr" (or if not, if there's a "certificate_request") field, - the file "basename.csr" will be produced. -* if there is a "bundle" field, the file "basename-bundle.pem" will - be produced. -* if there is a "ocspResponse" field, the file "basename-response.der" will - be produced. +* if __cert__ or __certificate__ is specified, __basename.pem__ will be produced. +* if __key__ or __private_key__ is specified, __basename-key.pem__ will be produced. +* if __csr__ or __certificate_request__ is specified, __basename.csr__ will be produced. +* if __bundle__ is specified, __basename-bundle.pem__ will be produced. +* if __ocspResponse__ is specified, __basename-response.der__ will be produced. Instead of saving to a file, you can pass `-stdout` to output the encoded -contents. +contents to standard output. ### Static Builds By default, the web assets are accessed from disk, based on their -relative locations. If you’re wishing to distribute a single, -statically-linked, cfssl binary, you’ll want to embed these resources -before building. This can by done with the +relative locations. If you wish to distribute a single, +statically-linked, `cfssl` binary, you’ll want to embed these resources +before building. This can by done with the [go.rice](https://github.com/GeertJohan/go.rice) tool. ``` @@ -387,16 +383,18 @@ Then building with `go build` will use the embedded resources. ### Using a PKCS#11 hardware token / HSM -For better security, you may want to store your private key in an HSM or +For better security, you may wish to store your private key in an HSM or smartcard. The interface to both of these categories of device is described by the PKCS#11 spec. If you need to do approximately one signing operation per second or fewer, the Yubikey NEO and NEO-n are inexpensive smartcard options: -https://www.yubico.com/products/yubikey-hardware/yubikey-neo/. In general you -are looking for a product that supports PIV (personal identity verification). If + + https://www.yubico.com/products/yubikey-hardware/yubikey-neo/ + +In general you should look for a product that supports PIV (personal identity verification). If your signing needs are in the hundreds of signatures per second, you will need to purchase an expensive HSM (in the thousands to many thousands of USD). -If you want to try out the PKCS#11 signing modes without a hardware token, you +If you wish to try out the PKCS#11 signing modes without a hardware token, you can use the [SoftHSM](https://github.com/opendnssec/SoftHSMv1#softhsm) implementation. Please note that using SoftHSM simply stores your private key in a file on disk and does not increase security. @@ -404,14 +402,14 @@ a file on disk and does not increase security. To get started with your PKCS#11 token you will need to initialize it with a private key, PIN, and token label. The instructions to do this will be specific to each hardware device, and you should follow the instructions provided by your -vendor. You will also need to find the path to your 'module', a shared object +vendor. You will also need to find the path to your `module`, a shared object file (.so). Having initialized your device, you can query it to check your token label with: pkcs11-tool --module --list-token-slots You'll also want to check the label of the private key you imported (or -generated). Run the following command and look for a 'Private Key Object': +generated). Run the following command and look for a `Private Key Object`: pkcs11-tool --module --pin \ --list-token-slots --login --list-objects @@ -421,7 +419,7 @@ CFSSL supports PKCS#11 for certificate signing and OCSP signing. To create a Signer (for certificate signing), import `signer/universal` and call NewSigner with a Root object containing the module, pin, token label and private label from above, plus a path to your certificate. The structure of the Root object is -documented in universal.go. +documented in `universal.go`. Alternately, you can construct a pkcs11key.Key or pkcs11key.Pool yourself, and pass it to ocsp.NewSigner (for OCSP) or local.NewSigner (for certificate @@ -431,8 +429,8 @@ same time. ### Additional Documentation -Additional documentation can be found in the "doc/" directory: +Additional documentation can be found in the "doc" directory: -* `api.txt`: documents the API endpoints +* `api/intro.txt`: documents the API endpoints * `bootstrap.txt`: a walkthrough from building the package to getting up and running diff --git a/vendor/github.com/cloudflare/cfssl/api/api.go b/vendor/github.com/cloudflare/cfssl/api/api.go index f1040caded156..98b0ec462e185 100644 --- a/vendor/github.com/cloudflare/cfssl/api/api.go +++ b/vendor/github.com/cloudflare/cfssl/api/api.go @@ -34,8 +34,8 @@ func (f HandlerFunc) Handle(w http.ResponseWriter, r *http.Request) error { return f(w, r) } -// handleError is the centralised error handling and reporting. -func handleError(w http.ResponseWriter, err error) (code int) { +// HandleError is the centralised error handling and reporting. +func HandleError(w http.ResponseWriter, err error) (code int) { if err == nil { return http.StatusOK } @@ -82,7 +82,7 @@ func (h HTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } else { err = errors.NewMethodNotAllowed(r.Method) } - status := handleError(w, err) + status := HandleError(w, err) log.Infof("%s - \"%s %s\" %d", r.RemoteAddr, r.Method, r.URL, status) } diff --git a/vendor/github.com/cloudflare/cfssl/certdb/certdb.go b/vendor/github.com/cloudflare/cfssl/certdb/certdb.go index 96694f7685960..dc8c856c3b99f 100644 --- a/vendor/github.com/cloudflare/cfssl/certdb/certdb.go +++ b/vendor/github.com/cloudflare/cfssl/certdb/certdb.go @@ -31,6 +31,8 @@ type Accessor interface { InsertCertificate(cr CertificateRecord) error GetCertificate(serial, aki string) ([]CertificateRecord, error) GetUnexpiredCertificates() ([]CertificateRecord, error) + GetRevokedAndUnexpiredCertificates() ([]CertificateRecord, error) + GetRevokedAndUnexpiredCertificatesByLabel(label string) ([]CertificateRecord, error) RevokeCertificate(serial, aki string, reasonCode int) error InsertOCSP(rr OCSPRecord) error GetOCSP(serial, aki string) ([]OCSPRecord, error) diff --git a/vendor/github.com/cloudflare/cfssl/config/config.go b/vendor/github.com/cloudflare/cfssl/config/config.go index d5eb7c3760f7e..b04ed40ed5ffa 100644 --- a/vendor/github.com/cloudflare/cfssl/config/config.go +++ b/vendor/github.com/cloudflare/cfssl/config/config.go @@ -551,16 +551,16 @@ func (p *Signing) Valid() bool { // KeyUsage contains a mapping of string names to key usages. var KeyUsage = map[string]x509.KeyUsage{ - "signing": x509.KeyUsageDigitalSignature, - "digital signature": x509.KeyUsageDigitalSignature, - "content committment": x509.KeyUsageContentCommitment, - "key encipherment": x509.KeyUsageKeyEncipherment, - "key agreement": x509.KeyUsageKeyAgreement, - "data encipherment": x509.KeyUsageDataEncipherment, - "cert sign": x509.KeyUsageCertSign, - "crl sign": x509.KeyUsageCRLSign, - "encipher only": x509.KeyUsageEncipherOnly, - "decipher only": x509.KeyUsageDecipherOnly, + "signing": x509.KeyUsageDigitalSignature, + "digital signature": x509.KeyUsageDigitalSignature, + "content commitment": x509.KeyUsageContentCommitment, + "key encipherment": x509.KeyUsageKeyEncipherment, + "key agreement": x509.KeyUsageKeyAgreement, + "data encipherment": x509.KeyUsageDataEncipherment, + "cert sign": x509.KeyUsageCertSign, + "crl sign": x509.KeyUsageCRLSign, + "encipher only": x509.KeyUsageEncipherOnly, + "decipher only": x509.KeyUsageDecipherOnly, } // ExtKeyUsage contains a mapping of string names to extended key diff --git a/vendor/github.com/cloudflare/cfssl/crypto/pkcs7/pkcs7.go b/vendor/github.com/cloudflare/cfssl/crypto/pkcs7/pkcs7.go index 8db547fce5975..d57daf51b5e50 100644 --- a/vendor/github.com/cloudflare/cfssl/crypto/pkcs7/pkcs7.go +++ b/vendor/github.com/cloudflare/cfssl/crypto/pkcs7/pkcs7.go @@ -1,7 +1,7 @@ // Package pkcs7 implements the subset of the CMS PKCS #7 datatype that is typically // used to package certificates and CRLs. Using openssl, every certificate converted // to PKCS #7 format from another encoding such as PEM conforms to this implementation. -// reference: https://www.openssl.org/docs/apps/crl2pkcs7.html +// reference: https://www.openssl.org/docs/man1.1.0/apps/crl2pkcs7.html // // PKCS #7 Data type, reference: https://tools.ietf.org/html/rfc2315 // diff --git a/vendor/github.com/cloudflare/cfssl/csr/csr.go b/vendor/github.com/cloudflare/cfssl/csr/csr.go index 4329b79564d3f..c4ccea60b0902 100644 --- a/vendor/github.com/cloudflare/cfssl/csr/csr.go +++ b/vendor/github.com/cloudflare/cfssl/csr/csr.go @@ -47,8 +47,8 @@ type KeyRequest interface { // A BasicKeyRequest contains the algorithm and key size for a new private key. type BasicKeyRequest struct { - A string `json:"algo"` - S int `json:"size"` + A string `json:"algo" yaml:"algo"` + S int `json:"size" yaml:"size"` } // NewBasicKeyRequest returns a default BasicKeyRequest. @@ -130,20 +130,21 @@ func (kr *BasicKeyRequest) SigAlgo() x509.SignatureAlgorithm { // CAConfig is a section used in the requests initialising a new CA. type CAConfig struct { - PathLength int `json:"pathlen"` - PathLenZero bool `json:"pathlenzero"` - Expiry string `json:"expiry"` + PathLength int `json:"pathlen" yaml:"pathlen"` + PathLenZero bool `json:"pathlenzero" yaml:"pathlenzero"` + Expiry string `json:"expiry" yaml:"expiry"` + Backdate string `json:"backdate" yaml:"backdate"` } // A CertificateRequest encapsulates the API interface to the // certificate request functionality. type CertificateRequest struct { CN string - Names []Name `json:"names"` - Hosts []string `json:"hosts"` - KeyRequest KeyRequest `json:"key,omitempty"` - CA *CAConfig `json:"ca,omitempty"` - SerialNumber string `json:"serialnumber,omitempty"` + Names []Name `json:"names" yaml:"names"` + Hosts []string `json:"hosts" yaml:"hosts"` + KeyRequest KeyRequest `json:"key,omitempty" yaml:"key,omitempty"` + CA *CAConfig `json:"ca,omitempty" yaml:"ca,omitempty"` + SerialNumber string `json:"serialnumber,omitempty" yaml:"serialnumber,omitempty"` } // New returns a new, empty CertificateRequest with a @@ -327,7 +328,7 @@ func (g *Generator) ProcessRequest(req *CertificateRequest) (csr, key []byte, er err = g.Validator(req) if err != nil { log.Warningf("invalid request: %v", err) - return + return nil, nil, err } csr, key, err = ParseRequest(req) diff --git a/vendor/github.com/cloudflare/cfssl/errors/error.go b/vendor/github.com/cloudflare/cfssl/errors/error.go index 9913a84e1088c..9715a7cfbd252 100644 --- a/vendor/github.com/cloudflare/cfssl/errors/error.go +++ b/vendor/github.com/cloudflare/cfssl/errors/error.go @@ -191,6 +191,16 @@ const ( // PrecertSubmissionFailed occurs when submitting a precertificate to // a log server fails PrecertSubmissionFailed = 100 * (iota + 1) + // CTClientConstructionFailed occurs when the construction of a new + // github.com/google/certificate-transparency client fails. + CTClientConstructionFailed + // PrecertMissingPoison occurs when a precert is passed to SignFromPrecert + // and is missing the CT poison extension. + PrecertMissingPoison + // PrecertInvalidPoison occurs when a precert is passed to SignFromPrecert + // and has a invalid CT poison extension value or the extension is not + // critical. + PrecertInvalidPoison ) // Certificate persistence related errors specified with CertStoreError @@ -366,6 +376,10 @@ func New(category Category, reason Reason) *Error { msg = "Certificate transparency parsing failed due to unknown error" case PrecertSubmissionFailed: msg = "Certificate transparency precertificate submission failed" + case PrecertMissingPoison: + msg = "Precertificate is missing CT poison extension" + case PrecertInvalidPoison: + msg = "Precertificate contains an invalid CT poison extension" default: panic(fmt.Sprintf("Unsupported CF-SSL error reason %d under category CTError.", reason)) } @@ -412,7 +426,7 @@ func Wrap(category Category, reason Reason, err error) *Error { } } case PrivateKeyError, IntermediatesError, RootError, PolicyError, DialError, - APIClientError, CSRError, CTError, CertStoreError: + APIClientError, CSRError, CTError, CertStoreError, OCSPError: // no-op, just use the error default: panic(fmt.Sprintf("Unsupported CFSSL error type: %d.", diff --git a/vendor/github.com/cloudflare/cfssl/helpers/helpers.go b/vendor/github.com/cloudflare/cfssl/helpers/helpers.go index 48d096a9877a4..8218ba53ad7cc 100644 --- a/vendor/github.com/cloudflare/cfssl/helpers/helpers.go +++ b/vendor/github.com/cloudflare/cfssl/helpers/helpers.go @@ -10,11 +10,18 @@ import ( "crypto/rsa" "crypto/tls" "crypto/x509" + "crypto/x509/pkix" "encoding/asn1" "encoding/pem" "errors" + "fmt" "io/ioutil" - "math/big" + "os" + + "github.com/google/certificate-transparency-go" + cttls "github.com/google/certificate-transparency-go/tls" + ctx509 "github.com/google/certificate-transparency-go/x509" + "golang.org/x/crypto/ocsp" "strings" "time" @@ -177,7 +184,7 @@ func HashAlgoString(alg x509.SignatureAlgorithm) string { } } -// EncodeCertificatesPEM encodes a number of x509 certficates to PEM +// EncodeCertificatesPEM encodes a number of x509 certificates to PEM func EncodeCertificatesPEM(certs []*x509.Certificate) []byte { var buffer bytes.Buffer for _, cert := range certs { @@ -190,7 +197,7 @@ func EncodeCertificatesPEM(certs []*x509.Certificate) []byte { return buffer.Bytes() } -// EncodeCertificatePEM encodes a single x509 certficates to PEM +// EncodeCertificatePEM encodes a single x509 certificates to PEM func EncodeCertificatePEM(cert *x509.Certificate) []byte { return EncodeCertificatesPEM([]*x509.Certificate{cert}) } @@ -374,51 +381,6 @@ func GetKeyDERFromPEM(in []byte, password []byte) ([]byte, error) { return nil, cferr.New(cferr.PrivateKeyError, cferr.DecodeFailed) } -// CheckSignature verifies a signature made by the key on a CSR, such -// as on the CSR itself. -func CheckSignature(csr *x509.CertificateRequest, algo x509.SignatureAlgorithm, signed, signature []byte) error { - var hashType crypto.Hash - - switch algo { - case x509.SHA1WithRSA, x509.ECDSAWithSHA1: - hashType = crypto.SHA1 - case x509.SHA256WithRSA, x509.ECDSAWithSHA256: - hashType = crypto.SHA256 - case x509.SHA384WithRSA, x509.ECDSAWithSHA384: - hashType = crypto.SHA384 - case x509.SHA512WithRSA, x509.ECDSAWithSHA512: - hashType = crypto.SHA512 - default: - return x509.ErrUnsupportedAlgorithm - } - - if !hashType.Available() { - return x509.ErrUnsupportedAlgorithm - } - h := hashType.New() - - h.Write(signed) - digest := h.Sum(nil) - - switch pub := csr.PublicKey.(type) { - case *rsa.PublicKey: - return rsa.VerifyPKCS1v15(pub, hashType, digest, signature) - case *ecdsa.PublicKey: - ecdsaSig := new(struct{ R, S *big.Int }) - if _, err := asn1.Unmarshal(signature, ecdsaSig); err != nil { - return err - } - if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { - return errors.New("x509: ECDSA signature contained zero or negative values") - } - if !ecdsa.Verify(pub, digest, ecdsaSig.R, ecdsaSig.S) { - return errors.New("x509: ECDSA verification failure") - } - return nil - } - return x509.ErrUnsupportedAlgorithm -} - // ParseCSR parses a PEM- or DER-encoded PKCS #10 certificate signing request. func ParseCSR(in []byte) (csr *x509.CertificateRequest, rest []byte, err error) { in = bytes.TrimSpace(in) @@ -437,7 +399,7 @@ func ParseCSR(in []byte) (csr *x509.CertificateRequest, rest []byte, err error) return nil, rest, err } - err = CheckSignature(csr, csr.SignatureAlgorithm, csr.RawTBSCertificateRequest, csr.Signature) + err = csr.CheckSignature() if err != nil { return nil, rest, err } @@ -445,13 +407,15 @@ func ParseCSR(in []byte) (csr *x509.CertificateRequest, rest []byte, err error) return csr, rest, nil } -// ParseCSRPEM parses a PEM-encoded certificiate signing request. +// ParseCSRPEM parses a PEM-encoded certificate signing request. // It does not check the signature. This is useful for dumping data from a CSR // locally. func ParseCSRPEM(csrPEM []byte) (*x509.CertificateRequest, error) { block, _ := pem.Decode([]byte(csrPEM)) - der := block.Bytes - csrObject, err := x509.ParseCertificateRequest(der) + if block == nil { + return nil, cferr.New(cferr.CSRError, cferr.DecodeFailed) + } + csrObject, err := x509.ParseCertificateRequest(block.Bytes) if err != nil { return nil, err @@ -516,3 +480,98 @@ func CreateTLSConfig(remoteCAs *x509.CertPool, cert *tls.Certificate) *tls.Confi RootCAs: remoteCAs, } } + +// SerializeSCTList serializes a list of SCTs. +func SerializeSCTList(sctList []ct.SignedCertificateTimestamp) ([]byte, error) { + list := ctx509.SignedCertificateTimestampList{} + for _, sct := range sctList { + sctBytes, err := cttls.Marshal(sct) + if err != nil { + return nil, err + } + list.SCTList = append(list.SCTList, ctx509.SerializedSCT{Val: sctBytes}) + } + return cttls.Marshal(list) +} + +// DeserializeSCTList deserializes a list of SCTs. +func DeserializeSCTList(serializedSCTList []byte) ([]ct.SignedCertificateTimestamp, error) { + var sctList ctx509.SignedCertificateTimestampList + rest, err := cttls.Unmarshal(serializedSCTList, &sctList) + if err != nil { + return nil, err + } + if len(rest) != 0 { + return nil, cferr.Wrap(cferr.CTError, cferr.Unknown, errors.New("serialized SCT list contained trailing garbage")) + } + list := make([]ct.SignedCertificateTimestamp, len(sctList.SCTList)) + for i, serializedSCT := range sctList.SCTList { + var sct ct.SignedCertificateTimestamp + rest, err := cttls.Unmarshal(serializedSCT.Val, &sct) + if err != nil { + return nil, err + } + if len(rest) != 0 { + return nil, cferr.Wrap(cferr.CTError, cferr.Unknown, errors.New("serialized SCT contained trailing garbage")) + } + list[i] = sct + } + return list, nil +} + +// SCTListFromOCSPResponse extracts the SCTList from an ocsp.Response, +// returning an empty list if the SCT extension was not found or could not be +// unmarshalled. +func SCTListFromOCSPResponse(response *ocsp.Response) ([]ct.SignedCertificateTimestamp, error) { + // This loop finds the SCTListExtension in the OCSP response. + var SCTListExtension, ext pkix.Extension + for _, ext = range response.Extensions { + // sctExtOid is the ObjectIdentifier of a Signed Certificate Timestamp. + sctExtOid := asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 5} + if ext.Id.Equal(sctExtOid) { + SCTListExtension = ext + break + } + } + + // This code block extracts the sctList from the SCT extension. + var sctList []ct.SignedCertificateTimestamp + var err error + if numBytes := len(SCTListExtension.Value); numBytes != 0 { + var serializedSCTList []byte + rest := make([]byte, numBytes) + copy(rest, SCTListExtension.Value) + for len(rest) != 0 { + rest, err = asn1.Unmarshal(rest, &serializedSCTList) + if err != nil { + return nil, cferr.Wrap(cferr.CTError, cferr.Unknown, err) + } + } + sctList, err = DeserializeSCTList(serializedSCTList) + } + return sctList, err +} + +// ReadBytes reads a []byte either from a file or an environment variable. +// If valFile has a prefix of 'env:', the []byte is read from the environment +// using the subsequent name. If the prefix is 'file:' the []byte is read from +// the subsequent file. If no prefix is provided, valFile is assumed to be a +// file path. +func ReadBytes(valFile string) ([]byte, error) { + switch splitVal := strings.SplitN(valFile, ":", 2); len(splitVal) { + case 1: + return ioutil.ReadFile(valFile) + case 2: + switch splitVal[0] { + case "env": + return []byte(os.Getenv(splitVal[1])), nil + case "file": + return ioutil.ReadFile(splitVal[1]) + default: + return nil, fmt.Errorf("unknown prefix: %s", splitVal[0]) + } + default: + return nil, fmt.Errorf("multiple prefixes: %s", + strings.Join(splitVal[:len(splitVal)-1], ", ")) + } +} diff --git a/vendor/github.com/cloudflare/cfssl/initca/initca.go b/vendor/github.com/cloudflare/cfssl/initca/initca.go index c9ab3bbc352b6..2cdc0925f184a 100644 --- a/vendor/github.com/cloudflare/cfssl/initca/initca.go +++ b/vendor/github.com/cloudflare/cfssl/initca/initca.go @@ -5,10 +5,11 @@ package initca import ( "crypto" "crypto/ecdsa" + "crypto/rand" "crypto/rsa" "crypto/x509" + "encoding/pem" "errors" - "io/ioutil" "time" "github.com/cloudflare/cfssl/config" @@ -53,8 +54,15 @@ func New(req *csr.CertificateRequest) (cert, csrPEM, key []byte, err error) { } } + if req.CA.Backdate != "" { + policy.Default.Backdate, err = time.ParseDuration(req.CA.Backdate) + if err != nil { + return + } + } + policy.Default.CAConstraint.MaxPathLen = req.CA.PathLength - if req.CA.PathLength != 0 && req.CA.PathLenZero == true { + if req.CA.PathLength != 0 && req.CA.PathLenZero { log.Infof("ignore invalid 'pathlenzero' value") } else { policy.Default.CAConstraint.MaxPathLenZero = req.CA.PathLenZero @@ -90,7 +98,7 @@ func New(req *csr.CertificateRequest) (cert, csrPEM, key []byte, err error) { // NewFromPEM creates a new root certificate from the key file passed in. func NewFromPEM(req *csr.CertificateRequest, keyFile string) (cert, csrPEM []byte, err error) { - privData, err := ioutil.ReadFile(keyFile) + privData, err := helpers.ReadBytes(keyFile) if err != nil { return nil, nil, err } @@ -105,11 +113,11 @@ func NewFromPEM(req *csr.CertificateRequest, keyFile string) (cert, csrPEM []byt // RenewFromPEM re-creates a root certificate from the CA cert and key // files. The resulting root certificate will have the input CA certificate -// as the template and have the same expiry length. E.g. the exsiting CA +// as the template and have the same expiry length. E.g. the existing CA // is valid for a year from Jan 01 2015 to Jan 01 2016, the renewed certificate // will be valid from now and expire in one year as well. func RenewFromPEM(caFile, keyFile string) ([]byte, error) { - caBytes, err := ioutil.ReadFile(caFile) + caBytes, err := helpers.ReadBytes(caFile) if err != nil { return nil, err } @@ -119,7 +127,7 @@ func RenewFromPEM(caFile, keyFile string) ([]byte, error) { return nil, err } - keyBytes, err := ioutil.ReadFile(keyFile) + keyBytes, err := helpers.ReadBytes(keyFile) if err != nil { return nil, err } @@ -130,7 +138,6 @@ func RenewFromPEM(caFile, keyFile string) ([]byte, error) { } return RenewFromSigner(ca, key) - } // NewFromSigner creates a new root certificate from a crypto.Signer. @@ -171,7 +178,7 @@ func NewFromSigner(req *csr.CertificateRequest, priv crypto.Signer) (cert, csrPE // RenewFromSigner re-creates a root certificate from the CA cert and crypto.Signer. // The resulting root certificate will have ca certificate -// as the template and have the same expiry length. E.g. the exsiting CA +// as the template and have the same expiry length. E.g. the existing CA // is valid for a year from Jan 01 2015 to Jan 01 2016, the renewed certificate // will be valid from now and expire in one year as well. func RenewFromSigner(ca *x509.Certificate, priv crypto.Signer) ([]byte, error) { @@ -182,7 +189,6 @@ func RenewFromSigner(ca *x509.Certificate, priv crypto.Signer) ([]byte, error) { // matching certificate public key vs private key switch { case ca.PublicKeyAlgorithm == x509.RSA: - var rsaPublicKey *rsa.PublicKey var ok bool if rsaPublicKey, ok = priv.Public().(*rsa.PublicKey); !ok { @@ -205,7 +211,6 @@ func RenewFromSigner(ca *x509.Certificate, priv crypto.Signer) ([]byte, error) { } req := csr.ExtractCertificateRequest(ca) - cert, _, err := NewFromSigner(req, priv) return cert, err @@ -222,3 +227,23 @@ var CAPolicy = func() *config.Signing { }, } } + +// Update copies the CA certificate, updates the NotBefore and +// NotAfter fields, and then re-signs the certificate. +func Update(ca *x509.Certificate, priv crypto.Signer) (cert []byte, err error) { + copy, err := x509.ParseCertificate(ca.Raw) + if err != nil { + return + } + + validity := ca.NotAfter.Sub(ca.NotBefore) + copy.NotBefore = time.Now().Round(time.Minute).Add(-5 * time.Minute) + copy.NotAfter = copy.NotBefore.Add(validity) + cert, err = x509.CreateCertificate(rand.Reader, copy, copy, priv.Public(), priv) + if err != nil { + return + } + + cert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert}) + return +} diff --git a/vendor/github.com/cloudflare/cfssl/signer/local/local.go b/vendor/github.com/cloudflare/cfssl/signer/local/local.go index 6f2b5e57aac99..a92b8f59171f6 100644 --- a/vendor/github.com/cloudflare/cfssl/signer/local/local.go +++ b/vendor/github.com/cloudflare/cfssl/signer/local/local.go @@ -8,14 +8,13 @@ import ( "crypto/x509" "crypto/x509/pkix" "encoding/asn1" - "encoding/binary" "encoding/hex" "encoding/pem" "errors" "io" - "io/ioutil" "math/big" "net" + "net/http" "net/mail" "os" @@ -26,8 +25,10 @@ import ( "github.com/cloudflare/cfssl/info" "github.com/cloudflare/cfssl/log" "github.com/cloudflare/cfssl/signer" - "github.com/google/certificate-transparency/go" - "github.com/google/certificate-transparency/go/client" + "github.com/google/certificate-transparency-go" + "github.com/google/certificate-transparency-go/client" + "github.com/google/certificate-transparency-go/jsonclient" + "golang.org/x/net/context" ) // Signer contains a signer that uses the standard library to @@ -65,12 +66,12 @@ func NewSigner(priv crypto.Signer, cert *x509.Certificate, sigAlgo x509.Signatur // and a caKey file, both PEM encoded. func NewSignerFromFile(caFile, caKeyFile string, policy *config.Signing) (*Signer, error) { log.Debug("Loading CA: ", caFile) - ca, err := ioutil.ReadFile(caFile) + ca, err := helpers.ReadBytes(caFile) if err != nil { return nil, err } log.Debug("Loading CA key: ", caKeyFile) - cakey, err := ioutil.ReadFile(caKeyFile) + cakey, err := helpers.ReadBytes(caKeyFile) if err != nil { return nil, cferr.Wrap(cferr.CertificateError, cferr.ReadFailed, err) } @@ -95,16 +96,7 @@ func NewSignerFromFile(caFile, caKeyFile string, policy *config.Signing) (*Signe return NewSigner(priv, parsedCa, signer.DefaultSigAlgo(priv), policy) } -func (s *Signer) sign(template *x509.Certificate, profile *config.SigningProfile) (cert []byte, err error) { - var distPoints = template.CRLDistributionPoints - err = signer.FillTemplate(template, s.policy.Default, profile) - if distPoints != nil && len(distPoints) > 0 { - template.CRLDistributionPoints = distPoints - } - if err != nil { - return - } - +func (s *Signer) sign(template *x509.Certificate) (cert []byte, err error) { var initRoot bool if s.ca == nil { if !template.IsCA { @@ -204,7 +196,7 @@ func (s *Signer) Sign(req signer.SignRequest) (cert []byte, err error) { if block.Type != "NEW CERTIFICATE REQUEST" && block.Type != "CERTIFICATE REQUEST" { return nil, cferr.Wrap(cferr.CSRError, - cferr.BadRequest, errors.New("not a certificate or csr")) + cferr.BadRequest, errors.New("not a csr")) } csrTemplate, err := signer.ParseCertificateRequest(s, block.Bytes) @@ -334,27 +326,44 @@ func (s *Signer) Sign(req signer.SignRequest) (cert []byte, err error) { } } + var distPoints = safeTemplate.CRLDistributionPoints + err = signer.FillTemplate(&safeTemplate, s.policy.Default, profile, req.NotBefore, req.NotAfter) + if err != nil { + return nil, err + } + if distPoints != nil && len(distPoints) > 0 { + safeTemplate.CRLDistributionPoints = distPoints + } + var certTBS = safeTemplate - if len(profile.CTLogServers) > 0 { + if len(profile.CTLogServers) > 0 || req.ReturnPrecert { // Add a poison extension which prevents validation var poisonExtension = pkix.Extension{Id: signer.CTPoisonOID, Critical: true, Value: []byte{0x05, 0x00}} var poisonedPreCert = certTBS poisonedPreCert.ExtraExtensions = append(safeTemplate.ExtraExtensions, poisonExtension) - cert, err = s.sign(&poisonedPreCert, profile) + cert, err = s.sign(&poisonedPreCert) if err != nil { return } + if req.ReturnPrecert { + return cert, nil + } + derCert, _ := pem.Decode(cert) - prechain := []ct.ASN1Cert{derCert.Bytes, s.ca.Raw} + prechain := []ct.ASN1Cert{{Data: derCert.Bytes}, {Data: s.ca.Raw}} var sctList []ct.SignedCertificateTimestamp for _, server := range profile.CTLogServers { log.Infof("submitting poisoned precertificate to %s", server) - var ctclient = client.New(server, nil) + ctclient, err := client.New(server, nil, jsonclient.Options{}) + if err != nil { + return nil, cferr.Wrap(cferr.CTError, cferr.PrecertSubmissionFailed, err) + } var resp *ct.SignedCertificateTimestamp - resp, err = ctclient.AddPreChain(prechain) + ctx := context.Background() + resp, err = ctclient.AddPreChain(ctx, prechain) if err != nil { return nil, cferr.Wrap(cferr.CTError, cferr.PrecertSubmissionFailed, err) } @@ -362,7 +371,7 @@ func (s *Signer) Sign(req signer.SignRequest) (cert []byte, err error) { } var serializedSCTList []byte - serializedSCTList, err = serializeSCTList(sctList) + serializedSCTList, err = helpers.SerializeSCTList(sctList) if err != nil { return nil, cferr.Wrap(cferr.CTError, cferr.Unknown, err) } @@ -377,17 +386,22 @@ func (s *Signer) Sign(req signer.SignRequest) (cert []byte, err error) { certTBS.ExtraExtensions = append(certTBS.ExtraExtensions, SCTListExtension) } var signedCert []byte - signedCert, err = s.sign(&certTBS, profile) + signedCert, err = s.sign(&certTBS) if err != nil { return nil, err } + // Get the AKI from signedCert. This is required to support Go 1.9+. + // In prior versions of Go, x509.CreateCertificate updated the + // AuthorityKeyId of certTBS. + parsedCert, _ := helpers.ParseCertificatePEM(signedCert) + if s.dbAccessor != nil { var certRecord = certdb.CertificateRecord{ Serial: certTBS.SerialNumber.String(), // this relies on the specific behavior of x509.CreateCertificate - // which updates certTBS AuthorityKeyId from the signer's SubjectKeyId - AKI: hex.EncodeToString(certTBS.AuthorityKeyId), + // which sets the AuthorityKeyId from the signer's SubjectKeyId + AKI: hex.EncodeToString(parsedCert.AuthorityKeyId), CALabel: req.Label, Status: "good", Expiry: certTBS.NotAfter, @@ -404,20 +418,83 @@ func (s *Signer) Sign(req signer.SignRequest) (cert []byte, err error) { return signedCert, nil } -func serializeSCTList(sctList []ct.SignedCertificateTimestamp) ([]byte, error) { - var buf bytes.Buffer - for _, sct := range sctList { - sct, err := ct.SerializeSCT(sct) - if err != nil { - return nil, err +// SignFromPrecert creates and signs a certificate from an existing precertificate +// that was previously signed by Signer.ca and inserts the provided SCTs into the +// new certificate. The resulting certificate will be a exact copy of the precert +// except for the removal of the poison extension and the addition of the SCT list +// extension. SignFromPrecert does not verify that the contents of the certificate +// still match the signing profile of the signer, it only requires that the precert +// was previously signed by the Signers CA. +func (s *Signer) SignFromPrecert(precert *x509.Certificate, scts []ct.SignedCertificateTimestamp) ([]byte, error) { + // Verify certificate was signed by s.ca + if err := precert.CheckSignatureFrom(s.ca); err != nil { + return nil, err + } + + // Verify certificate is a precert + isPrecert := false + poisonIndex := 0 + for i, ext := range precert.Extensions { + if ext.Id.Equal(signer.CTPoisonOID) { + if !ext.Critical { + return nil, cferr.New(cferr.CTError, cferr.PrecertInvalidPoison) + } + // Check extension contains ASN.1 NULL + if bytes.Compare(ext.Value, []byte{0x05, 0x00}) != 0 { + return nil, cferr.New(cferr.CTError, cferr.PrecertInvalidPoison) + } + isPrecert = true + poisonIndex = i + break } - binary.Write(&buf, binary.BigEndian, uint16(len(sct))) - buf.Write(sct) + } + if !isPrecert { + return nil, cferr.New(cferr.CTError, cferr.PrecertMissingPoison) } - var sctListLengthField = make([]byte, 2) - binary.BigEndian.PutUint16(sctListLengthField, uint16(buf.Len())) - return bytes.Join([][]byte{sctListLengthField, buf.Bytes()}, nil), nil + // Serialize SCTs into list format and create extension + serializedList, err := helpers.SerializeSCTList(scts) + if err != nil { + return nil, err + } + // Serialize again as an octet string before embedding + serializedList, err = asn1.Marshal(serializedList) + if err != nil { + return nil, cferr.Wrap(cferr.CTError, cferr.Unknown, err) + } + sctExt := pkix.Extension{Id: signer.SCTListOID, Critical: false, Value: serializedList} + + // Create the new tbsCert from precert. Do explicit copies of any slices so that we don't + // use memory that may be altered by us or the caller at a later stage. + tbsCert := x509.Certificate{ + SignatureAlgorithm: precert.SignatureAlgorithm, + PublicKeyAlgorithm: precert.PublicKeyAlgorithm, + PublicKey: precert.PublicKey, + Version: precert.Version, + SerialNumber: precert.SerialNumber, + Issuer: precert.Issuer, + Subject: precert.Subject, + NotBefore: precert.NotBefore, + NotAfter: precert.NotAfter, + KeyUsage: precert.KeyUsage, + BasicConstraintsValid: precert.BasicConstraintsValid, + IsCA: precert.IsCA, + MaxPathLen: precert.MaxPathLen, + MaxPathLenZero: precert.MaxPathLenZero, + PermittedDNSDomainsCritical: precert.PermittedDNSDomainsCritical, + } + if len(precert.Extensions) > 0 { + tbsCert.ExtraExtensions = make([]pkix.Extension, len(precert.Extensions)) + copy(tbsCert.ExtraExtensions, precert.Extensions) + } + + // Remove the poison extension from ExtraExtensions + tbsCert.ExtraExtensions = append(tbsCert.ExtraExtensions[:poisonIndex], tbsCert.ExtraExtensions[poisonIndex+1:]...) + // Insert the SCT list extension + tbsCert.ExtraExtensions = append(tbsCert.ExtraExtensions, sctExt) + + // Sign the tbsCert + return s.sign(&tbsCert) } // Info return a populated info.Resp struct or an error. @@ -463,6 +540,16 @@ func (s *Signer) SetDBAccessor(dba certdb.Accessor) { s.dbAccessor = dba } +// GetDBAccessor returns the signers' cert db accessor +func (s *Signer) GetDBAccessor() certdb.Accessor { + return s.dbAccessor +} + +// SetReqModifier does nothing for local +func (s *Signer) SetReqModifier(func(*http.Request, []byte)) { + // noop +} + // Policy returns the signer's policy. func (s *Signer) Policy() *config.Signing { return s.policy diff --git a/vendor/github.com/cloudflare/cfssl/signer/signer.go b/vendor/github.com/cloudflare/cfssl/signer/signer.go index a34fa453bc27b..97d123673f856 100644 --- a/vendor/github.com/cloudflare/cfssl/signer/signer.go +++ b/vendor/github.com/cloudflare/cfssl/signer/signer.go @@ -12,6 +12,7 @@ import ( "encoding/asn1" "errors" "math/big" + "net/http" "strings" "time" @@ -19,7 +20,6 @@ import ( "github.com/cloudflare/cfssl/config" "github.com/cloudflare/cfssl/csr" cferr "github.com/cloudflare/cfssl/errors" - "github.com/cloudflare/cfssl/helpers" "github.com/cloudflare/cfssl/info" ) @@ -55,6 +55,21 @@ type SignRequest struct { Label string `json:"label"` Serial *big.Int `json:"serial,omitempty"` Extensions []Extension `json:"extensions,omitempty"` + // If provided, NotBefore will be used without modification (except + // for canonicalization) as the value of the notBefore field of the + // certificate. In particular no backdating adjustment will be made + // when NotBefore is provided. + NotBefore time.Time + // If provided, NotAfter will be used without modification (except + // for canonicalization) as the value of the notAfter field of the + // certificate. + NotAfter time.Time + // If ReturnPrecert is true a certificate with the CT poison extension + // will be returned from the Signer instead of attempting to retrieve + // SCTs and populate the tbsCert with them itself. This precert can then + // be passed to SignFromPrecert with the SCTs in order to create a + // valid certificate. + ReturnPrecert bool } // appendIf appends to a if s is not an empty string. @@ -96,9 +111,11 @@ type Signer interface { Info(info.Req) (*info.Resp, error) Policy() *config.Signing SetDBAccessor(certdb.Accessor) + GetDBAccessor() certdb.Accessor SetPolicy(*config.Signing) SigAlgo() x509.SignatureAlgorithm Sign(req SignRequest) (cert []byte, err error) + SetReqModifier(func(*http.Request, []byte)) } // Profile gets the specific profile from the signer @@ -161,7 +178,7 @@ func ParseCertificateRequest(s Signer, csrBytes []byte) (template *x509.Certific return } - err = helpers.CheckSignature(csrv, csrv.SignatureAlgorithm, csrv.RawTBSCertificateRequest, csrv.Signature) + err = csrv.CheckSignature() if err != nil { err = cferr.Wrap(cferr.CSRError, cferr.KeyMismatch, err) return @@ -229,16 +246,17 @@ func ComputeSKI(template *x509.Certificate) ([]byte, error) { // the certificate template as possible from the profiles and current // template. It fills in the key uses, expiration, revocation URLs // and SKI. -func FillTemplate(template *x509.Certificate, defaultProfile, profile *config.SigningProfile) error { +func FillTemplate(template *x509.Certificate, defaultProfile, profile *config.SigningProfile, notBefore time.Time, notAfter time.Time) error { ski, err := ComputeSKI(template) + if err != nil { + return err + } var ( eku []x509.ExtKeyUsage ku x509.KeyUsage backdate time.Duration expiry time.Duration - notBefore time.Time - notAfter time.Time crlURL, ocspURL string issuerURL = profile.IssuerURL ) @@ -265,23 +283,29 @@ func FillTemplate(template *x509.Certificate, defaultProfile, profile *config.Si if ocspURL = profile.OCSP; ocspURL == "" { ocspURL = defaultProfile.OCSP } - if backdate = profile.Backdate; backdate == 0 { - backdate = -5 * time.Minute - } else { - backdate = -1 * profile.Backdate - } - if !profile.NotBefore.IsZero() { - notBefore = profile.NotBefore.UTC() - } else { - notBefore = time.Now().Round(time.Minute).Add(backdate).UTC() + if notBefore.IsZero() { + if !profile.NotBefore.IsZero() { + notBefore = profile.NotBefore + } else { + if backdate = profile.Backdate; backdate == 0 { + backdate = -5 * time.Minute + } else { + backdate = -1 * profile.Backdate + } + notBefore = time.Now().Round(time.Minute).Add(backdate) + } } + notBefore = notBefore.UTC() - if !profile.NotAfter.IsZero() { - notAfter = profile.NotAfter.UTC() - } else { - notAfter = notBefore.Add(expiry).UTC() + if notAfter.IsZero() { + if !profile.NotAfter.IsZero() { + notAfter = profile.NotAfter + } else { + notAfter = notBefore.Add(expiry) + } } + notAfter = notAfter.UTC() template.NotBefore = notBefore template.NotAfter = notAfter diff --git a/vendor/github.com/containerd/console/console_linux.go b/vendor/github.com/containerd/console/console_linux.go index 312bce17d7f56..42274e100e687 100644 --- a/vendor/github.com/containerd/console/console_linux.go +++ b/vendor/github.com/containerd/console/console_linux.go @@ -72,7 +72,7 @@ func NewEpoller() (*Epoller, error) { }, nil } -// Add creates a epoll console based on the provided console. The console will +// Add creates an epoll console based on the provided console. The console will // be registered with EPOLLET (i.e. using edge-triggered notification) and its // file descriptor will be set to non-blocking mode. After this, user should use // the return console to perform I/O. @@ -134,7 +134,7 @@ func (e *Epoller) Wait() error { } } -// Close unregister the console's file descriptor from epoll interface +// CloseConsole unregisters the console's file descriptor from epoll interface func (e *Epoller) CloseConsole(fd int) error { e.mu.Lock() defer e.mu.Unlock() @@ -149,12 +149,12 @@ func (e *Epoller) getConsole(sysfd int) *EpollConsole { return f } -// Close the epoll fd +// Close closes the epoll fd func (e *Epoller) Close() error { return unix.Close(e.efd) } -// EpollConsole acts like a console but register its file descriptor with a +// EpollConsole acts like a console but registers its file descriptor with an // epoll fd and uses epoll API to perform I/O. type EpollConsole struct { Console @@ -167,7 +167,7 @@ type EpollConsole struct { // Read reads up to len(p) bytes into p. It returns the number of bytes read // (0 <= n <= len(p)) and any error encountered. // -// If the console's read returns EAGAIN or EIO, we assumes that its a +// If the console's read returns EAGAIN or EIO, we assume that it's a // temporary error because the other side went away and wait for the signal // generated by epoll event to continue. func (ec *EpollConsole) Read(p []byte) (n int, err error) { @@ -207,7 +207,7 @@ func (ec *EpollConsole) Read(p []byte) (n int, err error) { // written from p (0 <= n <= len(p)) and any error encountered that caused // the write to stop early. // -// If writes to the console returns EAGAIN or EIO, we assumes that its a +// If writes to the console returns EAGAIN or EIO, we assume that it's a // temporary error because the other side went away and wait for the signal // generated by epoll event to continue. func (ec *EpollConsole) Write(p []byte) (n int, err error) { @@ -224,7 +224,7 @@ func (ec *EpollConsole) Write(p []byte) (n int, err error) { } else { hangup = (err == unix.EAGAIN || err == unix.EIO) } - // if the other end disappear, assume this is temporary and wait for the + // if the other end disappears, assume this is temporary and wait for the // signal to continue again. if hangup { ec.writec.Wait() @@ -242,7 +242,7 @@ func (ec *EpollConsole) Write(p []byte) (n int, err error) { return n, err } -// Close closed the file descriptor and signal call waiters for this fd. +// Shutdown closes the file descriptor and signals call waiters for this fd. // It accepts a callback which will be called with the console's fd. The // callback typically will be used to do further cleanup such as unregister the // console's fd from the epoll interface. @@ -262,10 +262,14 @@ func (ec *EpollConsole) Shutdown(close func(int) error) error { // signalRead signals that the console is readable. func (ec *EpollConsole) signalRead() { + ec.readc.L.Lock() ec.readc.Signal() + ec.readc.L.Unlock() } // signalWrite signals that the console is writable. func (ec *EpollConsole) signalWrite() { + ec.writec.L.Lock() ec.writec.Signal() + ec.writec.L.Unlock() } diff --git a/vendor/github.com/containerd/console/console_windows.go b/vendor/github.com/containerd/console/console_windows.go index 7aa726f9951cd..ff0174df4f671 100644 --- a/vendor/github.com/containerd/console/console_windows.go +++ b/vendor/github.com/containerd/console/console_windows.go @@ -17,7 +17,6 @@ package console import ( - "fmt" "os" "github.com/pkg/errors" @@ -29,90 +28,55 @@ var ( ErrNotImplemented = errors.New("not implemented") ) -func (m *master) initStdios() { - m.in = windows.Handle(os.Stdin.Fd()) - if err := windows.GetConsoleMode(m.in, &m.inMode); err == nil { - // Validate that windows.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it. - if err = windows.SetConsoleMode(m.in, m.inMode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err == nil { - vtInputSupported = true - } - // Unconditionally set the console mode back even on failure because SetConsoleMode - // remembers invalid bits on input handles. - windows.SetConsoleMode(m.in, m.inMode) - } else { - fmt.Printf("failed to get console mode for stdin: %v\n", err) - } - - m.out = windows.Handle(os.Stdout.Fd()) - if err := windows.GetConsoleMode(m.out, &m.outMode); err == nil { - if err := windows.SetConsoleMode(m.out, m.outMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil { - m.outMode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING +func (m *master) init() { + m.h = windows.Handle(m.f.Fd()) + if err := windows.GetConsoleMode(m.h, &m.mode); err == nil { + if m.f == os.Stdin { + // Validate that windows.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it. + if err = windows.SetConsoleMode(m.h, m.mode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err == nil { + vtInputSupported = true + } + // Unconditionally set the console mode back even on failure because SetConsoleMode + // remembers invalid bits on input handles. + windows.SetConsoleMode(m.h, m.mode) + } else if err := windows.SetConsoleMode(m.h, m.mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil { + m.mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING } else { - windows.SetConsoleMode(m.out, m.outMode) + windows.SetConsoleMode(m.h, m.mode) } - } else { - fmt.Printf("failed to get console mode for stdout: %v\n", err) - } - - m.err = windows.Handle(os.Stderr.Fd()) - if err := windows.GetConsoleMode(m.err, &m.errMode); err == nil { - if err := windows.SetConsoleMode(m.err, m.errMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil { - m.errMode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING - } else { - windows.SetConsoleMode(m.err, m.errMode) - } - } else { - fmt.Printf("failed to get console mode for stderr: %v\n", err) } } type master struct { - in windows.Handle - inMode uint32 - - out windows.Handle - outMode uint32 - - err windows.Handle - errMode uint32 + h windows.Handle + mode uint32 + f *os.File } func (m *master) SetRaw() error { - if err := makeInputRaw(m.in, m.inMode); err != nil { - return err + if m.f == os.Stdin { + if err := makeInputRaw(m.h, m.mode); err != nil { + return err + } + } else { + // Set StdOut and StdErr to raw mode, we ignore failures since + // windows.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this version of + // Windows. + windows.SetConsoleMode(m.h, m.mode|windows.DISABLE_NEWLINE_AUTO_RETURN) } - - // Set StdOut and StdErr to raw mode, we ignore failures since - // windows.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this version of - // Windows. - - windows.SetConsoleMode(m.out, m.outMode|windows.DISABLE_NEWLINE_AUTO_RETURN) - - windows.SetConsoleMode(m.err, m.errMode|windows.DISABLE_NEWLINE_AUTO_RETURN) - return nil } func (m *master) Reset() error { - for _, s := range []struct { - fd windows.Handle - mode uint32 - }{ - {m.in, m.inMode}, - {m.out, m.outMode}, - {m.err, m.errMode}, - } { - if err := windows.SetConsoleMode(s.fd, s.mode); err != nil { - return errors.Wrap(err, "unable to restore console mode") - } + if err := windows.SetConsoleMode(m.h, m.mode); err != nil { + return errors.Wrap(err, "unable to restore console mode") } - return nil } func (m *master) Size() (WinSize, error) { var info windows.ConsoleScreenBufferInfo - err := windows.GetConsoleScreenBufferInfo(m.out, &info) + err := windows.GetConsoleScreenBufferInfo(m.h, &info) if err != nil { return WinSize{}, errors.Wrap(err, "unable to get console info") } @@ -134,11 +98,11 @@ func (m *master) ResizeFrom(c Console) error { } func (m *master) DisableEcho() error { - mode := m.inMode &^ windows.ENABLE_ECHO_INPUT + mode := m.mode &^ windows.ENABLE_ECHO_INPUT mode |= windows.ENABLE_PROCESSED_INPUT mode |= windows.ENABLE_LINE_INPUT - if err := windows.SetConsoleMode(m.in, mode); err != nil { + if err := windows.SetConsoleMode(m.h, mode); err != nil { return errors.Wrap(err, "unable to set console to disable echo") } @@ -150,15 +114,15 @@ func (m *master) Close() error { } func (m *master) Read(b []byte) (int, error) { - panic("not implemented on windows") + return m.f.Read(b) } func (m *master) Write(b []byte) (int, error) { - panic("not implemented on windows") + return m.f.Write(b) } func (m *master) Fd() uintptr { - return uintptr(m.in) + return uintptr(m.h) } // on windows, console can only be made from os.Std{in,out,err}, hence there @@ -210,7 +174,7 @@ func newMaster(f *os.File) (Console, error) { if f != os.Stdin && f != os.Stdout && f != os.Stderr { return nil, errors.New("creating a console from a file is not supported on windows") } - m := &master{} - m.initStdios() + m := &master{f: f} + m.init() return m, nil } diff --git a/vendor/github.com/containerd/containerd/cio/io.go b/vendor/github.com/containerd/containerd/cio/io.go index 2f9b5f10379fb..10ad36ba87d89 100644 --- a/vendor/github.com/containerd/containerd/cio/io.go +++ b/vendor/github.com/containerd/containerd/cio/io.go @@ -255,3 +255,14 @@ func (l *logIO) Wait() { func (l *logIO) Close() error { return nil } + +// Load the io for a container but do not attach +// +// Allows io to be loaded on the task for deletion without +// starting copy routines +func Load(set *FIFOSet) (IO, error) { + return &cio{ + config: set.Config, + closers: []io.Closer{set}, + }, nil +} diff --git a/vendor/github.com/containerd/containerd/cio/io_unix.go b/vendor/github.com/containerd/containerd/cio/io_unix.go index 8715c65c85a72..3ab2a30b0ca02 100644 --- a/vendor/github.com/containerd/containerd/cio/io_unix.go +++ b/vendor/github.com/containerd/containerd/cio/io_unix.go @@ -141,18 +141,8 @@ func openFifos(ctx context.Context, fifos *FIFOSet) (pipes, error) { // NewDirectIO returns an IO implementation that exposes the IO streams as io.ReadCloser // and io.WriteCloser. func NewDirectIO(ctx context.Context, fifos *FIFOSet) (*DirectIO, error) { - return newDirectIO(ctx, fifos, false) -} - -// NewDirectIOWithTerminal returns an IO implementation that exposes the streams with terminal enabled -func NewDirectIOWithTerminal(ctx context.Context, fifos *FIFOSet) (*DirectIO, error) { - return newDirectIO(ctx, fifos, true) -} - -func newDirectIO(ctx context.Context, fifos *FIFOSet, terminal bool) (*DirectIO, error) { ctx, cancel := context.WithCancel(ctx) pipes, err := openFifos(ctx, fifos) - fifos.Config.Terminal = terminal return &DirectIO{ pipes: pipes, cio: cio{ diff --git a/vendor/github.com/containerd/containerd/container.go b/vendor/github.com/containerd/containerd/container.go index 001b16c740b27..23f6d4db611cc 100644 --- a/vendor/github.com/containerd/containerd/container.go +++ b/vendor/github.com/containerd/containerd/container.go @@ -307,6 +307,12 @@ func (c *container) get(ctx context.Context) (containers.Container, error) { // get the existing fifo paths from the task information stored by the daemon func attachExistingIO(response *tasks.GetResponse, ioAttach cio.Attach) (cio.IO, error) { + fifoSet := loadFifos(response) + return ioAttach(fifoSet) +} + +// loadFifos loads the containers fifos +func loadFifos(response *tasks.GetResponse) *cio.FIFOSet { path := getFifoDir([]string{ response.Process.Stdin, response.Process.Stdout, @@ -315,13 +321,12 @@ func attachExistingIO(response *tasks.GetResponse, ioAttach cio.Attach) (cio.IO, closer := func() error { return os.RemoveAll(path) } - fifoSet := cio.NewFIFOSet(cio.Config{ + return cio.NewFIFOSet(cio.Config{ Stdin: response.Process.Stdin, Stdout: response.Process.Stdout, Stderr: response.Process.Stderr, Terminal: response.Process.Terminal, }, closer) - return ioAttach(fifoSet) } // getFifoDir looks for any non-empty path for a stdio fifo diff --git a/vendor/github.com/containerd/containerd/container_opts_unix.go b/vendor/github.com/containerd/containerd/container_opts_unix.go index 8ae9551132eca..a4935b2b454e5 100644 --- a/vendor/github.com/containerd/containerd/container_opts_unix.go +++ b/vendor/github.com/containerd/containerd/container_opts_unix.go @@ -31,14 +31,14 @@ import ( "github.com/containerd/containerd/content" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" - "github.com/containerd/containerd/linux/runctypes" "github.com/containerd/containerd/mount" "github.com/containerd/containerd/platforms" + "github.com/containerd/containerd/runtime/linux/runctypes" "github.com/gogo/protobuf/proto" protobuf "github.com/gogo/protobuf/types" - digest "github.com/opencontainers/go-digest" "github.com/opencontainers/image-spec/identity" "github.com/opencontainers/image-spec/specs-go/v1" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" ) @@ -50,10 +50,9 @@ func WithCheckpoint(im Image, snapshotKey string) NewContainerOpts { return func(ctx context.Context, client *Client, c *containers.Container) error { var ( desc = im.Target() - id = desc.Digest store = client.ContentStore() ) - index, err := decodeIndex(ctx, store, id) + index, err := decodeIndex(ctx, store, desc) if err != nil { return err } @@ -80,7 +79,7 @@ func WithCheckpoint(im Image, snapshotKey string) NewContainerOpts { } c.Image = index.Annotations["image.name"] case images.MediaTypeContainerd1CheckpointConfig: - data, err := content.ReadBlob(ctx, store, m.Digest) + data, err := content.ReadBlob(ctx, store, m) if err != nil { return errors.Wrap(err, "unable to read checkpoint config") } @@ -113,7 +112,7 @@ func WithTaskCheckpoint(im Image) NewTaskOpts { return func(ctx context.Context, c *Client, info *TaskInfo) error { desc := im.Target() id := desc.Digest - index, err := decodeIndex(ctx, c.ContentStore(), id) + index, err := decodeIndex(ctx, c.ContentStore(), desc) if err != nil { return err } @@ -131,9 +130,9 @@ func WithTaskCheckpoint(im Image) NewTaskOpts { } } -func decodeIndex(ctx context.Context, store content.Provider, id digest.Digest) (*v1.Index, error) { +func decodeIndex(ctx context.Context, store content.Provider, desc ocispec.Descriptor) (*v1.Index, error) { var index v1.Index - p, err := content.ReadBlob(ctx, store, id) + p, err := content.ReadBlob(ctx, store, desc) if err != nil { return nil, err } diff --git a/vendor/github.com/containerd/containerd/content/content.go b/vendor/github.com/containerd/containerd/content/content.go index de9dd48f55035..aabf4c8f31ea5 100644 --- a/vendor/github.com/containerd/containerd/content/content.go +++ b/vendor/github.com/containerd/containerd/content/content.go @@ -22,6 +22,7 @@ import ( "time" "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) // ReaderAt extends the standard io.ReaderAt interface with reporting of Size and io.Closer @@ -33,12 +34,16 @@ type ReaderAt interface { // Provider provides a reader interface for specific content type Provider interface { - ReaderAt(ctx context.Context, dgst digest.Digest) (ReaderAt, error) + // ReaderAt only requires desc.Digest to be set. + // Other fields in the descriptor may be used internally for resolving + // the location of the actual data. + ReaderAt(ctx context.Context, dec ocispec.Descriptor) (ReaderAt, error) } // Ingester writes content type Ingester interface { - Writer(ctx context.Context, ref string, size int64, expected digest.Digest) (Writer, error) + // Some implementations require WithRef to be included in opts. + Writer(ctx context.Context, opts ...WriterOpt) (Writer, error) } // Info holds content specific information @@ -142,3 +147,33 @@ func WithLabels(labels map[string]string) Opt { return nil } } + +// WriterOpts is internally used by WriterOpt. +type WriterOpts struct { + Ref string + Desc ocispec.Descriptor +} + +// WriterOpt is used for passing options to Ingester.Writer. +type WriterOpt func(*WriterOpts) error + +// WithDescriptor specifies an OCI descriptor. +// Writer may optionally use the descriptor internally for resolving +// the location of the actual data. +// Write does not require any field of desc to be set. +// If the data size is unknown, desc.Size should be set to 0. +// Some implementations may also accept negative values as "unknown". +func WithDescriptor(desc ocispec.Descriptor) WriterOpt { + return func(opts *WriterOpts) error { + opts.Desc = desc + return nil + } +} + +// WithRef specifies a ref string. +func WithRef(ref string) WriterOpt { + return func(opts *WriterOpts) error { + opts.Ref = ref + return nil + } +} diff --git a/vendor/github.com/containerd/containerd/content/helpers.go b/vendor/github.com/containerd/containerd/content/helpers.go index 3b0de7ac1bf84..819b7ea1e320f 100644 --- a/vendor/github.com/containerd/containerd/content/helpers.go +++ b/vendor/github.com/containerd/containerd/content/helpers.go @@ -26,6 +26,7 @@ import ( "github.com/containerd/containerd/errdefs" "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" ) @@ -45,8 +46,8 @@ func NewReader(ra ReaderAt) io.Reader { // ReadBlob retrieves the entire contents of the blob from the provider. // // Avoid using this for large blobs, such as layers. -func ReadBlob(ctx context.Context, provider Provider, dgst digest.Digest) ([]byte, error) { - ra, err := provider.ReaderAt(ctx, dgst) +func ReadBlob(ctx context.Context, provider Provider, desc ocispec.Descriptor) ([]byte, error) { + ra, err := provider.ReaderAt(ctx, desc) if err != nil { return nil, err } @@ -65,8 +66,8 @@ func ReadBlob(ctx context.Context, provider Provider, dgst digest.Digest) ([]byt // This is useful when the digest and size are known beforehand. // // Copy is buffered, so no need to wrap reader in buffered io. -func WriteBlob(ctx context.Context, cs Ingester, ref string, r io.Reader, size int64, expected digest.Digest, opts ...Opt) error { - cw, err := OpenWriter(ctx, cs, ref, size, expected) +func WriteBlob(ctx context.Context, cs Ingester, ref string, r io.Reader, desc ocispec.Descriptor, opts ...Opt) error { + cw, err := OpenWriter(ctx, cs, WithRef(ref), WithDescriptor(desc)) if err != nil { if !errdefs.IsAlreadyExists(err) { return err @@ -76,19 +77,19 @@ func WriteBlob(ctx context.Context, cs Ingester, ref string, r io.Reader, size i } defer cw.Close() - return Copy(ctx, cw, r, size, expected, opts...) + return Copy(ctx, cw, r, desc.Size, desc.Digest, opts...) } // OpenWriter opens a new writer for the given reference, retrying if the writer // is locked until the reference is available or returns an error. -func OpenWriter(ctx context.Context, cs Ingester, ref string, size int64, expected digest.Digest) (Writer, error) { +func OpenWriter(ctx context.Context, cs Ingester, opts ...WriterOpt) (Writer, error) { var ( cw Writer err error retry = 16 ) for { - cw, err = cs.Writer(ctx, ref, size, expected) + cw, err = cs.Writer(ctx, opts...) if err != nil { if !errdefs.IsUnavailable(err) { return nil, err diff --git a/vendor/github.com/containerd/containerd/content/local/store.go b/vendor/github.com/containerd/containerd/content/local/store.go index 69437dfd19f5f..11dfd61a3b248 100644 --- a/vendor/github.com/containerd/containerd/content/local/store.go +++ b/vendor/github.com/containerd/containerd/content/local/store.go @@ -34,6 +34,7 @@ import ( "github.com/containerd/containerd/filters" "github.com/containerd/containerd/log" digest "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" ) @@ -119,15 +120,15 @@ func (s *store) info(dgst digest.Digest, fi os.FileInfo, labels map[string]strin } // ReaderAt returns an io.ReaderAt for the blob. -func (s *store) ReaderAt(ctx context.Context, dgst digest.Digest) (content.ReaderAt, error) { - p := s.blobPath(dgst) +func (s *store) ReaderAt(ctx context.Context, desc ocispec.Descriptor) (content.ReaderAt, error) { + p := s.blobPath(desc.Digest) fi, err := os.Stat(p) if err != nil { if !os.IsNotExist(err) { return nil, err } - return nil, errors.Wrapf(errdefs.ErrNotFound, "blob %s expected at %s", dgst, p) + return nil, errors.Wrapf(errdefs.ErrNotFound, "blob %s expected at %s", desc.Digest, p) } fp, err := os.Open(p) @@ -136,7 +137,7 @@ func (s *store) ReaderAt(ctx context.Context, dgst digest.Digest) (content.Reade return nil, err } - return nil, errors.Wrapf(errdefs.ErrNotFound, "blob %s expected at %s", dgst, p) + return nil, errors.Wrapf(errdefs.ErrNotFound, "blob %s expected at %s", desc.Digest, p) } return sizeReaderAt{size: fi.Size(), fp: fp}, nil @@ -400,11 +401,22 @@ func (s *store) total(ingestPath string) int64 { // ref at a time. // // The argument `ref` is used to uniquely identify a long-lived writer transaction. -func (s *store) Writer(ctx context.Context, ref string, total int64, expected digest.Digest) (content.Writer, error) { +func (s *store) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) { + var wOpts content.WriterOpts + for _, opt := range opts { + if err := opt(&wOpts); err != nil { + return nil, err + } + } + // TODO(AkihiroSuda): we could create a random string or one calculated based on the context + // https://github.com/containerd/containerd/issues/2129#issuecomment-380255019 + if wOpts.Ref == "" { + return nil, errors.Wrap(errdefs.ErrInvalidArgument, "ref must not be empty") + } var lockErr error for count := uint64(0); count < 10; count++ { time.Sleep(time.Millisecond * time.Duration(rand.Intn(1< 0 { - ra, err := nw.provider.ReaderAt(ctx, nw.expected) + if desc.Size > 0 { + ra, err := nw.provider.ReaderAt(ctx, nw.desc) if err != nil { return err } defer ra.Close() - if err := content.CopyReaderAt(w, ra, size); err != nil { + if err := content.CopyReaderAt(w, ra, desc.Size); err != nil { nw.w.Close() nw.w = nil return err @@ -544,14 +560,14 @@ func (nw *namespacedWriter) commit(ctx context.Context, tx *bolt.Tx, size int64, var actual digest.Digest if nw.w == nil { - if size != 0 && size != nw.size { - return "", errors.Errorf("%q failed size validation: %v != %v", nw.ref, nw.size, size) + if size != 0 && size != nw.desc.Size { + return "", errors.Errorf("%q failed size validation: %v != %v", nw.ref, nw.desc.Size, size) } - if expected != "" && expected != nw.expected { + if expected != "" && expected != nw.desc.Digest { return "", errors.Errorf("%q unexpected digest", nw.ref) } - size = nw.size - actual = nw.expected + size = nw.desc.Size + actual = nw.desc.Digest if getBlobBucket(tx, nw.namespace, actual) != nil { return "", errors.Wrapf(errdefs.ErrAlreadyExists, "content %v", actual) } @@ -601,11 +617,11 @@ func (nw *namespacedWriter) Status() (st content.Status, err error) { if nw.w != nil { st, err = nw.w.Status() } else { - st.Offset = nw.size - st.Total = nw.size + st.Offset = nw.desc.Size + st.Total = nw.desc.Size st.StartedAt = nw.started st.UpdatedAt = nw.started - st.Expected = nw.expected + st.Expected = nw.desc.Digest } if err == nil { st.Ref = nw.ref @@ -613,11 +629,11 @@ func (nw *namespacedWriter) Status() (st content.Status, err error) { return } -func (cs *contentStore) ReaderAt(ctx context.Context, dgst digest.Digest) (content.ReaderAt, error) { - if err := cs.checkAccess(ctx, dgst); err != nil { +func (cs *contentStore) ReaderAt(ctx context.Context, desc ocispec.Descriptor) (content.ReaderAt, error) { + if err := cs.checkAccess(ctx, desc.Digest); err != nil { return nil, err } - return cs.Store.ReaderAt(ctx, dgst) + return cs.Store.ReaderAt(ctx, desc) } func (cs *contentStore) checkAccess(ctx context.Context, dgst digest.Digest) error { diff --git a/vendor/github.com/containerd/containerd/oci/spec_opts_unix.go b/vendor/github.com/containerd/containerd/oci/spec_opts_unix.go index 04b18e2f4dde2..27be93a209ff3 100644 --- a/vendor/github.com/containerd/containerd/oci/spec_opts_unix.go +++ b/vendor/github.com/containerd/containerd/oci/spec_opts_unix.go @@ -117,7 +117,7 @@ func WithImageConfig(image Image) SpecOpts { ) switch ic.MediaType { case v1.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config: - p, err := content.ReadBlob(ctx, image.ContentStore(), ic.Digest) + p, err := content.ReadBlob(ctx, image.ContentStore(), ic) if err != nil { return err } diff --git a/vendor/github.com/containerd/containerd/oci/spec_opts_windows.go b/vendor/github.com/containerd/containerd/oci/spec_opts_windows.go index 7fe76ea5bac85..3688a582d29ea 100644 --- a/vendor/github.com/containerd/containerd/oci/spec_opts_windows.go +++ b/vendor/github.com/containerd/containerd/oci/spec_opts_windows.go @@ -44,7 +44,7 @@ func WithImageConfig(image Image) SpecOpts { ) switch ic.MediaType { case v1.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config: - p, err := content.ReadBlob(ctx, image.ContentStore(), ic.Digest) + p, err := content.ReadBlob(ctx, image.ContentStore(), ic) if err != nil { return err } diff --git a/vendor/github.com/containerd/containerd/oci/spec_unix.go b/vendor/github.com/containerd/containerd/oci/spec_unix.go index f791c357fa779..f8d8524ddbcdb 100644 --- a/vendor/github.com/containerd/containerd/oci/spec_unix.go +++ b/vendor/github.com/containerd/containerd/oci/spec_unix.go @@ -153,7 +153,9 @@ func createDefaultSpec(ctx context.Context, id string) (*Spec, error) { }, Linux: &specs.Linux{ MaskedPaths: []string{ + "/proc/acpi", "/proc/kcore", + "/proc/keys", "/proc/latency_stats", "/proc/timer_list", "/proc/timer_stats", diff --git a/vendor/github.com/containerd/containerd/platforms/database.go b/vendor/github.com/containerd/containerd/platforms/database.go index df9cc2392ae6c..8e85448ed028c 100644 --- a/vendor/github.com/containerd/containerd/platforms/database.go +++ b/vendor/github.com/containerd/containerd/platforms/database.go @@ -89,18 +89,21 @@ func normalizeArch(arch, variant string) (string, string) { case "x86_64", "x86-64": arch = "amd64" variant = "" - case "aarch64": + case "aarch64", "arm64": arch = "arm64" - variant = "" // v8 is implied + switch variant { + case "8", "v8": + variant = "" + } case "armhf": arch = "arm" - variant = "" + variant = "v7" case "armel": arch = "arm" variant = "v6" case "arm": switch variant { - case "v7", "7": + case "", "7": variant = "v7" case "5", "6", "8": variant = "v" + variant diff --git a/vendor/github.com/containerd/containerd/platforms/platforms.go b/vendor/github.com/containerd/containerd/platforms/platforms.go index 1f91ed288fe26..29b7ad6b5e0b7 100644 --- a/vendor/github.com/containerd/containerd/platforms/platforms.go +++ b/vendor/github.com/containerd/containerd/platforms/platforms.go @@ -135,7 +135,7 @@ type Matcher interface { // Applications should opt to use `Match` over directly parsing specifiers. func NewMatcher(platform specs.Platform) Matcher { return &matcher{ - Platform: platform, + Platform: Normalize(platform), } } @@ -197,6 +197,9 @@ func Parse(specifier string) (specs.Platform, error) { } p.Architecture, p.Variant = normalizeArch(parts[0], "") + if p.Architecture == "arm" && p.Variant == "v7" { + p.Variant = "" + } if isKnownArch(p.Architecture) { p.OS = runtime.GOOS return p, nil @@ -208,12 +211,18 @@ func Parse(specifier string) (specs.Platform, error) { // about whether or not we know of the platform. p.OS = normalizeOS(parts[0]) p.Architecture, p.Variant = normalizeArch(parts[1], "") + if p.Architecture == "arm" && p.Variant == "v7" { + p.Variant = "" + } return p, nil case 3: // we have a fully specified variant, this is rare p.OS = normalizeOS(parts[0]) p.Architecture, p.Variant = normalizeArch(parts[1], parts[2]) + if p.Architecture == "arm64" && p.Variant == "" { + p.Variant = "v8" + } return p, nil } diff --git a/vendor/github.com/containerd/containerd/remotes/docker/pusher.go b/vendor/github.com/containerd/containerd/remotes/docker/pusher.go index e2caf70286372..c3c0923f08ac1 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/pusher.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/pusher.go @@ -155,9 +155,18 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten location := resp.Header.Get("Location") // Support paths without host in location if strings.HasPrefix(location, "/") { - u := p.base - u.Path = location - location = u.String() + // Support location string containing path and query + qmIndex := strings.Index(location, "?") + if qmIndex > 0 { + u := p.base + u.Path = location[:qmIndex] + u.RawQuery = location[qmIndex+1:] + location = u.String() + } else { + u := p.base + u.Path = location + location = u.String() + } } req, err = http.NewRequest(http.MethodPut, location, nil) diff --git a/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go b/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go index 19a0d98569515..cf1137fc3075f 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go @@ -211,12 +211,12 @@ func (c *Converter) Convert(ctx context.Context, opts ...ConvertOpt) (ocispec.De } ref := remotes.MakeRefKey(ctx, desc) - if err := content.WriteBlob(ctx, c.contentStore, ref, bytes.NewReader(mb), desc.Size, desc.Digest, content.WithLabels(labels)); err != nil { + if err := content.WriteBlob(ctx, c.contentStore, ref, bytes.NewReader(mb), desc, content.WithLabels(labels)); err != nil { return ocispec.Descriptor{}, errors.Wrap(err, "failed to write config") } ref = remotes.MakeRefKey(ctx, config) - if err := content.WriteBlob(ctx, c.contentStore, ref, bytes.NewReader(b), config.Size, config.Digest); err != nil { + if err := content.WriteBlob(ctx, c.contentStore, ref, bytes.NewReader(b), config); err != nil { return ocispec.Descriptor{}, errors.Wrap(err, "failed to write config") } @@ -257,15 +257,15 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro var ( ref = remotes.MakeRefKey(ctx, desc) calc = newBlobStateCalculator() - size = desc.Size ) // size may be unknown, set to zero for content ingest - if size == -1 { - size = 0 + ingestDesc := desc + if ingestDesc.Size == -1 { + ingestDesc.Size = 0 } - cw, err := content.OpenWriter(ctx, c.contentStore, ref, size, desc.Digest) + cw, err := content.OpenWriter(ctx, c.contentStore, content.WithRef(ref), content.WithDescriptor(ingestDesc)) if err != nil { if !errdefs.IsAlreadyExists(err) { return err @@ -274,7 +274,7 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro // TODO: Check if blob -> diff id mapping already exists // TODO: Check if blob empty label exists - ra, err := c.contentStore.ReaderAt(ctx, desc.Digest) + ra, err := c.contentStore.ReaderAt(ctx, desc) if err != nil { return err } @@ -317,7 +317,7 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro eg.Go(func() error { defer pw.Close() - return content.Copy(ctx, cw, io.TeeReader(rc, pw), size, desc.Digest) + return content.Copy(ctx, cw, io.TeeReader(rc, pw), ingestDesc.Size, ingestDesc.Digest) }) if err := eg.Wait(); err != nil { diff --git a/vendor/github.com/containerd/containerd/remotes/handlers.go b/vendor/github.com/containerd/containerd/remotes/handlers.go index f0334d516f31b..5c2d84ce47ae1 100644 --- a/vendor/github.com/containerd/containerd/remotes/handlers.go +++ b/vendor/github.com/containerd/containerd/remotes/handlers.go @@ -81,7 +81,7 @@ func FetchHandler(ingester content.Ingester, fetcher Fetcher) images.HandlerFunc func fetch(ctx context.Context, ingester content.Ingester, fetcher Fetcher, desc ocispec.Descriptor) error { log.G(ctx).Debug("fetch") - cw, err := content.OpenWriter(ctx, ingester, MakeRefKey(ctx, desc), desc.Size, desc.Digest) + cw, err := content.OpenWriter(ctx, ingester, content.WithRef(MakeRefKey(ctx, desc)), content.WithDescriptor(desc)) if err != nil { if errdefs.IsAlreadyExists(err) { return nil @@ -141,7 +141,7 @@ func push(ctx context.Context, provider content.Provider, pusher Pusher, desc oc } defer cw.Close() - ra, err := provider.ReaderAt(ctx, desc.Digest) + ra, err := provider.ReaderAt(ctx, desc) if err != nil { return err } diff --git a/vendor/github.com/containerd/containerd/linux/bundle.go b/vendor/github.com/containerd/containerd/runtime/linux/bundle.go similarity index 96% rename from vendor/github.com/containerd/containerd/linux/bundle.go rename to vendor/github.com/containerd/containerd/runtime/linux/bundle.go index e8745713139eb..990309280b6e0 100644 --- a/vendor/github.com/containerd/containerd/linux/bundle.go +++ b/vendor/github.com/containerd/containerd/runtime/linux/bundle.go @@ -25,9 +25,9 @@ import ( "path/filepath" "github.com/containerd/containerd/events/exchange" - "github.com/containerd/containerd/linux/runctypes" - "github.com/containerd/containerd/linux/shim" - "github.com/containerd/containerd/linux/shim/client" + "github.com/containerd/containerd/runtime/linux/runctypes" + "github.com/containerd/containerd/runtime/shim" + "github.com/containerd/containerd/runtime/shim/client" "github.com/pkg/errors" ) diff --git a/vendor/github.com/containerd/containerd/linux/proc/deleted_state.go b/vendor/github.com/containerd/containerd/runtime/linux/proc/deleted_state.go similarity index 95% rename from vendor/github.com/containerd/containerd/linux/proc/deleted_state.go rename to vendor/github.com/containerd/containerd/runtime/linux/proc/deleted_state.go index 87a3fd0c062d3..ab89c3ecbaf68 100644 --- a/vendor/github.com/containerd/containerd/linux/proc/deleted_state.go +++ b/vendor/github.com/containerd/containerd/runtime/linux/proc/deleted_state.go @@ -22,6 +22,7 @@ import ( "context" "github.com/containerd/console" + "github.com/containerd/containerd/runtime/proc" google_protobuf "github.com/gogo/protobuf/types" "github.com/pkg/errors" ) @@ -65,6 +66,6 @@ func (s *deletedState) SetExited(status int) { // no op } -func (s *deletedState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { +func (s *deletedState) Exec(ctx context.Context, path string, r *ExecConfig) (proc.Process, error) { return nil, errors.Errorf("cannot exec in a deleted state") } diff --git a/vendor/github.com/containerd/containerd/linux/proc/exec.go b/vendor/github.com/containerd/containerd/runtime/linux/proc/exec.go similarity index 97% rename from vendor/github.com/containerd/containerd/linux/proc/exec.go rename to vendor/github.com/containerd/containerd/runtime/linux/proc/exec.go index 5fa667ae6a299..337db1b74cbd2 100644 --- a/vendor/github.com/containerd/containerd/linux/proc/exec.go +++ b/vendor/github.com/containerd/containerd/runtime/linux/proc/exec.go @@ -31,6 +31,7 @@ import ( "golang.org/x/sys/unix" "github.com/containerd/console" + "github.com/containerd/containerd/runtime/proc" "github.com/containerd/fifo" runc "github.com/containerd/go-runc" specs "github.com/opencontainers/runtime-spec/specs-go" @@ -40,7 +41,7 @@ import ( type execProcess struct { wg sync.WaitGroup - State + proc.State mu sync.Mutex id string @@ -51,7 +52,7 @@ type execProcess struct { pid int closers []io.Closer stdin io.Closer - stdio Stdio + stdio proc.Stdio path string spec specs.Process @@ -127,7 +128,7 @@ func (e *execProcess) Stdin() io.Closer { return e.stdin } -func (e *execProcess) Stdio() Stdio { +func (e *execProcess) Stdio() proc.Stdio { return e.stdio } diff --git a/vendor/github.com/containerd/containerd/linux/proc/exec_state.go b/vendor/github.com/containerd/containerd/runtime/linux/proc/exec_state.go similarity index 100% rename from vendor/github.com/containerd/containerd/linux/proc/exec_state.go rename to vendor/github.com/containerd/containerd/runtime/linux/proc/exec_state.go diff --git a/vendor/github.com/containerd/containerd/linux/proc/init.go b/vendor/github.com/containerd/containerd/runtime/linux/proc/init.go similarity index 96% rename from vendor/github.com/containerd/containerd/linux/proc/init.go rename to vendor/github.com/containerd/containerd/runtime/linux/proc/init.go index 82f9ebdf2f7d4..c538b7937944d 100644 --- a/vendor/github.com/containerd/containerd/linux/proc/init.go +++ b/vendor/github.com/containerd/containerd/runtime/linux/proc/init.go @@ -31,9 +31,10 @@ import ( "time" "github.com/containerd/console" - "github.com/containerd/containerd/linux/runctypes" "github.com/containerd/containerd/log" "github.com/containerd/containerd/mount" + "github.com/containerd/containerd/runtime/linux/runctypes" + "github.com/containerd/containerd/runtime/proc" "github.com/containerd/fifo" runc "github.com/containerd/go-runc" "github.com/containerd/typeurl" @@ -63,7 +64,7 @@ type Init struct { id string bundle string console console.Console - platform Platform + platform proc.Platform io runc.IO runtime *runc.Runc status int @@ -71,7 +72,7 @@ type Init struct { pid int closers []io.Closer stdin io.Closer - stdio Stdio + stdio proc.Stdio rootfs string IoUID int IoGID int @@ -94,7 +95,7 @@ func NewRunc(root, path, namespace, runtime, criu string, systemd bool) *runc.Ru } // New returns a new init process -func New(context context.Context, path, workDir, runtimeRoot, namespace, criu string, systemdCgroup bool, platform Platform, r *CreateConfig) (*Init, error) { +func New(context context.Context, path, workDir, runtimeRoot, namespace, criu string, systemdCgroup bool, platform proc.Platform, r *CreateConfig) (*Init, error) { var success bool var options runctypes.CreateOptions @@ -134,7 +135,7 @@ func New(context context.Context, path, workDir, runtimeRoot, namespace, criu st bundle: r.Bundle, runtime: runtime, platform: platform, - stdio: Stdio{ + stdio: proc.Stdio{ Stdin: r.Stdin, Stdout: r.Stdout, Stderr: r.Stderr, @@ -363,7 +364,7 @@ func (p *Init) Runtime() *runc.Runc { } // exec returns a new exec'd process -func (p *Init) exec(context context.Context, path string, r *ExecConfig) (Process, error) { +func (p *Init) exec(context context.Context, path string, r *ExecConfig) (proc.Process, error) { // process exec request var spec specs.Process if err := json.Unmarshal(r.Spec.Value, &spec); err != nil { @@ -376,7 +377,7 @@ func (p *Init) exec(context context.Context, path string, r *ExecConfig) (Proces path: path, parent: p, spec: spec, - stdio: Stdio{ + stdio: proc.Stdio{ Stdin: r.Stdin, Stdout: r.Stdout, Stderr: r.Stderr, @@ -430,7 +431,7 @@ func (p *Init) update(context context.Context, r *google_protobuf.Any) error { } // Stdio of the process -func (p *Init) Stdio() Stdio { +func (p *Init) Stdio() proc.Stdio { return p.stdio } diff --git a/vendor/github.com/containerd/containerd/linux/proc/init_state.go b/vendor/github.com/containerd/containerd/runtime/linux/proc/init_state.go similarity index 97% rename from vendor/github.com/containerd/containerd/linux/proc/init_state.go rename to vendor/github.com/containerd/containerd/runtime/linux/proc/init_state.go index 0cbdc7ac157e7..55c0f05591aed 100644 --- a/vendor/github.com/containerd/containerd/linux/proc/init_state.go +++ b/vendor/github.com/containerd/containerd/runtime/linux/proc/init_state.go @@ -25,6 +25,7 @@ import ( "github.com/containerd/console" "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/runtime/proc" "github.com/containerd/fifo" runc "github.com/containerd/go-runc" google_protobuf "github.com/gogo/protobuf/types" @@ -32,13 +33,13 @@ import ( ) type initState interface { - State + proc.State Pause(context.Context) error Resume(context.Context) error Update(context.Context, *google_protobuf.Any) error Checkpoint(context.Context, *CheckpointConfig) error - Exec(context.Context, string, *ExecConfig) (Process, error) + Exec(context.Context, string, *ExecConfig) (proc.Process, error) } type createdState struct { @@ -130,7 +131,7 @@ func (s *createdState) SetExited(status int) { } } -func (s *createdState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { +func (s *createdState) Exec(ctx context.Context, path string, r *ExecConfig) (proc.Process, error) { s.p.mu.Lock() defer s.p.mu.Unlock() return s.p.exec(ctx, path, r) @@ -272,7 +273,7 @@ func (s *createdCheckpointState) SetExited(status int) { } } -func (s *createdCheckpointState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { +func (s *createdCheckpointState) Exec(ctx context.Context, path string, r *ExecConfig) (proc.Process, error) { s.p.mu.Lock() defer s.p.mu.Unlock() @@ -364,7 +365,7 @@ func (s *runningState) SetExited(status int) { } } -func (s *runningState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { +func (s *runningState) Exec(ctx context.Context, path string, r *ExecConfig) (proc.Process, error) { s.p.mu.Lock() defer s.p.mu.Unlock() return s.p.exec(ctx, path, r) @@ -456,7 +457,7 @@ func (s *pausedState) SetExited(status int) { } } -func (s *pausedState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { +func (s *pausedState) Exec(ctx context.Context, path string, r *ExecConfig) (proc.Process, error) { s.p.mu.Lock() defer s.p.mu.Unlock() @@ -536,7 +537,7 @@ func (s *stoppedState) SetExited(status int) { // no op } -func (s *stoppedState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { +func (s *stoppedState) Exec(ctx context.Context, path string, r *ExecConfig) (proc.Process, error) { s.p.mu.Lock() defer s.p.mu.Unlock() diff --git a/vendor/github.com/containerd/containerd/linux/proc/io.go b/vendor/github.com/containerd/containerd/runtime/linux/proc/io.go similarity index 100% rename from vendor/github.com/containerd/containerd/linux/proc/io.go rename to vendor/github.com/containerd/containerd/runtime/linux/proc/io.go diff --git a/vendor/github.com/containerd/containerd/runtime/linux/proc/process.go b/vendor/github.com/containerd/containerd/runtime/linux/proc/process.go new file mode 100644 index 0000000000000..53252ec604f5f --- /dev/null +++ b/vendor/github.com/containerd/containerd/runtime/linux/proc/process.go @@ -0,0 +1,42 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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. +*/ + +package proc + +import ( + "github.com/pkg/errors" +) + +// RuncRoot is the path to the root runc state directory +const RuncRoot = "/run/containerd/runc" + +func stateName(v interface{}) string { + switch v.(type) { + case *runningState, *execRunningState: + return "running" + case *createdState, *execCreatedState, *createdCheckpointState: + return "created" + case *pausedState: + return "paused" + case *deletedState: + return "deleted" + case *stoppedState: + return "stopped" + } + panic(errors.Errorf("invalid state %v", v)) +} diff --git a/vendor/github.com/containerd/containerd/linux/proc/types.go b/vendor/github.com/containerd/containerd/runtime/linux/proc/types.go similarity index 100% rename from vendor/github.com/containerd/containerd/linux/proc/types.go rename to vendor/github.com/containerd/containerd/runtime/linux/proc/types.go diff --git a/vendor/github.com/containerd/containerd/linux/proc/utils.go b/vendor/github.com/containerd/containerd/runtime/linux/proc/utils.go similarity index 100% rename from vendor/github.com/containerd/containerd/linux/proc/utils.go rename to vendor/github.com/containerd/containerd/runtime/linux/proc/utils.go diff --git a/vendor/github.com/containerd/containerd/linux/process.go b/vendor/github.com/containerd/containerd/runtime/linux/process.go similarity index 97% rename from vendor/github.com/containerd/containerd/linux/process.go rename to vendor/github.com/containerd/containerd/runtime/linux/process.go index 0790d8a52d5f5..02cccd206bb9d 100644 --- a/vendor/github.com/containerd/containerd/linux/process.go +++ b/vendor/github.com/containerd/containerd/runtime/linux/process.go @@ -24,10 +24,10 @@ import ( eventstypes "github.com/containerd/containerd/api/events" "github.com/containerd/containerd/api/types/task" "github.com/containerd/containerd/errdefs" - shim "github.com/containerd/containerd/linux/shim/v1" "github.com/containerd/containerd/runtime" + shim "github.com/containerd/containerd/runtime/shim/v1" + "github.com/containerd/ttrpc" "github.com/pkg/errors" - "github.com/stevvooe/ttrpc" ) // Process implements a linux process diff --git a/vendor/github.com/containerd/containerd/runtime/linux/runctypes/doc.go b/vendor/github.com/containerd/containerd/runtime/linux/runctypes/doc.go new file mode 100644 index 0000000000000..8d074c534723d --- /dev/null +++ b/vendor/github.com/containerd/containerd/runtime/linux/runctypes/doc.go @@ -0,0 +1,17 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package runctypes diff --git a/vendor/github.com/containerd/containerd/linux/runctypes/runc.pb.go b/vendor/github.com/containerd/containerd/runtime/linux/runctypes/runc.pb.go similarity index 90% rename from vendor/github.com/containerd/containerd/linux/runctypes/runc.pb.go rename to vendor/github.com/containerd/containerd/runtime/linux/runctypes/runc.pb.go index 00a27bf06f3a2..c13d233599bd1 100644 --- a/vendor/github.com/containerd/containerd/linux/runctypes/runc.pb.go +++ b/vendor/github.com/containerd/containerd/runtime/linux/runctypes/runc.pb.go @@ -1,11 +1,11 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: github.com/containerd/containerd/linux/runctypes/runc.proto +// source: github.com/containerd/containerd/runtime/linux/runctypes/runc.proto /* Package runctypes is a generated protocol buffer package. It is generated from these files: - github.com/containerd/containerd/linux/runctypes/runc.proto + github.com/containerd/containerd/runtime/linux/runctypes/runc.proto It has these top-level messages: RuncOptions @@ -1408,43 +1408,43 @@ var ( ) func init() { - proto.RegisterFile("github.com/containerd/containerd/linux/runctypes/runc.proto", fileDescriptorRunc) + proto.RegisterFile("github.com/containerd/containerd/runtime/linux/runctypes/runc.proto", fileDescriptorRunc) } var fileDescriptorRunc = []byte{ - // 540 bytes of a gzipped FileDescriptorProto + // 541 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x93, 0xc1, 0x6e, 0xd3, 0x40, 0x10, 0x86, 0x6b, 0xda, 0x26, 0xce, 0xa4, 0x29, 0xb0, 0x50, 0xc9, 0x14, 0x91, 0x86, 0x00, 0x52, - 0xb8, 0xa4, 0x12, 0x88, 0x0b, 0xbd, 0xb5, 0x45, 0xa8, 0x02, 0x4a, 0x65, 0x5a, 0x09, 0x71, 0x59, - 0xb9, 0xeb, 0x21, 0x59, 0x25, 0xde, 0x59, 0xed, 0xae, 0xa9, 0x73, 0xeb, 0x13, 0xf0, 0x5c, 0x3d, - 0x72, 0xe4, 0x84, 0x68, 0x5e, 0x04, 0xe4, 0x75, 0x1c, 0xb8, 0x72, 0xe5, 0xf6, 0xcf, 0xf7, 0x8f, - 0x3d, 0xa3, 0x7f, 0x35, 0xb0, 0x37, 0x92, 0x6e, 0x9c, 0x9f, 0x0f, 0x05, 0x65, 0xbb, 0x82, 0x94, - 0x4b, 0xa4, 0x42, 0x93, 0xfe, 0x2d, 0xa7, 0x52, 0xe5, 0xc5, 0xae, 0xc9, 0x95, 0x70, 0x33, 0x8d, - 0xd6, 0xab, 0xa1, 0x36, 0xe4, 0x88, 0x6d, 0xfd, 0x69, 0x1b, 0xfa, 0xb6, 0x61, 0x69, 0x6e, 0xdf, - 0x1d, 0xd1, 0x88, 0x7c, 0xc7, 0x6e, 0xa9, 0xaa, 0xe6, 0xfe, 0xd7, 0x00, 0xda, 0x71, 0xae, 0xc4, - 0x7b, 0xed, 0x24, 0x29, 0xcb, 0x22, 0x68, 0x9a, 0x5c, 0x39, 0x99, 0x61, 0x14, 0xf4, 0x82, 0x41, - 0x2b, 0xae, 0x4b, 0xf6, 0x10, 0x36, 0x16, 0x92, 0x1b, 0x22, 0x17, 0xdd, 0xf0, 0x76, 0x7b, 0xc1, - 0x62, 0x22, 0xc7, 0xee, 0x43, 0x4b, 0x18, 0x99, 0x73, 0x9d, 0xb8, 0x71, 0xb4, 0xea, 0xfd, 0xb0, - 0x04, 0x27, 0x89, 0x1b, 0xb3, 0x27, 0xb0, 0x69, 0x67, 0xd6, 0x61, 0x96, 0x72, 0x31, 0x32, 0x94, - 0xeb, 0x68, 0xad, 0x17, 0x0c, 0xc2, 0xb8, 0xb3, 0xa0, 0x07, 0x1e, 0xf6, 0x2f, 0x57, 0xa1, 0x73, - 0x60, 0x30, 0x71, 0x58, 0xaf, 0xd4, 0x87, 0x8e, 0x22, 0xae, 0xe5, 0x17, 0x72, 0xd5, 0xe4, 0xc0, - 0x7f, 0xd7, 0x56, 0x74, 0x52, 0x32, 0x3f, 0xf9, 0x1e, 0x84, 0xa4, 0x51, 0x71, 0x27, 0xb4, 0x5f, - 0x2c, 0x8c, 0x9b, 0x65, 0x7d, 0x2a, 0x34, 0x7b, 0x06, 0x5b, 0x58, 0x38, 0x34, 0x2a, 0x99, 0xf2, - 0x5c, 0xc9, 0x82, 0x5b, 0x12, 0x13, 0x74, 0xd6, 0x2f, 0x18, 0xc6, 0x77, 0x6a, 0xf3, 0x4c, 0xc9, - 0xe2, 0x43, 0x65, 0xb1, 0x6d, 0x08, 0x1d, 0x9a, 0x4c, 0xaa, 0x64, 0xba, 0xd8, 0x72, 0x59, 0xb3, - 0x07, 0x00, 0x9f, 0xe5, 0x14, 0xf9, 0x94, 0xc4, 0xc4, 0x46, 0xeb, 0xde, 0x6d, 0x95, 0xe4, 0x6d, - 0x09, 0xd8, 0x53, 0xb8, 0x85, 0x99, 0x76, 0x33, 0xae, 0x92, 0x0c, 0xad, 0x4e, 0x04, 0xda, 0xa8, - 0xd1, 0x5b, 0x1d, 0xb4, 0xe2, 0x9b, 0x9e, 0x1f, 0x2f, 0x71, 0x99, 0x68, 0x95, 0x84, 0xe5, 0x19, - 0xa5, 0x18, 0x35, 0xab, 0x44, 0x17, 0xec, 0x1d, 0xa5, 0xc8, 0x1e, 0xc3, 0xa6, 0x22, 0xae, 0xf0, - 0x82, 0x4f, 0x70, 0x66, 0xa4, 0x1a, 0x45, 0xa1, 0x1f, 0xb8, 0xa1, 0xe8, 0x18, 0x2f, 0xde, 0x54, - 0x8c, 0xed, 0x40, 0xdb, 0x8e, 0x65, 0x56, 0xe7, 0xda, 0xf2, 0xff, 0x81, 0x12, 0x55, 0xa1, 0xb2, - 0x2d, 0x68, 0x48, 0xe2, 0xb9, 0x4c, 0x23, 0xe8, 0x05, 0x83, 0x4e, 0xbc, 0x2e, 0xe9, 0x4c, 0xa6, - 0x0b, 0x3c, 0x92, 0x69, 0xd4, 0xae, 0xf1, 0x6b, 0x99, 0xf6, 0x7f, 0x05, 0x70, 0xfb, 0x60, 0x8c, - 0x62, 0xa2, 0x49, 0x2a, 0x57, 0x3f, 0x03, 0x83, 0x35, 0x2c, 0x64, 0x9d, 0xbe, 0xd7, 0xff, 0x6b, - 0xec, 0xfd, 0x17, 0xb0, 0x79, 0x62, 0x48, 0xa0, 0xb5, 0x87, 0xe8, 0x12, 0x39, 0xb5, 0xec, 0x11, - 0x34, 0xb1, 0x40, 0xc1, 0x65, 0x5a, 0xdd, 0xc5, 0x3e, 0xcc, 0x7f, 0xec, 0x34, 0x5e, 0x15, 0x28, - 0x8e, 0x0e, 0xe3, 0x46, 0x69, 0x1d, 0xa5, 0xfb, 0xa7, 0x57, 0xd7, 0xdd, 0x95, 0xef, 0xd7, 0xdd, - 0x95, 0xcb, 0x79, 0x37, 0xb8, 0x9a, 0x77, 0x83, 0x6f, 0xf3, 0x6e, 0xf0, 0x73, 0xde, 0x0d, 0x3e, - 0xbd, 0xfc, 0xd7, 0x83, 0xde, 0x5b, 0xaa, 0x8f, 0x2b, 0xe7, 0x0d, 0x7f, 0xab, 0xcf, 0x7f, 0x07, - 0x00, 0x00, 0xff, 0xff, 0xb1, 0xca, 0x85, 0x39, 0x17, 0x04, 0x00, 0x00, + 0xb8, 0xa4, 0x12, 0x88, 0x13, 0xb7, 0xa6, 0x08, 0x55, 0x40, 0xa9, 0x0c, 0x95, 0x10, 0x42, 0x5a, + 0xb9, 0xeb, 0x21, 0x59, 0xc5, 0xde, 0x59, 0x79, 0xd7, 0xd4, 0xb9, 0xf5, 0x09, 0x78, 0xae, 0x1e, + 0x39, 0x72, 0x42, 0x34, 0x2f, 0x02, 0xf2, 0xda, 0x0e, 0x9c, 0x39, 0x72, 0xfb, 0xe7, 0xfb, 0xc7, + 0x9e, 0xd1, 0xbf, 0x1a, 0x98, 0x4c, 0xa5, 0x9d, 0xe5, 0x67, 0x63, 0x41, 0xe9, 0xbe, 0x20, 0x65, + 0x23, 0xa9, 0x30, 0x8b, 0xff, 0x96, 0x59, 0xae, 0xac, 0x4c, 0x71, 0x3f, 0x91, 0x2a, 0x2f, 0xca, + 0x4a, 0xd8, 0x85, 0x46, 0xe3, 0xd4, 0x58, 0x67, 0x64, 0x89, 0xed, 0xfc, 0x69, 0x1f, 0xbb, 0xb6, + 0x71, 0x69, 0xee, 0xde, 0x9e, 0xd2, 0x94, 0x5c, 0xc7, 0x7e, 0xa9, 0xaa, 0xe6, 0xe1, 0x57, 0x0f, + 0xba, 0x61, 0xae, 0xc4, 0x5b, 0x6d, 0x25, 0x29, 0xc3, 0x02, 0x68, 0xd7, 0x23, 0x02, 0x6f, 0xe0, + 0x8d, 0x3a, 0x61, 0x53, 0xb2, 0xfb, 0xb0, 0x55, 0x4b, 0x9e, 0x11, 0xd9, 0xe0, 0x9a, 0xb3, 0xbb, + 0x35, 0x0b, 0x89, 0x2c, 0xbb, 0x0b, 0x1d, 0x91, 0xc9, 0x9c, 0xeb, 0xc8, 0xce, 0x82, 0x75, 0xe7, + 0xfb, 0x25, 0x38, 0x89, 0xec, 0x8c, 0x3d, 0x82, 0x6d, 0xb3, 0x30, 0x16, 0xd3, 0x98, 0x8b, 0x69, + 0x46, 0xb9, 0x0e, 0x36, 0x06, 0xde, 0xc8, 0x0f, 0x7b, 0x35, 0x9d, 0x38, 0x38, 0xbc, 0x58, 0x87, + 0xde, 0x24, 0xc3, 0xc8, 0x62, 0xb3, 0xd2, 0x10, 0x7a, 0x8a, 0xb8, 0x96, 0x5f, 0xc8, 0x56, 0x93, + 0x3d, 0xf7, 0x5d, 0x57, 0xd1, 0x49, 0xc9, 0xdc, 0xe4, 0x3b, 0xe0, 0x93, 0x46, 0xc5, 0xad, 0xd0, + 0x6e, 0x31, 0x3f, 0x6c, 0x97, 0xf5, 0x7b, 0xa1, 0xd9, 0x13, 0xd8, 0xc1, 0xc2, 0x62, 0xa6, 0xa2, + 0x84, 0xe7, 0x4a, 0x16, 0xdc, 0x90, 0x98, 0xa3, 0x35, 0x6e, 0x41, 0x3f, 0xbc, 0xd5, 0x98, 0xa7, + 0x4a, 0x16, 0xef, 0x2a, 0x8b, 0xed, 0x82, 0x6f, 0x31, 0x4b, 0xa5, 0x8a, 0x92, 0x7a, 0xcb, 0x55, + 0xcd, 0xee, 0x01, 0x7c, 0x96, 0x09, 0xf2, 0x84, 0xc4, 0xdc, 0x04, 0x9b, 0xce, 0xed, 0x94, 0xe4, + 0x75, 0x09, 0xd8, 0x63, 0xb8, 0x81, 0xa9, 0xb6, 0x0b, 0xae, 0xa2, 0x14, 0x8d, 0x8e, 0x04, 0x9a, + 0xa0, 0x35, 0x58, 0x1f, 0x75, 0xc2, 0xeb, 0x8e, 0x1f, 0xaf, 0x70, 0x99, 0x68, 0x95, 0x84, 0xe1, + 0x29, 0xc5, 0x18, 0xb4, 0xab, 0x44, 0x6b, 0xf6, 0x86, 0x62, 0x64, 0x0f, 0x61, 0x5b, 0x11, 0x57, + 0x78, 0xce, 0xe7, 0xb8, 0xc8, 0xa4, 0x9a, 0x06, 0xbe, 0x1b, 0xb8, 0xa5, 0xe8, 0x18, 0xcf, 0x5f, + 0x55, 0x8c, 0xed, 0x41, 0xd7, 0xcc, 0x64, 0xda, 0xe4, 0xda, 0x71, 0xff, 0x81, 0x12, 0x55, 0xa1, + 0xb2, 0x1d, 0x68, 0x49, 0xe2, 0xb9, 0x8c, 0x03, 0x18, 0x78, 0xa3, 0x5e, 0xb8, 0x29, 0xe9, 0x54, + 0xc6, 0x35, 0x9e, 0xca, 0x38, 0xe8, 0x36, 0xf8, 0xa5, 0x8c, 0x87, 0xbf, 0x3c, 0xb8, 0x39, 0x99, + 0xa1, 0x98, 0x6b, 0x92, 0xca, 0x36, 0xcf, 0xc0, 0x60, 0x03, 0x0b, 0xd9, 0xa4, 0xef, 0xf4, 0xff, + 0x1a, 0xfb, 0xf0, 0x19, 0x6c, 0x9f, 0x64, 0x24, 0xd0, 0x98, 0x43, 0xb4, 0x91, 0x4c, 0x0c, 0x7b, + 0x00, 0x6d, 0x2c, 0x50, 0x70, 0x19, 0x57, 0x77, 0x71, 0x00, 0xcb, 0x1f, 0x7b, 0xad, 0x17, 0x05, + 0x8a, 0xa3, 0xc3, 0xb0, 0x55, 0x5a, 0x47, 0xf1, 0xc1, 0xa7, 0xcb, 0xab, 0xfe, 0xda, 0xf7, 0xab, + 0xfe, 0xda, 0xc5, 0xb2, 0xef, 0x5d, 0x2e, 0xfb, 0xde, 0xb7, 0x65, 0xdf, 0xfb, 0xb9, 0xec, 0x7b, + 0x1f, 0x0f, 0xfe, 0xf5, 0xb0, 0x9f, 0xaf, 0xd4, 0x87, 0xb5, 0xb3, 0x96, 0xbb, 0xd9, 0xa7, 0xbf, + 0x03, 0x00, 0x00, 0xff, 0xff, 0x18, 0xa1, 0x4b, 0x5b, 0x27, 0x04, 0x00, 0x00, } diff --git a/vendor/github.com/containerd/containerd/linux/runctypes/runc.proto b/vendor/github.com/containerd/containerd/runtime/linux/runctypes/runc.proto similarity index 89% rename from vendor/github.com/containerd/containerd/linux/runctypes/runc.proto rename to vendor/github.com/containerd/containerd/runtime/linux/runctypes/runc.proto index a73b1cafa7d47..ddd3f8d112892 100644 --- a/vendor/github.com/containerd/containerd/linux/runctypes/runc.proto +++ b/vendor/github.com/containerd/containerd/runtime/linux/runctypes/runc.proto @@ -4,7 +4,7 @@ package containerd.linux.runc; import weak "gogoproto/gogo.proto"; -option go_package = "github.com/containerd/containerd/linux/runctypes;runctypes"; +option go_package = "github.com/containerd/containerd/runtime/linux/runctypes;runctypes"; message RuncOptions { string runtime = 1; diff --git a/vendor/github.com/containerd/containerd/linux/runtime.go b/vendor/github.com/containerd/containerd/runtime/linux/runtime.go similarity index 98% rename from vendor/github.com/containerd/containerd/linux/runtime.go rename to vendor/github.com/containerd/containerd/runtime/linux/runtime.go index 26dedbfa97983..5dcbca56e72e0 100644 --- a/vendor/github.com/containerd/containerd/linux/runtime.go +++ b/vendor/github.com/containerd/containerd/runtime/linux/runtime.go @@ -33,9 +33,6 @@ import ( "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/events/exchange" "github.com/containerd/containerd/identifiers" - "github.com/containerd/containerd/linux/proc" - "github.com/containerd/containerd/linux/runctypes" - shim "github.com/containerd/containerd/linux/shim/v1" "github.com/containerd/containerd/log" "github.com/containerd/containerd/metadata" "github.com/containerd/containerd/mount" @@ -43,6 +40,9 @@ import ( "github.com/containerd/containerd/platforms" "github.com/containerd/containerd/plugin" "github.com/containerd/containerd/runtime" + "github.com/containerd/containerd/runtime/linux/proc" + "github.com/containerd/containerd/runtime/linux/runctypes" + shim "github.com/containerd/containerd/runtime/shim/v1" runc "github.com/containerd/go-runc" "github.com/containerd/typeurl" ptypes "github.com/gogo/protobuf/types" diff --git a/vendor/github.com/containerd/containerd/linux/task.go b/vendor/github.com/containerd/containerd/runtime/linux/task.go similarity index 98% rename from vendor/github.com/containerd/containerd/linux/task.go rename to vendor/github.com/containerd/containerd/runtime/linux/task.go index eca82fbbd0f58..43c93eaba87e1 100644 --- a/vendor/github.com/containerd/containerd/linux/task.go +++ b/vendor/github.com/containerd/containerd/runtime/linux/task.go @@ -28,13 +28,13 @@ import ( "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/events/exchange" "github.com/containerd/containerd/identifiers" - "github.com/containerd/containerd/linux/shim/client" - shim "github.com/containerd/containerd/linux/shim/v1" "github.com/containerd/containerd/runtime" + "github.com/containerd/containerd/runtime/shim/client" + shim "github.com/containerd/containerd/runtime/shim/v1" runc "github.com/containerd/go-runc" + "github.com/containerd/ttrpc" "github.com/gogo/protobuf/types" "github.com/pkg/errors" - "github.com/stevvooe/ttrpc" ) // Task on a linux based system diff --git a/vendor/github.com/containerd/containerd/linux/proc/process.go b/vendor/github.com/containerd/containerd/runtime/proc/proc.go similarity index 80% rename from vendor/github.com/containerd/containerd/linux/proc/process.go rename to vendor/github.com/containerd/containerd/runtime/proc/proc.go index 135f9962b14dd..02bc9bda816f8 100644 --- a/vendor/github.com/containerd/containerd/linux/proc/process.go +++ b/vendor/github.com/containerd/containerd/runtime/proc/proc.go @@ -1,5 +1,3 @@ -// +build !windows - /* Copyright The containerd Authors. @@ -25,12 +23,8 @@ import ( "time" "github.com/containerd/console" - "github.com/pkg/errors" ) -// RuncRoot is the path to the root runc state directory -const RuncRoot = "/run/containerd/runc" - // Stdio of a process type Stdio struct { Stdin string @@ -44,7 +38,7 @@ func (s Stdio) IsNull() bool { return s.Stdin == "" && s.Stdout == "" && s.Stderr == "" } -// Process on a linux system +// Process on a system type Process interface { State // ID returns the id for the process @@ -79,22 +73,6 @@ type State interface { SetExited(status int) } -func stateName(v interface{}) string { - switch v.(type) { - case *runningState, *execRunningState: - return "running" - case *createdState, *execCreatedState, *createdCheckpointState: - return "created" - case *pausedState: - return "paused" - case *deletedState: - return "deleted" - case *stoppedState: - return "stopped" - } - panic(errors.Errorf("invalid state %v", v)) -} - // Platform handles platform-specific behavior that may differs across // platform implementations type Platform interface { diff --git a/vendor/github.com/containerd/containerd/linux/shim/client/client.go b/vendor/github.com/containerd/containerd/runtime/shim/client/client.go similarity index 97% rename from vendor/github.com/containerd/containerd/linux/shim/client/client.go rename to vendor/github.com/containerd/containerd/runtime/shim/client/client.go index f779d071f6ef2..009ccc93982c8 100644 --- a/vendor/github.com/containerd/containerd/linux/shim/client/client.go +++ b/vendor/github.com/containerd/containerd/runtime/shim/client/client.go @@ -31,14 +31,14 @@ import ( "golang.org/x/sys/unix" + "github.com/containerd/ttrpc" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/stevvooe/ttrpc" "github.com/containerd/containerd/events" - "github.com/containerd/containerd/linux/shim" - shimapi "github.com/containerd/containerd/linux/shim/v1" "github.com/containerd/containerd/log" + "github.com/containerd/containerd/runtime/shim" + shimapi "github.com/containerd/containerd/runtime/shim/v1" "github.com/containerd/containerd/sys" ptypes "github.com/gogo/protobuf/types" ) @@ -136,6 +136,7 @@ func newCommand(binary, daemonAddress string, debug bool, config shim.Config, so // will be mounted by the shim cmd.SysProcAttr = getSysProcAttr() cmd.ExtraFiles = append(cmd.ExtraFiles, socket) + cmd.Env = append(os.Environ(), "GOMAXPROCS=2") if debug { cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr diff --git a/vendor/github.com/containerd/containerd/linux/shim/client/client_linux.go b/vendor/github.com/containerd/containerd/runtime/shim/client/client_linux.go similarity index 100% rename from vendor/github.com/containerd/containerd/linux/shim/client/client_linux.go rename to vendor/github.com/containerd/containerd/runtime/shim/client/client_linux.go diff --git a/vendor/github.com/containerd/containerd/linux/shim/client/client_unix.go b/vendor/github.com/containerd/containerd/runtime/shim/client/client_unix.go similarity index 100% rename from vendor/github.com/containerd/containerd/linux/shim/client/client_unix.go rename to vendor/github.com/containerd/containerd/runtime/shim/client/client_unix.go diff --git a/vendor/github.com/containerd/containerd/linux/shim/local.go b/vendor/github.com/containerd/containerd/runtime/shim/local.go similarity index 98% rename from vendor/github.com/containerd/containerd/linux/shim/local.go rename to vendor/github.com/containerd/containerd/runtime/shim/local.go index 1600ef6f1e81c..8206b869c3293 100644 --- a/vendor/github.com/containerd/containerd/linux/shim/local.go +++ b/vendor/github.com/containerd/containerd/runtime/shim/local.go @@ -22,8 +22,8 @@ import ( "context" "path/filepath" - shimapi "github.com/containerd/containerd/linux/shim/v1" "github.com/containerd/containerd/mount" + shimapi "github.com/containerd/containerd/runtime/shim/v1" ptypes "github.com/gogo/protobuf/types" ) diff --git a/vendor/github.com/containerd/containerd/linux/shim/reaper.go b/vendor/github.com/containerd/containerd/runtime/shim/reaper.go similarity index 100% rename from vendor/github.com/containerd/containerd/linux/shim/reaper.go rename to vendor/github.com/containerd/containerd/runtime/shim/reaper.go diff --git a/vendor/github.com/containerd/containerd/linux/shim/service.go b/vendor/github.com/containerd/containerd/runtime/shim/service.go similarity index 97% rename from vendor/github.com/containerd/containerd/linux/shim/service.go rename to vendor/github.com/containerd/containerd/runtime/shim/service.go index 02ac59ef50775..379e5cf5bc129 100644 --- a/vendor/github.com/containerd/containerd/linux/shim/service.go +++ b/vendor/github.com/containerd/containerd/runtime/shim/service.go @@ -29,12 +29,13 @@ import ( "github.com/containerd/containerd/api/types/task" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/events" - "github.com/containerd/containerd/linux/proc" - "github.com/containerd/containerd/linux/runctypes" - shimapi "github.com/containerd/containerd/linux/shim/v1" "github.com/containerd/containerd/log" "github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/runtime" + "github.com/containerd/containerd/runtime/linux/proc" + "github.com/containerd/containerd/runtime/linux/runctypes" + rproc "github.com/containerd/containerd/runtime/proc" + shimapi "github.com/containerd/containerd/runtime/shim/v1" runc "github.com/containerd/go-runc" "github.com/containerd/typeurl" ptypes "github.com/gogo/protobuf/types" @@ -78,7 +79,7 @@ func NewService(config Config, publisher events.Publisher) (*Service, error) { s := &Service{ config: config, context: ctx, - processes: make(map[string]proc.Process), + processes: make(map[string]rproc.Process), events: make(chan interface{}, 128), ec: Default.Subscribe(), } @@ -96,9 +97,9 @@ type Service struct { config Config context context.Context - processes map[string]proc.Process + processes map[string]rproc.Process events chan interface{} - platform proc.Platform + platform rproc.Platform ec chan runc.Exit // Filled by Create() diff --git a/vendor/github.com/containerd/containerd/linux/shim/service_linux.go b/vendor/github.com/containerd/containerd/runtime/shim/service_linux.go similarity index 100% rename from vendor/github.com/containerd/containerd/linux/shim/service_linux.go rename to vendor/github.com/containerd/containerd/runtime/shim/service_linux.go diff --git a/vendor/github.com/containerd/containerd/linux/shim/service_unix.go b/vendor/github.com/containerd/containerd/runtime/shim/service_unix.go similarity index 100% rename from vendor/github.com/containerd/containerd/linux/shim/service_unix.go rename to vendor/github.com/containerd/containerd/runtime/shim/service_unix.go diff --git a/vendor/github.com/containerd/containerd/runtime/shim/v1/doc.go b/vendor/github.com/containerd/containerd/runtime/shim/v1/doc.go new file mode 100644 index 0000000000000..55bfe47d72931 --- /dev/null +++ b/vendor/github.com/containerd/containerd/runtime/shim/v1/doc.go @@ -0,0 +1,17 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package shim diff --git a/vendor/github.com/containerd/containerd/linux/shim/v1/shim.pb.go b/vendor/github.com/containerd/containerd/runtime/shim/v1/shim.pb.go similarity index 93% rename from vendor/github.com/containerd/containerd/linux/shim/v1/shim.pb.go rename to vendor/github.com/containerd/containerd/runtime/shim/v1/shim.pb.go index 2f39e6a781a53..e4dae343d53ff 100644 --- a/vendor/github.com/containerd/containerd/linux/shim/v1/shim.pb.go +++ b/vendor/github.com/containerd/containerd/runtime/shim/v1/shim.pb.go @@ -1,11 +1,11 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: github.com/containerd/containerd/linux/shim/v1/shim.proto +// source: github.com/containerd/containerd/runtime/shim/v1/shim.proto /* Package shim is a generated protocol buffer package. It is generated from these files: - github.com/containerd/containerd/linux/shim/v1/shim.proto + github.com/containerd/containerd/runtime/shim/v1/shim.proto It has these top-level messages: CreateTaskRequest @@ -50,7 +50,7 @@ import strings "strings" import reflect "reflect" import context "context" -import ttrpc "github.com/stevvooe/ttrpc" +import ttrpc "github.com/containerd/ttrpc" import io "io" @@ -4352,80 +4352,80 @@ var ( ) func init() { - proto.RegisterFile("github.com/containerd/containerd/linux/shim/v1/shim.proto", fileDescriptorShim) + proto.RegisterFile("github.com/containerd/containerd/runtime/shim/v1/shim.proto", fileDescriptorShim) } var fileDescriptorShim = []byte{ - // 1133 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0xcd, 0x6e, 0xdb, 0x46, - 0x10, 0x36, 0x69, 0xfd, 0x8e, 0x22, 0xd7, 0xde, 0x3a, 0x2e, 0xa3, 0x00, 0xb2, 0x40, 0xa0, 0x81, - 0x8b, 0x22, 0x54, 0x2d, 0x17, 0x49, 0xd3, 0x02, 0x01, 0x6c, 0x27, 0x28, 0x8c, 0xd6, 0x88, 0x41, - 0x3b, 0x4d, 0xd0, 0xa2, 0x30, 0x68, 0x71, 0x2d, 0x2d, 0x2c, 0x91, 0x0c, 0x77, 0xe9, 0xda, 0x3d, - 0xf5, 0xd4, 0x73, 0x1f, 0xa7, 0x8f, 0xe0, 0x43, 0x0e, 0x3d, 0xf6, 0x94, 0x36, 0xba, 0xf7, 0x1d, - 0x8a, 0xfd, 0x91, 0x49, 0x49, 0x66, 0x48, 0xf9, 0x62, 0x71, 0x76, 0xbf, 0xd9, 0x9d, 0x9d, 0xef, - 0xdb, 0x99, 0x35, 0x3c, 0xe9, 0x11, 0xd6, 0x8f, 0x4e, 0xac, 0xae, 0x3f, 0x6c, 0x77, 0x7d, 0x8f, - 0x39, 0xc4, 0xc3, 0xa1, 0x9b, 0xfc, 0x1c, 0x10, 0x2f, 0xba, 0x68, 0xd3, 0x3e, 0x19, 0xb6, 0xcf, - 0x37, 0xc5, 0xaf, 0x15, 0x84, 0x3e, 0xf3, 0x51, 0x2b, 0x06, 0x59, 0x61, 0xe4, 0x31, 0x32, 0xc4, - 0x96, 0x00, 0x5b, 0x02, 0x74, 0xbe, 0xd9, 0xb8, 0xd7, 0xf3, 0xfd, 0xde, 0x00, 0xb7, 0x05, 0xfe, - 0x24, 0x3a, 0x6d, 0x3b, 0xde, 0xa5, 0x74, 0x6e, 0xdc, 0x9f, 0x9e, 0xc2, 0xc3, 0x80, 0x8d, 0x27, - 0x57, 0x7b, 0x7e, 0xcf, 0x17, 0x9f, 0x6d, 0xfe, 0xa5, 0x46, 0xd7, 0xa7, 0x5d, 0xf8, 0x8e, 0x94, - 0x39, 0xc3, 0x40, 0x01, 0x1e, 0x65, 0x9e, 0xc5, 0x09, 0x48, 0x9b, 0x5d, 0x06, 0x98, 0xb6, 0x87, - 0x7e, 0xe4, 0x31, 0xe5, 0xf7, 0xf5, 0x1c, 0x7e, 0xcc, 0xa1, 0x67, 0xe2, 0x8f, 0xf4, 0x35, 0xff, - 0xd3, 0x61, 0x65, 0x37, 0xc4, 0x0e, 0xc3, 0x47, 0x0e, 0x3d, 0xb3, 0xf1, 0x9b, 0x08, 0x53, 0x86, - 0xd6, 0x40, 0x27, 0xae, 0xa1, 0xb5, 0xb4, 0x8d, 0xea, 0x4e, 0x69, 0xf4, 0x6e, 0x5d, 0xdf, 0x7b, - 0x66, 0xeb, 0xc4, 0x45, 0x6b, 0x50, 0x3a, 0x89, 0x3c, 0x77, 0x80, 0x0d, 0x9d, 0xcf, 0xd9, 0xca, - 0x42, 0x06, 0x94, 0x55, 0x06, 0x8d, 0x45, 0x31, 0x31, 0x36, 0x51, 0x1b, 0x4a, 0xa1, 0xef, 0xb3, - 0x53, 0x6a, 0x14, 0x5a, 0x8b, 0x1b, 0xb5, 0xce, 0x27, 0x56, 0x22, 0xeb, 0x22, 0x24, 0x6b, 0x9f, - 0x1f, 0xc5, 0x56, 0x30, 0xd4, 0x80, 0x0a, 0xc3, 0xe1, 0x90, 0x78, 0xce, 0xc0, 0x28, 0xb6, 0xb4, - 0x8d, 0x8a, 0x7d, 0x6d, 0xa3, 0x55, 0x28, 0x52, 0xe6, 0x12, 0xcf, 0x28, 0x89, 0x4d, 0xa4, 0xc1, - 0x83, 0xa2, 0xcc, 0xf5, 0x23, 0x66, 0x94, 0x65, 0x50, 0xd2, 0x52, 0xe3, 0x38, 0x0c, 0x8d, 0xca, - 0xf5, 0x38, 0x0e, 0x43, 0xd4, 0x04, 0xe8, 0xf6, 0x71, 0xf7, 0x2c, 0xf0, 0x89, 0xc7, 0x8c, 0xaa, - 0x98, 0x4b, 0x8c, 0xa0, 0xcf, 0x61, 0x25, 0x70, 0x42, 0xec, 0xb1, 0xe3, 0x04, 0x0c, 0x04, 0x6c, - 0x59, 0x4e, 0xec, 0xc6, 0x60, 0x0b, 0xca, 0x7e, 0xc0, 0x88, 0xef, 0x51, 0xa3, 0xd6, 0xd2, 0x36, - 0x6a, 0x9d, 0x55, 0x4b, 0xd2, 0x6c, 0x8d, 0x69, 0xb6, 0xb6, 0xbd, 0x4b, 0x7b, 0x0c, 0x32, 0x1f, - 0x00, 0x4a, 0xa6, 0x9b, 0x06, 0xbe, 0x47, 0x31, 0x5a, 0x86, 0xc5, 0x40, 0x25, 0xbc, 0x6e, 0xf3, - 0x4f, 0xf3, 0x77, 0x0d, 0x96, 0x9e, 0xe1, 0x01, 0x66, 0x38, 0x1d, 0x84, 0xd6, 0xa1, 0x86, 0x2f, - 0x08, 0x3b, 0xa6, 0xcc, 0x61, 0x11, 0x15, 0x9c, 0xd4, 0x6d, 0xe0, 0x43, 0x87, 0x62, 0x04, 0x6d, - 0x43, 0x95, 0x5b, 0xd8, 0x3d, 0x76, 0x98, 0x60, 0xa6, 0xd6, 0x69, 0xcc, 0xc4, 0x77, 0x34, 0x96, - 0xe1, 0x4e, 0xe5, 0xea, 0xdd, 0xfa, 0xc2, 0x1f, 0xff, 0xac, 0x6b, 0x76, 0x45, 0xba, 0x6d, 0x33, - 0xd3, 0x82, 0x55, 0x19, 0xc7, 0x41, 0xe8, 0x77, 0x31, 0xa5, 0x19, 0x12, 0x31, 0xff, 0xd4, 0x00, - 0x3d, 0xbf, 0xc0, 0xdd, 0x7c, 0xf0, 0x09, 0xba, 0xf5, 0x34, 0xba, 0x17, 0x6f, 0xa6, 0xbb, 0x90, - 0x42, 0x77, 0x71, 0x82, 0xee, 0x0d, 0x28, 0xd0, 0x00, 0x77, 0x85, 0x66, 0xd2, 0xe8, 0x11, 0x08, - 0xf3, 0x2e, 0x7c, 0x3c, 0x11, 0xb9, 0xcc, 0xbb, 0xf9, 0x1a, 0x96, 0x6d, 0x4c, 0xc9, 0xaf, 0xf8, - 0x80, 0x5d, 0x66, 0x1d, 0x67, 0x15, 0x8a, 0xbf, 0x10, 0x97, 0xf5, 0x15, 0x17, 0xd2, 0xe0, 0xa1, - 0xf5, 0x31, 0xe9, 0xf5, 0x25, 0x07, 0x75, 0x5b, 0x59, 0xe6, 0x03, 0xb8, 0xc3, 0x89, 0xc2, 0x59, - 0x39, 0x7d, 0xab, 0x43, 0x5d, 0x01, 0x95, 0x16, 0xe6, 0xbd, 0xa0, 0x4a, 0x3b, 0x8b, 0xb1, 0x76, - 0xb6, 0x78, 0xba, 0x84, 0x6c, 0x78, 0x1a, 0x97, 0x3a, 0xf7, 0x93, 0x17, 0xf3, 0x7c, 0x53, 0xdd, - 0x4d, 0xa9, 0x23, 0x5b, 0x41, 0x63, 0x46, 0x8a, 0x37, 0x33, 0x52, 0x4a, 0x61, 0xa4, 0x3c, 0xc1, - 0x48, 0x92, 0xf3, 0xca, 0x14, 0xe7, 0x53, 0x92, 0xae, 0x7e, 0x58, 0xd2, 0x70, 0x2b, 0x49, 0xbf, - 0x80, 0xda, 0x77, 0x64, 0x30, 0xc8, 0x51, 0xec, 0x28, 0xe9, 0x8d, 0x85, 0x59, 0xb7, 0x95, 0xc5, - 0x73, 0xe9, 0x0c, 0x06, 0x22, 0x97, 0x15, 0x9b, 0x7f, 0x9a, 0x4f, 0x61, 0x69, 0x77, 0xe0, 0x53, - 0xbc, 0xf7, 0x22, 0x87, 0x3e, 0x64, 0x02, 0xa5, 0xd6, 0xa5, 0x61, 0x7e, 0x06, 0x1f, 0x7d, 0x4f, - 0x28, 0x3b, 0x20, 0x6e, 0xe6, 0xf5, 0xb2, 0x61, 0x39, 0x86, 0x2a, 0x31, 0x3c, 0x85, 0x6a, 0x20, - 0x35, 0x8b, 0xa9, 0xa1, 0x89, 0x32, 0xdb, 0xba, 0x91, 0x4d, 0xa5, 0xec, 0x3d, 0xef, 0xd4, 0xb7, - 0x63, 0x17, 0xf3, 0x27, 0xb8, 0x1b, 0x57, 0xb4, 0x64, 0x1b, 0x40, 0x50, 0x08, 0x1c, 0xd6, 0x97, - 0x61, 0xd8, 0xe2, 0x3b, 0x59, 0xf0, 0xf4, 0x3c, 0x05, 0xef, 0x21, 0x2c, 0x1f, 0xf6, 0xc9, 0x50, - 0xec, 0x39, 0x0e, 0xf8, 0x1e, 0x54, 0x78, 0x8b, 0x3d, 0x8e, 0xcb, 0x59, 0x99, 0xdb, 0x07, 0xc4, - 0x35, 0xbf, 0x85, 0x95, 0x97, 0x81, 0x3b, 0xd5, 0x8e, 0x3a, 0x50, 0x0d, 0x31, 0xf5, 0xa3, 0xb0, - 0x2b, 0x0e, 0x98, 0xbe, 0x6b, 0x0c, 0x53, 0x77, 0x2b, 0x64, 0x59, 0x09, 0x7d, 0x22, 0xae, 0x16, - 0xc7, 0x65, 0x5c, 0x2d, 0x75, 0x85, 0xf4, 0xb8, 0x46, 0x7f, 0x0a, 0xb5, 0x57, 0x0e, 0xc9, 0xdc, - 0x21, 0x84, 0x3b, 0x12, 0xa6, 0x36, 0x98, 0x92, 0xb8, 0xf6, 0x61, 0x89, 0xeb, 0xb7, 0x91, 0x78, - 0xe7, 0x6d, 0x0d, 0x0a, 0x3c, 0xed, 0xa8, 0x0f, 0x45, 0x51, 0x39, 0x90, 0x65, 0x65, 0x3d, 0x77, - 0xac, 0x64, 0x2d, 0x6a, 0xb4, 0x73, 0xe3, 0xd5, 0xb1, 0x28, 0x94, 0x64, 0x67, 0x43, 0x5b, 0xd9, - 0xae, 0x33, 0x4f, 0x8e, 0xc6, 0x97, 0xf3, 0x39, 0xa9, 0x4d, 0xe5, 0xf1, 0x42, 0x96, 0xf3, 0x78, - 0xd7, 0x72, 0xc8, 0x79, 0xbc, 0x84, 0x2c, 0x6c, 0x28, 0xc9, 0x3e, 0x88, 0xd6, 0x66, 0xb8, 0x78, - 0xce, 0xdf, 0x7e, 0x8d, 0x2f, 0xb2, 0x97, 0x9c, 0xea, 0xe8, 0x97, 0x50, 0x9f, 0xe8, 0xad, 0xe8, - 0x51, 0xde, 0x25, 0x26, 0xbb, 0xeb, 0x2d, 0xb6, 0x7e, 0x03, 0x95, 0x71, 0x1d, 0x41, 0x9b, 0xd9, - 0xde, 0x53, 0xe5, 0xa9, 0xd1, 0x99, 0xc7, 0x45, 0x6d, 0xf9, 0x18, 0x8a, 0x07, 0x4e, 0x44, 0xd3, - 0x13, 0x98, 0x32, 0x8e, 0xbe, 0x82, 0x92, 0x8d, 0x69, 0x34, 0x9c, 0xdf, 0xf3, 0x67, 0x80, 0xc4, - 0x5b, 0xed, 0x71, 0x0e, 0x89, 0xdd, 0x54, 0x07, 0x53, 0x97, 0xdf, 0x87, 0x02, 0x6f, 0x24, 0xe8, - 0x61, 0xf6, 0xc2, 0x89, 0x86, 0x93, 0xba, 0xdc, 0x11, 0x14, 0xf8, 0xfb, 0x03, 0xe5, 0xb8, 0x0a, - 0xb3, 0x2f, 0xac, 0xd4, 0x55, 0x5f, 0x41, 0xf5, 0xfa, 0xf9, 0x82, 0x72, 0xf0, 0x36, 0xfd, 0xd6, - 0x49, 0x5d, 0xf8, 0x10, 0xca, 0xaa, 0xeb, 0xa1, 0x1c, 0xfa, 0x9b, 0x6c, 0x90, 0xa9, 0x8b, 0xfe, - 0x00, 0x95, 0x71, 0xbb, 0x48, 0x65, 0x3b, 0xc7, 0x21, 0x66, 0x5a, 0xce, 0x4b, 0x28, 0xc9, 0xbe, - 0x92, 0xa7, 0x3a, 0xcd, 0x74, 0xa0, 0xd4, 0x70, 0x31, 0x14, 0x78, 0x6d, 0xcf, 0xa3, 0x80, 0x44, - 0xab, 0x68, 0x58, 0x79, 0xe1, 0x32, 0xfa, 0x9d, 0xfd, 0xab, 0xf7, 0xcd, 0x85, 0xbf, 0xdf, 0x37, - 0x17, 0x7e, 0x1b, 0x35, 0xb5, 0xab, 0x51, 0x53, 0xfb, 0x6b, 0xd4, 0xd4, 0xfe, 0x1d, 0x35, 0xb5, - 0x1f, 0xb7, 0xe6, 0xfb, 0xff, 0xf7, 0x1b, 0xfe, 0xfb, 0x5a, 0x3f, 0x29, 0x89, 0x73, 0x6c, 0xfd, - 0x1f, 0x00, 0x00, 0xff, 0xff, 0x4d, 0xd0, 0xe6, 0x46, 0x3f, 0x0f, 0x00, 0x00, + // 1135 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0x4d, 0x6f, 0xdb, 0x46, + 0x13, 0x36, 0x69, 0x7d, 0x8e, 0x22, 0xbf, 0xf6, 0xbe, 0x8e, 0xcb, 0x28, 0x80, 0x2c, 0x10, 0x68, + 0xe0, 0xa2, 0x08, 0x55, 0xcb, 0x6d, 0xd2, 0x36, 0x40, 0x00, 0xdb, 0x09, 0x0a, 0xa3, 0x0d, 0x6c, + 0xd0, 0x4e, 0x13, 0xb4, 0x28, 0x0c, 0x5a, 0x5c, 0x4b, 0x0b, 0x4b, 0x24, 0xc3, 0x5d, 0xba, 0x76, + 0x4f, 0x3d, 0xf5, 0xdc, 0x9f, 0xd3, 0x9f, 0xe0, 0x43, 0x0e, 0x3d, 0xf6, 0x94, 0x36, 0xba, 0xf7, + 0x3f, 0x14, 0xfb, 0x21, 0x93, 0x92, 0xcc, 0x90, 0xf2, 0xc5, 0xe2, 0xec, 0x3e, 0xb3, 0x3b, 0x3b, + 0xcf, 0xb3, 0x33, 0x6b, 0x78, 0xd2, 0x23, 0xac, 0x1f, 0x9d, 0x58, 0x5d, 0x7f, 0xd8, 0xee, 0xfa, + 0x1e, 0x73, 0x88, 0x87, 0x43, 0x37, 0xf9, 0x19, 0x46, 0x1e, 0x23, 0x43, 0xdc, 0xa6, 0x7d, 0x32, + 0x6c, 0x9f, 0x6f, 0x8a, 0x5f, 0x2b, 0x08, 0x7d, 0xe6, 0xa3, 0x56, 0x0c, 0xb3, 0x14, 0xcc, 0x1a, + 0x10, 0x2f, 0xba, 0xb0, 0x04, 0xe8, 0x7c, 0xb3, 0x71, 0xaf, 0xe7, 0xfb, 0xbd, 0x01, 0x6e, 0x0b, + 0xfc, 0x49, 0x74, 0xda, 0x76, 0xbc, 0x4b, 0xe9, 0xdc, 0xb8, 0x3f, 0x3d, 0x85, 0x87, 0x01, 0x1b, + 0x4f, 0xae, 0xf6, 0xfc, 0x9e, 0x2f, 0x3e, 0xdb, 0xfc, 0x4b, 0x8d, 0xae, 0x4f, 0xbb, 0xf0, 0x1d, + 0x29, 0x73, 0x86, 0x81, 0x02, 0x3c, 0xca, 0x3c, 0x8d, 0x13, 0x90, 0x36, 0xbb, 0x0c, 0x30, 0x6d, + 0x0f, 0xfd, 0xc8, 0x63, 0xca, 0xef, 0xeb, 0x39, 0xfc, 0x98, 0x43, 0xcf, 0xc4, 0x1f, 0xe9, 0x6b, + 0xfe, 0xab, 0xc3, 0xca, 0x6e, 0x88, 0x1d, 0x86, 0x8f, 0x1c, 0x7a, 0x66, 0xe3, 0x37, 0x11, 0xa6, + 0x0c, 0xad, 0x81, 0x4e, 0x5c, 0x43, 0x6b, 0x69, 0x1b, 0xd5, 0x9d, 0xd2, 0xe8, 0xdd, 0xba, 0xbe, + 0xf7, 0xcc, 0xd6, 0x89, 0x8b, 0xd6, 0xa0, 0x74, 0x12, 0x79, 0xee, 0x00, 0x1b, 0x3a, 0x9f, 0xb3, + 0x95, 0x85, 0x0c, 0x28, 0xab, 0x0c, 0x1a, 0x8b, 0x62, 0x62, 0x6c, 0xa2, 0x36, 0x94, 0x42, 0xdf, + 0x67, 0xa7, 0xd4, 0x28, 0xb4, 0x16, 0x37, 0x6a, 0x9d, 0x8f, 0xac, 0x44, 0xd6, 0x45, 0x48, 0xd6, + 0x0b, 0x7e, 0x14, 0x5b, 0xc1, 0x50, 0x03, 0x2a, 0x0c, 0x87, 0x43, 0xe2, 0x39, 0x03, 0xa3, 0xd8, + 0xd2, 0x36, 0x2a, 0xf6, 0xb5, 0x8d, 0x56, 0xa1, 0x48, 0x99, 0x4b, 0x3c, 0xa3, 0x24, 0x36, 0x91, + 0x06, 0x0f, 0x8a, 0x32, 0xd7, 0x8f, 0x98, 0x51, 0x96, 0x41, 0x49, 0x4b, 0x8d, 0xe3, 0x30, 0x34, + 0x2a, 0xd7, 0xe3, 0x38, 0x0c, 0x51, 0x13, 0xa0, 0xdb, 0xc7, 0xdd, 0xb3, 0xc0, 0x27, 0x1e, 0x33, + 0xaa, 0x62, 0x2e, 0x31, 0x82, 0x3e, 0x85, 0x95, 0xc0, 0x09, 0xb1, 0xc7, 0x8e, 0x13, 0x30, 0x10, + 0xb0, 0x65, 0x39, 0xb1, 0x1b, 0x83, 0x2d, 0x28, 0xfb, 0x01, 0x23, 0xbe, 0x47, 0x8d, 0x5a, 0x4b, + 0xdb, 0xa8, 0x75, 0x56, 0x2d, 0x49, 0xb3, 0x35, 0xa6, 0xd9, 0xda, 0xf6, 0x2e, 0xed, 0x31, 0xc8, + 0x7c, 0x00, 0x28, 0x99, 0x6e, 0x1a, 0xf8, 0x1e, 0xc5, 0x68, 0x19, 0x16, 0x03, 0x95, 0xf0, 0xba, + 0xcd, 0x3f, 0xcd, 0xdf, 0x34, 0x58, 0x7a, 0x86, 0x07, 0x98, 0xe1, 0x74, 0x10, 0x5a, 0x87, 0x1a, + 0xbe, 0x20, 0xec, 0x98, 0x32, 0x87, 0x45, 0x54, 0x70, 0x52, 0xb7, 0x81, 0x0f, 0x1d, 0x8a, 0x11, + 0xb4, 0x0d, 0x55, 0x6e, 0x61, 0xf7, 0xd8, 0x61, 0x82, 0x99, 0x5a, 0xa7, 0x31, 0x13, 0xdf, 0xd1, + 0x58, 0x86, 0x3b, 0x95, 0xab, 0x77, 0xeb, 0x0b, 0xbf, 0xff, 0xbd, 0xae, 0xd9, 0x15, 0xe9, 0xb6, + 0xcd, 0x4c, 0x0b, 0x56, 0x65, 0x1c, 0x07, 0xa1, 0xdf, 0xc5, 0x94, 0x66, 0x48, 0xc4, 0xfc, 0x43, + 0x03, 0xf4, 0xfc, 0x02, 0x77, 0xf3, 0xc1, 0x27, 0xe8, 0xd6, 0xd3, 0xe8, 0x5e, 0xbc, 0x99, 0xee, + 0x42, 0x0a, 0xdd, 0xc5, 0x09, 0xba, 0x37, 0xa0, 0x40, 0x03, 0xdc, 0x15, 0x9a, 0x49, 0xa3, 0x47, + 0x20, 0xcc, 0xbb, 0xf0, 0xff, 0x89, 0xc8, 0x65, 0xde, 0xcd, 0xd7, 0xb0, 0x6c, 0x63, 0x4a, 0x7e, + 0xc1, 0x07, 0xec, 0x32, 0xeb, 0x38, 0xab, 0x50, 0xfc, 0x99, 0xb8, 0xac, 0xaf, 0xb8, 0x90, 0x06, + 0x0f, 0xad, 0x8f, 0x49, 0xaf, 0x2f, 0x39, 0xa8, 0xdb, 0xca, 0x32, 0x1f, 0xc0, 0x1d, 0x4e, 0x14, + 0xce, 0xca, 0xe9, 0x5b, 0x1d, 0xea, 0x0a, 0xa8, 0xb4, 0x30, 0xef, 0x05, 0x55, 0xda, 0x59, 0x8c, + 0xb5, 0xb3, 0xc5, 0xd3, 0x25, 0x64, 0xc3, 0xd3, 0xb8, 0xd4, 0xb9, 0x9f, 0xbc, 0x98, 0xe7, 0x9b, + 0xea, 0x6e, 0x4a, 0x1d, 0xd9, 0x0a, 0x1a, 0x33, 0x52, 0xbc, 0x99, 0x91, 0x52, 0x0a, 0x23, 0xe5, + 0x09, 0x46, 0x92, 0x9c, 0x57, 0xa6, 0x38, 0x9f, 0x92, 0x74, 0xf5, 0xc3, 0x92, 0x86, 0x5b, 0x49, + 0x7a, 0x1f, 0x6a, 0xdf, 0x92, 0xc1, 0x20, 0x47, 0xb1, 0xa3, 0xa4, 0x37, 0x16, 0x66, 0xdd, 0x56, + 0x16, 0xcf, 0xa5, 0x33, 0x18, 0x88, 0x5c, 0x56, 0x6c, 0xfe, 0x69, 0x3e, 0x85, 0xa5, 0xdd, 0x81, + 0x4f, 0xf1, 0xde, 0x7e, 0x0e, 0x7d, 0xc8, 0x04, 0x4a, 0xad, 0x4b, 0xc3, 0xfc, 0x04, 0xfe, 0xf7, + 0x1d, 0xa1, 0xec, 0x80, 0xb8, 0x99, 0xd7, 0xcb, 0x86, 0xe5, 0x18, 0xaa, 0xc4, 0xf0, 0x14, 0xaa, + 0x81, 0xd4, 0x2c, 0xa6, 0x86, 0x26, 0xca, 0x6c, 0xeb, 0x46, 0x36, 0x95, 0xb2, 0xf7, 0xbc, 0x53, + 0xdf, 0x8e, 0x5d, 0xcc, 0x1f, 0xe1, 0x6e, 0x5c, 0xd1, 0x92, 0x6d, 0x00, 0x41, 0x21, 0x70, 0x58, + 0x5f, 0x86, 0x61, 0x8b, 0xef, 0x64, 0xc1, 0xd3, 0xf3, 0x14, 0xbc, 0x87, 0xb0, 0x7c, 0xd8, 0x27, + 0x43, 0xb1, 0xe7, 0x38, 0xe0, 0x7b, 0x50, 0xe1, 0x2d, 0xf6, 0x38, 0x2e, 0x67, 0x65, 0x6e, 0x1f, + 0x10, 0xd7, 0xfc, 0x06, 0x56, 0x5e, 0x06, 0xee, 0x54, 0x3b, 0xea, 0x40, 0x35, 0xc4, 0xd4, 0x8f, + 0xc2, 0xae, 0x38, 0x60, 0xfa, 0xae, 0x31, 0x4c, 0xdd, 0xad, 0x90, 0x65, 0x25, 0xf4, 0x2b, 0x71, + 0xb5, 0x38, 0x2e, 0xe3, 0x6a, 0xa9, 0x2b, 0xa4, 0xc7, 0x35, 0xfa, 0x63, 0xa8, 0xbd, 0x72, 0x48, + 0xe6, 0x0e, 0x21, 0xdc, 0x91, 0x30, 0xb5, 0xc1, 0x94, 0xc4, 0xb5, 0x0f, 0x4b, 0x5c, 0xbf, 0x8d, + 0xc4, 0x3b, 0x6f, 0x6b, 0x50, 0xe0, 0x69, 0x47, 0x7d, 0x28, 0x8a, 0xca, 0x81, 0x2c, 0x2b, 0xeb, + 0xb9, 0x63, 0x25, 0x6b, 0x51, 0xa3, 0x9d, 0x1b, 0xaf, 0x8e, 0x45, 0xa1, 0x24, 0x3b, 0x1b, 0xda, + 0xca, 0x76, 0x9d, 0x79, 0x72, 0x34, 0x3e, 0x9f, 0xcf, 0x49, 0x6d, 0x2a, 0x8f, 0x17, 0xb2, 0x9c, + 0xc7, 0xbb, 0x96, 0x43, 0xce, 0xe3, 0x25, 0x64, 0x61, 0x43, 0x49, 0xf6, 0x41, 0xb4, 0x36, 0xc3, + 0xc5, 0x73, 0xfe, 0xf6, 0x6b, 0x7c, 0x96, 0xbd, 0xe4, 0x54, 0x47, 0xbf, 0x84, 0xfa, 0x44, 0x6f, + 0x45, 0x8f, 0xf2, 0x2e, 0x31, 0xd9, 0x5d, 0x6f, 0xb1, 0xf5, 0x1b, 0xa8, 0x8c, 0xeb, 0x08, 0xda, + 0xcc, 0xf6, 0x9e, 0x2a, 0x4f, 0x8d, 0xce, 0x3c, 0x2e, 0x6a, 0xcb, 0xc7, 0x50, 0x3c, 0x70, 0x22, + 0x9a, 0x9e, 0xc0, 0x94, 0x71, 0xf4, 0x25, 0x94, 0x6c, 0x4c, 0xa3, 0xe1, 0xfc, 0x9e, 0x3f, 0x01, + 0x24, 0xde, 0x6a, 0x8f, 0x73, 0x48, 0xec, 0xa6, 0x3a, 0x98, 0xba, 0xfc, 0x0b, 0x28, 0xf0, 0x46, + 0x82, 0x1e, 0x66, 0x2f, 0x9c, 0x68, 0x38, 0xa9, 0xcb, 0x1d, 0x41, 0x81, 0xbf, 0x3f, 0x50, 0x8e, + 0xab, 0x30, 0xfb, 0xc2, 0x4a, 0x5d, 0xf5, 0x15, 0x54, 0xaf, 0x9f, 0x2f, 0x28, 0x07, 0x6f, 0xd3, + 0x6f, 0x9d, 0xd4, 0x85, 0x0f, 0xa1, 0xac, 0xba, 0x1e, 0xca, 0xa1, 0xbf, 0xc9, 0x06, 0x99, 0xba, + 0xe8, 0xf7, 0x50, 0x19, 0xb7, 0x8b, 0x54, 0xb6, 0x73, 0x1c, 0x62, 0xa6, 0xe5, 0xbc, 0x84, 0x92, + 0xec, 0x2b, 0x79, 0xaa, 0xd3, 0x4c, 0x07, 0x4a, 0x0d, 0x17, 0x43, 0x81, 0xd7, 0xf6, 0x3c, 0x0a, + 0x48, 0xb4, 0x8a, 0x86, 0x95, 0x17, 0x2e, 0xa3, 0xdf, 0xd9, 0xbf, 0x7a, 0xdf, 0x5c, 0xf8, 0xeb, + 0x7d, 0x73, 0xe1, 0xd7, 0x51, 0x53, 0xbb, 0x1a, 0x35, 0xb5, 0x3f, 0x47, 0x4d, 0xed, 0x9f, 0x51, + 0x53, 0xfb, 0xe1, 0x8b, 0x79, 0xff, 0x03, 0x7e, 0xc2, 0x7f, 0x5f, 0xeb, 0x27, 0x25, 0x71, 0x92, + 0xad, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0x5b, 0x88, 0x3a, 0x56, 0x43, 0x0f, 0x00, 0x00, } diff --git a/vendor/github.com/containerd/containerd/linux/shim/v1/shim.proto b/vendor/github.com/containerd/containerd/runtime/shim/v1/shim.proto similarity index 97% rename from vendor/github.com/containerd/containerd/linux/shim/v1/shim.proto rename to vendor/github.com/containerd/containerd/runtime/shim/v1/shim.proto index 6de8f1382e58e..8248b2aa026fb 100644 --- a/vendor/github.com/containerd/containerd/linux/shim/v1/shim.proto +++ b/vendor/github.com/containerd/containerd/runtime/shim/v1/shim.proto @@ -9,7 +9,7 @@ import "google/protobuf/timestamp.proto"; import "github.com/containerd/containerd/api/types/mount.proto"; import "github.com/containerd/containerd/api/types/task/task.proto"; -option go_package = "github.com/containerd/containerd/linux/shim/v1;shim"; +option go_package = "github.com/containerd/containerd/runtime/shim/v1;shim"; // Shim service is launched for each container and is responsible for owning the IO // for the container and its additional processes. The shim is also the parent of diff --git a/vendor/github.com/containerd/containerd/services/server/server.go b/vendor/github.com/containerd/containerd/services/server/server.go index d6e22cb715b0e..33d4b6c759845 100644 --- a/vendor/github.com/containerd/containerd/services/server/server.go +++ b/vendor/github.com/containerd/containerd/services/server/server.go @@ -66,12 +66,18 @@ func New(ctx context.Context, config *Config) (*Server, error) { if err != nil { return nil, err } - rpc := grpc.NewServer( - grpc.MaxRecvMsgSize(config.GRPC.MaxRecvMsgSize), - grpc.MaxSendMsgSize(config.GRPC.MaxSendMsgSize), + + serverOpts := []grpc.ServerOption{ grpc.UnaryInterceptor(grpc_prometheus.UnaryServerInterceptor), grpc.StreamInterceptor(grpc_prometheus.StreamServerInterceptor), - ) + } + if config.GRPC.MaxRecvMsgSize > 0 { + serverOpts = append(serverOpts, grpc.MaxRecvMsgSize(config.GRPC.MaxRecvMsgSize)) + } + if config.GRPC.MaxSendMsgSize > 0 { + serverOpts = append(serverOpts, grpc.MaxSendMsgSize(config.GRPC.MaxSendMsgSize)) + } + rpc := grpc.NewServer(serverOpts...) var ( services []plugin.Service s = &Server{ diff --git a/vendor/github.com/containerd/containerd/task.go b/vendor/github.com/containerd/containerd/task.go index a8f0e1f73f32b..2ea53ed02d24f 100644 --- a/vendor/github.com/containerd/containerd/task.go +++ b/vendor/github.com/containerd/containerd/task.go @@ -597,7 +597,7 @@ func (t *task) writeIndex(ctx context.Context, index *v1.Index) (d v1.Descriptor } func writeContent(ctx context.Context, store content.Ingester, mediaType, ref string, r io.Reader, opts ...content.Opt) (d v1.Descriptor, err error) { - writer, err := store.Writer(ctx, ref, 0, "") + writer, err := store.Writer(ctx, content.WithRef(ref)) if err != nil { return d, err } diff --git a/vendor/github.com/containerd/containerd/task_opts.go b/vendor/github.com/containerd/containerd/task_opts.go index 495d4225b9b47..6f2897726b9d0 100644 --- a/vendor/github.com/containerd/containerd/task_opts.go +++ b/vendor/github.com/containerd/containerd/task_opts.go @@ -21,8 +21,8 @@ import ( "syscall" "github.com/containerd/containerd/errdefs" - "github.com/containerd/containerd/linux/runctypes" "github.com/containerd/containerd/mount" + "github.com/containerd/containerd/runtime/linux/runctypes" ) // NewTaskOpts allows the caller to set options on a new task diff --git a/vendor/github.com/containerd/containerd/task_opts_linux.go b/vendor/github.com/containerd/containerd/task_opts_linux.go index dab72808ab876..551cb996c196c 100644 --- a/vendor/github.com/containerd/containerd/task_opts_linux.go +++ b/vendor/github.com/containerd/containerd/task_opts_linux.go @@ -20,7 +20,7 @@ import ( "context" "errors" - "github.com/containerd/containerd/linux/runctypes" + "github.com/containerd/containerd/runtime/linux/runctypes" "github.com/opencontainers/runtime-spec/specs-go" ) diff --git a/vendor/github.com/containerd/containerd/vendor.conf b/vendor/github.com/containerd/containerd/vendor.conf index fe20273ec7b98..b9e98a85e2140 100644 --- a/vendor/github.com/containerd/containerd/vendor.conf +++ b/vendor/github.com/containerd/containerd/vendor.conf @@ -1,7 +1,7 @@ github.com/containerd/go-runc f271fa2021de855d4d918dbef83c5fe19db1bdd5 -github.com/containerd/console cb7008ab3d8359b78c5f464cb7cf160107ad5925 +github.com/containerd/console 5d1b48d6114b8c9666f0c8b916f871af97b0a761 github.com/containerd/cgroups fe281dd265766145e943a034aa41086474ea6130 -github.com/containerd/typeurl f6943554a7e7e88b3c14aad190bf05932da84788 +github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40 github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c github.com/containerd/btrfs 2e1aa0ddf94f91fa282b6ed87c23bf0d64911244 github.com/containerd/continuity d3c23511c1bf5851696cba83143d9cbcd666869b @@ -22,7 +22,6 @@ github.com/golang/protobuf v1.1.0 github.com/opencontainers/runtime-spec v1.0.1 github.com/opencontainers/runc 69663f0bd4b60df09991c08812a60108003fa340 github.com/sirupsen/logrus v1.0.0 -github.com/pmezard/go-difflib v1.0.0 github.com/urfave/cli 7bc6a0acffa589f415f88aca16cc1de5ffd66f9c golang.org/x/net b3756b4b77d7b13260a0a2ec658753cf48922eac google.golang.org/grpc v1.12.0 @@ -33,19 +32,19 @@ github.com/opencontainers/image-spec v1.0.1 golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895 github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0 -github.com/Microsoft/go-winio v0.4.5 -github.com/Microsoft/hcsshim v0.6.10 +github.com/Microsoft/go-winio v0.4.7 +github.com/Microsoft/hcsshim v0.6.11 github.com/boltdb/bolt e9cf4fae01b5a8ff89d0ec6b32f0d9c9f79aefdd google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944 golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4 -github.com/stevvooe/ttrpc d4528379866b0ce7e9d71f3eb96f0582fc374577 +github.com/containerd/ttrpc 94dde388801693c54f88a6596f713b51a8b30b2d github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16 -github.com/gotestyourself/gotestyourself 44dbf532bbf5767611f6f2a61bded572e337010a +gotest.tools v2.1.0 github.com/google/go-cmp v0.1.0 # cri dependencies -github.com/containerd/cri v1.0.0 -github.com/containerd/go-cni f2d7272f12d045b16ed924f50e91f9f9cecc55a7 +github.com/containerd/cri v1.11.0 +github.com/containerd/go-cni 5882530828ecf62032409b298a3e8b19e08b6534 github.com/blang/semver v3.1.0 github.com/containernetworking/cni v0.6.0 github.com/containernetworking/plugins v0.7.0 @@ -59,22 +58,26 @@ github.com/golang/glog 44145f04b68cf362d9c4df2182967c2275eaefed github.com/google/gofuzz 44d81051d367757e1c7c6a5a86423ece9afcf63c github.com/hashicorp/errwrap 7554cd9344cec97297fa6649b055a8c98c2a1e55 github.com/hashicorp/go-multierror ed905158d87462226a13fe39ddf685ea65f1c11f -github.com/json-iterator/go 1.0.4 -github.com/opencontainers/runtime-tools 6073aff4ac61897f75895123f7e24135204a404d +github.com/json-iterator/go f2b4162afba35581b6d4a50d3b8f34e33c144682 +github.com/modern-go/reflect2 05fbef0ca5da472bbf96c9322b84a53edc03c9fd +github.com/modern-go/concurrent 1.0.3 +github.com/opencontainers/runtime-tools v0.6.0 github.com/opencontainers/selinux 4a2974bf1ee960774ffd517717f1f45325af0206 github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0 -github.com/spf13/pflag v1.0.0 github.com/tchap/go-patricia 5ad6cdb7538b0097d5598c7e57f0a24072adf7dc +github.com/xeipuuv/gojsonpointer 4e3ac2762d5f479393488629ee9370b50873b3a6 +github.com/xeipuuv/gojsonreference bd5ef7bd5415a7ac448318e64f11a24cd21e594b +github.com/xeipuuv/gojsonschema 1d523034197ff1f222f6429836dd36a2457a1874 golang.org/x/crypto 49796115aa4b964c318aad4f3084fdb41e9aa067 golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631 gopkg.in/inf.v0 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 gopkg.in/yaml.v2 53feefa2559fb8dfa8d81baad31be332c97d6c77 -k8s.io/api 7e796de92438aede7cb5d6bcf6c10f4fa65db560 -k8s.io/apimachinery fcb9a12f7875d01f8390b28faedc37dcf2e713b9 -k8s.io/apiserver 4a8377c547bbff4576a35b5b5bf4026d9b5aa763 -k8s.io/client-go b9a0cf870f239c4a4ecfd3feb075a50e7cbe1473 -k8s.io/kubernetes v1.10.0 -k8s.io/utils 258e2a2fa64568210fbd6267cf1d8fd87c3cb86e +k8s.io/api 9e5ffd1f1320950b238cfce291b926411f0af722 +k8s.io/apimachinery ed135c5b96450fd24e5e981c708114fbbd950697 +k8s.io/apiserver a90e3a95c2e91b944bfca8225c4e0d12e42a9eb5 +k8s.io/client-go 03bfb9bdcfe5482795b999f39ca3ed9ad42ce5bb +k8s.io/kubernetes v1.11.0 +k8s.io/utils 733eca437aa39379e4bcc25e726439dfca40fcff # zfs dependencies github.com/containerd/zfs 9a0b8b8b5982014b729cd34eb7cd7a11062aa6ec diff --git a/vendor/github.com/containerd/continuity/driver/driver_unix.go b/vendor/github.com/containerd/continuity/driver/driver_unix.go index 67493ade0897b..c7d4e6ba151d4 100644 --- a/vendor/github.com/containerd/continuity/driver/driver_unix.go +++ b/vendor/github.com/containerd/continuity/driver/driver_unix.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "os" - "path/filepath" "sort" "github.com/containerd/continuity/devices" @@ -26,18 +25,6 @@ func (d *driver) Mkfifo(path string, mode os.FileMode) error { return devices.Mknod(path, mode, 0, 0) } -// Lchmod changes the mode of an file not following symlinks. -func (d *driver) Lchmod(path string, mode os.FileMode) (err error) { - if !filepath.IsAbs(path) { - path, err = filepath.Abs(path) - if err != nil { - return - } - } - - return sysx.Fchmodat(0, path, uint32(mode), sysx.AtSymlinkNofollow) -} - // Getxattr returns all of the extended attributes for the file at path p. func (d *driver) Getxattr(p string) (map[string][]byte, error) { xattrs, err := sysx.Listxattr(p) diff --git a/vendor/github.com/containerd/continuity/driver/lchmod_linux.go b/vendor/github.com/containerd/continuity/driver/lchmod_linux.go new file mode 100644 index 0000000000000..39ffe9cc32252 --- /dev/null +++ b/vendor/github.com/containerd/continuity/driver/lchmod_linux.go @@ -0,0 +1,19 @@ +package driver + +import ( + "os" + + "golang.org/x/sys/unix" +) + +// Lchmod changes the mode of a file not following symlinks. +func (d *driver) Lchmod(path string, mode os.FileMode) error { + // On Linux, file mode is not supported for symlinks, + // and fchmodat() does not support AT_SYMLINK_NOFOLLOW, + // so symlinks need to be skipped entirely. + if st, err := os.Stat(path); err == nil && st.Mode()&os.ModeSymlink != 0 { + return nil + } + + return unix.Fchmodat(unix.AT_FDCWD, path, uint32(mode), 0) +} diff --git a/vendor/github.com/containerd/continuity/driver/lchmod_unix.go b/vendor/github.com/containerd/continuity/driver/lchmod_unix.go new file mode 100644 index 0000000000000..1b539f78e3970 --- /dev/null +++ b/vendor/github.com/containerd/continuity/driver/lchmod_unix.go @@ -0,0 +1,14 @@ +// +build darwin freebsd solaris + +package driver + +import ( + "os" + + "golang.org/x/sys/unix" +) + +// Lchmod changes the mode of a file not following symlinks. +func (d *driver) Lchmod(path string, mode os.FileMode) error { + return unix.Fchmodat(unix.AT_FDCWD, path, uint32(mode), unix.AT_SYMLINK_NOFOLLOW) +} diff --git a/vendor/github.com/containerd/continuity/fs/du.go b/vendor/github.com/containerd/continuity/fs/du.go index 26f53331547fe..f8fc9a9946209 100644 --- a/vendor/github.com/containerd/continuity/fs/du.go +++ b/vendor/github.com/containerd/continuity/fs/du.go @@ -10,8 +10,8 @@ type Usage struct { // DiskUsage counts the number of inodes and disk usage for the resources under // path. -func DiskUsage(roots ...string) (Usage, error) { - return diskUsage(roots...) +func DiskUsage(ctx context.Context, roots ...string) (Usage, error) { + return diskUsage(ctx, roots...) } // DiffUsage counts the numbers of inodes and disk usage in the diff --git a/vendor/github.com/containerd/continuity/fs/du_unix.go b/vendor/github.com/containerd/continuity/fs/du_unix.go index fe3426d2783f2..9f6bc55fd9b32 100644 --- a/vendor/github.com/containerd/continuity/fs/du_unix.go +++ b/vendor/github.com/containerd/continuity/fs/du_unix.go @@ -24,7 +24,7 @@ func newInode(stat *syscall.Stat_t) inode { } } -func diskUsage(roots ...string) (Usage, error) { +func diskUsage(ctx context.Context, roots ...string) (Usage, error) { var ( size int64 @@ -37,6 +37,12 @@ func diskUsage(roots ...string) (Usage, error) { return err } + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + inoKey := newInode(fi.Sys().(*syscall.Stat_t)) if _, ok := inodes[inoKey]; !ok { inodes[inoKey] = struct{}{} diff --git a/vendor/github.com/containerd/continuity/fs/du_windows.go b/vendor/github.com/containerd/continuity/fs/du_windows.go index 3f852fc15eee1..faa443fedacc3 100644 --- a/vendor/github.com/containerd/continuity/fs/du_windows.go +++ b/vendor/github.com/containerd/continuity/fs/du_windows.go @@ -8,7 +8,7 @@ import ( "path/filepath" ) -func diskUsage(roots ...string) (Usage, error) { +func diskUsage(ctx context.Context, roots ...string) (Usage, error) { var ( size int64 ) @@ -21,6 +21,12 @@ func diskUsage(roots ...string) (Usage, error) { return err } + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + size += fi.Size() return nil }); err != nil { diff --git a/vendor/github.com/containerd/continuity/sysx/chmod_darwin.go b/vendor/github.com/containerd/continuity/sysx/chmod_darwin.go deleted file mode 100644 index e3ae2b7bbf4b5..0000000000000 --- a/vendor/github.com/containerd/continuity/sysx/chmod_darwin.go +++ /dev/null @@ -1,18 +0,0 @@ -package sysx - -const ( - // AtSymlinkNoFollow defined from AT_SYMLINK_NOFOLLOW in - AtSymlinkNofollow = 0x20 -) - -const ( - - // SYS_FCHMODAT defined from golang.org/sys/unix - SYS_FCHMODAT = 467 -) - -// These functions will be generated by generate.sh -// $ GOOS=darwin GOARCH=386 ./generate.sh chmod -// $ GOOS=darwin GOARCH=amd64 ./generate.sh chmod - -//sys Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) diff --git a/vendor/github.com/containerd/continuity/sysx/chmod_darwin_386.go b/vendor/github.com/containerd/continuity/sysx/chmod_darwin_386.go deleted file mode 100644 index 5a8cf5b57db01..0000000000000 --- a/vendor/github.com/containerd/continuity/sysx/chmod_darwin_386.go +++ /dev/null @@ -1,25 +0,0 @@ -// mksyscall.pl -l32 chmod_darwin.go -// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT - -package sysx - -import ( - "syscall" - "unsafe" -) - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) { - var _p0 *byte - _p0, err = syscall.BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := syscall.Syscall6(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) - use(unsafe.Pointer(_p0)) - if e1 != 0 { - err = errnoErr(e1) - } - return -} diff --git a/vendor/github.com/containerd/continuity/sysx/chmod_darwin_amd64.go b/vendor/github.com/containerd/continuity/sysx/chmod_darwin_amd64.go deleted file mode 100644 index 3287d1d5799aa..0000000000000 --- a/vendor/github.com/containerd/continuity/sysx/chmod_darwin_amd64.go +++ /dev/null @@ -1,25 +0,0 @@ -// mksyscall.pl chmod_darwin.go -// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT - -package sysx - -import ( - "syscall" - "unsafe" -) - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) { - var _p0 *byte - _p0, err = syscall.BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := syscall.Syscall6(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) - use(unsafe.Pointer(_p0)) - if e1 != 0 { - err = errnoErr(e1) - } - return -} diff --git a/vendor/github.com/containerd/continuity/sysx/chmod_freebsd.go b/vendor/github.com/containerd/continuity/sysx/chmod_freebsd.go deleted file mode 100644 index b64a708be1b51..0000000000000 --- a/vendor/github.com/containerd/continuity/sysx/chmod_freebsd.go +++ /dev/null @@ -1,17 +0,0 @@ -package sysx - -const ( - // AtSymlinkNoFollow defined from AT_SYMLINK_NOFOLLOW in - AtSymlinkNofollow = 0x200 -) - -const ( - - // SYS_FCHMODAT defined from golang.org/sys/unix - SYS_FCHMODAT = 490 -) - -// These functions will be generated by generate.sh -// $ GOOS=freebsd GOARCH=amd64 ./generate.sh chmod - -//sys Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) diff --git a/vendor/github.com/containerd/continuity/sysx/chmod_freebsd_amd64.go b/vendor/github.com/containerd/continuity/sysx/chmod_freebsd_amd64.go deleted file mode 100644 index 5a271abb1e7fb..0000000000000 --- a/vendor/github.com/containerd/continuity/sysx/chmod_freebsd_amd64.go +++ /dev/null @@ -1,25 +0,0 @@ -// mksyscall.pl chmod_freebsd.go -// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT - -package sysx - -import ( - "syscall" - "unsafe" -) - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) { - var _p0 *byte - _p0, err = syscall.BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := syscall.Syscall6(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) - use(unsafe.Pointer(_p0)) - if e1 != 0 { - err = errnoErr(e1) - } - return -} diff --git a/vendor/github.com/containerd/continuity/sysx/chmod_linux.go b/vendor/github.com/containerd/continuity/sysx/chmod_linux.go deleted file mode 100644 index 89df6d38ef150..0000000000000 --- a/vendor/github.com/containerd/continuity/sysx/chmod_linux.go +++ /dev/null @@ -1,12 +0,0 @@ -package sysx - -import "syscall" - -const ( - // AtSymlinkNoFollow defined from AT_SYMLINK_NOFOLLOW in /usr/include/linux/fcntl.h - AtSymlinkNofollow = 0x100 -) - -func Fchmodat(dirfd int, path string, mode uint32, flags int) error { - return syscall.Fchmodat(dirfd, path, mode, flags) -} diff --git a/vendor/github.com/containerd/continuity/sysx/chmod_solaris.go b/vendor/github.com/containerd/continuity/sysx/chmod_solaris.go deleted file mode 100644 index 3ba6e5edc8a35..0000000000000 --- a/vendor/github.com/containerd/continuity/sysx/chmod_solaris.go +++ /dev/null @@ -1,11 +0,0 @@ -package sysx - -import "golang.org/x/sys/unix" - -const ( - AtSymlinkNofollow = unix.AT_SYMLINK_NOFOLLOW -) - -func Fchmodat(dirfd int, path string, mode uint32, flags int) error { - return unix.Fchmodat(dirfd, path, mode, flags) -} diff --git a/vendor/github.com/containerd/continuity/sysx/xattr_darwin.go b/vendor/github.com/containerd/continuity/sysx/xattr_darwin.go index 1164a7d11c1cd..58da430706206 100644 --- a/vendor/github.com/containerd/continuity/sysx/xattr_darwin.go +++ b/vendor/github.com/containerd/continuity/sysx/xattr_darwin.go @@ -8,7 +8,6 @@ package sysx //sys setxattr(path string, attr string, data []byte, flags int) (err error) //sys removexattr(path string, attr string, options int) (err error) //sys listxattr(path string, dest []byte, options int) (sz int, err error) -//sys Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) const ( xattrNoFollow = 0x01 diff --git a/vendor/github.com/stevvooe/ttrpc/LICENSE b/vendor/github.com/containerd/ttrpc/LICENSE similarity index 100% rename from vendor/github.com/stevvooe/ttrpc/LICENSE rename to vendor/github.com/containerd/ttrpc/LICENSE diff --git a/vendor/github.com/stevvooe/ttrpc/README.md b/vendor/github.com/containerd/ttrpc/README.md similarity index 90% rename from vendor/github.com/stevvooe/ttrpc/README.md rename to vendor/github.com/containerd/ttrpc/README.md index b246e578b2ab3..d1eed6b120c6f 100644 --- a/vendor/github.com/stevvooe/ttrpc/README.md +++ b/vendor/github.com/containerd/ttrpc/README.md @@ -1,6 +1,6 @@ # ttrpc -[![Build Status](https://travis-ci.org/stevvooe/ttrpc.svg?branch=master)](https://travis-ci.org/stevvooe/ttrpc) +[![Build Status](https://travis-ci.org/containerd/ttrpc.svg?branch=master)](https://travis-ci.org/containerd/ttrpc) GRPC for low-memory environments. @@ -25,7 +25,7 @@ Create a gogo vanity binary (see [`cmd/protoc-gen-gogottrpc/main.go`](cmd/protoc-gen-gogottrpc/main.go) for an example with the ttrpc plugin enabled. -It's recommended to use [`protobuild`](https://github.com/stevvooe/protobuild) +It's recommended to use [`protobuild`](https://github.com//stevvooe/protobuild) to build the protobufs for this project, but this will work with protoc directly, if required. diff --git a/vendor/github.com/stevvooe/ttrpc/channel.go b/vendor/github.com/containerd/ttrpc/channel.go similarity index 85% rename from vendor/github.com/stevvooe/ttrpc/channel.go rename to vendor/github.com/containerd/ttrpc/channel.go index 9493d68624505..22f5496b4b952 100644 --- a/vendor/github.com/stevvooe/ttrpc/channel.go +++ b/vendor/github.com/containerd/ttrpc/channel.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + package ttrpc import ( diff --git a/vendor/github.com/stevvooe/ttrpc/client.go b/vendor/github.com/containerd/ttrpc/client.go similarity index 87% rename from vendor/github.com/stevvooe/ttrpc/client.go rename to vendor/github.com/containerd/ttrpc/client.go index f04718167855e..ede2e6b7413bb 100644 --- a/vendor/github.com/stevvooe/ttrpc/client.go +++ b/vendor/github.com/containerd/ttrpc/client.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + package ttrpc import ( @@ -9,9 +25,9 @@ import ( "sync" "syscall" - "github.com/containerd/containerd/log" "github.com/gogo/protobuf/proto" "github.com/pkg/errors" + "github.com/sirupsen/logrus" "google.golang.org/grpc/status" ) @@ -180,7 +196,7 @@ func (c *Client) run() { case msg := <-incoming: call, ok := waiters[msg.StreamID] if !ok { - log.L.Errorf("ttrpc: received message for unknown channel %v", msg.StreamID) + logrus.Errorf("ttrpc: received message for unknown channel %v", msg.StreamID) continue } diff --git a/vendor/github.com/containerd/ttrpc/codec.go b/vendor/github.com/containerd/ttrpc/codec.go new file mode 100644 index 0000000000000..b4aac2fac0388 --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/codec.go @@ -0,0 +1,42 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package ttrpc + +import ( + "github.com/gogo/protobuf/proto" + "github.com/pkg/errors" +) + +type codec struct{} + +func (c codec) Marshal(msg interface{}) ([]byte, error) { + switch v := msg.(type) { + case proto.Message: + return proto.Marshal(v) + default: + return nil, errors.Errorf("ttrpc: cannot marshal unknown type: %T", msg) + } +} + +func (c codec) Unmarshal(p []byte, msg interface{}) error { + switch v := msg.(type) { + case proto.Message: + return proto.Unmarshal(p, v) + default: + return errors.Errorf("ttrpc: cannot unmarshal into unknown type: %T", msg) + } +} diff --git a/vendor/github.com/containerd/ttrpc/config.go b/vendor/github.com/containerd/ttrpc/config.go new file mode 100644 index 0000000000000..019b7a09dd85d --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/config.go @@ -0,0 +1,39 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package ttrpc + +import "github.com/pkg/errors" + +type serverConfig struct { + handshaker Handshaker +} + +type ServerOpt func(*serverConfig) error + +// WithServerHandshaker can be passed to NewServer to ensure that the +// handshaker is called before every connection attempt. +// +// Only one handshaker is allowed per server. +func WithServerHandshaker(handshaker Handshaker) ServerOpt { + return func(c *serverConfig) error { + if c.handshaker != nil { + return errors.New("only one handshaker allowed per server") + } + c.handshaker = handshaker + return nil + } +} diff --git a/vendor/github.com/stevvooe/ttrpc/handshake.go b/vendor/github.com/containerd/ttrpc/handshake.go similarity index 65% rename from vendor/github.com/stevvooe/ttrpc/handshake.go rename to vendor/github.com/containerd/ttrpc/handshake.go index a08ae8ee4b764..a424b67a4976a 100644 --- a/vendor/github.com/stevvooe/ttrpc/handshake.go +++ b/vendor/github.com/containerd/ttrpc/handshake.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + package ttrpc import ( diff --git a/vendor/github.com/stevvooe/ttrpc/server.go b/vendor/github.com/containerd/ttrpc/server.go similarity index 88% rename from vendor/github.com/stevvooe/ttrpc/server.go rename to vendor/github.com/containerd/ttrpc/server.go index fd29b719e420a..1a518b1b4f771 100644 --- a/vendor/github.com/stevvooe/ttrpc/server.go +++ b/vendor/github.com/containerd/ttrpc/server.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + package ttrpc import ( @@ -9,8 +25,8 @@ import ( "sync/atomic" "time" - "github.com/containerd/containerd/log" "github.com/pkg/errors" + "github.com/sirupsen/logrus" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -51,12 +67,11 @@ func (s *Server) Register(name string, methods map[string]Method) { s.services.register(name, methods) } -func (s *Server) Serve(l net.Listener) error { +func (s *Server) Serve(ctx context.Context, l net.Listener) error { s.addListener(l) defer s.closeListener(l) var ( - ctx = context.Background() backoff time.Duration handshaker = s.config.handshaker ) @@ -88,7 +103,7 @@ func (s *Server) Serve(l net.Listener) error { } sleep := time.Duration(rand.Int63n(int64(backoff))) - log.L.WithError(err).Errorf("ttrpc: failed accept; backoff %v", sleep) + logrus.WithError(err).Errorf("ttrpc: failed accept; backoff %v", sleep) time.Sleep(sleep) continue } @@ -100,7 +115,7 @@ func (s *Server) Serve(l net.Listener) error { approved, handshake, err := handshaker.Handshake(ctx, conn) if err != nil { - log.L.WithError(err).Errorf("ttrpc: refusing connection after handshake") + logrus.WithError(err).Errorf("ttrpc: refusing connection after handshake") conn.Close() continue } @@ -416,12 +431,12 @@ func (c *serverConn) run(sctx context.Context) { case response := <-responses: p, err := c.server.codec.Marshal(response.resp) if err != nil { - log.L.WithError(err).Error("failed marshaling response") + logrus.WithError(err).Error("failed marshaling response") return } if err := ch.send(ctx, response.id, messageTypeResponse, p); err != nil { - log.L.WithError(err).Error("failed sending message on channel") + logrus.WithError(err).Error("failed sending message on channel") return } @@ -432,7 +447,7 @@ func (c *serverConn) run(sctx context.Context) { // requests due to a terminal error. recvErr = nil // connection is now "closing" if err != nil && err != io.EOF { - log.L.WithError(err).Error("error receiving message") + logrus.WithError(err).Error("error receiving message") } case <-shutdown: return diff --git a/vendor/github.com/stevvooe/ttrpc/services.go b/vendor/github.com/containerd/ttrpc/services.go similarity index 84% rename from vendor/github.com/stevvooe/ttrpc/services.go rename to vendor/github.com/containerd/ttrpc/services.go index b9a749e3de1fe..e90963825912a 100644 --- a/vendor/github.com/stevvooe/ttrpc/services.go +++ b/vendor/github.com/containerd/ttrpc/services.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + package ttrpc import ( diff --git a/vendor/github.com/stevvooe/ttrpc/types.go b/vendor/github.com/containerd/ttrpc/types.go similarity index 56% rename from vendor/github.com/stevvooe/ttrpc/types.go rename to vendor/github.com/containerd/ttrpc/types.go index a522b0cf261f9..1f7969e5cf46d 100644 --- a/vendor/github.com/stevvooe/ttrpc/types.go +++ b/vendor/github.com/containerd/ttrpc/types.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + package ttrpc import ( diff --git a/vendor/github.com/stevvooe/ttrpc/unixcreds_linux.go b/vendor/github.com/containerd/ttrpc/unixcreds_linux.go similarity index 82% rename from vendor/github.com/stevvooe/ttrpc/unixcreds_linux.go rename to vendor/github.com/containerd/ttrpc/unixcreds_linux.go index 812d927dc5981..a471bd365ca95 100644 --- a/vendor/github.com/stevvooe/ttrpc/unixcreds_linux.go +++ b/vendor/github.com/containerd/ttrpc/unixcreds_linux.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + package ttrpc import ( diff --git a/vendor/github.com/containerd/typeurl/types.go b/vendor/github.com/containerd/typeurl/types.go index 10a78228ba358..153c488d0aa62 100644 --- a/vendor/github.com/containerd/typeurl/types.go +++ b/vendor/github.com/containerd/typeurl/types.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + package typeurl import ( diff --git a/vendor/github.com/docker/libnetwork/controller.go b/vendor/github.com/docker/libnetwork/controller.go index fe9c4ddf97ecf..f3b6b02aba5e2 100644 --- a/vendor/github.com/docker/libnetwork/controller.go +++ b/vendor/github.com/docker/libnetwork/controller.go @@ -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" ) @@ -870,7 +871,7 @@ addToStore: } }() - if len(network.loadBalancerIP) != 0 { + if network.hasLoadBalancerEndpoint() { if err = network.createLoadBalancerSandbox(); err != nil { return nil, err } @@ -1143,6 +1144,11 @@ func (c *controller) NewSandbox(containerID string, options ...SandboxOption) (S } } + if sb.osSbox != nil { + // Apply operating specific knobs on the load balancer sandbox + sb.osSbox.ApplyOSTweaks(sb.oslTypes) + } + c.Lock() c.sandboxes[sb.id] = sb c.Unlock() @@ -1252,7 +1258,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 diff --git a/vendor/github.com/docker/libnetwork/default_gateway.go b/vendor/github.com/docker/libnetwork/default_gateway.go index 9a60fd675831f..cf094b39eec80 100644 --- a/vendor/github.com/docker/libnetwork/default_gateway.go +++ b/vendor/github.com/docker/libnetwork/default_gateway.go @@ -49,9 +49,11 @@ func (sb *sandbox) setupDefaultGW() error { createOptions := []EndpointOption{CreateOptionAnonymous()} - eplen := gwEPlen - if len(sb.containerID) < gwEPlen { - eplen = len(sb.containerID) + var gwName string + if len(sb.containerID) <= gwEPlen { + gwName = "gateway_" + sb.containerID + } else { + gwName = "gateway_" + sb.id[:gwEPlen] } sbLabels := sb.Labels() @@ -69,7 +71,7 @@ func (sb *sandbox) setupDefaultGW() error { createOptions = append(createOptions, epOption) } - newEp, err := n.CreateEndpoint("gateway_"+sb.containerID[0:eplen], createOptions...) + newEp, err := n.CreateEndpoint(gwName, createOptions...) if err != nil { return fmt.Errorf("container %s: endpoint create on GW Network failed: %v", sb.containerID, err) } diff --git a/vendor/github.com/docker/libnetwork/diagnostic/types.go b/vendor/github.com/docker/libnetwork/diagnostic/types.go index 4eb4ca0d9febf..e6b4831263a1c 100644 --- a/vendor/github.com/docker/libnetwork/diagnostic/types.go +++ b/vendor/github.com/docker/libnetwork/diagnostic/types.go @@ -120,3 +120,13 @@ type TablePeersResult struct { TableObj Elements []PeerEntryObj `json:"entries"` } + +// NetworkStatsResult network db stats related to entries and queue len for a network +type NetworkStatsResult struct { + Entries int `json:"entries"` + QueueLen int `jsoin:"qlen"` +} + +func (n *NetworkStatsResult) String() string { + return fmt.Sprintf("entries: %d, qlen: %d\n", n.Entries, n.QueueLen) +} diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/bridge.go b/vendor/github.com/docker/libnetwork/drivers/bridge/bridge.go index 581aa008ad1a3..0383ce35a8076 100644 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/bridge.go +++ b/vendor/github.com/docker/libnetwork/drivers/bridge/bridge.go @@ -614,9 +614,7 @@ func (d *driver) checkConflict(config *networkConfiguration) error { return nil } -func (d *driver) createNetwork(config *networkConfiguration) error { - var err error - +func (d *driver) createNetwork(config *networkConfiguration) (err error) { defer osl.InitOSContext()() networkList := d.getNetworks() @@ -775,7 +773,7 @@ func (d *driver) deleteNetwork(nid string) error { } if err := d.storeDelete(ep); err != nil { - logrus.Warnf("Failed to remove bridge endpoint %s from store: %v", ep.id[0:7], err) + logrus.Warnf("Failed to remove bridge endpoint %.7s from store: %v", ep.id, err) } } @@ -1050,7 +1048,7 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, } if err = d.storeUpdate(endpoint); err != nil { - return fmt.Errorf("failed to save bridge endpoint %s to store: %v", endpoint.id[0:7], err) + return fmt.Errorf("failed to save bridge endpoint %.7s to store: %v", endpoint.id, err) } return nil @@ -1116,7 +1114,7 @@ func (d *driver) DeleteEndpoint(nid, eid string) error { } if err := d.storeDelete(ep); err != nil { - logrus.Warnf("Failed to remove bridge endpoint %s from store: %v", ep.id[0:7], err) + logrus.Warnf("Failed to remove bridge endpoint %.7s from store: %v", ep.id, err) } return nil @@ -1290,7 +1288,7 @@ func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string }() if err = d.storeUpdate(endpoint); err != nil { - return fmt.Errorf("failed to update bridge endpoint %s to store: %v", endpoint.id[0:7], err) + return fmt.Errorf("failed to update bridge endpoint %.7s to store: %v", endpoint.id, err) } if !network.config.EnableICC { @@ -1332,7 +1330,7 @@ func (d *driver) RevokeExternalConnectivity(nid, eid string) error { clearEndpointConnections(d.nlh, endpoint) if err = d.storeUpdate(endpoint); err != nil { - return fmt.Errorf("failed to update bridge endpoint %s to store: %v", endpoint.id[0:7], err) + return fmt.Errorf("failed to update bridge endpoint %.7s to store: %v", endpoint.id, err) } return nil diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/bridge_store.go b/vendor/github.com/docker/libnetwork/drivers/bridge/bridge_store.go index b0e4ff02b51e2..2988c34fa58bf 100644 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/bridge_store.go +++ b/vendor/github.com/docker/libnetwork/drivers/bridge/bridge_store.go @@ -62,7 +62,7 @@ func (d *driver) populateNetworks() error { if err = d.createNetwork(ncfg); err != nil { logrus.Warnf("could not create bridge network for id %s bridge name %s while booting up from persistent state: %v", ncfg.ID, ncfg.BridgeName, err) } - logrus.Debugf("Network (%s) restored", ncfg.ID[0:7]) + logrus.Debugf("Network (%.7s) restored", ncfg.ID) } return nil @@ -82,16 +82,16 @@ func (d *driver) populateEndpoints() error { ep := kvo.(*bridgeEndpoint) n, ok := d.networks[ep.nid] if !ok { - logrus.Debugf("Network (%s) not found for restored bridge endpoint (%s)", ep.nid[0:7], ep.id[0:7]) - logrus.Debugf("Deleting stale bridge endpoint (%s) from store", ep.id[0:7]) + logrus.Debugf("Network (%.7s) not found for restored bridge endpoint (%.7s)", ep.nid, ep.id) + logrus.Debugf("Deleting stale bridge endpoint (%.7s) from store", ep.id) if err := d.storeDelete(ep); err != nil { - logrus.Debugf("Failed to delete stale bridge endpoint (%s) from store", ep.id[0:7]) + logrus.Debugf("Failed to delete stale bridge endpoint (%.7s) from store", ep.id) } continue } n.endpoints[ep.id] = ep n.restorePortAllocations(ep) - logrus.Debugf("Endpoint (%s) restored to network (%s)", ep.id[0:7], ep.nid[0:7]) + logrus.Debugf("Endpoint (%.7s) restored to network (%.7s)", ep.id, ep.nid) } return nil @@ -382,7 +382,7 @@ func (n *bridgeNetwork) restorePortAllocations(ep *bridgeEndpoint) { ep.extConnConfig.PortBindings = ep.portMapping _, err := n.allocatePorts(ep, n.config.DefaultBindingIP, n.driver.config.EnableUserlandProxy) if err != nil { - logrus.Warnf("Failed to reserve existing port mapping for endpoint %s:%v", ep.id[0:7], err) + logrus.Warnf("Failed to reserve existing port mapping for endpoint %.7s:%v", ep.id, err) } ep.extConnConfig.PortBindings = tmp } diff --git a/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_endpoint.go b/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_endpoint.go index 139cbaf8b7c54..89e2edd7c9298 100644 --- a/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_endpoint.go +++ b/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_endpoint.go @@ -53,7 +53,7 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, } if err := d.storeUpdate(ep); err != nil { - return fmt.Errorf("failed to save ipvlan endpoint %s to store: %v", ep.id[0:7], err) + return fmt.Errorf("failed to save ipvlan endpoint %.7s to store: %v", ep.id, err) } n.addEndpoint(ep) @@ -82,7 +82,7 @@ func (d *driver) DeleteEndpoint(nid, eid string) error { } if err := d.storeDelete(ep); err != nil { - logrus.Warnf("Failed to remove ipvlan endpoint %s from store: %v", ep.id[0:7], err) + logrus.Warnf("Failed to remove ipvlan endpoint %.7s from store: %v", ep.id, err) } n.deleteEndpoint(ep.id) return nil diff --git a/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_joinleave.go b/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_joinleave.go index 9d229a245eaa8..bcbbbfdfd2b04 100644 --- a/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_joinleave.go +++ b/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_joinleave.go @@ -117,7 +117,7 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, return err } if err = d.storeUpdate(ep); err != nil { - return fmt.Errorf("failed to save ipvlan endpoint %s to store: %v", ep.id[0:7], err) + return fmt.Errorf("failed to save ipvlan endpoint %.7s to store: %v", ep.id, err) } return nil diff --git a/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_network.go b/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_network.go index 437919cf9d5a1..5451cced91c1c 100644 --- a/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_network.go +++ b/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_network.go @@ -156,7 +156,7 @@ func (d *driver) DeleteNetwork(nid string) error { } if err := d.storeDelete(ep); err != nil { - logrus.Warnf("Failed to remove ipvlan endpoint %s from store: %v", ep.id[0:7], err) + logrus.Warnf("Failed to remove ipvlan endpoint %.7s from store: %v", ep.id, err) } } // delete the *network diff --git a/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_store.go b/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_store.go index 197e29966a91f..72eb3fc4fffdf 100644 --- a/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_store.go +++ b/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_store.go @@ -95,15 +95,15 @@ func (d *driver) populateEndpoints() error { ep := kvo.(*endpoint) n, ok := d.networks[ep.nid] if !ok { - logrus.Debugf("Network (%s) not found for restored ipvlan endpoint (%s)", ep.nid[0:7], ep.id[0:7]) - logrus.Debugf("Deleting stale ipvlan endpoint (%s) from store", ep.id[0:7]) + logrus.Debugf("Network (%.7s) not found for restored ipvlan endpoint (%.7s)", ep.nid, ep.id) + logrus.Debugf("Deleting stale ipvlan endpoint (%.7s) from store", ep.id) if err := d.storeDelete(ep); err != nil { - logrus.Debugf("Failed to delete stale ipvlan endpoint (%s) from store", ep.id[0:7]) + logrus.Debugf("Failed to delete stale ipvlan endpoint (%.7s) from store", ep.id) } continue } n.endpoints[ep.id] = ep - logrus.Debugf("Endpoint (%s) restored to network (%s)", ep.id[0:7], ep.nid[0:7]) + logrus.Debugf("Endpoint (%.7s) restored to network (%.7s)", ep.id, ep.nid) } return nil diff --git a/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_endpoint.go b/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_endpoint.go index bf3e0bbd149d6..dc3ce365433dc 100644 --- a/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_endpoint.go +++ b/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_endpoint.go @@ -58,7 +58,7 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, } if err := d.storeUpdate(ep); err != nil { - return fmt.Errorf("failed to save macvlan endpoint %s to store: %v", ep.id[0:7], err) + return fmt.Errorf("failed to save macvlan endpoint %.7s to store: %v", ep.id, err) } n.addEndpoint(ep) @@ -87,7 +87,7 @@ func (d *driver) DeleteEndpoint(nid, eid string) error { } if err := d.storeDelete(ep); err != nil { - logrus.Warnf("Failed to remove macvlan endpoint %s from store: %v", ep.id[0:7], err) + logrus.Warnf("Failed to remove macvlan endpoint %.7s from store: %v", ep.id, err) } n.deleteEndpoint(ep.id) diff --git a/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_joinleave.go b/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_joinleave.go index 778613db773d1..6716d58de6a7a 100644 --- a/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_joinleave.go +++ b/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_joinleave.go @@ -78,7 +78,7 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, return err } if err := d.storeUpdate(ep); err != nil { - return fmt.Errorf("failed to save macvlan endpoint %s to store: %v", ep.id[0:7], err) + return fmt.Errorf("failed to save macvlan endpoint %.7s to store: %v", ep.id, err) } return nil } diff --git a/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_network.go b/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_network.go index be4cf0f445b8f..abeb0e6ecb3b7 100644 --- a/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_network.go +++ b/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_network.go @@ -160,7 +160,7 @@ func (d *driver) DeleteNetwork(nid string) error { } if err := d.storeDelete(ep); err != nil { - logrus.Warnf("Failed to remove macvlan endpoint %s from store: %v", ep.id[0:7], err) + logrus.Warnf("Failed to remove macvlan endpoint %.7s from store: %v", ep.id, err) } } // delete the *network diff --git a/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_store.go b/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_store.go index 655a49c08b687..8683cacd02c82 100644 --- a/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_store.go +++ b/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_store.go @@ -95,15 +95,15 @@ func (d *driver) populateEndpoints() error { ep := kvo.(*endpoint) n, ok := d.networks[ep.nid] if !ok { - logrus.Debugf("Network (%s) not found for restored macvlan endpoint (%s)", ep.nid[0:7], ep.id[0:7]) - logrus.Debugf("Deleting stale macvlan endpoint (%s) from store", ep.id[0:7]) + logrus.Debugf("Network (%.7s) not found for restored macvlan endpoint (%.7s)", ep.nid, ep.id) + logrus.Debugf("Deleting stale macvlan endpoint (%.7s) from store", ep.id) if err := d.storeDelete(ep); err != nil { - logrus.Debugf("Failed to delete stale macvlan endpoint (%s) from store", ep.id[0:7]) + logrus.Debugf("Failed to delete stale macvlan endpoint (%.7s) from store", ep.id) } continue } n.endpoints[ep.id] = ep - logrus.Debugf("Endpoint (%s) restored to network (%s)", ep.id[0:7], ep.nid[0:7]) + logrus.Debugf("Endpoint (%.7s) restored to network (%.7s)", ep.id, ep.nid) } return nil diff --git a/vendor/github.com/docker/libnetwork/drivers/overlay/encryption.go b/vendor/github.com/docker/libnetwork/drivers/overlay/encryption.go index bcae0bd4e5590..8e10906dc1bee 100644 --- a/vendor/github.com/docker/libnetwork/drivers/overlay/encryption.go +++ b/vendor/github.com/docker/libnetwork/drivers/overlay/encryption.go @@ -78,7 +78,7 @@ func (e *encrMap) String() string { } func (d *driver) checkEncryption(nid string, rIP net.IP, vxlanID uint32, isLocal, add bool) error { - logrus.Debugf("checkEncryption(%s, %v, %d, %t)", nid[0:7], rIP, vxlanID, isLocal) + logrus.Debugf("checkEncryption(%.7s, %v, %d, %t)", nid, rIP, vxlanID, isLocal) n := d.network(nid) if n == nil || !n.secure { @@ -101,7 +101,7 @@ func (d *driver) checkEncryption(nid string, rIP net.IP, vxlanID uint32, isLocal } return false }); err != nil { - logrus.Warnf("Failed to retrieve list of participating nodes in overlay network %s: %v", nid[0:5], err) + logrus.Warnf("Failed to retrieve list of participating nodes in overlay network %.5s: %v", nid, err) } default: if len(d.network(nid).endpoints) > 0 { diff --git a/vendor/github.com/docker/libnetwork/drivers/overlay/joinleave.go b/vendor/github.com/docker/libnetwork/drivers/overlay/joinleave.go index 0770513e7d541..985d9977844ec 100644 --- a/vendor/github.com/docker/libnetwork/drivers/overlay/joinleave.go +++ b/vendor/github.com/docker/libnetwork/drivers/overlay/joinleave.go @@ -69,7 +69,7 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, ep.ifName = containerIfName if err = d.writeEndpointToStore(ep); err != nil { - return fmt.Errorf("failed to update overlay endpoint %s to local data store: %v", ep.id[0:7], err) + return fmt.Errorf("failed to update overlay endpoint %.7s to local data store: %v", ep.id, err) } // Set the container interface and its peer MTU to 1450 to allow diff --git a/vendor/github.com/docker/libnetwork/drivers/overlay/ostweaks_linux.go b/vendor/github.com/docker/libnetwork/drivers/overlay/ostweaks_linux.go index 68f1ee9ceeedf..de946a58463cf 100644 --- a/vendor/github.com/docker/libnetwork/drivers/overlay/ostweaks_linux.go +++ b/vendor/github.com/docker/libnetwork/drivers/overlay/ostweaks_linux.go @@ -1,72 +1,23 @@ package overlay import ( - "io/ioutil" - "path" "strconv" - "strings" - "github.com/sirupsen/logrus" + "github.com/docker/libnetwork/osl/kernel" ) -type conditionalCheck func(val1, val2 string) bool - -type osValue struct { - value string - checkFn conditionalCheck -} - -var osConfig = map[string]osValue{ +var ovConfig = map[string]*kernel.OSValue{ "net.ipv4.neigh.default.gc_thresh1": {"8192", checkHigher}, "net.ipv4.neigh.default.gc_thresh2": {"49152", checkHigher}, "net.ipv4.neigh.default.gc_thresh3": {"65536", checkHigher}, } -func propertyIsValid(val1, val2 string, check conditionalCheck) bool { - if check == nil || check(val1, val2) { - return true - } - return false -} - func checkHigher(val1, val2 string) bool { val1Int, _ := strconv.ParseInt(val1, 10, 32) val2Int, _ := strconv.ParseInt(val2, 10, 32) return val1Int < val2Int } -// writeSystemProperty writes the value to a path under /proc/sys as determined from the key. -// For e.g. net.ipv4.ip_forward translated to /proc/sys/net/ipv4/ip_forward. -func writeSystemProperty(key, value string) error { - keyPath := strings.Replace(key, ".", "/", -1) - return ioutil.WriteFile(path.Join("/proc/sys", keyPath), []byte(value), 0644) -} - -func readSystemProperty(key string) (string, error) { - keyPath := strings.Replace(key, ".", "/", -1) - value, err := ioutil.ReadFile(path.Join("/proc/sys", keyPath)) - if err != nil { - return "", err - } - return string(value), nil -} - func applyOStweaks() { - for k, v := range osConfig { - // read the existing property from disk - oldv, err := readSystemProperty(k) - if err != nil { - logrus.Errorf("error reading the kernel parameter %s, error: %s", k, err) - continue - } - - if propertyIsValid(oldv, v.value, v.checkFn) { - // write new prop value to disk - if err := writeSystemProperty(k, v.value); err != nil { - logrus.Errorf("error setting the kernel parameter %s = %s, (leaving as %s) error: %s", k, v.value, oldv, err) - continue - } - logrus.Debugf("updated kernel parameter %s = %s (was %s)", k, v.value, oldv) - } - } + kernel.ApplyOSTweaks(ovConfig) } diff --git a/vendor/github.com/docker/libnetwork/drivers/overlay/ov_endpoint.go b/vendor/github.com/docker/libnetwork/drivers/overlay/ov_endpoint.go index bb08de465c028..0aaaac59d9007 100644 --- a/vendor/github.com/docker/libnetwork/drivers/overlay/ov_endpoint.go +++ b/vendor/github.com/docker/libnetwork/drivers/overlay/ov_endpoint.go @@ -90,7 +90,7 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, n.addEndpoint(ep) if err := d.writeEndpointToStore(ep); err != nil { - return fmt.Errorf("failed to update overlay endpoint %s to local store: %v", ep.id[0:7], err) + return fmt.Errorf("failed to update overlay endpoint %.7s to local store: %v", ep.id, err) } return nil @@ -116,7 +116,7 @@ func (d *driver) DeleteEndpoint(nid, eid string) error { n.deleteEndpoint(eid) if err := d.deleteEndpointFromStore(ep); err != nil { - logrus.Warnf("Failed to delete overlay endpoint %s from local store: %v", ep.id[0:7], err) + logrus.Warnf("Failed to delete overlay endpoint %.7s from local store: %v", ep.id, err) } if ep.ifName == "" { diff --git a/vendor/github.com/docker/libnetwork/drivers/overlay/ov_network.go b/vendor/github.com/docker/libnetwork/drivers/overlay/ov_network.go index 9c83127d4f83f..4dda2801fb8a9 100644 --- a/vendor/github.com/docker/libnetwork/drivers/overlay/ov_network.go +++ b/vendor/github.com/docker/libnetwork/drivers/overlay/ov_network.go @@ -244,7 +244,15 @@ func (d *driver) DeleteNetwork(nid string) error { } d.Lock() - defer d.Unlock() + // Only perform a peer flush operation (if required) AFTER unlocking + // the driver lock to avoid deadlocking w/ the peerDB. + var doPeerFlush bool + defer func() { + d.Unlock() + if doPeerFlush { + d.peerFlush(nid) + } + }() // This is similar to d.network(), but we need to keep holding the lock // until we are done removing this network. @@ -266,11 +274,11 @@ func (d *driver) DeleteNetwork(nid string) error { } if err := d.deleteEndpointFromStore(ep); err != nil { - logrus.Warnf("Failed to delete overlay endpoint %s from local store: %v", ep.id[0:7], err) + logrus.Warnf("Failed to delete overlay endpoint %.7s from local store: %v", ep.id, err) } } // flush the peerDB entries - d.peerFlush(nid) + doPeerFlush = true delete(d.networks, nid) vnis, err := n.releaseVxlanID() diff --git a/vendor/github.com/docker/libnetwork/drivers/overlay/overlay.go b/vendor/github.com/docker/libnetwork/drivers/overlay/overlay.go index f029c5cce4769..f31a8ca597b66 100644 --- a/vendor/github.com/docker/libnetwork/drivers/overlay/overlay.go +++ b/vendor/github.com/docker/libnetwork/drivers/overlay/overlay.go @@ -137,10 +137,10 @@ func (d *driver) restoreEndpoints() error { ep := kvo.(*endpoint) n := d.network(ep.nid) if n == nil { - logrus.Debugf("Network (%s) not found for restored endpoint (%s)", ep.nid[0:7], ep.id[0:7]) - logrus.Debugf("Deleting stale overlay endpoint (%s) from store", ep.id[0:7]) + logrus.Debugf("Network (%.7s) not found for restored endpoint (%.7s)", ep.nid, ep.id) + logrus.Debugf("Deleting stale overlay endpoint (%.7s) from store", ep.id) if err := d.deleteEndpointFromStore(ep); err != nil { - logrus.Debugf("Failed to delete stale overlay endpoint (%s) from store", ep.id[0:7]) + logrus.Debugf("Failed to delete stale overlay endpoint (%.7s) from store", ep.id) } continue } diff --git a/vendor/github.com/docker/libnetwork/drivers/overlay/overlay.pb.go b/vendor/github.com/docker/libnetwork/drivers/overlay/overlay.pb.go index cfa0eeeae4326..243c196541a7e 100644 --- a/vendor/github.com/docker/libnetwork/drivers/overlay/overlay.pb.go +++ b/vendor/github.com/docker/libnetwork/drivers/overlay/overlay.pb.go @@ -1,12 +1,11 @@ -// Code generated by protoc-gen-gogo. -// source: overlay.proto -// DO NOT EDIT! +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: drivers/overlay/overlay.proto /* Package overlay is a generated protocol buffer package. It is generated from these files: - overlay.proto + drivers/overlay/overlay.proto It has these top-level messages: PeerRecord @@ -19,9 +18,6 @@ import math "math" import _ "github.com/gogo/protobuf/gogoproto" import strings "strings" -import github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto" -import sort "sort" -import strconv "strconv" import reflect "reflect" import io "io" @@ -33,7 +29,9 @@ var _ = math.Inf // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. -const _ = proto.GoGoProtoPackageIsVersion1 +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package // PeerRecord defines the information corresponding to a peer // container in the overlay network. @@ -54,6 +52,27 @@ func (m *PeerRecord) Reset() { *m = PeerRecord{} } func (*PeerRecord) ProtoMessage() {} func (*PeerRecord) Descriptor() ([]byte, []int) { return fileDescriptorOverlay, []int{0} } +func (m *PeerRecord) GetEndpointIP() string { + if m != nil { + return m.EndpointIP + } + return "" +} + +func (m *PeerRecord) GetEndpointMAC() string { + if m != nil { + return m.EndpointMAC + } + return "" +} + +func (m *PeerRecord) GetTunnelEndpointIP() string { + if m != nil { + return m.TunnelEndpointIP + } + return "" +} + func init() { proto.RegisterType((*PeerRecord)(nil), "overlay.PeerRecord") } @@ -77,84 +96,49 @@ func valueToGoStringOverlay(v interface{}, typ string) string { pv := reflect.Indirect(rv).Interface() return fmt.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv) } -func extensionToGoStringOverlay(e map[int32]github_com_gogo_protobuf_proto.Extension) string { - if e == nil { - return "nil" - } - s := "map[int32]proto.Extension{" - keys := make([]int, 0, len(e)) - for k := range e { - keys = append(keys, int(k)) - } - sort.Ints(keys) - ss := []string{} - for _, k := range keys { - ss = append(ss, strconv.Itoa(k)+": "+e[int32(k)].GoString()) - } - s += strings.Join(ss, ",") + "}" - return s -} -func (m *PeerRecord) Marshal() (data []byte, err error) { +func (m *PeerRecord) Marshal() (dAtA []byte, err error) { size := m.Size() - data = make([]byte, size) - n, err := m.MarshalTo(data) + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) if err != nil { return nil, err } - return data[:n], nil + return dAtA[:n], nil } -func (m *PeerRecord) MarshalTo(data []byte) (int, error) { +func (m *PeerRecord) MarshalTo(dAtA []byte) (int, error) { var i int _ = i var l int _ = l if len(m.EndpointIP) > 0 { - data[i] = 0xa + dAtA[i] = 0xa i++ - i = encodeVarintOverlay(data, i, uint64(len(m.EndpointIP))) - i += copy(data[i:], m.EndpointIP) + i = encodeVarintOverlay(dAtA, i, uint64(len(m.EndpointIP))) + i += copy(dAtA[i:], m.EndpointIP) } if len(m.EndpointMAC) > 0 { - data[i] = 0x12 + dAtA[i] = 0x12 i++ - i = encodeVarintOverlay(data, i, uint64(len(m.EndpointMAC))) - i += copy(data[i:], m.EndpointMAC) + i = encodeVarintOverlay(dAtA, i, uint64(len(m.EndpointMAC))) + i += copy(dAtA[i:], m.EndpointMAC) } if len(m.TunnelEndpointIP) > 0 { - data[i] = 0x1a + dAtA[i] = 0x1a i++ - i = encodeVarintOverlay(data, i, uint64(len(m.TunnelEndpointIP))) - i += copy(data[i:], m.TunnelEndpointIP) + i = encodeVarintOverlay(dAtA, i, uint64(len(m.TunnelEndpointIP))) + i += copy(dAtA[i:], m.TunnelEndpointIP) } return i, nil } -func encodeFixed64Overlay(data []byte, offset int, v uint64) int { - data[offset] = uint8(v) - data[offset+1] = uint8(v >> 8) - data[offset+2] = uint8(v >> 16) - data[offset+3] = uint8(v >> 24) - data[offset+4] = uint8(v >> 32) - data[offset+5] = uint8(v >> 40) - data[offset+6] = uint8(v >> 48) - data[offset+7] = uint8(v >> 56) - return offset + 8 -} -func encodeFixed32Overlay(data []byte, offset int, v uint32) int { - data[offset] = uint8(v) - data[offset+1] = uint8(v >> 8) - data[offset+2] = uint8(v >> 16) - data[offset+3] = uint8(v >> 24) - return offset + 4 -} -func encodeVarintOverlay(data []byte, offset int, v uint64) int { +func encodeVarintOverlay(dAtA []byte, offset int, v uint64) int { for v >= 1<<7 { - data[offset] = uint8(v&0x7f | 0x80) + dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } - data[offset] = uint8(v) + dAtA[offset] = uint8(v) return offset + 1 } func (m *PeerRecord) Size() (n int) { @@ -208,8 +192,8 @@ func valueToStringOverlay(v interface{}) string { pv := reflect.Indirect(rv).Interface() return fmt.Sprintf("*%v", pv) } -func (m *PeerRecord) Unmarshal(data []byte) error { - l := len(data) +func (m *PeerRecord) Unmarshal(dAtA []byte) error { + l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx @@ -221,7 +205,7 @@ func (m *PeerRecord) Unmarshal(data []byte) error { if iNdEx >= l { return io.ErrUnexpectedEOF } - b := data[iNdEx] + b := dAtA[iNdEx] iNdEx++ wire |= (uint64(b) & 0x7F) << shift if b < 0x80 { @@ -249,7 +233,7 @@ func (m *PeerRecord) Unmarshal(data []byte) error { if iNdEx >= l { return io.ErrUnexpectedEOF } - b := data[iNdEx] + b := dAtA[iNdEx] iNdEx++ stringLen |= (uint64(b) & 0x7F) << shift if b < 0x80 { @@ -264,7 +248,7 @@ func (m *PeerRecord) Unmarshal(data []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.EndpointIP = string(data[iNdEx:postIndex]) + m.EndpointIP = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { @@ -278,7 +262,7 @@ func (m *PeerRecord) Unmarshal(data []byte) error { if iNdEx >= l { return io.ErrUnexpectedEOF } - b := data[iNdEx] + b := dAtA[iNdEx] iNdEx++ stringLen |= (uint64(b) & 0x7F) << shift if b < 0x80 { @@ -293,7 +277,7 @@ func (m *PeerRecord) Unmarshal(data []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.EndpointMAC = string(data[iNdEx:postIndex]) + m.EndpointMAC = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { @@ -307,7 +291,7 @@ func (m *PeerRecord) Unmarshal(data []byte) error { if iNdEx >= l { return io.ErrUnexpectedEOF } - b := data[iNdEx] + b := dAtA[iNdEx] iNdEx++ stringLen |= (uint64(b) & 0x7F) << shift if b < 0x80 { @@ -322,11 +306,11 @@ func (m *PeerRecord) Unmarshal(data []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.TunnelEndpointIP = string(data[iNdEx:postIndex]) + m.TunnelEndpointIP = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex - skippy, err := skipOverlay(data[iNdEx:]) + skippy, err := skipOverlay(dAtA[iNdEx:]) if err != nil { return err } @@ -345,8 +329,8 @@ func (m *PeerRecord) Unmarshal(data []byte) error { } return nil } -func skipOverlay(data []byte) (n int, err error) { - l := len(data) +func skipOverlay(dAtA []byte) (n int, err error) { + l := len(dAtA) iNdEx := 0 for iNdEx < l { var wire uint64 @@ -357,7 +341,7 @@ func skipOverlay(data []byte) (n int, err error) { if iNdEx >= l { return 0, io.ErrUnexpectedEOF } - b := data[iNdEx] + b := dAtA[iNdEx] iNdEx++ wire |= (uint64(b) & 0x7F) << shift if b < 0x80 { @@ -375,7 +359,7 @@ func skipOverlay(data []byte) (n int, err error) { return 0, io.ErrUnexpectedEOF } iNdEx++ - if data[iNdEx-1] < 0x80 { + if dAtA[iNdEx-1] < 0x80 { break } } @@ -392,7 +376,7 @@ func skipOverlay(data []byte) (n int, err error) { if iNdEx >= l { return 0, io.ErrUnexpectedEOF } - b := data[iNdEx] + b := dAtA[iNdEx] iNdEx++ length |= (int(b) & 0x7F) << shift if b < 0x80 { @@ -415,7 +399,7 @@ func skipOverlay(data []byte) (n int, err error) { if iNdEx >= l { return 0, io.ErrUnexpectedEOF } - b := data[iNdEx] + b := dAtA[iNdEx] iNdEx++ innerWire |= (uint64(b) & 0x7F) << shift if b < 0x80 { @@ -426,7 +410,7 @@ func skipOverlay(data []byte) (n int, err error) { if innerWireType == 4 { break } - next, err := skipOverlay(data[start:]) + next, err := skipOverlay(dAtA[start:]) if err != nil { return 0, err } @@ -450,19 +434,22 @@ var ( ErrIntOverflowOverlay = fmt.Errorf("proto: integer overflow") ) +func init() { proto.RegisterFile("drivers/overlay/overlay.proto", fileDescriptorOverlay) } + var fileDescriptorOverlay = []byte{ - // 195 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0xcd, 0x2f, 0x4b, 0x2d, - 0xca, 0x49, 0xac, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x87, 0x72, 0xa5, 0x44, 0xd2, - 0xf3, 0xd3, 0xf3, 0xc1, 0x62, 0xfa, 0x20, 0x16, 0x44, 0x5a, 0x69, 0x2b, 0x23, 0x17, 0x57, 0x40, - 0x6a, 0x6a, 0x51, 0x50, 0x6a, 0x72, 0x7e, 0x51, 0x8a, 0x90, 0x3e, 0x17, 0x77, 0x6a, 0x5e, 0x4a, - 0x41, 0x7e, 0x66, 0x5e, 0x49, 0x7c, 0x66, 0x81, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0xa7, 0x13, 0xdf, - 0xa3, 0x7b, 0xf2, 0x5c, 0xae, 0x50, 0x61, 0xcf, 0x80, 0x20, 0x2e, 0x98, 0x12, 0xcf, 0x02, 0x21, - 0x23, 0x2e, 0x1e, 0xb8, 0x86, 0xdc, 0xc4, 0x64, 0x09, 0x26, 0xb0, 0x0e, 0x7e, 0xa0, 0x0e, 0x6e, - 0x98, 0x0e, 0x5f, 0x47, 0xe7, 0x20, 0xb8, 0xa9, 0xbe, 0x89, 0xc9, 0x42, 0x4e, 0x5c, 0x42, 0x25, - 0xa5, 0x79, 0x79, 0xa9, 0x39, 0xf1, 0xc8, 0x76, 0x31, 0x83, 0x75, 0x8a, 0x00, 0x75, 0x0a, 0x84, - 0x80, 0x65, 0x91, 0x6c, 0x14, 0x28, 0x41, 0x15, 0x29, 0x70, 0x92, 0xb8, 0xf1, 0x50, 0x8e, 0xe1, - 0xc3, 0x43, 0x39, 0xc6, 0x86, 0x47, 0x72, 0x8c, 0x27, 0x80, 0xf8, 0x02, 0x10, 0x3f, 0x00, 0xe2, - 0x24, 0x36, 0xb0, 0xc7, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xbf, 0xd7, 0x7d, 0x7d, 0x08, - 0x01, 0x00, 0x00, + // 212 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4d, 0x29, 0xca, 0x2c, + 0x4b, 0x2d, 0x2a, 0xd6, 0xcf, 0x2f, 0x4b, 0x2d, 0xca, 0x49, 0xac, 0x84, 0xd1, 0x7a, 0x05, 0x45, + 0xf9, 0x25, 0xf9, 0x42, 0xec, 0x50, 0xae, 0x94, 0x48, 0x7a, 0x7e, 0x7a, 0x3e, 0x58, 0x4c, 0x1f, + 0xc4, 0x82, 0x48, 0x2b, 0x6d, 0x65, 0xe4, 0xe2, 0x0a, 0x48, 0x4d, 0x2d, 0x0a, 0x4a, 0x4d, 0xce, + 0x2f, 0x4a, 0x11, 0xd2, 0xe7, 0xe2, 0x4e, 0xcd, 0x4b, 0x29, 0xc8, 0xcf, 0xcc, 0x2b, 0x89, 0xcf, + 0x2c, 0x90, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x74, 0xe2, 0x7b, 0x74, 0x4f, 0x9e, 0xcb, 0x15, 0x2a, + 0xec, 0x19, 0x10, 0xc4, 0x05, 0x53, 0xe2, 0x59, 0x20, 0x64, 0xc4, 0xc5, 0x03, 0xd7, 0x90, 0x9b, + 0x98, 0x2c, 0xc1, 0x04, 0xd6, 0xc1, 0xff, 0xe8, 0x9e, 0x3c, 0x37, 0x4c, 0x87, 0xaf, 0xa3, 0x73, + 0x10, 0xdc, 0x54, 0xdf, 0xc4, 0x64, 0x21, 0x27, 0x2e, 0xa1, 0x92, 0xd2, 0xbc, 0xbc, 0xd4, 0x9c, + 0x78, 0x64, 0xbb, 0x98, 0xc1, 0x3a, 0x45, 0x1e, 0xdd, 0x93, 0x17, 0x08, 0x01, 0xcb, 0x22, 0xd9, + 0x28, 0x50, 0x82, 0x2a, 0x52, 0xe0, 0x24, 0x71, 0xe3, 0xa1, 0x1c, 0xc3, 0x87, 0x87, 0x72, 0x8c, + 0x0d, 0x8f, 0xe4, 0x18, 0x4f, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, + 0xc6, 0x24, 0x36, 0xb0, 0xc7, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x48, 0x07, 0xf6, 0xf3, + 0x18, 0x01, 0x00, 0x00, } diff --git a/vendor/github.com/docker/libnetwork/drivers/remote/driver.go b/vendor/github.com/docker/libnetwork/drivers/remote/driver.go index b52163025a723..9786d9e74689c 100644 --- a/vendor/github.com/docker/libnetwork/drivers/remote/driver.go +++ b/vendor/github.com/docker/libnetwork/drivers/remote/driver.go @@ -1,16 +1,17 @@ package remote import ( - "errors" "fmt" "net" + "github.com/docker/docker/pkg/plugingetter" "github.com/docker/docker/pkg/plugins" "github.com/docker/libnetwork/datastore" "github.com/docker/libnetwork/discoverapi" "github.com/docker/libnetwork/driverapi" "github.com/docker/libnetwork/drivers/remote/api" "github.com/docker/libnetwork/types" + "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -49,7 +50,11 @@ func Init(dc driverapi.DriverCallback, config map[string]interface{}) error { handleFunc = pg.Handle activePlugins := pg.GetAllManagedPluginsByCap(driverapi.NetworkPluginEndpointType) for _, ap := range activePlugins { - newPluginHandler(ap.Name(), ap.Client()) + client, err := getPluginClient(ap) + if err != nil { + return err + } + newPluginHandler(ap.Name(), client) } } handleFunc(driverapi.NetworkPluginEndpointType, newPluginHandler) @@ -57,6 +62,28 @@ func Init(dc driverapi.DriverCallback, config map[string]interface{}) error { return nil } +func getPluginClient(p plugingetter.CompatPlugin) (*plugins.Client, error) { + if v1, ok := p.(plugingetter.PluginWithV1Client); ok { + return v1.Client(), nil + } + + pa, ok := p.(plugingetter.PluginAddr) + if !ok { + return nil, errors.Errorf("unknown plugin type %T", p) + } + + if pa.Protocol() != plugins.ProtocolSchemeHTTPV1 { + return nil, errors.Errorf("unsupported plugin protocol %s", pa.Protocol()) + } + + addr := pa.Addr() + client, err := plugins.NewClientWithTimeout(addr.Network()+"://"+addr.String(), nil, pa.Timeout()) + if err != nil { + return nil, errors.Wrap(err, "error creating plugin client") + } + return client, nil +} + // Get capability from client func (d *driver) getCapabilities() (*driverapi.Capability, error) { var capResp api.GetCapabilityResponse diff --git a/vendor/github.com/docker/libnetwork/drivers/windows/overlay/ov_endpoint_windows.go b/vendor/github.com/docker/libnetwork/drivers/windows/overlay/ov_endpoint_windows.go index b7bda4a6b2de6..c990357922c40 100644 --- a/vendor/github.com/docker/libnetwork/drivers/windows/overlay/ov_endpoint_windows.go +++ b/vendor/github.com/docker/libnetwork/drivers/windows/overlay/ov_endpoint_windows.go @@ -80,7 +80,7 @@ func (n *network) removeEndpointWithAddress(addr *net.IPNet) { _, err := hcsshim.HNSEndpointRequest("DELETE", networkEndpoint.profileID, "") if err != nil { - logrus.Debugf("Failed to delete stale overlay endpoint (%s) from hns", networkEndpoint.id[0:7]) + logrus.Debugf("Failed to delete stale overlay endpoint (%.7s) from hns", networkEndpoint.id) } } } diff --git a/vendor/github.com/docker/libnetwork/drivers/windows/overlay/overlay.pb.go b/vendor/github.com/docker/libnetwork/drivers/windows/overlay/overlay.pb.go index cfa0eeeae4326..9d0cf2663ffc0 100644 --- a/vendor/github.com/docker/libnetwork/drivers/windows/overlay/overlay.pb.go +++ b/vendor/github.com/docker/libnetwork/drivers/windows/overlay/overlay.pb.go @@ -1,12 +1,11 @@ -// Code generated by protoc-gen-gogo. -// source: overlay.proto -// DO NOT EDIT! +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: drivers/windows/overlay/overlay.proto /* Package overlay is a generated protocol buffer package. It is generated from these files: - overlay.proto + drivers/windows/overlay/overlay.proto It has these top-level messages: PeerRecord @@ -19,9 +18,6 @@ import math "math" import _ "github.com/gogo/protobuf/gogoproto" import strings "strings" -import github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto" -import sort "sort" -import strconv "strconv" import reflect "reflect" import io "io" @@ -33,7 +29,9 @@ var _ = math.Inf // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. -const _ = proto.GoGoProtoPackageIsVersion1 +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package // PeerRecord defines the information corresponding to a peer // container in the overlay network. @@ -54,6 +52,27 @@ func (m *PeerRecord) Reset() { *m = PeerRecord{} } func (*PeerRecord) ProtoMessage() {} func (*PeerRecord) Descriptor() ([]byte, []int) { return fileDescriptorOverlay, []int{0} } +func (m *PeerRecord) GetEndpointIP() string { + if m != nil { + return m.EndpointIP + } + return "" +} + +func (m *PeerRecord) GetEndpointMAC() string { + if m != nil { + return m.EndpointMAC + } + return "" +} + +func (m *PeerRecord) GetTunnelEndpointIP() string { + if m != nil { + return m.TunnelEndpointIP + } + return "" +} + func init() { proto.RegisterType((*PeerRecord)(nil), "overlay.PeerRecord") } @@ -77,84 +96,49 @@ func valueToGoStringOverlay(v interface{}, typ string) string { pv := reflect.Indirect(rv).Interface() return fmt.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv) } -func extensionToGoStringOverlay(e map[int32]github_com_gogo_protobuf_proto.Extension) string { - if e == nil { - return "nil" - } - s := "map[int32]proto.Extension{" - keys := make([]int, 0, len(e)) - for k := range e { - keys = append(keys, int(k)) - } - sort.Ints(keys) - ss := []string{} - for _, k := range keys { - ss = append(ss, strconv.Itoa(k)+": "+e[int32(k)].GoString()) - } - s += strings.Join(ss, ",") + "}" - return s -} -func (m *PeerRecord) Marshal() (data []byte, err error) { +func (m *PeerRecord) Marshal() (dAtA []byte, err error) { size := m.Size() - data = make([]byte, size) - n, err := m.MarshalTo(data) + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) if err != nil { return nil, err } - return data[:n], nil + return dAtA[:n], nil } -func (m *PeerRecord) MarshalTo(data []byte) (int, error) { +func (m *PeerRecord) MarshalTo(dAtA []byte) (int, error) { var i int _ = i var l int _ = l if len(m.EndpointIP) > 0 { - data[i] = 0xa + dAtA[i] = 0xa i++ - i = encodeVarintOverlay(data, i, uint64(len(m.EndpointIP))) - i += copy(data[i:], m.EndpointIP) + i = encodeVarintOverlay(dAtA, i, uint64(len(m.EndpointIP))) + i += copy(dAtA[i:], m.EndpointIP) } if len(m.EndpointMAC) > 0 { - data[i] = 0x12 + dAtA[i] = 0x12 i++ - i = encodeVarintOverlay(data, i, uint64(len(m.EndpointMAC))) - i += copy(data[i:], m.EndpointMAC) + i = encodeVarintOverlay(dAtA, i, uint64(len(m.EndpointMAC))) + i += copy(dAtA[i:], m.EndpointMAC) } if len(m.TunnelEndpointIP) > 0 { - data[i] = 0x1a + dAtA[i] = 0x1a i++ - i = encodeVarintOverlay(data, i, uint64(len(m.TunnelEndpointIP))) - i += copy(data[i:], m.TunnelEndpointIP) + i = encodeVarintOverlay(dAtA, i, uint64(len(m.TunnelEndpointIP))) + i += copy(dAtA[i:], m.TunnelEndpointIP) } return i, nil } -func encodeFixed64Overlay(data []byte, offset int, v uint64) int { - data[offset] = uint8(v) - data[offset+1] = uint8(v >> 8) - data[offset+2] = uint8(v >> 16) - data[offset+3] = uint8(v >> 24) - data[offset+4] = uint8(v >> 32) - data[offset+5] = uint8(v >> 40) - data[offset+6] = uint8(v >> 48) - data[offset+7] = uint8(v >> 56) - return offset + 8 -} -func encodeFixed32Overlay(data []byte, offset int, v uint32) int { - data[offset] = uint8(v) - data[offset+1] = uint8(v >> 8) - data[offset+2] = uint8(v >> 16) - data[offset+3] = uint8(v >> 24) - return offset + 4 -} -func encodeVarintOverlay(data []byte, offset int, v uint64) int { +func encodeVarintOverlay(dAtA []byte, offset int, v uint64) int { for v >= 1<<7 { - data[offset] = uint8(v&0x7f | 0x80) + dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } - data[offset] = uint8(v) + dAtA[offset] = uint8(v) return offset + 1 } func (m *PeerRecord) Size() (n int) { @@ -208,8 +192,8 @@ func valueToStringOverlay(v interface{}) string { pv := reflect.Indirect(rv).Interface() return fmt.Sprintf("*%v", pv) } -func (m *PeerRecord) Unmarshal(data []byte) error { - l := len(data) +func (m *PeerRecord) Unmarshal(dAtA []byte) error { + l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx @@ -221,7 +205,7 @@ func (m *PeerRecord) Unmarshal(data []byte) error { if iNdEx >= l { return io.ErrUnexpectedEOF } - b := data[iNdEx] + b := dAtA[iNdEx] iNdEx++ wire |= (uint64(b) & 0x7F) << shift if b < 0x80 { @@ -249,7 +233,7 @@ func (m *PeerRecord) Unmarshal(data []byte) error { if iNdEx >= l { return io.ErrUnexpectedEOF } - b := data[iNdEx] + b := dAtA[iNdEx] iNdEx++ stringLen |= (uint64(b) & 0x7F) << shift if b < 0x80 { @@ -264,7 +248,7 @@ func (m *PeerRecord) Unmarshal(data []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.EndpointIP = string(data[iNdEx:postIndex]) + m.EndpointIP = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { @@ -278,7 +262,7 @@ func (m *PeerRecord) Unmarshal(data []byte) error { if iNdEx >= l { return io.ErrUnexpectedEOF } - b := data[iNdEx] + b := dAtA[iNdEx] iNdEx++ stringLen |= (uint64(b) & 0x7F) << shift if b < 0x80 { @@ -293,7 +277,7 @@ func (m *PeerRecord) Unmarshal(data []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.EndpointMAC = string(data[iNdEx:postIndex]) + m.EndpointMAC = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { @@ -307,7 +291,7 @@ func (m *PeerRecord) Unmarshal(data []byte) error { if iNdEx >= l { return io.ErrUnexpectedEOF } - b := data[iNdEx] + b := dAtA[iNdEx] iNdEx++ stringLen |= (uint64(b) & 0x7F) << shift if b < 0x80 { @@ -322,11 +306,11 @@ func (m *PeerRecord) Unmarshal(data []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.TunnelEndpointIP = string(data[iNdEx:postIndex]) + m.TunnelEndpointIP = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex - skippy, err := skipOverlay(data[iNdEx:]) + skippy, err := skipOverlay(dAtA[iNdEx:]) if err != nil { return err } @@ -345,8 +329,8 @@ func (m *PeerRecord) Unmarshal(data []byte) error { } return nil } -func skipOverlay(data []byte) (n int, err error) { - l := len(data) +func skipOverlay(dAtA []byte) (n int, err error) { + l := len(dAtA) iNdEx := 0 for iNdEx < l { var wire uint64 @@ -357,7 +341,7 @@ func skipOverlay(data []byte) (n int, err error) { if iNdEx >= l { return 0, io.ErrUnexpectedEOF } - b := data[iNdEx] + b := dAtA[iNdEx] iNdEx++ wire |= (uint64(b) & 0x7F) << shift if b < 0x80 { @@ -375,7 +359,7 @@ func skipOverlay(data []byte) (n int, err error) { return 0, io.ErrUnexpectedEOF } iNdEx++ - if data[iNdEx-1] < 0x80 { + if dAtA[iNdEx-1] < 0x80 { break } } @@ -392,7 +376,7 @@ func skipOverlay(data []byte) (n int, err error) { if iNdEx >= l { return 0, io.ErrUnexpectedEOF } - b := data[iNdEx] + b := dAtA[iNdEx] iNdEx++ length |= (int(b) & 0x7F) << shift if b < 0x80 { @@ -415,7 +399,7 @@ func skipOverlay(data []byte) (n int, err error) { if iNdEx >= l { return 0, io.ErrUnexpectedEOF } - b := data[iNdEx] + b := dAtA[iNdEx] iNdEx++ innerWire |= (uint64(b) & 0x7F) << shift if b < 0x80 { @@ -426,7 +410,7 @@ func skipOverlay(data []byte) (n int, err error) { if innerWireType == 4 { break } - next, err := skipOverlay(data[start:]) + next, err := skipOverlay(dAtA[start:]) if err != nil { return 0, err } @@ -450,19 +434,22 @@ var ( ErrIntOverflowOverlay = fmt.Errorf("proto: integer overflow") ) +func init() { proto.RegisterFile("drivers/windows/overlay/overlay.proto", fileDescriptorOverlay) } + var fileDescriptorOverlay = []byte{ - // 195 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0xcd, 0x2f, 0x4b, 0x2d, - 0xca, 0x49, 0xac, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x87, 0x72, 0xa5, 0x44, 0xd2, - 0xf3, 0xd3, 0xf3, 0xc1, 0x62, 0xfa, 0x20, 0x16, 0x44, 0x5a, 0x69, 0x2b, 0x23, 0x17, 0x57, 0x40, - 0x6a, 0x6a, 0x51, 0x50, 0x6a, 0x72, 0x7e, 0x51, 0x8a, 0x90, 0x3e, 0x17, 0x77, 0x6a, 0x5e, 0x4a, - 0x41, 0x7e, 0x66, 0x5e, 0x49, 0x7c, 0x66, 0x81, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0xa7, 0x13, 0xdf, - 0xa3, 0x7b, 0xf2, 0x5c, 0xae, 0x50, 0x61, 0xcf, 0x80, 0x20, 0x2e, 0x98, 0x12, 0xcf, 0x02, 0x21, - 0x23, 0x2e, 0x1e, 0xb8, 0x86, 0xdc, 0xc4, 0x64, 0x09, 0x26, 0xb0, 0x0e, 0x7e, 0xa0, 0x0e, 0x6e, - 0x98, 0x0e, 0x5f, 0x47, 0xe7, 0x20, 0xb8, 0xa9, 0xbe, 0x89, 0xc9, 0x42, 0x4e, 0x5c, 0x42, 0x25, - 0xa5, 0x79, 0x79, 0xa9, 0x39, 0xf1, 0xc8, 0x76, 0x31, 0x83, 0x75, 0x8a, 0x00, 0x75, 0x0a, 0x84, - 0x80, 0x65, 0x91, 0x6c, 0x14, 0x28, 0x41, 0x15, 0x29, 0x70, 0x92, 0xb8, 0xf1, 0x50, 0x8e, 0xe1, - 0xc3, 0x43, 0x39, 0xc6, 0x86, 0x47, 0x72, 0x8c, 0x27, 0x80, 0xf8, 0x02, 0x10, 0x3f, 0x00, 0xe2, - 0x24, 0x36, 0xb0, 0xc7, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xbf, 0xd7, 0x7d, 0x7d, 0x08, - 0x01, 0x00, 0x00, + // 220 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4d, 0x29, 0xca, 0x2c, + 0x4b, 0x2d, 0x2a, 0xd6, 0x2f, 0xcf, 0xcc, 0x4b, 0xc9, 0x2f, 0x2f, 0xd6, 0xcf, 0x2f, 0x4b, 0x2d, + 0xca, 0x49, 0xac, 0x84, 0xd1, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0xec, 0x50, 0xae, 0x94, + 0x48, 0x7a, 0x7e, 0x7a, 0x3e, 0x58, 0x4c, 0x1f, 0xc4, 0x82, 0x48, 0x2b, 0x6d, 0x65, 0xe4, 0xe2, + 0x0a, 0x48, 0x4d, 0x2d, 0x0a, 0x4a, 0x4d, 0xce, 0x2f, 0x4a, 0x11, 0xd2, 0xe7, 0xe2, 0x4e, 0xcd, + 0x4b, 0x29, 0xc8, 0xcf, 0xcc, 0x2b, 0x89, 0xcf, 0x2c, 0x90, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x74, + 0xe2, 0x7b, 0x74, 0x4f, 0x9e, 0xcb, 0x15, 0x2a, 0xec, 0x19, 0x10, 0xc4, 0x05, 0x53, 0xe2, 0x59, + 0x20, 0x64, 0xc4, 0xc5, 0x03, 0xd7, 0x90, 0x9b, 0x98, 0x2c, 0xc1, 0x04, 0xd6, 0xc1, 0xff, 0xe8, + 0x9e, 0x3c, 0x37, 0x4c, 0x87, 0xaf, 0xa3, 0x73, 0x10, 0xdc, 0x54, 0xdf, 0xc4, 0x64, 0x21, 0x27, + 0x2e, 0xa1, 0x92, 0xd2, 0xbc, 0xbc, 0xd4, 0x9c, 0x78, 0x64, 0xbb, 0x98, 0xc1, 0x3a, 0x45, 0x1e, + 0xdd, 0x93, 0x17, 0x08, 0x01, 0xcb, 0x22, 0xd9, 0x28, 0x50, 0x82, 0x2a, 0x52, 0xe0, 0x24, 0x71, + 0xe3, 0xa1, 0x1c, 0xc3, 0x87, 0x87, 0x72, 0x8c, 0x0d, 0x8f, 0xe4, 0x18, 0x4f, 0x3c, 0x92, 0x63, + 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x24, 0x36, 0xb0, 0xc7, 0x8c, 0x01, 0x01, + 0x00, 0x00, 0xff, 0xff, 0xc0, 0x48, 0xd1, 0xc0, 0x20, 0x01, 0x00, 0x00, } diff --git a/vendor/github.com/docker/libnetwork/drivers/windows/windows.go b/vendor/github.com/docker/libnetwork/drivers/windows/windows.go index 5927fd8560ca0..c1cc61aa352e2 100644 --- a/vendor/github.com/docker/libnetwork/drivers/windows/windows.go +++ b/vendor/github.com/docker/libnetwork/drivers/windows/windows.go @@ -415,7 +415,7 @@ func (d *driver) DeleteNetwork(nid string) error { // delele endpoints belong to this network for _, ep := range n.endpoints { if err := d.storeDelete(ep); err != nil { - logrus.Warnf("Failed to remove bridge endpoint %s from store: %v", ep.id[0:7], err) + logrus.Warnf("Failed to remove bridge endpoint %.7s from store: %v", ep.id, err) } } @@ -704,7 +704,7 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, } if err = d.storeUpdate(endpoint); err != nil { - logrus.Errorf("Failed to save endpoint %s to store: %v", endpoint.id[0:7], err) + logrus.Errorf("Failed to save endpoint %.7s to store: %v", endpoint.id, err) } return nil @@ -731,7 +731,7 @@ func (d *driver) DeleteEndpoint(nid, eid string) error { } if err := d.storeDelete(ep); err != nil { - logrus.Warnf("Failed to remove bridge endpoint %s from store: %v", ep.id[0:7], err) + logrus.Warnf("Failed to remove bridge endpoint %.7s from store: %v", ep.id, err) } return nil } diff --git a/vendor/github.com/docker/libnetwork/drivers/windows/windows_store.go b/vendor/github.com/docker/libnetwork/drivers/windows/windows_store.go index 9f8a7b18f5075..f717c1a613b27 100644 --- a/vendor/github.com/docker/libnetwork/drivers/windows/windows_store.go +++ b/vendor/github.com/docker/libnetwork/drivers/windows/windows_store.go @@ -64,7 +64,7 @@ func (d *driver) populateNetworks() error { if err = d.createNetwork(ncfg); err != nil { logrus.Warnf("could not create windows network for id %s hnsid %s while booting up from persistent state: %v", ncfg.ID, ncfg.HnsID, err) } - logrus.Debugf("Network %v (%s) restored", d.name, ncfg.ID[0:7]) + logrus.Debugf("Network %v (%.7s) restored", d.name, ncfg.ID) } return nil @@ -87,15 +87,15 @@ func (d *driver) populateEndpoints() error { } n, ok := d.networks[ep.nid] if !ok { - logrus.Debugf("Network (%s) not found for restored endpoint (%s)", ep.nid[0:7], ep.id[0:7]) - logrus.Debugf("Deleting stale endpoint (%s) from store", ep.id[0:7]) + logrus.Debugf("Network (%.7s) not found for restored endpoint (%.7s)", ep.nid, ep.id) + logrus.Debugf("Deleting stale endpoint (%.7s) from store", ep.id) if err := d.storeDelete(ep); err != nil { - logrus.Debugf("Failed to delete stale endpoint (%s) from store", ep.id[0:7]) + logrus.Debugf("Failed to delete stale endpoint (%.7s) from store", ep.id) } continue } n.endpoints[ep.id] = ep - logrus.Debugf("Endpoint (%s) restored to network (%s)", ep.id[0:7], ep.nid[0:7]) + logrus.Debugf("Endpoint (%.7s) restored to network (%.7s)", ep.id, ep.nid) } return nil diff --git a/vendor/github.com/docker/libnetwork/endpoint.go b/vendor/github.com/docker/libnetwork/endpoint.go index e2457864756c9..822f88bd3ef2c 100644 --- a/vendor/github.com/docker/libnetwork/endpoint.go +++ b/vendor/github.com/docker/libnetwork/endpoint.go @@ -540,6 +540,12 @@ func (ep *endpoint) sbJoin(sb *sandbox, options ...EndpointOption) (err error) { } }() + // Load balancing endpoints should never have a default gateway nor + // should they alter the status of a network's default gateway + if ep.loadBalancer && !sb.ingress { + return nil + } + if sb.needDefaultGW() && sb.getEndpointInGWNetwork() == nil { return sb.setupDefaultGW() } diff --git a/vendor/github.com/docker/libnetwork/endpoint_info.go b/vendor/github.com/docker/libnetwork/endpoint_info.go index 68d3e8673dc7f..80b662defa478 100644 --- a/vendor/github.com/docker/libnetwork/endpoint_info.go +++ b/vendor/github.com/docker/libnetwork/endpoint_info.go @@ -49,6 +49,9 @@ type InterfaceInfo interface { // LinkLocalAddresses returns the list of link-local (IPv4/IPv6) addresses assigned to the endpoint. LinkLocalAddresses() []*net.IPNet + + // SrcName returns the name of the interface w/in the container + SrcName() string } type endpointInterface struct { @@ -272,6 +275,10 @@ func (epi *endpointInterface) LinkLocalAddresses() []*net.IPNet { return epi.llAddrs } +func (epi *endpointInterface) SrcName() string { + return epi.srcName +} + func (epi *endpointInterface) SetNames(srcName string, dstPrefix string) error { epi.srcName = srcName epi.dstPrefix = dstPrefix diff --git a/vendor/github.com/docker/libnetwork/ipam/allocator.go b/vendor/github.com/docker/libnetwork/ipam/allocator.go index 29473c5cc3224..44070119dc4a8 100644 --- a/vendor/github.com/docker/libnetwork/ipam/allocator.go +++ b/vendor/github.com/docker/libnetwork/ipam/allocator.go @@ -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) @@ -197,6 +203,10 @@ func (a *Allocator) GetDefaultAddressSpaces() (string, string, error) { } // RequestPool returns an address pool along with its unique id. +// addressSpace must be a valid address space name and must not be the empty string. +// If pool is the empty string then the default predefined pool for addressSpace will be used, otherwise pool must be a valid IP address and length in CIDR notation. +// If subPool is not empty, it must be a valid IP address and length in CIDR notation which is a sub-range of pool. +// subPool must be empty if pool is empty. func (a *Allocator) RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) { logrus.Debugf("RequestPool(%s, %s, %s, %v, %t)", addressSpace, pool, subPool, options, v6) @@ -277,8 +287,8 @@ retry: return remove() } -// Given the address space, returns the local or global PoolConfig based on the -// address space is local or global. AddressSpace locality is being registered with IPAM out of band. +// Given the address space, returns the local or global PoolConfig based on whether the +// address space is local or global. AddressSpace locality is registered with IPAM out of band. func (a *Allocator) getAddrSpace(as string) (*addrSpace, error) { a.Lock() defer a.Unlock() @@ -289,6 +299,8 @@ func (a *Allocator) getAddrSpace(as string) (*addrSpace, error) { return aSpace, nil } +// parsePoolRequest parses and validates a request to create a new pool under addressSpace and returns +// a SubnetKey, network and range describing the request. func (a *Allocator) parsePoolRequest(addressSpace, pool, subPool string, v6 bool) (*SubnetKey, *net.IPNet, *AddressRange, error) { var ( nw *net.IPNet @@ -374,11 +386,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 +422,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) } diff --git a/vendor/github.com/docker/libnetwork/ipam/structures.go b/vendor/github.com/docker/libnetwork/ipam/structures.go index 09a77695dd4f3..2e6d75eaa4db1 100644 --- a/vendor/github.com/docker/libnetwork/ipam/structures.go +++ b/vendor/github.com/docker/libnetwork/ipam/structures.go @@ -257,17 +257,19 @@ func (aSpace *addrSpace) New() datastore.KVObject { } } +// updatePoolDBOnAdd returns a closure which will add the subnet k to the address space when executed. func (aSpace *addrSpace) updatePoolDBOnAdd(k SubnetKey, nw *net.IPNet, ipr *AddressRange, pdf bool) (func() error, error) { aSpace.Lock() 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 @@ -280,7 +282,7 @@ func (aSpace *addrSpace) updatePoolDBOnAdd(k SubnetKey, nw *net.IPNet, ipr *Addr return func() error { return aSpace.alloc.insertBitMask(k, nw) }, nil } - // This is a new non-master pool + // This is a new non-master pool (subPool) p := &PoolData{ ParentKey: SubnetKey{AddressSpace: k.AddressSpace, Subnet: k.Subnet}, Pool: nw, diff --git a/vendor/github.com/docker/libnetwork/ipams/remote/remote.go b/vendor/github.com/docker/libnetwork/ipams/remote/remote.go index 9f2f1d5fed3f8..6f2173f3085a2 100644 --- a/vendor/github.com/docker/libnetwork/ipams/remote/remote.go +++ b/vendor/github.com/docker/libnetwork/ipams/remote/remote.go @@ -4,11 +4,13 @@ import ( "fmt" "net" + "github.com/docker/docker/pkg/plugingetter" "github.com/docker/docker/pkg/plugins" "github.com/docker/libnetwork/discoverapi" "github.com/docker/libnetwork/ipamapi" "github.com/docker/libnetwork/ipams/remote/api" "github.com/docker/libnetwork/types" + "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -52,13 +54,39 @@ func Init(cb ipamapi.Callback, l, g interface{}) error { handleFunc = pg.Handle activePlugins := pg.GetAllManagedPluginsByCap(ipamapi.PluginEndpointType) for _, ap := range activePlugins { - newPluginHandler(ap.Name(), ap.Client()) + client, err := getPluginClient(ap) + if err != nil { + return err + } + newPluginHandler(ap.Name(), client) } } handleFunc(ipamapi.PluginEndpointType, newPluginHandler) return nil } +func getPluginClient(p plugingetter.CompatPlugin) (*plugins.Client, error) { + if v1, ok := p.(plugingetter.PluginWithV1Client); ok { + return v1.Client(), nil + } + + pa, ok := p.(plugingetter.PluginAddr) + if !ok { + return nil, errors.Errorf("unknown plugin type %T", p) + } + + if pa.Protocol() != plugins.ProtocolSchemeHTTPV1 { + return nil, errors.Errorf("unsupported plugin protocol %s", pa.Protocol()) + } + + addr := pa.Addr() + client, err := plugins.NewClientWithTimeout(addr.Network()+"://"+addr.String(), nil, pa.Timeout()) + if err != nil { + return nil, errors.Wrap(err, "error creating plugin client") + } + return client, nil +} + func (a *allocator) call(methodName string, arg interface{}, retVal PluginResponse) error { method := ipamapi.PluginEndpointType + "." + methodName err := a.endpoint.Call(method, arg, retVal) diff --git a/vendor/github.com/docker/libnetwork/network.go b/vendor/github.com/docker/libnetwork/network.go index e283d658153ea..5ca660c3b55cb 100644 --- a/vendor/github.com/docker/libnetwork/network.go +++ b/vendor/github.com/docker/libnetwork/network.go @@ -40,7 +40,7 @@ type Network interface { CreateEndpoint(name string, options ...EndpointOption) (Endpoint, error) // Delete the network. - Delete() error + Delete(options ...NetworkDeleteOption) error // Endpoints returns the list of Endpoint(s) in this network. Endpoints() []Endpoint @@ -875,6 +875,28 @@ func (n *network) processOptions(options ...NetworkOption) { } } +type networkDeleteParams struct { + rmLBEndpoint bool +} + +// NetworkDeleteOption is a type for optional parameters to pass to the +// network.Delete() function. +type NetworkDeleteOption func(p *networkDeleteParams) + +// NetworkDeleteOptionRemoveLB informs a network.Delete() operation that should +// remove the load balancer endpoint for this network. Note that the Delete() +// method will automatically remove a load balancing endpoint for most networks +// when the network is otherwise empty. However, this does not occur for some +// networks. In particular, networks marked as ingress (which are supposed to +// be more permanent than other overlay networks) won't automatically remove +// the LB endpoint on Delete(). This method allows for explicit removal of +// such networks provided there are no other endpoints present in the network. +// If the network still has non-LB endpoints present, Delete() will not +// remove the LB endpoint and will return an error. +func NetworkDeleteOptionRemoveLB(p *networkDeleteParams) { + p.rmLBEndpoint = true +} + func (n *network) resolveDriver(name string, load bool) (driverapi.Driver, *driverapi.Capability, error) { c := n.getController() @@ -938,11 +960,23 @@ func (n *network) driver(load bool) (driverapi.Driver, error) { return d, nil } -func (n *network) Delete() error { - return n.delete(false) +func (n *network) Delete(options ...NetworkDeleteOption) error { + var params networkDeleteParams + for _, opt := range options { + opt(¶ms) + } + return n.delete(false, params.rmLBEndpoint) } -func (n *network) delete(force bool) error { +// This function gets called in 3 ways: +// * Delete() -- (false, false) +// remove if endpoint count == 0 or endpoint count == 1 and +// there is a load balancer IP +// * Delete(libnetwork.NetworkDeleteOptionRemoveLB) -- (false, true) +// remove load balancer and network if endpoint count == 1 +// * controller.networkCleanup() -- (true, true) +// remove the network no matter what +func (n *network) delete(force bool, rmLBEndpoint bool) error { n.Lock() c := n.ctrlr name := n.name @@ -957,10 +991,32 @@ func (n *network) delete(force bool) error { return &UnknownNetworkError{name: name, id: id} } - if len(n.loadBalancerIP) != 0 { - endpoints := n.Endpoints() - if force || (len(endpoints) == 1 && !n.ingress) { - n.deleteLoadBalancerSandbox() + // Only remove ingress on force removal or explicit LB endpoint removal + if n.ingress && !force && !rmLBEndpoint { + return &ActiveEndpointsError{name: n.name, id: n.id} + } + + // Check that the network is empty + var emptyCount uint64 + if n.hasLoadBalancerEndpoint() { + emptyCount = 1 + } + if !force && n.getEpCnt().EndpointCnt() > emptyCount { + if n.configOnly { + return types.ForbiddenErrorf("configuration network %q is in use", n.Name()) + } + return &ActiveEndpointsError{name: n.name, id: n.id} + } + + if n.hasLoadBalancerEndpoint() { + // If we got to this point, then the following must hold: + // * force is true OR endpoint count == 1 + if err := n.deleteLoadBalancerSandbox(); err != nil { + if !force { + return err + } + // continue deletion when force is true even on error + logrus.Warnf("Error deleting load balancer sandbox: %v", err) } //Reload the network from the store to update the epcnt. n, err = c.getNetworkFromStore(id) @@ -969,12 +1025,10 @@ func (n *network) delete(force bool) error { } } - if !force && n.getEpCnt().EndpointCnt() != 0 { - if n.configOnly { - return types.ForbiddenErrorf("configuration network %q is in use", n.Name()) - } - return &ActiveEndpointsError{name: n.name, id: n.id} - } + // Up to this point, errors that we returned were recoverable. + // From here on, any errors leave us in an inconsistent state. + // This is unfortunate, but there isn't a safe way to + // reconstitute a load-balancer endpoint after removing it. // Mark the network for deletion n.inDelete = true @@ -1023,9 +1077,6 @@ func (n *network) delete(force bool) error { // Cleanup the service discovery for this network c.cleanupServiceDiscovery(n.ID()) - // Cleanup the load balancer - c.cleanupServiceBindings(n.ID()) - removeFromStore: // deleteFromStore performs an atomic delete operation and the // network.epCnt will help prevent any possible @@ -1156,26 +1207,27 @@ 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 { + + if err = n.addEndpoint(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 e := ep.deleteEndpoint(false); e != nil { + logrus.Warnf("cleaning up endpoint failed %s : %v", name, e) } } }() - if err = n.addEndpoint(ep); err != nil { + // 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 := ep.deleteEndpoint(false); e != nil { - logrus.Warnf("cleaning up endpoint failed %s : %v", name, e) + if e := n.getController().deleteFromStore(ep); e != nil { + logrus.Warnf("error rolling back endpoint %s from store: %v", name, e) } } }() @@ -1338,7 +1390,7 @@ func (n *network) addSvcRecords(eID, name, serviceID string, epIP, epIPv6 net.IP return } - logrus.Debugf("%s (%s).addSvcRecords(%s, %s, %s, %t) %s sid:%s", eID, n.ID()[0:7], name, epIP, epIPv6, ipMapUpdate, method, serviceID) + logrus.Debugf("%s (%.7s).addSvcRecords(%s, %s, %s, %t) %s sid:%s", eID, n.ID(), name, epIP, epIPv6, ipMapUpdate, method, serviceID) c := n.getController() c.Lock() @@ -1374,7 +1426,7 @@ func (n *network) deleteSvcRecords(eID, name, serviceID string, epIP net.IP, epI return } - logrus.Debugf("%s (%s).deleteSvcRecords(%s, %s, %s, %t) %s sid:%s ", eID, n.ID()[0:7], name, epIP, epIPv6, ipMapUpdate, method, serviceID) + logrus.Debugf("%s (%.7s).deleteSvcRecords(%s, %s, %s, %t) %s sid:%s ", eID, n.ID(), name, epIP, epIPv6, ipMapUpdate, method, serviceID) c := n.getController() c.Lock() @@ -1876,6 +1928,10 @@ func (n *network) hasSpecialDriver() bool { return n.Type() == "host" || n.Type() == "null" } +func (n *network) hasLoadBalancerEndpoint() bool { + return len(n.loadBalancerIP) != 0 +} + func (n *network) ResolveName(req string, ipType int) ([]net.IP, bool) { var ipv6Miss bool @@ -2055,9 +2111,22 @@ func (c *controller) getConfigNetwork(name string) (*network, error) { return n.(*network), nil } -func (n *network) createLoadBalancerSandbox() error { - sandboxName := n.name + "-sbox" - sbOptions := []SandboxOption{} +func (n *network) lbSandboxName() string { + name := "lb-" + n.name + if n.ingress { + name = n.name + "-sbox" + } + return name +} + +func (n *network) lbEndpointName() string { + return n.name + "-endpoint" +} + +func (n *network) createLoadBalancerSandbox() (retErr error) { + sandboxName := n.lbSandboxName() + // Mark the sandbox to be a load balancer + sbOptions := []SandboxOption{OptionLoadBalancer()} if n.ingress { sbOptions = append(sbOptions, OptionIngress()) } @@ -2066,26 +2135,30 @@ func (n *network) createLoadBalancerSandbox() error { return err } defer func() { - if err != nil { + if retErr != nil { if e := n.ctrlr.SandboxDestroy(sandboxName); e != nil { - logrus.Warnf("could not delete sandbox %s on failure on failure (%v): %v", sandboxName, err, e) + logrus.Warnf("could not delete sandbox %s on failure on failure (%v): %v", sandboxName, retErr, e) } } }() - endpointName := n.name + "-endpoint" + endpointName := n.lbEndpointName() epOptions := []EndpointOption{ CreateOptionIpam(n.loadBalancerIP, nil, nil, nil), CreateOptionLoadBalancer(), } + if n.hasLoadBalancerEndpoint() && !n.ingress { + // Mark LB endpoints as anonymous so they don't show up in DNS + epOptions = append(epOptions, CreateOptionAnonymous()) + } ep, err := n.createEndpoint(endpointName, epOptions...) if err != nil { return err } defer func() { - if err != nil { + if retErr != nil { if e := ep.Delete(true); e != nil { - logrus.Warnf("could not delete endpoint %s on failure on failure (%v): %v", endpointName, err, e) + logrus.Warnf("could not delete endpoint %s on failure on failure (%v): %v", endpointName, retErr, e) } } }() @@ -2093,17 +2166,18 @@ func (n *network) createLoadBalancerSandbox() error { if err := ep.Join(sb, nil); err != nil { return err } + return sb.EnableService() } -func (n *network) deleteLoadBalancerSandbox() { +func (n *network) deleteLoadBalancerSandbox() error { n.Lock() c := n.ctrlr name := n.name n.Unlock() - endpointName := name + "-endpoint" - sandboxName := name + "-sbox" + sandboxName := n.lbSandboxName() + endpointName := n.lbEndpointName() endpoint, err := n.EndpointByName(endpointName) if err != nil { @@ -2128,6 +2202,7 @@ func (n *network) deleteLoadBalancerSandbox() { } if err := c.SandboxDestroy(sandboxName); err != nil { - logrus.Warnf("Failed to delete %s sandbox: %v", sandboxName, err) + return fmt.Errorf("Failed to delete %s sandbox: %v", sandboxName, err) } + return nil } diff --git a/vendor/github.com/docker/libnetwork/networkdb/broadcast.go b/vendor/github.com/docker/libnetwork/networkdb/broadcast.go index 174023b22b83d..efcfcc24268be 100644 --- a/vendor/github.com/docker/libnetwork/networkdb/broadcast.go +++ b/vendor/github.com/docker/libnetwork/networkdb/broadcast.go @@ -110,7 +110,6 @@ type tableEventMessage struct { tname string key string msg []byte - node string } func (m *tableEventMessage) Invalidates(other memberlist.Broadcast) bool { @@ -168,7 +167,6 @@ func (nDB *NetworkDB) sendTableEvent(event TableEvent_Type, nid string, tname st id: nid, tname: tname, key: key, - node: nDB.config.NodeID, }) return nil } diff --git a/vendor/github.com/docker/libnetwork/networkdb/cluster.go b/vendor/github.com/docker/libnetwork/networkdb/cluster.go index bd48fb9f1819c..0a64787df9d0f 100644 --- a/vendor/github.com/docker/libnetwork/networkdb/cluster.go +++ b/vendor/github.com/docker/libnetwork/networkdb/cluster.go @@ -24,6 +24,9 @@ const ( retryInterval = 1 * time.Second nodeReapInterval = 24 * time.Hour nodeReapPeriod = 2 * time.Hour + // considering a cluster with > 20 nodes and a drain speed of 100 msg/s + // the following is roughly 1 minute + maxQueueLenBroadcastOnSync = 500 ) type logWriter struct{} @@ -52,7 +55,7 @@ func (l *logWriter) Write(p []byte) (int, error) { // SetKey adds a new key to the key ring func (nDB *NetworkDB) SetKey(key []byte) { - logrus.Debugf("Adding key %s", hex.EncodeToString(key)[0:5]) + logrus.Debugf("Adding key %.5s", hex.EncodeToString(key)) nDB.Lock() defer nDB.Unlock() for _, dbKey := range nDB.config.Keys { @@ -69,7 +72,7 @@ func (nDB *NetworkDB) SetKey(key []byte) { // SetPrimaryKey sets the given key as the primary key. This should have // been added apriori through SetKey func (nDB *NetworkDB) SetPrimaryKey(key []byte) { - logrus.Debugf("Primary Key %s", hex.EncodeToString(key)[0:5]) + logrus.Debugf("Primary Key %.5s", hex.EncodeToString(key)) nDB.RLock() defer nDB.RUnlock() for _, dbKey := range nDB.config.Keys { @@ -85,7 +88,7 @@ func (nDB *NetworkDB) SetPrimaryKey(key []byte) { // RemoveKey removes a key from the key ring. The key being removed // can't be the primary key func (nDB *NetworkDB) RemoveKey(key []byte) { - logrus.Debugf("Remove Key %s", hex.EncodeToString(key)[0:5]) + logrus.Debugf("Remove Key %.5s", hex.EncodeToString(key)) nDB.Lock() defer nDB.Unlock() for i, dbKey := range nDB.config.Keys { @@ -123,7 +126,7 @@ func (nDB *NetworkDB) clusterInit() error { var err error if len(nDB.config.Keys) > 0 { for i, key := range nDB.config.Keys { - logrus.Debugf("Encryption key %d: %s", i+1, hex.EncodeToString(key)[0:5]) + logrus.Debugf("Encryption key %d: %.5s", i+1, hex.EncodeToString(key)) } nDB.keyring, err = memberlist.NewKeyring(nDB.config.Keys, nDB.config.Keys[0]) if err != nil { @@ -285,18 +288,35 @@ func (nDB *NetworkDB) rejoinClusterBootStrap() { return } + myself, _ := nDB.nodes[nDB.config.NodeID] bootStrapIPs := make([]string, 0, len(nDB.bootStrapIP)) for _, bootIP := range nDB.bootStrapIP { - for _, node := range nDB.nodes { - if node.Addr.Equal(bootIP) { - // One of the bootstrap nodes is part of the cluster, return - nDB.RUnlock() - return + // botostrap IPs are usually IP:port from the Join + var bootstrapIP net.IP + ipStr, _, err := net.SplitHostPort(bootIP) + if err != nil { + // try to parse it as an IP with port + // Note this seems to be the case for swarm that do not specify any port + ipStr = bootIP + } + bootstrapIP = net.ParseIP(ipStr) + if bootstrapIP != nil { + for _, node := range nDB.nodes { + if node.Addr.Equal(bootstrapIP) && !node.Addr.Equal(myself.Addr) { + // One of the bootstrap nodes (and not myself) is part of the cluster, return + nDB.RUnlock() + return + } } + bootStrapIPs = append(bootStrapIPs, bootIP) } - bootStrapIPs = append(bootStrapIPs, bootIP.String()) } nDB.RUnlock() + if len(bootStrapIPs) == 0 { + // this will also avoid to call the Join with an empty list erasing the current bootstrap ip list + logrus.Debug("rejoinClusterBootStrap did not find any valid IP") + return + } // None of the bootStrap nodes are in the cluster, call memberlist join logrus.Debugf("rejoinClusterBootStrap, calling cluster join with bootStrap %v", bootStrapIPs) ctx, cancel := context.WithTimeout(nDB.ctx, rejoinClusterDuration) @@ -555,6 +575,7 @@ func (nDB *NetworkDB) bulkSync(nodes []string, all bool) ([]string, error) { var err error var networks []string + var success bool for _, node := range nodes { if node == nDB.config.NodeID { continue @@ -562,21 +583,25 @@ func (nDB *NetworkDB) bulkSync(nodes []string, all bool) ([]string, error) { logrus.Debugf("%v(%v): Initiating bulk sync with node %v", nDB.config.Hostname, nDB.config.NodeID, node) networks = nDB.findCommonNetworks(node) err = nDB.bulkSyncNode(networks, node, true) - // if its periodic bulksync stop after the first successful sync - if !all && err == nil { - break - } if err != nil { err = fmt.Errorf("bulk sync to node %s failed: %v", node, err) logrus.Warn(err.Error()) + } else { + // bulk sync succeeded + success = true + // if its periodic bulksync stop after the first successful sync + if !all { + break + } } } - if err != nil { - return nil, err + if success { + // if at least one node sync succeeded + return networks, nil } - return networks, nil + return nil, err } // Bulk sync all the table entries belonging to a set of networks to a diff --git a/vendor/github.com/docker/libnetwork/networkdb/delegate.go b/vendor/github.com/docker/libnetwork/networkdb/delegate.go index 9a379fe7cdb20..14e19bbdd7fb3 100644 --- a/vendor/github.com/docker/libnetwork/networkdb/delegate.go +++ b/vendor/github.com/docker/libnetwork/networkdb/delegate.go @@ -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 } @@ -142,7 +142,7 @@ func (nDB *NetworkDB) handleNetworkEvent(nEvent *NetworkEvent) bool { return true } -func (nDB *NetworkDB) handleTableEvent(tEvent *TableEvent) bool { +func (nDB *NetworkDB) handleTableEvent(tEvent *TableEvent, isBulkSync bool) bool { // Update our local clock if the received messages has newer time. nDB.tableClock.Witness(tEvent.LTime) @@ -175,6 +175,14 @@ func (nDB *NetworkDB) handleTableEvent(tEvent *TableEvent) bool { nDB.Unlock() return false } + } else if tEvent.Type == TableEventTypeDelete && !isBulkSync { + nDB.Unlock() + // We don't know the entry, the entry is being deleted and the message is an async message + // In this case the safest approach is to ignore it, it is possible that the queue grew so much to + // exceed the garbage collection time (the residual reap time that is in the message is not being + // updated, to avoid inserting too many messages in the queue). + // Instead the messages coming from TCP bulk sync are safe with the latest value for the garbage collection time + return false } e = &entry{ @@ -197,11 +205,17 @@ func (nDB *NetworkDB) handleTableEvent(tEvent *TableEvent) bool { nDB.Unlock() if err != nil && tEvent.Type == TableEventTypeDelete { - // If it is a delete event and we did not have a state for it, don't propagate to the application + // Again we don't know the entry but this is coming from a TCP sync so the message body is up to date. + // We had saved the state so to speed up convergence and be able to avoid accepting create events. + // Now we will rebroadcast the message if 2 conditions are met: + // 1) we had already synced this network (during the network join) + // 2) the residual reapTime is higher than 1/6 of the total reapTime. // If the residual reapTime is lower or equal to 1/6 of the total reapTime don't bother broadcasting it around - // most likely the cluster is already aware of it, if not who will sync with this node will catch the state too. - // This also avoids that deletion of entries close to their garbage collection ends up circuling around forever - return e.reapTime > nDB.config.reapEntryInterval/6 + // most likely the cluster is already aware of it + // This also reduce the possibility that deletion of entries close to their garbage collection ends up circuling around + // forever + //logrus.Infof("exiting on delete not knowing the obj with rebroadcast:%t", network.inSync) + return network.inSync && e.reapTime > nDB.config.reapEntryInterval/6 } var op opType @@ -215,7 +229,7 @@ func (nDB *NetworkDB) handleTableEvent(tEvent *TableEvent) bool { } nDB.broadcaster.Write(makeEvent(op, tEvent.TableName, tEvent.NetworkID, tEvent.Key, tEvent.Value)) - return true + return network.inSync } func (nDB *NetworkDB) handleCompound(buf []byte, isBulkSync bool) { @@ -244,7 +258,7 @@ func (nDB *NetworkDB) handleTableMessage(buf []byte, isBulkSync bool) { return } - if rebroadcast := nDB.handleTableEvent(&tEvent); rebroadcast { + if rebroadcast := nDB.handleTableEvent(&tEvent, isBulkSync); rebroadcast { var err error buf, err = encodeRawMessage(MessageTypeTableEvent, buf) if err != nil { @@ -261,12 +275,16 @@ func (nDB *NetworkDB) handleTableMessage(buf []byte, isBulkSync bool) { return } + // if the queue is over the threshold, avoid distributing information coming from TCP sync + if isBulkSync && n.tableBroadcasts.NumQueued() > maxQueueLenBroadcastOnSync { + return + } + n.tableBroadcasts.QueueBroadcast(&tableEventMessage{ msg: buf, id: tEvent.NetworkID, tname: tEvent.TableName, key: tEvent.Key, - node: tEvent.NodeName, }) } } diff --git a/vendor/github.com/docker/libnetwork/networkdb/networkdb.go b/vendor/github.com/docker/libnetwork/networkdb/networkdb.go index c433913a4648c..b79f346eeacb7 100644 --- a/vendor/github.com/docker/libnetwork/networkdb/networkdb.go +++ b/vendor/github.com/docker/libnetwork/networkdb/networkdb.go @@ -5,7 +5,6 @@ package networkdb import ( "context" "fmt" - "net" "os" "strings" "sync" @@ -96,7 +95,7 @@ type NetworkDB struct { // bootStrapIP is the list of IPs that can be used to bootstrap // the gossip. - bootStrapIP []net.IP + bootStrapIP []string // lastStatsTimestamp is the last timestamp when the stats got printed lastStatsTimestamp time.Time @@ -131,6 +130,9 @@ type network struct { // Lamport time for the latest state of the entry. ltime serf.LamportTime + // Gets set to true after the first bulk sync happens + inSync bool + // Node leave is in progress. leaving bool @@ -268,10 +270,8 @@ func New(c *Config) (*NetworkDB, error) { // instances passed by the caller in the form of addr:port func (nDB *NetworkDB) Join(members []string) error { nDB.Lock() - nDB.bootStrapIP = make([]net.IP, 0, len(members)) - for _, m := range members { - nDB.bootStrapIP = append(nDB.bootStrapIP, net.ParseIP(m)) - } + nDB.bootStrapIP = append([]string(nil), members...) + logrus.Infof("The new bootstrap node list is:%v", nDB.bootStrapIP) nDB.Unlock() return nDB.clusterJoin(members) } @@ -619,6 +619,7 @@ func (nDB *NetworkDB) JoinNetwork(nid string) error { } nDB.addNetworkNode(nid, nDB.config.NodeID) networkNodes := nDB.networkNodes[nid] + n = nodeNetworks[nid] nDB.Unlock() if err := nDB.sendNetworkEvent(nid, NetworkEventTypeJoin, ltime); err != nil { @@ -630,6 +631,12 @@ func (nDB *NetworkDB) JoinNetwork(nid string) error { logrus.Errorf("Error bulk syncing while joining network %s: %v", nid, err) } + // Mark the network as being synced + // note this is a best effort, we are not checking the result of the bulk sync + nDB.Lock() + n.inSync = true + nDB.Unlock() + return nil } diff --git a/vendor/github.com/docker/libnetwork/networkdb/networkdb.pb.go b/vendor/github.com/docker/libnetwork/networkdb/networkdb.pb.go index 7087a57ca06d6..043c23b605a9c 100644 --- a/vendor/github.com/docker/libnetwork/networkdb/networkdb.pb.go +++ b/vendor/github.com/docker/libnetwork/networkdb/networkdb.pb.go @@ -1,11 +1,11 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: networkdb.proto +// source: networkdb/networkdb.proto /* Package networkdb is a generated protocol buffer package. It is generated from these files: - networkdb.proto + networkdb/networkdb.proto It has these top-level messages: GossipMessage @@ -476,7 +476,7 @@ func (m *CompoundMessage) GetMessages() []*CompoundMessage_SimpleMessage { type CompoundMessage_SimpleMessage struct { // Bytestring payload of a message constructed using // other message type definitions. - Payload []byte `protobuf:"bytes,1,opt,name=Payload,proto3" json:"Payload,omitempty"` + Payload []byte `protobuf:"bytes,1,opt,name=Payload,json=payload,proto3" json:"Payload,omitempty"` } func (m *CompoundMessage_SimpleMessage) Reset() { *m = CompoundMessage_SimpleMessage{} } @@ -997,24 +997,6 @@ func (m *CompoundMessage_SimpleMessage) MarshalTo(dAtA []byte) (int, error) { return i, nil } -func encodeFixed64Networkdb(dAtA []byte, offset int, v uint64) int { - dAtA[offset] = uint8(v) - dAtA[offset+1] = uint8(v >> 8) - dAtA[offset+2] = uint8(v >> 16) - dAtA[offset+3] = uint8(v >> 24) - dAtA[offset+4] = uint8(v >> 32) - dAtA[offset+5] = uint8(v >> 40) - dAtA[offset+6] = uint8(v >> 48) - dAtA[offset+7] = uint8(v >> 56) - return offset + 8 -} -func encodeFixed32Networkdb(dAtA []byte, offset int, v uint32) int { - dAtA[offset] = uint8(v) - dAtA[offset+1] = uint8(v >> 8) - dAtA[offset+2] = uint8(v >> 16) - dAtA[offset+3] = uint8(v >> 24) - return offset + 4 -} func encodeVarintNetworkdb(dAtA []byte, offset int, v uint64) int { for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) @@ -2666,68 +2648,68 @@ var ( ErrIntOverflowNetworkdb = fmt.Errorf("proto: integer overflow") ) -func init() { proto.RegisterFile("networkdb.proto", fileDescriptorNetworkdb) } +func init() { proto.RegisterFile("networkdb/networkdb.proto", fileDescriptorNetworkdb) } var fileDescriptorNetworkdb = []byte{ - // 953 bytes of a gzipped FileDescriptorProto + // 955 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x96, 0xcd, 0x6e, 0xe3, 0x54, - 0x14, 0xc7, 0x7b, 0xf3, 0xd5, 0xe4, 0x34, 0xa5, 0xe6, 0x4e, 0x67, 0xc6, 0xe3, 0x81, 0xc4, 0x98, - 0x99, 0x2a, 0x53, 0x41, 0x8a, 0x3a, 0x4f, 0xd0, 0x24, 0x16, 0x64, 0x26, 0xe3, 0x44, 0x6e, 0x52, - 0xc4, 0x2a, 0xba, 0xad, 0x2f, 0xa9, 0x55, 0xc7, 0xb6, 0x6c, 0x27, 0x28, 0x2b, 0x10, 0xab, 0x51, - 0x16, 0xbc, 0x41, 0x56, 0xc3, 0x9a, 0x07, 0x40, 0x2c, 0x59, 0xcc, 0x82, 0x05, 0xec, 0x10, 0x8b, - 0x88, 0xe6, 0x09, 0x78, 0x04, 0xe4, 0x6b, 0x3b, 0xb9, 0x49, 0xab, 0x91, 0x10, 0x23, 0xc1, 0x26, - 0xb9, 0x1f, 0xbf, 0x1c, 0x9f, 0xf3, 0xf7, 0xff, 0xdc, 0x1b, 0xd8, 0xb3, 0x69, 0xf0, 0x95, 0xe3, - 0x5d, 0x19, 0xe7, 0x55, 0xd7, 0x73, 0x02, 0x07, 0x17, 0x96, 0x0b, 0xd2, 0xfe, 0xc0, 0x19, 0x38, - 0x6c, 0xf5, 0x28, 0x1c, 0x45, 0x80, 0xd2, 0x86, 0xdd, 0x4f, 0x1d, 0xdf, 0x37, 0xdd, 0x17, 0xd4, - 0xf7, 0xc9, 0x80, 0xe2, 0x43, 0xc8, 0x04, 0x13, 0x97, 0x8a, 0x48, 0x46, 0x95, 0x77, 0x8e, 0xef, - 0x55, 0x57, 0x11, 0x63, 0xa2, 0x3b, 0x71, 0xa9, 0xce, 0x18, 0x8c, 0x21, 0x63, 0x90, 0x80, 0x88, - 0x29, 0x19, 0x55, 0x8a, 0x3a, 0x1b, 0x2b, 0xaf, 0x52, 0x50, 0xd0, 0x1c, 0x83, 0xaa, 0x63, 0x6a, - 0x07, 0xf8, 0xe3, 0xb5, 0x68, 0x0f, 0xb8, 0x68, 0x4b, 0xa6, 0xca, 0x05, 0x6c, 0x42, 0xce, 0xea, - 0x07, 0xe6, 0x90, 0xb2, 0x90, 0x99, 0xda, 0xf1, 0xeb, 0x79, 0x79, 0xeb, 0x8f, 0x79, 0xf9, 0x70, - 0x60, 0x06, 0x97, 0xa3, 0xf3, 0xea, 0x85, 0x33, 0x3c, 0xba, 0x24, 0xfe, 0xa5, 0x79, 0xe1, 0x78, - 0xee, 0x91, 0x4f, 0xbd, 0x2f, 0xd9, 0x47, 0xb5, 0x45, 0x86, 0xae, 0xe3, 0x05, 0x5d, 0x73, 0x48, - 0xf5, 0xac, 0x15, 0x7e, 0xe1, 0x87, 0x50, 0xb0, 0x1d, 0x83, 0xf6, 0x6d, 0x32, 0xa4, 0x62, 0x5a, - 0x46, 0x95, 0x82, 0x9e, 0x0f, 0x17, 0x34, 0x32, 0xa4, 0xca, 0xd7, 0x90, 0x09, 0x9f, 0x8a, 0x1f, - 0xc3, 0x76, 0x53, 0x3b, 0x3b, 0x69, 0x35, 0x1b, 0xc2, 0x96, 0x24, 0x4e, 0x67, 0xf2, 0xfe, 0x32, - 0xad, 0x70, 0xbf, 0x69, 0x8f, 0x89, 0x65, 0x1a, 0xb8, 0x0c, 0x99, 0x67, 0xed, 0xa6, 0x26, 0x20, - 0xe9, 0xee, 0x74, 0x26, 0xbf, 0xbb, 0xc6, 0x3c, 0x73, 0x4c, 0x1b, 0x7f, 0x00, 0xd9, 0x96, 0x7a, - 0x72, 0xa6, 0x0a, 0x29, 0xe9, 0xde, 0x74, 0x26, 0xe3, 0x35, 0xa2, 0x45, 0xc9, 0x98, 0x4a, 0xc5, - 0x97, 0xaf, 0x4a, 0x5b, 0x3f, 0x7e, 0x5f, 0x62, 0x0f, 0x56, 0xae, 0x53, 0x50, 0xd4, 0x22, 0x2d, - 0x22, 0xa1, 0x3e, 0x59, 0x13, 0xea, 0x3d, 0x5e, 0x28, 0x0e, 0xfb, 0x0f, 0xb4, 0xc2, 0x1f, 0x01, - 0xc4, 0xc9, 0xf4, 0x4d, 0x43, 0xcc, 0x84, 0xbb, 0xb5, 0xdd, 0xc5, 0xbc, 0x5c, 0x88, 0x13, 0x6b, - 0x36, 0xf4, 0xc4, 0x65, 0x4d, 0x43, 0x79, 0x89, 0x62, 0x69, 0x2b, 0xbc, 0xb4, 0x0f, 0xa7, 0x33, - 0xf9, 0x3e, 0x5f, 0x08, 0xaf, 0xae, 0xb2, 0x54, 0x37, 0x7a, 0x03, 0x1b, 0x18, 0x13, 0xf8, 0xd1, - 0x4a, 0xe0, 0x07, 0xd3, 0x99, 0x7c, 0x77, 0x13, 0xba, 0x4d, 0xe3, 0x5f, 0xd0, 0x4a, 0x63, 0x3b, - 0xf0, 0x26, 0x1b, 0x95, 0xa0, 0x37, 0x57, 0xf2, 0x36, 0xf5, 0x7d, 0x72, 0x43, 0xdf, 0x5a, 0x71, - 0x31, 0x2f, 0xe7, 0xb5, 0x58, 0x63, 0x4e, 0x6d, 0x11, 0xb6, 0x2d, 0x4a, 0xc6, 0xa6, 0x3d, 0x60, - 0x52, 0xe7, 0xf5, 0x64, 0xaa, 0xfc, 0x84, 0x60, 0x2f, 0x4e, 0xb4, 0x33, 0xf2, 0x2f, 0x3b, 0x23, - 0xcb, 0xe2, 0x72, 0x44, 0xff, 0x36, 0xc7, 0xa7, 0x90, 0x8f, 0x6b, 0xf7, 0xc5, 0x94, 0x9c, 0xae, - 0xec, 0x1c, 0xdf, 0xbf, 0xc5, 0x84, 0xa1, 0x8e, 0xfa, 0x12, 0xfc, 0x07, 0x85, 0x29, 0xdf, 0x65, - 0x00, 0xba, 0xe4, 0xdc, 0x8a, 0x0f, 0x86, 0xea, 0x9a, 0xdf, 0x25, 0xee, 0x51, 0x2b, 0xe8, 0x7f, - 0xef, 0x76, 0xfc, 0x3e, 0x40, 0x10, 0xa6, 0x1b, 0xc5, 0xca, 0xb2, 0x58, 0x05, 0xb6, 0xc2, 0x82, - 0x09, 0x90, 0xbe, 0xa2, 0x13, 0x31, 0xc7, 0xd6, 0xc3, 0x21, 0xde, 0x87, 0xec, 0x98, 0x58, 0x23, - 0x2a, 0x6e, 0xb3, 0x23, 0x33, 0x9a, 0xe0, 0x1a, 0x60, 0x8f, 0xfa, 0xa6, 0x31, 0x22, 0x56, 0xdf, - 0xa3, 0xc4, 0x8d, 0x0a, 0xcd, 0xcb, 0xa8, 0x92, 0xad, 0xed, 0x2f, 0xe6, 0x65, 0x41, 0x8f, 0x77, - 0x75, 0x4a, 0x5c, 0x56, 0x8a, 0xe0, 0x6d, 0xac, 0x28, 0x3f, 0x24, 0x8d, 0x77, 0xc0, 0x37, 0x1e, - 0x6b, 0x96, 0x95, 0xa2, 0x7c, 0xdb, 0x3d, 0x82, 0x5c, 0x5d, 0x57, 0x4f, 0xba, 0x6a, 0xd2, 0x78, - 0xeb, 0x58, 0xdd, 0xa3, 0x24, 0xa0, 0x21, 0xd5, 0xeb, 0x34, 0x42, 0x2a, 0x75, 0x1b, 0xd5, 0x73, - 0x8d, 0x98, 0x6a, 0xa8, 0x2d, 0xb5, 0xab, 0x0a, 0xe9, 0xdb, 0xa8, 0x06, 0xb5, 0x68, 0xb0, 0xd9, - 0x9e, 0xbf, 0x21, 0xd8, 0xab, 0x8d, 0xac, 0xab, 0xd3, 0x89, 0x7d, 0x91, 0x5c, 0x3e, 0x6f, 0xd1, - 0xcf, 0x32, 0xec, 0x8c, 0x6c, 0xdf, 0xb1, 0xcc, 0x0b, 0x33, 0xa0, 0x06, 0x73, 0x4d, 0x5e, 0xe7, - 0x97, 0xde, 0xec, 0x03, 0x89, 0x6b, 0x87, 0x8c, 0x9c, 0x66, 0x7b, 0x89, 0xeb, 0x45, 0xd8, 0x76, - 0xc9, 0xc4, 0x72, 0x88, 0xc1, 0x5e, 0x79, 0x51, 0x4f, 0xa6, 0xca, 0xb7, 0x08, 0xf6, 0xea, 0xce, - 0xd0, 0x75, 0x46, 0xb6, 0x91, 0xd4, 0xd4, 0x80, 0xfc, 0x30, 0x1a, 0xfa, 0x22, 0x62, 0x8d, 0x55, - 0xe1, 0xdc, 0xbe, 0x41, 0x57, 0x4f, 0xcd, 0xa1, 0x6b, 0xd1, 0x78, 0xa6, 0x2f, 0x7f, 0x29, 0x3d, - 0x81, 0xdd, 0xb5, 0xad, 0x30, 0x89, 0x4e, 0x9c, 0x04, 0x8a, 0x92, 0x88, 0xa7, 0x87, 0x3f, 0xa7, - 0x60, 0x87, 0xbb, 0xab, 0xf1, 0x87, 0xbc, 0x21, 0xd8, 0xf5, 0xc4, 0xed, 0x26, 0x6e, 0xa8, 0xc2, - 0xae, 0xa6, 0x76, 0x3f, 0x6f, 0xeb, 0xcf, 0xfb, 0xea, 0x99, 0xaa, 0x75, 0x05, 0x14, 0x1d, 0xda, - 0x1c, 0xba, 0x76, 0x5f, 0x1d, 0xc2, 0x4e, 0xf7, 0xa4, 0xd6, 0x52, 0x63, 0x3a, 0x3e, 0x96, 0x39, - 0x9a, 0xeb, 0xf5, 0x03, 0x28, 0x74, 0x7a, 0xa7, 0x9f, 0xf5, 0x3b, 0xbd, 0x56, 0x4b, 0x48, 0x4b, - 0xf7, 0xa7, 0x33, 0xf9, 0x0e, 0x47, 0x2e, 0x4f, 0xb3, 0x03, 0x28, 0xd4, 0x7a, 0xad, 0xe7, 0xfd, - 0xd3, 0x2f, 0xb4, 0xba, 0x90, 0xb9, 0xc1, 0x25, 0x66, 0xc1, 0x8f, 0x21, 0x5f, 0x6f, 0xbf, 0xe8, - 0xb4, 0x7b, 0x5a, 0x43, 0xc8, 0xde, 0xc0, 0x12, 0x45, 0x71, 0x05, 0x40, 0x6b, 0x37, 0x92, 0x0c, - 0x73, 0x91, 0x31, 0xf9, 0x7a, 0x92, 0x4b, 0x5a, 0xba, 0x13, 0x1b, 0x93, 0x97, 0xad, 0x26, 0xfe, - 0x7e, 0x5d, 0xda, 0xfa, 0xeb, 0xba, 0x84, 0xbe, 0x59, 0x94, 0xd0, 0xeb, 0x45, 0x09, 0xfd, 0xba, - 0x28, 0xa1, 0x3f, 0x17, 0x25, 0x74, 0x9e, 0x63, 0x7f, 0x9d, 0x9e, 0xfe, 0x1d, 0x00, 0x00, 0xff, - 0xff, 0x92, 0x82, 0xdb, 0x1a, 0x6e, 0x09, 0x00, 0x00, + 0x14, 0xc7, 0x7b, 0xf3, 0xd1, 0x26, 0xa7, 0x29, 0x35, 0x77, 0x3a, 0x53, 0xd7, 0x03, 0x89, 0x31, + 0x33, 0x55, 0xa6, 0x82, 0x14, 0x75, 0x9e, 0xa0, 0x49, 0x2c, 0xc8, 0x4c, 0xc6, 0x89, 0xdc, 0xa4, + 0x88, 0x55, 0x74, 0x5b, 0x5f, 0x52, 0xab, 0x8e, 0x6d, 0xd9, 0x4e, 0x50, 0x56, 0x20, 0x56, 0xa3, + 0x2c, 0x78, 0x83, 0xac, 0x86, 0x35, 0x0f, 0x80, 0x58, 0xb2, 0x98, 0x05, 0x0b, 0xd8, 0x21, 0x16, + 0x11, 0xcd, 0x13, 0xf0, 0x08, 0xc8, 0xd7, 0x76, 0x72, 0x93, 0x56, 0x23, 0x21, 0x46, 0x82, 0x4d, + 0x72, 0x3f, 0x7e, 0x39, 0x3e, 0xe7, 0xef, 0xff, 0xb9, 0x37, 0x70, 0x60, 0xd3, 0xe0, 0x2b, 0xc7, + 0xbb, 0x36, 0x2e, 0x8e, 0x17, 0xa3, 0x8a, 0xeb, 0x39, 0x81, 0x83, 0xf3, 0x8b, 0x05, 0x69, 0xaf, + 0xef, 0xf4, 0x1d, 0xb6, 0x7a, 0x1c, 0x8e, 0x22, 0x40, 0x69, 0xc1, 0xce, 0xa7, 0x8e, 0xef, 0x9b, + 0xee, 0x0b, 0xea, 0xfb, 0xa4, 0x4f, 0xf1, 0x11, 0x64, 0x82, 0xb1, 0x4b, 0x45, 0x24, 0xa3, 0xf2, + 0x3b, 0x27, 0x0f, 0x2a, 0xcb, 0x88, 0x31, 0xd1, 0x19, 0xbb, 0x54, 0x67, 0x0c, 0xc6, 0x90, 0x31, + 0x48, 0x40, 0xc4, 0x94, 0x8c, 0xca, 0x05, 0x9d, 0x8d, 0x95, 0x57, 0x29, 0xc8, 0x6b, 0x8e, 0x41, + 0xd5, 0x11, 0xb5, 0x03, 0xfc, 0xf1, 0x4a, 0xb4, 0x03, 0x2e, 0xda, 0x82, 0xa9, 0x70, 0x01, 0x1b, + 0xb0, 0x69, 0xf5, 0x02, 0x73, 0x40, 0x59, 0xc8, 0x4c, 0xf5, 0xe4, 0xf5, 0xac, 0xb4, 0xf1, 0xc7, + 0xac, 0x74, 0xd4, 0x37, 0x83, 0xab, 0xe1, 0x45, 0xe5, 0xd2, 0x19, 0x1c, 0x5f, 0x11, 0xff, 0xca, + 0xbc, 0x74, 0x3c, 0xf7, 0xd8, 0xa7, 0xde, 0x97, 0xec, 0xa3, 0xd2, 0x24, 0x03, 0xd7, 0xf1, 0x82, + 0x8e, 0x39, 0xa0, 0x7a, 0xd6, 0x0a, 0xbf, 0xf0, 0x43, 0xc8, 0xdb, 0x8e, 0x41, 0x7b, 0x36, 0x19, + 0x50, 0x31, 0x2d, 0xa3, 0x72, 0x5e, 0xcf, 0x85, 0x0b, 0x1a, 0x19, 0x50, 0xe5, 0x6b, 0xc8, 0x84, + 0x4f, 0xc5, 0x8f, 0x61, 0xab, 0xa1, 0x9d, 0x9f, 0x36, 0x1b, 0x75, 0x61, 0x43, 0x12, 0x27, 0x53, + 0x79, 0x6f, 0x91, 0x56, 0xb8, 0xdf, 0xb0, 0x47, 0xc4, 0x32, 0x0d, 0x5c, 0x82, 0xcc, 0xb3, 0x56, + 0x43, 0x13, 0x90, 0x74, 0x7f, 0x32, 0x95, 0xdf, 0x5d, 0x61, 0x9e, 0x39, 0xa6, 0x8d, 0x3f, 0x80, + 0x6c, 0x53, 0x3d, 0x3d, 0x57, 0x85, 0x94, 0xf4, 0x60, 0x32, 0x95, 0xf1, 0x0a, 0xd1, 0xa4, 0x64, + 0x44, 0xa5, 0xc2, 0xcb, 0x57, 0xc5, 0x8d, 0x1f, 0xbf, 0x2f, 0xb2, 0x07, 0x2b, 0x37, 0x29, 0x28, + 0x68, 0x91, 0x16, 0x91, 0x50, 0x9f, 0xac, 0x08, 0xf5, 0x1e, 0x2f, 0x14, 0x87, 0xfd, 0x07, 0x5a, + 0xe1, 0x8f, 0x00, 0xe2, 0x64, 0x7a, 0xa6, 0x21, 0x66, 0xc2, 0xdd, 0xea, 0xce, 0x7c, 0x56, 0xca, + 0xc7, 0x89, 0x35, 0xea, 0x7a, 0xe2, 0xb2, 0x86, 0xa1, 0xbc, 0x44, 0xb1, 0xb4, 0x65, 0x5e, 0xda, + 0x87, 0x93, 0xa9, 0xbc, 0xcf, 0x17, 0xc2, 0xab, 0xab, 0x2c, 0xd4, 0x8d, 0xde, 0xc0, 0x1a, 0xc6, + 0x04, 0x7e, 0xb4, 0x14, 0xf8, 0x60, 0x32, 0x95, 0xef, 0xaf, 0x43, 0x77, 0x69, 0xfc, 0x0b, 0x5a, + 0x6a, 0x6c, 0x07, 0xde, 0x78, 0xad, 0x12, 0xf4, 0xe6, 0x4a, 0xde, 0xa6, 0xbe, 0x4f, 0x6e, 0xe9, + 0x5b, 0x2d, 0xcc, 0x67, 0xa5, 0x9c, 0x16, 0x6b, 0xcc, 0xa9, 0x2d, 0xc2, 0x96, 0x45, 0xc9, 0xc8, + 0xb4, 0xfb, 0x4c, 0xea, 0x9c, 0x9e, 0x4c, 0x95, 0x9f, 0x10, 0xec, 0xc6, 0x89, 0xb6, 0x87, 0xfe, + 0x55, 0x7b, 0x68, 0x59, 0x5c, 0x8e, 0xe8, 0xdf, 0xe6, 0xf8, 0x14, 0x72, 0x71, 0xed, 0xbe, 0x98, + 0x92, 0xd3, 0xe5, 0xed, 0x93, 0xfd, 0x3b, 0x4c, 0x18, 0xea, 0xa8, 0x2f, 0xc0, 0x7f, 0x50, 0x98, + 0xf2, 0x5d, 0x06, 0xa0, 0x43, 0x2e, 0xac, 0xf8, 0x60, 0xa8, 0xac, 0xf8, 0x5d, 0xe2, 0x1e, 0xb5, + 0x84, 0xfe, 0xf7, 0x6e, 0xc7, 0xef, 0x03, 0x04, 0x61, 0xba, 0x51, 0xac, 0x2c, 0x8b, 0x95, 0x67, + 0x2b, 0x2c, 0x98, 0x00, 0xe9, 0x6b, 0x3a, 0x16, 0x37, 0xd9, 0x7a, 0x38, 0xc4, 0x7b, 0x90, 0x1d, + 0x11, 0x6b, 0x48, 0xc5, 0x2d, 0x76, 0x64, 0x46, 0x13, 0x5c, 0x05, 0xec, 0x51, 0xdf, 0x34, 0x86, + 0xc4, 0xea, 0x79, 0x94, 0xb8, 0x51, 0xa1, 0x39, 0x19, 0x95, 0xb3, 0xd5, 0xbd, 0xf9, 0xac, 0x24, + 0xe8, 0xf1, 0xae, 0x4e, 0x89, 0xcb, 0x4a, 0x11, 0xbc, 0xb5, 0x15, 0xe5, 0x87, 0xa4, 0xf1, 0x0e, + 0xf9, 0xc6, 0x63, 0xcd, 0xb2, 0x54, 0x94, 0x6f, 0xbb, 0x47, 0xb0, 0x59, 0xd3, 0xd5, 0xd3, 0x8e, + 0x9a, 0x34, 0xde, 0x2a, 0x56, 0xf3, 0x28, 0x09, 0x68, 0x48, 0x75, 0xdb, 0xf5, 0x90, 0x4a, 0xdd, + 0x45, 0x75, 0x5d, 0x23, 0xa6, 0xea, 0x6a, 0x53, 0xed, 0xa8, 0x42, 0xfa, 0x2e, 0xaa, 0x4e, 0x2d, + 0x1a, 0xac, 0xb7, 0xe7, 0x6f, 0x08, 0x76, 0xab, 0x43, 0xeb, 0xfa, 0x6c, 0x6c, 0x5f, 0x26, 0x97, + 0xcf, 0x5b, 0xf4, 0xb3, 0x0c, 0xdb, 0x43, 0xdb, 0x77, 0x2c, 0xf3, 0xd2, 0x0c, 0xa8, 0xc1, 0x5c, + 0x93, 0xd3, 0xf9, 0xa5, 0x37, 0xfb, 0x40, 0xe2, 0xda, 0x21, 0x23, 0xa7, 0xd9, 0x5e, 0xe2, 0x7a, + 0x11, 0xb6, 0x5c, 0x32, 0xb6, 0x1c, 0x62, 0xb0, 0x57, 0x5e, 0xd0, 0x93, 0xa9, 0xf2, 0x2d, 0x82, + 0xdd, 0x9a, 0x33, 0x70, 0x9d, 0xa1, 0x6d, 0x24, 0x35, 0xd5, 0x21, 0x37, 0x88, 0x86, 0xbe, 0x88, + 0x58, 0x63, 0x95, 0x39, 0xb7, 0xaf, 0xd1, 0x95, 0x33, 0x73, 0xe0, 0x5a, 0x34, 0x9e, 0xe9, 0x8b, + 0x5f, 0x4a, 0x4f, 0x60, 0x67, 0x65, 0x2b, 0x4c, 0xa2, 0x1d, 0x27, 0x81, 0x56, 0x92, 0x38, 0xfa, + 0x39, 0x05, 0xdb, 0xdc, 0x5d, 0x8d, 0x3f, 0xe4, 0x0d, 0xc1, 0xae, 0x27, 0x6e, 0x37, 0x71, 0x43, + 0x05, 0x76, 0x34, 0xb5, 0xf3, 0x79, 0x4b, 0x7f, 0xde, 0x53, 0xcf, 0x55, 0xad, 0x23, 0xa0, 0xe8, + 0xd0, 0xe6, 0xd0, 0x95, 0xfb, 0xea, 0x08, 0xb6, 0x3b, 0xa7, 0xd5, 0xa6, 0x1a, 0xd3, 0xf1, 0xb1, + 0xcc, 0xd1, 0x5c, 0xaf, 0x1f, 0x42, 0xbe, 0xdd, 0x3d, 0xfb, 0xac, 0xd7, 0xee, 0x36, 0x9b, 0x42, + 0x5a, 0xda, 0x9f, 0x4c, 0xe5, 0x7b, 0x1c, 0xb9, 0x38, 0xcd, 0x0e, 0x21, 0x5f, 0xed, 0x36, 0x9f, + 0xf7, 0xce, 0xbe, 0xd0, 0x6a, 0x42, 0xe6, 0x16, 0x97, 0x98, 0x05, 0x3f, 0x86, 0x5c, 0xad, 0xf5, + 0xa2, 0xdd, 0xea, 0x6a, 0x75, 0x21, 0x7b, 0x0b, 0x4b, 0x14, 0xc5, 0x65, 0x00, 0xad, 0x55, 0x4f, + 0x32, 0xdc, 0x8c, 0x8c, 0xc9, 0xd7, 0x93, 0x5c, 0xd2, 0xd2, 0xbd, 0xd8, 0x98, 0xbc, 0x6c, 0x55, + 0xf1, 0xf7, 0x9b, 0xe2, 0xc6, 0x5f, 0x37, 0x45, 0xf4, 0xcd, 0xbc, 0x88, 0x5e, 0xcf, 0x8b, 0xe8, + 0xd7, 0x79, 0x11, 0xfd, 0x39, 0x2f, 0xa2, 0x8b, 0x4d, 0xf6, 0xd7, 0xe9, 0xe9, 0xdf, 0x01, 0x00, + 0x00, 0xff, 0xff, 0x02, 0x9d, 0x53, 0x72, 0x78, 0x09, 0x00, 0x00, } diff --git a/vendor/github.com/docker/libnetwork/networkdb/networkdbdiagnostic.go b/vendor/github.com/docker/libnetwork/networkdb/networkdbdiagnostic.go index ffeb98d607a5e..a0e95987992e5 100644 --- a/vendor/github.com/docker/libnetwork/networkdb/networkdbdiagnostic.go +++ b/vendor/github.com/docker/libnetwork/networkdb/networkdbdiagnostic.go @@ -28,6 +28,7 @@ var NetDbPaths2Func = map[string]diagnostic.HTTPHandlerFunc{ "/deleteentry": dbDeleteEntry, "/getentry": dbGetEntry, "/gettable": dbGetTable, + "/networkstats": dbNetworkStats, } func dbJoin(ctx interface{}, w http.ResponseWriter, r *http.Request) { @@ -411,3 +412,41 @@ func dbGetTable(ctx interface{}, w http.ResponseWriter, r *http.Request) { } diagnostic.HTTPReply(w, diagnostic.FailCommand(fmt.Errorf("%s", dbNotAvailable)), json) } + +func dbNetworkStats(ctx interface{}, w http.ResponseWriter, r *http.Request) { + r.ParseForm() + diagnostic.DebugHTTPForm(r) + _, json := diagnostic.ParseHTTPFormOptions(r) + + // audit logs + log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) + log.Info("network stats") + + if len(r.Form["nid"]) < 1 { + rsp := diagnostic.WrongCommand(missingParameter, fmt.Sprintf("%s?nid=test", r.URL.Path)) + log.Error("network stats failed, wrong input") + diagnostic.HTTPReply(w, rsp, json) + return + } + + nDB, ok := ctx.(*NetworkDB) + if ok { + nDB.RLock() + networks := nDB.networks[nDB.config.NodeID] + network, ok := networks[r.Form["nid"][0]] + + entries := -1 + qLen := -1 + if ok { + entries = network.entriesNumber + qLen = network.tableBroadcasts.NumQueued() + } + nDB.RUnlock() + + rsp := diagnostic.CommandSucceed(&diagnostic.NetworkStatsResult{Entries: entries, QueueLen: qLen}) + log.WithField("response", fmt.Sprintf("%+v", rsp)).Info("network stats done") + diagnostic.HTTPReply(w, rsp, json) + return + } + diagnostic.HTTPReply(w, diagnostic.FailCommand(fmt.Errorf("%s", dbNotAvailable)), json) +} diff --git a/vendor/github.com/docker/libnetwork/osl/kernel/knobs.go b/vendor/github.com/docker/libnetwork/osl/kernel/knobs.go new file mode 100644 index 0000000000000..5088f0e7f950a --- /dev/null +++ b/vendor/github.com/docker/libnetwork/osl/kernel/knobs.go @@ -0,0 +1,16 @@ +package kernel + +type conditionalCheck func(val1, val2 string) bool + +// OSValue represents a tuple, value defired, check function when to apply the value +type OSValue struct { + Value string + CheckFn conditionalCheck +} + +func propertyIsValid(val1, val2 string, check conditionalCheck) bool { + if check == nil || check(val1, val2) { + return true + } + return false +} diff --git a/vendor/github.com/docker/libnetwork/osl/kernel/knobs_linux.go b/vendor/github.com/docker/libnetwork/osl/kernel/knobs_linux.go new file mode 100644 index 0000000000000..964280650c580 --- /dev/null +++ b/vendor/github.com/docker/libnetwork/osl/kernel/knobs_linux.go @@ -0,0 +1,47 @@ +package kernel + +import ( + "io/ioutil" + "path" + "strings" + + "github.com/sirupsen/logrus" +) + +// writeSystemProperty writes the value to a path under /proc/sys as determined from the key. +// For e.g. net.ipv4.ip_forward translated to /proc/sys/net/ipv4/ip_forward. +func writeSystemProperty(key, value string) error { + keyPath := strings.Replace(key, ".", "/", -1) + return ioutil.WriteFile(path.Join("/proc/sys", keyPath), []byte(value), 0644) +} + +// readSystemProperty reads the value from the path under /proc/sys and returns it +func readSystemProperty(key string) (string, error) { + keyPath := strings.Replace(key, ".", "/", -1) + value, err := ioutil.ReadFile(path.Join("/proc/sys", keyPath)) + if err != nil { + return "", err + } + return strings.TrimSpace(string(value)), nil +} + +// ApplyOSTweaks applies the configuration values passed as arguments +func ApplyOSTweaks(osConfig map[string]*OSValue) { + for k, v := range osConfig { + // read the existing property from disk + oldv, err := readSystemProperty(k) + if err != nil { + logrus.WithError(err).Errorf("error reading the kernel parameter %s", k) + continue + } + + if propertyIsValid(oldv, v.Value, v.CheckFn) { + // write new prop value to disk + if err := writeSystemProperty(k, v.Value); err != nil { + logrus.WithError(err).Errorf("error setting the kernel parameter %s = %s, (leaving as %s)", k, v.Value, oldv) + continue + } + logrus.Debugf("updated kernel parameter %s = %s (was %s)", k, v.Value, oldv) + } + } +} diff --git a/vendor/github.com/docker/libnetwork/osl/kernel/knobs_unsupported.go b/vendor/github.com/docker/libnetwork/osl/kernel/knobs_unsupported.go new file mode 100644 index 0000000000000..32f258f41625d --- /dev/null +++ b/vendor/github.com/docker/libnetwork/osl/kernel/knobs_unsupported.go @@ -0,0 +1,7 @@ +// +build !linux + +package kernel + +// ApplyOSTweaks applies the configuration values passed as arguments +func ApplyOSTweaks(osConfig map[string]*OSValue) { +} diff --git a/vendor/github.com/docker/libnetwork/osl/namespace_linux.go b/vendor/github.com/docker/libnetwork/osl/namespace_linux.go index 6389215e1a737..45c46852fc57d 100644 --- a/vendor/github.com/docker/libnetwork/osl/namespace_linux.go +++ b/vendor/github.com/docker/libnetwork/osl/namespace_linux.go @@ -16,6 +16,7 @@ import ( "github.com/docker/docker/pkg/reexec" "github.com/docker/libnetwork/ns" + "github.com/docker/libnetwork/osl/kernel" "github.com/docker/libnetwork/types" "github.com/sirupsen/logrus" "github.com/vishvananda/netlink" @@ -29,13 +30,18 @@ func init() { } var ( - once sync.Once - garbagePathMap = make(map[string]bool) - gpmLock sync.Mutex - gpmWg sync.WaitGroup - gpmCleanupPeriod = 60 * time.Second - gpmChan = make(chan chan struct{}) - prefix = defaultPrefix + once sync.Once + garbagePathMap = make(map[string]bool) + gpmLock sync.Mutex + gpmWg sync.WaitGroup + gpmCleanupPeriod = 60 * time.Second + gpmChan = make(chan chan struct{}) + prefix = defaultPrefix + loadBalancerConfig = map[string]*kernel.OSValue{ + // expires connection from the IPVS connection table when the backend is not available + // more info: https://github.com/torvalds/linux/blob/master/Documentation/networking/ipvs-sysctl.txt#L126:1 + "net.ipv4.vs.expire_nodest_conn": {"1", nil}, + } ) // The networkNamespace type is the linux implementation of the Sandbox @@ -358,16 +364,20 @@ func (n *networkNamespace) loopbackUp() error { return n.nlHandle.LinkSetUp(iface) } -func (n *networkNamespace) AddLoopbackAliasIP(ip *net.IPNet) error { - iface, err := n.nlHandle.LinkByName("lo") +func (n *networkNamespace) GetLoopbackIfaceName() string { + return "lo" +} + +func (n *networkNamespace) AddAliasIP(ifName string, ip *net.IPNet) error { + iface, err := n.nlHandle.LinkByName(ifName) if err != nil { return err } return n.nlHandle.AddrAdd(iface, &netlink.Addr{IPNet: ip}) } -func (n *networkNamespace) RemoveLoopbackAliasIP(ip *net.IPNet) error { - iface, err := n.nlHandle.LinkByName("lo") +func (n *networkNamespace) RemoveAliasIP(ifName string, ip *net.IPNet) error { + iface, err := n.nlHandle.LinkByName(ifName) if err != nil { return err } @@ -606,6 +616,15 @@ func reexecSetIPv6() { value = byte('0') } + if _, err := os.Stat(path); err != nil { + if os.IsNotExist(err) { + logrus.Warnf("file does not exist: %s : %v Has IPv6 been disabled in this node's kernel?", path, err) + os.Exit(0) + } + logrus.Errorf("failed to stat %s : %v", path, err) + os.Exit(5) + } + if err = ioutil.WriteFile(path, []byte{value, '\n'}, 0644); err != nil { logrus.Errorf("failed to %s IPv6 forwarding for container's interface %s: %v", action, os.Args[2], err) os.Exit(4) @@ -626,3 +645,13 @@ func setIPv6(path, iface string, enable bool) error { } return nil } + +// ApplyOSTweaks applies linux configs on the sandbox +func (n *networkNamespace) ApplyOSTweaks(types []SandboxType) { + for _, t := range types { + switch t { + case SandboxTypeLoadBalancer: + kernel.ApplyOSTweaks(loadBalancerConfig) + } + } +} diff --git a/vendor/github.com/docker/libnetwork/osl/sandbox.go b/vendor/github.com/docker/libnetwork/osl/sandbox.go index 6ffc46775cca1..5019e068df9d2 100644 --- a/vendor/github.com/docker/libnetwork/osl/sandbox.go +++ b/vendor/github.com/docker/libnetwork/osl/sandbox.go @@ -7,6 +7,16 @@ import ( "github.com/docker/libnetwork/types" ) +// SandboxType specify the time of the sandbox, this can be used to apply special configs +type SandboxType int + +const ( + // SandboxTypeIngress indicates that the sandbox is for the ingress + SandboxTypeIngress = iota + // SandboxTypeLoadBalancer indicates that the sandbox is a load balancer + SandboxTypeLoadBalancer = iota +) + // Sandbox represents a network sandbox, identified by a specific key. It // holds a list of Interfaces, routes etc, and more can be added dynamically. type Sandbox interface { @@ -32,11 +42,14 @@ type Sandbox interface { // Unset the previously set default IPv6 gateway in the sandbox UnsetGatewayIPv6() error - // AddLoopbackAliasIP adds the passed IP address to the sandbox loopback interface - AddLoopbackAliasIP(ip *net.IPNet) error + // GetLoopbackIfaceName returns the name of the loopback interface + GetLoopbackIfaceName() string - // RemoveLoopbackAliasIP removes the passed IP address from the sandbox loopback interface - RemoveLoopbackAliasIP(ip *net.IPNet) error + // AddAliasIP adds the passed IP address to the named interface + AddAliasIP(ifName string, ip *net.IPNet) error + + // RemoveAliasIP removes the passed IP address from the named interface + RemoveAliasIP(ifName string, ip *net.IPNet) error // Add a static route to the sandbox. AddStaticRoute(*types.StaticRoute) error @@ -67,6 +80,9 @@ type Sandbox interface { // restore sandbox Restore(ifsopt map[string][]IfaceOption, routes []*types.StaticRoute, gw net.IP, gw6 net.IP) error + + // ApplyOSTweaks applies operating system specific knobs on the sandbox + ApplyOSTweaks([]SandboxType) } // NeighborOptionSetter interface defines the option setter methods for interface options diff --git a/vendor/github.com/docker/libnetwork/sandbox.go b/vendor/github.com/docker/libnetwork/sandbox.go index daa0f5e581181..0f9a53a36715e 100644 --- a/vendor/github.com/docker/libnetwork/sandbox.go +++ b/vendor/github.com/docker/libnetwork/sandbox.go @@ -83,6 +83,7 @@ type sandbox struct { inDelete bool ingress bool ndotsSet bool + oslTypes []osl.SandboxType // slice of properties of this sandbox sync.Mutex // This mutex is used to serialize service related operation for an endpoint // The lock is here because the endpoint is saved into the store so is not unique @@ -740,15 +741,8 @@ func releaseOSSboxResources(osSbox osl.Sandbox, ep *endpoint) { ep.Lock() joinInfo := ep.joinInfo - vip := ep.virtualIP ep.Unlock() - if len(vip) != 0 { - if err := osSbox.RemoveLoopbackAliasIP(&net.IPNet{IP: vip, Mask: net.CIDRMask(32, 32)}); err != nil { - logrus.Warnf("Remove virtual IP %v failed: %v", vip, err) - } - } - if joinInfo == nil { return } @@ -861,13 +855,6 @@ func (sb *sandbox) populateNetworkResources(ep *endpoint) error { } } - if len(ep.virtualIP) != 0 { - err := sb.osSbox.AddLoopbackAliasIP(&net.IPNet{IP: ep.virtualIP, Mask: net.CIDRMask(32, 32)}) - if err != nil { - return fmt.Errorf("failed to add virtual IP %v: %v", ep.virtualIP, err) - } - } - if joinInfo != nil { // Set up non-interface routes. for _, r := range joinInfo.StaticRoutes { @@ -893,7 +880,7 @@ func (sb *sandbox) populateNetworkResources(ep *endpoint) error { // information including gateway and other routes so that // loadbalancers are populated all the network state is in // place in the sandbox. - sb.populateLoadbalancers(ep) + sb.populateLoadBalancers(ep) // Only update the store if we did not come here as part of // sandbox delete. If we came here as part of delete then do @@ -1176,6 +1163,15 @@ func OptionPortMapping(portBindings []types.PortBinding) SandboxOption { func OptionIngress() SandboxOption { return func(sb *sandbox) { sb.ingress = true + sb.oslTypes = append(sb.oslTypes, osl.SandboxTypeIngress) + } +} + +// OptionLoadBalancer function returns an option setter for marking a +// sandbox as a load balancer sandbox. +func OptionLoadBalancer() SandboxOption { + return func(sb *sandbox) { + sb.oslTypes = append(sb.oslTypes, osl.SandboxTypeLoadBalancer) } } diff --git a/vendor/github.com/docker/libnetwork/sandbox_dns_unix.go b/vendor/github.com/docker/libnetwork/sandbox_dns_unix.go index c4da9272e502b..3641f60f7c092 100644 --- a/vendor/github.com/docker/libnetwork/sandbox_dns_unix.go +++ b/vendor/github.com/docker/libnetwork/sandbox_dns_unix.go @@ -369,11 +369,13 @@ dnsOpt: return fmt.Errorf("invalid ndots option %v", option) } if num, err := strconv.Atoi(parts[1]); err != nil { - return fmt.Errorf("invalid number for ndots option %v", option) - } else if num > 0 { + return fmt.Errorf("invalid number for ndots option: %v", parts[1]) + } else if num >= 0 { // if the user sets ndots, use the user setting sb.ndotsSet = true break dnsOpt + } else { + return fmt.Errorf("invalid number for ndots option: %v", num) } } } diff --git a/vendor/github.com/docker/libnetwork/sandbox_store.go b/vendor/github.com/docker/libnetwork/sandbox_store.go index ebc884e6f0a7c..1e53815aeea77 100644 --- a/vendor/github.com/docker/libnetwork/sandbox_store.go +++ b/vendor/github.com/docker/libnetwork/sandbox_store.go @@ -244,7 +244,7 @@ func (c *controller) sandboxCleanup(activeSandboxes map[string]interface{}) { } sb.osSbox, err = osl.NewSandbox(sb.Key(), create, isRestore) if err != nil { - logrus.Errorf("failed to create osl sandbox while trying to restore sandbox %s%s: %v", sb.ID()[0:7], msg, err) + logrus.Errorf("failed to create osl sandbox while trying to restore sandbox %.7s%s: %v", sb.ID(), msg, err) continue } diff --git a/vendor/github.com/docker/libnetwork/service_common.go b/vendor/github.com/docker/libnetwork/service_common.go index 8be2c3852851c..4c30e465e2baf 100644 --- a/vendor/github.com/docker/libnetwork/service_common.go +++ b/vendor/github.com/docker/libnetwork/service_common.go @@ -225,6 +225,13 @@ func makeServiceCleanupFunc(c *controller, s *service, nID, eID string, vip net. func (c *controller) addServiceBinding(svcName, svcID, nID, eID, containerName string, vip net.IP, ingressPorts []*PortConfig, serviceAliases, taskAliases []string, ip net.IP, method string) error { var addService bool + // Failure to lock the network ID on add can result in racing + // racing against network deletion resulting in inconsistent + // state in the c.serviceBindings map and it's sub-maps. Also, + // always lock network ID before services to avoid deadlock. + c.networkLocker.Lock(nID) + defer c.networkLocker.Unlock(nID) + n, err := c.NetworkByID(nID) if err != nil { return err @@ -289,11 +296,8 @@ func (c *controller) addServiceBinding(svcName, svcID, nID, eID, containerName s logrus.Warnf("addServiceBinding %s possible transient state ok:%t entries:%d set:%t %s", eID, ok, entries, b, setStr) } - // Add loadbalancer service and backend in all sandboxes in - // the network only if vip is valid. - if len(vip) != 0 { - n.(*network).addLBBackend(ip, vip, lb, ingressPorts) - } + // Add loadbalancer service and backend to the network + n.(*network).addLBBackend(ip, lb) // Add the appropriate name resolutions c.addEndpointNameResolution(svcName, svcID, nID, eID, containerName, vip, serviceAliases, taskAliases, ip, addService, "addServiceBinding") @@ -307,11 +311,6 @@ func (c *controller) rmServiceBinding(svcName, svcID, nID, eID, containerName st var rmService bool - n, err := c.NetworkByID(nID) - if err != nil { - return err - } - skey := serviceKey{ id: svcID, ports: portConfigs(ingressPorts).String(), @@ -368,8 +367,15 @@ func (c *controller) rmServiceBinding(svcName, svcID, nID, eID, containerName st // Remove loadbalancer service(if needed) and backend in all // sandboxes in the network only if the vip is valid. - if len(vip) != 0 && entries == 0 { - n.(*network).rmLBBackend(ip, vip, lb, ingressPorts, rmService, fullRemove) + if entries == 0 { + // The network may well have been deleted before the last + // of the service bindings. That's ok, because removing + // the network sandbox implicitly removes the backend + // service bindings. + n, err := c.NetworkByID(nID) + if err == nil { + n.(*network).rmLBBackend(ip, lb, rmService, fullRemove) + } } // Delete the name resolutions diff --git a/vendor/github.com/docker/libnetwork/service_linux.go b/vendor/github.com/docker/libnetwork/service_linux.go index ef0590df78e2c..3ffa60719cafd 100644 --- a/vendor/github.com/docker/libnetwork/service_linux.go +++ b/vendor/github.com/docker/libnetwork/service_linux.go @@ -30,40 +30,9 @@ func init() { reexec.Register("redirecter", redirecter) } -// Get all loadbalancers on this network that is currently discovered -// on this node. -func (n *network) connectedLoadbalancers() []*loadBalancer { - c := n.getController() - - c.Lock() - serviceBindings := make([]*service, 0, len(c.serviceBindings)) - for _, s := range c.serviceBindings { - serviceBindings = append(serviceBindings, s) - } - c.Unlock() - - var lbs []*loadBalancer - for _, s := range serviceBindings { - s.Lock() - // Skip the serviceBindings that got deleted - if s.deleted { - s.Unlock() - continue - } - if lb, ok := s.loadBalancers[n.ID()]; ok { - lbs = append(lbs, lb) - } - s.Unlock() - } - - return lbs -} - // Populate all loadbalancers on the network that the passed endpoint // belongs to, into this sandbox. -func (sb *sandbox) populateLoadbalancers(ep *endpoint) { - var gwIP net.IP - +func (sb *sandbox) populateLoadBalancers(ep *endpoint) { // This is an interface less endpoint. Nothing to do. if ep.Iface() == nil { return @@ -74,130 +43,113 @@ func (sb *sandbox) populateLoadbalancers(ep *endpoint) { if n.ingress { if err := addRedirectRules(sb.Key(), eIP, ep.ingressPorts); err != nil { - logrus.Errorf("Failed to add redirect rules for ep %s (%s): %v", ep.Name(), ep.ID()[0:7], err) + logrus.Errorf("Failed to add redirect rules for ep %s (%.7s): %v", ep.Name(), ep.ID(), err) } } +} - if sb.ingress { - // For the ingress sandbox if this is not gateway - // endpoint do nothing. - if ep != sb.getGatewayEndpoint() { - return - } - - // This is the gateway endpoint. Now get the ingress - // network and plumb the loadbalancers. - gwIP = ep.Iface().Address().IP - for _, ep := range sb.getConnectedEndpoints() { - if !ep.endpointInGWNetwork() { - n = ep.getNetwork() - eIP = ep.Iface().Address() - } +func (n *network) findLBEndpointSandbox() (*endpoint, *sandbox, error) { + // TODO: get endpoint from store? See EndpointInfo() + var ep *endpoint + // Find this node's LB sandbox endpoint: there should be exactly one + for _, e := range n.Endpoints() { + epi := e.Info() + if epi != nil && epi.LoadBalancer() { + ep = e.(*endpoint) + break } } - - for _, lb := range n.connectedLoadbalancers() { - // Skip if vip is not valid. - if len(lb.vip) == 0 { - continue - } - - lb.service.Lock() - for _, be := range lb.backEnds { - if !be.disabled { - sb.addLBBackend(be.ip, lb.vip, lb.fwMark, lb.service.ingressPorts, eIP, gwIP, n.ingress) - } - } - lb.service.Unlock() + if ep == nil { + return nil, nil, fmt.Errorf("Unable to find load balancing endpoint for network %s", n.ID()) } + // Get the load balancer sandbox itself as well + sb, ok := ep.getSandbox() + if !ok { + return nil, nil, fmt.Errorf("Unable to get sandbox for %s(%s) in for %s", ep.Name(), ep.ID(), n.ID()) + } + ep = sb.getEndpoint(ep.ID()) + if ep == nil { + return nil, nil, fmt.Errorf("Load balancing endpoint %s(%s) removed from %s", ep.Name(), ep.ID(), n.ID()) + } + return ep, sb, nil } -// Add loadbalancer backend to all sandboxes which has a connection to -// this network. If needed add the service as well. -func (n *network) addLBBackend(ip, vip net.IP, lb *loadBalancer, ingressPorts []*PortConfig) { - n.WalkEndpoints(func(e Endpoint) bool { - ep := e.(*endpoint) - if sb, ok := ep.getSandbox(); ok { - if !sb.isEndpointPopulated(ep) { - return false - } - - var gwIP net.IP - if ep := sb.getGatewayEndpoint(); ep != nil { - gwIP = ep.Iface().Address().IP - } - - sb.addLBBackend(ip, vip, lb.fwMark, ingressPorts, ep.Iface().Address(), gwIP, n.ingress) - } - - return false - }) -} - -// Remove loadbalancer backend from all sandboxes which has a -// connection to this network. If needed remove the service entry as -// well, as specified by the rmService bool. -func (n *network) rmLBBackend(ip, vip net.IP, lb *loadBalancer, ingressPorts []*PortConfig, rmService bool, fullRemove bool) { - n.WalkEndpoints(func(e Endpoint) bool { - ep := e.(*endpoint) - if sb, ok := ep.getSandbox(); ok { - if !sb.isEndpointPopulated(ep) { - return false - } - - var gwIP net.IP - if ep := sb.getGatewayEndpoint(); ep != nil { - gwIP = ep.Iface().Address().IP - } - - sb.rmLBBackend(ip, vip, lb.fwMark, ingressPorts, ep.Iface().Address(), gwIP, rmService, fullRemove, n.ingress) +// Searches the OS sandbox for the name of the endpoint interface +// within the sandbox. This is required for adding/removing IP +// aliases to the interface. +func findIfaceDstName(sb *sandbox, ep *endpoint) string { + srcName := ep.Iface().SrcName() + for _, i := range sb.osSbox.Info().Interfaces() { + if i.SrcName() == srcName { + return i.DstName() } - - return false - }) + } + return "" } -// Add loadbalancer backend into one connected sandbox. -func (sb *sandbox) addLBBackend(ip, vip net.IP, fwMark uint32, ingressPorts []*PortConfig, eIP *net.IPNet, gwIP net.IP, isIngressNetwork bool) { - if sb.osSbox == nil { +// Add loadbalancer backend to the loadbalncer sandbox for the network. +// If needed add the service as well. +func (n *network) addLBBackend(ip net.IP, lb *loadBalancer) { + if len(lb.vip) == 0 { return } - - if isIngressNetwork && !sb.ingress { + ep, sb, err := n.findLBEndpointSandbox() + if err != nil { + logrus.Errorf("addLBBackend %s/%s: %v", n.ID(), n.Name(), err) + return + } + if sb.osSbox == nil { return } + eIP := ep.Iface().Address() + i, err := ipvs.New(sb.Key()) if err != nil { - logrus.Errorf("Failed to create an ipvs handle for sbox %s (%s,%s) for lb addition: %v", sb.ID()[0:7], sb.ContainerID()[0:7], sb.Key(), err) + logrus.Errorf("Failed to create an ipvs handle for sbox %.7s (%.7s,%s) for lb addition: %v", sb.ID(), sb.ContainerID(), sb.Key(), err) return } defer i.Close() s := &ipvs.Service{ AddressFamily: nl.FAMILY_V4, - FWMark: fwMark, + FWMark: lb.fwMark, SchedName: ipvs.RoundRobin, } if !i.IsServicePresent(s) { - var filteredPorts []*PortConfig + // Add IP alias for the VIP to the endpoint + ifName := findIfaceDstName(sb, ep) + if ifName == "" { + logrus.Errorf("Failed find interface name for endpoint %s(%s) to create LB alias", ep.ID(), ep.Name()) + return + } + err := sb.osSbox.AddAliasIP(ifName, &net.IPNet{IP: lb.vip, Mask: net.CIDRMask(32, 32)}) + if err != nil { + logrus.Errorf("Failed add IP alias %s to network %s LB endpoint interface %s: %v", lb.vip, n.ID(), ifName, err) + return + } + if sb.ingress { - filteredPorts = filterPortConfigs(ingressPorts, false) + var gwIP net.IP + if ep := sb.getGatewayEndpoint(); ep != nil { + gwIP = ep.Iface().Address().IP + } + filteredPorts := filterPortConfigs(lb.service.ingressPorts, false) if err := programIngress(gwIP, filteredPorts, false); err != nil { logrus.Errorf("Failed to add ingress: %v", err) return } } - logrus.Debugf("Creating service for vip %s fwMark %d ingressPorts %#v in sbox %s (%s)", vip, fwMark, ingressPorts, sb.ID()[0:7], sb.ContainerID()[0:7]) - if err := invokeFWMarker(sb.Key(), vip, fwMark, ingressPorts, eIP, false); err != nil { - logrus.Errorf("Failed to add firewall mark rule in sbox %s (%s): %v", sb.ID()[0:7], sb.ContainerID()[0:7], err) + logrus.Debugf("Creating service for vip %s fwMark %d ingressPorts %#v in sbox %.7s (%.7s)", lb.vip, lb.fwMark, lb.service.ingressPorts, sb.ID(), sb.ContainerID()) + if err := invokeFWMarker(sb.Key(), lb.vip, lb.fwMark, lb.service.ingressPorts, eIP, false); err != nil { + logrus.Errorf("Failed to add firewall mark rule in sbox %.7s (%.7s): %v", sb.ID(), sb.ContainerID(), err) return } if err := i.NewService(s); err != nil && err != syscall.EEXIST { - logrus.Errorf("Failed to create a new service for vip %s fwmark %d in sbox %s (%s): %v", vip, fwMark, sb.ID()[0:7], sb.ContainerID()[0:7], err) + logrus.Errorf("Failed to create a new service for vip %s fwmark %d in sbox %.7s (%.7s): %v", lb.vip, lb.fwMark, sb.ID(), sb.ContainerID(), err) return } } @@ -212,30 +164,39 @@ func (sb *sandbox) addLBBackend(ip, vip net.IP, fwMark uint32, ingressPorts []*P // destination. s.SchedName = "" if err := i.NewDestination(s, d); err != nil && err != syscall.EEXIST { - logrus.Errorf("Failed to create real server %s for vip %s fwmark %d in sbox %s (%s): %v", ip, vip, fwMark, sb.ID()[0:7], sb.ContainerID()[0:7], err) + logrus.Errorf("Failed to create real server %s for vip %s fwmark %d in sbox %.7s (%.7s): %v", ip, lb.vip, lb.fwMark, sb.ID(), sb.ContainerID(), err) } } -// Remove loadbalancer backend from one connected sandbox. -func (sb *sandbox) rmLBBackend(ip, vip net.IP, fwMark uint32, ingressPorts []*PortConfig, eIP *net.IPNet, gwIP net.IP, rmService bool, fullRemove bool, isIngressNetwork bool) { - if sb.osSbox == nil { +// Remove loadbalancer backend the load balancing endpoint for this +// network. If 'rmService' is true, then remove the service entry as well. +// If 'fullRemove' is true then completely remove the entry, otherwise +// just deweight it for now. +func (n *network) rmLBBackend(ip net.IP, lb *loadBalancer, rmService bool, fullRemove bool) { + if len(lb.vip) == 0 { return } - - if isIngressNetwork && !sb.ingress { + ep, sb, err := n.findLBEndpointSandbox() + if err != nil { + logrus.Debugf("rmLBBackend for %s/%s: %v -- probably transient state", n.ID(), n.Name(), err) return } + if sb.osSbox == nil { + return + } + + eIP := ep.Iface().Address() i, err := ipvs.New(sb.Key()) if err != nil { - logrus.Errorf("Failed to create an ipvs handle for sbox %s (%s,%s) for lb removal: %v", sb.ID()[0:7], sb.ContainerID()[0:7], sb.Key(), err) + logrus.Errorf("Failed to create an ipvs handle for sbox %.7s (%.7s,%s) for lb removal: %v", sb.ID(), sb.ContainerID(), sb.Key(), err) return } defer i.Close() s := &ipvs.Service{ AddressFamily: nl.FAMILY_V4, - FWMark: fwMark, + FWMark: lb.fwMark, } d := &ipvs.Destination{ @@ -246,31 +207,45 @@ func (sb *sandbox) rmLBBackend(ip, vip net.IP, fwMark uint32, ingressPorts []*Po if fullRemove { if err := i.DelDestination(s, d); err != nil && err != syscall.ENOENT { - logrus.Errorf("Failed to delete real server %s for vip %s fwmark %d in sbox %s (%s): %v", ip, vip, fwMark, sb.ID()[0:7], sb.ContainerID()[0:7], err) + logrus.Errorf("Failed to delete real server %s for vip %s fwmark %d in sbox %.7s (%.7s): %v", ip, lb.vip, lb.fwMark, sb.ID(), sb.ContainerID(), err) } } else { d.Weight = 0 if err := i.UpdateDestination(s, d); err != nil && err != syscall.ENOENT { - logrus.Errorf("Failed to set LB weight of real server %s to 0 for vip %s fwmark %d in sbox %s (%s): %v", ip, vip, fwMark, sb.ID()[0:7], sb.ContainerID()[0:7], err) + logrus.Errorf("Failed to set LB weight of real server %s to 0 for vip %s fwmark %d in sbox %.7s (%.7s): %v", ip, lb.vip, lb.fwMark, sb.ID(), sb.ContainerID(), err) } } if rmService { s.SchedName = ipvs.RoundRobin if err := i.DelService(s); err != nil && err != syscall.ENOENT { - logrus.Errorf("Failed to delete service for vip %s fwmark %d in sbox %s (%s): %v", vip, fwMark, sb.ID()[0:7], sb.ContainerID()[0:7], err) + logrus.Errorf("Failed to delete service for vip %s fwmark %d in sbox %.7s (%.7s): %v", lb.vip, lb.fwMark, sb.ID(), sb.ContainerID(), err) } - var filteredPorts []*PortConfig if sb.ingress { - filteredPorts = filterPortConfigs(ingressPorts, true) + var gwIP net.IP + if ep := sb.getGatewayEndpoint(); ep != nil { + gwIP = ep.Iface().Address().IP + } + filteredPorts := filterPortConfigs(lb.service.ingressPorts, true) if err := programIngress(gwIP, filteredPorts, true); err != nil { logrus.Errorf("Failed to delete ingress: %v", err) } } - if err := invokeFWMarker(sb.Key(), vip, fwMark, ingressPorts, eIP, true); err != nil { - logrus.Errorf("Failed to delete firewall mark rule in sbox %s (%s): %v", sb.ID()[0:7], sb.ContainerID()[0:7], err) + if err := invokeFWMarker(sb.Key(), lb.vip, lb.fwMark, lb.service.ingressPorts, eIP, true); err != nil { + logrus.Errorf("Failed to delete firewall mark rule in sbox %.7s (%.7s): %v", sb.ID(), sb.ContainerID(), err) + } + + // Remove IP alias from the VIP to the endpoint + ifName := findIfaceDstName(sb, ep) + if ifName == "" { + logrus.Errorf("Failed find interface name for endpoint %s(%s) to create LB alias", ep.ID(), ep.Name()) + return + } + err := sb.osSbox.RemoveAliasIP(ifName, &net.IPNet{IP: lb.vip, Mask: net.CIDRMask(32, 32)}) + if err != nil { + logrus.Errorf("Failed add IP alias %s to network %s LB endpoint interface %s: %v", lb.vip, n.ID(), ifName, err) } } } @@ -279,7 +254,7 @@ const ingressChain = "DOCKER-INGRESS" var ( ingressOnce sync.Once - ingressProxyMu sync.Mutex + ingressMu sync.Mutex // lock for operations on ingress ingressProxyTbl = make(map[string]io.Closer) portConfigMu sync.Mutex portConfigTbl = make(map[PortConfig]int) @@ -328,6 +303,9 @@ func programIngress(gwIP net.IP, ingressPorts []*PortConfig, isDelete bool) erro addDelOpt = "-D" } + ingressMu.Lock() + defer ingressMu.Unlock() + chainExists := iptables.ExistChain(ingressChain, iptables.Nat) filterChainExists := iptables.ExistChain(ingressChain, iptables.Filter) @@ -497,13 +475,11 @@ func plumbProxy(iPort *PortConfig, isDelete bool) error { portSpec := fmt.Sprintf("%d/%s", iPort.PublishedPort, strings.ToLower(PortConfig_Protocol_name[int32(iPort.Protocol)])) if isDelete { - ingressProxyMu.Lock() if listener, ok := ingressProxyTbl[portSpec]; ok { if listener != nil { listener.Close() } } - ingressProxyMu.Unlock() return nil } @@ -523,9 +499,7 @@ func plumbProxy(iPort *PortConfig, isDelete bool) error { return err } - ingressProxyMu.Lock() ingressProxyTbl[portSpec] = l - ingressProxyMu.Unlock() return nil } @@ -618,7 +592,7 @@ func fwMarker() { ingressPorts, err = readPortsFromFile(os.Args[5]) if err != nil { logrus.Errorf("Failed reading ingress ports file: %v", err) - os.Exit(6) + os.Exit(2) } } @@ -626,7 +600,7 @@ func fwMarker() { fwMark, err := strconv.ParseUint(os.Args[3], 10, 32) if err != nil { logrus.Errorf("bad fwmark value(%s) passed: %v", os.Args[3], err) - os.Exit(2) + os.Exit(3) } addDelOpt := os.Args[4] @@ -640,20 +614,20 @@ func fwMarker() { ns, err := netns.GetFromPath(os.Args[1]) if err != nil { logrus.Errorf("failed get network namespace %q: %v", os.Args[1], err) - os.Exit(3) + os.Exit(4) } defer ns.Close() if err := netns.Set(ns); err != nil { logrus.Errorf("setting into container net ns %v failed, %v", os.Args[1], err) - os.Exit(4) + os.Exit(5) } if addDelOpt == "-A" { eIP, subnet, err := net.ParseCIDR(os.Args[6]) if err != nil { logrus.Errorf("Failed to parse endpoint IP %s: %v", os.Args[6], err) - os.Exit(9) + os.Exit(6) } ruleParams := strings.Fields(fmt.Sprintf("-m ipvs --ipvs -d %s -j SNAT --to-source %s", subnet, eIP)) @@ -664,21 +638,18 @@ func fwMarker() { err := ioutil.WriteFile("/proc/sys/net/ipv4/vs/conntrack", []byte{'1', '\n'}, 0644) if err != nil { logrus.Errorf("Failed to write to /proc/sys/net/ipv4/vs/conntrack: %v", err) - os.Exit(8) + os.Exit(7) } } } - rule := strings.Fields(fmt.Sprintf("-t mangle %s OUTPUT -d %s/32 -j MARK --set-mark %d", addDelOpt, vip, fwMark)) - rules = append(rules, rule) - - rule = strings.Fields(fmt.Sprintf("-t nat %s OUTPUT -p icmp --icmp echo-request -d %s -j DNAT --to 127.0.0.1", addDelOpt, vip)) + rule := strings.Fields(fmt.Sprintf("-t mangle %s INPUT -d %s/32 -j MARK --set-mark %d", addDelOpt, vip, fwMark)) rules = append(rules, rule) for _, rule := range rules { if err := iptables.RawCombinedOutputNative(rule...); err != nil { logrus.Errorf("setting up rule failed, %v: %v", rule, err) - os.Exit(5) + os.Exit(8) } } } diff --git a/vendor/github.com/docker/libnetwork/service_unsupported.go b/vendor/github.com/docker/libnetwork/service_unsupported.go index 37b9828191808..ee9750600c05b 100644 --- a/vendor/github.com/docker/libnetwork/service_unsupported.go +++ b/vendor/github.com/docker/libnetwork/service_unsupported.go @@ -18,7 +18,7 @@ func (c *controller) rmServiceBinding(name, sid, nid, eid string, vip net.IP, in return fmt.Errorf("not supported") } -func (sb *sandbox) populateLoadbalancers(ep *endpoint) { +func (sb *sandbox) populateLoadBalancers(ep *endpoint) { } func arrangeIngressFilterRule() { diff --git a/vendor/github.com/docker/libnetwork/service_windows.go b/vendor/github.com/docker/libnetwork/service_windows.go index a9ce244cfd855..944f3d30cae92 100644 --- a/vendor/github.com/docker/libnetwork/service_windows.go +++ b/vendor/github.com/docker/libnetwork/service_windows.go @@ -19,7 +19,13 @@ func init() { lbPolicylistMap = make(map[*loadBalancer]*policyLists) } -func (n *network) addLBBackend(ip, vip net.IP, lb *loadBalancer, ingressPorts []*PortConfig) { +func (n *network) addLBBackend(ip net.IP, lb *loadBalancer) { + if len(lb.vip) == 0 { + return + } + + vip := lb.vip + ingressPorts := lb.service.ingressPorts if system.GetOSVersion().Build > 16236 { lb.Lock() @@ -117,11 +123,15 @@ func (n *network) addLBBackend(ip, vip net.IP, lb *loadBalancer, ingressPorts [] } } -func (n *network) rmLBBackend(ip, vip net.IP, lb *loadBalancer, ingressPorts []*PortConfig, rmService bool, fullRemove bool) { +func (n *network) rmLBBackend(ip net.IP, lb *loadBalancer, rmService bool, fullRemove bool) { + if len(lb.vip) == 0 { + return + } + if system.GetOSVersion().Build > 16236 { if numEnabledBackends(lb) > 0 { //Reprogram HNS (actually VFP) with the existing backends. - n.addLBBackend(ip, vip, lb, ingressPorts) + n.addLBBackend(ip, lb) } else { lb.Lock() defer lb.Unlock() @@ -156,7 +166,7 @@ func numEnabledBackends(lb *loadBalancer) int { return nEnabled } -func (sb *sandbox) populateLoadbalancers(ep *endpoint) { +func (sb *sandbox) populateLoadBalancers(ep *endpoint) { } func arrangeIngressFilterRule() { diff --git a/vendor/github.com/docker/libnetwork/store.go b/vendor/github.com/docker/libnetwork/store.go index 95943f6f45c01..0a7c5754d31de 100644 --- a/vendor/github.com/docker/libnetwork/store.go +++ b/vendor/github.com/docker/libnetwork/store.go @@ -479,7 +479,7 @@ func (c *controller) networkCleanup() { for _, n := range networks { if n.inDelete { logrus.Infof("Removing stale network %s (%s)", n.Name(), n.ID()) - if err := n.delete(true); err != nil { + if err := n.delete(true, true); err != nil { logrus.Debugf("Error while removing stale network: %v", err) } } diff --git a/vendor/github.com/docker/libnetwork/types/types.go b/vendor/github.com/docker/libnetwork/types/types.go index f851d6fbb7c45..b102ba4c390be 100644 --- a/vendor/github.com/docker/libnetwork/types/types.go +++ b/vendor/github.com/docker/libnetwork/types/types.go @@ -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) + hoststr, portstr, err := net.SplitHostPort(s) + if err != nil { + return nil, 0, err } - var ip net.IP - if pp[0] != "" { - if ip = net.ParseIP(pp[0]); ip == nil { - return nil, 0, BadRequestErrorf("invalid ip: %s", pp[0]) - } + ip := net.ParseIP(hoststr) + if ip == nil { + return nil, 0, BadRequestErrorf("invalid ip: %s", hoststr) } - port, err := strconv.ParseUint(pp[1], 10, 16) + port, err := strconv.ParseUint(portstr, 10, 16) if err != nil { - return nil, 0, BadRequestErrorf("invalid port: %s", pp[1]) + return nil, 0, BadRequestErrorf("invalid port: %s", portstr) } return ip, uint16(port), nil @@ -329,6 +332,8 @@ func CompareIPNet(a, b *net.IPNet) bool { } // 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() diff --git a/vendor/github.com/docker/libnetwork/vendor.conf b/vendor/github.com/docker/libnetwork/vendor.conf index 9480699b87cd0..0a99a438f6159 100644 --- a/vendor/github.com/docker/libnetwork/vendor.conf +++ b/vendor/github.com/docker/libnetwork/vendor.conf @@ -1,27 +1,24 @@ 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/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/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/gorilla/context v1.1 github.com/gorilla/mux v1.1 github.com/hashicorp/consul v0.5.2 @@ -29,27 +26,26 @@ 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/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/miekg/dns v1.0.7 +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/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 + +gotest.tools v2.1.0 +github.com/google/go-cmp v0.2.0 diff --git a/vendor/github.com/docker/swarmkit/api/objects.pb.go b/vendor/github.com/docker/swarmkit/api/objects.pb.go index e7c95c51756b6..5ae93011c8fca 100644 --- a/vendor/github.com/docker/swarmkit/api/objects.pb.go +++ b/vendor/github.com/docker/swarmkit/api/objects.pb.go @@ -2014,6 +2014,10 @@ func sozObjects(x uint64) (n int) { type NodeCheckFunc func(t1, t2 *Node) bool +type EventNode interface { + IsEventNode() bool +} + type EventCreateNode struct { Node *Node Checks []NodeCheckFunc @@ -2033,6 +2037,14 @@ func (e EventCreateNode) Matches(apiEvent go_events.Event) bool { return true } +func (e EventCreateNode) IsEventCreate() bool { + return true +} + +func (e EventCreateNode) IsEventNode() bool { + return true +} + type EventUpdateNode struct { Node *Node OldNode *Node @@ -2053,6 +2065,14 @@ func (e EventUpdateNode) Matches(apiEvent go_events.Event) bool { return true } +func (e EventUpdateNode) IsEventUpdate() bool { + return true +} + +func (e EventUpdateNode) IsEventNode() bool { + return true +} + type EventDeleteNode struct { Node *Node Checks []NodeCheckFunc @@ -2071,6 +2091,15 @@ func (e EventDeleteNode) Matches(apiEvent go_events.Event) bool { } return true } + +func (e EventDeleteNode) IsEventDelete() bool { + return true +} + +func (e EventDeleteNode) IsEventNode() bool { + return true +} + func (m *Node) CopyStoreObject() StoreObject { return m.Copy() } @@ -2261,6 +2290,10 @@ func (indexer NodeCustomIndexer) FromObject(obj interface{}) (bool, [][]byte, er type ServiceCheckFunc func(t1, t2 *Service) bool +type EventService interface { + IsEventService() bool +} + type EventCreateService struct { Service *Service Checks []ServiceCheckFunc @@ -2280,6 +2313,14 @@ func (e EventCreateService) Matches(apiEvent go_events.Event) bool { return true } +func (e EventCreateService) IsEventCreate() bool { + return true +} + +func (e EventCreateService) IsEventService() bool { + return true +} + type EventUpdateService struct { Service *Service OldService *Service @@ -2300,6 +2341,14 @@ func (e EventUpdateService) Matches(apiEvent go_events.Event) bool { return true } +func (e EventUpdateService) IsEventUpdate() bool { + return true +} + +func (e EventUpdateService) IsEventService() bool { + return true +} + type EventDeleteService struct { Service *Service Checks []ServiceCheckFunc @@ -2318,6 +2367,15 @@ func (e EventDeleteService) Matches(apiEvent go_events.Event) bool { } return true } + +func (e EventDeleteService) IsEventDelete() bool { + return true +} + +func (e EventDeleteService) IsEventService() bool { + return true +} + func (m *Service) CopyStoreObject() StoreObject { return m.Copy() } @@ -2478,6 +2536,10 @@ func (indexer ServiceCustomIndexer) FromObject(obj interface{}) (bool, [][]byte, type TaskCheckFunc func(t1, t2 *Task) bool +type EventTask interface { + IsEventTask() bool +} + type EventCreateTask struct { Task *Task Checks []TaskCheckFunc @@ -2497,6 +2559,14 @@ func (e EventCreateTask) Matches(apiEvent go_events.Event) bool { return true } +func (e EventCreateTask) IsEventCreate() bool { + return true +} + +func (e EventCreateTask) IsEventTask() bool { + return true +} + type EventUpdateTask struct { Task *Task OldTask *Task @@ -2517,6 +2587,14 @@ func (e EventUpdateTask) Matches(apiEvent go_events.Event) bool { return true } +func (e EventUpdateTask) IsEventUpdate() bool { + return true +} + +func (e EventUpdateTask) IsEventTask() bool { + return true +} + type EventDeleteTask struct { Task *Task Checks []TaskCheckFunc @@ -2535,6 +2613,15 @@ func (e EventDeleteTask) Matches(apiEvent go_events.Event) bool { } return true } + +func (e EventDeleteTask) IsEventDelete() bool { + return true +} + +func (e EventDeleteTask) IsEventTask() bool { + return true +} + func (m *Task) CopyStoreObject() StoreObject { return m.Copy() } @@ -2738,6 +2825,10 @@ func (indexer TaskCustomIndexer) FromObject(obj interface{}) (bool, [][]byte, er type NetworkCheckFunc func(t1, t2 *Network) bool +type EventNetwork interface { + IsEventNetwork() bool +} + type EventCreateNetwork struct { Network *Network Checks []NetworkCheckFunc @@ -2757,6 +2848,14 @@ func (e EventCreateNetwork) Matches(apiEvent go_events.Event) bool { return true } +func (e EventCreateNetwork) IsEventCreate() bool { + return true +} + +func (e EventCreateNetwork) IsEventNetwork() bool { + return true +} + type EventUpdateNetwork struct { Network *Network OldNetwork *Network @@ -2777,6 +2876,14 @@ func (e EventUpdateNetwork) Matches(apiEvent go_events.Event) bool { return true } +func (e EventUpdateNetwork) IsEventUpdate() bool { + return true +} + +func (e EventUpdateNetwork) IsEventNetwork() bool { + return true +} + type EventDeleteNetwork struct { Network *Network Checks []NetworkCheckFunc @@ -2795,6 +2902,15 @@ func (e EventDeleteNetwork) Matches(apiEvent go_events.Event) bool { } return true } + +func (e EventDeleteNetwork) IsEventDelete() bool { + return true +} + +func (e EventDeleteNetwork) IsEventNetwork() bool { + return true +} + func (m *Network) CopyStoreObject() StoreObject { return m.Copy() } @@ -2955,6 +3071,10 @@ func (indexer NetworkCustomIndexer) FromObject(obj interface{}) (bool, [][]byte, type ClusterCheckFunc func(t1, t2 *Cluster) bool +type EventCluster interface { + IsEventCluster() bool +} + type EventCreateCluster struct { Cluster *Cluster Checks []ClusterCheckFunc @@ -2974,6 +3094,14 @@ func (e EventCreateCluster) Matches(apiEvent go_events.Event) bool { return true } +func (e EventCreateCluster) IsEventCreate() bool { + return true +} + +func (e EventCreateCluster) IsEventCluster() bool { + return true +} + type EventUpdateCluster struct { Cluster *Cluster OldCluster *Cluster @@ -2994,6 +3122,14 @@ func (e EventUpdateCluster) Matches(apiEvent go_events.Event) bool { return true } +func (e EventUpdateCluster) IsEventUpdate() bool { + return true +} + +func (e EventUpdateCluster) IsEventCluster() bool { + return true +} + type EventDeleteCluster struct { Cluster *Cluster Checks []ClusterCheckFunc @@ -3012,6 +3148,15 @@ func (e EventDeleteCluster) Matches(apiEvent go_events.Event) bool { } return true } + +func (e EventDeleteCluster) IsEventDelete() bool { + return true +} + +func (e EventDeleteCluster) IsEventCluster() bool { + return true +} + func (m *Cluster) CopyStoreObject() StoreObject { return m.Copy() } @@ -3172,6 +3317,10 @@ func (indexer ClusterCustomIndexer) FromObject(obj interface{}) (bool, [][]byte, type SecretCheckFunc func(t1, t2 *Secret) bool +type EventSecret interface { + IsEventSecret() bool +} + type EventCreateSecret struct { Secret *Secret Checks []SecretCheckFunc @@ -3191,6 +3340,14 @@ func (e EventCreateSecret) Matches(apiEvent go_events.Event) bool { return true } +func (e EventCreateSecret) IsEventCreate() bool { + return true +} + +func (e EventCreateSecret) IsEventSecret() bool { + return true +} + type EventUpdateSecret struct { Secret *Secret OldSecret *Secret @@ -3211,6 +3368,14 @@ func (e EventUpdateSecret) Matches(apiEvent go_events.Event) bool { return true } +func (e EventUpdateSecret) IsEventUpdate() bool { + return true +} + +func (e EventUpdateSecret) IsEventSecret() bool { + return true +} + type EventDeleteSecret struct { Secret *Secret Checks []SecretCheckFunc @@ -3229,6 +3394,15 @@ func (e EventDeleteSecret) Matches(apiEvent go_events.Event) bool { } return true } + +func (e EventDeleteSecret) IsEventDelete() bool { + return true +} + +func (e EventDeleteSecret) IsEventSecret() bool { + return true +} + func (m *Secret) CopyStoreObject() StoreObject { return m.Copy() } @@ -3389,6 +3563,10 @@ func (indexer SecretCustomIndexer) FromObject(obj interface{}) (bool, [][]byte, type ConfigCheckFunc func(t1, t2 *Config) bool +type EventConfig interface { + IsEventConfig() bool +} + type EventCreateConfig struct { Config *Config Checks []ConfigCheckFunc @@ -3408,6 +3586,14 @@ func (e EventCreateConfig) Matches(apiEvent go_events.Event) bool { return true } +func (e EventCreateConfig) IsEventCreate() bool { + return true +} + +func (e EventCreateConfig) IsEventConfig() bool { + return true +} + type EventUpdateConfig struct { Config *Config OldConfig *Config @@ -3428,6 +3614,14 @@ func (e EventUpdateConfig) Matches(apiEvent go_events.Event) bool { return true } +func (e EventUpdateConfig) IsEventUpdate() bool { + return true +} + +func (e EventUpdateConfig) IsEventConfig() bool { + return true +} + type EventDeleteConfig struct { Config *Config Checks []ConfigCheckFunc @@ -3446,6 +3640,15 @@ func (e EventDeleteConfig) Matches(apiEvent go_events.Event) bool { } return true } + +func (e EventDeleteConfig) IsEventDelete() bool { + return true +} + +func (e EventDeleteConfig) IsEventConfig() bool { + return true +} + func (m *Config) CopyStoreObject() StoreObject { return m.Copy() } @@ -3606,6 +3809,10 @@ func (indexer ConfigCustomIndexer) FromObject(obj interface{}) (bool, [][]byte, type ResourceCheckFunc func(t1, t2 *Resource) bool +type EventResource interface { + IsEventResource() bool +} + type EventCreateResource struct { Resource *Resource Checks []ResourceCheckFunc @@ -3625,6 +3832,14 @@ func (e EventCreateResource) Matches(apiEvent go_events.Event) bool { return true } +func (e EventCreateResource) IsEventCreate() bool { + return true +} + +func (e EventCreateResource) IsEventResource() bool { + return true +} + type EventUpdateResource struct { Resource *Resource OldResource *Resource @@ -3645,6 +3860,14 @@ func (e EventUpdateResource) Matches(apiEvent go_events.Event) bool { return true } +func (e EventUpdateResource) IsEventUpdate() bool { + return true +} + +func (e EventUpdateResource) IsEventResource() bool { + return true +} + type EventDeleteResource struct { Resource *Resource Checks []ResourceCheckFunc @@ -3663,6 +3886,15 @@ func (e EventDeleteResource) Matches(apiEvent go_events.Event) bool { } return true } + +func (e EventDeleteResource) IsEventDelete() bool { + return true +} + +func (e EventDeleteResource) IsEventResource() bool { + return true +} + func (m *Resource) CopyStoreObject() StoreObject { return m.Copy() } @@ -3829,6 +4061,10 @@ func (indexer ResourceCustomIndexer) FromObject(obj interface{}) (bool, [][]byte type ExtensionCheckFunc func(t1, t2 *Extension) bool +type EventExtension interface { + IsEventExtension() bool +} + type EventCreateExtension struct { Extension *Extension Checks []ExtensionCheckFunc @@ -3848,6 +4084,14 @@ func (e EventCreateExtension) Matches(apiEvent go_events.Event) bool { return true } +func (e EventCreateExtension) IsEventCreate() bool { + return true +} + +func (e EventCreateExtension) IsEventExtension() bool { + return true +} + type EventUpdateExtension struct { Extension *Extension OldExtension *Extension @@ -3868,6 +4112,14 @@ func (e EventUpdateExtension) Matches(apiEvent go_events.Event) bool { return true } +func (e EventUpdateExtension) IsEventUpdate() bool { + return true +} + +func (e EventUpdateExtension) IsEventExtension() bool { + return true +} + type EventDeleteExtension struct { Extension *Extension Checks []ExtensionCheckFunc @@ -3886,6 +4138,15 @@ func (e EventDeleteExtension) Matches(apiEvent go_events.Event) bool { } return true } + +func (e EventDeleteExtension) IsEventDelete() bool { + return true +} + +func (e EventDeleteExtension) IsEventExtension() bool { + return true +} + func (m *Extension) CopyStoreObject() StoreObject { return m.Copy() } diff --git a/vendor/github.com/docker/swarmkit/api/storeobject.go b/vendor/github.com/docker/swarmkit/api/storeobject.go index 48b50b72dd9d3..d140fa3e0ca21 100644 --- a/vendor/github.com/docker/swarmkit/api/storeobject.go +++ b/vendor/github.com/docker/swarmkit/api/storeobject.go @@ -38,6 +38,21 @@ type Event interface { Matches(events.Event) bool } +// EventCreate is an interface implemented by every creation event type +type EventCreate interface { + IsEventCreate() bool +} + +// EventUpdate is an interface impelemented by every update event type +type EventUpdate interface { + IsEventUpdate() bool +} + +// EventDelete is an interface implemented by every delete event type +type EventDelete interface { + IsEventDelete() +} + func customIndexer(kind string, annotations *Annotations) (bool, [][]byte, error) { var converted [][]byte diff --git a/vendor/github.com/docker/swarmkit/manager/dispatcher/dispatcher.go b/vendor/github.com/docker/swarmkit/manager/dispatcher/dispatcher.go index 6f40fe3e238cc..d783fd4c35e01 100644 --- a/vendor/github.com/docker/swarmkit/manager/dispatcher/dispatcher.go +++ b/vendor/github.com/docker/swarmkit/manager/dispatcher/dispatcher.go @@ -293,9 +293,16 @@ func (d *Dispatcher) Run(ctx context.Context) error { publishManagers(ev.([]*api.Peer)) case <-d.processUpdatesTrigger: d.processUpdates(ctx) + batchTimer.Stop() + // drain the timer, if it has already expired + select { + case <-batchTimer.C: + default: + } batchTimer.Reset(maxBatchInterval) case <-batchTimer.C: d.processUpdates(ctx) + // batch timer has already expired, so no need to drain batchTimer.Reset(maxBatchInterval) case v := <-configWatcher: cluster := v.(api.EventUpdateCluster) @@ -416,7 +423,7 @@ func (d *Dispatcher) markNodesUnknown(ctx context.Context) error { expireFunc := func() { log := log.WithField("node", nodeID) - log.Info(`heartbeat expiration for node %s in state "unknown"`, nodeID) + log.Infof(`heartbeat expiration for node %s in state "unknown"`, nodeID) if err := d.markNodeNotReady(nodeID, api.NodeStatus_DOWN, `heartbeat failure for node in "unknown" state`); err != nil { log.WithError(err).Error(`failed deregistering node after heartbeat expiration for node in "unknown" state`) } @@ -537,7 +544,7 @@ func (d *Dispatcher) register(ctx context.Context, nodeID string, description *a } expireFunc := func() { - log.G(ctx).Debug("heartbeat expiration for worker %s, setting worker status to NodeStatus_DOWN ", nodeID) + log.G(ctx).Debugf("heartbeat expiration for worker %s, setting worker status to NodeStatus_DOWN ", nodeID) if err := d.markNodeNotReady(nodeID, api.NodeStatus_DOWN, "heartbeat failure"); err != nil { log.G(ctx).WithError(err).Errorf("failed deregistering node after heartbeat expiration") } @@ -1145,11 +1152,8 @@ func (d *Dispatcher) Heartbeat(ctx context.Context, r *api.HeartbeatRequest) (*a d.rpcRW.RLock() defer d.rpcRW.RUnlock() - // Its OK to call isRunning() here instead of isRunningLocked() - // because of the rpcRW readlock above. - // TODO(anshul) other uses of isRunningLocked() can probably - // also be removed. - if !d.isRunning() { + // TODO(anshul) Explore if its possible to check context here without locking. + if _, err := d.isRunningLocked(); err != nil { return nil, status.Errorf(codes.Aborted, "dispatcher is stopped") } diff --git a/vendor/github.com/docker/swarmkit/manager/metrics/collector.go b/vendor/github.com/docker/swarmkit/manager/metrics/collector.go index d5adcb8306436..384743707d6fc 100644 --- a/vendor/github.com/docker/swarmkit/manager/metrics/collector.go +++ b/vendor/github.com/docker/swarmkit/manager/metrics/collector.go @@ -5,21 +5,36 @@ import ( "strings" + "github.com/docker/go-events" metrics "github.com/docker/go-metrics" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/manager/state/store" ) var ( - ns = metrics.NewNamespace("swarm", "manager", nil) + ns = metrics.NewNamespace("swarm", "manager", nil) + + // counts of the various objects in swarmkit nodesMetric metrics.LabeledGauge + tasksMetric metrics.LabeledGauge + + // none of these objects have state, so they're just regular gauges + servicesMetric metrics.Gauge + networksMetric metrics.Gauge + secretsMetric metrics.Gauge + configsMetric metrics.Gauge ) func init() { nodesMetric = ns.NewLabeledGauge("nodes", "The number of nodes", "", "state") - for _, state := range api.NodeStatus_State_name { - nodesMetric.WithValues(strings.ToLower(state)).Set(0) - } + tasksMetric = ns.NewLabeledGauge("tasks", "The number of tasks in the cluster object store", metrics.Total, "state") + servicesMetric = ns.NewGauge("services", "The number of services in the cluster object store", metrics.Total) + networksMetric = ns.NewGauge("networks", "The number of networks in the cluster object store", metrics.Total) + secretsMetric = ns.NewGauge("secrets", "The number of secrets in the cluster object store", metrics.Total) + configsMetric = ns.NewGauge("configs", "The number of configs in the cluster object store", metrics.Total) + + resetMetrics() + metrics.Register(ns) } @@ -42,20 +57,6 @@ func NewCollector(store *store.MemoryStore) *Collector { } } -func (c *Collector) updateNodeState(prevNode, newNode *api.Node) { - // Skip updates if nothing changed. - if prevNode != nil && newNode != nil && prevNode.Status.State == newNode.Status.State { - return - } - - if prevNode != nil { - nodesMetric.WithValues(strings.ToLower(prevNode.Status.State.String())).Dec(1) - } - if newNode != nil { - nodesMetric.WithValues(strings.ToLower(newNode.Status.State.String())).Inc(1) - } -} - // Run contains the collector event loop func (c *Collector) Run(ctx context.Context) error { defer close(c.doneChan) @@ -65,9 +66,46 @@ func (c *Collector) Run(ctx context.Context) error { if err != nil { return err } - for _, node := range nodes { - c.updateNodeState(nil, node) + tasks, err := store.FindTasks(readTx, store.All) + if err != nil { + return err } + services, err := store.FindServices(readTx, store.All) + if err != nil { + return err + } + networks, err := store.FindNetworks(readTx, store.All) + if err != nil { + return err + } + secrets, err := store.FindSecrets(readTx, store.All) + if err != nil { + return err + } + configs, err := store.FindConfigs(readTx, store.All) + if err != nil { + return err + } + + for _, obj := range nodes { + c.handleEvent(obj.EventCreate()) + } + for _, obj := range tasks { + c.handleEvent(obj.EventCreate()) + } + for _, obj := range services { + c.handleEvent(obj.EventCreate()) + } + for _, obj := range networks { + c.handleEvent(obj.EventCreate()) + } + for _, obj := range secrets { + c.handleEvent(obj.EventCreate()) + } + for _, obj := range configs { + c.handleEvent(obj.EventCreate()) + } + return nil }) if err != nil { @@ -78,14 +116,7 @@ func (c *Collector) Run(ctx context.Context) error { for { select { case event := <-watcher: - switch v := event.(type) { - case api.EventCreateNode: - c.updateNodeState(nil, v.Node) - case api.EventUpdateNode: - c.updateNodeState(v.OldNode, v.Node) - case api.EventDeleteNode: - c.updateNodeState(v.Node, nil) - } + c.handleEvent(event) case <-c.stopChan: return nil } @@ -98,7 +129,131 @@ func (c *Collector) Stop() { <-c.doneChan // Clean the metrics on exit. + resetMetrics() +} + +// resetMetrics resets all metrics to their default (base) value +func resetMetrics() { for _, state := range api.NodeStatus_State_name { nodesMetric.WithValues(strings.ToLower(state)).Set(0) } + for _, state := range api.TaskState_name { + tasksMetric.WithValues(strings.ToLower(state)).Set(0) + } + servicesMetric.Set(0) + networksMetric.Set(0) + secretsMetric.Set(0) + configsMetric.Set(0) + +} + +// handleEvent handles a single incoming cluster event. +func (c *Collector) handleEvent(event events.Event) { + switch event.(type) { + case api.EventNode: + c.handleNodeEvent(event) + case api.EventTask: + c.handleTaskEvent(event) + case api.EventService: + c.handleServiceEvent(event) + case api.EventNetwork: + c.handleNetworkEvent(event) + case api.EventSecret: + c.handleSecretsEvent(event) + case api.EventConfig: + c.handleConfigsEvent(event) + } +} + +func (c *Collector) handleNodeEvent(event events.Event) { + var prevNode, newNode *api.Node + + switch v := event.(type) { + case api.EventCreateNode: + prevNode, newNode = nil, v.Node + case api.EventUpdateNode: + prevNode, newNode = v.OldNode, v.Node + case api.EventDeleteNode: + prevNode, newNode = v.Node, nil + } + + // Skip updates if nothing changed. + if prevNode != nil && newNode != nil && prevNode.Status.State == newNode.Status.State { + return + } + + if prevNode != nil { + nodesMetric.WithValues(strings.ToLower(prevNode.Status.State.String())).Dec(1) + } + if newNode != nil { + nodesMetric.WithValues(strings.ToLower(newNode.Status.State.String())).Inc(1) + } + return +} + +func (c *Collector) handleTaskEvent(event events.Event) { + var prevTask, newTask *api.Task + + switch v := event.(type) { + case api.EventCreateTask: + prevTask, newTask = nil, v.Task + case api.EventUpdateTask: + prevTask, newTask = v.OldTask, v.Task + case api.EventDeleteTask: + prevTask, newTask = v.Task, nil + } + + // Skip updates if nothing changed. + if prevTask != nil && newTask != nil && prevTask.Status.State == newTask.Status.State { + return + } + + if prevTask != nil { + tasksMetric.WithValues( + strings.ToLower(prevTask.Status.State.String()), + ).Dec(1) + } + if newTask != nil { + tasksMetric.WithValues( + strings.ToLower(newTask.Status.State.String()), + ).Inc(1) + } + + return +} + +func (c *Collector) handleServiceEvent(event events.Event) { + switch event.(type) { + case api.EventCreateService: + servicesMetric.Inc(1) + case api.EventDeleteService: + servicesMetric.Dec(1) + } +} + +func (c *Collector) handleNetworkEvent(event events.Event) { + switch event.(type) { + case api.EventCreateNetwork: + networksMetric.Inc(1) + case api.EventDeleteNetwork: + networksMetric.Dec(1) + } +} + +func (c *Collector) handleSecretsEvent(event events.Event) { + switch event.(type) { + case api.EventCreateSecret: + secretsMetric.Inc(1) + case api.EventDeleteSecret: + secretsMetric.Dec(1) + } +} + +func (c *Collector) handleConfigsEvent(event events.Event) { + switch event.(type) { + case api.EventCreateConfig: + configsMetric.Inc(1) + case api.EventDeleteConfig: + configsMetric.Dec(1) + } } diff --git a/vendor/github.com/docker/swarmkit/manager/orchestrator/taskreaper/task_reaper.go b/vendor/github.com/docker/swarmkit/manager/orchestrator/taskreaper/task_reaper.go index 1c93b77fbe62e..e3c2b8265dfac 100644 --- a/vendor/github.com/docker/swarmkit/manager/orchestrator/taskreaper/task_reaper.go +++ b/vendor/github.com/docker/swarmkit/manager/orchestrator/taskreaper/task_reaper.go @@ -40,6 +40,12 @@ type TaskReaper struct { cleanup []string stopChan chan struct{} doneChan chan struct{} + + // tickSignal is a channel that, if non-nil and available, will be written + // to to signal that a tick has occurred. its sole purpose is for testing + // code, to verify that take cleanup attempts are happening when they + // should be. + tickSignal chan struct{} } // New creates a new TaskReaper. @@ -115,7 +121,34 @@ func (tr *TaskReaper) Run(ctx context.Context) { // Clean up when we hit TaskHistoryRetentionLimit or when the timer expires, // whichever happens first. + // + // Specifically, the way this should work: + // - Create a timer and immediately stop it. We don't want to fire the + // cleanup routine yet, because we just did a cleanup as part of the + // initialization above. + // - Launch into an event loop + // - When we receive an event, handle the event as needed + // - After receiving the event: + // - If minimum batch size (maxDirty) is exceeded with dirty + cleanup, + // then immediately launch into the cleanup routine + // - Otherwise, if the timer is stopped, start it (reset). + // - If the timer expires and the timer channel is signaled, then Stop the + // timer (so that it will be ready to be started again as needed), and + // execute the cleanup routine (tick) timer := time.NewTimer(reaperBatchingInterval) + timer.Stop() + + // If stop is somehow called AFTER the timer has expired, there will be a + // value in the timer.C channel. If there is such a value, we should drain + // it out. This select statement allows us to drain that value if it's + // present, or continue straight through otherwise. + select { + case <-timer.C: + default: + } + + // keep track with a boolean of whether the timer is currently stopped + isTimerStopped := true // Watch for: // 1. EventCreateTask for cleaning slots, which is the best time to cleanup that node/slot. @@ -153,16 +186,35 @@ func (tr *TaskReaper) Run(ctx context.Context) { } if len(tr.dirty)+len(tr.cleanup) > maxDirty { + // stop the timer, so we don't fire it. if we get another event + // after we do this cleaning, we will reset the timer then timer.Stop() + // if the timer had fired, drain out the value. + select { + case <-timer.C: + default: + } + isTimerStopped = true tr.tick() } else { - timer.Reset(reaperBatchingInterval) + if isTimerStopped { + timer.Reset(reaperBatchingInterval) + isTimerStopped = false + } } case <-timer.C: - timer.Stop() + // we can safely ignore draining off of the timer channel, because + // we already know that the timer has expired. + isTimerStopped = true tr.tick() case <-tr.stopChan: + // even though this doesn't really matter in this context, it's + // good hygiene to drain the value. timer.Stop() + select { + case <-timer.C: + default: + } return } } @@ -170,6 +222,16 @@ func (tr *TaskReaper) Run(ctx context.Context) { // tick performs task history cleanup. func (tr *TaskReaper) tick() { + // this signals that a tick has occurred. it exists solely for testing. + if tr.tickSignal != nil { + // try writing to this channel, but if it's full, fall straight through + // and ignore it. + select { + case tr.tickSignal <- struct{}{}: + default: + } + } + if len(tr.dirty) == 0 && len(tr.cleanup) == 0 { return } @@ -184,10 +246,24 @@ func (tr *TaskReaper) tick() { } // Check history of dirty tasks for cleanup. + // Note: Clean out the dirty set at the end of this tick iteration + // in all but one scenarios (documented below). + // When tick() finishes, the tasks in the slot were either cleaned up, + // or it was skipped because it didn't meet the criteria for cleaning. + // Either way, we can discard the dirty set because future events on + // that slot will cause the task to be readded to the dirty set + // at that point. + // + // The only case when we keep the slot dirty is when there are more + // than one running tasks present for a given slot. + // In that case, we need to keep the slot dirty to allow it to be + // cleaned when tick() is called next and one or more the tasks + // in that slot have stopped running. tr.store.View(func(tx store.ReadTx) { for dirty := range tr.dirty { service := store.GetService(tx, dirty.ServiceID) if service == nil { + delete(tr.dirty, dirty) continue } @@ -211,6 +287,7 @@ func (tr *TaskReaper) tick() { // Negative value for TaskHistoryRetentionLimit is an indication to never clean up task history. if taskHistory < 0 { + delete(tr.dirty, dirty) continue } @@ -240,6 +317,7 @@ func (tr *TaskReaper) tick() { } if int64(len(historicTasks)) <= taskHistory { + delete(tr.dirty, dirty) continue } @@ -270,6 +348,12 @@ func (tr *TaskReaper) tick() { } } + // The only case when we keep the slot dirty at the end of tick() + // is when there are more than one running tasks present + // for a given slot. + // In that case, we keep the slot dirty to allow it to be + // cleaned when tick() is called next and one or more of + // the tasks in that slot have stopped running. if runningTasks <= 1 { delete(tr.dirty, dirty) } diff --git a/vendor/github.com/docker/swarmkit/manager/scheduler/scheduler.go b/vendor/github.com/docker/swarmkit/manager/scheduler/scheduler.go index 9ee0b9e5c91e6..9e708ed1b6b09 100644 --- a/vendor/github.com/docker/swarmkit/manager/scheduler/scheduler.go +++ b/vendor/github.com/docker/swarmkit/manager/scheduler/scheduler.go @@ -702,9 +702,20 @@ func (s *Scheduler) scheduleNTasksOnNodes(ctx context.Context, n int, taskGroup return tasksScheduled } +// noSuitableNode checks unassigned tasks and make sure they have an existing service in the store before +// updating the task status and adding it back to: schedulingDecisions, unassignedTasks and allTasks func (s *Scheduler) noSuitableNode(ctx context.Context, taskGroup map[string]*api.Task, schedulingDecisions map[string]schedulingDecision) { explanation := s.pipeline.Explain() for _, t := range taskGroup { + var service *api.Service + s.store.View(func(tx store.ReadTx) { + service = store.GetService(tx, t.ServiceID) + }) + if service == nil { + log.G(ctx).WithField("task.id", t.ID).Debug("removing task from the scheduler") + continue + } + log.G(ctx).WithField("task.id", t.ID).Debug("no suitable node available for task") newT := *t diff --git a/vendor/github.com/docker/swarmkit/node/node.go b/vendor/github.com/docker/swarmkit/node/node.go index 98283f936f7c8..83187cc75b1fd 100644 --- a/vendor/github.com/docker/swarmkit/node/node.go +++ b/vendor/github.com/docker/swarmkit/node/node.go @@ -1136,6 +1136,11 @@ func (n *Node) superviseManager(ctx context.Context, securityConfig *ca.Security // re-promoted. In this case, we must assume we were // re-promoted, and restart the manager. log.G(ctx).Warn("failed to get worker role after manager stop, forcing certificate renewal") + + // We can safely reset this timer without stopping/draining the timer + // first because the only way the code has reached this point is if the timer + // has already expired - if the role changed or the context were canceled, + // then we would have returned already. timer.Reset(roleChangeTimeout) renewer.Renew() diff --git a/vendor/github.com/fsnotify/fsnotify/kqueue.go b/vendor/github.com/fsnotify/fsnotify/kqueue.go index c2b4acb18ddf2..86e76a3d67683 100644 --- a/vendor/github.com/fsnotify/fsnotify/kqueue.go +++ b/vendor/github.com/fsnotify/fsnotify/kqueue.go @@ -22,7 +22,7 @@ import ( type Watcher struct { Events chan Event Errors chan error - done chan bool // Channel for sending a "quit message" to the reader goroutine + done chan struct{} // Channel for sending a "quit message" to the reader goroutine kq int // File descriptor (as returned by the kqueue() syscall). @@ -56,7 +56,7 @@ func NewWatcher() (*Watcher, error) { externalWatches: make(map[string]bool), Events: make(chan Event), Errors: make(chan error), - done: make(chan bool), + done: make(chan struct{}), } go w.readEvents() @@ -71,10 +71,8 @@ func (w *Watcher) Close() error { return nil } w.isClosed = true - w.mu.Unlock() // copy paths to remove while locked - w.mu.Lock() var pathsToRemove = make([]string, 0, len(w.watches)) for name := range w.watches { pathsToRemove = append(pathsToRemove, name) @@ -82,15 +80,12 @@ func (w *Watcher) Close() error { w.mu.Unlock() // unlock before calling Remove, which also locks - var err error for _, name := range pathsToRemove { - if e := w.Remove(name); e != nil && err == nil { - err = e - } + w.Remove(name) } - // Send "quit" message to the reader goroutine: - w.done <- true + // send a "quit" message to the reader goroutine + close(w.done) return nil } @@ -266,17 +261,12 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) { func (w *Watcher) readEvents() { eventBuffer := make([]unix.Kevent_t, 10) +loop: for { // See if there is a message on the "done" channel select { case <-w.done: - err := unix.Close(w.kq) - if err != nil { - w.Errors <- err - } - close(w.Events) - close(w.Errors) - return + break loop default: } @@ -284,7 +274,11 @@ func (w *Watcher) readEvents() { kevents, err := read(w.kq, eventBuffer, &keventWaitTime) // EINTR is okay, the syscall was interrupted before timeout expired. if err != nil && err != unix.EINTR { - w.Errors <- err + select { + case w.Errors <- err: + case <-w.done: + break loop + } continue } @@ -319,8 +313,12 @@ func (w *Watcher) readEvents() { if path.isDir && event.Op&Write == Write && !(event.Op&Remove == Remove) { w.sendDirectoryChangeEvents(event.Name) } else { - // Send the event on the Events channel - w.Events <- event + // Send the event on the Events channel. + select { + case w.Events <- event: + case <-w.done: + break loop + } } if event.Op&Remove == Remove { @@ -352,6 +350,18 @@ func (w *Watcher) readEvents() { kevents = kevents[1:] } } + + // cleanup + err := unix.Close(w.kq) + if err != nil { + // only way the previous loop breaks is if w.done was closed so we need to async send to w.Errors. + select { + case w.Errors <- err: + default: + } + } + close(w.Events) + close(w.Errors) } // newEvent returns an platform-independent Event based on kqueue Fflags. @@ -407,7 +417,11 @@ func (w *Watcher) sendDirectoryChangeEvents(dirPath string) { // Get all files files, err := ioutil.ReadDir(dirPath) if err != nil { - w.Errors <- err + select { + case w.Errors <- err: + case <-w.done: + return + } } // Search for new files @@ -428,7 +442,11 @@ func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInf w.mu.Unlock() if !doesExist { // Send create event - w.Events <- newCreateEvent(filePath) + select { + case w.Events <- newCreateEvent(filePath): + case <-w.done: + return + } } // like watchDirectoryFiles (but without doing another ReadDir) diff --git a/vendor/github.com/google/certificate-transparency/LICENSE b/vendor/github.com/google/certificate-transparency-go/LICENSE similarity index 100% rename from vendor/github.com/google/certificate-transparency/LICENSE rename to vendor/github.com/google/certificate-transparency-go/LICENSE diff --git a/vendor/github.com/google/certificate-transparency-go/README.md b/vendor/github.com/google/certificate-transparency-go/README.md new file mode 100644 index 0000000000000..6b71eaa987b9e --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/README.md @@ -0,0 +1,144 @@ +# Certificate Transparency: Go Code + +[![Build Status](https://travis-ci.org/google/certificate-transparency-go.svg?branch=master)](https://travis-ci.org/google/certificate-transparency-go) +[![Go Report Card](https://goreportcard.com/badge/github.com/google/certificate-transparency-go)](https://goreportcard.com/report/github.com/google/certificate-transparency-go) +[![GoDoc](https://godoc.org/github.com/google/certificate-transparency-go?status.svg)](https://godoc.org/github.com/google/certificate-transparency-go) + +This repository holds Go code related to +[Certificate Transparency](https://www.certificate-transparency.org/) (CT). The +repository requires Go version 1.9. + + - [Repository Structure](#repository-structure) + - [Trillian CT Personality](#trillian-ct-personality) + - [Working on the Code](#working-on-the-code) + - [Rebuilding Generated Code](#rebuilding-generated-code) + - [Updating Vendor Code](#updating-vendor-code) + - [Running Codebase Checks](#running-codebase-checks) + +## Repository Structure + +The main parts of the repository are: + + - Encoding libraries: + - `asn1/` and `x509/` are forks of the upstream Go `encoding/asn1` and + `crypto/x509` libraries. We maintain separate forks of these packages + because CT is intended to act as an observatory of certificates across the + ecosystem; as such, we need to be able to process somewhat-malformed + certificates that the stricter upstream code would (correctly) reject. + Our `x509` fork also includes code for working with the + [pre-certificates defined in RFC 6962](https://tools.ietf.org/html/rfc6962#section-3.1). + - `tls` holds a library for processing TLS-encoded data as described in + [RFC 5246](https://tools.ietf.org/html/rfc5246). + - `x509util` provides additional utilities for dealing with + `x509.Certificate`s. + - CT client libraries: + - The top-level `ct` package (in `.`) holds types and utilities for working + with CT data structures defined in + [RFC 6962](https://tools.ietf.org/html/rfc6962). + - `client/` and `jsonclient/` hold libraries that allow access to CT Logs + via entrypoints described in + [section 4 of RFC 6962](https://tools.ietf.org/html/rfc6962#section-4). + - `scanner/` holds a library for scanning the entire contents of an existing + CT Log. + - Command line tools: + - `./client/ctclient` allows interaction with a CT Log + - `./scanner/scanlog` allows an existing CT Log to be scanned for certificates + of interest; please be polite when running this tool against a Log. + - `./x509util/certcheck` allows display and verification of certificates + - `./x509util/crlcheck` allows display and verification of certificate + revocation lists (CRLs). + - CT Personality for [Trillian](https://github.com/google/trillian): + - `trillian/` holds code that allows a Certificate Transparency Log to be + run using a Trillian Log as its back-end -- see + [below](#trillian-ct-personality). + + +## Trillian CT Personality + +The `trillian/` subdirectory holds code and scripts for running a CT Log based +on the [Trillian](https://github.com/google/trillian) general transparency Log. + +The main code for the CT personality is held in `trillian/ctfe`; this code +responds to HTTP requests on the +[CT API paths](https://tools.ietf.org/html/rfc6962#section-4) and translates +them to the equivalent gRPC API requests to the Trillian Log. + +This obviously relies on the gRPC API definitions at +`github.com/google/trillian`; the code also uses common libraries from the +Trillian project for: + - exposing monitoring and statistics via an `interface` and corresponding + Prometheus implementation (`github.com/google/trillian/monitoring/...`) + - dealing with cryptographic keys (`github.com/google/trillian/crypto/...`). + +The `trillian/integration/` directory holds scripts and tests for running the whole +system locally. In particular: + - `trillian/integration/ct_integration_test.sh` brings up local processes + running a Trillian Log server, signer and a CT personality, and exercises the + complete set of RFC 6962 API entrypoints. + - `trillian/integration/ct_hammer_test.sh` brings up a complete system and runs + a continuous randomized test of the CT entrypoints. + +These scripts require a local database instance to be configured as described +in the [Trillian instructions](https://github.com/google/trillian#mysql-setup). + + +## Working on the Code + +Developers who want to make changes to the codebase need some additional +dependencies and tools, described in the following sections. The +[Travis configuration](.travis.yml) for the codebase is also useful reference +for the required tools and scripts, as it may be more up-to-date than this +document. + +### Rebuilding Generated Code + +Some of the CT Go code is autogenerated from other files: + + - [Protocol buffer](https://developers.google.com/protocol-buffers/) message + definitions are converted to `.pb.go` implementations. + - A mock implementation of the Trillian gRPC API (in `trillian/mockclient`) is + created with [GoMock](https://github.com/golang/mock). + +Re-generating mock or protobuffer files is only needed if you're changing +the original files; if you do, you'll need to install the prerequisites: + + - `mockgen` tool from https://github.com/golang/mock + - `protoc`, [Go support for protoc](https://github.com/golang/protobuf) (see + documentation linked from the + [protobuf site](https://github.com/google/protobuf)) + +and run the following: + +```bash +go generate -x ./... # hunts for //go:generate comments and runs them +``` + +### Updating Vendor Code + +The codebase includes a couple of external projects under the `vendor/` +subdirectory, to ensure that builds use a fixed version (typically because the +upstream repository does not guarantee back-compatibility between the tip +`master` branch and the current stable release). See +[instructions in the Trillian repo](https://github.com/google/trillian#updating-vendor-code) +for how to update vendored subtrees. + + +### Running Codebase Checks + +The [`scripts/presubmit.sh`](scripts/presubmit.sh) script runs various tools +and tests over the codebase. + +```bash +# Install gometalinter and all linters +go get -u github.com/alecthomas/gometalinter +gometalinter --install + +# Run code generation, build, test and linters +./scripts/presubmit.sh + +# Run build, test and linters but skip code generation +./scripts/presubmit.sh --no-generate + +# Or just run the linters alone: +gometalinter --config=gometalinter.json ./... +``` diff --git a/vendor/github.com/google/certificate-transparency/go/asn1/asn1.go b/vendor/github.com/google/certificate-transparency-go/asn1/asn1.go old mode 100755 new mode 100644 similarity index 63% rename from vendor/github.com/google/certificate-transparency/go/asn1/asn1.go rename to vendor/github.com/google/certificate-transparency-go/asn1/asn1.go index e9874779523ef..3af7c48760e86 --- a/vendor/github.com/google/certificate-transparency/go/asn1/asn1.go +++ b/vendor/github.com/google/certificate-transparency-go/asn1/asn1.go @@ -8,12 +8,10 @@ // See also ``A Layman's Guide to a Subset of ASN.1, BER, and DER,'' // http://luca.ntop.org/Teaching/Appunti/asn1.html. // -// START CT CHANGES // This is a fork of the Go standard library ASN.1 implementation // (encoding/asn1). The main difference is that this version tries to correct // for errors (e.g. use of tagPrintableString when the string data is really // ISO8859-1 - a common error present in many x509 certificates in the wild.) -// END CT CHANGES package asn1 // ASN.1 is a syntax for specifying abstract objects and BER, DER, PER, XER etc @@ -27,40 +25,53 @@ package asn1 // everything by any means. import ( - // START CT CHANGES "errors" "fmt" - // END CT CHANGES + "math" "math/big" "reflect" - // START CT CHANGES + "strconv" "strings" - // END CT CHANGES "time" + "unicode/utf8" ) // A StructuralError suggests that the ASN.1 data is valid, but the Go type // which is receiving it doesn't match. type StructuralError struct { - Msg string + Msg string + Field string } -func (e StructuralError) Error() string { return "asn1: structure error: " + e.Msg } +func (e StructuralError) Error() string { + var prefix string + if e.Field != "" { + prefix = e.Field + ": " + } + return "asn1: structure error: " + prefix + e.Msg +} // A SyntaxError suggests that the ASN.1 data is invalid. type SyntaxError struct { - Msg string + Msg string + Field string } -func (e SyntaxError) Error() string { return "asn1: syntax error: " + e.Msg } +func (e SyntaxError) Error() string { + var prefix string + if e.Field != "" { + prefix = e.Field + ": " + } + return "asn1: syntax error: " + prefix + e.Msg +} // We start by dealing with each of the primitive types in turn. // BOOLEAN -func parseBool(bytes []byte) (ret bool, err error) { +func parseBool(bytes []byte, fieldName string) (ret bool, err error) { if len(bytes) != 1 { - err = SyntaxError{"invalid boolean"} + err = SyntaxError{"invalid boolean", fieldName} return } @@ -73,7 +84,7 @@ func parseBool(bytes []byte) (ret bool, err error) { case 0xff: ret = true default: - err = SyntaxError{"invalid boolean"} + err = SyntaxError{"invalid boolean", fieldName} } return @@ -81,12 +92,31 @@ func parseBool(bytes []byte) (ret bool, err error) { // INTEGER +// checkInteger returns nil if the given bytes are a valid DER-encoded +// INTEGER and an error otherwise. +func checkInteger(bytes []byte, fieldName string) error { + if len(bytes) == 0 { + return StructuralError{"empty integer", fieldName} + } + if len(bytes) == 1 { + return nil + } + if (bytes[0] == 0 && bytes[1]&0x80 == 0) || (bytes[0] == 0xff && bytes[1]&0x80 == 0x80) { + return StructuralError{"integer not minimally-encoded", fieldName} + } + return nil +} + // parseInt64 treats the given bytes as a big-endian, signed integer and // returns the result. -func parseInt64(bytes []byte) (ret int64, err error) { +func parseInt64(bytes []byte, fieldName string) (ret int64, err error) { + err = checkInteger(bytes, fieldName) + if err != nil { + return + } if len(bytes) > 8 { // We'll overflow an int64 in this case. - err = StructuralError{"integer too large"} + err = StructuralError{"integer too large", fieldName} return } for bytesRead := 0; bytesRead < len(bytes); bytesRead++ { @@ -102,13 +132,16 @@ func parseInt64(bytes []byte) (ret int64, err error) { // parseInt treats the given bytes as a big-endian, signed integer and returns // the result. -func parseInt32(bytes []byte) (int32, error) { - ret64, err := parseInt64(bytes) +func parseInt32(bytes []byte, fieldName string) (int32, error) { + if err := checkInteger(bytes, fieldName); err != nil { + return 0, err + } + ret64, err := parseInt64(bytes, fieldName) if err != nil { return 0, err } if ret64 != int64(int32(ret64)) { - return 0, StructuralError{"integer too large"} + return 0, StructuralError{"integer too large", fieldName} } return int32(ret64), nil } @@ -117,7 +150,10 @@ var bigOne = big.NewInt(1) // parseBigInt treats the given bytes as a big-endian, signed integer and returns // the result. -func parseBigInt(bytes []byte) *big.Int { +func parseBigInt(bytes []byte, fieldName string) (*big.Int, error) { + if err := checkInteger(bytes, fieldName); err != nil { + return nil, err + } ret := new(big.Int) if len(bytes) > 0 && bytes[0]&0x80 == 0x80 { // This is a negative number. @@ -128,10 +164,10 @@ func parseBigInt(bytes []byte) *big.Int { ret.SetBytes(notBytes) ret.Add(ret, bigOne) ret.Neg(ret) - return ret + return ret, nil } ret.SetBytes(bytes) - return ret + return ret, nil } // BIT STRING @@ -174,16 +210,16 @@ func (b BitString) RightAlign() []byte { } // parseBitString parses an ASN.1 bit string from the given byte slice and returns it. -func parseBitString(bytes []byte) (ret BitString, err error) { +func parseBitString(bytes []byte, fieldName string) (ret BitString, err error) { if len(bytes) == 0 { - err = SyntaxError{"zero length BIT STRING"} + err = SyntaxError{"zero length BIT STRING", fieldName} return } paddingBits := int(bytes[0]) if paddingBits > 7 || len(bytes) == 1 && paddingBits > 0 || bytes[len(bytes)-1]&((1< 0 { + s += "." + } + s += strconv.Itoa(v) + } + + return s +} + // parseObjectIdentifier parses an OBJECT IDENTIFIER from the given bytes and // returns it. An object identifier is a sequence of variable length integers // that are assigned in a hierarchy. -func parseObjectIdentifier(bytes []byte) (s []int, err error) { +func parseObjectIdentifier(bytes []byte, fieldName string) (s []int, err error) { if len(bytes) == 0 { - err = SyntaxError{"zero length OBJECT IDENTIFIER"} + err = SyntaxError{"zero length OBJECT IDENTIFIER", fieldName} return } @@ -227,7 +284,7 @@ func parseObjectIdentifier(bytes []byte) (s []int, err error) { // According to this packing, value1 can take the values 0, 1 and 2 only. // When value1 = 0 or value1 = 1, then value2 is <= 39. When value1 = 2, // then there are no restrictions on value2. - v, offset, err := parseBase128Int(bytes, 0) + v, offset, err := parseBase128Int(bytes, 0, fieldName) if err != nil { return } @@ -241,7 +298,7 @@ func parseObjectIdentifier(bytes []byte) (s []int, err error) { i := 2 for ; offset < len(bytes); i++ { - v, offset, err = parseBase128Int(bytes, offset) + v, offset, err = parseBase128Int(bytes, offset, fieldName) if err != nil { return } @@ -263,22 +320,30 @@ type Flag bool // parseBase128Int parses a base-128 encoded int from the given offset in the // given byte slice. It returns the value and the new offset. -func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err error) { +func parseBase128Int(bytes []byte, initOffset int, fieldName string) (ret, offset int, err error) { offset = initOffset + var ret64 int64 for shifted := 0; offset < len(bytes); shifted++ { - if shifted > 4 { - err = StructuralError{"base 128 integer too large"} + // 5 * 7 bits per byte == 35 bits of data + // Thus the representation is either non-minimal or too large for an int32 + if shifted == 5 { + err = StructuralError{"base 128 integer too large", fieldName} return } - ret <<= 7 + ret64 <<= 7 b := bytes[offset] - ret |= int(b & 0x7f) + ret64 |= int64(b & 0x7f) offset++ if b&0x80 == 0 { + ret = int(ret64) + // Ensure that the returned value fits in an int on all platforms + if ret64 > math.MaxInt32 { + err = StructuralError{"base 128 integer too large", fieldName} + } return } } - err = SyntaxError{"truncated base 128 integer"} + err = SyntaxError{"truncated base 128 integer", fieldName} return } @@ -286,11 +351,23 @@ func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err error) func parseUTCTime(bytes []byte) (ret time.Time, err error) { s := string(bytes) - ret, err = time.Parse("0601021504Z0700", s) + + formatStr := "0601021504Z0700" + ret, err = time.Parse(formatStr, s) if err != nil { - ret, err = time.Parse("060102150405Z0700", s) + formatStr = "060102150405Z0700" + ret, err = time.Parse(formatStr, s) } - if err == nil && ret.Year() >= 2050 { + if err != nil { + return + } + + if serialized := ret.Format(formatStr); serialized != s { + err = fmt.Errorf("asn1: time did not serialize back to the original value and may be invalid: given %q, but serialized as %q", s, serialized) + return + } + + if ret.Year() >= 2050 { // UTCTime only encodes times prior to 2050. See https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1 ret = ret.AddDate(-100, 0, 0) } @@ -301,17 +378,47 @@ func parseUTCTime(bytes []byte) (ret time.Time, err error) { // parseGeneralizedTime parses the GeneralizedTime from the given byte slice // and returns the resulting time. func parseGeneralizedTime(bytes []byte) (ret time.Time, err error) { - return time.Parse("20060102150405Z0700", string(bytes)) + const formatStr = "20060102150405Z0700" + s := string(bytes) + + if ret, err = time.Parse(formatStr, s); err != nil { + return + } + + if serialized := ret.Format(formatStr); serialized != s { + err = fmt.Errorf("asn1: time did not serialize back to the original value and may be invalid: given %q, but serialized as %q", s, serialized) + } + + return +} + +// NumericString + +// parseNumericString parses an ASN.1 NumericString from the given byte array +// and returns it. +func parseNumericString(bytes []byte, fieldName string) (ret string, err error) { + for _, b := range bytes { + if !isNumeric(b) { + return "", SyntaxError{"NumericString contains invalid character", fieldName} + } + } + return string(bytes), nil +} + +// isNumeric reports whether the given b is in the ASN.1 NumericString set. +func isNumeric(b byte) bool { + return '0' <= b && b <= '9' || + b == ' ' } // PrintableString -// parsePrintableString parses a ASN.1 PrintableString from the given byte +// parsePrintableString parses an ASN.1 PrintableString from the given byte // array and returns it. -func parsePrintableString(bytes []byte) (ret string, err error) { +func parsePrintableString(bytes []byte, fieldName string) (ret string, err error) { for _, b := range bytes { - if !isPrintable(b) { - err = SyntaxError{"PrintableString contains invalid character"} + if !isPrintable(b, allowAsterisk, allowAmpersand) { + err = SyntaxError{"PrintableString contains invalid character", fieldName} return } } @@ -319,8 +426,21 @@ func parsePrintableString(bytes []byte) (ret string, err error) { return } -// isPrintable returns true iff the given b is in the ASN.1 PrintableString set. -func isPrintable(b byte) bool { +type asteriskFlag bool +type ampersandFlag bool + +const ( + allowAsterisk asteriskFlag = true + rejectAsterisk asteriskFlag = false + + allowAmpersand ampersandFlag = true + rejectAmpersand ampersandFlag = false +) + +// isPrintable reports whether the given b is in the ASN.1 PrintableString set. +// If asterisk is allowAsterisk then '*' is also allowed, reflecting existing +// practice. If ampersand is allowAmpersand then '&' is allowed as well. +func isPrintable(b byte, asterisk asteriskFlag, ampersand ampersandFlag) bool { return 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z' || '0' <= b && b <= '9' || @@ -333,17 +453,22 @@ func isPrintable(b byte) bool { // This is technically not allowed in a PrintableString. // However, x509 certificates with wildcard strings don't // always use the correct string type so we permit it. - b == '*' + (bool(asterisk) && b == '*') || + // This is not technically allowed either. However, not + // only is it relatively common, but there are also a + // handful of CA certificates that contain it. At least + // one of which will not expire until 2027. + (bool(ampersand) && b == '&') } // IA5String -// parseIA5String parses a ASN.1 IA5String (ASCII string) from the given +// parseIA5String parses an ASN.1 IA5String (ASCII string) from the given // byte slice and returns it. -func parseIA5String(bytes []byte) (ret string, err error) { +func parseIA5String(bytes []byte, fieldName string) (ret string, err error) { for _, b := range bytes { - if b >= 0x80 { - err = SyntaxError{"IA5String contains invalid character"} + if b >= utf8.RuneSelf { + err = SyntaxError{"IA5String contains invalid character", fieldName} return } } @@ -353,7 +478,7 @@ func parseIA5String(bytes []byte) (ret string, err error) { // T61String -// parseT61String parses a ASN.1 T61String (8-bit clean string) from the given +// parseT61String parses an ASN.1 T61String (8-bit clean string) from the given // byte slice and returns it. func parseT61String(bytes []byte) (ret string, err error) { return string(bytes), nil @@ -361,9 +486,12 @@ func parseT61String(bytes []byte) (ret string, err error) { // UTF8String -// parseUTF8String parses a ASN.1 UTF8String (raw UTF-8) from the given byte +// parseUTF8String parses an ASN.1 UTF8String (raw UTF-8) from the given byte // array and returns it. func parseUTF8String(bytes []byte) (ret string, err error) { + if !utf8.Valid(bytes) { + return "", errors.New("asn1: invalid UTF-8 string") + } return string(bytes), nil } @@ -386,8 +514,14 @@ type RawContent []byte // into a byte slice. It returns the parsed data and the new offset. SET and // SET OF (tag 17) are mapped to SEQUENCE and SEQUENCE OF (tag 16) since we // don't distinguish between ordered and unordered objects in this code. -func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset int, err error) { +func parseTagAndLength(bytes []byte, initOffset int, fieldName string) (ret tagAndLength, offset int, err error) { offset = initOffset + // parseTagAndLength should not be called without at least a single + // byte to read. Thus this check is for robustness: + if offset >= len(bytes) { + err = errors.New("asn1: internal error in parseTagAndLength") + return + } b := bytes[offset] offset++ ret.class = int(b >> 6) @@ -397,13 +531,18 @@ func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset i // If the bottom five bits are set, then the tag number is actually base 128 // encoded afterwards if ret.tag == 0x1f { - ret.tag, offset, err = parseBase128Int(bytes, offset) + ret.tag, offset, err = parseBase128Int(bytes, offset, fieldName) if err != nil { return } + // Tags should be encoded in minimal form. + if ret.tag < 0x1f { + err = SyntaxError{"non-minimal tag", fieldName} + return + } } if offset >= len(bytes) { - err = SyntaxError{"truncated tag or length"} + err = SyntaxError{"truncated tag or length", fieldName} return } b = bytes[offset] @@ -415,13 +554,13 @@ func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset i // Bottom 7 bits give the number of length bytes to follow. numBytes := int(b & 0x7f) if numBytes == 0 { - err = SyntaxError{"indefinite length found (not DER)"} + err = SyntaxError{"indefinite length found (not DER)", fieldName} return } ret.length = 0 for i := 0; i < numBytes; i++ { if offset >= len(bytes) { - err = SyntaxError{"truncated tag or length"} + err = SyntaxError{"truncated tag or length", fieldName} return } b = bytes[offset] @@ -429,17 +568,22 @@ func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset i if ret.length >= 1<<23 { // We can't shift ret.length up without // overflowing. - err = StructuralError{"length too large"} + err = StructuralError{"length too large", fieldName} return } ret.length <<= 8 ret.length |= int(b) if ret.length == 0 { // DER requires that lengths be minimal. - err = StructuralError{"superfluous leading zeros in length"} + err = StructuralError{"superfluous leading zeros in length", fieldName} return } } + // Short lengths must be encoded in short form. + if ret.length < 0x80 { + err = StructuralError{"non-minimal length", fieldName} + return + } } return @@ -448,10 +592,10 @@ func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset i // parseSequenceOf is used for SEQUENCE OF and SET OF values. It tries to parse // a number of ASN.1 values from the given byte slice and returns them as a // slice of Go values of the given type. -func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type) (ret reflect.Value, err error) { - expectedTag, compoundType, ok := getUniversalType(elemType) +func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type, fieldName string) (ret reflect.Value, err error) { + matchAny, expectedTag, compoundType, ok := getUniversalType(elemType) if !ok { - err = StructuralError{"unknown Go type for slice"} + err = StructuralError{"unknown Go type for slice", fieldName} return } @@ -460,21 +604,27 @@ func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type numElements := 0 for offset := 0; offset < len(bytes); { var t tagAndLength - t, offset, err = parseTagAndLength(bytes, offset) + t, offset, err = parseTagAndLength(bytes, offset, fieldName) if err != nil { return } - // We pretend that GENERAL STRINGs are PRINTABLE STRINGs so - // that a sequence of them can be parsed into a []string. - if t.tag == tagGeneralString { - t.tag = tagPrintableString + switch t.tag { + case TagIA5String, TagGeneralString, TagT61String, TagUTF8String, TagNumericString: + // We pretend that various other string types are + // PRINTABLE STRINGs so that a sequence of them can be + // parsed into a []string. + t.tag = TagPrintableString + case TagGeneralizedTime, TagUTCTime: + // Likewise, both time types are treated the same. + t.tag = TagUTCTime } - if t.class != classUniversal || t.isCompound != compoundType || t.tag != expectedTag { - err = StructuralError{"sequence tag mismatch"} + + if !matchAny && (t.class != ClassUniversal || t.isCompound != compoundType || t.tag != expectedTag) { + err = StructuralError{fmt.Sprintf("sequence tag mismatch (got:%+v, want:0/%d/%t)", t, expectedTag, compoundType), fieldName} return } if invalidLength(offset, t.length, len(bytes)) { - err = SyntaxError{"truncated sequence"} + err = SyntaxError{"truncated sequence", fieldName} return } offset += t.length @@ -509,8 +659,6 @@ func invalidLength(offset, length, sliceLength int) bool { return offset+length < offset || offset+length > sliceLength } -// START CT CHANGES - // Tests whether the data in |bytes| would be a valid ISO8859-1 string. // Clearly, a sequence of bytes comprised solely of valid ISO8859-1 // codepoints does not imply that the encoding MUST be ISO8859-1, rather that @@ -556,8 +704,6 @@ func iso8859_1ToUTF8(bytes []byte) string { return string(buf) } -// END CT CHANGES - // parseField is the main parsing function. Given a byte slice and an offset // into the array, it will try to parse a suitable ASN.1 value out and store it // in the given Value. @@ -568,46 +714,28 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam // If we have run out of data, it may be that there are optional elements at the end. if offset == len(bytes) { if !setDefaultValue(v, params) { - err = SyntaxError{"sequence truncated"} - } - return - } - - // Deal with raw values. - if fieldType == rawValueType { - var t tagAndLength - t, offset, err = parseTagAndLength(bytes, offset) - if err != nil { - return - } - if invalidLength(offset, t.length, len(bytes)) { - err = SyntaxError{"data truncated"} - return + err = SyntaxError{"sequence truncated", params.name} } - result := RawValue{t.class, t.tag, t.isCompound, bytes[offset : offset+t.length], bytes[initOffset : offset+t.length]} - offset += t.length - v.Set(reflect.ValueOf(result)) return } // Deal with the ANY type. if ifaceType := fieldType; ifaceType.Kind() == reflect.Interface && ifaceType.NumMethod() == 0 { var t tagAndLength - t, offset, err = parseTagAndLength(bytes, offset) + t, offset, err = parseTagAndLength(bytes, offset, params.name) if err != nil { return } if invalidLength(offset, t.length, len(bytes)) { - err = SyntaxError{"data truncated"} + err = SyntaxError{"data truncated", params.name} return } var result interface{} - if !t.isCompound && t.class == classUniversal { + if !t.isCompound && t.class == ClassUniversal { innerBytes := bytes[offset : offset+t.length] switch t.tag { - case tagPrintableString: - result, err = parsePrintableString(innerBytes) - // START CT CHANGES + case TagPrintableString: + result, err = parsePrintableString(innerBytes, params.name) if err != nil && strings.Contains(err.Error(), "PrintableString contains invalid character") { // Probably an ISO8859-1 string stuffed in, check if it // would be valid and assume that's what's happened if so, @@ -623,22 +751,25 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam err = errors.New("PrintableString contains invalid character, but couldn't determine correct String type.") } } - // END CT CHANGES - case tagIA5String: - result, err = parseIA5String(innerBytes) - case tagT61String: + case TagNumericString: + result, err = parseNumericString(innerBytes, params.name) + case TagIA5String: + result, err = parseIA5String(innerBytes, params.name) + case TagT61String: result, err = parseT61String(innerBytes) - case tagUTF8String: + case TagUTF8String: result, err = parseUTF8String(innerBytes) - case tagInteger: - result, err = parseInt64(innerBytes) - case tagBitString: - result, err = parseBitString(innerBytes) - case tagOID: - result, err = parseObjectIdentifier(innerBytes) - case tagUTCTime: + case TagInteger: + result, err = parseInt64(innerBytes, params.name) + case TagBitString: + result, err = parseBitString(innerBytes, params.name) + case TagOID: + result, err = parseObjectIdentifier(innerBytes, params.name) + case TagUTCTime: result, err = parseUTCTime(innerBytes) - case tagOctetString: + case TagGeneralizedTime: + result, err = parseGeneralizedTime(innerBytes) + case TagOctetString: result = innerBytes default: // If we don't know how to handle the type, we just leave Value as nil. @@ -653,30 +784,31 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam } return } - universalTag, compoundType, ok1 := getUniversalType(fieldType) - if !ok1 { - err = StructuralError{fmt.Sprintf("unknown Go type: %v", fieldType)} - return - } - t, offset, err := parseTagAndLength(bytes, offset) + t, offset, err := parseTagAndLength(bytes, offset, params.name) if err != nil { return } if params.explicit { - expectedClass := classContextSpecific + expectedClass := ClassContextSpecific if params.application { - expectedClass = classApplication + expectedClass = ClassApplication + } + if offset == len(bytes) { + err = StructuralError{"explicit tag has no child", params.name} + return } if t.class == expectedClass && t.tag == *params.tag && (t.length == 0 || t.isCompound) { - if t.length > 0 { - t, offset, err = parseTagAndLength(bytes, offset) + if fieldType == rawValueType { + // The inner element should not be parsed for RawValues. + } else if t.length > 0 { + t, offset, err = parseTagAndLength(bytes, offset, params.name) if err != nil { return } } else { if fieldType != flagType { - err = StructuralError{"zero length explicit tag was not an asn1.Flag"} + err = StructuralError{"zero length explicit tag was not an asn1.Flag", params.name} return } v.SetBool(true) @@ -688,55 +820,73 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam if ok { offset = initOffset } else { - err = StructuralError{"explicitly tagged member didn't match"} + err = StructuralError{"explicitly tagged member didn't match", params.name} } return } } + matchAny, universalTag, compoundType, ok1 := getUniversalType(fieldType) + if !ok1 { + err = StructuralError{fmt.Sprintf("unknown Go type: %v", fieldType), params.name} + return + } + // Special case for strings: all the ASN.1 string types map to the Go // type string. getUniversalType returns the tag for PrintableString // when it sees a string, so if we see a different string type on the // wire, we change the universal type to match. - if universalTag == tagPrintableString { - switch t.tag { - case tagIA5String, tagGeneralString, tagT61String, tagUTF8String: - universalTag = t.tag + if universalTag == TagPrintableString { + if t.class == ClassUniversal { + switch t.tag { + case TagIA5String, TagGeneralString, TagT61String, TagUTF8String, TagNumericString: + universalTag = t.tag + } + } else if params.stringType != 0 { + universalTag = params.stringType } } // Special case for time: UTCTime and GeneralizedTime both map to the // Go type time.Time. - if universalTag == tagUTCTime && t.tag == tagGeneralizedTime { - universalTag = tagGeneralizedTime + if universalTag == TagUTCTime && t.tag == TagGeneralizedTime && t.class == ClassUniversal { + universalTag = TagGeneralizedTime + } + + if params.set { + universalTag = TagSet } - expectedClass := classUniversal + matchAnyClassAndTag := matchAny + expectedClass := ClassUniversal expectedTag := universalTag if !params.explicit && params.tag != nil { - expectedClass = classContextSpecific + expectedClass = ClassContextSpecific expectedTag = *params.tag + matchAnyClassAndTag = false } if !params.explicit && params.application && params.tag != nil { - expectedClass = classApplication + expectedClass = ClassApplication expectedTag = *params.tag + matchAnyClassAndTag = false } // We have unwrapped any explicit tagging at this point. - if t.class != expectedClass || t.tag != expectedTag || t.isCompound != compoundType { + if !matchAnyClassAndTag && (t.class != expectedClass || t.tag != expectedTag) || + (!matchAny && t.isCompound != compoundType) { // Tags don't match. Again, it could be an optional element. ok := setDefaultValue(v, params) if ok { offset = initOffset } else { - err = StructuralError{fmt.Sprintf("tags don't match (%d vs %+v) %+v %s @%d", expectedTag, t, params, fieldType.Name(), offset)} + err = StructuralError{fmt.Sprintf("tags don't match (%d vs %+v) %+v %s @%d", expectedTag, t, params, fieldType.Name(), offset), params.name} } return } if invalidLength(offset, t.length, len(bytes)) { - err = SyntaxError{"data truncated"} + err = SyntaxError{"data truncated", params.name} return } innerBytes := bytes[offset : offset+t.length] @@ -744,8 +894,12 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam // We deal with the structures defined in this package first. switch fieldType { + case rawValueType: + result := RawValue{t.class, t.tag, t.isCompound, innerBytes, bytes[initOffset:offset]} + v.Set(reflect.ValueOf(result)) + return case objectIdentifierType: - newSlice, err1 := parseObjectIdentifier(innerBytes) + newSlice, err1 := parseObjectIdentifier(innerBytes, params.name) v.Set(reflect.MakeSlice(v.Type(), len(newSlice), len(newSlice))) if err1 == nil { reflect.Copy(v, reflect.ValueOf(newSlice)) @@ -753,7 +907,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam err = err1 return case bitStringType: - bs, err1 := parseBitString(innerBytes) + bs, err1 := parseBitString(innerBytes, params.name) if err1 == nil { v.Set(reflect.ValueOf(bs)) } @@ -762,7 +916,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam case timeType: var time time.Time var err1 error - if universalTag == tagUTCTime { + if universalTag == TagUTCTime { time, err1 = parseUTCTime(innerBytes) } else { time, err1 = parseGeneralizedTime(innerBytes) @@ -773,7 +927,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam err = err1 return case enumeratedType: - parsedInt, err1 := parseInt32(innerBytes) + parsedInt, err1 := parseInt32(innerBytes, params.name) if err1 == nil { v.SetInt(int64(parsedInt)) } @@ -783,13 +937,16 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam v.SetBool(true) return case bigIntType: - parsedInt := parseBigInt(innerBytes) - v.Set(reflect.ValueOf(parsedInt)) + parsedInt, err1 := parseBigInt(innerBytes, params.name) + if err1 == nil { + v.Set(reflect.ValueOf(parsedInt)) + } + err = err1 return } switch val := v; val.Kind() { case reflect.Bool: - parsedBool, err1 := parseBool(innerBytes) + parsedBool, err1 := parseBool(innerBytes, params.name) if err1 == nil { val.SetBool(parsedBool) } @@ -797,13 +954,13 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam return case reflect.Int, reflect.Int32, reflect.Int64: if val.Type().Size() == 4 { - parsedInt, err1 := parseInt32(innerBytes) + parsedInt, err1 := parseInt32(innerBytes, params.name) if err1 == nil { val.SetInt(int64(parsedInt)) } err = err1 } else { - parsedInt, err1 := parseInt64(innerBytes) + parsedInt, err1 := parseInt64(innerBytes, params.name) if err1 == nil { val.SetInt(parsedInt) } @@ -814,6 +971,13 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam case reflect.Struct: structType := fieldType + for i := 0; i < structType.NumField(); i++ { + if structType.Field(i).PkgPath != "" { + err = StructuralError{"struct contains unexported fields", structType.Field(i).Name} + return + } + } + if structType.NumField() > 0 && structType.Field(0).Type == rawContentsType { bytes := bytes[initOffset:offset] @@ -826,7 +990,9 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam if i == 0 && field.Type == rawContentsType { continue } - innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, parseFieldParameters(field.Tag.Get("asn1"))) + innerParams := parseFieldParameters(field.Tag.Get("asn1")) + innerParams.name = field.Name + innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, innerParams) if err != nil { return } @@ -842,7 +1008,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam reflect.Copy(val, reflect.ValueOf(innerBytes)) return } - newSlice, err1 := parseSequenceOf(innerBytes, sliceType, sliceType.Elem()) + newSlice, err1 := parseSequenceOf(innerBytes, sliceType, sliceType.Elem(), params.name) if err1 == nil { val.Set(newSlice) } @@ -851,34 +1017,47 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam case reflect.String: var v string switch universalTag { - case tagPrintableString: - v, err = parsePrintableString(innerBytes) - case tagIA5String: - v, err = parseIA5String(innerBytes) - case tagT61String: + case TagPrintableString: + v, err = parsePrintableString(innerBytes, params.name) + case TagNumericString: + v, err = parseNumericString(innerBytes, params.name) + case TagIA5String: + v, err = parseIA5String(innerBytes, params.name) + case TagT61String: v, err = parseT61String(innerBytes) - case tagUTF8String: + case TagUTF8String: v, err = parseUTF8String(innerBytes) - case tagGeneralString: + case TagGeneralString: // GeneralString is specified in ISO-2022/ECMA-35, // A brief review suggests that it includes structures // that allow the encoding to change midstring and // such. We give up and pass it as an 8-bit string. v, err = parseT61String(innerBytes) default: - err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag)} + err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag), params.name} } if err == nil { val.SetString(v) } return } - err = StructuralError{"unsupported: " + v.Type().String()} + err = StructuralError{"unsupported: " + v.Type().String(), params.name} return } +// canHaveDefaultValue reports whether k is a Kind that we will set a default +// value for. (A signed integer, essentially.) +func canHaveDefaultValue(k reflect.Kind) bool { + switch k { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return true + } + + return false +} + // setDefaultValue is used to install a default value, from a tag string, into -// a Value. It is successful is the field was optional, even if a default value +// a Value. It is successful if the field was optional, even if a default value // wasn't provided or it failed to install it into the Value. func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) { if !params.optional { @@ -888,9 +1067,8 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) { if params.defaultValue == nil { return } - switch val := v; val.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - val.SetInt(*params.defaultValue) + if canHaveDefaultValue(v.Kind()) { + v.SetInt(*params.defaultValue) } return } @@ -916,7 +1094,7 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) { // // An ASN.1 UTCTIME or GENERALIZEDTIME can be written to a time.Time. // -// An ASN.1 PrintableString or IA5String can be written to a string. +// An ASN.1 PrintableString, IA5String, or NumericString can be written to a string. // // Any of the above ASN.1 values can be written to an interface{}. // The value stored in the interface has the corresponding Go type. @@ -931,13 +1109,20 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) { // // The following tags on struct fields have special meaning to Unmarshal: // -// optional marks the field as ASN.1 OPTIONAL -// [explicit] tag:x specifies the ASN.1 tag number; implies ASN.1 CONTEXT SPECIFIC -// default:x sets the default value for optional integer fields +// application specifies that an APPLICATION tag is used +// default:x sets the default value for optional integer fields (only used if optional is also present) +// explicit specifies that an additional, explicit tag wraps the implicit one +// optional marks the field as ASN.1 OPTIONAL +// set causes a SET, rather than a SEQUENCE type to be expected +// tag:x specifies the ASN.1 tag number; implies ASN.1 CONTEXT SPECIFIC // // If the type of the first field of a structure is RawContent then the raw // ASN1 contents of the struct will be stored in it. // +// If the type name of a slice element ends with "SET" then it's treated as if +// the "set" tag was set on it. This can be used with nested slices where a +// struct tag cannot be given. +// // Other ASN.1 types are not supported; if it encounters them, // Unmarshal returns a parse error. func Unmarshal(b []byte, val interface{}) (rest []byte, err error) { diff --git a/vendor/github.com/google/certificate-transparency/go/asn1/common.go b/vendor/github.com/google/certificate-transparency-go/asn1/common.go old mode 100755 new mode 100644 similarity index 68% rename from vendor/github.com/google/certificate-transparency/go/asn1/common.go rename to vendor/github.com/google/certificate-transparency-go/asn1/common.go index 33a117ece1954..3c40856bec3ab --- a/vendor/github.com/google/certificate-transparency/go/asn1/common.go +++ b/vendor/github.com/google/certificate-transparency-go/asn1/common.go @@ -18,29 +18,33 @@ import ( // Here are some standard tags and classes +// ASN.1 tags represent the type of the following object. const ( - tagBoolean = 1 - tagInteger = 2 - tagBitString = 3 - tagOctetString = 4 - tagOID = 6 - tagEnum = 10 - tagUTF8String = 12 - tagSequence = 16 - tagSet = 17 - tagPrintableString = 19 - tagT61String = 20 - tagIA5String = 22 - tagUTCTime = 23 - tagGeneralizedTime = 24 - tagGeneralString = 27 + TagBoolean = 1 + TagInteger = 2 + TagBitString = 3 + TagOctetString = 4 + TagNull = 5 + TagOID = 6 + TagEnum = 10 + TagUTF8String = 12 + TagSequence = 16 + TagSet = 17 + TagNumericString = 18 + TagPrintableString = 19 + TagT61String = 20 + TagIA5String = 22 + TagUTCTime = 23 + TagGeneralizedTime = 24 + TagGeneralString = 27 ) +// ASN.1 class types represent the namespace of the tag. const ( - classUniversal = 0 - classApplication = 1 - classContextSpecific = 2 - classPrivate = 3 + ClassUniversal = 0 + ClassApplication = 1 + ClassContextSpecific = 2 + ClassPrivate = 3 ) type tagAndLength struct { @@ -74,8 +78,10 @@ type fieldParameters struct { defaultValue *int64 // a default value for INTEGER typed fields (maybe nil). tag *int // the EXPLICIT or IMPLICIT tag (maybe nil). stringType int // the string tag to use when marshaling. + timeType int // the time tag to use when marshaling. set bool // true iff this should be encoded as a SET omitEmpty bool // true iff this should be omitted if empty when marshaling. + name string // name of field for better diagnostics // Invariants: // if explicit is set, tag is non-nil. @@ -94,12 +100,18 @@ func parseFieldParameters(str string) (ret fieldParameters) { if ret.tag == nil { ret.tag = new(int) } + case part == "generalized": + ret.timeType = TagGeneralizedTime + case part == "utc": + ret.timeType = TagUTCTime case part == "ia5": - ret.stringType = tagIA5String + ret.stringType = TagIA5String case part == "printable": - ret.stringType = tagPrintableString + ret.stringType = TagPrintableString + case part == "numeric": + ret.stringType = TagNumericString case part == "utf8": - ret.stringType = tagUTF8String + ret.stringType = TagUTF8String case strings.HasPrefix(part, "default:"): i, err := strconv.ParseInt(part[8:], 10, 64) if err == nil { @@ -128,36 +140,38 @@ func parseFieldParameters(str string) (ret fieldParameters) { // Given a reflected Go type, getUniversalType returns the default tag number // and expected compound flag. -func getUniversalType(t reflect.Type) (tagNumber int, isCompound, ok bool) { +func getUniversalType(t reflect.Type) (matchAny bool, tagNumber int, isCompound, ok bool) { switch t { + case rawValueType: + return true, -1, false, true case objectIdentifierType: - return tagOID, false, true + return false, TagOID, false, true case bitStringType: - return tagBitString, false, true + return false, TagBitString, false, true case timeType: - return tagUTCTime, false, true + return false, TagUTCTime, false, true case enumeratedType: - return tagEnum, false, true + return false, TagEnum, false, true case bigIntType: - return tagInteger, false, true + return false, TagInteger, false, true } switch t.Kind() { case reflect.Bool: - return tagBoolean, false, true + return false, TagBoolean, false, true case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return tagInteger, false, true + return false, TagInteger, false, true case reflect.Struct: - return tagSequence, true, true + return false, TagSequence, true, true case reflect.Slice: if t.Elem().Kind() == reflect.Uint8 { - return tagOctetString, false, true + return false, TagOctetString, false, true } if strings.HasSuffix(t.Name(), "SET") { - return tagSet, true, true + return false, TagSet, true, true } - return tagSequence, true, true + return false, TagSequence, true, true case reflect.String: - return tagPrintableString, false, true + return false, TagPrintableString, false, true } - return 0, false, false + return false, 0, false, false } diff --git a/vendor/github.com/google/certificate-transparency-go/asn1/marshal.go b/vendor/github.com/google/certificate-transparency-go/asn1/marshal.go new file mode 100644 index 0000000000000..22591282f655f --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/asn1/marshal.go @@ -0,0 +1,689 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package asn1 + +import ( + "errors" + "fmt" + "math/big" + "reflect" + "time" + "unicode/utf8" +) + +var ( + byte00Encoder encoder = byteEncoder(0x00) + byteFFEncoder encoder = byteEncoder(0xff) +) + +// encoder represents an ASN.1 element that is waiting to be marshaled. +type encoder interface { + // Len returns the number of bytes needed to marshal this element. + Len() int + // Encode encodes this element by writing Len() bytes to dst. + Encode(dst []byte) +} + +type byteEncoder byte + +func (c byteEncoder) Len() int { + return 1 +} + +func (c byteEncoder) Encode(dst []byte) { + dst[0] = byte(c) +} + +type bytesEncoder []byte + +func (b bytesEncoder) Len() int { + return len(b) +} + +func (b bytesEncoder) Encode(dst []byte) { + if copy(dst, b) != len(b) { + panic("internal error") + } +} + +type stringEncoder string + +func (s stringEncoder) Len() int { + return len(s) +} + +func (s stringEncoder) Encode(dst []byte) { + if copy(dst, s) != len(s) { + panic("internal error") + } +} + +type multiEncoder []encoder + +func (m multiEncoder) Len() int { + var size int + for _, e := range m { + size += e.Len() + } + return size +} + +func (m multiEncoder) Encode(dst []byte) { + var off int + for _, e := range m { + e.Encode(dst[off:]) + off += e.Len() + } +} + +type taggedEncoder struct { + // scratch contains temporary space for encoding the tag and length of + // an element in order to avoid extra allocations. + scratch [8]byte + tag encoder + body encoder +} + +func (t *taggedEncoder) Len() int { + return t.tag.Len() + t.body.Len() +} + +func (t *taggedEncoder) Encode(dst []byte) { + t.tag.Encode(dst) + t.body.Encode(dst[t.tag.Len():]) +} + +type int64Encoder int64 + +func (i int64Encoder) Len() int { + n := 1 + + for i > 127 { + n++ + i >>= 8 + } + + for i < -128 { + n++ + i >>= 8 + } + + return n +} + +func (i int64Encoder) Encode(dst []byte) { + n := i.Len() + + for j := 0; j < n; j++ { + dst[j] = byte(i >> uint((n-1-j)*8)) + } +} + +func base128IntLength(n int64) int { + if n == 0 { + return 1 + } + + l := 0 + for i := n; i > 0; i >>= 7 { + l++ + } + + return l +} + +func appendBase128Int(dst []byte, n int64) []byte { + l := base128IntLength(n) + + for i := l - 1; i >= 0; i-- { + o := byte(n >> uint(i*7)) + o &= 0x7f + if i != 0 { + o |= 0x80 + } + + dst = append(dst, o) + } + + return dst +} + +func makeBigInt(n *big.Int, fieldName string) (encoder, error) { + if n == nil { + return nil, StructuralError{"empty integer", fieldName} + } + + if n.Sign() < 0 { + // A negative number has to be converted to two's-complement + // form. So we'll invert and subtract 1. If the + // most-significant-bit isn't set then we'll need to pad the + // beginning with 0xff in order to keep the number negative. + nMinus1 := new(big.Int).Neg(n) + nMinus1.Sub(nMinus1, bigOne) + bytes := nMinus1.Bytes() + for i := range bytes { + bytes[i] ^= 0xff + } + if len(bytes) == 0 || bytes[0]&0x80 == 0 { + return multiEncoder([]encoder{byteFFEncoder, bytesEncoder(bytes)}), nil + } + return bytesEncoder(bytes), nil + } else if n.Sign() == 0 { + // Zero is written as a single 0 zero rather than no bytes. + return byte00Encoder, nil + } else { + bytes := n.Bytes() + if len(bytes) > 0 && bytes[0]&0x80 != 0 { + // We'll have to pad this with 0x00 in order to stop it + // looking like a negative number. + return multiEncoder([]encoder{byte00Encoder, bytesEncoder(bytes)}), nil + } + return bytesEncoder(bytes), nil + } +} + +func appendLength(dst []byte, i int) []byte { + n := lengthLength(i) + + for ; n > 0; n-- { + dst = append(dst, byte(i>>uint((n-1)*8))) + } + + return dst +} + +func lengthLength(i int) (numBytes int) { + numBytes = 1 + for i > 255 { + numBytes++ + i >>= 8 + } + return +} + +func appendTagAndLength(dst []byte, t tagAndLength) []byte { + b := uint8(t.class) << 6 + if t.isCompound { + b |= 0x20 + } + if t.tag >= 31 { + b |= 0x1f + dst = append(dst, b) + dst = appendBase128Int(dst, int64(t.tag)) + } else { + b |= uint8(t.tag) + dst = append(dst, b) + } + + if t.length >= 128 { + l := lengthLength(t.length) + dst = append(dst, 0x80|byte(l)) + dst = appendLength(dst, t.length) + } else { + dst = append(dst, byte(t.length)) + } + + return dst +} + +type bitStringEncoder BitString + +func (b bitStringEncoder) Len() int { + return len(b.Bytes) + 1 +} + +func (b bitStringEncoder) Encode(dst []byte) { + dst[0] = byte((8 - b.BitLength%8) % 8) + if copy(dst[1:], b.Bytes) != len(b.Bytes) { + panic("internal error") + } +} + +type oidEncoder []int + +func (oid oidEncoder) Len() int { + l := base128IntLength(int64(oid[0]*40 + oid[1])) + for i := 2; i < len(oid); i++ { + l += base128IntLength(int64(oid[i])) + } + return l +} + +func (oid oidEncoder) Encode(dst []byte) { + dst = appendBase128Int(dst[:0], int64(oid[0]*40+oid[1])) + for i := 2; i < len(oid); i++ { + dst = appendBase128Int(dst, int64(oid[i])) + } +} + +func makeObjectIdentifier(oid []int, fieldName string) (e encoder, err error) { + if len(oid) < 2 || oid[0] > 2 || (oid[0] < 2 && oid[1] >= 40) { + return nil, StructuralError{"invalid object identifier", fieldName} + } + + return oidEncoder(oid), nil +} + +func makePrintableString(s, fieldName string) (e encoder, err error) { + for i := 0; i < len(s); i++ { + // The asterisk is often used in PrintableString, even though + // it is invalid. If a PrintableString was specifically + // requested then the asterisk is permitted by this code. + // Ampersand is allowed in parsing due a handful of CA + // certificates, however when making new certificates + // it is rejected. + if !isPrintable(s[i], allowAsterisk, rejectAmpersand) { + return nil, StructuralError{"PrintableString contains invalid character", fieldName} + } + } + + return stringEncoder(s), nil +} + +func makeIA5String(s, fieldName string) (e encoder, err error) { + for i := 0; i < len(s); i++ { + if s[i] > 127 { + return nil, StructuralError{"IA5String contains invalid character", fieldName} + } + } + + return stringEncoder(s), nil +} + +func makeNumericString(s string, fieldName string) (e encoder, err error) { + for i := 0; i < len(s); i++ { + if !isNumeric(s[i]) { + return nil, StructuralError{"NumericString contains invalid character", fieldName} + } + } + + return stringEncoder(s), nil +} + +func makeUTF8String(s string) encoder { + return stringEncoder(s) +} + +func appendTwoDigits(dst []byte, v int) []byte { + return append(dst, byte('0'+(v/10)%10), byte('0'+v%10)) +} + +func appendFourDigits(dst []byte, v int) []byte { + var bytes [4]byte + for i := range bytes { + bytes[3-i] = '0' + byte(v%10) + v /= 10 + } + return append(dst, bytes[:]...) +} + +func outsideUTCRange(t time.Time) bool { + year := t.Year() + return year < 1950 || year >= 2050 +} + +func makeUTCTime(t time.Time, fieldName string) (e encoder, err error) { + dst := make([]byte, 0, 18) + + dst, err = appendUTCTime(dst, t, fieldName) + if err != nil { + return nil, err + } + + return bytesEncoder(dst), nil +} + +func makeGeneralizedTime(t time.Time, fieldName string) (e encoder, err error) { + dst := make([]byte, 0, 20) + + dst, err = appendGeneralizedTime(dst, t, fieldName) + if err != nil { + return nil, err + } + + return bytesEncoder(dst), nil +} + +func appendUTCTime(dst []byte, t time.Time, fieldName string) (ret []byte, err error) { + year := t.Year() + + switch { + case 1950 <= year && year < 2000: + dst = appendTwoDigits(dst, year-1900) + case 2000 <= year && year < 2050: + dst = appendTwoDigits(dst, year-2000) + default: + return nil, StructuralError{"cannot represent time as UTCTime", fieldName} + } + + return appendTimeCommon(dst, t), nil +} + +func appendGeneralizedTime(dst []byte, t time.Time, fieldName string) (ret []byte, err error) { + year := t.Year() + if year < 0 || year > 9999 { + return nil, StructuralError{"cannot represent time as GeneralizedTime", fieldName} + } + + dst = appendFourDigits(dst, year) + + return appendTimeCommon(dst, t), nil +} + +func appendTimeCommon(dst []byte, t time.Time) []byte { + _, month, day := t.Date() + + dst = appendTwoDigits(dst, int(month)) + dst = appendTwoDigits(dst, day) + + hour, min, sec := t.Clock() + + dst = appendTwoDigits(dst, hour) + dst = appendTwoDigits(dst, min) + dst = appendTwoDigits(dst, sec) + + _, offset := t.Zone() + + switch { + case offset/60 == 0: + return append(dst, 'Z') + case offset > 0: + dst = append(dst, '+') + case offset < 0: + dst = append(dst, '-') + } + + offsetMinutes := offset / 60 + if offsetMinutes < 0 { + offsetMinutes = -offsetMinutes + } + + dst = appendTwoDigits(dst, offsetMinutes/60) + dst = appendTwoDigits(dst, offsetMinutes%60) + + return dst +} + +func stripTagAndLength(in []byte) []byte { + _, offset, err := parseTagAndLength(in, 0, "") + if err != nil { + return in + } + return in[offset:] +} + +func makeBody(value reflect.Value, params fieldParameters) (e encoder, err error) { + switch value.Type() { + case flagType: + return bytesEncoder(nil), nil + case timeType: + t := value.Interface().(time.Time) + if params.timeType == TagGeneralizedTime || outsideUTCRange(t) { + return makeGeneralizedTime(t, params.name) + } + return makeUTCTime(t, params.name) + case bitStringType: + return bitStringEncoder(value.Interface().(BitString)), nil + case objectIdentifierType: + return makeObjectIdentifier(value.Interface().(ObjectIdentifier), params.name) + case bigIntType: + return makeBigInt(value.Interface().(*big.Int), params.name) + } + + switch v := value; v.Kind() { + case reflect.Bool: + if v.Bool() { + return byteFFEncoder, nil + } + return byte00Encoder, nil + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return int64Encoder(v.Int()), nil + case reflect.Struct: + t := v.Type() + + for i := 0; i < t.NumField(); i++ { + if t.Field(i).PkgPath != "" { + return nil, StructuralError{"struct contains unexported fields", t.Field(i).Name} + } + } + + startingField := 0 + + n := t.NumField() + if n == 0 { + return bytesEncoder(nil), nil + } + + // If the first element of the structure is a non-empty + // RawContents, then we don't bother serializing the rest. + if t.Field(0).Type == rawContentsType { + s := v.Field(0) + if s.Len() > 0 { + bytes := s.Bytes() + /* The RawContents will contain the tag and + * length fields but we'll also be writing + * those ourselves, so we strip them out of + * bytes */ + return bytesEncoder(stripTagAndLength(bytes)), nil + } + + startingField = 1 + } + + switch n1 := n - startingField; n1 { + case 0: + return bytesEncoder(nil), nil + case 1: + return makeField(v.Field(startingField), parseFieldParameters(t.Field(startingField).Tag.Get("asn1"))) + default: + m := make([]encoder, n1) + for i := 0; i < n1; i++ { + m[i], err = makeField(v.Field(i+startingField), parseFieldParameters(t.Field(i+startingField).Tag.Get("asn1"))) + if err != nil { + return nil, err + } + } + + return multiEncoder(m), nil + } + case reflect.Slice: + sliceType := v.Type() + if sliceType.Elem().Kind() == reflect.Uint8 { + return bytesEncoder(v.Bytes()), nil + } + + var fp fieldParameters + + switch l := v.Len(); l { + case 0: + return bytesEncoder(nil), nil + case 1: + return makeField(v.Index(0), fp) + default: + m := make([]encoder, l) + + for i := 0; i < l; i++ { + m[i], err = makeField(v.Index(i), fp) + if err != nil { + return nil, err + } + } + + return multiEncoder(m), nil + } + case reflect.String: + switch params.stringType { + case TagIA5String: + return makeIA5String(v.String(), params.name) + case TagPrintableString: + return makePrintableString(v.String(), params.name) + case TagNumericString: + return makeNumericString(v.String(), params.name) + default: + return makeUTF8String(v.String()), nil + } + } + + return nil, StructuralError{"unknown Go type", params.name} +} + +func makeField(v reflect.Value, params fieldParameters) (e encoder, err error) { + if !v.IsValid() { + return nil, fmt.Errorf("asn1: cannot marshal nil value") + } + // If the field is an interface{} then recurse into it. + if v.Kind() == reflect.Interface && v.Type().NumMethod() == 0 { + return makeField(v.Elem(), params) + } + + if v.Kind() == reflect.Slice && v.Len() == 0 && params.omitEmpty { + return bytesEncoder(nil), nil + } + + if params.optional && params.defaultValue != nil && canHaveDefaultValue(v.Kind()) { + defaultValue := reflect.New(v.Type()).Elem() + defaultValue.SetInt(*params.defaultValue) + + if reflect.DeepEqual(v.Interface(), defaultValue.Interface()) { + return bytesEncoder(nil), nil + } + } + + // If no default value is given then the zero value for the type is + // assumed to be the default value. This isn't obviously the correct + // behavior, but it's what Go has traditionally done. + if params.optional && params.defaultValue == nil { + if reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) { + return bytesEncoder(nil), nil + } + } + + if v.Type() == rawValueType { + rv := v.Interface().(RawValue) + if len(rv.FullBytes) != 0 { + return bytesEncoder(rv.FullBytes), nil + } + + t := new(taggedEncoder) + + t.tag = bytesEncoder(appendTagAndLength(t.scratch[:0], tagAndLength{rv.Class, rv.Tag, len(rv.Bytes), rv.IsCompound})) + t.body = bytesEncoder(rv.Bytes) + + return t, nil + } + + matchAny, tag, isCompound, ok := getUniversalType(v.Type()) + if !ok || matchAny { + return nil, StructuralError{fmt.Sprintf("unknown Go type: %v", v.Type()), params.name} + } + + if params.timeType != 0 && tag != TagUTCTime { + return nil, StructuralError{"explicit time type given to non-time member", params.name} + } + + if params.stringType != 0 && tag != TagPrintableString { + return nil, StructuralError{"explicit string type given to non-string member", params.name} + } + + switch tag { + case TagPrintableString: + if params.stringType == 0 { + // This is a string without an explicit string type. We'll use + // a PrintableString if the character set in the string is + // sufficiently limited, otherwise we'll use a UTF8String. + for _, r := range v.String() { + if r >= utf8.RuneSelf || !isPrintable(byte(r), rejectAsterisk, rejectAmpersand) { + if !utf8.ValidString(v.String()) { + return nil, errors.New("asn1: string not valid UTF-8") + } + tag = TagUTF8String + break + } + } + } else { + tag = params.stringType + } + case TagUTCTime: + if params.timeType == TagGeneralizedTime || outsideUTCRange(v.Interface().(time.Time)) { + tag = TagGeneralizedTime + } + } + + if params.set { + if tag != TagSequence { + return nil, StructuralError{"non sequence tagged as set", params.name} + } + tag = TagSet + } + + t := new(taggedEncoder) + + t.body, err = makeBody(v, params) + if err != nil { + return nil, err + } + + bodyLen := t.body.Len() + + class := ClassUniversal + if params.tag != nil { + if params.application { + class = ClassApplication + } else { + class = ClassContextSpecific + } + + if params.explicit { + t.tag = bytesEncoder(appendTagAndLength(t.scratch[:0], tagAndLength{ClassUniversal, tag, bodyLen, isCompound})) + + tt := new(taggedEncoder) + + tt.body = t + + tt.tag = bytesEncoder(appendTagAndLength(tt.scratch[:0], tagAndLength{ + class: class, + tag: *params.tag, + length: bodyLen + t.tag.Len(), + isCompound: true, + })) + + return tt, nil + } + + // implicit tag. + tag = *params.tag + } + + t.tag = bytesEncoder(appendTagAndLength(t.scratch[:0], tagAndLength{class, tag, bodyLen, isCompound})) + + return t, nil +} + +// Marshal returns the ASN.1 encoding of val. +// +// In addition to the struct tags recognised by Unmarshal, the following can be +// used: +// +// ia5: causes strings to be marshaled as ASN.1, IA5String values +// omitempty: causes empty slices to be skipped +// printable: causes strings to be marshaled as ASN.1, PrintableString values +// utf8: causes strings to be marshaled as ASN.1, UTF8String values +// utc: causes time.Time to be marshaled as ASN.1, UTCTime values +// generalized: causes time.Time to be marshaled as ASN.1, GeneralizedTime values +func Marshal(val interface{}) ([]byte, error) { + return MarshalWithParams(val, "") +} + +// MarshalWithParams allows field parameters to be specified for the +// top-level element. The form of the params is the same as the field tags. +func MarshalWithParams(val interface{}, params string) ([]byte, error) { + e, err := makeField(reflect.ValueOf(val), parseFieldParameters(params)) + if err != nil { + return nil, err + } + b := make([]byte, e.Len()) + e.Encode(b) + return b, nil +} diff --git a/vendor/github.com/google/certificate-transparency-go/client/configpb/gen.go b/vendor/github.com/google/certificate-transparency-go/client/configpb/gen.go new file mode 100644 index 0000000000000..1d0c9a7ffddec --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/client/configpb/gen.go @@ -0,0 +1,17 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// 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. + +package configpb + +//go:generate protoc -I=. -I=$GOPATH/src --go_out=:. multilog.proto diff --git a/vendor/github.com/google/certificate-transparency-go/client/configpb/multilog.pb.go b/vendor/github.com/google/certificate-transparency-go/client/configpb/multilog.pb.go new file mode 100644 index 0000000000000..2e55408452f3d --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/client/configpb/multilog.pb.go @@ -0,0 +1,158 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: multilog.proto + +package configpb + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" +import timestamp "github.com/golang/protobuf/ptypes/timestamp" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +// TemporalLogConfig is a set of LogShardConfig messages, whose +// time limits should be contiguous. +type TemporalLogConfig struct { + Shard []*LogShardConfig `protobuf:"bytes,1,rep,name=shard,proto3" json:"shard,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TemporalLogConfig) Reset() { *m = TemporalLogConfig{} } +func (m *TemporalLogConfig) String() string { return proto.CompactTextString(m) } +func (*TemporalLogConfig) ProtoMessage() {} +func (*TemporalLogConfig) Descriptor() ([]byte, []int) { + return fileDescriptor_multilog_3c9b797b88da6f07, []int{0} +} +func (m *TemporalLogConfig) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_TemporalLogConfig.Unmarshal(m, b) +} +func (m *TemporalLogConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_TemporalLogConfig.Marshal(b, m, deterministic) +} +func (dst *TemporalLogConfig) XXX_Merge(src proto.Message) { + xxx_messageInfo_TemporalLogConfig.Merge(dst, src) +} +func (m *TemporalLogConfig) XXX_Size() int { + return xxx_messageInfo_TemporalLogConfig.Size(m) +} +func (m *TemporalLogConfig) XXX_DiscardUnknown() { + xxx_messageInfo_TemporalLogConfig.DiscardUnknown(m) +} + +var xxx_messageInfo_TemporalLogConfig proto.InternalMessageInfo + +func (m *TemporalLogConfig) GetShard() []*LogShardConfig { + if m != nil { + return m.Shard + } + return nil +} + +// LogShardConfig describes the acceptable date range for a single shard of a temporal +// log. +type LogShardConfig struct { + Uri string `protobuf:"bytes,1,opt,name=uri,proto3" json:"uri,omitempty"` + // The log's public key in DER-encoded PKIX form. + PublicKeyDer []byte `protobuf:"bytes,2,opt,name=public_key_der,json=publicKeyDer,proto3" json:"public_key_der,omitempty"` + // not_after_start defines the start of the range of acceptable NotAfter + // values, inclusive. + // Leaving this unset implies no lower bound to the range. + NotAfterStart *timestamp.Timestamp `protobuf:"bytes,3,opt,name=not_after_start,json=notAfterStart,proto3" json:"not_after_start,omitempty"` + // not_after_limit defines the end of the range of acceptable NotAfter values, + // exclusive. + // Leaving this unset implies no upper bound to the range. + NotAfterLimit *timestamp.Timestamp `protobuf:"bytes,4,opt,name=not_after_limit,json=notAfterLimit,proto3" json:"not_after_limit,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *LogShardConfig) Reset() { *m = LogShardConfig{} } +func (m *LogShardConfig) String() string { return proto.CompactTextString(m) } +func (*LogShardConfig) ProtoMessage() {} +func (*LogShardConfig) Descriptor() ([]byte, []int) { + return fileDescriptor_multilog_3c9b797b88da6f07, []int{1} +} +func (m *LogShardConfig) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_LogShardConfig.Unmarshal(m, b) +} +func (m *LogShardConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_LogShardConfig.Marshal(b, m, deterministic) +} +func (dst *LogShardConfig) XXX_Merge(src proto.Message) { + xxx_messageInfo_LogShardConfig.Merge(dst, src) +} +func (m *LogShardConfig) XXX_Size() int { + return xxx_messageInfo_LogShardConfig.Size(m) +} +func (m *LogShardConfig) XXX_DiscardUnknown() { + xxx_messageInfo_LogShardConfig.DiscardUnknown(m) +} + +var xxx_messageInfo_LogShardConfig proto.InternalMessageInfo + +func (m *LogShardConfig) GetUri() string { + if m != nil { + return m.Uri + } + return "" +} + +func (m *LogShardConfig) GetPublicKeyDer() []byte { + if m != nil { + return m.PublicKeyDer + } + return nil +} + +func (m *LogShardConfig) GetNotAfterStart() *timestamp.Timestamp { + if m != nil { + return m.NotAfterStart + } + return nil +} + +func (m *LogShardConfig) GetNotAfterLimit() *timestamp.Timestamp { + if m != nil { + return m.NotAfterLimit + } + return nil +} + +func init() { + proto.RegisterType((*TemporalLogConfig)(nil), "configpb.TemporalLogConfig") + proto.RegisterType((*LogShardConfig)(nil), "configpb.LogShardConfig") +} + +func init() { proto.RegisterFile("multilog.proto", fileDescriptor_multilog_3c9b797b88da6f07) } + +var fileDescriptor_multilog_3c9b797b88da6f07 = []byte{ + // 241 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x8f, 0xb1, 0x4e, 0xc3, 0x30, + 0x14, 0x45, 0x65, 0x02, 0x08, 0xdc, 0x12, 0xc0, 0x93, 0xd5, 0x85, 0xa8, 0x62, 0xc8, 0xe4, 0x4a, + 0xe5, 0x0b, 0xa0, 0x6c, 0x64, 0x4a, 0xbb, 0x47, 0x4e, 0xeb, 0x18, 0x0b, 0x3b, 0xcf, 0x72, 0x5e, + 0x86, 0xfe, 0x25, 0x9f, 0x84, 0x1c, 0x2b, 0x43, 0x37, 0xb6, 0xa7, 0x77, 0xcf, 0xb9, 0xd2, 0xa5, + 0xb9, 0x1b, 0x2d, 0x1a, 0x0b, 0x5a, 0xf8, 0x00, 0x08, 0xec, 0xee, 0x08, 0x7d, 0x67, 0xb4, 0x6f, + 0x57, 0x2f, 0x1a, 0x40, 0x5b, 0xb5, 0x99, 0xfe, 0xed, 0xd8, 0x6d, 0xd0, 0x38, 0x35, 0xa0, 0x74, + 0x3e, 0xa1, 0xeb, 0x1d, 0x7d, 0x3e, 0x28, 0xe7, 0x21, 0x48, 0x5b, 0x81, 0xde, 0x4d, 0x1e, 0x13, + 0xf4, 0x66, 0xf8, 0x96, 0xe1, 0xc4, 0x49, 0x91, 0x95, 0x8b, 0x2d, 0x17, 0x73, 0x9f, 0xa8, 0x40, + 0xef, 0x63, 0x92, 0xc0, 0x3a, 0x61, 0xeb, 0x5f, 0x42, 0xf3, 0xcb, 0x84, 0x3d, 0xd1, 0x6c, 0x0c, + 0x86, 0x93, 0x82, 0x94, 0xf7, 0x75, 0x3c, 0xd9, 0x2b, 0xcd, 0xfd, 0xd8, 0x5a, 0x73, 0x6c, 0x7e, + 0xd4, 0xb9, 0x39, 0xa9, 0xc0, 0xaf, 0x0a, 0x52, 0x2e, 0xeb, 0x65, 0xfa, 0x7e, 0xa9, 0xf3, 0xa7, + 0x0a, 0xec, 0x83, 0x3e, 0xf6, 0x80, 0x8d, 0xec, 0x50, 0x85, 0x66, 0x40, 0x19, 0x90, 0x67, 0x05, + 0x29, 0x17, 0xdb, 0x95, 0x48, 0x53, 0xc4, 0x3c, 0x45, 0x1c, 0xe6, 0x29, 0xf5, 0x43, 0x0f, 0xf8, + 0x1e, 0x8d, 0x7d, 0x14, 0x2e, 0x3b, 0xac, 0x71, 0x06, 0xf9, 0xf5, 0xff, 0x3b, 0xaa, 0x28, 0xb4, + 0xb7, 0x13, 0xf2, 0xf6, 0x17, 0x00, 0x00, 0xff, 0xff, 0xf8, 0xd9, 0x50, 0x5b, 0x5b, 0x01, 0x00, + 0x00, +} diff --git a/vendor/github.com/google/certificate-transparency-go/client/configpb/multilog.proto b/vendor/github.com/google/certificate-transparency-go/client/configpb/multilog.proto new file mode 100644 index 0000000000000..b396a90a9c582 --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/client/configpb/multilog.proto @@ -0,0 +1,43 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// 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. + +syntax = "proto3"; + +package configpb; + +import "google/protobuf/timestamp.proto"; + +// TemporalLogConfig is a set of LogShardConfig messages, whose +// time limits should be contiguous. +message TemporalLogConfig { + repeated LogShardConfig shard = 1; +} + +// LogShardConfig describes the acceptable date range for a single shard of a temporal +// log. +message LogShardConfig { + string uri = 1; + + // The log's public key in DER-encoded PKIX form. + bytes public_key_der = 2; + + // not_after_start defines the start of the range of acceptable NotAfter + // values, inclusive. + // Leaving this unset implies no lower bound to the range. + google.protobuf.Timestamp not_after_start = 3; + // not_after_limit defines the end of the range of acceptable NotAfter values, + // exclusive. + // Leaving this unset implies no upper bound to the range. + google.protobuf.Timestamp not_after_limit = 4; +} diff --git a/vendor/github.com/google/certificate-transparency-go/client/getentries.go b/vendor/github.com/google/certificate-transparency-go/client/getentries.go new file mode 100644 index 0000000000000..e2cde55c22a5c --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/client/getentries.go @@ -0,0 +1,75 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// 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. + +package client + +import ( + "context" + "errors" + "strconv" + + ct "github.com/google/certificate-transparency-go" + "github.com/google/certificate-transparency-go/x509" +) + +// GetRawEntries exposes the /ct/v1/get-entries result with only the JSON parsing done. +func (c *LogClient) GetRawEntries(ctx context.Context, start, end int64) (*ct.GetEntriesResponse, error) { + if end < 0 { + return nil, errors.New("end should be >= 0") + } + if end < start { + return nil, errors.New("start should be <= end") + } + + params := map[string]string{ + "start": strconv.FormatInt(start, 10), + "end": strconv.FormatInt(end, 10), + } + if ctx == nil { + ctx = context.TODO() + } + + var resp ct.GetEntriesResponse + httpRsp, body, err := c.GetAndParse(ctx, ct.GetEntriesPath, params, &resp) + if err != nil { + if httpRsp != nil { + return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body} + } + return nil, err + } + + return &resp, nil +} + +// GetEntries attempts to retrieve the entries in the sequence [start, end] from the CT log server +// (RFC6962 s4.6) as parsed [pre-]certificates for convenience, held in a slice of ct.LogEntry structures. +// However, this does mean that any certificate parsing failures will cause a failure of the whole +// retrieval operation; for more robust retrieval of parsed certificates, use GetRawEntries() and invoke +// ct.LogEntryFromLeaf() on each individual entry. +func (c *LogClient) GetEntries(ctx context.Context, start, end int64) ([]ct.LogEntry, error) { + resp, err := c.GetRawEntries(ctx, start, end) + if err != nil { + return nil, err + } + entries := make([]ct.LogEntry, len(resp.Entries)) + for i, entry := range resp.Entries { + index := start + int64(i) + logEntry, err := ct.LogEntryFromLeaf(index, &entry) + if _, ok := err.(x509.NonFatalErrors); !ok && err != nil { + return nil, err + } + entries[i] = *logEntry + } + return entries, nil +} diff --git a/vendor/github.com/google/certificate-transparency-go/client/logclient.go b/vendor/github.com/google/certificate-transparency-go/client/logclient.go new file mode 100644 index 0000000000000..a79ef3083cf42 --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/client/logclient.go @@ -0,0 +1,289 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// 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. + +// Package client is a CT log client implementation and contains types and code +// for interacting with RFC6962-compliant CT Log instances. +// See http://tools.ietf.org/html/rfc6962 for details +package client + +import ( + "context" + "encoding/base64" + "fmt" + "net/http" + "strconv" + + ct "github.com/google/certificate-transparency-go" + "github.com/google/certificate-transparency-go/jsonclient" + "github.com/google/certificate-transparency-go/tls" +) + +// LogClient represents a client for a given CT Log instance +type LogClient struct { + jsonclient.JSONClient +} + +// CheckLogClient is an interface that allows (just) checking of various log contents. +type CheckLogClient interface { + BaseURI() string + GetSTH(context.Context) (*ct.SignedTreeHead, error) + GetSTHConsistency(ctx context.Context, first, second uint64) ([][]byte, error) + GetProofByHash(ctx context.Context, hash []byte, treeSize uint64) (*ct.GetProofByHashResponse, error) +} + +// New constructs a new LogClient instance. +// |uri| is the base URI of the CT log instance to interact with, e.g. +// https://ct.googleapis.com/pilot +// |hc| is the underlying client to be used for HTTP requests to the CT log. +// |opts| can be used to provide a custom logger interface and a public key +// for signature verification. +func New(uri string, hc *http.Client, opts jsonclient.Options) (*LogClient, error) { + logClient, err := jsonclient.New(uri, hc, opts) + if err != nil { + return nil, err + } + return &LogClient{*logClient}, err +} + +// RspError represents an error that occurred when processing a response from a server, +// and also includes key details from the http.Response that triggered the error. +type RspError struct { + Err error + StatusCode int + Body []byte +} + +// Error formats the RspError instance, focusing on the error. +func (e RspError) Error() string { + return e.Err.Error() +} + +// Attempts to add |chain| to the log, using the api end-point specified by +// |path|. If provided context expires before submission is complete an +// error will be returned. +func (c *LogClient) addChainWithRetry(ctx context.Context, ctype ct.LogEntryType, path string, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) { + var resp ct.AddChainResponse + var req ct.AddChainRequest + for _, link := range chain { + req.Chain = append(req.Chain, link.Data) + } + + httpRsp, body, err := c.PostAndParseWithRetry(ctx, path, &req, &resp) + if err != nil { + if httpRsp != nil { + return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body} + } + return nil, err + } + + var ds ct.DigitallySigned + if rest, err := tls.Unmarshal(resp.Signature, &ds); err != nil { + return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body} + } else if len(rest) > 0 { + return nil, RspError{ + Err: fmt.Errorf("trailing data (%d bytes) after DigitallySigned", len(rest)), + StatusCode: httpRsp.StatusCode, + Body: body, + } + } + + exts, err := base64.StdEncoding.DecodeString(resp.Extensions) + if err != nil { + return nil, RspError{ + Err: fmt.Errorf("invalid base64 data in Extensions (%q): %v", resp.Extensions, err), + StatusCode: httpRsp.StatusCode, + Body: body, + } + } + + var logID ct.LogID + copy(logID.KeyID[:], resp.ID) + sct := &ct.SignedCertificateTimestamp{ + SCTVersion: resp.SCTVersion, + LogID: logID, + Timestamp: resp.Timestamp, + Extensions: ct.CTExtensions(exts), + Signature: ds, + } + if err := c.VerifySCTSignature(*sct, ctype, chain); err != nil { + return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body} + } + return sct, nil +} + +// AddChain adds the (DER represented) X509 |chain| to the log. +func (c *LogClient) AddChain(ctx context.Context, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) { + return c.addChainWithRetry(ctx, ct.X509LogEntryType, ct.AddChainPath, chain) +} + +// AddPreChain adds the (DER represented) Precertificate |chain| to the log. +func (c *LogClient) AddPreChain(ctx context.Context, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) { + return c.addChainWithRetry(ctx, ct.PrecertLogEntryType, ct.AddPreChainPath, chain) +} + +// AddJSON submits arbitrary data to to XJSON server. +func (c *LogClient) AddJSON(ctx context.Context, data interface{}) (*ct.SignedCertificateTimestamp, error) { + req := ct.AddJSONRequest{Data: data} + var resp ct.AddChainResponse + httpRsp, body, err := c.PostAndParse(ctx, ct.AddJSONPath, &req, &resp) + if err != nil { + if httpRsp != nil { + return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body} + } + return nil, err + } + var ds ct.DigitallySigned + if rest, err := tls.Unmarshal(resp.Signature, &ds); err != nil { + return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body} + } else if len(rest) > 0 { + return nil, RspError{ + Err: fmt.Errorf("trailing data (%d bytes) after DigitallySigned", len(rest)), + StatusCode: httpRsp.StatusCode, + Body: body, + } + } + var logID ct.LogID + copy(logID.KeyID[:], resp.ID) + return &ct.SignedCertificateTimestamp{ + SCTVersion: resp.SCTVersion, + LogID: logID, + Timestamp: resp.Timestamp, + Extensions: ct.CTExtensions(resp.Extensions), + Signature: ds, + }, nil +} + +// GetSTH retrieves the current STH from the log. +// Returns a populated SignedTreeHead, or a non-nil error (which may be of type +// RspError if a raw http.Response is available). +func (c *LogClient) GetSTH(ctx context.Context) (*ct.SignedTreeHead, error) { + var resp ct.GetSTHResponse + httpRsp, body, err := c.GetAndParse(ctx, ct.GetSTHPath, nil, &resp) + if err != nil { + if httpRsp != nil { + return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body} + } + return nil, err + } + + sth, err := resp.ToSignedTreeHead() + if err != nil { + return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body} + } + + if err := c.VerifySTHSignature(*sth); err != nil { + return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body} + } + return sth, nil +} + +// VerifySTHSignature checks the signature in sth, returning any error encountered or nil if verification is +// successful. +func (c *LogClient) VerifySTHSignature(sth ct.SignedTreeHead) error { + if c.Verifier == nil { + // Can't verify signatures without a verifier + return nil + } + return c.Verifier.VerifySTHSignature(sth) +} + +// VerifySCTSignature checks the signature in sct for the given LogEntryType, with associated certificate chain. +func (c *LogClient) VerifySCTSignature(sct ct.SignedCertificateTimestamp, ctype ct.LogEntryType, certData []ct.ASN1Cert) error { + if c.Verifier == nil { + // Can't verify signatures without a verifier + return nil + } + leaf, err := ct.MerkleTreeLeafFromRawChain(certData, ctype, sct.Timestamp) + if err != nil { + return fmt.Errorf("failed to build MerkleTreeLeaf: %v", err) + } + entry := ct.LogEntry{Leaf: *leaf} + return c.Verifier.VerifySCTSignature(sct, entry) +} + +// GetSTHConsistency retrieves the consistency proof between two snapshots. +func (c *LogClient) GetSTHConsistency(ctx context.Context, first, second uint64) ([][]byte, error) { + base10 := 10 + params := map[string]string{ + "first": strconv.FormatUint(first, base10), + "second": strconv.FormatUint(second, base10), + } + var resp ct.GetSTHConsistencyResponse + httpRsp, body, err := c.GetAndParse(ctx, ct.GetSTHConsistencyPath, params, &resp) + if err != nil { + if httpRsp != nil { + return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body} + } + return nil, err + } + return resp.Consistency, nil +} + +// GetProofByHash returns an audit path for the hash of an SCT. +func (c *LogClient) GetProofByHash(ctx context.Context, hash []byte, treeSize uint64) (*ct.GetProofByHashResponse, error) { + b64Hash := base64.StdEncoding.EncodeToString(hash) + base10 := 10 + params := map[string]string{ + "tree_size": strconv.FormatUint(treeSize, base10), + "hash": b64Hash, + } + var resp ct.GetProofByHashResponse + httpRsp, body, err := c.GetAndParse(ctx, ct.GetProofByHashPath, params, &resp) + if err != nil { + if httpRsp != nil { + return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body} + } + return nil, err + } + return &resp, nil +} + +// GetAcceptedRoots retrieves the set of acceptable root certificates for a log. +func (c *LogClient) GetAcceptedRoots(ctx context.Context) ([]ct.ASN1Cert, error) { + var resp ct.GetRootsResponse + httpRsp, body, err := c.GetAndParse(ctx, ct.GetRootsPath, nil, &resp) + if err != nil { + if httpRsp != nil { + return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body} + } + return nil, err + } + var roots []ct.ASN1Cert + for _, cert64 := range resp.Certificates { + cert, err := base64.StdEncoding.DecodeString(cert64) + if err != nil { + return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body} + } + roots = append(roots, ct.ASN1Cert{Data: cert}) + } + return roots, nil +} + +// GetEntryAndProof returns a log entry and audit path for the index of a leaf. +func (c *LogClient) GetEntryAndProof(ctx context.Context, index, treeSize uint64) (*ct.GetEntryAndProofResponse, error) { + base10 := 10 + params := map[string]string{ + "leaf_index": strconv.FormatUint(index, base10), + "tree_size": strconv.FormatUint(treeSize, base10), + } + var resp ct.GetEntryAndProofResponse + httpRsp, body, err := c.GetAndParse(ctx, ct.GetEntryAndProofPath, params, &resp) + if err != nil { + if httpRsp != nil { + return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body} + } + return nil, err + } + return &resp, nil +} diff --git a/vendor/github.com/google/certificate-transparency-go/client/multilog.go b/vendor/github.com/google/certificate-transparency-go/client/multilog.go new file mode 100644 index 0000000000000..a4860b6d2050a --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/client/multilog.go @@ -0,0 +1,221 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// 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. + +package client + +import ( + "context" + "crypto/sha256" + "errors" + "fmt" + "io/ioutil" + "net/http" + "time" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + ct "github.com/google/certificate-transparency-go" + "github.com/google/certificate-transparency-go/client/configpb" + "github.com/google/certificate-transparency-go/jsonclient" + "github.com/google/certificate-transparency-go/x509" +) + +type interval struct { + lower *time.Time // nil => no lower bound + upper *time.Time // nil => no upper bound +} + +// TemporalLogConfigFromFile creates a TemporalLogConfig object from the given +// filename, which should contain text-protobuf encoded configuration data. +func TemporalLogConfigFromFile(filename string) (*configpb.TemporalLogConfig, error) { + if len(filename) == 0 { + return nil, errors.New("log config filename empty") + } + + cfgText, err := ioutil.ReadFile(filename) + if err != nil { + return nil, fmt.Errorf("failed to read log config: %v", err) + } + + var cfg configpb.TemporalLogConfig + if err := proto.UnmarshalText(string(cfgText), &cfg); err != nil { + return nil, fmt.Errorf("failed to parse log config: %v", err) + } + + if len(cfg.Shard) == 0 { + return nil, errors.New("empty log config found") + } + return &cfg, nil +} + +// AddLogClient is an interface that allows adding certificates and pre-certificates to a log. +// Both LogClient and TemporalLogClient implement this interface, which allows users to +// commonize code for adding certs to normal/temporal logs. +type AddLogClient interface { + AddChain(ctx context.Context, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) + AddPreChain(ctx context.Context, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) + GetAcceptedRoots(ctx context.Context) ([]ct.ASN1Cert, error) +} + +// TemporalLogClient allows [pre-]certificates to be uploaded to a temporal log. +type TemporalLogClient struct { + Clients []*LogClient + intervals []interval +} + +// NewTemporalLogClient builds a new client for interacting with a temporal log. +// The provided config should be contiguous and chronological. +func NewTemporalLogClient(cfg configpb.TemporalLogConfig, hc *http.Client) (*TemporalLogClient, error) { + if len(cfg.Shard) == 0 { + return nil, errors.New("empty config") + } + + overall, err := shardInterval(cfg.Shard[0]) + if err != nil { + return nil, fmt.Errorf("cfg.Shard[0] invalid: %v", err) + } + intervals := make([]interval, 0, len(cfg.Shard)) + intervals = append(intervals, overall) + for i := 1; i < len(cfg.Shard); i++ { + interval, err := shardInterval(cfg.Shard[i]) + if err != nil { + return nil, fmt.Errorf("cfg.Shard[%d] invalid: %v", i, err) + } + if overall.upper == nil { + return nil, fmt.Errorf("cfg.Shard[%d] extends an interval with no upper bound", i) + } + if interval.lower == nil { + return nil, fmt.Errorf("cfg.Shard[%d] has no lower bound but extends an interval", i) + } + if !interval.lower.Equal(*overall.upper) { + return nil, fmt.Errorf("cfg.Shard[%d] starts at %v but previous interval ended at %v", i, interval.lower, overall.upper) + } + overall.upper = interval.upper + intervals = append(intervals, interval) + } + clients := make([]*LogClient, 0, len(cfg.Shard)) + for i, shard := range cfg.Shard { + opts := jsonclient.Options{} + opts.PublicKeyDER = shard.GetPublicKeyDer() + c, err := New(shard.Uri, hc, opts) + if err != nil { + return nil, fmt.Errorf("failed to create client for cfg.Shard[%d]: %v", i, err) + } + clients = append(clients, c) + } + tlc := TemporalLogClient{ + Clients: clients, + intervals: intervals, + } + return &tlc, nil +} + +// GetAcceptedRoots retrieves the set of acceptable root certificates for all +// of the shards of a temporal log (i.e. the union). +func (tlc *TemporalLogClient) GetAcceptedRoots(ctx context.Context) ([]ct.ASN1Cert, error) { + type result struct { + roots []ct.ASN1Cert + err error + } + results := make(chan result, len(tlc.Clients)) + for _, c := range tlc.Clients { + go func(c *LogClient) { + var r result + r.roots, r.err = c.GetAcceptedRoots(ctx) + results <- r + }(c) + } + + var allRoots []ct.ASN1Cert + seen := make(map[[sha256.Size]byte]bool) + for range tlc.Clients { + r := <-results + if r.err != nil { + return nil, r.err + } + for _, root := range r.roots { + h := sha256.Sum256(root.Data) + if seen[h] { + continue + } + seen[h] = true + allRoots = append(allRoots, root) + } + } + return allRoots, nil +} + +// AddChain adds the (DER represented) X509 chain to the appropriate log. +func (tlc *TemporalLogClient) AddChain(ctx context.Context, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) { + return tlc.addChain(ctx, ct.X509LogEntryType, ct.AddChainPath, chain) +} + +// AddPreChain adds the (DER represented) Precertificate chain to the appropriate log. +func (tlc *TemporalLogClient) AddPreChain(ctx context.Context, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) { + return tlc.addChain(ctx, ct.PrecertLogEntryType, ct.AddPreChainPath, chain) +} + +func (tlc *TemporalLogClient) addChain(ctx context.Context, ctype ct.LogEntryType, path string, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) { + // Parse the first entry in the chain + if len(chain) == 0 { + return nil, errors.New("missing chain") + } + cert, err := x509.ParseCertificate(chain[0].Data) + if err != nil { + return nil, fmt.Errorf("failed to parse initial chain entry: %v", err) + } + cidx, err := tlc.IndexByDate(cert.NotAfter) + if err != nil { + return nil, fmt.Errorf("failed to find log to process cert: %v", err) + } + return tlc.Clients[cidx].addChainWithRetry(ctx, ctype, path, chain) +} + +// IndexByDate returns the index of the Clients entry that is appropriate for the given +// date. +func (tlc *TemporalLogClient) IndexByDate(when time.Time) (int, error) { + for i, interval := range tlc.intervals { + if (interval.lower != nil) && when.Before(*interval.lower) { + continue + } + if (interval.upper != nil) && !when.Before(*interval.upper) { + continue + } + return i, nil + } + return -1, fmt.Errorf("no log found encompassing date %v", when) +} + +func shardInterval(cfg *configpb.LogShardConfig) (interval, error) { + var interval interval + if cfg.NotAfterStart != nil { + t, err := ptypes.Timestamp(cfg.NotAfterStart) + if err != nil { + return interval, fmt.Errorf("failed to parse NotAfterStart: %v", err) + } + interval.lower = &t + } + if cfg.NotAfterLimit != nil { + t, err := ptypes.Timestamp(cfg.NotAfterLimit) + if err != nil { + return interval, fmt.Errorf("failed to parse NotAfterLimit: %v", err) + } + interval.upper = &t + } + + if interval.lower != nil && interval.upper != nil && !(*interval.lower).Before(*interval.upper) { + return interval, errors.New("inverted interval") + } + return interval, nil +} diff --git a/vendor/github.com/google/certificate-transparency-go/jsonclient/backoff.go b/vendor/github.com/google/certificate-transparency-go/jsonclient/backoff.go new file mode 100644 index 0000000000000..0c969d094e2cf --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/jsonclient/backoff.go @@ -0,0 +1,72 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// 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. + +package jsonclient + +import ( + "sync" + "time" +) + +type backoff struct { + mu sync.RWMutex + multiplier uint + notBefore time.Time +} + +const ( + // maximum backoff is 2^(maxMultiplier-1) = 128 seconds + maxMultiplier = 8 +) + +func (b *backoff) set(override *time.Duration) time.Duration { + b.mu.Lock() + defer b.mu.Unlock() + if b.notBefore.After(time.Now()) { + if override != nil { + // If existing backoff is set but override would be longer than + // it then set it to that. + notBefore := time.Now().Add(*override) + if notBefore.After(b.notBefore) { + b.notBefore = notBefore + } + } + return time.Until(b.notBefore) + } + var wait time.Duration + if override != nil { + wait = *override + } else { + if b.multiplier < maxMultiplier { + b.multiplier++ + } + wait = time.Second * time.Duration(1<<(b.multiplier-1)) + } + b.notBefore = time.Now().Add(wait) + return wait +} + +func (b *backoff) decreaseMultiplier() { + b.mu.Lock() + defer b.mu.Unlock() + if b.multiplier > 0 { + b.multiplier-- + } +} + +func (b *backoff) until() time.Time { + b.mu.RLock() + defer b.mu.RUnlock() + return b.notBefore +} diff --git a/vendor/github.com/google/certificate-transparency-go/jsonclient/client.go b/vendor/github.com/google/certificate-transparency-go/jsonclient/client.go new file mode 100644 index 0000000000000..c34fa833d54a0 --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/jsonclient/client.go @@ -0,0 +1,294 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// 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. + +package jsonclient + +import ( + "bytes" + "context" + "crypto" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "log" + "math/rand" + "net/http" + "net/url" + "strconv" + "strings" + "time" + + ct "github.com/google/certificate-transparency-go" + "github.com/google/certificate-transparency-go/x509" + "golang.org/x/net/context/ctxhttp" +) + +const maxJitter = 250 * time.Millisecond + +type backoffer interface { + // set adjusts/increases the current backoff interval (typically on retryable failure); + // if the optional parameter is provided, this will be used as the interval if it is greater + // than the currently set interval. Returns the current wait period so that it can be + // logged along with any error message. + set(*time.Duration) time.Duration + // decreaseMultiplier reduces the current backoff multiplier, typically on success. + decreaseMultiplier() + // until returns the time until which the client should wait before making a request, + // it may be in the past in which case it should be ignored. + until() time.Time +} + +// JSONClient provides common functionality for interacting with a JSON server +// that uses cryptographic signatures. +type JSONClient struct { + uri string // the base URI of the server. e.g. https://ct.googleapis/pilot + httpClient *http.Client // used to interact with the server via HTTP + Verifier *ct.SignatureVerifier // nil for no verification (e.g. no public key available) + logger Logger // interface to use for logging warnings and errors + backoff backoffer // object used to store and calculate backoff information +} + +// Logger is a simple logging interface used to log internal errors and warnings +type Logger interface { + // Printf formats and logs a message + Printf(string, ...interface{}) +} + +// Options are the options for creating a new JSONClient. +type Options struct { + // Interface to use for logging warnings and errors, if nil the + // standard library log package will be used. + Logger Logger + // PEM format public key to use for signature verification. + PublicKey string + // DER format public key to use for signature verification. + PublicKeyDER []byte +} + +// ParsePublicKey parses and returns the public key contained in opts. +// If both opts.PublicKey and opts.PublicKeyDER are set, PublicKeyDER is used. +// If neither is set, nil will be returned. +func (opts *Options) ParsePublicKey() (crypto.PublicKey, error) { + if len(opts.PublicKeyDER) > 0 { + return x509.ParsePKIXPublicKey(opts.PublicKeyDER) + } + + if opts.PublicKey != "" { + pubkey, _ /* keyhash */, rest, err := ct.PublicKeyFromPEM([]byte(opts.PublicKey)) + if err != nil { + return nil, err + } + if len(rest) > 0 { + return nil, errors.New("extra data found after PEM key decoded") + } + return pubkey, nil + } + + return nil, nil +} + +type basicLogger struct{} + +func (bl *basicLogger) Printf(msg string, args ...interface{}) { + log.Printf(msg, args...) +} + +// New constructs a new JSONClient instance, for the given base URI, using the +// given http.Client object (if provided) and the Options object. +// If opts does not specify a public key, signatures will not be verified. +func New(uri string, hc *http.Client, opts Options) (*JSONClient, error) { + pubkey, err := opts.ParsePublicKey() + if err != nil { + return nil, fmt.Errorf("invalid public key: %v", err) + } + + var verifier *ct.SignatureVerifier + if pubkey != nil { + var err error + verifier, err = ct.NewSignatureVerifier(pubkey) + if err != nil { + return nil, err + } + } + + if hc == nil { + hc = new(http.Client) + } + logger := opts.Logger + if logger == nil { + logger = &basicLogger{} + } + return &JSONClient{ + uri: strings.TrimRight(uri, "/"), + httpClient: hc, + Verifier: verifier, + logger: logger, + backoff: &backoff{}, + }, nil +} + +// BaseURI returns the base URI that the JSONClient makes queries to. +func (c *JSONClient) BaseURI() string { + return c.uri +} + +// GetAndParse makes a HTTP GET call to the given path, and attempta to parse +// the response as a JSON representation of the rsp structure. Returns the +// http.Response, the body of the response, and an error. Note that the +// returned http.Response can be non-nil even when an error is returned, +// in particular when the HTTP status is not OK or when the JSON parsing fails. +func (c *JSONClient) GetAndParse(ctx context.Context, path string, params map[string]string, rsp interface{}) (*http.Response, []byte, error) { + if ctx == nil { + return nil, nil, errors.New("context.Context required") + } + // Build a GET request with URL-encoded parameters. + vals := url.Values{} + for k, v := range params { + vals.Add(k, v) + } + fullURI := fmt.Sprintf("%s%s?%s", c.uri, path, vals.Encode()) + httpReq, err := http.NewRequest(http.MethodGet, fullURI, nil) + if err != nil { + return nil, nil, err + } + + httpRsp, err := ctxhttp.Do(ctx, c.httpClient, httpReq) + if err != nil { + return nil, nil, err + } + + // Read everything now so http.Client can reuse the connection. + body, err := ioutil.ReadAll(httpRsp.Body) + httpRsp.Body.Close() + if err != nil { + return httpRsp, body, fmt.Errorf("failed to read response body: %v", err) + } + + if httpRsp.StatusCode != http.StatusOK { + return httpRsp, body, fmt.Errorf("got HTTP Status %q", httpRsp.Status) + } + + if err := json.NewDecoder(bytes.NewReader(body)).Decode(rsp); err != nil { + return httpRsp, body, err + } + + return httpRsp, body, nil +} + +// PostAndParse makes a HTTP POST call to the given path, including the request +// parameters, and attempts to parse the response as a JSON representation of +// the rsp structure. Returns the http.Response, the body of the response, and +// an error. Note that the returned http.Response can be non-nil even when an +// error is returned, in particular when the HTTP status is not OK or when the +// JSON parsing fails. +func (c *JSONClient) PostAndParse(ctx context.Context, path string, req, rsp interface{}) (*http.Response, []byte, error) { + if ctx == nil { + return nil, nil, errors.New("context.Context required") + } + // Build a POST request with JSON body. + postBody, err := json.Marshal(req) + if err != nil { + return nil, nil, err + } + fullURI := fmt.Sprintf("%s%s", c.uri, path) + httpReq, err := http.NewRequest(http.MethodPost, fullURI, bytes.NewReader(postBody)) + if err != nil { + return nil, nil, err + } + httpReq.Header.Set("Content-Type", "application/json") + + httpRsp, err := ctxhttp.Do(ctx, c.httpClient, httpReq) + + // Read all of the body, if there is one, so that the http.Client can do Keep-Alive. + var body []byte + if httpRsp != nil { + body, err = ioutil.ReadAll(httpRsp.Body) + httpRsp.Body.Close() + } + if err != nil { + return httpRsp, body, err + } + + if httpRsp.StatusCode == http.StatusOK { + if err = json.Unmarshal(body, &rsp); err != nil { + return httpRsp, body, err + } + } + return httpRsp, body, nil +} + +// waitForBackoff blocks until the defined backoff interval or context has expired, if the returned +// not before time is in the past it returns immediately. +func (c *JSONClient) waitForBackoff(ctx context.Context) error { + dur := time.Until(c.backoff.until().Add(time.Millisecond * time.Duration(rand.Intn(int(maxJitter.Seconds()*1000))))) + if dur < 0 { + dur = 0 + } + backoffTimer := time.NewTimer(dur) + select { + case <-ctx.Done(): + return ctx.Err() + case <-backoffTimer.C: + } + return nil +} + +// PostAndParseWithRetry makes a HTTP POST call, but retries (with backoff) on +// retriable errors; the caller should set a deadline on the provided context +// to prevent infinite retries. Return values are as for PostAndParse. +func (c *JSONClient) PostAndParseWithRetry(ctx context.Context, path string, req, rsp interface{}) (*http.Response, []byte, error) { + if ctx == nil { + return nil, nil, errors.New("context.Context required") + } + for { + httpRsp, body, err := c.PostAndParse(ctx, path, req, rsp) + if err != nil { + // Don't retry context errors. + if err == context.Canceled || err == context.DeadlineExceeded { + return nil, nil, err + } + wait := c.backoff.set(nil) + c.logger.Printf("Request failed, backing-off for %s: %s", wait, err) + } else { + switch { + case httpRsp.StatusCode == http.StatusOK: + return httpRsp, body, nil + case httpRsp.StatusCode == http.StatusRequestTimeout: + // Request timeout, retry immediately + c.logger.Printf("Request timed out, retrying immediately") + case httpRsp.StatusCode == http.StatusServiceUnavailable: + var backoff *time.Duration + // Retry-After may be either a number of seconds as a int or a RFC 1123 + // date string (RFC 7231 Section 7.1.3) + if retryAfter := httpRsp.Header.Get("Retry-After"); retryAfter != "" { + if seconds, err := strconv.Atoi(retryAfter); err == nil { + b := time.Duration(seconds) * time.Second + backoff = &b + } else if date, err := time.Parse(time.RFC1123, retryAfter); err == nil { + b := date.Sub(time.Now()) + backoff = &b + } + } + wait := c.backoff.set(backoff) + c.logger.Printf("Request failed, backing-off for %s: got HTTP status %s", wait, httpRsp.Status) + default: + return httpRsp, body, fmt.Errorf("got HTTP Status %q", httpRsp.Status) + } + } + if err := c.waitForBackoff(ctx); err != nil { + return nil, nil, err + } + } +} diff --git a/vendor/github.com/google/certificate-transparency-go/serialization.go b/vendor/github.com/google/certificate-transparency-go/serialization.go new file mode 100644 index 0000000000000..39053ecd30955 --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/serialization.go @@ -0,0 +1,311 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// 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. + +package ct + +import ( + "crypto" + "crypto/sha256" + "encoding/json" + "fmt" + "strings" + "time" + + "github.com/google/certificate-transparency-go/tls" + "github.com/google/certificate-transparency-go/x509" +) + +// SerializeSCTSignatureInput serializes the passed in sct and log entry into +// the correct format for signing. +func SerializeSCTSignatureInput(sct SignedCertificateTimestamp, entry LogEntry) ([]byte, error) { + switch sct.SCTVersion { + case V1: + input := CertificateTimestamp{ + SCTVersion: sct.SCTVersion, + SignatureType: CertificateTimestampSignatureType, + Timestamp: sct.Timestamp, + EntryType: entry.Leaf.TimestampedEntry.EntryType, + Extensions: sct.Extensions, + } + switch entry.Leaf.TimestampedEntry.EntryType { + case X509LogEntryType: + input.X509Entry = entry.Leaf.TimestampedEntry.X509Entry + case PrecertLogEntryType: + input.PrecertEntry = &PreCert{ + IssuerKeyHash: entry.Leaf.TimestampedEntry.PrecertEntry.IssuerKeyHash, + TBSCertificate: entry.Leaf.TimestampedEntry.PrecertEntry.TBSCertificate, + } + case XJSONLogEntryType: + input.JSONEntry = entry.Leaf.TimestampedEntry.JSONEntry + default: + return nil, fmt.Errorf("unsupported entry type %s", entry.Leaf.TimestampedEntry.EntryType) + } + return tls.Marshal(input) + default: + return nil, fmt.Errorf("unknown SCT version %d", sct.SCTVersion) + } +} + +// SerializeSTHSignatureInput serializes the passed in STH into the correct +// format for signing. +func SerializeSTHSignatureInput(sth SignedTreeHead) ([]byte, error) { + switch sth.Version { + case V1: + if len(sth.SHA256RootHash) != crypto.SHA256.Size() { + return nil, fmt.Errorf("invalid TreeHash length, got %d expected %d", len(sth.SHA256RootHash), crypto.SHA256.Size()) + } + + input := TreeHeadSignature{ + Version: sth.Version, + SignatureType: TreeHashSignatureType, + Timestamp: sth.Timestamp, + TreeSize: sth.TreeSize, + SHA256RootHash: sth.SHA256RootHash, + } + return tls.Marshal(input) + default: + return nil, fmt.Errorf("unsupported STH version %d", sth.Version) + } +} + +// CreateX509MerkleTreeLeaf generates a MerkleTreeLeaf for an X509 cert +func CreateX509MerkleTreeLeaf(cert ASN1Cert, timestamp uint64) *MerkleTreeLeaf { + return &MerkleTreeLeaf{ + Version: V1, + LeafType: TimestampedEntryLeafType, + TimestampedEntry: &TimestampedEntry{ + Timestamp: timestamp, + EntryType: X509LogEntryType, + X509Entry: &cert, + }, + } +} + +// CreateJSONMerkleTreeLeaf creates the merkle tree leaf for json data. +func CreateJSONMerkleTreeLeaf(data interface{}, timestamp uint64) *MerkleTreeLeaf { + jsonData, err := json.Marshal(AddJSONRequest{Data: data}) + if err != nil { + return nil + } + // Match the JSON serialization implemented by json-c + jsonStr := strings.Replace(string(jsonData), ":", ": ", -1) + jsonStr = strings.Replace(jsonStr, ",", ", ", -1) + jsonStr = strings.Replace(jsonStr, "{", "{ ", -1) + jsonStr = strings.Replace(jsonStr, "}", " }", -1) + jsonStr = strings.Replace(jsonStr, "/", `\/`, -1) + // TODO: Pending google/certificate-transparency#1243, replace with + // ObjectHash once supported by CT server. + + return &MerkleTreeLeaf{ + Version: V1, + LeafType: TimestampedEntryLeafType, + TimestampedEntry: &TimestampedEntry{ + Timestamp: timestamp, + EntryType: XJSONLogEntryType, + JSONEntry: &JSONDataEntry{Data: []byte(jsonStr)}, + }, + } +} + +// MerkleTreeLeafFromRawChain generates a MerkleTreeLeaf from a chain (in DER-encoded form) and timestamp. +func MerkleTreeLeafFromRawChain(rawChain []ASN1Cert, etype LogEntryType, timestamp uint64) (*MerkleTreeLeaf, error) { + // Need at most 3 of the chain + count := 3 + if count > len(rawChain) { + count = len(rawChain) + } + chain := make([]*x509.Certificate, count) + for i := range chain { + cert, err := x509.ParseCertificate(rawChain[i].Data) + if err != nil { + return nil, fmt.Errorf("failed to parse chain[%d] cert: %v", i, err) + } + chain[i] = cert + } + return MerkleTreeLeafFromChain(chain, etype, timestamp) +} + +// MerkleTreeLeafFromChain generates a MerkleTreeLeaf from a chain and timestamp. +func MerkleTreeLeafFromChain(chain []*x509.Certificate, etype LogEntryType, timestamp uint64) (*MerkleTreeLeaf, error) { + leaf := MerkleTreeLeaf{ + Version: V1, + LeafType: TimestampedEntryLeafType, + TimestampedEntry: &TimestampedEntry{ + EntryType: etype, + Timestamp: timestamp, + }, + } + if etype == X509LogEntryType { + leaf.TimestampedEntry.X509Entry = &ASN1Cert{Data: chain[0].Raw} + return &leaf, nil + } + if etype != PrecertLogEntryType { + return nil, fmt.Errorf("unknown LogEntryType %d", etype) + } + + // Pre-certs are more complicated. First, parse the leaf pre-cert and its + // putative issuer. + if len(chain) < 2 { + return nil, fmt.Errorf("no issuer cert available for precert leaf building") + } + issuer := chain[1] + cert := chain[0] + + var preIssuer *x509.Certificate + if IsPreIssuer(issuer) { + // Replace the cert's issuance information with details from the pre-issuer. + preIssuer = issuer + + // The issuer of the pre-cert is not going to be the issuer of the final + // cert. Change to use the final issuer's key hash. + if len(chain) < 3 { + return nil, fmt.Errorf("no issuer cert available for pre-issuer") + } + issuer = chain[2] + } + + // Next, post-process the DER-encoded TBSCertificate, to remove the CT poison + // extension and possibly update the issuer field. + defangedTBS, err := x509.BuildPrecertTBS(cert.RawTBSCertificate, preIssuer) + if err != nil { + return nil, fmt.Errorf("failed to remove poison extension: %v", err) + } + + leaf.TimestampedEntry.EntryType = PrecertLogEntryType + leaf.TimestampedEntry.PrecertEntry = &PreCert{ + IssuerKeyHash: sha256.Sum256(issuer.RawSubjectPublicKeyInfo), + TBSCertificate: defangedTBS, + } + return &leaf, nil +} + +// MerkleTreeLeafForEmbeddedSCT generates a MerkleTreeLeaf from a chain and an +// SCT timestamp, where the leaf certificate at chain[0] is a certificate that +// contains embedded SCTs. It is assumed that the timestamp provided is from +// one of the SCTs embedded within the leaf certificate. +func MerkleTreeLeafForEmbeddedSCT(chain []*x509.Certificate, timestamp uint64) (*MerkleTreeLeaf, error) { + // For building the leaf for a certificate and SCT where the SCT is embedded + // in the certificate, we need to build the original precertificate TBS + // data. First, parse the leaf cert and its issuer. + if len(chain) < 2 { + return nil, fmt.Errorf("no issuer cert available for precert leaf building") + } + issuer := chain[1] + cert := chain[0] + + // Next, post-process the DER-encoded TBSCertificate, to remove the SCTList + // extension. + tbs, err := x509.RemoveSCTList(cert.RawTBSCertificate) + if err != nil { + return nil, fmt.Errorf("failed to remove SCT List extension: %v", err) + } + + return &MerkleTreeLeaf{ + Version: V1, + LeafType: TimestampedEntryLeafType, + TimestampedEntry: &TimestampedEntry{ + EntryType: PrecertLogEntryType, + Timestamp: timestamp, + PrecertEntry: &PreCert{ + IssuerKeyHash: sha256.Sum256(issuer.RawSubjectPublicKeyInfo), + TBSCertificate: tbs, + }, + }, + }, nil +} + +// LeafHashForLeaf returns the leaf hash for a Merkle tree leaf. +func LeafHashForLeaf(leaf *MerkleTreeLeaf) ([sha256.Size]byte, error) { + leafData, err := tls.Marshal(*leaf) + if err != nil { + return [sha256.Size]byte{}, fmt.Errorf("failed to tls-encode MerkleTreeLeaf: %s", err) + } + + data := append([]byte{TreeLeafPrefix}, leafData...) + leafHash := sha256.Sum256(data) + return leafHash, nil +} + +// IsPreIssuer indicates whether a certificate is a pre-cert issuer with the specific +// certificate transparency extended key usage. +func IsPreIssuer(issuer *x509.Certificate) bool { + for _, eku := range issuer.ExtKeyUsage { + if eku == x509.ExtKeyUsageCertificateTransparency { + return true + } + } + return false +} + +// LogEntryFromLeaf converts a LeafEntry object (which has the raw leaf data after JSON parsing) +// into a LogEntry object (which includes x509.Certificate objects, after TLS and ASN.1 parsing). +// Note that this function may return a valid LogEntry object and a non-nil error value, when +// the error indicates a non-fatal parsing error (of type x509.NonFatalErrors). +func LogEntryFromLeaf(index int64, leafEntry *LeafEntry) (*LogEntry, error) { + var leaf MerkleTreeLeaf + if rest, err := tls.Unmarshal(leafEntry.LeafInput, &leaf); err != nil { + return nil, fmt.Errorf("failed to unmarshal MerkleTreeLeaf for index %d: %v", index, err) + } else if len(rest) > 0 { + return nil, fmt.Errorf("trailing data (%d bytes) after MerkleTreeLeaf for index %d", len(rest), index) + } + + var err error + entry := LogEntry{Index: index, Leaf: leaf} + switch leaf.TimestampedEntry.EntryType { + case X509LogEntryType: + var certChain CertificateChain + if rest, err := tls.Unmarshal(leafEntry.ExtraData, &certChain); err != nil { + return nil, fmt.Errorf("failed to unmarshal ExtraData for index %d: %v", index, err) + } else if len(rest) > 0 { + return nil, fmt.Errorf("trailing data (%d bytes) after CertificateChain for index %d", len(rest), index) + } + entry.Chain = certChain.Entries + entry.X509Cert, err = leaf.X509Certificate() + if _, ok := err.(x509.NonFatalErrors); !ok && err != nil { + return nil, fmt.Errorf("failed to parse certificate in MerkleTreeLeaf for index %d: %v", index, err) + } + + case PrecertLogEntryType: + var precertChain PrecertChainEntry + if rest, err := tls.Unmarshal(leafEntry.ExtraData, &precertChain); err != nil { + return nil, fmt.Errorf("failed to unmarshal PrecertChainEntry for index %d: %v", index, err) + } else if len(rest) > 0 { + return nil, fmt.Errorf("trailing data (%d bytes) after PrecertChainEntry for index %d", len(rest), index) + } + entry.Chain = precertChain.CertificateChain + var tbsCert *x509.Certificate + tbsCert, err = leaf.Precertificate() + if _, ok := err.(x509.NonFatalErrors); !ok && err != nil { + return nil, fmt.Errorf("failed to parse precertificate in MerkleTreeLeaf for index %d: %v", index, err) + } + entry.Precert = &Precertificate{ + Submitted: precertChain.PreCertificate, + IssuerKeyHash: leaf.TimestampedEntry.PrecertEntry.IssuerKeyHash, + TBSCertificate: tbsCert, + } + + default: + return nil, fmt.Errorf("saw unknown entry type at index %d: %v", index, leaf.TimestampedEntry.EntryType) + } + // err may hold a x509.NonFatalErrors object. + return &entry, err +} + +// TimestampToTime converts a timestamp in the style of RFC 6962 (milliseconds +// since UNIX epoch) to a Go Time. +func TimestampToTime(ts uint64) time.Time { + secs := int64(ts / 1000) + msecs := int64(ts % 1000) + return time.Unix(secs, msecs*1000000) +} diff --git a/vendor/github.com/google/certificate-transparency-go/signatures.go b/vendor/github.com/google/certificate-transparency-go/signatures.go new file mode 100644 index 0000000000000..b1000ba464032 --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/signatures.go @@ -0,0 +1,112 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// 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. + +package ct + +import ( + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rsa" + "crypto/sha256" + "encoding/base64" + "encoding/pem" + "fmt" + "log" + + "github.com/google/certificate-transparency-go/tls" + "github.com/google/certificate-transparency-go/x509" +) + +// AllowVerificationWithNonCompliantKeys may be set to true in order to allow +// SignatureVerifier to use keys which are technically non-compliant with +// RFC6962. +var AllowVerificationWithNonCompliantKeys = false + +// PublicKeyFromPEM parses a PEM formatted block and returns the public key contained within and any remaining unread bytes, or an error. +func PublicKeyFromPEM(b []byte) (crypto.PublicKey, SHA256Hash, []byte, error) { + p, rest := pem.Decode(b) + if p == nil { + return nil, [sha256.Size]byte{}, rest, fmt.Errorf("no PEM block found in %s", string(b)) + } + k, err := x509.ParsePKIXPublicKey(p.Bytes) + return k, sha256.Sum256(p.Bytes), rest, err +} + +// PublicKeyFromB64 parses a base64-encoded public key. +func PublicKeyFromB64(b64PubKey string) (crypto.PublicKey, error) { + der, err := base64.StdEncoding.DecodeString(b64PubKey) + if err != nil { + return nil, fmt.Errorf("error decoding public key: %s", err) + } + return x509.ParsePKIXPublicKey(der) +} + +// SignatureVerifier can verify signatures on SCTs and STHs +type SignatureVerifier struct { + pubKey crypto.PublicKey +} + +// NewSignatureVerifier creates a new SignatureVerifier using the passed in PublicKey. +func NewSignatureVerifier(pk crypto.PublicKey) (*SignatureVerifier, error) { + switch pkType := pk.(type) { + case *rsa.PublicKey: + if pkType.N.BitLen() < 2048 { + e := fmt.Errorf("public key is RSA with < 2048 bits (size:%d)", pkType.N.BitLen()) + if !AllowVerificationWithNonCompliantKeys { + return nil, e + } + log.Printf("WARNING: %v", e) + } + case *ecdsa.PublicKey: + params := *(pkType.Params()) + if params != *elliptic.P256().Params() { + e := fmt.Errorf("public is ECDSA, but not on the P256 curve") + if !AllowVerificationWithNonCompliantKeys { + return nil, e + } + log.Printf("WARNING: %v", e) + + } + default: + return nil, fmt.Errorf("Unsupported public key type %v", pkType) + } + + return &SignatureVerifier{ + pubKey: pk, + }, nil +} + +// VerifySignature verifies the given signature sig matches the data. +func (s SignatureVerifier) VerifySignature(data []byte, sig tls.DigitallySigned) error { + return tls.VerifySignature(s.pubKey, data, sig) +} + +// VerifySCTSignature verifies that the SCT's signature is valid for the given LogEntry. +func (s SignatureVerifier) VerifySCTSignature(sct SignedCertificateTimestamp, entry LogEntry) error { + sctData, err := SerializeSCTSignatureInput(sct, entry) + if err != nil { + return err + } + return s.VerifySignature(sctData, tls.DigitallySigned(sct.Signature)) +} + +// VerifySTHSignature verifies that the STH's signature is valid. +func (s SignatureVerifier) VerifySTHSignature(sth SignedTreeHead) error { + sthData, err := SerializeSTHSignatureInput(sth) + if err != nil { + return err + } + return s.VerifySignature(sthData, tls.DigitallySigned(sth.TreeHeadSignature)) +} diff --git a/vendor/github.com/google/certificate-transparency-go/tls/signature.go b/vendor/github.com/google/certificate-transparency-go/tls/signature.go new file mode 100644 index 0000000000000..bfdb016d2fdb6 --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/tls/signature.go @@ -0,0 +1,152 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// 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. + +package tls + +import ( + "crypto" + "crypto/dsa" + "crypto/ecdsa" + _ "crypto/md5" // For registration side-effect + "crypto/rand" + "crypto/rsa" + _ "crypto/sha1" // For registration side-effect + _ "crypto/sha256" // For registration side-effect + _ "crypto/sha512" // For registration side-effect + "errors" + "fmt" + "log" + "math/big" + + "github.com/google/certificate-transparency-go/asn1" +) + +type dsaSig struct { + R, S *big.Int +} + +func generateHash(algo HashAlgorithm, data []byte) ([]byte, crypto.Hash, error) { + var hashType crypto.Hash + switch algo { + case MD5: + hashType = crypto.MD5 + case SHA1: + hashType = crypto.SHA1 + case SHA224: + hashType = crypto.SHA224 + case SHA256: + hashType = crypto.SHA256 + case SHA384: + hashType = crypto.SHA384 + case SHA512: + hashType = crypto.SHA512 + default: + return nil, hashType, fmt.Errorf("unsupported Algorithm.Hash in signature: %v", algo) + } + + hasher := hashType.New() + if _, err := hasher.Write(data); err != nil { + return nil, hashType, fmt.Errorf("failed to write to hasher: %v", err) + } + return hasher.Sum([]byte{}), hashType, nil +} + +// VerifySignature verifies that the passed in signature over data was created by the given PublicKey. +func VerifySignature(pubKey crypto.PublicKey, data []byte, sig DigitallySigned) error { + hash, hashType, err := generateHash(sig.Algorithm.Hash, data) + if err != nil { + return err + } + + switch sig.Algorithm.Signature { + case RSA: + rsaKey, ok := pubKey.(*rsa.PublicKey) + if !ok { + return fmt.Errorf("cannot verify RSA signature with %T key", pubKey) + } + if err := rsa.VerifyPKCS1v15(rsaKey, hashType, hash, sig.Signature); err != nil { + return fmt.Errorf("failed to verify rsa signature: %v", err) + } + case DSA: + dsaKey, ok := pubKey.(*dsa.PublicKey) + if !ok { + return fmt.Errorf("cannot verify DSA signature with %T key", pubKey) + } + var dsaSig dsaSig + rest, err := asn1.Unmarshal(sig.Signature, &dsaSig) + if err != nil { + return fmt.Errorf("failed to unmarshal DSA signature: %v", err) + } + if len(rest) != 0 { + log.Printf("Garbage following signature %v", rest) + } + if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 { + return errors.New("DSA signature contained zero or negative values") + } + if !dsa.Verify(dsaKey, hash, dsaSig.R, dsaSig.S) { + return errors.New("failed to verify DSA signature") + } + case ECDSA: + ecdsaKey, ok := pubKey.(*ecdsa.PublicKey) + if !ok { + return fmt.Errorf("cannot verify ECDSA signature with %T key", pubKey) + } + var ecdsaSig dsaSig + rest, err := asn1.Unmarshal(sig.Signature, &ecdsaSig) + if err != nil { + return fmt.Errorf("failed to unmarshal ECDSA signature: %v", err) + } + if len(rest) != 0 { + log.Printf("Garbage following signature %v", rest) + } + if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { + return errors.New("ECDSA signature contained zero or negative values") + } + + if !ecdsa.Verify(ecdsaKey, hash, ecdsaSig.R, ecdsaSig.S) { + return errors.New("failed to verify ECDSA signature") + } + default: + return fmt.Errorf("unsupported Algorithm.Signature in signature: %v", sig.Algorithm.Hash) + } + return nil +} + +// CreateSignature builds a signature over the given data using the specified hash algorithm and private key. +func CreateSignature(privKey crypto.PrivateKey, hashAlgo HashAlgorithm, data []byte) (DigitallySigned, error) { + var sig DigitallySigned + sig.Algorithm.Hash = hashAlgo + hash, hashType, err := generateHash(sig.Algorithm.Hash, data) + if err != nil { + return sig, err + } + + switch privKey := privKey.(type) { + case rsa.PrivateKey: + sig.Algorithm.Signature = RSA + sig.Signature, err = rsa.SignPKCS1v15(rand.Reader, &privKey, hashType, hash) + return sig, err + case ecdsa.PrivateKey: + sig.Algorithm.Signature = ECDSA + var ecdsaSig dsaSig + ecdsaSig.R, ecdsaSig.S, err = ecdsa.Sign(rand.Reader, &privKey, hash) + if err != nil { + return sig, err + } + sig.Signature, err = asn1.Marshal(ecdsaSig) + return sig, err + default: + return sig, fmt.Errorf("unsupported private key type %T", privKey) + } +} diff --git a/vendor/github.com/google/certificate-transparency-go/tls/tls.go b/vendor/github.com/google/certificate-transparency-go/tls/tls.go new file mode 100644 index 0000000000000..1bcd3a379669f --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/tls/tls.go @@ -0,0 +1,711 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// 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. + +// Package tls implements functionality for dealing with TLS-encoded data, +// as defined in RFC 5246. This includes parsing and generation of TLS-encoded +// data, together with utility functions for dealing with the DigitallySigned +// TLS type. +package tls + +import ( + "bytes" + "encoding/binary" + "fmt" + "reflect" + "strconv" + "strings" +) + +// This file holds utility functions for TLS encoding/decoding data +// as per RFC 5246 section 4. + +// A structuralError suggests that the TLS data is valid, but the Go type +// which is receiving it doesn't match. +type structuralError struct { + field string + msg string +} + +func (e structuralError) Error() string { + var prefix string + if e.field != "" { + prefix = e.field + ": " + } + return "tls: structure error: " + prefix + e.msg +} + +// A syntaxError suggests that the TLS data is invalid. +type syntaxError struct { + field string + msg string +} + +func (e syntaxError) Error() string { + var prefix string + if e.field != "" { + prefix = e.field + ": " + } + return "tls: syntax error: " + prefix + e.msg +} + +// Uint24 is an unsigned 3-byte integer. +type Uint24 uint32 + +// Enum is an unsigned integer. +type Enum uint64 + +var ( + uint8Type = reflect.TypeOf(uint8(0)) + uint16Type = reflect.TypeOf(uint16(0)) + uint24Type = reflect.TypeOf(Uint24(0)) + uint32Type = reflect.TypeOf(uint32(0)) + uint64Type = reflect.TypeOf(uint64(0)) + enumType = reflect.TypeOf(Enum(0)) +) + +// Unmarshal parses the TLS-encoded data in b and uses the reflect package to +// fill in an arbitrary value pointed at by val. Because Unmarshal uses the +// reflect package, the structs being written to must use exported fields +// (upper case names). +// +// The mappings between TLS types and Go types is as follows; some fields +// must have tags (to indicate their encoded size). +// +// TLS Go Required Tags +// opaque byte / uint8 +// uint8 byte / uint8 +// uint16 uint16 +// uint24 tls.Uint24 +// uint32 uint32 +// uint64 uint64 +// enum tls.Enum size:S or maxval:N +// Type []Type minlen:N,maxlen:M +// opaque[N] [N]byte / [N]uint8 +// uint8[N] [N]byte / [N]uint8 +// struct { } struct { } +// select(T) { +// case e1: Type *T selector:Field,val:e1 +// } +// +// TLS variants (RFC 5246 s4.6.1) are only supported when the value of the +// associated enumeration type is available earlier in the same enclosing +// struct, and each possible variant is marked with a selector tag (to +// indicate which field selects the variants) and a val tag (to indicate +// what value of the selector picks this particular field). +// +// For example, a TLS structure: +// +// enum { e1(1), e2(2) } EnumType; +// struct { +// EnumType sel; +// select(sel) { +// case e1: uint16 +// case e2: uint32 +// } data; +// } VariantItem; +// +// would have a corresponding Go type: +// +// type VariantItem struct { +// Sel tls.Enum `tls:"maxval:2"` +// Data16 *uint16 `tls:"selector:Sel,val:1"` +// Data32 *uint32 `tls:"selector:Sel,val:2"` +// } +// +// TLS fixed-length vectors of types other than opaque or uint8 are not supported. +// +// For TLS variable-length vectors that are themselves used in other vectors, +// create a single-field structure to represent the inner type. For example, for: +// +// opaque InnerType<1..65535>; +// struct { +// InnerType inners<1,65535>; +// } Something; +// +// convert to: +// +// type InnerType struct { +// Val []byte `tls:"minlen:1,maxlen:65535"` +// } +// type Something struct { +// Inners []InnerType `tls:"minlen:1,maxlen:65535"` +// } +// +// If the encoded value does not fit in the Go type, Unmarshal returns a parse error. +func Unmarshal(b []byte, val interface{}) ([]byte, error) { + return UnmarshalWithParams(b, val, "") +} + +// UnmarshalWithParams allows field parameters to be specified for the +// top-level element. The form of the params is the same as the field tags. +func UnmarshalWithParams(b []byte, val interface{}, params string) ([]byte, error) { + info, err := fieldTagToFieldInfo(params, "") + if err != nil { + return nil, err + } + // The passed in interface{} is a pointer (to allow the value to be written + // to); extract the pointed-to object as a reflect.Value, so parseField + // can do various introspection things. + v := reflect.ValueOf(val).Elem() + offset, err := parseField(v, b, 0, info) + if err != nil { + return nil, err + } + return b[offset:], nil +} + +// Return the number of bytes needed to encode values up to (and including) x. +func byteCount(x uint64) uint { + switch { + case x < 0x100: + return 1 + case x < 0x10000: + return 2 + case x < 0x1000000: + return 3 + case x < 0x100000000: + return 4 + case x < 0x10000000000: + return 5 + case x < 0x1000000000000: + return 6 + case x < 0x100000000000000: + return 7 + default: + return 8 + } +} + +type fieldInfo struct { + count uint // Number of bytes + countSet bool + minlen uint64 // Only relevant for slices + maxlen uint64 // Only relevant for slices + selector string // Only relevant for select sub-values + val uint64 // Only relevant for select sub-values + name string // Used for better error messages +} + +func (i *fieldInfo) fieldName() string { + if i == nil { + return "" + } + return i.name +} + +// Given a tag string, return a fieldInfo describing the field. +func fieldTagToFieldInfo(str string, name string) (*fieldInfo, error) { + var info *fieldInfo + // Iterate over clauses in the tag, ignoring any that don't parse properly. + for _, part := range strings.Split(str, ",") { + switch { + case strings.HasPrefix(part, "maxval:"): + if v, err := strconv.ParseUint(part[7:], 10, 64); err == nil { + info = &fieldInfo{count: byteCount(v), countSet: true} + } + case strings.HasPrefix(part, "size:"): + if sz, err := strconv.ParseUint(part[5:], 10, 32); err == nil { + info = &fieldInfo{count: uint(sz), countSet: true} + } + case strings.HasPrefix(part, "maxlen:"): + v, err := strconv.ParseUint(part[7:], 10, 64) + if err != nil { + continue + } + if info == nil { + info = &fieldInfo{} + } + info.count = byteCount(v) + info.countSet = true + info.maxlen = v + case strings.HasPrefix(part, "minlen:"): + v, err := strconv.ParseUint(part[7:], 10, 64) + if err != nil { + continue + } + if info == nil { + info = &fieldInfo{} + } + info.minlen = v + case strings.HasPrefix(part, "selector:"): + if info == nil { + info = &fieldInfo{} + } + info.selector = part[9:] + case strings.HasPrefix(part, "val:"): + v, err := strconv.ParseUint(part[4:], 10, 64) + if err != nil { + continue + } + if info == nil { + info = &fieldInfo{} + } + info.val = v + } + } + if info != nil { + info.name = name + if info.selector == "" { + if info.count < 1 { + return nil, structuralError{name, "field of unknown size in " + str} + } else if info.count > 8 { + return nil, structuralError{name, "specified size too large in " + str} + } else if info.minlen > info.maxlen { + return nil, structuralError{name, "specified length range inverted in " + str} + } else if info.val > 0 { + return nil, structuralError{name, "specified selector value but not field in " + str} + } + } + } else if name != "" { + info = &fieldInfo{name: name} + } + return info, nil +} + +// Check that a value fits into a field described by a fieldInfo structure. +func (i fieldInfo) check(val uint64, fldName string) error { + if val >= (1 << (8 * i.count)) { + return structuralError{fldName, fmt.Sprintf("value %d too large for size", val)} + } + if i.maxlen != 0 { + if val < i.minlen { + return structuralError{fldName, fmt.Sprintf("value %d too small for minimum %d", val, i.minlen)} + } + if val > i.maxlen { + return structuralError{fldName, fmt.Sprintf("value %d too large for maximum %d", val, i.maxlen)} + } + } + return nil +} + +// readVarUint reads an big-endian unsigned integer of the given size in +// bytes. +func readVarUint(data []byte, info *fieldInfo) (uint64, error) { + if info == nil || !info.countSet { + return 0, structuralError{info.fieldName(), "no field size information available"} + } + if len(data) < int(info.count) { + return 0, syntaxError{info.fieldName(), "truncated variable-length integer"} + } + var result uint64 + for i := uint(0); i < info.count; i++ { + result = (result << 8) | uint64(data[i]) + } + if err := info.check(result, info.name); err != nil { + return 0, err + } + return result, nil +} + +// parseField is the main parsing function. Given a byte slice and an offset +// (in bytes) into the data, it will try to parse a suitable ASN.1 value out +// and store it in the given Value. +func parseField(v reflect.Value, data []byte, initOffset int, info *fieldInfo) (int, error) { + offset := initOffset + rest := data[offset:] + + fieldType := v.Type() + // First look for known fixed types. + switch fieldType { + case uint8Type: + if len(rest) < 1 { + return offset, syntaxError{info.fieldName(), "truncated uint8"} + } + v.SetUint(uint64(rest[0])) + offset++ + return offset, nil + case uint16Type: + if len(rest) < 2 { + return offset, syntaxError{info.fieldName(), "truncated uint16"} + } + v.SetUint(uint64(binary.BigEndian.Uint16(rest))) + offset += 2 + return offset, nil + case uint24Type: + if len(rest) < 3 { + return offset, syntaxError{info.fieldName(), "truncated uint24"} + } + v.SetUint(uint64(data[0])<<16 | uint64(data[1])<<8 | uint64(data[2])) + offset += 3 + return offset, nil + case uint32Type: + if len(rest) < 4 { + return offset, syntaxError{info.fieldName(), "truncated uint32"} + } + v.SetUint(uint64(binary.BigEndian.Uint32(rest))) + offset += 4 + return offset, nil + case uint64Type: + if len(rest) < 8 { + return offset, syntaxError{info.fieldName(), "truncated uint64"} + } + v.SetUint(uint64(binary.BigEndian.Uint64(rest))) + offset += 8 + return offset, nil + } + + // Now deal with user-defined types. + switch v.Kind() { + case enumType.Kind(): + // Assume that anything of the same kind as Enum is an Enum, so that + // users can alias types of their own to Enum. + val, err := readVarUint(rest, info) + if err != nil { + return offset, err + } + v.SetUint(val) + offset += int(info.count) + return offset, nil + case reflect.Struct: + structType := fieldType + // TLS includes a select(Enum) {..} construct, where the value of an enum + // indicates which variant field is present (like a C union). We require + // that the enum value be an earlier field in the same structure (the selector), + // and that each of the possible variant destination fields be pointers. + // So the Go mapping looks like: + // type variantType struct { + // Which tls.Enum `tls:"size:1"` // this is the selector + // Val1 *type1 `tls:"selector:Which,val:1"` // this is a destination + // Val2 *type2 `tls:"selector:Which,val:1"` // this is a destination + // } + + // To deal with this, we track any enum-like fields and their values... + enums := make(map[string]uint64) + // .. and we track which selector names we've seen (in the destination field tags), + // and whether a destination for that selector has been chosen. + selectorSeen := make(map[string]bool) + for i := 0; i < structType.NumField(); i++ { + // Find information about this field. + tag := structType.Field(i).Tag.Get("tls") + fieldInfo, err := fieldTagToFieldInfo(tag, structType.Field(i).Name) + if err != nil { + return offset, err + } + + destination := v.Field(i) + if fieldInfo.selector != "" { + // This is a possible select(Enum) destination, so first check that the referenced + // selector field has already been seen earlier in the struct. + choice, ok := enums[fieldInfo.selector] + if !ok { + return offset, structuralError{fieldInfo.name, "selector not seen: " + fieldInfo.selector} + } + if structType.Field(i).Type.Kind() != reflect.Ptr { + return offset, structuralError{fieldInfo.name, "choice field not a pointer type"} + } + // Is this the first mention of the selector field name? If so, remember it. + seen, ok := selectorSeen[fieldInfo.selector] + if !ok { + selectorSeen[fieldInfo.selector] = false + } + if choice != fieldInfo.val { + // This destination field was not the chosen one, so make it nil (we checked + // it was a pointer above). + v.Field(i).Set(reflect.Zero(structType.Field(i).Type)) + continue + } + if seen { + // We already saw a different destination field receive the value for this + // selector value, which indicates a badly annotated structure. + return offset, structuralError{fieldInfo.name, "duplicate selector value for " + fieldInfo.selector} + } + selectorSeen[fieldInfo.selector] = true + // Make an object of the pointed-to type and parse into that. + v.Field(i).Set(reflect.New(structType.Field(i).Type.Elem())) + destination = v.Field(i).Elem() + } + offset, err = parseField(destination, data, offset, fieldInfo) + if err != nil { + return offset, err + } + + // Remember any possible tls.Enum values encountered in case they are selectors. + if structType.Field(i).Type.Kind() == enumType.Kind() { + enums[structType.Field(i).Name] = v.Field(i).Uint() + } + + } + + // Now we have seen all fields in the structure, check that all select(Enum) {..} selector + // fields found a destination to put their data in. + for selector, seen := range selectorSeen { + if !seen { + return offset, syntaxError{info.fieldName(), selector + ": unhandled value for selector"} + } + } + return offset, nil + case reflect.Array: + datalen := v.Len() + + if datalen > len(rest) { + return offset, syntaxError{info.fieldName(), "truncated array"} + } + inner := rest[:datalen] + offset += datalen + if fieldType.Elem().Kind() != reflect.Uint8 { + // Only byte/uint8 arrays are supported + return offset, structuralError{info.fieldName(), "unsupported array type: " + v.Type().String()} + } + reflect.Copy(v, reflect.ValueOf(inner)) + return offset, nil + + case reflect.Slice: + sliceType := fieldType + // Slices represent variable-length vectors, which are prefixed by a length field. + // The fieldInfo indicates the size of that length field. + varlen, err := readVarUint(rest, info) + if err != nil { + return offset, err + } + datalen := int(varlen) + offset += int(info.count) + rest = rest[info.count:] + + if datalen > len(rest) { + return offset, syntaxError{info.fieldName(), "truncated slice"} + } + inner := rest[:datalen] + offset += datalen + if fieldType.Elem().Kind() == reflect.Uint8 { + // Fast version for []byte + v.Set(reflect.MakeSlice(sliceType, datalen, datalen)) + reflect.Copy(v, reflect.ValueOf(inner)) + return offset, nil + } + + v.Set(reflect.MakeSlice(sliceType, 0, datalen)) + single := reflect.New(sliceType.Elem()) + for innerOffset := 0; innerOffset < len(inner); { + var err error + innerOffset, err = parseField(single.Elem(), inner, innerOffset, nil) + if err != nil { + return offset, err + } + v.Set(reflect.Append(v, single.Elem())) + } + return offset, nil + + default: + return offset, structuralError{info.fieldName(), fmt.Sprintf("unsupported type: %s of kind %s", fieldType, v.Kind())} + } +} + +// Marshal returns the TLS encoding of val. +func Marshal(val interface{}) ([]byte, error) { + return MarshalWithParams(val, "") +} + +// MarshalWithParams returns the TLS encoding of val, and allows field +// parameters to be specified for the top-level element. The form +// of the params is the same as the field tags. +func MarshalWithParams(val interface{}, params string) ([]byte, error) { + info, err := fieldTagToFieldInfo(params, "") + if err != nil { + return nil, err + } + var out bytes.Buffer + v := reflect.ValueOf(val) + if err := marshalField(&out, v, info); err != nil { + return nil, err + } + return out.Bytes(), err +} + +func marshalField(out *bytes.Buffer, v reflect.Value, info *fieldInfo) error { + var prefix string + if info != nil && len(info.name) > 0 { + prefix = info.name + ": " + } + fieldType := v.Type() + // First look for known fixed types. + switch fieldType { + case uint8Type: + out.WriteByte(byte(v.Uint())) + return nil + case uint16Type: + scratch := make([]byte, 2) + binary.BigEndian.PutUint16(scratch, uint16(v.Uint())) + out.Write(scratch) + return nil + case uint24Type: + i := v.Uint() + if i > 0xffffff { + return structuralError{info.fieldName(), fmt.Sprintf("uint24 overflow %d", i)} + } + scratch := make([]byte, 4) + binary.BigEndian.PutUint32(scratch, uint32(i)) + out.Write(scratch[1:]) + return nil + case uint32Type: + scratch := make([]byte, 4) + binary.BigEndian.PutUint32(scratch, uint32(v.Uint())) + out.Write(scratch) + return nil + case uint64Type: + scratch := make([]byte, 8) + binary.BigEndian.PutUint64(scratch, uint64(v.Uint())) + out.Write(scratch) + return nil + } + + // Now deal with user-defined types. + switch v.Kind() { + case enumType.Kind(): + i := v.Uint() + if info == nil { + return structuralError{info.fieldName(), "enum field tag missing"} + } + if err := info.check(i, prefix); err != nil { + return err + } + scratch := make([]byte, 8) + binary.BigEndian.PutUint64(scratch, uint64(i)) + out.Write(scratch[(8 - info.count):]) + return nil + case reflect.Struct: + structType := fieldType + enums := make(map[string]uint64) // Values of any Enum fields + // The comment parseField() describes the mapping of the TLS select(Enum) {..} construct; + // here we have selector and source (rather than destination) fields. + + // Track which selector names we've seen (in the source field tags), and whether a source + // value for that selector has been processed. + selectorSeen := make(map[string]bool) + for i := 0; i < structType.NumField(); i++ { + // Find information about this field. + tag := structType.Field(i).Tag.Get("tls") + fieldInfo, err := fieldTagToFieldInfo(tag, structType.Field(i).Name) + if err != nil { + return err + } + + source := v.Field(i) + if fieldInfo.selector != "" { + // This field is a possible source for a select(Enum) {..}. First check + // the selector field name has been seen. + choice, ok := enums[fieldInfo.selector] + if !ok { + return structuralError{fieldInfo.name, "selector not seen: " + fieldInfo.selector} + } + if structType.Field(i).Type.Kind() != reflect.Ptr { + return structuralError{fieldInfo.name, "choice field not a pointer type"} + } + // Is this the first mention of the selector field name? If so, remember it. + seen, ok := selectorSeen[fieldInfo.selector] + if !ok { + selectorSeen[fieldInfo.selector] = false + } + if choice != fieldInfo.val { + // This source was not chosen; police that it should be nil. + if v.Field(i).Pointer() != uintptr(0) { + return structuralError{fieldInfo.name, "unchosen field is non-nil"} + } + continue + } + if seen { + // We already saw a different source field generate the value for this + // selector value, which indicates a badly annotated structure. + return structuralError{fieldInfo.name, "duplicate selector value for " + fieldInfo.selector} + } + selectorSeen[fieldInfo.selector] = true + if v.Field(i).Pointer() == uintptr(0) { + return structuralError{fieldInfo.name, "chosen field is nil"} + } + // Marshal from the pointed-to source object. + source = v.Field(i).Elem() + } + + var fieldData bytes.Buffer + if err := marshalField(&fieldData, source, fieldInfo); err != nil { + return err + } + out.Write(fieldData.Bytes()) + + // Remember any tls.Enum values encountered in case they are selectors. + if structType.Field(i).Type.Kind() == enumType.Kind() { + enums[structType.Field(i).Name] = v.Field(i).Uint() + } + } + // Now we have seen all fields in the structure, check that all select(Enum) {..} selector + // fields found a source field get get their data from. + for selector, seen := range selectorSeen { + if !seen { + return syntaxError{info.fieldName(), selector + ": unhandled value for selector"} + } + } + return nil + + case reflect.Array: + datalen := v.Len() + arrayType := fieldType + if arrayType.Elem().Kind() != reflect.Uint8 { + // Only byte/uint8 arrays are supported + return structuralError{info.fieldName(), "unsupported array type"} + } + bytes := make([]byte, datalen) + for i := 0; i < datalen; i++ { + bytes[i] = uint8(v.Index(i).Uint()) + } + _, err := out.Write(bytes) + return err + + case reflect.Slice: + if info == nil { + return structuralError{info.fieldName(), "slice field tag missing"} + } + + sliceType := fieldType + if sliceType.Elem().Kind() == reflect.Uint8 { + // Fast version for []byte: first write the length as info.count bytes. + datalen := v.Len() + scratch := make([]byte, 8) + binary.BigEndian.PutUint64(scratch, uint64(datalen)) + out.Write(scratch[(8 - info.count):]) + + if err := info.check(uint64(datalen), prefix); err != nil { + return err + } + // Then just write the data. + bytes := make([]byte, datalen) + for i := 0; i < datalen; i++ { + bytes[i] = uint8(v.Index(i).Uint()) + } + _, err := out.Write(bytes) + return err + } + // General version: use a separate Buffer to write the slice entries into. + var innerBuf bytes.Buffer + for i := 0; i < v.Len(); i++ { + if err := marshalField(&innerBuf, v.Index(i), nil); err != nil { + return err + } + } + + // Now insert (and check) the size. + size := uint64(innerBuf.Len()) + if err := info.check(size, prefix); err != nil { + return err + } + scratch := make([]byte, 8) + binary.BigEndian.PutUint64(scratch, size) + out.Write(scratch[(8 - info.count):]) + + // Then copy the data. + _, err := out.Write(innerBuf.Bytes()) + return err + + default: + return structuralError{info.fieldName(), fmt.Sprintf("unsupported type: %s of kind %s", fieldType, v.Kind())} + } +} diff --git a/vendor/github.com/google/certificate-transparency-go/tls/types.go b/vendor/github.com/google/certificate-transparency-go/tls/types.go new file mode 100644 index 0000000000000..14471ad264c15 --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/tls/types.go @@ -0,0 +1,117 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// 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. + +package tls + +import ( + "crypto" + "crypto/dsa" + "crypto/ecdsa" + "crypto/rsa" + "fmt" +) + +// DigitallySigned gives information about a signature, including the algorithm used +// and the signature value. Defined in RFC 5246 s4.7. +type DigitallySigned struct { + Algorithm SignatureAndHashAlgorithm + Signature []byte `tls:"minlen:0,maxlen:65535"` +} + +func (d DigitallySigned) String() string { + return fmt.Sprintf("Signature: HashAlgo=%v SignAlgo=%v Value=%x", d.Algorithm.Hash, d.Algorithm.Signature, d.Signature) +} + +// SignatureAndHashAlgorithm gives information about the algorithms used for a +// signature. Defined in RFC 5246 s7.4.1.4.1. +type SignatureAndHashAlgorithm struct { + Hash HashAlgorithm `tls:"maxval:255"` + Signature SignatureAlgorithm `tls:"maxval:255"` +} + +// HashAlgorithm enum from RFC 5246 s7.4.1.4.1. +type HashAlgorithm Enum + +// HashAlgorithm constants from RFC 5246 s7.4.1.4.1. +const ( + None HashAlgorithm = 0 + MD5 HashAlgorithm = 1 + SHA1 HashAlgorithm = 2 + SHA224 HashAlgorithm = 3 + SHA256 HashAlgorithm = 4 + SHA384 HashAlgorithm = 5 + SHA512 HashAlgorithm = 6 +) + +func (h HashAlgorithm) String() string { + switch h { + case None: + return "None" + case MD5: + return "MD5" + case SHA1: + return "SHA1" + case SHA224: + return "SHA224" + case SHA256: + return "SHA256" + case SHA384: + return "SHA384" + case SHA512: + return "SHA512" + default: + return fmt.Sprintf("UNKNOWN(%d)", h) + } +} + +// SignatureAlgorithm enum from RFC 5246 s7.4.1.4.1. +type SignatureAlgorithm Enum + +// SignatureAlgorithm constants from RFC 5246 s7.4.1.4.1. +const ( + Anonymous SignatureAlgorithm = 0 + RSA SignatureAlgorithm = 1 + DSA SignatureAlgorithm = 2 + ECDSA SignatureAlgorithm = 3 +) + +func (s SignatureAlgorithm) String() string { + switch s { + case Anonymous: + return "Anonymous" + case RSA: + return "RSA" + case DSA: + return "DSA" + case ECDSA: + return "ECDSA" + default: + return fmt.Sprintf("UNKNOWN(%d)", s) + } +} + +// SignatureAlgorithmFromPubKey returns the algorithm used for this public key. +// ECDSA, RSA, and DSA keys are supported. Other key types will return Anonymous. +func SignatureAlgorithmFromPubKey(k crypto.PublicKey) SignatureAlgorithm { + switch k.(type) { + case *ecdsa.PublicKey: + return ECDSA + case *rsa.PublicKey: + return RSA + case *dsa.PublicKey: + return DSA + default: + return Anonymous + } +} diff --git a/vendor/github.com/google/certificate-transparency-go/types.go b/vendor/github.com/google/certificate-transparency-go/types.go new file mode 100644 index 0000000000000..bcdd7e9222d6d --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/types.go @@ -0,0 +1,509 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// 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. + +// Package ct holds core types and utilities for Certificate Transparency. +package ct + +import ( + "crypto/sha256" + "encoding/base64" + "encoding/json" + "fmt" + + "github.com/google/certificate-transparency-go/tls" + "github.com/google/certificate-transparency-go/x509" +) + +/////////////////////////////////////////////////////////////////////////////// +// The following structures represent those outlined in RFC6962; any section +// numbers mentioned refer to that RFC. +/////////////////////////////////////////////////////////////////////////////// + +// LogEntryType represents the LogEntryType enum from section 3.1: +// enum { x509_entry(0), precert_entry(1), (65535) } LogEntryType; +type LogEntryType tls.Enum // tls:"maxval:65535" + +// LogEntryType constants from section 3.1. +const ( + X509LogEntryType LogEntryType = 0 + PrecertLogEntryType LogEntryType = 1 + XJSONLogEntryType LogEntryType = 0x8000 // Experimental. Don't rely on this! +) + +func (e LogEntryType) String() string { + switch e { + case X509LogEntryType: + return "X509LogEntryType" + case PrecertLogEntryType: + return "PrecertLogEntryType" + case XJSONLogEntryType: + return "XJSONLogEntryType" + default: + return fmt.Sprintf("UnknownEntryType(%d)", e) + } +} + +// RFC6962 section 2.1 requires a prefix byte on hash inputs for second preimage resistance. +const ( + TreeLeafPrefix = byte(0x00) + TreeNodePrefix = byte(0x01) +) + +// MerkleLeafType represents the MerkleLeafType enum from section 3.4: +// enum { timestamped_entry(0), (255) } MerkleLeafType; +type MerkleLeafType tls.Enum // tls:"maxval:255" + +// TimestampedEntryLeafType is the only defined MerkleLeafType constant from section 3.4. +const TimestampedEntryLeafType MerkleLeafType = 0 // Entry type for an SCT + +func (m MerkleLeafType) String() string { + switch m { + case TimestampedEntryLeafType: + return "TimestampedEntryLeafType" + default: + return fmt.Sprintf("UnknownLeafType(%d)", m) + } +} + +// Version represents the Version enum from section 3.2: +// enum { v1(0), (255) } Version; +type Version tls.Enum // tls:"maxval:255" + +// CT Version constants from section 3.2. +const ( + V1 Version = 0 +) + +func (v Version) String() string { + switch v { + case V1: + return "V1" + default: + return fmt.Sprintf("UnknownVersion(%d)", v) + } +} + +// SignatureType differentiates STH signatures from SCT signatures, see section 3.2. +// enum { certificate_timestamp(0), tree_hash(1), (255) } SignatureType; +type SignatureType tls.Enum // tls:"maxval:255" + +// SignatureType constants from section 3.2. +const ( + CertificateTimestampSignatureType SignatureType = 0 + TreeHashSignatureType SignatureType = 1 +) + +func (st SignatureType) String() string { + switch st { + case CertificateTimestampSignatureType: + return "CertificateTimestamp" + case TreeHashSignatureType: + return "TreeHash" + default: + return fmt.Sprintf("UnknownSignatureType(%d)", st) + } +} + +// ASN1Cert type for holding the raw DER bytes of an ASN.1 Certificate +// (section 3.1). +type ASN1Cert struct { + Data []byte `tls:"minlen:1,maxlen:16777215"` +} + +// LogID holds the hash of the Log's public key (section 3.2). +// TODO(pphaneuf): Users should be migrated to the one in the logid package. +type LogID struct { + KeyID [sha256.Size]byte +} + +// PreCert represents a Precertificate (section 3.2). +type PreCert struct { + IssuerKeyHash [sha256.Size]byte + TBSCertificate []byte `tls:"minlen:1,maxlen:16777215"` // DER-encoded TBSCertificate +} + +// CTExtensions is a representation of the raw bytes of any CtExtension +// structure (see section 3.2). +// nolint: golint +type CTExtensions []byte // tls:"minlen:0,maxlen:65535"` + +// MerkleTreeNode represents an internal node in the CT tree. +type MerkleTreeNode []byte + +// ConsistencyProof represents a CT consistency proof (see sections 2.1.2 and +// 4.4). +type ConsistencyProof []MerkleTreeNode + +// AuditPath represents a CT inclusion proof (see sections 2.1.1 and 4.5). +type AuditPath []MerkleTreeNode + +// LeafInput represents a serialized MerkleTreeLeaf structure. +type LeafInput []byte + +// DigitallySigned is a local alias for tls.DigitallySigned so that we can +// attach a MarshalJSON method. +type DigitallySigned tls.DigitallySigned + +// FromBase64String populates the DigitallySigned structure from the base64 data passed in. +// Returns an error if the base64 data is invalid. +func (d *DigitallySigned) FromBase64String(b64 string) error { + raw, err := base64.StdEncoding.DecodeString(b64) + if err != nil { + return fmt.Errorf("failed to unbase64 DigitallySigned: %v", err) + } + var ds tls.DigitallySigned + if rest, err := tls.Unmarshal(raw, &ds); err != nil { + return fmt.Errorf("failed to unmarshal DigitallySigned: %v", err) + } else if len(rest) > 0 { + return fmt.Errorf("trailing data (%d bytes) after DigitallySigned", len(rest)) + } + *d = DigitallySigned(ds) + return nil +} + +// Base64String returns the base64 representation of the DigitallySigned struct. +func (d DigitallySigned) Base64String() (string, error) { + b, err := tls.Marshal(d) + if err != nil { + return "", err + } + return base64.StdEncoding.EncodeToString(b), nil +} + +// MarshalJSON implements the json.Marshaller interface. +func (d DigitallySigned) MarshalJSON() ([]byte, error) { + b64, err := d.Base64String() + if err != nil { + return []byte{}, err + } + return []byte(`"` + b64 + `"`), nil +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (d *DigitallySigned) UnmarshalJSON(b []byte) error { + var content string + if err := json.Unmarshal(b, &content); err != nil { + return fmt.Errorf("failed to unmarshal DigitallySigned: %v", err) + } + return d.FromBase64String(content) +} + +// LogEntry represents the (parsed) contents of an entry in a CT log. This is described +// in section 3.1, but note that this structure does *not* match the TLS structure +// defined there (the TLS structure is never used directly in RFC6962). +type LogEntry struct { + Index int64 + Leaf MerkleTreeLeaf + // Exactly one of the following three fields should be non-empty. + X509Cert *x509.Certificate // Parsed X.509 certificate + Precert *Precertificate // Extracted precertificate + JSONData []byte + + // Chain holds the issuing certificate chain, starting with the + // issuer of the leaf certificate / pre-certificate. + Chain []ASN1Cert +} + +// PrecertChainEntry holds an precertificate together with a validation chain +// for it; see section 3.1. +type PrecertChainEntry struct { + PreCertificate ASN1Cert `tls:"minlen:1,maxlen:16777215"` + CertificateChain []ASN1Cert `tls:"minlen:0,maxlen:16777215"` +} + +// CertificateChain holds a chain of certificates, as returned as extra data +// for get-entries (section 4.6). +type CertificateChain struct { + Entries []ASN1Cert `tls:"minlen:0,maxlen:16777215"` +} + +// JSONDataEntry holds arbitrary data. +type JSONDataEntry struct { + Data []byte `tls:"minlen:0,maxlen:1677215"` +} + +// SHA256Hash represents the output from the SHA256 hash function. +type SHA256Hash [sha256.Size]byte + +// FromBase64String populates the SHA256 struct with the contents of the base64 data passed in. +func (s *SHA256Hash) FromBase64String(b64 string) error { + bs, err := base64.StdEncoding.DecodeString(b64) + if err != nil { + return fmt.Errorf("failed to unbase64 LogID: %v", err) + } + if len(bs) != sha256.Size { + return fmt.Errorf("invalid SHA256 length, expected 32 but got %d", len(bs)) + } + copy(s[:], bs) + return nil +} + +// Base64String returns the base64 representation of this SHA256Hash. +func (s SHA256Hash) Base64String() string { + return base64.StdEncoding.EncodeToString(s[:]) +} + +// MarshalJSON implements the json.Marshaller interface for SHA256Hash. +func (s SHA256Hash) MarshalJSON() ([]byte, error) { + return []byte(`"` + s.Base64String() + `"`), nil +} + +// UnmarshalJSON implements the json.Unmarshaller interface. +func (s *SHA256Hash) UnmarshalJSON(b []byte) error { + var content string + if err := json.Unmarshal(b, &content); err != nil { + return fmt.Errorf("failed to unmarshal SHA256Hash: %v", err) + } + return s.FromBase64String(content) +} + +// SignedTreeHead represents the structure returned by the get-sth CT method +// after base64 decoding; see sections 3.5 and 4.3. +type SignedTreeHead struct { + Version Version `json:"sth_version"` // The version of the protocol to which the STH conforms + TreeSize uint64 `json:"tree_size"` // The number of entries in the new tree + Timestamp uint64 `json:"timestamp"` // The time at which the STH was created + SHA256RootHash SHA256Hash `json:"sha256_root_hash"` // The root hash of the log's Merkle tree + TreeHeadSignature DigitallySigned `json:"tree_head_signature"` // Log's signature over a TLS-encoded TreeHeadSignature + LogID SHA256Hash `json:"log_id"` // The SHA256 hash of the log's public key +} + +// TreeHeadSignature holds the data over which the signature in an STH is +// generated; see section 3.5 +type TreeHeadSignature struct { + Version Version `tls:"maxval:255"` + SignatureType SignatureType `tls:"maxval:255"` // == TreeHashSignatureType + Timestamp uint64 + TreeSize uint64 + SHA256RootHash SHA256Hash +} + +// SignedCertificateTimestamp represents the structure returned by the +// add-chain and add-pre-chain methods after base64 decoding; see sections +// 3.2, 4.1 and 4.2. +type SignedCertificateTimestamp struct { + SCTVersion Version `tls:"maxval:255"` + LogID LogID + Timestamp uint64 + Extensions CTExtensions `tls:"minlen:0,maxlen:65535"` + Signature DigitallySigned // Signature over TLS-encoded CertificateTimestamp +} + +// CertificateTimestamp is the collection of data that the signature in an +// SCT is over; see section 3.2. +type CertificateTimestamp struct { + SCTVersion Version `tls:"maxval:255"` + SignatureType SignatureType `tls:"maxval:255"` + Timestamp uint64 + EntryType LogEntryType `tls:"maxval:65535"` + X509Entry *ASN1Cert `tls:"selector:EntryType,val:0"` + PrecertEntry *PreCert `tls:"selector:EntryType,val:1"` + JSONEntry *JSONDataEntry `tls:"selector:EntryType,val:32768"` + Extensions CTExtensions `tls:"minlen:0,maxlen:65535"` +} + +func (s SignedCertificateTimestamp) String() string { + return fmt.Sprintf("{Version:%d LogId:%s Timestamp:%d Extensions:'%s' Signature:%v}", s.SCTVersion, + base64.StdEncoding.EncodeToString(s.LogID.KeyID[:]), + s.Timestamp, + s.Extensions, + s.Signature) +} + +// TimestampedEntry is part of the MerkleTreeLeaf structure; see section 3.4. +type TimestampedEntry struct { + Timestamp uint64 + EntryType LogEntryType `tls:"maxval:65535"` + X509Entry *ASN1Cert `tls:"selector:EntryType,val:0"` + PrecertEntry *PreCert `tls:"selector:EntryType,val:1"` + JSONEntry *JSONDataEntry `tls:"selector:EntryType,val:32768"` + Extensions CTExtensions `tls:"minlen:0,maxlen:65535"` +} + +// MerkleTreeLeaf represents the deserialized structure of the hash input for the +// leaves of a log's Merkle tree; see section 3.4. +type MerkleTreeLeaf struct { + Version Version `tls:"maxval:255"` + LeafType MerkleLeafType `tls:"maxval:255"` + TimestampedEntry *TimestampedEntry `tls:"selector:LeafType,val:0"` +} + +// Precertificate represents the parsed CT Precertificate structure. +type Precertificate struct { + // DER-encoded pre-certificate as originally added, which includes a + // poison extension and a signature generated over the pre-cert by + // the pre-cert issuer (which might differ from the issuer of the final + // cert, see RFC6962 s3.1). + Submitted ASN1Cert + // SHA256 hash of the issuing key + IssuerKeyHash [sha256.Size]byte + // Parsed TBSCertificate structure, held in an x509.Certificate for convenience. + TBSCertificate *x509.Certificate +} + +// X509Certificate returns the X.509 Certificate contained within the +// MerkleTreeLeaf. +func (m *MerkleTreeLeaf) X509Certificate() (*x509.Certificate, error) { + if m.TimestampedEntry.EntryType != X509LogEntryType { + return nil, fmt.Errorf("cannot call X509Certificate on a MerkleTreeLeaf that is not an X509 entry") + } + return x509.ParseCertificate(m.TimestampedEntry.X509Entry.Data) +} + +// Precertificate returns the X.509 Precertificate contained within the MerkleTreeLeaf. +// +// The returned precertificate is embedded in an x509.Certificate, but is in the +// form stored internally in the log rather than the original submitted form +// (i.e. it does not include the poison extension and any changes to reflect the +// final certificate's issuer have been made; see x509.BuildPrecertTBS). +func (m *MerkleTreeLeaf) Precertificate() (*x509.Certificate, error) { + if m.TimestampedEntry.EntryType != PrecertLogEntryType { + return nil, fmt.Errorf("cannot call Precertificate on a MerkleTreeLeaf that is not a precert entry") + } + return x509.ParseTBSCertificate(m.TimestampedEntry.PrecertEntry.TBSCertificate) +} + +// APIEndpoint is a string that represents one of the Certificate Transparency +// Log API endpoints. +type APIEndpoint string + +// Certificate Transparency Log API endpoints; see section 4. +// WARNING: Should match the URI paths without the "/ct/v1/" prefix. If +// changing these constants, may need to change those too. +const ( + AddChainStr APIEndpoint = "add-chain" + AddPreChainStr APIEndpoint = "add-pre-chain" + GetSTHStr APIEndpoint = "get-sth" + GetEntriesStr APIEndpoint = "get-entries" + GetProofByHashStr APIEndpoint = "get-proof-by-hash" + GetSTHConsistencyStr APIEndpoint = "get-sth-consistency" + GetRootsStr APIEndpoint = "get-roots" + GetEntryAndProofStr APIEndpoint = "get-entry-and-proof" +) + +// URI paths for Log requests; see section 4. +// WARNING: Should match the API endpoints, with the "/ct/v1/" prefix. If +// changing these constants, may need to change those too. +const ( + AddChainPath = "/ct/v1/add-chain" + AddPreChainPath = "/ct/v1/add-pre-chain" + GetSTHPath = "/ct/v1/get-sth" + GetEntriesPath = "/ct/v1/get-entries" + GetProofByHashPath = "/ct/v1/get-proof-by-hash" + GetSTHConsistencyPath = "/ct/v1/get-sth-consistency" + GetRootsPath = "/ct/v1/get-roots" + GetEntryAndProofPath = "/ct/v1/get-entry-and-proof" + + AddJSONPath = "/ct/v1/add-json" // Experimental addition +) + +// AddChainRequest represents the JSON request body sent to the add-chain and +// add-pre-chain POST methods from sections 4.1 and 4.2. +type AddChainRequest struct { + Chain [][]byte `json:"chain"` +} + +// AddChainResponse represents the JSON response to the add-chain and +// add-pre-chain POST methods. +// An SCT represents a Log's promise to integrate a [pre-]certificate into the +// log within a defined period of time. +type AddChainResponse struct { + SCTVersion Version `json:"sct_version"` // SCT structure version + ID []byte `json:"id"` // Log ID + Timestamp uint64 `json:"timestamp"` // Timestamp of issuance + Extensions string `json:"extensions"` // Holder for any CT extensions + Signature []byte `json:"signature"` // Log signature for this SCT +} + +// AddJSONRequest represents the JSON request body sent to the add-json POST method. +// The corresponding response re-uses AddChainResponse. +// This is an experimental addition not covered by RFC6962. +type AddJSONRequest struct { + Data interface{} `json:"data"` +} + +// GetSTHResponse respresents the JSON response to the get-sth GET method from section 4.3. +type GetSTHResponse struct { + TreeSize uint64 `json:"tree_size"` // Number of certs in the current tree + Timestamp uint64 `json:"timestamp"` // Time that the tree was created + SHA256RootHash []byte `json:"sha256_root_hash"` // Root hash of the tree + TreeHeadSignature []byte `json:"tree_head_signature"` // Log signature for this STH +} + +// ToSignedTreeHead creates a SignedTreeHead from the GetSTHResponse. +func (r *GetSTHResponse) ToSignedTreeHead() (*SignedTreeHead, error) { + sth := SignedTreeHead{ + TreeSize: r.TreeSize, + Timestamp: r.Timestamp, + } + + if len(r.SHA256RootHash) != sha256.Size { + return nil, fmt.Errorf("sha256_root_hash is invalid length, expected %d got %d", sha256.Size, len(r.SHA256RootHash)) + } + copy(sth.SHA256RootHash[:], r.SHA256RootHash) + + var ds DigitallySigned + if rest, err := tls.Unmarshal(r.TreeHeadSignature, &ds); err != nil { + return nil, fmt.Errorf("tls.Unmarshal(): %s", err) + } else if len(rest) > 0 { + return nil, fmt.Errorf("trailing data (%d bytes) after DigitallySigned", len(rest)) + } + sth.TreeHeadSignature = ds + + return &sth, nil +} + +// GetSTHConsistencyResponse represents the JSON response to the get-sth-consistency +// GET method from section 4.4. (The corresponding GET request has parameters 'first' and +// 'second'.) +type GetSTHConsistencyResponse struct { + Consistency [][]byte `json:"consistency"` +} + +// GetProofByHashResponse represents the JSON response to the get-proof-by-hash GET +// method from section 4.5. (The corresponding GET request has parameters 'hash' +// and 'tree_size'.) +type GetProofByHashResponse struct { + LeafIndex int64 `json:"leaf_index"` // The 0-based index of the end entity corresponding to the "hash" parameter. + AuditPath [][]byte `json:"audit_path"` // An array of base64-encoded Merkle Tree nodes proving the inclusion of the chosen certificate. +} + +// LeafEntry represents a leaf in the Log's Merkle tree, as returned by the get-entries +// GET method from section 4.6. +type LeafEntry struct { + // LeafInput is a TLS-encoded MerkleTreeLeaf + LeafInput []byte `json:"leaf_input"` + // ExtraData holds (unsigned) extra data, normally the cert validation chain. + ExtraData []byte `json:"extra_data"` +} + +// GetEntriesResponse respresents the JSON response to the get-entries GET method +// from section 4.6. +type GetEntriesResponse struct { + Entries []LeafEntry `json:"entries"` // the list of returned entries +} + +// GetRootsResponse represents the JSON response to the get-roots GET method from section 4.7. +type GetRootsResponse struct { + Certificates []string `json:"certificates"` +} + +// GetEntryAndProofResponse represents the JSON response to the get-entry-and-proof +// GET method from section 4.8. (The corresponding GET request has parameters 'leaf_index' +// and 'tree_size'.) +type GetEntryAndProofResponse struct { + LeafInput []byte `json:"leaf_input"` // the entry itself + ExtraData []byte `json:"extra_data"` // any chain provided when the entry was added to the log + AuditPath [][]byte `json:"audit_path"` // the corresponding proof +} diff --git a/vendor/github.com/google/certificate-transparency/go/x509/cert_pool.go b/vendor/github.com/google/certificate-transparency-go/x509/cert_pool.go old mode 100755 new mode 100644 similarity index 73% rename from vendor/github.com/google/certificate-transparency/go/x509/cert_pool.go rename to vendor/github.com/google/certificate-transparency-go/x509/cert_pool.go index babe94d41c553..71ffbdf0e0460 --- a/vendor/github.com/google/certificate-transparency/go/x509/cert_pool.go +++ b/vendor/github.com/google/certificate-transparency-go/x509/cert_pool.go @@ -6,6 +6,8 @@ package x509 import ( "encoding/pem" + "errors" + "runtime" ) // CertPool is a set of certificates. @@ -18,12 +20,24 @@ type CertPool struct { // NewCertPool returns a new, empty CertPool. func NewCertPool() *CertPool { return &CertPool{ - make(map[string][]int), - make(map[string][]int), - nil, + bySubjectKeyId: make(map[string][]int), + byName: make(map[string][]int), } } +// SystemCertPool returns a copy of the system cert pool. +// +// Any mutations to the returned pool are not written to disk and do +// not affect any other pool. +func SystemCertPool() (*CertPool, error) { + if runtime.GOOS == "windows" { + // Issue 16736, 18609: + return nil, errors.New("crypto/x509: system root pool is not available on Windows") + } + + return loadSystemRoots() +} + // findVerifiedParents attempts to find certificates in s which have signed the // given certificate. If any candidates were rejected then errCert will be set // to one of them, arbitrarily, and err will contain the reason that it was @@ -52,6 +66,21 @@ func (s *CertPool) findVerifiedParents(cert *Certificate) (parents []int, errCer return } +func (s *CertPool) contains(cert *Certificate) bool { + if s == nil { + return false + } + + candidates := s.byName[string(cert.RawSubject)] + for _, c := range candidates { + if s.certs[c].Equal(cert) { + return true + } + } + + return false +} + // AddCert adds a certificate to a pool. func (s *CertPool) AddCert(cert *Certificate) { if cert == nil { @@ -59,10 +88,8 @@ func (s *CertPool) AddCert(cert *Certificate) { } // Check that the certificate isn't being added twice. - for _, c := range s.certs { - if c.Equal(cert) { - return - } + if s.contains(cert) { + return } n := len(s.certs) @@ -77,7 +104,7 @@ func (s *CertPool) AddCert(cert *Certificate) { } // AppendCertsFromPEM attempts to parse a series of PEM encoded certificates. -// It appends any certificates found to s and returns true if any certificates +// It appends any certificates found to s and reports whether any certificates // were successfully parsed. // // On many Linux systems, /etc/ssl/cert.pem will contain the system wide set @@ -107,10 +134,10 @@ func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) { // Subjects returns a list of the DER-encoded subjects of // all of the certificates in the pool. -func (s *CertPool) Subjects() (res [][]byte) { - res = make([][]byte, len(s.certs)) +func (s *CertPool) Subjects() [][]byte { + res := make([][]byte, len(s.certs)) for i, c := range s.certs { res[i] = c.RawSubject } - return + return res } diff --git a/vendor/github.com/google/certificate-transparency-go/x509/error.go b/vendor/github.com/google/certificate-transparency-go/x509/error.go new file mode 100644 index 0000000000000..63360ec8e26e2 --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/x509/error.go @@ -0,0 +1,230 @@ +package x509 + +import ( + "bytes" + "fmt" + "strconv" + "strings" +) + +// Error implements the error interface and describes a single error in an X.509 certificate or CRL. +type Error struct { + ID ErrorID + Category ErrCategory + Summary string + Field string + SpecRef string + SpecText string + // Fatal indicates that parsing has been aborted. + Fatal bool +} + +func (err Error) Error() string { + var msg bytes.Buffer + if err.ID != ErrInvalidID { + if err.Fatal { + msg.WriteRune('E') + } else { + msg.WriteRune('W') + } + msg.WriteString(fmt.Sprintf("%03d: ", err.ID)) + } + msg.WriteString(err.Summary) + return msg.String() +} + +// VerboseError creates a more verbose error string, including spec details. +func (err Error) VerboseError() string { + var msg bytes.Buffer + msg.WriteString(err.Error()) + if len(err.Field) > 0 || err.Category != UnknownCategory || len(err.SpecRef) > 0 || len(err.SpecText) > 0 { + msg.WriteString(" (") + needSep := false + if len(err.Field) > 0 { + msg.WriteString(err.Field) + needSep = true + } + if err.Category != UnknownCategory { + if needSep { + msg.WriteString(": ") + } + msg.WriteString(err.Category.String()) + needSep = true + } + if len(err.SpecRef) > 0 { + if needSep { + msg.WriteString(": ") + } + msg.WriteString(err.SpecRef) + needSep = true + } + if len(err.SpecText) > 0 { + if needSep { + if len(err.SpecRef) > 0 { + msg.WriteString(", ") + } else { + msg.WriteString(": ") + } + } + msg.WriteString("'") + msg.WriteString(err.SpecText) + msg.WriteString("'") + } + msg.WriteString(")") + } + + return msg.String() +} + +// ErrCategory indicates the category of an x509.Error. +type ErrCategory int + +// ErrCategory values. +const ( + UnknownCategory ErrCategory = iota + // Errors in ASN.1 encoding + InvalidASN1Encoding + InvalidASN1Content + InvalidASN1DER + // Errors in ASN.1 relative to schema + InvalidValueRange + InvalidASN1Type + UnexpectedAdditionalData + // Errors in X.509 + PoorlyFormedCertificate // Fails a SHOULD clause + MalformedCertificate // Fails a MUST clause + PoorlyFormedCRL // Fails a SHOULD clause + MalformedCRL // Fails a MUST clause + // Errors relative to CA/Browser Forum guidelines + BaselineRequirementsFailure + EVRequirementsFailure + // Other errors + InsecureAlgorithm + UnrecognizedValue +) + +func (category ErrCategory) String() string { + switch category { + case InvalidASN1Encoding: + return "Invalid ASN.1 encoding" + case InvalidASN1Content: + return "Invalid ASN.1 content" + case InvalidASN1DER: + return "Invalid ASN.1 distinguished encoding" + case InvalidValueRange: + return "Invalid value for range given in schema" + case InvalidASN1Type: + return "Invalid ASN.1 type for schema" + case UnexpectedAdditionalData: + return "Unexpected additional data present" + case PoorlyFormedCertificate: + return "Certificate does not comply with SHOULD clause in spec" + case MalformedCertificate: + return "Certificate does not comply with MUST clause in spec" + case PoorlyFormedCRL: + return "Certificate Revocation List does not comply with SHOULD clause in spec" + case MalformedCRL: + return "Certificate Revocation List does not comply with MUST clause in spec" + case BaselineRequirementsFailure: + return "Certificate does not comply with CA/BF baseline requirements" + case EVRequirementsFailure: + return "Certificate does not comply with CA/BF EV requirements" + case InsecureAlgorithm: + return "Certificate uses an insecure algorithm" + case UnrecognizedValue: + return "Certificate uses an unrecognized value" + default: + return fmt.Sprintf("Unknown (%d)", category) + } +} + +// ErrorID is an identifier for an x509.Error, to allow filtering. +type ErrorID int + +// Errors implements the error interface and holds a collection of errors found in a certificate or CRL. +type Errors struct { + Errs []Error +} + +// Error converts to a string. +func (e *Errors) Error() string { + return e.combineErrors(Error.Error) +} + +// VerboseError creates a more verbose error string, including spec details. +func (e *Errors) VerboseError() string { + return e.combineErrors(Error.VerboseError) +} + +// Fatal indicates whether e includes a fatal error +func (e *Errors) Fatal() bool { + return (e.FirstFatal() != nil) +} + +// Empty indicates whether e has no errors. +func (e *Errors) Empty() bool { + return len(e.Errs) == 0 +} + +// FirstFatal returns the first fatal error in e, or nil +// if there is no fatal error. +func (e *Errors) FirstFatal() error { + for _, err := range e.Errs { + if err.Fatal { + return err + } + } + return nil + +} + +// AddID adds the Error identified by the given id to an x509.Errors. +func (e *Errors) AddID(id ErrorID, args ...interface{}) { + e.Errs = append(e.Errs, NewError(id, args...)) +} + +func (e Errors) combineErrors(errfn func(Error) string) string { + if len(e.Errs) == 0 { + return "" + } + if len(e.Errs) == 1 { + return errfn((e.Errs)[0]) + } + var msg bytes.Buffer + msg.WriteString("Errors:") + for _, err := range e.Errs { + msg.WriteString("\n ") + msg.WriteString(errfn(err)) + } + return msg.String() +} + +// Filter creates a new Errors object with any entries from the filtered +// list of IDs removed. +func (e Errors) Filter(filtered []ErrorID) Errors { + var results Errors +eloop: + for _, v := range e.Errs { + for _, f := range filtered { + if v.ID == f { + break eloop + } + } + results.Errs = append(results.Errs, v) + } + return results +} + +// ErrorFilter builds a list of error IDs (suitable for use with Errors.Filter) from a comma-separated string. +func ErrorFilter(ignore string) []ErrorID { + var ids []ErrorID + filters := strings.Split(ignore, ",") + for _, f := range filters { + v, err := strconv.Atoi(f) + if err != nil { + continue + } + ids = append(ids, ErrorID(v)) + } + return ids +} diff --git a/vendor/github.com/google/certificate-transparency-go/x509/errors.go b/vendor/github.com/google/certificate-transparency-go/x509/errors.go new file mode 100644 index 0000000000000..ec2fe06a99fb5 --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/x509/errors.go @@ -0,0 +1,302 @@ +package x509 + +import "fmt" + +// To preserve error IDs, only append to this list, never insert. +const ( + ErrInvalidID ErrorID = iota + ErrInvalidCertList + ErrTrailingCertList + ErrUnexpectedlyCriticalCertListExtension + ErrUnexpectedlyNonCriticalCertListExtension + ErrInvalidCertListAuthKeyID + ErrTrailingCertListAuthKeyID + ErrInvalidCertListIssuerAltName + ErrInvalidCertListCRLNumber + ErrTrailingCertListCRLNumber + ErrNegativeCertListCRLNumber + ErrInvalidCertListDeltaCRL + ErrTrailingCertListDeltaCRL + ErrNegativeCertListDeltaCRL + ErrInvalidCertListIssuingDP + ErrTrailingCertListIssuingDP + ErrCertListIssuingDPMultipleTypes + ErrCertListIssuingDPInvalidFullName + ErrInvalidCertListFreshestCRL + ErrInvalidCertListAuthInfoAccess + ErrTrailingCertListAuthInfoAccess + ErrUnhandledCriticalCertListExtension + ErrUnexpectedlyCriticalRevokedCertExtension + ErrUnexpectedlyNonCriticalRevokedCertExtension + ErrInvalidRevocationReason + ErrTrailingRevocationReason + ErrInvalidRevocationInvalidityDate + ErrTrailingRevocationInvalidityDate + ErrInvalidRevocationIssuer + ErrUnhandledCriticalRevokedCertExtension + + ErrMaxID +) + +// idToError gives a template x509.Error for each defined ErrorID; where the Summary +// field may hold format specifiers that take field parameters. +var idToError map[ErrorID]Error + +var errorInfo = []Error{ + { + ID: ErrInvalidCertList, + Summary: "x509: failed to parse CertificateList: %v", + Field: "CertificateList", + SpecRef: "RFC 5280 s5.1", + Category: InvalidASN1Content, + Fatal: true, + }, + { + ID: ErrTrailingCertList, + Summary: "x509: trailing data after CertificateList", + Field: "CertificateList", + SpecRef: "RFC 5280 s5.1", + Category: InvalidASN1Content, + Fatal: true, + }, + + { + ID: ErrUnexpectedlyCriticalCertListExtension, + Summary: "x509: certificate list extension %v marked critical but expected to be non-critical", + Field: "tbsCertList.crlExtensions.*.critical", + SpecRef: "RFC 5280 s5.2", + Category: MalformedCRL, + }, + { + ID: ErrUnexpectedlyNonCriticalCertListExtension, + Summary: "x509: certificate list extension %v marked non-critical but expected to be critical", + Field: "tbsCertList.crlExtensions.*.critical", + SpecRef: "RFC 5280 s5.2", + Category: MalformedCRL, + }, + + { + ID: ErrInvalidCertListAuthKeyID, + Summary: "x509: failed to unmarshal certificate-list authority key-id: %v", + Field: "tbsCertList.crlExtensions.*.AuthorityKeyIdentifier", + SpecRef: "RFC 5280 s5.2.1", + Category: InvalidASN1Content, + Fatal: true, + }, + { + ID: ErrTrailingCertListAuthKeyID, + Summary: "x509: trailing data after certificate list auth key ID", + Field: "tbsCertList.crlExtensions.*.AuthorityKeyIdentifier", + SpecRef: "RFC 5280 s5.2.1", + Category: InvalidASN1Content, + Fatal: true, + }, + { + ID: ErrInvalidCertListIssuerAltName, + Summary: "x509: failed to parse CRL issuer alt name: %v", + Field: "tbsCertList.crlExtensions.*.IssuerAltName", + SpecRef: "RFC 5280 s5.2.2", + Category: InvalidASN1Content, + Fatal: true, + }, + { + ID: ErrInvalidCertListCRLNumber, + Summary: "x509: failed to unmarshal certificate-list crl-number: %v", + Field: "tbsCertList.crlExtensions.*.CRLNumber", + SpecRef: "RFC 5280 s5.2.3", + Category: InvalidASN1Content, + Fatal: true, + }, + { + ID: ErrTrailingCertListCRLNumber, + Summary: "x509: trailing data after certificate list crl-number", + Field: "tbsCertList.crlExtensions.*.CRLNumber", + SpecRef: "RFC 5280 s5.2.3", + Category: InvalidASN1Content, + Fatal: true, + }, + { + ID: ErrNegativeCertListCRLNumber, + Summary: "x509: negative certificate list crl-number: %d", + Field: "tbsCertList.crlExtensions.*.CRLNumber", + SpecRef: "RFC 5280 s5.2.3", + Category: MalformedCRL, + Fatal: true, + }, + { + ID: ErrInvalidCertListDeltaCRL, + Summary: "x509: failed to unmarshal certificate-list delta-crl: %v", + Field: "tbsCertList.crlExtensions.*.BaseCRLNumber", + SpecRef: "RFC 5280 s5.2.4", + Category: InvalidASN1Content, + Fatal: true, + }, + { + ID: ErrTrailingCertListDeltaCRL, + Summary: "x509: trailing data after certificate list delta-crl", + Field: "tbsCertList.crlExtensions.*.BaseCRLNumber", + SpecRef: "RFC 5280 s5.2.4", + Category: InvalidASN1Content, + Fatal: true, + }, + { + ID: ErrNegativeCertListDeltaCRL, + Summary: "x509: negative certificate list base-crl-number: %d", + Field: "tbsCertList.crlExtensions.*.BaseCRLNumber", + SpecRef: "RFC 5280 s5.2.4", + Category: MalformedCRL, + Fatal: true, + }, + { + ID: ErrInvalidCertListIssuingDP, + Summary: "x509: failed to unmarshal certificate list issuing distribution point: %v", + Field: "tbsCertList.crlExtensions.*.IssuingDistributionPoint", + SpecRef: "RFC 5280 s5.2.5", + Category: InvalidASN1Content, + Fatal: true, + }, + { + ID: ErrTrailingCertListIssuingDP, + Summary: "x509: trailing data after certificate list issuing distribution point", + Field: "tbsCertList.crlExtensions.*.IssuingDistributionPoint", + SpecRef: "RFC 5280 s5.2.5", + Category: InvalidASN1Content, + Fatal: true, + }, + { + ID: ErrCertListIssuingDPMultipleTypes, + Summary: "x509: multiple cert types set in issuing-distribution-point: user:%v CA:%v attr:%v", + Field: "tbsCertList.crlExtensions.*.IssuingDistributionPoint", + SpecRef: "RFC 5280 s5.2.5", + SpecText: "at most one of onlyContainsUserCerts, onlyContainsCACerts, and onlyContainsAttributeCerts may be set to TRUE.", + Category: MalformedCRL, + Fatal: true, + }, + { + ID: ErrCertListIssuingDPInvalidFullName, + Summary: "x509: failed to parse CRL issuing-distribution-point fullName: %v", + Field: "tbsCertList.crlExtensions.*.IssuingDistributionPoint.distributionPoint", + SpecRef: "RFC 5280 s5.2.5", + Category: InvalidASN1Content, + Fatal: true, + }, + { + ID: ErrInvalidCertListFreshestCRL, + Summary: "x509: failed to unmarshal certificate list freshestCRL: %v", + Field: "tbsCertList.crlExtensions.*.FreshestCRL", + SpecRef: "RFC 5280 s5.2.6", + Category: InvalidASN1Content, + Fatal: true, + }, + { + ID: ErrInvalidCertListAuthInfoAccess, + Summary: "x509: failed to unmarshal certificate list authority info access: %v", + Field: "tbsCertList.crlExtensions.*.AuthorityInfoAccess", + SpecRef: "RFC 5280 s5.2.7", + Category: InvalidASN1Content, + Fatal: true, + }, + { + ID: ErrTrailingCertListAuthInfoAccess, + Summary: "x509: trailing data after certificate list authority info access", + Field: "tbsCertList.crlExtensions.*.AuthorityInfoAccess", + SpecRef: "RFC 5280 s5.2.7", + Category: InvalidASN1Content, + Fatal: true, + }, + { + ID: ErrUnhandledCriticalCertListExtension, + Summary: "x509: unhandled critical extension in certificate list: %v", + Field: "tbsCertList.revokedCertificates.crlExtensions.*", + SpecRef: "RFC 5280 s5.2", + SpecText: "If a CRL contains a critical extension that the application cannot process, then the application MUST NOT use that CRL to determine the status of certificates.", + Category: MalformedCRL, + Fatal: true, + }, + + { + ID: ErrUnexpectedlyCriticalRevokedCertExtension, + Summary: "x509: revoked certificate extension %v marked critical but expected to be non-critical", + Field: "tbsCertList.revokedCertificates.crlEntryExtensions.*.critical", + SpecRef: "RFC 5280 s5.3", + Category: MalformedCRL, + }, + { + ID: ErrUnexpectedlyNonCriticalRevokedCertExtension, + Summary: "x509: revoked certificate extension %v marked non-critical but expected to be critical", + Field: "tbsCertList.revokedCertificates.crlEntryExtensions.*.critical", + SpecRef: "RFC 5280 s5.3", + Category: MalformedCRL, + }, + + { + ID: ErrInvalidRevocationReason, + Summary: "x509: failed to parse revocation reason: %v", + Field: "tbsCertList.revokedCertificates.crlEntryExtensions.*.CRLReason", + SpecRef: "RFC 5280 s5.3.1", + Category: InvalidASN1Content, + Fatal: true, + }, + { + ID: ErrTrailingRevocationReason, + Summary: "x509: trailing data after revoked certificate reason", + Field: "tbsCertList.revokedCertificates.crlEntryExtensions.*.CRLReason", + SpecRef: "RFC 5280 s5.3.1", + Category: InvalidASN1Content, + Fatal: true, + }, + { + ID: ErrInvalidRevocationInvalidityDate, + Summary: "x509: failed to parse revoked certificate invalidity date: %v", + Field: "tbsCertList.revokedCertificates.crlEntryExtensions.*.InvalidityDate", + SpecRef: "RFC 5280 s5.3.2", + Category: InvalidASN1Content, + Fatal: true, + }, + { + ID: ErrTrailingRevocationInvalidityDate, + Summary: "x509: trailing data after revoked certificate invalidity date", + Field: "tbsCertList.revokedCertificates.crlEntryExtensions.*.InvalidityDate", + SpecRef: "RFC 5280 s5.3.2", + Category: InvalidASN1Content, + Fatal: true, + }, + { + ID: ErrInvalidRevocationIssuer, + Summary: "x509: failed to parse revocation issuer %v", + Field: "tbsCertList.revokedCertificates.crlEntryExtensions.*.CertificateIssuer", + SpecRef: "RFC 5280 s5.3.3", + Category: InvalidASN1Content, + Fatal: true, + }, + { + ID: ErrUnhandledCriticalRevokedCertExtension, + Summary: "x509: unhandled critical extension in revoked certificate: %v", + Field: "tbsCertList.revokedCertificates.crlEntryExtensions.*", + SpecRef: "RFC 5280 s5.3", + SpecText: "If a CRL contains a critical CRL entry extension that the application cannot process, then the application MUST NOT use that CRL to determine the status of any certificates.", + Category: MalformedCRL, + Fatal: true, + }, +} + +func init() { + idToError = make(map[ErrorID]Error, len(errorInfo)) + for _, info := range errorInfo { + idToError[info.ID] = info + } +} + +// NewError builds a new x509.Error based on the template for the given id. +func NewError(id ErrorID, args ...interface{}) Error { + var err Error + if id >= ErrMaxID { + err.ID = id + err.Summary = fmt.Sprintf("Unknown error ID %v: args %+v", id, args) + err.Fatal = true + } else { + err = idToError[id] + err.Summary = fmt.Sprintf(err.Summary, args...) + } + return err +} diff --git a/vendor/github.com/google/certificate-transparency-go/x509/names.go b/vendor/github.com/google/certificate-transparency-go/x509/names.go new file mode 100644 index 0000000000000..3ff0b7d428671 --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/x509/names.go @@ -0,0 +1,164 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package x509 + +import ( + "fmt" + "net" + + "github.com/google/certificate-transparency-go/asn1" + "github.com/google/certificate-transparency-go/x509/pkix" +) + +const ( + // GeneralName tag values from RFC 5280, 4.2.1.6 + tagOtherName = 0 + tagRFC822Name = 1 + tagDNSName = 2 + tagX400Address = 3 + tagDirectoryName = 4 + tagEDIPartyName = 5 + tagURI = 6 + tagIPAddress = 7 + tagRegisteredID = 8 +) + +// OtherName describes a name related to a certificate which is not in one +// of the standard name formats. RFC 5280, 4.2.1.6: +// OtherName ::= SEQUENCE { +// type-id OBJECT IDENTIFIER, +// value [0] EXPLICIT ANY DEFINED BY type-id } +type OtherName struct { + TypeID asn1.ObjectIdentifier + Value asn1.RawValue +} + +// GeneralNames holds a collection of names related to a certificate. +type GeneralNames struct { + DNSNames []string + EmailAddresses []string + DirectoryNames []pkix.Name + URIs []string + IPNets []net.IPNet + RegisteredIDs []asn1.ObjectIdentifier + OtherNames []OtherName +} + +// Len returns the total number of names in a GeneralNames object. +func (gn GeneralNames) Len() int { + return (len(gn.DNSNames) + len(gn.EmailAddresses) + len(gn.DirectoryNames) + + len(gn.URIs) + len(gn.IPNets) + len(gn.RegisteredIDs) + len(gn.OtherNames)) +} + +// Empty indicates whether a GeneralNames object is empty. +func (gn GeneralNames) Empty() bool { + return gn.Len() == 0 +} + +func parseGeneralNames(value []byte, gname *GeneralNames) error { + // RFC 5280, 4.2.1.6 + // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + // + // GeneralName ::= CHOICE { + // otherName [0] OtherName, + // rfc822Name [1] IA5String, + // dNSName [2] IA5String, + // x400Address [3] ORAddress, + // directoryName [4] Name, + // ediPartyName [5] EDIPartyName, + // uniformResourceIdentifier [6] IA5String, + // iPAddress [7] OCTET STRING, + // registeredID [8] OBJECT IDENTIFIER } + var seq asn1.RawValue + var rest []byte + if rest, err := asn1.Unmarshal(value, &seq); err != nil { + return fmt.Errorf("x509: failed to parse GeneralNames: %v", err) + } else if len(rest) != 0 { + return fmt.Errorf("x509: trailing data after GeneralNames") + } + if !seq.IsCompound || seq.Tag != asn1.TagSequence || seq.Class != asn1.ClassUniversal { + return fmt.Errorf("x509: failed to parse GeneralNames sequence, tag %+v", seq) + } + + rest = seq.Bytes + for len(rest) > 0 { + var err error + rest, err = parseGeneralName(rest, gname, false) + if err != nil { + return fmt.Errorf("x509: failed to parse GeneralName: %v", err) + } + } + return nil +} + +func parseGeneralName(data []byte, gname *GeneralNames, withMask bool) ([]byte, error) { + var v asn1.RawValue + var rest []byte + var err error + rest, err = asn1.Unmarshal(data, &v) + if err != nil { + return nil, fmt.Errorf("x509: failed to unmarshal GeneralNames: %v", err) + } + switch v.Tag { + case tagOtherName: + if !v.IsCompound { + return nil, fmt.Errorf("x509: failed to unmarshal GeneralNames.otherName: not compound") + } + var other OtherName + v.FullBytes = append([]byte{}, v.FullBytes...) + v.FullBytes[0] = asn1.TagSequence | 0x20 + _, err = asn1.Unmarshal(v.FullBytes, &other) + if err != nil { + return nil, fmt.Errorf("x509: failed to unmarshal GeneralNames.otherName: %v", err) + } + gname.OtherNames = append(gname.OtherNames, other) + case tagRFC822Name: + gname.EmailAddresses = append(gname.EmailAddresses, string(v.Bytes)) + case tagDNSName: + dns := string(v.Bytes) + gname.DNSNames = append(gname.DNSNames, dns) + case tagDirectoryName: + var rdnSeq pkix.RDNSequence + if _, err := asn1.Unmarshal(v.Bytes, &rdnSeq); err != nil { + return nil, fmt.Errorf("x509: failed to unmarshal GeneralNames.directoryName: %v", err) + } + var dirName pkix.Name + dirName.FillFromRDNSequence(&rdnSeq) + gname.DirectoryNames = append(gname.DirectoryNames, dirName) + case tagURI: + gname.URIs = append(gname.URIs, string(v.Bytes)) + case tagIPAddress: + vlen := len(v.Bytes) + if withMask { + switch vlen { + case (2 * net.IPv4len), (2 * net.IPv6len): + ipNet := net.IPNet{IP: v.Bytes[0 : vlen/2], Mask: v.Bytes[vlen/2:]} + gname.IPNets = append(gname.IPNets, ipNet) + default: + return nil, fmt.Errorf("x509: invalid IP/mask length %d in GeneralNames.iPAddress", vlen) + } + } else { + switch vlen { + case net.IPv4len, net.IPv6len: + ipNet := net.IPNet{IP: v.Bytes} + gname.IPNets = append(gname.IPNets, ipNet) + default: + return nil, fmt.Errorf("x509: invalid IP length %d in GeneralNames.iPAddress", vlen) + } + } + case tagRegisteredID: + var oid asn1.ObjectIdentifier + v.FullBytes = append([]byte{}, v.FullBytes...) + v.FullBytes[0] = asn1.TagOID + _, err = asn1.Unmarshal(v.FullBytes, &oid) + if err != nil { + return nil, fmt.Errorf("x509: failed to unmarshal GeneralNames.registeredID: %v", err) + } + gname.RegisteredIDs = append(gname.RegisteredIDs, oid) + default: + return nil, fmt.Errorf("x509: failed to unmarshal GeneralName: unknown tag %d", v.Tag) + } + return rest, nil +} diff --git a/vendor/github.com/google/certificate-transparency-go/x509/nilref_nil_darwin.go b/vendor/github.com/google/certificate-transparency-go/x509/nilref_nil_darwin.go new file mode 100644 index 0000000000000..d3e8af7729c76 --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/x509/nilref_nil_darwin.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build cgo,!arm,!arm64,!ios,!go1.10 + +package x509 + +/* +#cgo CFLAGS: -mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1080 +#cgo LDFLAGS: -framework CoreFoundation -framework Security + +#include +*/ +import "C" + +// For Go versions before 1.10, nil values for Apple's CoreFoundation +// CF*Ref types were represented by nil. See: +// https://github.com/golang/go/commit/b868616b63a8 +func setNilCFRef(v *C.CFDataRef) { + *v = nil +} + +func isNilCFRef(v C.CFDataRef) bool { + return v == nil +} diff --git a/vendor/github.com/google/certificate-transparency-go/x509/nilref_zero_darwin.go b/vendor/github.com/google/certificate-transparency-go/x509/nilref_zero_darwin.go new file mode 100644 index 0000000000000..6d8ad49866e0a --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/x509/nilref_zero_darwin.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build cgo,!arm,!arm64,!ios,go1.10 + +package x509 + +/* +#cgo CFLAGS: -mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1080 +#cgo LDFLAGS: -framework CoreFoundation -framework Security + +#include +*/ +import "C" + +// For Go versions >= 1.10, nil values for Apple's CoreFoundation +// CF*Ref types are represented by zero. See: +// https://github.com/golang/go/commit/b868616b63a8 +func setNilCFRef(v *C.CFDataRef) { + *v = 0 +} + +func isNilCFRef(v C.CFDataRef) bool { + return v == 0 +} diff --git a/vendor/github.com/google/certificate-transparency/go/x509/pem_decrypt.go b/vendor/github.com/google/certificate-transparency-go/x509/pem_decrypt.go old mode 100755 new mode 100644 similarity index 93% rename from vendor/github.com/google/certificate-transparency/go/x509/pem_decrypt.go rename to vendor/github.com/google/certificate-transparency-go/x509/pem_decrypt.go index 194c81bf688c8..0388d63e14953 --- a/vendor/github.com/google/certificate-transparency/go/x509/pem_decrypt.go +++ b/vendor/github.com/google/certificate-transparency-go/x509/pem_decrypt.go @@ -42,7 +42,7 @@ type rfc1423Algo struct { } // rfc1423Algos holds a slice of the possible ways to encrypt a PEM -// block. The ivSize numbers were taken from the OpenSSL source. +// block. The ivSize numbers were taken from the OpenSSL source. var rfc1423Algos = []rfc1423Algo{{ cipher: PEMCipherDES, name: "DES-CBC", @@ -108,7 +108,10 @@ var IncorrectPasswordError = errors.New("x509: decryption password incorrect") // encrypt it and returns a slice of decrypted DER encoded bytes. It inspects // the DEK-Info header to determine the algorithm used for decryption. If no // DEK-Info header is present, an error is returned. If an incorrect password -// is detected an IncorrectPasswordError is returned. +// is detected an IncorrectPasswordError is returned. Because of deficiencies +// in the encrypted-PEM format, it's not always possible to detect an incorrect +// password. In these cases no error will be returned but the decrypted DER +// bytes will be random noise. func DecryptPEMBlock(b *pem.Block, password []byte) ([]byte, error) { dek, ok := b.Headers["DEK-Info"] if !ok { @@ -141,6 +144,10 @@ func DecryptPEMBlock(b *pem.Block, password []byte) ([]byte, error) { return nil, err } + if len(b.Bytes)%block.BlockSize() != 0 { + return nil, errors.New("x509: encrypted PEM data is not a multiple of the block size") + } + data := make([]byte, len(b.Bytes)) dec := cipher.NewCBCDecrypter(block, iv) dec.CryptBlocks(data, b.Bytes) diff --git a/vendor/github.com/google/certificate-transparency/go/x509/pkcs1.go b/vendor/github.com/google/certificate-transparency-go/x509/pkcs1.go old mode 100755 new mode 100644 similarity index 69% rename from vendor/github.com/google/certificate-transparency/go/x509/pkcs1.go rename to vendor/github.com/google/certificate-transparency-go/x509/pkcs1.go index 638bc67146856..e50e1a8517087 --- a/vendor/github.com/google/certificate-transparency/go/x509/pkcs1.go +++ b/vendor/github.com/google/certificate-transparency-go/x509/pkcs1.go @@ -6,11 +6,10 @@ package x509 import ( "crypto/rsa" - // START CT CHANGES - "github.com/google/certificate-transparency/go/asn1" - // END CT CHANGES "errors" "math/big" + + "github.com/google/certificate-transparency-go/asn1" ) // pkcs1PrivateKey is a structure which mirrors the PKCS#1 ASN.1 for an RSA private key. @@ -37,16 +36,21 @@ type pkcs1AdditionalRSAPrime struct { Coeff *big.Int } +// pkcs1PublicKey reflects the ASN.1 structure of a PKCS#1 public key. +type pkcs1PublicKey struct { + N *big.Int + E int +} + // ParsePKCS1PrivateKey returns an RSA private key from its ASN.1 PKCS#1 DER encoded form. -func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err error) { +func ParsePKCS1PrivateKey(der []byte) (*rsa.PrivateKey, error) { var priv pkcs1PrivateKey rest, err := asn1.Unmarshal(der, &priv) if len(rest) > 0 { - err = asn1.SyntaxError{Msg: "trailing data"} - return + return nil, asn1.SyntaxError{Msg: "trailing data"} } if err != nil { - return + return nil, err } if priv.Version > 1 { @@ -57,7 +61,7 @@ func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err error) { return nil, errors.New("x509: private key contains zero or negative value") } - key = new(rsa.PrivateKey) + key := new(rsa.PrivateKey) key.PublicKey = rsa.PublicKey{ E: priv.E, N: priv.N, @@ -82,7 +86,7 @@ func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err error) { } key.Precompute() - return + return key, nil } // MarshalPKCS1PrivateKey converts a private key to ASN.1 DER encoded form. @@ -117,8 +121,35 @@ func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte { return b } -// rsaPublicKey reflects the ASN.1 structure of a PKCS#1 public key. -type rsaPublicKey struct { - N *big.Int - E int +// ParsePKCS1PublicKey parses a PKCS#1 public key in ASN.1 DER form. +func ParsePKCS1PublicKey(der []byte) (*rsa.PublicKey, error) { + var pub pkcs1PublicKey + rest, err := asn1.Unmarshal(der, &pub) + if err != nil { + return nil, err + } + if len(rest) > 0 { + return nil, asn1.SyntaxError{Msg: "trailing data"} + } + + if pub.N.Sign() <= 0 || pub.E <= 0 { + return nil, errors.New("x509: public key contains zero or negative value") + } + if pub.E > 1<<31-1 { + return nil, errors.New("x509: public key contains large public exponent") + } + + return &rsa.PublicKey{ + E: pub.E, + N: pub.N, + }, nil +} + +// MarshalPKCS1PublicKey converts an RSA public key to PKCS#1, ASN.1 DER form. +func MarshalPKCS1PublicKey(key *rsa.PublicKey) []byte { + derBytes, _ := asn1.Marshal(pkcs1PublicKey{ + N: key.N, + E: key.E, + }) + return derBytes } diff --git a/vendor/github.com/google/certificate-transparency-go/x509/pkcs8.go b/vendor/github.com/google/certificate-transparency-go/x509/pkcs8.go new file mode 100644 index 0000000000000..b22338ccdf642 --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/x509/pkcs8.go @@ -0,0 +1,102 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package x509 + +import ( + "crypto/ecdsa" + "crypto/rsa" + "errors" + "fmt" + + "github.com/google/certificate-transparency-go/asn1" + "github.com/google/certificate-transparency-go/x509/pkix" +) + +// pkcs8 reflects an ASN.1, PKCS#8 PrivateKey. See +// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-8/pkcs-8v1_2.asn +// and RFC 5208. +type pkcs8 struct { + Version int + Algo pkix.AlgorithmIdentifier + PrivateKey []byte + // optional attributes omitted. +} + +// ParsePKCS8PrivateKey parses an unencrypted, PKCS#8 private key. +// See RFC 5208. +func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) { + var privKey pkcs8 + if _, err := asn1.Unmarshal(der, &privKey); err != nil { + return nil, err + } + switch { + case privKey.Algo.Algorithm.Equal(OIDPublicKeyRSA): + key, err = ParsePKCS1PrivateKey(privKey.PrivateKey) + if err != nil { + return nil, errors.New("x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error()) + } + return key, nil + + case privKey.Algo.Algorithm.Equal(OIDPublicKeyECDSA): + bytes := privKey.Algo.Parameters.FullBytes + namedCurveOID := new(asn1.ObjectIdentifier) + if _, err := asn1.Unmarshal(bytes, namedCurveOID); err != nil { + namedCurveOID = nil + } + key, err = parseECPrivateKey(namedCurveOID, privKey.PrivateKey) + if err != nil { + return nil, errors.New("x509: failed to parse EC private key embedded in PKCS#8: " + err.Error()) + } + return key, nil + + default: + return nil, fmt.Errorf("x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm) + } +} + +// MarshalPKCS8PrivateKey converts a private key to PKCS#8 encoded form. +// The following key types are supported: *rsa.PrivateKey, *ecdsa.PublicKey. +// Unsupported key types result in an error. +// +// See RFC 5208. +func MarshalPKCS8PrivateKey(key interface{}) ([]byte, error) { + var privKey pkcs8 + + switch k := key.(type) { + case *rsa.PrivateKey: + privKey.Algo = pkix.AlgorithmIdentifier{ + Algorithm: OIDPublicKeyRSA, + Parameters: asn1.NullRawValue, + } + privKey.PrivateKey = MarshalPKCS1PrivateKey(k) + + case *ecdsa.PrivateKey: + oid, ok := OIDFromNamedCurve(k.Curve) + if !ok { + return nil, errors.New("x509: unknown curve while marshalling to PKCS#8") + } + + oidBytes, err := asn1.Marshal(oid) + if err != nil { + return nil, errors.New("x509: failed to marshal curve OID: " + err.Error()) + } + + privKey.Algo = pkix.AlgorithmIdentifier{ + Algorithm: OIDPublicKeyECDSA, + Parameters: asn1.RawValue{ + FullBytes: oidBytes, + }, + } + + if privKey.PrivateKey, err = marshalECPrivateKeyWithOID(k, nil); err != nil { + return nil, errors.New("x509: failed to marshal EC private key while building PKCS#8: " + err.Error()) + } + + default: + return nil, fmt.Errorf("x509: unknown key type while marshalling PKCS#8: %T", key) + } + + return asn1.Marshal(privKey) +} diff --git a/vendor/github.com/google/certificate-transparency-go/x509/pkix/pkix.go b/vendor/github.com/google/certificate-transparency-go/x509/pkix/pkix.go new file mode 100644 index 0000000000000..ccba8761f27d2 --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/x509/pkix/pkix.go @@ -0,0 +1,288 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package pkix contains shared, low level structures used for ASN.1 parsing +// and serialization of X.509 certificates, CRL and OCSP. +package pkix + +import ( + // START CT CHANGES + "encoding/hex" + "fmt" + + "github.com/google/certificate-transparency-go/asn1" + // END CT CHANGES + "math/big" + "time" +) + +// AlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC +// 5280, section 4.1.1.2. +type AlgorithmIdentifier struct { + Algorithm asn1.ObjectIdentifier + Parameters asn1.RawValue `asn1:"optional"` +} + +type RDNSequence []RelativeDistinguishedNameSET + +var attributeTypeNames = map[string]string{ + "2.5.4.6": "C", + "2.5.4.10": "O", + "2.5.4.11": "OU", + "2.5.4.3": "CN", + "2.5.4.5": "SERIALNUMBER", + "2.5.4.7": "L", + "2.5.4.8": "ST", + "2.5.4.9": "STREET", + "2.5.4.17": "POSTALCODE", +} + +// String returns a string representation of the sequence r, +// roughly following the RFC 2253 Distinguished Names syntax. +func (r RDNSequence) String() string { + s := "" + for i := 0; i < len(r); i++ { + rdn := r[len(r)-1-i] + if i > 0 { + s += "," + } + for j, tv := range rdn { + if j > 0 { + s += "+" + } + + oidString := tv.Type.String() + typeName, ok := attributeTypeNames[oidString] + if !ok { + derBytes, err := asn1.Marshal(tv.Value) + if err == nil { + s += oidString + "=#" + hex.EncodeToString(derBytes) + continue // No value escaping necessary. + } + + typeName = oidString + } + + valueString := fmt.Sprint(tv.Value) + escaped := make([]rune, 0, len(valueString)) + + for k, c := range valueString { + escape := false + + switch c { + case ',', '+', '"', '\\', '<', '>', ';': + escape = true + + case ' ': + escape = k == 0 || k == len(valueString)-1 + + case '#': + escape = k == 0 + } + + if escape { + escaped = append(escaped, '\\', c) + } else { + escaped = append(escaped, c) + } + } + + s += typeName + "=" + string(escaped) + } + } + + return s +} + +type RelativeDistinguishedNameSET []AttributeTypeAndValue + +// AttributeTypeAndValue mirrors the ASN.1 structure of the same name in +// http://tools.ietf.org/html/rfc5280#section-4.1.2.4 +type AttributeTypeAndValue struct { + Type asn1.ObjectIdentifier + Value interface{} +} + +// AttributeTypeAndValueSET represents a set of ASN.1 sequences of +// AttributeTypeAndValue sequences from RFC 2986 (PKCS #10). +type AttributeTypeAndValueSET struct { + Type asn1.ObjectIdentifier + Value [][]AttributeTypeAndValue `asn1:"set"` +} + +// Extension represents the ASN.1 structure of the same name. See RFC +// 5280, section 4.2. +type Extension struct { + Id asn1.ObjectIdentifier + Critical bool `asn1:"optional"` + Value []byte +} + +// Name represents an X.509 distinguished name. This only includes the common +// elements of a DN. When parsing, all elements are stored in Names and +// non-standard elements can be extracted from there. When marshaling, elements +// in ExtraNames are appended and override other values with the same OID. +type Name struct { + Country, Organization, OrganizationalUnit []string + Locality, Province []string + StreetAddress, PostalCode []string + SerialNumber, CommonName string + + Names []AttributeTypeAndValue + ExtraNames []AttributeTypeAndValue +} + +func (n *Name) FillFromRDNSequence(rdns *RDNSequence) { + for _, rdn := range *rdns { + if len(rdn) == 0 { + continue + } + + for _, atv := range rdn { + n.Names = append(n.Names, atv) + value, ok := atv.Value.(string) + if !ok { + continue + } + + t := atv.Type + if len(t) == 4 && t[0] == OIDAttribute[0] && t[1] == OIDAttribute[1] && t[2] == OIDAttribute[2] { + switch t[3] { + case OIDCommonName[3]: + n.CommonName = value + case OIDSerialNumber[3]: + n.SerialNumber = value + case OIDCountry[3]: + n.Country = append(n.Country, value) + case OIDLocality[3]: + n.Locality = append(n.Locality, value) + case OIDProvince[3]: + n.Province = append(n.Province, value) + case OIDStreetAddress[3]: + n.StreetAddress = append(n.StreetAddress, value) + case OIDOrganization[3]: + n.Organization = append(n.Organization, value) + case OIDOrganizationalUnit[3]: + n.OrganizationalUnit = append(n.OrganizationalUnit, value) + case OIDPostalCode[3]: + n.PostalCode = append(n.PostalCode, value) + } + } + } + } +} + +var ( + OIDAttribute = asn1.ObjectIdentifier{2, 5, 4} + OIDCountry = asn1.ObjectIdentifier{2, 5, 4, 6} + OIDOrganization = asn1.ObjectIdentifier{2, 5, 4, 10} + OIDOrganizationalUnit = asn1.ObjectIdentifier{2, 5, 4, 11} + OIDCommonName = asn1.ObjectIdentifier{2, 5, 4, 3} + OIDSerialNumber = asn1.ObjectIdentifier{2, 5, 4, 5} + OIDLocality = asn1.ObjectIdentifier{2, 5, 4, 7} + OIDProvince = asn1.ObjectIdentifier{2, 5, 4, 8} + OIDStreetAddress = asn1.ObjectIdentifier{2, 5, 4, 9} + OIDPostalCode = asn1.ObjectIdentifier{2, 5, 4, 17} + + OIDPseudonym = asn1.ObjectIdentifier{2, 5, 4, 65} + OIDTitle = asn1.ObjectIdentifier{2, 5, 4, 12} + OIDDnQualifier = asn1.ObjectIdentifier{2, 5, 4, 46} + OIDName = asn1.ObjectIdentifier{2, 5, 4, 41} + OIDSurname = asn1.ObjectIdentifier{2, 5, 4, 4} + OIDGivenName = asn1.ObjectIdentifier{2, 5, 4, 42} + OIDInitials = asn1.ObjectIdentifier{2, 5, 4, 43} + OIDGenerationQualifier = asn1.ObjectIdentifier{2, 5, 4, 44} +) + +// appendRDNs appends a relativeDistinguishedNameSET to the given RDNSequence +// and returns the new value. The relativeDistinguishedNameSET contains an +// attributeTypeAndValue for each of the given values. See RFC 5280, A.1, and +// search for AttributeTypeAndValue. +func (n Name) appendRDNs(in RDNSequence, values []string, oid asn1.ObjectIdentifier) RDNSequence { + if len(values) == 0 || oidInAttributeTypeAndValue(oid, n.ExtraNames) { + return in + } + + s := make([]AttributeTypeAndValue, len(values)) + for i, value := range values { + s[i].Type = oid + s[i].Value = value + } + + return append(in, s) +} + +func (n Name) ToRDNSequence() (ret RDNSequence) { + ret = n.appendRDNs(ret, n.Country, OIDCountry) + ret = n.appendRDNs(ret, n.Province, OIDProvince) + ret = n.appendRDNs(ret, n.Locality, OIDLocality) + ret = n.appendRDNs(ret, n.StreetAddress, OIDStreetAddress) + ret = n.appendRDNs(ret, n.PostalCode, OIDPostalCode) + ret = n.appendRDNs(ret, n.Organization, OIDOrganization) + ret = n.appendRDNs(ret, n.OrganizationalUnit, OIDOrganizationalUnit) + if len(n.CommonName) > 0 { + ret = n.appendRDNs(ret, []string{n.CommonName}, OIDCommonName) + } + if len(n.SerialNumber) > 0 { + ret = n.appendRDNs(ret, []string{n.SerialNumber}, OIDSerialNumber) + } + for _, atv := range n.ExtraNames { + ret = append(ret, []AttributeTypeAndValue{atv}) + } + + return ret +} + +// String returns the string form of n, roughly following +// the RFC 2253 Distinguished Names syntax. +func (n Name) String() string { + return n.ToRDNSequence().String() +} + +// oidInAttributeTypeAndValue returns whether a type with the given OID exists +// in atv. +func oidInAttributeTypeAndValue(oid asn1.ObjectIdentifier, atv []AttributeTypeAndValue) bool { + for _, a := range atv { + if a.Type.Equal(oid) { + return true + } + } + return false +} + +// CertificateList represents the ASN.1 structure of the same name. See RFC +// 5280, section 5.1. Use Certificate.CheckCRLSignature to verify the +// signature. +type CertificateList struct { + TBSCertList TBSCertificateList + SignatureAlgorithm AlgorithmIdentifier + SignatureValue asn1.BitString +} + +// HasExpired reports whether certList should have been updated by now. +func (certList *CertificateList) HasExpired(now time.Time) bool { + return !now.Before(certList.TBSCertList.NextUpdate) +} + +// TBSCertificateList represents the ASN.1 structure TBSCertList. See RFC +// 5280, section 5.1. +type TBSCertificateList struct { + Raw asn1.RawContent + Version int `asn1:"optional,default:0"` + Signature AlgorithmIdentifier + Issuer RDNSequence + ThisUpdate time.Time + NextUpdate time.Time `asn1:"optional"` + RevokedCertificates []RevokedCertificate `asn1:"optional"` + Extensions []Extension `asn1:"tag:0,optional,explicit"` +} + +// RevokedCertificate represents the unnamed ASN.1 structure that makes up the +// revokedCertificates member of the TBSCertList structure. See RFC +// 5280, section 5.1. +type RevokedCertificate struct { + SerialNumber *big.Int + RevocationTime time.Time + Extensions []Extension `asn1:"optional"` +} diff --git a/vendor/github.com/google/certificate-transparency-go/x509/ptr_sysptr_windows.go b/vendor/github.com/google/certificate-transparency-go/x509/ptr_sysptr_windows.go new file mode 100644 index 0000000000000..3543e3042cfde --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/x509/ptr_sysptr_windows.go @@ -0,0 +1,20 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.11 + +package x509 + +import ( + "syscall" + "unsafe" +) + +// For Go versions >= 1.11, the ExtraPolicyPara field in +// syscall.CertChainPolicyPara is of type syscall.Pointer. See: +// https://github.com/golang/go/commit/4869ec00e87ef + +func convertToPolicyParaType(p unsafe.Pointer) syscall.Pointer { + return (syscall.Pointer)(p) +} diff --git a/vendor/github.com/google/certificate-transparency-go/x509/ptr_uint_windows.go b/vendor/github.com/google/certificate-transparency-go/x509/ptr_uint_windows.go new file mode 100644 index 0000000000000..3908833a89ded --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/x509/ptr_uint_windows.go @@ -0,0 +1,17 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.11 + +package x509 + +import "unsafe" + +// For Go versions before 1.11, the ExtraPolicyPara field in +// syscall.CertChainPolicyPara was of type uintptr. See: +// https://github.com/golang/go/commit/4869ec00e87ef + +func convertToPolicyParaType(p unsafe.Pointer) uintptr { + return uintptr(p) +} diff --git a/vendor/github.com/google/certificate-transparency-go/x509/revoked.go b/vendor/github.com/google/certificate-transparency-go/x509/revoked.go new file mode 100644 index 0000000000000..e704441639800 --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/x509/revoked.go @@ -0,0 +1,362 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package x509 + +import ( + "bytes" + "encoding/pem" + "time" + + "github.com/google/certificate-transparency-go/asn1" + "github.com/google/certificate-transparency-go/x509/pkix" +) + +var ( + // OID values for CRL extensions (TBSCertList.Extensions), RFC 5280 s5.2. + OIDExtensionCRLNumber = asn1.ObjectIdentifier{2, 5, 29, 20} + OIDExtensionDeltaCRLIndicator = asn1.ObjectIdentifier{2, 5, 29, 27} + OIDExtensionIssuingDistributionPoint = asn1.ObjectIdentifier{2, 5, 29, 28} + // OID values for CRL entry extensions (RevokedCertificate.Extensions), RFC 5280 s5.3 + OIDExtensionCRLReasons = asn1.ObjectIdentifier{2, 5, 29, 21} + OIDExtensionInvalidityDate = asn1.ObjectIdentifier{2, 5, 29, 24} + OIDExtensionCertificateIssuer = asn1.ObjectIdentifier{2, 5, 29, 29} +) + +// RevocationReasonCode represents the reason for a certificate revocation; see RFC 5280 s5.3.1. +type RevocationReasonCode asn1.Enumerated + +// RevocationReasonCode values. +var ( + Unspecified = RevocationReasonCode(0) + KeyCompromise = RevocationReasonCode(1) + CACompromise = RevocationReasonCode(2) + AffiliationChanged = RevocationReasonCode(3) + Superseded = RevocationReasonCode(4) + CessationOfOperation = RevocationReasonCode(5) + CertificateHold = RevocationReasonCode(6) + RemoveFromCRL = RevocationReasonCode(8) + PrivilegeWithdrawn = RevocationReasonCode(9) + AACompromise = RevocationReasonCode(10) +) + +// ReasonFlag holds a bitmask of applicable revocation reasons, from RFC 5280 s4.2.1.13 +type ReasonFlag int + +// ReasonFlag values. +const ( + UnusedFlag ReasonFlag = 1 << iota + KeyCompromiseFlag + CACompromiseFlag + AffiliationChangedFlag + SupersededFlag + CessationOfOperationFlag + CertificateHoldFlag + PrivilegeWithdrawnFlag + AACompromiseFlag +) + +// CertificateList represents the ASN.1 structure of the same name from RFC 5280, s5.1. +// It has the same content as pkix.CertificateList, but the contents include parsed versions +// of any extensions. +type CertificateList struct { + Raw asn1.RawContent + TBSCertList TBSCertList + SignatureAlgorithm pkix.AlgorithmIdentifier + SignatureValue asn1.BitString +} + +// ExpiredAt reports whether now is past the expiry time of certList. +func (certList *CertificateList) ExpiredAt(now time.Time) bool { + return now.After(certList.TBSCertList.NextUpdate) +} + +// Indication of whether extensions need to be critical or non-critical. Extensions that +// can be either are omitted from the map. +var listExtCritical = map[string]bool{ + // From RFC 5280... + OIDExtensionAuthorityKeyId.String(): false, // s5.2.1 + OIDExtensionIssuerAltName.String(): false, // s5.2.2 + OIDExtensionCRLNumber.String(): false, // s5.2.3 + OIDExtensionDeltaCRLIndicator.String(): true, // s5.2.4 + OIDExtensionIssuingDistributionPoint.String(): true, // s5.2.5 + OIDExtensionFreshestCRL.String(): false, // s5.2.6 + OIDExtensionAuthorityInfoAccess.String(): false, // s5.2.7 +} + +var certExtCritical = map[string]bool{ + // From RFC 5280... + OIDExtensionCRLReasons.String(): false, // s5.3.1 + OIDExtensionInvalidityDate.String(): false, // s5.3.2 + OIDExtensionCertificateIssuer.String(): true, // s5.3.3 +} + +// IssuingDistributionPoint represents the ASN.1 structure of the same +// name +type IssuingDistributionPoint struct { + DistributionPoint distributionPointName `asn1:"optional,tag:0"` + OnlyContainsUserCerts bool `asn1:"optional,tag:1"` + OnlyContainsCACerts bool `asn1:"optional,tag:2"` + OnlySomeReasons asn1.BitString `asn1:"optional,tag:3"` + IndirectCRL bool `asn1:"optional,tag:4"` + OnlyContainsAttributeCerts bool `asn1:"optional,tag:5"` +} + +// TBSCertList represents the ASN.1 structure of the same name from RFC +// 5280, section 5.1. It has the same content as pkix.TBSCertificateList +// but the extensions are included in a parsed format. +type TBSCertList struct { + Raw asn1.RawContent + Version int + Signature pkix.AlgorithmIdentifier + Issuer pkix.RDNSequence + ThisUpdate time.Time + NextUpdate time.Time + RevokedCertificates []*RevokedCertificate + Extensions []pkix.Extension + // Cracked out extensions: + AuthorityKeyID []byte + IssuerAltNames GeneralNames + CRLNumber int + BaseCRLNumber int // -1 if no delta CRL present + IssuingDistributionPoint IssuingDistributionPoint + IssuingDPFullNames GeneralNames + FreshestCRLDistributionPoint []string + OCSPServer []string + IssuingCertificateURL []string +} + +// ParseCertificateList parses a CertificateList (e.g. a CRL) from the given +// bytes. It's often the case that PEM encoded CRLs will appear where they +// should be DER encoded, so this function will transparently handle PEM +// encoding as long as there isn't any leading garbage. +func ParseCertificateList(clBytes []byte) (*CertificateList, error) { + if bytes.HasPrefix(clBytes, pemCRLPrefix) { + block, _ := pem.Decode(clBytes) + if block != nil && block.Type == pemType { + clBytes = block.Bytes + } + } + return ParseCertificateListDER(clBytes) +} + +// ParseCertificateListDER parses a DER encoded CertificateList from the given bytes. +// For non-fatal errors, this function returns both an error and a CertificateList +// object. +func ParseCertificateListDER(derBytes []byte) (*CertificateList, error) { + var errs Errors + // First parse the DER into the pkix structures. + pkixList := new(pkix.CertificateList) + if rest, err := asn1.Unmarshal(derBytes, pkixList); err != nil { + errs.AddID(ErrInvalidCertList, err) + return nil, &errs + } else if len(rest) != 0 { + errs.AddID(ErrTrailingCertList) + return nil, &errs + } + + // Transcribe the revoked certs but crack out extensions. + revokedCerts := make([]*RevokedCertificate, len(pkixList.TBSCertList.RevokedCertificates)) + for i, pkixRevoked := range pkixList.TBSCertList.RevokedCertificates { + revokedCerts[i] = parseRevokedCertificate(pkixRevoked, &errs) + if revokedCerts[i] == nil { + return nil, &errs + } + } + + certList := CertificateList{ + Raw: derBytes, + TBSCertList: TBSCertList{ + Raw: pkixList.TBSCertList.Raw, + Version: pkixList.TBSCertList.Version, + Signature: pkixList.TBSCertList.Signature, + Issuer: pkixList.TBSCertList.Issuer, + ThisUpdate: pkixList.TBSCertList.ThisUpdate, + NextUpdate: pkixList.TBSCertList.NextUpdate, + RevokedCertificates: revokedCerts, + Extensions: pkixList.TBSCertList.Extensions, + CRLNumber: -1, + BaseCRLNumber: -1, + }, + SignatureAlgorithm: pkixList.SignatureAlgorithm, + SignatureValue: pkixList.SignatureValue, + } + + // Now crack out extensions. + for _, e := range certList.TBSCertList.Extensions { + if expectCritical, present := listExtCritical[e.Id.String()]; present { + if e.Critical && !expectCritical { + errs.AddID(ErrUnexpectedlyCriticalCertListExtension, e.Id) + } else if !e.Critical && expectCritical { + errs.AddID(ErrUnexpectedlyNonCriticalCertListExtension, e.Id) + } + } + switch { + case e.Id.Equal(OIDExtensionAuthorityKeyId): + // RFC 5280 s5.2.1 + var a authKeyId + if rest, err := asn1.Unmarshal(e.Value, &a); err != nil { + errs.AddID(ErrInvalidCertListAuthKeyID, err) + } else if len(rest) != 0 { + errs.AddID(ErrTrailingCertListAuthKeyID) + } + certList.TBSCertList.AuthorityKeyID = a.Id + case e.Id.Equal(OIDExtensionIssuerAltName): + // RFC 5280 s5.2.2 + if err := parseGeneralNames(e.Value, &certList.TBSCertList.IssuerAltNames); err != nil { + errs.AddID(ErrInvalidCertListIssuerAltName, err) + } + case e.Id.Equal(OIDExtensionCRLNumber): + // RFC 5280 s5.2.3 + if rest, err := asn1.Unmarshal(e.Value, &certList.TBSCertList.CRLNumber); err != nil { + errs.AddID(ErrInvalidCertListCRLNumber, err) + } else if len(rest) != 0 { + errs.AddID(ErrTrailingCertListCRLNumber) + } + if certList.TBSCertList.CRLNumber < 0 { + errs.AddID(ErrNegativeCertListCRLNumber, certList.TBSCertList.CRLNumber) + } + case e.Id.Equal(OIDExtensionDeltaCRLIndicator): + // RFC 5280 s5.2.4 + if rest, err := asn1.Unmarshal(e.Value, &certList.TBSCertList.BaseCRLNumber); err != nil { + errs.AddID(ErrInvalidCertListDeltaCRL, err) + } else if len(rest) != 0 { + errs.AddID(ErrTrailingCertListDeltaCRL) + } + if certList.TBSCertList.BaseCRLNumber < 0 { + errs.AddID(ErrNegativeCertListDeltaCRL, certList.TBSCertList.BaseCRLNumber) + } + case e.Id.Equal(OIDExtensionIssuingDistributionPoint): + parseIssuingDistributionPoint(e.Value, &certList.TBSCertList.IssuingDistributionPoint, &certList.TBSCertList.IssuingDPFullNames, &errs) + case e.Id.Equal(OIDExtensionFreshestCRL): + // RFC 5280 s5.2.6 + if err := parseDistributionPoints(e.Value, &certList.TBSCertList.FreshestCRLDistributionPoint); err != nil { + errs.AddID(ErrInvalidCertListFreshestCRL, err) + return nil, err + } + case e.Id.Equal(OIDExtensionAuthorityInfoAccess): + // RFC 5280 s5.2.7 + var aia []authorityInfoAccess + if rest, err := asn1.Unmarshal(e.Value, &aia); err != nil { + errs.AddID(ErrInvalidCertListAuthInfoAccess, err) + } else if len(rest) != 0 { + errs.AddID(ErrTrailingCertListAuthInfoAccess) + } + + for _, v := range aia { + // GeneralName: uniformResourceIdentifier [6] IA5String + if v.Location.Tag != tagURI { + continue + } + switch { + case v.Method.Equal(OIDAuthorityInfoAccessOCSP): + certList.TBSCertList.OCSPServer = append(certList.TBSCertList.OCSPServer, string(v.Location.Bytes)) + case v.Method.Equal(OIDAuthorityInfoAccessIssuers): + certList.TBSCertList.IssuingCertificateURL = append(certList.TBSCertList.IssuingCertificateURL, string(v.Location.Bytes)) + } + // TODO(drysdale): cope with more possibilities + } + default: + if e.Critical { + errs.AddID(ErrUnhandledCriticalCertListExtension, e.Id) + } + } + } + + if errs.Fatal() { + return nil, &errs + } + if errs.Empty() { + return &certList, nil + } + return &certList, &errs +} + +func parseIssuingDistributionPoint(data []byte, idp *IssuingDistributionPoint, name *GeneralNames, errs *Errors) { + // RFC 5280 s5.2.5 + if rest, err := asn1.Unmarshal(data, idp); err != nil { + errs.AddID(ErrInvalidCertListIssuingDP, err) + } else if len(rest) != 0 { + errs.AddID(ErrTrailingCertListIssuingDP) + } + + typeCount := 0 + if idp.OnlyContainsUserCerts { + typeCount++ + } + if idp.OnlyContainsCACerts { + typeCount++ + } + if idp.OnlyContainsAttributeCerts { + typeCount++ + } + if typeCount > 1 { + errs.AddID(ErrCertListIssuingDPMultipleTypes, idp.OnlyContainsUserCerts, idp.OnlyContainsCACerts, idp.OnlyContainsAttributeCerts) + } + for _, fn := range idp.DistributionPoint.FullName { + if _, err := parseGeneralName(fn.FullBytes, name, false); err != nil { + errs.AddID(ErrCertListIssuingDPInvalidFullName, err) + } + } +} + +// RevokedCertificate represents the unnamed ASN.1 structure that makes up the +// revokedCertificates member of the TBSCertList structure from RFC 5280, s5.1. +// It has the same content as pkix.RevokedCertificate but the extensions are +// included in a parsed format. +type RevokedCertificate struct { + pkix.RevokedCertificate + // Cracked out extensions: + RevocationReason RevocationReasonCode + InvalidityDate time.Time + Issuer GeneralNames +} + +func parseRevokedCertificate(pkixRevoked pkix.RevokedCertificate, errs *Errors) *RevokedCertificate { + result := RevokedCertificate{RevokedCertificate: pkixRevoked} + for _, e := range pkixRevoked.Extensions { + if expectCritical, present := certExtCritical[e.Id.String()]; present { + if e.Critical && !expectCritical { + errs.AddID(ErrUnexpectedlyCriticalRevokedCertExtension, e.Id) + } else if !e.Critical && expectCritical { + errs.AddID(ErrUnexpectedlyNonCriticalRevokedCertExtension, e.Id) + } + } + switch { + case e.Id.Equal(OIDExtensionCRLReasons): + // RFC 5280, s5.3.1 + var reason asn1.Enumerated + if rest, err := asn1.Unmarshal(e.Value, &reason); err != nil { + errs.AddID(ErrInvalidRevocationReason, err) + } else if len(rest) != 0 { + errs.AddID(ErrTrailingRevocationReason) + } + result.RevocationReason = RevocationReasonCode(reason) + case e.Id.Equal(OIDExtensionInvalidityDate): + // RFC 5280, s5.3.2 + if rest, err := asn1.Unmarshal(e.Value, &result.InvalidityDate); err != nil { + errs.AddID(ErrInvalidRevocationInvalidityDate, err) + } else if len(rest) != 0 { + errs.AddID(ErrTrailingRevocationInvalidityDate) + } + case e.Id.Equal(OIDExtensionCertificateIssuer): + // RFC 5280, s5.3.3 + if err := parseGeneralNames(e.Value, &result.Issuer); err != nil { + errs.AddID(ErrInvalidRevocationIssuer, err) + } + default: + if e.Critical { + errs.AddID(ErrUnhandledCriticalRevokedCertExtension, e.Id) + } + } + } + return &result +} + +// CheckCertificateListSignature checks that the signature in crl is from c. +func (c *Certificate) CheckCertificateListSignature(crl *CertificateList) error { + algo := SignatureAlgorithmFromAI(crl.SignatureAlgorithm) + return c.CheckSignature(algo, crl.TBSCertList.Raw, crl.SignatureValue.RightAlign()) +} diff --git a/vendor/github.com/google/certificate-transparency/go/x509/root.go b/vendor/github.com/google/certificate-transparency-go/x509/root.go old mode 100755 new mode 100644 similarity index 65% rename from vendor/github.com/google/certificate-transparency/go/x509/root.go rename to vendor/github.com/google/certificate-transparency-go/x509/root.go index 8aae14e09ee72..787d955be47db --- a/vendor/github.com/google/certificate-transparency/go/x509/root.go +++ b/vendor/github.com/google/certificate-transparency-go/x509/root.go @@ -7,11 +7,16 @@ package x509 import "sync" var ( - once sync.Once - systemRoots *CertPool + once sync.Once + systemRoots *CertPool + systemRootsErr error ) func systemRootsPool() *CertPool { once.Do(initSystemRoots) return systemRoots } + +func initSystemRoots() { + systemRoots, systemRootsErr = loadSystemRoots() +} diff --git a/vendor/github.com/google/certificate-transparency-go/x509/root_bsd.go b/vendor/github.com/google/certificate-transparency-go/x509/root_bsd.go new file mode 100644 index 0000000000000..1371933891030 --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/x509/root_bsd.go @@ -0,0 +1,15 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build dragonfly freebsd netbsd openbsd + +package x509 + +// Possible certificate files; stop after finding one. +var certFiles = []string{ + "/usr/local/etc/ssl/cert.pem", // FreeBSD + "/etc/ssl/cert.pem", // OpenBSD + "/usr/local/share/certs/ca-root-nss.crt", // DragonFly + "/etc/openssl/certs/ca-certificates.crt", // NetBSD +} diff --git a/vendor/github.com/google/certificate-transparency-go/x509/root_cgo_darwin.go b/vendor/github.com/google/certificate-transparency-go/x509/root_cgo_darwin.go new file mode 100644 index 0000000000000..6c2f21d903cb5 --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/x509/root_cgo_darwin.go @@ -0,0 +1,252 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build cgo,!arm,!arm64,!ios + +package x509 + +/* +#cgo CFLAGS: -mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1080 +#cgo LDFLAGS: -framework CoreFoundation -framework Security + +#include +#include + +#include +#include + +// FetchPEMRootsCTX509_MountainLion is the version of FetchPEMRoots from Go 1.6 +// which still works on OS X 10.8 (Mountain Lion). +// It lacks support for admin & user cert domains. +// See golang.org/issue/16473 +int FetchPEMRootsCTX509_MountainLion(CFDataRef *pemRoots) { + if (pemRoots == NULL) { + return -1; + } + CFArrayRef certs = NULL; + OSStatus err = SecTrustCopyAnchorCertificates(&certs); + if (err != noErr) { + return -1; + } + CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0); + int i, ncerts = CFArrayGetCount(certs); + for (i = 0; i < ncerts; i++) { + CFDataRef data = NULL; + SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, i); + if (cert == NULL) { + continue; + } + // Note: SecKeychainItemExport is deprecated as of 10.7 in favor of SecItemExport. + // Once we support weak imports via cgo we should prefer that, and fall back to this + // for older systems. + err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data); + if (err != noErr) { + continue; + } + if (data != NULL) { + CFDataAppendBytes(combinedData, CFDataGetBytePtr(data), CFDataGetLength(data)); + CFRelease(data); + } + } + CFRelease(certs); + *pemRoots = combinedData; + return 0; +} + +// useOldCodeCTX509 reports whether the running machine is OS X 10.8 Mountain Lion +// or older. We only support Mountain Lion and higher, but we'll at least try our +// best on older machines and continue to use the old code path. +// +// See golang.org/issue/16473 +int useOldCodeCTX509() { + char str[256]; + size_t size = sizeof(str); + memset(str, 0, size); + sysctlbyname("kern.osrelease", str, &size, NULL, 0); + // OS X 10.8 is osrelease "12.*", 10.7 is 11.*, 10.6 is 10.*. + // We never supported things before that. + return memcmp(str, "12.", 3) == 0 || memcmp(str, "11.", 3) == 0 || memcmp(str, "10.", 3) == 0; +} + +// FetchPEMRootsCTX509 fetches the system's list of trusted X.509 root certificates. +// +// On success it returns 0 and fills pemRoots with a CFDataRef that contains the extracted root +// certificates of the system. On failure, the function returns -1. +// Additionally, it fills untrustedPemRoots with certs that must be removed from pemRoots. +// +// Note: The CFDataRef returned in pemRoots and untrustedPemRoots must +// be released (using CFRelease) after we've consumed its content. +int FetchPEMRootsCTX509(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots) { + if (useOldCodeCTX509()) { + return FetchPEMRootsCTX509_MountainLion(pemRoots); + } + + // Get certificates from all domains, not just System, this lets + // the user add CAs to their "login" keychain, and Admins to add + // to the "System" keychain + SecTrustSettingsDomain domains[] = { kSecTrustSettingsDomainSystem, + kSecTrustSettingsDomainAdmin, + kSecTrustSettingsDomainUser }; + + int numDomains = sizeof(domains)/sizeof(SecTrustSettingsDomain); + if (pemRoots == NULL) { + return -1; + } + + // kSecTrustSettingsResult is defined as CFSTR("kSecTrustSettingsResult"), + // but the Go linker's internal linking mode can't handle CFSTR relocations. + // Create our own dynamic string instead and release it below. + CFStringRef policy = CFStringCreateWithCString(NULL, "kSecTrustSettingsResult", kCFStringEncodingUTF8); + + CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0); + CFMutableDataRef combinedUntrustedData = CFDataCreateMutable(kCFAllocatorDefault, 0); + for (int i = 0; i < numDomains; i++) { + CFArrayRef certs = NULL; + OSStatus err = SecTrustSettingsCopyCertificates(domains[i], &certs); + if (err != noErr) { + continue; + } + + CFIndex numCerts = CFArrayGetCount(certs); + for (int j = 0; j < numCerts; j++) { + CFDataRef data = NULL; + CFErrorRef errRef = NULL; + CFArrayRef trustSettings = NULL; + SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, j); + if (cert == NULL) { + continue; + } + // We only want trusted certs. + int untrusted = 0; + int trustAsRoot = 0; + int trustRoot = 0; + if (i == 0) { + trustAsRoot = 1; + } else { + // Certs found in the system domain are always trusted. If the user + // configures "Never Trust" on such a cert, it will also be found in the + // admin or user domain, causing it to be added to untrustedPemRoots. The + // Go code will then clean this up. + + // Trust may be stored in any of the domains. According to Apple's + // SecTrustServer.c, "user trust settings overrule admin trust settings", + // so take the last trust settings array we find. + // Skip the system domain since it is always trusted. + for (int k = i; k < numDomains; k++) { + CFArrayRef domainTrustSettings = NULL; + err = SecTrustSettingsCopyTrustSettings(cert, domains[k], &domainTrustSettings); + if (err == errSecSuccess && domainTrustSettings != NULL) { + if (trustSettings) { + CFRelease(trustSettings); + } + trustSettings = domainTrustSettings; + } + } + if (trustSettings == NULL) { + // "this certificate must be verified to a known trusted certificate"; aka not a root. + continue; + } + for (CFIndex k = 0; k < CFArrayGetCount(trustSettings); k++) { + CFNumberRef cfNum; + CFDictionaryRef tSetting = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, k); + if (CFDictionaryGetValueIfPresent(tSetting, policy, (const void**)&cfNum)){ + SInt32 result = 0; + CFNumberGetValue(cfNum, kCFNumberSInt32Type, &result); + // TODO: The rest of the dictionary specifies conditions for evaluation. + if (result == kSecTrustSettingsResultDeny) { + untrusted = 1; + } else if (result == kSecTrustSettingsResultTrustAsRoot) { + trustAsRoot = 1; + } else if (result == kSecTrustSettingsResultTrustRoot) { + trustRoot = 1; + } + } + } + CFRelease(trustSettings); + } + + if (trustRoot) { + // We only want to add Root CAs, so make sure Subject and Issuer Name match + CFDataRef subjectName = SecCertificateCopyNormalizedSubjectContent(cert, &errRef); + if (errRef != NULL) { + CFRelease(errRef); + continue; + } + CFDataRef issuerName = SecCertificateCopyNormalizedIssuerContent(cert, &errRef); + if (errRef != NULL) { + CFRelease(subjectName); + CFRelease(errRef); + continue; + } + Boolean equal = CFEqual(subjectName, issuerName); + CFRelease(subjectName); + CFRelease(issuerName); + if (!equal) { + continue; + } + } + + // Note: SecKeychainItemExport is deprecated as of 10.7 in favor of SecItemExport. + // Once we support weak imports via cgo we should prefer that, and fall back to this + // for older systems. + err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data); + if (err != noErr) { + continue; + } + + if (data != NULL) { + if (!trustRoot && !trustAsRoot) { + untrusted = 1; + } + CFMutableDataRef appendTo = untrusted ? combinedUntrustedData : combinedData; + CFDataAppendBytes(appendTo, CFDataGetBytePtr(data), CFDataGetLength(data)); + CFRelease(data); + } + } + CFRelease(certs); + } + CFRelease(policy); + *pemRoots = combinedData; + *untrustedPemRoots = combinedUntrustedData; + return 0; +} +*/ +import "C" +import ( + "errors" + "unsafe" +) + +func loadSystemRoots() (*CertPool, error) { + roots := NewCertPool() + + var data C.CFDataRef + setNilCFRef(&data) + var untrustedData C.CFDataRef + setNilCFRef(&untrustedData) + err := C.FetchPEMRootsCTX509(&data, &untrustedData) + if err == -1 { + // TODO: better error message + return nil, errors.New("crypto/x509: failed to load darwin system roots with cgo") + } + + defer C.CFRelease(C.CFTypeRef(data)) + buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data))) + roots.AppendCertsFromPEM(buf) + if isNilCFRef(untrustedData) { + return roots, nil + } + defer C.CFRelease(C.CFTypeRef(untrustedData)) + buf = C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(untrustedData)), C.int(C.CFDataGetLength(untrustedData))) + untrustedRoots := NewCertPool() + untrustedRoots.AppendCertsFromPEM(buf) + + trustedRoots := NewCertPool() + for _, c := range roots.certs { + if !untrustedRoots.contains(c) { + trustedRoots.AddCert(c) + } + } + return trustedRoots, nil +} diff --git a/vendor/github.com/google/certificate-transparency-go/x509/root_darwin.go b/vendor/github.com/google/certificate-transparency-go/x509/root_darwin.go new file mode 100644 index 0000000000000..bc35a1cf212e5 --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/x509/root_darwin.go @@ -0,0 +1,264 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:generate go run root_darwin_arm_gen.go -output root_darwin_armx.go + +package x509 + +import ( + "bufio" + "bytes" + "crypto/sha1" + "encoding/pem" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "os/user" + "path/filepath" + "strings" + "sync" +) + +var debugExecDarwinRoots = strings.Contains(os.Getenv("GODEBUG"), "x509roots=1") + +func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { + return nil, nil +} + +// This code is only used when compiling without cgo. +// It is here, instead of root_nocgo_darwin.go, so that tests can check it +// even if the tests are run with cgo enabled. +// The linker will not include these unused functions in binaries built with cgo enabled. + +// execSecurityRoots finds the macOS list of trusted root certificates +// using only command-line tools. This is our fallback path when cgo isn't available. +// +// The strategy is as follows: +// +// 1. Run "security trust-settings-export" and "security +// trust-settings-export -d" to discover the set of certs with some +// user-tweaked trust policy. We're too lazy to parse the XML (at +// least at this stage of Go 1.8) to understand what the trust +// policy actually is. We just learn that there is _some_ policy. +// +// 2. Run "security find-certificate" to dump the list of system root +// CAs in PEM format. +// +// 3. For each dumped cert, conditionally verify it with "security +// verify-cert" if that cert was in the set discovered in Step 1. +// Without the Step 1 optimization, running "security verify-cert" +// 150-200 times takes 3.5 seconds. With the optimization, the +// whole process takes about 180 milliseconds with 1 untrusted root +// CA. (Compared to 110ms in the cgo path) +func execSecurityRoots() (*CertPool, error) { + hasPolicy, err := getCertsWithTrustPolicy() + if err != nil { + return nil, err + } + if debugExecDarwinRoots { + println(fmt.Sprintf("crypto/x509: %d certs have a trust policy", len(hasPolicy))) + } + + args := []string{"find-certificate", "-a", "-p", + "/System/Library/Keychains/SystemRootCertificates.keychain", + "/Library/Keychains/System.keychain", + } + + u, err := user.Current() + if err != nil { + if debugExecDarwinRoots { + println(fmt.Sprintf("crypto/x509: get current user: %v", err)) + } + } else { + args = append(args, + filepath.Join(u.HomeDir, "/Library/Keychains/login.keychain"), + + // Fresh installs of Sierra use a slightly different path for the login keychain + filepath.Join(u.HomeDir, "/Library/Keychains/login.keychain-db"), + ) + } + + cmd := exec.Command("/usr/bin/security", args...) + data, err := cmd.Output() + if err != nil { + return nil, err + } + + var ( + mu sync.Mutex + roots = NewCertPool() + numVerified int // number of execs of 'security verify-cert', for debug stats + ) + + blockCh := make(chan *pem.Block) + var wg sync.WaitGroup + + // Using 4 goroutines to pipe into verify-cert seems to be + // about the best we can do. The verify-cert binary seems to + // just RPC to another server with coarse locking anyway, so + // running 16 at a time for instance doesn't help at all. Due + // to the "if hasPolicy" check below, though, we will rarely + // (or never) call verify-cert on stock macOS systems, though. + // The hope is that we only call verify-cert when the user has + // tweaked their trust policy. These 4 goroutines are only + // defensive in the pathological case of many trust edits. + for i := 0; i < 4; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for block := range blockCh { + cert, err := ParseCertificate(block.Bytes) + if err != nil { + continue + } + sha1CapHex := fmt.Sprintf("%X", sha1.Sum(block.Bytes)) + + valid := true + verifyChecks := 0 + if hasPolicy[sha1CapHex] { + verifyChecks++ + if !verifyCertWithSystem(block, cert) { + valid = false + } + } + + mu.Lock() + numVerified += verifyChecks + if valid { + roots.AddCert(cert) + } + mu.Unlock() + } + }() + } + for len(data) > 0 { + var block *pem.Block + block, data = pem.Decode(data) + if block == nil { + break + } + if block.Type != "CERTIFICATE" || len(block.Headers) != 0 { + continue + } + blockCh <- block + } + close(blockCh) + wg.Wait() + + if debugExecDarwinRoots { + mu.Lock() + defer mu.Unlock() + println(fmt.Sprintf("crypto/x509: ran security verify-cert %d times", numVerified)) + } + + return roots, nil +} + +func verifyCertWithSystem(block *pem.Block, cert *Certificate) bool { + data := pem.EncodeToMemory(block) + + f, err := ioutil.TempFile("", "cert") + if err != nil { + fmt.Fprintf(os.Stderr, "can't create temporary file for cert: %v", err) + return false + } + defer os.Remove(f.Name()) + if _, err := f.Write(data); err != nil { + fmt.Fprintf(os.Stderr, "can't write temporary file for cert: %v", err) + return false + } + if err := f.Close(); err != nil { + fmt.Fprintf(os.Stderr, "can't write temporary file for cert: %v", err) + return false + } + cmd := exec.Command("/usr/bin/security", "verify-cert", "-c", f.Name(), "-l", "-L") + var stderr bytes.Buffer + if debugExecDarwinRoots { + cmd.Stderr = &stderr + } + if err := cmd.Run(); err != nil { + if debugExecDarwinRoots { + println(fmt.Sprintf("crypto/x509: verify-cert rejected %s: %q", cert.Subject.CommonName, bytes.TrimSpace(stderr.Bytes()))) + } + return false + } + if debugExecDarwinRoots { + println(fmt.Sprintf("crypto/x509: verify-cert approved %s", cert.Subject.CommonName)) + } + return true +} + +// getCertsWithTrustPolicy returns the set of certs that have a +// possibly-altered trust policy. The keys of the map are capitalized +// sha1 hex of the raw cert. +// They are the certs that should be checked against `security +// verify-cert` to see whether the user altered the default trust +// settings. This code is only used for cgo-disabled builds. +func getCertsWithTrustPolicy() (map[string]bool, error) { + set := map[string]bool{} + td, err := ioutil.TempDir("", "x509trustpolicy") + if err != nil { + return nil, err + } + defer os.RemoveAll(td) + run := func(file string, args ...string) error { + file = filepath.Join(td, file) + args = append(args, file) + cmd := exec.Command("/usr/bin/security", args...) + var stderr bytes.Buffer + cmd.Stderr = &stderr + if err := cmd.Run(); err != nil { + // If there are no trust settings, the + // `security trust-settings-export` command + // fails with: + // exit status 1, SecTrustSettingsCreateExternalRepresentation: No Trust Settings were found. + // Rather than match on English substrings that are probably + // localized on macOS, just interpret any failure to mean that + // there are no trust settings. + if debugExecDarwinRoots { + println(fmt.Sprintf("crypto/x509: exec %q: %v, %s", cmd.Args, err, stderr.Bytes())) + } + return nil + } + + f, err := os.Open(file) + if err != nil { + return err + } + defer f.Close() + + // Gather all the runs of 40 capitalized hex characters. + br := bufio.NewReader(f) + var hexBuf bytes.Buffer + for { + b, err := br.ReadByte() + isHex := ('A' <= b && b <= 'F') || ('0' <= b && b <= '9') + if isHex { + hexBuf.WriteByte(b) + } else { + if hexBuf.Len() == 40 { + set[hexBuf.String()] = true + } + hexBuf.Reset() + } + if err == io.EOF { + break + } + if err != nil { + return err + } + } + + return nil + } + if err := run("user", "trust-settings-export"); err != nil { + return nil, fmt.Errorf("dump-trust-settings (user): %v", err) + } + if err := run("admin", "trust-settings-export", "-d"); err != nil { + return nil, fmt.Errorf("dump-trust-settings (admin): %v", err) + } + return set, nil +} diff --git a/vendor/github.com/google/certificate-transparency-go/x509/root_darwin_armx.go b/vendor/github.com/google/certificate-transparency-go/x509/root_darwin_armx.go new file mode 100644 index 0000000000000..fcbbd6b170150 --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/x509/root_darwin_armx.go @@ -0,0 +1,4313 @@ +// Code generated by root_darwin_arm_gen --output root_darwin_armx.go; DO NOT EDIT. + +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build cgo +// +build darwin +// +build arm arm64 ios + +package x509 + +func loadSystemRoots() (*CertPool, error) { + p := NewCertPool() + p.AppendCertsFromPEM([]byte(systemRootsPEM)) + return p, nil +} + +const systemRootsPEM = ` +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj +YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM +GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua +BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe +3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4 +YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR +rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm +ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU +oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v +QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t +b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF +AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q +GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 +G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi +l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 +smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE +BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w +MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC +SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1 +ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv +UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX +4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9 +KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/ +gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb +rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ +51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F +be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe +KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F +v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn +fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7 +jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz +ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL +e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70 +jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz +WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V +SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j +pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX +X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok +fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R +K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU +ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU +LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT +LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3 +b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMw +MTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYD +VQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ul +CDtbKRY654eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6n +tGO0/7Gcrjyvd7ZWxbWroulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyl +dI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1Zmne3yzxbrww2ywkEtvrNTVokMsAsJch +PXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJuiGMx1I4S+6+JNM3GOGvDC ++Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8wHQYDVR0O +BBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBl +MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFk +ZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENB +IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxtZBsfzQ3duQH6lmM0MkhHma6X +7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0PhiVYrqW9yTkkz +43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY +eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJl +pz/+0WatC7xrmYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOA +WiFeIc9TVPC6b4nbqKqVz4vjccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs +IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290 +MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux +FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h +bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v +dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt +H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9 +uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX +mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX +a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN +E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0 +WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD +VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0 +Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU +cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx +IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN +AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH +YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 +6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC +Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX +c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a +mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFVTCCBD2gAwIBAgIEO/OB0DANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQGEwJj +aDEOMAwGA1UEChMFYWRtaW4xETAPBgNVBAsTCFNlcnZpY2VzMSIwIAYDVQQLExlD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0aWVzMRYwFAYDVQQDEw1BZG1pbi1Sb290LUNB +MB4XDTAxMTExNTA4NTEwN1oXDTIxMTExMDA3NTEwN1owbDELMAkGA1UEBhMCY2gx +DjAMBgNVBAoTBWFkbWluMREwDwYDVQQLEwhTZXJ2aWNlczEiMCAGA1UECxMZQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdGllczEWMBQGA1UEAxMNQWRtaW4tUm9vdC1DQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMvgr0QUIv5qF0nyXZ3PXAJi +C4C5Wr+oVTN7oxIkXkxvO0GJToM9n7OVJjSmzBL0zJ2HXj0MDRcvhSY+KiZZc6Go +vDvr5Ua481l7ILFeQAFtumeza+vvxeL5Nd0Maga2miiacLNAKXbAcUYRa0Ov5VZB +++YcOYNNt/aisWbJqA2y8He+NsEgJzK5zNdayvYXQTZN+7tVgWOck16Da3+4FXdy +fH1NCWtZlebtMKtERtkVAaVbiWW24CjZKAiVfggjsiLo3yVMPGj3budLx5D9hEEm +vlyDOtcjebca+AcZglppWMX/iHIrx7740y0zd6cWEqiLIcZCrnpkr/KzwO135GkC +AwEAAaOCAf0wggH5MA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIASBkTCBjjCBiwYI +YIV0AREDAQAwfzArBggrBgEFBQcCAjAfGh1UaGlzIGlzIHRoZSBBZG1pbi1Sb290 +LUNBIENQUzBQBggrBgEFBQcCARZEaHR0cDovL3d3dy5pbmZvcm1hdGlrLmFkbWlu +LmNoL1BLSS9saW5rcy9DUFNfMl8xNl83NTZfMV8xN18zXzFfMC5wZGYwfwYDVR0f +BHgwdjB0oHKgcKRuMGwxFjAUBgNVBAMTDUFkbWluLVJvb3QtQ0ExIjAgBgNVBAsT +GUNlcnRpZmljYXRpb24gQXV0aG9yaXRpZXMxETAPBgNVBAsTCFNlcnZpY2VzMQ4w +DAYDVQQKEwVhZG1pbjELMAkGA1UEBhMCY2gwHQYDVR0OBBYEFIKf+iNzIPGXi7JM +Tb5CxX9mzWToMIGZBgNVHSMEgZEwgY6AFIKf+iNzIPGXi7JMTb5CxX9mzWTooXCk +bjBsMQswCQYDVQQGEwJjaDEOMAwGA1UEChMFYWRtaW4xETAPBgNVBAsTCFNlcnZp +Y2VzMSIwIAYDVQQLExlDZXJ0aWZpY2F0aW9uIEF1dGhvcml0aWVzMRYwFAYDVQQD +Ew1BZG1pbi1Sb290LUNBggQ784HQMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0B +AQUFAAOCAQEAeE96XCYRpy6umkPKXDWCRn7INo96ZrWpMggcDORuofHIwdTkgOeM +vWOxDN/yuT7CC3FAaUajbPRbDw0hRMcqKz0aC8CgwcyIyhw/rFK29mfNTG3EviP9 +QSsEbnelFnjpm1wjz4EaBiFjatwpUbI6+Zv3XbEt9QQXBn+c6DeFLe4xvC4B+MTr +a440xTk59pSYux8OHhEvqIwHCkiijGqZhTS3KmGFeBopaR+dJVBRBMoXwzk4B3Hn +0Zib1dEYFZa84vPJZyvxCbLOnPRDJgH6V2uQqbG+6DXVaf/wORVOvF/wzzv0viM/ +RWbEtJZdvo8N3sdtCULzifnxP/V0T9+4ZQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP +Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr +ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL +MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1 +yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr +VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/ +nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG +XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj +vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt +Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g +N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC +nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y +YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua +kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL +QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp +6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG +yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i +QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO +tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu +QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ +Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u +olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48 +x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC +VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ +cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ +BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt +VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D +0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9 +ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G +A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs +aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I +flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz +dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG +A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U +cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf +qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ +JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ ++jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS +s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5 +HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7 +70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG +V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S +qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S +5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia +C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX +OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE +FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2 +KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B +8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ +MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc +0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ +u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF +u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH +YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8 +GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO +RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e +KeC2uAloGRwYQw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIIGDCCBgCgAwIBAgIGAT8vMXfmMA0GCSqGSIb3DQEBCwUAMIIBCjELMAkGA1UE +BhMCRVMxEjAQBgNVBAgMCUJhcmNlbG9uYTFYMFYGA1UEBwxPQmFyY2Vsb25hIChz +ZWUgY3VycmVudCBhZGRyZXNzIGF0IGh0dHA6Ly93d3cuYW5mLmVzL2VzL2FkZHJl +c3MtZGlyZWNjaW9uLmh0bWwgKTEnMCUGA1UECgweQU5GIEF1dG9yaWRhZCBkZSBD +ZXJ0aWZpY2FjaW9uMRcwFQYDVQQLDA5BTkYgQ2xhc2UgMSBDQTEaMBgGCSqGSIb3 +DQEJARYLaW5mb0BhbmYuZXMxEjAQBgNVBAUTCUc2MzI4NzUxMDEbMBkGA1UEAwwS +QU5GIEdsb2JhbCBSb290IENBMB4XDTEzMDYxMDE3NDUzOFoXDTMzMDYwNTE3NDUz +OFowggEKMQswCQYDVQQGEwJFUzESMBAGA1UECAwJQmFyY2Vsb25hMVgwVgYDVQQH +DE9CYXJjZWxvbmEgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgaHR0cDovL3d3dy5h +bmYuZXMvZXMvYWRkcmVzcy1kaXJlY2Npb24uaHRtbCApMScwJQYDVQQKDB5BTkYg +QXV0b3JpZGFkIGRlIENlcnRpZmljYWNpb24xFzAVBgNVBAsMDkFORiBDbGFzZSAx +IENBMRowGAYJKoZIhvcNAQkBFgtpbmZvQGFuZi5lczESMBAGA1UEBRMJRzYzMjg3 +NTEwMRswGQYDVQQDDBJBTkYgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQDHPi9xy4wynbcUbWjorVUgQKeUAVh937J7P37XmsfH +ZLOBZKIIlhhCtRwnDlg7x+BUvtJOTkIbEGMujDygUQ2s3HDYr5I41hTyM2Pl0cq2 +EuSGEbPIHb3dEX8NAguFexM0jqNjrreN3hM2/+TOkAxSdDJP2aMurlySC5zwl47K +ZLHtcVrkZnkDa0o5iN24hJT4vBDT4t2q9khQ+qb1D8KgCOb02r1PxWXu3vfd6Ha2 +mkdB97iGuEh5gO2n4yOmFS5goFlVA2UdPbbhJsb8oKVKDd+YdCKGQDCkQyG4AjmC +YiNm3UPG/qtftTH5cWri67DlLtm6fyUFOMmO6NSh0RtR745pL8GyWJUanyq/Q4bF +HQB21E+WtTsCaqjGaoFcrBunMypmCd+jUZXl27TYENRFbrwNdAh7m2UztcIyb+Sg +VJFyfvVsBQNvnp7GPimVxXZNc4VpxEXObRuPWQN1oZN/90PcZVqTia/SHzEyTryL +ckhiLG3jZiaFZ7pTZ5I9wti9Pn+4kOHvE3Y/4nEnUo4mTxPX9pOlinF+VCiybtV2 +u1KSlc+YaIM7VmuyndDZCJRXm3v0/qTE7t5A5fArZl9lvibigMbWB8fpD+c1GpGH +Eo8NRY0lkaM+DkIqQoaziIsz3IKJrfdKaq9bQMSlIfameKBZ8fNYTBZrH9KZAIhz +YwIDAQABo4IBfjCCAXowHQYDVR0OBBYEFIf6nt9SdnXsSUogb1twlo+d77sXMB8G +A1UdIwQYMBaAFIf6nt9SdnXsSUogb1twlo+d77sXMA8GA1UdEwEB/wQFMAMBAf8w +DgYDVR0PAQH/BAQDAgEGMIIBFQYDVR0RBIIBDDCCAQiCEWh0dHA6Ly93d3cuYW5m +LmVzgQtpbmZvQGFuZi5lc6SB5TCB4jE0MDIGA1UECQwrR3JhbiBWaWEgZGUgbGVz +IENvcnRzIENhdGFsYW5lcy4gOTk2LiAwODAxODESMBAGA1UEBwwJQmFyY2Vsb25h +MScwJQYDVQQKDB5BTkYgQXV0b3JpZGFkIGRlIENlcnRpZmljYWNpb24xEjAQBgNV +BAUTCUc2MzI4NzUxMDFZMFcGA1UECwxQSW5zY3JpdGEgZW4gZWwgTWluaXN0ZXJp +byBkZWwgSW50ZXJpb3IgZGUgRXNwYcOxYSBjb24gZWwgbnVtZXJvIG5hY2lvbmFs +IDE3MS40NDMwDQYJKoZIhvcNAQELBQADggIBAIgR9tFTZ9BCYg+HViMxOfF0MHN2 +Pe/eC128ARdS+GH8A4thtbqiH/SOYbWofO/0zssHhNKa5iQEj45lCAb8BANpWJMD +nWkPr6jq2+50a6d0MMgSS2l1rvjSF+3nIrEuicshHXSTi3q/vBLKr7uGKMVFaM68 +XAropIwk6ndlA0JseARSPsbetv7ALESMIZAxlHV1TcctYHd0bB3c/Jz+PLszJQqs +Cg/kBPo2D111OXZkIY8W/fJuG9veR783khAK2gUnC0zLLCNsYzEbdGt8zUmBsAsM +cGxqGm6B6vDXd65OxWqw13xdq/24+5R8Ng1PF9tvfjZkUFBF30CxjWur7P90WiKI +G7IGfr6BE1NgXlhEQQu4F+HizB1ypEPzGWltecXQ4yOzO+H0WfFTjLTYX6VSveyW +DQV18ixF8M4tHP/SwNE+yyv2b2JJ3/3RpxjtFlLk+opJ574x0gD/dMJuWTH0JqVY +3PbRfE1jIxFpk164Qz/Xp7H7w7f6xh+tQCkBs3PUYmnGIZcPwq44Q6JHlCNsKx4K +hxfggTvRCk4w79cUID45c2qDsRCqTPoOo/cbOpcfVhbH9LdMORpmuLwNogRZEUSE +fWpqR9q+0kcQf4zGSWIURIyDrogdpDgoHDxktqgMgc+qA4ZE2WQl1D8hmev53A46 +lUSrWUiWfDXtK3ux +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFkjCCA3qgAwIBAgIIAeDltYNno+AwDQYJKoZIhvcNAQEMBQAwZzEbMBkGA1UE +AwwSQXBwbGUgUm9vdCBDQSAtIEcyMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMw +HhcNMTQwNDMwMTgxMDA5WhcNMzkwNDMwMTgxMDA5WjBnMRswGQYDVQQDDBJBcHBs +ZSBSb290IENBIC0gRzIxJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBANgREkhI2imKScUcx+xuM23+TfvgHN6s +XuI2pyT5f1BrTM65MFQn5bPW7SXmMLYFN14UIhHF6Kob0vuy0gmVOKTvKkmMXT5x +ZgM4+xb1hYjkWpIMBDLyyED7Ul+f9sDx47pFoFDVEovy3d6RhiPw9bZyLgHaC/Yu +OQhfGaFjQQscp5TBhsRTL3b2CtcM0YM/GlMZ81fVJ3/8E7j4ko380yhDPLVoACVd +J2LT3VXdRCCQgzWTxb+4Gftr49wIQuavbfqeQMpOhYV4SbHXw8EwOTKrfl+q04tv +ny0aIWhwZ7Oj8ZhBbZF8+NfbqOdfIRqMM78xdLe40fTgIvS/cjTf94FNcX1RoeKz +8NMoFnNvzcytN31O661A4T+B/fc9Cj6i8b0xlilZ3MIZgIxbdMYs0xBTJh0UT8TU +gWY8h2czJxQI6bR3hDRSj4n4aJgXv8O7qhOTH11UL6jHfPsNFL4VPSQ08prcdUFm +IrQB1guvkJ4M6mL4m1k8COKWNORj3rw31OsMiANDC1CvoDTdUE0V+1ok2Az6DGOe +HwOx4e7hqkP0ZmUoNwIx7wHHHtHMn23KVDpA287PT0aLSmWaasZobNfMmRtHsHLD +d4/E92GcdB/O/WuhwpyUgquUoue9G7q5cDmVF8Up8zlYNPXEpMZ7YLlmQ1A/bmH8 +DvmGqmAMQ0uVAgMBAAGjQjBAMB0GA1UdDgQWBBTEmRNsGAPCe8CjoA1/coB6HHcm +jTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQwF +AAOCAgEAUabz4vS4PZO/Lc4Pu1vhVRROTtHlznldgX/+tvCHM/jvlOV+3Gp5pxy+ +8JS3ptEwnMgNCnWefZKVfhidfsJxaXwU6s+DDuQUQp50DhDNqxq6EWGBeNjxtUVA +eKuowM77fWM3aPbn+6/Gw0vsHzYmE1SGlHKy6gLti23kDKaQwFd1z4xCfVzmMX3z +ybKSaUYOiPjjLUKyOKimGY3xn83uamW8GrAlvacp/fQ+onVJv57byfenHmOZ4VxG +/5IFjPoeIPmGlFYl5bRXOJ3riGQUIUkhOb9iZqmxospvPyFgxYnURTbImHy99v6Z +SYA7LNKmp4gDBDEZt7Y6YUX6yfIjyGNzv1aJMbDZfGKnexWoiIqrOEDCzBL/FePw +N983csvMmOa/orz6JopxVtfnJBtIRD6e/J/JzBrsQzwBvDR4yGn1xuZW7AYJNpDr +FEobXsmII9oDMJELuDY++ee1KG++P+w8j2Ud5cAeh6Squpj9kuNsJnfdBrRkBof0 +Tta6SqoWqPQFZ2aWuuJVecMsXUmPgEkrihLHdoBR37q9ZV0+N0djMenl9MU/S60E +inpxLK8JQzcPqOMyT/RFtm2XNuyE9QoB6he7hY1Ck3DDUOUUi78/w0EP3SIEIwiK +um1xRKtzCTrJ+VKACd+66eYWyi4uTLLT3OUEVLLUNIAytbwPF+E= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICQzCCAcmgAwIBAgIILcX8iNLFS5UwCgYIKoZIzj0EAwMwZzEbMBkGA1UEAwwS +QXBwbGUgUm9vdCBDQSAtIEczMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9u +IEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwHhcN +MTQwNDMwMTgxOTA2WhcNMzkwNDMwMTgxOTA2WjBnMRswGQYDVQQDDBJBcHBsZSBS +b290IENBIC0gRzMxJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9y +aXR5MRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzB2MBAGByqGSM49 +AgEGBSuBBAAiA2IABJjpLz1AcqTtkyJygRMc3RCV8cWjTnHcFBbZDuWmBSp3ZHtf +TjjTuxxEtX/1H7YyYl3J6YRbTzBPEVoA/VhYDKX1DyxNB0cTddqXl5dvMVztK517 +IDvYuVTZXpmkOlEKMaNCMEAwHQYDVR0OBBYEFLuw3qFYM4iapIqZ3r6966/ayySr +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2gA +MGUCMQCD6cHEFl4aXTQY2e3v9GwOAEZLuN+yRhHFD/3meoyhpmvOwgPUnPWTxnS4 +at+qIxUCMG1mihDK1A3UT82NQz60imOlM27jbdoXt2QfyFMm+YhidDkLF1vLUagM +6BgD56KyKA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEuzCCA6OgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQGEwJVUzET +MBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwHhcNMDYwNDI1MjE0 +MDM2WhcNMzUwMjA5MjE0MDM2WjBiMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQXBw +bGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkx +FjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDkkakJH5HbHkdQ6wXtXnmELes2oldMVeyLGYne+Uts9QerIjAC6Bg+ ++FAJ039BqJj50cpmnCRrEdCju+QbKsMflZ56DKRHi1vUFjczy8QPTc4UadHJGXL1 +XQ7Vf1+b8iUDulWPTV0N8WQ1IxVLFVkds5T39pyez1C6wVhQZ48ItCD3y6wsIG9w +tj8BMIy3Q88PnT3zK0koGsj+zrW5DtleHNbLPbU6rfQPDgCSC7EhFi501TwN22IW +q6NxkkdTVcGvL0Gz+PvjcM3mo0xFfh9Ma1CWQYnEdGILEINBhzOKgbEwWOxaBDKM +aLOPHd5lc/9nXmW8Sdh2nzMUZaF3lMktAgMBAAGjggF6MIIBdjAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUK9BpR5R2Cf70a40uQKb3 +R01/CF4wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wggERBgNVHSAE +ggEIMIIBBDCCAQAGCSqGSIb3Y2QFATCB8jAqBggrBgEFBQcCARYeaHR0cHM6Ly93 +d3cuYXBwbGUuY29tL2FwcGxlY2EvMIHDBggrBgEFBQcCAjCBthqBs1JlbGlhbmNl +IG9uIHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0 +YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRlcm1zIGFuZCBj +b25kaXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFuZCBjZXJ0aWZp +Y2F0aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMA0GCSqGSIb3DQEBBQUAA4IBAQBc +NplMLXi37Yyb3PN3m/J20ncwT8EfhYOFG5k9RzfyqZtAjizUsZAS2L70c5vu0mQP +y3lPNNiiPvl4/2vIB+x9OYOLUyDTOMSxv5pPCmv/K/xZpwUJfBdAVhEedNO3iyM7 +R6PVbyTi69G3cN8PReEnyvFteO3ntRcXqNx+IjXKJdXZD9Zr1KIkIxH3oayPc4Fg +xhtbCS+SsvhESPBgOJ4V9T0mZyCKM2r3DYLP3uujL/lTaltkwGMzd/c6ByxW69oP +IQ7aunMZT7XZNn/Bh1XZp5m5MkL72NVxnn6hUrcbvZNCJBIqxw8dtk2cXmPIS4AX +UKqK1drk/NAJBzewdXUh +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFujCCBKKgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhjELMAkGA1UEBhMCVVMx +HTAbBgNVBAoTFEFwcGxlIENvbXB1dGVyLCBJbmMuMS0wKwYDVQQLEyRBcHBsZSBD +b21wdXRlciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxKTAnBgNVBAMTIEFwcGxlIFJv +b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTA1MDIxMDAwMTgxNFoXDTI1MDIx +MDAwMTgxNFowgYYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRBcHBsZSBDb21wdXRl +ciwgSW5jLjEtMCsGA1UECxMkQXBwbGUgQ29tcHV0ZXIgQ2VydGlmaWNhdGUgQXV0 +aG9yaXR5MSkwJwYDVQQDEyBBcHBsZSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0 +eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOSRqQkfkdseR1DrBe1e +eYQt6zaiV0xV7IsZid75S2z1B6siMALoGD74UAnTf0GomPnRymacJGsR0KO75Bsq +wx+VnnoMpEeLW9QWNzPLxA9NzhRp0ckZcvVdDtV/X5vyJQO6VY9NXQ3xZDUjFUsV +WR2zlPf2nJ7PULrBWFBnjwi0IPfLrCwgb3C2PwEwjLdDzw+dPfMrSSgayP7OtbkO +2V4c1ss9tTqt9A8OAJILsSEWLnTVPA3bYharo3GSR1NVwa8vQbP4++NwzeajTEV+ +H0xrUJZBicR0YgsQg0GHM4qBsTBY7FoEMoxos48d3mVz/2deZbxJ2HafMxRloXeU +yS0CAwEAAaOCAi8wggIrMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/ +MB0GA1UdDgQWBBQr0GlHlHYJ/vRrjS5ApvdHTX8IXjAfBgNVHSMEGDAWgBQr0GlH +lHYJ/vRrjS5ApvdHTX8IXjCCASkGA1UdIASCASAwggEcMIIBGAYJKoZIhvdjZAUB +MIIBCTBBBggrBgEFBQcCARY1aHR0cHM6Ly93d3cuYXBwbGUuY29tL2NlcnRpZmlj +YXRlYXV0aG9yaXR5L3Rlcm1zLmh0bWwwgcMGCCsGAQUFBwICMIG2GoGzUmVsaWFu +Y2Ugb24gdGhpcyBjZXJ0aWZpY2F0ZSBieSBhbnkgcGFydHkgYXNzdW1lcyBhY2Nl +cHRhbmNlIG9mIHRoZSB0aGVuIGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5k +IGNvbmRpdGlvbnMgb2YgdXNlLCBjZXJ0aWZpY2F0ZSBwb2xpY3kgYW5kIGNlcnRp +ZmljYXRpb24gcHJhY3RpY2Ugc3RhdGVtZW50cy4wRAYDVR0fBD0wOzA5oDegNYYz +aHR0cHM6Ly93d3cuYXBwbGUuY29tL2NlcnRpZmljYXRlYXV0aG9yaXR5L3Jvb3Qu +Y3JsMFUGCCsGAQUFBwEBBEkwRzBFBggrBgEFBQcwAoY5aHR0cHM6Ly93d3cuYXBw +bGUuY29tL2NlcnRpZmljYXRlYXV0aG9yaXR5L2Nhc2lnbmVycy5odG1sMA0GCSqG +SIb3DQEBBQUAA4IBAQCd2i0oWC99dgS5BNM+zrdmY06PL9T+S61yvaM5xlJNBZhS +9YlRASR5vhoy9+VEi0tEBzmC1lrKtCBe2a4VXR2MHTK/ODFiSF3H4ZCx+CRA+F9Y +m1FdV53B5f88zHIhbsTp6aF31ywXJsM/65roCwO66bNKcuszCVut5mIxauivL9Wv +Hld2j383LS4CXN1jyfJxuCZA3xWNdUQ/eb3mHZnhQyw+rW++uaT+DjUZUWOxw961 +kj5ReAFziqQjyqSI8R5cH0EWLX6VCqrpiUGYGxrdyyC/R14MJsVVNU3GMIuZZxTH +CR+6R8faAQmHJEKVvRNgGQrv6n8Obs3BREM6StXj +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIID9zCCAt+gAwIBAgILMTI1MzcyODI4MjgwDQYJKoZIhvcNAQELBQAwWDELMAkG +A1UEBhMCSlAxHDAaBgNVBAoTE0phcGFuZXNlIEdvdmVybm1lbnQxDTALBgNVBAsT +BEdQS0kxHDAaBgNVBAMTE0FwcGxpY2F0aW9uQ0EyIFJvb3QwHhcNMTMwMzEyMTUw +MDAwWhcNMzMwMzEyMTUwMDAwWjBYMQswCQYDVQQGEwJKUDEcMBoGA1UEChMTSmFw +YW5lc2UgR292ZXJubWVudDENMAsGA1UECxMER1BLSTEcMBoGA1UEAxMTQXBwbGlj +YXRpb25DQTIgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKaq +rSVl1gAR1uh6dqr05rRL88zDUrSNrKZPtZJxb0a11a2LEiIXJc5F6BR6hZrkIxCo ++rFnUOVtR+BqiRPjrq418fRCxQX3TZd+PCj8sCaRHoweOBqW3FhEl2LjMsjRFUFN +dZh4vqtoqV7tR76kuo6hApfek3SZbWe0BSXulMjtqqS6MmxCEeu+yxcGkOGThchk +KM4fR8fAXWDudjbcMztR63vPctgPeKgZggiQPhqYjY60zxU2pm7dt+JNQCBT2XYq +0HisifBPizJtROouurCp64ndt295D6uBbrjmiykLWa+2SQ1RLKn9nShjZrhwlXOa +2Po7M7xCQhsyrLEy+z0CAwEAAaOBwTCBvjAdBgNVHQ4EFgQUVqesqgIdsqw9kA6g +by5Bxnbne9owDgYDVR0PAQH/BAQDAgEGMHwGA1UdEQR1MHOkcTBvMQswCQYDVQQG +EwJKUDEYMBYGA1UECgwP5pel5pys5Zu95pS/5bqcMRswGQYDVQQLDBLmlL/lupzo +qo3oqLzln7rnm6QxKTAnBgNVBAMMIOOCouODl+ODquOCseODvOOCt+ODp+ODs0NB +MiBSb290MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAH+aCXWs +B9FydC53VzDCBJzUgKaD56WgG5/+q/OAvdVKo6GPtkxgEefK4WCB10jBIFmlYTKL +nZ6X02aD2mUuWD7b5S+lzYxzplG+WCigeVxpL0PfY7KJR8q73rk0EWOgDiUX5Yf0 +HbCwpc9BqHTG6FPVQvSCLVMJEWgmcZR1E02qdog8dLHW40xPYsNJTE5t8XB+w3+m +Bcx4m+mB26jIx1ye/JKSLaaX8ji1bnOVDMA/zqaUMLX6BbfeniCq/BNkyYq6ZO/i +Y+TYmK5rtT6mVbgzPixy+ywRAPtbFi+E0hOe+gXFwctyTiLdhMpLvNIthhoEdlkf +SUJiOxMfFui61/0= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE +AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG +EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM +FUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMC +REUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVhTuXbyo7LjvPpvMp +Nb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr54rM +VD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+ +SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ +4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L +cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQi +eowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgG +A1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3 +DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8j +vZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kP +DpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pc +maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D +lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv +KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UE +BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h +cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEy +MzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg +Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9 +thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM +cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG +L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i +NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h +X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b +m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy +Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja +EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T +KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF +6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh +OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNHDhpkLzCBpgYD +VR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp +cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBv +ACAAZABlACAAbABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBl +AGwAbwBuAGEAIAAwADgAMAAxADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF +661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx51tkljYyGOylMnfX40S2wBEqgLk9 +am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qkR71kMrv2JYSiJ0L1 +ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaPT481 +PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS +3a/DTg4fJl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5k +SeTy36LssUzAKh3ntLFlosS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF +3dvd6qJ2gHN99ZwExEWN57kci57q13XRcrHedUTnQn3iV2t93Jm8PYMo6oCTjcVM +ZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoRsaS8I8nkvof/uZS2+F0g +StRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTDKCOM/icz +Q0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQB +jLMi6Et8Vcad+qMUu2WFbm5PEn4KPJ2V +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIJmzCCB4OgAwIBAgIBATANBgkqhkiG9w0BAQwFADCCAR4xPjA8BgNVBAMTNUF1 +dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIFJhaXogZGVsIEVzdGFkbyBWZW5lem9s +YW5vMQswCQYDVQQGEwJWRTEQMA4GA1UEBxMHQ2FyYWNhczEZMBcGA1UECBMQRGlz +dHJpdG8gQ2FwaXRhbDE2MDQGA1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0 +aWZpY2FjaW9uIEVsZWN0cm9uaWNhMUMwQQYDVQQLEzpTdXBlcmludGVuZGVuY2lh +IGRlIFNlcnZpY2lvcyBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMSUwIwYJ +KoZIhvcNAQkBFhZhY3JhaXpAc3VzY2VydGUuZ29iLnZlMB4XDTEwMTIyMjE4MDgy +MVoXDTMwMTIxNzIzNTk1OVowggEeMT4wPAYDVQQDEzVBdXRvcmlkYWQgZGUgQ2Vy +dGlmaWNhY2lvbiBSYWl6IGRlbCBFc3RhZG8gVmVuZXpvbGFubzELMAkGA1UEBhMC +VkUxEDAOBgNVBAcTB0NhcmFjYXMxGTAXBgNVBAgTEERpc3RyaXRvIENhcGl0YWwx +NjA0BgNVBAoTLVNpc3RlbWEgTmFjaW9uYWwgZGUgQ2VydGlmaWNhY2lvbiBFbGVj +dHJvbmljYTFDMEEGA1UECxM6U3VwZXJpbnRlbmRlbmNpYSBkZSBTZXJ2aWNpb3Mg +ZGUgQ2VydGlmaWNhY2lvbiBFbGVjdHJvbmljYTElMCMGCSqGSIb3DQEJARYWYWNy +YWl6QHN1c2NlcnRlLmdvYi52ZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC +ggIBAME77xNS8ZlW47RsBeEaaRZhJoZ4rw785UAFCuPZOAVMqNS1wMYqzy95q6Gk +UO81ER/ugiQX/KMcq/4HBn83fwdYWxPZfwBfK7BP2p/JsFgzYeFP0BXOLmvoJIzl +Jb6FW+1MPwGBjuaZGFImWZsSmGUclb51mRYMZETh9/J5CLThR1exStxHQptwSzra +zNFpkQY/zmj7+YZNA9yDoroVFv6sybYOZ7OxNDo7zkSLo45I7gMwtxqWZ8VkJZkC +8+p0dX6mkhUT0QAV64Zc9HsZiH/oLhEkXjhrgZ28cF73MXIqLx1fyM4kPH1yOJi/ +R72nMwL7D+Sd6mZgI035TxuHXc2/uOwXfKrrTjaJDz8Jp6DdessOkxIgkKXRjP+F +K3ze3n4NUIRGhGRtyvEjK95/2g02t6PeYiYVGur6ruS49n0RAaSS0/LJb6XzaAAe +0mmO2evnEqxIKwy2mZRNPfAVW1l3wCnWiUwryBU6OsbFcFFrQm+00wOicXvOTHBM +aiCVAVZTb9RSLyi+LJ1llzJZO3pq3IRiiBj38Nooo+2ZNbMEciSgmig7YXaUcmud +SVQvLSL+Yw+SqawyezwZuASbp7d/0rutQ59d81zlbMt3J7yB567rT2IqIydQ8qBW +k+fmXzghX+/FidYsh/aK+zZ7Wy68kKHuzEw1Vqkat5DGs+VzAgMBAAGjggLeMIIC +2jASBgNVHRMBAf8ECDAGAQH/AgECMDcGA1UdEgQwMC6CD3N1c2NlcnRlLmdvYi52 +ZaAbBgVghl4CAqASDBBSSUYtRy0yMDAwNDAzNi0wMB0GA1UdDgQWBBStuyIdxuDS +Aaj9dlBSk+2YwU2u0zCCAVAGA1UdIwSCAUcwggFDgBStuyIdxuDSAaj9dlBSk+2Y +wU2u06GCASakggEiMIIBHjE+MDwGA1UEAxM1QXV0b3JpZGFkIGRlIENlcnRpZmlj +YWNpb24gUmFpeiBkZWwgRXN0YWRvIFZlbmV6b2xhbm8xCzAJBgNVBAYTAlZFMRAw +DgYDVQQHEwdDYXJhY2FzMRkwFwYDVQQIExBEaXN0cml0byBDYXBpdGFsMTYwNAYD +VQQKEy1TaXN0ZW1hIE5hY2lvbmFsIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25p +Y2ExQzBBBgNVBAsTOlN1cGVyaW50ZW5kZW5jaWEgZGUgU2VydmljaW9zIGRlIENl +cnRpZmljYWNpb24gRWxlY3Ryb25pY2ExJTAjBgkqhkiG9w0BCQEWFmFjcmFpekBz +dXNjZXJ0ZS5nb2IudmWCAQEwDgYDVR0PAQH/BAQDAgEGMDcGA1UdEQQwMC6CD3N1 +c2NlcnRlLmdvYi52ZaAbBgVghl4CAqASDBBSSUYtRy0yMDAwNDAzNi0wMFQGA1Ud +HwRNMEswJKAioCCGHmhodHA6Ly93d3cuc3VzY2VydGUuZ29iLnZlL2xjcjAjoCGg +H4YdbGRhcDovL2FjcmFpei5zdXNjZXJ0ZS5nb2IudmUwNwYIKwYBBQUHAQEEKzAp +MCcGCCsGAQUFBzABhhtoaHRwOi8vb2NzcC5zdXNjZXJ0ZS5nb2IudmUwQAYDVR0g +BDkwNzA1BgVghl4BAjAsMCoGCCsGAQUFBwIBFh5odHRwOi8vd3d3LnN1c2NlcnRl +LmdvYi52ZS9kcGMwDQYJKoZIhvcNAQEMBQADggIBAK4qy/zmZ9zBwfW3yOYtLcBT +Oy4szJyPz7/RhNH3bPVH7HbDTGpi6JZ4YXdXMBeJE5qBF4a590Kgj8Rlnltt+Rbo +OFQOU1UDqKuTdBsA//Zry5899fmn8jBUkg4nh09jhHHbLlaUScdz704Zz2+UVg7i +s/r3Legxap60KzmdrmTAE9VKte1TQRgavQwVX5/2mO/J+SCas//UngI+h8SyOucq +mjudYEgBrZaodUsagUfn/+AzFNrGLy+al+5nZeHb8JnCfLHWS0M9ZyhgoeO/czyn +99+5G93VWNv4zfc4KiavHZKrkn8F9pg0ycIZh+OwPT/RE2zq4gTazBMlP3ACIe/p +olkNaOEa8KvgzW96sjBZpMW49zFmyINYkcj+uaNCJrVGsXgdBmkuRGJNWFZ9r0cG +woIaxViFBypsz045r1ESfYPlfDOavBhZ/giR/Xocm9CHkPRY2BApMMR0DUCyGETg +Ql+L3kfdTKzuDjUp2DM9FqysQmaM81YDZufWkMhlZPfHwC7KbNougoLroa5Umeos +bqAXWmk46SwIdWRPLLqbUpDTKooynZKpSYIkkotdgJoVZUUCY+RCO8jsVPEU6ece +SxztNUm5UOta1OJPMwSAKRHOo3ilVb9c6lAixDdvV8MeNbqe6asM1mpCHWbJ/0rg +5Ls9Cxx8hracyp0ev7b0 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX +DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y +ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy +VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr +mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr +IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK +mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu +XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy +dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye +jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 +BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 +DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 +9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx +jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 +Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz +ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS +R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIIKv++n6Lw6YcwDQYJKoZIhvcNAQEFBQAwKDELMAkGA1UE +BhMCQkUxGTAXBgNVBAMTEEJlbGdpdW0gUm9vdCBDQTIwHhcNMDcxMDA0MTAwMDAw +WhcNMjExMjE1MDgwMDAwWjAoMQswCQYDVQQGEwJCRTEZMBcGA1UEAxMQQmVsZ2l1 +bSBSb290IENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZzQh6S +/3UPi790hqc/7bIYLS2X+an7mEoj39WN4IzGMhwWLQdC1i22bi+n9fzGhYJdld61 +IgDMqFNAn68KNaJ6x+HK92AQZw6nUHMXU5WfIp8MXW+2QbyM69odRr2nlL/zGsvU ++40OHjPIltfsjFPekx40HopQcSZYtF3CiInaYNKJIT/e1wEYNm7hLHADBGXvmAYr +XR5i3FVr/mZkIV/4L+HXmymvb82fqgxG0YjFnaKVn6w/Fa7yYd/vw2uaItgscf1Y +HewApDgglVrH1Tdjuk+bqv5WRi5j2Qsj1Yr6tSPwiRuhFA0m2kHwOI8w7QUmecFL +TqG4flVSOmlGhHUCAwEAAaOBuzCBuDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zBCBgNVHSAEOzA5MDcGBWA4CQEBMC4wLAYIKwYBBQUHAgEWIGh0dHA6 +Ly9yZXBvc2l0b3J5LmVpZC5iZWxnaXVtLmJlMB0GA1UdDgQWBBSFiuv0xbu+DlkD +lN7WgAEV4xCcOTARBglghkgBhvhCAQEEBAMCAAcwHwYDVR0jBBgwFoAUhYrr9MW7 +vg5ZA5Te1oABFeMQnDkwDQYJKoZIhvcNAQEFBQADggEBAFHYhd27V2/MoGy1oyCc +UwnzSgEMdL8rs5qauhjyC4isHLMzr87lEwEnkoRYmhC598wUkmt0FoqW6FHvv/pK +JaeJtmMrXZRY0c8RcrYeuTlBFk0pvDVTC9rejg7NqZV3JcqUWumyaa7YwBO+mPyW +nIR/VRPmPIfjvCCkpDZoa01gZhz5v6yAlGYuuUGK02XThIAC71AdXkbc98m6tTR8 +KvPG2F9fVJ3bTc0R5/0UAoNmXsimABKgX77OFP67H6dh96tK8QYUn8pJQsKpvO2F +sauBQeYNxUJpU4c5nUwfAA4+Bw11V0SoU7Q2dmSZ3G7rPUZuFF1eR1ONeE3gJ7uO +hXY= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr +6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV +L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91 +1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx +MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ +QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB +arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr +Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi +FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS +P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN +9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz +uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h +9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t +OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo ++fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7 +KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2 +DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us +H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ +I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7 +5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h +3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz +Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y +ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E +N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9 +tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX +0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c +/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X +KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY +zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS +O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D +34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP +K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3 +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv +Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj +QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS +IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2 +HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa +O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv +033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u +dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE +kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41 +3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD +u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq +4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNV +BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu +MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQy +MDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx +EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjEw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy3QRk +D2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/o +OI7bm+V8u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3A +fQ+lekLZWnDZv6fXARz2m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJe +IgpFy4QxTaz+29FHuvlglzmxZcfe+5nkCiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8n +oc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTaYVKvJrT1cU/J19IG32PK +/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6vpmumwKj +rckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD +3AjLLhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE +7cderVC6xkGbrPAXZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkC +yC2fg69naQanMVXVz0tv/wQFx1isXxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLd +qvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ04IwDQYJKoZI +hvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR +xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaA +SfX8MPWbTx9BLxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXo +HqJPYNcHKfyyo6SdbhWSVhlMCrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpB +emOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5GfbVSUZP/3oNn6z4eGBrxEWi1CXYBmC +AMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85YmLLW1AL14FABZyb +7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKSds+x +DzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvk +F7mGnjixlAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqF +a3qdnom2piiZk4hA9z7NUaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsT +Q6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJa7+h89n07eLw4+1knj0vllJPgFOL +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV +BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu +MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy +MDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx +EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjIw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbCw3Oe +NcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNH +PWSb6WiaxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3I +x2ymrdMxp7zo5eFm1tL7A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbe +QTg06ov80egEFGEtQX6sx3dOy1FU+16SGBsEWmjGycT6txOgmLcRK7fWV8x8nhfR +yyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqVg8NTEQxzHQuyRpDRQjrO +QG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa5Beny912 +H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJ +QfYEkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUD +i/ZnWejBBhG93c+AAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORs +nLMOPReisjQS1n6yqEm70XooQL6iFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1 +rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5uQu0wDQYJKoZI +hvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqf +GopTpti72TVVsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkb +lvdhuDvEK7Z4bLQjb/D907JedR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka ++elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W81k/BfDxujRNt+3vrMNDcTa/F1bal +TFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjxmHHEt38OFdAlab0i +nSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01utI3 +gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr +G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os +zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x +L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV +BAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4X +DTA3MDYyOTE1MTMwNVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQ +BgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwIQ2VydGlnbmEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7qXOEm7RFHYeGifBZ4 +QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyHGxny +gQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbw +zBfsV1/pogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q +130yGLMLLGq/jj8UEYkgDncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2 +JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKfIrjxwo1p3Po6WAbfAgMBAAGjgbwwgbkw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQtCRZvgHyUtVF9lo53BEw +ZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJBgNVBAYT +AkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzj +AQ/JSP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG +9w0BAQUFAAOCAQEAhQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8h +bV6lUmPOEvjvKtpv6zf+EwLHyzs+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFnc +fca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1kluPBS1xp81HlDQwY9qcEQCYsuu +HWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY1gkIl2PlwS6w +t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjET +MBEGA1UEChMKQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAk +BgNVBAMMHUNlcnRpbm9taXMgLSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4 +Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNl +cnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYwJAYDVQQDDB1DZXJ0 +aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jY +F1AMnmHawE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N +8y4oH3DfVS9O7cdxbwlyLu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWe +rP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K +/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92NjMD2AR5vpTESOH2VwnHu +7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9qc1pkIuVC +28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6 +lSTClrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1E +nn1So2+WLhl+HPNbxxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB +0iSVL1N6aaLwD4ZFjliCK0wi1F6g530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql09 +5gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna4NH4+ej9Uji29YnfAgMBAAGj +WzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBQN +jLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ +KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9s +ov3/4gbIOZ/xWqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZM +OH8oMDX/nyNTt7buFHAAQCvaR6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q +619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40nJ+U8/aGH88bc62UeYdocMMzpXDn +2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1BCxMjidPJC+iKunqj +o3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjvJL1v +nxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG +5ERQL1TEqkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWq +pdEdnV1j6CTmNhTih60bWfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZb +dsLLO7XSAPCjDuGtbkD326C00EauFddEwk01+dIL8hf2rGbVJLJP0RyZwG71fet0 +BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/vgt2Fl43N+bYdJeimUV5 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFkjCCA3qgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJGUjET +MBEGA1UEChMKQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxHTAb +BgNVBAMTFENlcnRpbm9taXMgLSBSb290IENBMB4XDTEzMTAyMTA5MTcxOFoXDTMz +MTAyMTA5MTcxOFowWjELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMx +FzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMR0wGwYDVQQDExRDZXJ0aW5vbWlzIC0g +Um9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANTMCQosP5L2 +fxSeC5yaah1AMGT9qt8OHgZbn1CF6s2Nq0Nn3rD6foCWnoR4kkjW4znuzuRZWJfl +LieY6pOod5tK8O90gC3rMB+12ceAnGInkYjwSond3IjmFPnVAy//ldu9n+ws+hQV +WZUKxkd8aRi5pwP5ynapz8dvtF4F/u7BUrJ1Mofs7SlmO/NKFoL21prbcpjp3vDF +TKWrteoB4owuZH9kb/2jJZOLyKIOSY008B/sWEUuNKqEUL3nskoTuLAPrjhdsKkb +5nPJWqHZZkCqqU2mNAKthH6yI8H7KsZn9DS2sJVqM09xRLWtwHkziOC/7aOgFLSc +CbAK42C++PhmiM1b8XcF4LVzbsF9Ri6OSyemzTUK/eVNfaoqoynHWmgE6OXWk6Ri +wsXm9E/G+Z8ajYJJGYrKWUM66A0ywfRMEwNvbqY/kXPLynNvEiCL7sCCeN5LLsJJ +wx3tFvYk9CcbXFcx3FXuqB5vbKziRcxXV4p1VxngtViZSTYxPDMBbRZKzbgqg4SG +m/lg0h9tkQPTYKbVPZrdd5A9NaSfD171UkRpucC63M9933zZxKyGIjK8e2uR73r4 +F2iw4lNVYC2vPsKD2NkJK/DAZNuHi5HMkesE/Xa0lZrmFAYb1TQdvtj/dBxThZng +WVJKYe2InmtJiUZ+IFrZ50rlau7SZRFDAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTvkUz1pcMw6C8I6tNxIqSSaHh0 +2TAfBgNVHSMEGDAWgBTvkUz1pcMw6C8I6tNxIqSSaHh02TANBgkqhkiG9w0BAQsF +AAOCAgEAfj1U2iJdGlg+O1QnurrMyOMaauo++RLrVl89UM7g6kgmJs95Vn6RHJk/ +0KGRHCwPT5iVWVO90CLYiF2cN/z7ZMF4jIuaYAnq1fohX9B0ZedQxb8uuQsLrbWw +F6YSjNRieOpWauwK0kDDPAUwPk2Ut59KA9N9J0u2/kTO+hkzGm2kQtHdzMjI1xZS +g081lLMSVX3l4kLr5JyTCcBMWwerx20RoFAXlCOotQqSD7J6wWAsOMwaplv/8gzj +qh8c3LigkyfeY+N/IZ865Z764BNqdeuWXGKRlI5nU7aJ+BIJy29SWwNyhlCVCNSN +h4YVH5Uk2KRvms6knZtt0rJ2BobGVgjF6wnaNsIbW0G+YSrjcOa4pvi2WsS9Iff/ +ql+hbHY5ZtbqTFXhADObE5hjyW/QASAJN1LnDE8+zbz1X5YnpyACleAu6AdBBR8V +btaw5BngDwKTACdyxYvRVB9dSsNAl35VpnzBMwQUAR1JIGkLGZOdblgi90AMRgwj +Y/M50n92Uaf0yKHxDHYiI0ZSKS3io0EHVmmY0gUJvGnHWmHNj4FgFU2A3ZDifcRQ +8ow7bkrHxuaAKzyBvBGAFhAn1/DNP3nMcyrDflOR1m749fPH0FFNjkulW+YZFzvW +gQncItzujrnEj1PhZ7szuIgVRs/taTX/dQ1G885x4cVrhkIGuUE= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYT +AlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBD +QTAeFw0wNjA3MDQxNzIwMDRaFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJP +MREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7IJUqOtdu0KBuqV5Do +0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHHrfAQ +UySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5d +RdY4zTW2ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQ +OA7+j0xbm0bqQfWwCHTD0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwv +JoIQ4uNllAoEwF73XVv4EOLQunpL+943AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08C +AwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAcYwHQYDVR0O +BBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IBAQA+0hyJ +LjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecY +MnQ8SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ +44gx+FkagQnIl6Z0x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6I +Jd1hJyMctTEHBDa0GpC9oHRxUIltvBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNw +i/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5VaZVDADlN +9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD +QTAeFw0wMjA2MTExMDQ2MzlaFw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD +QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6xwS7TT3zNJc4YPk/E +jG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdLkKWo +ePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GI +ULdtlkIJ89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapu +Ob7kky/ZR6By6/qmW6/KUz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUg +AKpoC6EahQGcxEZjgoi2IrHu/qpGWX7PNSzVttpd90gzFFS269lvzs2I1qsb2pY7 +HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEA +uI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+GXYkHAQa +TOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTg +xSvgGrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1q +CjqTE5s7FCMTY5w/0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5x +O/fIR/RpbxXyEV6DHpx8Uq79AtoSqFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs +6GAqm4VKQPNriiTsBhYscw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCB +gDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu +QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIG +A1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMCIYDzIwMTExMDA2MDgz +OTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQTDEiMCAGA1UEChMZ +VW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3 +b3JrIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWA +DGSdhhuWZGc/IjoedQF97/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn +0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+oCgCXhVqqndwpyeI1B+twTUrWwbNWuKFB +OJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40bRr5HMNUuctHFY9rnY3lE +fktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2puTRZCr+E +Sv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1m +o130GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02i +sx7QBlrd9pPPV3WZ9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOW +OZV7bIBaTxNyxtd9KXpEulKkKtVBRgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgez +Tv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pyehizKV/Ma5ciSixqClnrDvFAS +adgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vMBhBgu4M1t15n +3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQ +F/xlhMcQSZDe28cmk4gmb3DWAl45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTf +CVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuAL55MYIR4PSFk1vtBHxgP58l1cb29 +XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMoclm2q8KMZiYcdywm +djWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tMpkT/ +WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jb +AoJnwTnbw3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksq +P/ujmv5zMnHCnsZy4YpoJ/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Ko +b7a6bINDd82Kkhehnlt4Fj1F4jNy3eFmypnTycUm/Q1oBEauttmbjL4ZvrHG8hnj +XALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLXis7VmFxWlgPF7ncGNf/P +5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7zAYspsbi +DrW5viSP +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM +MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D +ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU +cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3 +WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg +Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw +IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH +UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM +TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU +BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM +kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x +AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV +HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y +sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL +I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8 +J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY +VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD +TjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y +aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkx +MjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5j +aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJP +T1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnVBU03 +sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpL +TIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5 +/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp +7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRz +EpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgt +hxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvP +a931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqot +aK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNg +TnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfV +PKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hv +cWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj/i39KNAL +tbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd +BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB +ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObT +ej/tUxPQ4i9qecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdL +jOztUmCypAbqTuv0axn96/Ua4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBS +ESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qy +P5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfXBDrDMlI1Dlb4pd19 +xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjnaH9d +Ci77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN +5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe +/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+Z +AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ +5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYD +VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 +IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 +MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xKTAnBgNVBAMTIENoYW1iZXJz +IG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEyMjk1MFoXDTM4MDcz +MTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBj +dXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIw +EAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEp +MCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW9 +28sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKAXuFixrYp4YFs8r/lfTJq +VKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorjh40G072Q +DuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR +5gN/ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfL +ZEFHcpOrUMPrCXZkNNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05a +Sd+pZgvMPMZ4fKecHePOjlO+Bd5gD2vlGts/4+EhySnB8esHnFIbAURRPHsl18Tl +UlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331lubKgdaX8ZSD6e2wsWsSaR6s ++12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ0wlf2eOKNcx5 +Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj +ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAx +hduub+84Mxh2EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNV +HQ4EFgQU+SSsD7K1+HnA+mCIG8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1 ++HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpN +YWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29t +L2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVy +ZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAt +IDIwMDiCCQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRV +HSAAMCowKAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20w +DQYJKoZIhvcNAQEFBQADggIBAJASryI1wqM58C7e6bXpeHxIvj99RZJe6dqxGfwW +PJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH3qLPaYRgM+gQDROpI9CF +5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbURWpGqOt1 +glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaH +FoI6M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2 +pSB7+R5KBWIBpih1YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MD +xvbxrN8y8NmBGuScvfaAFPDRLLmF9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QG +tjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcKzBIKinmwPQN/aUv0NCB9szTq +jktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvGnrDQWzilm1De +fhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg +OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZ +d0jQ +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEn +MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL +ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMg +b2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAxNjEzNDNaFw0zNzA5MzAxNjEzNDRa +MH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZpcm1hIFNBIENJRiBB +ODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3JnMSIw +IAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0B +AQEFAAOCAQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtb +unXF/KGIJPov7coISjlUxFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0d +BmpAPrMMhe5cG3nCYsS4No41XQEMIwRHNaqbYE6gZj3LJgqcQKH0XZi/caulAGgq +7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jWDA+wWFjbw2Y3npuRVDM3 +0pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFVd9oKDMyX +roDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIG +A1UdEwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5j +aGFtYmVyc2lnbi5vcmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p +26EpW1eLTXYGduHRooowDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIA +BzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hhbWJlcnNpZ24ub3JnMCcGA1Ud +EgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYDVR0gBFEwTzBN +BgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz +aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEB +AAxBl8IahsAifJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZd +p0AJPaxJRUXcLo0waLIJuvvDL8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi +1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wNUPf6s+xCX6ndbcj0dc97wXImsQEc +XCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/nADydb47kMgkdTXg0 +eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1erfu +tGWaIZDgqtCYvDi1czyL+Nw= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDQzCCAiugAwIBAgIQX/h7KCtU3I1CoxW1aMmt/zANBgkqhkiG9w0BAQUFADA1 +MRYwFAYDVQQKEw1DaXNjbyBTeXN0ZW1zMRswGQYDVQQDExJDaXNjbyBSb290IENB +IDIwNDgwHhcNMDQwNTE0MjAxNzEyWhcNMjkwNTE0MjAyNTQyWjA1MRYwFAYDVQQK +Ew1DaXNjbyBTeXN0ZW1zMRswGQYDVQQDExJDaXNjbyBSb290IENBIDIwNDgwggEg +MA0GCSqGSIb3DQEBAQUAA4IBDQAwggEIAoIBAQCwmrmrp68Kd6ficba0ZmKUeIhH +xmJVhEAyv8CrLqUccda8bnuoqrpu0hWISEWdovyD0My5jOAmaHBKeN8hF570YQXJ +FcjPFto1YYmUQ6iEqDGYeJu5Tm8sUxJszR2tKyS7McQr/4NEb7Y9JHcJ6r8qqB9q +VvYgDxFUl4F1pyXOWWqCZe+36ufijXWLbvLdT6ZeYpzPEApk0E5tzivMW/VgpSdH +jWn0f84bcN5wGyDWbs2mAag8EtKpP6BrXruOIIt6keO1aO6g58QBdKhTCytKmg9l +Eg6CTY5j/e/rmxrbU6YTYK/CfdfHbBcl1HP7R2RQgYCUTOG/rksc35LtLgXfAgED +o1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUJ/PI +FR5umgIJFq0roIlgX9p7L6owEAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZIhvcNAQEF +BQADggEBAJ2dhISjQal8dwy3U8pORFBi71R803UXHOjgxkhLtv5MOhmBVrBW7hmW +Yqpao2TB9k5UM8Z3/sUcuuVdJcr18JOagxEu5sv4dEX+5wW4q+ffy0vhN4TauYuX +cB7w4ovXsNgOnbFp1iqRe6lJT37mjpXYgyc81WhJDtSd9i7rp77rMKSsH0T8lasz +Bvt9YAretIpjsJyp8qS5UwGH0GikJ3+r/+n6yUA4iGe0OcaEb1fJU9u6ju7AQ7L4 +CYNu/2bPPu8Xs1gYJQk0XuPL1hS27PKSb3TkL4Eq1ZKR4OCXPDJoBYVL0fdX4lId +kxpUnwVwwEpxYB5DC2Ae/qPOgRnhCzU= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAw +PTELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFz +cyAyIFByaW1hcnkgQ0EwHhcNOTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9 +MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2VydHBsdXMxGzAZBgNVBAMTEkNsYXNz +IDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANxQ +ltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR5aiR +VhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyL +kcAbmXuZVg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCd +EgETjdyAYveVqUSISnFOYFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yas +H7WLO7dDWWuwJKZtkIvEcupdM5i3y95ee++U8Rs+yskhwcWYAqqi9lt3m/V+llU0 +HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRMECDAGAQH/AgEKMAsGA1Ud +DwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJYIZIAYb4 +QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMu +Y29tL0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/ +AN9WM2K191EBkOvDP9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8 +yfFC82x/xXp8HVGIutIKPidd3i1RTtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMR +FcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+7UCmnYR0ObncHoUW2ikbhiMA +ybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW//1IMwrh3KWB +kJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7 +l7+ijrRU +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDoTCCAomgAwIBAgIQKTZHquOKrIZKI1byyrdhrzANBgkqhkiG9w0BAQUFADBO +MQswCQYDVQQGEwJ1czEYMBYGA1UEChMPVS5TLiBHb3Zlcm5tZW50MQ0wCwYDVQQL +EwRGQkNBMRYwFAYDVQQDEw1Db21tb24gUG9saWN5MB4XDTA3MTAxNTE1NTgwMFoX +DTI3MTAxNTE2MDgwMFowTjELMAkGA1UEBhMCdXMxGDAWBgNVBAoTD1UuUy4gR292 +ZXJubWVudDENMAsGA1UECxMERkJDQTEWMBQGA1UEAxMNQ29tbW9uIFBvbGljeTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJeNvTMn5K1b+3i9L0dHbsd4 +6ZOcpN7JHP0vGzk4rEcXwH53KQA7Ax9oD81Npe53uCxiazH2+nIJfTApBnznfKM9 +hBiKHa4skqgf6F5PjY7rPxr4nApnnbBnTfAu0DDew5SwoM8uCjR/VAnTNr2kSVdS +c+md/uRIeUYbW40y5KVIZPMiDZKdCBW/YDyD90ciJSKtKXG3d+8XyaK2lF7IMJCk +FEhcVlcLQUwF1CpMP64Sm1kRdXAHImktLNMxzJJ+zM2kfpRHqpwJCPZLr1LoakCR +xVW9QLHIbVeGlRfmH3O+Ry4+i0wXubklHKVSFzYIWcBCvgortFZRPBtVyYyQd+sC +AwEAAaN7MHkwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFC9Yl9ipBZilVh/72at17wI8NjTHMBIGCSsGAQQBgjcVAQQFAgMBAAEwIwYJ +KwYBBAGCNxUCBBYEFHa3YJbdFFYprHWF03BjwbxHhhyLMA0GCSqGSIb3DQEBBQUA +A4IBAQBgrvNIFkBypgiIybxHLCRLXaCRc+1leJDwZ5B6pb8KrbYq+Zln34PFdx80 +CTj5fp5B4Ehg/uKqXYeI6oj9XEWyyWrafaStsU+/HA2fHprA1RRzOCuKeEBuMPdi +4c2Z/FFpZ2wR3bgQo2jeJqVW/TZsN5hs++58PGxrcD/3SDcJjwtCga1GRrgLgwb0 +Gzigf0/NC++DiYeXHIowZ9z9VKEDfgHLhUyxCynDvux84T8PCVI8L6eaSP436REG +WOE2QYrEtr+O3c5Ks7wawM36GpnScZv6z7zyxFSjiDV2zBssRm8MtNHDYXaSdBHq +S4CNHIkRi+xb/xfJSPzn4AYR4oRe +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB +gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV +BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw +MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl +YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P +RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3 +UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI +2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8 +Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp ++2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+ +DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O +nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW +/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g +PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u +QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY +SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv +IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4 +zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd +BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB +ZQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT +IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw +MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy +ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N +T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR +FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J +cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW +BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm +fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv +GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB +hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV +BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5 +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT +EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR +6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X +pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC +9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV +/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf +Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z ++pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w +qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah +SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC +u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf +Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq +crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl +wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM +4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV +2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna +FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ +CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK +boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke +jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL +S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb +QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl +0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB +NVOFBkpdn627G190 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDkzCCAnugAwIBAgIQFBOWgxRVjOp7Y+X8NId3RDANBgkqhkiG9w0BAQUFADA0 +MRMwEQYDVQQDEwpDb21TaWduIENBMRAwDgYDVQQKEwdDb21TaWduMQswCQYDVQQG +EwJJTDAeFw0wNDAzMjQxMTMyMThaFw0yOTAzMTkxNTAyMThaMDQxEzARBgNVBAMT +CkNvbVNpZ24gQ0ExEDAOBgNVBAoTB0NvbVNpZ24xCzAJBgNVBAYTAklMMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8ORUaSvTx49qROR+WCf4C9DklBKK +8Rs4OC8fMZwG1Cyn3gsqrhqg455qv588x26i+YtkbDqthVVRVKU4VbirgwTyP2Q2 +98CNQ0NqZtH3FyrV7zb6MBBC11PN+fozc0yz6YQgitZBJzXkOPqUm7h65HkfM/sb +2CEJKHxNGGleZIp6GZPKfuzzcuc3B1hZKKxC+cX/zT/npfo4sdAMx9lSGlPWgcxC +ejVb7Us6eva1jsz/D3zkYDaHL63woSV9/9JLEYhwVKZBqGdTUkJe5DSe5L6j7Kpi +Xd3DTKaCQeQzC6zJMw9kglcq/QytNuEMrkvF7zuZ2SOzW120V+x0cAwqTwIDAQAB +o4GgMIGdMAwGA1UdEwQFMAMBAf8wPQYDVR0fBDYwNDAyoDCgLoYsaHR0cDovL2Zl +ZGlyLmNvbXNpZ24uY28uaWwvY3JsL0NvbVNpZ25DQS5jcmwwDgYDVR0PAQH/BAQD +AgGGMB8GA1UdIwQYMBaAFEsBmz5WGmU2dst7l6qSBe4y5ygxMB0GA1UdDgQWBBRL +AZs+VhplNnbLe5eqkgXuMucoMTANBgkqhkiG9w0BAQUFAAOCAQEA0Nmlfv4pYEWd +foPPbrxHbvUanlR2QnG0PFg/LUAlQvaBnPGJEMgOqnhPOAlXsDzACPw1jvFIUY0M +cXS6hMTXcpuEfDhOZAYnKuGntewImbQKDdSFc8gS4TXt8QUxHXOZDOuWyt3T5oWq +8Ir7dcHyCTxlZWTzTNity4hp8+SDtwy9F1qWF8pb/627HOkthIDYIb6FUtnUdLlp +hbpN7Sgy6/lhSuTENh4Z3G+EER+V9YMoGKgzkkMn3V0TBEVPh9VGzT2ouvDzuFYk +Res3x+F2T3I5GN9+dHLHcy056mDmrRGiVod7w2ia/viMcKjfZTL0pECMocJEAw6U +AGegcQCCSA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGATCCA+mgAwIBAgIRAI9hcRW6eVgXjH0ROqzW264wDQYJKoZIhvcNAQELBQAw +RTEfMB0GA1UEAxMWQ29tU2lnbiBHbG9iYWwgUm9vdCBDQTEVMBMGA1UEChMMQ29t +U2lnbiBMdGQuMQswCQYDVQQGEwJJTDAeFw0xMTA3MTgxMDI0NTRaFw0zNjA3MTYx +MDI0NTVaMEUxHzAdBgNVBAMTFkNvbVNpZ24gR2xvYmFsIFJvb3QgQ0ExFTATBgNV +BAoTDENvbVNpZ24gTHRkLjELMAkGA1UEBhMCSUwwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQCyKClzKh3rm6n1nvigmV/VU1D4hSwYW2ro3VqpzpPo0Ph3 +3LguqjXd5juDwN4mpxTpD99d7Xu5X6KGTlMVtfN+bTbA4t3x7DU0Zqn0BE5XuOgs +3GLH41Vmr5wox1bShVpM+IsjcN4E/hMnDtt/Bkb5s33xCG+ohz5dlq0gA9qfr/g4 +O9lkHZXTCeYrmVzd/il4x79CqNvGkdL3um+OKYl8rg1dPtD8UsytMaDgBAopKR+W +igc16QJzCbvcinlETlrzP/Ny76BWPnAQgaYBULax/Q5thVU+N3sEOKp6uviTdD+X +O6i96gARU4H0xxPFI75PK/YdHrHjfjQevXl4J37FJfPMSHAbgPBhHC+qn/014DOx +46fEGXcdw2BFeIIIwbj2GH70VyJWmuk/xLMCHHpJ/nIF8w25BQtkPpkwESL6esaU +b1CyB4Vgjyf16/0nRiCAKAyC/DY/Yh+rDWtXK8c6QkXD2XamrVJo43DVNFqGZzbf +5bsUXqiVDOz71AxqqK+p4ek9374xPNMJ2rB5MLPAPycwI0bUuLHhLy6nAIFHLhut +TNI+6Y/soYpi5JSaEjcY7pxI8WIkUAzr2r+6UoT0vAdyOt7nt1y8844a7szo/aKf +woziHl2O1w6ZXUC30K+ptXVaOiW79pBDcbLZ9ZdbONhS7Ea3iH4HJNwktrBJLQID +AQABo4HrMIHoMA8GA1UdEwEB/wQFMAMBAf8wgYQGA1UdHwR9MHswPKA6oDiGNmh0 +dHA6Ly9mZWRpci5jb21zaWduLmNvLmlsL2NybC9jb21zaWduZ2xvYmFscm9vdGNh +LmNybDA7oDmgN4Y1aHR0cDovL2NybDEuY29tc2lnbi5jby5pbC9jcmwvY29tc2ln +bmdsb2JhbHJvb3RjYS5jcmwwDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBQCRZPY +DUhirGm6rgZbPvuqJpFQsTAfBgNVHSMEGDAWgBQCRZPYDUhirGm6rgZbPvuqJpFQ +sTANBgkqhkiG9w0BAQsFAAOCAgEAk1V5V9701xsfy4mfX+tP9Ln5e9h3N+QMwUfj +kr+k3e8iXOqADjTpUHeBkEee5tJq09ZLp/43F5tZ2eHdYq2ZEX7iWHCnOQet6Yw9 +SU1TahsrGDA6JJD9sdPFnNZooGsU1520e0zNB0dNWwxrWAmu4RsBxvEpWCJbvzQL +dOfyX85RWwli81OiVMBc5XvJ1mxsIIqli45oRynKtsWP7E+b0ISJ1n+XFLdQo/Nm +WA/5sDfT0F5YPzWdZymudMbXitimxC+n4oQE4mbQ4Zm718Iwg3pP9gMMcSc7Qc1J +kJHPH9O7gVubkKHuSYj9T3Ym6c6egL1pb4pz/uT7cT26Fiopc/jdqbe2EAfoJZkv +hlp/zdzOoXTWjiKNA5zmgWnZn943FuE9KMRyKtyi/ezJXCh8ypnqLIKxeFfZl69C +BwJsPXUTuqj8Fic0s3aZmmr7C4jXycP+Q8V+akMEIoHAxcd960b4wVWKqOcI/kZS +Q0cYqWOY1LNjznRt9lweWEfwDBL3FhrHOmD4++1N3FkkM4W+Q1b2WOL24clDMj+i +2n9Iw0lc1llHMSMvA5D0vpsXZpOgcCVahfXczQKi9wQ3oZyonJeWx4/rXdMtagAB +VBYGFuMEUEQtybI+eIbnp5peO2WAAblQI4eTy/jMVowe5tfMEXovV3sz9ULgmGb3 +DscLP1I= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDqzCCApOgAwIBAgIRAMcoRwmzuGxFjB36JPU2TukwDQYJKoZIhvcNAQEFBQAw +PDEbMBkGA1UEAxMSQ29tU2lnbiBTZWN1cmVkIENBMRAwDgYDVQQKEwdDb21TaWdu +MQswCQYDVQQGEwJJTDAeFw0wNDAzMjQxMTM3MjBaFw0yOTAzMTYxNTA0NTZaMDwx +GzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBDQTEQMA4GA1UEChMHQ29tU2lnbjEL +MAkGA1UEBhMCSUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGtWhf +HZQVw6QIVS3joFd67+l0Kru5fFdJGhFeTymHDEjWaueP1H5XJLkGieQcPOqs49oh +gHMhCu95mGwfCP+hUH3ymBvJVG8+pSjsIQQPRbsHPaHA+iqYHU4Gk/v1iDurX8sW +v+bznkqH7Rnqwp9D5PGBpX8QTz7RSmKtUxvLg/8HZaWSLWapW7ha9B20IZFKF3ue +Mv5WJDmyVIRD9YTC2LxBkMyd1mja6YJQqTtoz7VdApRgFrFD2UNd3V2Hbuq7s8lr +9gOUCXDeFhF6K+h2j0kQmHe5Y1yLM5d19guMsqtb3nQgJT/j8xH5h2iGNXHDHYwt +6+UarA9z1YJZQIDTAgMBAAGjgacwgaQwDAYDVR0TBAUwAwEB/zBEBgNVHR8EPTA7 +MDmgN6A1hjNodHRwOi8vZmVkaXIuY29tc2lnbi5jby5pbC9jcmwvQ29tU2lnblNl +Y3VyZWRDQS5jcmwwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFMFL7XC29z58 +ADsAj8c+DkWfHl3sMB0GA1UdDgQWBBTBS+1wtvc+fAA7AI/HPg5Fnx5d7DANBgkq +hkiG9w0BAQUFAAOCAQEAFs/ukhNQq3sUnjO2QiBq1BW9Cav8cujvR3qQrFHBZE7p +iL1DRYHjZiM/EoZNGeQFsOY3wo3aBijJD4mkU6l1P7CW+6tMM1X5eCZGbxs2mPtC +dsGCuY7e+0X5YxtiOzkGynd6qDwJz2w2PQ8KRUtpFhpFfTMDZflScZAmlaxMDPWL +kz/MdXSFmLr/YnpNH4n+rr2UAJm/EaXc4HnFFgt9AmEd6oX5AhVP51qJThRv4zdL +hfXBPGHg/QVBspJ/wx2g0K5SZGBrGMYmnNj1ZOQ2GmKfig8+/21OGVZOIJFsnzQz +OjRXUDpvgV4GxvU+fE6OK85lBi5d0ipTdF7Tbieejw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha +ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM +HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03 +UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42 +tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R +ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM +lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp +/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G +A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj +dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy +MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl +cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js +L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL +BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni +acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K +zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8 +PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y +Johw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw +NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV +BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn +ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0 +3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z +qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR +p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8 +HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw +ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea +HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw +Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh +c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E +RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt +dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku +Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp +3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF +CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na +xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX +KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEc +MBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2Vj +IFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENB +IDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5MjM1OTAwWjBxMQswCQYDVQQGEwJE +RTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxl +U2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290 +IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEU +ha88EOQ5bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhC +QN/Po7qCWWqSG6wcmtoIKyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1Mjwr +rFDa1sPeg5TKqAyZMg4ISFZbavva4VhYAUlfckE8FQYBjl2tqriTtM2e66foai1S +NNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aKSe5TBY8ZTNXeWHmb0moc +QqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTVjlsB9WoH +txa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAP +BgNVHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC +AQEAlGRZrTlk5ynrE/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756Abrsp +tJh6sTtU6zkXR34ajgv8HzFZMQSyzhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpa +IzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8rZ7/gFnkm0W09juwzTkZmDLl +6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4Gdyd1Lx+4ivn+ +xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU +Cm26OWMohpLzGITY+9HPBVZkVw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c +JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP +mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+ +wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4 +VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/ +AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB +AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun +pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC +dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf +fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm +NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx +H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA +n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc +biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp +EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA +bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu +YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB +AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW +BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI +QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I +0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni +lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9 +B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv +ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg +RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf +Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q +RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD +AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY +JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv +6pZjamVFkpUBtA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB +CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 +nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt +43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P +T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 +gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR +TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw +DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr +hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg +06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF +PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls +YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe +Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw +EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x +IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG +fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO +Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd +BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx +AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ +oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 +sycX +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm ++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW +PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM +xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB +Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 +hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg +EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA +FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec +nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z +eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF +hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 +Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep ++OkuE6N36B9K +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg +RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y +ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If +xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV +ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO +DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ +jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ +CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi +EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM +fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY +uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK +chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t +9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 +SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd ++SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc +fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa +sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N +cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N +0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie +4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI +r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 +/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm +gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBb +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3Qx +ETAPBgNVBAsTCERTVCBBQ0VTMRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0w +MzExMjAyMTE5NThaFw0xNzExMjAyMTE5NThaMFsxCzAJBgNVBAYTAlVTMSAwHgYD +VQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UECxMIRFNUIEFDRVMx +FzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPu +ktKe1jzIDZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7 +gLFViYsx+tC3dr5BPTCapCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZH +fAjIgrrep4c9oW24MFbCswKBXy314powGCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4a +ahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPyMjwmR/onJALJfh1biEIT +ajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1UdEwEB/wQF +MAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rk +c3QuY29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjto +dHRwOi8vd3d3LnRydXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMt +aW5kZXguaHRtbDAdBgNVHQ4EFgQUCXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZI +hvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V25FYrnJmQ6AgwbN99Pe7lv7Uk +QIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6tFr8hlxCBPeP/ +h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq +nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpR +rscL9yuwNwXsvFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf2 +9w4LTJxoeHtxMcfrHuBnQfO3oKfN5XozNmr6mis= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/ +MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT +DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow +PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD +Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O +rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq +OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b +xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw +7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD +aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG +SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69 +ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr +AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz +R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5 +JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo +Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDOzCCAiOgAwIBAgIRANAeRlAAACmMAAAAAgAAAAIwDQYJKoZIhvcNAQEFBQAw +PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD +Ew5EU1QgUm9vdCBDQSBYNDAeFw0wMDA5MTMwNjIyNTBaFw0yMDA5MTMwNjIyNTBa +MD8xJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjEXMBUGA1UE +AxMORFNUIFJvb3QgQ0EgWDQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCthX3OFEYY8gSeIYur0O4ypOT68HnDrjLfIutL5PZHRwQGjzCPb9PFo/ihboJ8 +RvfGhBAqpQCo47zwYEhpWm1jB+L/OE/dBBiyn98krfU2NiBKSom2J58RBeAwHGEy +cO+lewyjVvbDDLUy4CheY059vfMjPAftCRXjqSZIolQb9FdPcAoa90mFwB7rKniE +J7vppdrUScSS0+eBrHSUPLdvwyn4RGp+lSwbWYcbg5EpSpE0GRJdchic0YDjvIoC +YHpe7Rkj93PYRTQyU4bhC88ck8tMqbvRYqMRqR+vobbkrj5LLCOQCHV5WEoxWh+0 +E2SpIFe7RkV++MmpIAc0h1tZAgMBAAGjMjAwMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFPCD6nPIP1ubWzdf9UyPWvf0hki9MA0GCSqGSIb3DQEBBQUAA4IBAQCE +G85wl5eEWd7adH6XW/ikGN5salvpq/Fix6yVTzE6CrhlP5LBdkf6kx1bSPL18M45 +g0rw2zA/MWOhJ3+S6U+BE0zPGCuu8YQaZibR7snm3HiHUaZNMu5c8D0x0bcMxDjY +AVVcHCoNiL53Q4PLW27nbY6wwG0ffFKmgV3blxrYWfuUDgGpyPwHwkfVFvz9qjaV +mf12VJffL6W8omBPtgteb6UaT/k1oJ7YI0ldGf+ngpVbRhD+LC3cUtT6GO/BEPZu +8YTV/hbiDH5v3khVqMIeKT6o8IuXGG7F6a6vKwP1F1FwTXf4UC/ivhme7vdUH7B/ +Vv4AEbT8dNfEeFxrkDbh +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNV +BAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBC +aWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNV +BAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQDDB9FLVR1 +Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMwNTEyMDk0OFoXDTIz +MDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+ +BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhp +em1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN +ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4vU/kwVRHoViVF56C/UY +B4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vdhQd2h8y/L5VMzH2nPbxH +D5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5KCKpbknSF +Q9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEo +q1+gElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3D +k14opz8n8Y4e0ypQBaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcH +fC425lAcP9tDJMW/hkd5s3kc91r0E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsut +dEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gzrt48Ue7LE3wBf4QOXVGUnhMM +ti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAqjqFGOjGY5RH8 +zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn +rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUX +U8u3Zg5mTPj5dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6 +Jyr+zE7S6E5UMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5 +XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAF +Nzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAKkEh47U6YA5n+KGCR +HTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jOXKqY +GwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c +77NCR807VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3 ++GbHeJAAFS6LrVE1Uweoa2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WK +vJUawSg5TB9D0pH0clmKuVb8P7Sd2nCcdlqMQ1DujjByTd//SffGqWfZbawCEeI6 +FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEVKV0jq9BgoRJP3vQXzTLl +yb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gTDx4JnW2P +AJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpD +y4Q08ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8d +NL/+I5c30jn6PQ0GC7TbO6Orb1wdtn7os4I07QZcJA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIE5zCCA8+gAwIBAgIBADANBgkqhkiG9w0BAQUFADCBjTELMAkGA1UEBhMCQ0Ex +EDAOBgNVBAgTB09udGFyaW8xEDAOBgNVBAcTB1Rvcm9udG8xHTAbBgNVBAoTFEVj +aG93b3J4IENvcnBvcmF0aW9uMR8wHQYDVQQLExZDZXJ0aWZpY2F0aW9uIFNlcnZp +Y2VzMRowGAYDVQQDExFFY2hvd29yeCBSb290IENBMjAeFw0wNTEwMDYxMDQ5MTNa +Fw0zMDEwMDcxMDQ5MTNaMIGNMQswCQYDVQQGEwJDQTEQMA4GA1UECBMHT250YXJp +bzEQMA4GA1UEBxMHVG9yb250bzEdMBsGA1UEChMURWNob3dvcnggQ29ycG9yYXRp +b24xHzAdBgNVBAsTFkNlcnRpZmljYXRpb24gU2VydmljZXMxGjAYBgNVBAMTEUVj +aG93b3J4IFJvb3QgQ0EyMIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKCAQEA +utU/5BkV15UBf+s+JQruKQxr77s3rjp/RpOtmhHILIiO5gsEWP8MMrfrVEiidjI6 +Qh6ans0KAWc2Dw0/j4qKAQzOSyAZgjcdypNTBZ7muv212DA2Pu41rXqwMrlBrVi/ +KTghfdLlNRu6JrC5y8HarrnRFSKF1Thbzz921kLDRoCi+FVs5eVuK5LvIfkhNAqA +byrTgO3T9zfZgk8upmEkANPDL1+8y7dGPB/d6lk0I5mv8PESKX02TlvwgRSIiTHR +k8++iOPLBWlGp7ZfqTEXkPUZhgrQQvxcrwCUo6mk8TqgxCDP5FgPoHFiPLef5szP +ZLBJDWp7GLyE1PmkQI6WiwIBA6OCAVAwggFMMA8GA1UdEwEB/wQFMAMBAf8wCwYD +VR0PBAQDAgEGMB0GA1UdDgQWBBQ74YEboKs/OyGC1eISrq5QqxSlEzCBugYDVR0j +BIGyMIGvgBQ74YEboKs/OyGC1eISrq5QqxSlE6GBk6SBkDCBjTELMAkGA1UEBhMC +Q0ExEDAOBgNVBAgTB09udGFyaW8xEDAOBgNVBAcTB1Rvcm9udG8xHTAbBgNVBAoT +FEVjaG93b3J4IENvcnBvcmF0aW9uMR8wHQYDVQQLExZDZXJ0aWZpY2F0aW9uIFNl +cnZpY2VzMRowGAYDVQQDExFFY2hvd29yeCBSb290IENBMoIBADBQBgNVHSAESTBH +MEUGCysGAQQB+REKAQMBMDYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cuZWNob3dv +cnguY29tL2NhL3Jvb3QyL2Nwcy5wZGYwDQYJKoZIhvcNAQEFBQADggEBAG+nrPi/ +0RpfEzrj02C6JGPUar4nbjIhcY6N7DWNeqBoUulBSIH/PYGNHYx7/lnJefiixPGE +7TQ5xPgElxb9bK8zoAApO7U33OubqZ7M7DlHnFeCoOoIAZnG1kuwKwD5CXKB2a74 +HzcqNnFW0IsBFCYqrVh/rQgJOzDA8POGbH0DeD0xjwBBooAolkKT+7ZItJF1Pb56 +QpDL9G+16F7GkmnKlAIYT3QTS3yFGYChnJcd+6txUPhKi9sSOOmAIaKHnkH9Scz+ +A2cSi4A3wUYXVatuVNHpRb2lygfH3SuCX9MU8Ure3zBlSU1LALtMqI4JmcQmQpIq +zIzvO2jHyu9PQqo= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1 +MQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1 +czEoMCYGA1UEAwwfRUUgQ2VydGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYG +CSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIwMTAxMDMwMTAxMDMwWhgPMjAzMDEy +MTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNl +ZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBS +b290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUy +euuOF0+W2Ap7kaJjbMeMTC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvO +bntl8jixwKIy72KyaOBhU8E2lf/slLo2rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIw +WFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw93X2PaRka9ZP585ArQ/d +MtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtNP2MbRMNE +1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/ +zQas8fElyalL1BSZMEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYB +BQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEF +BQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+RjxY6hUFaTlrg4wCQiZrxTFGGV +v9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqMlIpPnTX/dqQG +E5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u +uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIW +iAYLtqZLICjU3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/v +GVCJYMzpJJUPwssd8m92kMfMdcGWxZ0= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG +A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3 +d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu +dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq +RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy +MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD +VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0 +L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g +Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi +A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt +ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH +Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC +R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX +hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50 +cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs +IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz +dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy +NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu +dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt +dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0 +aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T +RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN +cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW +wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1 +U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0 +jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN +BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/ +jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ +Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v +1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R +nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH +VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0 +Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW +KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw +NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw +NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy +ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV +BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo +Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4 +4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9 +KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI +rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi +94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB +sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi +gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo +kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE +vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t +O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua +AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP +9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/ +eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m +0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEXDCCA0SgAwIBAgIEOGO5ZjANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML +RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp +bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5 +IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0xOTEy +MjQxODIwNTFaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3 +LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp +YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG +A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq +K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe +sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX +MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT +XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/ +HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH +4QIDAQABo3QwcjARBglghkgBhvhCAQEEBAMCAAcwHwYDVR0jBBgwFoAUVeSB0RGA +vtiJuQijMfmhJAkWuXAwHQYDVR0OBBYEFFXkgdERgL7YibkIozH5oSQJFrlwMB0G +CSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0BAQUFAAOCAQEA +WUesIYSKF8mciVMeuoCFGsY8Tj6xnLZ8xpJdGGQC49MGCBFhfGPjK50xA3B20qMo +oPS7mmNz7W3lKtvtFKkrxjYR0CvrB4ul2p5cGZ1WEvVUKcgF7bISKo30Axv/55IQ +h7A6tcOdBTcSo8f0FbnVpDkWm1M6I5HxqIKiaohowXkCIryqptau37AUX7iH0N18 +f3v/rxzP5tsHrV7bhZ3QKw0z2wTR5klAEyt2+z7pnIkPFc4YsIV4IU9rTw76NmfN +B/L/CNDi3tm/Kq+4h4YhPATKt5Rof8886ZjXOP/swNlQ8C5LWK5Gb9Auw2DaclVy +vUxFnmG6v4SBkgPR0ml8xQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML +RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp +bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5 +IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3 +MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3 +LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp +YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG +A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq +K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe +sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX +MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT +XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/ +HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH +4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQADggEBADub +j1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo +U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5b +u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+ +bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er +fF6adulZkMV8gzURZVE= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe +MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 +ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMxMjdaMF4xCzAJBgNVBAYTAlRXMSMw +IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBL +SSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAH +SyZbCUNsIZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAh +ijHyl3SJCRImHJ7K2RKilTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3X +DZoTM1PRYfl61dd4s5oz9wCGzh1NlDivqOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1 +TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX12ruOzjjK9SXDrkb5wdJ +fzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0OWQqraffA +sgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uU +WH1+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLS +nT0IFaUQAS2zMnaolQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pH +dmX2Os+PYhcZewoozRrSgx4hxyy/vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJip +NiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXiZo1jDiVN1Rmy5nk3pyKdVDEC +AwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/QkqiMAwGA1UdEwQF +MAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGB +uvl2ICO1J2B01GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6Yl +PwZpVnPDimZI+ymBV3QGypzqKOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkP +JXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdVxrsStZf0X4OFunHB2WyBEXYKCrC/ +gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEPNXubrjlpC2JgQCA2 +j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+rGNm6 +5ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUB +o2M3IUxExJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS +/jQ6fbjpKdx2qcgw+BRxgMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2z +Gp1iro2C6pSe3VkQw63d4k3jMdXH7OjysP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTE +W9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmODBCEIZ43ygknQW/2xzQ+D +hNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEYDCCA0igAwIBAgICATAwDQYJKoZIhvcNAQELBQAwWTELMAkGA1UEBhMCVVMx +GDAWBgNVBAoTD1UuUy4gR292ZXJubWVudDENMAsGA1UECxMERlBLSTEhMB8GA1UE +AxMYRmVkZXJhbCBDb21tb24gUG9saWN5IENBMB4XDTEwMTIwMTE2NDUyN1oXDTMw +MTIwMTE2NDUyN1owWTELMAkGA1UEBhMCVVMxGDAWBgNVBAoTD1UuUy4gR292ZXJu +bWVudDENMAsGA1UECxMERlBLSTEhMB8GA1UEAxMYRmVkZXJhbCBDb21tb24gUG9s +aWN5IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2HX7NRY0WkG/ +Wq9cMAQUHK14RLXqJup1YcfNNnn4fNi9KVFmWSHjeavUeL6wLbCh1bI1FiPQzB6+ +Duir3MPJ1hLXp3JoGDG4FyKyPn66CG3G/dFYLGmgA/Aqo/Y/ISU937cyxY4nsyOl +4FKzXZbpsLjFxZ+7xaBugkC7xScFNknWJidpDDSPzyd6KgqjQV+NHQOGgxXgVcHF +mCye7Bpy3EjBPvmE0oSCwRvDdDa3ucc2Mnr4MrbQNq4iGDGMUHMhnv6DOzCIJOPp +wX7e7ZjHH5IQip9bYi+dpLzVhW86/clTpyBLqtsgqyFOHQ1O5piF5asRR12dP8Qj +wOMUBm7+nQIDAQABo4IBMDCCASwwDwYDVR0TAQH/BAUwAwEB/zCB6QYIKwYBBQUH +AQsEgdwwgdkwPwYIKwYBBQUHMAWGM2h0dHA6Ly9odHRwLmZwa2kuZ292L2ZjcGNh +L2NhQ2VydHNJc3N1ZWRCeWZjcGNhLnA3YzCBlQYIKwYBBQUHMAWGgYhsZGFwOi8v +bGRhcC5mcGtpLmdvdi9jbj1GZWRlcmFsJTIwQ29tbW9uJTIwUG9saWN5JTIwQ0Es +b3U9RlBLSSxvPVUuUy4lMjBHb3Zlcm5tZW50LGM9VVM/Y0FDZXJ0aWZpY2F0ZTti +aW5hcnksY3Jvc3NDZXJ0aWZpY2F0ZVBhaXI7YmluYXJ5MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUrQx6dVzl85jEeZgOrCj9l/TnAvwwDQYJKoZIhvcNAQELBQAD +ggEBAI9z2uF/gLGH9uwsz9GEYx728Yi3mvIRte9UrYpuGDco71wb5O9Qt2wmGCMi +TR0mRyDpCZzicGJxqxHPkYnos/UqoEfAFMtOQsHdDA4b8Idb7OV316rgVNdF9IU+ +7LQd3nyKf1tNnJaK0KIyn9psMQz4pO9+c+iR3Ah6cFqgr2KBWfgAdKLI3VTKQVZH +venAT+0g3eOlCd+uKML80cgX2BLHb94u6b2akfI8WpQukSKAiaGMWMyDeiYZdQKl +Dn0KJnNR6obLB6jI/WNaNZvSr79PMUjBhHDbNXuaGQ/lj/RqDG8z2esccKIN47lQ +A2EC/0rskqTcLe4qNJMHtyznGI8= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg +R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 +9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq +fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv +iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU +1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ +bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW +MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA +ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l +uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn +Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS +tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF +PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un +hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV +5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDEL +MAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChj +KSAyMDA3IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2 +MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1OVowgZgxCzAJBgNV +BAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykgMjAw +NyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNV +BAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH +MjB2MBAGByqGSM49AgEGBSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcL +So17VDs6bl8VAsBQps8lL33KSLjHUGMcKiEIfJo22Av+0SbFWDEwKCXzXV2juLal +tJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+EVXVMAoG +CCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGT +qQ7mndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBucz +rD6ogRLQy7rQkgu2npaqBA+K +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCB +mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT +MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s +eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIzNTk1OVowgZgxCzAJ +BgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg +MjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0 +BgNVBAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz ++uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5jK/BGvESyiaHAKAxJcCGVn2TAppMSAmUm +hsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdEc5IiaacDiGydY8hS2pgn +5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3CIShwiP/W +JmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exAL +DmKudlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZC +huOl1UcCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw +HQYDVR0OBBYEFMR5yo6hTgMdHNxr2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IB +AQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9cr5HqQ6XErhK8WTTOd8lNNTB +zU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbEAp7aDHdlDkQN +kv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD +AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUH +SJsMC8tJP33st/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2G +spki4cErx5z481+oghLrGREt +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBY +MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo +R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx +MjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQK +Ew1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQcmltYXJ5IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9 +AWbK7hWNb6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjA +ZIVcFU2Ix7e64HXprQU9nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE0 +7e9GceBrAqg1cmuXm2bgyxx5X9gaBGgeRwLmnWDiNpcB3841kt++Z8dtd1k7j53W +kBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGttm/81w7a4DSwDRp35+MI +mO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJ +KoZIhvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ1 +6CePbJC/kRYkRj5KTs4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl +4b7UVXGYNTq+k+qurUKykG/g/CFNNWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6K +oKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHaFloxt/m0cYASSJlyc1pZU8Fj +UjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG1riR/aYNKxoU +AT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYD +VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 +IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 +MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD +aGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMxNDBaFw0zODA3MzEx +MjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3Vy +cmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAG +A1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAl +BgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZI +hvcNAQEBBQADggIPADCCAgoCggIBAMDfVtPkOpt2RbQT2//BthmLN0EYlVJH6xed +KYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXfXjaOcNFccUMd2drvXNL7 +G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0ZJJ0YPP2 +zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4 +ddPB/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyG +HoiMvvKRhI9lNNgATH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2 +Id3UwD2ln58fQ1DJu7xsepeY7s2MH/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3V +yJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfeOx2YItaswTXbo6Al/3K1dh3e +beksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSFHTynyQbehP9r +6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh +wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsog +zCtLkykPAgMBAAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQW +BBS5CcqcHtvTbDprru1U8VuTBjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDpr +ru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UEBhMCRVUxQzBBBgNVBAcTOk1hZHJp +ZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJmaXJtYS5jb20vYWRk +cmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJmaXJt +YSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiC +CQDJzdPp1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCow +KAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZI +hvcNAQEFBQADggIBAICIf3DekijZBZRG/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZ +UohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6ReAJ3spED8IXDneRRXoz +X1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/sdZ7LoR/x +fxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVz +a2Mg9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yyd +Yhz2rXzdpjEetrHHfoUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMd +SqlapskD7+3056huirRXhOukP9DuqqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9O +AP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETrP3iZ8ntxPjzxmKfFGBI/5rso +M0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVqc5iJWzouE4ge +v8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z +09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEn +MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL +ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENo +YW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYxNDE4WhcNMzcwOTMwMTYxNDE4WjB9 +MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgy +NzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4G +A1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUA +A4IBDQAwggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0 +Mi+ITaFgCPS3CU6gSS9J1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/s +QJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8Oby4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpV +eAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl6DJWk0aJqCWKZQbua795 +B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c8lCrEqWh +z0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0T +AQH/BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1i +ZXJzaWduLm9yZy9jaGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4w +TcbOX60Qq+UDpfqpFDAOBgNVHQ8BAf8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAH +MCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBjaGFtYmVyc2lnbi5vcmcwKgYD +VR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9yZzBbBgNVHSAE +VDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh +bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0B +AQUFAAOCAQEAPDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUM +bKGKfKX0j//U2K0X1S0E0T9YgOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXi +ryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJPJ7oKXqJ1/6v/2j1pReQvayZzKWG +VwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4IBHNfTIzSJRUTN3c +ecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREest2d/ +AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw +MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT +aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ +jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp +xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp +1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG +snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ +U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 +9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B +AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz +yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE +38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP +AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad +DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME +HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprlOQcJ +FspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61F +uOJAf/sKbvu+M8k8o4TVMAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGX +kPoUVy0D7O48027KqGx2vKLeuwIgJ6iFJzWbVsaj8kfSt24bAgAXqmemFZHe+pTs +ewv4n4Q= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc +8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke +hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI +KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg +515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO +xwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1 +MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL +v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8 +eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq +tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd +C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa +zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB +mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH +V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n +bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG +3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs +J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO +291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS +ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd +AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 +TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 +MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 +RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT +gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm +KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd +QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ +XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o +LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU +RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp +jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK +6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX +mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs +Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH +WD9f +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh +MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE +YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3 +MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo +ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg +MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN +ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA +PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w +wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi +EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY +avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+ +YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE +sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h +/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5 +IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD +ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy +OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P +TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER +dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf +ReYNnyicsbkqWletNw+vHX/bvZ8= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT +EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp +ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz +NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH +EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE +AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD +E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH +/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy +DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh +GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR +tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA +AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX +WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu +9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr +gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo +2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI +4uJEvlz36hz1 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFSzCCAzOgAwIBAgIRALZLiAfiI+7IXBKtpg4GofIwDQYJKoZIhvcNAQELBQAw +PzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dvdmVybm1lbnQgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eTAeFw0xMjA5MjgwODU4NTFaFw0zNzEyMzExNTU5NTla +MD8xCzAJBgNVBAYTAlRXMTAwLgYDVQQKDCdHb3Zlcm5tZW50IFJvb3QgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQC2/5c8gb4BWCQnr44BK9ZykjAyG1+bfNTUf+ihYHMwVxAA+lCWJP5Q5ow6ldFX +eYTVZ1MMKoI+GFy4MCYa1l7GLbIEUQ7v3wxjR+vEEghRK5lxXtVpe+FdyXcdIOxW +juVhYC386RyA3/pqg7sFtR4jEpyCygrzFB0g5AaPQySZn7YKk1pzGxY5vgW28Yyl +ZJKPBeRcdvc5w88tvQ7Yy6gOMZvJRg9nU0MEj8iyyIOAX7ryD6uBNaIgIZfOD4k0 +eA/PH07p+4woPN405+2f0mb1xcoxeNLOUNFggmOd4Ez3B66DNJ1JSUPUfr0t4urH +cWWACOQ2nnlwCjyHKenkkpTqBpIpJ3jmrdc96QoLXvTg1oadLXLLi2RW5vSueKWg +OTNYPNyoj420ai39iHPplVBzBN8RiD5C1gJ0+yzEb7xs1uCAb9GGpTJXA9ZN9E4K +mSJ2fkpAgvjJ5E7LUy3Hsbbi08J1J265DnGyNPy/HE7CPfg26QrMWJqhGIZO4uGq +s3NZbl6dtMIIr69c/aQCb/+4DbvVq9dunxpPkUDwH0ZVbaCSw4nNt7H/HLPLo5wK +4/7NqrwB7N1UypHdTxOHpPaY7/1J1lcqPKZc9mA3v9g+fk5oKiMyOr5u5CI9ByTP +isubXVGzMNJxbc5Gim18SjNE2hIvNkvy6fFRCW3bapcOFwIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBTVZx3gnHosnMvFmOcdByYqhux0zTAOBgNV +HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAJA75cJTQijq9TFOjj2Rnk0J +89ixUuZPrAwxIbvx6pnMg/y2KOTshAcOD06Xu29oRo8OURWV+Do7H1+CDgxxDryR +T64zLiNB9CZrTxOH+nj2LsIPkQWXqmrBap+8hJ4IKifd2ocXhuGzyl3tOKkpboTe +Rmv8JxlQpRJ6jH1i/NrnzLyfSa8GuCcn8on3Fj0Y5r3e9YwSkZ/jBI3+BxQaWqw5 +ghvxOBnhY+OvbLamURfr+kvriyL2l/4QOl+UoEtTcT9a4RD4co+WgN2NApgAYT2N +vC2xR8zaXeEgp4wxXPHj2rkKhkfIoT0Hozymc26Uke1uJDr5yTDRB6iBfSZ9fYTf +hsmL5a4NHr6JSFEVg5iWL0rrczTXdM3Jb9DCuiv2mv6Z3WAUjhv5nDk8f0OJU+jl +wqu+Iq0nOJt3KLejY2OngeepaUXrjnhWzAWEx/uttjB8YwWfLYwkf0uLkvw4Hp+g +pVezbp3YZLhwmmBScMip0P/GnO0QYV7Ngw5u6E0CQUridgR51lQ/ipgyFKDdLZzn +uoJxo4ZVKZnSKdt1OvfbQ/+2W/u3fjWAjg1srnm3Ni2XUqGwB5wH5Ss2zQOXlL0t +DjQG/MAWifw3VOTWzz0TBPKR2ck2Lj7FWtClTILD/y58Jnb38/1FoqVuVa4uzM8s +iTTa9g3nkagQ6hed8vbs +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1Ix +RDBCBgNVBAoTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 +dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1p +YyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIFJvb3RDQSAyMDExMB4XDTExMTIw +NjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYTAkdSMUQwQgYDVQQK +EztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIENl +cnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPz +dYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJ +fel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa71HFK9+WXesyHgLacEns +bgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u8yBRQlqD +75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSP +FEDH3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNV +HRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp +5dgTBCPuQSUwRwYDVR0eBEAwPqA8MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQu +b3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQub3JnMA0GCSqGSIb3DQEBBQUA +A4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVtXdMiKahsog2p +6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 +TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7 +dIsXRSZMFpGD/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8Acys +Nnq/onN694/BtZqhFLKPM58N7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXI +l7WdmplNsDz4SgCbZN2fOUvRJ9e4 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsx +FjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3Qg +Um9vdCBDQSAxMB4XDTAzMDUxNTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkG +A1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdr +b25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1ApzQ +jVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEn +PzlTCeqrauh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjh +ZY4bXSNmO7ilMlHIhqqhqZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9 +nnV0ttgCXjqQesBCNnLsak3c78QA3xMYV18meMjWCnl3v/evt3a5pQuEF10Q6m/h +q5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNVHRMBAf8ECDAGAQH/AgED +MA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7ih9legYsC +mEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI3 +7piol7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clB +oiMBdDhViw+5LmeiIAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJs +EhTkYY2sEJCehFC78JZvRZ+K88psT/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpO +fMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilTc4afU9hDDl3WY4JxHYB0yvbi +AmvZWg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFHjCCBAagAwIBAgIEAKA3oDANBgkqhkiG9w0BAQsFADCBtzELMAkGA1UEBhMC +Q1oxOjA4BgNVBAMMMUkuQ0EgLSBRdWFsaWZpZWQgQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHksIDA5LzIwMDkxLTArBgNVBAoMJFBydm7DrSBjZXJ0aWZpa2HEjW7DrSBh +dXRvcml0YSwgYS5zLjE9MDsGA1UECww0SS5DQSAtIEFjY3JlZGl0ZWQgUHJvdmlk +ZXIgb2YgQ2VydGlmaWNhdGlvbiBTZXJ2aWNlczAeFw0wOTA5MDEwMDAwMDBaFw0x +OTA5MDEwMDAwMDBaMIG3MQswCQYDVQQGEwJDWjE6MDgGA1UEAwwxSS5DQSAtIFF1 +YWxpZmllZCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSwgMDkvMjAwOTEtMCsGA1UE +CgwkUHJ2bsOtIGNlcnRpZmlrYcSNbsOtIGF1dG9yaXRhLCBhLnMuMT0wOwYDVQQL +DDRJLkNBIC0gQWNjcmVkaXRlZCBQcm92aWRlciBvZiBDZXJ0aWZpY2F0aW9uIFNl +cnZpY2VzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtTaEy0KC8M9l +4lSaWHMs4+sVV1LwzyJYiIQNeCrv1HHm/YpGIdY/Z640ceankjQvIX7m23BK4OSC +6KO8kZYA3zopOz6GFCOKV2PvLukbc+c2imF6kLHEv6qNA8WxhPbR3xKwlHDwB2yh +Wzo7V3QVgDRG83sugqQntKYC3LnlTGbJpNP+Az72gpO9AHUn/IBhFk4ksc8lYS2L +9GCy9CsmdKSBP78p9w8Lx7vDLqkDgt1/zBrcUWmSSb7AE/BPEeMryQV1IdI6nlGn +BhWkXOYf6GSdayJw86btuxC7viDKNrbp44HjQRaSxnp6O3eto1x4DfiYdw/YbJFe +7EjkxSQBywIDAQABo4IBLjCCASowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E +BAMCAQYwgecGA1UdIASB3zCB3DCB2QYEVR0gADCB0DCBzQYIKwYBBQUHAgIwgcAa +gb1UZW50byBjZXJ0aWZpa2F0IGplIHZ5ZGFuIGpha28ga3ZhbGlmaWtvdmFueSBz +eXN0ZW1vdnkgY2VydGlmaWthdCBwb2RsZSB6YWtvbmEgYy4gMjI3LzIwMDAgU2Iu +IHYgcGxhdG5lbSB6bmVuaS9UaGlzIGlzIHF1YWxpZmllZCBzeXN0ZW0gY2VydGlm +aWNhdGUgYWNjb3JkaW5nIHRvIEN6ZWNoIEFjdCBOby4gMjI3LzIwMDAgQ29sbC4w +HQYDVR0OBBYEFHnL0CPpOmdwkXRP01Hi4CD94Sj7MA0GCSqGSIb3DQEBCwUAA4IB +AQB9laU214hYaBHPZftbDS/2dIGLWdmdSbj1OZbJ8LIPBMxYjPoEMqzAR74tw96T +i6aWRa5WdOWaS6I/qibEKFZhJAVXX5mkx2ewGFLJ+0Go+eTxnjLOnhVF2V2s+57b +m8c8j6/bS6Ij6DspcHEYpfjjh64hE2r0aSpZDjGzKFM6YpqsCJN8qYe2X1qmGMLQ +wvNdjG+nPzCJOOuUEypIWt555ZDLXqS5F7ZjBjlfyDZjEfS2Es9Idok8alf563Mi +9/o+Ba46wMYOkk3P1IlU0RqCajdbliioACKDztAqubONU1guZVzV8tuMASVzbJeL +/GAB7ECTwe1RuKrLYtglMKI9 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu +VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw +MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw +JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT +3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU ++ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp +S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1 +bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi +T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL +vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK +Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK +dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT +c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv +l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N +iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD +ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH +6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt +LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93 +nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3 ++wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK +W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT +AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq +l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG +4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ +mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A +7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu +VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN +MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0 +MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7 +ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy +RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS +bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF +/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R +3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw +EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy +9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V +GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ +2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV +WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD +W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN +AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj +t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV +DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9 +TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G +lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW +mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df +WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5 ++bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ +tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA +GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv +8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEXzCCA0egAwIBAgIBATANBgkqhkiG9w0BAQUFADCB0DELMAkGA1UEBhMCRVMx +SDBGBgNVBAoTP0laRU5QRSBTLkEuIC0gQ0lGIEEtMDEzMzcyNjAtUk1lcmMuVml0 +b3JpYS1HYXN0ZWl6IFQxMDU1IEY2MiBTODFCMEAGA1UEBxM5QXZkYSBkZWwgTWVk +aXRlcnJhbmVvIEV0b3JiaWRlYSAzIC0gMDEwMTAgVml0b3JpYS1HYXN0ZWl6MRMw +EQYDVQQDEwpJemVucGUuY29tMR4wHAYJKoZIhvcNAQkBFg9JbmZvQGl6ZW5wZS5j +b20wHhcNMDMwMTMwMjMwMDAwWhcNMTgwMTMwMjMwMDAwWjCB0DELMAkGA1UEBhMC +RVMxSDBGBgNVBAoTP0laRU5QRSBTLkEuIC0gQ0lGIEEtMDEzMzcyNjAtUk1lcmMu +Vml0b3JpYS1HYXN0ZWl6IFQxMDU1IEY2MiBTODFCMEAGA1UEBxM5QXZkYSBkZWwg +TWVkaXRlcnJhbmVvIEV0b3JiaWRlYSAzIC0gMDEwMTAgVml0b3JpYS1HYXN0ZWl6 +MRMwEQYDVQQDEwpJemVucGUuY29tMR4wHAYJKoZIhvcNAQkBFg9JbmZvQGl6ZW5w +ZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1btoCXXhp3xIW +D+Bxl8nUCxkyiazWfpt0e68t+Qt9+lZjKZSdEw2Omj4qvr+ovRmDXO3iWpWVOWDl +3JHJjAzFCe8ZEBNDH+QNYwZHmPBaMYFOYFdbAFVHWvys152C308hcFJ6xWWGmjvl +2eMiEl9P2nR2LWue368DCu+ak7j3gjAXaCOdP1a7Bfr+RW3X2SC5R4Xyp8iHlL5J +PHJD/WBkLrezwzQPdACw8m9EG7q9kUwlNpL32mROujS3ZkT6mQTzJieLiE3X04s0 +uIUqVkk5MhjcHFf7al0N5CzjtTcnXYJKN2Z9EDVskk4olAdGi46eSoZXbjUOP5gk +Ej6wVZAXAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG +MB0GA1UdDgQWBBTqVk/sPIOhFIh4gbIrBSLAB0FbQjANBgkqhkiG9w0BAQUFAAOC +AQEAYp7mEzzhw6o5Hf5+T5kcI+t4BJyiIWy7vHlLs/G8dLYXO81aN/Mzg928eMTR +TxxYZL8dd9uwsJ50TVfX6L0R4Dyw6wikh3fHRrat9ufXi63j5K91Ysr7aXqnF38d +iAgHYkrwC3kuxHBb9C0KBz6h8Q45/KCyN7d37wWAq38yyhPDlaOvyoE6bdUuK5hT +m5EYA5JmPyrhQ1moDOyueWBAjxzMEMj+OAY1H90cLv6wszsqerxRrdTOHBdv7MjB +EIpvEEQkXUxVXAzFuuT6m2t91Lfnwfl/IvljHaVC7DlyyhRYHD6D4Rx+4QKp4tWL +vpw6LkI+gKNJ/YdMCsRZQzEEFA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF8DCCA9igAwIBAgIPBuhGJy8fCo/RhFzjafbVMA0GCSqGSIb3DQEBBQUAMDgx +CzAJBgNVBAYTAkVTMRQwEgYDVQQKDAtJWkVOUEUgUy5BLjETMBEGA1UEAwwKSXpl +bnBlLmNvbTAeFw0wNzEyMTMxMzA4MjdaFw0zNzEyMTMwODI3MjVaMDgxCzAJBgNV +BAYTAkVTMRQwEgYDVQQKDAtJWkVOUEUgUy5BLjETMBEGA1UEAwwKSXplbnBlLmNv +bTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMnTesoPHqynhugWZWqx +whtFMnGV2f4QW8yv56V5AY+Jw8ryVXH3d753lPNypCxE2J6SmxQ6oeckkAoKVo7F +2CaU4dlI4S0+2gpy3aOZFdqBoof0e24md4lYrdbrDLJBenNubdt6eEHpCIgSfocu +ZhFjbFT7PJ1ywLwu/8K33Q124zrX97RovqL144FuwUZvXY3gTcZUVYkaMzEKsVe5 +o4qYw+w7NMWVQWl+dcI8IMVhulFHoCCQk6GQS/NOfIVFVJrRBSZBsLVNHTO+xAPI +JXzBcNs79AktVCdIrC/hxKw+yMuSTFM5NyPs0wH54AlETU1kwOENWocivK0bo/4m +tRXzp/yEGensoYi0RGmEg/OJ0XQGqcwL1sLeJ4VQJsoXuMl6h1YsGgEebL4TrRCs +tST1OJGh1kva8bvS3ke18byB9llrzxlT6Y0Vy0rLqW9E5RtBz+GGp8rQap+8TI0G +M1qiheWQNaBiXBZO8OOi+gMatCxxs1gs3nsL2xoP694hHwZ3BgOwye+Z/MC5TwuG +KP7Suerj2qXDR2kS4Nvw9hmL7Xtw1wLW7YcYKCwEJEx35EiKGsY7mtQPyvp10gFA +Wo15v4vPS8+qFsGV5K1Mij4XkdSxYuWC5YAEpAN+jb/af6IPl08M0w3719Hlcn4c +yHf/W5oPt64FRuXxqBbsR6QXAgMBAAGjgfYwgfMwgbAGA1UdEQSBqDCBpYEPaW5m +b0BpemVucGUuY29tpIGRMIGOMUcwRQYDVQQKDD5JWkVOUEUgUy5BLiAtIENJRiBB +MDEzMzcyNjAtUk1lcmMuVml0b3JpYS1HYXN0ZWl6IFQxMDU1IEY2MiBTODFDMEEG +A1UECQw6QXZkYSBkZWwgTWVkaXRlcnJhbmVvIEV0b3JiaWRlYSAxNCAtIDAxMDEw +IFZpdG9yaWEtR2FzdGVpejAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUHRxlDqjyJXu0kc/ksbHmvVV0bAUwDQYJKoZIhvcNAQEFBQAD +ggIBAMeBRm8hGE+gBe/n1bqXUKJg7aWSFBpSm/nxiEqg3Hh10dUflU7F57dp5iL0 ++CmoKom+z892j+Mxc50m0xwbRxYpB2iEitL7sRskPtKYGCwkjq/2e+pEFhsqxPqg +l+nqbFik73WrAGLRne0TNtsiC7bw0fRue0aHwp28vb5CO7dz0JoqPLRbEhYArxk5 +ja2DUBzIgU+9Ag89njWW7u/kwgN8KRwCfr00J16vU9adF79XbOnQgxCvv11N75B7 +XSus7Op9ACYXzAJcY9cZGKfsK8eKPlgOiofmg59OsjQerFQJTx0CCzl+gQgVuaBp +E8gyK+OtbBPWg50jLbJtooiGfqgNASYJQNntKE6MkyQP2/EeTXp6WuKlWPHcj1+Z +ggwuz7LdmMySlD/5CbOlliVbN/UShUHiGUzGigjB3Bh6Dx4/glmimj4/+eAJn/3B +kUtdyXvWton83x18hqrNA/ILUpLxYm9/h+qrdslsUMIZgq+qHfUgKGgu1fxkN0/P +pUTEvnK0jHS0bKf68r10OEMr3q/53NjgnZ/cPcqlY0S/kqJPTIAcuxrDmkoEVU3K +7iYLHL8CxWTTnn7S05EcS6L1HOUXHA0MUqORH5zwIe0ClG+poEnK6EOMxPQ02nwi +o8ZmPrgbBYhdurz3vOXcFD2nhqi2WVIhA16L4wTtSyoeo09Q +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4 +MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6 +ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD +VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j +b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq +scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO +xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H +LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX +uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD +yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+ +JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q +rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN +BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L +hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB +QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+ +HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu +Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg +QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB +BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA +A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb +laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56 +awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo +JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw +LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT +VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk +LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb +UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/ +QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+ +naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls +QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDczCCAlugAwIBAgIBBDANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJLUjEN +MAsGA1UECgwES0lTQTEuMCwGA1UECwwlS29yZWEgQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkgQ2VudHJhbDEWMBQGA1UEAwwNS0lTQSBSb290Q0EgMTAeFw0wNTA4MjQw +ODA1NDZaFw0yNTA4MjQwODA1NDZaMGQxCzAJBgNVBAYTAktSMQ0wCwYDVQQKDARL +SVNBMS4wLAYDVQQLDCVLb3JlYSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBDZW50 +cmFsMRYwFAYDVQQDDA1LSVNBIFJvb3RDQSAxMIIBIDANBgkqhkiG9w0BAQEFAAOC +AQ0AMIIBCAKCAQEAvATk+hM58DSWIGtsaLv623f/J/es7C/n/fB/bW+MKs0lCVsk +9KFo/CjsySXirO3eyDOE9bClCTqnsUdIxcxPjHmc+QZXfd3uOPbPFLKc6tPAXXdi +8EcNuRpAU1xkcK8IWsD3z3X5bI1kKB4g/rcbGdNaZoNy4rCbvdMlFQ0yb2Q3lIVG +yHK+d9VuHygvx2nt54OJM1jT3qC/QOhDUO7cTWu8peqmyGGO9cNkrwYV3CmLP3WM +vHFE2/yttRcdbYmDz8Yzvb9Fov4Kn6MRXw+5H5wawkbMnChmn3AmPC7fqoD+jMUE +CSVPzZNHPDfqAmeS/vwiJFys0izgXAEzisEZ2wIBA6MyMDAwHQYDVR0OBBYEFL+2 +J9gDWnZlTGEBQVYx5Yt7OtnMMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEF +BQADggEBABOvUQveimpb5poKyLGQSk6hAp3MiNKrZr097LuxQpVqslxa/6FjZJap +aBV/JV6K+KRzwYCKhQoOUugy50X4TmWAkZl0Q+VFnUkq8JSV3enhMNITbslOsXfl +BM+tWh6UCVrXPAgcrnrpFDLBRa3SJkhyrKhB2vAhhzle3/xk/2F0KpzZm4tfwjeT +2KM3LzuTa7IbB6d/CVDv0zq+IWuKkDsnSlFOa56ch534eJAx7REnxqhZvvwYC/uO +fi5C4e3nCSG9uRPFVmf0JqZCQ5BEVLRxm3bkGhKsGigA35vB1fjbXKP4krG9tNT5 +UNkAAk/bg9ART6RCVmE6fhMy04Qfybo= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD +VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0 +ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G +CSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0y +OTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3Qx +FjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3pp +Z25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvP +kd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc +cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4U +fIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7 +N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbC +xkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1 ++rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPM +Pcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqG +SIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5DwpL7v8u8h +mLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk +ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c +2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t +HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG +EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3 +MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl +cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR +dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB +pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM +b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm +aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz +IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT +lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz +AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5 +VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG +ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2 +BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG +AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M +U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh +bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C ++C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F +uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2 +XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBi +MQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu +MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3Jp +dHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMxMjM1OTU5WjBiMQswCQYDVQQGEwJV +UzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydO +ZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwz +c7MEL7xxjOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPP +OCwGJgl6cvf6UDL4wpPTaaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rl +mGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXTcrA/vGp97Eh/jcOrqnErU2lBUzS1sLnF +BgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc/Qzpf14Dl847ABSHJ3A4 +qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMBAAGjgZcw +gZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwu +bmV0c29sc3NsLmNvbS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3Jp +dHkuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc8 +6fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q4LqILPxFzBiwmZVRDuwduIj/ +h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/GGUsyfJj4akH +/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv +wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHN +pGxlaKFJdlxDydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCB +ijELMAkGA1UEBhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHly +aWdodCAoYykgMjAwNTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl +ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQSBDQTAeFw0w +NTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYDVQQGEwJDSDEQMA4G +A1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIwIAYD +VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBX +SVNlS2V5IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAy0+zAJs9Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxR +VVuuk+g3/ytr6dTqvirdqFEr12bDYVxgAsj1znJ7O7jyTmUIms2kahnBAbtzptf2 +w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbDd50kc3vkDIzh2TbhmYsF +mQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ/yxViJGg +4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t9 +4B3RLoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQw +EAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOx +SPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vImMMkQyh2I+3QZH4VFvbBsUfk2 +ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4+vg1YFkCExh8 +vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa +hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZi +Fj4A4xylNoEYokxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ +/L7fCg0= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBt +MQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUg +Rm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9i +YWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAwMzJaFw0zOTEyMDExNTEwMzFaMG0x +CzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBG +b3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh +bCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3 +HEokKtaXscriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGx +WuR51jIjK+FTzJlFXHtPrby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX +1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNk +u7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4oQnc/nSMbsrY9gBQHTC5P +99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvgGUpuuy9r +M2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUB +BAMCAQAwDQYJKoZIhvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrgh +cViXfa43FK8+5/ea4n32cZiZBKpDdHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5 +gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0VQreUGdNZtGn//3ZwLWoo4rO +ZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEuiHZeeevJuQHHf +aPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic +Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00 +MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV +wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe +rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341 +68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh +4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp +UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o +abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc +3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G +KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt +hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO +Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt +zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD +ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC +MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2 +cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN +qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5 +YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv +b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2 +8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k +NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj +ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp +q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt +nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa +GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg +Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J +WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB +rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp ++ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1 +ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i +Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz +PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og +/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH +oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI +yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud +EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2 +A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL +MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT +ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f +BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn +g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl +fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K +WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha +B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc +hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR +TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD +mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z +ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y +4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza +8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00 +MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR +/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu +FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR +U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c +ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR +FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k +A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw +eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl +sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp +VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q +A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ +ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD +ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px +KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI +FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv +oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg +u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP +0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf +3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl +8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+ +DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN +PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ +ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM +V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB +4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr +H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd +8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv +vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT +mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe +btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc +T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt +WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ +c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A +4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD +VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG +CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0 +aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 +aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu +dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw +czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G +A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg +Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0 +7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem +d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd ++LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B +4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN +t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x +DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57 +k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s +zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j +Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT +mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK +4SVhM7JZG+Ju1zdXtg2pEto= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz +MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw +IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR +dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp +li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D +rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ +WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug +F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU +xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC +Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv +dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw +ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl +IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh +c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy +ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh +Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI +KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T +KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq +y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p +dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD +VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL +MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk +fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8 +7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R +cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y +mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW +xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK +SnQ2+Q== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBK +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +GTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkx +MjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3Qg +Q29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jxYDiJ +iQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa +/FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJ +jnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI +HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7 +sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0w +gZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCsw +KaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsG +AQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0L +URYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXO +H0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895P4vkp9Mm +I50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY +iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz +MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv +cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz +Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO +0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao +wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj +7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS +8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT +BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg +JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3 +6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/ +3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm +D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS +CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDEl +MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMh +U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIz +MloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09N +IFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNlY3VyaXR5IENvbW11 +bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSE +RMqm4miO/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gO +zXppFodEtZDkBp2uoQSXWHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5 +bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4zZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDF +MxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4bepJz11sS6/vmsJWXMY1 +VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK9U2vP9eC +OKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HW +tWS3irO4G8za+6xmiEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZ +q51ihPZRwSzJIxXYKLerJRO1RuGGAv8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDb +EJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnWmHyojf6GPgcWkuF75x3sM3Z+ +Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEWT1MKZPlO9L9O +VL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEY +MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21t +dW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5 +WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYD +VQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw8yl8 +9f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJ +DKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9 +Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N +QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJ +xrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0G +A1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vG +kl3g0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfr +Uj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5 +Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJU +JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot +RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl +MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe +U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX +DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy +dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj +YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV +OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr +zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM +VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ +hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO +ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw +awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs +OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF +coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc +okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8 +t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy +1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/ +SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP +MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAx +MDQwNjA3Mjk0MFoXDTIxMDQwNjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNV +BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMiBDQTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3/Ei9vX+ALTU74W+o +Z6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybTdXnt +5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s +3TmVToMGf+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2Ej +vOr7nQKV0ba5cTppCD8PtOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu +8nYybieDwnPz3BjotJPqdURrBGAgcVeHnfO+oJAjPYok4doh28MCAwEAAaMzMDEw +DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITTXjwwCwYDVR0PBAQDAgEG +MA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt0jSv9zil +zqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/ +3DEIcbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvD +FNr450kkkdAdavphOe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6 +Tk6ezAyNlNzZRZxe7EJQY670XcSxEtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2 +ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLHllpwrN9M +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gRVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0y +MjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIg +TmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRlcmxhbmRlbiBFViBS +b290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkkSzrS +M4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nC +UiY4iKTWO0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3d +Z//BYY1jTw+bbRcwJu+r0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46p +rfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13l +pJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gVXJrm0w912fxBmJc+qiXb +j5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr08C+eKxC +KFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS +/ZbV0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0X +cgOPvZuM5l5Tnrmd74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH +1vI4gnPah1vlPNOePqc7nvQDs/nxfRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrP +px9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwaivsnuL8wbqg7 +MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI +eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u +2dfOWBfoqSmuc0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHS +v4ilf0X8rLiltTMMgsT7B/Zq5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTC +wPTxGfARKbalGAKb12NMcIxHowNDXLldRqANb/9Zjr7dn3LDWyvfjFvO5QxGbJKy +CqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tNf1zuacpzEPuKqf2e +vTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi5Dp6 +Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIa +Gl6I6lD4WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeL +eG9QgkRQP2YGiqtDhFZKDyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8 +FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGyeUN51q1veieQA6TqJIc/2b3Z6fJfUEkc +7uzXLg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oX +DTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl +ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv +b3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ5291 +qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8Sp +uOUfiUtnvWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPU +Z5uW6M7XxgpT0GtJlvOjCwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvE +pMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiile7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp +5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCROME4HYYEhLoaJXhena/M +UGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpICT0ugpTN +GmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy +5V6548r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv +6q012iDTiIJh8BIitrzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEK +eN5KzlW/HdXZt1bv8Hb/C3m1r737qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6 +B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMBAAGjgZcwgZQwDwYDVR0TAQH/ +BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcCARYxaHR0cDov +L3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqG +SIb3DQEBCwUAA4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLyS +CZa59sCrI2AGeYwRTlHSeYAz+51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen +5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwjf/ST7ZwaUb7dRUG/kSS0H4zpX897 +IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaNkqbG9AclVMwWVxJK +gnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfkCpYL ++63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxL +vJxxcypFURmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkm +bEgeqmiSBeGCc1qb3AdbCG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvk +N1trSt8sV4pAWja63XVECDdCcAz+3F4hoKOKwJCcaNpQ5kUQR3i2TtJlycM33+FC +Y7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoVIPVVYpbtbZNQvOSqeK3Z +ywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm66+KAQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloX +DTI4MTExMzIzMDAwMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl +ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv +b3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL4yolQP +cPssXFnrbMSkUeiFKrPMSjTysF/zDsccPVMeiAho2G89rcKezIJnByeHaHE6n3WW +IkYFsO2tx1ueKt6c/DrGlaf1F2cY5y9JCAxcz+bMNO14+1Cx3Gsy8KL+tjzk7FqX +xz8ecAgwoNzFs21v0IJyEavSgWhZghe3eJJg+szeP4TrjTgzkApyI/o1zCZxMdFy +KJLZWyNtZrVtB0LrpjPOktvA9mxjeM3KTj215VKb8b475lRgsGYeCasH/lSJEULR +9yS6YHgamPfJEf0WwTUaVHXvQ9Plrk7O53vDxk5hUUurmkVLoR9BvUhTFXFkC4az +5S6+zqQbwSmEorXLCCN2QyIkHxcE1G6cxvx/K2Ya7Irl1s9N9WMJtxU51nus6+N8 +6U78dULI7ViVDAZCopz35HCz33JvWjdAidiFpNfxC95DGdRKWCyMijmev4SH8RY7 +Ngzp07TKbBlBUgmhHbBqv4LvcFEhMtwFdozL92TkA1CvjJFnq8Xy7ljY3r735zHP +bMk7ccHViLVlvMDoFxcHErVc0qsgk7TmgoNwNsXNo42ti+yjwUOH5kPiNL6VizXt +BznaqB16nzaeErAMZRKQFWDZJkBE41ZgpRDUajz9QdwOWke275dhdU/Z/seyHdTt +XUmzqWrLZoQT1Vyg3N9udwbRcXXIV2+vD3dbAgMBAAGjQjBAMA8GA1UdEwEB/wQF +MAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRUrfrHkleuyjWcLhL75Lpd +INyUVzANBgkqhkiG9w0BAQsFAAOCAgEAMJmdBTLIXg47mAE6iqTnB/d6+Oea31BD +U5cqPco8R5gu4RV78ZLzYdqQJRZlwJ9UXQ4DO1t3ApyEtg2YXzTdO2PCwyiBwpwp +LiniyMMB8jPqKqrMCQj3ZWfGzd/TtiunvczRDnBfuCPRy5FOCvTIeuXZYzbB1N/8 +Ipf3YF3qKS9Ysr1YvY2WTxB1v0h7PVGHoTx0IsL8B3+A3MSs/mrBcDCw6Y5p4ixp +gZQJut3+TcCDjJRYwEYgr5wfAvg1VUkvRtTA8KCWAg8zxXHzniN9lLf9OtMJgwYh +/WA9rjLA0u6NpvDntIJ8CsxwyXmA+P5M9zWEGYox+wrZ13+b8KKaa8MFSu1BYBQw +0aoRQm7TIwIEC8Zl3d1Sd9qBa7Ko+gE4uZbqKmxnl4mUnrzhVNXkanjvSr0rmj1A +fsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq +4BZ+Extq1z7XnvwtdbLBFNUjA9tbbws+eC8N3jONFrdI54OagQ97wUNNVQQXOEpR +1VmiiXTTn74eS9fGbbeIJG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/ +QFH1T/U67cjF68IeHRaVesd+QnGTbksVtzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM +94B7IWcnMFk= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl +MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp +U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw +NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE +ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp +ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3 +DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf +8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN ++lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0 +X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa +K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA +1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G +A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR +zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0 +YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD +bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3 +L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D +eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp +VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY +WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs +ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw +MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj +aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp +Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg +nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 +HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N +Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN +dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 +HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G +CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU +sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 +4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg +8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 +mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs +ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD +VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy +ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy +dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p +OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2 +8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K +Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe +hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk +6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q +AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI +bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB +ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z +qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn +0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN +sSi6 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEW +MBQGA1UEChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkgRzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1 +OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjEsMCoG +A1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRzIwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8Oo1XJ +JZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsD +vfOpL9HG4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnoo +D/Uefyf3lLE3PbfHkffiAez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/ +Q0kGi4xDuFby2X8hQxfqp0iVAXV16iulQ5XqFYSdCI0mblWbq9zSOdIxHWDirMxW +RST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbsO+wmETRIjfaAKxojAuuK +HDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8HvKTlXcxN +nw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM +0D4LnMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/i +UUjXuG+v+E5+M5iSFGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9 +Ha90OrInwMEePnWjFqmveiJdnxMaz6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHg +TuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE +AwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJKoZIhvcNAQEL +BQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K +2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfX +UfEpY9Z1zRbkJ4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl +6/2o1PXWT6RbdejF0mCy2wl+JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK +9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG/+gyRr61M3Z3qAFdlsHB1b6uJcDJ +HgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTcnIhT76IxW1hPkWLI +wpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/XldblhY +XzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5l +IxKVCCIcl85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoo +hdVddLHRDiBYmxOlsGOm7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulr +so8uBtjRkcfGEvRM/TAXw8HaOFvjqermobp573PYtlNXLfbQ4ddI +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW +MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg +Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9 +MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi +U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh +cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk +pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf +OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C +Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT +Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi +HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM +Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w ++2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+ +Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3 +Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B +26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID +AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE +FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j +ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js +LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM +BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0 +Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy +dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh +cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh +YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg +dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp +bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ +YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT +TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ +9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8 +jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW +FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz +ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1 +ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L +EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu +L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq +yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC +O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V +um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh +NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEW +MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg +Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM3WhcNMzYwOTE3MTk0NjM2WjB9 +MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi +U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh +cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk +pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf +OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C +Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT +Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi +HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM +Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w ++2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+ +Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3 +Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B +26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID +AQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFul +F2mHMMo0aEPQQa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCC +ATgwLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5w +ZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL2ludGVybWVk +aWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENvbW1lcmNpYWwgKFN0 +YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0aGUg +c2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93 +d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgG +CWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5fPGFf59Jb2vKXfuM/gTF +wWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWmN3PH/UvS +Ta0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst +0OcNOrg+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNc +pRJvkrKTlMeIFw6Ttn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKl +CcWw0bdT82AUuoVpaiF8H3VhFyAXe2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVF +P0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA2MFrLH9ZXF2RsXAiV+uKa0hK +1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBsHvUwyKMQ5bLm +KhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE +JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ +8dCAWZvLMdibD4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnm +fyWl8kgAwKQB2j8= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF2TCCA8GgAwIBAgIQXAuFXAvnWUHfV8w/f52oNjANBgkqhkiG9w0BAQUFADBk +MQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0 +YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3Qg +Q0EgMTAeFw0wNTA4MTgxMjA2MjBaFw0yNTA4MTgyMjA2MjBaMGQxCzAJBgNVBAYT +AmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZp +Y2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAxMIICIjAN +BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0LmwqAzZuz8h+BvVM5OAFmUgdbI9 +m2BtRsiMMW8Xw/qabFbtPMWRV8PNq5ZJkCoZSx6jbVfd8StiKHVFXqrWW/oLJdih +FvkcxC7mlSpnzNApbjyFNDhhSbEAn9Y6cV9Nbc5fuankiX9qUvrKm/LcqfmdmUc/ +TilftKaNXXsLmREDA/7n29uj/x2lzZAeAR81sH8A25Bvxn570e56eqeqDFdvpG3F +EzuwpdntMhy0XmeLVNxzh+XTF3xmUHJd1BpYwdnP2IkCb6dJtDZd0KTeByy2dbco +kdaXvij1mB7qWybJvbCXc9qukSbraMH5ORXWZ0sKbU/Lz7DkQnGMU3nn7uHbHaBu +HYwadzVcFh4rUx80i9Fs/PJnB3r1re3WmquhsUvhzDdf/X/NTa64H5xD+SpYVUNF +vJbNcA78yeNmuk6NO4HLFWR7uZToXTNShXEuT46iBhFRyePLoW4xCGQMwtI89Tbo +19AOeCMgkckkKmUpWyL3Ic6DXqTz3kvTaI9GdVyDCW4pa8RwjPWd1yAv/0bSKzjC +L3UcPX7ape8eYIVpQtPM+GP+HkM5haa2Y0EQs3MevNP6yn0WR+Kn1dCjigoIlmJW +bjTb2QK5MHXjBNLnj8KwEUAKrNVxAmKLMb7dxiNYMUJDLXT5xp6mig/p/r+D5kNX +JLrvRjSq1xIBOO0CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0hBBYw +FDASBgdghXQBUwABBgdghXQBUwABMBIGA1UdEwEB/wQIMAYBAf8CAQcwHwYDVR0j +BBgwFoAUAyUv3m+CATpcLNwroWm1Z9SM0/0wHQYDVR0OBBYEFAMlL95vggE6XCzc +K6FptWfUjNP9MA0GCSqGSIb3DQEBBQUAA4ICAQA1EMvspgQNDQ/NwNurqPKIlwzf +ky9NfEBWMXrrpA9gzXrzvsMnjgM+pN0S734edAY8PzHyHHuRMSG08NBsl9Tpl7Ik +Vh5WwzW9iAUPWxAaZOHHgjD5Mq2eUCzneAXQMbFamIp1TpBcahQq4FJHgmDmHtqB +sfsUC1rxn9KVuj7QG9YVHaO+htXbD8BJZLsuUBlL0iT43R4HVtA4oJVwIHaM190e +3p9xxCPvgxNcoyQVTSlAPGrEqdi3pkSlDfTgnXceQHAm/NrZNuR55LU/vJtlvrsR +ls/bxig5OgjOR1tTWsWZ/l2p3e9M1MalrQLmjAcSHm8D0W+go/MpvRLHUKKwf4ip +mXeascClOS5cfGniLLDqN2qk4Vrh9VDlg++luyqI54zb/W1elxmofmZ1a3Hqv7HH +b6D0jqTsNFFbjCYDcKF31QESVwA12yPeDooomf2xEG9L/zgtYE4snOtnta1J7ksf +rK/7DZBaZmBwXarNeNQk7shBoJMBkpxqnvy5JMWzFYJ+vq6VK+uxwNrjAWALXmms +hFZhvnEX/h0TD/7Gh0Xp/jKgGg0TpJRVcaUWi7rKibCyx/yP2FS1k2Kdzs9Z+z0Y +zirLNRWCXf9UIltxUvu3yf5gmwBBZPCqKuy2QkPOiWaByIufOVQDJdMWNY6E0F/6 +MBr1mmz0DlP5OlvRHA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF2TCCA8GgAwIBAgIQHp4o6Ejy5e/DfEoeWhhntjANBgkqhkiG9w0BAQsFADBk +MQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0 +YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3Qg +Q0EgMjAeFw0xMTA2MjQwODM4MTRaFw0zMTA2MjUwNzM4MTRaMGQxCzAJBgNVBAYT +AmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZp +Y2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAyMIICIjAN +BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlUJOhJ1R5tMJ6HJaI2nbeHCOFvEr +jw0DzpPMLgAIe6szjPTpQOYXTKueuEcUMncy3SgM3hhLX3af+Dk7/E6J2HzFZ++r +0rk0X2s682Q2zsKwzxNoysjL67XiPS4h3+os1OD5cJZM/2pYmLcX5BtS5X4HAB1f +2uY+lQS3aYg5oUFgJWFLlTloYhyxCwWJwDaCFCE/rtuh/bxvHGCGtlOUSbkrRsVP +ACu/obvLP+DHVxxX6NZp+MEkUp2IVd3Chy50I9AU/SpHWrumnf2U5NGKpV+GY3aF +y6//SSj8gO1MedK75MDvAe5QQQg1I3ArqRa0jG6F6bYRzzHdUyYb3y1aSgJA/MTA +tukxGggo5WDDH8SQjhBiYEQN7Aq+VRhxLKX0srwVYv8c474d2h5Xszx+zYIdkeNL +6yxSNLCK/RJOlrDrcH+eOfdmQrGrrFLadkBXeyq96G4DsguAhYidDMfCd7Camlf0 +uPoTXGiTOmekl9AbmbeGMktg2M7v0Ax/lZ9vh0+Hio5fCHyqW/xavqGRn1V9TrAL +acywlKinh/LTSlDcX3KwFnUey7QYYpqwpzmqm59m2I2mbJYV4+by+PGDYmy7Velh +k6M99bFXi08jsJvllGov34zflVEpYKELKeRcVVi3qPyZ7iVNTA6z00yPhOgpD/0Q +VAKFyPnlw4vP5w8CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0hBBYw +FDASBgdghXQBUwIBBgdghXQBUwIBMBIGA1UdEwEB/wQIMAYBAf8CAQcwHQYDVR0O +BBYEFE0mICKJS9PVpAqhb97iEoHF8TwuMB8GA1UdIwQYMBaAFE0mICKJS9PVpAqh +b97iEoHF8TwuMA0GCSqGSIb3DQEBCwUAA4ICAQAyCrKkG8t9voJXiblqf/P0wS4R +fbgZPnm3qKhyN2abGu2sEzsOv2LwnN+ee6FTSA5BesogpxcbtnjsQJHzQq0Qw1zv +/2BZf82Fo4s9SBwlAjxnffUy6S8w5X2lejjQ82YqZh6NM4OKb3xuqFp1mrjX2lhI +REeoTPpMSQpKwhI3qEAMw8jh0FcNlzKVxzqfl9NX+Ave5XLzo9v/tdhZsnPdTSpx +srpJ9csc1fV5yJmz/MFMdOO0vSk3FQQoHt5FRnDsr7p4DooqzgB53MBfGWcsa0vv +aGgLQ+OswWIJ76bdZWGgr4RVSJFSHMYlkSrQwSIjYVmvRRGFHQEkNI/Ps/8XciAT +woCqISxxOQ7Qj1zB09GOInJGTB2Wrk9xseEFKZZZ9LuedT3PDTcNYtsmjGOpI99n +Bjx8Oto0QuFmtEYE3saWmA9LSHokMnWRn6z3aOkquVVlzl1h0ydw2Df+n7mvoC5W +t6NlUe07qxS/TFED6F+KBZvuim6c779o+sjaC+NCydAXFJy3SuCvkychVSa1ZC+N +8f+mQAWFBVzKBxlcCxMoTFh/wqXvRdpg065lYZ1Tg3TCrvJcwhbtkj6EPnNgiLx2 +9CzP0H1907he0ZESEOnN3col49XtmS++dYFLJPlFRpTJKSFTnCZFqhMX5OfNeOI5 +wSsSnqaeG8XmDtkx2Q== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF4DCCA8igAwIBAgIRAPL6ZOJ0Y9ON/RAdBB92ylgwDQYJKoZIhvcNAQELBQAw +ZzELMAkGA1UEBhMCY2gxETAPBgNVBAoTCFN3aXNzY29tMSUwIwYDVQQLExxEaWdp +dGFsIENlcnRpZmljYXRlIFNlcnZpY2VzMR4wHAYDVQQDExVTd2lzc2NvbSBSb290 +IEVWIENBIDIwHhcNMTEwNjI0MDk0NTA4WhcNMzEwNjI1MDg0NTA4WjBnMQswCQYD +VQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2Vy +dGlmaWNhdGUgU2VydmljZXMxHjAcBgNVBAMTFVN3aXNzY29tIFJvb3QgRVYgQ0Eg +MjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMT3HS9X6lds93BdY7Bx +UglgRCgzo3pOCvrY6myLURYaVa5UJsTMRQdBTxB5f3HSek4/OE6zAMaVylvNwSqD +1ycfMQ4jFrclyxy0uYAyXhqdk/HoPGAsp15XGVhRXrwsVgu42O+LgrQ8uMIkqBPH +oCE2G3pXKSinLr9xJZDzRINpUKTk4RtiGZQJo/PDvO/0vezbE53PnUgJUmfANykR +HvvSEaeFGHR55E+FFOtSN+KxRdjMDUN/rhPSays/p8LiqG12W0OfvrSdsyaGOx9/ +5fLoZigWJdBLlzin5M8J0TbDC77aO0RYjb7xnglrPvMyxyuHxuxenPaHZa0zKcQv +idm5y8kDnftslFGXEBuGCxobP/YCfnvUxVFkKJ3106yDgYjTdLRZncHrYTNaRdHL +OdAGalNgHa/2+2m8atwBz735j9m9W8E6X47aD0upm50qKGsaCnw8qyIL5XctcfaC +NYGu+HuB5ur+rPQam3Rc6I8k9l2dRsQs0h4rIWqDJ2dVSqTjyDKXZpBy2uPUZC5f +46Fq9mDU5zXNysRojddxyNMkM3OxbPlq4SjbX8Y96L5V5jcb7STZDxmPX2MYWFCB +UWVv8p9+agTnNCRxunZLWB4ZvRVgRaoMEkABnRDixzgHcgplwLa7JSnaFp6LNYth +7eVxV4O1PHGf40+/fh6Bn0GXAgMBAAGjgYYwgYMwDgYDVR0PAQH/BAQDAgGGMB0G +A1UdIQQWMBQwEgYHYIV0AVMCAgYHYIV0AVMCAjASBgNVHRMBAf8ECDAGAQH/AgED +MB0GA1UdDgQWBBRF2aWBbj2ITY1x0kbBbkUe88SAnTAfBgNVHSMEGDAWgBRF2aWB +bj2ITY1x0kbBbkUe88SAnTANBgkqhkiG9w0BAQsFAAOCAgEAlDpzBp9SSzBc1P6x +XCX5145v9Ydkn+0UjrgEjihLj6p7jjm02Vj2e6E1CqGdivdj5eu9OYLU43otb98T +PLr+flaYC/NUn81ETm484T4VvwYmneTwkLbUwp4wLh/vx3rEUMfqe9pQy3omywC0 +Wqu1kx+AiYQElY2NfwmTv9SoqORjbdlk5LgpWgi/UOGED1V7XwgiG/W9mR4U9s70 +WBCCswo9GcG/W6uqmdjyMb3lOGbcWAXH7WMaLgqXfIeTK7KK4/HsGOV1timH59yL +Gn602MnTihdsfSlEvoqq9X46Lmgxk7lq2prg2+kupYTNHAq4Sgj5nPFhJpiTt3tm +7JFe3VE/23MPrQRYCd0EApUKPtN236YQHoA96M2kZNEzx5LH4k5E4wnJTsJdhw4S +nr8PyQUQ3nqjsTzyP6WqJ3mtMX0f/fwZacXduT98zca0wjAefm6S139hdlqP65VN +vBFuIXxZN5nQBrz5Bm0yFqXZaajh3DyAHmBR3NdUIR7KYndP+tiPsys6DXhyyWhB +WkdKwqPrGtcKqzwyVcgKEZzfdNbwQBUdyLmPtTbFr/giuMod89a2GQ+fYWVq6nTI +fI/DT11lgh/ZDYnadXL77/FHZxOzyNEZiCcmmpl5fx7kLD977vHeTYuWl8PVP3wb +I+2ksx0WckNLIOFZfsLorSa/ovc= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV +BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2ln +biBHb2xkIENBIC0gRzIwHhcNMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBF +MQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMR8wHQYDVQQDExZT +d2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUqt2/8 +76LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+ +bbqBHH5CjCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c +6bM8K8vzARO/Ws/BtQpgvd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqE +emA8atufK+ze3gE/bk3lUIbLtK/tREDFylqM2tIrfKjuvqblCqoOpd8FUrdVxyJd +MmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvRAiTysybUa9oEVeXBCsdt +MDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuendjIj3o02y +MszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69y +FGkOpeUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPi +aG59je883WX0XaxR7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxM +gI93e2CaHt+28kgeDrpOVG2Y4OGiGqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCB +qTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUWyV7 +lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64OfPAeGZe6Drn +8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov +L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe6 +45R88a7A3hfm5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczO +UYrHUDFu4Up+GC9pWbY9ZIEr44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5 +O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOfMke6UiI0HTJ6CVanfCU2qT1L2sCC +bwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6mGu6uLftIdxf+u+yv +GPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxpmo/a +77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCC +hdiDyyJkvC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid3 +92qgQmwLOM7XdVAyksLfKzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEpp +Ld6leNcG2mqeSz53OiATIgHQv2ieY2BrNU0LbbqhPcCT4H8js1WtciVORvnSFu+w +ZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6LqjviOvrv1vA+ACOzB2+htt +Qc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFejCCA2KgAwIBAgIJAN7E8kTzHab8MA0GCSqGSIb3DQEBCwUAMEoxCzAJBgNV +BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxJDAiBgNVBAMTG1N3aXNzU2ln +biBHb2xkIFJvb3QgQ0EgLSBHMzAeFw0wOTA4MDQxMzMxNDdaFw0zNzA4MDQxMzMx +NDdaMEoxCzAJBgNVBAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxJDAiBgNV +BAMTG1N3aXNzU2lnbiBHb2xkIFJvb3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBAMPon8hlWp1nG8FFl7S0h0NbYWCAnvJ/XvlnRN1E+qu1 +q3f/KhlMzm/Ej0Gf4OLNcuDR1FJhQQkKvwpw++CDaWEpytsimlul5t0XlbBvhI46 +PmRaQfsbWPz9Kz6ypOasyYK8zvaV+Jd37Sb2WK6eJ+IPg+zFNljIe8/Vh6GphxoT +Z2EBbaZpnOKQ8StoZfPosHz8gj3erdgKAAlEeROc8P5udXvCvLNZAQt8xdUt8L// +bVfSSYHrtLNQrFv5CxUVjGn/ozkB7fzc3CeXjnuL1Wqm1uAdX80Bkeb1Ipi6LgkY +OG8TqIHS+yE35y20YueBkLDGeVm3Z3X+vo87+jbsr63ST3Q2AeVXqyMEzEpel89+ +xu+MzJUjaY3LOMcZ9taKABQeND1v2gwLw7qX/BFLUmE+vzNnUxC/eBsJwke6Hq9Y +9XWBf71W8etW19lpDAfpNzGwEhwy71bZvnorfL3TPbxqM006PFAQhyfHegpnU9t/ +gJvoniP6+Qg6i6GONFpIM19k05eGBxl9iJTOKnzFat+vvKmfzTqmurtU+X+P388O +WsStmryzOndzg0yTPJBotXxQlRHIgl6UcdBBGPvJxmXszom2ziKzEVs/4J0+Gxho +DaoDoWdZv2udvPjyZS+aQTpF2F7QNmxvOx5jtI6YTBPbIQ6fe+3qoKpxw+ujoNIl +AgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBRclwZGNKvfMMV8xQ1VcWYwtWCPnjAfBgNVHSMEGDAWgBRclwZGNKvfMMV8 +xQ1VcWYwtWCPnjANBgkqhkiG9w0BAQsFAAOCAgEAd0tN3uqFSqssJ9ZFx/FfIMFb +YO0Hy6Iz3DbPx5TxBsfV2s/NrYQ+/xJIf0HopWZXMMQd5KcaLy1Cwe9Gc7LV9Vr9 +Dnpr0sgxow1IlldlY1UYwPzkisyYhlurDIonN/ojaFlcJtehwcK5Tiz/KV7mlAu+ +zXJPleiP9ve4Pl7Oz54RyawDKUiKqbamNLmsQP/EtnM3scd/qVHbSypHX0AkB4gG +tySz+3/3sIsz+r8jdaNc/qplGsK+8X2BdwOBsY3XlQ16PEKYt4+pfVDh31IGmqBS +VHiDB2FSCTdeipynxlHRXGPRhNzC29L6Wxg2fWa81CiXL3WWHIQHrIuOUxG+JCGq +Z/LBrYic07B4Z3j101gDIApdIPG152XMDiDj1d/mLxkrhWjBBCbPj+0FU6HdBw7r +QSbHtKksW+NpPWbAYhvAqobAN8MxBIZwOb5rXyFAQaB/5dkPOEtwX0n4hbgrLqof +k0FD+PuydDwfS1dbt9RRoZJKzr4Qou7YFCJ7uUG9jemIqdGPAxpg/z+HiaCZJyJm +sD5onnKIUTidEz5FbQXlRrVz7UOGsRQKHrzaDb8eJFxmjw6+of3G62m8Q3nXA3b5 +3IeZuJjEzX9tEPkQvixC/pwpTYNrCr21jsRIiv0hB6aAfR+b6au9gmFECnEnX22b +kJ6u/zYks2gD1pWMa3M= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFwTCCA6mgAwIBAgIITrIAZwwDXU8wDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE +BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEjMCEGA1UEAxMaU3dpc3NTaWdu +IFBsYXRpbnVtIENBIC0gRzIwHhcNMDYxMDI1MDgzNjAwWhcNMzYxMDI1MDgzNjAw +WjBJMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMSMwIQYDVQQD +ExpTd2lzc1NpZ24gUGxhdGludW0gQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAMrfogLi2vj8Bxax3mCq3pZcZB/HL37PZ/pEQtZ2Y5Wu669y +IIpFR4ZieIbWIDkm9K6j/SPnpZy1IiEZtzeTIsBQnIJ71NUERFzLtMKfkr4k2Htn +IuJpX+UFeNSH2XFwMyVTtIc7KZAoNppVRDBopIOXfw0enHb/FZ1glwCNioUD7IC+ +6ixuEFGSzH7VozPY1kneWCqv9hbrS3uQMpe5up1Y8fhXSQQeol0GcN1x2/ndi5ob +jM89o03Oy3z2u5yg+gnOI2Ky6Q0f4nIoj5+saCB9bzuohTEJfwvH6GXp43gOCWcw +izSC+13gzJ2BbWLuCB4ELE6b7P6pT1/9aXjvCR+htL/68++QHkwFix7qepF6w9fl ++zC8bBsQWJj3Gl/QKTIDE0ZNYWqFTFJ0LwYfexHihJfGmfNtf9dng34TaNhxKFrY +zt3oEBSa/m0jh26OWnA81Y0JAKeqvLAxN23IhBQeW71FYyBrS3SMvds6DsHPWhaP +pZjydomyExI7C3d3rLvlPClKknLKYRorXkzig3R3+jVIeoVNjZpTxN94ypeRSCtF +KwH3HBqi7Ri6Cr2D+m+8jVeTO9TUps4e8aCxzqv9KyiaTxvXw3LbpMS/XUz13XuW +ae5ogObnmLo2t/5u7Su9IPhlGdpVCX4l3P5hYnL5fhgC72O00Puv5TtjjGePAgMB +AAGjgawwgakwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFFCvzAeHFUdvOMW0ZdHelarp35zMMB8GA1UdIwQYMBaAFFCvzAeHFUdvOMW0 +ZdHelarp35zMMEYGA1UdIAQ/MD0wOwYJYIV0AVkBAQEBMC4wLAYIKwYBBQUHAgEW +IGh0dHA6Ly9yZXBvc2l0b3J5LnN3aXNzc2lnbi5jb20vMA0GCSqGSIb3DQEBBQUA +A4ICAQAIhab1Fgz8RBrBY+D5VUYI/HAcQiiWjrfFwUF1TglxeeVtlspLpYhg0DB0 +uMoI3LQwnkAHFmtllXcBrqS3NQuB2nEVqXQXOHtYyvkv+8Bldo1bAbl93oI9ZLi+ +FHSjClTTLJUYFzX1UWs/j6KWYTl4a0vlpqD4U99REJNi54Av4tHgvI42Rncz7Lj7 +jposiU0xEQ8mngS7twSNC/K5/FqdOxa3L8iYq/6KUFkuozv8KV2LwUvJ4ooTHbG/ +u0IdUt1O2BReEMYxB+9xJ/cbOQncguqLs5WGXv312l0xpuAxtpTmREl0xRbl9x8D +YSjFyMsSoEJL+WuICI20MhjzdZ/EfwBPBZWcoxcCw7NTm6ogOSkrZvqdr16zktK1 +puEa+S1BaYEUtLS17Yk9zvupnTVCRLEcFHOBzyoBNZox1S2PbYTfgE1X4z/FhHXa +icYwu+uPyyIIoK6q8QNsOktNCaUOcsZWayFCTiMlFGiudgp8DAdwZPmaL/YFOSbG +DI8Zf0NebvRbFS/bYV3mZy8/CJT5YLSYMdp08YSTcU1f+2BY0fvEwW2JorsgH51x +kcsymxM9Pn2SUjWskpSi0xjCfMfqr3YFFt1nJ8J+HAciIfNAChs0B0QTwoRqjt8Z +Wr9/6x3iGjjRXK9HkmuAtTClyY3YqzGBH9/CZjfTk6mFhnll0g== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFgTCCA2mgAwIBAgIIIj+pFyDegZQwDQYJKoZIhvcNAQELBQAwTjELMAkGA1UE +BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEoMCYGA1UEAxMfU3dpc3NTaWdu +IFBsYXRpbnVtIFJvb3QgQ0EgLSBHMzAeFw0wOTA4MDQxMzM0MDRaFw0zNzA4MDQx +MzM0MDRaME4xCzAJBgNVBAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxKDAm +BgNVBAMTH1N3aXNzU2lnbiBQbGF0aW51bSBSb290IENBIC0gRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCUoO8TG59EIBvNxaoiu9nyUj56Wlh35o2h +K8ncpPPksxOUAGKbHPJDUEOBfq8wNkmsGIkMGEW4PsdUbePYmllriholqba1Dbd9 +I/BffagHqfc+hi7IAU3c5jbtHeU3B2kSS+OD0QQcJPAfcHHnGe1zSG6VKxW2VuYC +31bpm/rqpu7gwsO64MzGyHvXbzqVmzqPvlss0qmgOD7WiOGxYhOO3KswZ82oaqZj +K4Kwy8c9Tu1y9n2rMk5lAusPmXT4HBoojA5FAJMsFJ9txxue9orce3jjtJRHHU0F +bYR6kFSynot1woDfhzk/n/tIVAeNoCn1+WBfWnLou5ugQuAIADSjFTwT49YaawKy +lCGjnUG8KmtOMzumlDj8PccrM7MuKwZ0rJsQb8VORfddoVYDLA1fer0e3h13kGva +pS2KTOnfQfTnS+x9lUKfTKkJD0OIPz2T5yv0ekjaaMTdEoAxGl0kVCamJCGzTK3a +Fwg2AlfGnIZwyXXJnnxh2HjmuegUafkcECgSXUt1ULo80GdwVVVWS/s9HNjbeU2X +37ie2xcs1TUHuFCp9473Vv96Z0NPINnKZtY4YEvulDHWDaJIm/80aZTGNfWWiO+q +ZsyBputMU/8ydKe2nZhXtLomqfEzM2J+OrADEVf/3G8RI60+xgrQzFS3LcKTHeXC +pozH2O9T9wIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUVio/kFj0F1oUstcIG4VbVGpUGigwHwYDVR0jBBgwFoAUVio/ +kFj0F1oUstcIG4VbVGpUGigwDQYJKoZIhvcNAQELBQADggIBAGztiudDqHknm7jP +hz5kOBiMEUKShjfgWMMb7gQu94TsgxBoDH94LZzCl442ThbYDuprSK1Pnl0NzA2p +PhiFfsxomTk11tifhsEy+01lsyIUS8iFZtoX/3GRrJxWV95xLFZCv/jNDvCi0//S +IhX70HgKfuGwWs6ON9upnueVz2PyLA3S+m/zyNX7ALf3NWcQ03tS7BAy+L/dXsmm +gqTxsL8dLt0l5L1N8DWpkQFH+BAClFvrPusNutUdYyylLqvn4x6j7kuqX7FmAbSC +WvlGS8fx+N8svv113ZY4mjc6bqXmMhVus5DAOYp0pZWgvg0uiXnNKVaOw15XUcQF +bwRVj4HpTL1ZRssqvE3JHfLGTwXkyAQN925P2sM6nNLC9enGJHoUPhxCMKgCRTGp +/FCp3NyGOA9bkz9/CE5qDSc6EHlWwxW4PgaG9tlwZ691eoviWMzGdU8yVcVsFAko +O/KV5GreLCgHraB9Byjd1Fqj6aZ8E4yZC1J429nR3z5aQ3Z/RmBTws3ndkd8Vc20 +OWQQW5VLNV1EgyTV4C4kDMGAbmkAgAZ3CmaCEAxRbzeJV9vzTOW4ue4jZpdgt1Ld +2Zb7uoo7oE3OXvBETJDMIU8bOphrjjGD+YMIUssZwTVr7qEVW4g/bazyNJJTpjAq +E9fmhqhd2ULSx52peovL3+6iMcLl +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UE +BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWdu +IFNpbHZlciBDQSAtIEcyMB4XDTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0Nlow +RzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMY +U3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644N0Mv +Fz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7br +YT7QbNHm+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieF +nbAVlDLaYQ1HTWBCrpJH6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH +6ATK72oxh9TAtvmUcXtnZLi2kUpCe2UuMGoM9ZDulebyzYLs2aFK7PayS+VFheZt +eJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5hqAaEuSh6XzjZG6k4sIN/ +c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5FZGkECwJ +MoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRH +HTBsROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTf +jNFusB3hB48IHpmccelM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb6 +5i/4z3GcRm25xBWNOHkDRUjvxF3XCO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOB +rDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU +F6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRBtjpbO8tFnb0c +wpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0 +cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIB +AHPGgeAn0i0P4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShp +WJHckRE1qTodvBqlYJ7YH39FkWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9 +xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L3XWgwF15kIwb4FDm3jH+mHtwX6WQ +2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx/uNncqCxv1yL5PqZ +IseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFaDGi8 +aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2X +em1ZqSqPe97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQR +dAtq/gsD/KNVV4n+SsuuWxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/ +OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJDIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+ +hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ubDgEj8Z+7fNzcbBGXJbLy +tGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFfjCCA2agAwIBAgIJAKqIsFoLsXabMA0GCSqGSIb3DQEBCwUAMEwxCzAJBgNV +BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxJjAkBgNVBAMTHVN3aXNzU2ln +biBTaWx2ZXIgUm9vdCBDQSAtIEczMB4XDTA5MDgwNDEzMTkxNFoXDTM3MDgwNDEz +MTkxNFowTDELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEmMCQG +A1UEAxMdU3dpc3NTaWduIFNpbHZlciBSb290IENBIC0gRzMwggIiMA0GCSqGSIb3 +DQEBAQUAA4ICDwAwggIKAoICAQC+h5sF5nF8Um9t7Dep6bPczF9/01DqIZsE8D2/ +vo7JpRQWMhDPmfzscK1INmckDBcy1inlSjmxN+umeAxsbxnKTvdR2hro+iE4bJWc +L9aLzDsCm78mmxFFtrg0Wh2mVEhSyJ14cc5ISsyneIPcaKtmHncH0zYYCNfUbWD4 +8HnTMzYJkmO3BJr1p5baRa90GvyC46hbDjo/UleYfrycjMHAslrfxH7+DKZUdoN+ +ut3nKvRKNk+HZS6lujmNWWEp89OOJHCMU5sRpUcHsnUFXA2E2UTZzckmRFduAn2V +AdSrJIbuPXD7V/qwKRTQnfLFl8sJyvHyPefYS5bpiC+eR1GKVGWYSNIS5FR3DAfm +vluc8d0Dfo2E/L7JYtX8yTroibVfwgVSYfCcPuwuTYxykY7IQ8GiKF71gCTc4i+H +O1MA5cvwsnyNeRmgiM14+MWKWnflBqzdSt7mcG6+r771sasOCLDboD+Uxb4Subx7 +J3m1MildrsUgI5IDe1Q5sIkiVG0S48N46jpA/aSTrOktiDzbpkdmTN/YF+0W3hrW +10Fmvx2A8aTgZBEpXgwnBWLr5cQEYtHEnwxqVdZYOJxmD537q1SAmZzsSdaCn9pF +1j9TBgO3/R/shn104KS06DK2qgcj+O8kQZ5jMHj0VN2O8Fo4jhJ/eMdvAlYhM864 +uK1pVQIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUoYxFkwoSYwunV18ySn3hIee3PmYwHwYDVR0jBBgwFoAUoYxFkwoS +YwunV18ySn3hIee3PmYwDQYJKoZIhvcNAQELBQADggIBAIeuYW1IOCrGHNxKLoR4 +ScAjKkW4NU3RBfq5BTPEZL3brVQWKrA+DVoo2qYagHMMxEFvr7g0tnfUW44dC4tG +kES1s+5JGInBSzSzhzV0op5FZ+1FcWa2uaElc9fCrIj70h2na9rAWubYWWQ0l2Ug +MTMDT86tCZ6u6cI+GHW0MyUSuwXsULpxQOK93ohGBSGEi6MrHuswMIm/EfVcRPiR +i0tZRQswDcoMT29jvgT+we3gh/7IzVa/5dyOetTWKU6A26ubP45lByL3RM2WHy3H +9Qm2mHD/ONxQFRGEO3+p8NgkVMgXjCsTSdaZf0XRD46/aXI3Uwf05q79Wz55uQbN +uIF4tE2g0DW65K7/00m8Ne1jxrP846thWgW2C+T/qSq+31ROwktcaNqjMqLJTVcY +UzRZPGaZ1zwCeKdMcdC/2/HEPOcB5gTyRPZIJjAzybEBGesC8cwh+joCMBedyF+A +P90lrAKb4xfevcqSFNJSgVPm6vwwZzKpYvaTFxUHMV4PG2n19Km3fC2z7YREMkco +BzuGaUWpxzaWkHJ02BKmcyPRTrm2ejrEKaFQBhG52fQmbmIIEiAW8AFXF9QFNmeX +61H5/zMkDAUPVr/vPRxSjoreaQ9aH/DVAzFEs5LG6nWorrvHYAOImP/HBIRSkIbh +tJOpUC/o69I2rDBgp9ADE7UK +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICqDCCAi2gAwIBAgIQIW4zpcvTiKRvKQe0JzzE2DAKBggqhkjOPQQDAzCBlDEL +MAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYD +VQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRlYyBD +bGFzcyAxIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0g +RzQwHhcNMTExMDA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBlDELMAkGA1UEBhMC +VVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYDVQQLExZTeW1h +bnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRlYyBDbGFzcyAxIFB1 +YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAATXZrUb266zYO5G6ohjdTsqlG3zXxL24w+etgoUU0hS +yNw6s8tIICYSTvqJhNTfkeQpfSgB2dsYQ2mhH7XThhbcx39nI9/fMTGDAzVwsUu3 +yBe7UcvclBfb6gk7dhLeqrWjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBRlwI0l9Qy6l3eQP54u4Fr1ztXh5DAKBggqhkjOPQQD +AwNpADBmAjEApa7jRlP4mDbjIvouKEkN7jB+M/PsP3FezFWJeJmssv3cHFwzjim5 +axfIEWi13IMHAjEAnMhE2mnCNsNUGRCFAtqdR+9B52wmnQk9922Q0QVEL7C8g5No +8gxFSTm/mQQc0xCg +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIID9jCCAt6gAwIBAgIQJDJ18h0v0gkz97RqytDzmDANBgkqhkiG9w0BAQsFADCB +lDELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8w +HQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRl +YyBDbGFzcyAxIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +IC0gRzYwHhcNMTExMDE4MDAwMDAwWhcNMzcxMjAxMjM1OTU5WjCBlDELMAkGA1UE +BhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYDVQQLExZT +eW1hbnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRlYyBDbGFzcyAx +IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzYwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHOddJZKmZgiJM6kXZBxbje/SD +6Jlz+muxNuCad6BAwoGNAcfMjL2Pffd543pMA03Z+/2HOCgs3ZqLVAjbZ/sbjP4o +ki++t7JIp4Gh2F6Iw8w5QEFa0dzl2hCfL9oBTf0uRnz5LicKaTfukaMbasxEvxvH +w9QRslBglwm9LiL1QYRmn81ApqkAgMEflZKf3vNI79sdd2H8f9/ulqRy0LY+/3gn +r8uSFWkI22MQ4uaXrG7crPaizh5HmbmJtxLmodTNWRFnw2+F2EJOKL5ZVVkElauP +N4C/DfD8HzpkMViBeNfiNfYgPym4jxZuPkjctUwH4fIa6n4KedaovetdhitNAgMB +AAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBQzQejIORIVk0jyljIuWvXalF9TYDANBgkqhkiG9w0BAQsFAAOCAQEAFeNzV7EX +tl9JaUSm9l56Z6zS3nVJq/4lVcc6yUQVEG6/MWvL2QeTfxyFYwDjMhLgzMv7OWyP +4lPiPEAz2aSMR+atWPuJr+PehilWNCxFuBL6RIluLRQlKCQBZdbqUqwFblYSCT3Q +dPTXvQbKqDqNVkL6jXI+dPEDct+HG14OelWWLDi3mIXNTTNEyZSPWjEwN0ujOhKz +5zbRIWhLLTjmU64cJVYIVgNnhJ3Gw84kYsdMNs+wBkS39V8C3dlU6S+QTnrIToNA +DJqXPDe/v+z28LSFdyjBC8hnghAXOKK3Buqbvzr46SMHv3TgmDgVVXjucgBcGaP0 +0jPg/73RVDkpDw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICqDCCAi2gAwIBAgIQNBdlEkA7t1aALYDLeVWmHjAKBggqhkjOPQQDAzCBlDEL +MAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYD +VQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRlYyBD +bGFzcyAyIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0g +RzQwHhcNMTExMDA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBlDELMAkGA1UEBhMC +VVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYDVQQLExZTeW1h +bnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRlYyBDbGFzcyAyIFB1 +YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAATR2UqOTA2ESlG6fO/TzPo6mrWnYxM9AeBJPvrBR8mS +szrX/m+c95o6D/UOCgrDP8jnEhSO1dVtmCyzcTIK6yq99tdqIAtnRZzSsr9TImYJ +XdsR8/EFM1ij4rjPfM2Cm72jQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBQ9MvM6qQyQhPmijGkGYVQvh3L+BTAKBggqhkjOPQQD +AwNpADBmAjEAyKapr0F/tckRQhZoaUxcuCcYtpjxwH+QbYfTjEYX8D5P/OqwCMR6 +S7wIL8fip29lAjEA1lnehs5fDspU1cbQFQ78i5Ry1I4AWFPPfrFLDeVQhuuea9// +KabYR9mglhjb8kWz +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIID9jCCAt6gAwIBAgIQZIKe/DcedF38l/+XyLH/QTANBgkqhkiG9w0BAQsFADCB +lDELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8w +HQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRl +YyBDbGFzcyAyIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +IC0gRzYwHhcNMTExMDE4MDAwMDAwWhcNMzcxMjAxMjM1OTU5WjCBlDELMAkGA1UE +BhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYDVQQLExZT +eW1hbnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRlYyBDbGFzcyAy +IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzYwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNzOkFyGOFyz9AYxe9GPo15gRn +V2WYKaRPyVyPDzTS+NqoE2KquB5QZ3iwFkygOakVeq7t0qLA8JA3KRgmXOgNPLZs +ST/B4NzZS7YUGQum05bh1gnjGSYc+R9lS/kaQxwAg9bQqkmi1NvmYji6UBRDbfkx ++FYW2TgCkc/rbN27OU6Z4TBnRfHU8I3D3/7yOAchfQBeVkSz5GC9kSucq1sEcg+y +KNlyqwUgQiWpWwNqIBDMMfAr2jUs0Pual07wgksr2F82owstr2MNHSV/oW5cYqGN +KD6h/Bwg+AEvulWaEbAZ0shQeWsOagXXqgQ2sqPy4V93p3ec5R7c6d9qwWVdAgMB +AAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBSHjCCVyJhK0daABkqQNETfHE2/sDANBgkqhkiG9w0BAQsFAAOCAQEAgY6ypWaW +tyGltu9vI1pf24HFQqV4wWn99DzX+VxrcHIa/FqXTQCAiIiCisNxDY7FiZss7Y0L +0nJU9X3UXENX6fOupQIR9nYrgVfdfdp0MP1UR/bgFm6mtApI5ud1Bw8pGTnOefS2 +bMVfmdUfS/rfbSw8DVSAcPCIC4DPxmiiuB1w2XaM/O6lyc+tHc+ZJVdaYkXLFmu9 +Sc2lo4xpeSWuuExsi0BmSxY/zwIa3eFsawdhanYVKZl/G92IgMG/tY9zxaaWI4Sm +KIYkM2oBLldzJbZev4/mHWGoQClnHYebHX+bn5nNMdZUvmK7OaxoEkiRIKXLsd3+ +b/xa5IJVWa8xqQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICpzCCAi2gAwIBAgIQTHm1miicdjFk9YlE0JEC3jAKBggqhkjOPQQDAzCBlDEL +MAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYD +VQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRlYyBD +bGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0g +RzQwHhcNMTIxMDE4MDAwMDAwWhcNMzcxMjAxMjM1OTU5WjCBlDELMAkGA1UEBhMC +VVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYDVQQLExZTeW1h +bnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRlYyBDbGFzcyAzIFB1 +YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAARXz+qzOU0/oSHgbi84csaHl/OFC0fnD1HI0fSZm8pZ +Zf9M+eoLtyXV0vbsMS0yYhLXdoan+jjJZdT+c+KEOfhMSWIT3brViKBfPchPsD+P +oVAR5JNGrcNfy/GkapVW6MCjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBQknbzScfcdwiW+IvGJpSwVOzQeXjAKBggqhkjOPQQD +AwNoADBlAjEAuWZoZdsF0Dh9DvPIdWG40CjEsUozUVj78jwQyK5HeHbKZiQXhj5Q +Vm6lLZmIuL0kAjAD6qfnqDzqnWLGX1TamPR3vU+PGJyRXEdrQE0QHbPhicoLIsga +xcX+i93B3294n5E= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF9jCCA96gAwIBAgIQZWNxhdNvRcaPfzH5CYeSgjANBgkqhkiG9w0BAQwFADCB +lDELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8w +HQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRl +YyBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +IC0gRzYwHhcNMTIxMDE4MDAwMDAwWhcNMzcxMjAxMjM1OTU5WjCBlDELMAkGA1UE +BhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYDVQQLExZT +eW1hbnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRlYyBDbGFzcyAz +IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzYwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC3DrL6TbyachX7d1vb/UMPywv3 +YC6zK34Mu1PyzE5l8xm7/zUd99Opu0Attd141Kb5N+qFBXttt+YTSwZ8+3ZjjyAd +LTgrBIXy6LDRX01KIclq2JTqHgJQpqqQB6BHIepm+QSg5oPwxPVeluInTWHDs8GM +IrZmoQDRVin77cF/JMo9+lqUsITDx7pDHP1kDvEo+0dZ8ibhMblE+avd+76+LDfj +rAsY0/wBovGkCjWCR0yrvYpe3xOF/CDMSFmvr0FvyyPNypOn3dVfyGQ7/wEDoApP +LW49hL6vyDKyUymQFfewBZoKPPa5BpDJpeFdoDuw/qi2v/WJKFckOiGGceTciotB +VeweMCRZ0cBZuHivqlp03iWAMJjtMERvIXAc2xJTDtamKGaTLB/MTzwbgcW59nhv +0DI6CHLbaw5GF4WU87zvvPekXo7p6bVk5bdLRRIsTDe3YEMKTXEGAJQmNXQfu3o5 +XE475rgD4seTi4QsJUlF3X8jlGAfy+nN9quX92Hn+39igcjcCjBcGHzmzu/Hbh6H +fLPpysh7avRo/IOlDFa0urKNSgrHl5fFiDAVPRAIVBVycmczM/R8t84AJ1NlziTx +WmTnNi/yLgLCl99y6AIeoPc9tftoYAP6M6nmEm0G4amoXU48/tnnAGWsthlNe4N/ +NEfq4RhtsYsceavnnQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUOXEIAD7eyIbnkP/k/SEPziQZFvYwDQYJKoZIhvcN +AQEMBQADggIBAFBriE1gSM5a4yLOZ3yEp80c/ekMA4w2rwqHDmquV64B0Da78v25 +c8FftaiuTKL6ScsHRhY2vePIVzh+OOS/JTNgxtw3nGO7XpgeGrKC8K6mdxGAREeh +KcXwszrOmPC47NMOgAZ3IzBM/3lkYyJbd5NDS3Wz2ztuO0rd8ciutTeKlYg6EGhw +OLlbcH7VQ8n8X0/l5ns27vAg7UdXEyYQXhQGDXt2B8LGLRb0rqdsD7yID08sAraj +1yLmmUc12I2lT4ESOhF9s8wLdfMecKMbA+r6mujmLjY5zJnOOj8Mt674Q5mwk25v +qtkPajGRu5zTtCj7g0x6c4JQZ9IOrO1gxbJdNZjPh34eWR0kvFa62qRa2MzmvB4Q +jxuMjvPB27e+1LBbZY8WaPNWxSoZFk0PuGWHbSSDuGLc4EdhGoh7zk5//dzGDVqa +pPO1TPbdMaboHREhMzAEYX0c4D5PjT+1ixIAWn2poQDUg+twuxj4pNIcgS23CBHI +Jnu21OUPA0Zy1CVAHr5JXW2T8VyyO3VUaTqg7kwiuqya4gitRWMFSlI1dsQ09V4H +Mq3cfCbRW4+t5OaqG3Wf61206MCpFXxOSgdy30bJ1JGSdVaw4e43NmUoxRXIK3bM +bW8Zg/T92hXiQeczeUaDV/nxpbZt07zXU+fucW14qZen7iCcGRVyFT0E +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDcTCCAlmgAwIBAgIVAOYJ/nrqAGiM4CS07SAbH+9StETRMA0GCSqGSIb3DQEB +BQUAMFAxCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9LcmFqb3dhIEl6YmEgUm96bGlj +emVuaW93YSBTLkEuMRcwFQYDVQQDDA5TWkFGSVIgUk9PVCBDQTAeFw0xMTEyMDYx +MTEwNTdaFw0zMTEyMDYxMTEwNTdaMFAxCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9L +cmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRcwFQYDVQQDDA5TWkFGSVIg +Uk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKxHL49ZMTml +6g3wpYwrvQKkvc0Kc6oJ5sxfgmp1qZfluwbv88BdocHSiXlY8NzrVYzuWBp7J/9K +ULMAoWoTIzOQ6C9TNm4YbA9A1jdX1wYNL5Akylf8W5L/I4BXhT9KnlI6x+a7BVAm +nr/Ttl+utT/Asms2fRfEsF2vZPMxH4UFqOAhFjxTkmJWf2Cu4nvRQJHcttB+cEAo +ag/hERt/+tzo4URz6x6r19toYmxx4FjjBkUhWQw1X21re//Hof2+0YgiwYT84zLb +eqDqCOMOXxvH480yGDkh/QoazWX3U75HQExT/iJlwnu7I1V6HXztKIwCBjsxffbH +3jOshCJtywcCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFFOSo33/gnbwM9TrkmdHYTMbaDsqMA0GCSqGSIb3DQEBBQUA +A4IBAQA5UFWd5EL/pBviIMm1zD2JLUCpp0mJG7JkwznIOzawhGmFFaxGoxAhQBEg +haP+E0KR66oAwVC6xe32QUVSHfWqWndzbODzLB8yj7WAR0cDM45ZngSBPBuFE3Wu +GLJX9g100ETfIX+4YBR/4NR/uvTnpnd9ete7Whl0ZfY94yuu4xQqB5QFv+P7IXXV +lTOjkjuGXEcyQAjQzbFaT9vIABSbeCXWBbjvOXukJy6WgAiclzGNSYprre8Ryydd +fmjW9HIGwsIO03EldivvqEYL1Hv1w/Pur+6FUEOaL68PEIUovfgwIB2BAw+vZDuw +cH0mX548PojGyg434cDjkSXa3mHF +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd +AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC +FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi +1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq +jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ +wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/ +WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy +NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC +uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw +IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6 +g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP +BSeOE6Fuwg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN +8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/ +RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4 +hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5 +ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM +EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1 +A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy +WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ +1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30 +6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT +91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p +TpPDpFQUWw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAw +NzEUMBIGA1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJv +b3QgQ0EgdjEwHhcNMDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYD +VQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwWVGVsaWFTb25lcmEgUm9vdCBDQSB2 +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+6yfwIaPzaSZVfp3F +VRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA3GV1 +7CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+X +Z75Ljo1kB1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+ +/jXh7VB7qTCNGdMJjmhnXb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs +81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxHoLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkm +dtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3F0fUTPHSiXk+TT2YqGHe +Oh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJoWjiUIMu +sDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4 +pgd7gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fs +slESl1MpWtTwEhDcTwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQ +arMCpgKIv7NHfirZ1fpoeDVNAgMBAAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYD +VR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qWDNXr+nuqF+gTEjANBgkqhkiG +9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNmzqjMDfz1mgbl +dxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx +0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1Tj +TQpgcmLNkQfWpb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBed +Y2gea+zDTYa4EzAvXUYNR0PVG6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7 +Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpcc41teyWRyu5FrgZLAMzTsVlQ2jqI +OylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOTJsjrDNYmiLbAJM+7 +vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2qReW +t88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcn +HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx +SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMp +IDIwMDcgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAi +BgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMjAeFw0wNzExMDUwMDAw +MDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh +d3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBGb3Ig +YXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9v +dCBDQSAtIEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/ +BebfowJPDQfGAFG6DAJSLSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6 +papu+7qzcMBniKI11KOasf2twu8x+qi58/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUmtgAMADna3+FGO6Lts6K +DPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUNG4k8VIZ3 +KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41ox +XZ3Krr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCB +rjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNV +BAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0wODA0MDIwMDAwMDBa +Fw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3Rl +LCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9u +MTgwNgYDVQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXpl +ZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEcz +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsr8nLPvb2FvdeHsbnndm +gcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2AtP0LMqmsywCPLLEHd5N/8 +YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC+BsUa0Lf +b1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS9 +9irY7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2S +zhkGcuYMXDhpxwTWvGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUk +OQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV +HQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJKoZIhvcNAQELBQADggEBABpA +2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweKA3rD6z8KLFIW +oCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu +t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7c +KUGRIjxpp7sC8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fM +m7v/OeZWYdMKp8RcTGB7BXcmer/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZu +MdRAGmI0Nj81Aa6sY6A= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB +qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV +BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw +NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j +LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG +A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs +W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta +3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk +6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6 +Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J +NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP +r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU +DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz +YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX +xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2 +/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/ +LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7 +jVaMaA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGHDCCBASgAwIBAgIES45gAzANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJE +SzESMBAGA1UEChMJVFJVU1QyNDA4MSIwIAYDVQQDExlUUlVTVDI0MDggT0NFUyBQ +cmltYXJ5IENBMB4XDTEwMDMwMzEyNDEzNFoXDTM3MTIwMzEzMTEzNFowRTELMAkG +A1UEBhMCREsxEjAQBgNVBAoTCVRSVVNUMjQwODEiMCAGA1UEAxMZVFJVU1QyNDA4 +IE9DRVMgUHJpbWFyeSBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +AJlJodr3U1Fa+v8HnyACHV81/wLevLS0KUk58VIABl6Wfs3LLNoj5soVAZv4LBi5 +gs7E8CZ9w0F2CopW8vzM8i5HLKE4eedPdnaFqHiBZ0q5aaaQArW+qKJx1rT/AaXt +alMB63/yvJcYlXS2lpexk5H/zDBUXeEQyvfmK+slAySWT6wKxIPDwVapauFY9QaG ++VBhCa5jBstWS7A5gQfEvYqn6csZ3jW472kW6OFNz6ftBcTwufomGJBMkonf4ZLr +6t0AdRi9jflBPz3MNNRGxyjIuAmFqGocYFA/OODBRjvSHB2DygqQ8k+9tlpvzMRr +kU7jq3RKL+83G1dJ3/LTjCLz4ryEMIC/OJ/gNZfE0qXddpPtzflIPtUFVffXdbFV +1t6XZFhJ+wBHQCpJobq/BjqLWUA86upsDbfwnePtmIPRCemeXkY0qabC+2Qmd2Fe +xyZphwTyMnbqy6FG1tB65dYf3mOqStmLa3RcHn9+2dwNfUkh0tjO2FXD7drWcU0O +I9DW8oAypiPhm/QCjMU6j6t+0pzqJ/S0tdAo+BeiXK5hwk6aR+sRb608QfBbRAs3 +U/q8jSPByenggac2BtTN6cl+AA1Mfcgl8iXWNFVGegzd/VS9vINClJCe3FNVoUnR +YCKkj+x0fqxvBLopOkJkmuZw/yhgMxljUi2qYYGn90OzAgMBAAGjggESMIIBDjAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjARBgNVHSAECjAIMAYGBFUd +IAAwgZcGA1UdHwSBjzCBjDAsoCqgKIYmaHR0cDovL2NybC5vY2VzLnRydXN0MjQw +OC5jb20vb2Nlcy5jcmwwXKBaoFikVjBUMQswCQYDVQQGEwJESzESMBAGA1UEChMJ +VFJVU1QyNDA4MSIwIAYDVQQDExlUUlVTVDI0MDggT0NFUyBQcmltYXJ5IENBMQ0w +CwYDVQQDEwRDUkwxMB8GA1UdIwQYMBaAFPZt+LFIs0FDAduGROUYBbdezAY3MB0G +A1UdDgQWBBT2bfixSLNBQwHbhkTlGAW3XswGNzANBgkqhkiG9w0BAQsFAAOCAgEA +VPAQGrT7dIjD3/sIbQW86f9CBPu0c7JKN6oUoRUtKqgJ2KCdcB5ANhCoyznHpu3m +/dUfVUI5hc31CaPgZyY37hch1q4/c9INcELGZVE/FWfehkH+acpdNr7j8UoRZlkN +15b/0UUBfGeiiJG/ugo4llfoPrp8bUmXEGggK3wyqIPcJatPtHwlb6ympfC2b/Ld +v/0IdIOzIOm+A89Q0utx+1cOBq72OHy8gpGb6MfncVFMoL2fjP652Ypgtr8qN9Ka +/XOazktiIf+2Pzp7hLi92hRc9QMYexrV/nnFSQoWdU8TqULFUoZ3zTEC3F/g2yj+ +FhbrgXHGo5/A4O74X+lpbY2XV47aSuw+DzcPt/EhMj2of7SA55WSgbjPMbmNX0rb +oenSIte2HRFW5Tr2W+qqkc/StixgkKdyzGLoFx/xeTWdJkZKwyjqge2wJqws2upY +EiThhC497+/mTiSuXd69eVUwKyqYp9SD2rTtNmF6TCghRM/dNsJOl+osxDVGcwvt +WIVFF/Onlu5fu1NHXdqNEfzldKDUvCfii3L2iATTZyHwU9CALE+2eIA+PIaLgnM1 +1oCfUnYBkQurTrihvzz9PryCVkLxiqRmBVvUz+D4N5G/wvvKDS6t6cPCS+hqM482 +cbBsn0R9fFLO4El62S9eH1tqOzO20OAOK65yJIsOpSE= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBF +MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQL +ExNUcnVzdGlzIEZQUyBSb290IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTEx +MzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1RydXN0aXMgTGltaXRlZDEc +MBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQRUN+ +AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihH +iTHcDnlkH5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjj +vSkCqPoc4Vu5g6hBSLwacY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA +0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zto3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlB +OrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEAAaNTMFEwDwYDVR0TAQH/ +BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAdBgNVHQ4E +FgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01 +GX2cGE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmW +zaD+vkAMXBJV+JOCyinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP4 +1BIy+Q7DsdwyhEQsb8tGD+pmQQ9P8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZE +f1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHVl/9D7S3B2l0pKoU/rGXuhg8F +jZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYliB6XzCGcKQEN +ZetX2fNXlrtIzYE= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx +EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT +VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5 +NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT +B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF +10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz +0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh +MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH +zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc +46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2 +yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi +laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP +oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA +BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE +qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm +4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL +1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF +H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo +RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+ +nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh +15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW +6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW +nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j +wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz +aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy +KwbQBM0= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES +MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU +V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz +WhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FO +LUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFE +AcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HH +K3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX +RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/z +rX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx +3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkq +hkiG9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeC +MErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdls +XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D +lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn +aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ +YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRS +MRgwFgYDVQQHDA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJp +bGltc2VsIHZlIFRla25vbG9qaWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSw +VEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ryb25payB2ZSBLcmlwdG9sb2ppIEFy +YcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNVBAsMGkthbXUgU2Vy +dGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUgS8O2 +ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAe +Fw0wNzA4MjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIx +GDAWBgNVBAcMD0dlYnplIC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmls +aW1zZWwgdmUgVGVrbm9sb2ppayBBcmHFn3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBU +QUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZlIEtyaXB0b2xvamkgQXJh +xZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2FtdSBTZXJ0 +aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7Zr +IFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4h +gb46ezzb8R1Sf1n68yJMlaCQvEhOEav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yK +O7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1xnnRFDDtG1hba+818qEhTsXO +fJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR6Oqeyjh1jmKw +lZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL +hmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQID +AQABo0IwQDAdBgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/ +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmP +NOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4N5EY3ATIZJkrGG2AA1nJrvhY0D7t +wyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLTy9LQQfMmNkqblWwM +7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYhLBOh +gLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5n +oN+J1q2MdqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUs +yZyQ2uypQjyttgI= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEPTCCAyWgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvzE/MD0GA1UEAww2VMOc +UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx +c8SxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMV4wXAYDVQQKDFVUw5xS +S1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kg +SGl6bWV0bGVyaSBBLsWeLiAoYykgQXJhbMSxayAyMDA3MB4XDTA3MTIyNTE4Mzcx +OVoXDTE3MTIyMjE4MzcxOVowgb8xPzA9BgNVBAMMNlTDnFJLVFJVU1QgRWxla3Ry +b25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTELMAkGA1UEBhMC +VFIxDzANBgNVBAcMBkFua2FyYTFeMFwGA1UECgxVVMOcUktUUlVTVCBCaWxnaSDE +sGxldGnFn2ltIHZlIEJpbGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7F +ni4gKGMpIEFyYWzEsWsgMjAwNzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAKu3PgqMyKVYFeaK7yc9SrToJdPNM8Ig3BnuiD9NYvDdE3ePYakqtdTyuTFY +KTsvP2qcb3N2Je40IIDu6rfwxArNK4aUyeNgsURSsloptJGXg9i3phQvKUmi8wUG ++7RP2qFsmmaf8EMJyupyj+sA1zU511YXRxcw9L6/P8JorzZAwan0qafoEGsIiveG +HtyaKhUG9qPw9ODHFNRRf8+0222vR5YXm3dx2KdxnSQM9pQ/hTEST7ruToK4uT6P +IzdezKKqdfcYbwnTrqdUKDT74eA7YH2gvnmJhsifLfkKS8RQouf9eRbHegsYz85M +733WB2+Y8a+xwXrXgTW4qhe04MsCAwEAAaNCMEAwHQYDVR0OBBYEFCnFkKslrxHk +Yb+j/4hhkeYO/pyBMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBBQUAA4IBAQAQDdr4Ouwo0RSVgrESLFF6QSU2TJ/sPx+EnWVUXKgW +AkD6bho3hO9ynYYKVZ1WKKxmLNA6VpM0ByWtCLCPyA8JWcqdmBzlVPi5RX9ql2+I +aE1KBiY3iAIOtsbWcpnOa3faYjGkVh+uX4132l32iPwa2Z61gfAyuOOI0JzzaqC5 +mxRZNTZPz/OOXl0XrRWV2N2y1RVuAE6zS89mlOTgzbUF2mNXi+WzqtvALhyQRNsa +XRik7r4EW5nVcV9VZWRi1aKbBFmGyGJ353yCRWo9F7/snXUMrqNvWtMvmDb08PUZ +qxFdyKbjKlhqQgnDvZImZjINXQhVdP+MmNAKpoRq0Tl9 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFkjCCA3qgAwIBAgIBCDANBgkqhkiG9w0BAQUFADA6MQswCQYDVQQGEwJDTjER +MA8GA1UEChMIVW5pVHJ1c3QxGDAWBgNVBAMTD1VDQSBHbG9iYWwgUm9vdDAeFw0w +ODAxMDEwMDAwMDBaFw0zNzEyMzEwMDAwMDBaMDoxCzAJBgNVBAYTAkNOMREwDwYD +VQQKEwhVbmlUcnVzdDEYMBYGA1UEAxMPVUNBIEdsb2JhbCBSb290MIICIjANBgkq +hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2rPlBlA/9nP3xDK/RqUlYjOHsGj+p9+I +A2N9Apb964fJ7uIIu527u+RBj8cwiQ9tJMAEbBSUgU2gDXRm8/CFr/hkGd656YGT +0CiFmUdCSiw8OCdKzP/5bBnXtfPvm65bNAbXj6ITBpyKhELVs6OQaG2BkO5NhOxM +cE4t3iQ5zhkAQ5N4+QiGHUPR9HK8BcBn+sBR0smFBySuOR56zUHSNqth6iur8CBV +mTxtLRwuLnWW2HKX4AzKaXPudSsVCeCObbvaE/9GqOgADKwHLx25urnRoPeZnnRc +GQVmMc8+KlL+b5/zub35wYH1N9ouTIElXfbZlJrTNYsgKDdfUet9Ysepk9H50DTL +qScmLCiQkjtVY7cXDlRzq6987DqrcDOsIfsiJrOGrCOp139tywgg8q9A9f9ER3Hd +J90TKKHqdjn5EKCgTUCkJ7JZFStsLSS3JGN490MYeg9NEePorIdCjedYcaSrbqLA +l3y74xNLytu7awj5abQEctXDRrl36v+6++nwOgw19o8PrgaEFt2UVdTvyie3AzzF +HCYq9TyopZWbhvGKiWf4xwxmse1Bv4KmAGg6IjTuHuvlb4l0T2qqaqhXZ1LUIGHB +zlPL/SR/XybfoQhplqCe/klD4tPq2sTxiDEhbhzhzfN1DiBEFsx9c3Q1RSw7gdQg +7LYJjD5IskkCAwEAAaOBojCBnzALBgNVHQ8EBAMCAQYwDAYDVR0TBAUwAwEB/zBj +BgNVHSUEXDBaBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcD +BAYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEFBQcDBwYIKwYBBQUHAwgGCCsGAQUF +BwMJMB0GA1UdDgQWBBTZw9P4gJJnzF3SOqLXcaK0xDiALTANBgkqhkiG9w0BAQUF +AAOCAgEA0Ih5ygiq9ws0oE4Jwul+NUiJcIQjL1HDKy9e21NrW3UIKlS6Mg7VxnGF +sZdJgPaE0PC6t3GUyHlrpsVE6EKirSUtVy/m1jEp+hmJVCl+t35HNmktbjK81HXa +QnO4TuWDQHOyXd/URHOmYgvbqm4FjMh/Rk85hZCdvBtUKayl1/7lWFZXbSyZoUkh +1WHGjGHhdSTBAd0tGzbDLxLMC9Z4i3WA6UG5iLHKPKkWxk4V43I29tSgQYWvimVw +TbVEEFDs7d9t5tnGwBLxSzovc+k8qe4bqi81pZufTcU0hF8mFGmzI7GJchT46U1R +IgP/SobEHOh7eQrbRyWBfvw0hKxZuFhD5D1DCVR0wtD92e9uWfdyYJl2b/Unp7uD +pEqB7CmB9HdL4UISVdSGKhK28FWbAS7d9qjjGcPORy/AeGEYWsdl/J1GW1fcfA67 +loMQfFUYCQSu0feLKj6g5lDWMDbX54s4U+xJRODPpN/xU3uLWrb2EZBL1nXz/gLz +Ka/wI3J9FO2pXd96gZ6bkiL8HvgBRUGXx2sBYb4zaPKgZYRmvOAqpGjTcezHCN6j +w8k2SjTxF+KAryAhk5Qe5hXTVGLxtTgv48y5ZwSpuuXu+RBuyy5+E6+SFP7zJ3N7 +OPxzbbm5iPZujAv1/P8JDrMtXnt145Ik4ubhWD5LKAN1axibRww= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDhDCCAmygAwIBAgIBCTANBgkqhkiG9w0BAQUFADAzMQswCQYDVQQGEwJDTjER +MA8GA1UEChMIVW5pVHJ1c3QxETAPBgNVBAMTCFVDQSBSb290MB4XDTA0MDEwMTAw +MDAwMFoXDTI5MTIzMTAwMDAwMFowMzELMAkGA1UEBhMCQ04xETAPBgNVBAoTCFVu +aVRydXN0MREwDwYDVQQDEwhVQ0EgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBALNdB8qGJn1r4vs4CQ7MgsJqGgCiFV/W6dQBt1YDAVmP9ThpJHbC +XivF9iu/r/tB/Q9a/KvXg3BNMJjRnrJ2u5LWu+kQKGkoNkTo8SzXWHwk1n8COvCB +a2FgP/Qz3m3l6ihST/ypHWN8C7rqrsRoRuTej8GnsrZYWm0dLNmMOreIy4XU9+gD +Xv2yTVDo1h//rgI/i0+WITyb1yXJHT/7mLFZ5PCpO6+zzYUs4mBGzG+OoOvwNMXx +QhhgrhLtRnUc5dipllq+3lrWeGeWW5N3UPJuG96WUUqm1ktDdSFmjXfsAoR2XEQQ +th1hbOSjIH23jboPkXXHjd+8AmCoKai9PUMCAwEAAaOBojCBnzALBgNVHQ8EBAMC +AQYwDAYDVR0TBAUwAwEB/zBjBgNVHSUEXDBaBggrBgEFBQcDAQYIKwYBBQUHAwIG +CCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEFBQcD +BwYIKwYBBQUHAwgGCCsGAQUFBwMJMB0GA1UdDgQWBBTbHzXza0z/QjFkm827Wh4d +SBC37jANBgkqhkiG9w0BAQUFAAOCAQEAOGy3iPGt+lg3dNHocN6cJ1nL5BXXoMNg +14iABMUwTD3UGusGXllH5rxmy+AI/Og17GJ9ysDawXiv5UZv+4mCI4/211NmVaDe +JRI7cTYWVRJ2+z34VFsxugAG+H1V5ad2g6pcSpemKijfvcZsCyOVjjN/Hl5AHxNU +LJzltQ7dFyiuawHTUin1Ih+QOfTcYmjwPIZH7LgFRbu3DJaUxmfLI3HQjnQi1kHr +A6i26r7EARK1s11AdgYg1GS4KUYGis4fk5oQ7vuqWrTcL9Ury/bXBYSYBZELhPc9 ++tb5evosFeo2gkO3t7jj83EB7UNDogVFwygFBzXjAaU4HoDU18PZ3g== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl +eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT +JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT +Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg +VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo +I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng +o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G +A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB +zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW +RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB +iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl +cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV +BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw +MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B +3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY +tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ +Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 +VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT +79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 +c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT +Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l +c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee +UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE +Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF +Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO +VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 +ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs +8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR +iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze +Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ +XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ +qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB +VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB +L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG +jjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCB +kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw +IFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBaMIGTMQswCQYDVQQG +EwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYD +VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cu +dXNlcnRydXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6 +E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZ +D0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK +4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykq +lXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulW +bfXv33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQAB +o4GrMIGoMAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRT +MtGzz3/64PGgXYVOktKeRR20TzA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3Js +LnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dDLmNybDAqBgNVHSUEIzAhBggr +BgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3DQEBBQUAA4IB +AQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft +Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyj +j98C5OBxOvG0I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVH +KWss5nbZqSl9Mt3JNjy9rjXxEZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv +2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwPDPafepE39peC4N1xaf92P2BNPM/3 +mfnGV/TJVTl4uix5yaaIK/QI +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEojCCA4qgAwIBAgIQRL4Mi1AAJLQR0zYlJWfJiTANBgkqhkiG9w0BAQUFADCB +rjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xNjA0BgNVBAMTLVVUTi1VU0VSRmlyc3Qt +Q2xpZW50IEF1dGhlbnRpY2F0aW9uIGFuZCBFbWFpbDAeFw05OTA3MDkxNzI4NTBa +Fw0xOTA3MDkxNzM2NThaMIGuMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAV +BgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5l +dHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTE2MDQGA1UE +AxMtVVROLVVTRVJGaXJzdC1DbGllbnQgQXV0aGVudGljYXRpb24gYW5kIEVtYWls +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsjmFpPJ9q0E7YkY3rs3B +YHW8OWX5ShpHornMSMxqmNVNNRm5pELlzkniii8efNIxB8dOtINknS4p1aJkxIW9 +hVE1eaROaJB7HHqkkqgX8pgV8pPMyaQylbsMTzC9mKALi+VuG6JG+ni8om+rWV6l +L8/K2m2qL+usobNqqrcuZzWLeeEeaYji5kbNoKXqvgvOdjp6Dpvq/NonWz1zHyLm +SGHGTPNpsaguG7bUMSAsvIKKjqQOpdeJQ/wWWq8dcdcRWdq6hw2v+vPhwvCkxWeM +1tZUOt4KpLoDd7NlyP0e03RiqhjKaJMeoYV+9Udly/hNVyh00jT/MLbu9mIwFIws +6wIDAQABo4G5MIG2MAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBSJgmd9xJ0mcABLtFBIfN49rgRufTBYBgNVHR8EUTBPME2gS6BJhkdodHRw +Oi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLVVTRVJGaXJzdC1DbGllbnRBdXRoZW50 +aWNhdGlvbmFuZEVtYWlsLmNybDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH +AwQwDQYJKoZIhvcNAQEFBQADggEBALFtYV2mGn98q0rkMPxTbyUkxsrt4jFcKw7u +7mFVbwQ+zznexRtJlOTrIEy05p5QLnLZjfWqo7NK2lYcYJeA3IKirUq9iiv/Cwm0 +xtcgBEXkzYABurorbs6q15L+5K/r9CYdFip/bDCVNy8zEqx/3cfREYxRmLLQo5HQ +rfafnoOTHh1CuEava2bwm3/q4wMC5QJRwarVNZ1yQAOJujEdxRBoUp7fooXFXAim +eOZTT7Hot9MUnpOmw2TjrH5xzbyf6QMbzPvprDHBr3wVdAKZw7JHpsIyYdfHb0gk +USeh1YdV8nuPmD0Wnu51tvjQjvLzxq4oW6fw8zYX/MMF08oDSlQ= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCB +lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt +SGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgxOTIyWjCBlzELMAkG +A1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEe +MBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8v +d3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdh +cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn +0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJ +M6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4a +MXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNd +oI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqI +DsjfPe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9Ksy +oUhbAgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0 +dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3YXJlLmNy +bDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEF +BQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM +//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28Gpgoiskli +CE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gE +CJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t +3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kniCrVWFCVH/A7HFe7fRQ5YiuayZSS +KqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67nfhmqA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEZjCCA06gAwIBAgIQRL4Mi1AAJLQR0zYt4LNfGzANBgkqhkiG9w0BAQUFADCB +lTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHTAbBgNVBAMTFFVUTi1VU0VSRmlyc3Qt +T2JqZWN0MB4XDTk5MDcwOTE4MzEyMFoXDTE5MDcwOTE4NDAzNlowgZUxCzAJBgNV +BAYTAlVTMQswCQYDVQQIEwJVVDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxHjAc +BgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEhMB8GA1UECxMYaHR0cDovL3d3 +dy51c2VydHJ1c3QuY29tMR0wGwYDVQQDExRVVE4tVVNFUkZpcnN0LU9iamVjdDCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6qgT+jo2F4qjEAVZURnicP +HxzfOpuCaDDASmEd8S8O+r5596Uj71VRloTN2+O5bj4x2AogZ8f02b+U60cEPgLO +KqJdhwQJ9jCdGIqXsqoc/EHSoTbL+z2RuufZcDX65OeQw5ujm9M89RKZd7G3CeBo +5hy485RjiGpq/gt2yb70IuRnuasaXnfBhQfdDWy/7gbHd2pBnqcP1/vulBe3/IW+ +pKvEHDHd17bR5PDv3xaPslKT16HUiaEHLr/hARJCHhrh2JU022R5KP+6LhHC5ehb +kkj7RwvCbNqtMoNB86XlQXD9ZZBt+vpRxPm9lisZBCzTbafc8H9vg2XiaquHhnUC +AwEAAaOBrzCBrDALBgNVHQ8EBAMCAcYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQU2u1kdBScFDyr3ZmpvVsoTYs8ydgwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDov +L2NybC51c2VydHJ1c3QuY29tL1VUTi1VU0VSRmlyc3QtT2JqZWN0LmNybDApBgNV +HSUEIjAgBggrBgEFBQcDAwYIKwYBBQUHAwgGCisGAQQBgjcKAwQwDQYJKoZIhvcN +AQEFBQADggEBAAgfUrE3RHjb/c652pWWmKpVZIC1WkDdIaXFwfNfLEzIR1pp6ujw +NTX00CXzyKakh0q9G7FzCL3Uw8q2NbtZhncxzaeAFK4T7/yxSPlrJSUtUbYsbUXB +mMiKVl0+7kNOPmsnjtA6S4ULX9Ptaqd1y9Fahy85dRNacrACgZ++8A+EVCBibGnU +4U3GDZlDAQ0Slox4nb9QorFEqmrPF3rPbw/U+CRVX/A0FklmPlBGyWNxODFiuGK5 +81OtbLUrohKqGU8J2l7nk8aOFAj+8DCAGKCGhU3IfdeLA/5u1fedFqySLKAj5ZyR +Uh+U3xeUc8OzwcFxBSAAeL0TUh2oPs0AH8g= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCLW3VWhFSFCwDPrzhIzrGkMA0GCSqGSIb3DQEBBQUAMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl +cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu +LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT +aWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD +VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT +aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ +bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu +IENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN2E1Lm0+afY8wR4 +nN493GwTFtl63SRRZsDHJlkNrAYIwpTRMx/wgzUfbhvI3qpuFU5UJ+/EbRrsC+MO +8ESlV8dAWB6jRx9x7GD2bZTIGDnt/kIYVt/kTEkQeE4BdjVjEjbdZrwBBDajVWjV +ojYJrKshJlQGrT/KFOCsyq0GHZXi+J3x4GD/wn91K0zM2v6HmSHquv4+VNfSWXjb +PG7PoBMAGrgnoeS+Z5bKoMWznN3JdZ7rMJpfo83ZrngZPyPpXNspva1VyBtUjGP2 +6KbqxzcSXKMpHgLZ2x87tNcPVkeBFQRKr4Mn0cVYiMHd9qqnoxjaaKptEVHhv2Vr +n5Z20T0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAq2aN17O6x5q25lXQBfGfMY1a +qtmqRiYPce2lrVNWYgFHKkTp/j90CxObufRNG7LRX7K20ohcs5/Ny9Sn2WCVhDr4 +wTcdYcrnsMXlkdpUpqwxga6X3s0IrLjAl4B/bnKk52kTlWUfxJM8/XmPBNQ+T+r3 +ns7NZ3xPZQL/kYVUc8f/NveGLezQXk//EZ9yBta4GvFMDSZl4kSAHsef493oCtrs +pSCAaWihT37ha88HQfqDjrw43bAuEbFrskLMmrz5SCJ5ShkPshw+IHTZasO+8ih4 +E1Z5T21Q6huwtVexN2ZYI/PcD98Kh8TvhgXVOBRgmaNL3gaWcSzy27YfpO8/7g== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEGTCCAwECEGFwy0mMX5hFKeewptlQW3owDQYJKoZIhvcNAQEFBQAwgcoxCzAJ +BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVy +aVNpZ24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24s +IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNp +Z24gQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eSAtIEczMB4XDTk5MTAwMTAwMDAwMFoXDTM2MDcxNjIzNTk1OVowgcoxCzAJBgNV +BAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNp +Z24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24sIElu +Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNpZ24g +Q2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt +IEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArwoNwtUs22e5LeWU +J92lvuCwTY+zYVY81nzD9M0+hsuiiOLh2KRpxbXiv8GmR1BeRjmL1Za6tW8UvxDO +JxOeBUebMXoT2B/Z0wI3i60sR/COgQanDTAM6/c8DyAd3HJG7qUCyFvDyVZpTMUY +wZF7C9UTAJu878NIPkZgIIUq1ZC2zYugzDLdt/1AVbJQHFauzI13TccgTacxdu9o +koqQHgiBVrKtaaNS0MscxCM9H5n+TOgWY47GCI72MfbS+uV23bUckqNJzc0BzWjN +qWm6o+sdDZykIKbBoMXRRkwXbdKsZj+WjOCE1Db/IlnF+RFgqF8EffIa9iVCYQ/E +Srg+iQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQA0JhU8wI1NQ0kdvekhktdmnLfe +xbjQ5F1fdiLAJvmEOjr5jLX77GDx6M4EsMjdpwOPMPOY36TmpDHf0xwLRtxyID+u +7gU8pDM/CzmscHhzS5kr3zDCVLCoO1Wh/hYozUK9dG6A2ydEp85EXdQbkJgNHkKU +sQAsBNB0owIFImNjzYO1+8FtYmtpdf1dcEG59b98377BMnMiIYtYgXsVkXq642RI +sH/7NiXaldDxJBQX3RiAa0YjOVT1jmIJBB2UkKab5iXiQkWquJCtvgiPqQtCGJTP +cjnhsUPgKM+351psE2tJs//jGHyJizNdrDPXp/naOlXJWBD5qu9ats9LS98q +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl +cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu +LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT +aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD +VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT +aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ +bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu +IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b +N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t +KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu +kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm +CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ +Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu +imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te +2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe +DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC +/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p +F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt +TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp +U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y +aXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjELMAkG +A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJp +U2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwg +SW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2ln +biBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8Utpkmw4tXNherJI9/gHm +GUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGzrl0Bp3ve +fLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJ +aW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYj +aHR0cDovL2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMW +kf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMDA2gAMGUCMGYhDBgmYFo4e1ZC +4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIxAJw9SDkjOVga +FRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW +ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp +U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y +aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1 +nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex +t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz +SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG +BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+ +rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/ +NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E +BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH +BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy +aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv +MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE +p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y +5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK +WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ +4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N +hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCB +vTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9W +ZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJVUzEX +MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0 +IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9y +IGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNh +bCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj1mCOkdeQmIN65lgZOIzF +9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGPMiJhgsWH +H26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+H +LL729fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN +/BMReYTtXlT2NJ8IAfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPT +rJ9VAMf2CGqUuV/c4DPxhGD5WycRtPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1Ud +EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0GCCsGAQUFBwEMBGEwX6FdoFsw +WTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2Oa8PPgGrUSBgs +exkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud +DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4 +sAPmLGd75JR3Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+ +seQxIcaBlVZaDrHC1LGmWazxY8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz +4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTxP/jgdFcrGJ2BtMQo2pSXpXDrrB2+ +BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+PwGZsY6rp2aQW9IHR +lRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4mJO3 +7M2CYfE45k+XmCpajQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBr +MQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRl +cm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv +bW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2WhcNMjIwNjI0MDAxNjEyWjBrMQsw +CQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5h +dGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1l +cmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h +2mCxlCfLF9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4E +lpF7sDPwsRROEW+1QK8bRaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdV +ZqW1LS7YgFmypw23RuwhY/81q6UCzyr0TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq +299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI/k4+oKsGGelT84ATB+0t +vz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzsGHxBvfaL +dXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUF +AAOCAQEAX/FBfXxcCLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcR +zCSs00Rsca4BIGsDoo8Ytyk6feUWYFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3 +LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pzzkWKsKZJ/0x9nXGIxHYdkFsd +7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBuYQa7FkKMcPcw +++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt +398znM/jra6O1I7mT1GvFpLgXPYHDw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIID+TCCAuGgAwIBAgIQW1fXqEywr9nTb0ugMbTW4jANBgkqhkiG9w0BAQUFADB5 +MQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRl +cm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xKjAoBgNVBAMTIVZpc2EgSW5m +b3JtYXRpb24gRGVsaXZlcnkgUm9vdCBDQTAeFw0wNTA2MjcxNzQyNDJaFw0yNTA2 +MjkxNzQyNDJaMHkxCzAJBgNVBAYTAlVTMQ0wCwYDVQQKEwRWSVNBMS8wLQYDVQQL +EyZWaXNhIEludGVybmF0aW9uYWwgU2VydmljZSBBc3NvY2lhdGlvbjEqMCgGA1UE +AxMhVmlzYSBJbmZvcm1hdGlvbiBEZWxpdmVyeSBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyREA4R/QkkfpLx0cYjga/EhIPZpchH0MZsRZ +FfP6C2ITtf/Wc+MtgD4yTK0yoiXvni3d+aCtEgK3GDvkdgYrgF76ROJFZwUQjQ9l +x42gRT05DbXvWFoy7dTglCZ9z/Tt2Cnktv9oxKgmkeHY/CyfpCBg1S8xth2JlGMR +0ug/GMO5zANuegZOv438p5Lt5So+du2Gl+RMFQqEPwqN5uJSqAe0VtmB4gWdQ8on +Bj2ZAM2R73QW7UW0Igt2vA4JaSiNtaAG/Y/58VXWHGgbq7rDtNK1R30X0kJV0rGA +ib3RSwB3LpG7bOjbIucV5mQgJoVjoA1e05w6g1x/KmNTmOGRVwIDAQABo30wezAP +BgNVHRMBAf8EBTADAQH/MDkGA1UdIAQyMDAwLgYFZ4EDAgEwJTAVBggrBgEFBQcC +ARYJMS4yLjMuNC41MAwGCCsGAQUFBwICMAAwDgYDVR0PAQH/BAQDAgEGMB0GA1Ud +DgQWBBRPitp2/2d3I5qmgH1924h1hfeBejANBgkqhkiG9w0BAQUFAAOCAQEACUW1 +QdUHdDJydgDPmYt+telnG/Su+DPaf1cregzlN43bJaJosMP7NwjoJY/H2He4XLWb +5rXEkl+xH1UyUwF7mtaUoxbGxEvt8hPZSTB4da2mzXgwKvXuHyzF5Qjy1hOB0/pS +WaF9ARpVKJJ7TOJQdGKBsF2Ty4fSCLqZLgfxbqwMsd9sysXI3rDXjIhekqvbgeLz +PqZr+pfgFhwCCLSMQWl5Ll3u7Qk9wR094DZ6jj6+JCVCRUS3HyabH4OlM0Vc2K+j +INsF/64Or7GNtRf9HYEJvrPxHINxl3JVwhYj4ASeaO4KwhVbwtw94Tc/XrGcexDo +c5lC3rAi4/UZqweYCw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEGjCCAwKgAwIBAgIDAYagMA0GCSqGSIb3DQEBBQUAMIGjMQswCQYDVQQGEwJG +STEQMA4GA1UECBMHRmlubGFuZDEhMB8GA1UEChMYVmFlc3RvcmVraXN0ZXJpa2Vz +a3VzIENBMSkwJwYDVQQLEyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBTZXJ2aWNl +czEZMBcGA1UECxMQVmFybWVubmVwYWx2ZWx1dDEZMBcGA1UEAxMQVlJLIEdvdi4g +Um9vdCBDQTAeFw0wMjEyMTgxMzUzMDBaFw0yMzEyMTgxMzUxMDhaMIGjMQswCQYD +VQQGEwJGSTEQMA4GA1UECBMHRmlubGFuZDEhMB8GA1UEChMYVmFlc3RvcmVraXN0 +ZXJpa2Vza3VzIENBMSkwJwYDVQQLEyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBT +ZXJ2aWNlczEZMBcGA1UECxMQVmFybWVubmVwYWx2ZWx1dDEZMBcGA1UEAxMQVlJL +IEdvdi4gUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALCF +FdrIAzfQo0Y3bBseljDCWoUSZyPyu5/nioFgJ/gTqTy894aqqvTzJSm0/nWuHoGG +igWyHWWyOOi0zCia+xc28ZPVec7Bg4shT8MNrUHfeJ1I4x9CRPw8bSEga60ihCRC +jxdNwlAfZM0tOSJWiP2yY51U2kJpwMhP1xjiPshphJQ9LIDGfM6911Mf64i5psu7 +hVfvV3ZdDIvTXhJBnyHAOfQmbQj6OLOhd7HuFtjQaNq0mKWgZUZKa41+qk1guPjI +DfxxPu45h4G02fhukO4/DmHXHSto5i7hQkQmeCxY8n0Wf2HASSQqiYe2XS8pGfim +545SnkFLWg6quMJmQlMCAwEAAaNVMFMwDwYDVR0TAQH/BAUwAwEB/zARBglghkgB +hvhCAQEEBAMCAAcwDgYDVR0PAQH/BAQDAgHGMB0GA1UdDgQWBBTb6eGb0tEkC/yr +46Bn6q6cS3f0sDANBgkqhkiG9w0BAQUFAAOCAQEArX1ID1QRnljurw2bEi8hpM2b +uoRH5sklVSPj3xhYKizbXvfNVPVRJHtiZ+GxH0mvNNDrsczZog1Sf0JLiGCXzyVy +t08pLWKfT6HAVVdWDsRol5EfnGTCKTIB6dTI2riBmCguGMcs/OubUpbf9MiQGS0j +8/G7cdqehSO9Gu8u5Hp5t8OdhkktY7ktdM9lDzJmid87Ie4pbzlj2RXBbvbfgD5Q +eBmK3QOjFKU3p7UsfLYRh+cF8ry23tT/l4EohP7+bEaFEEGfTXWMB9SZZ291im/k +UJL2mdUQuMSpe/cXjUu/15WfCdxEDx4yw8DP03kN5Mc7h/CQNIghYkmSBAQfvA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB +gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk +MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY +UmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcx +NDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3 +dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2Vy +dmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS6 +38eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP +KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7Q +DxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4 +qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRa +JSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNVi +PvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0P +BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASs +jVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0 +eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcNAQEFBQAD +ggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR +vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa +IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy +i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ +O+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- +` diff --git a/vendor/github.com/google/certificate-transparency-go/x509/root_linux.go b/vendor/github.com/google/certificate-transparency-go/x509/root_linux.go new file mode 100644 index 0000000000000..aa1785e4c6375 --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/x509/root_linux.go @@ -0,0 +1,14 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package x509 + +// Possible certificate files; stop after finding one. +var certFiles = []string{ + "/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc. + "/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL 6 + "/etc/ssl/ca-bundle.pem", // OpenSUSE + "/etc/pki/tls/cacert.pem", // OpenELEC + "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", // CentOS/RHEL 7 +} diff --git a/vendor/github.com/google/certificate-transparency-go/x509/root_nacl.go b/vendor/github.com/google/certificate-transparency-go/x509/root_nacl.go new file mode 100644 index 0000000000000..4413f64738a7f --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/x509/root_nacl.go @@ -0,0 +1,8 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package x509 + +// Possible certificate files; stop after finding one. +var certFiles = []string{} diff --git a/vendor/github.com/google/certificate-transparency-go/x509/root_nocgo_darwin.go b/vendor/github.com/google/certificate-transparency-go/x509/root_nocgo_darwin.go new file mode 100644 index 0000000000000..2ac4666aff65b --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/x509/root_nocgo_darwin.go @@ -0,0 +1,11 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !cgo + +package x509 + +func loadSystemRoots() (*CertPool, error) { + return execSecurityRoots() +} diff --git a/vendor/github.com/google/certificate-transparency/go/x509/root_plan9.go b/vendor/github.com/google/certificate-transparency-go/x509/root_plan9.go old mode 100755 new mode 100644 similarity index 71% rename from vendor/github.com/google/certificate-transparency/go/x509/root_plan9.go rename to vendor/github.com/google/certificate-transparency-go/x509/root_plan9.go index 9965caadee3a8..ebeb7dfccd86c --- a/vendor/github.com/google/certificate-transparency/go/x509/root_plan9.go +++ b/vendor/github.com/google/certificate-transparency-go/x509/root_plan9.go @@ -6,7 +6,10 @@ package x509 -import "io/ioutil" +import ( + "io/ioutil" + "os" +) // Possible certificate files; stop after finding one. var certFiles = []string{ @@ -17,17 +20,18 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate return nil, nil } -func initSystemRoots() { +func loadSystemRoots() (*CertPool, error) { roots := NewCertPool() + var bestErr error for _, file := range certFiles { data, err := ioutil.ReadFile(file) if err == nil { roots.AppendCertsFromPEM(data) - systemRoots = roots - return + return roots, nil + } + if bestErr == nil || (os.IsNotExist(bestErr) && !os.IsNotExist(err)) { + bestErr = err } } - - // All of the files failed to load. systemRoots will be nil which will - // trigger a specific error at verification time. + return nil, bestErr } diff --git a/vendor/github.com/google/certificate-transparency-go/x509/root_solaris.go b/vendor/github.com/google/certificate-transparency-go/x509/root_solaris.go new file mode 100644 index 0000000000000..e6d4e61399475 --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/x509/root_solaris.go @@ -0,0 +1,12 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package x509 + +// Possible certificate files; stop after finding one. +var certFiles = []string{ + "/etc/certs/ca-certificates.crt", // Solaris 11.2+ + "/etc/ssl/certs/ca-certificates.crt", // Joyent SmartOS + "/etc/ssl/cacert.pem", // OmniOS +} diff --git a/vendor/github.com/google/certificate-transparency-go/x509/root_unix.go b/vendor/github.com/google/certificate-transparency-go/x509/root_unix.go new file mode 100644 index 0000000000000..65b5a5fdbccb3 --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/x509/root_unix.go @@ -0,0 +1,88 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build dragonfly freebsd linux nacl netbsd openbsd solaris + +package x509 + +import ( + "io/ioutil" + "os" +) + +// Possible directories with certificate files; stop after successfully +// reading at least one file from a directory. +var certDirectories = []string{ + "/etc/ssl/certs", // SLES10/SLES11, https://golang.org/issue/12139 + "/system/etc/security/cacerts", // Android + "/usr/local/share/certs", // FreeBSD + "/etc/pki/tls/certs", // Fedora/RHEL + "/etc/openssl/certs", // NetBSD +} + +const ( + // certFileEnv is the environment variable which identifies where to locate + // the SSL certificate file. If set this overrides the system default. + certFileEnv = "SSL_CERT_FILE" + + // certDirEnv is the environment variable which identifies which directory + // to check for SSL certificate files. If set this overrides the system default. + certDirEnv = "SSL_CERT_DIR" +) + +func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { + return nil, nil +} + +func loadSystemRoots() (*CertPool, error) { + roots := NewCertPool() + + files := certFiles + if f := os.Getenv(certFileEnv); f != "" { + files = []string{f} + } + + var firstErr error + for _, file := range files { + data, err := ioutil.ReadFile(file) + if err == nil { + roots.AppendCertsFromPEM(data) + break + } + if firstErr == nil && !os.IsNotExist(err) { + firstErr = err + } + } + + dirs := certDirectories + if d := os.Getenv(certDirEnv); d != "" { + dirs = []string{d} + } + + for _, directory := range dirs { + fis, err := ioutil.ReadDir(directory) + if err != nil { + if firstErr == nil && !os.IsNotExist(err) { + firstErr = err + } + continue + } + rootsAdded := false + for _, fi := range fis { + data, err := ioutil.ReadFile(directory + "/" + fi.Name()) + if err == nil && roots.AppendCertsFromPEM(data) { + rootsAdded = true + } + } + if rootsAdded { + return roots, nil + } + } + + if len(roots.certs) > 0 { + return roots, nil + } + + return nil, firstErr +} diff --git a/vendor/github.com/google/certificate-transparency/go/x509/root_windows.go b/vendor/github.com/google/certificate-transparency-go/x509/root_windows.go old mode 100755 new mode 100644 similarity index 85% rename from vendor/github.com/google/certificate-transparency/go/x509/root_windows.go rename to vendor/github.com/google/certificate-transparency-go/x509/root_windows.go index 587a782c1bc69..304ad3a679f02 --- a/vendor/github.com/google/certificate-transparency/go/x509/root_windows.go +++ b/vendor/github.com/google/certificate-transparency-go/x509/root_windows.go @@ -87,7 +87,7 @@ func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) e status := chainCtx.TrustStatus.ErrorStatus switch status { case syscall.CERT_TRUST_IS_NOT_TIME_VALID: - return CertificateInvalidError{c, Expired} + return CertificateInvalidError{c, Expired, ""} default: return UnknownAuthorityError{c, nil, nil} } @@ -109,7 +109,7 @@ func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContex sslPara.Size = uint32(unsafe.Sizeof(*sslPara)) para := &syscall.CertChainPolicyPara{ - ExtraPolicyPara: syscall.Pointer(unsafe.Pointer(sslPara)), + ExtraPolicyPara: convertToPolicyParaType(unsafe.Pointer(sslPara)), } para.Size = uint32(unsafe.Sizeof(*para)) @@ -125,7 +125,7 @@ func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContex if status.Error != 0 { switch status.Error { case syscall.CERT_E_EXPIRED: - return CertificateInvalidError{c, Expired} + return CertificateInvalidError{c, Expired, ""} case syscall.CERT_E_CN_NO_MATCH: return HostnameError{c, opts.DNSName} case syscall.CERT_E_UNTRUSTEDROOT: @@ -179,7 +179,7 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate } // CertGetCertificateChain will traverse Windows's root stores - // in an attempt to build a verified certificate chain. Once + // in an attempt to build a verified certificate chain. Once // it has found a verified chain, it stops. MSDN docs on // CERT_CHAIN_CONTEXT: // @@ -225,5 +225,42 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate return chains, nil } -func initSystemRoots() { +func loadSystemRoots() (*CertPool, error) { + // TODO: restore this functionality on Windows. We tried to do + // it in Go 1.8 but had to revert it. See Issue 18609. + // Returning (nil, nil) was the old behavior, prior to CL 30578. + return nil, nil + + const CRYPT_E_NOT_FOUND = 0x80092004 + + store, err := syscall.CertOpenSystemStore(0, syscall.StringToUTF16Ptr("ROOT")) + if err != nil { + return nil, err + } + defer syscall.CertCloseStore(store, 0) + + roots := NewCertPool() + var cert *syscall.CertContext + for { + cert, err = syscall.CertEnumCertificatesInStore(store, cert) + if err != nil { + if errno, ok := err.(syscall.Errno); ok { + if errno == CRYPT_E_NOT_FOUND { + break + } + } + return nil, err + } + if cert == nil { + break + } + // Copy the buf, since ParseCertificate does not create its own copy. + buf := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:] + buf2 := make([]byte, cert.Length) + copy(buf2, buf) + if c, err := ParseCertificate(buf2); err == nil { + roots.AddCert(c) + } + } + return roots, nil } diff --git a/vendor/github.com/google/certificate-transparency/go/x509/sec1.go b/vendor/github.com/google/certificate-transparency-go/x509/sec1.go old mode 100755 new mode 100644 similarity index 59% rename from vendor/github.com/google/certificate-transparency/go/x509/sec1.go rename to vendor/github.com/google/certificate-transparency-go/x509/sec1.go index dbc255f0caa97..ae4f81e5609d0 --- a/vendor/github.com/google/certificate-transparency/go/x509/sec1.go +++ b/vendor/github.com/google/certificate-transparency-go/x509/sec1.go @@ -7,21 +7,20 @@ package x509 import ( "crypto/ecdsa" "crypto/elliptic" - // START CT CHANGES - "github.com/google/certificate-transparency/go/asn1" - // START CT CHANGES "errors" "fmt" "math/big" + + "github.com/google/certificate-transparency-go/asn1" ) const ecPrivKeyVersion = 1 // ecPrivateKey reflects an ASN.1 Elliptic Curve Private Key Structure. // References: -// RFC5915 -// SEC1 - http://www.secg.org/download/aid-780/sec1-v2.pdf -// Per RFC5915 the NamedCurveOID is marked as ASN.1 OPTIONAL, however in +// RFC 5915 +// SEC1 - http://www.secg.org/sec1-v2.pdf +// Per RFC 5915 the NamedCurveOID is marked as ASN.1 OPTIONAL, however in // most cases it is not. type ecPrivateKey struct { Version int @@ -31,19 +30,30 @@ type ecPrivateKey struct { } // ParseECPrivateKey parses an ASN.1 Elliptic Curve Private Key Structure. -func ParseECPrivateKey(der []byte) (key *ecdsa.PrivateKey, err error) { +func ParseECPrivateKey(der []byte) (*ecdsa.PrivateKey, error) { return parseECPrivateKey(nil, der) } // MarshalECPrivateKey marshals an EC private key into ASN.1, DER format. func MarshalECPrivateKey(key *ecdsa.PrivateKey) ([]byte, error) { - oid, ok := oidFromNamedCurve(key.Curve) + oid, ok := OIDFromNamedCurve(key.Curve) if !ok { return nil, errors.New("x509: unknown elliptic curve") } + + return marshalECPrivateKeyWithOID(key, oid) +} + +// marshalECPrivateKey marshals an EC private key into ASN.1, DER format and +// sets the curve ID to the given OID, or omits it if OID is nil. +func marshalECPrivateKeyWithOID(key *ecdsa.PrivateKey, oid asn1.ObjectIdentifier) ([]byte, error) { + privateKeyBytes := key.D.Bytes() + paddedPrivateKey := make([]byte, (key.Curve.Params().N.BitLen()+7)/8) + copy(paddedPrivateKey[len(paddedPrivateKey)-len(privateKeyBytes):], privateKeyBytes) + return asn1.Marshal(ecPrivateKey{ Version: 1, - PrivateKey: key.D.Bytes(), + PrivateKey: paddedPrivateKey, NamedCurveOID: oid, PublicKey: asn1.BitString{Bytes: elliptic.Marshal(key.Curve, key.X, key.Y)}, }) @@ -73,13 +83,30 @@ func parseECPrivateKey(namedCurveOID *asn1.ObjectIdentifier, der []byte) (key *e } k := new(big.Int).SetBytes(privKey.PrivateKey) - if k.Cmp(curve.Params().N) >= 0 { + curveOrder := curve.Params().N + if k.Cmp(curveOrder) >= 0 { return nil, errors.New("x509: invalid elliptic curve private key value") } priv := new(ecdsa.PrivateKey) priv.Curve = curve priv.D = k - priv.X, priv.Y = curve.ScalarBaseMult(privKey.PrivateKey) + + privateKey := make([]byte, (curveOrder.BitLen()+7)/8) + + // Some private keys have leading zero padding. This is invalid + // according to [SEC1], but this code will ignore it. + for len(privKey.PrivateKey) > len(privateKey) { + if privKey.PrivateKey[0] != 0 { + return nil, errors.New("x509: invalid private key length") + } + privKey.PrivateKey = privKey.PrivateKey[1:] + } + + // Some private keys remove all leading zeros, this is also invalid + // according to [SEC1] but since OpenSSL used to do this, we ignore + // this too. + copy(privateKey[len(privateKey)-len(privKey.PrivateKey):], privKey.PrivateKey) + priv.X, priv.Y = curve.ScalarBaseMult(privateKey) return priv, nil } diff --git a/vendor/github.com/google/certificate-transparency-go/x509/verify.go b/vendor/github.com/google/certificate-transparency-go/x509/verify.go new file mode 100644 index 0000000000000..beafc3b00085b --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/x509/verify.go @@ -0,0 +1,1090 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package x509 + +import ( + "bytes" + "errors" + "fmt" + "net" + "net/url" + "reflect" + "runtime" + "strconv" + "strings" + "time" + "unicode/utf8" + + "github.com/google/certificate-transparency-go/asn1" +) + +type InvalidReason int + +const ( + // NotAuthorizedToSign results when a certificate is signed by another + // which isn't marked as a CA certificate. + NotAuthorizedToSign InvalidReason = iota + // Expired results when a certificate has expired, based on the time + // given in the VerifyOptions. + Expired + // CANotAuthorizedForThisName results when an intermediate or root + // certificate has a name constraint which doesn't permit a DNS or + // other name (including IP address) in the leaf certificate. + CANotAuthorizedForThisName + // TooManyIntermediates results when a path length constraint is + // violated. + TooManyIntermediates + // IncompatibleUsage results when the certificate's key usage indicates + // that it may only be used for a different purpose. + IncompatibleUsage + // NameMismatch results when the subject name of a parent certificate + // does not match the issuer name in the child. + NameMismatch + // NameConstraintsWithoutSANs results when a leaf certificate doesn't + // contain a Subject Alternative Name extension, but a CA certificate + // contains name constraints. + NameConstraintsWithoutSANs + // UnconstrainedName results when a CA certificate contains permitted + // name constraints, but leaf certificate contains a name of an + // unsupported or unconstrained type. + UnconstrainedName + // TooManyConstraints results when the number of comparision operations + // needed to check a certificate exceeds the limit set by + // VerifyOptions.MaxConstraintComparisions. This limit exists to + // prevent pathological certificates can consuming excessive amounts of + // CPU time to verify. + TooManyConstraints + // CANotAuthorizedForExtKeyUsage results when an intermediate or root + // certificate does not permit an extended key usage that is claimed by + // the leaf certificate. + CANotAuthorizedForExtKeyUsage +) + +// CertificateInvalidError results when an odd error occurs. Users of this +// library probably want to handle all these errors uniformly. +type CertificateInvalidError struct { + Cert *Certificate + Reason InvalidReason + Detail string +} + +func (e CertificateInvalidError) Error() string { + switch e.Reason { + case NotAuthorizedToSign: + return "x509: certificate is not authorized to sign other certificates" + case Expired: + return "x509: certificate has expired or is not yet valid" + case CANotAuthorizedForThisName: + return "x509: a root or intermediate certificate is not authorized to sign for this name: " + e.Detail + case CANotAuthorizedForExtKeyUsage: + return "x509: a root or intermediate certificate is not authorized for an extended key usage: " + e.Detail + case TooManyIntermediates: + return "x509: too many intermediates for path length constraint" + case IncompatibleUsage: + return "x509: certificate specifies an incompatible key usage: " + e.Detail + case NameMismatch: + return "x509: issuer name does not match subject from issuing certificate" + case NameConstraintsWithoutSANs: + return "x509: issuer has name constraints but leaf doesn't have a SAN extension" + case UnconstrainedName: + return "x509: issuer has name constraints but leaf contains unknown or unconstrained name: " + e.Detail + } + return "x509: unknown error" +} + +// HostnameError results when the set of authorized names doesn't match the +// requested name. +type HostnameError struct { + Certificate *Certificate + Host string +} + +func (h HostnameError) Error() string { + c := h.Certificate + + var valid string + if ip := net.ParseIP(h.Host); ip != nil { + // Trying to validate an IP + if len(c.IPAddresses) == 0 { + return "x509: cannot validate certificate for " + h.Host + " because it doesn't contain any IP SANs" + } + for _, san := range c.IPAddresses { + if len(valid) > 0 { + valid += ", " + } + valid += san.String() + } + } else { + if c.hasSANExtension() { + valid = strings.Join(c.DNSNames, ", ") + } else { + valid = c.Subject.CommonName + } + } + + if len(valid) == 0 { + return "x509: certificate is not valid for any names, but wanted to match " + h.Host + } + return "x509: certificate is valid for " + valid + ", not " + h.Host +} + +// UnknownAuthorityError results when the certificate issuer is unknown +type UnknownAuthorityError struct { + Cert *Certificate + // hintErr contains an error that may be helpful in determining why an + // authority wasn't found. + hintErr error + // hintCert contains a possible authority certificate that was rejected + // because of the error in hintErr. + hintCert *Certificate +} + +func (e UnknownAuthorityError) Error() string { + s := "x509: certificate signed by unknown authority" + if e.hintErr != nil { + certName := e.hintCert.Subject.CommonName + if len(certName) == 0 { + if len(e.hintCert.Subject.Organization) > 0 { + certName = e.hintCert.Subject.Organization[0] + } else { + certName = "serial:" + e.hintCert.SerialNumber.String() + } + } + s += fmt.Sprintf(" (possibly because of %q while trying to verify candidate authority certificate %q)", e.hintErr, certName) + } + return s +} + +// SystemRootsError results when we fail to load the system root certificates. +type SystemRootsError struct { + Err error +} + +func (se SystemRootsError) Error() string { + msg := "x509: failed to load system roots and no roots provided" + if se.Err != nil { + return msg + "; " + se.Err.Error() + } + return msg +} + +// errNotParsed is returned when a certificate without ASN.1 contents is +// verified. Platform-specific verification needs the ASN.1 contents. +var errNotParsed = errors.New("x509: missing ASN.1 contents; use ParseCertificate") + +// VerifyOptions contains parameters for Certificate.Verify. It's a structure +// because other PKIX verification APIs have ended up needing many options. +type VerifyOptions struct { + DNSName string + Intermediates *CertPool + Roots *CertPool // if nil, the system roots are used + CurrentTime time.Time // if zero, the current time is used + // Options to disable various verification checks. + DisableTimeChecks bool + DisableCriticalExtensionChecks bool + DisableNameChecks bool + DisableEKUChecks bool + DisablePathLenChecks bool + DisableNameConstraintChecks bool + // KeyUsage specifies which Extended Key Usage values are acceptable. A leaf + // certificate is accepted if it contains any of the listed values. An empty + // list means ExtKeyUsageServerAuth. To accept any key usage, include + // ExtKeyUsageAny. + // + // Certificate chains are required to nest extended key usage values, + // irrespective of this value. This matches the Windows CryptoAPI behavior, + // but not the spec. + KeyUsages []ExtKeyUsage + // MaxConstraintComparisions is the maximum number of comparisons to + // perform when checking a given certificate's name constraints. If + // zero, a sensible default is used. This limit prevents pathological + // certificates from consuming excessive amounts of CPU time when + // validating. + MaxConstraintComparisions int +} + +const ( + leafCertificate = iota + intermediateCertificate + rootCertificate +) + +// rfc2821Mailbox represents a “mailbox” (which is an email address to most +// people) by breaking it into the “local” (i.e. before the '@') and “domain” +// parts. +type rfc2821Mailbox struct { + local, domain string +} + +// parseRFC2821Mailbox parses an email address into local and domain parts, +// based on the ABNF for a “Mailbox” from RFC 2821. According to +// https://tools.ietf.org/html/rfc5280#section-4.2.1.6 that's correct for an +// rfc822Name from a certificate: “The format of an rfc822Name is a "Mailbox" +// as defined in https://tools.ietf.org/html/rfc2821#section-4.1.2”. +func parseRFC2821Mailbox(in string) (mailbox rfc2821Mailbox, ok bool) { + if len(in) == 0 { + return mailbox, false + } + + localPartBytes := make([]byte, 0, len(in)/2) + + if in[0] == '"' { + // Quoted-string = DQUOTE *qcontent DQUOTE + // non-whitespace-control = %d1-8 / %d11 / %d12 / %d14-31 / %d127 + // qcontent = qtext / quoted-pair + // qtext = non-whitespace-control / + // %d33 / %d35-91 / %d93-126 + // quoted-pair = ("\" text) / obs-qp + // text = %d1-9 / %d11 / %d12 / %d14-127 / obs-text + // + // (Names beginning with “obs-” are the obsolete syntax from + // https://tools.ietf.org/html/rfc2822#section-4. Since it has + // been 16 years, we no longer accept that.) + in = in[1:] + QuotedString: + for { + if len(in) == 0 { + return mailbox, false + } + c := in[0] + in = in[1:] + + switch { + case c == '"': + break QuotedString + + case c == '\\': + // quoted-pair + if len(in) == 0 { + return mailbox, false + } + if in[0] == 11 || + in[0] == 12 || + (1 <= in[0] && in[0] <= 9) || + (14 <= in[0] && in[0] <= 127) { + localPartBytes = append(localPartBytes, in[0]) + in = in[1:] + } else { + return mailbox, false + } + + case c == 11 || + c == 12 || + // Space (char 32) is not allowed based on the + // BNF, but RFC 3696 gives an example that + // assumes that it is. Several “verified” + // errata continue to argue about this point. + // We choose to accept it. + c == 32 || + c == 33 || + c == 127 || + (1 <= c && c <= 8) || + (14 <= c && c <= 31) || + (35 <= c && c <= 91) || + (93 <= c && c <= 126): + // qtext + localPartBytes = append(localPartBytes, c) + + default: + return mailbox, false + } + } + } else { + // Atom ("." Atom)* + NextChar: + for len(in) > 0 { + // atext from https://tools.ietf.org/html/rfc2822#section-3.2.4 + c := in[0] + + switch { + case c == '\\': + // Examples given in RFC 3696 suggest that + // escaped characters can appear outside of a + // quoted string. Several “verified” errata + // continue to argue the point. We choose to + // accept it. + in = in[1:] + if len(in) == 0 { + return mailbox, false + } + fallthrough + + case ('0' <= c && c <= '9') || + ('a' <= c && c <= 'z') || + ('A' <= c && c <= 'Z') || + c == '!' || c == '#' || c == '$' || c == '%' || + c == '&' || c == '\'' || c == '*' || c == '+' || + c == '-' || c == '/' || c == '=' || c == '?' || + c == '^' || c == '_' || c == '`' || c == '{' || + c == '|' || c == '}' || c == '~' || c == '.': + localPartBytes = append(localPartBytes, in[0]) + in = in[1:] + + default: + break NextChar + } + } + + if len(localPartBytes) == 0 { + return mailbox, false + } + + // https://tools.ietf.org/html/rfc3696#section-3 + // “period (".") may also appear, but may not be used to start + // or end the local part, nor may two or more consecutive + // periods appear.” + twoDots := []byte{'.', '.'} + if localPartBytes[0] == '.' || + localPartBytes[len(localPartBytes)-1] == '.' || + bytes.Contains(localPartBytes, twoDots) { + return mailbox, false + } + } + + if len(in) == 0 || in[0] != '@' { + return mailbox, false + } + in = in[1:] + + // The RFC species a format for domains, but that's known to be + // violated in practice so we accept that anything after an '@' is the + // domain part. + if _, ok := domainToReverseLabels(in); !ok { + return mailbox, false + } + + mailbox.local = string(localPartBytes) + mailbox.domain = in + return mailbox, true +} + +// domainToReverseLabels converts a textual domain name like foo.example.com to +// the list of labels in reverse order, e.g. ["com", "example", "foo"]. +func domainToReverseLabels(domain string) (reverseLabels []string, ok bool) { + for len(domain) > 0 { + if i := strings.LastIndexByte(domain, '.'); i == -1 { + reverseLabels = append(reverseLabels, domain) + domain = "" + } else { + reverseLabels = append(reverseLabels, domain[i+1:len(domain)]) + domain = domain[:i] + } + } + + if len(reverseLabels) > 0 && len(reverseLabels[0]) == 0 { + // An empty label at the end indicates an absolute value. + return nil, false + } + + for _, label := range reverseLabels { + if len(label) == 0 { + // Empty labels are otherwise invalid. + return nil, false + } + + for _, c := range label { + if c < 33 || c > 126 { + // Invalid character. + return nil, false + } + } + } + + return reverseLabels, true +} + +func matchEmailConstraint(mailbox rfc2821Mailbox, constraint string) (bool, error) { + // If the constraint contains an @, then it specifies an exact mailbox + // name. + if strings.Contains(constraint, "@") { + constraintMailbox, ok := parseRFC2821Mailbox(constraint) + if !ok { + return false, fmt.Errorf("x509: internal error: cannot parse constraint %q", constraint) + } + return mailbox.local == constraintMailbox.local && strings.EqualFold(mailbox.domain, constraintMailbox.domain), nil + } + + // Otherwise the constraint is like a DNS constraint of the domain part + // of the mailbox. + return matchDomainConstraint(mailbox.domain, constraint) +} + +func matchURIConstraint(uri *url.URL, constraint string) (bool, error) { + // https://tools.ietf.org/html/rfc5280#section-4.2.1.10 + // “a uniformResourceIdentifier that does not include an authority + // component with a host name specified as a fully qualified domain + // name (e.g., if the URI either does not include an authority + // component or includes an authority component in which the host name + // is specified as an IP address), then the application MUST reject the + // certificate.” + + host := uri.Host + if len(host) == 0 { + return false, fmt.Errorf("URI with empty host (%q) cannot be matched against constraints", uri.String()) + } + + if strings.Contains(host, ":") && !strings.HasSuffix(host, "]") { + var err error + host, _, err = net.SplitHostPort(uri.Host) + if err != nil { + return false, err + } + } + + if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") || + net.ParseIP(host) != nil { + return false, fmt.Errorf("URI with IP (%q) cannot be matched against constraints", uri.String()) + } + + return matchDomainConstraint(host, constraint) +} + +func matchIPConstraint(ip net.IP, constraint *net.IPNet) (bool, error) { + if len(ip) != len(constraint.IP) { + return false, nil + } + + for i := range ip { + if mask := constraint.Mask[i]; ip[i]&mask != constraint.IP[i]&mask { + return false, nil + } + } + + return true, nil +} + +func matchDomainConstraint(domain, constraint string) (bool, error) { + // The meaning of zero length constraints is not specified, but this + // code follows NSS and accepts them as matching everything. + if len(constraint) == 0 { + return true, nil + } + + domainLabels, ok := domainToReverseLabels(domain) + if !ok { + return false, fmt.Errorf("x509: internal error: cannot parse domain %q", domain) + } + + // RFC 5280 says that a leading period in a domain name means that at + // least one label must be prepended, but only for URI and email + // constraints, not DNS constraints. The code also supports that + // behaviour for DNS constraints. + + mustHaveSubdomains := false + if constraint[0] == '.' { + mustHaveSubdomains = true + constraint = constraint[1:] + } + + constraintLabels, ok := domainToReverseLabels(constraint) + if !ok { + return false, fmt.Errorf("x509: internal error: cannot parse domain %q", constraint) + } + + if len(domainLabels) < len(constraintLabels) || + (mustHaveSubdomains && len(domainLabels) == len(constraintLabels)) { + return false, nil + } + + for i, constraintLabel := range constraintLabels { + if !strings.EqualFold(constraintLabel, domainLabels[i]) { + return false, nil + } + } + + return true, nil +} + +// checkNameConstraints checks that c permits a child certificate to claim the +// given name, of type nameType. The argument parsedName contains the parsed +// form of name, suitable for passing to the match function. The total number +// of comparisons is tracked in the given count and should not exceed the given +// limit. +func (c *Certificate) checkNameConstraints(count *int, + maxConstraintComparisons int, + nameType string, + name string, + parsedName interface{}, + match func(parsedName, constraint interface{}) (match bool, err error), + permitted, excluded interface{}) error { + + excludedValue := reflect.ValueOf(excluded) + + *count += excludedValue.Len() + if *count > maxConstraintComparisons { + return CertificateInvalidError{c, TooManyConstraints, ""} + } + + for i := 0; i < excludedValue.Len(); i++ { + constraint := excludedValue.Index(i).Interface() + match, err := match(parsedName, constraint) + if err != nil { + return CertificateInvalidError{c, CANotAuthorizedForThisName, err.Error()} + } + + if match { + return CertificateInvalidError{c, CANotAuthorizedForThisName, fmt.Sprintf("%s %q is excluded by constraint %q", nameType, name, constraint)} + } + } + + permittedValue := reflect.ValueOf(permitted) + + *count += permittedValue.Len() + if *count > maxConstraintComparisons { + return CertificateInvalidError{c, TooManyConstraints, ""} + } + + ok := true + for i := 0; i < permittedValue.Len(); i++ { + constraint := permittedValue.Index(i).Interface() + + var err error + if ok, err = match(parsedName, constraint); err != nil { + return CertificateInvalidError{c, CANotAuthorizedForThisName, err.Error()} + } + + if ok { + break + } + } + + if !ok { + return CertificateInvalidError{c, CANotAuthorizedForThisName, fmt.Sprintf("%s %q is not permitted by any constraint", nameType, name)} + } + + return nil +} + +const ( + checkingAgainstIssuerCert = iota + checkingAgainstLeafCert +) + +// ekuPermittedBy returns true iff the given extended key usage is permitted by +// the given EKU from a certificate. Normally, this would be a simple +// comparison plus a special case for the “any” EKU. But, in order to support +// existing certificates, some exceptions are made. +func ekuPermittedBy(eku, certEKU ExtKeyUsage, context int) bool { + if certEKU == ExtKeyUsageAny || eku == certEKU { + return true + } + + // Some exceptions are made to support existing certificates. Firstly, + // the ServerAuth and SGC EKUs are treated as a group. + mapServerAuthEKUs := func(eku ExtKeyUsage) ExtKeyUsage { + if eku == ExtKeyUsageNetscapeServerGatedCrypto || eku == ExtKeyUsageMicrosoftServerGatedCrypto { + return ExtKeyUsageServerAuth + } + return eku + } + + eku = mapServerAuthEKUs(eku) + certEKU = mapServerAuthEKUs(certEKU) + + if eku == certEKU { + return true + } + + // If checking a requested EKU against the list in a leaf certificate there + // are fewer exceptions. + if context == checkingAgainstLeafCert { + return false + } + + // ServerAuth in a CA permits ClientAuth in the leaf. + return (eku == ExtKeyUsageClientAuth && certEKU == ExtKeyUsageServerAuth) || + // Any CA may issue an OCSP responder certificate. + eku == ExtKeyUsageOCSPSigning || + // Code-signing CAs can use Microsoft's commercial and + // kernel-mode EKUs. + (eku == ExtKeyUsageMicrosoftCommercialCodeSigning || eku == ExtKeyUsageMicrosoftKernelCodeSigning) && certEKU == ExtKeyUsageCodeSigning +} + +// isValid performs validity checks on c given that it is a candidate to append +// to the chain in currentChain. +func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *VerifyOptions) error { + if !opts.DisableCriticalExtensionChecks && len(c.UnhandledCriticalExtensions) > 0 { + return UnhandledCriticalExtension{ID: c.UnhandledCriticalExtensions[0]} + } + + if !opts.DisableNameChecks && len(currentChain) > 0 { + child := currentChain[len(currentChain)-1] + if !bytes.Equal(child.RawIssuer, c.RawSubject) { + return CertificateInvalidError{c, NameMismatch, ""} + } + } + + if !opts.DisableTimeChecks { + now := opts.CurrentTime + if now.IsZero() { + now = time.Now() + } + if now.Before(c.NotBefore) || now.After(c.NotAfter) { + return CertificateInvalidError{c, Expired, ""} + } + } + + maxConstraintComparisons := opts.MaxConstraintComparisions + if maxConstraintComparisons == 0 { + maxConstraintComparisons = 250000 + } + comparisonCount := 0 + + var leaf *Certificate + if certType == intermediateCertificate || certType == rootCertificate { + if len(currentChain) == 0 { + return errors.New("x509: internal error: empty chain when appending CA cert") + } + leaf = currentChain[0] + } + + if !opts.DisableNameConstraintChecks && (certType == intermediateCertificate || certType == rootCertificate) && c.hasNameConstraints() { + sanExtension, ok := leaf.getSANExtension() + if !ok { + // This is the deprecated, legacy case of depending on + // the CN as a hostname. Chains modern enough to be + // using name constraints should not be depending on + // CNs. + return CertificateInvalidError{c, NameConstraintsWithoutSANs, ""} + } + + err := forEachSAN(sanExtension, func(tag int, data []byte) error { + switch tag { + case nameTypeEmail: + name := string(data) + mailbox, ok := parseRFC2821Mailbox(name) + if !ok { + return fmt.Errorf("x509: cannot parse rfc822Name %q", mailbox) + } + + if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "email address", name, mailbox, + func(parsedName, constraint interface{}) (bool, error) { + return matchEmailConstraint(parsedName.(rfc2821Mailbox), constraint.(string)) + }, c.PermittedEmailAddresses, c.ExcludedEmailAddresses); err != nil { + return err + } + + case nameTypeDNS: + name := string(data) + if _, ok := domainToReverseLabels(name); !ok { + return fmt.Errorf("x509: cannot parse dnsName %q", name) + } + + if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "DNS name", name, name, + func(parsedName, constraint interface{}) (bool, error) { + return matchDomainConstraint(parsedName.(string), constraint.(string)) + }, c.PermittedDNSDomains, c.ExcludedDNSDomains); err != nil { + return err + } + + case nameTypeURI: + name := string(data) + uri, err := url.Parse(name) + if err != nil { + return fmt.Errorf("x509: internal error: URI SAN %q failed to parse", name) + } + + if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "URI", name, uri, + func(parsedName, constraint interface{}) (bool, error) { + return matchURIConstraint(parsedName.(*url.URL), constraint.(string)) + }, c.PermittedURIDomains, c.ExcludedURIDomains); err != nil { + return err + } + + case nameTypeIP: + ip := net.IP(data) + if l := len(ip); l != net.IPv4len && l != net.IPv6len { + return fmt.Errorf("x509: internal error: IP SAN %x failed to parse", data) + } + + if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "IP address", ip.String(), ip, + func(parsedName, constraint interface{}) (bool, error) { + return matchIPConstraint(parsedName.(net.IP), constraint.(*net.IPNet)) + }, c.PermittedIPRanges, c.ExcludedIPRanges); err != nil { + return err + } + + default: + // Unknown SAN types are ignored. + } + + return nil + }) + + if err != nil { + return err + } + } + + checkEKUs := !opts.DisableEKUChecks && certType == intermediateCertificate + + // If no extended key usages are specified, then all are acceptable. + if checkEKUs && (len(c.ExtKeyUsage) == 0 && len(c.UnknownExtKeyUsage) == 0) { + checkEKUs = false + } + + // If the “any” key usage is permitted, then no more checks are needed. + if checkEKUs { + for _, caEKU := range c.ExtKeyUsage { + comparisonCount++ + if caEKU == ExtKeyUsageAny { + checkEKUs = false + break + } + } + } + + if checkEKUs { + NextEKU: + for _, eku := range leaf.ExtKeyUsage { + if comparisonCount > maxConstraintComparisons { + return CertificateInvalidError{c, TooManyConstraints, ""} + } + + for _, caEKU := range c.ExtKeyUsage { + comparisonCount++ + if ekuPermittedBy(eku, caEKU, checkingAgainstIssuerCert) { + continue NextEKU + } + } + + oid, _ := oidFromExtKeyUsage(eku) + return CertificateInvalidError{c, CANotAuthorizedForExtKeyUsage, fmt.Sprintf("EKU not permitted: %#v", oid)} + } + + NextUnknownEKU: + for _, eku := range leaf.UnknownExtKeyUsage { + if comparisonCount > maxConstraintComparisons { + return CertificateInvalidError{c, TooManyConstraints, ""} + } + + for _, caEKU := range c.UnknownExtKeyUsage { + comparisonCount++ + if caEKU.Equal(eku) { + continue NextUnknownEKU + } + } + + return CertificateInvalidError{c, CANotAuthorizedForExtKeyUsage, fmt.Sprintf("EKU not permitted: %#v", eku)} + } + } + + // KeyUsage status flags are ignored. From Engineering Security, Peter + // Gutmann: A European government CA marked its signing certificates as + // being valid for encryption only, but no-one noticed. Another + // European CA marked its signature keys as not being valid for + // signatures. A different CA marked its own trusted root certificate + // as being invalid for certificate signing. Another national CA + // distributed a certificate to be used to encrypt data for the + // country’s tax authority that was marked as only being usable for + // digital signatures but not for encryption. Yet another CA reversed + // the order of the bit flags in the keyUsage due to confusion over + // encoding endianness, essentially setting a random keyUsage in + // certificates that it issued. Another CA created a self-invalidating + // certificate by adding a certificate policy statement stipulating + // that the certificate had to be used strictly as specified in the + // keyUsage, and a keyUsage containing a flag indicating that the RSA + // encryption key could only be used for Diffie-Hellman key agreement. + + if certType == intermediateCertificate && (!c.BasicConstraintsValid || !c.IsCA) { + return CertificateInvalidError{c, NotAuthorizedToSign, ""} + } + + if !opts.DisablePathLenChecks && c.BasicConstraintsValid && c.MaxPathLen >= 0 { + numIntermediates := len(currentChain) - 1 + if numIntermediates > c.MaxPathLen { + return CertificateInvalidError{c, TooManyIntermediates, ""} + } + } + + return nil +} + +// formatOID formats an ASN.1 OBJECT IDENTIFER in the common, dotted style. +func formatOID(oid asn1.ObjectIdentifier) string { + ret := "" + for i, v := range oid { + if i > 0 { + ret += "." + } + ret += strconv.Itoa(v) + } + return ret +} + +// Verify attempts to verify c by building one or more chains from c to a +// certificate in opts.Roots, using certificates in opts.Intermediates if +// needed. If successful, it returns one or more chains where the first +// element of the chain is c and the last element is from opts.Roots. +// +// If opts.Roots is nil and system roots are unavailable the returned error +// will be of type SystemRootsError. +// +// Name constraints in the intermediates will be applied to all names claimed +// in the chain, not just opts.DNSName. Thus it is invalid for a leaf to claim +// example.com if an intermediate doesn't permit it, even if example.com is not +// the name being validated. Note that DirectoryName constraints are not +// supported. +// +// Extended Key Usage values are enforced down a chain, so an intermediate or +// root that enumerates EKUs prevents a leaf from asserting an EKU not in that +// list. +// +// WARNING: this function doesn't do any revocation checking. +func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error) { + // Platform-specific verification needs the ASN.1 contents so + // this makes the behavior consistent across platforms. + if len(c.Raw) == 0 { + return nil, errNotParsed + } + if opts.Intermediates != nil { + for _, intermediate := range opts.Intermediates.certs { + if len(intermediate.Raw) == 0 { + return nil, errNotParsed + } + } + } + + // Use Windows's own verification and chain building. + if opts.Roots == nil && runtime.GOOS == "windows" { + return c.systemVerify(&opts) + } + + if opts.Roots == nil { + opts.Roots = systemRootsPool() + if opts.Roots == nil { + return nil, SystemRootsError{systemRootsErr} + } + } + + err = c.isValid(leafCertificate, nil, &opts) + if err != nil { + return + } + + if len(opts.DNSName) > 0 { + err = c.VerifyHostname(opts.DNSName) + if err != nil { + return + } + } + + requestedKeyUsages := make([]ExtKeyUsage, len(opts.KeyUsages)) + copy(requestedKeyUsages, opts.KeyUsages) + if len(requestedKeyUsages) == 0 { + requestedKeyUsages = append(requestedKeyUsages, ExtKeyUsageServerAuth) + } + + // If no key usages are specified, then any are acceptable. + checkEKU := !opts.DisableEKUChecks && len(c.ExtKeyUsage) > 0 + + for _, eku := range requestedKeyUsages { + if eku == ExtKeyUsageAny { + checkEKU = false + break + } + } + + if checkEKU { + foundMatch := false + NextUsage: + for _, eku := range requestedKeyUsages { + for _, leafEKU := range c.ExtKeyUsage { + if ekuPermittedBy(eku, leafEKU, checkingAgainstLeafCert) { + foundMatch = true + break NextUsage + } + } + } + + if !foundMatch { + msg := "leaf contains the following, recognized EKUs: " + + for i, leafEKU := range c.ExtKeyUsage { + oid, ok := oidFromExtKeyUsage(leafEKU) + if !ok { + continue + } + + if i > 0 { + msg += ", " + } + msg += formatOID(oid) + } + + return nil, CertificateInvalidError{c, IncompatibleUsage, msg} + } + } + + var candidateChains [][]*Certificate + if opts.Roots.contains(c) { + candidateChains = append(candidateChains, []*Certificate{c}) + } else { + if candidateChains, err = c.buildChains(make(map[int][][]*Certificate), []*Certificate{c}, &opts); err != nil { + return nil, err + } + } + + return candidateChains, nil +} + +func appendToFreshChain(chain []*Certificate, cert *Certificate) []*Certificate { + n := make([]*Certificate, len(chain)+1) + copy(n, chain) + n[len(chain)] = cert + return n +} + +func (c *Certificate) buildChains(cache map[int][][]*Certificate, currentChain []*Certificate, opts *VerifyOptions) (chains [][]*Certificate, err error) { + possibleRoots, failedRoot, rootErr := opts.Roots.findVerifiedParents(c) +nextRoot: + for _, rootNum := range possibleRoots { + root := opts.Roots.certs[rootNum] + + for _, cert := range currentChain { + if cert.Equal(root) { + continue nextRoot + } + } + + err = root.isValid(rootCertificate, currentChain, opts) + if err != nil { + continue + } + chains = append(chains, appendToFreshChain(currentChain, root)) + } + + possibleIntermediates, failedIntermediate, intermediateErr := opts.Intermediates.findVerifiedParents(c) +nextIntermediate: + for _, intermediateNum := range possibleIntermediates { + intermediate := opts.Intermediates.certs[intermediateNum] + for _, cert := range currentChain { + if cert.Equal(intermediate) { + continue nextIntermediate + } + } + err = intermediate.isValid(intermediateCertificate, currentChain, opts) + if err != nil { + continue + } + var childChains [][]*Certificate + childChains, ok := cache[intermediateNum] + if !ok { + childChains, err = intermediate.buildChains(cache, appendToFreshChain(currentChain, intermediate), opts) + cache[intermediateNum] = childChains + } + chains = append(chains, childChains...) + } + + if len(chains) > 0 { + err = nil + } + + if len(chains) == 0 && err == nil { + hintErr := rootErr + hintCert := failedRoot + if hintErr == nil { + hintErr = intermediateErr + hintCert = failedIntermediate + } + err = UnknownAuthorityError{c, hintErr, hintCert} + } + + return +} + +func matchHostnames(pattern, host string) bool { + host = strings.TrimSuffix(host, ".") + pattern = strings.TrimSuffix(pattern, ".") + + if len(pattern) == 0 || len(host) == 0 { + return false + } + + patternParts := strings.Split(pattern, ".") + hostParts := strings.Split(host, ".") + + if len(patternParts) != len(hostParts) { + return false + } + + for i, patternPart := range patternParts { + if i == 0 && patternPart == "*" { + continue + } + if patternPart != hostParts[i] { + return false + } + } + + return true +} + +// toLowerCaseASCII returns a lower-case version of in. See RFC 6125 6.4.1. We use +// an explicitly ASCII function to avoid any sharp corners resulting from +// performing Unicode operations on DNS labels. +func toLowerCaseASCII(in string) string { + // If the string is already lower-case then there's nothing to do. + isAlreadyLowerCase := true + for _, c := range in { + if c == utf8.RuneError { + // If we get a UTF-8 error then there might be + // upper-case ASCII bytes in the invalid sequence. + isAlreadyLowerCase = false + break + } + if 'A' <= c && c <= 'Z' { + isAlreadyLowerCase = false + break + } + } + + if isAlreadyLowerCase { + return in + } + + out := []byte(in) + for i, c := range out { + if 'A' <= c && c <= 'Z' { + out[i] += 'a' - 'A' + } + } + return string(out) +} + +// VerifyHostname returns nil if c is a valid certificate for the named host. +// Otherwise it returns an error describing the mismatch. +func (c *Certificate) VerifyHostname(h string) error { + // IP addresses may be written in [ ]. + candidateIP := h + if len(h) >= 3 && h[0] == '[' && h[len(h)-1] == ']' { + candidateIP = h[1 : len(h)-1] + } + if ip := net.ParseIP(candidateIP); ip != nil { + // We only match IP addresses against IP SANs. + // https://tools.ietf.org/html/rfc6125#appendix-B.2 + for _, candidate := range c.IPAddresses { + if ip.Equal(candidate) { + return nil + } + } + return HostnameError{c, candidateIP} + } + + lowered := toLowerCaseASCII(h) + + if c.hasSANExtension() { + for _, match := range c.DNSNames { + if matchHostnames(toLowerCaseASCII(match), lowered) { + return nil + } + } + // If Subject Alt Name is given, we ignore the common name. + } else if matchHostnames(toLowerCaseASCII(c.Subject.CommonName), lowered) { + return nil + } + + return HostnameError{c, h} +} diff --git a/vendor/github.com/google/certificate-transparency-go/x509/x509.go b/vendor/github.com/google/certificate-transparency-go/x509/x509.go new file mode 100644 index 0000000000000..23f2a6a228f71 --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/x509/x509.go @@ -0,0 +1,2900 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package x509 parses X.509-encoded keys and certificates. +// +// On UNIX systems the environment variables SSL_CERT_FILE and SSL_CERT_DIR +// can be used to override the system default locations for the SSL certificate +// file and SSL certificate files directory, respectively. +// +// This is a fork of the go library crypto/x509 package, it's more relaxed +// about certificates that it'll accept, and exports the TBSCertificate +// structure. +package x509 + +import ( + "bytes" + "crypto" + "crypto/dsa" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rsa" + _ "crypto/sha1" + _ "crypto/sha256" + _ "crypto/sha512" + "encoding/pem" + "errors" + "fmt" + "io" + "math/big" + "net" + "net/url" + "strconv" + "strings" + "time" + "unicode/utf8" + + cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1" + + "github.com/google/certificate-transparency-go/asn1" + "github.com/google/certificate-transparency-go/tls" + "github.com/google/certificate-transparency-go/x509/pkix" + "golang.org/x/crypto/cryptobyte" +) + +// pkixPublicKey reflects a PKIX public key structure. See SubjectPublicKeyInfo +// in RFC 3280. +type pkixPublicKey struct { + Algo pkix.AlgorithmIdentifier + BitString asn1.BitString +} + +// ParsePKIXPublicKey parses a DER encoded public key. These values are +// typically found in PEM blocks with "BEGIN PUBLIC KEY". +// +// Supported key types include RSA, DSA, and ECDSA. Unknown key +// types result in an error. +// +// On success, pub will be of type *rsa.PublicKey, *dsa.PublicKey, +// or *ecdsa.PublicKey. +func ParsePKIXPublicKey(derBytes []byte) (pub interface{}, err error) { + var pki publicKeyInfo + if rest, err := asn1.Unmarshal(derBytes, &pki); err != nil { + return nil, err + } else if len(rest) != 0 { + return nil, errors.New("x509: trailing data after ASN.1 of public-key") + } + algo := getPublicKeyAlgorithmFromOID(pki.Algorithm.Algorithm) + if algo == UnknownPublicKeyAlgorithm { + return nil, errors.New("x509: unknown public key algorithm") + } + return parsePublicKey(algo, &pki) +} + +func marshalPublicKey(pub interface{}) (publicKeyBytes []byte, publicKeyAlgorithm pkix.AlgorithmIdentifier, err error) { + switch pub := pub.(type) { + case *rsa.PublicKey: + publicKeyBytes, err = asn1.Marshal(pkcs1PublicKey{ + N: pub.N, + E: pub.E, + }) + if err != nil { + return nil, pkix.AlgorithmIdentifier{}, err + } + publicKeyAlgorithm.Algorithm = OIDPublicKeyRSA + // This is a NULL parameters value which is required by + // https://tools.ietf.org/html/rfc3279#section-2.3.1. + publicKeyAlgorithm.Parameters = asn1.NullRawValue + case *ecdsa.PublicKey: + publicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y) + oid, ok := OIDFromNamedCurve(pub.Curve) + if !ok { + return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: unsupported elliptic curve") + } + publicKeyAlgorithm.Algorithm = OIDPublicKeyECDSA + var paramBytes []byte + paramBytes, err = asn1.Marshal(oid) + if err != nil { + return + } + publicKeyAlgorithm.Parameters.FullBytes = paramBytes + default: + return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: only RSA and ECDSA public keys supported") + } + + return publicKeyBytes, publicKeyAlgorithm, nil +} + +// MarshalPKIXPublicKey serialises a public key to DER-encoded PKIX format. +func MarshalPKIXPublicKey(pub interface{}) ([]byte, error) { + var publicKeyBytes []byte + var publicKeyAlgorithm pkix.AlgorithmIdentifier + var err error + + if publicKeyBytes, publicKeyAlgorithm, err = marshalPublicKey(pub); err != nil { + return nil, err + } + + pkix := pkixPublicKey{ + Algo: publicKeyAlgorithm, + BitString: asn1.BitString{ + Bytes: publicKeyBytes, + BitLength: 8 * len(publicKeyBytes), + }, + } + + ret, _ := asn1.Marshal(pkix) + return ret, nil +} + +// These structures reflect the ASN.1 structure of X.509 certificates.: + +type certificate struct { + Raw asn1.RawContent + TBSCertificate tbsCertificate + SignatureAlgorithm pkix.AlgorithmIdentifier + SignatureValue asn1.BitString +} + +type tbsCertificate struct { + Raw asn1.RawContent + Version int `asn1:"optional,explicit,default:0,tag:0"` + SerialNumber *big.Int + SignatureAlgorithm pkix.AlgorithmIdentifier + Issuer asn1.RawValue + Validity validity + Subject asn1.RawValue + PublicKey publicKeyInfo + UniqueId asn1.BitString `asn1:"optional,tag:1"` + SubjectUniqueId asn1.BitString `asn1:"optional,tag:2"` + Extensions []pkix.Extension `asn1:"optional,explicit,tag:3"` +} + +type dsaAlgorithmParameters struct { + P, Q, G *big.Int +} + +type dsaSignature struct { + R, S *big.Int +} + +type ecdsaSignature dsaSignature + +type validity struct { + NotBefore, NotAfter time.Time +} + +type publicKeyInfo struct { + Raw asn1.RawContent + Algorithm pkix.AlgorithmIdentifier + PublicKey asn1.BitString +} + +// RFC 5280, 4.2.1.1 +type authKeyId struct { + Id []byte `asn1:"optional,tag:0"` +} + +// SignatureAlgorithm indicates the algorithm used to sign a certificate. +type SignatureAlgorithm int + +// SignatureAlgorithm values: +const ( + UnknownSignatureAlgorithm SignatureAlgorithm = iota + MD2WithRSA + MD5WithRSA + SHA1WithRSA + SHA256WithRSA + SHA384WithRSA + SHA512WithRSA + DSAWithSHA1 + DSAWithSHA256 + ECDSAWithSHA1 + ECDSAWithSHA256 + ECDSAWithSHA384 + ECDSAWithSHA512 + SHA256WithRSAPSS + SHA384WithRSAPSS + SHA512WithRSAPSS +) + +func (algo SignatureAlgorithm) isRSAPSS() bool { + switch algo { + case SHA256WithRSAPSS, SHA384WithRSAPSS, SHA512WithRSAPSS: + return true + default: + return false + } +} + +func (algo SignatureAlgorithm) String() string { + for _, details := range signatureAlgorithmDetails { + if details.algo == algo { + return details.name + } + } + return strconv.Itoa(int(algo)) +} + +// PublicKeyAlgorithm indicates the algorithm used for a certificate's public key. +type PublicKeyAlgorithm int + +// PublicKeyAlgorithm values: +const ( + UnknownPublicKeyAlgorithm PublicKeyAlgorithm = iota + RSA + DSA + ECDSA +) + +var publicKeyAlgoName = [...]string{ + RSA: "RSA", + DSA: "DSA", + ECDSA: "ECDSA", +} + +func (algo PublicKeyAlgorithm) String() string { + if 0 < algo && int(algo) < len(publicKeyAlgoName) { + return publicKeyAlgoName[algo] + } + return strconv.Itoa(int(algo)) +} + +// OIDs for signature algorithms +// +// pkcs-1 OBJECT IDENTIFIER ::= { +// iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } +// +// +// RFC 3279 2.2.1 RSA Signature Algorithms +// +// md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 } +// +// md5WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 } +// +// sha-1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 } +// +// dsaWithSha1 OBJECT IDENTIFIER ::= { +// iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 3 } +// +// RFC 3279 2.2.3 ECDSA Signature Algorithm +// +// ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { +// iso(1) member-body(2) us(840) ansi-x962(10045) +// signatures(4) ecdsa-with-SHA1(1)} +// +// +// RFC 4055 5 PKCS #1 Version 1.5 +// +// sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 } +// +// sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 } +// +// sha512WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 13 } +// +// +// RFC 5758 3.1 DSA Signature Algorithms +// +// dsaWithSha256 OBJECT IDENTIFIER ::= { +// joint-iso-ccitt(2) country(16) us(840) organization(1) gov(101) +// csor(3) algorithms(4) id-dsa-with-sha2(3) 2} +// +// RFC 5758 3.2 ECDSA Signature Algorithm +// +// ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2) +// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 } +// +// ecdsa-with-SHA384 OBJECT IDENTIFIER ::= { iso(1) member-body(2) +// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 3 } +// +// ecdsa-with-SHA512 OBJECT IDENTIFIER ::= { iso(1) member-body(2) +// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 4 } + +var ( + oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2} + oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4} + oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} + oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} + oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} + oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} + oidSignatureRSAPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10} + oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} + oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 2} + oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1} + oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2} + oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3} + oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4} + + oidSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1} + oidSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2} + oidSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3} + + oidMGF1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 8} + + // oidISOSignatureSHA1WithRSA means the same as oidSignatureSHA1WithRSA + // but it's specified by ISO. Microsoft's makecert.exe has been known + // to produce certificates with this OID. + oidISOSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 29} +) + +var signatureAlgorithmDetails = []struct { + algo SignatureAlgorithm + name string + oid asn1.ObjectIdentifier + pubKeyAlgo PublicKeyAlgorithm + hash crypto.Hash +}{ + {MD2WithRSA, "MD2-RSA", oidSignatureMD2WithRSA, RSA, crypto.Hash(0) /* no value for MD2 */}, + {MD5WithRSA, "MD5-RSA", oidSignatureMD5WithRSA, RSA, crypto.MD5}, + {SHA1WithRSA, "SHA1-RSA", oidSignatureSHA1WithRSA, RSA, crypto.SHA1}, + {SHA1WithRSA, "SHA1-RSA", oidISOSignatureSHA1WithRSA, RSA, crypto.SHA1}, + {SHA256WithRSA, "SHA256-RSA", oidSignatureSHA256WithRSA, RSA, crypto.SHA256}, + {SHA384WithRSA, "SHA384-RSA", oidSignatureSHA384WithRSA, RSA, crypto.SHA384}, + {SHA512WithRSA, "SHA512-RSA", oidSignatureSHA512WithRSA, RSA, crypto.SHA512}, + {SHA256WithRSAPSS, "SHA256-RSAPSS", oidSignatureRSAPSS, RSA, crypto.SHA256}, + {SHA384WithRSAPSS, "SHA384-RSAPSS", oidSignatureRSAPSS, RSA, crypto.SHA384}, + {SHA512WithRSAPSS, "SHA512-RSAPSS", oidSignatureRSAPSS, RSA, crypto.SHA512}, + {DSAWithSHA1, "DSA-SHA1", oidSignatureDSAWithSHA1, DSA, crypto.SHA1}, + {DSAWithSHA256, "DSA-SHA256", oidSignatureDSAWithSHA256, DSA, crypto.SHA256}, + {ECDSAWithSHA1, "ECDSA-SHA1", oidSignatureECDSAWithSHA1, ECDSA, crypto.SHA1}, + {ECDSAWithSHA256, "ECDSA-SHA256", oidSignatureECDSAWithSHA256, ECDSA, crypto.SHA256}, + {ECDSAWithSHA384, "ECDSA-SHA384", oidSignatureECDSAWithSHA384, ECDSA, crypto.SHA384}, + {ECDSAWithSHA512, "ECDSA-SHA512", oidSignatureECDSAWithSHA512, ECDSA, crypto.SHA512}, +} + +// pssParameters reflects the parameters in an AlgorithmIdentifier that +// specifies RSA PSS. See https://tools.ietf.org/html/rfc3447#appendix-A.2.3 +type pssParameters struct { + // The following three fields are not marked as + // optional because the default values specify SHA-1, + // which is no longer suitable for use in signatures. + Hash pkix.AlgorithmIdentifier `asn1:"explicit,tag:0"` + MGF pkix.AlgorithmIdentifier `asn1:"explicit,tag:1"` + SaltLength int `asn1:"explicit,tag:2"` + TrailerField int `asn1:"optional,explicit,tag:3,default:1"` +} + +// rsaPSSParameters returns an asn1.RawValue suitable for use as the Parameters +// in an AlgorithmIdentifier that specifies RSA PSS. +func rsaPSSParameters(hashFunc crypto.Hash) asn1.RawValue { + var hashOID asn1.ObjectIdentifier + + switch hashFunc { + case crypto.SHA256: + hashOID = oidSHA256 + case crypto.SHA384: + hashOID = oidSHA384 + case crypto.SHA512: + hashOID = oidSHA512 + } + + params := pssParameters{ + Hash: pkix.AlgorithmIdentifier{ + Algorithm: hashOID, + Parameters: asn1.NullRawValue, + }, + MGF: pkix.AlgorithmIdentifier{ + Algorithm: oidMGF1, + }, + SaltLength: hashFunc.Size(), + TrailerField: 1, + } + + mgf1Params := pkix.AlgorithmIdentifier{ + Algorithm: hashOID, + Parameters: asn1.NullRawValue, + } + + var err error + params.MGF.Parameters.FullBytes, err = asn1.Marshal(mgf1Params) + if err != nil { + panic(err) + } + + serialized, err := asn1.Marshal(params) + if err != nil { + panic(err) + } + + return asn1.RawValue{FullBytes: serialized} +} + +// SignatureAlgorithmFromAI converts an PKIX algorithm identifier to the +// equivalent local constant. +func SignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) SignatureAlgorithm { + if !ai.Algorithm.Equal(oidSignatureRSAPSS) { + for _, details := range signatureAlgorithmDetails { + if ai.Algorithm.Equal(details.oid) { + return details.algo + } + } + return UnknownSignatureAlgorithm + } + + // RSA PSS is special because it encodes important parameters + // in the Parameters. + + var params pssParameters + if _, err := asn1.Unmarshal(ai.Parameters.FullBytes, ¶ms); err != nil { + return UnknownSignatureAlgorithm + } + + var mgf1HashFunc pkix.AlgorithmIdentifier + if _, err := asn1.Unmarshal(params.MGF.Parameters.FullBytes, &mgf1HashFunc); err != nil { + return UnknownSignatureAlgorithm + } + + // PSS is greatly overburdened with options. This code forces + // them into three buckets by requiring that the MGF1 hash + // function always match the message hash function (as + // recommended in + // https://tools.ietf.org/html/rfc3447#section-8.1), that the + // salt length matches the hash length, and that the trailer + // field has the default value. + if !bytes.Equal(params.Hash.Parameters.FullBytes, asn1.NullBytes) || + !params.MGF.Algorithm.Equal(oidMGF1) || + !mgf1HashFunc.Algorithm.Equal(params.Hash.Algorithm) || + !bytes.Equal(mgf1HashFunc.Parameters.FullBytes, asn1.NullBytes) || + params.TrailerField != 1 { + return UnknownSignatureAlgorithm + } + + switch { + case params.Hash.Algorithm.Equal(oidSHA256) && params.SaltLength == 32: + return SHA256WithRSAPSS + case params.Hash.Algorithm.Equal(oidSHA384) && params.SaltLength == 48: + return SHA384WithRSAPSS + case params.Hash.Algorithm.Equal(oidSHA512) && params.SaltLength == 64: + return SHA512WithRSAPSS + } + + return UnknownSignatureAlgorithm +} + +// RFC 3279, 2.3 Public Key Algorithms +// +// pkcs-1 OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840) +// rsadsi(113549) pkcs(1) 1 } +// +// rsaEncryption OBJECT IDENTIFIER ::== { pkcs1-1 1 } +// +// id-dsa OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840) +// x9-57(10040) x9cm(4) 1 } +// +// RFC 5480, 2.1.1 Unrestricted Algorithm Identifier and Parameters +// +// id-ecPublicKey OBJECT IDENTIFIER ::= { +// iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 } +var ( + OIDPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} + OIDPublicKeyDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1} + OIDPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1} + OIDPublicKeyRSAObsolete = asn1.ObjectIdentifier{2, 5, 8, 1, 1} +) + +func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm { + switch { + case oid.Equal(OIDPublicKeyRSA): + return RSA + case oid.Equal(OIDPublicKeyDSA): + return DSA + case oid.Equal(OIDPublicKeyECDSA): + return ECDSA + } + return UnknownPublicKeyAlgorithm +} + +// RFC 5480, 2.1.1.1. Named Curve +// +// secp224r1 OBJECT IDENTIFIER ::= { +// iso(1) identified-organization(3) certicom(132) curve(0) 33 } +// +// secp256r1 OBJECT IDENTIFIER ::= { +// iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3) +// prime(1) 7 } +// +// secp384r1 OBJECT IDENTIFIER ::= { +// iso(1) identified-organization(3) certicom(132) curve(0) 34 } +// +// secp521r1 OBJECT IDENTIFIER ::= { +// iso(1) identified-organization(3) certicom(132) curve(0) 35 } +// +// NB: secp256r1 is equivalent to prime256v1 +var ( + OIDNamedCurveP224 = asn1.ObjectIdentifier{1, 3, 132, 0, 33} + OIDNamedCurveP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7} + OIDNamedCurveP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34} + OIDNamedCurveP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35} +) + +func namedCurveFromOID(oid asn1.ObjectIdentifier) elliptic.Curve { + switch { + case oid.Equal(OIDNamedCurveP224): + return elliptic.P224() + case oid.Equal(OIDNamedCurveP256): + return elliptic.P256() + case oid.Equal(OIDNamedCurveP384): + return elliptic.P384() + case oid.Equal(OIDNamedCurveP521): + return elliptic.P521() + } + return nil +} + +// OIDFromNamedCurve returns the OID used to specify the use of the given +// elliptic curve. +func OIDFromNamedCurve(curve elliptic.Curve) (asn1.ObjectIdentifier, bool) { + switch curve { + case elliptic.P224(): + return OIDNamedCurveP224, true + case elliptic.P256(): + return OIDNamedCurveP256, true + case elliptic.P384(): + return OIDNamedCurveP384, true + case elliptic.P521(): + return OIDNamedCurveP521, true + } + + return nil, false +} + +// KeyUsage represents the set of actions that are valid for a given key. It's +// a bitmap of the KeyUsage* constants. +type KeyUsage int + +// KeyUsage values: +const ( + KeyUsageDigitalSignature KeyUsage = 1 << iota + KeyUsageContentCommitment + KeyUsageKeyEncipherment + KeyUsageDataEncipherment + KeyUsageKeyAgreement + KeyUsageCertSign + KeyUsageCRLSign + KeyUsageEncipherOnly + KeyUsageDecipherOnly +) + +// RFC 5280, 4.2.1.12 Extended Key Usage +// +// anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 } +// +// id-kp OBJECT IDENTIFIER ::= { id-pkix 3 } +// +// id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 } +// id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 } +// id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 } +// id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 } +// id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 } +// id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 } +var ( + oidExtKeyUsageAny = asn1.ObjectIdentifier{2, 5, 29, 37, 0} + oidExtKeyUsageServerAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 1} + oidExtKeyUsageClientAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 2} + oidExtKeyUsageCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 3} + oidExtKeyUsageEmailProtection = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 4} + oidExtKeyUsageIPSECEndSystem = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 5} + oidExtKeyUsageIPSECTunnel = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 6} + oidExtKeyUsageIPSECUser = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 7} + oidExtKeyUsageTimeStamping = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 8} + oidExtKeyUsageOCSPSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 9} + oidExtKeyUsageMicrosoftServerGatedCrypto = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 3} + oidExtKeyUsageNetscapeServerGatedCrypto = asn1.ObjectIdentifier{2, 16, 840, 1, 113730, 4, 1} + oidExtKeyUsageMicrosoftCommercialCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 2, 1, 22} + oidExtKeyUsageMicrosoftKernelCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 61, 1, 1} + // RFC 6962 s3.1 + oidExtKeyUsageCertificateTransparency = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 4} +) + +// ExtKeyUsage represents an extended set of actions that are valid for a given key. +// Each of the ExtKeyUsage* constants define a unique action. +type ExtKeyUsage int + +// ExtKeyUsage values: +const ( + ExtKeyUsageAny ExtKeyUsage = iota + ExtKeyUsageServerAuth + ExtKeyUsageClientAuth + ExtKeyUsageCodeSigning + ExtKeyUsageEmailProtection + ExtKeyUsageIPSECEndSystem + ExtKeyUsageIPSECTunnel + ExtKeyUsageIPSECUser + ExtKeyUsageTimeStamping + ExtKeyUsageOCSPSigning + ExtKeyUsageMicrosoftServerGatedCrypto + ExtKeyUsageNetscapeServerGatedCrypto + ExtKeyUsageMicrosoftCommercialCodeSigning + ExtKeyUsageMicrosoftKernelCodeSigning + ExtKeyUsageCertificateTransparency +) + +// extKeyUsageOIDs contains the mapping between an ExtKeyUsage and its OID. +var extKeyUsageOIDs = []struct { + extKeyUsage ExtKeyUsage + oid asn1.ObjectIdentifier +}{ + {ExtKeyUsageAny, oidExtKeyUsageAny}, + {ExtKeyUsageServerAuth, oidExtKeyUsageServerAuth}, + {ExtKeyUsageClientAuth, oidExtKeyUsageClientAuth}, + {ExtKeyUsageCodeSigning, oidExtKeyUsageCodeSigning}, + {ExtKeyUsageEmailProtection, oidExtKeyUsageEmailProtection}, + {ExtKeyUsageIPSECEndSystem, oidExtKeyUsageIPSECEndSystem}, + {ExtKeyUsageIPSECTunnel, oidExtKeyUsageIPSECTunnel}, + {ExtKeyUsageIPSECUser, oidExtKeyUsageIPSECUser}, + {ExtKeyUsageTimeStamping, oidExtKeyUsageTimeStamping}, + {ExtKeyUsageOCSPSigning, oidExtKeyUsageOCSPSigning}, + {ExtKeyUsageMicrosoftServerGatedCrypto, oidExtKeyUsageMicrosoftServerGatedCrypto}, + {ExtKeyUsageNetscapeServerGatedCrypto, oidExtKeyUsageNetscapeServerGatedCrypto}, + {ExtKeyUsageMicrosoftCommercialCodeSigning, oidExtKeyUsageMicrosoftCommercialCodeSigning}, + {ExtKeyUsageMicrosoftKernelCodeSigning, oidExtKeyUsageMicrosoftKernelCodeSigning}, + {ExtKeyUsageCertificateTransparency, oidExtKeyUsageCertificateTransparency}, +} + +func extKeyUsageFromOID(oid asn1.ObjectIdentifier) (eku ExtKeyUsage, ok bool) { + for _, pair := range extKeyUsageOIDs { + if oid.Equal(pair.oid) { + return pair.extKeyUsage, true + } + } + return +} + +func oidFromExtKeyUsage(eku ExtKeyUsage) (oid asn1.ObjectIdentifier, ok bool) { + for _, pair := range extKeyUsageOIDs { + if eku == pair.extKeyUsage { + return pair.oid, true + } + } + return +} + +// SerializedSCT represents a single TLS-encoded signed certificate timestamp, from RFC6962 s3.3. +type SerializedSCT struct { + Val []byte `tls:"minlen:1,maxlen:65535"` +} + +// SignedCertificateTimestampList is a list of signed certificate timestamps, from RFC6962 s3.3. +type SignedCertificateTimestampList struct { + SCTList []SerializedSCT `tls:"minlen:1,maxlen:65335"` +} + +// A Certificate represents an X.509 certificate. +type Certificate struct { + Raw []byte // Complete ASN.1 DER content (certificate, signature algorithm and signature). + RawTBSCertificate []byte // Certificate part of raw ASN.1 DER content. + RawSubjectPublicKeyInfo []byte // DER encoded SubjectPublicKeyInfo. + RawSubject []byte // DER encoded Subject + RawIssuer []byte // DER encoded Issuer + + Signature []byte + SignatureAlgorithm SignatureAlgorithm + + PublicKeyAlgorithm PublicKeyAlgorithm + PublicKey interface{} + + Version int + SerialNumber *big.Int + Issuer pkix.Name + Subject pkix.Name + NotBefore, NotAfter time.Time // Validity bounds. + KeyUsage KeyUsage + + // Extensions contains raw X.509 extensions. When parsing certificates, + // this can be used to extract non-critical extensions that are not + // parsed by this package. When marshaling certificates, the Extensions + // field is ignored, see ExtraExtensions. + Extensions []pkix.Extension + + // ExtraExtensions contains extensions to be copied, raw, into any + // marshaled certificates. Values override any extensions that would + // otherwise be produced based on the other fields. The ExtraExtensions + // field is not populated when parsing certificates, see Extensions. + ExtraExtensions []pkix.Extension + + // UnhandledCriticalExtensions contains a list of extension IDs that + // were not (fully) processed when parsing. Verify will fail if this + // slice is non-empty, unless verification is delegated to an OS + // library which understands all the critical extensions. + // + // Users can access these extensions using Extensions and can remove + // elements from this slice if they believe that they have been + // handled. + UnhandledCriticalExtensions []asn1.ObjectIdentifier + + ExtKeyUsage []ExtKeyUsage // Sequence of extended key usages. + UnknownExtKeyUsage []asn1.ObjectIdentifier // Encountered extended key usages unknown to this package. + + // BasicConstraintsValid indicates whether IsCA, MaxPathLen, + // and MaxPathLenZero are valid. + BasicConstraintsValid bool + IsCA bool + + // MaxPathLen and MaxPathLenZero indicate the presence and + // value of the BasicConstraints' "pathLenConstraint". + // + // When parsing a certificate, a positive non-zero MaxPathLen + // means that the field was specified, -1 means it was unset, + // and MaxPathLenZero being true mean that the field was + // explicitly set to zero. The case of MaxPathLen==0 with MaxPathLenZero==false + // should be treated equivalent to -1 (unset). + // + // When generating a certificate, an unset pathLenConstraint + // can be requested with either MaxPathLen == -1 or using the + // zero value for both MaxPathLen and MaxPathLenZero. + MaxPathLen int + // MaxPathLenZero indicates that BasicConstraintsValid==true + // and MaxPathLen==0 should be interpreted as an actual + // maximum path length of zero. Otherwise, that combination is + // interpreted as MaxPathLen not being set. + MaxPathLenZero bool + + SubjectKeyId []byte + AuthorityKeyId []byte + + // RFC 5280, 4.2.2.1 (Authority Information Access) + OCSPServer []string + IssuingCertificateURL []string + + // Subject Alternate Name values. (Note that these values may not be valid + // if invalid values were contained within a parsed certificate. For + // example, an element of DNSNames may not be a valid DNS domain name.) + DNSNames []string + EmailAddresses []string + IPAddresses []net.IP + URIs []*url.URL + + // Name constraints + PermittedDNSDomainsCritical bool // if true then the name constraints are marked critical. + PermittedDNSDomains []string + ExcludedDNSDomains []string + PermittedIPRanges []*net.IPNet + ExcludedIPRanges []*net.IPNet + PermittedEmailAddresses []string + ExcludedEmailAddresses []string + PermittedURIDomains []string + ExcludedURIDomains []string + + // CRL Distribution Points + CRLDistributionPoints []string + + PolicyIdentifiers []asn1.ObjectIdentifier + + // Certificate Transparency SCT extension contents; this is a TLS-encoded + // SignedCertificateTimestampList (RFC 6962 s3.3). + RawSCT []byte + SCTList SignedCertificateTimestampList +} + +// ErrUnsupportedAlgorithm results from attempting to perform an operation that +// involves algorithms that are not currently implemented. +var ErrUnsupportedAlgorithm = errors.New("x509: cannot verify signature: algorithm unimplemented") + +// InsecureAlgorithmError results when the signature algorithm for a certificate +// is known to be insecure. +type InsecureAlgorithmError SignatureAlgorithm + +func (e InsecureAlgorithmError) Error() string { + return fmt.Sprintf("x509: cannot verify signature: insecure algorithm %v", SignatureAlgorithm(e)) +} + +// ConstraintViolationError results when a requested usage is not permitted by +// a certificate. For example: checking a signature when the public key isn't a +// certificate signing key. +type ConstraintViolationError struct{} + +func (ConstraintViolationError) Error() string { + return "x509: invalid signature: parent certificate cannot sign this kind of certificate" +} + +// Equal indicates whether two Certificate objects are equal (by comparing their +// DER-encoded values). +func (c *Certificate) Equal(other *Certificate) bool { + return bytes.Equal(c.Raw, other.Raw) +} + +// IsPrecertificate checks whether the certificate is a precertificate, by +// checking for the presence of the CT Poison extension. +func (c *Certificate) IsPrecertificate() bool { + if c == nil { + return false + } + for _, ext := range c.Extensions { + if ext.Id.Equal(OIDExtensionCTPoison) { + return true + } + } + return false +} + +func (c *Certificate) hasSANExtension() bool { + return oidInExtensions(OIDExtensionSubjectAltName, c.Extensions) +} + +// Entrust have a broken root certificate (CN=Entrust.net Certification +// Authority (2048)) which isn't marked as a CA certificate and is thus invalid +// according to PKIX. +// We recognise this certificate by its SubjectPublicKeyInfo and exempt it +// from the Basic Constraints requirement. +// See http://www.entrust.net/knowledge-base/technote.cfm?tn=7869 +// +// TODO(agl): remove this hack once their reissued root is sufficiently +// widespread. +var entrustBrokenSPKI = []byte{ + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, + 0x00, 0x97, 0xa3, 0x2d, 0x3c, 0x9e, 0xde, 0x05, + 0xda, 0x13, 0xc2, 0x11, 0x8d, 0x9d, 0x8e, 0xe3, + 0x7f, 0xc7, 0x4b, 0x7e, 0x5a, 0x9f, 0xb3, 0xff, + 0x62, 0xab, 0x73, 0xc8, 0x28, 0x6b, 0xba, 0x10, + 0x64, 0x82, 0x87, 0x13, 0xcd, 0x57, 0x18, 0xff, + 0x28, 0xce, 0xc0, 0xe6, 0x0e, 0x06, 0x91, 0x50, + 0x29, 0x83, 0xd1, 0xf2, 0xc3, 0x2a, 0xdb, 0xd8, + 0xdb, 0x4e, 0x04, 0xcc, 0x00, 0xeb, 0x8b, 0xb6, + 0x96, 0xdc, 0xbc, 0xaa, 0xfa, 0x52, 0x77, 0x04, + 0xc1, 0xdb, 0x19, 0xe4, 0xae, 0x9c, 0xfd, 0x3c, + 0x8b, 0x03, 0xef, 0x4d, 0xbc, 0x1a, 0x03, 0x65, + 0xf9, 0xc1, 0xb1, 0x3f, 0x72, 0x86, 0xf2, 0x38, + 0xaa, 0x19, 0xae, 0x10, 0x88, 0x78, 0x28, 0xda, + 0x75, 0xc3, 0x3d, 0x02, 0x82, 0x02, 0x9c, 0xb9, + 0xc1, 0x65, 0x77, 0x76, 0x24, 0x4c, 0x98, 0xf7, + 0x6d, 0x31, 0x38, 0xfb, 0xdb, 0xfe, 0xdb, 0x37, + 0x02, 0x76, 0xa1, 0x18, 0x97, 0xa6, 0xcc, 0xde, + 0x20, 0x09, 0x49, 0x36, 0x24, 0x69, 0x42, 0xf6, + 0xe4, 0x37, 0x62, 0xf1, 0x59, 0x6d, 0xa9, 0x3c, + 0xed, 0x34, 0x9c, 0xa3, 0x8e, 0xdb, 0xdc, 0x3a, + 0xd7, 0xf7, 0x0a, 0x6f, 0xef, 0x2e, 0xd8, 0xd5, + 0x93, 0x5a, 0x7a, 0xed, 0x08, 0x49, 0x68, 0xe2, + 0x41, 0xe3, 0x5a, 0x90, 0xc1, 0x86, 0x55, 0xfc, + 0x51, 0x43, 0x9d, 0xe0, 0xb2, 0xc4, 0x67, 0xb4, + 0xcb, 0x32, 0x31, 0x25, 0xf0, 0x54, 0x9f, 0x4b, + 0xd1, 0x6f, 0xdb, 0xd4, 0xdd, 0xfc, 0xaf, 0x5e, + 0x6c, 0x78, 0x90, 0x95, 0xde, 0xca, 0x3a, 0x48, + 0xb9, 0x79, 0x3c, 0x9b, 0x19, 0xd6, 0x75, 0x05, + 0xa0, 0xf9, 0x88, 0xd7, 0xc1, 0xe8, 0xa5, 0x09, + 0xe4, 0x1a, 0x15, 0xdc, 0x87, 0x23, 0xaa, 0xb2, + 0x75, 0x8c, 0x63, 0x25, 0x87, 0xd8, 0xf8, 0x3d, + 0xa6, 0xc2, 0xcc, 0x66, 0xff, 0xa5, 0x66, 0x68, + 0x55, 0x02, 0x03, 0x01, 0x00, 0x01, +} + +// CheckSignatureFrom verifies that the signature on c is a valid signature +// from parent. +func (c *Certificate) CheckSignatureFrom(parent *Certificate) error { + // RFC 5280, 4.2.1.9: + // "If the basic constraints extension is not present in a version 3 + // certificate, or the extension is present but the cA boolean is not + // asserted, then the certified public key MUST NOT be used to verify + // certificate signatures." + // (except for Entrust, see comment above entrustBrokenSPKI) + if (parent.Version == 3 && !parent.BasicConstraintsValid || + parent.BasicConstraintsValid && !parent.IsCA) && + !bytes.Equal(c.RawSubjectPublicKeyInfo, entrustBrokenSPKI) { + return ConstraintViolationError{} + } + + if parent.KeyUsage != 0 && parent.KeyUsage&KeyUsageCertSign == 0 { + return ConstraintViolationError{} + } + + if parent.PublicKeyAlgorithm == UnknownPublicKeyAlgorithm { + return ErrUnsupportedAlgorithm + } + + // TODO(agl): don't ignore the path length constraint. + + return parent.CheckSignature(c.SignatureAlgorithm, c.RawTBSCertificate, c.Signature) +} + +// CheckSignature verifies that signature is a valid signature over signed from +// c's public key. +func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature []byte) error { + return checkSignature(algo, signed, signature, c.PublicKey) +} + +func (c *Certificate) hasNameConstraints() bool { + for _, e := range c.Extensions { + if len(e.Id) == 4 && e.Id[0] == OIDExtensionNameConstraints[0] && e.Id[1] == OIDExtensionNameConstraints[1] && e.Id[2] == OIDExtensionNameConstraints[2] && e.Id[3] == OIDExtensionNameConstraints[3] { + return true + } + } + + return false +} + +func (c *Certificate) getSANExtension() ([]byte, bool) { + for _, e := range c.Extensions { + if len(e.Id) == 4 && e.Id[0] == OIDExtensionSubjectAltName[0] && e.Id[1] == OIDExtensionSubjectAltName[1] && e.Id[2] == OIDExtensionSubjectAltName[2] && e.Id[3] == OIDExtensionSubjectAltName[3] { + return e.Value, true + } + } + + return nil, false +} + +func signaturePublicKeyAlgoMismatchError(expectedPubKeyAlgo PublicKeyAlgorithm, pubKey interface{}) error { + return fmt.Errorf("x509: signature algorithm specifies an %s public key, but have public key of type %T", expectedPubKeyAlgo.String(), pubKey) +} + +// CheckSignature verifies that signature is a valid signature over signed from +// a crypto.PublicKey. +func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey crypto.PublicKey) (err error) { + var hashType crypto.Hash + var pubKeyAlgo PublicKeyAlgorithm + + for _, details := range signatureAlgorithmDetails { + if details.algo == algo { + hashType = details.hash + pubKeyAlgo = details.pubKeyAlgo + } + } + + switch hashType { + case crypto.Hash(0): + return ErrUnsupportedAlgorithm + case crypto.MD5: + return InsecureAlgorithmError(algo) + } + + if !hashType.Available() { + return ErrUnsupportedAlgorithm + } + h := hashType.New() + + h.Write(signed) + digest := h.Sum(nil) + + switch pub := publicKey.(type) { + case *rsa.PublicKey: + if pubKeyAlgo != RSA { + return signaturePublicKeyAlgoMismatchError(pubKeyAlgo, pub) + } + if algo.isRSAPSS() { + return rsa.VerifyPSS(pub, hashType, digest, signature, &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash}) + } else { + return rsa.VerifyPKCS1v15(pub, hashType, digest, signature) + } + case *dsa.PublicKey: + if pubKeyAlgo != DSA { + return signaturePublicKeyAlgoMismatchError(pubKeyAlgo, pub) + } + dsaSig := new(dsaSignature) + if rest, err := asn1.Unmarshal(signature, dsaSig); err != nil { + return err + } else if len(rest) != 0 { + return errors.New("x509: trailing data after DSA signature") + } + if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 { + return errors.New("x509: DSA signature contained zero or negative values") + } + if !dsa.Verify(pub, digest, dsaSig.R, dsaSig.S) { + return errors.New("x509: DSA verification failure") + } + return + case *ecdsa.PublicKey: + if pubKeyAlgo != ECDSA { + return signaturePublicKeyAlgoMismatchError(pubKeyAlgo, pub) + } + ecdsaSig := new(ecdsaSignature) + if rest, err := asn1.Unmarshal(signature, ecdsaSig); err != nil { + return err + } else if len(rest) != 0 { + return errors.New("x509: trailing data after ECDSA signature") + } + if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { + return errors.New("x509: ECDSA signature contained zero or negative values") + } + if !ecdsa.Verify(pub, digest, ecdsaSig.R, ecdsaSig.S) { + return errors.New("x509: ECDSA verification failure") + } + return + } + return ErrUnsupportedAlgorithm +} + +// CheckCRLSignature checks that the signature in crl is from c. +func (c *Certificate) CheckCRLSignature(crl *pkix.CertificateList) error { + algo := SignatureAlgorithmFromAI(crl.SignatureAlgorithm) + return c.CheckSignature(algo, crl.TBSCertList.Raw, crl.SignatureValue.RightAlign()) +} + +// UnhandledCriticalExtension results when the certificate contains an extension +// that is marked as critical but which is not handled by this library. +type UnhandledCriticalExtension struct { + ID asn1.ObjectIdentifier +} + +func (h UnhandledCriticalExtension) Error() string { + return fmt.Sprintf("x509: unhandled critical extension (%v)", h.ID) +} + +// removeExtension takes a DER-encoded TBSCertificate, removes the extension +// specified by oid (preserving the order of other extensions), and returns the +// result still as a DER-encoded TBSCertificate. This function will fail if +// there is not exactly 1 extension of the type specified by the oid present. +func removeExtension(tbsData []byte, oid asn1.ObjectIdentifier) ([]byte, error) { + var tbs tbsCertificate + rest, err := asn1.Unmarshal(tbsData, &tbs) + if err != nil { + return nil, fmt.Errorf("failed to parse TBSCertificate: %v", err) + } else if rLen := len(rest); rLen > 0 { + return nil, fmt.Errorf("trailing data (%d bytes) after TBSCertificate", rLen) + } + extAt := -1 + for i, ext := range tbs.Extensions { + if ext.Id.Equal(oid) { + if extAt != -1 { + return nil, errors.New("multiple extensions of specified type present") + } + extAt = i + } + } + if extAt == -1 { + return nil, errors.New("no extension of specified type present") + } + tbs.Extensions = append(tbs.Extensions[:extAt], tbs.Extensions[extAt+1:]...) + // Clear out the asn1.RawContent so the re-marshal operation sees the + // updated structure (rather than just copying the out-of-date DER data). + tbs.Raw = nil + + data, err := asn1.Marshal(tbs) + if err != nil { + return nil, fmt.Errorf("failed to re-marshal TBSCertificate: %v", err) + } + return data, nil +} + +// RemoveSCTList takes a DER-encoded TBSCertificate and removes the CT SCT +// extension that contains the SCT list (preserving the order of other +// extensions), and returns the result still as a DER-encoded TBSCertificate. +// This function will fail if there is not exactly 1 CT SCT extension present. +func RemoveSCTList(tbsData []byte) ([]byte, error) { + return removeExtension(tbsData, OIDExtensionCTSCT) +} + +// RemoveCTPoison takes a DER-encoded TBSCertificate and removes the CT poison +// extension (preserving the order of other extensions), and returns the result +// still as a DER-encoded TBSCertificate. This function will fail if there is +// not exactly 1 CT poison extension present. +func RemoveCTPoison(tbsData []byte) ([]byte, error) { + return BuildPrecertTBS(tbsData, nil) +} + +// BuildPrecertTBS builds a Certificate Transparency pre-certificate (RFC 6962 +// s3.1) from the given DER-encoded TBSCertificate, returning a DER-encoded +// TBSCertificate. +// +// This function removes the CT poison extension (there must be exactly 1 of +// these), preserving the order of other extensions. +// +// If preIssuer is provided, this should be a special intermediate certificate +// that was used to sign the precert (indicated by having the special +// CertificateTransparency extended key usage). In this case, the issuance +// information of the pre-cert is updated to reflect the next issuer in the +// chain, i.e. the issuer of this special intermediate: +// - The precert's Issuer is changed to the Issuer of the intermediate +// - The precert's AuthorityKeyId is changed to the AuthorityKeyId of the +// intermediate. +func BuildPrecertTBS(tbsData []byte, preIssuer *Certificate) ([]byte, error) { + data, err := removeExtension(tbsData, OIDExtensionCTPoison) + if err != nil { + return nil, err + } + + var tbs tbsCertificate + rest, err := asn1.Unmarshal(data, &tbs) + if err != nil { + return nil, fmt.Errorf("failed to parse TBSCertificate: %v", err) + } else if rLen := len(rest); rLen > 0 { + return nil, fmt.Errorf("trailing data (%d bytes) after TBSCertificate", rLen) + } + + if preIssuer != nil { + // Update the precert's Issuer field. Use the RawIssuer rather than the + // parsed Issuer to avoid any chance of ASN.1 differences (e.g. switching + // from UTF8String to PrintableString). + tbs.Issuer.FullBytes = preIssuer.RawIssuer + + // Also need to update the cert's AuthorityKeyID extension + // to that of the preIssuer. + var issuerKeyID []byte + for _, ext := range preIssuer.Extensions { + if ext.Id.Equal(OIDExtensionAuthorityKeyId) { + issuerKeyID = ext.Value + break + } + } + + // Check the preIssuer has the CT EKU. + seenCTEKU := false + for _, eku := range preIssuer.ExtKeyUsage { + if eku == ExtKeyUsageCertificateTransparency { + seenCTEKU = true + break + } + } + if !seenCTEKU { + return nil, fmt.Errorf("issuer does not have CertificateTransparency extended key usage") + } + + keyAt := -1 + for i, ext := range tbs.Extensions { + if ext.Id.Equal(OIDExtensionAuthorityKeyId) { + keyAt = i + break + } + } + if keyAt >= 0 { + // PreCert has an auth-key-id; replace it with the value from the preIssuer + if issuerKeyID != nil { + tbs.Extensions[keyAt].Value = issuerKeyID + } else { + tbs.Extensions = append(tbs.Extensions[:keyAt], tbs.Extensions[keyAt+1:]...) + } + } else if issuerKeyID != nil { + // PreCert did not have an auth-key-id, but the preIssuer does, so add it at the end. + authKeyIDExt := pkix.Extension{ + Id: OIDExtensionAuthorityKeyId, + Critical: false, + Value: issuerKeyID, + } + tbs.Extensions = append(tbs.Extensions, authKeyIDExt) + } + + // Clear out the asn1.RawContent so the re-marshal operation sees the + // updated structure (rather than just copying the out-of-date DER data). + tbs.Raw = nil + } + + data, err = asn1.Marshal(tbs) + if err != nil { + return nil, fmt.Errorf("failed to re-marshal TBSCertificate: %v", err) + } + return data, nil +} + +type basicConstraints struct { + IsCA bool `asn1:"optional"` + MaxPathLen int `asn1:"optional,default:-1"` +} + +// RFC 5280, 4.2.1.4 +type policyInformation struct { + Policy asn1.ObjectIdentifier + // policyQualifiers omitted +} + +const ( + nameTypeEmail = 1 + nameTypeDNS = 2 + nameTypeURI = 6 + nameTypeIP = 7 +) + +// RFC 5280, 4.2.2.1 +type authorityInfoAccess struct { + Method asn1.ObjectIdentifier + Location asn1.RawValue +} + +// RFC 5280, 4.2.1.14 +type distributionPoint struct { + DistributionPoint distributionPointName `asn1:"optional,tag:0"` + Reason asn1.BitString `asn1:"optional,tag:1"` + CRLIssuer asn1.RawValue `asn1:"optional,tag:2"` +} + +type distributionPointName struct { + FullName []asn1.RawValue `asn1:"optional,tag:0"` + RelativeName pkix.RDNSequence `asn1:"optional,tag:1"` +} + +func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{}, error) { + asn1Data := keyData.PublicKey.RightAlign() + switch algo { + case RSA: + // RSA public keys must have a NULL in the parameters + // (https://tools.ietf.org/html/rfc3279#section-2.3.1). + if !bytes.Equal(keyData.Algorithm.Parameters.FullBytes, asn1.NullBytes) { + return nil, errors.New("x509: RSA key missing NULL parameters") + } + + p := new(pkcs1PublicKey) + rest, err := asn1.Unmarshal(asn1Data, p) + if err != nil { + return nil, err + } + if len(rest) != 0 { + return nil, errors.New("x509: trailing data after RSA public key") + } + + if p.N.Sign() <= 0 { + return nil, errors.New("x509: RSA modulus is not a positive number") + } + if p.E <= 0 { + return nil, errors.New("x509: RSA public exponent is not a positive number") + } + + pub := &rsa.PublicKey{ + E: p.E, + N: p.N, + } + return pub, nil + case DSA: + var p *big.Int + rest, err := asn1.Unmarshal(asn1Data, &p) + if err != nil { + return nil, err + } + if len(rest) != 0 { + return nil, errors.New("x509: trailing data after DSA public key") + } + paramsData := keyData.Algorithm.Parameters.FullBytes + params := new(dsaAlgorithmParameters) + rest, err = asn1.Unmarshal(paramsData, params) + if err != nil { + return nil, err + } + if len(rest) != 0 { + return nil, errors.New("x509: trailing data after DSA parameters") + } + if p.Sign() <= 0 || params.P.Sign() <= 0 || params.Q.Sign() <= 0 || params.G.Sign() <= 0 { + return nil, errors.New("x509: zero or negative DSA parameter") + } + pub := &dsa.PublicKey{ + Parameters: dsa.Parameters{ + P: params.P, + Q: params.Q, + G: params.G, + }, + Y: p, + } + return pub, nil + case ECDSA: + paramsData := keyData.Algorithm.Parameters.FullBytes + namedCurveOID := new(asn1.ObjectIdentifier) + rest, err := asn1.Unmarshal(paramsData, namedCurveOID) + if err != nil { + return nil, err + } + if len(rest) != 0 { + return nil, errors.New("x509: trailing data after ECDSA parameters") + } + namedCurve := namedCurveFromOID(*namedCurveOID) + if namedCurve == nil { + return nil, errors.New("x509: unsupported elliptic curve") + } + x, y := elliptic.Unmarshal(namedCurve, asn1Data) + if x == nil { + return nil, errors.New("x509: failed to unmarshal elliptic curve point") + } + pub := &ecdsa.PublicKey{ + Curve: namedCurve, + X: x, + Y: y, + } + return pub, nil + default: + return nil, nil + } +} + +// NonFatalErrors is an error type which can hold a number of other errors. +// It's used to collect a range of non-fatal errors which occur while parsing +// a certificate, that way we can still match on certs which technically are +// invalid. +type NonFatalErrors struct { + Errors []error +} + +// AddError adds an error to the list of errors contained by NonFatalErrors. +func (e *NonFatalErrors) AddError(err error) { + e.Errors = append(e.Errors, err) +} + +// Returns a string consisting of the values of Error() from all of the errors +// contained in |e| +func (e NonFatalErrors) Error() string { + r := "NonFatalErrors: " + for _, err := range e.Errors { + r += err.Error() + "; " + } + return r +} + +// HasError returns true if |e| contains at least one error +func (e *NonFatalErrors) HasError() bool { + return len(e.Errors) > 0 +} + +func parseDistributionPoints(data []byte, crldp *[]string) error { + // CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint + // + // DistributionPoint ::= SEQUENCE { + // distributionPoint [0] DistributionPointName OPTIONAL, + // reasons [1] ReasonFlags OPTIONAL, + // cRLIssuer [2] GeneralNames OPTIONAL } + // + // DistributionPointName ::= CHOICE { + // fullName [0] GeneralNames, + // nameRelativeToCRLIssuer [1] RelativeDistinguishedName } + + var cdp []distributionPoint + if rest, err := asn1.Unmarshal(data, &cdp); err != nil { + return err + } else if len(rest) != 0 { + return errors.New("x509: trailing data after X.509 CRL distribution point") + } + + for _, dp := range cdp { + // Per RFC 5280, 4.2.1.13, one of distributionPoint or cRLIssuer may be empty. + if len(dp.DistributionPoint.FullName) == 0 { + continue + } + + for _, fullName := range dp.DistributionPoint.FullName { + if fullName.Tag == 6 { + *crldp = append(*crldp, string(fullName.Bytes)) + } + } + } + return nil +} + +func forEachSAN(extension []byte, callback func(tag int, data []byte) error) error { + // RFC 5280, 4.2.1.6 + + // SubjectAltName ::= GeneralNames + // + // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + // + // GeneralName ::= CHOICE { + // otherName [0] OtherName, + // rfc822Name [1] IA5String, + // dNSName [2] IA5String, + // x400Address [3] ORAddress, + // directoryName [4] Name, + // ediPartyName [5] EDIPartyName, + // uniformResourceIdentifier [6] IA5String, + // iPAddress [7] OCTET STRING, + // registeredID [8] OBJECT IDENTIFIER } + var seq asn1.RawValue + rest, err := asn1.Unmarshal(extension, &seq) + if err != nil { + return err + } else if len(rest) != 0 { + return errors.New("x509: trailing data after X.509 extension") + } + if !seq.IsCompound || seq.Tag != asn1.TagSequence || seq.Class != asn1.ClassUniversal { + return asn1.StructuralError{Msg: "bad SAN sequence"} + } + + rest = seq.Bytes + for len(rest) > 0 { + var v asn1.RawValue + rest, err = asn1.Unmarshal(rest, &v) + if err != nil { + return err + } + + if err := callback(v.Tag, v.Bytes); err != nil { + return err + } + } + + return nil +} + +func parseSANExtension(value []byte, nfe *NonFatalErrors) (dnsNames, emailAddresses []string, ipAddresses []net.IP, uris []*url.URL, err error) { + err = forEachSAN(value, func(tag int, data []byte) error { + switch tag { + case nameTypeEmail: + emailAddresses = append(emailAddresses, string(data)) + case nameTypeDNS: + dnsNames = append(dnsNames, string(data)) + case nameTypeURI: + uri, err := url.Parse(string(data)) + if err != nil { + return fmt.Errorf("x509: cannot parse URI %q: %s", string(data), err) + } + if len(uri.Host) > 0 { + if _, ok := domainToReverseLabels(uri.Host); !ok { + return fmt.Errorf("x509: cannot parse URI %q: invalid domain", string(data)) + } + } + uris = append(uris, uri) + case nameTypeIP: + switch len(data) { + case net.IPv4len, net.IPv6len: + ipAddresses = append(ipAddresses, data) + default: + nfe.AddError(errors.New("x509: cannot parse IP address of length " + strconv.Itoa(len(data)))) + } + } + + return nil + }) + + return +} + +// isValidIPMask returns true iff mask consists of zero or more 1 bits, followed by zero bits. +func isValidIPMask(mask []byte) bool { + seenZero := false + + for _, b := range mask { + if seenZero { + if b != 0 { + return false + } + + continue + } + + switch b { + case 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe: + seenZero = true + case 0xff: + default: + return false + } + } + + return true +} + +func parseNameConstraintsExtension(out *Certificate, e pkix.Extension, nfe *NonFatalErrors) (unhandled bool, err error) { + // RFC 5280, 4.2.1.10 + + // NameConstraints ::= SEQUENCE { + // permittedSubtrees [0] GeneralSubtrees OPTIONAL, + // excludedSubtrees [1] GeneralSubtrees OPTIONAL } + // + // GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree + // + // GeneralSubtree ::= SEQUENCE { + // base GeneralName, + // minimum [0] BaseDistance DEFAULT 0, + // maximum [1] BaseDistance OPTIONAL } + // + // BaseDistance ::= INTEGER (0..MAX) + + outer := cryptobyte.String(e.Value) + var toplevel, permitted, excluded cryptobyte.String + var havePermitted, haveExcluded bool + if !outer.ReadASN1(&toplevel, cryptobyte_asn1.SEQUENCE) || + !outer.Empty() || + !toplevel.ReadOptionalASN1(&permitted, &havePermitted, cryptobyte_asn1.Tag(0).ContextSpecific().Constructed()) || + !toplevel.ReadOptionalASN1(&excluded, &haveExcluded, cryptobyte_asn1.Tag(1).ContextSpecific().Constructed()) || + !toplevel.Empty() { + return false, errors.New("x509: invalid NameConstraints extension") + } + + if !havePermitted && !haveExcluded || len(permitted) == 0 && len(excluded) == 0 { + // https://tools.ietf.org/html/rfc5280#section-4.2.1.10: + // “either the permittedSubtrees field + // or the excludedSubtrees MUST be + // present” + return false, errors.New("x509: empty name constraints extension") + } + + getValues := func(subtrees cryptobyte.String) (dnsNames []string, ips []*net.IPNet, emails, uriDomains []string, err error) { + for !subtrees.Empty() { + var seq, value cryptobyte.String + var tag cryptobyte_asn1.Tag + if !subtrees.ReadASN1(&seq, cryptobyte_asn1.SEQUENCE) || + !seq.ReadAnyASN1(&value, &tag) { + return nil, nil, nil, nil, fmt.Errorf("x509: invalid NameConstraints extension") + } + + var ( + dnsTag = cryptobyte_asn1.Tag(2).ContextSpecific() + emailTag = cryptobyte_asn1.Tag(1).ContextSpecific() + ipTag = cryptobyte_asn1.Tag(7).ContextSpecific() + uriTag = cryptobyte_asn1.Tag(6).ContextSpecific() + ) + + switch tag { + case dnsTag: + domain := string(value) + if err := isIA5String(domain); err != nil { + return nil, nil, nil, nil, errors.New("x509: invalid constraint value: " + err.Error()) + } + + trimmedDomain := domain + if len(trimmedDomain) > 0 && trimmedDomain[0] == '.' { + // constraints can have a leading + // period to exclude the domain + // itself, but that's not valid in a + // normal domain name. + trimmedDomain = trimmedDomain[1:] + } + if _, ok := domainToReverseLabels(trimmedDomain); !ok { + nfe.AddError(fmt.Errorf("x509: failed to parse dnsName constraint %q", domain)) + } + dnsNames = append(dnsNames, domain) + + case ipTag: + l := len(value) + var ip, mask []byte + + switch l { + case 8: + ip = value[:4] + mask = value[4:] + + case 32: + ip = value[:16] + mask = value[16:] + + default: + return nil, nil, nil, nil, fmt.Errorf("x509: IP constraint contained value of length %d", l) + } + + if !isValidIPMask(mask) { + return nil, nil, nil, nil, fmt.Errorf("x509: IP constraint contained invalid mask %x", mask) + } + + ips = append(ips, &net.IPNet{IP: net.IP(ip), Mask: net.IPMask(mask)}) + + case emailTag: + constraint := string(value) + if err := isIA5String(constraint); err != nil { + return nil, nil, nil, nil, errors.New("x509: invalid constraint value: " + err.Error()) + } + + // If the constraint contains an @ then + // it specifies an exact mailbox name. + if strings.Contains(constraint, "@") { + if _, ok := parseRFC2821Mailbox(constraint); !ok { + nfe.AddError(fmt.Errorf("x509: failed to parse rfc822Name constraint %q", constraint)) + } + } else { + // Otherwise it's a domain name. + domain := constraint + if len(domain) > 0 && domain[0] == '.' { + domain = domain[1:] + } + if _, ok := domainToReverseLabels(domain); !ok { + nfe.AddError(fmt.Errorf("x509: failed to parse rfc822Name constraint %q", constraint)) + } + } + emails = append(emails, constraint) + + case uriTag: + domain := string(value) + if err := isIA5String(domain); err != nil { + return nil, nil, nil, nil, errors.New("x509: invalid constraint value: " + err.Error()) + } + + if net.ParseIP(domain) != nil { + return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse URI constraint %q: cannot be IP address", domain) + } + + trimmedDomain := domain + if len(trimmedDomain) > 0 && trimmedDomain[0] == '.' { + // constraints can have a leading + // period to exclude the domain itself, + // but that's not valid in a normal + // domain name. + trimmedDomain = trimmedDomain[1:] + } + if _, ok := domainToReverseLabels(trimmedDomain); !ok { + nfe.AddError(fmt.Errorf("x509: failed to parse URI constraint %q", domain)) + } + uriDomains = append(uriDomains, domain) + + default: + unhandled = true + } + } + + return dnsNames, ips, emails, uriDomains, nil + } + + if out.PermittedDNSDomains, out.PermittedIPRanges, out.PermittedEmailAddresses, out.PermittedURIDomains, err = getValues(permitted); err != nil { + return false, err + } + if out.ExcludedDNSDomains, out.ExcludedIPRanges, out.ExcludedEmailAddresses, out.ExcludedURIDomains, err = getValues(excluded); err != nil { + return false, err + } + out.PermittedDNSDomainsCritical = e.Critical + + return unhandled, nil +} + +func parseCertificate(in *certificate) (*Certificate, error) { + var nfe NonFatalErrors + + out := new(Certificate) + out.Raw = in.Raw + out.RawTBSCertificate = in.TBSCertificate.Raw + out.RawSubjectPublicKeyInfo = in.TBSCertificate.PublicKey.Raw + out.RawSubject = in.TBSCertificate.Subject.FullBytes + out.RawIssuer = in.TBSCertificate.Issuer.FullBytes + + out.Signature = in.SignatureValue.RightAlign() + out.SignatureAlgorithm = SignatureAlgorithmFromAI(in.TBSCertificate.SignatureAlgorithm) + + out.PublicKeyAlgorithm = + getPublicKeyAlgorithmFromOID(in.TBSCertificate.PublicKey.Algorithm.Algorithm) + var err error + out.PublicKey, err = parsePublicKey(out.PublicKeyAlgorithm, &in.TBSCertificate.PublicKey) + if err != nil { + return nil, err + } + + out.Version = in.TBSCertificate.Version + 1 + out.SerialNumber = in.TBSCertificate.SerialNumber + + var issuer, subject pkix.RDNSequence + if rest, err := asn1.Unmarshal(in.TBSCertificate.Subject.FullBytes, &subject); err != nil { + return nil, err + } else if len(rest) != 0 { + return nil, errors.New("x509: trailing data after X.509 subject") + } + if rest, err := asn1.Unmarshal(in.TBSCertificate.Issuer.FullBytes, &issuer); err != nil { + return nil, err + } else if len(rest) != 0 { + return nil, errors.New("x509: trailing data after X.509 subject") + } + + out.Issuer.FillFromRDNSequence(&issuer) + out.Subject.FillFromRDNSequence(&subject) + + out.NotBefore = in.TBSCertificate.Validity.NotBefore + out.NotAfter = in.TBSCertificate.Validity.NotAfter + + for _, e := range in.TBSCertificate.Extensions { + out.Extensions = append(out.Extensions, e) + unhandled := false + + if len(e.Id) == 4 && e.Id[0] == OIDExtensionArc[0] && e.Id[1] == OIDExtensionArc[1] && e.Id[2] == OIDExtensionArc[2] { + switch e.Id[3] { + case OIDExtensionKeyUsage[3]: + // RFC 5280, 4.2.1.3 + var usageBits asn1.BitString + if rest, err := asn1.Unmarshal(e.Value, &usageBits); err != nil { + return nil, err + } else if len(rest) != 0 { + return nil, errors.New("x509: trailing data after X.509 KeyUsage") + } + + var usage int + for i := 0; i < 9; i++ { + if usageBits.At(i) != 0 { + usage |= 1 << uint(i) + } + } + out.KeyUsage = KeyUsage(usage) + + case OIDExtensionBasicConstraints[3]: + // RFC 5280, 4.2.1.9 + var constraints basicConstraints + if rest, err := asn1.Unmarshal(e.Value, &constraints); err != nil { + return nil, err + } else if len(rest) != 0 { + return nil, errors.New("x509: trailing data after X.509 BasicConstraints") + } + + out.BasicConstraintsValid = true + out.IsCA = constraints.IsCA + out.MaxPathLen = constraints.MaxPathLen + out.MaxPathLenZero = out.MaxPathLen == 0 + // TODO: map out.MaxPathLen to 0 if it has the -1 default value? (Issue 19285) + + case OIDExtensionSubjectAltName[3]: + out.DNSNames, out.EmailAddresses, out.IPAddresses, out.URIs, err = parseSANExtension(e.Value, &nfe) + if err != nil { + return nil, err + } + + if len(out.DNSNames) == 0 && len(out.EmailAddresses) == 0 && len(out.IPAddresses) == 0 && len(out.URIs) == 0 { + // If we didn't parse anything then we do the critical check, below. + unhandled = true + } + + case OIDExtensionNameConstraints[3]: + unhandled, err = parseNameConstraintsExtension(out, e, &nfe) + if err != nil { + return nil, err + } + + case OIDExtensionCRLDistributionPoints[3]: + // RFC 5280, 4.2.1.13 + if err := parseDistributionPoints(e.Value, &out.CRLDistributionPoints); err != nil { + return nil, err + } + + case OIDExtensionAuthorityKeyId[3]: + // RFC 5280, 4.2.1.1 + var a authKeyId + if rest, err := asn1.Unmarshal(e.Value, &a); err != nil { + return nil, err + } else if len(rest) != 0 { + return nil, errors.New("x509: trailing data after X.509 authority key-id") + } + out.AuthorityKeyId = a.Id + + case OIDExtensionExtendedKeyUsage[3]: + // RFC 5280, 4.2.1.12. Extended Key Usage + + // id-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-ce 37 } + // + // ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId + // + // KeyPurposeId ::= OBJECT IDENTIFIER + + var keyUsage []asn1.ObjectIdentifier + if rest, err := asn1.Unmarshal(e.Value, &keyUsage); err != nil { + return nil, err + } else if len(rest) != 0 { + return nil, errors.New("x509: trailing data after X.509 ExtendedKeyUsage") + } + + for _, u := range keyUsage { + if extKeyUsage, ok := extKeyUsageFromOID(u); ok { + out.ExtKeyUsage = append(out.ExtKeyUsage, extKeyUsage) + } else { + out.UnknownExtKeyUsage = append(out.UnknownExtKeyUsage, u) + } + } + + case OIDExtensionSubjectKeyId[3]: + // RFC 5280, 4.2.1.2 + var keyid []byte + if rest, err := asn1.Unmarshal(e.Value, &keyid); err != nil { + return nil, err + } else if len(rest) != 0 { + return nil, errors.New("x509: trailing data after X.509 key-id") + } + out.SubjectKeyId = keyid + + case OIDExtensionCertificatePolicies[3]: + // RFC 5280 4.2.1.4: Certificate Policies + var policies []policyInformation + if rest, err := asn1.Unmarshal(e.Value, &policies); err != nil { + return nil, err + } else if len(rest) != 0 { + return nil, errors.New("x509: trailing data after X.509 certificate policies") + } + out.PolicyIdentifiers = make([]asn1.ObjectIdentifier, len(policies)) + for i, policy := range policies { + out.PolicyIdentifiers[i] = policy.Policy + } + + default: + // Unknown extensions are recorded if critical. + unhandled = true + } + } else if e.Id.Equal(OIDExtensionAuthorityInfoAccess) { + // RFC 5280 4.2.2.1: Authority Information Access + var aia []authorityInfoAccess + if rest, err := asn1.Unmarshal(e.Value, &aia); err != nil { + return nil, err + } else if len(rest) != 0 { + return nil, errors.New("x509: trailing data after X.509 authority information") + } + + for _, v := range aia { + // GeneralName: uniformResourceIdentifier [6] IA5String + if v.Location.Tag != 6 { + continue + } + if v.Method.Equal(OIDAuthorityInfoAccessOCSP) { + out.OCSPServer = append(out.OCSPServer, string(v.Location.Bytes)) + } else if v.Method.Equal(OIDAuthorityInfoAccessIssuers) { + out.IssuingCertificateURL = append(out.IssuingCertificateURL, string(v.Location.Bytes)) + } + } + } else if e.Id.Equal(OIDExtensionCTSCT) { + if rest, err := asn1.Unmarshal(e.Value, &out.RawSCT); err != nil { + nfe.AddError(fmt.Errorf("failed to asn1.Unmarshal SCT list extension: %v", err)) + } else if len(rest) != 0 { + nfe.AddError(errors.New("trailing data after ASN1-encoded SCT list")) + } else { + if rest, err := tls.Unmarshal(out.RawSCT, &out.SCTList); err != nil { + nfe.AddError(fmt.Errorf("failed to tls.Unmarshal SCT list: %v", err)) + } else if len(rest) != 0 { + nfe.AddError(errors.New("trailing data after TLS-encoded SCT list")) + } + } + } else { + // Unknown extensions are recorded if critical. + unhandled = true + } + + if e.Critical && unhandled { + out.UnhandledCriticalExtensions = append(out.UnhandledCriticalExtensions, e.Id) + } + } + if nfe.HasError() { + return out, nfe + } + return out, nil +} + +// ParseTBSCertificate parses a single TBSCertificate from the given ASN.1 DER data. +// The parsed data is returned in a Certificate struct for ease of access. +func ParseTBSCertificate(asn1Data []byte) (*Certificate, error) { + var tbsCert tbsCertificate + rest, err := asn1.Unmarshal(asn1Data, &tbsCert) + if err != nil { + return nil, err + } + if len(rest) > 0 { + return nil, asn1.SyntaxError{Msg: "trailing data"} + } + return parseCertificate(&certificate{ + Raw: tbsCert.Raw, + TBSCertificate: tbsCert}) +} + +// ParseCertificate parses a single certificate from the given ASN.1 DER data. +// This function can return both a Certificate and an error (in which case the +// error will be of type NonFatalErrors). +func ParseCertificate(asn1Data []byte) (*Certificate, error) { + var cert certificate + rest, err := asn1.Unmarshal(asn1Data, &cert) + if err != nil { + return nil, err + } + if len(rest) > 0 { + return nil, asn1.SyntaxError{Msg: "trailing data"} + } + + return parseCertificate(&cert) +} + +// ParseCertificates parses one or more certificates from the given ASN.1 DER +// data. The certificates must be concatenated with no intermediate padding. +// This function can return both a slice of Certificate and an error (in which +// case the error will be of type NonFatalErrors). +func ParseCertificates(asn1Data []byte) ([]*Certificate, error) { + var v []*certificate + + for len(asn1Data) > 0 { + cert := new(certificate) + var err error + asn1Data, err = asn1.Unmarshal(asn1Data, cert) + if err != nil { + return nil, err + } + v = append(v, cert) + } + + var nfe NonFatalErrors + ret := make([]*Certificate, len(v)) + for i, ci := range v { + cert, err := parseCertificate(ci) + if err != nil { + if errs, ok := err.(NonFatalErrors); !ok { + return nil, err + } else { + nfe.Errors = append(nfe.Errors, errs.Errors...) + } + } + ret[i] = cert + } + + if nfe.HasError() { + return ret, nfe + } + return ret, nil +} + +func reverseBitsInAByte(in byte) byte { + b1 := in>>4 | in<<4 + b2 := b1>>2&0x33 | b1<<2&0xcc + b3 := b2>>1&0x55 | b2<<1&0xaa + return b3 +} + +// asn1BitLength returns the bit-length of bitString by considering the +// most-significant bit in a byte to be the "first" bit. This convention +// matches ASN.1, but differs from almost everything else. +func asn1BitLength(bitString []byte) int { + bitLen := len(bitString) * 8 + + for i := range bitString { + b := bitString[len(bitString)-i-1] + + for bit := uint(0); bit < 8; bit++ { + if (b>>bit)&1 == 1 { + return bitLen + } + bitLen-- + } + } + + return 0 +} + +// OID values for standard extensions from RFC 5280. +var ( + OIDExtensionArc = asn1.ObjectIdentifier{2, 5, 29} // id-ce RFC5280 s4.2.1 + OIDExtensionSubjectKeyId = asn1.ObjectIdentifier{2, 5, 29, 14} + OIDExtensionKeyUsage = asn1.ObjectIdentifier{2, 5, 29, 15} + OIDExtensionExtendedKeyUsage = asn1.ObjectIdentifier{2, 5, 29, 37} + OIDExtensionAuthorityKeyId = asn1.ObjectIdentifier{2, 5, 29, 35} + OIDExtensionBasicConstraints = asn1.ObjectIdentifier{2, 5, 29, 19} + OIDExtensionSubjectAltName = asn1.ObjectIdentifier{2, 5, 29, 17} + OIDExtensionCertificatePolicies = asn1.ObjectIdentifier{2, 5, 29, 32} + OIDExtensionNameConstraints = asn1.ObjectIdentifier{2, 5, 29, 30} + OIDExtensionCRLDistributionPoints = asn1.ObjectIdentifier{2, 5, 29, 31} + OIDExtensionIssuerAltName = asn1.ObjectIdentifier{2, 5, 29, 18} + OIDExtensionSubjectDirectoryAttributes = asn1.ObjectIdentifier{2, 5, 29, 9} + OIDExtensionInhibitAnyPolicy = asn1.ObjectIdentifier{2, 5, 29, 54} + OIDExtensionPolicyConstraints = asn1.ObjectIdentifier{2, 5, 29, 36} + OIDExtensionPolicyMappings = asn1.ObjectIdentifier{2, 5, 29, 33} + OIDExtensionFreshestCRL = asn1.ObjectIdentifier{2, 5, 29, 46} + + OIDExtensionAuthorityInfoAccess = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 1} + OIDExtensionSubjectInfoAccess = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 11} + // OIDExtensionCTPoison is defined in RFC 6962 s3.1. + OIDExtensionCTPoison = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 3} + // OIDExtensionCTSCT is defined in RFC 6962 s3.3. + OIDExtensionCTSCT = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 2} +) + +var ( + OIDAuthorityInfoAccessOCSP = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 1} + OIDAuthorityInfoAccessIssuers = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 2} +) + +// oidInExtensions returns whether an extension with the given oid exists in +// extensions. +func oidInExtensions(oid asn1.ObjectIdentifier, extensions []pkix.Extension) bool { + for _, e := range extensions { + if e.Id.Equal(oid) { + return true + } + } + return false +} + +// marshalSANs marshals a list of addresses into a the contents of an X.509 +// SubjectAlternativeName extension. +func marshalSANs(dnsNames, emailAddresses []string, ipAddresses []net.IP, uris []*url.URL) (derBytes []byte, err error) { + var rawValues []asn1.RawValue + for _, name := range dnsNames { + rawValues = append(rawValues, asn1.RawValue{Tag: nameTypeDNS, Class: asn1.ClassContextSpecific, Bytes: []byte(name)}) + } + for _, email := range emailAddresses { + rawValues = append(rawValues, asn1.RawValue{Tag: nameTypeEmail, Class: asn1.ClassContextSpecific, Bytes: []byte(email)}) + } + for _, rawIP := range ipAddresses { + // If possible, we always want to encode IPv4 addresses in 4 bytes. + ip := rawIP.To4() + if ip == nil { + ip = rawIP + } + rawValues = append(rawValues, asn1.RawValue{Tag: nameTypeIP, Class: asn1.ClassContextSpecific, Bytes: ip}) + } + for _, uri := range uris { + rawValues = append(rawValues, asn1.RawValue{Tag: nameTypeURI, Class: asn1.ClassContextSpecific, Bytes: []byte(uri.String())}) + } + return asn1.Marshal(rawValues) +} + +func isIA5String(s string) error { + for _, r := range s { + if r >= utf8.RuneSelf { + return fmt.Errorf("x509: %q cannot be encoded as an IA5String", s) + } + } + + return nil +} + +func buildExtensions(template *Certificate, subjectIsEmpty bool, authorityKeyId []byte) (ret []pkix.Extension, err error) { + ret = make([]pkix.Extension, 11 /* maximum number of elements. */) + n := 0 + + if template.KeyUsage != 0 && + !oidInExtensions(OIDExtensionKeyUsage, template.ExtraExtensions) { + ret[n].Id = OIDExtensionKeyUsage + ret[n].Critical = true + + var a [2]byte + a[0] = reverseBitsInAByte(byte(template.KeyUsage)) + a[1] = reverseBitsInAByte(byte(template.KeyUsage >> 8)) + + l := 1 + if a[1] != 0 { + l = 2 + } + + bitString := a[:l] + ret[n].Value, err = asn1.Marshal(asn1.BitString{Bytes: bitString, BitLength: asn1BitLength(bitString)}) + if err != nil { + return + } + n++ + } + + if (len(template.ExtKeyUsage) > 0 || len(template.UnknownExtKeyUsage) > 0) && + !oidInExtensions(OIDExtensionExtendedKeyUsage, template.ExtraExtensions) { + ret[n].Id = OIDExtensionExtendedKeyUsage + + var oids []asn1.ObjectIdentifier + for _, u := range template.ExtKeyUsage { + if oid, ok := oidFromExtKeyUsage(u); ok { + oids = append(oids, oid) + } else { + panic("internal error") + } + } + + oids = append(oids, template.UnknownExtKeyUsage...) + + ret[n].Value, err = asn1.Marshal(oids) + if err != nil { + return + } + n++ + } + + if template.BasicConstraintsValid && !oidInExtensions(OIDExtensionBasicConstraints, template.ExtraExtensions) { + // Leaving MaxPathLen as zero indicates that no maximum path + // length is desired, unless MaxPathLenZero is set. A value of + // -1 causes encoding/asn1 to omit the value as desired. + maxPathLen := template.MaxPathLen + if maxPathLen == 0 && !template.MaxPathLenZero { + maxPathLen = -1 + } + ret[n].Id = OIDExtensionBasicConstraints + ret[n].Value, err = asn1.Marshal(basicConstraints{template.IsCA, maxPathLen}) + ret[n].Critical = true + if err != nil { + return + } + n++ + } + + if len(template.SubjectKeyId) > 0 && !oidInExtensions(OIDExtensionSubjectKeyId, template.ExtraExtensions) { + ret[n].Id = OIDExtensionSubjectKeyId + ret[n].Value, err = asn1.Marshal(template.SubjectKeyId) + if err != nil { + return + } + n++ + } + + if len(authorityKeyId) > 0 && !oidInExtensions(OIDExtensionAuthorityKeyId, template.ExtraExtensions) { + ret[n].Id = OIDExtensionAuthorityKeyId + ret[n].Value, err = asn1.Marshal(authKeyId{authorityKeyId}) + if err != nil { + return + } + n++ + } + + if (len(template.OCSPServer) > 0 || len(template.IssuingCertificateURL) > 0) && + !oidInExtensions(OIDExtensionAuthorityInfoAccess, template.ExtraExtensions) { + ret[n].Id = OIDExtensionAuthorityInfoAccess + var aiaValues []authorityInfoAccess + for _, name := range template.OCSPServer { + aiaValues = append(aiaValues, authorityInfoAccess{ + Method: OIDAuthorityInfoAccessOCSP, + Location: asn1.RawValue{Tag: 6, Class: asn1.ClassContextSpecific, Bytes: []byte(name)}, + }) + } + for _, name := range template.IssuingCertificateURL { + aiaValues = append(aiaValues, authorityInfoAccess{ + Method: OIDAuthorityInfoAccessIssuers, + Location: asn1.RawValue{Tag: 6, Class: asn1.ClassContextSpecific, Bytes: []byte(name)}, + }) + } + ret[n].Value, err = asn1.Marshal(aiaValues) + if err != nil { + return + } + n++ + } + + if (len(template.DNSNames) > 0 || len(template.EmailAddresses) > 0 || len(template.IPAddresses) > 0 || len(template.URIs) > 0) && + !oidInExtensions(OIDExtensionSubjectAltName, template.ExtraExtensions) { + ret[n].Id = OIDExtensionSubjectAltName + // https://tools.ietf.org/html/rfc5280#section-4.2.1.6 + // “If the subject field contains an empty sequence ... then + // subjectAltName extension ... is marked as critical” + ret[n].Critical = subjectIsEmpty + ret[n].Value, err = marshalSANs(template.DNSNames, template.EmailAddresses, template.IPAddresses, template.URIs) + if err != nil { + return + } + n++ + } + + if len(template.PolicyIdentifiers) > 0 && + !oidInExtensions(OIDExtensionCertificatePolicies, template.ExtraExtensions) { + ret[n].Id = OIDExtensionCertificatePolicies + policies := make([]policyInformation, len(template.PolicyIdentifiers)) + for i, policy := range template.PolicyIdentifiers { + policies[i].Policy = policy + } + ret[n].Value, err = asn1.Marshal(policies) + if err != nil { + return + } + n++ + } + + if (len(template.PermittedDNSDomains) > 0 || len(template.ExcludedDNSDomains) > 0 || + len(template.PermittedIPRanges) > 0 || len(template.ExcludedIPRanges) > 0 || + len(template.PermittedEmailAddresses) > 0 || len(template.ExcludedEmailAddresses) > 0 || + len(template.PermittedURIDomains) > 0 || len(template.ExcludedURIDomains) > 0) && + !oidInExtensions(OIDExtensionNameConstraints, template.ExtraExtensions) { + ret[n].Id = OIDExtensionNameConstraints + ret[n].Critical = template.PermittedDNSDomainsCritical + + ipAndMask := func(ipNet *net.IPNet) []byte { + maskedIP := ipNet.IP.Mask(ipNet.Mask) + ipAndMask := make([]byte, 0, len(maskedIP)+len(ipNet.Mask)) + ipAndMask = append(ipAndMask, maskedIP...) + ipAndMask = append(ipAndMask, ipNet.Mask...) + return ipAndMask + } + + serialiseConstraints := func(dns []string, ips []*net.IPNet, emails []string, uriDomains []string) (der []byte, err error) { + var b cryptobyte.Builder + + for _, name := range dns { + if err = isIA5String(name); err != nil { + return nil, err + } + + b.AddASN1(cryptobyte_asn1.SEQUENCE, func(b *cryptobyte.Builder) { + b.AddASN1(cryptobyte_asn1.Tag(2).ContextSpecific(), func(b *cryptobyte.Builder) { + b.AddBytes([]byte(name)) + }) + }) + } + + for _, ipNet := range ips { + b.AddASN1(cryptobyte_asn1.SEQUENCE, func(b *cryptobyte.Builder) { + b.AddASN1(cryptobyte_asn1.Tag(7).ContextSpecific(), func(b *cryptobyte.Builder) { + b.AddBytes(ipAndMask(ipNet)) + }) + }) + } + + for _, email := range emails { + if err = isIA5String(email); err != nil { + return nil, err + } + + b.AddASN1(cryptobyte_asn1.SEQUENCE, func(b *cryptobyte.Builder) { + b.AddASN1(cryptobyte_asn1.Tag(1).ContextSpecific(), func(b *cryptobyte.Builder) { + b.AddBytes([]byte(email)) + }) + }) + } + + for _, uriDomain := range uriDomains { + if err = isIA5String(uriDomain); err != nil { + return nil, err + } + + b.AddASN1(cryptobyte_asn1.SEQUENCE, func(b *cryptobyte.Builder) { + b.AddASN1(cryptobyte_asn1.Tag(6).ContextSpecific(), func(b *cryptobyte.Builder) { + b.AddBytes([]byte(uriDomain)) + }) + }) + } + + return b.Bytes() + } + + permitted, err := serialiseConstraints(template.PermittedDNSDomains, template.PermittedIPRanges, template.PermittedEmailAddresses, template.PermittedURIDomains) + if err != nil { + return nil, err + } + + excluded, err := serialiseConstraints(template.ExcludedDNSDomains, template.ExcludedIPRanges, template.ExcludedEmailAddresses, template.ExcludedURIDomains) + if err != nil { + return nil, err + } + + var b cryptobyte.Builder + b.AddASN1(cryptobyte_asn1.SEQUENCE, func(b *cryptobyte.Builder) { + if len(permitted) > 0 { + b.AddASN1(cryptobyte_asn1.Tag(0).ContextSpecific().Constructed(), func(b *cryptobyte.Builder) { + b.AddBytes(permitted) + }) + } + + if len(excluded) > 0 { + b.AddASN1(cryptobyte_asn1.Tag(1).ContextSpecific().Constructed(), func(b *cryptobyte.Builder) { + b.AddBytes(excluded) + }) + } + }) + + ret[n].Value, err = b.Bytes() + if err != nil { + return nil, err + } + n++ + } + + if len(template.CRLDistributionPoints) > 0 && + !oidInExtensions(OIDExtensionCRLDistributionPoints, template.ExtraExtensions) { + ret[n].Id = OIDExtensionCRLDistributionPoints + + var crlDp []distributionPoint + for _, name := range template.CRLDistributionPoints { + dp := distributionPoint{ + DistributionPoint: distributionPointName{ + FullName: []asn1.RawValue{ + asn1.RawValue{Tag: 6, Class: asn1.ClassContextSpecific, Bytes: []byte(name)}, + }, + }, + } + crlDp = append(crlDp, dp) + } + + ret[n].Value, err = asn1.Marshal(crlDp) + if err != nil { + return + } + n++ + } + + if (len(template.RawSCT) > 0 || len(template.SCTList.SCTList) > 0) && !oidInExtensions(OIDExtensionCTSCT, template.ExtraExtensions) { + rawSCT := template.RawSCT + if len(template.SCTList.SCTList) > 0 { + rawSCT, err = tls.Marshal(template.SCTList) + if err != nil { + return + } + } + ret[n].Id = OIDExtensionCTSCT + ret[n].Value, err = asn1.Marshal(rawSCT) + if err != nil { + return + } + n++ + } + + // Adding another extension here? Remember to update the maximum number + // of elements in the make() at the top of the function. + + return append(ret[:n], template.ExtraExtensions...), nil +} + +func subjectBytes(cert *Certificate) ([]byte, error) { + if len(cert.RawSubject) > 0 { + return cert.RawSubject, nil + } + + return asn1.Marshal(cert.Subject.ToRDNSequence()) +} + +// signingParamsForPublicKey returns the parameters to use for signing with +// priv. If requestedSigAlgo is not zero then it overrides the default +// signature algorithm. +func signingParamsForPublicKey(pub interface{}, requestedSigAlgo SignatureAlgorithm) (hashFunc crypto.Hash, sigAlgo pkix.AlgorithmIdentifier, err error) { + var pubType PublicKeyAlgorithm + + switch pub := pub.(type) { + case *rsa.PublicKey: + pubType = RSA + hashFunc = crypto.SHA256 + sigAlgo.Algorithm = oidSignatureSHA256WithRSA + sigAlgo.Parameters = asn1.NullRawValue + + case *ecdsa.PublicKey: + pubType = ECDSA + + switch pub.Curve { + case elliptic.P224(), elliptic.P256(): + hashFunc = crypto.SHA256 + sigAlgo.Algorithm = oidSignatureECDSAWithSHA256 + case elliptic.P384(): + hashFunc = crypto.SHA384 + sigAlgo.Algorithm = oidSignatureECDSAWithSHA384 + case elliptic.P521(): + hashFunc = crypto.SHA512 + sigAlgo.Algorithm = oidSignatureECDSAWithSHA512 + default: + err = errors.New("x509: unknown elliptic curve") + } + + default: + err = errors.New("x509: only RSA and ECDSA keys supported") + } + + if err != nil { + return + } + + if requestedSigAlgo == 0 { + return + } + + found := false + for _, details := range signatureAlgorithmDetails { + if details.algo == requestedSigAlgo { + if details.pubKeyAlgo != pubType { + err = errors.New("x509: requested SignatureAlgorithm does not match private key type") + return + } + sigAlgo.Algorithm, hashFunc = details.oid, details.hash + if hashFunc == 0 { + err = errors.New("x509: cannot sign with hash function requested") + return + } + if requestedSigAlgo.isRSAPSS() { + sigAlgo.Parameters = rsaPSSParameters(hashFunc) + } + found = true + break + } + } + + if !found { + err = errors.New("x509: unknown SignatureAlgorithm") + } + + return +} + +// emptyASN1Subject is the ASN.1 DER encoding of an empty Subject, which is +// just an empty SEQUENCE. +var emptyASN1Subject = []byte{0x30, 0} + +// CreateCertificate creates a new X.509v3 certificate based on a template. +// The following members of template are used: AuthorityKeyId, +// BasicConstraintsValid, DNSNames, ExcludedDNSDomains, ExtKeyUsage, +// IsCA, KeyUsage, MaxPathLen, MaxPathLenZero, NotAfter, NotBefore, +// PermittedDNSDomains, PermittedDNSDomainsCritical, SerialNumber, +// SignatureAlgorithm, Subject, SubjectKeyId, UnknownExtKeyUsage, +// and RawSCT. +// +// The certificate is signed by parent. If parent is equal to template then the +// certificate is self-signed. The parameter pub is the public key of the +// signee and priv is the private key of the signer. +// +// The returned slice is the certificate in DER encoding. +// +// All keys types that are implemented via crypto.Signer are supported (This +// includes *rsa.PublicKey and *ecdsa.PublicKey.) +// +// The AuthorityKeyId will be taken from the SubjectKeyId of parent, if any, +// unless the resulting certificate is self-signed. Otherwise the value from +// template will be used. +func CreateCertificate(rand io.Reader, template, parent *Certificate, pub, priv interface{}) (cert []byte, err error) { + key, ok := priv.(crypto.Signer) + if !ok { + return nil, errors.New("x509: certificate private key does not implement crypto.Signer") + } + + if template.SerialNumber == nil { + return nil, errors.New("x509: no SerialNumber given") + } + + hashFunc, signatureAlgorithm, err := signingParamsForPublicKey(key.Public(), template.SignatureAlgorithm) + if err != nil { + return nil, err + } + + publicKeyBytes, publicKeyAlgorithm, err := marshalPublicKey(pub) + if err != nil { + return nil, err + } + + asn1Issuer, err := subjectBytes(parent) + if err != nil { + return + } + + asn1Subject, err := subjectBytes(template) + if err != nil { + return + } + + authorityKeyId := template.AuthorityKeyId + if !bytes.Equal(asn1Issuer, asn1Subject) && len(parent.SubjectKeyId) > 0 { + authorityKeyId = parent.SubjectKeyId + } + + extensions, err := buildExtensions(template, bytes.Equal(asn1Subject, emptyASN1Subject), authorityKeyId) + if err != nil { + return + } + + encodedPublicKey := asn1.BitString{BitLength: len(publicKeyBytes) * 8, Bytes: publicKeyBytes} + c := tbsCertificate{ + Version: 2, + SerialNumber: template.SerialNumber, + SignatureAlgorithm: signatureAlgorithm, + Issuer: asn1.RawValue{FullBytes: asn1Issuer}, + Validity: validity{template.NotBefore.UTC(), template.NotAfter.UTC()}, + Subject: asn1.RawValue{FullBytes: asn1Subject}, + PublicKey: publicKeyInfo{nil, publicKeyAlgorithm, encodedPublicKey}, + Extensions: extensions, + } + + tbsCertContents, err := asn1.Marshal(c) + if err != nil { + return + } + + c.Raw = tbsCertContents + + h := hashFunc.New() + h.Write(tbsCertContents) + digest := h.Sum(nil) + + var signerOpts crypto.SignerOpts + signerOpts = hashFunc + if template.SignatureAlgorithm != 0 && template.SignatureAlgorithm.isRSAPSS() { + signerOpts = &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthEqualsHash, + Hash: hashFunc, + } + } + + var signature []byte + signature, err = key.Sign(rand, digest, signerOpts) + if err != nil { + return + } + + return asn1.Marshal(certificate{ + nil, + c, + signatureAlgorithm, + asn1.BitString{Bytes: signature, BitLength: len(signature) * 8}, + }) +} + +// pemCRLPrefix is the magic string that indicates that we have a PEM encoded +// CRL. +var pemCRLPrefix = []byte("-----BEGIN X509 CRL") + +// pemType is the type of a PEM encoded CRL. +var pemType = "X509 CRL" + +// ParseCRL parses a CRL from the given bytes. It's often the case that PEM +// encoded CRLs will appear where they should be DER encoded, so this function +// will transparently handle PEM encoding as long as there isn't any leading +// garbage. +func ParseCRL(crlBytes []byte) (*pkix.CertificateList, error) { + if bytes.HasPrefix(crlBytes, pemCRLPrefix) { + block, _ := pem.Decode(crlBytes) + if block != nil && block.Type == pemType { + crlBytes = block.Bytes + } + } + return ParseDERCRL(crlBytes) +} + +// ParseDERCRL parses a DER encoded CRL from the given bytes. +func ParseDERCRL(derBytes []byte) (*pkix.CertificateList, error) { + certList := new(pkix.CertificateList) + if rest, err := asn1.Unmarshal(derBytes, certList); err != nil { + return nil, err + } else if len(rest) != 0 { + return nil, errors.New("x509: trailing data after CRL") + } + return certList, nil +} + +// CreateCRL returns a DER encoded CRL, signed by this Certificate, that +// contains the given list of revoked certificates. +func (c *Certificate) CreateCRL(rand io.Reader, priv interface{}, revokedCerts []pkix.RevokedCertificate, now, expiry time.Time) (crlBytes []byte, err error) { + key, ok := priv.(crypto.Signer) + if !ok { + return nil, errors.New("x509: certificate private key does not implement crypto.Signer") + } + + hashFunc, signatureAlgorithm, err := signingParamsForPublicKey(key.Public(), 0) + if err != nil { + return nil, err + } + + // Force revocation times to UTC per RFC 5280. + revokedCertsUTC := make([]pkix.RevokedCertificate, len(revokedCerts)) + for i, rc := range revokedCerts { + rc.RevocationTime = rc.RevocationTime.UTC() + revokedCertsUTC[i] = rc + } + + tbsCertList := pkix.TBSCertificateList{ + Version: 1, + Signature: signatureAlgorithm, + Issuer: c.Subject.ToRDNSequence(), + ThisUpdate: now.UTC(), + NextUpdate: expiry.UTC(), + RevokedCertificates: revokedCertsUTC, + } + + // Authority Key Id + if len(c.SubjectKeyId) > 0 { + var aki pkix.Extension + aki.Id = OIDExtensionAuthorityKeyId + aki.Value, err = asn1.Marshal(authKeyId{Id: c.SubjectKeyId}) + if err != nil { + return + } + tbsCertList.Extensions = append(tbsCertList.Extensions, aki) + } + + tbsCertListContents, err := asn1.Marshal(tbsCertList) + if err != nil { + return + } + + h := hashFunc.New() + h.Write(tbsCertListContents) + digest := h.Sum(nil) + + var signature []byte + signature, err = key.Sign(rand, digest, hashFunc) + if err != nil { + return + } + + return asn1.Marshal(pkix.CertificateList{ + TBSCertList: tbsCertList, + SignatureAlgorithm: signatureAlgorithm, + SignatureValue: asn1.BitString{Bytes: signature, BitLength: len(signature) * 8}, + }) +} + +// CertificateRequest represents a PKCS #10, certificate signature request. +type CertificateRequest struct { + Raw []byte // Complete ASN.1 DER content (CSR, signature algorithm and signature). + RawTBSCertificateRequest []byte // Certificate request info part of raw ASN.1 DER content. + RawSubjectPublicKeyInfo []byte // DER encoded SubjectPublicKeyInfo. + RawSubject []byte // DER encoded Subject. + + Version int + Signature []byte + SignatureAlgorithm SignatureAlgorithm + + PublicKeyAlgorithm PublicKeyAlgorithm + PublicKey interface{} + + Subject pkix.Name + + // Attributes is the dried husk of a bug and shouldn't be used. + Attributes []pkix.AttributeTypeAndValueSET + + // Extensions contains raw X.509 extensions. When parsing CSRs, this + // can be used to extract extensions that are not parsed by this + // package. + Extensions []pkix.Extension + + // ExtraExtensions contains extensions to be copied, raw, into any + // marshaled CSR. Values override any extensions that would otherwise + // be produced based on the other fields but are overridden by any + // extensions specified in Attributes. + // + // The ExtraExtensions field is not populated when parsing CSRs, see + // Extensions. + ExtraExtensions []pkix.Extension + + // Subject Alternate Name values. + DNSNames []string + EmailAddresses []string + IPAddresses []net.IP + URIs []*url.URL +} + +// These structures reflect the ASN.1 structure of X.509 certificate +// signature requests (see RFC 2986): + +type tbsCertificateRequest struct { + Raw asn1.RawContent + Version int + Subject asn1.RawValue + PublicKey publicKeyInfo + RawAttributes []asn1.RawValue `asn1:"tag:0"` +} + +type certificateRequest struct { + Raw asn1.RawContent + TBSCSR tbsCertificateRequest + SignatureAlgorithm pkix.AlgorithmIdentifier + SignatureValue asn1.BitString +} + +// oidExtensionRequest is a PKCS#9 OBJECT IDENTIFIER that indicates requested +// extensions in a CSR. +var oidExtensionRequest = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 14} + +// newRawAttributes converts AttributeTypeAndValueSETs from a template +// CertificateRequest's Attributes into tbsCertificateRequest RawAttributes. +func newRawAttributes(attributes []pkix.AttributeTypeAndValueSET) ([]asn1.RawValue, error) { + var rawAttributes []asn1.RawValue + b, err := asn1.Marshal(attributes) + if err != nil { + return nil, err + } + rest, err := asn1.Unmarshal(b, &rawAttributes) + if err != nil { + return nil, err + } + if len(rest) != 0 { + return nil, errors.New("x509: failed to unmarshal raw CSR Attributes") + } + return rawAttributes, nil +} + +// parseRawAttributes Unmarshals RawAttributes intos AttributeTypeAndValueSETs. +func parseRawAttributes(rawAttributes []asn1.RawValue) []pkix.AttributeTypeAndValueSET { + var attributes []pkix.AttributeTypeAndValueSET + for _, rawAttr := range rawAttributes { + var attr pkix.AttributeTypeAndValueSET + rest, err := asn1.Unmarshal(rawAttr.FullBytes, &attr) + // Ignore attributes that don't parse into pkix.AttributeTypeAndValueSET + // (i.e.: challengePassword or unstructuredName). + if err == nil && len(rest) == 0 { + attributes = append(attributes, attr) + } + } + return attributes +} + +// parseCSRExtensions parses the attributes from a CSR and extracts any +// requested extensions. +func parseCSRExtensions(rawAttributes []asn1.RawValue) ([]pkix.Extension, error) { + // pkcs10Attribute reflects the Attribute structure from section 4.1 of + // https://tools.ietf.org/html/rfc2986. + type pkcs10Attribute struct { + Id asn1.ObjectIdentifier + Values []asn1.RawValue `asn1:"set"` + } + + var ret []pkix.Extension + for _, rawAttr := range rawAttributes { + var attr pkcs10Attribute + if rest, err := asn1.Unmarshal(rawAttr.FullBytes, &attr); err != nil || len(rest) != 0 || len(attr.Values) == 0 { + // Ignore attributes that don't parse. + continue + } + + if !attr.Id.Equal(oidExtensionRequest) { + continue + } + + var extensions []pkix.Extension + if _, err := asn1.Unmarshal(attr.Values[0].FullBytes, &extensions); err != nil { + return nil, err + } + ret = append(ret, extensions...) + } + + return ret, nil +} + +// CreateCertificateRequest creates a new certificate request based on a +// template. The following members of template are used: Attributes, DNSNames, +// EmailAddresses, ExtraExtensions, IPAddresses, URIs, SignatureAlgorithm, and +// Subject. The private key is the private key of the signer. +// +// The returned slice is the certificate request in DER encoding. +// +// All keys types that are implemented via crypto.Signer are supported (This +// includes *rsa.PublicKey and *ecdsa.PublicKey.) +func CreateCertificateRequest(rand io.Reader, template *CertificateRequest, priv interface{}) (csr []byte, err error) { + key, ok := priv.(crypto.Signer) + if !ok { + return nil, errors.New("x509: certificate private key does not implement crypto.Signer") + } + + var hashFunc crypto.Hash + var sigAlgo pkix.AlgorithmIdentifier + hashFunc, sigAlgo, err = signingParamsForPublicKey(key.Public(), template.SignatureAlgorithm) + if err != nil { + return nil, err + } + + var publicKeyBytes []byte + var publicKeyAlgorithm pkix.AlgorithmIdentifier + publicKeyBytes, publicKeyAlgorithm, err = marshalPublicKey(key.Public()) + if err != nil { + return nil, err + } + + var extensions []pkix.Extension + + if (len(template.DNSNames) > 0 || len(template.EmailAddresses) > 0 || len(template.IPAddresses) > 0 || len(template.URIs) > 0) && + !oidInExtensions(OIDExtensionSubjectAltName, template.ExtraExtensions) { + sanBytes, err := marshalSANs(template.DNSNames, template.EmailAddresses, template.IPAddresses, template.URIs) + if err != nil { + return nil, err + } + + extensions = append(extensions, pkix.Extension{ + Id: OIDExtensionSubjectAltName, + Value: sanBytes, + }) + } + + extensions = append(extensions, template.ExtraExtensions...) + + var attributes []pkix.AttributeTypeAndValueSET + attributes = append(attributes, template.Attributes...) + + if len(extensions) > 0 { + // specifiedExtensions contains all the extensions that we + // found specified via template.Attributes. + specifiedExtensions := make(map[string]bool) + + for _, atvSet := range template.Attributes { + if !atvSet.Type.Equal(oidExtensionRequest) { + continue + } + + for _, atvs := range atvSet.Value { + for _, atv := range atvs { + specifiedExtensions[atv.Type.String()] = true + } + } + } + + atvs := make([]pkix.AttributeTypeAndValue, 0, len(extensions)) + for _, e := range extensions { + if specifiedExtensions[e.Id.String()] { + // Attributes already contained a value for + // this extension and it takes priority. + continue + } + + atvs = append(atvs, pkix.AttributeTypeAndValue{ + // There is no place for the critical flag in a CSR. + Type: e.Id, + Value: e.Value, + }) + } + + // Append the extensions to an existing attribute if possible. + appended := false + for _, atvSet := range attributes { + if !atvSet.Type.Equal(oidExtensionRequest) || len(atvSet.Value) == 0 { + continue + } + + atvSet.Value[0] = append(atvSet.Value[0], atvs...) + appended = true + break + } + + // Otherwise, add a new attribute for the extensions. + if !appended { + attributes = append(attributes, pkix.AttributeTypeAndValueSET{ + Type: oidExtensionRequest, + Value: [][]pkix.AttributeTypeAndValue{ + atvs, + }, + }) + } + } + + asn1Subject := template.RawSubject + if len(asn1Subject) == 0 { + asn1Subject, err = asn1.Marshal(template.Subject.ToRDNSequence()) + if err != nil { + return + } + } + + rawAttributes, err := newRawAttributes(attributes) + if err != nil { + return + } + + tbsCSR := tbsCertificateRequest{ + Version: 0, // PKCS #10, RFC 2986 + Subject: asn1.RawValue{FullBytes: asn1Subject}, + PublicKey: publicKeyInfo{ + Algorithm: publicKeyAlgorithm, + PublicKey: asn1.BitString{ + Bytes: publicKeyBytes, + BitLength: len(publicKeyBytes) * 8, + }, + }, + RawAttributes: rawAttributes, + } + + tbsCSRContents, err := asn1.Marshal(tbsCSR) + if err != nil { + return + } + tbsCSR.Raw = tbsCSRContents + + h := hashFunc.New() + h.Write(tbsCSRContents) + digest := h.Sum(nil) + + var signature []byte + signature, err = key.Sign(rand, digest, hashFunc) + if err != nil { + return + } + + return asn1.Marshal(certificateRequest{ + TBSCSR: tbsCSR, + SignatureAlgorithm: sigAlgo, + SignatureValue: asn1.BitString{ + Bytes: signature, + BitLength: len(signature) * 8, + }, + }) +} + +// ParseCertificateRequest parses a single certificate request from the +// given ASN.1 DER data. +func ParseCertificateRequest(asn1Data []byte) (*CertificateRequest, error) { + var csr certificateRequest + + rest, err := asn1.Unmarshal(asn1Data, &csr) + if err != nil { + return nil, err + } else if len(rest) != 0 { + return nil, asn1.SyntaxError{Msg: "trailing data"} + } + + return parseCertificateRequest(&csr) +} + +func parseCertificateRequest(in *certificateRequest) (*CertificateRequest, error) { + out := &CertificateRequest{ + Raw: in.Raw, + RawTBSCertificateRequest: in.TBSCSR.Raw, + RawSubjectPublicKeyInfo: in.TBSCSR.PublicKey.Raw, + RawSubject: in.TBSCSR.Subject.FullBytes, + + Signature: in.SignatureValue.RightAlign(), + SignatureAlgorithm: SignatureAlgorithmFromAI(in.SignatureAlgorithm), + + PublicKeyAlgorithm: getPublicKeyAlgorithmFromOID(in.TBSCSR.PublicKey.Algorithm.Algorithm), + + Version: in.TBSCSR.Version, + Attributes: parseRawAttributes(in.TBSCSR.RawAttributes), + } + + var err error + out.PublicKey, err = parsePublicKey(out.PublicKeyAlgorithm, &in.TBSCSR.PublicKey) + if err != nil { + return nil, err + } + + var subject pkix.RDNSequence + if rest, err := asn1.Unmarshal(in.TBSCSR.Subject.FullBytes, &subject); err != nil { + return nil, err + } else if len(rest) != 0 { + return nil, errors.New("x509: trailing data after X.509 Subject") + } + + out.Subject.FillFromRDNSequence(&subject) + + if out.Extensions, err = parseCSRExtensions(in.TBSCSR.RawAttributes); err != nil { + return nil, err + } + + var nfe NonFatalErrors + for _, extension := range out.Extensions { + if extension.Id.Equal(OIDExtensionSubjectAltName) { + out.DNSNames, out.EmailAddresses, out.IPAddresses, out.URIs, err = parseSANExtension(extension.Value, &nfe) + if err != nil { + return nil, err + } + } + } + + return out, nil +} + +// CheckSignature reports whether the signature on c is valid. +func (c *CertificateRequest) CheckSignature() error { + return checkSignature(c.SignatureAlgorithm, c.RawTBSCertificateRequest, c.Signature, c.PublicKey) +} diff --git a/vendor/github.com/google/certificate-transparency/README-MacOS.md b/vendor/github.com/google/certificate-transparency/README-MacOS.md deleted file mode 100644 index 7186f53761aad..0000000000000 --- a/vendor/github.com/google/certificate-transparency/README-MacOS.md +++ /dev/null @@ -1,55 +0,0 @@ -OS X Specific Instructions -========================== - -Builds ------- - -We recommend that you use GClient to build on OSX. Please follow the -instructions in the [main readme](README.md) file. - -Trusted root certificates -------------------------- - -The CT code requires a set of trusted root certificates in order to: - 1. Validate outbound HTTPS connections - 2. (In the case of the log-server) decide whether to accept a certificate - chain for inclusion. - -On OSX, the system version of OpenSSL (0.9.8gz at time of writing) contains -Apple-provided patches which intercept failed chain validations and re-attempts -them using roots obtained from the system keychain. Since we use a much more -recent (and unpatched) version of OpenSSL this behaviour is unsupported and so -a PEM file containing the trusted root certs must be used. - -To use a certificate PEM bundle file with the CT C++ code, the following -methods may be used. - -### Incoming inclusion requests (ct-server only) - -Set the `--trusted_cert_file` flag to point to the location of the PEM file -containing the set of root certificates whose chains should be accepted for -inclusion into the log. - -### For verifying outbound HTTPS connections (ct-mirror) - -Either set the `--trusted_roots_certs` flag, or the `SSL_CERT_FILE` -environment variable, to point to the location of the PEM file containing the -root certificates to be used to verify the outbound HTTPS connection. - -Sources of trusted roots ------------------------- - -Obviously the choice of root certificates to trust for outbound HTTPS -connections and incoming inclusion requests are a matter of operating policy, -but it is often useful to have a set of common roots for testing and -development at the very least. - -While OSX ships with a set of common trusted roots, they are not directly -available to OpenSSL and must be exported from the keychain first. This can be -achieved with the following command: - -```bash -security find-certificates -a -p /Library/Keychains/System.keychain > certs.pem -security find-certificates -a -p /System/Library/Keychains/SystemRootCertificates.keychain >> certs.pem -``` - diff --git a/vendor/github.com/google/certificate-transparency/README.md b/vendor/github.com/google/certificate-transparency/README.md deleted file mode 100644 index ffe459f7864f3..0000000000000 --- a/vendor/github.com/google/certificate-transparency/README.md +++ /dev/null @@ -1,303 +0,0 @@ -certificate-transparency: Auditing for TLS certificates -======================================================= - -[![Build Status](https://travis-ci.org/google/certificate-transparency.svg?branch=master)](https://travis-ci.org/google/certificate-transparency) - - - [Introduction](#introduction) - - [Build Quick Start](#build-quick-start) - - [Code Layout](#code-layout) - - [Building the code](#building-the-code) - - [Build Dependencies](#build-dependencies) - - [Software Dependencies](#software-dependencies) - - [Build Troubleshooting](#build-troubleshooting) - - [Compiler Warnings/Errors](#compiler-warnings-errors) - - [Working on a Branch](#working-on-a-branch) - - [Using BoringSSL](#using-boringssl) - - [Testing the code](#testing-the-code) - - [Unit Tests](#unit-tests) - - [Testing and Logging Options](#testing-and-logging-options) - - [Deploying a Log](#deploying-a-log) - - [Operating a Log](#operating-a-log) - -Introduction ------------- - -This repository holds open-source code for functionality related -to [certificate transparency](https://www.certificate-transparency.org/) (CT). -The main areas covered are: - - - An open-source, distributed, implementation of a CT Log server, also including: - - An implementation of a read-only ["mirror" server](docs/MirrorLog.md) - that mimics a remote Log. - - Ancillary tools needed for managing and maintaining the Log. - - A collection of client tools and libraries for interacting with a CT Log, in - various programming languages. - - An **experimental** implementation of a [DNS server](docs/DnsServer.md) that - returns CT proofs in the form of DNS records. - - An **experimental** implementation of a [general Log](docs/XjsonServer.md) - that allows arbitrary data (not just TLS certificates) to be logged. - -The supported platforms are: - - - **Linux**: tested on Ubuntu 14.04; other variants (Fedora 22, CentOS 7) may - require tweaking of [compiler options](#build-troubleshooting). - - **OS X**: version 10.10 - - **FreeBSD**: version 10.* - - -Build Quick Start ------------------ - -First, ensure that the build machine has all of the required [build dependencies](#build-dependencies). -Then use -[gclient](https://www.chromium.org/developers/how-tos/depottools#TOC-gclient) to -retrieve and build the [other software](#software-dependencies) needed by the Log, -and then use (GNU) `make` to build and test the CT code: - -```bash -export CXX=clang++ CC=clang -mkdir ct # or whatever directory you prefer -cd ct -gclient config --name="certificate-transparency" https://github.com/google/certificate-transparency.git -gclient sync # retrieve and build dependencies -# substitute gmake or gnumake below if that's what your platform calls it: -make -C certificate-transparency check # build the CT software & self-test -``` - -Code Layout ------------ - -The source code is generally arranged according to implementation language, in -the `cpp`, `go`, `java` and `python` subdirectories. The key subdirectories -are: - - - For the main distributed CT Log itself: - - `cpp/log`: Main distributed CT Log implementation. - - `cpp/merkletree`: Merkle tree implementation. - - `cpp/server`: Top-level code for server implementations. - - `cpp/monitoring`: Code to export operation statistics from CT Log. - - The [CT mirror Log](docs/MirrorLog.md) implementation also uses: - - `cpp/fetcher`: Code to fetch entries from another Log - - Client code for accessing a CT Log instance: - - `cpp/client`: CT Log client code in C++ - - `go/client`: CT Log client code in Go - - `python/ct`: CT Log client code in Python - - `java/src/org/certificatetransparency/ctlog`: CT Log client code in Java - - Other tools: - - `go/fixchain`: Tool to fix up certificate chains - - `go/gossip`: Code to allow gossip-based synchronization of cert info - - `go/scanner`: CT Log scanner tool - - `go/merkletree`: Merkle tree implementation in Go. - -Building the Code ------------------ - -The CT software in this repository relies on a number of other -[open-source projects](#software-dependencies), and we recommend that: - - - The CT software should be built using local copies of these dependencies - rather than installed packages, to prevent version incompatibilities. - - The dependent libraries should be statically linked into the CT binaries, - rather than relying on dynamically linked libraries that may be different in - the deployed environment. - -The supported build system uses the -[gclient](https://www.chromium.org/developers/how-tos/depottools#TOC-gclient) -tool from the Chromium project to handle these requirements and to ensure a -reliable, reproducible build. Older build instructions for using -[Ubuntu](docs/archive/BuildUbuntu.md) or -[Fedora](docs/archive/BuildFedora.md) packages and for -[manually building dependencies from source](docs/archive/BuildSrc.md) are no -longer supported. - -Within a main top-level directory, gclient handles the process of: - - - generating subdirectories for each dependency - - generating a subdirectory for for the CT Log code itself - - building all of the dependencies - - installing the built dependencies into an `install/` subdirectory - - configuring the CT build to reference the built dependencies. - -Under the covers, this gclient build process is controlled by: - - - The master [DEPS](DEPS) file, which configures the locations and versions - of the source code needed for the dependencies, and which hooks onto ... - - The makefiles in the [build/](build) subdirectory, which govern the build - process for each dependency, ensuring that: - - Static libraries are built. - - Built code is installed into the local `install/` directory, where it - is available for the build of the CT code itself. - - -### Build Dependencies - -The following tools are needed to build the CT software and its dependencies. - - - [depot_tools](https://www.chromium.org/developers/how-tos/install-depot-tools) - - autoconf/automake etc. - - libtool - - shtool - - clang++ (>=3.4) - - cmake (>=v3.1.2) - - git - - GNU make - - Tcl - - pkg-config - - Python 2.7 - -The exact packages required to install these tools depends on the platform. -For a Debian-based system, the relevant packages are: -`autoconf automake libtool shtool cmake clang git make tcl pkg-config python2.7` - -### Software Dependencies - -The following collections of additional software are used by the main CT -Log codebase. - - - Google utility libraries: - - [gflags](https://github.com/gflags/gflags): command-line flag handling - - [glog](https://github.com/google/glog): logging infrastructure, which - also requires libunwind. - - [Google Mock](https://github.com/google/googlemock.git): C++ test framework - - [Google Test](https://github.com/google/googletest.git): C++ mocking - framework - - [Protocol Buffers](https://developers.google.com/protocol-buffers/): - language-neutral data serialization library - - [tcmalloc](http://goog-perftools.sourceforge.net/doc/tcmalloc.html): - efficient `malloc` replacement optimized for multi-threaded use - - Other utility libraries: - - [libevent](http://libevent.org/): event-processing library - - [libevhtp](https://github.com/ellzey/libevhtp): HTTP server - plug-in/replacement for libevent - - [json-c](https://github.com/json-c/json-c): JSON processing library - - [libunwind](http://www.nongnu.org/libunwind/): library for generating - stack traces - - Cryptographic library: one of the following, selected via the `SSL` build - variable. - - [OpenSSL](https://github.com/google/googletest.git): default - cryptography library. - - [BoringSSL](https://boringssl.googlesource.com/boringssl/): Google's - fork of OpenSSL - - Data storage functionality: one of the following, defaulting (and highly - recommended to stick with) LevelDB. - - [LevelDB](https://github.com/google/leveldb): fast key-value store, - which uses: - - [Snappy](http://google.github.io/snappy/): compression library - - [SQLite](https://www.sqlite.org/): file-based SQL library - -The extra (experimental) CT projects in this repo involve additional -dependencies: - - - The experimental CT [DNS server](docs/DnsServer.md) uses: - - [ldnbs](http://www.nlnetlabs.nl/projects/ldns/): DNS library, including - DNSSEC function (which relies on OpenSSL for crypto functionality) - - The experimental [general Log](docs/XjsonServer.md) uses: - - [objecthash](https://github.com/benlaurie/objecthash): tools for - hashing objects in a language/encoding-agnostic manner - - [ICU](http://site.icu-project.org/): Unicode libraries (needed to - normalize international text in objects) - - - -Build Troubleshooting ---------------------- - -### Compiler Warnings/Errors - -The CT C++ codebase is built with the Clang `-Werror` flag so that the -codebase stays warning-free. However, this can cause build errors when -newer/different versions of the C++ compiler are used, as any newly created -warnings are treated as errors. To fix this, add the appropriate -`-Wno-error=` option to `CXXFLAGS`. - -For example, on errors involving unused variables try using: - -```bash -CXXFLAGS="-O2 -Wno-error=unused-variable" gclient sync -``` - -If an error about an unused typedef in a `glog` header file occurs, try this: - -```bash -CXXFLAGS="-O2 -Wno-error=unused-variable -Wno-error=unused-local-typedefs" gclient sync -``` - -When changing `CXXFLAGS` it's safer to remove the existing build directories -in case not all dependencies are properly accounted for and rebuilt. If -problems persist, check that the Makefile in `certificate-transparency` -contains the options that were passed in `CXXFLAGS`. - -### Working on a Branch - -If you're trying to clone from a branch on the CT repository then you'll need -to substitute the following command for the `gclient config` command -[above](#build-quick-start), replacing `branch` as appropriate - -```bash -gclient config --name="certificate-transparency" https://github.com/google/certificate-transparency.git@branch -``` - -### Using BoringSSL - -The BoringSSL fork of OpenSSL can be used in place of OpenSSL (but note that -the experimental [CT DNS server](docs/DnsServer.md) does not support this -configuration). To enable this, after the first step (`gclient config ...`) -in the gclient [build process](#build-quick-start), modify the top-level -`.gclient` to add: - -```python - "custom_vars": { "ssl_impl": "boringssl" } }, -``` - -Then continue the [build process](#build-quick-start) with the `gclient sync` step. - - -Testing the Code ----------------- - -### Unit Tests - -The unit tests for the CT code can be run with the `make check` target of -`certificate-transparency/Makefile`. - -## Testing and Logging Options ## - -Note that several tests write files on disk. The default directory for -storing temporary testdata is `/tmp`. You can change this by setting -`TMPDIR=` for make. - -End-to-end tests also create temporary certificate and server files in -`test/tmp`. All these files are cleaned up after a successful test -run. - -For logging options, see the -[glog documentation](http://htmlpreview.github.io/?https://github.com/google/glog/blob/master/doc/glog.html). - -By default, unit tests log to `stderr`, and log only messages with a FATAL -level (i.e., those that result in abnormal program termination). You can -override the defaults with command-line flags. - - -Deploying a Log ---------------- - -The build process described so far generates a set of executables; however, -other components and configuration is needed to set up a running CT Log. -In particular, as shown in the following diagram: - - A set of web servers that act as HTTPS terminators and load - balancers is needed in front of the CT Log instances. - - A cluster of [etcd](https://github.com/coreos/etcd) instances is needed to - provide replication and synchronization services for the CT Log instances. - - - -Configuring and setting up a distributed production Log is covered in a -[separate document](docs/Deployment.md). - - -Operating a Log ---------------- - -Running a successful, trusted, certificate transparency Log involves more than -just deploying a set of binaries. Information and advice on operating a -running CT Log is covered in a [separate document](docs/Operation.md) diff --git a/vendor/github.com/google/certificate-transparency/cpp/third_party/curl/hostcheck.c b/vendor/github.com/google/certificate-transparency/cpp/third_party/curl/hostcheck.c deleted file mode 100644 index b89469253b11d..0000000000000 --- a/vendor/github.com/google/certificate-transparency/cpp/third_party/curl/hostcheck.c +++ /dev/null @@ -1,214 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ - -/* This file is an amalgamation of hostcheck.c and most of rawstr.c - from cURL. The contents of the COPYING file mentioned above are: - -COPYRIGHT AND PERMISSION NOTICE - -Copyright (c) 1996 - 2013, Daniel Stenberg, . - -All rights reserved. - -Permission to use, copy, modify, and distribute this software for any purpose -with or without fee is hereby granted, provided that the above copyright -notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN -NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE -OR OTHER DEALINGS IN THE SOFTWARE. - -Except as contained in this notice, the name of a copyright holder shall not -be used in advertising or otherwise to promote the sale, use or other dealings -in this Software without prior written authorization of the copyright holder. -*/ - -#include "hostcheck.h" -#include - -/* Portable, consistent toupper (remember EBCDIC). Do not use toupper() because - its behavior is altered by the current locale. */ -static char Curl_raw_toupper(char in) { - switch (in) { - case 'a': - return 'A'; - case 'b': - return 'B'; - case 'c': - return 'C'; - case 'd': - return 'D'; - case 'e': - return 'E'; - case 'f': - return 'F'; - case 'g': - return 'G'; - case 'h': - return 'H'; - case 'i': - return 'I'; - case 'j': - return 'J'; - case 'k': - return 'K'; - case 'l': - return 'L'; - case 'm': - return 'M'; - case 'n': - return 'N'; - case 'o': - return 'O'; - case 'p': - return 'P'; - case 'q': - return 'Q'; - case 'r': - return 'R'; - case 's': - return 'S'; - case 't': - return 'T'; - case 'u': - return 'U'; - case 'v': - return 'V'; - case 'w': - return 'W'; - case 'x': - return 'X'; - case 'y': - return 'Y'; - case 'z': - return 'Z'; - } - return in; -} - -/* - * Curl_raw_equal() is for doing "raw" case insensitive strings. This is meant - * to be locale independent and only compare strings we know are safe for - * this. See http://daniel.haxx.se/blog/2008/10/15/strcasecmp-in-turkish/ for - * some further explanation to why this function is necessary. - * - * The function is capable of comparing a-z case insensitively even for - * non-ascii. - */ - -static int Curl_raw_equal(const char *first, const char *second) { - while (*first && *second) { - if (Curl_raw_toupper(*first) != Curl_raw_toupper(*second)) - /* get out of the loop as soon as they don't match */ - break; - first++; - second++; - } - /* we do the comparison here (possibly again), just to make sure that if the - loop above is skipped because one of the strings reached zero, we must not - return this as a successful match */ - return (Curl_raw_toupper(*first) == Curl_raw_toupper(*second)); -} - -static int Curl_raw_nequal(const char *first, const char *second, size_t max) { - while (*first && *second && max) { - if (Curl_raw_toupper(*first) != Curl_raw_toupper(*second)) { - break; - } - max--; - first++; - second++; - } - if (0 == max) - return 1; /* they are equal this far */ - - return Curl_raw_toupper(*first) == Curl_raw_toupper(*second); -} - -/* - * Match a hostname against a wildcard pattern. - * E.g. - * "foo.host.com" matches "*.host.com". - * - * We use the matching rule described in RFC6125, section 6.4.3. - * http://tools.ietf.org/html/rfc6125#section-6.4.3 - */ - -static int hostmatch(const char *hostname, const char *pattern) { - const char *pattern_label_end, *pattern_wildcard, *hostname_label_end; - int wildcard_enabled; - size_t prefixlen, suffixlen; - pattern_wildcard = strchr(pattern, '*'); - if (pattern_wildcard == NULL) - return Curl_raw_equal(pattern, hostname) ? CURL_HOST_MATCH - : CURL_HOST_NOMATCH; - - /* We require at least 2 dots in pattern to avoid too wide wildcard - match. */ - wildcard_enabled = 1; - pattern_label_end = strchr(pattern, '.'); - if (pattern_label_end == NULL || - strchr(pattern_label_end + 1, '.') == NULL || - pattern_wildcard > pattern_label_end || - Curl_raw_nequal(pattern, "xn--", 4)) { - wildcard_enabled = 0; - } - if (!wildcard_enabled) - return Curl_raw_equal(pattern, hostname) ? CURL_HOST_MATCH - : CURL_HOST_NOMATCH; - - hostname_label_end = strchr(hostname, '.'); - if (hostname_label_end == NULL || - !Curl_raw_equal(pattern_label_end, hostname_label_end)) - return CURL_HOST_NOMATCH; - - /* The wildcard must match at least one character, so the left-most - label of the hostname is at least as large as the left-most label - of the pattern. */ - if (hostname_label_end - hostname < pattern_label_end - pattern) - return CURL_HOST_NOMATCH; - - prefixlen = pattern_wildcard - pattern; - suffixlen = pattern_label_end - (pattern_wildcard + 1); - return Curl_raw_nequal(pattern, hostname, prefixlen) && - Curl_raw_nequal(pattern_wildcard + 1, - hostname_label_end - suffixlen, suffixlen) - ? CURL_HOST_MATCH - : CURL_HOST_NOMATCH; -} - -int Curl_cert_hostcheck(const char *match_pattern, const char *hostname) { - if (!match_pattern || !*match_pattern || !hostname || - !*hostname) /* sanity check */ - return 0; - - if (Curl_raw_equal(hostname, match_pattern)) /* trivial case */ - return 1; - - if (hostmatch(hostname, match_pattern) == CURL_HOST_MATCH) - return 1; - return 0; -} diff --git a/vendor/github.com/google/certificate-transparency/cpp/third_party/curl/hostcheck.h b/vendor/github.com/google/certificate-transparency/cpp/third_party/curl/hostcheck.h deleted file mode 100644 index f709917ae9451..0000000000000 --- a/vendor/github.com/google/certificate-transparency/cpp/third_party/curl/hostcheck.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef HEADER_CURL_HOSTCHECK_H -#define HEADER_CURL_HOSTCHECK_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ - -#define CURL_HOST_NOMATCH 0 -#define CURL_HOST_MATCH 1 -int Curl_cert_hostcheck(const char* match_pattern, const char* hostname); - -#endif /* HEADER_CURL_HOSTCHECK_H */ diff --git a/vendor/github.com/google/certificate-transparency/cpp/third_party/isec_partners/openssl_hostname_validation.c b/vendor/github.com/google/certificate-transparency/cpp/third_party/isec_partners/openssl_hostname_validation.c deleted file mode 100644 index b16abadb684fc..0000000000000 --- a/vendor/github.com/google/certificate-transparency/cpp/third_party/isec_partners/openssl_hostname_validation.c +++ /dev/null @@ -1,180 +0,0 @@ -/* Obtained from: https://github.com/iSECPartners/ssl-conservatory */ - -/* -Copyright (C) 2012, iSEC Partners. - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - */ - -/* - * Helper functions to perform basic hostname validation using OpenSSL. - * - * Please read "everything-you-wanted-to-know-about-openssl.pdf" before - * attempting to use this code. This whitepaper describes how the code works, - * how it should be used, and what its limitations are. - * - * Author: Alban Diquet - * License: See LICENSE - * - */ - - -#include -#include - -#include "third_party/curl/hostcheck.h" -#include "third_party/isec_partners/openssl_hostname_validation.h" - -#define HOSTNAME_MAX_SIZE 255 - -/** -* Tries to find a match for hostname in the certificate's Common Name field. -* -* Returns MatchFound if a match was found. -* Returns MatchNotFound if no matches were found. -* Returns MalformedCertificate if the Common Name had a NUL character embedded -* in it. -* Returns Error if the Common Name could not be extracted. -*/ -static HostnameValidationResult matches_common_name(const char *hostname, - const X509 *server_cert) { - int common_name_loc = -1; - X509_NAME_ENTRY *common_name_entry = NULL; - ASN1_STRING *common_name_asn1 = NULL; - char *common_name_str = NULL; - - // Find the position of the CN field in the Subject field of the certificate - common_name_loc = - X509_NAME_get_index_by_NID(X509_get_subject_name((X509 *)server_cert), - NID_commonName, -1); - if (common_name_loc < 0) { - return Error; - } - - // Extract the CN field - common_name_entry = - X509_NAME_get_entry(X509_get_subject_name((X509 *)server_cert), - common_name_loc); - if (common_name_entry == NULL) { - return Error; - } - - // Convert the CN field to a C string - common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry); - if (common_name_asn1 == NULL) { - return Error; - } - common_name_str = (char *)ASN1_STRING_data(common_name_asn1); - - // Make sure there isn't an embedded NUL character in the CN - if ((size_t)ASN1_STRING_length(common_name_asn1) != - strlen(common_name_str)) { - return MalformedCertificate; - } - - // Compare expected hostname with the CN - if (Curl_cert_hostcheck(common_name_str, hostname) == CURL_HOST_MATCH) { - return MatchFound; - } else { - return MatchNotFound; - } -} - - -/** -* Tries to find a match for hostname in the certificate's Subject Alternative -* Name extension. -* -* Returns MatchFound if a match was found. -* Returns MatchNotFound if no matches were found. -* Returns MalformedCertificate if any of the hostnames had a NUL character -* embedded in it. -* Returns NoSANPresent if the SAN extension was not present in the certificate. -*/ -static HostnameValidationResult matches_subject_alternative_name( - const char *hostname, const X509 *server_cert) { - HostnameValidationResult result = MatchNotFound; - int i; - int san_names_nb = -1; - STACK_OF(GENERAL_NAME) *san_names = NULL; - - // Try to extract the names within the SAN extension from the certificate - san_names = - X509_get_ext_d2i((X509 *)server_cert, NID_subject_alt_name, NULL, NULL); - if (san_names == NULL) { - return NoSANPresent; - } - san_names_nb = sk_GENERAL_NAME_num(san_names); - - // Check each name within the extension - for (i = 0; i < san_names_nb; i++) { - const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(san_names, i); - - if (current_name->type == GEN_DNS) { - // Current name is a DNS name, let's check it - char *dns_name = (char *)ASN1_STRING_data(current_name->d.dNSName); - - // Make sure there isn't an embedded NUL character in the DNS name - if ((size_t)ASN1_STRING_length(current_name->d.dNSName) != - strlen(dns_name)) { - result = MalformedCertificate; - break; - } else { // Compare expected hostname with the DNS name - if (Curl_cert_hostcheck(dns_name, hostname) == CURL_HOST_MATCH) { - result = MatchFound; - break; - } - } - } - } - sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free); - - return result; -} - - -/** -* Validates the server's identity by looking for the expected hostname in the -* server's certificate. As described in RFC 6125, it first tries to find a -* match -* in the Subject Alternative Name extension. If the extension is not present in -* the certificate, it checks the Common Name instead. -* -* Returns MatchFound if a match was found. -* Returns MatchNotFound if no matches were found. -* Returns MalformedCertificate if any of the hostnames had a NUL character -* embedded in it. -* Returns Error if there was an error. -*/ -HostnameValidationResult validate_hostname(const char *hostname, - const X509 *server_cert) { - HostnameValidationResult result; - - if ((hostname == NULL) || (server_cert == NULL)) - return Error; - - // First try the Subject Alternative Names extension - result = matches_subject_alternative_name(hostname, server_cert); - if (result == NoSANPresent) { - // Extension was not found: try the Common Name - result = matches_common_name(hostname, server_cert); - } - - return result; -} diff --git a/vendor/github.com/google/certificate-transparency/cpp/third_party/isec_partners/openssl_hostname_validation.h b/vendor/github.com/google/certificate-transparency/cpp/third_party/isec_partners/openssl_hostname_validation.h deleted file mode 100644 index b5902a1fb5239..0000000000000 --- a/vendor/github.com/google/certificate-transparency/cpp/third_party/isec_partners/openssl_hostname_validation.h +++ /dev/null @@ -1,59 +0,0 @@ -/* Obtained from: https://github.com/iSECPartners/ssl-conservatory */ - -/* -Copyright (C) 2012, iSEC Partners. - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - */ - -/* - * Helper functions to perform basic hostname validation using OpenSSL. - * - * Please read "everything-you-wanted-to-know-about-openssl.pdf" before - * attempting to use this code. This whitepaper describes how the code works, - * how it should be used, and what its limitations are. - * - * Author: Alban Diquet - * License: See LICENSE - * - */ - -typedef enum { - MatchFound, - MatchNotFound, - NoSANPresent, - MalformedCertificate, - Error -} HostnameValidationResult; - -/** -* Validates the server's identity by looking for the expected hostname in the -* server's certificate. As described in RFC 6125, it first tries to find a -* match -* in the Subject Alternative Name extension. If the extension is not present in -* the certificate, it checks the Common Name instead. -* -* Returns MatchFound if a match was found. -* Returns MatchNotFound if no matches were found. -* Returns MalformedCertificate if any of the hostnames had a NUL character -* embedded in it. -* Returns Error if there was an error. -*/ -HostnameValidationResult validate_hostname(const char* hostname, - const X509* server_cert); diff --git a/vendor/github.com/google/certificate-transparency/cpp/version.h b/vendor/github.com/google/certificate-transparency/cpp/version.h deleted file mode 100644 index 53afc73272e49..0000000000000 --- a/vendor/github.com/google/certificate-transparency/cpp/version.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef CERT_TRANS_VERSION_H_ -#define CERT_TRANS_VERSION_H_ - -namespace cert_trans { - - -extern const char kBuildVersion[]; - - -} // namespace cert_trans - -#endif // CERT_TRANS_VERSION_H_ diff --git a/vendor/github.com/google/certificate-transparency/go/README.md b/vendor/github.com/google/certificate-transparency/go/README.md deleted file mode 100644 index 82c5d1b3afd64..0000000000000 --- a/vendor/github.com/google/certificate-transparency/go/README.md +++ /dev/null @@ -1,25 +0,0 @@ -This is the really early beginnings of a certificate transparency log -client written in Go, along with a log scanner tool. - -You'll need go v1.1 or higher to compile. - -# Installation - -This go code must be imported into your go workspace before you can -use it, which can be done with: - - go get github.com/google/certificate-transparency/go/client - go get github.com/google/certificate-transparency/go/scanner - etc. - -# Building the binaries - -To compile the log scanner run: - - go build github.com/google/certificate-transparency/go/scanner/main/scanner.go - -# Contributing - -When sending pull requests, please ensure that everything's been run -through ```gofmt``` beforehand so we can keep everything nice and -tidy. diff --git a/vendor/github.com/google/certificate-transparency/go/asn1/marshal.go b/vendor/github.com/google/certificate-transparency/go/asn1/marshal.go deleted file mode 100755 index ed17e41a55cfd..0000000000000 --- a/vendor/github.com/google/certificate-transparency/go/asn1/marshal.go +++ /dev/null @@ -1,581 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package asn1 - -import ( - "bytes" - "errors" - "fmt" - "io" - "math/big" - "reflect" - "time" - "unicode/utf8" -) - -// A forkableWriter is an in-memory buffer that can be -// 'forked' to create new forkableWriters that bracket the -// original. After -// pre, post := w.fork(); -// the overall sequence of bytes represented is logically w+pre+post. -type forkableWriter struct { - *bytes.Buffer - pre, post *forkableWriter -} - -func newForkableWriter() *forkableWriter { - return &forkableWriter{new(bytes.Buffer), nil, nil} -} - -func (f *forkableWriter) fork() (pre, post *forkableWriter) { - if f.pre != nil || f.post != nil { - panic("have already forked") - } - f.pre = newForkableWriter() - f.post = newForkableWriter() - return f.pre, f.post -} - -func (f *forkableWriter) Len() (l int) { - l += f.Buffer.Len() - if f.pre != nil { - l += f.pre.Len() - } - if f.post != nil { - l += f.post.Len() - } - return -} - -func (f *forkableWriter) writeTo(out io.Writer) (n int, err error) { - n, err = out.Write(f.Bytes()) - if err != nil { - return - } - - var nn int - - if f.pre != nil { - nn, err = f.pre.writeTo(out) - n += nn - if err != nil { - return - } - } - - if f.post != nil { - nn, err = f.post.writeTo(out) - n += nn - } - return -} - -func marshalBase128Int(out *forkableWriter, n int64) (err error) { - if n == 0 { - err = out.WriteByte(0) - return - } - - l := 0 - for i := n; i > 0; i >>= 7 { - l++ - } - - for i := l - 1; i >= 0; i-- { - o := byte(n >> uint(i*7)) - o &= 0x7f - if i != 0 { - o |= 0x80 - } - err = out.WriteByte(o) - if err != nil { - return - } - } - - return nil -} - -func marshalInt64(out *forkableWriter, i int64) (err error) { - n := int64Length(i) - - for ; n > 0; n-- { - err = out.WriteByte(byte(i >> uint((n-1)*8))) - if err != nil { - return - } - } - - return nil -} - -func int64Length(i int64) (numBytes int) { - numBytes = 1 - - for i > 127 { - numBytes++ - i >>= 8 - } - - for i < -128 { - numBytes++ - i >>= 8 - } - - return -} - -func marshalBigInt(out *forkableWriter, n *big.Int) (err error) { - if n.Sign() < 0 { - // A negative number has to be converted to two's-complement - // form. So we'll subtract 1 and invert. If the - // most-significant-bit isn't set then we'll need to pad the - // beginning with 0xff in order to keep the number negative. - nMinus1 := new(big.Int).Neg(n) - nMinus1.Sub(nMinus1, bigOne) - bytes := nMinus1.Bytes() - for i := range bytes { - bytes[i] ^= 0xff - } - if len(bytes) == 0 || bytes[0]&0x80 == 0 { - err = out.WriteByte(0xff) - if err != nil { - return - } - } - _, err = out.Write(bytes) - } else if n.Sign() == 0 { - // Zero is written as a single 0 zero rather than no bytes. - err = out.WriteByte(0x00) - } else { - bytes := n.Bytes() - if len(bytes) > 0 && bytes[0]&0x80 != 0 { - // We'll have to pad this with 0x00 in order to stop it - // looking like a negative number. - err = out.WriteByte(0) - if err != nil { - return - } - } - _, err = out.Write(bytes) - } - return -} - -func marshalLength(out *forkableWriter, i int) (err error) { - n := lengthLength(i) - - for ; n > 0; n-- { - err = out.WriteByte(byte(i >> uint((n-1)*8))) - if err != nil { - return - } - } - - return nil -} - -func lengthLength(i int) (numBytes int) { - numBytes = 1 - for i > 255 { - numBytes++ - i >>= 8 - } - return -} - -func marshalTagAndLength(out *forkableWriter, t tagAndLength) (err error) { - b := uint8(t.class) << 6 - if t.isCompound { - b |= 0x20 - } - if t.tag >= 31 { - b |= 0x1f - err = out.WriteByte(b) - if err != nil { - return - } - err = marshalBase128Int(out, int64(t.tag)) - if err != nil { - return - } - } else { - b |= uint8(t.tag) - err = out.WriteByte(b) - if err != nil { - return - } - } - - if t.length >= 128 { - l := lengthLength(t.length) - err = out.WriteByte(0x80 | byte(l)) - if err != nil { - return - } - err = marshalLength(out, t.length) - if err != nil { - return - } - } else { - err = out.WriteByte(byte(t.length)) - if err != nil { - return - } - } - - return nil -} - -func marshalBitString(out *forkableWriter, b BitString) (err error) { - paddingBits := byte((8 - b.BitLength%8) % 8) - err = out.WriteByte(paddingBits) - if err != nil { - return - } - _, err = out.Write(b.Bytes) - return -} - -func marshalObjectIdentifier(out *forkableWriter, oid []int) (err error) { - if len(oid) < 2 || oid[0] > 2 || (oid[0] < 2 && oid[1] >= 40) { - return StructuralError{"invalid object identifier"} - } - - err = marshalBase128Int(out, int64(oid[0]*40+oid[1])) - if err != nil { - return - } - for i := 2; i < len(oid); i++ { - err = marshalBase128Int(out, int64(oid[i])) - if err != nil { - return - } - } - - return -} - -func marshalPrintableString(out *forkableWriter, s string) (err error) { - b := []byte(s) - for _, c := range b { - if !isPrintable(c) { - return StructuralError{"PrintableString contains invalid character"} - } - } - - _, err = out.Write(b) - return -} - -func marshalIA5String(out *forkableWriter, s string) (err error) { - b := []byte(s) - for _, c := range b { - if c > 127 { - return StructuralError{"IA5String contains invalid character"} - } - } - - _, err = out.Write(b) - return -} - -func marshalUTF8String(out *forkableWriter, s string) (err error) { - _, err = out.Write([]byte(s)) - return -} - -func marshalTwoDigits(out *forkableWriter, v int) (err error) { - err = out.WriteByte(byte('0' + (v/10)%10)) - if err != nil { - return - } - return out.WriteByte(byte('0' + v%10)) -} - -func marshalUTCTime(out *forkableWriter, t time.Time) (err error) { - year, month, day := t.Date() - - switch { - case 1950 <= year && year < 2000: - err = marshalTwoDigits(out, int(year-1900)) - case 2000 <= year && year < 2050: - err = marshalTwoDigits(out, int(year-2000)) - default: - return StructuralError{"cannot represent time as UTCTime"} - } - if err != nil { - return - } - - err = marshalTwoDigits(out, int(month)) - if err != nil { - return - } - - err = marshalTwoDigits(out, day) - if err != nil { - return - } - - hour, min, sec := t.Clock() - - err = marshalTwoDigits(out, hour) - if err != nil { - return - } - - err = marshalTwoDigits(out, min) - if err != nil { - return - } - - err = marshalTwoDigits(out, sec) - if err != nil { - return - } - - _, offset := t.Zone() - - switch { - case offset/60 == 0: - err = out.WriteByte('Z') - return - case offset > 0: - err = out.WriteByte('+') - case offset < 0: - err = out.WriteByte('-') - } - - if err != nil { - return - } - - offsetMinutes := offset / 60 - if offsetMinutes < 0 { - offsetMinutes = -offsetMinutes - } - - err = marshalTwoDigits(out, offsetMinutes/60) - if err != nil { - return - } - - err = marshalTwoDigits(out, offsetMinutes%60) - return -} - -func stripTagAndLength(in []byte) []byte { - _, offset, err := parseTagAndLength(in, 0) - if err != nil { - return in - } - return in[offset:] -} - -func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameters) (err error) { - switch value.Type() { - case timeType: - return marshalUTCTime(out, value.Interface().(time.Time)) - case bitStringType: - return marshalBitString(out, value.Interface().(BitString)) - case objectIdentifierType: - return marshalObjectIdentifier(out, value.Interface().(ObjectIdentifier)) - case bigIntType: - return marshalBigInt(out, value.Interface().(*big.Int)) - } - - switch v := value; v.Kind() { - case reflect.Bool: - if v.Bool() { - return out.WriteByte(255) - } else { - return out.WriteByte(0) - } - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return marshalInt64(out, int64(v.Int())) - case reflect.Struct: - t := v.Type() - - startingField := 0 - - // If the first element of the structure is a non-empty - // RawContents, then we don't bother serializing the rest. - if t.NumField() > 0 && t.Field(0).Type == rawContentsType { - s := v.Field(0) - if s.Len() > 0 { - bytes := make([]byte, s.Len()) - for i := 0; i < s.Len(); i++ { - bytes[i] = uint8(s.Index(i).Uint()) - } - /* The RawContents will contain the tag and - * length fields but we'll also be writing - * those ourselves, so we strip them out of - * bytes */ - _, err = out.Write(stripTagAndLength(bytes)) - return - } else { - startingField = 1 - } - } - - for i := startingField; i < t.NumField(); i++ { - var pre *forkableWriter - pre, out = out.fork() - err = marshalField(pre, v.Field(i), parseFieldParameters(t.Field(i).Tag.Get("asn1"))) - if err != nil { - return - } - } - return - case reflect.Slice: - sliceType := v.Type() - if sliceType.Elem().Kind() == reflect.Uint8 { - bytes := make([]byte, v.Len()) - for i := 0; i < v.Len(); i++ { - bytes[i] = uint8(v.Index(i).Uint()) - } - _, err = out.Write(bytes) - return - } - - var fp fieldParameters - for i := 0; i < v.Len(); i++ { - var pre *forkableWriter - pre, out = out.fork() - err = marshalField(pre, v.Index(i), fp) - if err != nil { - return - } - } - return - case reflect.String: - switch params.stringType { - case tagIA5String: - return marshalIA5String(out, v.String()) - case tagPrintableString: - return marshalPrintableString(out, v.String()) - default: - return marshalUTF8String(out, v.String()) - } - } - - return StructuralError{"unknown Go type"} -} - -func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters) (err error) { - // If the field is an interface{} then recurse into it. - if v.Kind() == reflect.Interface && v.Type().NumMethod() == 0 { - return marshalField(out, v.Elem(), params) - } - - if v.Kind() == reflect.Slice && v.Len() == 0 && params.omitEmpty { - return - } - - if params.optional && reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) { - return - } - - if v.Type() == rawValueType { - rv := v.Interface().(RawValue) - if len(rv.FullBytes) != 0 { - _, err = out.Write(rv.FullBytes) - } else { - err = marshalTagAndLength(out, tagAndLength{rv.Class, rv.Tag, len(rv.Bytes), rv.IsCompound}) - if err != nil { - return - } - _, err = out.Write(rv.Bytes) - } - return - } - - tag, isCompound, ok := getUniversalType(v.Type()) - if !ok { - err = StructuralError{fmt.Sprintf("unknown Go type: %v", v.Type())} - return - } - class := classUniversal - - if params.stringType != 0 && tag != tagPrintableString { - return StructuralError{"explicit string type given to non-string member"} - } - - if tag == tagPrintableString { - if params.stringType == 0 { - // This is a string without an explicit string type. We'll use - // a PrintableString if the character set in the string is - // sufficiently limited, otherwise we'll use a UTF8String. - for _, r := range v.String() { - if r >= utf8.RuneSelf || !isPrintable(byte(r)) { - if !utf8.ValidString(v.String()) { - return errors.New("asn1: string not valid UTF-8") - } - tag = tagUTF8String - break - } - } - } else { - tag = params.stringType - } - } - - if params.set { - if tag != tagSequence { - return StructuralError{"non sequence tagged as set"} - } - tag = tagSet - } - - tags, body := out.fork() - - err = marshalBody(body, v, params) - if err != nil { - return - } - - bodyLen := body.Len() - - var explicitTag *forkableWriter - if params.explicit { - explicitTag, tags = tags.fork() - } - - if !params.explicit && params.tag != nil { - // implicit tag. - tag = *params.tag - class = classContextSpecific - } - - err = marshalTagAndLength(tags, tagAndLength{class, tag, bodyLen, isCompound}) - if err != nil { - return - } - - if params.explicit { - err = marshalTagAndLength(explicitTag, tagAndLength{ - class: classContextSpecific, - tag: *params.tag, - length: bodyLen + tags.Len(), - isCompound: true, - }) - } - - return nil -} - -// Marshal returns the ASN.1 encoding of val. -func Marshal(val interface{}) ([]byte, error) { - var out bytes.Buffer - v := reflect.ValueOf(val) - f := newForkableWriter() - err := marshalField(f, v, fieldParameters{}) - if err != nil { - return nil, err - } - _, err = f.writeTo(&out) - return out.Bytes(), nil -} diff --git a/vendor/github.com/google/certificate-transparency/go/client/getentries.go b/vendor/github.com/google/certificate-transparency/go/client/getentries.go deleted file mode 100644 index 32be65c92f804..0000000000000 --- a/vendor/github.com/google/certificate-transparency/go/client/getentries.go +++ /dev/null @@ -1,88 +0,0 @@ -package client - -import ( - "bytes" - "errors" - "fmt" - "net/http" - "net/url" - "strconv" - "strings" - - ct "github.com/google/certificate-transparency/go" - "golang.org/x/net/context" -) - -// LeafEntry respresents a JSON leaf entry. -type LeafEntry struct { - LeafInput []byte `json:"leaf_input"` - ExtraData []byte `json:"extra_data"` -} - -// GetEntriesResponse respresents the JSON response to the CT get-entries method. -type GetEntriesResponse struct { - Entries []LeafEntry `json:"entries"` // the list of returned entries -} - -// GetRawEntries exposes the /ct/v1/get-entries result with only the JSON parsing done. -func GetRawEntries(ctx context.Context, httpClient *http.Client, logURL string, start, end int64) (*GetEntriesResponse, error) { - if end < 0 { - return nil, errors.New("end should be >= 0") - } - if end < start { - return nil, errors.New("start should be <= end") - } - - baseURL, err := url.Parse(strings.TrimRight(logURL, "/") + GetEntriesPath) - if err != nil { - return nil, err - } - - baseURL.RawQuery = url.Values{ - "start": []string{strconv.FormatInt(start, 10)}, - "end": []string{strconv.FormatInt(end, 10)}, - }.Encode() - - var resp GetEntriesResponse - err = fetchAndParse(context.TODO(), httpClient, baseURL.String(), &resp) - if err != nil { - return nil, err - } - - return &resp, nil -} - -// GetEntries attempts to retrieve the entries in the sequence [|start|, |end|] from the CT log server. (see section 4.6.) -// Returns a slice of LeafInputs or a non-nil error. -func (c *LogClient) GetEntries(start, end int64) ([]ct.LogEntry, error) { - resp, err := GetRawEntries(context.TODO(), c.httpClient, c.uri, start, end) - if err != nil { - return nil, err - } - entries := make([]ct.LogEntry, len(resp.Entries)) - for index, entry := range resp.Entries { - leaf, err := ct.ReadMerkleTreeLeaf(bytes.NewBuffer(entry.LeafInput)) - if err != nil { - return nil, err - } - entries[index].Leaf = *leaf - - var chain []ct.ASN1Cert - switch leaf.TimestampedEntry.EntryType { - case ct.X509LogEntryType: - chain, err = ct.UnmarshalX509ChainArray(entry.ExtraData) - - case ct.PrecertLogEntryType: - chain, err = ct.UnmarshalPrecertChainArray(entry.ExtraData) - - default: - return nil, fmt.Errorf("saw unknown entry type: %v", leaf.TimestampedEntry.EntryType) - } - if err != nil { - return nil, err - } - entries[index].Chain = chain - entries[index].Index = start + int64(index) - } - return entries, nil -} diff --git a/vendor/github.com/google/certificate-transparency/go/client/logclient.go b/vendor/github.com/google/certificate-transparency/go/client/logclient.go deleted file mode 100644 index 7b3dbbc23433b..0000000000000 --- a/vendor/github.com/google/certificate-transparency/go/client/logclient.go +++ /dev/null @@ -1,412 +0,0 @@ -// Package client is a CT log client implementation and contains types and code -// for interacting with RFC6962-compliant CT Log instances. -// See http://tools.ietf.org/html/rfc6962 for details -package client - -import ( - "bytes" - "crypto/sha256" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "log" - "net/http" - "net/url" - "strconv" - "time" - - ct "github.com/google/certificate-transparency/go" - "golang.org/x/net/context" -) - -// URI paths for CT Log endpoints -const ( - AddChainPath = "/ct/v1/add-chain" - AddPreChainPath = "/ct/v1/add-pre-chain" - AddJSONPath = "/ct/v1/add-json" - GetSTHPath = "/ct/v1/get-sth" - GetEntriesPath = "/ct/v1/get-entries" - GetProofByHashPath = "/ct/v1/get-proof-by-hash" - GetSTHConsistencyPath = "/ct/v1/get-sth-consistency" -) - -// LogClient represents a client for a given CT Log instance -type LogClient struct { - uri string // the base URI of the log. e.g. http://ct.googleapis/pilot - httpClient *http.Client // used to interact with the log via HTTP - verifier *ct.SignatureVerifier // nil if no public key for log available -} - -////////////////////////////////////////////////////////////////////////////////// -// JSON structures follow. -// These represent the structures returned by the CT Log server. -////////////////////////////////////////////////////////////////////////////////// - -// addChainRequest represents the JSON request body sent to the add-chain CT -// method. -type addChainRequest struct { - Chain [][]byte `json:"chain"` -} - -// addChainResponse represents the JSON response to the add-chain CT method. -// An SCT represents a Log's promise to integrate a [pre-]certificate into the -// log within a defined period of time. -type addChainResponse struct { - SCTVersion ct.Version `json:"sct_version"` // SCT structure version - ID []byte `json:"id"` // Log ID - Timestamp uint64 `json:"timestamp"` // Timestamp of issuance - Extensions string `json:"extensions"` // Holder for any CT extensions - Signature []byte `json:"signature"` // Log signature for this SCT -} - -// addJSONRequest represents the JSON request body sent to the add-json CT -// method. -type addJSONRequest struct { - Data interface{} `json:"data"` -} - -// getSTHResponse respresents the JSON response to the get-sth CT method -type getSTHResponse struct { - TreeSize uint64 `json:"tree_size"` // Number of certs in the current tree - Timestamp uint64 `json:"timestamp"` // Time that the tree was created - SHA256RootHash []byte `json:"sha256_root_hash"` // Root hash of the tree - TreeHeadSignature []byte `json:"tree_head_signature"` // Log signature for this STH -} - -// getConsistencyProofResponse represents the JSON response to the get-consistency-proof CT method -type getConsistencyProofResponse struct { - Consistency [][]byte `json:"consistency"` -} - -// getAuditProofResponse represents the JSON response to the CT get-audit-proof method -type getAuditProofResponse struct { - Hash []string `json:"hash"` // the hashes which make up the proof - TreeSize uint64 `json:"tree_size"` // the tree size against which this proof is constructed -} - -// getAcceptedRootsResponse represents the JSON response to the CT get-roots method. -type getAcceptedRootsResponse struct { - Certificates []string `json:"certificates"` -} - -// getEntryAndProodReponse represents the JSON response to the CT get-entry-and-proof method -type getEntryAndProofResponse struct { - LeafInput string `json:"leaf_input"` // the entry itself - ExtraData string `json:"extra_data"` // any chain provided when the entry was added to the log - AuditPath []string `json:"audit_path"` // the corresponding proof -} - -// GetProofByHashResponse represents the JSON response to the CT get-proof-by-hash method. -type GetProofByHashResponse struct { - LeafIndex int64 `json:"leaf_index"` // The 0-based index of the end entity corresponding to the "hash" parameter. - AuditPath [][]byte `json:"audit_path"` // An array of base64-encoded Merkle Tree nodes proving the inclusion of the chosen certificate. -} - -// New constructs a new LogClient instance. -// |uri| is the base URI of the CT log instance to interact with, e.g. -// http://ct.googleapis.com/pilot -// |hc| is the underlying client to be used for HTTP requests to the CT log. -func New(uri string, hc *http.Client) *LogClient { - if hc == nil { - hc = new(http.Client) - } - return &LogClient{uri: uri, httpClient: hc} -} - -// NewWithPubKey constructs a new LogClient instance that includes public -// key information for the log; this instance will check signatures on -// responses from the log. -func NewWithPubKey(uri string, hc *http.Client, pemEncodedKey string) (*LogClient, error) { - pubkey, _, rest, err := ct.PublicKeyFromPEM([]byte(pemEncodedKey)) - if err != nil { - return nil, err - } - if len(rest) > 0 { - return nil, errors.New("extra data found after PEM key decoded") - } - - verifier, err := ct.NewSignatureVerifier(pubkey) - if err != nil { - return nil, err - } - - if hc == nil { - hc = new(http.Client) - } - return &LogClient{uri: uri, httpClient: hc, verifier: verifier}, nil -} - -// Makes a HTTP call to |uri|, and attempts to parse the response as a -// JSON representation of the structure in |res|. Uses |ctx| to -// control the HTTP call (so it can have a timeout or be cancelled by -// the caller), and |httpClient| to make the actual HTTP call. -// Returns a non-nil |error| if there was a problem. -func fetchAndParse(ctx context.Context, httpClient *http.Client, uri string, res interface{}) error { - req, err := http.NewRequest(http.MethodGet, uri, nil) - if err != nil { - return err - } - req.Cancel = ctx.Done() - resp, err := httpClient.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - // Make sure everything is read, so http.Client can reuse the connection. - defer ioutil.ReadAll(resp.Body) - - if resp.StatusCode != 200 { - return fmt.Errorf("got HTTP Status %s", resp.Status) - } - - if err := json.NewDecoder(resp.Body).Decode(res); err != nil { - return err - } - - return nil -} - -// Makes a HTTP POST call to |uri|, and attempts to parse the response as a JSON -// representation of the structure in |res|. -// Returns a non-nil |error| if there was a problem. -func (c *LogClient) postAndParse(uri string, req interface{}, res interface{}) (*http.Response, string, error) { - postBody, err := json.Marshal(req) - if err != nil { - return nil, "", err - } - httpReq, err := http.NewRequest(http.MethodPost, uri, bytes.NewReader(postBody)) - if err != nil { - return nil, "", err - } - httpReq.Header.Set("Content-Type", "application/json") - resp, err := c.httpClient.Do(httpReq) - // Read all of the body, if there is one, so that the http.Client can do - // Keep-Alive: - var body []byte - if resp != nil { - body, err = ioutil.ReadAll(resp.Body) - resp.Body.Close() - } - if err != nil { - return resp, string(body), err - } - if resp.StatusCode == 200 { - if err != nil { - return resp, string(body), err - } - if err = json.Unmarshal(body, &res); err != nil { - return resp, string(body), err - } - } - return resp, string(body), nil -} - -func backoffForRetry(ctx context.Context, d time.Duration) error { - backoffTimer := time.NewTimer(d) - if ctx != nil { - select { - case <-ctx.Done(): - return ctx.Err() - case <-backoffTimer.C: - } - } else { - <-backoffTimer.C - } - return nil -} - -// Attempts to add |chain| to the log, using the api end-point specified by -// |path|. If provided context expires before submission is complete an -// error will be returned. -func (c *LogClient) addChainWithRetry(ctx context.Context, ctype ct.LogEntryType, path string, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) { - var resp addChainResponse - var req addChainRequest - for _, link := range chain { - req.Chain = append(req.Chain, link) - } - httpStatus := "Unknown" - backoffSeconds := 0 - done := false - for !done { - if backoffSeconds > 0 { - log.Printf("Got %s, backing-off %d seconds", httpStatus, backoffSeconds) - } - err := backoffForRetry(ctx, time.Second*time.Duration(backoffSeconds)) - if err != nil { - return nil, err - } - if backoffSeconds > 0 { - backoffSeconds = 0 - } - httpResp, _, err := c.postAndParse(c.uri+path, &req, &resp) - if err != nil { - backoffSeconds = 10 - continue - } - switch { - case httpResp.StatusCode == 200: - done = true - case httpResp.StatusCode == 408: - // request timeout, retry immediately - case httpResp.StatusCode == 503: - // Retry - backoffSeconds = 10 - if retryAfter := httpResp.Header.Get("Retry-After"); retryAfter != "" { - if seconds, err := strconv.Atoi(retryAfter); err == nil { - backoffSeconds = seconds - } - } - default: - return nil, fmt.Errorf("got HTTP Status %s", httpResp.Status) - } - httpStatus = httpResp.Status - } - - ds, err := ct.UnmarshalDigitallySigned(bytes.NewReader(resp.Signature)) - if err != nil { - return nil, err - } - - var logID ct.SHA256Hash - copy(logID[:], resp.ID) - sct := &ct.SignedCertificateTimestamp{ - SCTVersion: resp.SCTVersion, - LogID: logID, - Timestamp: resp.Timestamp, - Extensions: ct.CTExtensions(resp.Extensions), - Signature: *ds} - err = c.VerifySCTSignature(*sct, ctype, chain) - if err != nil { - return nil, err - } - return sct, nil -} - -// AddChain adds the (DER represented) X509 |chain| to the log. -func (c *LogClient) AddChain(chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) { - return c.addChainWithRetry(nil, ct.X509LogEntryType, AddChainPath, chain) -} - -// AddPreChain adds the (DER represented) Precertificate |chain| to the log. -func (c *LogClient) AddPreChain(chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) { - return c.addChainWithRetry(nil, ct.PrecertLogEntryType, AddPreChainPath, chain) -} - -// AddChainWithContext adds the (DER represented) X509 |chain| to the log and -// fails if the provided context expires before the chain is submitted. -func (c *LogClient) AddChainWithContext(ctx context.Context, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) { - return c.addChainWithRetry(ctx, ct.X509LogEntryType, AddChainPath, chain) -} - -// AddJSON submits arbitrary data to to XJSON server. -func (c *LogClient) AddJSON(data interface{}) (*ct.SignedCertificateTimestamp, error) { - req := addJSONRequest{ - Data: data, - } - var resp addChainResponse - _, _, err := c.postAndParse(c.uri+AddJSONPath, &req, &resp) - if err != nil { - return nil, err - } - ds, err := ct.UnmarshalDigitallySigned(bytes.NewReader(resp.Signature)) - if err != nil { - return nil, err - } - var logID ct.SHA256Hash - copy(logID[:], resp.ID) - return &ct.SignedCertificateTimestamp{ - SCTVersion: resp.SCTVersion, - LogID: logID, - Timestamp: resp.Timestamp, - Extensions: ct.CTExtensions(resp.Extensions), - Signature: *ds}, nil -} - -// GetSTH retrieves the current STH from the log. -// Returns a populated SignedTreeHead, or a non-nil error. -func (c *LogClient) GetSTH() (sth *ct.SignedTreeHead, err error) { - var resp getSTHResponse - if err = fetchAndParse(context.TODO(), c.httpClient, c.uri+GetSTHPath, &resp); err != nil { - return - } - sth = &ct.SignedTreeHead{ - TreeSize: resp.TreeSize, - Timestamp: resp.Timestamp, - } - - if len(resp.SHA256RootHash) != sha256.Size { - return nil, fmt.Errorf("sha256_root_hash is invalid length, expected %d got %d", sha256.Size, len(resp.SHA256RootHash)) - } - copy(sth.SHA256RootHash[:], resp.SHA256RootHash) - - ds, err := ct.UnmarshalDigitallySigned(bytes.NewReader(resp.TreeHeadSignature)) - if err != nil { - return nil, err - } - sth.TreeHeadSignature = *ds - err = c.VerifySTHSignature(*sth) - if err != nil { - return nil, err - } - return -} - -// VerifySTHSignature checks the signature in sth, returning any error encountered or nil if verification is -// successful. -func (c *LogClient) VerifySTHSignature(sth ct.SignedTreeHead) error { - if c.verifier == nil { - // Can't verify signatures without a verifier - return nil - } - return c.verifier.VerifySTHSignature(sth) -} - -// VerifySCTSignature checks the signature in sct for the given LogEntryType, with associated certificate chain. -func (c *LogClient) VerifySCTSignature(sct ct.SignedCertificateTimestamp, ctype ct.LogEntryType, certData []ct.ASN1Cert) error { - if c.verifier == nil { - // Can't verify signatures without a verifier - return nil - } - - if ctype == ct.PrecertLogEntryType { - // TODO(drysdale): cope with pre-certs, which need to have the - // following fields set: - // leaf.PrecertEntry.TBSCertificate - // leaf.PrecertEntry.IssuerKeyHash (SHA-256 of issuer's public key) - return errors.New("SCT verification for pre-certificates unimplemented") - } - // Build enough of a Merkle tree leaf for the verifier to work on. - leaf := ct.MerkleTreeLeaf{ - Version: sct.SCTVersion, - LeafType: ct.TimestampedEntryLeafType, - TimestampedEntry: ct.TimestampedEntry{ - Timestamp: sct.Timestamp, - EntryType: ctype, - X509Entry: certData[0], - Extensions: sct.Extensions}} - entry := ct.LogEntry{Leaf: leaf} - return c.verifier.VerifySCTSignature(sct, entry) -} - -// GetSTHConsistency retrieves the consistency proof between two snapshots. -func (c *LogClient) GetSTHConsistency(ctx context.Context, first, second uint64) ([][]byte, error) { - u := fmt.Sprintf("%s%s?first=%d&second=%d", c.uri, GetSTHConsistencyPath, first, second) - var resp getConsistencyProofResponse - if err := fetchAndParse(ctx, c.httpClient, u, &resp); err != nil { - return nil, err - } - return resp.Consistency, nil -} - -// GetProofByHash returns an audit path for the hash of an SCT. -func (c *LogClient) GetProofByHash(ctx context.Context, hash []byte, treeSize uint64) (*GetProofByHashResponse, error) { - b64Hash := url.QueryEscape(base64.StdEncoding.EncodeToString(hash)) - u := fmt.Sprintf("%s%s?tree_size=%d&hash=%v", c.uri, GetProofByHashPath, treeSize, b64Hash) - var resp GetProofByHashResponse - if err := fetchAndParse(ctx, c.httpClient, u, &resp); err != nil { - return nil, err - } - return &resp, nil -} diff --git a/vendor/github.com/google/certificate-transparency/go/serialization.go b/vendor/github.com/google/certificate-transparency/go/serialization.go deleted file mode 100644 index b83c90117898e..0000000000000 --- a/vendor/github.com/google/certificate-transparency/go/serialization.go +++ /dev/null @@ -1,691 +0,0 @@ -package ct - -import ( - "bytes" - "container/list" - "crypto" - "encoding/asn1" - "encoding/binary" - "encoding/json" - "errors" - "fmt" - "io" - "strings" -) - -// Variable size structure prefix-header byte lengths -const ( - CertificateLengthBytes = 3 - PreCertificateLengthBytes = 3 - ExtensionsLengthBytes = 2 - CertificateChainLengthBytes = 3 - SignatureLengthBytes = 2 - JSONLengthBytes = 3 -) - -// Max lengths -const ( - MaxCertificateLength = (1 << 24) - 1 - MaxExtensionsLength = (1 << 16) - 1 - MaxSCTInListLength = (1 << 16) - 1 - MaxSCTListLength = (1 << 16) - 1 -) - -func writeUint(w io.Writer, value uint64, numBytes int) error { - buf := make([]uint8, numBytes) - for i := 0; i < numBytes; i++ { - buf[numBytes-i-1] = uint8(value & 0xff) - value >>= 8 - } - if value != 0 { - return errors.New("numBytes was insufficiently large to represent value") - } - if _, err := w.Write(buf); err != nil { - return err - } - return nil -} - -func writeVarBytes(w io.Writer, value []byte, numLenBytes int) error { - if err := writeUint(w, uint64(len(value)), numLenBytes); err != nil { - return err - } - if _, err := w.Write(value); err != nil { - return err - } - return nil -} - -func readUint(r io.Reader, numBytes int) (uint64, error) { - var l uint64 - for i := 0; i < numBytes; i++ { - l <<= 8 - var t uint8 - if err := binary.Read(r, binary.BigEndian, &t); err != nil { - return 0, err - } - l |= uint64(t) - } - return l, nil -} - -// Reads a variable length array of bytes from |r|. |numLenBytes| specifies the -// number of (BigEndian) prefix-bytes which contain the length of the actual -// array data bytes that follow. -// Allocates an array to hold the contents and returns a slice view into it if -// the read was successful, or an error otherwise. -func readVarBytes(r io.Reader, numLenBytes int) ([]byte, error) { - switch { - case numLenBytes > 8: - return nil, fmt.Errorf("numLenBytes too large (%d)", numLenBytes) - case numLenBytes == 0: - return nil, errors.New("numLenBytes should be > 0") - } - l, err := readUint(r, numLenBytes) - if err != nil { - return nil, err - } - data := make([]byte, l) - if n, err := io.ReadFull(r, data); err != nil { - if err == io.EOF || err == io.ErrUnexpectedEOF { - return nil, fmt.Errorf("short read: expected %d but got %d", l, n) - } - return nil, err - } - return data, nil -} - -// Reads a list of ASN1Cert types from |r| -func readASN1CertList(r io.Reader, totalLenBytes int, elementLenBytes int) ([]ASN1Cert, error) { - listBytes, err := readVarBytes(r, totalLenBytes) - if err != nil { - return []ASN1Cert{}, err - } - list := list.New() - listReader := bytes.NewReader(listBytes) - var entry []byte - for err == nil { - entry, err = readVarBytes(listReader, elementLenBytes) - if err != nil { - if err != io.EOF { - return []ASN1Cert{}, err - } - } else { - list.PushBack(entry) - } - } - ret := make([]ASN1Cert, list.Len()) - i := 0 - for e := list.Front(); e != nil; e = e.Next() { - ret[i] = e.Value.([]byte) - i++ - } - return ret, nil -} - -// ReadTimestampedEntryInto parses the byte-stream representation of a -// TimestampedEntry from |r| and populates the struct |t| with the data. See -// RFC section 3.4 for details on the format. -// Returns a non-nil error if there was a problem. -func ReadTimestampedEntryInto(r io.Reader, t *TimestampedEntry) error { - var err error - if err = binary.Read(r, binary.BigEndian, &t.Timestamp); err != nil { - return err - } - if err = binary.Read(r, binary.BigEndian, &t.EntryType); err != nil { - return err - } - switch t.EntryType { - case X509LogEntryType: - if t.X509Entry, err = readVarBytes(r, CertificateLengthBytes); err != nil { - return err - } - case PrecertLogEntryType: - if err := binary.Read(r, binary.BigEndian, &t.PrecertEntry.IssuerKeyHash); err != nil { - return err - } - if t.PrecertEntry.TBSCertificate, err = readVarBytes(r, PreCertificateLengthBytes); err != nil { - return err - } - case XJSONLogEntryType: - if t.JSONData, err = readVarBytes(r, JSONLengthBytes); err != nil { - return err - } - default: - return fmt.Errorf("unknown EntryType: %d", t.EntryType) - } - t.Extensions, err = readVarBytes(r, ExtensionsLengthBytes) - return nil -} - -// SerializeTimestampedEntry writes timestamped entry to Writer. -// In case of error, w may contain garbage. -func SerializeTimestampedEntry(w io.Writer, t *TimestampedEntry) error { - if err := binary.Write(w, binary.BigEndian, t.Timestamp); err != nil { - return err - } - if err := binary.Write(w, binary.BigEndian, t.EntryType); err != nil { - return err - } - switch t.EntryType { - case X509LogEntryType: - if err := writeVarBytes(w, t.X509Entry, CertificateLengthBytes); err != nil { - return err - } - case PrecertLogEntryType: - if err := binary.Write(w, binary.BigEndian, t.PrecertEntry.IssuerKeyHash); err != nil { - return err - } - if err := writeVarBytes(w, t.PrecertEntry.TBSCertificate, PreCertificateLengthBytes); err != nil { - return err - } - case XJSONLogEntryType: - // TODO: Pending google/certificate-transparency#1243, replace - // with ObjectHash once supported by CT server. - //jsonhash := objecthash.CommonJSONHash(string(t.JSONData)) - if err := writeVarBytes(w, []byte(t.JSONData), JSONLengthBytes); err != nil { - return err - } - default: - return fmt.Errorf("unknown EntryType: %d", t.EntryType) - } - writeVarBytes(w, t.Extensions, ExtensionsLengthBytes) - return nil -} - -// ReadMerkleTreeLeaf parses the byte-stream representation of a MerkleTreeLeaf -// and returns a pointer to a new MerkleTreeLeaf structure containing the -// parsed data. -// See RFC section 3.4 for details on the format. -// Returns a pointer to a new MerkleTreeLeaf or non-nil error if there was a -// problem -func ReadMerkleTreeLeaf(r io.Reader) (*MerkleTreeLeaf, error) { - var m MerkleTreeLeaf - if err := binary.Read(r, binary.BigEndian, &m.Version); err != nil { - return nil, err - } - if m.Version != V1 { - return nil, fmt.Errorf("unknown Version %d", m.Version) - } - if err := binary.Read(r, binary.BigEndian, &m.LeafType); err != nil { - return nil, err - } - if m.LeafType != TimestampedEntryLeafType { - return nil, fmt.Errorf("unknown LeafType %d", m.LeafType) - } - if err := ReadTimestampedEntryInto(r, &m.TimestampedEntry); err != nil { - return nil, err - } - return &m, nil -} - -// UnmarshalX509ChainArray unmarshalls the contents of the "chain:" entry in a -// GetEntries response in the case where the entry refers to an X509 leaf. -func UnmarshalX509ChainArray(b []byte) ([]ASN1Cert, error) { - return readASN1CertList(bytes.NewReader(b), CertificateChainLengthBytes, CertificateLengthBytes) -} - -// UnmarshalPrecertChainArray unmarshalls the contents of the "chain:" entry in -// a GetEntries response in the case where the entry refers to a Precertificate -// leaf. -func UnmarshalPrecertChainArray(b []byte) ([]ASN1Cert, error) { - var chain []ASN1Cert - - reader := bytes.NewReader(b) - // read the pre-cert entry: - precert, err := readVarBytes(reader, CertificateLengthBytes) - if err != nil { - return chain, err - } - chain = append(chain, precert) - // and then read and return the chain up to the root: - remainingChain, err := readASN1CertList(reader, CertificateChainLengthBytes, CertificateLengthBytes) - if err != nil { - return chain, err - } - chain = append(chain, remainingChain...) - return chain, nil -} - -// UnmarshalDigitallySigned reconstructs a DigitallySigned structure from a Reader -func UnmarshalDigitallySigned(r io.Reader) (*DigitallySigned, error) { - var h byte - if err := binary.Read(r, binary.BigEndian, &h); err != nil { - return nil, fmt.Errorf("failed to read HashAlgorithm: %v", err) - } - - var s byte - if err := binary.Read(r, binary.BigEndian, &s); err != nil { - return nil, fmt.Errorf("failed to read SignatureAlgorithm: %v", err) - } - - sig, err := readVarBytes(r, SignatureLengthBytes) - if err != nil { - return nil, fmt.Errorf("failed to read Signature bytes: %v", err) - } - - return &DigitallySigned{ - HashAlgorithm: HashAlgorithm(h), - SignatureAlgorithm: SignatureAlgorithm(s), - Signature: sig, - }, nil -} - -func marshalDigitallySignedHere(ds DigitallySigned, here []byte) ([]byte, error) { - sigLen := len(ds.Signature) - dsOutLen := 2 + SignatureLengthBytes + sigLen - if here == nil { - here = make([]byte, dsOutLen) - } - if len(here) < dsOutLen { - return nil, ErrNotEnoughBuffer - } - here = here[0:dsOutLen] - - here[0] = byte(ds.HashAlgorithm) - here[1] = byte(ds.SignatureAlgorithm) - binary.BigEndian.PutUint16(here[2:4], uint16(sigLen)) - copy(here[4:], ds.Signature) - - return here, nil -} - -// MarshalDigitallySigned marshalls a DigitallySigned structure into a byte array -func MarshalDigitallySigned(ds DigitallySigned) ([]byte, error) { - return marshalDigitallySignedHere(ds, nil) -} - -func checkCertificateFormat(cert ASN1Cert) error { - if len(cert) == 0 { - return errors.New("certificate is zero length") - } - if len(cert) > MaxCertificateLength { - return errors.New("certificate too large") - } - return nil -} - -func checkExtensionsFormat(ext CTExtensions) error { - if len(ext) > MaxExtensionsLength { - return errors.New("extensions too large") - } - return nil -} - -func serializeV1CertSCTSignatureInput(timestamp uint64, cert ASN1Cert, ext CTExtensions) ([]byte, error) { - if err := checkCertificateFormat(cert); err != nil { - return nil, err - } - if err := checkExtensionsFormat(ext); err != nil { - return nil, err - } - var buf bytes.Buffer - if err := binary.Write(&buf, binary.BigEndian, V1); err != nil { - return nil, err - } - if err := binary.Write(&buf, binary.BigEndian, CertificateTimestampSignatureType); err != nil { - return nil, err - } - if err := binary.Write(&buf, binary.BigEndian, timestamp); err != nil { - return nil, err - } - if err := binary.Write(&buf, binary.BigEndian, X509LogEntryType); err != nil { - return nil, err - } - if err := writeVarBytes(&buf, cert, CertificateLengthBytes); err != nil { - return nil, err - } - if err := writeVarBytes(&buf, ext, ExtensionsLengthBytes); err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -func serializeV1JSONSCTSignatureInput(timestamp uint64, j []byte) ([]byte, error) { - var buf bytes.Buffer - if err := binary.Write(&buf, binary.BigEndian, V1); err != nil { - return nil, err - } - if err := binary.Write(&buf, binary.BigEndian, CertificateTimestampSignatureType); err != nil { - return nil, err - } - if err := binary.Write(&buf, binary.BigEndian, timestamp); err != nil { - return nil, err - } - if err := binary.Write(&buf, binary.BigEndian, XJSONLogEntryType); err != nil { - return nil, err - } - if err := writeVarBytes(&buf, j, JSONLengthBytes); err != nil { - return nil, err - } - if err := writeVarBytes(&buf, nil, ExtensionsLengthBytes); err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -func serializeV1PrecertSCTSignatureInput(timestamp uint64, issuerKeyHash [issuerKeyHashLength]byte, tbs []byte, ext CTExtensions) ([]byte, error) { - if err := checkCertificateFormat(tbs); err != nil { - return nil, err - } - if err := checkExtensionsFormat(ext); err != nil { - return nil, err - } - var buf bytes.Buffer - if err := binary.Write(&buf, binary.BigEndian, V1); err != nil { - return nil, err - } - if err := binary.Write(&buf, binary.BigEndian, CertificateTimestampSignatureType); err != nil { - return nil, err - } - if err := binary.Write(&buf, binary.BigEndian, timestamp); err != nil { - return nil, err - } - if err := binary.Write(&buf, binary.BigEndian, PrecertLogEntryType); err != nil { - return nil, err - } - if _, err := buf.Write(issuerKeyHash[:]); err != nil { - return nil, err - } - if err := writeVarBytes(&buf, tbs, CertificateLengthBytes); err != nil { - return nil, err - } - if err := writeVarBytes(&buf, ext, ExtensionsLengthBytes); err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -func serializeV1SCTSignatureInput(sct SignedCertificateTimestamp, entry LogEntry) ([]byte, error) { - if sct.SCTVersion != V1 { - return nil, fmt.Errorf("unsupported SCT version, expected V1, but got %s", sct.SCTVersion) - } - if entry.Leaf.LeafType != TimestampedEntryLeafType { - return nil, fmt.Errorf("Unsupported leaf type %s", entry.Leaf.LeafType) - } - switch entry.Leaf.TimestampedEntry.EntryType { - case X509LogEntryType: - return serializeV1CertSCTSignatureInput(sct.Timestamp, entry.Leaf.TimestampedEntry.X509Entry, entry.Leaf.TimestampedEntry.Extensions) - case PrecertLogEntryType: - return serializeV1PrecertSCTSignatureInput(sct.Timestamp, entry.Leaf.TimestampedEntry.PrecertEntry.IssuerKeyHash, - entry.Leaf.TimestampedEntry.PrecertEntry.TBSCertificate, - entry.Leaf.TimestampedEntry.Extensions) - case XJSONLogEntryType: - return serializeV1JSONSCTSignatureInput(sct.Timestamp, entry.Leaf.TimestampedEntry.JSONData) - default: - return nil, fmt.Errorf("unknown TimestampedEntryLeafType %s", entry.Leaf.TimestampedEntry.EntryType) - } -} - -// SerializeSCTSignatureInput serializes the passed in sct and log entry into -// the correct format for signing. -func SerializeSCTSignatureInput(sct SignedCertificateTimestamp, entry LogEntry) ([]byte, error) { - switch sct.SCTVersion { - case V1: - return serializeV1SCTSignatureInput(sct, entry) - default: - return nil, fmt.Errorf("unknown SCT version %d", sct.SCTVersion) - } -} - -// SerializedLength will return the space (in bytes) -func (sct SignedCertificateTimestamp) SerializedLength() (int, error) { - switch sct.SCTVersion { - case V1: - extLen := len(sct.Extensions) - sigLen := len(sct.Signature.Signature) - return 1 + 32 + 8 + 2 + extLen + 2 + 2 + sigLen, nil - default: - return 0, ErrInvalidVersion - } -} - -func serializeV1SCTHere(sct SignedCertificateTimestamp, here []byte) ([]byte, error) { - if sct.SCTVersion != V1 { - return nil, ErrInvalidVersion - } - sctLen, err := sct.SerializedLength() - if err != nil { - return nil, err - } - if here == nil { - here = make([]byte, sctLen) - } - if len(here) < sctLen { - return nil, ErrNotEnoughBuffer - } - if err := checkExtensionsFormat(sct.Extensions); err != nil { - return nil, err - } - - here = here[0:sctLen] - - // Write Version - here[0] = byte(sct.SCTVersion) - - // Write LogID - copy(here[1:33], sct.LogID[:]) - - // Write Timestamp - binary.BigEndian.PutUint64(here[33:41], sct.Timestamp) - - // Write Extensions - extLen := len(sct.Extensions) - binary.BigEndian.PutUint16(here[41:43], uint16(extLen)) - n := 43 + extLen - copy(here[43:n], sct.Extensions) - - // Write Signature - _, err = marshalDigitallySignedHere(sct.Signature, here[n:]) - if err != nil { - return nil, err - } - return here, nil -} - -// SerializeSCTHere serializes the passed in sct into the format specified -// by RFC6962 section 3.2. -// If a bytes slice here is provided then it will attempt to serialize into the -// provided byte slice, ErrNotEnoughBuffer will be returned if the buffer is -// too small. -// If a nil byte slice is provided, a buffer for will be allocated for you -// The returned slice will be sliced to the correct length. -func SerializeSCTHere(sct SignedCertificateTimestamp, here []byte) ([]byte, error) { - switch sct.SCTVersion { - case V1: - return serializeV1SCTHere(sct, here) - default: - return nil, fmt.Errorf("unknown SCT version %d", sct.SCTVersion) - } -} - -// SerializeSCT serializes the passed in sct into the format specified -// by RFC6962 section 3.2 -// Equivalent to SerializeSCTHere(sct, nil) -func SerializeSCT(sct SignedCertificateTimestamp) ([]byte, error) { - return SerializeSCTHere(sct, nil) -} - -func deserializeSCTV1(r io.Reader, sct *SignedCertificateTimestamp) error { - if err := binary.Read(r, binary.BigEndian, &sct.LogID); err != nil { - return err - } - if err := binary.Read(r, binary.BigEndian, &sct.Timestamp); err != nil { - return err - } - ext, err := readVarBytes(r, ExtensionsLengthBytes) - if err != nil { - return err - } - sct.Extensions = ext - ds, err := UnmarshalDigitallySigned(r) - if err != nil { - return err - } - sct.Signature = *ds - return nil -} - -// DeserializeSCT reads an SCT from Reader. -func DeserializeSCT(r io.Reader) (*SignedCertificateTimestamp, error) { - var sct SignedCertificateTimestamp - if err := binary.Read(r, binary.BigEndian, &sct.SCTVersion); err != nil { - return nil, err - } - switch sct.SCTVersion { - case V1: - return &sct, deserializeSCTV1(r, &sct) - default: - return nil, fmt.Errorf("unknown SCT version %d", sct.SCTVersion) - } -} - -func serializeV1STHSignatureInput(sth SignedTreeHead) ([]byte, error) { - if sth.Version != V1 { - return nil, fmt.Errorf("invalid STH version %d", sth.Version) - } - if sth.TreeSize < 0 { - return nil, fmt.Errorf("invalid tree size %d", sth.TreeSize) - } - if len(sth.SHA256RootHash) != crypto.SHA256.Size() { - return nil, fmt.Errorf("invalid TreeHash length, got %d expected %d", len(sth.SHA256RootHash), crypto.SHA256.Size()) - } - - var buf bytes.Buffer - if err := binary.Write(&buf, binary.BigEndian, V1); err != nil { - return nil, err - } - if err := binary.Write(&buf, binary.BigEndian, TreeHashSignatureType); err != nil { - return nil, err - } - if err := binary.Write(&buf, binary.BigEndian, sth.Timestamp); err != nil { - return nil, err - } - if err := binary.Write(&buf, binary.BigEndian, sth.TreeSize); err != nil { - return nil, err - } - if err := binary.Write(&buf, binary.BigEndian, sth.SHA256RootHash); err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -// SerializeSTHSignatureInput serializes the passed in sth into the correct -// format for signing. -func SerializeSTHSignatureInput(sth SignedTreeHead) ([]byte, error) { - switch sth.Version { - case V1: - return serializeV1STHSignatureInput(sth) - default: - return nil, fmt.Errorf("unsupported STH version %d", sth.Version) - } -} - -// SCTListSerializedLength determines the length of the required buffer should a SCT List need to be serialized -func SCTListSerializedLength(scts []SignedCertificateTimestamp) (int, error) { - if len(scts) == 0 { - return 0, fmt.Errorf("SCT List empty") - } - - sctListLen := 0 - for i, sct := range scts { - n, err := sct.SerializedLength() - if err != nil { - return 0, fmt.Errorf("unable to determine length of SCT in position %d: %v", i, err) - } - if n > MaxSCTInListLength { - return 0, fmt.Errorf("SCT in position %d too large: %d", i, n) - } - sctListLen += 2 + n - } - - return sctListLen, nil -} - -// SerializeSCTList serializes the passed-in slice of SignedCertificateTimestamp into a -// byte slice as a SignedCertificateTimestampList (see RFC6962 Section 3.3) -func SerializeSCTList(scts []SignedCertificateTimestamp) ([]byte, error) { - size, err := SCTListSerializedLength(scts) - if err != nil { - return nil, err - } - fullSize := 2 + size // 2 bytes for length + size of SCT list - if fullSize > MaxSCTListLength { - return nil, fmt.Errorf("SCT List too large to serialize: %d", fullSize) - } - buf := new(bytes.Buffer) - buf.Grow(fullSize) - if err = writeUint(buf, uint64(size), 2); err != nil { - return nil, err - } - for _, sct := range scts { - serialized, err := SerializeSCT(sct) - if err != nil { - return nil, err - } - if err = writeVarBytes(buf, serialized, 2); err != nil { - return nil, err - } - } - return asn1.Marshal(buf.Bytes()) // transform to Octet String -} - -// SerializeMerkleTreeLeaf writes MerkleTreeLeaf to Writer. -// In case of error, w may contain garbage. -func SerializeMerkleTreeLeaf(w io.Writer, m *MerkleTreeLeaf) error { - if m.Version != V1 { - return fmt.Errorf("unknown Version %d", m.Version) - } - if err := binary.Write(w, binary.BigEndian, m.Version); err != nil { - return err - } - if m.LeafType != TimestampedEntryLeafType { - return fmt.Errorf("unknown LeafType %d", m.LeafType) - } - if err := binary.Write(w, binary.BigEndian, m.LeafType); err != nil { - return err - } - if err := SerializeTimestampedEntry(w, &m.TimestampedEntry); err != nil { - return err - } - return nil -} - -// CreateX509MerkleTreeLeaf generates a MerkleTreeLeaf for an X509 cert -func CreateX509MerkleTreeLeaf(cert ASN1Cert, timestamp uint64) *MerkleTreeLeaf { - return &MerkleTreeLeaf{ - Version: V1, - LeafType: TimestampedEntryLeafType, - TimestampedEntry: TimestampedEntry{ - Timestamp: timestamp, - EntryType: X509LogEntryType, - X509Entry: cert, - }, - } -} - -// CreateJSONMerkleTreeLeaf creates the merkle tree leaf for json data. -func CreateJSONMerkleTreeLeaf(data interface{}, timestamp uint64) *MerkleTreeLeaf { - jsonData, err := json.Marshal(AddJSONRequest{Data: data}) - if err != nil { - return nil - } - // Match the JSON serialization implemented by json-c - jsonStr := strings.Replace(string(jsonData), ":", ": ", -1) - jsonStr = strings.Replace(jsonStr, ",", ", ", -1) - jsonStr = strings.Replace(jsonStr, "{", "{ ", -1) - jsonStr = strings.Replace(jsonStr, "}", " }", -1) - jsonStr = strings.Replace(jsonStr, "/", `\/`, -1) - // TODO: Pending google/certificate-transparency#1243, replace with - // ObjectHash once supported by CT server. - - return &MerkleTreeLeaf{ - Version: V1, - LeafType: TimestampedEntryLeafType, - TimestampedEntry: TimestampedEntry{ - Timestamp: timestamp, - EntryType: XJSONLogEntryType, - JSONData: []byte(jsonStr), - }, - } -} diff --git a/vendor/github.com/google/certificate-transparency/go/signatures.go b/vendor/github.com/google/certificate-transparency/go/signatures.go deleted file mode 100644 index 600db245493bd..0000000000000 --- a/vendor/github.com/google/certificate-transparency/go/signatures.go +++ /dev/null @@ -1,131 +0,0 @@ -package ct - -import ( - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rsa" - "crypto/sha256" - "crypto/x509" - "encoding/asn1" - "encoding/pem" - "errors" - "flag" - "fmt" - "log" - "math/big" -) - -var allowVerificationWithNonCompliantKeys = flag.Bool("allow_verification_with_non_compliant_keys", false, - "Allow a SignatureVerifier to use keys which are technically non-compliant with RFC6962.") - -// PublicKeyFromPEM parses a PEM formatted block and returns the public key contained within and any remaining unread bytes, or an error. -func PublicKeyFromPEM(b []byte) (crypto.PublicKey, SHA256Hash, []byte, error) { - p, rest := pem.Decode(b) - if p == nil { - return nil, [sha256.Size]byte{}, rest, fmt.Errorf("no PEM block found in %s", string(b)) - } - k, err := x509.ParsePKIXPublicKey(p.Bytes) - return k, sha256.Sum256(p.Bytes), rest, err -} - -// SignatureVerifier can verify signatures on SCTs and STHs -type SignatureVerifier struct { - pubKey crypto.PublicKey -} - -// NewSignatureVerifier creates a new SignatureVerifier using the passed in PublicKey. -func NewSignatureVerifier(pk crypto.PublicKey) (*SignatureVerifier, error) { - switch pkType := pk.(type) { - case *rsa.PublicKey: - if pkType.N.BitLen() < 2048 { - e := fmt.Errorf("public key is RSA with < 2048 bits (size:%d)", pkType.N.BitLen()) - if !(*allowVerificationWithNonCompliantKeys) { - return nil, e - } - log.Printf("WARNING: %v", e) - } - case *ecdsa.PublicKey: - params := *(pkType.Params()) - if params != *elliptic.P256().Params() { - e := fmt.Errorf("public is ECDSA, but not on the P256 curve") - if !(*allowVerificationWithNonCompliantKeys) { - return nil, e - } - log.Printf("WARNING: %v", e) - - } - default: - return nil, fmt.Errorf("Unsupported public key type %v", pkType) - } - - return &SignatureVerifier{ - pubKey: pk, - }, nil -} - -// verifySignature verifies that the passed in signature over data was created by our PublicKey. -// Currently, only SHA256 is supported as a HashAlgorithm, and only ECDSA and RSA signatures are supported. -func (s SignatureVerifier) verifySignature(data []byte, sig DigitallySigned) error { - if sig.HashAlgorithm != SHA256 { - return fmt.Errorf("unsupported HashAlgorithm in signature: %v", sig.HashAlgorithm) - } - - hasherType := crypto.SHA256 - hasher := hasherType.New() - if _, err := hasher.Write(data); err != nil { - return fmt.Errorf("failed to write to hasher: %v", err) - } - hash := hasher.Sum([]byte{}) - - switch sig.SignatureAlgorithm { - case RSA: - rsaKey, ok := s.pubKey.(*rsa.PublicKey) - if !ok { - return fmt.Errorf("cannot verify RSA signature with %T key", s.pubKey) - } - if err := rsa.VerifyPKCS1v15(rsaKey, hasherType, hash, sig.Signature); err != nil { - return fmt.Errorf("failed to verify rsa signature: %v", err) - } - case ECDSA: - ecdsaKey, ok := s.pubKey.(*ecdsa.PublicKey) - if !ok { - return fmt.Errorf("cannot verify ECDSA signature with %T key", s.pubKey) - } - var ecdsaSig struct { - R, S *big.Int - } - rest, err := asn1.Unmarshal(sig.Signature, &ecdsaSig) - if err != nil { - return fmt.Errorf("failed to unmarshal ECDSA signature: %v", err) - } - if len(rest) != 0 { - log.Printf("Garbage following signature %v", rest) - } - - if !ecdsa.Verify(ecdsaKey, hash, ecdsaSig.R, ecdsaSig.S) { - return errors.New("failed to verify ecdsa signature") - } - default: - return fmt.Errorf("unsupported signature type %v", sig.SignatureAlgorithm) - } - return nil -} - -// VerifySCTSignature verifies that the SCT's signature is valid for the given LogEntry -func (s SignatureVerifier) VerifySCTSignature(sct SignedCertificateTimestamp, entry LogEntry) error { - sctData, err := SerializeSCTSignatureInput(sct, entry) - if err != nil { - return err - } - return s.verifySignature(sctData, sct.Signature) -} - -// VerifySTHSignature verifies that the STH's signature is valid. -func (s SignatureVerifier) VerifySTHSignature(sth SignedTreeHead) error { - sthData, err := SerializeSTHSignatureInput(sth) - if err != nil { - return err - } - return s.verifySignature(sthData, sth.TreeHeadSignature) -} diff --git a/vendor/github.com/google/certificate-transparency/go/types.go b/vendor/github.com/google/certificate-transparency/go/types.go deleted file mode 100644 index 507e624a1df4c..0000000000000 --- a/vendor/github.com/google/certificate-transparency/go/types.go +++ /dev/null @@ -1,374 +0,0 @@ -package ct - -import ( - "bytes" - "crypto/sha256" - "encoding/base64" - "encoding/json" - "fmt" - - "github.com/google/certificate-transparency/go/x509" -) - -const ( - issuerKeyHashLength = 32 -) - -/////////////////////////////////////////////////////////////////////////////// -// The following structures represent those outlined in the RFC6962 document: -/////////////////////////////////////////////////////////////////////////////// - -// LogEntryType represents the LogEntryType enum from section 3.1 of the RFC: -// enum { x509_entry(0), precert_entry(1), (65535) } LogEntryType; -type LogEntryType uint16 - -func (e LogEntryType) String() string { - switch e { - case X509LogEntryType: - return "X509LogEntryType" - case PrecertLogEntryType: - return "PrecertLogEntryType" - case XJSONLogEntryType: - return "XJSONLogEntryType" - } - panic(fmt.Sprintf("No string defined for LogEntryType constant value %d", e)) -} - -// LogEntryType constants, see section 3.1 of RFC6962. -const ( - X509LogEntryType LogEntryType = 0 - PrecertLogEntryType LogEntryType = 1 - XJSONLogEntryType LogEntryType = 0x8000 // Experimental. Don't rely on this! -) - -// MerkleLeafType represents the MerkleLeafType enum from section 3.4 of the -// RFC: enum { timestamped_entry(0), (255) } MerkleLeafType; -type MerkleLeafType uint8 - -func (m MerkleLeafType) String() string { - switch m { - case TimestampedEntryLeafType: - return "TimestampedEntryLeafType" - default: - return fmt.Sprintf("UnknownLeafType(%d)", m) - } -} - -// MerkleLeafType constants, see section 3.4 of the RFC. -const ( - TimestampedEntryLeafType MerkleLeafType = 0 // Entry type for an SCT -) - -// Version represents the Version enum from section 3.2 of the RFC: -// enum { v1(0), (255) } Version; -type Version uint8 - -func (v Version) String() string { - switch v { - case V1: - return "V1" - default: - return fmt.Sprintf("UnknownVersion(%d)", v) - } -} - -// CT Version constants, see section 3.2 of the RFC. -const ( - V1 Version = 0 -) - -// SignatureType differentiates STH signatures from SCT signatures, see RFC -// section 3.2 -type SignatureType uint8 - -func (st SignatureType) String() string { - switch st { - case CertificateTimestampSignatureType: - return "CertificateTimestamp" - case TreeHashSignatureType: - return "TreeHash" - default: - return fmt.Sprintf("UnknownSignatureType(%d)", st) - } -} - -// SignatureType constants, see RFC section 3.2 -const ( - CertificateTimestampSignatureType SignatureType = 0 - TreeHashSignatureType SignatureType = 1 -) - -// ASN1Cert type for holding the raw DER bytes of an ASN.1 Certificate -// (section 3.1) -type ASN1Cert []byte - -// PreCert represents a Precertificate (section 3.2) -type PreCert struct { - IssuerKeyHash [issuerKeyHashLength]byte - TBSCertificate []byte -} - -// CTExtensions is a representation of the raw bytes of any CtExtension -// structure (see section 3.2) -type CTExtensions []byte - -// MerkleTreeNode represents an internal node in the CT tree -type MerkleTreeNode []byte - -// ConsistencyProof represents a CT consistency proof (see sections 2.1.2 and -// 4.4) -type ConsistencyProof []MerkleTreeNode - -// AuditPath represents a CT inclusion proof (see sections 2.1.1 and 4.5) -type AuditPath []MerkleTreeNode - -// LeafInput represents a serialized MerkleTreeLeaf structure -type LeafInput []byte - -// HashAlgorithm from the DigitallySigned struct -type HashAlgorithm byte - -// HashAlgorithm constants -const ( - None HashAlgorithm = 0 - MD5 HashAlgorithm = 1 - SHA1 HashAlgorithm = 2 - SHA224 HashAlgorithm = 3 - SHA256 HashAlgorithm = 4 - SHA384 HashAlgorithm = 5 - SHA512 HashAlgorithm = 6 -) - -func (h HashAlgorithm) String() string { - switch h { - case None: - return "None" - case MD5: - return "MD5" - case SHA1: - return "SHA1" - case SHA224: - return "SHA224" - case SHA256: - return "SHA256" - case SHA384: - return "SHA384" - case SHA512: - return "SHA512" - default: - return fmt.Sprintf("UNKNOWN(%d)", h) - } -} - -// SignatureAlgorithm from the the DigitallySigned struct -type SignatureAlgorithm byte - -// SignatureAlgorithm constants -const ( - Anonymous SignatureAlgorithm = 0 - RSA SignatureAlgorithm = 1 - DSA SignatureAlgorithm = 2 - ECDSA SignatureAlgorithm = 3 -) - -func (s SignatureAlgorithm) String() string { - switch s { - case Anonymous: - return "Anonymous" - case RSA: - return "RSA" - case DSA: - return "DSA" - case ECDSA: - return "ECDSA" - default: - return fmt.Sprintf("UNKNOWN(%d)", s) - } -} - -// DigitallySigned represents an RFC5246 DigitallySigned structure -type DigitallySigned struct { - HashAlgorithm HashAlgorithm - SignatureAlgorithm SignatureAlgorithm - Signature []byte -} - -// FromBase64String populates the DigitallySigned structure from the base64 data passed in. -// Returns an error if the base64 data is invalid. -func (d *DigitallySigned) FromBase64String(b64 string) error { - raw, err := base64.StdEncoding.DecodeString(b64) - if err != nil { - return fmt.Errorf("failed to unbase64 DigitallySigned: %v", err) - } - ds, err := UnmarshalDigitallySigned(bytes.NewReader(raw)) - if err != nil { - return fmt.Errorf("failed to unmarshal DigitallySigned: %v", err) - } - *d = *ds - return nil -} - -// Base64String returns the base64 representation of the DigitallySigned struct. -func (d DigitallySigned) Base64String() (string, error) { - b, err := MarshalDigitallySigned(d) - if err != nil { - return "", err - } - return base64.StdEncoding.EncodeToString(b), nil -} - -// MarshalJSON implements the json.Marshaller interface. -func (d DigitallySigned) MarshalJSON() ([]byte, error) { - b64, err := d.Base64String() - if err != nil { - return []byte{}, err - } - return []byte(`"` + b64 + `"`), nil -} - -// UnmarshalJSON implements the json.Unmarshaler interface. -func (d *DigitallySigned) UnmarshalJSON(b []byte) error { - var content string - if err := json.Unmarshal(b, &content); err != nil { - return fmt.Errorf("failed to unmarshal DigitallySigned: %v", err) - } - return d.FromBase64String(content) -} - -// LogEntry represents the contents of an entry in a CT log, see section 3.1. -type LogEntry struct { - Index int64 - Leaf MerkleTreeLeaf - X509Cert *x509.Certificate - Precert *Precertificate - JSONData []byte - Chain []ASN1Cert -} - -// SHA256Hash represents the output from the SHA256 hash function. -type SHA256Hash [sha256.Size]byte - -// FromBase64String populates the SHA256 struct with the contents of the base64 data passed in. -func (s *SHA256Hash) FromBase64String(b64 string) error { - bs, err := base64.StdEncoding.DecodeString(b64) - if err != nil { - return fmt.Errorf("failed to unbase64 LogID: %v", err) - } - if len(bs) != sha256.Size { - return fmt.Errorf("invalid SHA256 length, expected 32 but got %d", len(bs)) - } - copy(s[:], bs) - return nil -} - -// Base64String returns the base64 representation of this SHA256Hash. -func (s SHA256Hash) Base64String() string { - return base64.StdEncoding.EncodeToString(s[:]) -} - -// MarshalJSON implements the json.Marshaller interface for SHA256Hash. -func (s SHA256Hash) MarshalJSON() ([]byte, error) { - return []byte(`"` + s.Base64String() + `"`), nil -} - -// UnmarshalJSON implements the json.Unmarshaller interface. -func (s *SHA256Hash) UnmarshalJSON(b []byte) error { - var content string - if err := json.Unmarshal(b, &content); err != nil { - return fmt.Errorf("failed to unmarshal SHA256Hash: %v", err) - } - return s.FromBase64String(content) -} - -// SignedTreeHead represents the structure returned by the get-sth CT method -// after base64 decoding. See sections 3.5 and 4.3 in the RFC) -type SignedTreeHead struct { - Version Version `json:"sth_version"` // The version of the protocol to which the STH conforms - TreeSize uint64 `json:"tree_size"` // The number of entries in the new tree - Timestamp uint64 `json:"timestamp"` // The time at which the STH was created - SHA256RootHash SHA256Hash `json:"sha256_root_hash"` // The root hash of the log's Merkle tree - TreeHeadSignature DigitallySigned `json:"tree_head_signature"` // The Log's signature for this STH (see RFC section 3.5) - LogID SHA256Hash `json:"log_id"` // The SHA256 hash of the log's public key -} - -// SignedCertificateTimestamp represents the structure returned by the -// add-chain and add-pre-chain methods after base64 decoding. (see RFC sections -// 3.2 ,4.1 and 4.2) -type SignedCertificateTimestamp struct { - SCTVersion Version // The version of the protocol to which the SCT conforms - LogID SHA256Hash // the SHA-256 hash of the log's public key, calculated over - // the DER encoding of the key represented as SubjectPublicKeyInfo. - Timestamp uint64 // Timestamp (in ms since unix epoc) at which the SCT was issued - Extensions CTExtensions // For future extensions to the protocol - Signature DigitallySigned // The Log's signature for this SCT -} - -func (s SignedCertificateTimestamp) String() string { - return fmt.Sprintf("{Version:%d LogId:%s Timestamp:%d Extensions:'%s' Signature:%v}", s.SCTVersion, - base64.StdEncoding.EncodeToString(s.LogID[:]), - s.Timestamp, - s.Extensions, - s.Signature) -} - -// TimestampedEntry is part of the MerkleTreeLeaf structure. -// See RFC section 3.4 -type TimestampedEntry struct { - Timestamp uint64 - EntryType LogEntryType - X509Entry ASN1Cert - JSONData []byte - PrecertEntry PreCert - Extensions CTExtensions -} - -// MerkleTreeLeaf represents the deserialized sructure of the hash input for the -// leaves of a log's Merkle tree. See RFC section 3.4 -type MerkleTreeLeaf struct { - Version Version // the version of the protocol to which the MerkleTreeLeaf corresponds - LeafType MerkleLeafType // The type of the leaf input, currently only TimestampedEntry can exist - TimestampedEntry TimestampedEntry // The entry data itself -} - -// Precertificate represents the parsed CT Precertificate structure. -type Precertificate struct { - // Raw DER bytes of the precert - Raw []byte - // SHA256 hash of the issuing key - IssuerKeyHash [issuerKeyHashLength]byte - // Parsed TBSCertificate structure (held in an x509.Certificate for ease of - // access. - TBSCertificate x509.Certificate -} - -// X509Certificate returns the X.509 Certificate contained within the -// MerkleTreeLeaf. -// Returns a pointer to an x509.Certificate or a non-nil error. -func (m *MerkleTreeLeaf) X509Certificate() (*x509.Certificate, error) { - return x509.ParseCertificate(m.TimestampedEntry.X509Entry) -} - -type sctError int - -// Preallocate errors for performance -var ( - ErrInvalidVersion error = sctError(1) - ErrNotEnoughBuffer error = sctError(2) -) - -func (e sctError) Error() string { - switch e { - case ErrInvalidVersion: - return "invalid SCT version detected" - case ErrNotEnoughBuffer: - return "provided buffer was too small" - default: - return "unknown error" - } -} - -// AddJSONRequest represents the JSON request body sent ot the add-json CT -// method. -type AddJSONRequest struct { - Data interface{} `json:"data"` -} diff --git a/vendor/github.com/google/certificate-transparency/go/x509/pkcs8.go b/vendor/github.com/google/certificate-transparency/go/x509/pkcs8.go deleted file mode 100755 index c6ad70b3e6fba..0000000000000 --- a/vendor/github.com/google/certificate-transparency/go/x509/pkcs8.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package x509 - -import ( - // START CT CHANGES - "github.com/google/certificate-transparency/go/asn1" - "github.com/google/certificate-transparency/go/x509/pkix" - // END CT CHANGES - "errors" - "fmt" -) - -// pkcs8 reflects an ASN.1, PKCS#8 PrivateKey. See -// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-8/pkcs-8v1_2.asn -// and RFC5208. -type pkcs8 struct { - Version int - Algo pkix.AlgorithmIdentifier - PrivateKey []byte - // optional attributes omitted. -} - -// ParsePKCS8PrivateKey parses an unencrypted, PKCS#8 private key. See -// http://www.rsa.com/rsalabs/node.asp?id=2130 and RFC5208. -func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) { - var privKey pkcs8 - if _, err := asn1.Unmarshal(der, &privKey); err != nil { - return nil, err - } - switch { - case privKey.Algo.Algorithm.Equal(oidPublicKeyRSA): - key, err = ParsePKCS1PrivateKey(privKey.PrivateKey) - if err != nil { - return nil, errors.New("x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error()) - } - return key, nil - - case privKey.Algo.Algorithm.Equal(oidPublicKeyECDSA): - bytes := privKey.Algo.Parameters.FullBytes - namedCurveOID := new(asn1.ObjectIdentifier) - if _, err := asn1.Unmarshal(bytes, namedCurveOID); err != nil { - namedCurveOID = nil - } - key, err = parseECPrivateKey(namedCurveOID, privKey.PrivateKey) - if err != nil { - return nil, errors.New("x509: failed to parse EC private key embedded in PKCS#8: " + err.Error()) - } - return key, nil - - default: - return nil, fmt.Errorf("x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm) - } -} diff --git a/vendor/github.com/google/certificate-transparency/go/x509/pkix/pkix.go b/vendor/github.com/google/certificate-transparency/go/x509/pkix/pkix.go deleted file mode 100755 index 67db6ae5e3ee5..0000000000000 --- a/vendor/github.com/google/certificate-transparency/go/x509/pkix/pkix.go +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package pkix contains shared, low level structures used for ASN.1 parsing -// and serialization of X.509 certificates, CRL and OCSP. -package pkix - -import ( - // START CT CHANGES - "github.com/google/certificate-transparency/go/asn1" - // END CT CHANGES - "math/big" - "time" -) - -// AlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC -// 5280, section 4.1.1.2. -type AlgorithmIdentifier struct { - Algorithm asn1.ObjectIdentifier - Parameters asn1.RawValue `asn1:"optional"` -} - -type RDNSequence []RelativeDistinguishedNameSET - -type RelativeDistinguishedNameSET []AttributeTypeAndValue - -// AttributeTypeAndValue mirrors the ASN.1 structure of the same name in -// http://tools.ietf.org/html/rfc5280#section-4.1.2.4 -type AttributeTypeAndValue struct { - Type asn1.ObjectIdentifier - Value interface{} -} - -// Extension represents the ASN.1 structure of the same name. See RFC -// 5280, section 4.2. -type Extension struct { - Id asn1.ObjectIdentifier - Critical bool `asn1:"optional"` - Value []byte -} - -// Name represents an X.509 distinguished name. This only includes the common -// elements of a DN. Additional elements in the name are ignored. -type Name struct { - Country, Organization, OrganizationalUnit []string - Locality, Province []string - StreetAddress, PostalCode []string - SerialNumber, CommonName string - - Names []AttributeTypeAndValue -} - -func (n *Name) FillFromRDNSequence(rdns *RDNSequence) { - for _, rdn := range *rdns { - if len(rdn) == 0 { - continue - } - atv := rdn[0] - n.Names = append(n.Names, atv) - value, ok := atv.Value.(string) - if !ok { - continue - } - - t := atv.Type - if len(t) == 4 && t[0] == 2 && t[1] == 5 && t[2] == 4 { - switch t[3] { - case 3: - n.CommonName = value - case 5: - n.SerialNumber = value - case 6: - n.Country = append(n.Country, value) - case 7: - n.Locality = append(n.Locality, value) - case 8: - n.Province = append(n.Province, value) - case 9: - n.StreetAddress = append(n.StreetAddress, value) - case 10: - n.Organization = append(n.Organization, value) - case 11: - n.OrganizationalUnit = append(n.OrganizationalUnit, value) - case 17: - n.PostalCode = append(n.PostalCode, value) - } - } - } -} - -var ( - oidCountry = []int{2, 5, 4, 6} - oidOrganization = []int{2, 5, 4, 10} - oidOrganizationalUnit = []int{2, 5, 4, 11} - oidCommonName = []int{2, 5, 4, 3} - oidSerialNumber = []int{2, 5, 4, 5} - oidLocality = []int{2, 5, 4, 7} - oidProvince = []int{2, 5, 4, 8} - oidStreetAddress = []int{2, 5, 4, 9} - oidPostalCode = []int{2, 5, 4, 17} -) - -// appendRDNs appends a relativeDistinguishedNameSET to the given RDNSequence -// and returns the new value. The relativeDistinguishedNameSET contains an -// attributeTypeAndValue for each of the given values. See RFC 5280, A.1, and -// search for AttributeTypeAndValue. -func appendRDNs(in RDNSequence, values []string, oid asn1.ObjectIdentifier) RDNSequence { - if len(values) == 0 { - return in - } - - s := make([]AttributeTypeAndValue, len(values)) - for i, value := range values { - s[i].Type = oid - s[i].Value = value - } - - return append(in, s) -} - -func (n Name) ToRDNSequence() (ret RDNSequence) { - ret = appendRDNs(ret, n.Country, oidCountry) - ret = appendRDNs(ret, n.Organization, oidOrganization) - ret = appendRDNs(ret, n.OrganizationalUnit, oidOrganizationalUnit) - ret = appendRDNs(ret, n.Locality, oidLocality) - ret = appendRDNs(ret, n.Province, oidProvince) - ret = appendRDNs(ret, n.StreetAddress, oidStreetAddress) - ret = appendRDNs(ret, n.PostalCode, oidPostalCode) - if len(n.CommonName) > 0 { - ret = appendRDNs(ret, []string{n.CommonName}, oidCommonName) - } - if len(n.SerialNumber) > 0 { - ret = appendRDNs(ret, []string{n.SerialNumber}, oidSerialNumber) - } - - return ret -} - -// CertificateList represents the ASN.1 structure of the same name. See RFC -// 5280, section 5.1. Use Certificate.CheckCRLSignature to verify the -// signature. -type CertificateList struct { - TBSCertList TBSCertificateList - SignatureAlgorithm AlgorithmIdentifier - SignatureValue asn1.BitString -} - -// HasExpired reports whether now is past the expiry time of certList. -func (certList *CertificateList) HasExpired(now time.Time) bool { - return now.After(certList.TBSCertList.NextUpdate) -} - -// TBSCertificateList represents the ASN.1 structure of the same name. See RFC -// 5280, section 5.1. -type TBSCertificateList struct { - Raw asn1.RawContent - Version int `asn1:"optional,default:2"` - Signature AlgorithmIdentifier - Issuer RDNSequence - ThisUpdate time.Time - NextUpdate time.Time - RevokedCertificates []RevokedCertificate `asn1:"optional"` - Extensions []Extension `asn1:"tag:0,optional,explicit"` -} - -// RevokedCertificate represents the ASN.1 structure of the same name. See RFC -// 5280, section 5.1. -type RevokedCertificate struct { - SerialNumber *big.Int - RevocationTime time.Time - Extensions []Extension `asn1:"optional"` -} diff --git a/vendor/github.com/google/certificate-transparency/go/x509/root_darwin.go b/vendor/github.com/google/certificate-transparency/go/x509/root_darwin.go deleted file mode 100755 index 3d3fa4e4d19b7..0000000000000 --- a/vendor/github.com/google/certificate-transparency/go/x509/root_darwin.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin,cgo - -package x509 - -/* -#cgo CFLAGS: -mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1060 -#cgo LDFLAGS: -framework CoreFoundation -framework Security - -#include -#include - -// FetchPEMRootsCTX509 fetches the system's list of trusted X.509 root certificates. -// -// On success it returns 0 and fills pemRoots with a CFDataRef that contains the extracted root -// certificates of the system. On failure, the function returns -1. -// -// Note: The CFDataRef returned in pemRoots must be released (using CFRelease) after -// we've consumed its content. -int FetchPEMRootsCTX509(CFDataRef *pemRoots) { - if (pemRoots == NULL) { - return -1; - } - - CFArrayRef certs = NULL; - OSStatus err = SecTrustCopyAnchorCertificates(&certs); - if (err != noErr) { - return -1; - } - - CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0); - int i, ncerts = CFArrayGetCount(certs); - for (i = 0; i < ncerts; i++) { - CFDataRef data = NULL; - SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, i); - if (cert == NULL) { - continue; - } - - // Note: SecKeychainItemExport is deprecated as of 10.7 in favor of SecItemExport. - // Once we support weak imports via cgo we should prefer that, and fall back to this - // for older systems. - err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data); - if (err != noErr) { - continue; - } - - if (data != NULL) { - CFDataAppendBytes(combinedData, CFDataGetBytePtr(data), CFDataGetLength(data)); - CFRelease(data); - } - } - - CFRelease(certs); - - *pemRoots = combinedData; - return 0; -} -*/ -import "C" -import "unsafe" - -func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { - return nil, nil -} - -func initSystemRoots() { - roots := NewCertPool() - - var data C.CFDataRef = nil - err := C.FetchPEMRootsCTX509(&data) - if err == -1 { - return - } - - defer C.CFRelease(C.CFTypeRef(data)) - buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data))) - roots.AppendCertsFromPEM(buf) - systemRoots = roots -} diff --git a/vendor/github.com/google/certificate-transparency/go/x509/root_stub.go b/vendor/github.com/google/certificate-transparency/go/x509/root_stub.go deleted file mode 100755 index 4c742ccc3715e..0000000000000 --- a/vendor/github.com/google/certificate-transparency/go/x509/root_stub.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin,!cgo - -package x509 - -func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { - return nil, nil -} - -func initSystemRoots() { -} diff --git a/vendor/github.com/google/certificate-transparency/go/x509/root_unix.go b/vendor/github.com/google/certificate-transparency/go/x509/root_unix.go deleted file mode 100755 index a5bd19e821695..0000000000000 --- a/vendor/github.com/google/certificate-transparency/go/x509/root_unix.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build dragonfly freebsd linux openbsd netbsd solaris - -package x509 - -import "io/ioutil" - -// Possible certificate files; stop after finding one. -var certFiles = []string{ - "/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc. - "/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL - "/etc/ssl/ca-bundle.pem", // OpenSUSE - "/etc/ssl/cert.pem", // OpenBSD - "/usr/local/share/certs/ca-root-nss.crt", // FreeBSD/DragonFly -} - -func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { - return nil, nil -} - -func initSystemRoots() { - roots := NewCertPool() - for _, file := range certFiles { - data, err := ioutil.ReadFile(file) - if err == nil { - roots.AppendCertsFromPEM(data) - systemRoots = roots - return - } - } - - // All of the files failed to load. systemRoots will be nil which will - // trigger a specific error at verification time. -} diff --git a/vendor/github.com/google/certificate-transparency/go/x509/verify.go b/vendor/github.com/google/certificate-transparency/go/x509/verify.go deleted file mode 100755 index e82dbc12b0b67..0000000000000 --- a/vendor/github.com/google/certificate-transparency/go/x509/verify.go +++ /dev/null @@ -1,476 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package x509 - -import ( - "fmt" - "net" - "runtime" - "strings" - "time" - "unicode/utf8" -) - -type InvalidReason int - -const ( - // NotAuthorizedToSign results when a certificate is signed by another - // which isn't marked as a CA certificate. - NotAuthorizedToSign InvalidReason = iota - // Expired results when a certificate has expired, based on the time - // given in the VerifyOptions. - Expired - // CANotAuthorizedForThisName results when an intermediate or root - // certificate has a name constraint which doesn't include the name - // being checked. - CANotAuthorizedForThisName - // TooManyIntermediates results when a path length constraint is - // violated. - TooManyIntermediates - // IncompatibleUsage results when the certificate's key usage indicates - // that it may only be used for a different purpose. - IncompatibleUsage -) - -// CertificateInvalidError results when an odd error occurs. Users of this -// library probably want to handle all these errors uniformly. -type CertificateInvalidError struct { - Cert *Certificate - Reason InvalidReason -} - -func (e CertificateInvalidError) Error() string { - switch e.Reason { - case NotAuthorizedToSign: - return "x509: certificate is not authorized to sign other certificates" - case Expired: - return "x509: certificate has expired or is not yet valid" - case CANotAuthorizedForThisName: - return "x509: a root or intermediate certificate is not authorized to sign in this domain" - case TooManyIntermediates: - return "x509: too many intermediates for path length constraint" - case IncompatibleUsage: - return "x509: certificate specifies an incompatible key usage" - } - return "x509: unknown error" -} - -// HostnameError results when the set of authorized names doesn't match the -// requested name. -type HostnameError struct { - Certificate *Certificate - Host string -} - -func (h HostnameError) Error() string { - c := h.Certificate - - var valid string - if ip := net.ParseIP(h.Host); ip != nil { - // Trying to validate an IP - if len(c.IPAddresses) == 0 { - return "x509: cannot validate certificate for " + h.Host + " because it doesn't contain any IP SANs" - } - for _, san := range c.IPAddresses { - if len(valid) > 0 { - valid += ", " - } - valid += san.String() - } - } else { - if len(c.DNSNames) > 0 { - valid = strings.Join(c.DNSNames, ", ") - } else { - valid = c.Subject.CommonName - } - } - return "x509: certificate is valid for " + valid + ", not " + h.Host -} - -// UnknownAuthorityError results when the certificate issuer is unknown -type UnknownAuthorityError struct { - cert *Certificate - // hintErr contains an error that may be helpful in determining why an - // authority wasn't found. - hintErr error - // hintCert contains a possible authority certificate that was rejected - // because of the error in hintErr. - hintCert *Certificate -} - -func (e UnknownAuthorityError) Error() string { - s := "x509: certificate signed by unknown authority" - if e.hintErr != nil { - certName := e.hintCert.Subject.CommonName - if len(certName) == 0 { - if len(e.hintCert.Subject.Organization) > 0 { - certName = e.hintCert.Subject.Organization[0] - } - certName = "serial:" + e.hintCert.SerialNumber.String() - } - s += fmt.Sprintf(" (possibly because of %q while trying to verify candidate authority certificate %q)", e.hintErr, certName) - } - return s -} - -// SystemRootsError results when we fail to load the system root certificates. -type SystemRootsError struct { -} - -func (e SystemRootsError) Error() string { - return "x509: failed to load system roots and no roots provided" -} - -// VerifyOptions contains parameters for Certificate.Verify. It's a structure -// because other PKIX verification APIs have ended up needing many options. -type VerifyOptions struct { - DNSName string - Intermediates *CertPool - Roots *CertPool // if nil, the system roots are used - CurrentTime time.Time // if zero, the current time is used - DisableTimeChecks bool - // KeyUsage specifies which Extended Key Usage values are acceptable. - // An empty list means ExtKeyUsageServerAuth. Key usage is considered a - // constraint down the chain which mirrors Windows CryptoAPI behaviour, - // but not the spec. To accept any key usage, include ExtKeyUsageAny. - KeyUsages []ExtKeyUsage -} - -const ( - leafCertificate = iota - intermediateCertificate - rootCertificate -) - -// isValid performs validity checks on the c. -func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *VerifyOptions) error { - if !opts.DisableTimeChecks { - now := opts.CurrentTime - if now.IsZero() { - now = time.Now() - } - if now.Before(c.NotBefore) || now.After(c.NotAfter) { - return CertificateInvalidError{c, Expired} - } - } - - if len(c.PermittedDNSDomains) > 0 { - ok := false - for _, domain := range c.PermittedDNSDomains { - if opts.DNSName == domain || - (strings.HasSuffix(opts.DNSName, domain) && - len(opts.DNSName) >= 1+len(domain) && - opts.DNSName[len(opts.DNSName)-len(domain)-1] == '.') { - ok = true - break - } - } - - if !ok { - return CertificateInvalidError{c, CANotAuthorizedForThisName} - } - } - - // KeyUsage status flags are ignored. From Engineering Security, Peter - // Gutmann: A European government CA marked its signing certificates as - // being valid for encryption only, but no-one noticed. Another - // European CA marked its signature keys as not being valid for - // signatures. A different CA marked its own trusted root certificate - // as being invalid for certificate signing. Another national CA - // distributed a certificate to be used to encrypt data for the - // country’s tax authority that was marked as only being usable for - // digital signatures but not for encryption. Yet another CA reversed - // the order of the bit flags in the keyUsage due to confusion over - // encoding endianness, essentially setting a random keyUsage in - // certificates that it issued. Another CA created a self-invalidating - // certificate by adding a certificate policy statement stipulating - // that the certificate had to be used strictly as specified in the - // keyUsage, and a keyUsage containing a flag indicating that the RSA - // encryption key could only be used for Diffie-Hellman key agreement. - - if certType == intermediateCertificate && (!c.BasicConstraintsValid || !c.IsCA) { - return CertificateInvalidError{c, NotAuthorizedToSign} - } - - if c.BasicConstraintsValid && c.MaxPathLen >= 0 { - numIntermediates := len(currentChain) - 1 - if numIntermediates > c.MaxPathLen { - return CertificateInvalidError{c, TooManyIntermediates} - } - } - - return nil -} - -// Verify attempts to verify c by building one or more chains from c to a -// certificate in opts.Roots, using certificates in opts.Intermediates if -// needed. If successful, it returns one or more chains where the first -// element of the chain is c and the last element is from opts.Roots. -// -// WARNING: this doesn't do any revocation checking. -func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error) { - // Use Windows's own verification and chain building. - if opts.Roots == nil && runtime.GOOS == "windows" { - return c.systemVerify(&opts) - } - - if opts.Roots == nil { - opts.Roots = systemRootsPool() - if opts.Roots == nil { - return nil, SystemRootsError{} - } - } - - err = c.isValid(leafCertificate, nil, &opts) - if err != nil { - return - } - - if len(opts.DNSName) > 0 { - err = c.VerifyHostname(opts.DNSName) - if err != nil { - return - } - } - - candidateChains, err := c.buildChains(make(map[int][][]*Certificate), []*Certificate{c}, &opts) - if err != nil { - return - } - - keyUsages := opts.KeyUsages - if len(keyUsages) == 0 { - keyUsages = []ExtKeyUsage{ExtKeyUsageServerAuth} - } - - // If any key usage is acceptable then we're done. - for _, usage := range keyUsages { - if usage == ExtKeyUsageAny { - chains = candidateChains - return - } - } - - for _, candidate := range candidateChains { - if checkChainForKeyUsage(candidate, keyUsages) { - chains = append(chains, candidate) - } - } - - if len(chains) == 0 { - err = CertificateInvalidError{c, IncompatibleUsage} - } - - return -} - -func appendToFreshChain(chain []*Certificate, cert *Certificate) []*Certificate { - n := make([]*Certificate, len(chain)+1) - copy(n, chain) - n[len(chain)] = cert - return n -} - -func (c *Certificate) buildChains(cache map[int][][]*Certificate, currentChain []*Certificate, opts *VerifyOptions) (chains [][]*Certificate, err error) { - possibleRoots, failedRoot, rootErr := opts.Roots.findVerifiedParents(c) - for _, rootNum := range possibleRoots { - root := opts.Roots.certs[rootNum] - err = root.isValid(rootCertificate, currentChain, opts) - if err != nil { - continue - } - chains = append(chains, appendToFreshChain(currentChain, root)) - } - - possibleIntermediates, failedIntermediate, intermediateErr := opts.Intermediates.findVerifiedParents(c) -nextIntermediate: - for _, intermediateNum := range possibleIntermediates { - intermediate := opts.Intermediates.certs[intermediateNum] - for _, cert := range currentChain { - if cert == intermediate { - continue nextIntermediate - } - } - err = intermediate.isValid(intermediateCertificate, currentChain, opts) - if err != nil { - continue - } - var childChains [][]*Certificate - childChains, ok := cache[intermediateNum] - if !ok { - childChains, err = intermediate.buildChains(cache, appendToFreshChain(currentChain, intermediate), opts) - cache[intermediateNum] = childChains - } - chains = append(chains, childChains...) - } - - if len(chains) > 0 { - err = nil - } - - if len(chains) == 0 && err == nil { - hintErr := rootErr - hintCert := failedRoot - if hintErr == nil { - hintErr = intermediateErr - hintCert = failedIntermediate - } - err = UnknownAuthorityError{c, hintErr, hintCert} - } - - return -} - -func matchHostnames(pattern, host string) bool { - if len(pattern) == 0 || len(host) == 0 { - return false - } - - patternParts := strings.Split(pattern, ".") - hostParts := strings.Split(host, ".") - - if len(patternParts) != len(hostParts) { - return false - } - - for i, patternPart := range patternParts { - if patternPart == "*" { - continue - } - if patternPart != hostParts[i] { - return false - } - } - - return true -} - -// toLowerCaseASCII returns a lower-case version of in. See RFC 6125 6.4.1. We use -// an explicitly ASCII function to avoid any sharp corners resulting from -// performing Unicode operations on DNS labels. -func toLowerCaseASCII(in string) string { - // If the string is already lower-case then there's nothing to do. - isAlreadyLowerCase := true - for _, c := range in { - if c == utf8.RuneError { - // If we get a UTF-8 error then there might be - // upper-case ASCII bytes in the invalid sequence. - isAlreadyLowerCase = false - break - } - if 'A' <= c && c <= 'Z' { - isAlreadyLowerCase = false - break - } - } - - if isAlreadyLowerCase { - return in - } - - out := []byte(in) - for i, c := range out { - if 'A' <= c && c <= 'Z' { - out[i] += 'a' - 'A' - } - } - return string(out) -} - -// VerifyHostname returns nil if c is a valid certificate for the named host. -// Otherwise it returns an error describing the mismatch. -func (c *Certificate) VerifyHostname(h string) error { - // IP addresses may be written in [ ]. - candidateIP := h - if len(h) >= 3 && h[0] == '[' && h[len(h)-1] == ']' { - candidateIP = h[1 : len(h)-1] - } - if ip := net.ParseIP(candidateIP); ip != nil { - // We only match IP addresses against IP SANs. - // https://tools.ietf.org/html/rfc6125#appendix-B.2 - for _, candidate := range c.IPAddresses { - if ip.Equal(candidate) { - return nil - } - } - return HostnameError{c, candidateIP} - } - - lowered := toLowerCaseASCII(h) - - if len(c.DNSNames) > 0 { - for _, match := range c.DNSNames { - if matchHostnames(toLowerCaseASCII(match), lowered) { - return nil - } - } - // If Subject Alt Name is given, we ignore the common name. - } else if matchHostnames(toLowerCaseASCII(c.Subject.CommonName), lowered) { - return nil - } - - return HostnameError{c, h} -} - -func checkChainForKeyUsage(chain []*Certificate, keyUsages []ExtKeyUsage) bool { - usages := make([]ExtKeyUsage, len(keyUsages)) - copy(usages, keyUsages) - - if len(chain) == 0 { - return false - } - - usagesRemaining := len(usages) - - // We walk down the list and cross out any usages that aren't supported - // by each certificate. If we cross out all the usages, then the chain - // is unacceptable. - - for i := len(chain) - 1; i >= 0; i-- { - cert := chain[i] - if len(cert.ExtKeyUsage) == 0 && len(cert.UnknownExtKeyUsage) == 0 { - // The certificate doesn't have any extended key usage specified. - continue - } - - for _, usage := range cert.ExtKeyUsage { - if usage == ExtKeyUsageAny { - // The certificate is explicitly good for any usage. - continue - } - } - - const invalidUsage ExtKeyUsage = -1 - - NextRequestedUsage: - for i, requestedUsage := range usages { - if requestedUsage == invalidUsage { - continue - } - - for _, usage := range cert.ExtKeyUsage { - if requestedUsage == usage { - continue NextRequestedUsage - } else if requestedUsage == ExtKeyUsageServerAuth && - (usage == ExtKeyUsageNetscapeServerGatedCrypto || - usage == ExtKeyUsageMicrosoftServerGatedCrypto) { - // In order to support COMODO - // certificate chains, we have to - // accept Netscape or Microsoft SGC - // usages as equal to ServerAuth. - continue NextRequestedUsage - } - } - - usages[i] = invalidUsage - usagesRemaining-- - if usagesRemaining == 0 { - return false - } - } - } - - return true -} diff --git a/vendor/github.com/google/certificate-transparency/go/x509/x509.go b/vendor/github.com/google/certificate-transparency/go/x509/x509.go deleted file mode 100755 index cda72200f8d84..0000000000000 --- a/vendor/github.com/google/certificate-transparency/go/x509/x509.go +++ /dev/null @@ -1,1622 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package x509 parses X.509-encoded keys and certificates. -// -// START CT CHANGES -// This is a fork of the go library crypto/x509 package, it's more relaxed -// about certificates that it'll accept, and exports the TBSCertificate -// structure. -// END CT CHANGES -package x509 - -import ( - "bytes" - "crypto" - "crypto/dsa" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rsa" - "crypto/sha1" - // START CT CHANGES - "github.com/google/certificate-transparency/go/asn1" - "github.com/google/certificate-transparency/go/x509/pkix" - // END CT CHANGES - "encoding/pem" - "errors" - // START CT CHANGES - "fmt" - // END CT CHANGES - "io" - "math/big" - "net" - "time" -) - -// pkixPublicKey reflects a PKIX public key structure. See SubjectPublicKeyInfo -// in RFC 3280. -type pkixPublicKey struct { - Algo pkix.AlgorithmIdentifier - BitString asn1.BitString -} - -// ParsePKIXPublicKey parses a DER encoded public key. These values are -// typically found in PEM blocks with "BEGIN PUBLIC KEY". -func ParsePKIXPublicKey(derBytes []byte) (pub interface{}, err error) { - var pki publicKeyInfo - if _, err = asn1.Unmarshal(derBytes, &pki); err != nil { - return - } - algo := getPublicKeyAlgorithmFromOID(pki.Algorithm.Algorithm) - if algo == UnknownPublicKeyAlgorithm { - return nil, errors.New("x509: unknown public key algorithm") - } - return parsePublicKey(algo, &pki) -} - -func marshalPublicKey(pub interface{}) (publicKeyBytes []byte, publicKeyAlgorithm pkix.AlgorithmIdentifier, err error) { - switch pub := pub.(type) { - case *rsa.PublicKey: - publicKeyBytes, err = asn1.Marshal(rsaPublicKey{ - N: pub.N, - E: pub.E, - }) - publicKeyAlgorithm.Algorithm = oidPublicKeyRSA - // This is a NULL parameters value which is technically - // superfluous, but most other code includes it and, by - // doing this, we match their public key hashes. - publicKeyAlgorithm.Parameters = asn1.RawValue{ - Tag: 5, - } - case *ecdsa.PublicKey: - publicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y) - oid, ok := oidFromNamedCurve(pub.Curve) - if !ok { - return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: unsupported elliptic curve") - } - publicKeyAlgorithm.Algorithm = oidPublicKeyECDSA - var paramBytes []byte - paramBytes, err = asn1.Marshal(oid) - if err != nil { - return - } - publicKeyAlgorithm.Parameters.FullBytes = paramBytes - default: - return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: only RSA and ECDSA public keys supported") - } - - return publicKeyBytes, publicKeyAlgorithm, nil -} - -// MarshalPKIXPublicKey serialises a public key to DER-encoded PKIX format. -func MarshalPKIXPublicKey(pub interface{}) ([]byte, error) { - var publicKeyBytes []byte - var publicKeyAlgorithm pkix.AlgorithmIdentifier - var err error - - if publicKeyBytes, publicKeyAlgorithm, err = marshalPublicKey(pub); err != nil { - return nil, err - } - - pkix := pkixPublicKey{ - Algo: publicKeyAlgorithm, - BitString: asn1.BitString{ - Bytes: publicKeyBytes, - BitLength: 8 * len(publicKeyBytes), - }, - } - - ret, _ := asn1.Marshal(pkix) - return ret, nil -} - -// These structures reflect the ASN.1 structure of X.509 certificates.: - -type certificate struct { - Raw asn1.RawContent - TBSCertificate tbsCertificate - SignatureAlgorithm pkix.AlgorithmIdentifier - SignatureValue asn1.BitString -} - -type tbsCertificate struct { - Raw asn1.RawContent - Version int `asn1:"optional,explicit,default:1,tag:0"` - SerialNumber *big.Int - SignatureAlgorithm pkix.AlgorithmIdentifier - Issuer asn1.RawValue - Validity validity - Subject asn1.RawValue - PublicKey publicKeyInfo - UniqueId asn1.BitString `asn1:"optional,tag:1"` - SubjectUniqueId asn1.BitString `asn1:"optional,tag:2"` - Extensions []pkix.Extension `asn1:"optional,explicit,tag:3"` -} - -type dsaAlgorithmParameters struct { - P, Q, G *big.Int -} - -type dsaSignature struct { - R, S *big.Int -} - -type ecdsaSignature dsaSignature - -type validity struct { - NotBefore, NotAfter time.Time -} - -type publicKeyInfo struct { - Raw asn1.RawContent - Algorithm pkix.AlgorithmIdentifier - PublicKey asn1.BitString -} - -// RFC 5280, 4.2.1.1 -type authKeyId struct { - Id []byte `asn1:"optional,tag:0"` -} - -type SignatureAlgorithm int - -const ( - UnknownSignatureAlgorithm SignatureAlgorithm = iota - MD2WithRSA - MD5WithRSA - SHA1WithRSA - SHA256WithRSA - SHA384WithRSA - SHA512WithRSA - DSAWithSHA1 - DSAWithSHA256 - ECDSAWithSHA1 - ECDSAWithSHA256 - ECDSAWithSHA384 - ECDSAWithSHA512 -) - -type PublicKeyAlgorithm int - -const ( - UnknownPublicKeyAlgorithm PublicKeyAlgorithm = iota - RSA - DSA - ECDSA -) - -// OIDs for signature algorithms -// -// pkcs-1 OBJECT IDENTIFIER ::= { -// iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } -// -// -// RFC 3279 2.2.1 RSA Signature Algorithms -// -// md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 } -// -// md5WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 } -// -// sha-1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 } -// -// dsaWithSha1 OBJECT IDENTIFIER ::= { -// iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 3 } -// -// RFC 3279 2.2.3 ECDSA Signature Algorithm -// -// ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { -// iso(1) member-body(2) us(840) ansi-x962(10045) -// signatures(4) ecdsa-with-SHA1(1)} -// -// -// RFC 4055 5 PKCS #1 Version 1.5 -// -// sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 } -// -// sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 } -// -// sha512WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 13 } -// -// -// RFC 5758 3.1 DSA Signature Algorithms -// -// dsaWithSha256 OBJECT IDENTIFIER ::= { -// joint-iso-ccitt(2) country(16) us(840) organization(1) gov(101) -// csor(3) algorithms(4) id-dsa-with-sha2(3) 2} -// -// RFC 5758 3.2 ECDSA Signature Algorithm -// -// ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2) -// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 } -// -// ecdsa-with-SHA384 OBJECT IDENTIFIER ::= { iso(1) member-body(2) -// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 3 } -// -// ecdsa-with-SHA512 OBJECT IDENTIFIER ::= { iso(1) member-body(2) -// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 4 } - -var ( - oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2} - oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4} - oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} - oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} - oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} - oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} - oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} - oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 4, 3, 2} - oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1} - oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2} - oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3} - oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4} -) - -func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm { - switch { - case oid.Equal(oidSignatureMD2WithRSA): - return MD2WithRSA - case oid.Equal(oidSignatureMD5WithRSA): - return MD5WithRSA - case oid.Equal(oidSignatureSHA1WithRSA): - return SHA1WithRSA - case oid.Equal(oidSignatureSHA256WithRSA): - return SHA256WithRSA - case oid.Equal(oidSignatureSHA384WithRSA): - return SHA384WithRSA - case oid.Equal(oidSignatureSHA512WithRSA): - return SHA512WithRSA - case oid.Equal(oidSignatureDSAWithSHA1): - return DSAWithSHA1 - case oid.Equal(oidSignatureDSAWithSHA256): - return DSAWithSHA256 - case oid.Equal(oidSignatureECDSAWithSHA1): - return ECDSAWithSHA1 - case oid.Equal(oidSignatureECDSAWithSHA256): - return ECDSAWithSHA256 - case oid.Equal(oidSignatureECDSAWithSHA384): - return ECDSAWithSHA384 - case oid.Equal(oidSignatureECDSAWithSHA512): - return ECDSAWithSHA512 - } - return UnknownSignatureAlgorithm -} - -// RFC 3279, 2.3 Public Key Algorithms -// -// pkcs-1 OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840) -// rsadsi(113549) pkcs(1) 1 } -// -// rsaEncryption OBJECT IDENTIFIER ::== { pkcs1-1 1 } -// -// id-dsa OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840) -// x9-57(10040) x9cm(4) 1 } -// -// RFC 5480, 2.1.1 Unrestricted Algorithm Identifier and Parameters -// -// id-ecPublicKey OBJECT IDENTIFIER ::= { -// iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 } -var ( - oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} - oidPublicKeyDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1} - oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1} -) - -func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm { - switch { - case oid.Equal(oidPublicKeyRSA): - return RSA - case oid.Equal(oidPublicKeyDSA): - return DSA - case oid.Equal(oidPublicKeyECDSA): - return ECDSA - } - return UnknownPublicKeyAlgorithm -} - -// RFC 5480, 2.1.1.1. Named Curve -// -// secp224r1 OBJECT IDENTIFIER ::= { -// iso(1) identified-organization(3) certicom(132) curve(0) 33 } -// -// secp256r1 OBJECT IDENTIFIER ::= { -// iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3) -// prime(1) 7 } -// -// secp384r1 OBJECT IDENTIFIER ::= { -// iso(1) identified-organization(3) certicom(132) curve(0) 34 } -// -// secp521r1 OBJECT IDENTIFIER ::= { -// iso(1) identified-organization(3) certicom(132) curve(0) 35 } -// -// NB: secp256r1 is equivalent to prime256v1 -var ( - oidNamedCurveP224 = asn1.ObjectIdentifier{1, 3, 132, 0, 33} - oidNamedCurveP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7} - oidNamedCurveP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34} - oidNamedCurveP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35} -) - -func namedCurveFromOID(oid asn1.ObjectIdentifier) elliptic.Curve { - switch { - case oid.Equal(oidNamedCurveP224): - return elliptic.P224() - case oid.Equal(oidNamedCurveP256): - return elliptic.P256() - case oid.Equal(oidNamedCurveP384): - return elliptic.P384() - case oid.Equal(oidNamedCurveP521): - return elliptic.P521() - } - return nil -} - -func oidFromNamedCurve(curve elliptic.Curve) (asn1.ObjectIdentifier, bool) { - switch curve { - case elliptic.P224(): - return oidNamedCurveP224, true - case elliptic.P256(): - return oidNamedCurveP256, true - case elliptic.P384(): - return oidNamedCurveP384, true - case elliptic.P521(): - return oidNamedCurveP521, true - } - - return nil, false -} - -// KeyUsage represents the set of actions that are valid for a given key. It's -// a bitmap of the KeyUsage* constants. -type KeyUsage int - -const ( - KeyUsageDigitalSignature KeyUsage = 1 << iota - KeyUsageContentCommitment - KeyUsageKeyEncipherment - KeyUsageDataEncipherment - KeyUsageKeyAgreement - KeyUsageCertSign - KeyUsageCRLSign - KeyUsageEncipherOnly - KeyUsageDecipherOnly -) - -// RFC 5280, 4.2.1.12 Extended Key Usage -// -// anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 } -// -// id-kp OBJECT IDENTIFIER ::= { id-pkix 3 } -// -// id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 } -// id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 } -// id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 } -// id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 } -// id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 } -// id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 } -var ( - oidExtKeyUsageAny = asn1.ObjectIdentifier{2, 5, 29, 37, 0} - oidExtKeyUsageServerAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 1} - oidExtKeyUsageClientAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 2} - oidExtKeyUsageCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 3} - oidExtKeyUsageEmailProtection = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 4} - oidExtKeyUsageIPSECEndSystem = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 5} - oidExtKeyUsageIPSECTunnel = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 6} - oidExtKeyUsageIPSECUser = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 7} - oidExtKeyUsageTimeStamping = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 8} - oidExtKeyUsageOCSPSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 9} - oidExtKeyUsageMicrosoftServerGatedCrypto = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 3} - oidExtKeyUsageNetscapeServerGatedCrypto = asn1.ObjectIdentifier{2, 16, 840, 1, 113730, 4, 1} -) - -// ExtKeyUsage represents an extended set of actions that are valid for a given key. -// Each of the ExtKeyUsage* constants define a unique action. -type ExtKeyUsage int - -const ( - ExtKeyUsageAny ExtKeyUsage = iota - ExtKeyUsageServerAuth - ExtKeyUsageClientAuth - ExtKeyUsageCodeSigning - ExtKeyUsageEmailProtection - ExtKeyUsageIPSECEndSystem - ExtKeyUsageIPSECTunnel - ExtKeyUsageIPSECUser - ExtKeyUsageTimeStamping - ExtKeyUsageOCSPSigning - ExtKeyUsageMicrosoftServerGatedCrypto - ExtKeyUsageNetscapeServerGatedCrypto -) - -// extKeyUsageOIDs contains the mapping between an ExtKeyUsage and its OID. -var extKeyUsageOIDs = []struct { - extKeyUsage ExtKeyUsage - oid asn1.ObjectIdentifier -}{ - {ExtKeyUsageAny, oidExtKeyUsageAny}, - {ExtKeyUsageServerAuth, oidExtKeyUsageServerAuth}, - {ExtKeyUsageClientAuth, oidExtKeyUsageClientAuth}, - {ExtKeyUsageCodeSigning, oidExtKeyUsageCodeSigning}, - {ExtKeyUsageEmailProtection, oidExtKeyUsageEmailProtection}, - {ExtKeyUsageIPSECEndSystem, oidExtKeyUsageIPSECEndSystem}, - {ExtKeyUsageIPSECTunnel, oidExtKeyUsageIPSECTunnel}, - {ExtKeyUsageIPSECUser, oidExtKeyUsageIPSECUser}, - {ExtKeyUsageTimeStamping, oidExtKeyUsageTimeStamping}, - {ExtKeyUsageOCSPSigning, oidExtKeyUsageOCSPSigning}, - {ExtKeyUsageMicrosoftServerGatedCrypto, oidExtKeyUsageMicrosoftServerGatedCrypto}, - {ExtKeyUsageNetscapeServerGatedCrypto, oidExtKeyUsageNetscapeServerGatedCrypto}, -} - -func extKeyUsageFromOID(oid asn1.ObjectIdentifier) (eku ExtKeyUsage, ok bool) { - for _, pair := range extKeyUsageOIDs { - if oid.Equal(pair.oid) { - return pair.extKeyUsage, true - } - } - return -} - -func oidFromExtKeyUsage(eku ExtKeyUsage) (oid asn1.ObjectIdentifier, ok bool) { - for _, pair := range extKeyUsageOIDs { - if eku == pair.extKeyUsage { - return pair.oid, true - } - } - return -} - -// A Certificate represents an X.509 certificate. -type Certificate struct { - Raw []byte // Complete ASN.1 DER content (certificate, signature algorithm and signature). - RawTBSCertificate []byte // Certificate part of raw ASN.1 DER content. - RawSubjectPublicKeyInfo []byte // DER encoded SubjectPublicKeyInfo. - RawSubject []byte // DER encoded Subject - RawIssuer []byte // DER encoded Issuer - - Signature []byte - SignatureAlgorithm SignatureAlgorithm - - PublicKeyAlgorithm PublicKeyAlgorithm - PublicKey interface{} - - Version int - SerialNumber *big.Int - Issuer pkix.Name - Subject pkix.Name - NotBefore, NotAfter time.Time // Validity bounds. - KeyUsage KeyUsage - - // Extensions contains raw X.509 extensions. When parsing certificates, - // this can be used to extract non-critical extensions that are not - // parsed by this package. When marshaling certificates, the Extensions - // field is ignored, see ExtraExtensions. - Extensions []pkix.Extension - - // ExtraExtensions contains extensions to be copied, raw, into any - // marshaled certificates. Values override any extensions that would - // otherwise be produced based on the other fields. The ExtraExtensions - // field is not populated when parsing certificates, see Extensions. - ExtraExtensions []pkix.Extension - - ExtKeyUsage []ExtKeyUsage // Sequence of extended key usages. - UnknownExtKeyUsage []asn1.ObjectIdentifier // Encountered extended key usages unknown to this package. - - BasicConstraintsValid bool // if true then the next two fields are valid. - IsCA bool - MaxPathLen int - - SubjectKeyId []byte - AuthorityKeyId []byte - - // RFC 5280, 4.2.2.1 (Authority Information Access) - OCSPServer []string - IssuingCertificateURL []string - - // Subject Alternate Name values - DNSNames []string - EmailAddresses []string - IPAddresses []net.IP - - // Name constraints - PermittedDNSDomainsCritical bool // if true then the name constraints are marked critical. - PermittedDNSDomains []string - - // CRL Distribution Points - CRLDistributionPoints []string - - PolicyIdentifiers []asn1.ObjectIdentifier -} - -// ErrUnsupportedAlgorithm results from attempting to perform an operation that -// involves algorithms that are not currently implemented. -var ErrUnsupportedAlgorithm = errors.New("x509: cannot verify signature: algorithm unimplemented") - -// ConstraintViolationError results when a requested usage is not permitted by -// a certificate. For example: checking a signature when the public key isn't a -// certificate signing key. -type ConstraintViolationError struct{} - -func (ConstraintViolationError) Error() string { - return "x509: invalid signature: parent certificate cannot sign this kind of certificate" -} - -func (c *Certificate) Equal(other *Certificate) bool { - return bytes.Equal(c.Raw, other.Raw) -} - -// Entrust have a broken root certificate (CN=Entrust.net Certification -// Authority (2048)) which isn't marked as a CA certificate and is thus invalid -// according to PKIX. -// We recognise this certificate by its SubjectPublicKeyInfo and exempt it -// from the Basic Constraints requirement. -// See http://www.entrust.net/knowledge-base/technote.cfm?tn=7869 -// -// TODO(agl): remove this hack once their reissued root is sufficiently -// widespread. -var entrustBrokenSPKI = []byte{ - 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, - 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, - 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, - 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, - 0x00, 0x97, 0xa3, 0x2d, 0x3c, 0x9e, 0xde, 0x05, - 0xda, 0x13, 0xc2, 0x11, 0x8d, 0x9d, 0x8e, 0xe3, - 0x7f, 0xc7, 0x4b, 0x7e, 0x5a, 0x9f, 0xb3, 0xff, - 0x62, 0xab, 0x73, 0xc8, 0x28, 0x6b, 0xba, 0x10, - 0x64, 0x82, 0x87, 0x13, 0xcd, 0x57, 0x18, 0xff, - 0x28, 0xce, 0xc0, 0xe6, 0x0e, 0x06, 0x91, 0x50, - 0x29, 0x83, 0xd1, 0xf2, 0xc3, 0x2a, 0xdb, 0xd8, - 0xdb, 0x4e, 0x04, 0xcc, 0x00, 0xeb, 0x8b, 0xb6, - 0x96, 0xdc, 0xbc, 0xaa, 0xfa, 0x52, 0x77, 0x04, - 0xc1, 0xdb, 0x19, 0xe4, 0xae, 0x9c, 0xfd, 0x3c, - 0x8b, 0x03, 0xef, 0x4d, 0xbc, 0x1a, 0x03, 0x65, - 0xf9, 0xc1, 0xb1, 0x3f, 0x72, 0x86, 0xf2, 0x38, - 0xaa, 0x19, 0xae, 0x10, 0x88, 0x78, 0x28, 0xda, - 0x75, 0xc3, 0x3d, 0x02, 0x82, 0x02, 0x9c, 0xb9, - 0xc1, 0x65, 0x77, 0x76, 0x24, 0x4c, 0x98, 0xf7, - 0x6d, 0x31, 0x38, 0xfb, 0xdb, 0xfe, 0xdb, 0x37, - 0x02, 0x76, 0xa1, 0x18, 0x97, 0xa6, 0xcc, 0xde, - 0x20, 0x09, 0x49, 0x36, 0x24, 0x69, 0x42, 0xf6, - 0xe4, 0x37, 0x62, 0xf1, 0x59, 0x6d, 0xa9, 0x3c, - 0xed, 0x34, 0x9c, 0xa3, 0x8e, 0xdb, 0xdc, 0x3a, - 0xd7, 0xf7, 0x0a, 0x6f, 0xef, 0x2e, 0xd8, 0xd5, - 0x93, 0x5a, 0x7a, 0xed, 0x08, 0x49, 0x68, 0xe2, - 0x41, 0xe3, 0x5a, 0x90, 0xc1, 0x86, 0x55, 0xfc, - 0x51, 0x43, 0x9d, 0xe0, 0xb2, 0xc4, 0x67, 0xb4, - 0xcb, 0x32, 0x31, 0x25, 0xf0, 0x54, 0x9f, 0x4b, - 0xd1, 0x6f, 0xdb, 0xd4, 0xdd, 0xfc, 0xaf, 0x5e, - 0x6c, 0x78, 0x90, 0x95, 0xde, 0xca, 0x3a, 0x48, - 0xb9, 0x79, 0x3c, 0x9b, 0x19, 0xd6, 0x75, 0x05, - 0xa0, 0xf9, 0x88, 0xd7, 0xc1, 0xe8, 0xa5, 0x09, - 0xe4, 0x1a, 0x15, 0xdc, 0x87, 0x23, 0xaa, 0xb2, - 0x75, 0x8c, 0x63, 0x25, 0x87, 0xd8, 0xf8, 0x3d, - 0xa6, 0xc2, 0xcc, 0x66, 0xff, 0xa5, 0x66, 0x68, - 0x55, 0x02, 0x03, 0x01, 0x00, 0x01, -} - -// CheckSignatureFrom verifies that the signature on c is a valid signature -// from parent. -func (c *Certificate) CheckSignatureFrom(parent *Certificate) (err error) { - // RFC 5280, 4.2.1.9: - // "If the basic constraints extension is not present in a version 3 - // certificate, or the extension is present but the cA boolean is not - // asserted, then the certified public key MUST NOT be used to verify - // certificate signatures." - // (except for Entrust, see comment above entrustBrokenSPKI) - if (parent.Version == 3 && !parent.BasicConstraintsValid || - parent.BasicConstraintsValid && !parent.IsCA) && - !bytes.Equal(c.RawSubjectPublicKeyInfo, entrustBrokenSPKI) { - return ConstraintViolationError{} - } - - if parent.KeyUsage != 0 && parent.KeyUsage&KeyUsageCertSign == 0 { - return ConstraintViolationError{} - } - - if parent.PublicKeyAlgorithm == UnknownPublicKeyAlgorithm { - return ErrUnsupportedAlgorithm - } - - // TODO(agl): don't ignore the path length constraint. - - return parent.CheckSignature(c.SignatureAlgorithm, c.RawTBSCertificate, c.Signature) -} - -// CheckSignature verifies that signature is a valid signature over signed from -// c's public key. -func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature []byte) (err error) { - var hashType crypto.Hash - - switch algo { - case SHA1WithRSA, DSAWithSHA1, ECDSAWithSHA1: - hashType = crypto.SHA1 - case SHA256WithRSA, DSAWithSHA256, ECDSAWithSHA256: - hashType = crypto.SHA256 - case SHA384WithRSA, ECDSAWithSHA384: - hashType = crypto.SHA384 - case SHA512WithRSA, ECDSAWithSHA512: - hashType = crypto.SHA512 - default: - return ErrUnsupportedAlgorithm - } - - if !hashType.Available() { - return ErrUnsupportedAlgorithm - } - h := hashType.New() - - h.Write(signed) - digest := h.Sum(nil) - - switch pub := c.PublicKey.(type) { - case *rsa.PublicKey: - return rsa.VerifyPKCS1v15(pub, hashType, digest, signature) - case *dsa.PublicKey: - dsaSig := new(dsaSignature) - if _, err := asn1.Unmarshal(signature, dsaSig); err != nil { - return err - } - if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 { - return errors.New("x509: DSA signature contained zero or negative values") - } - if !dsa.Verify(pub, digest, dsaSig.R, dsaSig.S) { - return errors.New("x509: DSA verification failure") - } - return - case *ecdsa.PublicKey: - ecdsaSig := new(ecdsaSignature) - if _, err := asn1.Unmarshal(signature, ecdsaSig); err != nil { - return err - } - if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { - return errors.New("x509: ECDSA signature contained zero or negative values") - } - if !ecdsa.Verify(pub, digest, ecdsaSig.R, ecdsaSig.S) { - return errors.New("x509: ECDSA verification failure") - } - return - } - return ErrUnsupportedAlgorithm -} - -// CheckCRLSignature checks that the signature in crl is from c. -func (c *Certificate) CheckCRLSignature(crl *pkix.CertificateList) (err error) { - algo := getSignatureAlgorithmFromOID(crl.SignatureAlgorithm.Algorithm) - return c.CheckSignature(algo, crl.TBSCertList.Raw, crl.SignatureValue.RightAlign()) -} - -// START CT CHANGES -type UnhandledCriticalExtension struct { - ID asn1.ObjectIdentifier -} - -func (h UnhandledCriticalExtension) Error() string { - return fmt.Sprintf("x509: unhandled critical extension (%v)", h.ID) -} - -// END CT CHANGES - -type basicConstraints struct { - IsCA bool `asn1:"optional"` - MaxPathLen int `asn1:"optional,default:-1"` -} - -// RFC 5280 4.2.1.4 -type policyInformation struct { - Policy asn1.ObjectIdentifier - // policyQualifiers omitted -} - -// RFC 5280, 4.2.1.10 -type nameConstraints struct { - Permitted []generalSubtree `asn1:"optional,tag:0"` - Excluded []generalSubtree `asn1:"optional,tag:1"` -} - -type generalSubtree struct { - Name string `asn1:"tag:2,optional,ia5"` -} - -// RFC 5280, 4.2.2.1 -type authorityInfoAccess struct { - Method asn1.ObjectIdentifier - Location asn1.RawValue -} - -// RFC 5280, 4.2.1.14 -type distributionPoint struct { - DistributionPoint distributionPointName `asn1:"optional,tag:0"` - Reason asn1.BitString `asn1:"optional,tag:1"` - CRLIssuer asn1.RawValue `asn1:"optional,tag:2"` -} - -type distributionPointName struct { - FullName asn1.RawValue `asn1:"optional,tag:0"` - RelativeName pkix.RDNSequence `asn1:"optional,tag:1"` -} - -func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{}, error) { - asn1Data := keyData.PublicKey.RightAlign() - switch algo { - case RSA: - p := new(rsaPublicKey) - _, err := asn1.Unmarshal(asn1Data, p) - if err != nil { - return nil, err - } - - if p.N.Sign() <= 0 { - return nil, errors.New("x509: RSA modulus is not a positive number") - } - if p.E <= 0 { - return nil, errors.New("x509: RSA public exponent is not a positive number") - } - - pub := &rsa.PublicKey{ - E: p.E, - N: p.N, - } - return pub, nil - case DSA: - var p *big.Int - _, err := asn1.Unmarshal(asn1Data, &p) - if err != nil { - return nil, err - } - paramsData := keyData.Algorithm.Parameters.FullBytes - params := new(dsaAlgorithmParameters) - _, err = asn1.Unmarshal(paramsData, params) - if err != nil { - return nil, err - } - if p.Sign() <= 0 || params.P.Sign() <= 0 || params.Q.Sign() <= 0 || params.G.Sign() <= 0 { - return nil, errors.New("x509: zero or negative DSA parameter") - } - pub := &dsa.PublicKey{ - Parameters: dsa.Parameters{ - P: params.P, - Q: params.Q, - G: params.G, - }, - Y: p, - } - return pub, nil - case ECDSA: - paramsData := keyData.Algorithm.Parameters.FullBytes - namedCurveOID := new(asn1.ObjectIdentifier) - _, err := asn1.Unmarshal(paramsData, namedCurveOID) - if err != nil { - return nil, err - } - namedCurve := namedCurveFromOID(*namedCurveOID) - if namedCurve == nil { - return nil, errors.New("x509: unsupported elliptic curve") - } - x, y := elliptic.Unmarshal(namedCurve, asn1Data) - if x == nil { - return nil, errors.New("x509: failed to unmarshal elliptic curve point") - } - pub := &ecdsa.PublicKey{ - Curve: namedCurve, - X: x, - Y: y, - } - return pub, nil - default: - return nil, nil - } -} - -// START CT CHANGES - -// NonFatalErrors is an error type which can hold a number of other errors. -// It's used to collect a range of non-fatal errors which occur while parsing -// a certificate, that way we can still match on certs which technically are -// invalid. -type NonFatalErrors struct { - Errors []error -} - -// Adds an error to the list of errors contained by NonFatalErrors. -func (e *NonFatalErrors) AddError(err error) { - e.Errors = append(e.Errors, err) -} - -// Returns a string consisting of the values of Error() from all of the errors -// contained in |e| -func (e NonFatalErrors) Error() string { - r := "NonFatalErrors: " - for _, err := range e.Errors { - r += err.Error() + "; " - } - return r -} - -// Returns true if |e| contains at least one error -func (e *NonFatalErrors) HasError() bool { - return len(e.Errors) > 0 -} - -// END CT CHANGES - -func parseCertificate(in *certificate) (*Certificate, error) { - // START CT CHANGES - var nfe NonFatalErrors - // END CT CHANGES - - out := new(Certificate) - out.Raw = in.Raw - out.RawTBSCertificate = in.TBSCertificate.Raw - out.RawSubjectPublicKeyInfo = in.TBSCertificate.PublicKey.Raw - out.RawSubject = in.TBSCertificate.Subject.FullBytes - out.RawIssuer = in.TBSCertificate.Issuer.FullBytes - - out.Signature = in.SignatureValue.RightAlign() - out.SignatureAlgorithm = - getSignatureAlgorithmFromOID(in.TBSCertificate.SignatureAlgorithm.Algorithm) - - out.PublicKeyAlgorithm = - getPublicKeyAlgorithmFromOID(in.TBSCertificate.PublicKey.Algorithm.Algorithm) - var err error - out.PublicKey, err = parsePublicKey(out.PublicKeyAlgorithm, &in.TBSCertificate.PublicKey) - if err != nil { - return nil, err - } - - if in.TBSCertificate.SerialNumber.Sign() < 0 { - // START CT CHANGES - nfe.AddError(errors.New("x509: negative serial number")) - // END CT CHANGES - } - - out.Version = in.TBSCertificate.Version + 1 - out.SerialNumber = in.TBSCertificate.SerialNumber - - var issuer, subject pkix.RDNSequence - if _, err := asn1.Unmarshal(in.TBSCertificate.Subject.FullBytes, &subject); err != nil { - return nil, err - } - if _, err := asn1.Unmarshal(in.TBSCertificate.Issuer.FullBytes, &issuer); err != nil { - return nil, err - } - - out.Issuer.FillFromRDNSequence(&issuer) - out.Subject.FillFromRDNSequence(&subject) - - out.NotBefore = in.TBSCertificate.Validity.NotBefore - out.NotAfter = in.TBSCertificate.Validity.NotAfter - - for _, e := range in.TBSCertificate.Extensions { - out.Extensions = append(out.Extensions, e) - - if len(e.Id) == 4 && e.Id[0] == 2 && e.Id[1] == 5 && e.Id[2] == 29 { - switch e.Id[3] { - case 15: - // RFC 5280, 4.2.1.3 - var usageBits asn1.BitString - _, err := asn1.Unmarshal(e.Value, &usageBits) - - if err == nil { - var usage int - for i := 0; i < 9; i++ { - if usageBits.At(i) != 0 { - usage |= 1 << uint(i) - } - } - out.KeyUsage = KeyUsage(usage) - continue - } - case 19: - // RFC 5280, 4.2.1.9 - var constraints basicConstraints - _, err := asn1.Unmarshal(e.Value, &constraints) - - if err == nil { - out.BasicConstraintsValid = true - out.IsCA = constraints.IsCA - out.MaxPathLen = constraints.MaxPathLen - continue - } - case 17: - // RFC 5280, 4.2.1.6 - - // SubjectAltName ::= GeneralNames - // - // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName - // - // GeneralName ::= CHOICE { - // otherName [0] OtherName, - // rfc822Name [1] IA5String, - // dNSName [2] IA5String, - // x400Address [3] ORAddress, - // directoryName [4] Name, - // ediPartyName [5] EDIPartyName, - // uniformResourceIdentifier [6] IA5String, - // iPAddress [7] OCTET STRING, - // registeredID [8] OBJECT IDENTIFIER } - var seq asn1.RawValue - _, err := asn1.Unmarshal(e.Value, &seq) - if err != nil { - return nil, err - } - if !seq.IsCompound || seq.Tag != 16 || seq.Class != 0 { - return nil, asn1.StructuralError{Msg: "bad SAN sequence"} - } - - parsedName := false - - rest := seq.Bytes - for len(rest) > 0 { - var v asn1.RawValue - rest, err = asn1.Unmarshal(rest, &v) - if err != nil { - return nil, err - } - switch v.Tag { - case 1: - out.EmailAddresses = append(out.EmailAddresses, string(v.Bytes)) - parsedName = true - case 2: - out.DNSNames = append(out.DNSNames, string(v.Bytes)) - parsedName = true - case 7: - switch len(v.Bytes) { - case net.IPv4len, net.IPv6len: - out.IPAddresses = append(out.IPAddresses, v.Bytes) - default: - // START CT CHANGES - nfe.AddError(fmt.Errorf("x509: certificate contained IP address of length %d : %v", len(v.Bytes), v.Bytes)) - // END CT CHANGES - } - } - } - - if parsedName { - continue - } - // If we didn't parse any of the names then we - // fall through to the critical check below. - - case 30: - // RFC 5280, 4.2.1.10 - - // NameConstraints ::= SEQUENCE { - // permittedSubtrees [0] GeneralSubtrees OPTIONAL, - // excludedSubtrees [1] GeneralSubtrees OPTIONAL } - // - // GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree - // - // GeneralSubtree ::= SEQUENCE { - // base GeneralName, - // minimum [0] BaseDistance DEFAULT 0, - // maximum [1] BaseDistance OPTIONAL } - // - // BaseDistance ::= INTEGER (0..MAX) - - var constraints nameConstraints - _, err := asn1.Unmarshal(e.Value, &constraints) - if err != nil { - return nil, err - } - - if len(constraints.Excluded) > 0 && e.Critical { - // START CT CHANGES - nfe.AddError(UnhandledCriticalExtension{e.Id}) - // END CT CHANGES - } - - for _, subtree := range constraints.Permitted { - if len(subtree.Name) == 0 { - if e.Critical { - // START CT CHANGES - nfe.AddError(UnhandledCriticalExtension{e.Id}) - // END CT CHANGES - } - continue - } - out.PermittedDNSDomains = append(out.PermittedDNSDomains, subtree.Name) - } - continue - - case 31: - // RFC 5280, 4.2.1.14 - - // CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint - // - // DistributionPoint ::= SEQUENCE { - // distributionPoint [0] DistributionPointName OPTIONAL, - // reasons [1] ReasonFlags OPTIONAL, - // cRLIssuer [2] GeneralNames OPTIONAL } - // - // DistributionPointName ::= CHOICE { - // fullName [0] GeneralNames, - // nameRelativeToCRLIssuer [1] RelativeDistinguishedName } - - var cdp []distributionPoint - _, err := asn1.Unmarshal(e.Value, &cdp) - if err != nil { - return nil, err - } - - for _, dp := range cdp { - var n asn1.RawValue - _, err = asn1.Unmarshal(dp.DistributionPoint.FullName.Bytes, &n) - if err != nil { - return nil, err - } - - if n.Tag == 6 { - out.CRLDistributionPoints = append(out.CRLDistributionPoints, string(n.Bytes)) - } - } - continue - - case 35: - // RFC 5280, 4.2.1.1 - var a authKeyId - _, err = asn1.Unmarshal(e.Value, &a) - if err != nil { - return nil, err - } - out.AuthorityKeyId = a.Id - continue - - case 37: - // RFC 5280, 4.2.1.12. Extended Key Usage - - // id-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-ce 37 } - // - // ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId - // - // KeyPurposeId ::= OBJECT IDENTIFIER - - var keyUsage []asn1.ObjectIdentifier - _, err = asn1.Unmarshal(e.Value, &keyUsage) - if err != nil { - return nil, err - } - - for _, u := range keyUsage { - if extKeyUsage, ok := extKeyUsageFromOID(u); ok { - out.ExtKeyUsage = append(out.ExtKeyUsage, extKeyUsage) - } else { - out.UnknownExtKeyUsage = append(out.UnknownExtKeyUsage, u) - } - } - - continue - - case 14: - // RFC 5280, 4.2.1.2 - var keyid []byte - _, err = asn1.Unmarshal(e.Value, &keyid) - if err != nil { - return nil, err - } - out.SubjectKeyId = keyid - continue - - case 32: - // RFC 5280 4.2.1.4: Certificate Policies - var policies []policyInformation - if _, err = asn1.Unmarshal(e.Value, &policies); err != nil { - return nil, err - } - out.PolicyIdentifiers = make([]asn1.ObjectIdentifier, len(policies)) - for i, policy := range policies { - out.PolicyIdentifiers[i] = policy.Policy - } - } - } else if e.Id.Equal(oidExtensionAuthorityInfoAccess) { - // RFC 5280 4.2.2.1: Authority Information Access - var aia []authorityInfoAccess - if _, err = asn1.Unmarshal(e.Value, &aia); err != nil { - return nil, err - } - - for _, v := range aia { - // GeneralName: uniformResourceIdentifier [6] IA5String - if v.Location.Tag != 6 { - continue - } - if v.Method.Equal(oidAuthorityInfoAccessOcsp) { - out.OCSPServer = append(out.OCSPServer, string(v.Location.Bytes)) - } else if v.Method.Equal(oidAuthorityInfoAccessIssuers) { - out.IssuingCertificateURL = append(out.IssuingCertificateURL, string(v.Location.Bytes)) - } - } - } - - if e.Critical { - // START CT CHANGES - nfe.AddError(UnhandledCriticalExtension{e.Id}) - // END CT CHANGES - } - } - // START CT CHANGES - if nfe.HasError() { - return out, nfe - } - // END CT CHANGES - return out, nil -} - -// START CT CHANGES - -// ParseTBSCertificate parses a single TBSCertificate from the given ASN.1 DER data. -// The parsed data is returned in a Certificate struct for ease of access. -func ParseTBSCertificate(asn1Data []byte) (*Certificate, error) { - var tbsCert tbsCertificate - rest, err := asn1.Unmarshal(asn1Data, &tbsCert) - if err != nil { - return nil, err - } - if len(rest) > 0 { - return nil, asn1.SyntaxError{Msg: "trailing data"} - } - return parseCertificate(&certificate{ - Raw: tbsCert.Raw, - TBSCertificate: tbsCert}) -} - -// END CT CHANGES - -// ParseCertificate parses a single certificate from the given ASN.1 DER data. -func ParseCertificate(asn1Data []byte) (*Certificate, error) { - var cert certificate - rest, err := asn1.Unmarshal(asn1Data, &cert) - if err != nil { - return nil, err - } - if len(rest) > 0 { - return nil, asn1.SyntaxError{Msg: "trailing data"} - } - - return parseCertificate(&cert) -} - -// ParseCertificates parses one or more certificates from the given ASN.1 DER -// data. The certificates must be concatenated with no intermediate padding. -func ParseCertificates(asn1Data []byte) ([]*Certificate, error) { - var v []*certificate - - for len(asn1Data) > 0 { - cert := new(certificate) - var err error - asn1Data, err = asn1.Unmarshal(asn1Data, cert) - if err != nil { - return nil, err - } - v = append(v, cert) - } - - ret := make([]*Certificate, len(v)) - for i, ci := range v { - cert, err := parseCertificate(ci) - if err != nil { - return nil, err - } - ret[i] = cert - } - - return ret, nil -} - -func reverseBitsInAByte(in byte) byte { - b1 := in>>4 | in<<4 - b2 := b1>>2&0x33 | b1<<2&0xcc - b3 := b2>>1&0x55 | b2<<1&0xaa - return b3 -} - -var ( - oidExtensionSubjectKeyId = []int{2, 5, 29, 14} - oidExtensionKeyUsage = []int{2, 5, 29, 15} - oidExtensionExtendedKeyUsage = []int{2, 5, 29, 37} - oidExtensionAuthorityKeyId = []int{2, 5, 29, 35} - oidExtensionBasicConstraints = []int{2, 5, 29, 19} - oidExtensionSubjectAltName = []int{2, 5, 29, 17} - oidExtensionCertificatePolicies = []int{2, 5, 29, 32} - oidExtensionNameConstraints = []int{2, 5, 29, 30} - oidExtensionCRLDistributionPoints = []int{2, 5, 29, 31} - oidExtensionAuthorityInfoAccess = []int{1, 3, 6, 1, 5, 5, 7, 1, 1} -) - -var ( - oidAuthorityInfoAccessOcsp = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 1} - oidAuthorityInfoAccessIssuers = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 2} -) - -// oidNotInExtensions returns whether an extension with the given oid exists in -// extensions. -func oidInExtensions(oid asn1.ObjectIdentifier, extensions []pkix.Extension) bool { - for _, e := range extensions { - if e.Id.Equal(oid) { - return true - } - } - return false -} - -func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) { - ret = make([]pkix.Extension, 10 /* maximum number of elements. */) - n := 0 - - if template.KeyUsage != 0 && - !oidInExtensions(oidExtensionKeyUsage, template.ExtraExtensions) { - ret[n].Id = oidExtensionKeyUsage - ret[n].Critical = true - - var a [2]byte - a[0] = reverseBitsInAByte(byte(template.KeyUsage)) - a[1] = reverseBitsInAByte(byte(template.KeyUsage >> 8)) - - l := 1 - if a[1] != 0 { - l = 2 - } - - ret[n].Value, err = asn1.Marshal(asn1.BitString{Bytes: a[0:l], BitLength: l * 8}) - if err != nil { - return - } - n++ - } - - if (len(template.ExtKeyUsage) > 0 || len(template.UnknownExtKeyUsage) > 0) && - !oidInExtensions(oidExtensionExtendedKeyUsage, template.ExtraExtensions) { - ret[n].Id = oidExtensionExtendedKeyUsage - - var oids []asn1.ObjectIdentifier - for _, u := range template.ExtKeyUsage { - if oid, ok := oidFromExtKeyUsage(u); ok { - oids = append(oids, oid) - } else { - panic("internal error") - } - } - - oids = append(oids, template.UnknownExtKeyUsage...) - - ret[n].Value, err = asn1.Marshal(oids) - if err != nil { - return - } - n++ - } - - if template.BasicConstraintsValid && !oidInExtensions(oidExtensionBasicConstraints, template.ExtraExtensions) { - ret[n].Id = oidExtensionBasicConstraints - ret[n].Value, err = asn1.Marshal(basicConstraints{template.IsCA, template.MaxPathLen}) - ret[n].Critical = true - if err != nil { - return - } - n++ - } - - if len(template.SubjectKeyId) > 0 && !oidInExtensions(oidExtensionSubjectKeyId, template.ExtraExtensions) { - ret[n].Id = oidExtensionSubjectKeyId - ret[n].Value, err = asn1.Marshal(template.SubjectKeyId) - if err != nil { - return - } - n++ - } - - if len(template.AuthorityKeyId) > 0 && !oidInExtensions(oidExtensionAuthorityKeyId, template.ExtraExtensions) { - ret[n].Id = oidExtensionAuthorityKeyId - ret[n].Value, err = asn1.Marshal(authKeyId{template.AuthorityKeyId}) - if err != nil { - return - } - n++ - } - - if (len(template.OCSPServer) > 0 || len(template.IssuingCertificateURL) > 0) && - !oidInExtensions(oidExtensionAuthorityInfoAccess, template.ExtraExtensions) { - ret[n].Id = oidExtensionAuthorityInfoAccess - var aiaValues []authorityInfoAccess - for _, name := range template.OCSPServer { - aiaValues = append(aiaValues, authorityInfoAccess{ - Method: oidAuthorityInfoAccessOcsp, - Location: asn1.RawValue{Tag: 6, Class: 2, Bytes: []byte(name)}, - }) - } - for _, name := range template.IssuingCertificateURL { - aiaValues = append(aiaValues, authorityInfoAccess{ - Method: oidAuthorityInfoAccessIssuers, - Location: asn1.RawValue{Tag: 6, Class: 2, Bytes: []byte(name)}, - }) - } - ret[n].Value, err = asn1.Marshal(aiaValues) - if err != nil { - return - } - n++ - } - - if (len(template.DNSNames) > 0 || len(template.EmailAddresses) > 0 || len(template.IPAddresses) > 0) && - !oidInExtensions(oidExtensionSubjectAltName, template.ExtraExtensions) { - ret[n].Id = oidExtensionSubjectAltName - var rawValues []asn1.RawValue - for _, name := range template.DNSNames { - rawValues = append(rawValues, asn1.RawValue{Tag: 2, Class: 2, Bytes: []byte(name)}) - } - for _, email := range template.EmailAddresses { - rawValues = append(rawValues, asn1.RawValue{Tag: 1, Class: 2, Bytes: []byte(email)}) - } - for _, rawIP := range template.IPAddresses { - // If possible, we always want to encode IPv4 addresses in 4 bytes. - ip := rawIP.To4() - if ip == nil { - ip = rawIP - } - rawValues = append(rawValues, asn1.RawValue{Tag: 7, Class: 2, Bytes: ip}) - } - ret[n].Value, err = asn1.Marshal(rawValues) - if err != nil { - return - } - n++ - } - - if len(template.PolicyIdentifiers) > 0 && - !oidInExtensions(oidExtensionCertificatePolicies, template.ExtraExtensions) { - ret[n].Id = oidExtensionCertificatePolicies - policies := make([]policyInformation, len(template.PolicyIdentifiers)) - for i, policy := range template.PolicyIdentifiers { - policies[i].Policy = policy - } - ret[n].Value, err = asn1.Marshal(policies) - if err != nil { - return - } - n++ - } - - if len(template.PermittedDNSDomains) > 0 && - !oidInExtensions(oidExtensionNameConstraints, template.ExtraExtensions) { - ret[n].Id = oidExtensionNameConstraints - ret[n].Critical = template.PermittedDNSDomainsCritical - - var out nameConstraints - out.Permitted = make([]generalSubtree, len(template.PermittedDNSDomains)) - for i, permitted := range template.PermittedDNSDomains { - out.Permitted[i] = generalSubtree{Name: permitted} - } - ret[n].Value, err = asn1.Marshal(out) - if err != nil { - return - } - n++ - } - - if len(template.CRLDistributionPoints) > 0 && - !oidInExtensions(oidExtensionCRLDistributionPoints, template.ExtraExtensions) { - ret[n].Id = oidExtensionCRLDistributionPoints - - var crlDp []distributionPoint - for _, name := range template.CRLDistributionPoints { - rawFullName, _ := asn1.Marshal(asn1.RawValue{Tag: 6, Class: 2, Bytes: []byte(name)}) - - dp := distributionPoint{ - DistributionPoint: distributionPointName{ - FullName: asn1.RawValue{Tag: 0, Class: 2, Bytes: rawFullName}, - }, - } - crlDp = append(crlDp, dp) - } - - ret[n].Value, err = asn1.Marshal(crlDp) - if err != nil { - return - } - n++ - } - - // Adding another extension here? Remember to update the maximum number - // of elements in the make() at the top of the function. - - return append(ret[:n], template.ExtraExtensions...), nil -} - -func subjectBytes(cert *Certificate) ([]byte, error) { - if len(cert.RawSubject) > 0 { - return cert.RawSubject, nil - } - - return asn1.Marshal(cert.Subject.ToRDNSequence()) -} - -// CreateCertificate creates a new certificate based on a template. The -// following members of template are used: SerialNumber, Subject, NotBefore, -// NotAfter, KeyUsage, ExtKeyUsage, UnknownExtKeyUsage, BasicConstraintsValid, -// IsCA, MaxPathLen, SubjectKeyId, DNSNames, PermittedDNSDomainsCritical, -// PermittedDNSDomains. -// -// The certificate is signed by parent. If parent is equal to template then the -// certificate is self-signed. The parameter pub is the public key of the -// signee and priv is the private key of the signer. -// -// The returned slice is the certificate in DER encoding. -// -// The only supported key types are RSA and ECDSA (*rsa.PublicKey or -// *ecdsa.PublicKey for pub, *rsa.PrivateKey or *ecdsa.PublicKey for priv). -func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interface{}, priv interface{}) (cert []byte, err error) { - var publicKeyBytes []byte - var publicKeyAlgorithm pkix.AlgorithmIdentifier - - if publicKeyBytes, publicKeyAlgorithm, err = marshalPublicKey(pub); err != nil { - return nil, err - } - - var signatureAlgorithm pkix.AlgorithmIdentifier - var hashFunc crypto.Hash - - switch priv := priv.(type) { - case *rsa.PrivateKey: - signatureAlgorithm.Algorithm = oidSignatureSHA1WithRSA - hashFunc = crypto.SHA1 - case *ecdsa.PrivateKey: - switch priv.Curve { - case elliptic.P224(), elliptic.P256(): - hashFunc = crypto.SHA256 - signatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA256 - case elliptic.P384(): - hashFunc = crypto.SHA384 - signatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA384 - case elliptic.P521(): - hashFunc = crypto.SHA512 - signatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA512 - default: - return nil, errors.New("x509: unknown elliptic curve") - } - default: - return nil, errors.New("x509: only RSA and ECDSA private keys supported") - } - - if err != nil { - return - } - - if len(parent.SubjectKeyId) > 0 { - template.AuthorityKeyId = parent.SubjectKeyId - } - - extensions, err := buildExtensions(template) - if err != nil { - return - } - - asn1Issuer, err := subjectBytes(parent) - if err != nil { - return - } - - asn1Subject, err := subjectBytes(template) - if err != nil { - return - } - - encodedPublicKey := asn1.BitString{BitLength: len(publicKeyBytes) * 8, Bytes: publicKeyBytes} - c := tbsCertificate{ - Version: 2, - SerialNumber: template.SerialNumber, - SignatureAlgorithm: signatureAlgorithm, - Issuer: asn1.RawValue{FullBytes: asn1Issuer}, - Validity: validity{template.NotBefore.UTC(), template.NotAfter.UTC()}, - Subject: asn1.RawValue{FullBytes: asn1Subject}, - PublicKey: publicKeyInfo{nil, publicKeyAlgorithm, encodedPublicKey}, - Extensions: extensions, - } - - tbsCertContents, err := asn1.Marshal(c) - if err != nil { - return - } - - c.Raw = tbsCertContents - - h := hashFunc.New() - h.Write(tbsCertContents) - digest := h.Sum(nil) - - var signature []byte - - switch priv := priv.(type) { - case *rsa.PrivateKey: - signature, err = rsa.SignPKCS1v15(rand, priv, hashFunc, digest) - case *ecdsa.PrivateKey: - var r, s *big.Int - if r, s, err = ecdsa.Sign(rand, priv, digest); err == nil { - signature, err = asn1.Marshal(ecdsaSignature{r, s}) - } - default: - panic("internal error") - } - - if err != nil { - return - } - - cert, err = asn1.Marshal(certificate{ - nil, - c, - signatureAlgorithm, - asn1.BitString{Bytes: signature, BitLength: len(signature) * 8}, - }) - return -} - -// pemCRLPrefix is the magic string that indicates that we have a PEM encoded -// CRL. -var pemCRLPrefix = []byte("-----BEGIN X509 CRL") - -// pemType is the type of a PEM encoded CRL. -var pemType = "X509 CRL" - -// ParseCRL parses a CRL from the given bytes. It's often the case that PEM -// encoded CRLs will appear where they should be DER encoded, so this function -// will transparently handle PEM encoding as long as there isn't any leading -// garbage. -func ParseCRL(crlBytes []byte) (certList *pkix.CertificateList, err error) { - if bytes.HasPrefix(crlBytes, pemCRLPrefix) { - block, _ := pem.Decode(crlBytes) - if block != nil && block.Type == pemType { - crlBytes = block.Bytes - } - } - return ParseDERCRL(crlBytes) -} - -// ParseDERCRL parses a DER encoded CRL from the given bytes. -func ParseDERCRL(derBytes []byte) (certList *pkix.CertificateList, err error) { - certList = new(pkix.CertificateList) - _, err = asn1.Unmarshal(derBytes, certList) - if err != nil { - certList = nil - } - return -} - -// CreateCRL returns a DER encoded CRL, signed by this Certificate, that -// contains the given list of revoked certificates. -// -// The only supported key type is RSA (*rsa.PrivateKey for priv). -func (c *Certificate) CreateCRL(rand io.Reader, priv interface{}, revokedCerts []pkix.RevokedCertificate, now, expiry time.Time) (crlBytes []byte, err error) { - rsaPriv, ok := priv.(*rsa.PrivateKey) - if !ok { - return nil, errors.New("x509: non-RSA private keys not supported") - } - tbsCertList := pkix.TBSCertificateList{ - Version: 2, - Signature: pkix.AlgorithmIdentifier{ - Algorithm: oidSignatureSHA1WithRSA, - }, - Issuer: c.Subject.ToRDNSequence(), - ThisUpdate: now.UTC(), - NextUpdate: expiry.UTC(), - RevokedCertificates: revokedCerts, - } - - tbsCertListContents, err := asn1.Marshal(tbsCertList) - if err != nil { - return - } - - h := sha1.New() - h.Write(tbsCertListContents) - digest := h.Sum(nil) - - signature, err := rsa.SignPKCS1v15(rand, rsaPriv, crypto.SHA1, digest) - if err != nil { - return - } - - return asn1.Marshal(pkix.CertificateList{ - TBSCertList: tbsCertList, - SignatureAlgorithm: pkix.AlgorithmIdentifier{ - Algorithm: oidSignatureSHA1WithRSA, - }, - SignatureValue: asn1.BitString{Bytes: signature, BitLength: len(signature) * 8}, - }) -} diff --git a/vendor/github.com/google/certificate-transparency/proto/ct.proto b/vendor/github.com/google/certificate-transparency/proto/ct.proto deleted file mode 100644 index 84e968fe48a51..0000000000000 --- a/vendor/github.com/google/certificate-transparency/proto/ct.proto +++ /dev/null @@ -1,320 +0,0 @@ -syntax = "proto2"; - -package ct; - - -//////////////////////////////////////////////////////////////////////////////// -// These protocol buffers should be kept aligned with the I-D. // -//////////////////////////////////////////////////////////////////////////////// - -// RFC 5246 -message DigitallySigned { - enum HashAlgorithm { - NONE = 0; - MD5 = 1; - SHA1 = 2; - SHA224 = 3; - SHA256 = 4; - SHA384 = 5; - SHA512 = 6; - } - - enum SignatureAlgorithm { - ANONYMOUS = 0; - RSA = 1; - DSA = 2; - ECDSA = 3; - } - - // 1 byte - optional HashAlgorithm hash_algorithm = 1 [ default = NONE ]; - // 1 byte - optional SignatureAlgorithm sig_algorithm = 2 [ default = ANONYMOUS ]; - // 0..2^16-1 bytes - optional bytes signature = 3; -} - -enum LogEntryType { - X509_ENTRY = 0; - PRECERT_ENTRY = 1; - PRECERT_ENTRY_V2 = 2; - // Not part of the I-D, and outside the valid range. - X_JSON_ENTRY = 32768; // Experimental, don't rely on this! - UNKNOWN_ENTRY_TYPE = 65536; -} - -message X509ChainEntry { - // For V1 this entry just includes the certificate in the leaf_certificate - // field - // <1..2^24-1> - optional bytes leaf_certificate = 1; - // For V2 it includes the cert and key hash using CertInfo. The - // leaf_certificate field is not used - optional CertInfo cert_info = 3; - // <0..2^24-1> - // A chain from the leaf to a trusted root - // (excluding leaf and possibly root). - repeated bytes certificate_chain = 2; -} - -// opaque TBSCertificate<1..2^16-1>; -// struct { -// opaque issuer_key_hash[32]; -// TBSCertificate tbs_certificate; -// } PreCert; -// Retained for V1 API compatibility. May be removed in a future release. -message PreCert { - optional bytes issuer_key_hash = 1; - optional bytes tbs_certificate = 2; -} - -// In V2 this is used for both certificates and precertificates in SCTs. It -// replaces PreCert and has the same structure. The older message remains for -// compatibility with existing code that depends on this proto. -message CertInfo { - optional bytes issuer_key_hash = 1; - optional bytes tbs_certificate = 2; -} - -message PrecertChainEntry { - // <1..2^24-1> - optional bytes pre_certificate = 1; - // <0..2^24-1> - // The chain certifying the precertificate, as submitted by the CA. - repeated bytes precertificate_chain = 2; - - // PreCert input to the SCT. Can be computed from the above. - // Store it alongside the entry data so that the signers don't have to - // parse certificates to recompute it. - optional PreCert pre_cert = 3; - // As above for V2 messages. Only one of these fields will be set in a - // valid message - optional CertInfo cert_info = 4; -} - -message XJSONEntry { - optional string json = 1; -} - -// TODO(alcutter): Consider using extensions here instead. -message LogEntry { - optional LogEntryType type = 1 [ default = UNKNOWN_ENTRY_TYPE ]; - - optional X509ChainEntry x509_entry = 2; - - optional PrecertChainEntry precert_entry = 3; - - optional XJSONEntry x_json_entry = 4; -} - -enum SignatureType { - CERTIFICATE_TIMESTAMP = 0; - // TODO(ekasper): called tree_hash in I-D. - TREE_HEAD = 1; -} - -enum Version { - V1 = 0; - V2 = 1; - // Not part of the I-D, and outside the valid range. - UNKNOWN_VERSION = 256; -} - -message LogID { - // 32 bytes - optional bytes key_id = 1; -} - -message SctExtension { - // Valid range is 0-65534 - optional uint32 sct_extension_type = 1; - // Data is opaque and type specific. <0..2^16-1> bytes - optional bytes sct_extension_data = 2; -} - -// TODO(ekasper): implement support for id. -message SignedCertificateTimestamp { - optional Version version = 1 [ default = UNKNOWN_VERSION ]; - optional LogID id = 2; - // UTC time in milliseconds, since January 1, 1970, 00:00. - optional uint64 timestamp = 3; - optional DigitallySigned signature = 4; - // V1 extensions - optional bytes extensions = 5; - // V2 extensions <0..2^16-1>. Must be ordered by type (lowest first) - repeated SctExtension sct_extension = 6; -} - -message SignedCertificateTimestampList { - // One or more SCTs, <1..2^16-1> bytes each - repeated bytes sct_list = 1; -} - -enum MerkleLeafType { - TIMESTAMPED_ENTRY = 0; - UNKNOWN_LEAF_TYPE = 256; -} - -message SignedEntry { - // For V1 signed entries either the x509 or precert field will be set - optional bytes x509 = 1; - optional PreCert precert = 2; - optional bytes json = 3; - // For V2 all entries use the CertInfo field and the above fields are - // not set - optional CertInfo cert_info = 4; -} - -message TimestampedEntry { - optional uint64 timestamp = 1; - optional LogEntryType entry_type = 2; - optional SignedEntry signed_entry = 3; - // V1 extensions - optional bytes extensions = 4; - // V2 extensions <0..2^16-1>. Must be ordered by type (lowest first) - repeated SctExtension sct_extension = 5; -} - -// Stuff that's hashed into a Merkle leaf. -message MerkleTreeLeaf { - // The version of the corresponding SCT. - optional Version version = 1 [ default = UNKNOWN_VERSION ]; - optional MerkleLeafType type = 2 [ default = UNKNOWN_LEAF_TYPE ]; - optional TimestampedEntry timestamped_entry = 3; -} - -// TODO(benl): No longer needed? -// -// Used by cpp/client/ct: it assembles the one from the I-D JSON -// protocol. -// -// Used by cpp/server/blob-server: it uses one to call a variant of -// LogLookup::AuditProof. -message MerkleAuditProof { - optional Version version = 1 [ default = UNKNOWN_VERSION ]; - optional LogID id = 2; - optional int64 tree_size = 3; - optional uint64 timestamp = 4; - optional int64 leaf_index = 5; - repeated bytes path_node = 6; - optional DigitallySigned tree_head_signature = 7; -} - -message ShortMerkleAuditProof { - required int64 leaf_index = 1; - repeated bytes path_node = 2; -} - -//////////////////////////////////////////////////////////////////////////////// -// Finally, stuff that's not in the I-D but that we use internally // -// for logging entries and tree head state. // -//////////////////////////////////////////////////////////////////////////////// - -// TODO(alcutter): Come up with a better name :/ -message LoggedEntryPB { - optional int64 sequence_number = 1; - optional bytes merkle_leaf_hash = 2; - message Contents { - optional SignedCertificateTimestamp sct = 1; - optional LogEntry entry = 2; - } - required Contents contents = 3; -} - -message SthExtension { - // Valid range is 0-65534 - optional uint32 sth_extension_type = 1; - // Data is opaque and type specific <0..2^16-1> bytes - optional bytes sth_extension_data = 2; -} - -message SignedTreeHead { - // The version of the tree head signature. - // (Note that each leaf has its own version, so a V2 tree - // can contain V1 leaves, too. - optional Version version = 1 [ default = UNKNOWN_VERSION ]; - optional LogID id = 2; - optional uint64 timestamp = 3; - optional int64 tree_size = 4; - optional bytes sha256_root_hash = 5; - optional DigitallySigned signature = 6; - // Only supported in V2. <0..2^16-1> - repeated SthExtension sth_extension = 7; -} - -// Stuff the SSL client spits out from a connection. -message SSLClientCTData { - optional LogEntry reconstructed_entry = 1; - optional bytes certificate_sha256_hash = 2; - - message SCTInfo { - // There is an entry + sct -> leaf hash mapping. - optional SignedCertificateTimestamp sct = 1; - optional bytes merkle_leaf_hash = 2; - } - repeated SCTInfo attached_sct_info = 3; -} - -message ClusterNodeState { - optional string node_id = 1; - optional int64 contiguous_tree_size = 2 [deprecated = true]; - optional SignedTreeHead newest_sth = 3; - optional SignedTreeHead current_serving_sth = 4; - - // The following host_name/log_port pair are used to allow a log node to - // contact other nodes in the cluster, primarily for the purposes of - // replication. - // hostname/ip which can be used to contact [just] this log node - optional string hostname = 5; - // port on which this log node is listening. - optional int32 log_port = 6; -} - -message ClusterControl { - optional bool accept_new_entries = 1 [ default = true ]; -} - -message ClusterConfig { - ///////////////////////////////// - // This section of the config affects the selection of the cluster's current - // serving STH. - // The cluster will always attempt to determine the newest (and - // largest) possible STH which meets the constraints defined below from the - // set of STHs available at the individual cluster nodes. - // (Note that nodes with newer/larger STHs can, of course, serve - // earlier/smaller STHs.) - - - // The minimum number of nodes which must be able to serve a given STH. - // This setting allows you to configure the level of cluster resiliency - // against data (in the form of node/node database) loss. - // i.e.: Once an STH has been created, it must have been replicated to - // at least this many nodes before being considered as a candidate for - // the overall cluster serving STH. - optional int32 minimum_serving_nodes = 1; - - // The minimum fraction of nodes which must be able to serve a given STH. - // This setting allows you to configure the serving capacity redundancy of - // your cluster. - // e.g. you determine you need 3 nodes to serve your expected peak traffic - // levels, but want to be over-provisioned by 25% to ensure the cluster will - // continue to be able to handle the traffic in the case of a single node - // failure, you might set this to 0.75 to ensure that any cluster-wide - // serving STH candidate must be servable from at least 3 of your 4 nodes. - optional double minimum_serving_fraction = 2; - ///////////////////////////////// - - // When the number of entries in the EtcedConsistentStore exceeds this value, - // the log server will reject all calls to add-[pre-]chain to protect itself - // and etcd. - optional double etcd_reject_add_pending_threshold = 3 [default = 30000]; -} - -message SequenceMapping { - message Mapping { - optional bytes entry_hash = 1; - optional int64 sequence_number = 2; - } - - repeated Mapping mapping = 1; -} diff --git a/vendor/github.com/gotestyourself/gotestyourself/LICENSE b/vendor/github.com/google/shlex/COPYING similarity index 100% rename from vendor/github.com/gotestyourself/gotestyourself/LICENSE rename to vendor/github.com/google/shlex/COPYING diff --git a/vendor/github.com/google/shlex/README b/vendor/github.com/google/shlex/README new file mode 100644 index 0000000000000..c86bcc066fdd2 --- /dev/null +++ b/vendor/github.com/google/shlex/README @@ -0,0 +1,2 @@ +go-shlex is a simple lexer for go that supports shell-style quoting, +commenting, and escaping. diff --git a/vendor/github.com/google/shlex/shlex.go b/vendor/github.com/google/shlex/shlex.go new file mode 100644 index 0000000000000..3cb37b7e481e9 --- /dev/null +++ b/vendor/github.com/google/shlex/shlex.go @@ -0,0 +1,417 @@ +/* +Copyright 2012 Google Inc. All Rights Reserved. + +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. +*/ + +/* +Package shlex implements a simple lexer which splits input in to tokens using +shell-style rules for quoting and commenting. + +The basic use case uses the default ASCII lexer to split a string into sub-strings: + + shlex.Split("one \"two three\" four") -> []string{"one", "two three", "four"} + +To process a stream of strings: + + l := NewLexer(os.Stdin) + for ; token, err := l.Next(); err != nil { + // process token + } + +To access the raw token stream (which includes tokens for comments): + + t := NewTokenizer(os.Stdin) + for ; token, err := t.Next(); err != nil { + // process token + } + +*/ +package shlex + +import ( + "bufio" + "fmt" + "io" + "strings" +) + +// TokenType is a top-level token classification: A word, space, comment, unknown. +type TokenType int + +// runeTokenClass is the type of a UTF-8 character classification: A quote, space, escape. +type runeTokenClass int + +// the internal state used by the lexer state machine +type lexerState int + +// Token is a (type, value) pair representing a lexographical token. +type Token struct { + tokenType TokenType + value string +} + +// Equal reports whether tokens a, and b, are equal. +// Two tokens are equal if both their types and values are equal. A nil token can +// never be equal to another token. +func (a *Token) Equal(b *Token) bool { + if a == nil || b == nil { + return false + } + if a.tokenType != b.tokenType { + return false + } + return a.value == b.value +} + +// Named classes of UTF-8 runes +const ( + spaceRunes = " \t\r\n" + escapingQuoteRunes = `"` + nonEscapingQuoteRunes = "'" + escapeRunes = `\` + commentRunes = "#" +) + +// Classes of rune token +const ( + unknownRuneClass runeTokenClass = iota + spaceRuneClass + escapingQuoteRuneClass + nonEscapingQuoteRuneClass + escapeRuneClass + commentRuneClass + eofRuneClass +) + +// Classes of lexographic token +const ( + UnknownToken TokenType = iota + WordToken + SpaceToken + CommentToken +) + +// Lexer state machine states +const ( + startState lexerState = iota // no runes have been seen + inWordState // processing regular runes in a word + escapingState // we have just consumed an escape rune; the next rune is literal + escapingQuotedState // we have just consumed an escape rune within a quoted string + quotingEscapingState // we are within a quoted string that supports escaping ("...") + quotingState // we are within a string that does not support escaping ('...') + commentState // we are within a comment (everything following an unquoted or unescaped # +) + +// tokenClassifier is used for classifying rune characters. +type tokenClassifier map[rune]runeTokenClass + +func (typeMap tokenClassifier) addRuneClass(runes string, tokenType runeTokenClass) { + for _, runeChar := range runes { + typeMap[runeChar] = tokenType + } +} + +// newDefaultClassifier creates a new classifier for ASCII characters. +func newDefaultClassifier() tokenClassifier { + t := tokenClassifier{} + t.addRuneClass(spaceRunes, spaceRuneClass) + t.addRuneClass(escapingQuoteRunes, escapingQuoteRuneClass) + t.addRuneClass(nonEscapingQuoteRunes, nonEscapingQuoteRuneClass) + t.addRuneClass(escapeRunes, escapeRuneClass) + t.addRuneClass(commentRunes, commentRuneClass) + return t +} + +// ClassifyRune classifiees a rune +func (t tokenClassifier) ClassifyRune(runeVal rune) runeTokenClass { + return t[runeVal] +} + +// Lexer turns an input stream into a sequence of tokens. Whitespace and comments are skipped. +type Lexer Tokenizer + +// NewLexer creates a new lexer from an input stream. +func NewLexer(r io.Reader) *Lexer { + + return (*Lexer)(NewTokenizer(r)) +} + +// Next returns the next word, or an error. If there are no more words, +// the error will be io.EOF. +func (l *Lexer) Next() (string, error) { + for { + token, err := (*Tokenizer)(l).Next() + if err != nil { + return "", err + } + switch token.tokenType { + case WordToken: + return token.value, nil + case CommentToken: + // skip comments + default: + return "", fmt.Errorf("Unknown token type: %v", token.tokenType) + } + } +} + +// Tokenizer turns an input stream into a sequence of typed tokens +type Tokenizer struct { + input bufio.Reader + classifier tokenClassifier +} + +// NewTokenizer creates a new tokenizer from an input stream. +func NewTokenizer(r io.Reader) *Tokenizer { + input := bufio.NewReader(r) + classifier := newDefaultClassifier() + return &Tokenizer{ + input: *input, + classifier: classifier} +} + +// scanStream scans the stream for the next token using the internal state machine. +// It will panic if it encounters a rune which it does not know how to handle. +func (t *Tokenizer) scanStream() (*Token, error) { + state := startState + var tokenType TokenType + var value []rune + var nextRune rune + var nextRuneType runeTokenClass + var err error + + for { + nextRune, _, err = t.input.ReadRune() + nextRuneType = t.classifier.ClassifyRune(nextRune) + + if err == io.EOF { + nextRuneType = eofRuneClass + err = nil + } else if err != nil { + return nil, err + } + + switch state { + case startState: // no runes read yet + { + switch nextRuneType { + case eofRuneClass: + { + return nil, io.EOF + } + case spaceRuneClass: + { + } + case escapingQuoteRuneClass: + { + tokenType = WordToken + state = quotingEscapingState + } + case nonEscapingQuoteRuneClass: + { + tokenType = WordToken + state = quotingState + } + case escapeRuneClass: + { + tokenType = WordToken + state = escapingState + } + case commentRuneClass: + { + tokenType = CommentToken + state = commentState + } + default: + { + tokenType = WordToken + value = append(value, nextRune) + state = inWordState + } + } + } + case inWordState: // in a regular word + { + switch nextRuneType { + case eofRuneClass: + { + token := &Token{ + tokenType: tokenType, + value: string(value)} + return token, err + } + case spaceRuneClass: + { + t.input.UnreadRune() + token := &Token{ + tokenType: tokenType, + value: string(value)} + return token, err + } + case escapingQuoteRuneClass: + { + state = quotingEscapingState + } + case nonEscapingQuoteRuneClass: + { + state = quotingState + } + case escapeRuneClass: + { + state = escapingState + } + default: + { + value = append(value, nextRune) + } + } + } + case escapingState: // the rune after an escape character + { + switch nextRuneType { + case eofRuneClass: + { + err = fmt.Errorf("EOF found after escape character") + token := &Token{ + tokenType: tokenType, + value: string(value)} + return token, err + } + default: + { + state = inWordState + value = append(value, nextRune) + } + } + } + case escapingQuotedState: // the next rune after an escape character, in double quotes + { + switch nextRuneType { + case eofRuneClass: + { + err = fmt.Errorf("EOF found after escape character") + token := &Token{ + tokenType: tokenType, + value: string(value)} + return token, err + } + default: + { + state = quotingEscapingState + value = append(value, nextRune) + } + } + } + case quotingEscapingState: // in escaping double quotes + { + switch nextRuneType { + case eofRuneClass: + { + err = fmt.Errorf("EOF found when expecting closing quote") + token := &Token{ + tokenType: tokenType, + value: string(value)} + return token, err + } + case escapingQuoteRuneClass: + { + state = inWordState + } + case escapeRuneClass: + { + state = escapingQuotedState + } + default: + { + value = append(value, nextRune) + } + } + } + case quotingState: // in non-escaping single quotes + { + switch nextRuneType { + case eofRuneClass: + { + err = fmt.Errorf("EOF found when expecting closing quote") + token := &Token{ + tokenType: tokenType, + value: string(value)} + return token, err + } + case nonEscapingQuoteRuneClass: + { + state = inWordState + } + default: + { + value = append(value, nextRune) + } + } + } + case commentState: // in a comment + { + switch nextRuneType { + case eofRuneClass: + { + token := &Token{ + tokenType: tokenType, + value: string(value)} + return token, err + } + case spaceRuneClass: + { + if nextRune == '\n' { + state = startState + token := &Token{ + tokenType: tokenType, + value: string(value)} + return token, err + } else { + value = append(value, nextRune) + } + } + default: + { + value = append(value, nextRune) + } + } + } + default: + { + return nil, fmt.Errorf("Unexpected state: %v", state) + } + } + } +} + +// Next returns the next token in the stream. +func (t *Tokenizer) Next() (*Token, error) { + return t.scanStream() +} + +// Split partitions a string into a slice of strings. +func Split(s string) ([]string, error) { + l := NewLexer(strings.NewReader(s)) + subStrings := make([]string, 0) + for { + word, err := l.Next() + if err != nil { + if err == io.EOF { + return subStrings, nil + } + return subStrings, err + } + subStrings = append(subStrings, word) + } +} diff --git a/vendor/github.com/gotestyourself/gotestyourself/README.md b/vendor/github.com/gotestyourself/gotestyourself/README.md deleted file mode 100644 index aad893ae03362..0000000000000 --- a/vendor/github.com/gotestyourself/gotestyourself/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Go Test Yourself - -A collection of packages compatible with `go test` to support common testing -patterns. - -[![GoDoc](https://godoc.org/github.com/gotestyourself/gotestyourself?status.svg)](https://godoc.org/github.com/gotestyourself/gotestyourself) -[![CircleCI](https://circleci.com/gh/gotestyourself/gotestyourself/tree/master.svg?style=shield)](https://circleci.com/gh/gotestyourself/gotestyourself/tree/master) -[![Go Reportcard](https://goreportcard.com/badge/github.com/gotestyourself/gotestyourself)](https://goreportcard.com/report/github.com/gotestyourself/gotestyourself) - - -## Packages - -* [assert](http://godoc.org/github.com/gotestyourself/gotestyourself/assert) - - compare values and fail the test when the comparison fails -* [env](http://godoc.org/github.com/gotestyourself/gotestyourself/env) - - test code that uses environment variables -* [fs](http://godoc.org/github.com/gotestyourself/gotestyourself/fs) - - create test files and directories -* [golden](http://godoc.org/github.com/gotestyourself/gotestyourself/golden) - - compare large multi-line strings -* [icmd](http://godoc.org/github.com/gotestyourself/gotestyourself/icmd) - - execute binaries and test the output -* [poll](http://godoc.org/github.com/gotestyourself/gotestyourself/poll) - - test asynchronous code by polling until a desired state is reached -* [skip](http://godoc.org/github.com/gotestyourself/gotestyourself/skip) - - skip tests based on conditions -* [testsum](http://godoc.org/github.com/gotestyourself/gotestyourself/testsum) - - a program to summarize `go test` output and test failures - -## Related - -* [maxbrunsfeld/counterfeiter](https://github.com/maxbrunsfeld/counterfeiter) - generate fakes for interfaces -* [jonboulle/clockwork](https://github.com/jonboulle/clockwork) - a fake clock for testing code that uses `time` diff --git a/vendor/github.com/hashicorp/go-immutable-radix/iradix.go b/vendor/github.com/hashicorp/go-immutable-radix/iradix.go index b25558388f2b8..c7172c406b444 100644 --- a/vendor/github.com/hashicorp/go-immutable-radix/iradix.go +++ b/vendor/github.com/hashicorp/go-immutable-radix/iradix.go @@ -2,6 +2,7 @@ package iradix import ( "bytes" + "strings" "github.com/hashicorp/golang-lru/simplelru" ) @@ -11,7 +12,9 @@ const ( // cache used per transaction. This is used to cache the updates // to the nodes near the root, while the leaves do not need to be // cached. This is important for very large transactions to prevent - // the modified cache from growing to be enormous. + // the modified cache from growing to be enormous. This is also used + // to set the max size of the mutation notify maps since those should + // also be bounded in a similar way. defaultModifiedCache = 8192 ) @@ -27,7 +30,11 @@ type Tree struct { // New returns an empty Tree func New() *Tree { - t := &Tree{root: &Node{}} + t := &Tree{ + root: &Node{ + mutateCh: make(chan struct{}), + }, + } return t } @@ -40,75 +47,208 @@ func (t *Tree) Len() int { // atomically and returns a new tree when committed. A transaction // is not thread safe, and should only be used by a single goroutine. type Txn struct { - root *Node - size int - modified *simplelru.LRU + // root is the modified root for the transaction. + root *Node + + // snap is a snapshot of the root node for use if we have to run the + // slow notify algorithm. + snap *Node + + // size tracks the size of the tree as it is modified during the + // transaction. + size int + + // writable is a cache of writable nodes that have been created during + // the course of the transaction. This allows us to re-use the same + // nodes for further writes and avoid unnecessary copies of nodes that + // have never been exposed outside the transaction. This will only hold + // up to defaultModifiedCache number of entries. + writable *simplelru.LRU + + // trackChannels is used to hold channels that need to be notified to + // signal mutation of the tree. This will only hold up to + // defaultModifiedCache number of entries, after which we will set the + // trackOverflow flag, which will cause us to use a more expensive + // algorithm to perform the notifications. Mutation tracking is only + // performed if trackMutate is true. + trackChannels map[chan struct{}]struct{} + trackOverflow bool + trackMutate bool } // Txn starts a new transaction that can be used to mutate the tree func (t *Tree) Txn() *Txn { txn := &Txn{ root: t.root, + snap: t.root, size: t.size, } return txn } -// writeNode returns a node to be modified, if the current -// node as already been modified during the course of -// the transaction, it is used in-place. -func (t *Txn) writeNode(n *Node) *Node { - // Ensure the modified set exists - if t.modified == nil { +// TrackMutate can be used to toggle if mutations are tracked. If this is enabled +// then notifications will be issued for affected internal nodes and leaves when +// the transaction is committed. +func (t *Txn) TrackMutate(track bool) { + t.trackMutate = track +} + +// trackChannel safely attempts to track the given mutation channel, setting the +// overflow flag if we can no longer track any more. This limits the amount of +// state that will accumulate during a transaction and we have a slower algorithm +// to switch to if we overflow. +func (t *Txn) trackChannel(ch chan struct{}) { + // In overflow, make sure we don't store any more objects. + if t.trackOverflow { + return + } + + // If this would overflow the state we reject it and set the flag (since + // we aren't tracking everything that's required any longer). + if len(t.trackChannels) >= defaultModifiedCache { + // Mark that we are in the overflow state + t.trackOverflow = true + + // Clear the map so that the channels can be garbage collected. It is + // safe to do this since we have already overflowed and will be using + // the slow notify algorithm. + t.trackChannels = nil + return + } + + // Create the map on the fly when we need it. + if t.trackChannels == nil { + t.trackChannels = make(map[chan struct{}]struct{}) + } + + // Otherwise we are good to track it. + t.trackChannels[ch] = struct{}{} +} + +// writeNode returns a node to be modified, if the current node has already been +// modified during the course of the transaction, it is used in-place. Set +// forLeafUpdate to true if you are getting a write node to update the leaf, +// which will set leaf mutation tracking appropriately as well. +func (t *Txn) writeNode(n *Node, forLeafUpdate bool) *Node { + // Ensure the writable set exists. + if t.writable == nil { lru, err := simplelru.NewLRU(defaultModifiedCache, nil) if err != nil { panic(err) } - t.modified = lru + t.writable = lru } - // If this node has already been modified, we can - // continue to use it during this transaction. - if _, ok := t.modified.Get(n); ok { + // If this node has already been modified, we can continue to use it + // during this transaction. We know that we don't need to track it for + // a node update since the node is writable, but if this is for a leaf + // update we track it, in case the initial write to this node didn't + // update the leaf. + if _, ok := t.writable.Get(n); ok { + if t.trackMutate && forLeafUpdate && n.leaf != nil { + t.trackChannel(n.leaf.mutateCh) + } return n } - // Copy the existing node - nc := new(Node) + // Mark this node as being mutated. + if t.trackMutate { + t.trackChannel(n.mutateCh) + } + + // Mark its leaf as being mutated, if appropriate. + if t.trackMutate && forLeafUpdate && n.leaf != nil { + t.trackChannel(n.leaf.mutateCh) + } + + // Copy the existing node. If you have set forLeafUpdate it will be + // safe to replace this leaf with another after you get your node for + // writing. You MUST replace it, because the channel associated with + // this leaf will be closed when this transaction is committed. + nc := &Node{ + mutateCh: make(chan struct{}), + leaf: n.leaf, + } if n.prefix != nil { nc.prefix = make([]byte, len(n.prefix)) copy(nc.prefix, n.prefix) } - if n.leaf != nil { - nc.leaf = new(leafNode) - *nc.leaf = *n.leaf - } if len(n.edges) != 0 { nc.edges = make([]edge, len(n.edges)) copy(nc.edges, n.edges) } - // Mark this node as modified - t.modified.Add(n, nil) + // Mark this node as writable. + t.writable.Add(nc, nil) return nc } +// Visit all the nodes in the tree under n, and add their mutateChannels to the transaction +// Returns the size of the subtree visited +func (t *Txn) trackChannelsAndCount(n *Node) int { + // Count only leaf nodes + leaves := 0 + if n.leaf != nil { + leaves = 1 + } + // Mark this node as being mutated. + if t.trackMutate { + t.trackChannel(n.mutateCh) + } + + // Mark its leaf as being mutated, if appropriate. + if t.trackMutate && n.leaf != nil { + t.trackChannel(n.leaf.mutateCh) + } + + // Recurse on the children + for _, e := range n.edges { + leaves += t.trackChannelsAndCount(e.node) + } + return leaves +} + +// mergeChild is called to collapse the given node with its child. This is only +// called when the given node is not a leaf and has a single edge. +func (t *Txn) mergeChild(n *Node) { + // Mark the child node as being mutated since we are about to abandon + // it. We don't need to mark the leaf since we are retaining it if it + // is there. + e := n.edges[0] + child := e.node + if t.trackMutate { + t.trackChannel(child.mutateCh) + } + + // Merge the nodes. + n.prefix = concat(n.prefix, child.prefix) + n.leaf = child.leaf + if len(child.edges) != 0 { + n.edges = make([]edge, len(child.edges)) + copy(n.edges, child.edges) + } else { + n.edges = nil + } +} + // insert does a recursive insertion func (t *Txn) insert(n *Node, k, search []byte, v interface{}) (*Node, interface{}, bool) { - // Handle key exhaution + // Handle key exhaustion if len(search) == 0 { - nc := t.writeNode(n) + var oldVal interface{} + didUpdate := false if n.isLeaf() { - old := nc.leaf.val - nc.leaf.val = v - return nc, old, true - } else { - nc.leaf = &leafNode{ - key: k, - val: v, - } - return nc, nil, false + oldVal = n.leaf.val + didUpdate = true } + + nc := t.writeNode(n, true) + nc.leaf = &leafNode{ + mutateCh: make(chan struct{}), + key: k, + val: v, + } + return nc, oldVal, didUpdate } // Look for the edge @@ -119,14 +259,16 @@ func (t *Txn) insert(n *Node, k, search []byte, v interface{}) (*Node, interface e := edge{ label: search[0], node: &Node{ + mutateCh: make(chan struct{}), leaf: &leafNode{ - key: k, - val: v, + mutateCh: make(chan struct{}), + key: k, + val: v, }, prefix: search, }, } - nc := t.writeNode(n) + nc := t.writeNode(n, false) nc.addEdge(e) return nc, nil, false } @@ -137,7 +279,7 @@ func (t *Txn) insert(n *Node, k, search []byte, v interface{}) (*Node, interface search = search[commonPrefix:] newChild, oldVal, didUpdate := t.insert(child, k, search, v) if newChild != nil { - nc := t.writeNode(n) + nc := t.writeNode(n, false) nc.edges[idx].node = newChild return nc, oldVal, didUpdate } @@ -145,9 +287,10 @@ func (t *Txn) insert(n *Node, k, search []byte, v interface{}) (*Node, interface } // Split the node - nc := t.writeNode(n) + nc := t.writeNode(n, false) splitNode := &Node{ - prefix: search[:commonPrefix], + mutateCh: make(chan struct{}), + prefix: search[:commonPrefix], } nc.replaceEdge(edge{ label: search[0], @@ -155,7 +298,7 @@ func (t *Txn) insert(n *Node, k, search []byte, v interface{}) (*Node, interface }) // Restore the existing child node - modChild := t.writeNode(child) + modChild := t.writeNode(child, false) splitNode.addEdge(edge{ label: modChild.prefix[commonPrefix], node: modChild, @@ -164,8 +307,9 @@ func (t *Txn) insert(n *Node, k, search []byte, v interface{}) (*Node, interface // Create a new leaf node leaf := &leafNode{ - key: k, - val: v, + mutateCh: make(chan struct{}), + key: k, + val: v, } // If the new key is a subset, add to to this node @@ -179,8 +323,9 @@ func (t *Txn) insert(n *Node, k, search []byte, v interface{}) (*Node, interface splitNode.addEdge(edge{ label: search[0], node: &Node{ - leaf: leaf, - prefix: search, + mutateCh: make(chan struct{}), + leaf: leaf, + prefix: search, }, }) return nc, nil, false @@ -188,19 +333,19 @@ func (t *Txn) insert(n *Node, k, search []byte, v interface{}) (*Node, interface // delete does a recursive deletion func (t *Txn) delete(parent, n *Node, search []byte) (*Node, *leafNode) { - // Check for key exhaution + // Check for key exhaustion if len(search) == 0 { if !n.isLeaf() { return nil, nil } // Remove the leaf node - nc := t.writeNode(n) + nc := t.writeNode(n, true) nc.leaf = nil // Check if this node should be merged if n != t.root && len(nc.edges) == 1 { - nc.mergeChild() + t.mergeChild(nc) } return nc, n.leaf } @@ -219,14 +364,17 @@ func (t *Txn) delete(parent, n *Node, search []byte) (*Node, *leafNode) { return nil, nil } - // Copy this node - nc := t.writeNode(n) + // Copy this node. WATCH OUT - it's safe to pass "false" here because we + // will only ADD a leaf via nc.mergeChild() if there isn't one due to + // the !nc.isLeaf() check in the logic just below. This is pretty subtle, + // so be careful if you change any of the logic here. + nc := t.writeNode(n, false) // Delete the edge if the node has no edges if newChild.leaf == nil && len(newChild.edges) == 0 { nc.delEdge(label) if n != t.root && len(nc.edges) == 1 && !nc.isLeaf() { - nc.mergeChild() + t.mergeChild(nc) } } else { nc.edges[idx].node = newChild @@ -234,6 +382,56 @@ func (t *Txn) delete(parent, n *Node, search []byte) (*Node, *leafNode) { return nc, leaf } +// delete does a recursive deletion +func (t *Txn) deletePrefix(parent, n *Node, search []byte) (*Node, int) { + // Check for key exhaustion + if len(search) == 0 { + nc := t.writeNode(n, true) + if n.isLeaf() { + nc.leaf = nil + } + nc.edges = nil + return nc, t.trackChannelsAndCount(n) + } + + // Look for an edge + label := search[0] + idx, child := n.getEdge(label) + // We make sure that either the child node's prefix starts with the search term, or the search term starts with the child node's prefix + // Need to do both so that we can delete prefixes that don't correspond to any node in the tree + if child == nil || (!bytes.HasPrefix(child.prefix, search) && !bytes.HasPrefix(search, child.prefix)) { + return nil, 0 + } + + // Consume the search prefix + if len(child.prefix) > len(search) { + search = []byte("") + } else { + search = search[len(child.prefix):] + } + newChild, numDeletions := t.deletePrefix(n, child, search) + if newChild == nil { + return nil, 0 + } + // Copy this node. WATCH OUT - it's safe to pass "false" here because we + // will only ADD a leaf via nc.mergeChild() if there isn't one due to + // the !nc.isLeaf() check in the logic just below. This is pretty subtle, + // so be careful if you change any of the logic here. + + nc := t.writeNode(n, false) + + // Delete the edge if the node has no edges + if newChild.leaf == nil && len(newChild.edges) == 0 { + nc.delEdge(label) + if n != t.root && len(nc.edges) == 1 && !nc.isLeaf() { + t.mergeChild(nc) + } + } else { + nc.edges[idx].node = newChild + } + return nc, numDeletions +} + // Insert is used to add or update a given key. The return provides // the previous value and a bool indicating if any was set. func (t *Txn) Insert(k []byte, v interface{}) (interface{}, bool) { @@ -261,6 +459,19 @@ func (t *Txn) Delete(k []byte) (interface{}, bool) { return nil, false } +// DeletePrefix is used to delete an entire subtree that matches the prefix +// This will delete all nodes under that prefix +func (t *Txn) DeletePrefix(prefix []byte) bool { + newRoot, numDeletions := t.deletePrefix(nil, t.root, prefix) + if newRoot != nil { + t.root = newRoot + t.size = t.size - numDeletions + return true + } + return false + +} + // Root returns the current root of the radix tree within this // transaction. The root is not safe across insert and delete operations, // but can be used to read the current state during a transaction. @@ -274,10 +485,115 @@ func (t *Txn) Get(k []byte) (interface{}, bool) { return t.root.Get(k) } -// Commit is used to finalize the transaction and return a new tree +// GetWatch is used to lookup a specific key, returning +// the watch channel, value and if it was found +func (t *Txn) GetWatch(k []byte) (<-chan struct{}, interface{}, bool) { + return t.root.GetWatch(k) +} + +// Commit is used to finalize the transaction and return a new tree. If mutation +// tracking is turned on then notifications will also be issued. func (t *Txn) Commit() *Tree { - t.modified = nil - return &Tree{t.root, t.size} + nt := t.CommitOnly() + if t.trackMutate { + t.Notify() + } + return nt +} + +// CommitOnly is used to finalize the transaction and return a new tree, but +// does not issue any notifications until Notify is called. +func (t *Txn) CommitOnly() *Tree { + nt := &Tree{t.root, t.size} + t.writable = nil + return nt +} + +// slowNotify does a complete comparison of the before and after trees in order +// to trigger notifications. This doesn't require any additional state but it +// is very expensive to compute. +func (t *Txn) slowNotify() { + snapIter := t.snap.rawIterator() + rootIter := t.root.rawIterator() + for snapIter.Front() != nil || rootIter.Front() != nil { + // If we've exhausted the nodes in the old snapshot, we know + // there's nothing remaining to notify. + if snapIter.Front() == nil { + return + } + snapElem := snapIter.Front() + + // If we've exhausted the nodes in the new root, we know we need + // to invalidate everything that remains in the old snapshot. We + // know from the loop condition there's something in the old + // snapshot. + if rootIter.Front() == nil { + close(snapElem.mutateCh) + if snapElem.isLeaf() { + close(snapElem.leaf.mutateCh) + } + snapIter.Next() + continue + } + + // Do one string compare so we can check the various conditions + // below without repeating the compare. + cmp := strings.Compare(snapIter.Path(), rootIter.Path()) + + // If the snapshot is behind the root, then we must have deleted + // this node during the transaction. + if cmp < 0 { + close(snapElem.mutateCh) + if snapElem.isLeaf() { + close(snapElem.leaf.mutateCh) + } + snapIter.Next() + continue + } + + // If the snapshot is ahead of the root, then we must have added + // this node during the transaction. + if cmp > 0 { + rootIter.Next() + continue + } + + // If we have the same path, then we need to see if we mutated a + // node and possibly the leaf. + rootElem := rootIter.Front() + if snapElem != rootElem { + close(snapElem.mutateCh) + if snapElem.leaf != nil && (snapElem.leaf != rootElem.leaf) { + close(snapElem.leaf.mutateCh) + } + } + snapIter.Next() + rootIter.Next() + } +} + +// Notify is used along with TrackMutate to trigger notifications. This must +// only be done once a transaction is committed via CommitOnly, and it is called +// automatically by Commit. +func (t *Txn) Notify() { + if !t.trackMutate { + return + } + + // If we've overflowed the tracking state we can't use it in any way and + // need to do a full tree compare. + if t.trackOverflow { + t.slowNotify() + } else { + for ch := range t.trackChannels { + close(ch) + } + } + + // Clean up the tracking state so that a re-notify is safe (will trigger + // the else clause above which will be a no-op). + t.trackChannels = nil + t.trackOverflow = false } // Insert is used to add or update a given key. The return provides @@ -296,6 +612,14 @@ func (t *Tree) Delete(k []byte) (*Tree, interface{}, bool) { return txn.Commit(), old, ok } +// DeletePrefix is used to delete all nodes starting with a given prefix. Returns the new tree, +// and a bool indicating if the prefix matched any nodes +func (t *Tree) DeletePrefix(k []byte) (*Tree, bool) { + txn := t.Txn() + ok := txn.DeletePrefix(k) + return txn.Commit(), ok +} + // Root returns the root node of the tree which can be used for richer // query operations. func (t *Tree) Root() *Node { diff --git a/vendor/github.com/hashicorp/go-immutable-radix/iter.go b/vendor/github.com/hashicorp/go-immutable-radix/iter.go index 75cbaa110ff6e..9815e02538e5f 100644 --- a/vendor/github.com/hashicorp/go-immutable-radix/iter.go +++ b/vendor/github.com/hashicorp/go-immutable-radix/iter.go @@ -9,11 +9,13 @@ type Iterator struct { stack []edges } -// SeekPrefix is used to seek the iterator to a given prefix -func (i *Iterator) SeekPrefix(prefix []byte) { +// SeekPrefixWatch is used to seek the iterator to a given prefix +// and returns the watch channel of the finest granularity +func (i *Iterator) SeekPrefixWatch(prefix []byte) (watch <-chan struct{}) { // Wipe the stack i.stack = nil n := i.node + watch = n.mutateCh search := prefix for { // Check for key exhaution @@ -29,6 +31,9 @@ func (i *Iterator) SeekPrefix(prefix []byte) { return } + // Update to the finest granularity as the search makes progress + watch = n.mutateCh + // Consume the search prefix if bytes.HasPrefix(search, n.prefix) { search = search[len(n.prefix):] @@ -43,6 +48,11 @@ func (i *Iterator) SeekPrefix(prefix []byte) { } } +// SeekPrefix is used to seek the iterator to a given prefix +func (i *Iterator) SeekPrefix(prefix []byte) { + i.SeekPrefixWatch(prefix) +} + // Next returns the next node in order func (i *Iterator) Next() ([]byte, interface{}, bool) { // Initialize our stack if needed diff --git a/vendor/github.com/hashicorp/go-immutable-radix/node.go b/vendor/github.com/hashicorp/go-immutable-radix/node.go index fea6f6343669c..ef494fa7cacea 100644 --- a/vendor/github.com/hashicorp/go-immutable-radix/node.go +++ b/vendor/github.com/hashicorp/go-immutable-radix/node.go @@ -12,8 +12,9 @@ type WalkFn func(k []byte, v interface{}) bool // leafNode is used to represent a value type leafNode struct { - key []byte - val interface{} + mutateCh chan struct{} + key []byte + val interface{} } // edge is used to represent an edge node @@ -24,6 +25,9 @@ type edge struct { // Node is an immutable node in the radix tree type Node struct { + // mutateCh is closed if this node is modified + mutateCh chan struct{} + // leaf is used to store possible leaf leaf *leafNode @@ -87,31 +91,14 @@ func (n *Node) delEdge(label byte) { } } -func (n *Node) mergeChild() { - e := n.edges[0] - child := e.node - n.prefix = concat(n.prefix, child.prefix) - if child.leaf != nil { - n.leaf = new(leafNode) - *n.leaf = *child.leaf - } else { - n.leaf = nil - } - if len(child.edges) != 0 { - n.edges = make([]edge, len(child.edges)) - copy(n.edges, child.edges) - } else { - n.edges = nil - } -} - -func (n *Node) Get(k []byte) (interface{}, bool) { +func (n *Node) GetWatch(k []byte) (<-chan struct{}, interface{}, bool) { search := k + watch := n.mutateCh for { - // Check for key exhaution + // Check for key exhaustion if len(search) == 0 { if n.isLeaf() { - return n.leaf.val, true + return n.leaf.mutateCh, n.leaf.val, true } break } @@ -122,6 +109,9 @@ func (n *Node) Get(k []byte) (interface{}, bool) { break } + // Update to the finest granularity as the search makes progress + watch = n.mutateCh + // Consume the search prefix if bytes.HasPrefix(search, n.prefix) { search = search[len(n.prefix):] @@ -129,7 +119,12 @@ func (n *Node) Get(k []byte) (interface{}, bool) { break } } - return nil, false + return watch, nil, false +} + +func (n *Node) Get(k []byte) (interface{}, bool) { + _, val, ok := n.GetWatch(k) + return val, ok } // LongestPrefix is like Get, but instead of an @@ -204,6 +199,14 @@ func (n *Node) Iterator() *Iterator { return &Iterator{node: n} } +// rawIterator is used to return a raw iterator at the given node to walk the +// tree. +func (n *Node) rawIterator() *rawIterator { + iter := &rawIterator{node: n} + iter.Next() + return iter +} + // Walk is used to walk the tree func (n *Node) Walk(fn WalkFn) { recursiveWalk(n, fn) @@ -271,6 +274,66 @@ func (n *Node) WalkPath(path []byte, fn WalkFn) { } } +func (n *Node) Seek(prefix []byte) *Seeker { + search := prefix + p := &pos{n: n} + for { + // Check for key exhaution + if len(search) == 0 { + return &Seeker{p} + } + + num := len(n.edges) + idx := sort.Search(num, func(i int) bool { + return n.edges[i].label >= search[0] + }) + p.current = idx + if idx < len(n.edges) { + n = n.edges[idx].node + if bytes.HasPrefix(search, n.prefix) && len(n.edges) > 0 { + search = search[len(n.prefix):] + p.current++ + p = &pos{n: n, prev: p} + continue + } + } + p.current++ + return &Seeker{p} + } +} + +type Seeker struct { + *pos +} + +type pos struct { + n *Node + current int + prev *pos + isLeaf bool +} + +func (s *Seeker) Next() (k []byte, v interface{}, ok bool) { + if s.current >= len(s.n.edges) { + if s.prev == nil { + return nil, nil, false + } + s.pos = s.prev + return s.Next() + } + + edge := s.n.edges[s.current] + s.current++ + if edge.node.leaf != nil && !s.isLeaf { + s.isLeaf = true + s.current-- + return edge.node.leaf.key, edge.node.leaf.val, true + } + s.isLeaf = false + s.pos = &pos{n: edge.node, prev: s.pos} + return s.Next() +} + // recursiveWalk is used to do a pre-order walk of a node // recursively. Returns true if the walk should be aborted func recursiveWalk(n *Node, fn WalkFn) bool { diff --git a/vendor/github.com/hashicorp/go-immutable-radix/raw_iter.go b/vendor/github.com/hashicorp/go-immutable-radix/raw_iter.go new file mode 100644 index 0000000000000..04814c1323fb9 --- /dev/null +++ b/vendor/github.com/hashicorp/go-immutable-radix/raw_iter.go @@ -0,0 +1,78 @@ +package iradix + +// rawIterator visits each of the nodes in the tree, even the ones that are not +// leaves. It keeps track of the effective path (what a leaf at a given node +// would be called), which is useful for comparing trees. +type rawIterator struct { + // node is the starting node in the tree for the iterator. + node *Node + + // stack keeps track of edges in the frontier. + stack []rawStackEntry + + // pos is the current position of the iterator. + pos *Node + + // path is the effective path of the current iterator position, + // regardless of whether the current node is a leaf. + path string +} + +// rawStackEntry is used to keep track of the cumulative common path as well as +// its associated edges in the frontier. +type rawStackEntry struct { + path string + edges edges +} + +// Front returns the current node that has been iterated to. +func (i *rawIterator) Front() *Node { + return i.pos +} + +// Path returns the effective path of the current node, even if it's not actually +// a leaf. +func (i *rawIterator) Path() string { + return i.path +} + +// Next advances the iterator to the next node. +func (i *rawIterator) Next() { + // Initialize our stack if needed. + if i.stack == nil && i.node != nil { + i.stack = []rawStackEntry{ + rawStackEntry{ + edges: edges{ + edge{node: i.node}, + }, + }, + } + } + + for len(i.stack) > 0 { + // Inspect the last element of the stack. + n := len(i.stack) + last := i.stack[n-1] + elem := last.edges[0].node + + // Update the stack. + if len(last.edges) > 1 { + i.stack[n-1].edges = last.edges[1:] + } else { + i.stack = i.stack[:n-1] + } + + // Push the edges onto the frontier. + if len(elem.edges) > 0 { + path := last.path + string(elem.prefix) + i.stack = append(i.stack, rawStackEntry{path, elem.edges}) + } + + i.pos = elem + i.path = last.path + string(elem.prefix) + return + } + + i.pos = nil + i.path = "" +} diff --git a/vendor/github.com/hashicorp/go-sockaddr/ifaddr.go b/vendor/github.com/hashicorp/go-sockaddr/ifaddr.go index 3e4ff9fca433b..0811b27599056 100644 --- a/vendor/github.com/hashicorp/go-sockaddr/ifaddr.go +++ b/vendor/github.com/hashicorp/go-sockaddr/ifaddr.go @@ -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" <> | 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 diff --git a/vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go b/vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go index 8233be2022bf5..2a706c34e92b8 100644 --- a/vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go +++ b/vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go @@ -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 } diff --git a/vendor/github.com/hashicorp/go-sockaddr/ipv4addr.go b/vendor/github.com/hashicorp/go-sockaddr/ipv4addr.go index 9f2616a69f627..4d395dc954b65 100644 --- a/vendor/github.com/hashicorp/go-sockaddr/ipv4addr.go +++ b/vendor/github.com/hashicorp/go-sockaddr/ipv4addr.go @@ -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]] } diff --git a/vendor/github.com/hashicorp/go-sockaddr/rfc.go b/vendor/github.com/hashicorp/go-sockaddr/rfc.go index fd9be940b1130..02e188f6fe647 100644 --- a/vendor/github.com/hashicorp/go-sockaddr/rfc.go +++ b/vendor/github.com/hashicorp/go-sockaddr/rfc.go @@ -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 { diff --git a/vendor/github.com/hashicorp/go-sockaddr/route_info_linux.go b/vendor/github.com/hashicorp/go-sockaddr/route_info_linux.go index b33e4c0d0848e..c2ec91eaf456e 100644 --- a/vendor/github.com/hashicorp/go-sockaddr/route_info_linux.go +++ b/vendor/github.com/hashicorp/go-sockaddr/route_info_linux.go @@ -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 } diff --git a/vendor/github.com/hashicorp/go-sockaddr/sockaddr.go b/vendor/github.com/hashicorp/go-sockaddr/sockaddr.go index 51389ebe9a690..826c91c2e3d06 100644 --- a/vendor/github.com/hashicorp/go-sockaddr/sockaddr.go +++ b/vendor/github.com/hashicorp/go-sockaddr/sockaddr.go @@ -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 +} diff --git a/vendor/github.com/imdario/mergo/README.md b/vendor/github.com/imdario/mergo/README.md index 4f0f990fc1bc9..d1cefa87189d2 100644 --- a/vendor/github.com/imdario/mergo/README.md +++ b/vendor/github.com/imdario/mergo/README.md @@ -2,31 +2,72 @@ A helper to merge structs and maps in Golang. Useful for configuration default values, avoiding messy if-statements. -Also a lovely [comune](http://en.wikipedia.org/wiki/Mergo) (municipality) in the Province of Ancona in the Italian region Marche. - -![Mergo dall'alto](http://www.comune.mergo.an.it/Siti/Mergo/Immagini/Foto/mergo_dall_alto.jpg) +Also a lovely [comune](http://en.wikipedia.org/wiki/Mergo) (municipality) in the Province of Ancona in the Italian region of Marche. ## Status -It is ready for production use. It works fine after extensive use in the wild. +It is ready for production use. [It is used in several projects by Docker, Google, The Linux Foundation, VMWare, Shopify, etc](https://github.com/imdario/mergo#mergo-in-the-wild). +[![GoDoc][3]][4] +[![GoCard][5]][6] [![Build Status][1]][2] -[![GoDoc](https://godoc.org/github.com/imdario/mergo?status.svg)](https://godoc.org/github.com/imdario/mergo) +[![Coverage Status][7]][8] +[![Sourcegraph][9]][10] [1]: https://travis-ci.org/imdario/mergo.png [2]: https://travis-ci.org/imdario/mergo +[3]: https://godoc.org/github.com/imdario/mergo?status.svg +[4]: https://godoc.org/github.com/imdario/mergo +[5]: https://goreportcard.com/badge/imdario/mergo +[6]: https://goreportcard.com/report/github.com/imdario/mergo +[7]: https://coveralls.io/repos/github/imdario/mergo/badge.svg?branch=master +[8]: https://coveralls.io/github/imdario/mergo?branch=master +[9]: https://sourcegraph.com/github.com/imdario/mergo/-/badge.svg +[10]: https://sourcegraph.com/github.com/imdario/mergo?badge + +### Latest release + +[Release v0.3.4](https://github.com/imdario/mergo/releases/tag/v0.3.4). ### Important note -Mergo is intended to assign **only** zero value fields on destination with source value. Since April 6th it works like this. Before it didn't work properly, causing some random overwrites. After some issues and PRs I found it didn't merge as I designed it. Thanks to [imdario/mergo#8](https://github.com/imdario/mergo/pull/8) overwriting functions were added and the wrong behavior was clearly detected. +Please keep in mind that in [0.3.2](//github.com/imdario/mergo/releases/tag/0.3.2) Mergo changed `Merge()`and `Map()` signatures to support [transformers](#transformers). An optional/variadic argument has been added, so it won't break existing code. If you were using Mergo **before** April 6th 2015, please check your project works as intended after updating your local copy with ```go get -u github.com/imdario/mergo```. I apologize for any issue caused by its previous behavior and any future bug that Mergo could cause (I hope it won't!) in existing projects after the change (release 0.2.0). +### Donations + +If Mergo is useful to you, consider buying me a coffee, a beer or making a monthly donation so I can keep building great free software. :heart_eyes: + +Buy Me a Coffee at ko-fi.com +[![Beerpay](https://beerpay.io/imdario/mergo/badge.svg)](https://beerpay.io/imdario/mergo) +[![Beerpay](https://beerpay.io/imdario/mergo/make-wish.svg)](https://beerpay.io/imdario/mergo) +Donate using Liberapay + ### Mergo in the wild +- [moby/moby](https://github.com/moby/moby) +- [kubernetes/kubernetes](https://github.com/kubernetes/kubernetes) +- [vmware/dispatch](https://github.com/vmware/dispatch) +- [Shopify/themekit](https://github.com/Shopify/themekit) - [imdario/zas](https://github.com/imdario/zas) -- [GoogleCloudPlatform/kubernetes](https://github.com/GoogleCloudPlatform/kubernetes) +- [matcornic/hermes](https://github.com/matcornic/hermes) +- [OpenBazaar/openbazaar-go](https://github.com/OpenBazaar/openbazaar-go) +- [kataras/iris](https://github.com/kataras/iris) +- [michaelsauter/crane](https://github.com/michaelsauter/crane) +- [go-task/task](https://github.com/go-task/task) +- [sensu/uchiwa](https://github.com/sensu/uchiwa) +- [ory/hydra](https://github.com/ory/hydra) +- [sisatech/vcli](https://github.com/sisatech/vcli) +- [dairycart/dairycart](https://github.com/dairycart/dairycart) +- [projectcalico/felix](https://github.com/projectcalico/felix) +- [resin-os/balena](https://github.com/resin-os/balena) +- [go-kivik/kivik](https://github.com/go-kivik/kivik) +- [Telefonica/govice](https://github.com/Telefonica/govice) +- [supergiant/supergiant](supergiant/supergiant) +- [SergeyTsalkov/brooce](https://github.com/SergeyTsalkov/brooce) - [soniah/dnsmadeeasy](https://github.com/soniah/dnsmadeeasy) +- [ohsu-comp-bio/funnel](https://github.com/ohsu-comp-bio/funnel) - [EagerIO/Stout](https://github.com/EagerIO/Stout) - [lynndylanhurley/defsynth-api](https://github.com/lynndylanhurley/defsynth-api) - [russross/canvasassignments](https://github.com/russross/canvasassignments) @@ -44,6 +85,7 @@ If you were using Mergo **before** April 6th 2015, please check your project wor - [thoas/picfit](https://github.com/thoas/picfit) - [mantasmatelis/whooplist-server](https://github.com/mantasmatelis/whooplist-server) - [jnuthong/item_search](https://github.com/jnuthong/item_search) +- [bukalapak/snowboard](https://github.com/bukalapak/snowboard) ## Installation @@ -56,19 +98,31 @@ If you were using Mergo **before** April 6th 2015, please check your project wor ## Usage -You can only merge same-type structs with exported fields initialized as zero value of their type and same-types maps. Mergo won't merge unexported (private) fields but will do recursively any exported one. Also maps will be merged recursively except for structs inside maps (because they are not addressable using Go reflection). +You can only merge same-type structs with exported fields initialized as zero value of their type and same-types maps. Mergo won't merge unexported (private) fields but will do recursively any exported one. It won't merge empty structs value as [they are not considered zero values](https://golang.org/ref/spec#The_zero_value) either. Also maps will be merged recursively except for structs inside maps (because they are not addressable using Go reflection). - if err := mergo.Merge(&dst, src); err != nil { - // ... - } +```go +if err := mergo.Merge(&dst, src); err != nil { + // ... +} +``` -Additionally, you can map a map[string]interface{} to a struct (and otherwise, from struct to map), following the same restrictions as in Merge(). Keys are capitalized to find each corresponding exported field. +Also, you can merge overwriting values using the transformer `WithOverride`. - if err := mergo.Map(&dst, srcMap); err != nil { - // ... - } +```go +if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil { + // ... +} +``` -Warning: if you map a struct to map, it won't do it recursively. Don't expect Mergo to map struct members of your struct as map[string]interface{}. They will be just assigned as values. +Additionally, you can map a `map[string]interface{}` to a struct (and otherwise, from struct to map), following the same restrictions as in `Merge()`. Keys are capitalized to find each corresponding exported field. + +```go +if err := mergo.Map(&dst, srcMap); err != nil { + // ... +} +``` + +Warning: if you map a struct to map, it won't do it recursively. Don't expect Mergo to map struct members of your struct as `map[string]interface{}`. They will be just assigned as values. More information and examples in [godoc documentation](http://godoc.org/github.com/imdario/mergo). @@ -90,15 +144,12 @@ type Foo struct { func main() { src := Foo{ A: "one", + B: 2, } - dest := Foo{ A: "two", - B: 2, } - mergo.Merge(&dest, src) - fmt.Println(dest) // Will print // {two 2} @@ -107,7 +158,56 @@ func main() { Note: if test are failing due missing package, please execute: - go get gopkg.in/yaml.v1 + go get gopkg.in/yaml.v2 + +### Transformers + +Transformers allow to merge specific types differently than in the default behavior. In other words, now you can customize how some types are merged. For example, `time.Time` is a struct; it doesn't have zero value but IsZero can return true because it has fields with zero value. How can we merge a non-zero `time.Time`? + +```go +package main + +import ( + "fmt" + "github.com/imdario/mergo" + "reflect" + "time" +) + +type timeTransfomer struct { +} + +func (t timeTransfomer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error { + if typ == reflect.TypeOf(time.Time{}) { + return func(dst, src reflect.Value) error { + if dst.CanSet() { + isZero := dst.MethodByName("IsZero") + result := isZero.Call([]reflect.Value{}) + if result[0].Bool() { + dst.Set(src) + } + } + return nil + } + } + return nil +} + +type Snapshot struct { + Time time.Time + // ... +} + +func main() { + src := Snapshot{time.Now()} + dest := Snapshot{} + mergo.Merge(&dest, src, mergo.WithTransformers(timeTransfomer{})) + fmt.Println(dest) + // Will print + // { 2018-01-12 01:15:00 +0000 UTC m=+0.000000001 } +} +``` + ## Contact me diff --git a/vendor/github.com/imdario/mergo/map.go b/vendor/github.com/imdario/mergo/map.go index 1ed3d716aa4c7..6ea38e636b64f 100644 --- a/vendor/github.com/imdario/mergo/map.go +++ b/vendor/github.com/imdario/mergo/map.go @@ -31,7 +31,8 @@ func isExported(field reflect.StructField) bool { // Traverses recursively both values, assigning src's fields values to dst. // The map argument tracks comparisons that have already been seen, which allows // short circuiting on recursive types. -func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, overwrite bool) (err error) { +func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) { + overwrite := config.Overwrite if dst.CanAddr() { addr := dst.UnsafeAddr() h := 17 * addr @@ -61,6 +62,13 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, over dstMap[fieldName] = src.Field(i).Interface() } } + case reflect.Ptr: + if dst.IsNil() { + v := reflect.New(dst.Type().Elem()) + dst.Set(v) + } + dst = dst.Elem() + fallthrough case reflect.Struct: srcMap := src.Interface().(map[string]interface{}) for key := range srcMap { @@ -85,21 +93,24 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, over srcKind = reflect.Ptr } } + if !srcElement.IsValid() { continue } if srcKind == dstKind { - if err = deepMerge(dstElement, srcElement, visited, depth+1, overwrite); err != nil { + if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil { return } - } else { - if srcKind == reflect.Map { - if err = deepMap(dstElement, srcElement, visited, depth+1, overwrite); err != nil { - return - } - } else { - return fmt.Errorf("type mismatch on %s field: found %v, expected %v", fieldName, srcKind, dstKind) + } else if dstKind == reflect.Interface && dstElement.Kind() == reflect.Interface { + if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil { + return + } + } else if srcKind == reflect.Map { + if err = deepMap(dstElement, srcElement, visited, depth+1, config); err != nil { + return } + } else { + return fmt.Errorf("type mismatch on %s field: found %v, expected %v", fieldName, srcKind, dstKind) } } } @@ -117,26 +128,35 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, over // doesn't apply if dst is a map. // This is separated method from Merge because it is cleaner and it keeps sane // semantics: merging equal types, mapping different (restricted) types. -func Map(dst, src interface{}) error { - return _map(dst, src, false) +func Map(dst, src interface{}, opts ...func(*Config)) error { + return _map(dst, src, opts...) } -func MapWithOverwrite(dst, src interface{}) error { - return _map(dst, src, true) +// MapWithOverwrite will do the same as Map except that non-empty dst attributes will be overridden by +// non-empty src attribute values. +// Deprecated: Use Map(…) with WithOverride +func MapWithOverwrite(dst, src interface{}, opts ...func(*Config)) error { + return _map(dst, src, append(opts, WithOverride)...) } -func _map(dst, src interface{}, overwrite bool) error { +func _map(dst, src interface{}, opts ...func(*Config)) error { var ( vDst, vSrc reflect.Value err error ) + config := &Config{} + + for _, opt := range opts { + opt(config) + } + if vDst, vSrc, err = resolveValues(dst, src); err != nil { return err } // To be friction-less, we redirect equal-type arguments // to deepMerge. Only because arguments can be anything. if vSrc.Kind() == vDst.Kind() { - return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, overwrite) + return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config) } switch vSrc.Kind() { case reflect.Struct: @@ -150,5 +170,5 @@ func _map(dst, src interface{}, overwrite bool) error { default: return ErrNotSupported } - return deepMap(vDst, vSrc, make(map[uintptr]*visit), 0, overwrite) + return deepMap(vDst, vSrc, make(map[uintptr]*visit), 0, config) } diff --git a/vendor/github.com/imdario/mergo/merge.go b/vendor/github.com/imdario/mergo/merge.go index a7dd9d82d1d60..706b22069c3e4 100644 --- a/vendor/github.com/imdario/mergo/merge.go +++ b/vendor/github.com/imdario/mergo/merge.go @@ -12,10 +12,34 @@ import ( "reflect" ) +func hasExportedField(dst reflect.Value) (exported bool) { + for i, n := 0, dst.NumField(); i < n; i++ { + field := dst.Type().Field(i) + if field.Anonymous && dst.Field(i).Kind() == reflect.Struct { + exported = exported || hasExportedField(dst.Field(i)) + } else { + exported = exported || len(field.PkgPath) == 0 + } + } + return +} + +type Config struct { + Overwrite bool + AppendSlice bool + Transformers Transformers +} + +type Transformers interface { + Transformer(reflect.Type) func(dst, src reflect.Value) error +} + // Traverses recursively both values, assigning src's fields values to dst. // The map argument tracks comparisons that have already been seen, which allows // short circuiting on recursive types. -func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, overwrite bool) (err error) { +func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) { + overwrite := config.Overwrite + if !src.IsValid() { return } @@ -32,14 +56,31 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov // Remember, remember... visited[h] = &visit{addr, typ, seen} } + + if config.Transformers != nil && !isEmptyValue(dst) { + if fn := config.Transformers.Transformer(dst.Type()); fn != nil { + err = fn(dst, src) + return + } + } + switch dst.Kind() { case reflect.Struct: - for i, n := 0, dst.NumField(); i < n; i++ { - if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, overwrite); err != nil { - return + if hasExportedField(dst) { + for i, n := 0, dst.NumField(); i < n; i++ { + if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, config); err != nil { + return + } + } + } else { + if dst.CanSet() && !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) { + dst.Set(src) } } case reflect.Map: + if dst.IsNil() && !src.IsNil() { + dst.Set(reflect.MakeMap(dst.Type())) + } for _, key := range src.MapKeys() { srcElement := src.MapIndex(key) if !srcElement.IsValid() { @@ -47,40 +88,99 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov } dstElement := dst.MapIndex(key) switch srcElement.Kind() { - case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Slice: + case reflect.Chan, reflect.Func, reflect.Map, reflect.Interface, reflect.Slice: if srcElement.IsNil() { continue } fallthrough default: + if !srcElement.CanInterface() { + continue + } switch reflect.TypeOf(srcElement.Interface()).Kind() { case reflect.Struct: fallthrough case reflect.Ptr: fallthrough case reflect.Map: - if err = deepMerge(dstElement, srcElement, visited, depth+1, overwrite); err != nil { + srcMapElm := srcElement + dstMapElm := dstElement + if srcMapElm.CanInterface() { + srcMapElm = reflect.ValueOf(srcMapElm.Interface()) + if dstMapElm.IsValid() { + dstMapElm = reflect.ValueOf(dstMapElm.Interface()) + } + } + if err = deepMerge(dstMapElm, srcMapElm, visited, depth+1, config); err != nil { return } + case reflect.Slice: + srcSlice := reflect.ValueOf(srcElement.Interface()) + + var dstSlice reflect.Value + if !dstElement.IsValid() || dstElement.IsNil() { + dstSlice = reflect.MakeSlice(srcSlice.Type(), 0, srcSlice.Len()) + } else { + dstSlice = reflect.ValueOf(dstElement.Interface()) + } + + if !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice { + dstSlice = srcSlice + } else if config.AppendSlice { + dstSlice = reflect.AppendSlice(dstSlice, srcSlice) + } + dst.SetMapIndex(key, dstSlice) } } - if !isEmptyValue(srcElement) && (overwrite || (!dstElement.IsValid() || isEmptyValue(dst))) { + if dstElement.IsValid() && reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map { + continue + } + + if srcElement.IsValid() && (overwrite || (!dstElement.IsValid() || isEmptyValue(dstElement))) { if dst.IsNil() { dst.Set(reflect.MakeMap(dst.Type())) } dst.SetMapIndex(key, srcElement) } } + case reflect.Slice: + if !dst.CanSet() { + break + } + if !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice { + dst.Set(src) + } else if config.AppendSlice { + dst.Set(reflect.AppendSlice(dst, src)) + } case reflect.Ptr: fallthrough case reflect.Interface: if src.IsNil() { break - } else if dst.IsNil() { + } + if src.Kind() != reflect.Interface { + if dst.IsNil() || overwrite { + if dst.CanSet() && (overwrite || isEmptyValue(dst)) { + dst.Set(src) + } + } else if src.Kind() == reflect.Ptr { + if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil { + return + } + } else if dst.Elem().Type() == src.Type() { + if err = deepMerge(dst.Elem(), src, visited, depth+1, config); err != nil { + return + } + } else { + return ErrDifferentArgumentsTypes + } + break + } + if dst.IsNil() || overwrite { if dst.CanSet() && (overwrite || isEmptyValue(dst)) { dst.Set(src) } - } else if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, overwrite); err != nil { + } else if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil { return } default: @@ -91,30 +191,55 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov return } -// Merge sets fields' values in dst from src if they have a zero -// value of their type. -// dst and src must be valid same-type structs and dst must be -// a pointer to struct. -// It won't merge unexported (private) fields and will do recursively -// any exported field. -func Merge(dst, src interface{}) error { - return merge(dst, src, false) +// Merge will fill any empty for value type attributes on the dst struct using corresponding +// src attributes if they themselves are not empty. dst and src must be valid same-type structs +// and dst must be a pointer to struct. +// It won't merge unexported (private) fields and will do recursively any exported field. +func Merge(dst, src interface{}, opts ...func(*Config)) error { + return merge(dst, src, opts...) +} + +// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overriden by +// non-empty src attribute values. +// Deprecated: use Merge(…) with WithOverride +func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error { + return merge(dst, src, append(opts, WithOverride)...) +} + +// WithTransformers adds transformers to merge, allowing to customize the merging of some types. +func WithTransformers(transformers Transformers) func(*Config) { + return func(config *Config) { + config.Transformers = transformers + } +} + +// WithOverride will make merge override non-empty dst attributes with non-empty src attributes values. +func WithOverride(config *Config) { + config.Overwrite = true } -func MergeWithOverwrite(dst, src interface{}) error { - return merge(dst, src, true) +// WithAppendSlice will make merge append slices instead of overwriting it +func WithAppendSlice(config *Config) { + config.AppendSlice = true } -func merge(dst, src interface{}, overwrite bool) error { +func merge(dst, src interface{}, opts ...func(*Config)) error { var ( vDst, vSrc reflect.Value err error ) + + config := &Config{} + + for _, opt := range opts { + opt(config) + } + if vDst, vSrc, err = resolveValues(dst, src); err != nil { return err } if vDst.Type() != vSrc.Type() { return ErrDifferentArgumentsTypes } - return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, overwrite) + return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config) } diff --git a/vendor/github.com/imdario/mergo/mergo.go b/vendor/github.com/imdario/mergo/mergo.go index f8a0991ec63e4..a82fea2fdccc3 100644 --- a/vendor/github.com/imdario/mergo/mergo.go +++ b/vendor/github.com/imdario/mergo/mergo.go @@ -32,7 +32,7 @@ type visit struct { next *visit } -// From src/pkg/encoding/json. +// From src/pkg/encoding/json/encode.go. func isEmptyValue(v reflect.Value) bool { switch v.Kind() { case reflect.Array, reflect.Map, reflect.Slice, reflect.String: @@ -46,7 +46,14 @@ func isEmptyValue(v reflect.Value) bool { case reflect.Float32, reflect.Float64: return v.Float() == 0 case reflect.Interface, reflect.Ptr: + if v.IsNil() { + return true + } + return isEmptyValue(v.Elem()) + case reflect.Func: return v.IsNil() + case reflect.Invalid: + return true } return false } diff --git a/vendor/github.com/mitchellh/hashstructure/LICENSE b/vendor/github.com/mitchellh/hashstructure/LICENSE new file mode 100644 index 0000000000000..a3866a291fd14 --- /dev/null +++ b/vendor/github.com/mitchellh/hashstructure/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Mitchell Hashimoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/mitchellh/hashstructure/README.md b/vendor/github.com/mitchellh/hashstructure/README.md new file mode 100644 index 0000000000000..28ce45a3e1813 --- /dev/null +++ b/vendor/github.com/mitchellh/hashstructure/README.md @@ -0,0 +1,65 @@ +# hashstructure [![GoDoc](https://godoc.org/github.com/mitchellh/hashstructure?status.svg)](https://godoc.org/github.com/mitchellh/hashstructure) + +hashstructure is a Go library for creating a unique hash value +for arbitrary values in Go. + +This can be used to key values in a hash (for use in a map, set, etc.) +that are complex. The most common use case is comparing two values without +sending data across the network, caching values locally (de-dup), and so on. + +## Features + + * Hash any arbitrary Go value, including complex types. + + * Tag a struct field to ignore it and not affect the hash value. + + * Tag a slice type struct field to treat it as a set where ordering + doesn't affect the hash code but the field itself is still taken into + account to create the hash value. + + * Optionally specify a custom hash function to optimize for speed, collision + avoidance for your data set, etc. + + * Optionally hash the output of `.String()` on structs that implement fmt.Stringer, + allowing effective hashing of time.Time + +## Installation + +Standard `go get`: + +``` +$ go get github.com/mitchellh/hashstructure +``` + +## Usage & Example + +For usage and examples see the [Godoc](http://godoc.org/github.com/mitchellh/hashstructure). + +A quick code example is shown below: + +```go +type ComplexStruct struct { + Name string + Age uint + Metadata map[string]interface{} +} + +v := ComplexStruct{ + Name: "mitchellh", + Age: 64, + Metadata: map[string]interface{}{ + "car": true, + "location": "California", + "siblings": []string{"Bob", "John"}, + }, +} + +hash, err := hashstructure.Hash(v, nil) +if err != nil { + panic(err) +} + +fmt.Printf("%d", hash) +// Output: +// 2307517237273902113 +``` diff --git a/vendor/github.com/mitchellh/hashstructure/hashstructure.go b/vendor/github.com/mitchellh/hashstructure/hashstructure.go new file mode 100644 index 0000000000000..ea13a1583c3b5 --- /dev/null +++ b/vendor/github.com/mitchellh/hashstructure/hashstructure.go @@ -0,0 +1,358 @@ +package hashstructure + +import ( + "encoding/binary" + "fmt" + "hash" + "hash/fnv" + "reflect" +) + +// ErrNotStringer is returned when there's an error with hash:"string" +type ErrNotStringer struct { + Field string +} + +// Error implements error for ErrNotStringer +func (ens *ErrNotStringer) Error() string { + return fmt.Sprintf("hashstructure: %s has hash:\"string\" set, but does not implement fmt.Stringer", ens.Field) +} + +// HashOptions are options that are available for hashing. +type HashOptions struct { + // Hasher is the hash function to use. If this isn't set, it will + // default to FNV. + Hasher hash.Hash64 + + // TagName is the struct tag to look at when hashing the structure. + // By default this is "hash". + TagName string + + // ZeroNil is flag determining if nil pointer should be treated equal + // to a zero value of pointed type. By default this is false. + ZeroNil bool +} + +// Hash returns the hash value of an arbitrary value. +// +// If opts is nil, then default options will be used. See HashOptions +// for the default values. The same *HashOptions value cannot be used +// concurrently. None of the values within a *HashOptions struct are +// safe to read/write while hashing is being done. +// +// Notes on the value: +// +// * Unexported fields on structs are ignored and do not affect the +// hash value. +// +// * Adding an exported field to a struct with the zero value will change +// the hash value. +// +// For structs, the hashing can be controlled using tags. For example: +// +// struct { +// Name string +// UUID string `hash:"ignore"` +// } +// +// The available tag values are: +// +// * "ignore" or "-" - The field will be ignored and not affect the hash code. +// +// * "set" - The field will be treated as a set, where ordering doesn't +// affect the hash code. This only works for slices. +// +// * "string" - The field will be hashed as a string, only works when the +// field implements fmt.Stringer +// +func Hash(v interface{}, opts *HashOptions) (uint64, error) { + // Create default options + if opts == nil { + opts = &HashOptions{} + } + if opts.Hasher == nil { + opts.Hasher = fnv.New64() + } + if opts.TagName == "" { + opts.TagName = "hash" + } + + // Reset the hash + opts.Hasher.Reset() + + // Create our walker and walk the structure + w := &walker{ + h: opts.Hasher, + tag: opts.TagName, + zeronil: opts.ZeroNil, + } + return w.visit(reflect.ValueOf(v), nil) +} + +type walker struct { + h hash.Hash64 + tag string + zeronil bool +} + +type visitOpts struct { + // Flags are a bitmask of flags to affect behavior of this visit + Flags visitFlag + + // Information about the struct containing this field + Struct interface{} + StructField string +} + +func (w *walker) visit(v reflect.Value, opts *visitOpts) (uint64, error) { + t := reflect.TypeOf(0) + + // Loop since these can be wrapped in multiple layers of pointers + // and interfaces. + for { + // If we have an interface, dereference it. We have to do this up + // here because it might be a nil in there and the check below must + // catch that. + if v.Kind() == reflect.Interface { + v = v.Elem() + continue + } + + if v.Kind() == reflect.Ptr { + if w.zeronil { + t = v.Type().Elem() + } + v = reflect.Indirect(v) + continue + } + + break + } + + // If it is nil, treat it like a zero. + if !v.IsValid() { + v = reflect.Zero(t) + } + + // Binary writing can use raw ints, we have to convert to + // a sized-int, we'll choose the largest... + switch v.Kind() { + case reflect.Int: + v = reflect.ValueOf(int64(v.Int())) + case reflect.Uint: + v = reflect.ValueOf(uint64(v.Uint())) + case reflect.Bool: + var tmp int8 + if v.Bool() { + tmp = 1 + } + v = reflect.ValueOf(tmp) + } + + k := v.Kind() + + // We can shortcut numeric values by directly binary writing them + if k >= reflect.Int && k <= reflect.Complex64 { + // A direct hash calculation + w.h.Reset() + err := binary.Write(w.h, binary.LittleEndian, v.Interface()) + return w.h.Sum64(), err + } + + switch k { + case reflect.Array: + var h uint64 + l := v.Len() + for i := 0; i < l; i++ { + current, err := w.visit(v.Index(i), nil) + if err != nil { + return 0, err + } + + h = hashUpdateOrdered(w.h, h, current) + } + + return h, nil + + case reflect.Map: + var includeMap IncludableMap + if opts != nil && opts.Struct != nil { + if v, ok := opts.Struct.(IncludableMap); ok { + includeMap = v + } + } + + // Build the hash for the map. We do this by XOR-ing all the key + // and value hashes. This makes it deterministic despite ordering. + var h uint64 + for _, k := range v.MapKeys() { + v := v.MapIndex(k) + if includeMap != nil { + incl, err := includeMap.HashIncludeMap( + opts.StructField, k.Interface(), v.Interface()) + if err != nil { + return 0, err + } + if !incl { + continue + } + } + + kh, err := w.visit(k, nil) + if err != nil { + return 0, err + } + vh, err := w.visit(v, nil) + if err != nil { + return 0, err + } + + fieldHash := hashUpdateOrdered(w.h, kh, vh) + h = hashUpdateUnordered(h, fieldHash) + } + + return h, nil + + case reflect.Struct: + parent := v.Interface() + var include Includable + if impl, ok := parent.(Includable); ok { + include = impl + } + + t := v.Type() + h, err := w.visit(reflect.ValueOf(t.Name()), nil) + if err != nil { + return 0, err + } + + l := v.NumField() + for i := 0; i < l; i++ { + if innerV := v.Field(i); v.CanSet() || t.Field(i).Name != "_" { + var f visitFlag + fieldType := t.Field(i) + if fieldType.PkgPath != "" { + // Unexported + continue + } + + tag := fieldType.Tag.Get(w.tag) + if tag == "ignore" || tag == "-" { + // Ignore this field + continue + } + + // if string is set, use the string value + if tag == "string" { + if impl, ok := innerV.Interface().(fmt.Stringer); ok { + innerV = reflect.ValueOf(impl.String()) + } else { + return 0, &ErrNotStringer{ + Field: v.Type().Field(i).Name, + } + } + } + + // Check if we implement includable and check it + if include != nil { + incl, err := include.HashInclude(fieldType.Name, innerV) + if err != nil { + return 0, err + } + if !incl { + continue + } + } + + switch tag { + case "set": + f |= visitFlagSet + } + + kh, err := w.visit(reflect.ValueOf(fieldType.Name), nil) + if err != nil { + return 0, err + } + + vh, err := w.visit(innerV, &visitOpts{ + Flags: f, + Struct: parent, + StructField: fieldType.Name, + }) + if err != nil { + return 0, err + } + + fieldHash := hashUpdateOrdered(w.h, kh, vh) + h = hashUpdateUnordered(h, fieldHash) + } + } + + return h, nil + + case reflect.Slice: + // We have two behaviors here. If it isn't a set, then we just + // visit all the elements. If it is a set, then we do a deterministic + // hash code. + var h uint64 + var set bool + if opts != nil { + set = (opts.Flags & visitFlagSet) != 0 + } + l := v.Len() + for i := 0; i < l; i++ { + current, err := w.visit(v.Index(i), nil) + if err != nil { + return 0, err + } + + if set { + h = hashUpdateUnordered(h, current) + } else { + h = hashUpdateOrdered(w.h, h, current) + } + } + + return h, nil + + case reflect.String: + // Directly hash + w.h.Reset() + _, err := w.h.Write([]byte(v.String())) + return w.h.Sum64(), err + + default: + return 0, fmt.Errorf("unknown kind to hash: %s", k) + } + +} + +func hashUpdateOrdered(h hash.Hash64, a, b uint64) uint64 { + // For ordered updates, use a real hash function + h.Reset() + + // We just panic if the binary writes fail because we are writing + // an int64 which should never be fail-able. + e1 := binary.Write(h, binary.LittleEndian, a) + e2 := binary.Write(h, binary.LittleEndian, b) + if e1 != nil { + panic(e1) + } + if e2 != nil { + panic(e2) + } + + return h.Sum64() +} + +func hashUpdateUnordered(a, b uint64) uint64 { + return a ^ b +} + +// visitFlag is used as a bitmask for affecting visit behavior +type visitFlag uint + +const ( + visitFlagInvalid visitFlag = iota + visitFlagSet = iota << 1 +) diff --git a/vendor/github.com/mitchellh/hashstructure/include.go b/vendor/github.com/mitchellh/hashstructure/include.go new file mode 100644 index 0000000000000..b6289c0bee714 --- /dev/null +++ b/vendor/github.com/mitchellh/hashstructure/include.go @@ -0,0 +1,15 @@ +package hashstructure + +// Includable is an interface that can optionally be implemented by +// a struct. It will be called for each field in the struct to check whether +// it should be included in the hash. +type Includable interface { + HashInclude(field string, v interface{}) (bool, error) +} + +// IncludableMap is an interface that can optionally be implemented by +// a struct. It will be called when a map-type field is found to ask the +// struct if the map item should be included in the hash. +type IncludableMap interface { + HashIncludeMap(field string, k, v interface{}) (bool, error) +} diff --git a/vendor/github.com/moby/buildkit/README.md b/vendor/github.com/moby/buildkit/README.md index 4180753fc68ae..10b2b0e648bbc 100644 --- a/vendor/github.com/moby/buildkit/README.md +++ b/vendor/github.com/moby/buildkit/README.md @@ -1,5 +1,3 @@ -### Important: This repository is in an early development phase - [![asciicinema example](https://asciinema.org/a/gPEIEo1NzmDTUu2bEPsUboqmU.png)](https://asciinema.org/a/gPEIEo1NzmDTUu2bEPsUboqmU) @@ -29,6 +27,16 @@ Read the proposal from https://github.com/moby/moby/issues/32925 Introductory blog post https://blog.mobyproject.org/introducing-buildkit-17e056cc5317 +### Used by + +[Moby](https://github.com/moby/moby/pull/37151) + +[img](https://github.com/genuinetools/img) + +[OpenFaaS Cloud](https://github.com/openfaas/openfaas-cloud) + +[container build interface](https://github.com/containerbuilding/cbi) + ### Quick start Dependencies: @@ -130,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 @@ -248,7 +256,7 @@ make test TESTPKGS=./client make test TESTPKGS=./client TESTFLAGS="--run /TestCallDiskUsage -v" # run all integration tests with a specific worker -# supported workers are oci and containerd +# supported workers: oci, oci-rootless, containerd, containerd-1.0 make test TESTPKGS=./client TESTFLAGS="--run //worker=containerd -v" ``` diff --git a/vendor/github.com/moby/buildkit/api/services/control/control.pb.go b/vendor/github.com/moby/buildkit/api/services/control/control.pb.go new file mode 100644 index 0000000000000..9df84f12544d3 --- /dev/null +++ b/vendor/github.com/moby/buildkit/api/services/control/control.pb.go @@ -0,0 +1,4589 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: control.proto + +/* + Package moby_buildkit_v1 is a generated protocol buffer package. + + It is generated from these files: + control.proto + + It has these top-level messages: + PruneRequest + DiskUsageRequest + DiskUsageResponse + UsageRecord + SolveRequest + CacheOptions + SolveResponse + StatusRequest + StatusResponse + Vertex + VertexStatus + VertexLog + BytesMessage + ListWorkersRequest + ListWorkersResponse +*/ +package moby_buildkit_v1 + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" +import _ "github.com/gogo/protobuf/gogoproto" +import _ "github.com/golang/protobuf/ptypes/timestamp" +import pb "github.com/moby/buildkit/solver/pb" +import moby_buildkit_v1_types "github.com/moby/buildkit/api/types" + +import time "time" +import github_com_opencontainers_go_digest "github.com/opencontainers/go-digest" + +import context "golang.org/x/net/context" +import grpc "google.golang.org/grpc" + +import types "github.com/gogo/protobuf/types" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf +var _ = time.Kitchen + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +type PruneRequest struct { +} + +func (m *PruneRequest) Reset() { *m = PruneRequest{} } +func (m *PruneRequest) String() string { return proto.CompactTextString(m) } +func (*PruneRequest) ProtoMessage() {} +func (*PruneRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{0} } + +type DiskUsageRequest struct { + Filter string `protobuf:"bytes,1,opt,name=filter,proto3" json:"filter,omitempty"` +} + +func (m *DiskUsageRequest) Reset() { *m = DiskUsageRequest{} } +func (m *DiskUsageRequest) String() string { return proto.CompactTextString(m) } +func (*DiskUsageRequest) ProtoMessage() {} +func (*DiskUsageRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{1} } + +func (m *DiskUsageRequest) GetFilter() string { + if m != nil { + return m.Filter + } + return "" +} + +type DiskUsageResponse struct { + Record []*UsageRecord `protobuf:"bytes,1,rep,name=record" json:"record,omitempty"` +} + +func (m *DiskUsageResponse) Reset() { *m = DiskUsageResponse{} } +func (m *DiskUsageResponse) String() string { return proto.CompactTextString(m) } +func (*DiskUsageResponse) ProtoMessage() {} +func (*DiskUsageResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{2} } + +func (m *DiskUsageResponse) GetRecord() []*UsageRecord { + if m != nil { + return m.Record + } + return nil +} + +type UsageRecord struct { + ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` + Mutable bool `protobuf:"varint,2,opt,name=Mutable,proto3" json:"Mutable,omitempty"` + InUse bool `protobuf:"varint,3,opt,name=InUse,proto3" json:"InUse,omitempty"` + Size_ int64 `protobuf:"varint,4,opt,name=Size,proto3" json:"Size,omitempty"` + Parent string `protobuf:"bytes,5,opt,name=Parent,proto3" json:"Parent,omitempty"` + CreatedAt time.Time `protobuf:"bytes,6,opt,name=CreatedAt,stdtime" json:"CreatedAt"` + LastUsedAt *time.Time `protobuf:"bytes,7,opt,name=LastUsedAt,stdtime" json:"LastUsedAt,omitempty"` + UsageCount int64 `protobuf:"varint,8,opt,name=UsageCount,proto3" json:"UsageCount,omitempty"` + Description string `protobuf:"bytes,9,opt,name=Description,proto3" json:"Description,omitempty"` +} + +func (m *UsageRecord) Reset() { *m = UsageRecord{} } +func (m *UsageRecord) String() string { return proto.CompactTextString(m) } +func (*UsageRecord) ProtoMessage() {} +func (*UsageRecord) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{3} } + +func (m *UsageRecord) GetID() string { + if m != nil { + return m.ID + } + return "" +} + +func (m *UsageRecord) GetMutable() bool { + if m != nil { + return m.Mutable + } + return false +} + +func (m *UsageRecord) GetInUse() bool { + if m != nil { + return m.InUse + } + return false +} + +func (m *UsageRecord) GetSize_() int64 { + if m != nil { + return m.Size_ + } + return 0 +} + +func (m *UsageRecord) GetParent() string { + if m != nil { + return m.Parent + } + return "" +} + +func (m *UsageRecord) GetCreatedAt() time.Time { + if m != nil { + return m.CreatedAt + } + return time.Time{} +} + +func (m *UsageRecord) GetLastUsedAt() *time.Time { + if m != nil { + return m.LastUsedAt + } + return nil +} + +func (m *UsageRecord) GetUsageCount() int64 { + if m != nil { + return m.UsageCount + } + return 0 +} + +func (m *UsageRecord) GetDescription() string { + if m != nil { + return m.Description + } + return "" +} + +type SolveRequest struct { + Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"` + Definition *pb.Definition `protobuf:"bytes,2,opt,name=Definition" json:"Definition,omitempty"` + Exporter string `protobuf:"bytes,3,opt,name=Exporter,proto3" json:"Exporter,omitempty"` + ExporterAttrs map[string]string `protobuf:"bytes,4,rep,name=ExporterAttrs" json:"ExporterAttrs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Session string `protobuf:"bytes,5,opt,name=Session,proto3" json:"Session,omitempty"` + Frontend string `protobuf:"bytes,6,opt,name=Frontend,proto3" json:"Frontend,omitempty"` + FrontendAttrs map[string]string `protobuf:"bytes,7,rep,name=FrontendAttrs" json:"FrontendAttrs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Cache CacheOptions `protobuf:"bytes,8,opt,name=Cache" json:"Cache"` +} + +func (m *SolveRequest) Reset() { *m = SolveRequest{} } +func (m *SolveRequest) String() string { return proto.CompactTextString(m) } +func (*SolveRequest) ProtoMessage() {} +func (*SolveRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{4} } + +func (m *SolveRequest) GetRef() string { + if m != nil { + return m.Ref + } + return "" +} + +func (m *SolveRequest) GetDefinition() *pb.Definition { + if m != nil { + return m.Definition + } + return nil +} + +func (m *SolveRequest) GetExporter() string { + if m != nil { + return m.Exporter + } + return "" +} + +func (m *SolveRequest) GetExporterAttrs() map[string]string { + if m != nil { + return m.ExporterAttrs + } + return nil +} + +func (m *SolveRequest) GetSession() string { + if m != nil { + return m.Session + } + return "" +} + +func (m *SolveRequest) GetFrontend() string { + if m != nil { + return m.Frontend + } + return "" +} + +func (m *SolveRequest) GetFrontendAttrs() map[string]string { + if m != nil { + return m.FrontendAttrs + } + return nil +} + +func (m *SolveRequest) GetCache() CacheOptions { + if m != nil { + return m.Cache + } + return CacheOptions{} +} + +type CacheOptions struct { + ExportRef string `protobuf:"bytes,1,opt,name=ExportRef,proto3" json:"ExportRef,omitempty"` + ImportRefs []string `protobuf:"bytes,2,rep,name=ImportRefs" json:"ImportRefs,omitempty"` + ExportAttrs map[string]string `protobuf:"bytes,3,rep,name=ExportAttrs" json:"ExportAttrs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (m *CacheOptions) Reset() { *m = CacheOptions{} } +func (m *CacheOptions) String() string { return proto.CompactTextString(m) } +func (*CacheOptions) ProtoMessage() {} +func (*CacheOptions) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{5} } + +func (m *CacheOptions) GetExportRef() string { + if m != nil { + return m.ExportRef + } + return "" +} + +func (m *CacheOptions) GetImportRefs() []string { + if m != nil { + return m.ImportRefs + } + return nil +} + +func (m *CacheOptions) GetExportAttrs() map[string]string { + if m != nil { + return m.ExportAttrs + } + return nil +} + +type SolveResponse struct { + ExporterResponse map[string]string `protobuf:"bytes,1,rep,name=ExporterResponse" json:"ExporterResponse,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (m *SolveResponse) Reset() { *m = SolveResponse{} } +func (m *SolveResponse) String() string { return proto.CompactTextString(m) } +func (*SolveResponse) ProtoMessage() {} +func (*SolveResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{6} } + +func (m *SolveResponse) GetExporterResponse() map[string]string { + if m != nil { + return m.ExporterResponse + } + return nil +} + +type StatusRequest struct { + Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"` +} + +func (m *StatusRequest) Reset() { *m = StatusRequest{} } +func (m *StatusRequest) String() string { return proto.CompactTextString(m) } +func (*StatusRequest) ProtoMessage() {} +func (*StatusRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{7} } + +func (m *StatusRequest) GetRef() string { + if m != nil { + return m.Ref + } + return "" +} + +type StatusResponse struct { + Vertexes []*Vertex `protobuf:"bytes,1,rep,name=vertexes" json:"vertexes,omitempty"` + Statuses []*VertexStatus `protobuf:"bytes,2,rep,name=statuses" json:"statuses,omitempty"` + Logs []*VertexLog `protobuf:"bytes,3,rep,name=logs" json:"logs,omitempty"` +} + +func (m *StatusResponse) Reset() { *m = StatusResponse{} } +func (m *StatusResponse) String() string { return proto.CompactTextString(m) } +func (*StatusResponse) ProtoMessage() {} +func (*StatusResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{8} } + +func (m *StatusResponse) GetVertexes() []*Vertex { + if m != nil { + return m.Vertexes + } + return nil +} + +func (m *StatusResponse) GetStatuses() []*VertexStatus { + if m != nil { + return m.Statuses + } + return nil +} + +func (m *StatusResponse) GetLogs() []*VertexLog { + if m != nil { + return m.Logs + } + return nil +} + +type Vertex struct { + Digest github_com_opencontainers_go_digest.Digest `protobuf:"bytes,1,opt,name=digest,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"digest"` + Inputs []github_com_opencontainers_go_digest.Digest `protobuf:"bytes,2,rep,name=inputs,customtype=github.com/opencontainers/go-digest.Digest" json:"inputs"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + Cached bool `protobuf:"varint,4,opt,name=cached,proto3" json:"cached,omitempty"` + Started *time.Time `protobuf:"bytes,5,opt,name=started,stdtime" json:"started,omitempty"` + Completed *time.Time `protobuf:"bytes,6,opt,name=completed,stdtime" json:"completed,omitempty"` + Error string `protobuf:"bytes,7,opt,name=error,proto3" json:"error,omitempty"` +} + +func (m *Vertex) Reset() { *m = Vertex{} } +func (m *Vertex) String() string { return proto.CompactTextString(m) } +func (*Vertex) ProtoMessage() {} +func (*Vertex) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{9} } + +func (m *Vertex) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Vertex) GetCached() bool { + if m != nil { + return m.Cached + } + return false +} + +func (m *Vertex) GetStarted() *time.Time { + if m != nil { + return m.Started + } + return nil +} + +func (m *Vertex) GetCompleted() *time.Time { + if m != nil { + return m.Completed + } + return nil +} + +func (m *Vertex) GetError() string { + if m != nil { + return m.Error + } + return "" +} + +type VertexStatus struct { + ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` + Vertex github_com_opencontainers_go_digest.Digest `protobuf:"bytes,2,opt,name=vertex,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"vertex"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + Current int64 `protobuf:"varint,4,opt,name=current,proto3" json:"current,omitempty"` + Total int64 `protobuf:"varint,5,opt,name=total,proto3" json:"total,omitempty"` + // TODO: add started, completed + Timestamp time.Time `protobuf:"bytes,6,opt,name=timestamp,stdtime" json:"timestamp"` + Started *time.Time `protobuf:"bytes,7,opt,name=started,stdtime" json:"started,omitempty"` + Completed *time.Time `protobuf:"bytes,8,opt,name=completed,stdtime" json:"completed,omitempty"` +} + +func (m *VertexStatus) Reset() { *m = VertexStatus{} } +func (m *VertexStatus) String() string { return proto.CompactTextString(m) } +func (*VertexStatus) ProtoMessage() {} +func (*VertexStatus) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{10} } + +func (m *VertexStatus) GetID() string { + if m != nil { + return m.ID + } + return "" +} + +func (m *VertexStatus) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *VertexStatus) GetCurrent() int64 { + if m != nil { + return m.Current + } + return 0 +} + +func (m *VertexStatus) GetTotal() int64 { + if m != nil { + return m.Total + } + return 0 +} + +func (m *VertexStatus) GetTimestamp() time.Time { + if m != nil { + return m.Timestamp + } + return time.Time{} +} + +func (m *VertexStatus) GetStarted() *time.Time { + if m != nil { + return m.Started + } + return nil +} + +func (m *VertexStatus) GetCompleted() *time.Time { + if m != nil { + return m.Completed + } + return nil +} + +type VertexLog struct { + Vertex github_com_opencontainers_go_digest.Digest `protobuf:"bytes,1,opt,name=vertex,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"vertex"` + Timestamp time.Time `protobuf:"bytes,2,opt,name=timestamp,stdtime" json:"timestamp"` + Stream int64 `protobuf:"varint,3,opt,name=stream,proto3" json:"stream,omitempty"` + Msg []byte `protobuf:"bytes,4,opt,name=msg,proto3" json:"msg,omitempty"` +} + +func (m *VertexLog) Reset() { *m = VertexLog{} } +func (m *VertexLog) String() string { return proto.CompactTextString(m) } +func (*VertexLog) ProtoMessage() {} +func (*VertexLog) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{11} } + +func (m *VertexLog) GetTimestamp() time.Time { + if m != nil { + return m.Timestamp + } + return time.Time{} +} + +func (m *VertexLog) GetStream() int64 { + if m != nil { + return m.Stream + } + return 0 +} + +func (m *VertexLog) GetMsg() []byte { + if m != nil { + return m.Msg + } + return nil +} + +type BytesMessage struct { + Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` +} + +func (m *BytesMessage) Reset() { *m = BytesMessage{} } +func (m *BytesMessage) String() string { return proto.CompactTextString(m) } +func (*BytesMessage) ProtoMessage() {} +func (*BytesMessage) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{12} } + +func (m *BytesMessage) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +type ListWorkersRequest struct { + Filter []string `protobuf:"bytes,1,rep,name=filter" json:"filter,omitempty"` +} + +func (m *ListWorkersRequest) Reset() { *m = ListWorkersRequest{} } +func (m *ListWorkersRequest) String() string { return proto.CompactTextString(m) } +func (*ListWorkersRequest) ProtoMessage() {} +func (*ListWorkersRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{13} } + +func (m *ListWorkersRequest) GetFilter() []string { + if m != nil { + return m.Filter + } + return nil +} + +type ListWorkersResponse struct { + Record []*moby_buildkit_v1_types.WorkerRecord `protobuf:"bytes,1,rep,name=record" json:"record,omitempty"` +} + +func (m *ListWorkersResponse) Reset() { *m = ListWorkersResponse{} } +func (m *ListWorkersResponse) String() string { return proto.CompactTextString(m) } +func (*ListWorkersResponse) ProtoMessage() {} +func (*ListWorkersResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{14} } + +func (m *ListWorkersResponse) GetRecord() []*moby_buildkit_v1_types.WorkerRecord { + if m != nil { + return m.Record + } + return nil +} + +func init() { + proto.RegisterType((*PruneRequest)(nil), "moby.buildkit.v1.PruneRequest") + proto.RegisterType((*DiskUsageRequest)(nil), "moby.buildkit.v1.DiskUsageRequest") + proto.RegisterType((*DiskUsageResponse)(nil), "moby.buildkit.v1.DiskUsageResponse") + proto.RegisterType((*UsageRecord)(nil), "moby.buildkit.v1.UsageRecord") + proto.RegisterType((*SolveRequest)(nil), "moby.buildkit.v1.SolveRequest") + proto.RegisterType((*CacheOptions)(nil), "moby.buildkit.v1.CacheOptions") + proto.RegisterType((*SolveResponse)(nil), "moby.buildkit.v1.SolveResponse") + proto.RegisterType((*StatusRequest)(nil), "moby.buildkit.v1.StatusRequest") + proto.RegisterType((*StatusResponse)(nil), "moby.buildkit.v1.StatusResponse") + proto.RegisterType((*Vertex)(nil), "moby.buildkit.v1.Vertex") + proto.RegisterType((*VertexStatus)(nil), "moby.buildkit.v1.VertexStatus") + proto.RegisterType((*VertexLog)(nil), "moby.buildkit.v1.VertexLog") + proto.RegisterType((*BytesMessage)(nil), "moby.buildkit.v1.BytesMessage") + proto.RegisterType((*ListWorkersRequest)(nil), "moby.buildkit.v1.ListWorkersRequest") + proto.RegisterType((*ListWorkersResponse)(nil), "moby.buildkit.v1.ListWorkersResponse") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// Client API for Control service + +type ControlClient interface { + DiskUsage(ctx context.Context, in *DiskUsageRequest, opts ...grpc.CallOption) (*DiskUsageResponse, error) + Prune(ctx context.Context, in *PruneRequest, opts ...grpc.CallOption) (Control_PruneClient, error) + Solve(ctx context.Context, in *SolveRequest, opts ...grpc.CallOption) (*SolveResponse, error) + Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (Control_StatusClient, error) + Session(ctx context.Context, opts ...grpc.CallOption) (Control_SessionClient, error) + ListWorkers(ctx context.Context, in *ListWorkersRequest, opts ...grpc.CallOption) (*ListWorkersResponse, error) +} + +type controlClient struct { + cc *grpc.ClientConn +} + +func NewControlClient(cc *grpc.ClientConn) ControlClient { + return &controlClient{cc} +} + +func (c *controlClient) DiskUsage(ctx context.Context, in *DiskUsageRequest, opts ...grpc.CallOption) (*DiskUsageResponse, error) { + out := new(DiskUsageResponse) + err := grpc.Invoke(ctx, "/moby.buildkit.v1.Control/DiskUsage", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) Prune(ctx context.Context, in *PruneRequest, opts ...grpc.CallOption) (Control_PruneClient, error) { + stream, err := grpc.NewClientStream(ctx, &_Control_serviceDesc.Streams[0], c.cc, "/moby.buildkit.v1.Control/Prune", opts...) + if err != nil { + return nil, err + } + x := &controlPruneClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type Control_PruneClient interface { + Recv() (*UsageRecord, error) + grpc.ClientStream +} + +type controlPruneClient struct { + grpc.ClientStream +} + +func (x *controlPruneClient) Recv() (*UsageRecord, error) { + m := new(UsageRecord) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *controlClient) Solve(ctx context.Context, in *SolveRequest, opts ...grpc.CallOption) (*SolveResponse, error) { + out := new(SolveResponse) + err := grpc.Invoke(ctx, "/moby.buildkit.v1.Control/Solve", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (Control_StatusClient, error) { + stream, err := grpc.NewClientStream(ctx, &_Control_serviceDesc.Streams[1], c.cc, "/moby.buildkit.v1.Control/Status", opts...) + if err != nil { + return nil, err + } + x := &controlStatusClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type Control_StatusClient interface { + Recv() (*StatusResponse, error) + grpc.ClientStream +} + +type controlStatusClient struct { + grpc.ClientStream +} + +func (x *controlStatusClient) Recv() (*StatusResponse, error) { + m := new(StatusResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *controlClient) Session(ctx context.Context, opts ...grpc.CallOption) (Control_SessionClient, error) { + stream, err := grpc.NewClientStream(ctx, &_Control_serviceDesc.Streams[2], c.cc, "/moby.buildkit.v1.Control/Session", opts...) + if err != nil { + return nil, err + } + x := &controlSessionClient{stream} + return x, nil +} + +type Control_SessionClient interface { + Send(*BytesMessage) error + Recv() (*BytesMessage, error) + grpc.ClientStream +} + +type controlSessionClient struct { + grpc.ClientStream +} + +func (x *controlSessionClient) Send(m *BytesMessage) error { + return x.ClientStream.SendMsg(m) +} + +func (x *controlSessionClient) Recv() (*BytesMessage, error) { + m := new(BytesMessage) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *controlClient) ListWorkers(ctx context.Context, in *ListWorkersRequest, opts ...grpc.CallOption) (*ListWorkersResponse, error) { + out := new(ListWorkersResponse) + err := grpc.Invoke(ctx, "/moby.buildkit.v1.Control/ListWorkers", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for Control service + +type ControlServer interface { + DiskUsage(context.Context, *DiskUsageRequest) (*DiskUsageResponse, error) + Prune(*PruneRequest, Control_PruneServer) error + Solve(context.Context, *SolveRequest) (*SolveResponse, error) + Status(*StatusRequest, Control_StatusServer) error + Session(Control_SessionServer) error + ListWorkers(context.Context, *ListWorkersRequest) (*ListWorkersResponse, error) +} + +func RegisterControlServer(s *grpc.Server, srv ControlServer) { + s.RegisterService(&_Control_serviceDesc, srv) +} + +func _Control_DiskUsage_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DiskUsageRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).DiskUsage(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/moby.buildkit.v1.Control/DiskUsage", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).DiskUsage(ctx, req.(*DiskUsageRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_Prune_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(PruneRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(ControlServer).Prune(m, &controlPruneServer{stream}) +} + +type Control_PruneServer interface { + Send(*UsageRecord) error + grpc.ServerStream +} + +type controlPruneServer struct { + grpc.ServerStream +} + +func (x *controlPruneServer) Send(m *UsageRecord) error { + return x.ServerStream.SendMsg(m) +} + +func _Control_Solve_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SolveRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).Solve(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/moby.buildkit.v1.Control/Solve", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).Solve(ctx, req.(*SolveRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_Status_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(StatusRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(ControlServer).Status(m, &controlStatusServer{stream}) +} + +type Control_StatusServer interface { + Send(*StatusResponse) error + grpc.ServerStream +} + +type controlStatusServer struct { + grpc.ServerStream +} + +func (x *controlStatusServer) Send(m *StatusResponse) error { + return x.ServerStream.SendMsg(m) +} + +func _Control_Session_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(ControlServer).Session(&controlSessionServer{stream}) +} + +type Control_SessionServer interface { + Send(*BytesMessage) error + Recv() (*BytesMessage, error) + grpc.ServerStream +} + +type controlSessionServer struct { + grpc.ServerStream +} + +func (x *controlSessionServer) Send(m *BytesMessage) error { + return x.ServerStream.SendMsg(m) +} + +func (x *controlSessionServer) Recv() (*BytesMessage, error) { + m := new(BytesMessage) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func _Control_ListWorkers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListWorkersRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).ListWorkers(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/moby.buildkit.v1.Control/ListWorkers", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).ListWorkers(ctx, req.(*ListWorkersRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Control_serviceDesc = grpc.ServiceDesc{ + ServiceName: "moby.buildkit.v1.Control", + HandlerType: (*ControlServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "DiskUsage", + Handler: _Control_DiskUsage_Handler, + }, + { + MethodName: "Solve", + Handler: _Control_Solve_Handler, + }, + { + MethodName: "ListWorkers", + Handler: _Control_ListWorkers_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "Prune", + Handler: _Control_Prune_Handler, + ServerStreams: true, + }, + { + StreamName: "Status", + Handler: _Control_Status_Handler, + ServerStreams: true, + }, + { + StreamName: "Session", + Handler: _Control_Session_Handler, + ServerStreams: true, + ClientStreams: true, + }, + }, + Metadata: "control.proto", +} + +func (m *PruneRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PruneRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + return i, nil +} + +func (m *DiskUsageRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DiskUsageRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Filter) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintControl(dAtA, i, uint64(len(m.Filter))) + i += copy(dAtA[i:], m.Filter) + } + return i, nil +} + +func (m *DiskUsageResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DiskUsageResponse) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Record) > 0 { + for _, msg := range m.Record { + dAtA[i] = 0xa + i++ + i = encodeVarintControl(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + return i, nil +} + +func (m *UsageRecord) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *UsageRecord) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintControl(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + if m.Mutable { + dAtA[i] = 0x10 + i++ + if m.Mutable { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if m.InUse { + dAtA[i] = 0x18 + i++ + if m.InUse { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if m.Size_ != 0 { + dAtA[i] = 0x20 + i++ + i = encodeVarintControl(dAtA, i, uint64(m.Size_)) + } + if len(m.Parent) > 0 { + dAtA[i] = 0x2a + i++ + i = encodeVarintControl(dAtA, i, uint64(len(m.Parent))) + i += copy(dAtA[i:], m.Parent) + } + dAtA[i] = 0x32 + i++ + i = encodeVarintControl(dAtA, i, uint64(types.SizeOfStdTime(m.CreatedAt))) + n1, err := types.StdTimeMarshalTo(m.CreatedAt, dAtA[i:]) + if err != nil { + return 0, err + } + i += n1 + if m.LastUsedAt != nil { + dAtA[i] = 0x3a + i++ + i = encodeVarintControl(dAtA, i, uint64(types.SizeOfStdTime(*m.LastUsedAt))) + n2, err := types.StdTimeMarshalTo(*m.LastUsedAt, dAtA[i:]) + if err != nil { + return 0, err + } + i += n2 + } + if m.UsageCount != 0 { + dAtA[i] = 0x40 + i++ + i = encodeVarintControl(dAtA, i, uint64(m.UsageCount)) + } + if len(m.Description) > 0 { + dAtA[i] = 0x4a + i++ + i = encodeVarintControl(dAtA, i, uint64(len(m.Description))) + i += copy(dAtA[i:], m.Description) + } + return i, nil +} + +func (m *SolveRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SolveRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Ref) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintControl(dAtA, i, uint64(len(m.Ref))) + i += copy(dAtA[i:], m.Ref) + } + if m.Definition != nil { + dAtA[i] = 0x12 + i++ + i = encodeVarintControl(dAtA, i, uint64(m.Definition.Size())) + n3, err := m.Definition.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n3 + } + if len(m.Exporter) > 0 { + dAtA[i] = 0x1a + i++ + i = encodeVarintControl(dAtA, i, uint64(len(m.Exporter))) + i += copy(dAtA[i:], m.Exporter) + } + if len(m.ExporterAttrs) > 0 { + for k, _ := range m.ExporterAttrs { + dAtA[i] = 0x22 + i++ + v := m.ExporterAttrs[k] + mapSize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) + i = encodeVarintControl(dAtA, i, uint64(mapSize)) + dAtA[i] = 0xa + i++ + i = encodeVarintControl(dAtA, i, uint64(len(k))) + i += copy(dAtA[i:], k) + dAtA[i] = 0x12 + i++ + i = encodeVarintControl(dAtA, i, uint64(len(v))) + i += copy(dAtA[i:], v) + } + } + if len(m.Session) > 0 { + dAtA[i] = 0x2a + i++ + i = encodeVarintControl(dAtA, i, uint64(len(m.Session))) + i += copy(dAtA[i:], m.Session) + } + if len(m.Frontend) > 0 { + dAtA[i] = 0x32 + i++ + i = encodeVarintControl(dAtA, i, uint64(len(m.Frontend))) + i += copy(dAtA[i:], m.Frontend) + } + if len(m.FrontendAttrs) > 0 { + for k, _ := range m.FrontendAttrs { + dAtA[i] = 0x3a + i++ + v := m.FrontendAttrs[k] + mapSize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) + i = encodeVarintControl(dAtA, i, uint64(mapSize)) + dAtA[i] = 0xa + i++ + i = encodeVarintControl(dAtA, i, uint64(len(k))) + i += copy(dAtA[i:], k) + dAtA[i] = 0x12 + i++ + i = encodeVarintControl(dAtA, i, uint64(len(v))) + i += copy(dAtA[i:], v) + } + } + dAtA[i] = 0x42 + i++ + i = encodeVarintControl(dAtA, i, uint64(m.Cache.Size())) + n4, err := m.Cache.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n4 + return i, nil +} + +func (m *CacheOptions) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CacheOptions) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ExportRef) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintControl(dAtA, i, uint64(len(m.ExportRef))) + i += copy(dAtA[i:], m.ExportRef) + } + if len(m.ImportRefs) > 0 { + for _, s := range m.ImportRefs { + dAtA[i] = 0x12 + i++ + l = len(s) + for l >= 1<<7 { + dAtA[i] = uint8(uint64(l)&0x7f | 0x80) + l >>= 7 + i++ + } + dAtA[i] = uint8(l) + i++ + i += copy(dAtA[i:], s) + } + } + if len(m.ExportAttrs) > 0 { + for k, _ := range m.ExportAttrs { + dAtA[i] = 0x1a + i++ + v := m.ExportAttrs[k] + mapSize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) + i = encodeVarintControl(dAtA, i, uint64(mapSize)) + dAtA[i] = 0xa + i++ + i = encodeVarintControl(dAtA, i, uint64(len(k))) + i += copy(dAtA[i:], k) + dAtA[i] = 0x12 + i++ + i = encodeVarintControl(dAtA, i, uint64(len(v))) + i += copy(dAtA[i:], v) + } + } + return i, nil +} + +func (m *SolveResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SolveResponse) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ExporterResponse) > 0 { + for k, _ := range m.ExporterResponse { + dAtA[i] = 0xa + i++ + v := m.ExporterResponse[k] + mapSize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) + i = encodeVarintControl(dAtA, i, uint64(mapSize)) + dAtA[i] = 0xa + i++ + i = encodeVarintControl(dAtA, i, uint64(len(k))) + i += copy(dAtA[i:], k) + dAtA[i] = 0x12 + i++ + i = encodeVarintControl(dAtA, i, uint64(len(v))) + i += copy(dAtA[i:], v) + } + } + return i, nil +} + +func (m *StatusRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *StatusRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Ref) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintControl(dAtA, i, uint64(len(m.Ref))) + i += copy(dAtA[i:], m.Ref) + } + return i, nil +} + +func (m *StatusResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *StatusResponse) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Vertexes) > 0 { + for _, msg := range m.Vertexes { + dAtA[i] = 0xa + i++ + i = encodeVarintControl(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if len(m.Statuses) > 0 { + for _, msg := range m.Statuses { + dAtA[i] = 0x12 + i++ + i = encodeVarintControl(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if len(m.Logs) > 0 { + for _, msg := range m.Logs { + dAtA[i] = 0x1a + i++ + i = encodeVarintControl(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + return i, nil +} + +func (m *Vertex) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Vertex) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Digest) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintControl(dAtA, i, uint64(len(m.Digest))) + i += copy(dAtA[i:], m.Digest) + } + if len(m.Inputs) > 0 { + for _, s := range m.Inputs { + dAtA[i] = 0x12 + i++ + l = len(s) + for l >= 1<<7 { + dAtA[i] = uint8(uint64(l)&0x7f | 0x80) + l >>= 7 + i++ + } + dAtA[i] = uint8(l) + i++ + i += copy(dAtA[i:], s) + } + } + if len(m.Name) > 0 { + dAtA[i] = 0x1a + i++ + i = encodeVarintControl(dAtA, i, uint64(len(m.Name))) + i += copy(dAtA[i:], m.Name) + } + if m.Cached { + dAtA[i] = 0x20 + i++ + if m.Cached { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if m.Started != nil { + dAtA[i] = 0x2a + i++ + i = encodeVarintControl(dAtA, i, uint64(types.SizeOfStdTime(*m.Started))) + n5, err := types.StdTimeMarshalTo(*m.Started, dAtA[i:]) + if err != nil { + return 0, err + } + i += n5 + } + if m.Completed != nil { + dAtA[i] = 0x32 + i++ + i = encodeVarintControl(dAtA, i, uint64(types.SizeOfStdTime(*m.Completed))) + n6, err := types.StdTimeMarshalTo(*m.Completed, dAtA[i:]) + if err != nil { + return 0, err + } + i += n6 + } + if len(m.Error) > 0 { + dAtA[i] = 0x3a + i++ + i = encodeVarintControl(dAtA, i, uint64(len(m.Error))) + i += copy(dAtA[i:], m.Error) + } + return i, nil +} + +func (m *VertexStatus) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *VertexStatus) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintControl(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + if len(m.Vertex) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintControl(dAtA, i, uint64(len(m.Vertex))) + i += copy(dAtA[i:], m.Vertex) + } + if len(m.Name) > 0 { + dAtA[i] = 0x1a + i++ + i = encodeVarintControl(dAtA, i, uint64(len(m.Name))) + i += copy(dAtA[i:], m.Name) + } + if m.Current != 0 { + dAtA[i] = 0x20 + i++ + i = encodeVarintControl(dAtA, i, uint64(m.Current)) + } + if m.Total != 0 { + dAtA[i] = 0x28 + i++ + i = encodeVarintControl(dAtA, i, uint64(m.Total)) + } + dAtA[i] = 0x32 + i++ + i = encodeVarintControl(dAtA, i, uint64(types.SizeOfStdTime(m.Timestamp))) + n7, err := types.StdTimeMarshalTo(m.Timestamp, dAtA[i:]) + if err != nil { + return 0, err + } + i += n7 + if m.Started != nil { + dAtA[i] = 0x3a + i++ + i = encodeVarintControl(dAtA, i, uint64(types.SizeOfStdTime(*m.Started))) + n8, err := types.StdTimeMarshalTo(*m.Started, dAtA[i:]) + if err != nil { + return 0, err + } + i += n8 + } + if m.Completed != nil { + dAtA[i] = 0x42 + i++ + i = encodeVarintControl(dAtA, i, uint64(types.SizeOfStdTime(*m.Completed))) + n9, err := types.StdTimeMarshalTo(*m.Completed, dAtA[i:]) + if err != nil { + return 0, err + } + i += n9 + } + return i, nil +} + +func (m *VertexLog) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *VertexLog) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Vertex) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintControl(dAtA, i, uint64(len(m.Vertex))) + i += copy(dAtA[i:], m.Vertex) + } + dAtA[i] = 0x12 + i++ + i = encodeVarintControl(dAtA, i, uint64(types.SizeOfStdTime(m.Timestamp))) + n10, err := types.StdTimeMarshalTo(m.Timestamp, dAtA[i:]) + if err != nil { + return 0, err + } + i += n10 + if m.Stream != 0 { + dAtA[i] = 0x18 + i++ + i = encodeVarintControl(dAtA, i, uint64(m.Stream)) + } + if len(m.Msg) > 0 { + dAtA[i] = 0x22 + i++ + i = encodeVarintControl(dAtA, i, uint64(len(m.Msg))) + i += copy(dAtA[i:], m.Msg) + } + return i, nil +} + +func (m *BytesMessage) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *BytesMessage) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Data) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintControl(dAtA, i, uint64(len(m.Data))) + i += copy(dAtA[i:], m.Data) + } + return i, nil +} + +func (m *ListWorkersRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ListWorkersRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Filter) > 0 { + for _, s := range m.Filter { + dAtA[i] = 0xa + i++ + l = len(s) + for l >= 1<<7 { + dAtA[i] = uint8(uint64(l)&0x7f | 0x80) + l >>= 7 + i++ + } + dAtA[i] = uint8(l) + i++ + i += copy(dAtA[i:], s) + } + } + return i, nil +} + +func (m *ListWorkersResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ListWorkersResponse) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Record) > 0 { + for _, msg := range m.Record { + dAtA[i] = 0xa + i++ + i = encodeVarintControl(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + return i, nil +} + +func encodeVarintControl(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *PruneRequest) Size() (n int) { + var l int + _ = l + return n +} + +func (m *DiskUsageRequest) Size() (n int) { + var l int + _ = l + l = len(m.Filter) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *DiskUsageResponse) Size() (n int) { + var l int + _ = l + if len(m.Record) > 0 { + for _, e := range m.Record { + l = e.Size() + n += 1 + l + sovControl(uint64(l)) + } + } + return n +} + +func (m *UsageRecord) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + if m.Mutable { + n += 2 + } + if m.InUse { + n += 2 + } + if m.Size_ != 0 { + n += 1 + sovControl(uint64(m.Size_)) + } + l = len(m.Parent) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + l = types.SizeOfStdTime(m.CreatedAt) + n += 1 + l + sovControl(uint64(l)) + if m.LastUsedAt != nil { + l = types.SizeOfStdTime(*m.LastUsedAt) + n += 1 + l + sovControl(uint64(l)) + } + if m.UsageCount != 0 { + n += 1 + sovControl(uint64(m.UsageCount)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *SolveRequest) Size() (n int) { + var l int + _ = l + l = len(m.Ref) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + if m.Definition != nil { + l = m.Definition.Size() + n += 1 + l + sovControl(uint64(l)) + } + l = len(m.Exporter) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + if len(m.ExporterAttrs) > 0 { + for k, v := range m.ExporterAttrs { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) + n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize)) + } + } + l = len(m.Session) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + l = len(m.Frontend) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + if len(m.FrontendAttrs) > 0 { + for k, v := range m.FrontendAttrs { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) + n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize)) + } + } + l = m.Cache.Size() + n += 1 + l + sovControl(uint64(l)) + return n +} + +func (m *CacheOptions) Size() (n int) { + var l int + _ = l + l = len(m.ExportRef) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + if len(m.ImportRefs) > 0 { + for _, s := range m.ImportRefs { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + if len(m.ExportAttrs) > 0 { + for k, v := range m.ExportAttrs { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) + n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize)) + } + } + return n +} + +func (m *SolveResponse) Size() (n int) { + var l int + _ = l + if len(m.ExporterResponse) > 0 { + for k, v := range m.ExporterResponse { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) + n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize)) + } + } + return n +} + +func (m *StatusRequest) Size() (n int) { + var l int + _ = l + l = len(m.Ref) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *StatusResponse) Size() (n int) { + var l int + _ = l + if len(m.Vertexes) > 0 { + for _, e := range m.Vertexes { + l = e.Size() + n += 1 + l + sovControl(uint64(l)) + } + } + if len(m.Statuses) > 0 { + for _, e := range m.Statuses { + l = e.Size() + n += 1 + l + sovControl(uint64(l)) + } + } + if len(m.Logs) > 0 { + for _, e := range m.Logs { + l = e.Size() + n += 1 + l + sovControl(uint64(l)) + } + } + return n +} + +func (m *Vertex) Size() (n int) { + var l int + _ = l + l = len(m.Digest) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + if len(m.Inputs) > 0 { + for _, s := range m.Inputs { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + if m.Cached { + n += 2 + } + if m.Started != nil { + l = types.SizeOfStdTime(*m.Started) + n += 1 + l + sovControl(uint64(l)) + } + if m.Completed != nil { + l = types.SizeOfStdTime(*m.Completed) + n += 1 + l + sovControl(uint64(l)) + } + l = len(m.Error) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *VertexStatus) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + l = len(m.Vertex) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + if m.Current != 0 { + n += 1 + sovControl(uint64(m.Current)) + } + if m.Total != 0 { + n += 1 + sovControl(uint64(m.Total)) + } + l = types.SizeOfStdTime(m.Timestamp) + n += 1 + l + sovControl(uint64(l)) + if m.Started != nil { + l = types.SizeOfStdTime(*m.Started) + n += 1 + l + sovControl(uint64(l)) + } + if m.Completed != nil { + l = types.SizeOfStdTime(*m.Completed) + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *VertexLog) Size() (n int) { + var l int + _ = l + l = len(m.Vertex) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + l = types.SizeOfStdTime(m.Timestamp) + n += 1 + l + sovControl(uint64(l)) + if m.Stream != 0 { + n += 1 + sovControl(uint64(m.Stream)) + } + l = len(m.Msg) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *BytesMessage) Size() (n int) { + var l int + _ = l + l = len(m.Data) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *ListWorkersRequest) Size() (n int) { + var l int + _ = l + if len(m.Filter) > 0 { + for _, s := range m.Filter { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + return n +} + +func (m *ListWorkersResponse) Size() (n int) { + var l int + _ = l + if len(m.Record) > 0 { + for _, e := range m.Record { + l = e.Size() + n += 1 + l + sovControl(uint64(l)) + } + } + return n +} + +func sovControl(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozControl(x uint64) (n int) { + return sovControl(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *PruneRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PruneRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PruneRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DiskUsageRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DiskUsageRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DiskUsageRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Filter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Filter = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DiskUsageResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DiskUsageResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DiskUsageResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Record", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Record = append(m.Record, &UsageRecord{}) + if err := m.Record[len(m.Record)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UsageRecord) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UsageRecord: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UsageRecord: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Mutable", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Mutable = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field InUse", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.InUse = bool(v != 0) + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Size_", wireType) + } + m.Size_ = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Size_ |= (int64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Parent", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Parent = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CreatedAt", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := types.StdTimeUnmarshal(&m.CreatedAt, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LastUsedAt", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.LastUsedAt == nil { + m.LastUsedAt = new(time.Time) + } + if err := types.StdTimeUnmarshal(m.LastUsedAt, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UsageCount", wireType) + } + m.UsageCount = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.UsageCount |= (int64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SolveRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SolveRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SolveRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Ref", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Ref = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Definition", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Definition == nil { + m.Definition = &pb.Definition{} + } + if err := m.Definition.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Exporter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Exporter = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExporterAttrs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ExporterAttrs == nil { + m.ExporterAttrs = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthControl + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthControl + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.ExporterAttrs[mapkey] = mapvalue + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Session", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Session = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Frontend", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Frontend = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FrontendAttrs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.FrontendAttrs == nil { + m.FrontendAttrs = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthControl + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthControl + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.FrontendAttrs[mapkey] = mapvalue + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Cache", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Cache.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CacheOptions) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CacheOptions: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CacheOptions: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExportRef", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExportRef = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ImportRefs", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ImportRefs = append(m.ImportRefs, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExportAttrs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ExportAttrs == nil { + m.ExportAttrs = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthControl + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthControl + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.ExportAttrs[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SolveResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SolveResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SolveResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExporterResponse", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ExporterResponse == nil { + m.ExporterResponse = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthControl + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthControl + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.ExporterResponse[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *StatusRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: StatusRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: StatusRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Ref", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Ref = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *StatusResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: StatusResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: StatusResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Vertexes", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Vertexes = append(m.Vertexes, &Vertex{}) + if err := m.Vertexes[len(m.Vertexes)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Statuses", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Statuses = append(m.Statuses, &VertexStatus{}) + if err := m.Statuses[len(m.Statuses)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Logs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Logs = append(m.Logs, &VertexLog{}) + if err := m.Logs[len(m.Logs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Vertex) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Vertex: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Vertex: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Digest", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Digest = github_com_opencontainers_go_digest.Digest(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Inputs", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Inputs = append(m.Inputs, github_com_opencontainers_go_digest.Digest(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Cached", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Cached = bool(v != 0) + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Started", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Started == nil { + m.Started = new(time.Time) + } + if err := types.StdTimeUnmarshal(m.Started, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Completed", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Completed == nil { + m.Completed = new(time.Time) + } + if err := types.StdTimeUnmarshal(m.Completed, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Error = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *VertexStatus) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: VertexStatus: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: VertexStatus: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Vertex", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Vertex = github_com_opencontainers_go_digest.Digest(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Current", wireType) + } + m.Current = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Current |= (int64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Total", wireType) + } + m.Total = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Total |= (int64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := types.StdTimeUnmarshal(&m.Timestamp, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Started", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Started == nil { + m.Started = new(time.Time) + } + if err := types.StdTimeUnmarshal(m.Started, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Completed", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Completed == nil { + m.Completed = new(time.Time) + } + if err := types.StdTimeUnmarshal(m.Completed, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *VertexLog) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: VertexLog: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: VertexLog: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Vertex", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Vertex = github_com_opencontainers_go_digest.Digest(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := types.StdTimeUnmarshal(&m.Timestamp, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Stream", wireType) + } + m.Stream = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Stream |= (int64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Msg", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Msg = append(m.Msg[:0], dAtA[iNdEx:postIndex]...) + if m.Msg == nil { + m.Msg = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BytesMessage) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: BytesMessage: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BytesMessage: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ListWorkersRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ListWorkersRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ListWorkersRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Filter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Filter = append(m.Filter, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ListWorkersResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ListWorkersResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ListWorkersResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Record", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Record = append(m.Record, &moby_buildkit_v1_types.WorkerRecord{}) + if err := m.Record[len(m.Record)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipControl(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowControl + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowControl + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowControl + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthControl + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowControl + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipControl(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthControl = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowControl = fmt.Errorf("proto: integer overflow") +) + +func init() { proto.RegisterFile("control.proto", fileDescriptorControl) } + +var fileDescriptorControl = []byte{ + // 1176 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0x4d, 0x6f, 0x23, 0x45, + 0x13, 0x7e, 0xc7, 0x76, 0xfc, 0x51, 0x76, 0xa2, 0xbc, 0x0d, 0xac, 0x46, 0x03, 0x24, 0x66, 0x00, + 0xc9, 0x5a, 0xed, 0xce, 0x64, 0x03, 0x2b, 0xa1, 0x08, 0xad, 0x76, 0x1d, 0x2f, 0x22, 0x51, 0x22, + 0x96, 0xce, 0x86, 0x95, 0xb8, 0x8d, 0xed, 0x8e, 0x77, 0x14, 0x7b, 0x7a, 0xe8, 0xee, 0x09, 0x6b, + 0x7e, 0x05, 0x07, 0xfe, 0x09, 0x07, 0xce, 0x1c, 0x90, 0xf6, 0xc8, 0x99, 0x43, 0x16, 0xe5, 0x0e, + 0xbf, 0x01, 0xf5, 0xc7, 0xd8, 0xed, 0xd8, 0xf9, 0xdc, 0x53, 0xba, 0x2a, 0x4f, 0x3d, 0x53, 0x5d, + 0x4f, 0xb9, 0xab, 0x60, 0xb9, 0x47, 0x13, 0xc1, 0xe8, 0x30, 0x48, 0x19, 0x15, 0x14, 0xad, 0x8e, + 0x68, 0x77, 0x1c, 0x74, 0xb3, 0x78, 0xd8, 0x3f, 0x8e, 0x45, 0x70, 0xf2, 0xc0, 0xbb, 0x3f, 0x88, + 0xc5, 0xcb, 0xac, 0x1b, 0xf4, 0xe8, 0x28, 0x1c, 0xd0, 0x01, 0x0d, 0x15, 0xb0, 0x9b, 0x1d, 0x29, + 0x4b, 0x19, 0xea, 0xa4, 0x09, 0xbc, 0xf5, 0x01, 0xa5, 0x83, 0x21, 0x99, 0xa2, 0x44, 0x3c, 0x22, + 0x5c, 0x44, 0xa3, 0xd4, 0x00, 0xee, 0x59, 0x7c, 0xf2, 0x63, 0x61, 0xfe, 0xb1, 0x90, 0xd3, 0xe1, + 0x09, 0x61, 0x61, 0xda, 0x0d, 0x69, 0xca, 0x0d, 0x3a, 0xbc, 0x10, 0x1d, 0xa5, 0x71, 0x28, 0xc6, + 0x29, 0xe1, 0xe1, 0x8f, 0x94, 0x1d, 0x13, 0xa6, 0x03, 0xfc, 0x15, 0x68, 0x3c, 0x63, 0x59, 0x42, + 0x30, 0xf9, 0x21, 0x23, 0x5c, 0xf8, 0x77, 0x61, 0xb5, 0x13, 0xf3, 0xe3, 0x43, 0x1e, 0x0d, 0x72, + 0x1f, 0xba, 0x03, 0xe5, 0xa3, 0x78, 0x28, 0x08, 0x73, 0x9d, 0xa6, 0xd3, 0xaa, 0x61, 0x63, 0xf9, + 0xbb, 0xf0, 0x7f, 0x0b, 0xcb, 0x53, 0x9a, 0x70, 0x82, 0x1e, 0x42, 0x99, 0x91, 0x1e, 0x65, 0x7d, + 0xd7, 0x69, 0x16, 0x5b, 0xf5, 0xcd, 0x0f, 0x83, 0xf3, 0x25, 0x0a, 0x4c, 0x80, 0x04, 0x61, 0x03, + 0xf6, 0x7f, 0x2f, 0x40, 0xdd, 0xf2, 0xa3, 0x15, 0x28, 0xec, 0x74, 0xcc, 0xf7, 0x0a, 0x3b, 0x1d, + 0xe4, 0x42, 0x65, 0x3f, 0x13, 0x51, 0x77, 0x48, 0xdc, 0x42, 0xd3, 0x69, 0x55, 0x71, 0x6e, 0xa2, + 0x77, 0x61, 0x69, 0x27, 0x39, 0xe4, 0xc4, 0x2d, 0x2a, 0xbf, 0x36, 0x10, 0x82, 0xd2, 0x41, 0xfc, + 0x13, 0x71, 0x4b, 0x4d, 0xa7, 0x55, 0xc4, 0xea, 0x2c, 0xef, 0xf1, 0x2c, 0x62, 0x24, 0x11, 0xee, + 0x92, 0xbe, 0x87, 0xb6, 0x50, 0x1b, 0x6a, 0xdb, 0x8c, 0x44, 0x82, 0xf4, 0x9f, 0x08, 0xb7, 0xdc, + 0x74, 0x5a, 0xf5, 0x4d, 0x2f, 0xd0, 0xba, 0x04, 0xb9, 0x2e, 0xc1, 0xf3, 0x5c, 0x97, 0x76, 0xf5, + 0xf5, 0xe9, 0xfa, 0xff, 0x7e, 0x7e, 0xb3, 0xee, 0xe0, 0x69, 0x18, 0x7a, 0x0c, 0xb0, 0x17, 0x71, + 0x71, 0xc8, 0x15, 0x49, 0xe5, 0x4a, 0x92, 0x92, 0x22, 0xb0, 0x62, 0xd0, 0x1a, 0x80, 0x2a, 0xc0, + 0x36, 0xcd, 0x12, 0xe1, 0x56, 0x55, 0xde, 0x96, 0x07, 0x35, 0xa1, 0xde, 0x21, 0xbc, 0xc7, 0xe2, + 0x54, 0xc4, 0x34, 0x71, 0x6b, 0xea, 0x0a, 0xb6, 0xcb, 0xff, 0xa5, 0x04, 0x8d, 0x03, 0xd9, 0x14, + 0xb9, 0x70, 0xab, 0x50, 0xc4, 0xe4, 0xc8, 0x54, 0x51, 0x1e, 0x51, 0x00, 0xd0, 0x21, 0x47, 0x71, + 0x12, 0x2b, 0x8e, 0x82, 0x4a, 0x73, 0x25, 0x48, 0xbb, 0xc1, 0xd4, 0x8b, 0x2d, 0x04, 0xf2, 0xa0, + 0xfa, 0xf4, 0x55, 0x4a, 0x99, 0x14, 0xbf, 0xa8, 0x68, 0x26, 0x36, 0x7a, 0x01, 0xcb, 0xf9, 0xf9, + 0x89, 0x10, 0x8c, 0xbb, 0x25, 0x25, 0xf8, 0x83, 0x79, 0xc1, 0xed, 0xa4, 0x82, 0x99, 0x98, 0xa7, + 0x89, 0x60, 0x63, 0x3c, 0xcb, 0x23, 0xb5, 0x3e, 0x20, 0x9c, 0xcb, 0x0c, 0xb5, 0x50, 0xb9, 0x29, + 0xd3, 0xf9, 0x8a, 0xd1, 0x44, 0x90, 0xa4, 0xaf, 0x84, 0xaa, 0xe1, 0x89, 0x2d, 0xd3, 0xc9, 0xcf, + 0x3a, 0x9d, 0xca, 0xb5, 0xd2, 0x99, 0x89, 0x31, 0xe9, 0xcc, 0xf8, 0xd0, 0x16, 0x2c, 0x6d, 0x47, + 0xbd, 0x97, 0x44, 0x69, 0x52, 0xdf, 0x5c, 0x9b, 0x27, 0x54, 0xff, 0xfe, 0x46, 0x89, 0xc0, 0xdb, + 0x25, 0xd9, 0x1e, 0x58, 0x87, 0x78, 0x8f, 0x01, 0xcd, 0xdf, 0x57, 0xea, 0x72, 0x4c, 0xc6, 0xb9, + 0x2e, 0xc7, 0x64, 0x2c, 0x9b, 0xf8, 0x24, 0x1a, 0x66, 0xba, 0xb9, 0x6b, 0x58, 0x1b, 0x5b, 0x85, + 0x2f, 0x1c, 0xc9, 0x30, 0x9f, 0xe2, 0x4d, 0x18, 0xfc, 0x37, 0x0e, 0x34, 0xec, 0x0c, 0xd1, 0x07, + 0x50, 0xd3, 0x49, 0x4d, 0x9b, 0x63, 0xea, 0x90, 0x7d, 0xb8, 0x33, 0x32, 0x06, 0x77, 0x0b, 0xcd, + 0x62, 0xab, 0x86, 0x2d, 0x0f, 0xfa, 0x16, 0xea, 0x1a, 0xac, 0xab, 0x5c, 0x54, 0x55, 0x0e, 0x2f, + 0x2f, 0x4a, 0x60, 0x45, 0xe8, 0x1a, 0xdb, 0x1c, 0xde, 0x23, 0x58, 0x3d, 0x0f, 0xb8, 0xd1, 0x0d, + 0x7f, 0x73, 0x60, 0xd9, 0x88, 0x6a, 0x5e, 0xa1, 0x28, 0x67, 0x24, 0x2c, 0xf7, 0x99, 0xf7, 0xe8, + 0xe1, 0x85, 0xfd, 0xa0, 0x61, 0xc1, 0xf9, 0x38, 0x9d, 0xef, 0x1c, 0x9d, 0xb7, 0x0d, 0xef, 0x2d, + 0x84, 0xde, 0x28, 0xf3, 0x8f, 0x60, 0xf9, 0x40, 0x44, 0x22, 0xe3, 0x17, 0xfe, 0x64, 0xfd, 0x5f, + 0x1d, 0x58, 0xc9, 0x31, 0xe6, 0x76, 0x9f, 0x43, 0xf5, 0x84, 0x30, 0x41, 0x5e, 0x11, 0x6e, 0x6e, + 0xe5, 0xce, 0xdf, 0xea, 0x3b, 0x85, 0xc0, 0x13, 0x24, 0xda, 0x82, 0x2a, 0x57, 0x3c, 0x44, 0xcb, + 0xba, 0xb0, 0x95, 0x75, 0x94, 0xf9, 0xde, 0x04, 0x8f, 0x42, 0x28, 0x0d, 0xe9, 0x20, 0x57, 0xfb, + 0xfd, 0x8b, 0xe2, 0xf6, 0xe8, 0x00, 0x2b, 0xa0, 0x7f, 0x5a, 0x80, 0xb2, 0xf6, 0xa1, 0x5d, 0x28, + 0xf7, 0xe3, 0x01, 0xe1, 0x42, 0xdf, 0xaa, 0xbd, 0x29, 0x7f, 0x20, 0x7f, 0x9d, 0xae, 0xdf, 0xb5, + 0x66, 0x15, 0x4d, 0x49, 0x22, 0x27, 0x6b, 0x14, 0x27, 0x84, 0xf1, 0x70, 0x40, 0xef, 0xeb, 0x90, + 0xa0, 0xa3, 0xfe, 0x60, 0xc3, 0x20, 0xb9, 0xe2, 0x24, 0xcd, 0x84, 0x69, 0xcc, 0xdb, 0x71, 0x69, + 0x06, 0x39, 0x22, 0x92, 0x68, 0x44, 0xcc, 0xbb, 0xa6, 0xce, 0x72, 0x44, 0xf4, 0x64, 0xdf, 0xf6, + 0xd5, 0xe0, 0xa8, 0x62, 0x63, 0xa1, 0x2d, 0xa8, 0x70, 0x11, 0x31, 0x41, 0xfa, 0xea, 0x49, 0xba, + 0xce, 0xdb, 0x9e, 0x07, 0xa0, 0x47, 0x50, 0xeb, 0xd1, 0x51, 0x3a, 0x24, 0x32, 0xba, 0x7c, 0xcd, + 0xe8, 0x69, 0x88, 0xec, 0x1e, 0xc2, 0x18, 0x65, 0x6a, 0xaa, 0xd4, 0xb0, 0x36, 0xfc, 0x7f, 0x0b, + 0xd0, 0xb0, 0xc5, 0x9a, 0x9b, 0x98, 0xbb, 0x50, 0xd6, 0xd2, 0xeb, 0xae, 0xbb, 0x5d, 0xa9, 0x34, + 0xc3, 0xc2, 0x52, 0xb9, 0x50, 0xe9, 0x65, 0x4c, 0x8d, 0x53, 0x3d, 0x64, 0x73, 0x53, 0x26, 0x2c, + 0xa8, 0x88, 0x86, 0xaa, 0x54, 0x45, 0xac, 0x0d, 0x39, 0x65, 0x27, 0xbb, 0xcd, 0xcd, 0xa6, 0xec, + 0x24, 0xcc, 0x96, 0xa1, 0xf2, 0x56, 0x32, 0x54, 0x6f, 0x2c, 0x83, 0xff, 0x87, 0x03, 0xb5, 0x49, + 0x97, 0x5b, 0xd5, 0x75, 0xde, 0xba, 0xba, 0x33, 0x95, 0x29, 0xdc, 0xae, 0x32, 0x77, 0xa0, 0xcc, + 0x05, 0x23, 0xd1, 0x48, 0x69, 0x54, 0xc4, 0xc6, 0x92, 0xef, 0xc9, 0x88, 0x0f, 0x94, 0x42, 0x0d, + 0x2c, 0x8f, 0xbe, 0x0f, 0x8d, 0xf6, 0x58, 0x10, 0xbe, 0x4f, 0xb8, 0x5c, 0x2e, 0xa4, 0xb6, 0xfd, + 0x48, 0x44, 0xea, 0x1e, 0x0d, 0xac, 0xce, 0xfe, 0x3d, 0x40, 0x7b, 0x31, 0x17, 0x2f, 0xd4, 0xa6, + 0xc8, 0x17, 0xed, 0x81, 0x45, 0x6b, 0x0f, 0x3c, 0x80, 0x77, 0x66, 0xd0, 0xe6, 0x95, 0xfa, 0xf2, + 0xdc, 0x26, 0xf8, 0xc9, 0xfc, 0xab, 0xa1, 0x16, 0xd2, 0x40, 0x07, 0xce, 0x2e, 0x84, 0x9b, 0xff, + 0x14, 0xa1, 0xb2, 0xad, 0x77, 0x6d, 0xf4, 0x1c, 0x6a, 0x93, 0x45, 0x13, 0xf9, 0xf3, 0x34, 0xe7, + 0x37, 0x56, 0xef, 0xe3, 0x4b, 0x31, 0x26, 0xbf, 0xaf, 0x61, 0x49, 0xad, 0xbe, 0x68, 0xc1, 0x33, + 0x68, 0xef, 0xc4, 0xde, 0xe5, 0x2b, 0xec, 0x86, 0x23, 0x99, 0xd4, 0x0c, 0x59, 0xc4, 0x64, 0x2f, + 0x1b, 0xde, 0xfa, 0x15, 0xc3, 0x07, 0xed, 0x43, 0xd9, 0xfc, 0x9c, 0x17, 0x41, 0xed, 0x49, 0xe1, + 0x35, 0x2f, 0x06, 0x68, 0xb2, 0x0d, 0x07, 0xed, 0x4f, 0x36, 0xa9, 0x45, 0xa9, 0xd9, 0x6d, 0xe0, + 0x5d, 0xf1, 0xff, 0x96, 0xb3, 0xe1, 0xa0, 0xef, 0xa1, 0x6e, 0x09, 0x8d, 0x16, 0x08, 0x3a, 0xdf, + 0x35, 0xde, 0xa7, 0x57, 0xa0, 0x74, 0xb2, 0xed, 0xc6, 0xeb, 0xb3, 0x35, 0xe7, 0xcf, 0xb3, 0x35, + 0xe7, 0xef, 0xb3, 0x35, 0xa7, 0x5b, 0x56, 0x7d, 0xff, 0xd9, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, + 0xe1, 0xef, 0xcc, 0xf5, 0x6f, 0x0d, 0x00, 0x00, +} diff --git a/vendor/github.com/moby/buildkit/api/services/control/control.proto b/vendor/github.com/moby/buildkit/api/services/control/control.proto new file mode 100644 index 0000000000000..8d61c7eb6866d --- /dev/null +++ b/vendor/github.com/moby/buildkit/api/services/control/control.proto @@ -0,0 +1,121 @@ +syntax = "proto3"; + +package moby.buildkit.v1; + +// The control API is currently considered experimental and may break in a backwards +// incompatible way. + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "google/protobuf/timestamp.proto"; +import "github.com/moby/buildkit/solver/pb/ops.proto"; +import "github.com/moby/buildkit/api/types/worker.proto"; + +option (gogoproto.sizer_all) = true; +option (gogoproto.marshaler_all) = true; +option (gogoproto.unmarshaler_all) = true; + +service Control { + rpc DiskUsage(DiskUsageRequest) returns (DiskUsageResponse); + rpc Prune(PruneRequest) returns (stream UsageRecord); + rpc Solve(SolveRequest) returns (SolveResponse); + rpc Status(StatusRequest) returns (stream StatusResponse); + rpc Session(stream BytesMessage) returns (stream BytesMessage); + rpc ListWorkers(ListWorkersRequest) returns (ListWorkersResponse); + // rpc Info(InfoRequest) returns (InfoResponse); +} + +message PruneRequest { + // TODO: filter +} + +message DiskUsageRequest { + string filter = 1; // FIXME: this should be containerd-compatible repeated string? +} + +message DiskUsageResponse { + repeated UsageRecord record = 1; +} + +message UsageRecord { + string ID = 1; + bool Mutable = 2; + bool InUse = 3; + int64 Size = 4; + string Parent = 5; + google.protobuf.Timestamp CreatedAt = 6 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; + google.protobuf.Timestamp LastUsedAt = 7 [(gogoproto.stdtime) = true]; + int64 UsageCount = 8; + string Description = 9; +} + +message SolveRequest { + string Ref = 1; + pb.Definition Definition = 2; + string Exporter = 3; + map ExporterAttrs = 4; + string Session = 5; + string Frontend = 6; + map FrontendAttrs = 7; + CacheOptions Cache = 8 [(gogoproto.nullable) = false]; +} + +message CacheOptions { + string ExportRef = 1; + repeated string ImportRefs = 2; + map ExportAttrs = 3; +} + +message SolveResponse { + map ExporterResponse = 1; +} + +message StatusRequest { + string Ref = 1; +} + +message StatusResponse { + repeated Vertex vertexes = 1; + repeated VertexStatus statuses = 2; + repeated VertexLog logs = 3; +} + +message Vertex { + string digest = 1 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false]; + repeated string inputs = 2 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false]; + string name = 3; + bool cached = 4; + google.protobuf.Timestamp started = 5 [(gogoproto.stdtime) = true ]; + google.protobuf.Timestamp completed = 6 [(gogoproto.stdtime) = true ]; + string error = 7; // typed errors? +} + +message VertexStatus { + string ID = 1; + string vertex = 2 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false]; + string name = 3; + int64 current = 4; + int64 total = 5; + // TODO: add started, completed + google.protobuf.Timestamp timestamp = 6 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; + google.protobuf.Timestamp started = 7 [(gogoproto.stdtime) = true ]; + google.protobuf.Timestamp completed = 8 [(gogoproto.stdtime) = true ]; +} + +message VertexLog { + string vertex = 1 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false]; + google.protobuf.Timestamp timestamp = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; + int64 stream = 3; + bytes msg = 4; +} + +message BytesMessage { + bytes data = 1; +} + +message ListWorkersRequest { + repeated string filter = 1; // containerd style +} + +message ListWorkersResponse { + repeated moby.buildkit.v1.types.WorkerRecord record = 1; +} \ No newline at end of file diff --git a/vendor/github.com/moby/buildkit/api/services/control/generate.go b/vendor/github.com/moby/buildkit/api/services/control/generate.go new file mode 100644 index 0000000000000..1c161155f502d --- /dev/null +++ b/vendor/github.com/moby/buildkit/api/services/control/generate.go @@ -0,0 +1,3 @@ +package moby_buildkit_v1 + +//go:generate protoc -I=. -I=../../../vendor/ -I=../../../../../../ --gogo_out=plugins=grpc:. control.proto diff --git a/vendor/github.com/moby/buildkit/api/types/generate.go b/vendor/github.com/moby/buildkit/api/types/generate.go new file mode 100644 index 0000000000000..84007df1d9b8b --- /dev/null +++ b/vendor/github.com/moby/buildkit/api/types/generate.go @@ -0,0 +1,3 @@ +package moby_buildkit_v1_types + +//go:generate protoc -I=. -I=../../vendor/ -I=../../../../../ --gogo_out=plugins=grpc:. worker.proto diff --git a/vendor/github.com/moby/buildkit/api/types/worker.pb.go b/vendor/github.com/moby/buildkit/api/types/worker.pb.go new file mode 100644 index 0000000000000..46c8538f8e8bd --- /dev/null +++ b/vendor/github.com/moby/buildkit/api/types/worker.pb.go @@ -0,0 +1,523 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: worker.proto + +/* + Package moby_buildkit_v1_types is a generated protocol buffer package. + + It is generated from these files: + worker.proto + + It has these top-level messages: + WorkerRecord +*/ +package moby_buildkit_v1_types + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" +import _ "github.com/gogo/protobuf/gogoproto" +import pb "github.com/moby/buildkit/solver/pb" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +type WorkerRecord struct { + ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` + Labels map[string]string `protobuf:"bytes,2,rep,name=Labels" json:"Labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Platforms []pb.Platform `protobuf:"bytes,3,rep,name=platforms" json:"platforms"` +} + +func (m *WorkerRecord) Reset() { *m = WorkerRecord{} } +func (m *WorkerRecord) String() string { return proto.CompactTextString(m) } +func (*WorkerRecord) ProtoMessage() {} +func (*WorkerRecord) Descriptor() ([]byte, []int) { return fileDescriptorWorker, []int{0} } + +func (m *WorkerRecord) GetID() string { + if m != nil { + return m.ID + } + return "" +} + +func (m *WorkerRecord) GetLabels() map[string]string { + if m != nil { + return m.Labels + } + return nil +} + +func (m *WorkerRecord) GetPlatforms() []pb.Platform { + if m != nil { + return m.Platforms + } + return nil +} + +func init() { + proto.RegisterType((*WorkerRecord)(nil), "moby.buildkit.v1.types.WorkerRecord") +} +func (m *WorkerRecord) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *WorkerRecord) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintWorker(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + if len(m.Labels) > 0 { + for k, _ := range m.Labels { + dAtA[i] = 0x12 + i++ + v := m.Labels[k] + mapSize := 1 + len(k) + sovWorker(uint64(len(k))) + 1 + len(v) + sovWorker(uint64(len(v))) + i = encodeVarintWorker(dAtA, i, uint64(mapSize)) + dAtA[i] = 0xa + i++ + i = encodeVarintWorker(dAtA, i, uint64(len(k))) + i += copy(dAtA[i:], k) + dAtA[i] = 0x12 + i++ + i = encodeVarintWorker(dAtA, i, uint64(len(v))) + i += copy(dAtA[i:], v) + } + } + if len(m.Platforms) > 0 { + for _, msg := range m.Platforms { + dAtA[i] = 0x1a + i++ + i = encodeVarintWorker(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + return i, nil +} + +func encodeVarintWorker(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *WorkerRecord) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovWorker(uint64(l)) + } + if len(m.Labels) > 0 { + for k, v := range m.Labels { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovWorker(uint64(len(k))) + 1 + len(v) + sovWorker(uint64(len(v))) + n += mapEntrySize + 1 + sovWorker(uint64(mapEntrySize)) + } + } + if len(m.Platforms) > 0 { + for _, e := range m.Platforms { + l = e.Size() + n += 1 + l + sovWorker(uint64(l)) + } + } + return n +} + +func sovWorker(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozWorker(x uint64) (n int) { + return sovWorker(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *WorkerRecord) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowWorker + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: WorkerRecord: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: WorkerRecord: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowWorker + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthWorker + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowWorker + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthWorker + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Labels == nil { + m.Labels = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowWorker + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowWorker + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthWorker + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowWorker + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthWorker + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipWorker(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthWorker + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Labels[mapkey] = mapvalue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Platforms", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowWorker + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthWorker + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Platforms = append(m.Platforms, pb.Platform{}) + if err := m.Platforms[len(m.Platforms)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipWorker(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthWorker + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipWorker(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowWorker + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowWorker + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowWorker + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthWorker + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowWorker + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipWorker(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthWorker = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowWorker = fmt.Errorf("proto: integer overflow") +) + +func init() { proto.RegisterFile("worker.proto", fileDescriptorWorker) } + +var fileDescriptorWorker = []byte{ + // 273 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x8f, 0x41, 0x4b, 0xf3, 0x40, + 0x10, 0x86, 0xbf, 0x4d, 0x3e, 0x0b, 0xdd, 0x06, 0x91, 0x45, 0x24, 0xe4, 0x10, 0x8b, 0xa7, 0x1e, + 0x74, 0xb6, 0xea, 0x45, 0x3d, 0x96, 0x0a, 0x16, 0x3c, 0x48, 0x2e, 0x9e, 0xb3, 0xed, 0x36, 0x86, + 0x24, 0xce, 0xb2, 0xd9, 0x44, 0xf2, 0x0f, 0x7b, 0xf4, 0xe2, 0x55, 0x24, 0xbf, 0x44, 0xba, 0x89, + 0x98, 0x83, 0xb7, 0x79, 0x87, 0x67, 0x1e, 0xde, 0xa1, 0xde, 0x1b, 0xea, 0x4c, 0x6a, 0x50, 0x1a, + 0x0d, 0xb2, 0x93, 0x02, 0x45, 0x03, 0xa2, 0x4a, 0xf3, 0x4d, 0x96, 0x1a, 0xa8, 0x2f, 0xc1, 0x34, + 0x4a, 0x96, 0xc1, 0x45, 0x92, 0x9a, 0x97, 0x4a, 0xc0, 0x1a, 0x0b, 0x9e, 0x60, 0x82, 0xdc, 0xe2, + 0xa2, 0xda, 0xda, 0x64, 0x83, 0x9d, 0x3a, 0x4d, 0x70, 0x3e, 0xc0, 0xf7, 0x46, 0xfe, 0x63, 0xe4, + 0x25, 0xe6, 0xb5, 0xd4, 0x5c, 0x09, 0x8e, 0xaa, 0xec, 0xe8, 0xb3, 0x0f, 0x42, 0xbd, 0x67, 0xdb, + 0x22, 0x92, 0x6b, 0xd4, 0x1b, 0x76, 0x48, 0x9d, 0xd5, 0xd2, 0x27, 0x53, 0x32, 0x1b, 0x47, 0xce, + 0x6a, 0xc9, 0x1e, 0xe8, 0xe8, 0x31, 0x16, 0x32, 0x2f, 0x7d, 0x67, 0xea, 0xce, 0x26, 0x57, 0x73, + 0xf8, 0xbb, 0x26, 0x0c, 0x2d, 0xd0, 0x9d, 0xdc, 0xbf, 0x1a, 0xdd, 0x44, 0xfd, 0x3d, 0x9b, 0xd3, + 0xb1, 0xca, 0x63, 0xb3, 0x45, 0x5d, 0x94, 0xbe, 0x6b, 0x65, 0x1e, 0x28, 0x01, 0x4f, 0xfd, 0x72, + 0xf1, 0x7f, 0xf7, 0x79, 0xfa, 0x2f, 0xfa, 0x85, 0x82, 0x5b, 0x3a, 0x19, 0x88, 0xd8, 0x11, 0x75, + 0x33, 0xd9, 0xf4, 0xdd, 0xf6, 0x23, 0x3b, 0xa6, 0x07, 0x75, 0x9c, 0x57, 0xd2, 0x77, 0xec, 0xae, + 0x0b, 0x77, 0xce, 0x0d, 0x59, 0x78, 0xbb, 0x36, 0x24, 0xef, 0x6d, 0x48, 0xbe, 0xda, 0x90, 0x88, + 0x91, 0x7d, 0xf6, 0xfa, 0x3b, 0x00, 0x00, 0xff, 0xff, 0xa9, 0x5c, 0x8f, 0x26, 0x71, 0x01, 0x00, + 0x00, +} diff --git a/vendor/github.com/moby/buildkit/api/types/worker.proto b/vendor/github.com/moby/buildkit/api/types/worker.proto new file mode 100644 index 0000000000000..b1376b7431b96 --- /dev/null +++ b/vendor/github.com/moby/buildkit/api/types/worker.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package moby.buildkit.v1.types; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/moby/buildkit/solver/pb/ops.proto"; + +option (gogoproto.sizer_all) = true; +option (gogoproto.marshaler_all) = true; +option (gogoproto.unmarshaler_all) = true; + +message WorkerRecord { + string ID = 1; + map Labels = 2; + repeated pb.Platform platforms = 3 [(gogoproto.nullable) = false]; +} diff --git a/vendor/github.com/moby/buildkit/cache/contenthash/checksum.go b/vendor/github.com/moby/buildkit/cache/contenthash/checksum.go new file mode 100644 index 0000000000000..cf89a5f2092cc --- /dev/null +++ b/vendor/github.com/moby/buildkit/cache/contenthash/checksum.go @@ -0,0 +1,634 @@ +package contenthash + +import ( + "bytes" + "context" + "crypto/sha256" + "io" + "os" + "path" + "path/filepath" + "sync" + + "github.com/containerd/continuity/fs" + "github.com/docker/docker/pkg/locker" + iradix "github.com/hashicorp/go-immutable-radix" + "github.com/hashicorp/golang-lru/simplelru" + "github.com/moby/buildkit/cache" + "github.com/moby/buildkit/cache/metadata" + "github.com/moby/buildkit/snapshot" + digest "github.com/opencontainers/go-digest" + "github.com/pkg/errors" + "github.com/tonistiigi/fsutil" +) + +var errNotFound = errors.Errorf("not found") + +var defaultManager *cacheManager +var defaultManagerOnce sync.Once + +const keyContentHash = "buildkit.contenthash.v0" + +func getDefaultManager() *cacheManager { + defaultManagerOnce.Do(func() { + lru, _ := simplelru.NewLRU(20, nil) // error is impossible on positive size + defaultManager = &cacheManager{lru: lru, locker: locker.New()} + }) + return defaultManager +} + +// Layout in the radix tree: Every path is saved by cleaned absolute unix path. +// Directories have 2 records, one contains digest for directory header, other +// the recursive digest for directory contents. "/dir/" is the record for +// header, "/dir" is for contents. For the root node "" (empty string) is the +// key for root, "/" for the root header + +func Checksum(ctx context.Context, ref cache.ImmutableRef, path string) (digest.Digest, error) { + return getDefaultManager().Checksum(ctx, ref, path) +} + +func GetCacheContext(ctx context.Context, md *metadata.StorageItem) (CacheContext, error) { + return getDefaultManager().GetCacheContext(ctx, md) +} + +func SetCacheContext(ctx context.Context, md *metadata.StorageItem, cc CacheContext) error { + return getDefaultManager().SetCacheContext(ctx, md, cc) +} + +type CacheContext interface { + Checksum(ctx context.Context, ref cache.Mountable, p string) (digest.Digest, error) + HandleChange(kind fsutil.ChangeKind, p string, fi os.FileInfo, err error) error +} + +type Hashed interface { + Digest() digest.Digest +} + +type cacheManager struct { + locker *locker.Locker + lru *simplelru.LRU + lruMu sync.Mutex +} + +func (cm *cacheManager) Checksum(ctx context.Context, ref cache.ImmutableRef, p string) (digest.Digest, error) { + cc, err := cm.GetCacheContext(ctx, ensureOriginMetadata(ref.Metadata())) + if err != nil { + return "", nil + } + return cc.Checksum(ctx, ref, p) +} + +func (cm *cacheManager) GetCacheContext(ctx context.Context, md *metadata.StorageItem) (CacheContext, error) { + cm.locker.Lock(md.ID()) + cm.lruMu.Lock() + v, ok := cm.lru.Get(md.ID()) + cm.lruMu.Unlock() + if ok { + cm.locker.Unlock(md.ID()) + return v.(*cacheContext), nil + } + cc, err := newCacheContext(md) + if err != nil { + cm.locker.Unlock(md.ID()) + return nil, err + } + cm.lruMu.Lock() + cm.lru.Add(md.ID(), cc) + cm.lruMu.Unlock() + cm.locker.Unlock(md.ID()) + return cc, nil +} + +func (cm *cacheManager) SetCacheContext(ctx context.Context, md *metadata.StorageItem, cci CacheContext) error { + cc, ok := cci.(*cacheContext) + if !ok { + return errors.Errorf("invalid cachecontext: %T", cc) + } + if md.ID() != cc.md.ID() { + cc = &cacheContext{ + md: md, + tree: cci.(*cacheContext).tree, + dirtyMap: map[string]struct{}{}, + } + } else { + if err := cc.save(); err != nil { + return err + } + } + cm.lruMu.Lock() + cm.lru.Add(md.ID(), cc) + cm.lruMu.Unlock() + return nil +} + +type cacheContext struct { + mu sync.RWMutex + md *metadata.StorageItem + tree *iradix.Tree + dirty bool // needs to be persisted to disk + + // used in HandleChange + txn *iradix.Txn + node *iradix.Node + dirtyMap map[string]struct{} +} + +type mount struct { + mountable cache.Mountable + mountPath string + unmount func() error +} + +func (m *mount) mount(ctx context.Context) (string, error) { + if m.mountPath != "" { + return m.mountPath, nil + } + mounts, err := m.mountable.Mount(ctx, true) + if err != nil { + return "", err + } + + lm := snapshot.LocalMounter(mounts) + + mp, err := lm.Mount() + if err != nil { + return "", err + } + + m.mountPath = mp + m.unmount = lm.Unmount + return mp, nil +} + +func (m *mount) clean() error { + if m.mountPath != "" { + if err := m.unmount(); err != nil { + return err + } + m.mountPath = "" + } + return nil +} + +func newCacheContext(md *metadata.StorageItem) (*cacheContext, error) { + cc := &cacheContext{ + md: md, + tree: iradix.New(), + dirtyMap: map[string]struct{}{}, + } + if err := cc.load(); err != nil { + return nil, err + } + return cc, nil +} + +func (cc *cacheContext) load() error { + dt, err := cc.md.GetExternal(keyContentHash) + if err != nil { + return nil + } + + var l CacheRecords + if err := l.Unmarshal(dt); err != nil { + return err + } + + txn := cc.tree.Txn() + for _, p := range l.Paths { + txn.Insert([]byte(p.Path), p.Record) + } + cc.tree = txn.Commit() + return nil +} + +func (cc *cacheContext) save() error { + cc.mu.Lock() + defer cc.mu.Unlock() + + if cc.txn != nil { + cc.commitActiveTransaction() + } + + var l CacheRecords + node := cc.tree.Root() + node.Walk(func(k []byte, v interface{}) bool { + l.Paths = append(l.Paths, &CacheRecordWithPath{ + Path: string(k), + Record: v.(*CacheRecord), + }) + return false + }) + + dt, err := l.Marshal() + if err != nil { + return err + } + + return cc.md.SetExternal(keyContentHash, dt) +} + +// HandleChange notifies the source about a modification operation +func (cc *cacheContext) HandleChange(kind fsutil.ChangeKind, p string, fi os.FileInfo, err error) (retErr error) { + p = path.Join("/", filepath.ToSlash(p)) + if p == "/" { + p = "" + } + k := convertPathToKey([]byte(p)) + + deleteDir := func(cr *CacheRecord) { + if cr.Type == CacheRecordTypeDir { + cc.node.WalkPrefix(append(k, 0), func(k []byte, v interface{}) bool { + cc.txn.Delete(k) + return false + }) + } + } + + cc.mu.Lock() + defer cc.mu.Unlock() + if cc.txn == nil { + cc.txn = cc.tree.Txn() + cc.node = cc.tree.Root() + + // root is not called by HandleChange. need to fake it + if _, ok := cc.node.Get([]byte{0}); !ok { + cc.txn.Insert([]byte{0}, &CacheRecord{ + Type: CacheRecordTypeDirHeader, + Digest: digest.FromBytes(nil), + }) + cc.txn.Insert([]byte(""), &CacheRecord{ + Type: CacheRecordTypeDir, + }) + } + } + + if kind == fsutil.ChangeKindDelete { + v, ok := cc.txn.Delete(k) + if ok { + deleteDir(v.(*CacheRecord)) + } + d := path.Dir(p) + if d == "/" { + d = "" + } + cc.dirtyMap[d] = struct{}{} + return + } + + stat, ok := fi.Sys().(*fsutil.Stat) + if !ok { + return errors.Errorf("%s invalid change without stat information", p) + } + + h, ok := fi.(Hashed) + if !ok { + return errors.Errorf("invalid fileinfo: %s", p) + } + + v, ok := cc.node.Get(k) + if ok { + deleteDir(v.(*CacheRecord)) + } + + cr := &CacheRecord{ + Type: CacheRecordTypeFile, + } + if fi.Mode()&os.ModeSymlink != 0 { + cr.Type = CacheRecordTypeSymlink + cr.Linkname = filepath.ToSlash(stat.Linkname) + } + if fi.IsDir() { + cr.Type = CacheRecordTypeDirHeader + cr2 := &CacheRecord{ + Type: CacheRecordTypeDir, + } + cc.txn.Insert(k, cr2) + k = append(k, 0) + p += "/" + } + cr.Digest = h.Digest() + cc.txn.Insert(k, cr) + d := path.Dir(p) + if d == "/" { + d = "" + } + cc.dirtyMap[d] = struct{}{} + + return nil +} + +func (cc *cacheContext) Checksum(ctx context.Context, mountable cache.Mountable, p string) (digest.Digest, error) { + m := &mount{mountable: mountable} + defer m.clean() + + const maxSymlinkLimit = 255 + i := 0 + for { + if i > maxSymlinkLimit { + return "", errors.Errorf("too many symlinks: %s", p) + } + cr, err := cc.checksumNoFollow(ctx, m, p) + if err != nil { + return "", err + } + if cr.Type == CacheRecordTypeSymlink { + link := cr.Linkname + if !path.IsAbs(cr.Linkname) { + link = path.Join(path.Dir(p), link) + } + i++ + p = link + } else { + return cr.Digest, nil + } + } +} + +func (cc *cacheContext) checksumNoFollow(ctx context.Context, m *mount, p string) (*CacheRecord, error) { + p = path.Join("/", filepath.ToSlash(p)) + if p == "/" { + p = "" + } + + cc.mu.RLock() + if cc.txn == nil { + root := cc.tree.Root() + cc.mu.RUnlock() + v, ok := root.Get(convertPathToKey([]byte(p))) + if ok { + cr := v.(*CacheRecord) + if cr.Digest != "" { + return cr, nil + } + } + } else { + cc.mu.RUnlock() + } + + cc.mu.Lock() + defer cc.mu.Unlock() + + if cc.txn != nil { + cc.commitActiveTransaction() + } + + defer func() { + if cc.dirty { + go cc.save() + cc.dirty = false + } + }() + + return cc.lazyChecksum(ctx, m, p) +} + +func (cc *cacheContext) commitActiveTransaction() { + for d := range cc.dirtyMap { + addParentToMap(d, cc.dirtyMap) + } + for d := range cc.dirtyMap { + k := convertPathToKey([]byte(d)) + if _, ok := cc.txn.Get(k); ok { + cc.txn.Insert(k, &CacheRecord{Type: CacheRecordTypeDir}) + } + } + cc.tree = cc.txn.Commit() + cc.node = nil + cc.dirtyMap = map[string]struct{}{} + cc.txn = nil +} + +func (cc *cacheContext) lazyChecksum(ctx context.Context, m *mount, p string) (*CacheRecord, error) { + root := cc.tree.Root() + if cc.needsScan(root, p) { + if err := cc.scanPath(ctx, m, p); err != nil { + return nil, err + } + } + k := convertPathToKey([]byte(p)) + txn := cc.tree.Txn() + root = txn.Root() + cr, updated, err := cc.checksum(ctx, root, txn, m, k) + if err != nil { + return nil, err + } + cc.tree = txn.Commit() + cc.dirty = updated + return cr, err +} + +func (cc *cacheContext) checksum(ctx context.Context, root *iradix.Node, txn *iradix.Txn, m *mount, k []byte) (*CacheRecord, bool, error) { + v, ok := root.Get(k) + + if !ok { + return nil, false, errors.Wrapf(errNotFound, "%s not found", convertKeyToPath(k)) + } + cr := v.(*CacheRecord) + + if cr.Digest != "" { + return cr, false, nil + } + var dgst digest.Digest + + switch cr.Type { + case CacheRecordTypeDir: + h := sha256.New() + next := append(k, 0) + iter := root.Seek(next) + subk := next + ok := true + for { + if !ok || !bytes.HasPrefix(subk, next) { + break + } + h.Write(bytes.TrimPrefix(subk, k)) + + subcr, _, err := cc.checksum(ctx, root, txn, m, subk) + if err != nil { + return nil, false, err + } + + h.Write([]byte(subcr.Digest)) + + if subcr.Type == CacheRecordTypeDir { // skip subfiles + next := append(subk, 0, 0xff) + iter = root.Seek(next) + } + subk, _, ok = iter.Next() + } + dgst = digest.NewDigest(digest.SHA256, h) + + default: + p := string(convertKeyToPath(bytes.TrimSuffix(k, []byte{0}))) + + target, err := m.mount(ctx) + if err != nil { + return nil, false, err + } + + // no FollowSymlinkInScope because invalid paths should not be inserted + fp := filepath.Join(target, filepath.FromSlash(p)) + + fi, err := os.Lstat(fp) + if err != nil { + return nil, false, err + } + + dgst, err = prepareDigest(fp, p, fi) + if err != nil { + return nil, false, err + } + } + + cr2 := &CacheRecord{ + Digest: dgst, + Type: cr.Type, + Linkname: cr.Linkname, + } + + txn.Insert(k, cr2) + + return cr2, true, nil +} + +func (cc *cacheContext) needsScan(root *iradix.Node, p string) bool { + if p == "/" { + p = "" + } + if _, ok := root.Get(convertPathToKey([]byte(p))); !ok { + if p == "" { + return true + } + return cc.needsScan(root, path.Clean(path.Dir(p))) + } + return false +} + +func (cc *cacheContext) scanPath(ctx context.Context, m *mount, p string) (retErr error) { + p = path.Join("/", p) + d, _ := path.Split(p) + + mp, err := m.mount(ctx) + if err != nil { + return err + } + + parentPath, err := fs.RootPath(mp, filepath.FromSlash(d)) + if err != nil { + return err + } + + n := cc.tree.Root() + txn := cc.tree.Txn() + + err = filepath.Walk(parentPath, func(path string, fi os.FileInfo, err error) error { + if err != nil { + return errors.Wrapf(err, "failed to walk %s", path) + } + rel, err := filepath.Rel(mp, path) + if err != nil { + return err + } + k := []byte(filepath.Join("/", filepath.ToSlash(rel))) + if string(k) == "/" { + k = []byte{} + } + k = convertPathToKey(k) + if _, ok := n.Get(k); !ok { + cr := &CacheRecord{ + Type: CacheRecordTypeFile, + } + if fi.Mode()&os.ModeSymlink != 0 { + cr.Type = CacheRecordTypeSymlink + link, err := os.Readlink(path) + if err != nil { + return err + } + cr.Linkname = filepath.ToSlash(link) + } + if fi.IsDir() { + cr.Type = CacheRecordTypeDirHeader + cr2 := &CacheRecord{ + Type: CacheRecordTypeDir, + } + txn.Insert(k, cr2) + k = append(k, 0) + } + txn.Insert(k, cr) + } + return nil + }) + if err != nil { + return err + } + + cc.tree = txn.Commit() + return nil +} + +func prepareDigest(fp, p string, fi os.FileInfo) (digest.Digest, error) { + h, err := NewFileHash(fp, fi) + if err != nil { + return "", errors.Wrapf(err, "failed to create hash for %s", p) + } + if fi.Mode().IsRegular() && fi.Size() > 0 { + // TODO: would be nice to put the contents to separate hash first + // so it can be cached for hardlinks + f, err := os.Open(fp) + if err != nil { + return "", errors.Wrapf(err, "failed to open %s", p) + } + defer f.Close() + if _, err := poolsCopy(h, f); err != nil { + return "", errors.Wrapf(err, "failed to copy file data for %s", p) + } + } + return digest.NewDigest(digest.SHA256, h), nil +} + +func addParentToMap(d string, m map[string]struct{}) { + if d == "" { + return + } + d = path.Dir(d) + if d == "/" { + d = "" + } + m[d] = struct{}{} + addParentToMap(d, m) +} + +func ensureOriginMetadata(md *metadata.StorageItem) *metadata.StorageItem { + v := md.Get("cache.equalMutable") // TODO: const + if v == nil { + return md + } + var mutable string + if err := v.Unmarshal(&mutable); err != nil { + return md + } + si, ok := md.Storage().Get(mutable) + if ok { + return si + } + return md +} + +var pool32K = sync.Pool{ + New: func() interface{} { return make([]byte, 32*1024) }, // 32K +} + +func poolsCopy(dst io.Writer, src io.Reader) (written int64, err error) { + buf := pool32K.Get().([]byte) + written, err = io.CopyBuffer(dst, src, buf) + pool32K.Put(buf) + return +} + +func convertPathToKey(p []byte) []byte { + return bytes.Replace([]byte(p), []byte("/"), []byte{0}, -1) +} + +func convertKeyToPath(p []byte) []byte { + return bytes.Replace([]byte(p), []byte{0}, []byte("/"), -1) +} diff --git a/vendor/github.com/moby/buildkit/cache/contenthash/checksum.pb.go b/vendor/github.com/moby/buildkit/cache/contenthash/checksum.pb.go new file mode 100644 index 0000000000000..31dcce95ff87e --- /dev/null +++ b/vendor/github.com/moby/buildkit/cache/contenthash/checksum.pb.go @@ -0,0 +1,755 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: checksum.proto + +/* + Package contenthash is a generated protocol buffer package. + + It is generated from these files: + checksum.proto + + It has these top-level messages: + CacheRecord + CacheRecordWithPath + CacheRecords +*/ +package contenthash + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" +import _ "github.com/gogo/protobuf/gogoproto" + +import github_com_opencontainers_go_digest "github.com/opencontainers/go-digest" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +type CacheRecordType int32 + +const ( + CacheRecordTypeFile CacheRecordType = 0 + CacheRecordTypeDir CacheRecordType = 1 + CacheRecordTypeDirHeader CacheRecordType = 2 + CacheRecordTypeSymlink CacheRecordType = 3 +) + +var CacheRecordType_name = map[int32]string{ + 0: "FILE", + 1: "DIR", + 2: "DIR_HEADER", + 3: "SYMLINK", +} +var CacheRecordType_value = map[string]int32{ + "FILE": 0, + "DIR": 1, + "DIR_HEADER": 2, + "SYMLINK": 3, +} + +func (x CacheRecordType) String() string { + return proto.EnumName(CacheRecordType_name, int32(x)) +} +func (CacheRecordType) EnumDescriptor() ([]byte, []int) { return fileDescriptorChecksum, []int{0} } + +type CacheRecord struct { + Digest github_com_opencontainers_go_digest.Digest `protobuf:"bytes,1,opt,name=digest,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"digest"` + Type CacheRecordType `protobuf:"varint,2,opt,name=type,proto3,enum=contenthash.CacheRecordType" json:"type,omitempty"` + Linkname string `protobuf:"bytes,3,opt,name=linkname,proto3" json:"linkname,omitempty"` +} + +func (m *CacheRecord) Reset() { *m = CacheRecord{} } +func (m *CacheRecord) String() string { return proto.CompactTextString(m) } +func (*CacheRecord) ProtoMessage() {} +func (*CacheRecord) Descriptor() ([]byte, []int) { return fileDescriptorChecksum, []int{0} } + +func (m *CacheRecord) GetType() CacheRecordType { + if m != nil { + return m.Type + } + return CacheRecordTypeFile +} + +func (m *CacheRecord) GetLinkname() string { + if m != nil { + return m.Linkname + } + return "" +} + +type CacheRecordWithPath struct { + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + Record *CacheRecord `protobuf:"bytes,2,opt,name=record" json:"record,omitempty"` +} + +func (m *CacheRecordWithPath) Reset() { *m = CacheRecordWithPath{} } +func (m *CacheRecordWithPath) String() string { return proto.CompactTextString(m) } +func (*CacheRecordWithPath) ProtoMessage() {} +func (*CacheRecordWithPath) Descriptor() ([]byte, []int) { return fileDescriptorChecksum, []int{1} } + +func (m *CacheRecordWithPath) GetPath() string { + if m != nil { + return m.Path + } + return "" +} + +func (m *CacheRecordWithPath) GetRecord() *CacheRecord { + if m != nil { + return m.Record + } + return nil +} + +type CacheRecords struct { + Paths []*CacheRecordWithPath `protobuf:"bytes,1,rep,name=paths" json:"paths,omitempty"` +} + +func (m *CacheRecords) Reset() { *m = CacheRecords{} } +func (m *CacheRecords) String() string { return proto.CompactTextString(m) } +func (*CacheRecords) ProtoMessage() {} +func (*CacheRecords) Descriptor() ([]byte, []int) { return fileDescriptorChecksum, []int{2} } + +func (m *CacheRecords) GetPaths() []*CacheRecordWithPath { + if m != nil { + return m.Paths + } + return nil +} + +func init() { + proto.RegisterType((*CacheRecord)(nil), "contenthash.CacheRecord") + proto.RegisterType((*CacheRecordWithPath)(nil), "contenthash.CacheRecordWithPath") + proto.RegisterType((*CacheRecords)(nil), "contenthash.CacheRecords") + proto.RegisterEnum("contenthash.CacheRecordType", CacheRecordType_name, CacheRecordType_value) +} +func (m *CacheRecord) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CacheRecord) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Digest) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintChecksum(dAtA, i, uint64(len(m.Digest))) + i += copy(dAtA[i:], m.Digest) + } + if m.Type != 0 { + dAtA[i] = 0x10 + i++ + i = encodeVarintChecksum(dAtA, i, uint64(m.Type)) + } + if len(m.Linkname) > 0 { + dAtA[i] = 0x1a + i++ + i = encodeVarintChecksum(dAtA, i, uint64(len(m.Linkname))) + i += copy(dAtA[i:], m.Linkname) + } + return i, nil +} + +func (m *CacheRecordWithPath) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CacheRecordWithPath) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Path) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintChecksum(dAtA, i, uint64(len(m.Path))) + i += copy(dAtA[i:], m.Path) + } + if m.Record != nil { + dAtA[i] = 0x12 + i++ + i = encodeVarintChecksum(dAtA, i, uint64(m.Record.Size())) + n1, err := m.Record.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n1 + } + return i, nil +} + +func (m *CacheRecords) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CacheRecords) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Paths) > 0 { + for _, msg := range m.Paths { + dAtA[i] = 0xa + i++ + i = encodeVarintChecksum(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + return i, nil +} + +func encodeVarintChecksum(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *CacheRecord) Size() (n int) { + var l int + _ = l + l = len(m.Digest) + if l > 0 { + n += 1 + l + sovChecksum(uint64(l)) + } + if m.Type != 0 { + n += 1 + sovChecksum(uint64(m.Type)) + } + l = len(m.Linkname) + if l > 0 { + n += 1 + l + sovChecksum(uint64(l)) + } + return n +} + +func (m *CacheRecordWithPath) Size() (n int) { + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + sovChecksum(uint64(l)) + } + if m.Record != nil { + l = m.Record.Size() + n += 1 + l + sovChecksum(uint64(l)) + } + return n +} + +func (m *CacheRecords) Size() (n int) { + var l int + _ = l + if len(m.Paths) > 0 { + for _, e := range m.Paths { + l = e.Size() + n += 1 + l + sovChecksum(uint64(l)) + } + } + return n +} + +func sovChecksum(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozChecksum(x uint64) (n int) { + return sovChecksum(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *CacheRecord) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChecksum + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CacheRecord: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CacheRecord: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Digest", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChecksum + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthChecksum + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Digest = github_com_opencontainers_go_digest.Digest(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + m.Type = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChecksum + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Type |= (CacheRecordType(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Linkname", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChecksum + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthChecksum + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Linkname = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipChecksum(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthChecksum + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CacheRecordWithPath) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChecksum + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CacheRecordWithPath: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CacheRecordWithPath: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChecksum + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthChecksum + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Record", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChecksum + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthChecksum + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Record == nil { + m.Record = &CacheRecord{} + } + if err := m.Record.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipChecksum(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthChecksum + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CacheRecords) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChecksum + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CacheRecords: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CacheRecords: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Paths", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChecksum + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthChecksum + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Paths = append(m.Paths, &CacheRecordWithPath{}) + if err := m.Paths[len(m.Paths)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipChecksum(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthChecksum + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipChecksum(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowChecksum + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowChecksum + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowChecksum + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthChecksum + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowChecksum + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipChecksum(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthChecksum = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowChecksum = fmt.Errorf("proto: integer overflow") +) + +func init() { proto.RegisterFile("checksum.proto", fileDescriptorChecksum) } + +var fileDescriptorChecksum = []byte{ + // 418 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xc1, 0x6a, 0xd4, 0x40, + 0x18, 0xc7, 0x77, 0xba, 0xeb, 0xaa, 0xdf, 0x4a, 0x0d, 0x53, 0x68, 0xc3, 0x50, 0xb2, 0xe3, 0x5e, + 0x5c, 0x8a, 0xcd, 0x96, 0x08, 0xde, 0xad, 0xd9, 0xa5, 0xd1, 0x2a, 0x32, 0x15, 0x44, 0x3c, 0x48, + 0x36, 0x3b, 0x66, 0x42, 0x9b, 0x4c, 0x48, 0x66, 0x0f, 0xfb, 0x06, 0x92, 0x93, 0x2f, 0x90, 0x93, + 0x82, 0xef, 0xe0, 0x5d, 0xe8, 0xd1, 0xb3, 0x87, 0x22, 0xeb, 0x8b, 0x48, 0x26, 0x55, 0x42, 0xca, + 0x9e, 0xe6, 0xfb, 0x66, 0x7e, 0xdf, 0xff, 0xff, 0x9f, 0x61, 0x60, 0x3b, 0x10, 0x3c, 0x38, 0xcf, + 0x97, 0xb1, 0x9d, 0x66, 0x52, 0x49, 0x3c, 0x08, 0x64, 0xa2, 0x78, 0xa2, 0x84, 0x9f, 0x0b, 0x72, + 0x18, 0x46, 0x4a, 0x2c, 0xe7, 0x76, 0x20, 0xe3, 0x49, 0x28, 0x43, 0x39, 0xd1, 0xcc, 0x7c, 0xf9, + 0x51, 0x77, 0xba, 0xd1, 0x55, 0x3d, 0x3b, 0xfa, 0x86, 0x60, 0xf0, 0xcc, 0x0f, 0x04, 0x67, 0x3c, + 0x90, 0xd9, 0x02, 0x3f, 0x87, 0xfe, 0x22, 0x0a, 0x79, 0xae, 0x4c, 0x44, 0xd1, 0xf8, 0xee, 0xb1, + 0x73, 0x79, 0x35, 0xec, 0xfc, 0xba, 0x1a, 0x1e, 0x34, 0x64, 0x65, 0xca, 0x93, 0xca, 0xd2, 0x8f, + 0x12, 0x9e, 0xe5, 0x93, 0x50, 0x1e, 0xd6, 0x23, 0xb6, 0xab, 0x17, 0x76, 0xad, 0x80, 0x8f, 0xa0, + 0xa7, 0x56, 0x29, 0x37, 0xb7, 0x28, 0x1a, 0x6f, 0x3b, 0xfb, 0x76, 0x23, 0xa6, 0xdd, 0xf0, 0x7c, + 0xb3, 0x4a, 0x39, 0xd3, 0x24, 0x26, 0x70, 0xe7, 0x22, 0x4a, 0xce, 0x13, 0x3f, 0xe6, 0x66, 0xb7, + 0xf2, 0x67, 0xff, 0xfb, 0xd1, 0x7b, 0xd8, 0x69, 0x0c, 0xbd, 0x8d, 0x94, 0x78, 0xed, 0x2b, 0x81, + 0x31, 0xf4, 0x52, 0x5f, 0x89, 0x3a, 0x2e, 0xd3, 0x35, 0x3e, 0x82, 0x7e, 0xa6, 0x29, 0x6d, 0x3d, + 0x70, 0xcc, 0x4d, 0xd6, 0xec, 0x9a, 0x1b, 0xcd, 0xe0, 0x5e, 0x63, 0x3b, 0xc7, 0x4f, 0xe0, 0x56, + 0xa5, 0x94, 0x9b, 0x88, 0x76, 0xc7, 0x03, 0x87, 0x6e, 0x12, 0xf8, 0x17, 0x83, 0xd5, 0xf8, 0xc1, + 0x0f, 0x04, 0xf7, 0x5b, 0x57, 0xc3, 0x0f, 0xa0, 0x37, 0xf3, 0x4e, 0xa7, 0x46, 0x87, 0xec, 0x15, + 0x25, 0xdd, 0x69, 0x1d, 0xcf, 0xa2, 0x0b, 0x8e, 0x87, 0xd0, 0x75, 0x3d, 0x66, 0x20, 0xb2, 0x5b, + 0x94, 0x14, 0xb7, 0x08, 0x37, 0xca, 0xf0, 0x23, 0x00, 0xd7, 0x63, 0x1f, 0x4e, 0xa6, 0x4f, 0xdd, + 0x29, 0x33, 0xb6, 0xc8, 0x7e, 0x51, 0x52, 0xf3, 0x26, 0x77, 0xc2, 0xfd, 0x05, 0xcf, 0xf0, 0x43, + 0xb8, 0x7d, 0xf6, 0xee, 0xe5, 0xa9, 0xf7, 0xea, 0x85, 0xd1, 0x25, 0xa4, 0x28, 0xe9, 0x6e, 0x0b, + 0x3d, 0x5b, 0xc5, 0xd5, 0xbb, 0x92, 0xbd, 0x4f, 0x5f, 0xac, 0xce, 0xf7, 0xaf, 0x56, 0x3b, 0xf3, + 0xb1, 0x71, 0xb9, 0xb6, 0xd0, 0xcf, 0xb5, 0x85, 0x7e, 0xaf, 0x2d, 0xf4, 0xf9, 0x8f, 0xd5, 0x99, + 0xf7, 0xf5, 0x7f, 0x79, 0xfc, 0x37, 0x00, 0x00, 0xff, 0xff, 0x55, 0xf2, 0x2e, 0x06, 0x7d, 0x02, + 0x00, 0x00, +} diff --git a/vendor/github.com/moby/buildkit/cache/contenthash/checksum.proto b/vendor/github.com/moby/buildkit/cache/contenthash/checksum.proto new file mode 100644 index 0000000000000..d6e524ea7ca23 --- /dev/null +++ b/vendor/github.com/moby/buildkit/cache/contenthash/checksum.proto @@ -0,0 +1,30 @@ +syntax = "proto3"; + +package contenthash; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; + +enum CacheRecordType { + option (gogoproto.goproto_enum_prefix) = false; + option (gogoproto.enum_customname) = "CacheRecordType"; + + FILE = 0 [(gogoproto.enumvalue_customname) = "CacheRecordTypeFile"]; + DIR = 1 [(gogoproto.enumvalue_customname) = "CacheRecordTypeDir"]; + DIR_HEADER = 2 [(gogoproto.enumvalue_customname) = "CacheRecordTypeDirHeader"]; + SYMLINK = 3 [(gogoproto.enumvalue_customname) = "CacheRecordTypeSymlink"]; +} + +message CacheRecord { + string digest = 1 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false]; + CacheRecordType type = 2; + string linkname = 3; +} + +message CacheRecordWithPath { + string path = 1; + CacheRecord record = 2; +} + +message CacheRecords { + repeated CacheRecordWithPath paths = 1; +} \ No newline at end of file diff --git a/vendor/github.com/moby/buildkit/cache/contenthash/filehash.go b/vendor/github.com/moby/buildkit/cache/contenthash/filehash.go new file mode 100644 index 0000000000000..46c15cc6df19a --- /dev/null +++ b/vendor/github.com/moby/buildkit/cache/contenthash/filehash.go @@ -0,0 +1,98 @@ +package contenthash + +import ( + "archive/tar" + "crypto/sha256" + "hash" + "os" + "path/filepath" + "time" + + "github.com/tonistiigi/fsutil" +) + +// NewFileHash returns new hash that is used for the builder cache keys +func NewFileHash(path string, fi os.FileInfo) (hash.Hash, error) { + var link string + if fi.Mode()&os.ModeSymlink != 0 { + var err error + link, err = os.Readlink(path) + if err != nil { + return nil, err + } + } + + stat := &fsutil.Stat{ + Mode: uint32(fi.Mode()), + Size_: fi.Size(), + ModTime: fi.ModTime().UnixNano(), + Linkname: link, + } + + if fi.Mode()&os.ModeSymlink != 0 { + stat.Mode = stat.Mode | 0777 + } + + if err := setUnixOpt(path, fi, stat); err != nil { + return nil, err + } + return NewFromStat(stat) +} + +func NewFromStat(stat *fsutil.Stat) (hash.Hash, error) { + fi := &statInfo{stat} + hdr, err := tar.FileInfoHeader(fi, stat.Linkname) + if err != nil { + return nil, err + } + hdr.Name = "" // note: empty name is different from current has in docker build. Name is added on recursive directory scan instead + hdr.Mode = int64(chmodWindowsTarEntry(os.FileMode(hdr.Mode))) + hdr.Devmajor = stat.Devmajor + hdr.Devminor = stat.Devminor + + if len(stat.Xattrs) > 0 { + hdr.Xattrs = make(map[string]string, len(stat.Xattrs)) + for k, v := range stat.Xattrs { + hdr.Xattrs[k] = string(v) + } + } + // fmt.Printf("hdr: %#v\n", hdr) + tsh := &tarsumHash{hdr: hdr, Hash: sha256.New()} + tsh.Reset() // initialize header + return tsh, nil +} + +type tarsumHash struct { + hash.Hash + hdr *tar.Header +} + +// Reset resets the Hash to its initial state. +func (tsh *tarsumHash) Reset() { + // comply with hash.Hash and reset to the state hash had before any writes + tsh.Hash.Reset() + WriteV1TarsumHeaders(tsh.hdr, tsh.Hash) +} + +type statInfo struct { + *fsutil.Stat +} + +func (s *statInfo) Name() string { + return filepath.Base(s.Stat.Path) +} +func (s *statInfo) Size() int64 { + return s.Stat.Size_ +} +func (s *statInfo) Mode() os.FileMode { + return os.FileMode(s.Stat.Mode) +} +func (s *statInfo) ModTime() time.Time { + return time.Unix(s.Stat.ModTime/1e9, s.Stat.ModTime%1e9) +} +func (s *statInfo) IsDir() bool { + return s.Mode().IsDir() +} +func (s *statInfo) Sys() interface{} { + return s.Stat +} diff --git a/vendor/github.com/moby/buildkit/cache/contenthash/filehash_unix.go b/vendor/github.com/moby/buildkit/cache/contenthash/filehash_unix.go new file mode 100644 index 0000000000000..0939112cb7447 --- /dev/null +++ b/vendor/github.com/moby/buildkit/cache/contenthash/filehash_unix.go @@ -0,0 +1,47 @@ +// +build !windows + +package contenthash + +import ( + "os" + "syscall" + + "github.com/containerd/continuity/sysx" + "github.com/tonistiigi/fsutil" + + "golang.org/x/sys/unix" +) + +func chmodWindowsTarEntry(perm os.FileMode) os.FileMode { + return perm +} + +func setUnixOpt(path string, fi os.FileInfo, stat *fsutil.Stat) error { + s := fi.Sys().(*syscall.Stat_t) + + stat.Uid = s.Uid + stat.Gid = s.Gid + + if !fi.IsDir() { + if s.Mode&syscall.S_IFBLK != 0 || + s.Mode&syscall.S_IFCHR != 0 { + stat.Devmajor = int64(unix.Major(uint64(s.Rdev))) + stat.Devminor = int64(unix.Minor(uint64(s.Rdev))) + } + } + + attrs, err := sysx.LListxattr(path) + if err != nil { + return err + } + if len(attrs) > 0 { + stat.Xattrs = map[string][]byte{} + for _, attr := range attrs { + v, err := sysx.LGetxattr(path, attr) + if err == nil { + stat.Xattrs[attr] = v + } + } + } + return nil +} diff --git a/vendor/github.com/moby/buildkit/cache/contenthash/filehash_windows.go b/vendor/github.com/moby/buildkit/cache/contenthash/filehash_windows.go new file mode 100644 index 0000000000000..340954aa36bd2 --- /dev/null +++ b/vendor/github.com/moby/buildkit/cache/contenthash/filehash_windows.go @@ -0,0 +1,23 @@ +// +build windows + +package contenthash + +import ( + "os" + + "github.com/tonistiigi/fsutil" +) + +// chmodWindowsTarEntry is used to adjust the file permissions used in tar +// header based on the platform the archival is done. +func chmodWindowsTarEntry(perm os.FileMode) os.FileMode { + perm &= 0755 + // Add the x bit: make everything +x from windows + perm |= 0111 + + return perm +} + +func setUnixOpt(path string, fi os.FileInfo, stat *fsutil.Stat) error { + return nil +} diff --git a/vendor/github.com/moby/buildkit/cache/contenthash/generate.go b/vendor/github.com/moby/buildkit/cache/contenthash/generate.go new file mode 100644 index 0000000000000..e4bd2c50c0dad --- /dev/null +++ b/vendor/github.com/moby/buildkit/cache/contenthash/generate.go @@ -0,0 +1,3 @@ +package contenthash + +//go:generate protoc -I=. -I=../../vendor/ --gogofaster_out=. checksum.proto diff --git a/vendor/github.com/moby/buildkit/cache/contenthash/tarsum.go b/vendor/github.com/moby/buildkit/cache/contenthash/tarsum.go new file mode 100644 index 0000000000000..601c41ecb9249 --- /dev/null +++ b/vendor/github.com/moby/buildkit/cache/contenthash/tarsum.go @@ -0,0 +1,60 @@ +package contenthash + +import ( + "archive/tar" + "io" + "sort" + "strconv" +) + +// WriteV1TarsumHeaders writes a tar header to a writer in V1 tarsum format. +func WriteV1TarsumHeaders(h *tar.Header, w io.Writer) { + for _, elem := range v1TarHeaderSelect(h) { + w.Write([]byte(elem[0] + elem[1])) + } +} + +// Functions below are from docker legacy tarsum implementation. +// There is no valid technical reason to continue using them. + +func v0TarHeaderSelect(h *tar.Header) (orderedHeaders [][2]string) { + return [][2]string{ + {"name", h.Name}, + {"mode", strconv.FormatInt(h.Mode, 10)}, + {"uid", strconv.Itoa(h.Uid)}, + {"gid", strconv.Itoa(h.Gid)}, + {"size", strconv.FormatInt(h.Size, 10)}, + {"mtime", strconv.FormatInt(h.ModTime.UTC().Unix(), 10)}, + {"typeflag", string([]byte{h.Typeflag})}, + {"linkname", h.Linkname}, + {"uname", h.Uname}, + {"gname", h.Gname}, + {"devmajor", strconv.FormatInt(h.Devmajor, 10)}, + {"devminor", strconv.FormatInt(h.Devminor, 10)}, + } +} + +func v1TarHeaderSelect(h *tar.Header) (orderedHeaders [][2]string) { + // Get extended attributes. + xAttrKeys := make([]string, len(h.Xattrs)) + for k := range h.Xattrs { + xAttrKeys = append(xAttrKeys, k) + } + sort.Strings(xAttrKeys) + + // Make the slice with enough capacity to hold the 11 basic headers + // we want from the v0 selector plus however many xattrs we have. + orderedHeaders = make([][2]string, 0, 11+len(xAttrKeys)) + + // Copy all headers from v0 excluding the 'mtime' header (the 5th element). + v0headers := v0TarHeaderSelect(h) + orderedHeaders = append(orderedHeaders, v0headers[0:5]...) + orderedHeaders = append(orderedHeaders, v0headers[6:]...) + + // Finally, append the sorted xattrs. + for _, k := range xAttrKeys { + orderedHeaders = append(orderedHeaders, [2]string{k, h.Xattrs[k]}) + } + + return +} diff --git a/vendor/github.com/moby/buildkit/cache/fsutil.go b/vendor/github.com/moby/buildkit/cache/fsutil.go new file mode 100644 index 0000000000000..ec757d5e88f73 --- /dev/null +++ b/vendor/github.com/moby/buildkit/cache/fsutil.go @@ -0,0 +1,71 @@ +package cache + +import ( + "context" + "io" + "io/ioutil" + "os" + + "github.com/containerd/continuity/fs" + "github.com/moby/buildkit/snapshot" +) + +type ReadRequest struct { + Filename string + Range *FileRange +} + +type FileRange struct { + Offset int + Length int +} + +func ReadFile(ctx context.Context, ref ImmutableRef, req ReadRequest) ([]byte, error) { + mount, err := ref.Mount(ctx, true) + if err != nil { + return nil, err + } + + lm := snapshot.LocalMounter(mount) + + root, err := lm.Mount() + if err != nil { + return nil, err + } + + defer func() { + if lm != nil { + lm.Unmount() + } + }() + + fp, err := fs.RootPath(root, req.Filename) + if err != nil { + return nil, err + } + + var dt []byte + + if req.Range == nil { + dt, err = ioutil.ReadFile(fp) + if err != nil { + return nil, err + } + } else { + f, err := os.Open(fp) + if err != nil { + return nil, err + } + dt, err = ioutil.ReadAll(io.NewSectionReader(f, int64(req.Range.Offset), int64(req.Range.Length))) + f.Close() + if err != nil { + return nil, err + } + } + + if err := lm.Unmount(); err != nil { + return nil, err + } + lm = nil + return dt, err +} diff --git a/vendor/github.com/moby/buildkit/cache/gc.go b/vendor/github.com/moby/buildkit/cache/gc.go new file mode 100644 index 0000000000000..31a98b93c8fa8 --- /dev/null +++ b/vendor/github.com/moby/buildkit/cache/gc.go @@ -0,0 +1,27 @@ +package cache + +import ( + "context" + "errors" + "time" +) + +// GCPolicy defines policy for garbage collection +type GCPolicy struct { + MaxSize uint64 + MaxKeepDuration time.Duration +} + +// // CachePolicy defines policy for keeping a resource in cache +// type CachePolicy struct { +// Priority int +// LastUsed time.Time +// } +// +// func defaultCachePolicy() CachePolicy { +// return CachePolicy{Priority: 10, LastUsed: time.Now()} +// } + +func (cm *cacheManager) GC(ctx context.Context) error { + return errors.New("GC not implemented") +} diff --git a/vendor/github.com/moby/buildkit/cache/manager.go b/vendor/github.com/moby/buildkit/cache/manager.go new file mode 100644 index 0000000000000..cf096e576ed11 --- /dev/null +++ b/vendor/github.com/moby/buildkit/cache/manager.go @@ -0,0 +1,573 @@ +package cache + +import ( + "context" + "strings" + "sync" + "time" + + "github.com/containerd/containerd/snapshots" + "github.com/moby/buildkit/cache/metadata" + "github.com/moby/buildkit/client" + "github.com/moby/buildkit/identity" + "github.com/moby/buildkit/snapshot" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "golang.org/x/sync/errgroup" +) + +var ( + ErrLocked = errors.New("locked") + errNotFound = errors.New("not found") + errInvalid = errors.New("invalid") +) + +type ManagerOpt struct { + Snapshotter snapshot.SnapshotterBase + GCPolicy GCPolicy + MetadataStore *metadata.Store +} + +type Accessor interface { + Get(ctx context.Context, id string, opts ...RefOption) (ImmutableRef, error) + GetFromSnapshotter(ctx context.Context, id string, opts ...RefOption) (ImmutableRef, error) + New(ctx context.Context, s ImmutableRef, opts ...RefOption) (MutableRef, error) + GetMutable(ctx context.Context, id string) (MutableRef, error) // Rebase? +} + +type Controller interface { + DiskUsage(ctx context.Context, info client.DiskUsageInfo) ([]*client.UsageInfo, error) + Prune(ctx context.Context, ch chan client.UsageInfo) error + GC(ctx context.Context) error +} + +type Manager interface { + Accessor + Controller + Close() error +} + +type cacheManager struct { + records map[string]*cacheRecord + mu sync.Mutex + ManagerOpt + md *metadata.Store + + muPrune sync.Mutex // make sure parallel prune is not allowed so there will not be inconsistent results +} + +func NewManager(opt ManagerOpt) (Manager, error) { + cm := &cacheManager{ + ManagerOpt: opt, + md: opt.MetadataStore, + records: make(map[string]*cacheRecord), + } + + if err := cm.init(context.TODO()); err != nil { + return nil, err + } + + // cm.scheduleGC(5 * time.Minute) + + return cm, nil +} + +// init loads all snapshots from metadata state and tries to load the records +// from the snapshotter. If snaphot can't be found, metadata is deleted as well. +func (cm *cacheManager) init(ctx context.Context) error { + items, err := cm.md.All() + if err != nil { + return err + } + + for _, si := range items { + if _, err := cm.getRecord(ctx, si.ID(), false); err != nil { + logrus.Debugf("could not load snapshot %s: %v", si.ID(), err) + cm.md.Clear(si.ID()) + // TODO: make sure content is deleted as well + } + } + return nil +} + +// Close closes the manager and releases the metadata database lock. No other +// method should be called after Close. +func (cm *cacheManager) Close() error { + // TODO: allocate internal context and cancel it here + return cm.md.Close() +} + +// Get returns an immutable snapshot reference for ID +func (cm *cacheManager) Get(ctx context.Context, id string, opts ...RefOption) (ImmutableRef, error) { + cm.mu.Lock() + defer cm.mu.Unlock() + return cm.get(ctx, id, false, opts...) +} + +// Get returns an immutable snapshot reference for ID +func (cm *cacheManager) GetFromSnapshotter(ctx context.Context, id string, opts ...RefOption) (ImmutableRef, error) { + cm.mu.Lock() + defer cm.mu.Unlock() + return cm.get(ctx, id, true, opts...) +} + +// get requires manager lock to be taken +func (cm *cacheManager) get(ctx context.Context, id string, fromSnapshotter bool, opts ...RefOption) (ImmutableRef, error) { + rec, err := cm.getRecord(ctx, id, fromSnapshotter, opts...) + if err != nil { + return nil, err + } + rec.mu.Lock() + defer rec.mu.Unlock() + + if rec.mutable { + if len(rec.refs) != 0 { + return nil, errors.Wrapf(ErrLocked, "%s is locked", id) + } + if rec.equalImmutable != nil { + return rec.equalImmutable.ref(), nil + } + return rec.mref().commit(ctx) + } + + return rec.ref(), nil +} + +// getRecord returns record for id. Requires manager lock. +func (cm *cacheManager) getRecord(ctx context.Context, id string, fromSnapshotter bool, opts ...RefOption) (cr *cacheRecord, retErr error) { + if rec, ok := cm.records[id]; ok { + if rec.isDead() { + return nil, errNotFound + } + return rec, nil + } + + md, ok := cm.md.Get(id) + if !ok && !fromSnapshotter { + return nil, errNotFound + } + if mutableID := getEqualMutable(md); mutableID != "" { + mutable, err := cm.getRecord(ctx, mutableID, fromSnapshotter) + if err != nil { + // check loading mutable deleted record from disk + if errors.Cause(err) == errNotFound { + cm.md.Clear(id) + } + return nil, err + } + rec := &cacheRecord{ + mu: &sync.Mutex{}, + cm: cm, + refs: make(map[Mountable]struct{}), + parent: mutable.Parent(), + md: md, + equalMutable: &mutableRef{cacheRecord: mutable}, + } + mutable.equalImmutable = &immutableRef{cacheRecord: rec} + cm.records[id] = rec + return rec, nil + } + + info, err := cm.Snapshotter.Stat(ctx, id) + if err != nil { + return nil, errors.Wrap(errNotFound, err.Error()) + } + + var parent ImmutableRef + if info.Parent != "" { + parent, err = cm.get(ctx, info.Parent, fromSnapshotter, opts...) + if err != nil { + return nil, err + } + defer func() { + if retErr != nil { + parent.Release(context.TODO()) + } + }() + } + + rec := &cacheRecord{ + mu: &sync.Mutex{}, + mutable: info.Kind != snapshots.KindCommitted, + cm: cm, + refs: make(map[Mountable]struct{}), + parent: parent, + md: md, + } + + // the record was deleted but we crashed before data on disk was removed + if getDeleted(md) { + if err := rec.remove(ctx, true); err != nil { + return nil, err + } + return nil, errNotFound + } + + if err := initializeMetadata(rec, opts...); err != nil { + if parent != nil { + parent.Release(context.TODO()) + } + return nil, err + } + + cm.records[id] = rec + return rec, nil +} + +func (cm *cacheManager) New(ctx context.Context, s ImmutableRef, opts ...RefOption) (MutableRef, error) { + id := identity.NewID() + + var parent ImmutableRef + var parentID string + if s != nil { + var err error + parent, err = cm.Get(ctx, s.ID()) + if err != nil { + return nil, err + } + if err := parent.Finalize(ctx, true); err != nil { + return nil, err + } + parentID = parent.ID() + } + + if err := cm.Snapshotter.Prepare(ctx, id, parentID); err != nil { + if parent != nil { + parent.Release(context.TODO()) + } + return nil, errors.Wrapf(err, "failed to prepare %s", id) + } + + md, _ := cm.md.Get(id) + + rec := &cacheRecord{ + mu: &sync.Mutex{}, + mutable: true, + cm: cm, + refs: make(map[Mountable]struct{}), + parent: parent, + md: md, + } + + if err := initializeMetadata(rec, opts...); err != nil { + if parent != nil { + parent.Release(context.TODO()) + } + return nil, err + } + + cm.mu.Lock() + defer cm.mu.Unlock() + + cm.records[id] = rec // TODO: save to db + + return rec.mref(), nil +} +func (cm *cacheManager) GetMutable(ctx context.Context, id string) (MutableRef, error) { + cm.mu.Lock() + defer cm.mu.Unlock() + + rec, err := cm.getRecord(ctx, id, false) + if err != nil { + return nil, err + } + + rec.mu.Lock() + defer rec.mu.Unlock() + if !rec.mutable { + return nil, errors.Wrapf(errInvalid, "%s is not mutable", id) + } + + if len(rec.refs) != 0 { + return nil, errors.Wrapf(ErrLocked, "%s is locked", id) + } + + if rec.equalImmutable != nil { + if len(rec.equalImmutable.refs) != 0 { + return nil, errors.Wrapf(ErrLocked, "%s is locked", id) + } + delete(cm.records, rec.equalImmutable.ID()) + if err := rec.equalImmutable.remove(ctx, false); err != nil { + return nil, err + } + rec.equalImmutable = nil + } + + return rec.mref(), nil +} + +func (cm *cacheManager) Prune(ctx context.Context, ch chan client.UsageInfo) error { + cm.muPrune.Lock() + defer cm.muPrune.Unlock() + return cm.prune(ctx, ch) +} + +func (cm *cacheManager) prune(ctx context.Context, ch chan client.UsageInfo) error { + var toDelete []*cacheRecord + cm.mu.Lock() + + for _, cr := range cm.records { + cr.mu.Lock() + + // ignore duplicates that share data + if cr.equalImmutable != nil && len(cr.equalImmutable.refs) > 0 || cr.equalMutable != nil && len(cr.refs) == 0 { + cr.mu.Unlock() + continue + } + + if cr.isDead() { + cr.mu.Unlock() + continue + } + + if len(cr.refs) == 0 { + cr.dead = true + toDelete = append(toDelete, cr) + } + + // mark metadata as deleted in case we crash before cleanup finished + if err := setDeleted(cr.md); err != nil { + cr.mu.Unlock() + cm.mu.Unlock() + return err + } + cr.mu.Unlock() + } + + cm.mu.Unlock() + + if len(toDelete) == 0 { + return nil + } + + var err error + for _, cr := range toDelete { + cr.mu.Lock() + + usageCount, lastUsedAt := getLastUsed(cr.md) + + c := client.UsageInfo{ + ID: cr.ID(), + Mutable: cr.mutable, + InUse: len(cr.refs) > 0, + Size: getSize(cr.md), + CreatedAt: GetCreatedAt(cr.md), + Description: GetDescription(cr.md), + LastUsedAt: lastUsedAt, + UsageCount: usageCount, + } + + if cr.parent != nil { + c.Parent = cr.parent.ID() + } + + if c.Size == sizeUnknown { + cr.mu.Unlock() // all the non-prune modifications already protected by cr.dead + s, err := cr.Size(ctx) + if err != nil { + return err + } + c.Size = s + cr.mu.Lock() + } + + if cr.equalImmutable != nil { + if err1 := cr.equalImmutable.remove(ctx, false); err == nil { + err = err1 + } + } + if err1 := cr.remove(ctx, true); err == nil { + err = err1 + } + + if err == nil && ch != nil { + ch <- c + } + cr.mu.Unlock() + } + if err != nil { + return err + } + + select { + case <-ctx.Done(): + return ctx.Err() + default: + return cm.prune(ctx, ch) + } +} + +func (cm *cacheManager) DiskUsage(ctx context.Context, opt client.DiskUsageInfo) ([]*client.UsageInfo, error) { + cm.mu.Lock() + + type cacheUsageInfo struct { + refs int + parent string + size int64 + mutable bool + createdAt time.Time + usageCount int + lastUsedAt *time.Time + description string + doubleRef bool + } + + m := make(map[string]*cacheUsageInfo, len(cm.records)) + rescan := make(map[string]struct{}, len(cm.records)) + + for id, cr := range cm.records { + cr.mu.Lock() + // ignore duplicates that share data + if cr.equalImmutable != nil && len(cr.equalImmutable.refs) > 0 || cr.equalMutable != nil && len(cr.refs) == 0 { + cr.mu.Unlock() + continue + } + + usageCount, lastUsedAt := getLastUsed(cr.md) + c := &cacheUsageInfo{ + refs: len(cr.refs), + mutable: cr.mutable, + size: getSize(cr.md), + createdAt: GetCreatedAt(cr.md), + usageCount: usageCount, + lastUsedAt: lastUsedAt, + description: GetDescription(cr.md), + doubleRef: cr.equalImmutable != nil, + } + if cr.parent != nil { + c.parent = cr.parent.ID() + } + if cr.mutable && c.refs > 0 { + c.size = 0 // size can not be determined because it is changing + } + m[id] = c + rescan[id] = struct{}{} + cr.mu.Unlock() + } + cm.mu.Unlock() + + for { + if len(rescan) == 0 { + break + } + for id := range rescan { + v := m[id] + if v.refs == 0 && v.parent != "" { + m[v.parent].refs-- + if v.doubleRef { + m[v.parent].refs-- + } + rescan[v.parent] = struct{}{} + } + delete(rescan, id) + } + } + + var du []*client.UsageInfo + for id, cr := range m { + if opt.Filter != "" && !strings.HasPrefix(id, opt.Filter) { + continue + } + + c := &client.UsageInfo{ + ID: id, + Mutable: cr.mutable, + InUse: cr.refs > 0, + Size: cr.size, + Parent: cr.parent, + CreatedAt: cr.createdAt, + Description: cr.description, + LastUsedAt: cr.lastUsedAt, + UsageCount: cr.usageCount, + } + du = append(du, c) + } + + eg, ctx := errgroup.WithContext(ctx) + + for _, d := range du { + if d.Size == sizeUnknown { + func(d *client.UsageInfo) { + eg.Go(func() error { + ref, err := cm.Get(ctx, d.ID) + if err != nil { + d.Size = 0 + return nil + } + s, err := ref.Size(ctx) + if err != nil { + return err + } + d.Size = s + return ref.Release(context.TODO()) + }) + }(d) + } + } + + if err := eg.Wait(); err != nil { + return du, err + } + + return du, nil +} + +func IsLocked(err error) bool { + return errors.Cause(err) == ErrLocked +} + +func IsNotFound(err error) bool { + return errors.Cause(err) == errNotFound +} + +type RefOption func(withMetadata) error + +type cachePolicy int + +const ( + cachePolicyDefault cachePolicy = iota + cachePolicyRetain +) + +type withMetadata interface { + Metadata() *metadata.StorageItem +} + +func HasCachePolicyRetain(m withMetadata) bool { + return getCachePolicy(m.Metadata()) == cachePolicyRetain +} + +func CachePolicyRetain(m withMetadata) error { + return queueCachePolicy(m.Metadata(), cachePolicyRetain) +} + +func WithDescription(descr string) RefOption { + return func(m withMetadata) error { + return queueDescription(m.Metadata(), descr) + } +} + +func WithCreationTime(tm time.Time) RefOption { + return func(m withMetadata) error { + return queueCreatedAt(m.Metadata(), tm) + } +} + +func initializeMetadata(m withMetadata, opts ...RefOption) error { + md := m.Metadata() + if tm := GetCreatedAt(md); !tm.IsZero() { + return nil + } + + if err := queueCreatedAt(md, time.Now()); err != nil { + return err + } + + for _, opt := range opts { + if err := opt(m); err != nil { + return err + } + } + + return md.Commit() +} diff --git a/vendor/github.com/moby/buildkit/cache/metadata.go b/vendor/github.com/moby/buildkit/cache/metadata.go new file mode 100644 index 0000000000000..787b5a5b9f5ce --- /dev/null +++ b/vendor/github.com/moby/buildkit/cache/metadata.go @@ -0,0 +1,206 @@ +package cache + +import ( + "time" + + "github.com/boltdb/bolt" + "github.com/moby/buildkit/cache/metadata" + "github.com/pkg/errors" +) + +const sizeUnknown int64 = -1 +const keySize = "snapshot.size" +const keyEqualMutable = "cache.equalMutable" +const keyCachePolicy = "cache.cachePolicy" +const keyDescription = "cache.description" +const keyCreatedAt = "cache.createdAt" +const keyLastUsedAt = "cache.lastUsedAt" +const keyUsageCount = "cache.usageCount" + +const keyDeleted = "cache.deleted" + +func setDeleted(si *metadata.StorageItem) error { + v, err := metadata.NewValue(true) + if err != nil { + return errors.Wrap(err, "failed to create size value") + } + si.Update(func(b *bolt.Bucket) error { + return si.SetValue(b, keyDeleted, v) + }) + return nil +} + +func getDeleted(si *metadata.StorageItem) bool { + v := si.Get(keyDeleted) + if v == nil { + return false + } + var deleted bool + if err := v.Unmarshal(&deleted); err != nil { + return false + } + return deleted +} + +func setSize(si *metadata.StorageItem, s int64) error { + v, err := metadata.NewValue(s) + if err != nil { + return errors.Wrap(err, "failed to create size value") + } + si.Queue(func(b *bolt.Bucket) error { + return si.SetValue(b, keySize, v) + }) + return nil +} + +func getSize(si *metadata.StorageItem) int64 { + v := si.Get(keySize) + if v == nil { + return sizeUnknown + } + var size int64 + if err := v.Unmarshal(&size); err != nil { + return sizeUnknown + } + return size +} + +func getEqualMutable(si *metadata.StorageItem) string { + v := si.Get(keyEqualMutable) + if v == nil { + return "" + } + var str string + if err := v.Unmarshal(&str); err != nil { + return "" + } + return str +} + +func setEqualMutable(si *metadata.StorageItem, s string) error { + v, err := metadata.NewValue(s) + if err != nil { + return errors.Wrapf(err, "failed to create %s meta value", keyEqualMutable) + } + si.Queue(func(b *bolt.Bucket) error { + return si.SetValue(b, keyEqualMutable, v) + }) + return nil +} + +func clearEqualMutable(si *metadata.StorageItem) error { + si.Queue(func(b *bolt.Bucket) error { + return si.SetValue(b, keyEqualMutable, nil) + }) + return nil +} + +func queueCachePolicy(si *metadata.StorageItem, p cachePolicy) error { + v, err := metadata.NewValue(p) + if err != nil { + return errors.Wrap(err, "failed to create cachePolicy value") + } + si.Queue(func(b *bolt.Bucket) error { + return si.SetValue(b, keyCachePolicy, v) + }) + return nil +} + +func getCachePolicy(si *metadata.StorageItem) cachePolicy { + v := si.Get(keyCachePolicy) + if v == nil { + return cachePolicyDefault + } + var p cachePolicy + if err := v.Unmarshal(&p); err != nil { + return cachePolicyDefault + } + return p +} + +func queueDescription(si *metadata.StorageItem, descr string) error { + v, err := metadata.NewValue(descr) + if err != nil { + return errors.Wrap(err, "failed to create description value") + } + si.Queue(func(b *bolt.Bucket) error { + return si.SetValue(b, keyDescription, v) + }) + return nil +} + +func GetDescription(si *metadata.StorageItem) string { + v := si.Get(keyDescription) + if v == nil { + return "" + } + var str string + if err := v.Unmarshal(&str); err != nil { + return "" + } + return str +} + +func queueCreatedAt(si *metadata.StorageItem, tm time.Time) error { + v, err := metadata.NewValue(tm.UnixNano()) + if err != nil { + return errors.Wrap(err, "failed to create createdAt value") + } + si.Queue(func(b *bolt.Bucket) error { + return si.SetValue(b, keyCreatedAt, v) + }) + return nil +} + +func GetCreatedAt(si *metadata.StorageItem) time.Time { + v := si.Get(keyCreatedAt) + if v == nil { + return time.Time{} + } + var tm int64 + if err := v.Unmarshal(&tm); err != nil { + return time.Time{} + } + return time.Unix(tm/1e9, tm%1e9) +} + +func getLastUsed(si *metadata.StorageItem) (int, *time.Time) { + v := si.Get(keyUsageCount) + if v == nil { + return 0, nil + } + var usageCount int + if err := v.Unmarshal(&usageCount); err != nil { + return 0, nil + } + v = si.Get(keyLastUsedAt) + if v == nil { + return usageCount, nil + } + var lastUsedTs int64 + if err := v.Unmarshal(&lastUsedTs); err != nil || lastUsedTs == 0 { + return usageCount, nil + } + tm := time.Unix(lastUsedTs/1e9, lastUsedTs%1e9) + return usageCount, &tm +} + +func updateLastUsed(si *metadata.StorageItem) error { + count, _ := getLastUsed(si) + count++ + + v, err := metadata.NewValue(count) + if err != nil { + return errors.Wrap(err, "failed to create usageCount value") + } + v2, err := metadata.NewValue(time.Now().UnixNano()) + if err != nil { + return errors.Wrap(err, "failed to create lastUsedAt value") + } + return si.Update(func(b *bolt.Bucket) error { + if err := si.SetValue(b, keyUsageCount, v); err != nil { + return err + } + return si.SetValue(b, keyLastUsedAt, v2) + }) +} diff --git a/vendor/github.com/moby/buildkit/cache/metadata/metadata.go b/vendor/github.com/moby/buildkit/cache/metadata/metadata.go new file mode 100644 index 0000000000000..461ff6cfd7fe2 --- /dev/null +++ b/vendor/github.com/moby/buildkit/cache/metadata/metadata.go @@ -0,0 +1,382 @@ +package metadata + +import ( + "bytes" + "encoding/json" + "strings" + "sync" + + "github.com/boltdb/bolt" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +const ( + mainBucket = "_main" + indexBucket = "_index" + externalBucket = "_external" +) + +var errNotFound = errors.Errorf("not found") + +type Store struct { + db *bolt.DB +} + +func NewStore(dbPath string) (*Store, error) { + db, err := bolt.Open(dbPath, 0600, nil) + if err != nil { + return nil, errors.Wrapf(err, "failed to open database file %s", dbPath) + } + return &Store{db: db}, nil +} + +func (s *Store) DB() *bolt.DB { + return s.db +} + +func (s *Store) All() ([]*StorageItem, error) { + var out []*StorageItem + err := s.db.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(mainBucket)) + if b == nil { + return nil + } + return b.ForEach(func(key, _ []byte) error { + b := b.Bucket(key) + if b == nil { + return nil + } + si, err := newStorageItem(string(key), b, s) + if err != nil { + return err + } + out = append(out, si) + return nil + }) + }) + return out, err +} + +func (s *Store) Probe(index string) (bool, error) { + var exists bool + err := s.db.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(indexBucket)) + if b == nil { + return nil + } + main := tx.Bucket([]byte(mainBucket)) + if main == nil { + return nil + } + search := []byte(indexKey(index, "")) + c := b.Cursor() + k, _ := c.Seek(search) + if k != nil && bytes.HasPrefix(k, search) { + exists = true + } + return nil + }) + return exists, err +} + +func (s *Store) Search(index string) ([]*StorageItem, error) { + var out []*StorageItem + err := s.db.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(indexBucket)) + if b == nil { + return nil + } + main := tx.Bucket([]byte(mainBucket)) + if main == nil { + return nil + } + index = indexKey(index, "") + c := b.Cursor() + k, _ := c.Seek([]byte(index)) + for { + if k != nil && strings.HasPrefix(string(k), index) { + itemID := strings.TrimPrefix(string(k), index) + k, _ = c.Next() + b := main.Bucket([]byte(itemID)) + if b == nil { + logrus.Errorf("index pointing to missing record %s", itemID) + continue + } + si, err := newStorageItem(itemID, b, s) + if err != nil { + return err + } + out = append(out, si) + } else { + break + } + } + return nil + }) + return out, err +} + +func (s *Store) View(id string, fn func(b *bolt.Bucket) error) error { + return s.db.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(mainBucket)) + if b == nil { + return errors.WithStack(errNotFound) + } + b = b.Bucket([]byte(id)) + if b == nil { + return errors.WithStack(errNotFound) + } + return fn(b) + }) +} + +func (s *Store) Clear(id string) error { + return s.db.Update(func(tx *bolt.Tx) error { + external := tx.Bucket([]byte(externalBucket)) + if external != nil { + external.DeleteBucket([]byte(id)) + } + main := tx.Bucket([]byte(mainBucket)) + if main == nil { + return nil + } + b := main.Bucket([]byte(id)) + if b == nil { + return nil + } + si, err := newStorageItem(id, b, s) + if err != nil { + return err + } + if indexes := si.Indexes(); len(indexes) > 0 { + b := tx.Bucket([]byte(indexBucket)) + if b != nil { + for _, index := range indexes { + if err := b.Delete([]byte(indexKey(index, id))); err != nil { + return err + } + } + } + } + return main.DeleteBucket([]byte(id)) + }) +} + +func (s *Store) Update(id string, fn func(b *bolt.Bucket) error) error { + return s.db.Update(func(tx *bolt.Tx) error { + b, err := tx.CreateBucketIfNotExists([]byte(mainBucket)) + if err != nil { + return err + } + b, err = b.CreateBucketIfNotExists([]byte(id)) + if err != nil { + return err + } + return fn(b) + }) +} + +func (s *Store) Get(id string) (*StorageItem, bool) { + empty := func() *StorageItem { + si, _ := newStorageItem(id, nil, s) + return si + } + tx, err := s.db.Begin(false) + if err != nil { + return empty(), false + } + defer tx.Rollback() + b := tx.Bucket([]byte(mainBucket)) + if b == nil { + return empty(), false + } + b = b.Bucket([]byte(id)) + if b == nil { + return empty(), false + } + si, _ := newStorageItem(id, b, s) + return si, true +} + +func (s *Store) Close() error { + return s.db.Close() +} + +type StorageItem struct { + id string + values map[string]*Value + queue []func(*bolt.Bucket) error + storage *Store + mu sync.RWMutex +} + +func newStorageItem(id string, b *bolt.Bucket, s *Store) (*StorageItem, error) { + si := &StorageItem{ + id: id, + storage: s, + values: make(map[string]*Value), + } + if b != nil { + if err := b.ForEach(func(k, v []byte) error { + var sv Value + if len(v) > 0 { + if err := json.Unmarshal(v, &sv); err != nil { + return err + } + si.values[string(k)] = &sv + } + return nil + }); err != nil { + return si, err + } + } + return si, nil +} + +func (s *StorageItem) Storage() *Store { // TODO: used in local source. how to remove this? + return s.storage +} + +func (s *StorageItem) ID() string { + return s.id +} + +func (s *StorageItem) View(fn func(b *bolt.Bucket) error) error { + return s.storage.View(s.id, fn) +} + +func (s *StorageItem) Update(fn func(b *bolt.Bucket) error) error { + return s.storage.Update(s.id, fn) +} + +func (s *StorageItem) Keys() []string { + keys := make([]string, 0, len(s.values)) + for k := range s.values { + keys = append(keys, k) + } + return keys +} + +func (s *StorageItem) Get(k string) *Value { + s.mu.RLock() + v := s.values[k] + s.mu.RUnlock() + return v +} + +func (s *StorageItem) GetExternal(k string) ([]byte, error) { + var dt []byte + err := s.storage.db.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(externalBucket)) + if b == nil { + return errors.WithStack(errNotFound) + } + b = b.Bucket([]byte(s.id)) + if b == nil { + return errors.WithStack(errNotFound) + } + dt = b.Get([]byte(k)) + if dt == nil { + return errors.WithStack(errNotFound) + } + return nil + }) + if err != nil { + return nil, err + } + return dt, nil +} + +func (s *StorageItem) SetExternal(k string, dt []byte) error { + return s.storage.db.Update(func(tx *bolt.Tx) error { + b, err := tx.CreateBucketIfNotExists([]byte(externalBucket)) + if err != nil { + return err + } + b, err = b.CreateBucketIfNotExists([]byte(s.id)) + if err != nil { + return err + } + return b.Put([]byte(k), dt) + }) +} + +func (s *StorageItem) Queue(fn func(b *bolt.Bucket) error) { + s.mu.Lock() + defer s.mu.Unlock() + s.queue = append(s.queue, fn) +} + +func (s *StorageItem) Commit() error { + s.mu.Lock() + defer s.mu.Unlock() + return s.Update(func(b *bolt.Bucket) error { + for _, fn := range s.queue { + if err := fn(b); err != nil { + return err + } + } + s.queue = s.queue[:0] + return nil + }) +} + +func (s *StorageItem) Indexes() (out []string) { + for _, v := range s.values { + if v.Index != "" { + out = append(out, v.Index) + } + } + return +} + +func (s *StorageItem) SetValue(b *bolt.Bucket, key string, v *Value) error { + if v == nil { + if err := b.Put([]byte(key), nil); err != nil { + return err + } + delete(s.values, key) + return nil + } + dt, err := json.Marshal(v) + if err != nil { + return err + } + if err := b.Put([]byte(key), dt); err != nil { + return err + } + if v.Index != "" { + b, err := b.Tx().CreateBucketIfNotExists([]byte(indexBucket)) + if err != nil { + return err + } + if err := b.Put([]byte(indexKey(v.Index, s.ID())), []byte{}); err != nil { + return err + } + } + s.values[key] = v + return nil +} + +type Value struct { + Value json.RawMessage `json:"value,omitempty"` + Index string `json:"index,omitempty"` +} + +func NewValue(v interface{}) (*Value, error) { + dt, err := json.Marshal(v) + if err != nil { + return nil, err + } + return &Value{Value: json.RawMessage(dt)}, nil +} + +func (v *Value) Unmarshal(target interface{}) error { + err := json.Unmarshal(v.Value, target) + return err +} + +func indexKey(index, target string) string { + return index + "::" + target +} diff --git a/vendor/github.com/moby/buildkit/cache/refs.go b/vendor/github.com/moby/buildkit/cache/refs.go new file mode 100644 index 0000000000000..af92b80977329 --- /dev/null +++ b/vendor/github.com/moby/buildkit/cache/refs.go @@ -0,0 +1,387 @@ +package cache + +import ( + "context" + "sync" + + "github.com/containerd/containerd/mount" + "github.com/moby/buildkit/cache/metadata" + "github.com/moby/buildkit/identity" + "github.com/moby/buildkit/snapshot" + "github.com/moby/buildkit/util/flightcontrol" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// Ref is a reference to cacheable objects. +type Ref interface { + Mountable + ID() string + Release(context.Context) error + Size(ctx context.Context) (int64, error) + Metadata() *metadata.StorageItem +} + +type ImmutableRef interface { + Ref + Parent() ImmutableRef + Finalize(ctx context.Context, commit bool) error // Make sure reference is flushed to driver + Clone() ImmutableRef +} + +type MutableRef interface { + Ref + Commit(context.Context) (ImmutableRef, error) +} + +type Mountable interface { + Mount(ctx context.Context, readonly bool) (snapshot.Mountable, error) +} + +type cacheRecord struct { + cm *cacheManager + mu *sync.Mutex // the mutex is shared by records sharing data + + mutable bool + refs map[Mountable]struct{} + parent ImmutableRef + md *metadata.StorageItem + + // dead means record is marked as deleted + dead bool + + view string + viewMount snapshot.Mountable + + sizeG flightcontrol.Group + + // these are filled if multiple refs point to same data + equalMutable *mutableRef + equalImmutable *immutableRef +} + +// hold ref lock before calling +func (cr *cacheRecord) ref() *immutableRef { + ref := &immutableRef{cacheRecord: cr} + cr.refs[ref] = struct{}{} + return ref +} + +// hold ref lock before calling +func (cr *cacheRecord) mref() *mutableRef { + ref := &mutableRef{cacheRecord: cr} + cr.refs[ref] = struct{}{} + return ref +} + +// hold ref lock before calling +func (cr *cacheRecord) isDead() bool { + return cr.dead || (cr.equalImmutable != nil && cr.equalImmutable.dead) || (cr.equalMutable != nil && cr.equalMutable.dead) +} + +func (cr *cacheRecord) Size(ctx context.Context) (int64, error) { + // this expects that usage() is implemented lazily + s, err := cr.sizeG.Do(ctx, cr.ID(), func(ctx context.Context) (interface{}, error) { + cr.mu.Lock() + s := getSize(cr.md) + if s != sizeUnknown { + cr.mu.Unlock() + return s, nil + } + driverID := cr.ID() + if cr.equalMutable != nil { + driverID = cr.equalMutable.ID() + } + cr.mu.Unlock() + usage, err := cr.cm.ManagerOpt.Snapshotter.Usage(ctx, driverID) + if err != nil { + cr.mu.Lock() + isDead := cr.isDead() + cr.mu.Unlock() + if isDead { + return int64(0), nil + } + return s, errors.Wrapf(err, "failed to get usage for %s", cr.ID()) + } + cr.mu.Lock() + setSize(cr.md, usage.Size) + if err := cr.md.Commit(); err != nil { + cr.mu.Unlock() + return s, err + } + cr.mu.Unlock() + return usage.Size, nil + }) + return s.(int64), err +} + +func (cr *cacheRecord) Parent() ImmutableRef { + if cr.parent == nil { + return nil + } + p := cr.parent.(*immutableRef) + p.mu.Lock() + defer p.mu.Unlock() + return p.ref() +} + +func (cr *cacheRecord) Mount(ctx context.Context, readonly bool) (snapshot.Mountable, error) { + cr.mu.Lock() + defer cr.mu.Unlock() + + if cr.mutable { + m, err := cr.cm.Snapshotter.Mounts(ctx, cr.ID()) + if err != nil { + return nil, errors.Wrapf(err, "failed to mount %s", cr.ID()) + } + if readonly { + m = setReadonly(m) + } + return m, nil + } + + if cr.equalMutable != nil && readonly { + m, err := cr.cm.Snapshotter.Mounts(ctx, cr.equalMutable.ID()) + if err != nil { + return nil, errors.Wrapf(err, "failed to mount %s", cr.equalMutable.ID()) + } + return setReadonly(m), nil + } + + if err := cr.finalize(ctx, true); err != nil { + return nil, err + } + if cr.viewMount == nil { // TODO: handle this better + cr.view = identity.NewID() + m, err := cr.cm.Snapshotter.View(ctx, cr.view, cr.ID()) + if err != nil { + cr.view = "" + return nil, errors.Wrapf(err, "failed to mount %s", cr.ID()) + } + cr.viewMount = m + } + return cr.viewMount, nil +} + +// call when holding the manager lock +func (cr *cacheRecord) remove(ctx context.Context, removeSnapshot bool) error { + delete(cr.cm.records, cr.ID()) + if cr.parent != nil { + if err := cr.parent.(*immutableRef).release(ctx); err != nil { + return err + } + } + if removeSnapshot { + if err := cr.cm.Snapshotter.Remove(ctx, cr.ID()); err != nil { + return err + } + } + if err := cr.cm.md.Clear(cr.ID()); err != nil { + return err + } + return nil +} + +func (cr *cacheRecord) ID() string { + return cr.md.ID() +} + +type immutableRef struct { + *cacheRecord +} + +type mutableRef struct { + *cacheRecord +} + +func (sr *immutableRef) Clone() ImmutableRef { + sr.mu.Lock() + ref := sr.ref() + sr.mu.Unlock() + return ref +} + +func (sr *immutableRef) Release(ctx context.Context) error { + sr.cm.mu.Lock() + defer sr.cm.mu.Unlock() + + sr.mu.Lock() + defer sr.mu.Unlock() + + return sr.release(ctx) +} + +func (sr *immutableRef) release(ctx context.Context) error { + delete(sr.refs, sr) + + if len(sr.refs) == 0 { + updateLastUsed(sr.md) + if sr.viewMount != nil { // TODO: release viewMount earlier if possible + if err := sr.cm.Snapshotter.Remove(ctx, sr.view); err != nil { + return err + } + sr.view = "" + sr.viewMount = nil + } + + if sr.equalMutable != nil { + sr.equalMutable.release(ctx) + } + // go sr.cm.GC() + } + + return nil +} + +func (sr *immutableRef) Finalize(ctx context.Context, b bool) error { + sr.mu.Lock() + defer sr.mu.Unlock() + + return sr.finalize(ctx, b) +} + +func (cr *cacheRecord) Metadata() *metadata.StorageItem { + return cr.md +} + +func (cr *cacheRecord) finalize(ctx context.Context, commit bool) error { + mutable := cr.equalMutable + if mutable == nil { + return nil + } + if !commit { + if HasCachePolicyRetain(mutable) { + CachePolicyRetain(mutable) + return mutable.Metadata().Commit() + } + return nil + } + err := cr.cm.Snapshotter.Commit(ctx, cr.ID(), mutable.ID()) + if err != nil { + return errors.Wrapf(err, "failed to commit %s", mutable.ID()) + } + mutable.dead = true + go func() { + cr.cm.mu.Lock() + defer cr.cm.mu.Unlock() + if err := mutable.remove(context.TODO(), false); err != nil { + logrus.Error(err) + } + }() + cr.equalMutable = nil + clearEqualMutable(cr.md) + return cr.md.Commit() +} + +func (sr *mutableRef) commit(ctx context.Context) (ImmutableRef, error) { + if !sr.mutable || len(sr.refs) == 0 { + return nil, errors.Wrapf(errInvalid, "invalid mutable ref") + } + + id := identity.NewID() + md, _ := sr.cm.md.Get(id) + + rec := &cacheRecord{ + mu: sr.mu, + cm: sr.cm, + parent: sr.Parent(), + equalMutable: sr, + refs: make(map[Mountable]struct{}), + md: md, + } + + if descr := GetDescription(sr.md); descr != "" { + if err := queueDescription(md, descr); err != nil { + return nil, err + } + } + + if err := initializeMetadata(rec); err != nil { + return nil, err + } + + sr.cm.records[id] = rec + + if err := sr.md.Commit(); err != nil { + return nil, err + } + + setSize(md, sizeUnknown) + setEqualMutable(md, sr.ID()) + if err := md.Commit(); err != nil { + return nil, err + } + + ref := rec.ref() + sr.equalImmutable = ref + return ref, nil +} + +func (sr *mutableRef) Commit(ctx context.Context) (ImmutableRef, error) { + sr.cm.mu.Lock() + defer sr.cm.mu.Unlock() + + sr.mu.Lock() + defer sr.mu.Unlock() + + return sr.commit(ctx) +} + +func (sr *mutableRef) Release(ctx context.Context) error { + sr.cm.mu.Lock() + defer sr.cm.mu.Unlock() + + sr.mu.Lock() + defer sr.mu.Unlock() + + return sr.release(ctx) +} + +func (sr *mutableRef) release(ctx context.Context) error { + delete(sr.refs, sr) + if getCachePolicy(sr.md) != cachePolicyRetain { + if sr.equalImmutable != nil { + if getCachePolicy(sr.equalImmutable.md) == cachePolicyRetain { + return nil + } + if err := sr.equalImmutable.remove(ctx, false); err != nil { + return err + } + } + if sr.parent != nil { + if err := sr.parent.(*immutableRef).release(ctx); err != nil { + return err + } + } + return sr.remove(ctx, true) + } else { + updateLastUsed(sr.md) + } + return nil +} + +func setReadonly(mounts snapshot.Mountable) snapshot.Mountable { + return &readOnlyMounter{mounts} +} + +type readOnlyMounter struct { + snapshot.Mountable +} + +func (m *readOnlyMounter) Mount() ([]mount.Mount, error) { + mounts, err := m.Mountable.Mount() + if err != nil { + return nil, err + } + for i, m := range mounts { + opts := make([]string, 0, len(m.Options)) + for _, opt := range m.Options { + if opt != "rw" { + opts = append(opts, opt) + } + } + opts = append(opts, "ro") + mounts[i].Options = opts + } + return mounts, nil +} diff --git a/vendor/github.com/moby/buildkit/cache/remotecache/export.go b/vendor/github.com/moby/buildkit/cache/remotecache/export.go new file mode 100644 index 0000000000000..e40b8de206173 --- /dev/null +++ b/vendor/github.com/moby/buildkit/cache/remotecache/export.go @@ -0,0 +1,128 @@ +package remotecache + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "time" + + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/images" + v1 "github.com/moby/buildkit/cache/remotecache/v1" + "github.com/moby/buildkit/solver" + "github.com/moby/buildkit/util/contentutil" + "github.com/moby/buildkit/util/progress" + 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" +) + +type ResolveCacheExporterFunc func(ctx context.Context, typ, target string) (Exporter, error) + +func oneOffProgress(ctx context.Context, id string) func(err error) error { + pw, _, _ := progress.FromContext(ctx) + now := time.Now() + st := progress.Status{ + Started: &now, + } + pw.Write(id, st) + return func(err error) error { + now := time.Now() + st.Completed = &now + pw.Write(id, st) + pw.Close() + return err + } +} + +type Exporter interface { + solver.CacheExporterTarget + Finalize(ctx context.Context) error +} + +type contentCacheExporter struct { + solver.CacheExporterTarget + chains *v1.CacheChains + ingester content.Ingester +} + +func NewExporter(ingester content.Ingester) Exporter { + cc := v1.NewCacheChains() + return &contentCacheExporter{CacheExporterTarget: cc, chains: cc, ingester: ingester} +} + +func (ce *contentCacheExporter) Finalize(ctx context.Context) error { + return export(ctx, ce.ingester, ce.chains) +} + +func export(ctx context.Context, ingester content.Ingester, cc *v1.CacheChains) error { + config, descs, err := cc.Marshal() + if err != nil { + return err + } + + // own type because oci type can't be pushed and docker type doesn't have annotations + type manifestList struct { + specs.Versioned + + MediaType string `json:"mediaType,omitempty"` + + // Manifests references platform specific manifests. + Manifests []ocispec.Descriptor `json:"manifests"` + } + + var mfst manifestList + mfst.SchemaVersion = 2 + mfst.MediaType = images.MediaTypeDockerSchema2ManifestList + + for _, l := range config.Layers { + dgstPair, ok := descs[l.Blob] + if !ok { + return errors.Errorf("missing blob %s", l.Blob) + } + layerDone := oneOffProgress(ctx, fmt.Sprintf("writing layer %s", l.Blob)) + if err := contentutil.Copy(ctx, ingester, dgstPair.Provider, dgstPair.Descriptor); err != nil { + return layerDone(errors.Wrap(err, "error writing layer blob")) + } + layerDone(nil) + mfst.Manifests = append(mfst.Manifests, dgstPair.Descriptor) + } + + dt, err := json.Marshal(config) + if err != nil { + return err + } + dgst := digest.FromBytes(dt) + desc := ocispec.Descriptor{ + Digest: dgst, + Size: int64(len(dt)), + MediaType: v1.CacheConfigMediaTypeV0, + } + configDone := oneOffProgress(ctx, fmt.Sprintf("writing config %s", dgst)) + if err := content.WriteBlob(ctx, ingester, dgst.String(), bytes.NewReader(dt), desc); err != nil { + return configDone(errors.Wrap(err, "error writing config blob")) + } + configDone(nil) + + mfst.Manifests = append(mfst.Manifests, desc) + + dt, err = json.Marshal(mfst) + if err != nil { + return errors.Wrap(err, "failed to marshal manifest") + } + dgst = digest.FromBytes(dt) + + desc = ocispec.Descriptor{ + Digest: dgst, + Size: int64(len(dt)), + MediaType: mfst.MediaType, + } + mfstDone := oneOffProgress(ctx, fmt.Sprintf("writing manifest %s", dgst)) + if err := content.WriteBlob(ctx, ingester, dgst.String(), bytes.NewReader(dt), desc); err != nil { + return mfstDone(errors.Wrap(err, "error writing manifest blob")) + } + mfstDone(nil) + return nil +} diff --git a/vendor/github.com/moby/buildkit/cache/remotecache/import.go b/vendor/github.com/moby/buildkit/cache/remotecache/import.go new file mode 100644 index 0000000000000..a76762e6fff5a --- /dev/null +++ b/vendor/github.com/moby/buildkit/cache/remotecache/import.go @@ -0,0 +1,98 @@ +package remotecache + +import ( + "context" + "encoding/json" + "io" + + "github.com/containerd/containerd/content" + v1 "github.com/moby/buildkit/cache/remotecache/v1" + "github.com/moby/buildkit/solver" + "github.com/moby/buildkit/worker" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +// ResolveCacheImporterFunc returns importer and descriptor. +// Currently typ needs to be an empty string. +type ResolveCacheImporterFunc func(ctx context.Context, typ, ref string) (Importer, ocispec.Descriptor, error) + +type Importer interface { + Resolve(ctx context.Context, desc ocispec.Descriptor, id string, w worker.Worker) (solver.CacheManager, error) +} + +func NewImporter(provider content.Provider) Importer { + return &contentCacheImporter{provider: provider} +} + +type contentCacheImporter struct { + provider content.Provider +} + +func (ci *contentCacheImporter) Resolve(ctx context.Context, desc ocispec.Descriptor, id string, w worker.Worker) (solver.CacheManager, error) { + dt, err := readBlob(ctx, ci.provider, desc) + if err != nil { + return nil, err + } + + var mfst ocispec.Index + if err := json.Unmarshal(dt, &mfst); err != nil { + return nil, err + } + + allLayers := v1.DescriptorProvider{} + + var configDesc ocispec.Descriptor + + for _, m := range mfst.Manifests { + if m.MediaType == v1.CacheConfigMediaTypeV0 { + configDesc = m + continue + } + allLayers[m.Digest] = v1.DescriptorProviderPair{ + Descriptor: m, + Provider: ci.provider, + } + } + + if configDesc.Digest == "" { + return nil, errors.Errorf("invalid build cache from %+v", desc) + } + + dt, err = readBlob(ctx, ci.provider, configDesc) + if err != nil { + return nil, err + } + + cc := v1.NewCacheChains() + if err := v1.Parse(dt, allLayers, cc); err != nil { + return nil, err + } + + keysStorage, resultStorage, err := v1.NewCacheKeyStorage(cc, w) + if err != nil { + return nil, err + } + return solver.NewCacheManager(id, keysStorage, resultStorage), nil +} + +func readBlob(ctx context.Context, provider content.Provider, desc ocispec.Descriptor) ([]byte, error) { + maxBlobSize := int64(1 << 20) + if desc.Size > maxBlobSize { + return nil, errors.Errorf("blob %s is too large (%d > %d)", desc.Digest, desc.Size, maxBlobSize) + } + dt, err := content.ReadBlob(ctx, provider, desc) + if err != nil { + // NOTE: even if err == EOF, we might have got expected dt here. + // For instance, http.Response.Body is known to return non-zero bytes with EOF. + if err == io.EOF { + if dtDigest := desc.Digest.Algorithm().FromBytes(dt); dtDigest != desc.Digest { + err = errors.Wrapf(err, "got EOF, expected %s (%d bytes), got %s (%d bytes)", + desc.Digest, desc.Size, dtDigest, len(dt)) + } else { + err = nil + } + } + } + return dt, err +} diff --git a/vendor/github.com/moby/buildkit/cache/remotecache/registry/registry.go b/vendor/github.com/moby/buildkit/cache/remotecache/registry/registry.go new file mode 100644 index 0000000000000..fa9dc1a7f2cb0 --- /dev/null +++ b/vendor/github.com/moby/buildkit/cache/remotecache/registry/registry.go @@ -0,0 +1,73 @@ +package registry + +import ( + "context" + "time" + + "github.com/containerd/containerd/remotes" + "github.com/containerd/containerd/remotes/docker" + "github.com/moby/buildkit/cache/remotecache" + "github.com/moby/buildkit/session" + "github.com/moby/buildkit/session/auth" + "github.com/moby/buildkit/util/contentutil" + "github.com/moby/buildkit/util/tracing" + specs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +func ResolveCacheExporterFunc(sm *session.Manager) remotecache.ResolveCacheExporterFunc { + return func(ctx context.Context, typ, ref string) (remotecache.Exporter, error) { + if typ != "" { + return nil, errors.Errorf("unsupported cache exporter type: %s", typ) + } + remote := newRemoteResolver(ctx, sm) + pusher, err := remote.Pusher(ctx, ref) + if err != nil { + return nil, err + } + return remotecache.NewExporter(contentutil.FromPusher(pusher)), nil + } +} + +func ResolveCacheImporterFunc(sm *session.Manager) remotecache.ResolveCacheImporterFunc { + return func(ctx context.Context, typ, ref string) (remotecache.Importer, specs.Descriptor, error) { + if typ != "" { + return nil, specs.Descriptor{}, errors.Errorf("unsupported cache importer type: %s", typ) + } + remote := newRemoteResolver(ctx, sm) + xref, desc, err := remote.Resolve(ctx, ref) + if err != nil { + return nil, specs.Descriptor{}, err + } + fetcher, err := remote.Fetcher(ctx, xref) + if err != nil { + return nil, specs.Descriptor{}, err + } + return remotecache.NewImporter(contentutil.FromFetcher(fetcher)), desc, nil + } +} + +func newRemoteResolver(ctx context.Context, sm *session.Manager) remotes.Resolver { + return docker.NewResolver(docker.ResolverOptions{ + Client: tracing.DefaultClient, + Credentials: getCredentialsFunc(ctx, sm), + }) +} + +func getCredentialsFunc(ctx context.Context, sm *session.Manager) func(string) (string, string, error) { + id := session.FromContext(ctx) + if id == "" { + return nil + } + return func(host string) (string, string, error) { + timeoutCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + caller, err := sm.Get(timeoutCtx, id) + if err != nil { + return "", "", err + } + + return auth.CredentialsFunc(context.TODO(), caller)(host) + } +} diff --git a/vendor/github.com/moby/buildkit/cache/remotecache/v1/cachestorage.go b/vendor/github.com/moby/buildkit/cache/remotecache/v1/cachestorage.go new file mode 100644 index 0000000000000..27b19587c8200 --- /dev/null +++ b/vendor/github.com/moby/buildkit/cache/remotecache/v1/cachestorage.go @@ -0,0 +1,247 @@ +package cacheimport + +import ( + "context" + + "github.com/moby/buildkit/identity" + "github.com/moby/buildkit/solver" + "github.com/moby/buildkit/worker" + digest "github.com/opencontainers/go-digest" + "github.com/pkg/errors" +) + +func NewCacheKeyStorage(cc *CacheChains, w worker.Worker) (solver.CacheKeyStorage, solver.CacheResultStorage, error) { + storage := &cacheKeyStorage{ + byID: map[string]*itemWithOutgoingLinks{}, + byItem: map[*item]string{}, + byResult: map[string]map[string]struct{}{}, + } + + for _, it := range cc.items { + if _, err := addItemToStorage(storage, it); err != nil { + return nil, nil, err + } + } + + results := &cacheResultStorage{ + w: w, + byID: storage.byID, + byResult: storage.byResult, + } + + return storage, results, nil +} + +func addItemToStorage(k *cacheKeyStorage, it *item) (*itemWithOutgoingLinks, error) { + if id, ok := k.byItem[it]; ok { + if id == "" { + return nil, errors.Errorf("invalid loop") + } + return k.byID[id], nil + } + + var id string + if len(it.links) == 0 { + id = it.dgst.String() + } else { + id = identity.NewID() + } + + k.byItem[it] = "" + + for i, m := range it.links { + for l := range m { + src, err := addItemToStorage(k, l.src) + if err != nil { + return nil, err + } + cl := nlink{ + input: i, + dgst: it.dgst, + selector: l.selector, + } + src.links[cl] = append(src.links[cl], id) + } + } + + k.byItem[it] = id + + itl := &itemWithOutgoingLinks{ + item: it, + links: map[nlink][]string{}, + } + + k.byID[id] = itl + + if res := it.result; res != nil { + resultID := remoteID(res) + ids, ok := k.byResult[resultID] + if !ok { + ids = map[string]struct{}{} + k.byResult[resultID] = ids + } + ids[id] = struct{}{} + } + return itl, nil +} + +type cacheKeyStorage struct { + byID map[string]*itemWithOutgoingLinks + byItem map[*item]string + byResult map[string]map[string]struct{} +} + +type itemWithOutgoingLinks struct { + *item + links map[nlink][]string +} + +func (cs *cacheKeyStorage) Exists(id string) bool { + _, ok := cs.byID[id] + return ok +} + +func (cs *cacheKeyStorage) Walk(func(id string) error) error { + return nil +} + +func (cs *cacheKeyStorage) WalkResults(id string, fn func(solver.CacheResult) error) error { + it, ok := cs.byID[id] + if !ok { + return nil + } + if res := it.result; res != nil { + return fn(solver.CacheResult{ID: remoteID(res), CreatedAt: it.resultTime}) + } + return nil +} + +func (cs *cacheKeyStorage) Load(id string, resultID string) (solver.CacheResult, error) { + it, ok := cs.byID[id] + if !ok { + return solver.CacheResult{}, nil + } + if res := it.result; res != nil { + return solver.CacheResult{ID: remoteID(res), CreatedAt: it.resultTime}, nil + } + return solver.CacheResult{}, nil +} + +func (cs *cacheKeyStorage) AddResult(id string, res solver.CacheResult) error { + return nil +} + +func (cs *cacheKeyStorage) Release(resultID string) error { + return nil +} +func (cs *cacheKeyStorage) AddLink(id string, link solver.CacheInfoLink, target string) error { + return nil +} +func (cs *cacheKeyStorage) WalkLinks(id string, link solver.CacheInfoLink, fn func(id string) error) error { + it, ok := cs.byID[id] + if !ok { + return nil + } + for _, id := range it.links[nlink{ + dgst: outputKey(link.Digest, int(link.Output)), + input: int(link.Input), + selector: link.Selector.String(), + }] { + if err := fn(id); err != nil { + return err + } + } + return nil +} + +// TODO: +func (cs *cacheKeyStorage) WalkBacklinks(id string, fn func(id string, link solver.CacheInfoLink) error) error { + return nil +} + +func (cs *cacheKeyStorage) WalkIDsByResult(id string, fn func(id string) error) error { + ids := cs.byResult[id] + for id := range ids { + if err := fn(id); err != nil { + return err + } + } + return nil +} + +func (cs *cacheKeyStorage) HasLink(id string, link solver.CacheInfoLink, target string) bool { + l := nlink{ + dgst: outputKey(link.Digest, int(link.Output)), + input: int(link.Input), + selector: link.Selector.String(), + } + if it, ok := cs.byID[id]; ok { + for _, id := range it.links[l] { + if id == target { + return true + } + } + } + return false +} + +type cacheResultStorage struct { + w worker.Worker + byID map[string]*itemWithOutgoingLinks + byResult map[string]map[string]struct{} +} + +func (cs *cacheResultStorage) Save(res solver.Result) (solver.CacheResult, error) { + return solver.CacheResult{}, errors.Errorf("importer is immutable") +} + +func (cs *cacheResultStorage) Load(ctx context.Context, res solver.CacheResult) (solver.Result, error) { + remote, err := cs.LoadRemote(ctx, res) + if err != nil { + return nil, err + } + + ref, err := cs.w.FromRemote(ctx, remote) + if err != nil { + return nil, err + } + return worker.NewWorkerRefResult(ref, cs.w), nil +} + +func (cs *cacheResultStorage) LoadRemote(ctx context.Context, res solver.CacheResult) (*solver.Remote, error) { + if r := cs.byResultID(res.ID); r != nil { + return r, nil + } + return nil, errors.WithStack(solver.ErrNotFound) +} + +func (cs *cacheResultStorage) Exists(id string) bool { + return cs.byResultID(id) != nil +} + +func (cs *cacheResultStorage) byResultID(resultID string) *solver.Remote { + m, ok := cs.byResult[resultID] + if !ok || len(m) == 0 { + return nil + } + + for id := range m { + it, ok := cs.byID[id] + if ok { + if r := it.result; r != nil { + return r + } + } + } + + return nil +} + +// unique ID per remote. this ID is not stable. +func remoteID(r *solver.Remote) string { + dgstr := digest.Canonical.Digester() + for _, desc := range r.Descriptors { + dgstr.Hash().Write([]byte(desc.Digest)) + } + return dgstr.Digest().String() +} diff --git a/vendor/github.com/moby/buildkit/cache/remotecache/v1/chains.go b/vendor/github.com/moby/buildkit/cache/remotecache/v1/chains.go new file mode 100644 index 0000000000000..52806b9c4498d --- /dev/null +++ b/vendor/github.com/moby/buildkit/cache/remotecache/v1/chains.go @@ -0,0 +1,127 @@ +package cacheimport + +import ( + "time" + + "github.com/containerd/containerd/content" + "github.com/moby/buildkit/solver" + digest "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" +) + +func NewCacheChains() *CacheChains { + return &CacheChains{visited: map[interface{}]struct{}{}} +} + +type CacheChains struct { + items []*item + visited map[interface{}]struct{} +} + +func (c *CacheChains) Add(dgst digest.Digest) solver.CacheExporterRecord { + it := &item{c: c, dgst: dgst} + c.items = append(c.items, it) + return it +} + +func (c *CacheChains) Visit(v interface{}) { + c.visited[v] = struct{}{} +} + +func (c *CacheChains) Visited(v interface{}) bool { + _, ok := c.visited[v] + return ok +} + +func (c *CacheChains) normalize() error { + st := &normalizeState{ + added: map[*item]*item{}, + links: map[*item]map[nlink]map[digest.Digest]struct{}{}, + byKey: map[digest.Digest]*item{}, + } + + for _, it := range c.items { + _, err := normalizeItem(it, st) + if err != nil { + return err + } + } + + items := make([]*item, 0, len(st.byKey)) + for _, it := range st.byKey { + items = append(items, it) + } + c.items = items + return nil +} + +func (c *CacheChains) Marshal() (*CacheConfig, DescriptorProvider, error) { + if err := c.normalize(); err != nil { + return nil, nil, err + } + + st := &marshalState{ + chainsByID: map[string]int{}, + descriptors: DescriptorProvider{}, + recordsByItem: map[*item]int{}, + } + + for _, it := range c.items { + if err := marshalItem(it, st); err != nil { + return nil, nil, err + } + } + + cc := CacheConfig{ + Layers: st.layers, + Records: st.records, + } + sortConfig(&cc) + + return &cc, st.descriptors, nil +} + +type DescriptorProvider map[digest.Digest]DescriptorProviderPair + +type DescriptorProviderPair struct { + Descriptor ocispec.Descriptor + Provider content.Provider +} + +type item struct { + c *CacheChains + dgst digest.Digest + + result *solver.Remote + resultTime time.Time + + links []map[link]struct{} +} + +type link struct { + src *item + selector string +} + +func (c *item) AddResult(createdAt time.Time, result *solver.Remote) { + c.resultTime = createdAt + c.result = result +} + +func (c *item) LinkFrom(rec solver.CacheExporterRecord, index int, selector string) { + src, ok := rec.(*item) + if !ok { + return + } + + for { + if index < len(c.links) { + break + } + c.links = append(c.links, map[link]struct{}{}) + } + + c.links[index][link{src: src, selector: selector}] = struct{}{} +} + +var _ solver.CacheExporterTarget = &CacheChains{} diff --git a/vendor/github.com/moby/buildkit/cache/remotecache/v1/doc.go b/vendor/github.com/moby/buildkit/cache/remotecache/v1/doc.go new file mode 100644 index 0000000000000..4cff811490bfb --- /dev/null +++ b/vendor/github.com/moby/buildkit/cache/remotecache/v1/doc.go @@ -0,0 +1,50 @@ +package cacheimport + +// Distibutable build cache +// +// Main manifest is OCI image index +// https://github.com/opencontainers/image-spec/blob/master/image-index.md . +// Manifests array contains descriptors to the cache layers and one instance of +// build cache config with media type application/vnd.buildkit.cacheconfig.v0 . +// The cache layer descripts need to have an annotation with uncompressed digest +// to allow deduplication on extraction and optionally "buildkit/createdat" +// annotation to support maintaining original timestamps. +// +// Cache config file layout: +// +//{ +// "layers": [ +// { +// "blob": "sha256:deadbeef", <- digest of layer blob in index +// "parent": -1 <- index of parent layer, -1 if no parent +// }, +// { +// "blob": "sha256:deadbeef", +// "parent": 0 +// } +// ], +// +// "records": [ +// { +// "digest": "sha256:deadbeef", <- base digest for the record +// }, +// { +// "digest": "sha256:deadbeef", +// "output": 1, <- optional output index +// "layers": [ <- optional array or layer chains +// { +// "createdat": "", +// "layer": 1, <- index to the layer +// } +// ], +// "inputs": [ <- dependant records +// [ <- index of the dependency (0) +// { +// "selector": "sel", <- optional selector +// "link": 0, <- index to the dependant record +// } +// ] +// ] +// } +// ] +// } diff --git a/vendor/github.com/moby/buildkit/cache/remotecache/v1/parse.go b/vendor/github.com/moby/buildkit/cache/remotecache/v1/parse.go new file mode 100644 index 0000000000000..8aa6929ea05b7 --- /dev/null +++ b/vendor/github.com/moby/buildkit/cache/remotecache/v1/parse.go @@ -0,0 +1,102 @@ +package cacheimport + +import ( + "encoding/json" + + "github.com/moby/buildkit/solver" + "github.com/moby/buildkit/util/contentutil" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +func Parse(configJSON []byte, provider DescriptorProvider, t solver.CacheExporterTarget) error { + var config CacheConfig + if err := json.Unmarshal(configJSON, &config); err != nil { + return err + } + + cache := map[int]solver.CacheExporterRecord{} + + for i := range config.Records { + if _, err := parseRecord(config, i, provider, t, cache); err != nil { + return err + } + } + + return nil +} + +func parseRecord(cc CacheConfig, idx int, provider DescriptorProvider, t solver.CacheExporterTarget, cache map[int]solver.CacheExporterRecord) (solver.CacheExporterRecord, error) { + if r, ok := cache[idx]; ok { + if r == nil { + return nil, errors.Errorf("invalid looping record") + } + return r, nil + } + + if idx < 0 || idx >= len(cc.Records) { + return nil, errors.Errorf("invalid record ID: %d", idx) + } + rec := cc.Records[idx] + + r := t.Add(rec.Digest) + cache[idx] = nil + for i, inputs := range rec.Inputs { + for _, inp := range inputs { + src, err := parseRecord(cc, inp.LinkIndex, provider, t, cache) + if err != nil { + return nil, err + } + r.LinkFrom(src, i, inp.Selector) + } + } + + for _, res := range rec.Results { + visited := map[int]struct{}{} + remote, err := getRemoteChain(cc.Layers, res.LayerIndex, provider, visited) + if err != nil { + return nil, err + } + r.AddResult(res.CreatedAt, remote) + } + + cache[idx] = r + return r, nil +} + +func getRemoteChain(layers []CacheLayer, idx int, provider DescriptorProvider, visited map[int]struct{}) (*solver.Remote, error) { + if _, ok := visited[idx]; ok { + return nil, errors.Errorf("invalid looping layer") + } + visited[idx] = struct{}{} + + if idx < 0 || idx >= len(layers) { + return nil, errors.Errorf("invalid layer index %d", idx) + } + + l := layers[idx] + + descPair, ok := provider[l.Blob] + if !ok { + return nil, errors.Errorf("missing blob for %s", l.Blob) + } + + var r *solver.Remote + if l.ParentIndex != -1 { + var err error + r, err = getRemoteChain(layers, l.ParentIndex, provider, visited) + if err != nil { + return nil, err + } + r.Descriptors = append(r.Descriptors, descPair.Descriptor) + mp := contentutil.NewMultiProvider(r.Provider) + mp.Add(descPair.Descriptor.Digest, descPair.Provider) + r.Provider = mp + return r, nil + } + return &solver.Remote{ + Descriptors: []ocispec.Descriptor{descPair.Descriptor}, + Provider: descPair.Provider, + }, nil + +} diff --git a/vendor/github.com/moby/buildkit/cache/remotecache/v1/spec.go b/vendor/github.com/moby/buildkit/cache/remotecache/v1/spec.go new file mode 100644 index 0000000000000..4c6bc0bb26e53 --- /dev/null +++ b/vendor/github.com/moby/buildkit/cache/remotecache/v1/spec.go @@ -0,0 +1,35 @@ +package cacheimport + +import ( + "time" + + digest "github.com/opencontainers/go-digest" +) + +const CacheConfigMediaTypeV0 = "application/vnd.buildkit.cacheconfig.v0" + +type CacheConfig struct { + Layers []CacheLayer `json:"layers,omitempty"` + Records []CacheRecord `json:"records,omitempty"` +} + +type CacheLayer struct { + Blob digest.Digest `json:"blob,omitempty"` + ParentIndex int `json:"parent,omitempty"` +} + +type CacheRecord struct { + Results []CacheResult `json:"layers,omitempty"` + Digest digest.Digest `json:"digest,omitempty"` + Inputs [][]CacheInput `json:"inputs,omitempty"` +} + +type CacheResult struct { + LayerIndex int `json:"layer"` + CreatedAt time.Time `json:"createdAt,omitempty"` +} + +type CacheInput struct { + Selector string `json:"selector,omitempty"` + LinkIndex int `json:"link"` +} diff --git a/vendor/github.com/moby/buildkit/cache/remotecache/v1/utils.go b/vendor/github.com/moby/buildkit/cache/remotecache/v1/utils.go new file mode 100644 index 0000000000000..665eb330ca9d2 --- /dev/null +++ b/vendor/github.com/moby/buildkit/cache/remotecache/v1/utils.go @@ -0,0 +1,306 @@ +package cacheimport + +import ( + "fmt" + "sort" + + "github.com/containerd/containerd/content" + "github.com/moby/buildkit/solver" + digest "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +// sortConfig sorts the config structure to make sure it is deterministic +func sortConfig(cc *CacheConfig) { + type indexedLayer struct { + oldIndex int + newIndex int + l CacheLayer + } + + unsortedLayers := make([]*indexedLayer, len(cc.Layers)) + sortedLayers := make([]*indexedLayer, len(cc.Layers)) + + for i, l := range cc.Layers { + il := &indexedLayer{oldIndex: i, l: l} + unsortedLayers[i] = il + sortedLayers[i] = il + } + sort.Slice(sortedLayers, func(i, j int) bool { + li := sortedLayers[i].l + lj := sortedLayers[j].l + if li.Blob == lj.Blob { + return li.ParentIndex < lj.ParentIndex + } + return li.Blob < lj.Blob + }) + for i, l := range sortedLayers { + l.newIndex = i + } + + layers := make([]CacheLayer, len(sortedLayers)) + for i, l := range sortedLayers { + if pID := l.l.ParentIndex; pID != -1 { + l.l.ParentIndex = unsortedLayers[pID].newIndex + } + layers[i] = l.l + } + + type indexedRecord struct { + oldIndex int + newIndex int + r CacheRecord + } + + unsortedRecords := make([]*indexedRecord, len(cc.Records)) + sortedRecords := make([]*indexedRecord, len(cc.Records)) + + for i, r := range cc.Records { + ir := &indexedRecord{oldIndex: i, r: r} + unsortedRecords[i] = ir + sortedRecords[i] = ir + } + sort.Slice(sortedRecords, func(i, j int) bool { + ri := sortedRecords[i].r + rj := sortedRecords[j].r + if ri.Digest != rj.Digest { + return ri.Digest < rj.Digest + } + if len(ri.Inputs) != len(ri.Inputs) { + return len(ri.Inputs) < len(ri.Inputs) + } + for i, inputs := range ri.Inputs { + if len(ri.Inputs[i]) != len(rj.Inputs[i]) { + return len(ri.Inputs[i]) < len(rj.Inputs[i]) + } + for j := range inputs { + if ri.Inputs[i][j].Selector != rj.Inputs[i][j].Selector { + return ri.Inputs[i][j].Selector != rj.Inputs[i][j].Selector + } + return cc.Records[ri.Inputs[i][j].LinkIndex].Digest < cc.Records[rj.Inputs[i][j].LinkIndex].Digest + } + } + return ri.Digest < rj.Digest + }) + for i, l := range sortedRecords { + l.newIndex = i + } + + records := make([]CacheRecord, len(sortedRecords)) + for i, r := range sortedRecords { + for j := range r.r.Results { + r.r.Results[j].LayerIndex = unsortedLayers[r.r.Results[j].LayerIndex].newIndex + } + for j, inputs := range r.r.Inputs { + for k := range inputs { + r.r.Inputs[j][k].LinkIndex = unsortedRecords[r.r.Inputs[j][k].LinkIndex].newIndex + } + sort.Slice(inputs, func(i, j int) bool { + return inputs[i].LinkIndex < inputs[j].LinkIndex + }) + } + records[i] = r.r + } + + cc.Layers = layers + cc.Records = records +} + +func outputKey(dgst digest.Digest, idx int) digest.Digest { + return digest.FromBytes([]byte(fmt.Sprintf("%s@%d", dgst, idx))) +} + +type nlink struct { + dgst digest.Digest + input int + selector string +} +type normalizeState struct { + added map[*item]*item + links map[*item]map[nlink]map[digest.Digest]struct{} + byKey map[digest.Digest]*item + next int +} + +func normalizeItem(it *item, state *normalizeState) (*item, error) { + if it2, ok := state.added[it]; ok { + return it2, nil + } + + if len(it.links) == 0 { + id := it.dgst + if it2, ok := state.byKey[id]; ok { + state.added[it] = it2 + return it2, nil + } + state.byKey[id] = it + state.added[it] = it + return nil, nil + } + + matches := map[digest.Digest]struct{}{} + + // check if there is already a matching record + for i, m := range it.links { + if len(m) == 0 { + return nil, errors.Errorf("invalid incomplete links") + } + for l := range m { + nl := nlink{dgst: it.dgst, input: i, selector: l.selector} + it2, err := normalizeItem(l.src, state) + if err != nil { + return nil, err + } + links := state.links[it2][nl] + if i == 0 { + for id := range links { + matches[id] = struct{}{} + } + } else { + for id := range matches { + if _, ok := links[id]; !ok { + delete(matches, id) + } + } + } + } + } + + var id digest.Digest + + links := it.links + + if len(matches) > 0 { + for m := range matches { + if id == "" || id > m { + id = m + } + } + } else { + // keep tmp IDs deterministic + state.next++ + id = digest.FromBytes([]byte(fmt.Sprintf("%d", state.next))) + state.byKey[id] = it + it.links = make([]map[link]struct{}, len(it.links)) + for i := range it.links { + it.links[i] = map[link]struct{}{} + } + } + + it2 := state.byKey[id] + state.added[it] = it2 + + for i, m := range links { + for l := range m { + subIt, err := normalizeItem(l.src, state) + if err != nil { + return nil, err + } + it2.links[i][link{src: subIt, selector: l.selector}] = struct{}{} + + nl := nlink{dgst: it.dgst, input: i, selector: l.selector} + if _, ok := state.links[subIt]; !ok { + state.links[subIt] = map[nlink]map[digest.Digest]struct{}{} + } + if _, ok := state.links[subIt][nl]; !ok { + state.links[subIt][nl] = map[digest.Digest]struct{}{} + } + state.links[subIt][nl][id] = struct{}{} + } + } + + return it2, nil +} + +type marshalState struct { + layers []CacheLayer + chainsByID map[string]int + descriptors DescriptorProvider + + records []CacheRecord + recordsByItem map[*item]int +} + +func marshalRemote(r *solver.Remote, state *marshalState) string { + if len(r.Descriptors) == 0 { + return "" + } + type Remote struct { + Descriptors []ocispec.Descriptor + Provider content.Provider + } + var parentID string + if len(r.Descriptors) > 1 { + r2 := &solver.Remote{ + Descriptors: r.Descriptors[:len(r.Descriptors)-1], + Provider: r.Provider, + } + parentID = marshalRemote(r2, state) + } + desc := r.Descriptors[len(r.Descriptors)-1] + + state.descriptors[desc.Digest] = DescriptorProviderPair{ + Descriptor: desc, + Provider: r.Provider, + } + + id := desc.Digest.String() + parentID + + if _, ok := state.chainsByID[id]; ok { + return id + } + + state.chainsByID[id] = len(state.layers) + l := CacheLayer{ + Blob: desc.Digest, + ParentIndex: -1, + } + if parentID != "" { + l.ParentIndex = state.chainsByID[parentID] + } + state.layers = append(state.layers, l) + return id +} + +func marshalItem(it *item, state *marshalState) error { + if _, ok := state.recordsByItem[it]; ok { + return nil + } + + rec := CacheRecord{ + Digest: it.dgst, + Inputs: make([][]CacheInput, len(it.links)), + } + + for i, m := range it.links { + for l := range m { + if err := marshalItem(l.src, state); err != nil { + return err + } + idx, ok := state.recordsByItem[l.src] + if !ok { + return errors.Errorf("invalid source record: %v", l.src) + } + rec.Inputs[i] = append(rec.Inputs[i], CacheInput{ + Selector: l.selector, + LinkIndex: idx, + }) + } + } + + if it.result != nil { + id := marshalRemote(it.result, state) + if id != "" { + idx, ok := state.chainsByID[id] + if !ok { + return errors.Errorf("parent chainid not found") + } + rec.Results = append(rec.Results, CacheResult{LayerIndex: idx, CreatedAt: it.resultTime}) + } + } + + state.recordsByItem[it] = len(state.records) + state.records = append(state.records, rec) + return nil +} diff --git a/vendor/github.com/moby/buildkit/client/client.go b/vendor/github.com/moby/buildkit/client/client.go new file mode 100644 index 0000000000000..403ed52a8ce3c --- /dev/null +++ b/vendor/github.com/moby/buildkit/client/client.go @@ -0,0 +1,132 @@ +package client + +import ( + "context" + "crypto/tls" + "crypto/x509" + "io/ioutil" + + "github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc" + controlapi "github.com/moby/buildkit/api/services/control" + "github.com/moby/buildkit/util/appdefaults" + opentracing "github.com/opentracing/opentracing-go" + "github.com/pkg/errors" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" +) + +type Client struct { + conn *grpc.ClientConn +} + +type ClientOpt interface{} + +// New returns a new buildkit client. Address can be empty for the system-default address. +func New(ctx context.Context, address string, opts ...ClientOpt) (*Client, error) { + gopts := []grpc.DialOption{ + grpc.WithDialer(dialer), + grpc.FailOnNonTempDialError(true), + } + needWithInsecure := true + for _, o := range opts { + if _, ok := o.(*withBlockOpt); ok { + gopts = append(gopts, grpc.WithBlock(), grpc.FailOnNonTempDialError(true)) + } + if credInfo, ok := o.(*withCredentials); ok { + opt, err := loadCredentials(credInfo) + if err != nil { + return nil, err + } + gopts = append(gopts, opt) + needWithInsecure = false + } + if wt, ok := o.(*withTracer); ok { + gopts = append(gopts, + grpc.WithUnaryInterceptor(otgrpc.OpenTracingClientInterceptor(wt.tracer, otgrpc.LogPayloads())), + grpc.WithStreamInterceptor(otgrpc.OpenTracingStreamClientInterceptor(wt.tracer))) + } + } + if needWithInsecure { + gopts = append(gopts, grpc.WithInsecure()) + } + if address == "" { + address = appdefaults.Address + } + + conn, err := grpc.DialContext(ctx, address, gopts...) + if err != nil { + return nil, errors.Wrapf(err, "failed to dial %q . make sure buildkitd is running", address) + } + c := &Client{ + conn: conn, + } + return c, nil +} + +func (c *Client) controlClient() controlapi.ControlClient { + return controlapi.NewControlClient(c.conn) +} + +func (c *Client) Close() error { + return c.conn.Close() +} + +type withBlockOpt struct{} + +func WithBlock() ClientOpt { + return &withBlockOpt{} +} + +type withCredentials struct { + ServerName string + CACert string + Cert string + Key string +} + +// WithCredentials configures the TLS parameters of the client. +// Arguments: +// * serverName: specifies the name of the target server +// * ca: specifies the filepath of the CA certificate to use for verification +// * cert: specifies the filepath of the client certificate +// * key: specifies the filepath of the client key +func WithCredentials(serverName, ca, cert, key string) ClientOpt { + return &withCredentials{serverName, ca, cert, key} +} + +func loadCredentials(opts *withCredentials) (grpc.DialOption, error) { + ca, err := ioutil.ReadFile(opts.CACert) + if err != nil { + return nil, errors.Wrap(err, "could not read ca certificate") + } + + certPool := x509.NewCertPool() + if ok := certPool.AppendCertsFromPEM(ca); !ok { + return nil, errors.New("failed to append ca certs") + } + + cfg := &tls.Config{ + ServerName: opts.ServerName, + RootCAs: certPool, + } + + // we will produce an error if the user forgot about either cert or key if at least one is specified + if opts.Cert != "" || opts.Key != "" { + cert, err := tls.LoadX509KeyPair(opts.Cert, opts.Key) + if err != nil { + return nil, errors.Wrap(err, "could not read certificate/key") + } + cfg.Certificates = []tls.Certificate{cert} + cfg.BuildNameToCertificate() + } + + return grpc.WithTransportCredentials(credentials.NewTLS(cfg)), nil +} + +func WithTracer(t opentracing.Tracer) ClientOpt { + return &withTracer{t} +} + +type withTracer struct { + tracer opentracing.Tracer +} diff --git a/vendor/github.com/moby/buildkit/client/client_unix.go b/vendor/github.com/moby/buildkit/client/client_unix.go new file mode 100644 index 0000000000000..93afb956f1b0c --- /dev/null +++ b/vendor/github.com/moby/buildkit/client/client_unix.go @@ -0,0 +1,19 @@ +// +build !windows + +package client + +import ( + "net" + "strings" + "time" + + "github.com/pkg/errors" +) + +func dialer(address string, timeout time.Duration) (net.Conn, error) { + addrParts := strings.SplitN(address, "://", 2) + if len(addrParts) != 2 { + return nil, errors.Errorf("invalid address %s", address) + } + return net.DialTimeout(addrParts[0], addrParts[1], timeout) +} diff --git a/vendor/github.com/moby/buildkit/client/client_windows.go b/vendor/github.com/moby/buildkit/client/client_windows.go new file mode 100644 index 0000000000000..75905f520b188 --- /dev/null +++ b/vendor/github.com/moby/buildkit/client/client_windows.go @@ -0,0 +1,24 @@ +package client + +import ( + "net" + "strings" + "time" + + "github.com/Microsoft/go-winio" + "github.com/pkg/errors" +) + +func dialer(address string, timeout time.Duration) (net.Conn, error) { + addrParts := strings.SplitN(address, "://", 2) + if len(addrParts) != 2 { + return nil, errors.Errorf("invalid address %s", address) + } + switch addrParts[0] { + case "npipe": + address = strings.Replace(addrParts[1], "/", "\\", 0) + return winio.DialPipe(address, &timeout) + default: + return net.DialTimeout(addrParts[0], addrParts[1], timeout) + } +} diff --git a/vendor/github.com/moby/buildkit/client/diskusage.go b/vendor/github.com/moby/buildkit/client/diskusage.go new file mode 100644 index 0000000000000..5ed5043223ea5 --- /dev/null +++ b/vendor/github.com/moby/buildkit/client/diskusage.go @@ -0,0 +1,73 @@ +package client + +import ( + "context" + "sort" + "time" + + controlapi "github.com/moby/buildkit/api/services/control" + "github.com/pkg/errors" +) + +type UsageInfo struct { + ID string + Mutable bool + InUse bool + Size int64 + + CreatedAt time.Time + LastUsedAt *time.Time + UsageCount int + Parent string + Description string +} + +func (c *Client) DiskUsage(ctx context.Context, opts ...DiskUsageOption) ([]*UsageInfo, error) { + info := &DiskUsageInfo{} + for _, o := range opts { + o(info) + } + + req := &controlapi.DiskUsageRequest{Filter: info.Filter} + resp, err := c.controlClient().DiskUsage(ctx, req) + if err != nil { + return nil, errors.Wrap(err, "failed to call diskusage") + } + + var du []*UsageInfo + + for _, d := range resp.Record { + du = append(du, &UsageInfo{ + ID: d.ID, + Mutable: d.Mutable, + InUse: d.InUse, + Size: d.Size_, + Parent: d.Parent, + CreatedAt: d.CreatedAt, + Description: d.Description, + UsageCount: int(d.UsageCount), + LastUsedAt: d.LastUsedAt, + }) + } + + sort.Slice(du, func(i, j int) bool { + if du[i].Size == du[j].Size { + return du[i].ID > du[j].ID + } + return du[i].Size > du[j].Size + }) + + return du, nil +} + +type DiskUsageOption func(*DiskUsageInfo) + +type DiskUsageInfo struct { + Filter string +} + +func WithFilter(f string) DiskUsageOption { + return func(di *DiskUsageInfo) { + di.Filter = f + } +} diff --git a/vendor/github.com/moby/buildkit/client/exporters.go b/vendor/github.com/moby/buildkit/client/exporters.go new file mode 100644 index 0000000000000..4160d92a73f39 --- /dev/null +++ b/vendor/github.com/moby/buildkit/client/exporters.go @@ -0,0 +1,8 @@ +package client + +const ( + ExporterImage = "image" + ExporterLocal = "local" + ExporterOCI = "oci" + ExporterDocker = "docker" +) diff --git a/vendor/github.com/moby/buildkit/client/graph.go b/vendor/github.com/moby/buildkit/client/graph.go new file mode 100644 index 0000000000000..141a393cf9f75 --- /dev/null +++ b/vendor/github.com/moby/buildkit/client/graph.go @@ -0,0 +1,45 @@ +package client + +import ( + "time" + + digest "github.com/opencontainers/go-digest" +) + +type Vertex struct { + Digest digest.Digest + Inputs []digest.Digest + Name string + Started *time.Time + Completed *time.Time + Cached bool + Error string +} + +type VertexStatus struct { + ID string + Vertex digest.Digest + Name string + Total int64 + Current int64 + Timestamp time.Time + Started *time.Time + Completed *time.Time +} + +type VertexLog struct { + Vertex digest.Digest + Stream int + Data []byte + Timestamp time.Time +} + +type SolveStatus struct { + Vertexes []*Vertex + Statuses []*VertexStatus + Logs []*VertexLog +} + +type SolveResponse struct { + ExporterResponse map[string]string +} diff --git a/vendor/github.com/moby/buildkit/client/llb/exec.go b/vendor/github.com/moby/buildkit/client/llb/exec.go new file mode 100644 index 0000000000000..9dec170f0256d --- /dev/null +++ b/vendor/github.com/moby/buildkit/client/llb/exec.go @@ -0,0 +1,409 @@ +package llb + +import ( + _ "crypto/sha256" + "sort" + + "github.com/moby/buildkit/solver/pb" + digest "github.com/opencontainers/go-digest" + "github.com/pkg/errors" +) + +type Meta struct { + Args []string + Env EnvList + Cwd string + User string + ProxyEnv *ProxyEnv +} + +func NewExecOp(root Output, meta Meta, readOnly bool, c Constraints) *ExecOp { + e := &ExecOp{meta: meta, constraints: c} + rootMount := &mount{ + target: pb.RootMount, + source: root, + readonly: readOnly, + } + e.mounts = append(e.mounts, rootMount) + if readOnly { + e.root = root + } else { + o := &output{vertex: e, getIndex: e.getMountIndexFn(rootMount)} + if p := c.Platform; p != nil { + o.platform = p + } + e.root = o + } + rootMount.output = e.root + return e +} + +type mount struct { + target string + readonly bool + source Output + output Output + selector string + cacheID string + tmpfs bool + cacheSharing CacheMountSharingMode + // hasOutput bool +} + +type ExecOp struct { + MarshalCache + root Output + mounts []*mount + meta Meta + constraints Constraints + isValidated bool +} + +func (e *ExecOp) AddMount(target string, source Output, opt ...MountOption) Output { + m := &mount{ + target: target, + source: source, + } + for _, o := range opt { + o(m) + } + e.mounts = append(e.mounts, m) + if m.readonly { + m.output = source + } else if m.tmpfs { + m.output = &output{vertex: e, err: errors.Errorf("tmpfs mount for %s can't be used as a parent", target)} + } else { + o := &output{vertex: e, getIndex: e.getMountIndexFn(m)} + if p := e.constraints.Platform; p != nil { + o.platform = p + } + m.output = o + } + e.Store(nil, nil, nil) + e.isValidated = false + return m.output +} + +func (e *ExecOp) GetMount(target string) Output { + for _, m := range e.mounts { + if m.target == target { + return m.output + } + } + return nil +} + +func (e *ExecOp) Validate() error { + if e.isValidated { + return nil + } + if len(e.meta.Args) == 0 { + return errors.Errorf("arguments are required") + } + if e.meta.Cwd == "" { + return errors.Errorf("working directory is required") + } + for _, m := range e.mounts { + if m.source != nil { + if err := m.source.Vertex().Validate(); err != nil { + return err + } + } + } + e.isValidated = true + return nil +} + +func (e *ExecOp) Marshal(c *Constraints) (digest.Digest, []byte, *pb.OpMetadata, error) { + if e.Cached(c) { + return e.Load() + } + if err := e.Validate(); err != nil { + return "", nil, nil, err + } + // make sure mounts are sorted + sort.Slice(e.mounts, func(i, j int) bool { + return e.mounts[i].target < e.mounts[j].target + }) + + peo := &pb.ExecOp{ + Meta: &pb.Meta{ + Args: e.meta.Args, + Env: e.meta.Env.ToArray(), + Cwd: e.meta.Cwd, + User: e.meta.User, + }, + } + + if p := e.meta.ProxyEnv; p != nil { + peo.Meta.ProxyEnv = &pb.ProxyEnv{ + HttpProxy: p.HttpProxy, + HttpsProxy: p.HttpsProxy, + FtpProxy: p.FtpProxy, + NoProxy: p.NoProxy, + } + } + + pop, md := MarshalConstraints(c, &e.constraints) + pop.Op = &pb.Op_Exec{ + Exec: peo, + } + + outIndex := 0 + for _, m := range e.mounts { + inputIndex := pb.InputIndex(len(pop.Inputs)) + if m.source != nil { + if m.tmpfs { + return "", nil, nil, errors.Errorf("tmpfs mounts must use scratch") + } + inp, err := m.source.ToInput(c) + if err != nil { + return "", nil, nil, err + } + + newInput := true + + for i, inp2 := range pop.Inputs { + if *inp == *inp2 { + inputIndex = pb.InputIndex(i) + newInput = false + break + } + } + + if newInput { + pop.Inputs = append(pop.Inputs, inp) + } + } else { + inputIndex = pb.Empty + } + + outputIndex := pb.OutputIndex(-1) + if !m.readonly && m.cacheID == "" && !m.tmpfs { + outputIndex = pb.OutputIndex(outIndex) + outIndex++ + } + + pm := &pb.Mount{ + Input: inputIndex, + Dest: m.target, + Readonly: m.readonly, + Output: outputIndex, + Selector: m.selector, + } + if m.cacheID != "" { + pm.MountType = pb.MountType_CACHE + pm.CacheOpt = &pb.CacheOpt{ + ID: m.cacheID, + } + switch m.cacheSharing { + case CacheMountShared: + pm.CacheOpt.Sharing = pb.CacheSharingOpt_SHARED + case CacheMountPrivate: + pm.CacheOpt.Sharing = pb.CacheSharingOpt_PRIVATE + case CacheMountLocked: + pm.CacheOpt.Sharing = pb.CacheSharingOpt_LOCKED + } + } + if m.tmpfs { + pm.MountType = pb.MountType_TMPFS + } + peo.Mounts = append(peo.Mounts, pm) + } + + dt, err := pop.Marshal() + if err != nil { + return "", nil, nil, err + } + e.Store(dt, md, c) + return e.Load() +} + +func (e *ExecOp) Output() Output { + return e.root +} + +func (e *ExecOp) Inputs() (inputs []Output) { + mm := map[Output]struct{}{} + for _, m := range e.mounts { + if m.source != nil { + mm[m.source] = struct{}{} + } + } + for o := range mm { + inputs = append(inputs, o) + } + return +} + +func (e *ExecOp) getMountIndexFn(m *mount) func() (pb.OutputIndex, error) { + return func() (pb.OutputIndex, error) { + // make sure mounts are sorted + sort.Slice(e.mounts, func(i, j int) bool { + return e.mounts[i].target < e.mounts[j].target + }) + + i := 0 + for _, m2 := range e.mounts { + if m2.readonly || m2.cacheID != "" { + continue + } + if m == m2 { + return pb.OutputIndex(i), nil + } + i++ + } + return pb.OutputIndex(0), errors.Errorf("invalid mount: %s", m.target) + } +} + +type ExecState struct { + State + exec *ExecOp +} + +func (e ExecState) AddMount(target string, source State, opt ...MountOption) State { + return source.WithOutput(e.exec.AddMount(target, source.Output(), opt...)) +} + +func (e ExecState) GetMount(target string) State { + return NewState(e.exec.GetMount(target)) +} + +func (e ExecState) Root() State { + return e.State +} + +type MountOption func(*mount) + +func Readonly(m *mount) { + m.readonly = true +} + +func SourcePath(src string) MountOption { + return func(m *mount) { + m.selector = src + } +} + +func AsPersistentCacheDir(id string, sharing CacheMountSharingMode) MountOption { + return func(m *mount) { + m.cacheID = id + m.cacheSharing = sharing + } +} + +func Tmpfs() MountOption { + return func(m *mount) { + m.tmpfs = true + } +} + +type RunOption interface { + SetRunOption(es *ExecInfo) +} + +type runOptionFunc func(*ExecInfo) + +func (fn runOptionFunc) SetRunOption(ei *ExecInfo) { + fn(ei) +} + +func Shlex(str string) RunOption { + return Shlexf(str) +} +func Shlexf(str string, v ...interface{}) RunOption { + return runOptionFunc(func(ei *ExecInfo) { + ei.State = shlexf(str, v...)(ei.State) + }) +} + +func Args(a []string) RunOption { + return runOptionFunc(func(ei *ExecInfo) { + ei.State = args(a...)(ei.State) + }) +} + +func AddEnv(key, value string) RunOption { + return AddEnvf(key, value) +} + +func AddEnvf(key, value string, v ...interface{}) RunOption { + return runOptionFunc(func(ei *ExecInfo) { + ei.State = ei.State.AddEnvf(key, value, v...) + }) +} + +func User(str string) RunOption { + return runOptionFunc(func(ei *ExecInfo) { + ei.State = ei.State.User(str) + }) +} + +func Dir(str string) RunOption { + return Dirf(str) +} +func Dirf(str string, v ...interface{}) RunOption { + return runOptionFunc(func(ei *ExecInfo) { + ei.State = ei.State.Dirf(str, v...) + }) +} + +func Reset(s State) RunOption { + return runOptionFunc(func(ei *ExecInfo) { + ei.State = ei.State.Reset(s) + }) +} + +func With(so ...StateOption) RunOption { + return runOptionFunc(func(ei *ExecInfo) { + ei.State = ei.State.With(so...) + }) +} + +func AddMount(dest string, mountState State, opts ...MountOption) RunOption { + return runOptionFunc(func(ei *ExecInfo) { + ei.Mounts = append(ei.Mounts, MountInfo{dest, mountState.Output(), opts}) + }) +} + +func ReadonlyRootFS() RunOption { + return runOptionFunc(func(ei *ExecInfo) { + ei.ReadonlyRootFS = true + }) +} + +func WithProxy(ps ProxyEnv) RunOption { + return runOptionFunc(func(ei *ExecInfo) { + ei.ProxyEnv = &ps + }) +} + +type ExecInfo struct { + constraintsWrapper + State State + Mounts []MountInfo + ReadonlyRootFS bool + ProxyEnv *ProxyEnv +} + +type MountInfo struct { + Target string + Source Output + Opts []MountOption +} + +type ProxyEnv struct { + HttpProxy string + HttpsProxy string + FtpProxy string + NoProxy string +} + +type CacheMountSharingMode int + +const ( + CacheMountShared CacheMountSharingMode = iota + CacheMountPrivate + CacheMountLocked +) diff --git a/vendor/github.com/moby/buildkit/client/llb/imagemetaresolver/resolver.go b/vendor/github.com/moby/buildkit/client/llb/imagemetaresolver/resolver.go new file mode 100644 index 0000000000000..09d4aec1b1a1b --- /dev/null +++ b/vendor/github.com/moby/buildkit/client/llb/imagemetaresolver/resolver.go @@ -0,0 +1,92 @@ +package imagemetaresolver + +import ( + "context" + "net/http" + "sync" + + "github.com/containerd/containerd/remotes" + "github.com/containerd/containerd/remotes/docker" + "github.com/docker/docker/pkg/locker" + "github.com/moby/buildkit/client/llb" + "github.com/moby/buildkit/util/contentutil" + "github.com/moby/buildkit/util/imageutil" + digest "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/image-spec/specs-go/v1" +) + +var defaultImageMetaResolver llb.ImageMetaResolver +var defaultImageMetaResolverOnce sync.Once + +var WithDefault = llb.ImageOptionFunc(func(ii *llb.ImageInfo) { + llb.WithMetaResolver(Default()).SetImageOption(ii) +}) + +type imageMetaResolverOpts struct { + platform *specs.Platform +} + +type ImageMetaResolverOpt func(o *imageMetaResolverOpts) + +func WithDefaultPlatform(p *specs.Platform) ImageMetaResolverOpt { + return func(o *imageMetaResolverOpts) { + o.platform = p + } +} + +func New(with ...ImageMetaResolverOpt) llb.ImageMetaResolver { + var opts imageMetaResolverOpts + for _, f := range with { + f(&opts) + } + return &imageMetaResolver{ + resolver: docker.NewResolver(docker.ResolverOptions{ + Client: http.DefaultClient, + }), + platform: opts.platform, + buffer: contentutil.NewBuffer(), + cache: map[string]resolveResult{}, + locker: locker.New(), + } +} + +func Default() llb.ImageMetaResolver { + defaultImageMetaResolverOnce.Do(func() { + defaultImageMetaResolver = New() + }) + return defaultImageMetaResolver +} + +type imageMetaResolver struct { + resolver remotes.Resolver + buffer contentutil.Buffer + platform *specs.Platform + locker *locker.Locker + cache map[string]resolveResult +} + +type resolveResult struct { + config []byte + dgst digest.Digest +} + +func (imr *imageMetaResolver) ResolveImageConfig(ctx context.Context, ref string, platform *specs.Platform) (digest.Digest, []byte, error) { + imr.locker.Lock(ref) + defer imr.locker.Unlock(ref) + + if res, ok := imr.cache[ref]; ok { + return res.dgst, res.config, nil + } + + if platform == nil { + platform = imr.platform + } + + dgst, config, err := imageutil.Config(ctx, ref, imr.resolver, imr.buffer, platform) + if err != nil { + return "", nil, err + } + + imr.cache[ref] = resolveResult{dgst: dgst, config: config} + return dgst, config, nil +} diff --git a/vendor/github.com/moby/buildkit/client/llb/marshal.go b/vendor/github.com/moby/buildkit/client/llb/marshal.go new file mode 100644 index 0000000000000..65a352fae89c0 --- /dev/null +++ b/vendor/github.com/moby/buildkit/client/llb/marshal.go @@ -0,0 +1,112 @@ +package llb + +import ( + "io" + "io/ioutil" + + "github.com/containerd/containerd/platforms" + "github.com/moby/buildkit/solver/pb" + digest "github.com/opencontainers/go-digest" +) + +// Definition is the LLB definition structure with per-vertex metadata entries +// Corresponds to the Definition structure defined in solver/pb.Definition. +type Definition struct { + Def [][]byte + Metadata map[digest.Digest]pb.OpMetadata +} + +func (def *Definition) ToPB() *pb.Definition { + md := make(map[digest.Digest]pb.OpMetadata) + for k, v := range def.Metadata { + md[k] = v + } + return &pb.Definition{ + Def: def.Def, + Metadata: md, + } +} + +func (def *Definition) FromPB(x *pb.Definition) { + def.Def = x.Def + def.Metadata = make(map[digest.Digest]pb.OpMetadata) + for k, v := range x.Metadata { + def.Metadata[k] = v + } +} + +func WriteTo(def *Definition, w io.Writer) error { + b, err := def.ToPB().Marshal() + if err != nil { + return err + } + _, err = w.Write(b) + return err +} + +func ReadFrom(r io.Reader) (*Definition, error) { + b, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } + var pbDef pb.Definition + if err := pbDef.Unmarshal(b); err != nil { + return nil, err + } + var def Definition + def.FromPB(&pbDef) + return &def, nil +} + +func MarshalConstraints(base, override *Constraints) (*pb.Op, *pb.OpMetadata) { + c := *base + c.WorkerConstraints = append([]string{}, c.WorkerConstraints...) + + if p := override.Platform; p != nil { + c.Platform = p + } + + for _, wc := range override.WorkerConstraints { + c.WorkerConstraints = append(c.WorkerConstraints, wc) + } + + c.Metadata = mergeMetadata(c.Metadata, override.Metadata) + + if c.Platform == nil { + defaultPlatform := platforms.Normalize(platforms.DefaultSpec()) + c.Platform = &defaultPlatform + } + + return &pb.Op{ + Platform: &pb.Platform{ + OS: c.Platform.OS, + Architecture: c.Platform.Architecture, + Variant: c.Platform.Variant, + OSVersion: c.Platform.OSVersion, + OSFeatures: c.Platform.OSFeatures, + }, + Constraints: &pb.WorkerConstraints{ + Filter: c.WorkerConstraints, + }, + }, &c.Metadata +} + +type MarshalCache struct { + digest digest.Digest + dt []byte + md *pb.OpMetadata + constraints *Constraints +} + +func (mc *MarshalCache) Cached(c *Constraints) bool { + return mc.dt != nil && mc.constraints == c +} +func (mc *MarshalCache) Load() (digest.Digest, []byte, *pb.OpMetadata, error) { + return mc.digest, mc.dt, mc.md, nil +} +func (mc *MarshalCache) Store(dt []byte, md *pb.OpMetadata, c *Constraints) { + mc.digest = digest.FromBytes(dt) + mc.dt = dt + mc.md = md + mc.constraints = c +} diff --git a/vendor/github.com/moby/buildkit/client/llb/meta.go b/vendor/github.com/moby/buildkit/client/llb/meta.go new file mode 100644 index 0000000000000..22aea097d482a --- /dev/null +++ b/vendor/github.com/moby/buildkit/client/llb/meta.go @@ -0,0 +1,170 @@ +package llb + +import ( + "fmt" + "path" + + "github.com/containerd/containerd/platforms" + "github.com/google/shlex" + specs "github.com/opencontainers/image-spec/specs-go/v1" +) + +type contextKeyT string + +var ( + keyArgs = contextKeyT("llb.exec.args") + keyDir = contextKeyT("llb.exec.dir") + keyEnv = contextKeyT("llb.exec.env") + keyUser = contextKeyT("llb.exec.user") + keyPlatform = contextKeyT("llb.platform") +) + +func addEnv(key, value string) StateOption { + return addEnvf(key, value) +} + +func addEnvf(key, value string, v ...interface{}) StateOption { + return func(s State) State { + return s.WithValue(keyEnv, getEnv(s).AddOrReplace(key, fmt.Sprintf(value, v...))) + } +} + +func dir(str string) StateOption { + return dirf(str) +} + +func dirf(str string, v ...interface{}) StateOption { + return func(s State) State { + value := fmt.Sprintf(str, v...) + if !path.IsAbs(value) { + prev := getDir(s) + if prev == "" { + prev = "/" + } + value = path.Join(prev, value) + } + return s.WithValue(keyDir, value) + } +} + +func user(str string) StateOption { + return func(s State) State { + return s.WithValue(keyUser, str) + } +} + +func reset(s_ State) StateOption { + return func(s State) State { + s = NewState(s.Output()) + s.ctx = s_.ctx + return s + } +} + +func getEnv(s State) EnvList { + v := s.Value(keyEnv) + if v != nil { + return v.(EnvList) + } + return EnvList{} +} + +func getDir(s State) string { + v := s.Value(keyDir) + if v != nil { + return v.(string) + } + return "" +} + +func getArgs(s State) []string { + v := s.Value(keyArgs) + if v != nil { + return v.([]string) + } + return nil +} + +func getUser(s State) string { + v := s.Value(keyUser) + if v != nil { + return v.(string) + } + return "" +} + +func args(args ...string) StateOption { + return func(s State) State { + return s.WithValue(keyArgs, args) + } +} + +func shlexf(str string, v ...interface{}) StateOption { + return func(s State) State { + arg, err := shlex.Split(fmt.Sprintf(str, v...)) + if err != nil { + // TODO: handle error + } + return args(arg...)(s) + } +} + +func platform(p specs.Platform) StateOption { + return func(s State) State { + return s.WithValue(keyPlatform, platforms.Normalize(p)) + } +} + +func getPlatform(s State) *specs.Platform { + v := s.Value(keyPlatform) + if v != nil { + p := v.(specs.Platform) + return &p + } + return nil +} + +type EnvList []KeyValue + +type KeyValue struct { + key string + value string +} + +func (e EnvList) AddOrReplace(k, v string) EnvList { + e = e.Delete(k) + e = append(e, KeyValue{key: k, value: v}) + return e +} + +func (e EnvList) Delete(k string) EnvList { + e = append([]KeyValue(nil), e...) + if i, ok := e.Index(k); ok { + return append(e[:i], e[i+1:]...) + } + return e +} + +func (e EnvList) Get(k string) (string, bool) { + if index, ok := e.Index(k); ok { + return e[index].value, true + } + return "", false +} + +func (e EnvList) Index(k string) (int, bool) { + for i, kv := range e { + if kv.key == k { + return i, true + } + } + return -1, false +} + +func (e EnvList) ToArray() []string { + out := make([]string, 0, len(e)) + for _, kv := range e { + out = append(out, kv.key+"="+kv.value) + } + return out +} diff --git a/vendor/github.com/moby/buildkit/client/llb/resolver.go b/vendor/github.com/moby/buildkit/client/llb/resolver.go new file mode 100644 index 0000000000000..b539b1159e83e --- /dev/null +++ b/vendor/github.com/moby/buildkit/client/llb/resolver.go @@ -0,0 +1,18 @@ +package llb + +import ( + "context" + + digest "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/image-spec/specs-go/v1" +) + +func WithMetaResolver(mr ImageMetaResolver) ImageOption { + return ImageOptionFunc(func(ii *ImageInfo) { + ii.metaResolver = mr + }) +} + +type ImageMetaResolver interface { + ResolveImageConfig(ctx context.Context, ref string, platform *specs.Platform) (digest.Digest, []byte, error) +} diff --git a/vendor/github.com/moby/buildkit/client/llb/source.go b/vendor/github.com/moby/buildkit/client/llb/source.go new file mode 100644 index 0000000000000..5b4aa9bf2e4d6 --- /dev/null +++ b/vendor/github.com/moby/buildkit/client/llb/source.go @@ -0,0 +1,363 @@ +package llb + +import ( + "context" + _ "crypto/sha256" + "encoding/json" + "os" + "strconv" + "strings" + + "github.com/docker/distribution/reference" + "github.com/moby/buildkit/solver/pb" + digest "github.com/opencontainers/go-digest" + "github.com/pkg/errors" +) + +type SourceOp struct { + MarshalCache + id string + attrs map[string]string + output Output + constraints Constraints + err error +} + +func NewSource(id string, attrs map[string]string, c Constraints) *SourceOp { + s := &SourceOp{ + id: id, + attrs: attrs, + constraints: c, + } + s.output = &output{vertex: s, platform: c.Platform} + return s +} + +func (s *SourceOp) Validate() error { + if s.err != nil { + return s.err + } + if s.id == "" { + return errors.Errorf("source identifier can't be empty") + } + return nil +} + +func (s *SourceOp) Marshal(constraints *Constraints) (digest.Digest, []byte, *pb.OpMetadata, error) { + if s.Cached(constraints) { + return s.Load() + } + if err := s.Validate(); err != nil { + return "", nil, nil, err + } + + if strings.HasPrefix(s.id, "local://") { + if _, hasSession := s.attrs[pb.AttrLocalSessionID]; !hasSession { + uid := s.constraints.LocalUniqueID + if uid == "" { + uid = constraints.LocalUniqueID + } + s.attrs[pb.AttrLocalUniqueID] = uid + } + } + proto, md := MarshalConstraints(constraints, &s.constraints) + + proto.Op = &pb.Op_Source{ + Source: &pb.SourceOp{Identifier: s.id, Attrs: s.attrs}, + } + dt, err := proto.Marshal() + if err != nil { + return "", nil, nil, err + } + + s.Store(dt, md, constraints) + return s.Load() +} + +func (s *SourceOp) Output() Output { + return s.output +} + +func (s *SourceOp) Inputs() []Output { + return nil +} + +func Image(ref string, opts ...ImageOption) State { + r, err := reference.ParseNormalizedNamed(ref) + if err == nil { + ref = reference.TagNameOnly(r).String() + } + var info ImageInfo + for _, opt := range opts { + opt.SetImageOption(&info) + } + src := NewSource("docker-image://"+ref, nil, info.Constraints) // controversial + if err != nil { + src.err = err + } + if info.metaResolver != nil { + _, dt, err := info.metaResolver.ResolveImageConfig(context.TODO(), ref, info.Constraints.Platform) + if err != nil { + src.err = err + } else { + var img struct { + Config struct { + Env []string `json:"Env,omitempty"` + WorkingDir string `json:"WorkingDir,omitempty"` + User string `json:"User,omitempty"` + } `json:"config,omitempty"` + } + if err := json.Unmarshal(dt, &img); err != nil { + src.err = err + } else { + st := NewState(src.Output()) + for _, env := range img.Config.Env { + parts := strings.SplitN(env, "=", 2) + if len(parts[0]) > 0 { + var v string + if len(parts) > 1 { + v = parts[1] + } + st = st.AddEnv(parts[0], v) + } + } + st = st.Dir(img.Config.WorkingDir) + return st + } + } + } + return NewState(src.Output()) +} + +type ImageOption interface { + SetImageOption(*ImageInfo) +} + +type ImageOptionFunc func(*ImageInfo) + +func (fn ImageOptionFunc) SetImageOption(ii *ImageInfo) { + fn(ii) +} + +type ImageInfo struct { + constraintsWrapper + metaResolver ImageMetaResolver +} + +func Git(remote, ref string, opts ...GitOption) State { + url := "" + + for _, prefix := range []string{ + "http://", "https://", "git://", "git@", + } { + if strings.HasPrefix(remote, prefix) { + url = strings.Split(remote, "#")[0] + remote = strings.TrimPrefix(remote, prefix) + } + } + + id := remote + + if ref != "" { + id += "#" + ref + } + + gi := &GitInfo{} + for _, o := range opts { + o.SetGitOption(gi) + } + attrs := map[string]string{} + if gi.KeepGitDir { + attrs[pb.AttrKeepGitDir] = "true" + } + if url != "" { + attrs[pb.AttrFullRemoteURL] = url + } + source := NewSource("git://"+id, attrs, gi.Constraints) + return NewState(source.Output()) +} + +type GitOption interface { + SetGitOption(*GitInfo) +} +type gitOptionFunc func(*GitInfo) + +func (fn gitOptionFunc) SetGitOption(gi *GitInfo) { + fn(gi) +} + +type GitInfo struct { + constraintsWrapper + KeepGitDir bool +} + +func KeepGitDir() GitOption { + return gitOptionFunc(func(gi *GitInfo) { + gi.KeepGitDir = true + }) +} + +func Scratch() State { + return NewState(nil) +} + +func Local(name string, opts ...LocalOption) State { + gi := &LocalInfo{} + + for _, o := range opts { + o.SetLocalOption(gi) + } + attrs := map[string]string{} + if gi.SessionID != "" { + attrs[pb.AttrLocalSessionID] = gi.SessionID + } + if gi.IncludePatterns != "" { + attrs[pb.AttrIncludePatterns] = gi.IncludePatterns + } + if gi.FollowPaths != "" { + attrs[pb.AttrFollowPaths] = gi.FollowPaths + } + if gi.ExcludePatterns != "" { + attrs[pb.AttrExcludePatterns] = gi.ExcludePatterns + } + if gi.SharedKeyHint != "" { + attrs[pb.AttrSharedKeyHint] = gi.SharedKeyHint + } + + source := NewSource("local://"+name, attrs, gi.Constraints) + return NewState(source.Output()) +} + +type LocalOption interface { + SetLocalOption(*LocalInfo) +} + +type localOptionFunc func(*LocalInfo) + +func (fn localOptionFunc) SetLocalOption(li *LocalInfo) { + fn(li) +} + +func SessionID(id string) LocalOption { + return localOptionFunc(func(li *LocalInfo) { + li.SessionID = id + }) +} + +func IncludePatterns(p []string) LocalOption { + return localOptionFunc(func(li *LocalInfo) { + if len(p) == 0 { + li.IncludePatterns = "" + return + } + dt, _ := json.Marshal(p) // empty on error + li.IncludePatterns = string(dt) + }) +} + +func FollowPaths(p []string) LocalOption { + return localOptionFunc(func(li *LocalInfo) { + if len(p) == 0 { + li.FollowPaths = "" + return + } + dt, _ := json.Marshal(p) // empty on error + li.FollowPaths = string(dt) + }) +} + +func ExcludePatterns(p []string) LocalOption { + return localOptionFunc(func(li *LocalInfo) { + if len(p) == 0 { + li.ExcludePatterns = "" + return + } + dt, _ := json.Marshal(p) // empty on error + li.ExcludePatterns = string(dt) + }) +} + +func SharedKeyHint(h string) LocalOption { + return localOptionFunc(func(li *LocalInfo) { + li.SharedKeyHint = h + }) +} + +type LocalInfo struct { + constraintsWrapper + SessionID string + IncludePatterns string + ExcludePatterns string + FollowPaths string + SharedKeyHint string +} + +func HTTP(url string, opts ...HTTPOption) State { + hi := &HTTPInfo{} + for _, o := range opts { + o.SetHTTPOption(hi) + } + attrs := map[string]string{} + if hi.Checksum != "" { + attrs[pb.AttrHTTPChecksum] = hi.Checksum.String() + } + if hi.Filename != "" { + attrs[pb.AttrHTTPFilename] = hi.Filename + } + if hi.Perm != 0 { + attrs[pb.AttrHTTPPerm] = "0" + strconv.FormatInt(int64(hi.Perm), 8) + } + if hi.UID != 0 { + attrs[pb.AttrHTTPUID] = strconv.Itoa(hi.UID) + } + if hi.UID != 0 { + attrs[pb.AttrHTTPGID] = strconv.Itoa(hi.GID) + } + + source := NewSource(url, attrs, hi.Constraints) + return NewState(source.Output()) +} + +type HTTPInfo struct { + constraintsWrapper + Checksum digest.Digest + Filename string + Perm int + UID int + GID int +} + +type HTTPOption interface { + SetHTTPOption(*HTTPInfo) +} + +type httpOptionFunc func(*HTTPInfo) + +func (fn httpOptionFunc) SetHTTPOption(hi *HTTPInfo) { + fn(hi) +} + +func Checksum(dgst digest.Digest) HTTPOption { + return httpOptionFunc(func(hi *HTTPInfo) { + hi.Checksum = dgst + }) +} + +func Chmod(perm os.FileMode) HTTPOption { + return httpOptionFunc(func(hi *HTTPInfo) { + hi.Perm = int(perm) & 0777 + }) +} + +func Filename(name string) HTTPOption { + return httpOptionFunc(func(hi *HTTPInfo) { + hi.Filename = name + }) +} + +func Chown(uid, gid int) HTTPOption { + return httpOptionFunc(func(hi *HTTPInfo) { + hi.UID = uid + hi.GID = gid + }) +} diff --git a/vendor/github.com/moby/buildkit/client/llb/state.go b/vendor/github.com/moby/buildkit/client/llb/state.go new file mode 100644 index 0000000000000..a0a98500083e9 --- /dev/null +++ b/vendor/github.com/moby/buildkit/client/llb/state.go @@ -0,0 +1,396 @@ +package llb + +import ( + "context" + + "github.com/containerd/containerd/platforms" + "github.com/moby/buildkit/identity" + "github.com/moby/buildkit/solver/pb" + "github.com/moby/buildkit/util/system" + digest "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/image-spec/specs-go/v1" +) + +type StateOption func(State) State + +type Output interface { + ToInput(*Constraints) (*pb.Input, error) + Vertex() Vertex +} + +type Vertex interface { + Validate() error + Marshal(*Constraints) (digest.Digest, []byte, *pb.OpMetadata, error) + Output() Output + Inputs() []Output +} + +func NewState(o Output) State { + s := State{ + out: o, + ctx: context.Background(), + } + s = dir("/")(s) + s = addEnv("PATH", system.DefaultPathEnv)(s) + s = s.ensurePlatform() + return s +} + +type State struct { + out Output + ctx context.Context + opts []ConstraintsOpt +} + +func (s State) ensurePlatform() State { + if o, ok := s.out.(interface { + Platform() *specs.Platform + }); ok { + if p := o.Platform(); p != nil { + s = platform(*p)(s) + } + } + return s +} + +func (s State) WithValue(k, v interface{}) State { + return State{ + out: s.out, + ctx: context.WithValue(s.ctx, k, v), + } +} + +func (s State) Value(k interface{}) interface{} { + return s.ctx.Value(k) +} + +func (s State) SetMarhalDefaults(co ...ConstraintsOpt) State { + s.opts = co + return s +} + +func (s State) Marshal(co ...ConstraintsOpt) (*Definition, error) { + def := &Definition{ + Metadata: make(map[digest.Digest]pb.OpMetadata, 0), + } + if s.Output() == nil { + return def, nil + } + + defaultPlatform := platforms.Normalize(platforms.DefaultSpec()) + c := &Constraints{ + Platform: &defaultPlatform, + LocalUniqueID: identity.NewID(), + } + for _, o := range append(s.opts, co...) { + o.SetConstraintsOption(c) + } + + def, err := marshal(s.Output().Vertex(), def, map[digest.Digest]struct{}{}, map[Vertex]struct{}{}, c) + if err != nil { + return def, err + } + inp, err := s.Output().ToInput(c) + if err != nil { + return def, err + } + proto := &pb.Op{Inputs: []*pb.Input{inp}} + dt, err := proto.Marshal() + if err != nil { + return def, err + } + def.Def = append(def.Def, dt) + return def, nil +} + +func marshal(v Vertex, def *Definition, cache map[digest.Digest]struct{}, vertexCache map[Vertex]struct{}, c *Constraints) (*Definition, error) { + if _, ok := vertexCache[v]; ok { + return def, nil + } + for _, inp := range v.Inputs() { + var err error + def, err = marshal(inp.Vertex(), def, cache, vertexCache, c) + if err != nil { + return def, err + } + } + + dgst, dt, opMeta, err := v.Marshal(c) + if err != nil { + return def, err + } + vertexCache[v] = struct{}{} + if opMeta != nil { + def.Metadata[dgst] = mergeMetadata(def.Metadata[dgst], *opMeta) + } + if _, ok := cache[dgst]; ok { + return def, nil + } + def.Def = append(def.Def, dt) + cache[dgst] = struct{}{} + return def, nil +} + +func (s State) Validate() error { + return s.Output().Vertex().Validate() +} + +func (s State) Output() Output { + return s.out +} + +func (s State) WithOutput(o Output) State { + s = State{ + out: o, + ctx: s.ctx, + } + s = s.ensurePlatform() + return s +} + +func (s State) Run(ro ...RunOption) ExecState { + ei := &ExecInfo{State: s} + if p := s.GetPlatform(); p != nil { + ei.Constraints.Platform = p + } + for _, o := range ro { + o.SetRunOption(ei) + } + meta := Meta{ + Args: getArgs(ei.State), + Cwd: getDir(ei.State), + Env: getEnv(ei.State), + User: getUser(ei.State), + ProxyEnv: ei.ProxyEnv, + } + + exec := NewExecOp(s.Output(), meta, ei.ReadonlyRootFS, ei.Constraints) + for _, m := range ei.Mounts { + exec.AddMount(m.Target, m.Source, m.Opts...) + } + + return ExecState{ + State: s.WithOutput(exec.Output()), + exec: exec, + } +} + +func (s State) AddEnv(key, value string) State { + return s.AddEnvf(key, value) +} + +func (s State) AddEnvf(key, value string, v ...interface{}) State { + return addEnvf(key, value, v...)(s) +} + +func (s State) Dir(str string) State { + return s.Dirf(str) +} +func (s State) Dirf(str string, v ...interface{}) State { + return dirf(str, v...)(s) +} + +func (s State) GetEnv(key string) (string, bool) { + return getEnv(s).Get(key) +} + +func (s State) GetDir() string { + return getDir(s) +} + +func (s State) GetArgs() []string { + return getArgs(s) +} + +func (s State) Reset(s2 State) State { + return reset(s2)(s) +} + +func (s State) User(v string) State { + return user(v)(s) +} + +func (s State) Platform(p specs.Platform) State { + return platform(p)(s) +} + +func (s State) GetPlatform() *specs.Platform { + return getPlatform(s) +} + +func (s State) With(so ...StateOption) State { + for _, o := range so { + s = o(s) + } + return s +} + +type output struct { + vertex Vertex + getIndex func() (pb.OutputIndex, error) + err error + platform *specs.Platform +} + +func (o *output) ToInput(c *Constraints) (*pb.Input, error) { + if o.err != nil { + return nil, o.err + } + var index pb.OutputIndex + if o.getIndex != nil { + var err error + index, err = o.getIndex() + if err != nil { + return nil, err + } + } + dgst, _, _, err := o.vertex.Marshal(c) + if err != nil { + return nil, err + } + return &pb.Input{Digest: dgst, Index: index}, nil +} + +func (o *output) Vertex() Vertex { + return o.vertex +} + +func (o *output) Platform() *specs.Platform { + return o.platform +} + +type ConstraintsOpt interface { + SetConstraintsOption(*Constraints) + RunOption + LocalOption + HTTPOption + ImageOption + GitOption +} + +type constraintsOptFunc func(m *Constraints) + +func (fn constraintsOptFunc) SetConstraintsOption(m *Constraints) { + fn(m) +} + +func (fn constraintsOptFunc) SetRunOption(ei *ExecInfo) { + ei.applyConstraints(fn) +} + +func (fn constraintsOptFunc) SetLocalOption(li *LocalInfo) { + li.applyConstraints(fn) +} + +func (fn constraintsOptFunc) SetHTTPOption(hi *HTTPInfo) { + hi.applyConstraints(fn) +} + +func (fn constraintsOptFunc) SetImageOption(ii *ImageInfo) { + ii.applyConstraints(fn) +} + +func (fn constraintsOptFunc) SetGitOption(gi *GitInfo) { + gi.applyConstraints(fn) +} + +func mergeMetadata(m1, m2 pb.OpMetadata) pb.OpMetadata { + if m2.IgnoreCache { + m1.IgnoreCache = true + } + if len(m2.Description) > 0 { + if m1.Description == nil { + m1.Description = make(map[string]string) + } + for k, v := range m2.Description { + m1.Description[k] = v + } + } + if m2.ExportCache != nil { + m1.ExportCache = m2.ExportCache + } + + return m1 +} + +var IgnoreCache = constraintsOptFunc(func(c *Constraints) { + c.Metadata.IgnoreCache = true +}) + +func WithDescription(m map[string]string) ConstraintsOpt { + return constraintsOptFunc(func(c *Constraints) { + c.Metadata.Description = m + }) +} + +// WithExportCache forces results for this vertex to be exported with the cache +func WithExportCache() ConstraintsOpt { + return constraintsOptFunc(func(c *Constraints) { + c.Metadata.ExportCache = &pb.ExportCache{Value: true} + }) +} + +// WithoutExportCache sets results for this vertex to be not exported with +// the cache +func WithoutExportCache() ConstraintsOpt { + return constraintsOptFunc(func(c *Constraints) { + // ExportCache with value false means to disable exporting + c.Metadata.ExportCache = &pb.ExportCache{Value: false} + }) +} + +// WithoutDefaultExportCache resets the cache export for the vertex to use +// the default defined by the build configuration. +func WithoutDefaultExportCache() ConstraintsOpt { + return constraintsOptFunc(func(c *Constraints) { + // nil means no vertex based config has been set + c.Metadata.ExportCache = nil + }) +} + +type constraintsWrapper struct { + Constraints +} + +func (cw *constraintsWrapper) applyConstraints(f func(c *Constraints)) { + f(&cw.Constraints) +} + +type Constraints struct { + Platform *specs.Platform + WorkerConstraints []string + Metadata pb.OpMetadata + LocalUniqueID string +} + +func Platform(p specs.Platform) ConstraintsOpt { + return constraintsOptFunc(func(c *Constraints) { + c.Platform = &p + }) +} + +func LocalUniqueID(v string) ConstraintsOpt { + return constraintsOptFunc(func(c *Constraints) { + c.LocalUniqueID = v + }) +} + +var ( + LinuxAmd64 = Platform(specs.Platform{OS: "linux", Architecture: "amd64"}) + LinuxArmhf = Platform(specs.Platform{OS: "linux", Architecture: "arm", Variant: "v7"}) + LinuxArm = LinuxArmhf + LinuxArmel = Platform(specs.Platform{OS: "linux", Architecture: "arm", Variant: "v6"}) + LinuxArm64 = Platform(specs.Platform{OS: "linux", Architecture: "arm64"}) + LinuxS390x = Platform(specs.Platform{OS: "linux", Architecture: "s390x"}) + LinuxPpc64le = Platform(specs.Platform{OS: "linux", Architecture: "ppc64le"}) + Darwin = Platform(specs.Platform{OS: "darwin", Architecture: "amd64"}) + Windows = Platform(specs.Platform{OS: "windows", Architecture: "amd64"}) +) + +func Require(filters ...string) ConstraintsOpt { + return constraintsOptFunc(func(c *Constraints) { + for _, f := range filters { + c.WorkerConstraints = append(c.WorkerConstraints, f) + } + }) +} diff --git a/vendor/github.com/moby/buildkit/client/prune.go b/vendor/github.com/moby/buildkit/client/prune.go new file mode 100644 index 0000000000000..b3c1edcd2b6d7 --- /dev/null +++ b/vendor/github.com/moby/buildkit/client/prune.go @@ -0,0 +1,50 @@ +package client + +import ( + "context" + "io" + + controlapi "github.com/moby/buildkit/api/services/control" + "github.com/pkg/errors" +) + +func (c *Client) Prune(ctx context.Context, ch chan UsageInfo, opts ...PruneOption) error { + info := &PruneInfo{} + for _, o := range opts { + o(info) + } + + req := &controlapi.PruneRequest{} + cl, err := c.controlClient().Prune(ctx, req) + if err != nil { + return errors.Wrap(err, "failed to call prune") + } + + for { + d, err := cl.Recv() + if err != nil { + if err == io.EOF { + return nil + } + return err + } + if ch != nil { + ch <- UsageInfo{ + ID: d.ID, + Mutable: d.Mutable, + InUse: d.InUse, + Size: d.Size_, + Parent: d.Parent, + CreatedAt: d.CreatedAt, + Description: d.Description, + UsageCount: int(d.UsageCount), + LastUsedAt: d.LastUsedAt, + } + } + } +} + +type PruneOption func(*PruneInfo) + +type PruneInfo struct { +} diff --git a/vendor/github.com/moby/buildkit/client/solve.go b/vendor/github.com/moby/buildkit/client/solve.go new file mode 100644 index 0000000000000..972b6b3ec92a6 --- /dev/null +++ b/vendor/github.com/moby/buildkit/client/solve.go @@ -0,0 +1,251 @@ +package client + +import ( + "context" + "io" + "os" + "path/filepath" + "strings" + "time" + + controlapi "github.com/moby/buildkit/api/services/control" + "github.com/moby/buildkit/client/llb" + "github.com/moby/buildkit/identity" + "github.com/moby/buildkit/session" + "github.com/moby/buildkit/session/filesync" + "github.com/moby/buildkit/session/grpchijack" + "github.com/moby/buildkit/solver/pb" + opentracing "github.com/opentracing/opentracing-go" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "golang.org/x/sync/errgroup" +) + +type SolveOpt struct { + Exporter string + ExporterAttrs map[string]string + ExporterOutput io.WriteCloser // for ExporterOCI and ExporterDocker + ExporterOutputDir string // for ExporterLocal + LocalDirs map[string]string + SharedKey string + Frontend string + FrontendAttrs map[string]string + ExportCache string + ExportCacheAttrs map[string]string + ImportCache []string + Session []session.Attachable +} + +// Solve calls Solve on the controller. +// def must be nil if (and only if) opt.Frontend is set. +func (c *Client) Solve(ctx context.Context, def *llb.Definition, opt SolveOpt, statusChan chan *SolveStatus) (*SolveResponse, error) { + defer func() { + if statusChan != nil { + close(statusChan) + } + }() + + if opt.Frontend == "" && def == nil { + return nil, errors.New("invalid empty definition") + } + if opt.Frontend != "" && def != nil { + return nil, errors.Errorf("invalid definition for frontend %s", opt.Frontend) + } + + syncedDirs, err := prepareSyncedDirs(def, opt.LocalDirs) + if err != nil { + return nil, err + } + + ref := identity.NewID() + eg, ctx := errgroup.WithContext(ctx) + + statusContext, cancelStatus := context.WithCancel(context.Background()) + defer cancelStatus() + + if span := opentracing.SpanFromContext(ctx); span != nil { + statusContext = opentracing.ContextWithSpan(statusContext, span) + } + + s, err := session.NewSession(statusContext, defaultSessionName(), opt.SharedKey) + if err != nil { + return nil, errors.Wrap(err, "failed to create session") + } + + if len(syncedDirs) > 0 { + s.Allow(filesync.NewFSSyncProvider(syncedDirs)) + } + + for _, a := range opt.Session { + s.Allow(a) + } + + switch opt.Exporter { + case ExporterLocal: + if opt.ExporterOutput != nil { + return nil, errors.New("output file writer is not supported by local exporter") + } + if opt.ExporterOutputDir == "" { + return nil, errors.New("output directory is required for local exporter") + } + s.Allow(filesync.NewFSSyncTargetDir(opt.ExporterOutputDir)) + case ExporterOCI, ExporterDocker: + if opt.ExporterOutputDir != "" { + return nil, errors.Errorf("output directory %s is not supported by %s exporter", opt.ExporterOutputDir, opt.Exporter) + } + if opt.ExporterOutput == nil { + return nil, errors.Errorf("output file writer is required for %s exporter", opt.Exporter) + } + s.Allow(filesync.NewFSSyncTarget(opt.ExporterOutput)) + default: + if opt.ExporterOutput != nil { + return nil, errors.Errorf("output file writer is not supported by %s exporter", opt.Exporter) + } + if opt.ExporterOutputDir != "" { + return nil, errors.Errorf("output directory %s is not supported by %s exporter", opt.ExporterOutputDir, opt.Exporter) + } + } + + eg.Go(func() error { + return s.Run(statusContext, grpchijack.Dialer(c.controlClient())) + }) + + var res *SolveResponse + eg.Go(func() error { + defer func() { // make sure the Status ends cleanly on build errors + go func() { + <-time.After(3 * time.Second) + cancelStatus() + }() + logrus.Debugf("stopping session") + s.Close() + }() + var pbd *pb.Definition + if def != nil { + pbd = def.ToPB() + } + resp, err := c.controlClient().Solve(ctx, &controlapi.SolveRequest{ + Ref: ref, + Definition: pbd, + Exporter: opt.Exporter, + ExporterAttrs: opt.ExporterAttrs, + Session: s.ID(), + Frontend: opt.Frontend, + FrontendAttrs: opt.FrontendAttrs, + Cache: controlapi.CacheOptions{ + ExportRef: opt.ExportCache, + ImportRefs: opt.ImportCache, + ExportAttrs: opt.ExportCacheAttrs, + }, + }) + if err != nil { + return errors.Wrap(err, "failed to solve") + } + res = &SolveResponse{ + ExporterResponse: resp.ExporterResponse, + } + return nil + }) + + eg.Go(func() error { + stream, err := c.controlClient().Status(statusContext, &controlapi.StatusRequest{ + Ref: ref, + }) + if err != nil { + return errors.Wrap(err, "failed to get status") + } + for { + resp, err := stream.Recv() + if err != nil { + if err == io.EOF { + return nil + } + return errors.Wrap(err, "failed to receive status") + } + s := SolveStatus{} + for _, v := range resp.Vertexes { + s.Vertexes = append(s.Vertexes, &Vertex{ + Digest: v.Digest, + Inputs: v.Inputs, + Name: v.Name, + Started: v.Started, + Completed: v.Completed, + Error: v.Error, + Cached: v.Cached, + }) + } + for _, v := range resp.Statuses { + s.Statuses = append(s.Statuses, &VertexStatus{ + ID: v.ID, + Vertex: v.Vertex, + Name: v.Name, + Total: v.Total, + Current: v.Current, + Timestamp: v.Timestamp, + Started: v.Started, + Completed: v.Completed, + }) + } + for _, v := range resp.Logs { + s.Logs = append(s.Logs, &VertexLog{ + Vertex: v.Vertex, + Stream: int(v.Stream), + Data: v.Msg, + Timestamp: v.Timestamp, + }) + } + if statusChan != nil { + statusChan <- &s + } + } + }) + + if err := eg.Wait(); err != nil { + return nil, err + } + return res, nil +} + +func prepareSyncedDirs(def *llb.Definition, localDirs map[string]string) ([]filesync.SyncedDir, error) { + for _, d := range localDirs { + fi, err := os.Stat(d) + if err != nil { + return nil, errors.Wrapf(err, "could not find %s", d) + } + if !fi.IsDir() { + return nil, errors.Errorf("%s not a directory", d) + } + } + dirs := make([]filesync.SyncedDir, 0, len(localDirs)) + if def == nil { + for name, d := range localDirs { + dirs = append(dirs, filesync.SyncedDir{Name: name, Dir: d}) + } + } else { + for _, dt := range def.Def { + var op pb.Op + if err := (&op).Unmarshal(dt); err != nil { + return nil, errors.Wrap(err, "failed to parse llb proto op") + } + if src := op.GetSource(); src != nil { + if strings.HasPrefix(src.Identifier, "local://") { // TODO: just make a type property + name := strings.TrimPrefix(src.Identifier, "local://") + d, ok := localDirs[name] + if !ok { + return nil, errors.Errorf("local directory %s not enabled", name) + } + dirs = append(dirs, filesync.SyncedDir{Name: name, Dir: d}) // TODO: excludes + } + } + } + } + return dirs, nil +} + +func defaultSessionName() string { + wd, err := os.Getwd() + if err != nil { + return "unknown" + } + return filepath.Base(wd) +} diff --git a/vendor/github.com/moby/buildkit/client/workers.go b/vendor/github.com/moby/buildkit/client/workers.go new file mode 100644 index 0000000000000..e70dcbc609ad6 --- /dev/null +++ b/vendor/github.com/moby/buildkit/client/workers.go @@ -0,0 +1,53 @@ +package client + +import ( + "context" + + controlapi "github.com/moby/buildkit/api/services/control" + "github.com/moby/buildkit/solver/pb" + specs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +type WorkerInfo struct { + ID string + Labels map[string]string + Platforms []specs.Platform +} + +func (c *Client) ListWorkers(ctx context.Context, opts ...ListWorkersOption) ([]*WorkerInfo, error) { + info := &ListWorkersInfo{} + for _, o := range opts { + o(info) + } + + req := &controlapi.ListWorkersRequest{Filter: info.Filter} + resp, err := c.controlClient().ListWorkers(ctx, req) + if err != nil { + return nil, errors.Wrap(err, "failed to list workers") + } + + var wi []*WorkerInfo + + for _, w := range resp.Record { + wi = append(wi, &WorkerInfo{ + ID: w.ID, + Labels: w.Labels, + Platforms: pb.ToSpecPlatforms(w.Platforms), + }) + } + + return wi, nil +} + +type ListWorkersOption func(*ListWorkersInfo) + +type ListWorkersInfo struct { + Filter []string +} + +func WithWorkerFilter(f []string) ListWorkersOption { + return func(wi *ListWorkersInfo) { + wi.Filter = f + } +} diff --git a/vendor/github.com/moby/buildkit/control/control.go b/vendor/github.com/moby/buildkit/control/control.go new file mode 100644 index 0000000000000..b59c1eec93925 --- /dev/null +++ b/vendor/github.com/moby/buildkit/control/control.go @@ -0,0 +1,304 @@ +package control + +import ( + "context" + + "github.com/docker/distribution/reference" + controlapi "github.com/moby/buildkit/api/services/control" + apitypes "github.com/moby/buildkit/api/types" + "github.com/moby/buildkit/cache/remotecache" + "github.com/moby/buildkit/client" + "github.com/moby/buildkit/exporter" + "github.com/moby/buildkit/frontend" + "github.com/moby/buildkit/session" + "github.com/moby/buildkit/session/grpchijack" + "github.com/moby/buildkit/solver" + "github.com/moby/buildkit/solver/llbsolver" + "github.com/moby/buildkit/solver/pb" + "github.com/moby/buildkit/worker" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "golang.org/x/sync/errgroup" + "google.golang.org/grpc" +) + +type ResolveCacheExporterFunc func(ctx context.Context, typ, target string) (remotecache.Exporter, error) + +type Opt struct { + SessionManager *session.Manager + WorkerController *worker.Controller + Frontends map[string]frontend.Frontend + CacheKeyStorage solver.CacheKeyStorage + ResolveCacheExporterFunc remotecache.ResolveCacheExporterFunc + ResolveCacheImporterFunc remotecache.ResolveCacheImporterFunc +} + +type Controller struct { // TODO: ControlService + opt Opt + solver *llbsolver.Solver +} + +func NewController(opt Opt) (*Controller, error) { + solver, err := llbsolver.New(opt.WorkerController, opt.Frontends, opt.CacheKeyStorage, opt.ResolveCacheImporterFunc) + if err != nil { + return nil, errors.Wrap(err, "failed to create solver") + } + + c := &Controller{ + opt: opt, + solver: solver, + } + return c, nil +} + +func (c *Controller) Register(server *grpc.Server) error { + controlapi.RegisterControlServer(server, c) + return nil +} + +func (c *Controller) DiskUsage(ctx context.Context, r *controlapi.DiskUsageRequest) (*controlapi.DiskUsageResponse, error) { + resp := &controlapi.DiskUsageResponse{} + workers, err := c.opt.WorkerController.List() + if err != nil { + return nil, err + } + for _, w := range workers { + du, err := w.DiskUsage(ctx, client.DiskUsageInfo{ + Filter: r.Filter, + }) + if err != nil { + return nil, err + } + + for _, r := range du { + resp.Record = append(resp.Record, &controlapi.UsageRecord{ + // TODO: add worker info + ID: r.ID, + Mutable: r.Mutable, + InUse: r.InUse, + Size_: r.Size, + Parent: r.Parent, + UsageCount: int64(r.UsageCount), + Description: r.Description, + CreatedAt: r.CreatedAt, + LastUsedAt: r.LastUsedAt, + }) + } + } + return resp, nil +} + +func (c *Controller) Prune(req *controlapi.PruneRequest, stream controlapi.Control_PruneServer) error { + ch := make(chan client.UsageInfo) + + eg, ctx := errgroup.WithContext(stream.Context()) + workers, err := c.opt.WorkerController.List() + if err != nil { + return errors.Wrap(err, "failed to list workers for prune") + } + + for _, w := range workers { + func(w worker.Worker) { + eg.Go(func() error { + return w.Prune(ctx, ch) + }) + }(w) + } + + eg2, _ := errgroup.WithContext(stream.Context()) + + eg2.Go(func() error { + defer close(ch) + return eg.Wait() + }) + + eg2.Go(func() error { + for r := range ch { + if err := stream.Send(&controlapi.UsageRecord{ + // TODO: add worker info + ID: r.ID, + Mutable: r.Mutable, + InUse: r.InUse, + Size_: r.Size, + Parent: r.Parent, + UsageCount: int64(r.UsageCount), + Description: r.Description, + CreatedAt: r.CreatedAt, + LastUsedAt: r.LastUsedAt, + }); err != nil { + return err + } + } + return nil + }) + + return eg2.Wait() +} + +func (c *Controller) Solve(ctx context.Context, req *controlapi.SolveRequest) (*controlapi.SolveResponse, error) { + ctx = session.NewContext(ctx, req.Session) + + var expi exporter.ExporterInstance + // TODO: multiworker + // This is actually tricky, as the exporter should come from the worker that has the returned reference. We may need to delay this so that the solver loads this. + w, err := c.opt.WorkerController.GetDefault() + if err != nil { + return nil, err + } + if req.Exporter != "" { + exp, err := w.Exporter(req.Exporter) + if err != nil { + return nil, err + } + expi, err = exp.Resolve(ctx, req.ExporterAttrs) + if err != nil { + return nil, err + } + } + + var cacheExporter remotecache.Exporter + if ref := req.Cache.ExportRef; ref != "" && c.opt.ResolveCacheExporterFunc != nil { + parsed, err := reference.ParseNormalizedNamed(ref) + if err != nil { + return nil, err + } + exportCacheRef := reference.TagNameOnly(parsed).String() + typ := "" // unimplemented yet (typically registry) + cacheExporter, err = c.opt.ResolveCacheExporterFunc(ctx, typ, exportCacheRef) + if err != nil { + return nil, err + } + } + + var importCacheRefs []string + for _, ref := range req.Cache.ImportRefs { + parsed, err := reference.ParseNormalizedNamed(ref) + if err != nil { + return nil, err + } + importCacheRefs = append(importCacheRefs, reference.TagNameOnly(parsed).String()) + } + + resp, err := c.solver.Solve(ctx, req.Ref, frontend.SolveRequest{ + Frontend: req.Frontend, + Definition: req.Definition, + FrontendOpt: req.FrontendAttrs, + ImportCacheRefs: importCacheRefs, + }, llbsolver.ExporterRequest{ + Exporter: expi, + CacheExporter: cacheExporter, + CacheExportMode: parseCacheExporterOpt(req.Cache.ExportAttrs), + }) + if err != nil { + return nil, err + } + return &controlapi.SolveResponse{ + ExporterResponse: resp.ExporterResponse, + }, nil +} + +func (c *Controller) Status(req *controlapi.StatusRequest, stream controlapi.Control_StatusServer) error { + ch := make(chan *client.SolveStatus, 8) + + eg, ctx := errgroup.WithContext(stream.Context()) + eg.Go(func() error { + return c.solver.Status(ctx, req.Ref, ch) + }) + + eg.Go(func() error { + for { + ss, ok := <-ch + if !ok { + return nil + } + sr := controlapi.StatusResponse{} + for _, v := range ss.Vertexes { + sr.Vertexes = append(sr.Vertexes, &controlapi.Vertex{ + Digest: v.Digest, + Inputs: v.Inputs, + Name: v.Name, + Started: v.Started, + Completed: v.Completed, + Error: v.Error, + Cached: v.Cached, + }) + } + for _, v := range ss.Statuses { + sr.Statuses = append(sr.Statuses, &controlapi.VertexStatus{ + ID: v.ID, + Vertex: v.Vertex, + Name: v.Name, + Current: v.Current, + Total: v.Total, + Timestamp: v.Timestamp, + Started: v.Started, + Completed: v.Completed, + }) + } + for _, v := range ss.Logs { + sr.Logs = append(sr.Logs, &controlapi.VertexLog{ + Vertex: v.Vertex, + Stream: int64(v.Stream), + Msg: v.Data, + Timestamp: v.Timestamp, + }) + } + if err := stream.SendMsg(&sr); err != nil { + return err + } + } + }) + + return eg.Wait() +} + +func (c *Controller) Session(stream controlapi.Control_SessionServer) error { + logrus.Debugf("session started") + conn, closeCh, opts := grpchijack.Hijack(stream) + defer conn.Close() + + ctx, cancel := context.WithCancel(stream.Context()) + go func() { + <-closeCh + cancel() + }() + + err := c.opt.SessionManager.HandleConn(ctx, conn, opts) + logrus.Debugf("session finished: %v", err) + return err +} + +func (c *Controller) ListWorkers(ctx context.Context, r *controlapi.ListWorkersRequest) (*controlapi.ListWorkersResponse, error) { + resp := &controlapi.ListWorkersResponse{} + workers, err := c.opt.WorkerController.List(r.Filter...) + if err != nil { + return nil, err + } + for _, w := range workers { + resp.Record = append(resp.Record, &apitypes.WorkerRecord{ + ID: w.ID(), + Labels: w.Labels(), + Platforms: pb.PlatformsFromSpec(w.Platforms()), + }) + } + return resp, nil +} + +func parseCacheExporterOpt(opt map[string]string) solver.CacheExportMode { + for k, v := range opt { + switch k { + case "mode": + switch v { + case "min": + return solver.CacheExportModeMin + case "max": + return solver.CacheExportModeMax + default: + logrus.Debugf("skipping incalid cache export mode: %s", v) + } + default: + logrus.Warnf("skipping invalid cache export opt: %s", v) + } + } + return solver.CacheExportModeMin +} diff --git a/vendor/github.com/moby/buildkit/executor/executor.go b/vendor/github.com/moby/buildkit/executor/executor.go new file mode 100644 index 0000000000000..6c897926261ec --- /dev/null +++ b/vendor/github.com/moby/buildkit/executor/executor.go @@ -0,0 +1,30 @@ +package executor + +import ( + "context" + "io" + + "github.com/moby/buildkit/cache" +) + +type Meta struct { + Args []string + Env []string + User string + Cwd string + Tty bool + ReadonlyRootFS bool + // DisableNetworking bool +} + +type Mount struct { + Src cache.Mountable + Selector string + Dest string + Readonly bool +} + +type Executor interface { + // TODO: add stdout/err + Exec(ctx context.Context, meta Meta, rootfs cache.Mountable, mounts []Mount, stdin io.ReadCloser, stdout, stderr io.WriteCloser) error +} diff --git a/vendor/github.com/moby/buildkit/executor/oci/hosts.go b/vendor/github.com/moby/buildkit/executor/oci/hosts.go new file mode 100644 index 0000000000000..b7be99ccf3654 --- /dev/null +++ b/vendor/github.com/moby/buildkit/executor/oci/hosts.go @@ -0,0 +1,38 @@ +package oci + +import ( + "context" + "io/ioutil" + "os" + "path/filepath" +) + +const hostsContent = ` +127.0.0.1 localhost +::1 localhost ip6-localhost ip6-loopback +` + +func GetHostsFile(ctx context.Context, stateDir string) (string, error) { + p := filepath.Join(stateDir, "hosts") + _, err := g.Do(ctx, stateDir, func(ctx context.Context) (interface{}, error) { + _, err := os.Stat(p) + if err == nil { + return "", nil + } + if !os.IsNotExist(err) { + return "", err + } + if err := ioutil.WriteFile(p+".tmp", []byte(hostsContent), 0644); err != nil { + return "", err + } + + if err := os.Rename(p+".tmp", p); err != nil { + return "", err + } + return "", nil + }) + if err != nil { + return "", err + } + return p, nil +} diff --git a/vendor/github.com/moby/buildkit/executor/oci/mounts.go b/vendor/github.com/moby/buildkit/executor/oci/mounts.go new file mode 100644 index 0000000000000..a0fe8a9f925e0 --- /dev/null +++ b/vendor/github.com/moby/buildkit/executor/oci/mounts.go @@ -0,0 +1,68 @@ +package oci + +import ( + "context" + + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +// MountOpts sets oci spec specific info for mount points +type MountOpts func([]specs.Mount) []specs.Mount + +//GetMounts returns default required for buildkit +// https://github.com/moby/buildkit/issues/429 +func GetMounts(ctx context.Context, mountOpts ...MountOpts) []specs.Mount { + mounts := []specs.Mount{ + { + Destination: "/proc", + Type: "proc", + Source: "proc", + }, + { + Destination: "/dev", + Type: "tmpfs", + Source: "tmpfs", + Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"}, + }, + { + Destination: "/dev/pts", + Type: "devpts", + Source: "devpts", + Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"}, + }, + { + Destination: "/dev/shm", + Type: "tmpfs", + Source: "shm", + Options: []string{"nosuid", "noexec", "nodev", "mode=1777", "size=65536k"}, + }, + { + Destination: "/dev/mqueue", + Type: "mqueue", + Source: "mqueue", + Options: []string{"nosuid", "noexec", "nodev"}, + }, + { + Destination: "/sys", + Type: "sysfs", + Source: "sysfs", + Options: []string{"nosuid", "noexec", "nodev", "ro"}, + }, + } + for _, o := range mountOpts { + mounts = o(mounts) + } + return mounts +} + +func withROBind(src, dest string) func(m []specs.Mount) []specs.Mount { + return func(m []specs.Mount) []specs.Mount { + m = append(m, specs.Mount{ + Destination: dest, + Type: "bind", + Source: src, + Options: []string{"rbind", "ro"}, + }) + return m + } +} diff --git a/vendor/github.com/moby/buildkit/executor/oci/resolvconf.go b/vendor/github.com/moby/buildkit/executor/oci/resolvconf.go new file mode 100644 index 0000000000000..f22eceed22303 --- /dev/null +++ b/vendor/github.com/moby/buildkit/executor/oci/resolvconf.go @@ -0,0 +1,81 @@ +package oci + +import ( + "context" + "io/ioutil" + "os" + "path/filepath" + + "github.com/docker/libnetwork/resolvconf" + "github.com/moby/buildkit/util/flightcontrol" +) + +var g flightcontrol.Group +var notFirstRun bool +var lastNotEmpty bool + +func GetResolvConf(ctx context.Context, stateDir string) (string, error) { + p := filepath.Join(stateDir, "resolv.conf") + _, err := g.Do(ctx, stateDir, func(ctx context.Context) (interface{}, error) { + generate := !notFirstRun + notFirstRun = true + + if !generate { + fi, err := os.Stat(p) + if err != nil { + if !os.IsNotExist(err) { + return "", err + } + generate = true + } + if !generate { + fiMain, err := os.Stat("/etc/resolv.conf") + if err != nil { + if !os.IsNotExist(err) { + return nil, err + } + if lastNotEmpty { + generate = true + lastNotEmpty = false + } + } else { + if fi.ModTime().Before(fiMain.ModTime()) { + generate = true + } + } + } + } + + if !generate { + return "", nil + } + + var dt []byte + f, err := resolvconf.Get() + if err != nil { + if !os.IsNotExist(err) { + return "", err + } + } else { + dt = f.Content + } + + f, err = resolvconf.FilterResolvDNS(dt, true) + if err != nil { + return "", err + } + + if err := ioutil.WriteFile(p+".tmp", f.Content, 0644); err != nil { + return "", err + } + + if err := os.Rename(p+".tmp", p); err != nil { + return "", err + } + return "", nil + }) + if err != nil { + return "", err + } + return p, nil +} diff --git a/vendor/github.com/moby/buildkit/executor/oci/spec_unix.go b/vendor/github.com/moby/buildkit/executor/oci/spec_unix.go new file mode 100644 index 0000000000000..c628b5cab1032 --- /dev/null +++ b/vendor/github.com/moby/buildkit/executor/oci/spec_unix.go @@ -0,0 +1,163 @@ +// +build !windows + +package oci + +import ( + "context" + "path" + "sync" + + "github.com/containerd/containerd/containers" + "github.com/containerd/containerd/mount" + "github.com/containerd/containerd/namespaces" + "github.com/containerd/containerd/oci" + "github.com/mitchellh/hashstructure" + "github.com/moby/buildkit/executor" + "github.com/moby/buildkit/snapshot" + specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" +) + +// Ideally we don't have to import whole containerd just for the default spec + +// GenerateSpec generates spec using containerd functionality. +func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mount, id, resolvConf, hostsFile string, opts ...oci.SpecOpts) (*specs.Spec, func(), error) { + c := &containers.Container{ + ID: id, + } + _, ok := namespaces.Namespace(ctx) + if !ok { + ctx = namespaces.WithNamespace(ctx, "buildkit") + } + + opts = append(opts, + oci.WithHostNamespace(specs.NetworkNamespace), + ) + + // Note that containerd.GenerateSpec is namespaced so as to make + // specs.Linux.CgroupsPath namespaced + s, err := oci.GenerateSpec(ctx, nil, c, opts...) + if err != nil { + return nil, nil, err + } + s.Process.Args = meta.Args + s.Process.Env = meta.Env + s.Process.Cwd = meta.Cwd + + s.Mounts = GetMounts(ctx, + withROBind(resolvConf, "/etc/resolv.conf"), + withROBind(hostsFile, "/etc/hosts"), + ) + // TODO: User + + sm := &submounts{} + + var releasers []func() error + releaseAll := func() { + sm.cleanup() + for _, f := range releasers { + f() + } + } + + for _, m := range mounts { + if m.Src == nil { + return nil, nil, errors.Errorf("mount %s has no source", m.Dest) + } + mountable, err := m.Src.Mount(ctx, m.Readonly) + if err != nil { + releaseAll() + return nil, nil, errors.Wrapf(err, "failed to mount %s", m.Dest) + } + mounts, err := mountable.Mount() + if err != nil { + releaseAll() + return nil, nil, errors.WithStack(err) + } + releasers = append(releasers, mountable.Release) + for _, mount := range mounts { + mount, err = sm.subMount(mount, m.Selector) + if err != nil { + releaseAll() + return nil, nil, err + } + s.Mounts = append(s.Mounts, specs.Mount{ + Destination: m.Dest, + Type: mount.Type, + Source: mount.Source, + Options: mount.Options, + }) + } + } + + return s, releaseAll, nil +} + +type mountRef struct { + mount mount.Mount + unmount func() error +} + +type submounts struct { + m map[uint64]mountRef +} + +func (s *submounts) subMount(m mount.Mount, subPath string) (mount.Mount, error) { + if path.Join("/", subPath) == "/" { + return m, nil + } + if s.m == nil { + s.m = map[uint64]mountRef{} + } + h, err := hashstructure.Hash(m, nil) + if err != nil { + return mount.Mount{}, nil + } + if mr, ok := s.m[h]; ok { + return sub(mr.mount, subPath), nil + } + + lm := snapshot.LocalMounterWithMounts([]mount.Mount{m}) + + mp, err := lm.Mount() + if err != nil { + return mount.Mount{}, err + } + + opts := []string{"rbind"} + for _, opt := range m.Options { + if opt == "ro" { + opts = append(opts, opt) + } + } + + s.m[h] = mountRef{ + mount: mount.Mount{ + Source: mp, + Type: "bind", + Options: opts, + }, + unmount: lm.Unmount, + } + + return sub(s.m[h].mount, subPath), nil +} + +func (s *submounts) cleanup() { + var wg sync.WaitGroup + wg.Add(len(s.m)) + for _, m := range s.m { + func(m mountRef) { + go func() { + m.unmount() + wg.Done() + }() + }(m) + } + wg.Wait() +} + +func sub(m mount.Mount, subPath string) mount.Mount { + m.Source = path.Join(m.Source, subPath) + return m +} diff --git a/vendor/github.com/moby/buildkit/executor/oci/user.go b/vendor/github.com/moby/buildkit/executor/oci/user.go new file mode 100644 index 0000000000000..ac5dbebdf294a --- /dev/null +++ b/vendor/github.com/moby/buildkit/executor/oci/user.go @@ -0,0 +1,107 @@ +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, []uint32, error) { + // fast path from uid/gid + if uid, gid, err := ParseUIDGID(username); err == nil { + return uid, gid, nil, nil + } + + passwdPath, err := user.GetPasswdPath() + if err != nil { + return 0, 0, nil, err + } + groupPath, err := user.GetGroupPath() + if err != nil { + return 0, 0, nil, err + } + passwdFile, err := openUserFile(root, passwdPath) + if err == nil { + defer passwdFile.Close() + } + groupFile, err := openUserFile(root, groupPath) + if err == nil { + defer groupFile.Close() + } + + execUser, err := user.GetExecUser(username, nil, passwdFile, groupFile) + if err != nil { + return 0, 0, nil, err + } + var sgids []uint32 + for _, g := range execUser.Sgids { + sgids = append(sgids, uint32(g)) + } + return uint32(execUser.Uid), uint32(execUser.Gid), sgids, nil +} + +// 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) + 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 +} + +func openUserFile(root, p string) (*os.File, error) { + p, err := fs.RootPath(root, p) + if err != nil { + return nil, err + } + return os.Open(p) +} + +func parseUID(str string) (uint32, error) { + if str == "root" { + return 0, nil + } + uid, err := strconv.ParseUint(str, 10, 32) + if err != nil { + return 0, err + } + 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{} + } +} diff --git a/vendor/github.com/moby/buildkit/executor/runcexecutor/executor.go b/vendor/github.com/moby/buildkit/executor/runcexecutor/executor.go new file mode 100644 index 0000000000000..5225d5fc564e1 --- /dev/null +++ b/vendor/github.com/moby/buildkit/executor/runcexecutor/executor.go @@ -0,0 +1,247 @@ +package runcexecutor + +import ( + "context" + "encoding/json" + "io" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strconv" + "strings" + "syscall" + + "github.com/containerd/containerd/contrib/seccomp" + "github.com/containerd/containerd/mount" + containerdoci "github.com/containerd/containerd/oci" + "github.com/containerd/continuity/fs" + runc "github.com/containerd/go-runc" + "github.com/moby/buildkit/cache" + "github.com/moby/buildkit/executor" + "github.com/moby/buildkit/executor/oci" + "github.com/moby/buildkit/identity" + rootlessspecconv "github.com/moby/buildkit/util/rootless/specconv" + "github.com/moby/buildkit/util/system" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +type Opt struct { + // root directory + Root string + CommandCandidates []string + // without root privileges (has nothing to do with Opt.Root directory) + Rootless bool +} + +var defaultCommandCandidates = []string{"buildkit-runc", "runc"} + +type runcExecutor struct { + runc *runc.Runc + root string + cmd string + rootless bool +} + +func New(opt Opt) (executor.Executor, error) { + cmds := opt.CommandCandidates + if cmds == nil { + cmds = defaultCommandCandidates + } + + var cmd string + var found bool + for _, cmd = range cmds { + if _, err := exec.LookPath(cmd); err == nil { + found = true + break + } + } + if !found { + return nil, errors.Errorf("failed to find %s binary", cmd) + } + + root := opt.Root + + if err := os.MkdirAll(root, 0700); err != nil { + return nil, errors.Wrapf(err, "failed to create %s", root) + } + + root, err := filepath.Abs(root) + if err != nil { + return nil, err + } + root, err = filepath.EvalSymlinks(root) + if err != nil { + return nil, err + } + + runtime := &runc.Runc{ + Command: cmd, + Log: filepath.Join(root, "runc-log.json"), + LogFormat: runc.JSON, + PdeathSignal: syscall.SIGKILL, + Setpgid: true, + // we don't execute runc with --rootless=(true|false) explicitly, + // so as to support non-runc runtimes + } + + w := &runcExecutor{ + runc: runtime, + root: root, + rootless: opt.Rootless, + } + return w, nil +} + +func (w *runcExecutor) Exec(ctx context.Context, meta executor.Meta, root cache.Mountable, mounts []executor.Mount, stdin io.ReadCloser, stdout, stderr io.WriteCloser) error { + + resolvConf, err := oci.GetResolvConf(ctx, w.root) + if err != nil { + return err + } + + hostsFile, err := oci.GetHostsFile(ctx, w.root) + if err != nil { + return err + } + + mountable, err := root.Mount(ctx, false) + if err != nil { + return err + } + + rootMount, err := mountable.Mount() + if err != nil { + return err + } + defer mountable.Release() + + id := identity.NewID() + bundle := filepath.Join(w.root, id) + + if err := os.Mkdir(bundle, 0700); err != nil { + return err + } + defer os.RemoveAll(bundle) + rootFSPath := filepath.Join(bundle, "rootfs") + if err := os.Mkdir(rootFSPath, 0700); err != nil { + return err + } + if err := mount.All(rootMount, rootFSPath); err != nil { + return err + } + defer mount.Unmount(rootFSPath, 0) + + uid, gid, sgids, err := oci.GetUser(ctx, rootFSPath, meta.User) + if err != nil { + return err + } + + f, err := os.Create(filepath.Join(bundle, "config.json")) + if err != nil { + return err + } + defer f.Close() + opts := []containerdoci.SpecOpts{oci.WithUIDGID(uid, gid, sgids)} + if system.SeccompSupported() { + opts = append(opts, seccomp.WithDefaultProfile()) + } + if meta.ReadonlyRootFS { + opts = append(opts, containerdoci.WithRootFSReadonly()) + } + spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, opts...) + if err != nil { + return err + } + defer cleanup() + + spec.Root.Path = rootFSPath + if _, ok := root.(cache.ImmutableRef); ok { // TODO: pass in with mount, not ref type + spec.Root.Readonly = true + } + + newp, err := fs.RootPath(rootFSPath, meta.Cwd) + if err != nil { + return errors.Wrapf(err, "working dir %s points to invalid target", newp) + } + if err := os.MkdirAll(newp, 0700); err != nil { + return errors.Wrapf(err, "failed to create working directory %s", newp) + } + + if err := setOOMScoreAdj(spec); err != nil { + return err + } + if w.rootless { + if err := rootlessspecconv.ToRootless(spec); err != nil { + return err + } + } + + if err := json.NewEncoder(f).Encode(spec); err != nil { + return err + } + + logrus.Debugf("> running %s %v", id, meta.Args) + + status, err := w.runc.Run(ctx, id, bundle, &runc.CreateOpts{ + IO: &forwardIO{stdin: stdin, stdout: stdout, stderr: stderr}, + }) + logrus.Debugf("< completed %s %v %v", id, status, err) + if status != 0 { + select { + case <-ctx.Done(): + // runc can't report context.Cancelled directly + return errors.Wrapf(ctx.Err(), "exit code %d", status) + default: + } + return errors.Errorf("exit code %d", status) + } + + return err +} + +type forwardIO struct { + stdin io.ReadCloser + stdout, stderr io.WriteCloser +} + +func (s *forwardIO) Close() error { + return nil +} + +func (s *forwardIO) Set(cmd *exec.Cmd) { + cmd.Stdin = s.stdin + cmd.Stdout = s.stdout + cmd.Stderr = s.stderr +} + +func (s *forwardIO) Stdin() io.WriteCloser { + return nil +} + +func (s *forwardIO) Stdout() io.ReadCloser { + return nil +} + +func (s *forwardIO) Stderr() io.ReadCloser { + return nil +} + +// setOOMScoreAdj comes from https://github.com/genuinetools/img/blob/2fabe60b7dc4623aa392b515e013bbc69ad510ab/executor/runc/executor.go#L182-L192 +func setOOMScoreAdj(spec *specs.Spec) error { + // Set the oom_score_adj of our children containers to that of the current process. + b, err := ioutil.ReadFile("/proc/self/oom_score_adj") + if err != nil { + return errors.Wrap(err, "failed to read /proc/self/oom_score_adj") + } + s := strings.TrimSpace(string(b)) + oom, err := strconv.Atoi(s) + if err != nil { + return errors.Wrapf(err, "failed to parse %s as int", s) + } + spec.Process.OOMScoreAdj = &oom + return nil +} diff --git a/vendor/github.com/moby/buildkit/exporter/exporter.go b/vendor/github.com/moby/buildkit/exporter/exporter.go new file mode 100644 index 0000000000000..48c6a3bc0dd2f --- /dev/null +++ b/vendor/github.com/moby/buildkit/exporter/exporter.go @@ -0,0 +1,16 @@ +package exporter + +import ( + "context" + + "github.com/moby/buildkit/cache" +) + +type Exporter interface { + Resolve(context.Context, map[string]string) (ExporterInstance, error) +} + +type ExporterInstance interface { + Name() string + Export(context.Context, cache.ImmutableRef, map[string][]byte) (map[string]string, error) +} diff --git a/vendor/github.com/moby/buildkit/frontend/dockerfile/builder/build.go b/vendor/github.com/moby/buildkit/frontend/dockerfile/builder/build.go new file mode 100644 index 0000000000000..947eef43cdebb --- /dev/null +++ b/vendor/github.com/moby/buildkit/frontend/dockerfile/builder/build.go @@ -0,0 +1,310 @@ +package builder + +import ( + "archive/tar" + "bytes" + "context" + "encoding/json" + "regexp" + "strings" + + "github.com/containerd/containerd/platforms" + "github.com/docker/docker/builder/dockerignore" + "github.com/moby/buildkit/client/llb" + "github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb" + "github.com/moby/buildkit/frontend/gateway/client" + specs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" + "golang.org/x/sync/errgroup" +) + +const ( + LocalNameContext = "context" + LocalNameDockerfile = "dockerfile" + keyTarget = "target" + keyFilename = "filename" + keyCacheFrom = "cache-from" + exporterImageConfig = "containerimage.config" + defaultDockerfileName = "Dockerfile" + dockerignoreFilename = ".dockerignore" + buildArgPrefix = "build-arg:" + labelPrefix = "label:" + keyNoCache = "no-cache" + keyTargetPlatform = "platform" +) + +var httpPrefix = regexp.MustCompile("^https?://") +var gitUrlPathWithFragmentSuffix = regexp.MustCompile("\\.git(?:#.+)?$") + +func Build(ctx context.Context, c client.Client) (*client.Result, error) { + opts := c.BuildOpts().Opts + + defaultBuildPlatform := platforms.DefaultSpec() + if workers := c.BuildOpts().Workers; len(workers) > 0 && len(workers[0].Platforms) > 0 { + defaultBuildPlatform = workers[0].Platforms[0] + } + + buildPlatforms := []specs.Platform{defaultBuildPlatform} + targetPlatform := platforms.DefaultSpec() + if v := opts[keyTargetPlatform]; v != "" { + var err error + targetPlatform, err = platforms.Parse(v) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse target platform %s", v) + } + } + + filename := opts[keyFilename] + if filename == "" { + filename = defaultDockerfileName + } + + var ignoreCache []string + if v, ok := opts[keyNoCache]; ok { + if v == "" { + ignoreCache = []string{} // means all stages + } else { + ignoreCache = strings.Split(v, ",") + } + } + + src := llb.Local(LocalNameDockerfile, + llb.IncludePatterns([]string{filename}), + llb.SessionID(c.BuildOpts().SessionID), + llb.SharedKeyHint(defaultDockerfileName), + ) + var buildContext *llb.State + isScratchContext := false + if st, ok := detectGitContext(opts[LocalNameContext]); ok { + src = *st + buildContext = &src + } else if httpPrefix.MatchString(opts[LocalNameContext]) { + httpContext := llb.HTTP(opts[LocalNameContext], llb.Filename("context")) + def, err := httpContext.Marshal() + if err != nil { + return nil, errors.Wrapf(err, "failed to marshal httpcontext") + } + res, err := c.Solve(ctx, client.SolveRequest{ + Definition: def.ToPB(), + }) + if err != nil { + return nil, errors.Wrapf(err, "failed to resolve httpcontext") + } + + ref, err := res.SingleRef() + if err != nil { + return nil, err + } + + dt, err := ref.ReadFile(ctx, client.ReadRequest{ + Filename: "context", + Range: &client.FileRange{ + Length: 1024, + }, + }) + if err != nil { + return nil, errors.Errorf("failed to read downloaded context") + } + if isArchive(dt) { + unpack := llb.Image(dockerfile2llb.CopyImage). + Run(llb.Shlex("copy --unpack /src/context /out/"), llb.ReadonlyRootFS()) + unpack.AddMount("/src", httpContext, llb.Readonly) + src = unpack.AddMount("/out", llb.Scratch()) + buildContext = &src + } else { + filename = "context" + src = httpContext + buildContext = &src + isScratchContext = true + } + } + + def, err := src.Marshal() + if err != nil { + return nil, errors.Wrapf(err, "failed to marshal local source") + } + + eg, ctx2 := errgroup.WithContext(ctx) + var dtDockerfile []byte + eg.Go(func() error { + res, err := c.Solve(ctx2, client.SolveRequest{ + Definition: def.ToPB(), + }) + if err != nil { + return errors.Wrapf(err, "failed to resolve dockerfile") + } + + ref, err := res.SingleRef() + if err != nil { + return err + } + + dtDockerfile, err = ref.ReadFile(ctx2, client.ReadRequest{ + Filename: filename, + }) + if err != nil { + return errors.Wrapf(err, "failed to read dockerfile") + } + return nil + }) + var excludes []string + if !isScratchContext { + eg.Go(func() error { + dockerignoreState := buildContext + if dockerignoreState == nil { + st := llb.Local(LocalNameContext, + llb.SessionID(c.BuildOpts().SessionID), + llb.IncludePatterns([]string{dockerignoreFilename}), + llb.SharedKeyHint(dockerignoreFilename), + ) + dockerignoreState = &st + } + def, err := dockerignoreState.Marshal() + if err != nil { + return err + } + res, err := c.Solve(ctx2, client.SolveRequest{ + Definition: def.ToPB(), + }) + if err != nil { + return err + } + ref, err := res.SingleRef() + if err != nil { + return err + } + dtDockerignore, err := ref.ReadFile(ctx2, client.ReadRequest{ + Filename: dockerignoreFilename, + }) + if err == nil { + excludes, err = dockerignore.ReadAll(bytes.NewBuffer(dtDockerignore)) + if err != nil { + return errors.Wrap(err, "failed to parse dockerignore") + } + } + return nil + }) + } + + if err := eg.Wait(); err != nil { + return nil, err + } + + if _, ok := opts["cmdline"]; !ok { + ref, cmdline, ok := dockerfile2llb.DetectSyntax(bytes.NewBuffer(dtDockerfile)) + if ok { + return forwardGateway(ctx, c, ref, cmdline) + } + } + + st, img, err := dockerfile2llb.Dockerfile2LLB(ctx, dtDockerfile, dockerfile2llb.ConvertOpt{ + Target: opts[keyTarget], + MetaResolver: c, + BuildArgs: filter(opts, buildArgPrefix), + Labels: filter(opts, labelPrefix), + SessionID: c.BuildOpts().SessionID, + BuildContext: buildContext, + Excludes: excludes, + IgnoreCache: ignoreCache, + TargetPlatform: &targetPlatform, + BuildPlatforms: buildPlatforms, + }) + + if err != nil { + return nil, errors.Wrapf(err, "failed to create LLB definition") + } + + def, err = st.Marshal() + if err != nil { + return nil, errors.Wrapf(err, "failed to marshal LLB definition") + } + + config, err := json.Marshal(img) + if err != nil { + return nil, errors.Wrapf(err, "failed to marshal image config") + } + + var cacheFrom []string + if cacheFromStr := opts[keyCacheFrom]; cacheFromStr != "" { + cacheFrom = strings.Split(cacheFromStr, ",") + } + + res, err := c.Solve(ctx, client.SolveRequest{ + Definition: def.ToPB(), + ImportCacheRefs: cacheFrom, + }) + if err != nil { + return nil, err + } + + res.AddMeta(exporterImageConfig, config) + + return res, nil +} + +func forwardGateway(ctx context.Context, c client.Client, ref string, cmdline string) (*client.Result, error) { + opts := c.BuildOpts().Opts + if opts == nil { + opts = map[string]string{} + } + opts["cmdline"] = cmdline + opts["source"] = ref + return c.Solve(ctx, client.SolveRequest{ + Frontend: "gateway.v0", + FrontendOpt: opts, + }) +} + +func filter(opt map[string]string, key string) map[string]string { + m := map[string]string{} + for k, v := range opt { + if strings.HasPrefix(k, key) { + m[strings.TrimPrefix(k, key)] = v + } + } + return m +} + +func detectGitContext(ref string) (*llb.State, bool) { + found := false + if httpPrefix.MatchString(ref) && gitUrlPathWithFragmentSuffix.MatchString(ref) { + found = true + } + + for _, prefix := range []string{"git://", "github.com/", "git@"} { + if strings.HasPrefix(ref, prefix) { + found = true + break + } + } + if !found { + return nil, false + } + + parts := strings.SplitN(ref, "#", 2) + branch := "" + if len(parts) > 1 { + branch = parts[1] + } + st := llb.Git(parts[0], branch) + return &st, true +} + +func isArchive(header []byte) bool { + for _, m := range [][]byte{ + {0x42, 0x5A, 0x68}, // bzip2 + {0x1F, 0x8B, 0x08}, // gzip + {0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00}, // xz + } { + if len(header) < len(m) { + continue + } + if bytes.Equal(m, header[:len(m)]) { + return true + } + } + + r := tar.NewReader(bytes.NewBuffer(header)) + _, err := r.Next() + return err == nil +} diff --git a/vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert.go b/vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert.go new file mode 100644 index 0000000000000..983672f627502 --- /dev/null +++ b/vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert.go @@ -0,0 +1,1030 @@ +package dockerfile2llb + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/url" + "path" + "path/filepath" + "sort" + "strconv" + "strings" + + "github.com/containerd/containerd/platforms" + "github.com/docker/distribution/reference" + "github.com/docker/docker/pkg/signal" + "github.com/docker/go-connections/nat" + "github.com/moby/buildkit/client/llb" + "github.com/moby/buildkit/client/llb/imagemetaresolver" + "github.com/moby/buildkit/frontend/dockerfile/instructions" + "github.com/moby/buildkit/frontend/dockerfile/parser" + "github.com/moby/buildkit/frontend/dockerfile/shell" + specs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" + "golang.org/x/sync/errgroup" +) + +const ( + emptyImageName = "scratch" + localNameContext = "context" + historyComment = "buildkit.dockerfile.v0" + + CopyImage = "tonistiigi/copy:v0.1.3@sha256:e57a3b4d6240f55bac26b655d2cfb751f8b9412d6f7bb1f787e946391fb4b21b" +) + +type ConvertOpt struct { + Target string + MetaResolver llb.ImageMetaResolver + BuildArgs map[string]string + Labels map[string]string + SessionID string + BuildContext *llb.State + Excludes []string + // IgnoreCache contains names of the stages that should not use build cache. + // Empty slice means ignore cache for all stages. Nil doesn't disable cache. + IgnoreCache []string + // CacheIDNamespace scopes the IDs for different cache mounts + CacheIDNamespace string + TargetPlatform *specs.Platform + BuildPlatforms []specs.Platform +} + +func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State, *Image, error) { + if len(dt) == 0 { + return nil, nil, errors.Errorf("the Dockerfile cannot be empty") + } + + if opt.TargetPlatform != nil && opt.BuildPlatforms == nil { + opt.BuildPlatforms = []specs.Platform{*opt.TargetPlatform} + } + if len(opt.BuildPlatforms) == 0 { + opt.BuildPlatforms = []specs.Platform{platforms.DefaultSpec()} + } + implicitTargetPlatform := false + if opt.TargetPlatform == nil { + implicitTargetPlatform = true + opt.TargetPlatform = &opt.BuildPlatforms[0] + } + + dockerfile, err := parser.Parse(bytes.NewReader(dt)) + if err != nil { + return nil, nil, err + } + + proxyEnv := proxyEnvFromBuildArgs(opt.BuildArgs) + + stages, metaArgs, err := instructions.Parse(dockerfile.AST) + if err != nil { + return nil, nil, err + } + + optMetaArgs := []instructions.KeyValuePairOptional{} + for _, metaArg := range metaArgs { + optMetaArgs = append(optMetaArgs, setKVValue(metaArg.KeyValuePairOptional, opt.BuildArgs)) + } + + shlex := shell.NewLex(dockerfile.EscapeToken) + + metaResolver := opt.MetaResolver + if metaResolver == nil { + metaResolver = imagemetaresolver.Default() + } + + allDispatchStates := newDispatchStates() + + // set base state for every image + for _, st := range stages { + name, err := shlex.ProcessWord(st.BaseName, toEnvList(optMetaArgs, nil)) + 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{ + stage: st, + deps: make(map[*dispatchState]struct{}), + ctxPaths: make(map[string]struct{}), + } + + if v := st.Platform; v != "" { + v, err := shlex.ProcessWord(v, toEnvList(optMetaArgs, nil)) + if err != nil { + return nil, nil, errors.Wrapf(err, "failed to process arguments for platform %s", v) + } + + p, err := platforms.Parse(v) + if err != nil { + return nil, nil, errors.Wrapf(err, "failed to parse platform %s", v) + } + ds.platform = &p + } + + allDispatchStates.addState(ds) + if opt.IgnoreCache != nil { + if len(opt.IgnoreCache) == 0 { + ds.ignoreCache = true + } else if st.Name != "" { + for _, n := range opt.IgnoreCache { + if strings.EqualFold(n, st.Name) { + ds.ignoreCache = true + } + } + } + } + } + + var target *dispatchState + if opt.Target == "" { + target = allDispatchStates.lastTarget() + } else { + var ok bool + 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.states { + d.commands = make([]command, len(d.stage.Commands)) + for i, cmd := range d.stage.Commands { + newCmd, err := toCommand(cmd, allDispatchStates) + if err != nil { + return nil, nil, err + } + d.commands[i] = newCmd + for _, src := range newCmd.sources { + if src != nil { + d.deps[src] = struct{}{} + if src.unregistered { + allDispatchStates.addState(src) + } + } + } + } + } + + eg, ctx := errgroup.WithContext(ctx) + for i, d := range allDispatchStates.states { + reachable := isReachable(target, d) + // resolve image config for every stage + if d.base == nil { + if d.stage.BaseName == emptyImageName { + d.state = llb.Scratch() + d.image = emptyImage(*opt.TargetPlatform) + continue + } + func(i int, d *dispatchState) { + eg.Go(func() error { + ref, err := reference.ParseNormalizedNamed(d.stage.BaseName) + if err != nil { + return err + } + platform := d.platform + if platform == nil { + platform = opt.TargetPlatform + } + d.stage.BaseName = reference.TagNameOnly(ref).String() + var isScratch bool + if metaResolver != nil && reachable { + dgst, dt, err := metaResolver.ResolveImageConfig(ctx, d.stage.BaseName, platform) + if err == nil { // handle the error while builder is actually running + var img Image + if err := json.Unmarshal(dt, &img); err != nil { + return err + } + img.Created = nil + // if there is no explicit target platform, try to match based on image config + if d.platform == nil && implicitTargetPlatform { + p := autoDetectPlatform(img, *platform, opt.BuildPlatforms) + platform = &p + } + d.image = img + if dgst != "" { + ref, err = reference.WithDigest(ref, dgst) + if err != nil { + return err + } + } + d.stage.BaseName = ref.String() + _ = ref + if len(img.RootFS.DiffIDs) == 0 { + isScratch = true + } + } + } + if isScratch { + d.state = llb.Scratch() + } else { + d.state = llb.Image(d.stage.BaseName, dfCmd(d.stage.SourceCode), llb.Platform(*platform)) + } + return nil + }) + }(i, d) + } + } + + if err := eg.Wait(); err != nil { + return nil, nil, err + } + + buildContext := &mutableOutput{} + ctxPaths := map[string]struct{}{} + + for _, d := range allDispatchStates.states { + if !isReachable(target, d) { + continue + } + if d.base != nil { + d.state = d.base.state + d.image = clone(d.base.image) + } + + // initialize base metadata from image conf + for _, env := range d.image.Config.Env { + parts := strings.SplitN(env, "=", 2) + v := "" + if len(parts) > 1 { + v = parts[1] + } + if err := dispatchEnv(d, &instructions.EnvCommand{Env: []instructions.KeyValuePair{{Key: parts[0], Value: v}}}, false); err != nil { + return nil, nil, err + } + } + if d.image.Config.WorkingDir != "" { + if err = dispatchWorkdir(d, &instructions.WorkdirCommand{Path: d.image.Config.WorkingDir}, false); err != nil { + return nil, nil, err + } + } + if d.image.Config.User != "" { + if err = dispatchUser(d, &instructions.UserCommand{User: d.image.Config.User}, false); err != nil { + return nil, nil, err + } + } + + opt := dispatchOpt{ + allDispatchStates: allDispatchStates, + metaArgs: optMetaArgs, + 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 { + return nil, nil, err + } + + for _, cmd := range d.commands { + if err := dispatch(d, cmd, opt); err != nil { + return nil, nil, err + } + } + + for p := range d.ctxPaths { + ctxPaths[p] = struct{}{} + } + } + + if len(opt.Labels) != 0 && target.image.Config.Labels == nil { + target.image.Config.Labels = make(map[string]string, len(opt.Labels)) + } + for k, v := range opt.Labels { + target.image.Config.Labels[k] = v + } + + opts := []llb.LocalOption{ + llb.SessionID(opt.SessionID), + llb.ExcludePatterns(opt.Excludes), + llb.SharedKeyHint(localNameContext), + } + if includePatterns := normalizeContextPaths(ctxPaths); includePatterns != nil { + opts = append(opts, llb.FollowPaths(includePatterns)) + } + bc := llb.Local(localNameContext, opts...) + if opt.BuildContext != nil { + bc = *opt.BuildContext + } + buildContext.Output = bc.Output() + + st := target.state.SetMarhalDefaults(llb.Platform(*opt.TargetPlatform)) + + if !implicitTargetPlatform { + target.image.OS = opt.TargetPlatform.OS + target.image.Architecture = opt.TargetPlatform.Architecture + } + + return &st, &target.image, nil +} + +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 = allDispatchStates.findStateByName(c.From) + if !ok { + stn = &dispatchState{ + stage: instructions.Stage{BaseName: c.From}, + deps: make(map[*dispatchState]struct{}), + unregistered: true, + } + } + } else { + stn, err = allDispatchStates.findStateByIndex(index) + if err != nil { + return command{}, err + } + } + cmd.sources = []*dispatchState{stn} + } + } + + if ok := detectRunMount(&cmd, allDispatchStates); ok { + return cmd, nil + } + + return cmd, nil +} + +type dispatchOpt struct { + allDispatchStates *dispatchStates + metaArgs []instructions.KeyValuePairOptional + 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 { + if ex, ok := cmd.Command.(instructions.SupportsSingleWordExpansion); ok { + err := ex.Expand(func(word string) (string, error) { + return opt.shlex.ProcessWord(word, toEnvList(d.buildArgs, d.image.Config.Env)) + }) + if err != nil { + return err + } + } + + var err error + switch c := cmd.Command.(type) { + case *instructions.MaintainerCommand: + err = dispatchMaintainer(d, c) + case *instructions.EnvCommand: + err = dispatchEnv(d, c, true) + case *instructions.RunCommand: + err = dispatchRun(d, c, opt.proxyEnv, cmd.sources, opt) + case *instructions.WorkdirCommand: + err = dispatchWorkdir(d, c, true) + case *instructions.AddCommand: + err = dispatchCopy(d, c.SourcesAndDest, opt.buildContext, true, c, "", opt) + if err == nil { + for _, src := range c.Sources() { + d.ctxPaths[path.Join("/", filepath.ToSlash(src))] = struct{}{} + } + } + case *instructions.LabelCommand: + err = dispatchLabel(d, c) + case *instructions.OnbuildCommand: + err = dispatchOnbuild(d, c) + case *instructions.CmdCommand: + err = dispatchCmd(d, c) + case *instructions.EntrypointCommand: + err = dispatchEntrypoint(d, c) + case *instructions.HealthCheckCommand: + err = dispatchHealthcheck(d, c) + case *instructions.ExposeCommand: + err = dispatchExpose(d, c, opt.shlex) + case *instructions.UserCommand: + err = dispatchUser(d, c, true) + case *instructions.VolumeCommand: + err = dispatchVolume(d, c) + case *instructions.StopSignalCommand: + err = dispatchStopSignal(d, c) + case *instructions.ShellCommand: + err = dispatchShell(d, c) + case *instructions.ArgCommand: + err = dispatchArg(d, c, opt.metaArgs, opt.buildArgValues) + case *instructions.CopyCommand: + l := opt.buildContext + if len(cmd.sources) != 0 { + l = cmd.sources[0].state + } + err = dispatchCopy(d, c.SourcesAndDest, l, false, c, c.Chown, opt) + if err == nil && len(cmd.sources) == 0 { + for _, src := range c.Sources() { + d.ctxPaths[path.Join("/", filepath.ToSlash(src))] = struct{}{} + } + } + default: + } + return err +} + +type dispatchState struct { + state llb.State + image Image + platform *specs.Platform + stage instructions.Stage + base *dispatchState + deps map[*dispatchState]struct{} + buildArgs []instructions.KeyValuePairOptional + commands []command + ctxPaths map[string]struct{} + ignoreCache bool + cmdSet bool + 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 +} + +func dispatchOnBuild(d *dispatchState, triggers []string, opt dispatchOpt) error { + for _, trigger := range triggers { + ast, err := parser.Parse(strings.NewReader(trigger)) + if err != nil { + return err + } + if len(ast.AST.Children) != 1 { + return errors.New("onbuild trigger should be a single expression") + } + ic, err := instructions.ParseCommand(ast.AST.Children[0]) + if err != nil { + return err + } + cmd, err := toCommand(ic, opt.allDispatchStates) + if err != nil { + return err + } + if err := dispatch(d, cmd, opt); err != nil { + return err + } + } + return nil +} + +func dispatchEnv(d *dispatchState, c *instructions.EnvCommand, commit bool) error { + commitMessage := bytes.NewBufferString("ENV") + for _, e := range c.Env { + commitMessage.WriteString(" " + e.String()) + d.state = d.state.AddEnv(e.Key, e.Value) + d.image.Config.Env = addEnv(d.image.Config.Env, e.Key, e.Value, true) + } + if commit { + return commitToHistory(&d.image, commitMessage.String(), false, nil) + } + return nil +} + +func dispatchRun(d *dispatchState, c *instructions.RunCommand, proxy *llb.ProxyEnv, sources []*dispatchState, dopt dispatchOpt) error { + var args []string = c.CmdLine + if c.PrependShell { + args = withShell(d.image, args) + } else if d.image.Config.Entrypoint != nil { + args = append(d.image.Config.Entrypoint, args...) + } + opt := []llb.RunOption{llb.Args(args)} + for _, arg := range d.buildArgs { + opt = append(opt, llb.AddEnv(arg.Key, arg.ValueString())) + } + opt = append(opt, dfCmd(c)) + if d.ignoreCache { + opt = append(opt, llb.IgnoreCache) + } + if proxy != nil { + opt = append(opt, llb.WithProxy(*proxy)) + } + + runMounts, err := dispatchRunMounts(d, c, sources, dopt) + if err != nil { + return err + } + opt = append(opt, runMounts...) + + d.state = d.state.Run(opt...).Root() + return commitToHistory(&d.image, "RUN "+runCommandString(args, d.buildArgs), true, &d.state) +} + +func dispatchWorkdir(d *dispatchState, c *instructions.WorkdirCommand, commit bool) error { + d.state = d.state.Dir(c.Path) + wd := c.Path + if !path.IsAbs(c.Path) { + wd = path.Join("/", d.image.Config.WorkingDir, wd) + } + d.image.Config.WorkingDir = wd + if commit { + return commitToHistory(&d.image, "WORKDIR "+wd, false, nil) + } + return nil +} + +func dispatchCopy(d *dispatchState, c instructions.SourcesAndDest, sourceState llb.State, isAddCommand bool, cmdToPrint interface{}, chown string, opt dispatchOpt) error { + // TODO: this should use CopyOp instead. Current implementation is inefficient + img := llb.Image(CopyImage, llb.Platform(opt.buildPlatforms[0])) + + dest := path.Join(".", pathRelativeToWorkingDir(d.state, c.Dest())) + if c.Dest() == "." || c.Dest()[len(c.Dest())-1] == filepath.Separator { + dest += string(filepath.Separator) + } + args := []string{"copy"} + unpack := isAddCommand + + mounts := make([]llb.RunOption, 0, len(c.Sources())) + if chown != "" { + args = append(args, fmt.Sprintf("--chown=%s", chown)) + _, _, err := parseUser(chown) + if err != nil { + mounts = append(mounts, llb.AddMount("/etc/passwd", d.state, llb.SourcePath("/etc/passwd"), llb.Readonly)) + mounts = append(mounts, llb.AddMount("/etc/group", d.state, llb.SourcePath("/etc/group"), llb.Readonly)) + } + } + + commitMessage := bytes.NewBufferString("") + if isAddCommand { + commitMessage.WriteString("ADD") + } else { + commitMessage.WriteString("COPY") + } + + for i, src := range c.Sources() { + commitMessage.WriteString(" " + src) + 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 + // + // Note: mixing up remote archives and local archives in a single ADD instruction + // would result in undefined behavior: https://github.com/moby/buildkit/pull/387#discussion_r189494717 + unpack = false + u, err := url.Parse(src) + f := "__unnamed__" + if err == nil { + if base := path.Base(u.Path); base != "." && base != "/" { + f = base + } + } + target := path.Join(fmt.Sprintf("/src-%d", i), f) + args = append(args, target) + mounts = append(mounts, llb.AddMount(path.Dir(target), llb.HTTP(src, llb.Filename(f), dfCmd(c)), llb.Readonly)) + } else { + d, f := splitWildcards(src) + targetCmd := fmt.Sprintf("/src-%d", i) + targetMount := targetCmd + if f == "" { + f = path.Base(src) + targetMount = path.Join(targetMount, f) + } + targetCmd = path.Join(targetCmd, f) + args = append(args, targetCmd) + mounts = append(mounts, llb.AddMount(targetMount, sourceState, llb.SourcePath(d), llb.Readonly)) + } + } + + commitMessage.WriteString(" " + c.Dest()) + + args = append(args, dest) + if unpack { + args = append(args[:1], append([]string{"--unpack"}, args[1:]...)...) + } + + runOpt := []llb.RunOption{llb.Args(args), llb.Dir("/dest"), llb.ReadonlyRootFS(), dfCmd(cmdToPrint)} + if d.ignoreCache { + runOpt = append(runOpt, llb.IgnoreCache) + } + run := img.Run(append(runOpt, mounts...)...) + d.state = run.AddMount("/dest", d.state).Platform(opt.targetPlatform) + + return commitToHistory(&d.image, commitMessage.String(), true, &d.state) +} + +func dispatchMaintainer(d *dispatchState, c *instructions.MaintainerCommand) error { + d.image.Author = c.Maintainer + return commitToHistory(&d.image, fmt.Sprintf("MAINTAINER %v", c.Maintainer), false, nil) +} + +func dispatchLabel(d *dispatchState, c *instructions.LabelCommand) error { + commitMessage := bytes.NewBufferString("LABEL") + if d.image.Config.Labels == nil { + d.image.Config.Labels = make(map[string]string, len(c.Labels)) + } + for _, v := range c.Labels { + d.image.Config.Labels[v.Key] = v.Value + commitMessage.WriteString(" " + v.String()) + } + return commitToHistory(&d.image, commitMessage.String(), false, nil) +} + +func dispatchOnbuild(d *dispatchState, c *instructions.OnbuildCommand) error { + d.image.Config.OnBuild = append(d.image.Config.OnBuild, c.Expression) + return nil +} + +func dispatchCmd(d *dispatchState, c *instructions.CmdCommand) error { + var args []string = c.CmdLine + if c.PrependShell { + args = withShell(d.image, args) + } + d.image.Config.Cmd = args + d.image.Config.ArgsEscaped = true + d.cmdSet = true + return commitToHistory(&d.image, fmt.Sprintf("CMD %q", args), false, nil) +} + +func dispatchEntrypoint(d *dispatchState, c *instructions.EntrypointCommand) error { + var args []string = c.CmdLine + if c.PrependShell { + args = withShell(d.image, args) + } + d.image.Config.Entrypoint = args + if !d.cmdSet { + d.image.Config.Cmd = nil + } + return commitToHistory(&d.image, fmt.Sprintf("ENTRYPOINT %q", args), false, nil) +} + +func dispatchHealthcheck(d *dispatchState, c *instructions.HealthCheckCommand) error { + d.image.Config.Healthcheck = &HealthConfig{ + Test: c.Health.Test, + Interval: c.Health.Interval, + Timeout: c.Health.Timeout, + StartPeriod: c.Health.StartPeriod, + Retries: c.Health.Retries, + } + return commitToHistory(&d.image, fmt.Sprintf("HEALTHCHECK %q", d.image.Config.Healthcheck), false, nil) +} + +func dispatchExpose(d *dispatchState, c *instructions.ExposeCommand, shlex *shell.Lex) error { + ports := []string{} + for _, p := range c.Ports { + ps, err := shlex.ProcessWords(p, toEnvList(d.buildArgs, d.image.Config.Env)) + if err != nil { + return err + } + ports = append(ports, ps...) + } + c.Ports = ports + + ps, _, err := nat.ParsePortSpecs(c.Ports) + if err != nil { + return err + } + + if d.image.Config.ExposedPorts == nil { + d.image.Config.ExposedPorts = make(map[string]struct{}) + } + for p := range ps { + d.image.Config.ExposedPorts[string(p)] = struct{}{} + } + + return commitToHistory(&d.image, fmt.Sprintf("EXPOSE %v", ps), false, nil) +} + +func dispatchUser(d *dispatchState, c *instructions.UserCommand, commit bool) error { + d.state = d.state.User(c.User) + d.image.Config.User = c.User + if commit { + return commitToHistory(&d.image, fmt.Sprintf("USER %v", c.User), false, nil) + } + return nil +} + +func dispatchVolume(d *dispatchState, c *instructions.VolumeCommand) error { + if d.image.Config.Volumes == nil { + d.image.Config.Volumes = map[string]struct{}{} + } + for _, v := range c.Volumes { + if v == "" { + return errors.New("VOLUME specified can not be an empty string") + } + d.image.Config.Volumes[v] = struct{}{} + } + return commitToHistory(&d.image, fmt.Sprintf("VOLUME %v", c.Volumes), false, nil) +} + +func dispatchStopSignal(d *dispatchState, c *instructions.StopSignalCommand) error { + if _, err := signal.ParseSignal(c.Signal); err != nil { + return err + } + d.image.Config.StopSignal = c.Signal + return commitToHistory(&d.image, fmt.Sprintf("STOPSIGNAL %v", c.Signal), false, nil) +} + +func dispatchShell(d *dispatchState, c *instructions.ShellCommand) error { + d.image.Config.Shell = c.Shell + return commitToHistory(&d.image, fmt.Sprintf("SHELL %v", c.Shell), false, nil) +} + +func dispatchArg(d *dispatchState, c *instructions.ArgCommand, metaArgs []instructions.KeyValuePairOptional, buildArgValues map[string]string) error { + commitStr := "ARG " + c.Key + buildArg := setKVValue(c.KeyValuePairOptional, buildArgValues) + + if c.Value != nil { + commitStr += "=" + *c.Value + } + if buildArg.Value == nil { + for _, ma := range metaArgs { + if ma.Key == buildArg.Key { + buildArg.Value = ma.Value + } + } + } + + d.buildArgs = append(d.buildArgs, buildArg) + return commitToHistory(&d.image, commitStr, false, nil) +} + +func pathRelativeToWorkingDir(s llb.State, p string) string { + if path.IsAbs(p) { + return p + } + return path.Join(s.GetDir(), p) +} + +func splitWildcards(name string) (string, string) { + i := 0 + for ; i < len(name); i++ { + ch := name[i] + if ch == '\\' { + i++ + } else if ch == '*' || ch == '?' || ch == '[' { + break + } + } + if i == len(name) { + return name, "" + } + + base := path.Base(name[:i]) + if name[:i] == "" || strings.HasSuffix(name[:i], string(filepath.Separator)) { + base = "" + } + return path.Dir(name[:i]), base + name[i:] +} + +func addEnv(env []string, k, v string, override bool) []string { + gotOne := false + for i, envVar := range env { + envParts := strings.SplitN(envVar, "=", 2) + compareFrom := envParts[0] + if shell.EqualEnvKeys(compareFrom, k) { + if override { + env[i] = k + "=" + v + } + gotOne = true + break + } + } + if !gotOne { + env = append(env, k+"="+v) + } + return env +} + +func setKVValue(kvpo instructions.KeyValuePairOptional, values map[string]string) instructions.KeyValuePairOptional { + if v, ok := values[kvpo.Key]; ok { + kvpo.Value = &v + } + return kvpo +} + +func toEnvList(args []instructions.KeyValuePairOptional, env []string) []string { + for _, arg := range args { + env = addEnv(env, arg.Key, arg.ValueString(), false) + } + return env +} + +func dfCmd(cmd interface{}) llb.ConstraintsOpt { + // TODO: add fmt.Stringer to instructions.Command to remove interface{} + var cmdStr string + if cmd, ok := cmd.(fmt.Stringer); ok { + cmdStr = cmd.String() + } + if cmd, ok := cmd.(string); ok { + cmdStr = cmd + } + return llb.WithDescription(map[string]string{ + "com.docker.dockerfile.v1.command": cmdStr, + }) +} + +func runCommandString(args []string, buildArgs []instructions.KeyValuePairOptional) string { + var tmpBuildEnv []string + for _, arg := range buildArgs { + tmpBuildEnv = append(tmpBuildEnv, arg.Key+"="+arg.ValueString()) + } + if len(tmpBuildEnv) > 0 { + tmpBuildEnv = append([]string{fmt.Sprintf("|%d", len(tmpBuildEnv))}, tmpBuildEnv...) + } + + return strings.Join(append(tmpBuildEnv, args...), " ") +} + +func commitToHistory(img *Image, msg string, withLayer bool, st *llb.State) error { + if st != nil { + msg += " # buildkit" + } + + img.History = append(img.History, specs.History{ + CreatedBy: msg, + Comment: historyComment, + EmptyLayer: !withLayer, + }) + return nil +} + +func isReachable(from, to *dispatchState) (ret bool) { + if from == nil { + return false + } + if from == to || isReachable(from.base, to) { + return true + } + for d := range from.deps { + if isReachable(d, to) { + return true + } + } + return false +} + +func parseUser(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 + } + } + } + return +} + +func parseUID(str string) (uint32, error) { + if str == "root" { + return 0, nil + } + uid, err := strconv.ParseUint(str, 10, 32) + if err != nil { + return 0, err + } + return uint32(uid), nil +} + +func normalizeContextPaths(paths map[string]struct{}) []string { + pathSlice := make([]string, 0, len(paths)) + for p := range paths { + if p == "/" { + return nil + } + pathSlice = append(pathSlice, p) + } + + toDelete := map[string]struct{}{} + for i := range pathSlice { + for j := range pathSlice { + if i == j { + continue + } + if strings.HasPrefix(pathSlice[j], pathSlice[i]+"/") { + delete(paths, pathSlice[j]) + } + } + } + + toSort := make([]string, 0, len(paths)) + for p := range paths { + if _, ok := toDelete[p]; !ok { + toSort = append(toSort, path.Join(".", p)) + } + } + sort.Slice(toSort, func(i, j int) bool { + return toSort[i] < toSort[j] + }) + return toSort +} + +func proxyEnvFromBuildArgs(args map[string]string) *llb.ProxyEnv { + pe := &llb.ProxyEnv{} + isNil := true + for k, v := range args { + if strings.EqualFold(k, "http_proxy") { + pe.HttpProxy = v + isNil = false + } + if strings.EqualFold(k, "https_proxy") { + pe.HttpsProxy = v + isNil = false + } + if strings.EqualFold(k, "ftp_proxy") { + pe.FtpProxy = v + isNil = false + } + if strings.EqualFold(k, "no_proxy") { + pe.NoProxy = v + isNil = false + } + } + if isNil { + return nil + } + return pe +} + +type mutableOutput struct { + llb.Output +} + +func withShell(img Image, args []string) []string { + var shell []string + if len(img.Config.Shell) > 0 { + shell = append([]string{}, img.Config.Shell...) + } else { + shell = defaultShell() + } + return append(shell, strings.Join(args, " ")) +} + +func autoDetectPlatform(img Image, target specs.Platform, supported []specs.Platform) specs.Platform { + os := img.OS + arch := img.Architecture + if target.OS == os && target.Architecture == arch { + return target + } + for _, p := range supported { + if p.OS == os && p.Architecture == arch { + return p + } + } + return target +} diff --git a/vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert_norunmount.go b/vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert_norunmount.go new file mode 100644 index 0000000000000..d7643405aa616 --- /dev/null +++ b/vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert_norunmount.go @@ -0,0 +1,16 @@ +// +build !dfrunmount,!dfextall + +package dockerfile2llb + +import ( + "github.com/moby/buildkit/client/llb" + "github.com/moby/buildkit/frontend/dockerfile/instructions" +) + +func detectRunMount(cmd *command, allDispatchStates *dispatchStates) bool { + return false +} + +func dispatchRunMounts(d *dispatchState, c *instructions.RunCommand, sources []*dispatchState, opt dispatchOpt) ([]llb.RunOption, error) { + return nil, nil +} diff --git a/vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert_runmount.go b/vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert_runmount.go new file mode 100644 index 0000000000000..aea61b36751b9 --- /dev/null +++ b/vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert_runmount.go @@ -0,0 +1,85 @@ +// +build dfrunmount dfextall + +package dockerfile2llb + +import ( + "path" + "path/filepath" + + "github.com/moby/buildkit/client/llb" + "github.com/moby/buildkit/frontend/dockerfile/instructions" + "github.com/pkg/errors" +) + +func detectRunMount(cmd *command, allDispatchStates *dispatchStates) bool { + if c, ok := cmd.Command.(*instructions.RunCommand); ok { + mounts := instructions.GetMounts(c) + sources := make([]*dispatchState, len(mounts)) + for i, mount := range mounts { + if mount.From == "" && mount.Type == instructions.MountTypeCache { + mount.From = emptyImageName + } + from := mount.From + if from == "" || mount.Type == instructions.MountTypeTmpfs { + continue + } + stn, ok := allDispatchStates.findStateByName(from) + if !ok { + stn = &dispatchState{ + stage: instructions.Stage{BaseName: from}, + deps: make(map[*dispatchState]struct{}), + unregistered: true, + } + } + sources[i] = stn + } + cmd.sources = sources + return true + } + + return false +} + +func dispatchRunMounts(d *dispatchState, c *instructions.RunCommand, sources []*dispatchState, opt dispatchOpt) ([]llb.RunOption, error) { + var out []llb.RunOption + mounts := instructions.GetMounts(c) + + for i, mount := range mounts { + if mount.From == "" && mount.Type == instructions.MountTypeCache { + mount.From = emptyImageName + } + st := opt.buildContext + if mount.From != "" { + st = sources[i].state + } + var mountOpts []llb.MountOption + if mount.Type == instructions.MountTypeTmpfs { + st = llb.Scratch() + mountOpts = append(mountOpts, llb.Tmpfs()) + } + if mount.ReadOnly { + mountOpts = append(mountOpts, llb.Readonly) + } + if mount.Type == instructions.MountTypeCache { + sharing := llb.CacheMountShared + if mount.CacheSharing == instructions.MountSharingPrivate { + sharing = llb.CacheMountPrivate + } + if mount.CacheSharing == instructions.MountSharingLocked { + sharing = llb.CacheMountLocked + } + mountOpts = append(mountOpts, llb.AsPersistentCacheDir(opt.cacheIDNamespace+"/"+mount.CacheID, sharing)) + } + target := path.Join("/", mount.Target) + if target == "/" { + return nil, errors.Errorf("invalid mount target %q", mount.Target) + } + if src := path.Join("/", mount.Source); src != "/" { + mountOpts = append(mountOpts, llb.SourcePath(src)) + } + out = append(out, llb.AddMount(target, st, mountOpts...)) + + d.ctxPaths[path.Join("/", filepath.ToSlash(mount.Source))] = struct{}{} + } + return out, nil +} diff --git a/vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/defaultshell_unix.go b/vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/defaultshell_unix.go new file mode 100644 index 0000000000000..b5d541d1f5db8 --- /dev/null +++ b/vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/defaultshell_unix.go @@ -0,0 +1,7 @@ +// +build !windows + +package dockerfile2llb + +func defaultShell() []string { + return []string{"/bin/sh", "-c"} +} diff --git a/vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/defaultshell_windows.go b/vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/defaultshell_windows.go new file mode 100644 index 0000000000000..7693e050863a4 --- /dev/null +++ b/vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/defaultshell_windows.go @@ -0,0 +1,7 @@ +// +build windows + +package dockerfile2llb + +func defaultShell() []string { + return []string{"cmd", "/S", "/C"} +} diff --git a/vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/directives.go b/vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/directives.go new file mode 100644 index 0000000000000..cf06b5ad85bb3 --- /dev/null +++ b/vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/directives.go @@ -0,0 +1,38 @@ +package dockerfile2llb + +import ( + "bufio" + "io" + "regexp" + "strings" +) + +const keySyntax = "syntax" + +var reDirective = regexp.MustCompile(`^#\s*([a-zA-Z][a-zA-Z0-9]*)\s*=\s*(.+?)\s*$`) + +func DetectSyntax(r io.Reader) (string, string, bool) { + directives := ParseDirectives(r) + if len(directives) == 0 { + return "", "", false + } + v, ok := directives[keySyntax] + if !ok { + return "", "", false + } + p := strings.SplitN(v, " ", 2) + return p[0], v, true +} + +func ParseDirectives(r io.Reader) map[string]string { + m := map[string]string{} + s := bufio.NewScanner(r) + for s.Scan() { + match := reDirective.FindStringSubmatch(s.Text()) + if len(match) == 0 { + return m + } + m[strings.ToLower(match[1])] = match[2] + } + return m +} diff --git a/vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/image.go b/vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/image.go new file mode 100644 index 0000000000000..6085d1e4d20af --- /dev/null +++ b/vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/image.go @@ -0,0 +1,74 @@ +package dockerfile2llb + +import ( + "time" + + "github.com/docker/docker/api/types/strslice" + "github.com/moby/buildkit/util/system" + specs "github.com/opencontainers/image-spec/specs-go/v1" +) + +// HealthConfig holds configuration settings for the HEALTHCHECK feature. +type HealthConfig struct { + // Test is the test to perform to check that the container is healthy. + // An empty slice means to inherit the default. + // The options are: + // {} : inherit healthcheck + // {"NONE"} : disable healthcheck + // {"CMD", args...} : exec arguments directly + // {"CMD-SHELL", command} : run command with system's default shell + Test []string `json:",omitempty"` + + // Zero means to inherit. Durations are expressed as integer nanoseconds. + Interval time.Duration `json:",omitempty"` // Interval is the time to wait between checks. + Timeout time.Duration `json:",omitempty"` // Timeout is the time to wait before considering the check to have hung. + StartPeriod time.Duration `json:",omitempty"` // The start period for the container to initialize before the retries starts to count down. + + // Retries is the number of consecutive failures needed to consider a container as unhealthy. + // Zero means inherit. + Retries int `json:",omitempty"` +} + +type ImageConfig struct { + specs.ImageConfig + + Healthcheck *HealthConfig `json:",omitempty"` // Healthcheck describes how to check the container is healthy + ArgsEscaped bool `json:",omitempty"` // True if command is already escaped (Windows specific) + + // NetworkDisabled bool `json:",omitempty"` // Is network disabled + // MacAddress string `json:",omitempty"` // Mac Address of the container + OnBuild []string // ONBUILD metadata that were defined on the image Dockerfile + StopTimeout *int `json:",omitempty"` // Timeout (in seconds) to stop a container + Shell strslice.StrSlice `json:",omitempty"` // Shell for shell-form of RUN, CMD, ENTRYPOINT +} + +// Image is the JSON structure which describes some basic information about the image. +// This provides the `application/vnd.oci.image.config.v1+json` mediatype when marshalled to JSON. +type Image struct { + specs.Image + + // Config defines the execution parameters which should be used as a base when running a container using the image. + Config ImageConfig `json:"config,omitempty"` +} + +func clone(src Image) Image { + img := src + img.Config = src.Config + img.Config.Env = append([]string{}, src.Config.Env...) + img.Config.Cmd = append([]string{}, src.Config.Cmd...) + img.Config.Entrypoint = append([]string{}, src.Config.Entrypoint...) + return img +} + +func emptyImage(platform specs.Platform) Image { + img := Image{ + Image: specs.Image{ + Architecture: platform.Architecture, + OS: platform.OS, + }, + } + img.RootFS.Type = "layers" + img.Config.WorkingDir = "/" + img.Config.Env = []string{"PATH=" + system.DefaultPathEnv} + return img +} diff --git a/vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/bflag.go b/vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/bflag.go index 7a81e3c136201..d8bf74739499e 100644 --- a/vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/bflag.go +++ b/vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/bflag.go @@ -11,6 +11,7 @@ type FlagType int const ( boolType FlagType = iota stringType + stringsType ) // BFlags contains all flags information for the builder @@ -23,10 +24,11 @@ type BFlags struct { // Flag contains all information for a flag type Flag struct { - bf *BFlags - name string - flagType FlagType - Value string + bf *BFlags + name string + flagType FlagType + Value string + StringValues []string } // NewBFlags returns the new BFlags struct @@ -70,6 +72,15 @@ func (bf *BFlags) AddString(name string, def string) *Flag { return flag } +// 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 { + return nil + } + return flag +} + // addFlag is a generic func used by the other AddXXX() func // to add a new flag to the BFlags struct. // Note, any error will be generated when Parse() is called (see Parse). @@ -145,7 +156,7 @@ func (bf *BFlags) Parse() error { return fmt.Errorf("Unknown flag: %s", arg) } - if _, ok = bf.used[arg]; ok { + if _, ok = bf.used[arg]; ok && flag.flagType != stringsType { return fmt.Errorf("Duplicate flag specified: %s", arg) } @@ -173,6 +184,12 @@ func (bf *BFlags) Parse() error { } flag.Value = value + case stringsType: + if index < 0 { + return fmt.Errorf("Missing a value on flag: %s", arg) + } + flag.StringValues = append(flag.StringValues, value) + default: panic("No idea what kind of flag we have! Should never get here!") } diff --git a/vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/commands.go b/vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/commands.go index 12478463cd222..28b34f681b014 100644 --- a/vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/commands.go +++ b/vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/commands.go @@ -6,7 +6,6 @@ import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/strslice" - specs "github.com/opencontainers/image-spec/specs-go/v1" ) // KeyValuePair represent an arbitrary named value (useful in slice instead of map[string] string to preserve ordering) @@ -19,6 +18,20 @@ func (kvp *KeyValuePair) String() string { return kvp.Key + "=" + kvp.Value } +// KeyValuePairOptional is the same as KeyValuePair but Value is optional +type KeyValuePairOptional struct { + Key string + Value *string +} + +func (kvpo *KeyValuePairOptional) ValueString() string { + v := "" + if kvpo.Value != nil { + v = *kvpo.Value + } + return v +} + // Command is implemented by every command present in a dockerfile type Command interface { Name() string @@ -160,7 +173,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 { @@ -233,6 +246,7 @@ type ShellDependantCmdLine struct { // type RunCommand struct { withNameAndCode + withExternalData ShellDependantCmdLine } @@ -346,8 +360,7 @@ func (c *StopSignalCommand) CheckPlatform(platform string) error { // Dockerfile author may optionally set a default value of this variable. type ArgCommand struct { withNameAndCode - Key string - Value *string + KeyValuePairOptional } // Expand variables @@ -381,7 +394,7 @@ type Stage struct { Commands []Command BaseName string SourceCode string - Platform specs.Platform + Platform string } // AddCommand to the stage @@ -416,3 +429,18 @@ func HasStage(s []Stage, name string) (int, bool) { } return -1, false } + +type withExternalData struct { + m map[interface{}]interface{} +} + +func (c *withExternalData) getExternalValue(k interface{}) interface{} { + return c.m[k] +} + +func (c *withExternalData) setExternalValue(k, v interface{}) { + if c.m == nil { + c.m = map[interface{}]interface{}{} + } + c.m[k] = v +} diff --git a/vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/commands_runmount.go b/vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/commands_runmount.go new file mode 100644 index 0000000000000..569c60bc68bba --- /dev/null +++ b/vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/commands_runmount.go @@ -0,0 +1,181 @@ +// +build dfrunmount dfextall + +package instructions + +import ( + "encoding/csv" + "strconv" + "strings" + + "github.com/pkg/errors" +) + +const MountTypeBind = "bind" +const MountTypeCache = "cache" +const MountTypeTmpfs = "tmpfs" + +var allowedMountTypes = map[string]struct{}{ + MountTypeBind: {}, + MountTypeCache: {}, + MountTypeTmpfs: {}, +} + +const MountSharingShared = "shared" +const MountSharingPrivate = "private" +const MountSharingLocked = "locked" + +var allowedSharingTypes = map[string]struct{}{ + MountSharingShared: {}, + MountSharingPrivate: {}, + MountSharingLocked: {}, +} + +type mountsKeyT string + +var mountsKey = mountsKeyT("dockerfile/run/mounts") + +func init() { + parseRunPreHooks = append(parseRunPreHooks, runMountPreHook) + parseRunPostHooks = append(parseRunPostHooks, runMountPostHook) +} + +func isValidMountType(s string) bool { + _, ok := allowedMountTypes[s] + return ok +} + +func runMountPreHook(cmd *RunCommand, req parseRequest) error { + st := &mountState{} + st.flag = req.flags.AddStrings("mount") + cmd.setExternalValue(mountsKey, st) + return nil +} + +func runMountPostHook(cmd *RunCommand, req parseRequest) error { + st := getMountState(cmd) + if st == nil { + return errors.Errorf("no mount state") + } + var mounts []*Mount + for _, str := range st.flag.StringValues { + m, err := parseMount(str) + if err != nil { + return err + } + mounts = append(mounts, m) + } + st.mounts = mounts + return nil +} + +func getMountState(cmd *RunCommand) *mountState { + v := cmd.getExternalValue(mountsKey) + if v == nil { + return nil + } + return v.(*mountState) +} + +func GetMounts(cmd *RunCommand) []*Mount { + return getMountState(cmd).mounts +} + +type mountState struct { + flag *Flag + mounts []*Mount +} + +type Mount struct { + Type string + From string + Source string + Target string + ReadOnly bool + CacheID string + CacheSharing string +} + +func parseMount(value string) (*Mount, error) { + csvReader := csv.NewReader(strings.NewReader(value)) + fields, err := csvReader.Read() + if err != nil { + return nil, errors.Wrap(err, "failed to parse csv mounts") + } + + m := &Mount{Type: MountTypeBind} + + roAuto := true + + for _, field := range fields { + parts := strings.SplitN(field, "=", 2) + key := strings.ToLower(parts[0]) + + if len(parts) == 1 { + switch key { + case "readonly", "ro": + m.ReadOnly = true + roAuto = false + continue + case "readwrite", "rw": + m.ReadOnly = false + roAuto = false + continue + } + } + + if len(parts) != 2 { + return nil, errors.Errorf("invalid field '%s' must be a key=value pair", field) + } + + value := parts[1] + switch key { + case "type": + if !isValidMountType(strings.ToLower(value)) { + return nil, errors.Errorf("unsupported mount type %q", value) + } + m.Type = strings.ToLower(value) + case "from": + m.From = value + case "source", "src": + m.Source = value + case "target", "dst", "destination": + m.Target = value + case "readonly", "ro": + m.ReadOnly, err = strconv.ParseBool(value) + if err != nil { + return nil, errors.Errorf("invalid value for %s: %s", key, value) + } + roAuto = false + case "readwrite", "rw": + rw, err := strconv.ParseBool(value) + if err != nil { + return nil, errors.Errorf("invalid value for %s: %s", key, value) + } + m.ReadOnly = !rw + roAuto = false + case "id": + m.CacheID = value + case "sharing": + if _, ok := allowedSharingTypes[strings.ToLower(value)]; !ok { + return nil, errors.Errorf("unsupported sharing value %q", value) + } + m.CacheSharing = strings.ToLower(value) + default: + return nil, errors.Errorf("unexpected key '%s' in '%s'", key, field) + } + } + + if roAuto { + if m.Type == MountTypeCache { + m.ReadOnly = false + } else { + m.ReadOnly = true + } + } + + if m.CacheSharing != "" && m.Type != MountTypeCache { + return nil, errors.Errorf("invalid cache sharing set for %v mount", m.Type) + } + + return m, nil +} diff --git a/vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/parse.go b/vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/parse.go index 1285ac3306332..ef1745755903a 100644 --- a/vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/parse.go +++ b/vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/parse.go @@ -10,7 +10,6 @@ import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/strslice" - "github.com/docker/docker/pkg/system" "github.com/moby/buildkit/frontend/dockerfile/command" "github.com/moby/buildkit/frontend/dockerfile/parser" "github.com/pkg/errors" @@ -24,6 +23,9 @@ type parseRequest struct { original string } +var parseRunPreHooks []func(*RunCommand, parseRequest) error +var parseRunPostHooks []func(*RunCommand, parseRequest) error + func nodeArgs(node *parser.Node) []string { result := []string{} for ; node.Next != nil; node = node.Next { @@ -276,13 +278,14 @@ func parseFrom(req parseRequest) (*Stage, error) { if err := req.flags.Parse(); err != nil { return nil, err } + code := strings.TrimSpace(req.original) return &Stage{ BaseName: req.args[0], Name: stageName, SourceCode: code, Commands: []Command{}, - Platform: *system.ParsePlatform(flPlatform.Value), + Platform: flPlatform.Value, }, nil } @@ -355,15 +358,28 @@ func parseShellDependentCommand(req parseRequest, emptyAsNil bool) ShellDependan } func parseRun(req parseRequest) (*RunCommand, error) { + cmd := &RunCommand{} + + for _, fn := range parseRunPreHooks { + if err := fn(cmd, req); err != nil { + return nil, err + } + } if err := req.flags.Parse(); err != nil { return nil, err } - return &RunCommand{ - ShellDependantCmdLine: parseShellDependentCommand(req, false), - withNameAndCode: newWithNameAndCode(req), - }, nil + cmd.ShellDependantCmdLine = parseShellDependentCommand(req, false) + cmd.withNameAndCode = newWithNameAndCode(req) + + for _, fn := range parseRunPostHooks { + if err := fn(cmd, req); err != nil { + return nil, err + } + } + + return cmd, nil } func parseCmd(req parseRequest) (*CmdCommand, error) { @@ -564,10 +580,7 @@ func parseArg(req parseRequest) (*ArgCommand, error) { return nil, errExactlyOneArgument("ARG") } - var ( - name string - newValue *string - ) + kvpo := KeyValuePairOptional{} arg := req.args[0] // 'arg' can just be a name or name-value pair. Note that this is different @@ -581,16 +594,15 @@ func parseArg(req parseRequest) (*ArgCommand, error) { return nil, errBlankCommandNames("ARG") } - name = parts[0] - newValue = &parts[1] + kvpo.Key = parts[0] + kvpo.Value = &parts[1] } else { - name = arg + kvpo.Key = arg } return &ArgCommand{ - Key: name, - Value: newValue, - withNameAndCode: newWithNameAndCode(req), + KeyValuePairOptional: kvpo, + withNameAndCode: newWithNameAndCode(req), }, nil } diff --git a/vendor/github.com/moby/buildkit/frontend/dockerfile/shell/equal_env_unix.go b/vendor/github.com/moby/buildkit/frontend/dockerfile/shell/equal_env_unix.go index 6e3f6b890eda9..36903ec58d9a9 100644 --- a/vendor/github.com/moby/buildkit/frontend/dockerfile/shell/equal_env_unix.go +++ b/vendor/github.com/moby/buildkit/frontend/dockerfile/shell/equal_env_unix.go @@ -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 } diff --git a/vendor/github.com/moby/buildkit/frontend/dockerfile/shell/equal_env_windows.go b/vendor/github.com/moby/buildkit/frontend/dockerfile/shell/equal_env_windows.go index 7780fb67e8788..010569bbaa061 100644 --- a/vendor/github.com/moby/buildkit/frontend/dockerfile/shell/equal_env_windows.go +++ b/vendor/github.com/moby/buildkit/frontend/dockerfile/shell/equal_env_windows.go @@ -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) } diff --git a/vendor/github.com/moby/buildkit/frontend/frontend.go b/vendor/github.com/moby/buildkit/frontend/frontend.go new file mode 100644 index 0000000000000..5bae3d2637b72 --- /dev/null +++ b/vendor/github.com/moby/buildkit/frontend/frontend.go @@ -0,0 +1,29 @@ +package frontend + +import ( + "context" + "io" + + "github.com/moby/buildkit/cache" + "github.com/moby/buildkit/client" + "github.com/moby/buildkit/executor" + gatewayclient "github.com/moby/buildkit/frontend/gateway/client" + digest "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/image-spec/specs-go/v1" +) + +type Frontend interface { + Solve(ctx context.Context, llb FrontendLLBBridge, opt map[string]string) (*Result, error) +} + +type FrontendLLBBridge interface { + Solve(ctx context.Context, req SolveRequest) (*Result, error) + ResolveImageConfig(ctx context.Context, ref string, platform *specs.Platform) (digest.Digest, []byte, error) + Exec(ctx context.Context, meta executor.Meta, rootfs cache.ImmutableRef, stdin io.ReadCloser, stdout, stderr io.WriteCloser) error +} + +type SolveRequest = gatewayclient.SolveRequest + +type WorkerInfos interface { + WorkerInfos() []client.WorkerInfo +} diff --git a/vendor/github.com/moby/buildkit/frontend/gateway/client/client.go b/vendor/github.com/moby/buildkit/frontend/gateway/client/client.go new file mode 100644 index 0000000000000..2198a56b797fa --- /dev/null +++ b/vendor/github.com/moby/buildkit/frontend/gateway/client/client.go @@ -0,0 +1,52 @@ +package client + +import ( + "context" + + "github.com/moby/buildkit/solver/pb" + digest "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/image-spec/specs-go/v1" +) + +type Client interface { + Solve(ctx context.Context, req SolveRequest) (*Result, error) + ResolveImageConfig(ctx context.Context, ref string, platform *specs.Platform) (digest.Digest, []byte, error) + BuildOpts() BuildOpts +} + +type Reference interface { + ReadFile(ctx context.Context, req ReadRequest) ([]byte, error) + // StatFile(ctx context.Context, req StatRequest) (*StatResponse, error) + // ReadDir(ctx context.Context, req ReadDirRequest) ([]*StatResponse, error) +} + +type ReadRequest struct { + Filename string + Range *FileRange +} + +type FileRange struct { + Offset int + Length int +} + +// SolveRequest is same as frontend.SolveRequest but avoiding dependency +type SolveRequest struct { + Definition *pb.Definition + Frontend string + FrontendOpt map[string]string + ImportCacheRefs []string +} + +type WorkerInfo struct { + ID string + Labels map[string]string + Platforms []specs.Platform +} + +type BuildOpts struct { + Opts map[string]string + SessionID string + Workers []WorkerInfo + Product string +} diff --git a/vendor/github.com/moby/buildkit/frontend/gateway/client/result.go b/vendor/github.com/moby/buildkit/frontend/gateway/client/result.go new file mode 100644 index 0000000000000..bd54228478220 --- /dev/null +++ b/vendor/github.com/moby/buildkit/frontend/gateway/client/result.go @@ -0,0 +1,54 @@ +package client + +import ( + "context" + "sync" + + "github.com/pkg/errors" +) + +type BuildFunc func(context.Context, Client) (*Result, error) + +type Result struct { + mu sync.Mutex + Ref Reference + Refs map[string]Reference + Metadata map[string][]byte +} + +func NewResult() *Result { + return &Result{} +} + +func (r *Result) AddMeta(k string, v []byte) { + r.mu.Lock() + if r.Metadata == nil { + r.Metadata = map[string][]byte{} + } + r.Metadata[k] = v + r.mu.Unlock() +} + +func (r *Result) AddRef(k string, ref Reference) { + r.mu.Lock() + if r.Refs == nil { + r.Refs = map[string]Reference{} + } + r.Refs[k] = ref + r.mu.Unlock() +} + +func (r *Result) SetRef(ref Reference) { + r.Ref = ref +} + +func (r *Result) SingleRef() (Reference, error) { + r.mu.Lock() + defer r.mu.Unlock() + + if r.Refs != nil && r.Ref == nil { + return nil, errors.Errorf("invalid map result") + } + + return r.Ref, nil +} diff --git a/vendor/github.com/moby/buildkit/frontend/gateway/forwarder/forward.go b/vendor/github.com/moby/buildkit/frontend/gateway/forwarder/forward.go new file mode 100644 index 0000000000000..65597e5eb8b0b --- /dev/null +++ b/vendor/github.com/moby/buildkit/frontend/gateway/forwarder/forward.go @@ -0,0 +1,149 @@ +package forwarder + +import ( + "context" + "sync" + + "github.com/moby/buildkit/cache" + clienttypes "github.com/moby/buildkit/client" + "github.com/moby/buildkit/frontend" + "github.com/moby/buildkit/frontend/gateway/client" + "github.com/moby/buildkit/session" + "github.com/moby/buildkit/solver" + "github.com/moby/buildkit/util/apicaps" + "github.com/moby/buildkit/worker" + "github.com/pkg/errors" +) + +func llbBridgeToGatewayClient(ctx context.Context, llbBridge frontend.FrontendLLBBridge, opts map[string]string, workerInfos []clienttypes.WorkerInfo) (*bridgeClient, error) { + return &bridgeClient{ + opts: opts, + FrontendLLBBridge: llbBridge, + sid: session.FromContext(ctx), + workerInfos: workerInfos, + final: map[*ref]struct{}{}, + }, nil +} + +type bridgeClient struct { + frontend.FrontendLLBBridge + mu sync.Mutex + opts map[string]string + final map[*ref]struct{} + sid string + exporterAttr map[string][]byte + refs []*ref + workerInfos []clienttypes.WorkerInfo +} + +func (c *bridgeClient) Solve(ctx context.Context, req client.SolveRequest) (*client.Result, error) { + res, err := c.FrontendLLBBridge.Solve(ctx, frontend.SolveRequest{ + Definition: req.Definition, + Frontend: req.Frontend, + FrontendOpt: req.FrontendOpt, + ImportCacheRefs: req.ImportCacheRefs, + }) + if err != nil { + return nil, err + } + + cRes := &client.Result{} + c.mu.Lock() + for k, r := range res.Refs { + rr := &ref{r} + c.refs = append(c.refs, rr) + cRes.AddRef(k, rr) + } + if r := res.Ref; r != nil { + rr := &ref{r} + c.refs = append(c.refs, rr) + cRes.SetRef(rr) + } + c.mu.Unlock() + cRes.Metadata = res.Metadata + + return cRes, nil +} +func (c *bridgeClient) BuildOpts() client.BuildOpts { + workers := make([]client.WorkerInfo, 0, len(c.workerInfos)) + for _, w := range c.workerInfos { + workers = append(workers, client.WorkerInfo(w)) + } + + return client.BuildOpts{ + Opts: c.opts, + SessionID: c.sid, + Workers: workers, + Product: apicaps.ExportedProduct, + } +} + +func (c *bridgeClient) toFrontendResult(r *client.Result) (*frontend.Result, error) { + if r == nil { + return nil, nil + } + + res := &frontend.Result{} + + if r.Refs != nil { + res.Refs = make(map[string]solver.CachedResult, len(r.Refs)) + for k, r := range r.Refs { + rr, ok := r.(*ref) + if !ok { + return nil, errors.Errorf("invalid reference type for forward %T", r) + } + c.final[rr] = struct{}{} + res.Refs[k] = rr.CachedResult + } + } + if r := r.Ref; r != nil { + rr, ok := r.(*ref) + if !ok { + return nil, errors.Errorf("invalid reference type for forward %T", r) + } + c.final[rr] = struct{}{} + res.Ref = rr.CachedResult + } + res.Metadata = r.Metadata + + return res, nil +} + +func (c *bridgeClient) discard(err error) { + for _, r := range c.refs { + if r != nil { + if _, ok := c.final[r]; !ok || err != nil { + r.Release(context.TODO()) + } + } + } +} + +type ref struct { + solver.CachedResult +} + +func (r *ref) ReadFile(ctx context.Context, req client.ReadRequest) ([]byte, error) { + ref, err := r.getImmutableRef() + if err != nil { + return nil, err + } + newReq := cache.ReadRequest{ + Filename: req.Filename, + } + if r := req.Range; r != nil { + newReq.Range = &cache.FileRange{ + Offset: r.Offset, + Length: r.Length, + } + } + return cache.ReadFile(ctx, ref, newReq) +} + +func (r *ref) getImmutableRef() (cache.ImmutableRef, error) { + ref, ok := r.CachedResult.Sys().(*worker.WorkerRef) + if !ok { + return nil, errors.Errorf("invalid ref: %T", r.CachedResult.Sys()) + } + return ref.ImmutableRef, nil +} diff --git a/vendor/github.com/moby/buildkit/frontend/gateway/forwarder/frontend.go b/vendor/github.com/moby/buildkit/frontend/gateway/forwarder/frontend.go new file mode 100644 index 0000000000000..61a187dc37796 --- /dev/null +++ b/vendor/github.com/moby/buildkit/frontend/gateway/forwarder/frontend.go @@ -0,0 +1,38 @@ +package forwarder + +import ( + "context" + + "github.com/moby/buildkit/frontend" + "github.com/moby/buildkit/frontend/gateway/client" +) + +func NewGatewayForwarder(w frontend.WorkerInfos, f client.BuildFunc) frontend.Frontend { + return &GatewayForwarder{ + workers: w, + f: f, + } +} + +type GatewayForwarder struct { + workers frontend.WorkerInfos + f client.BuildFunc +} + +func (gf *GatewayForwarder) Solve(ctx context.Context, llbBridge frontend.FrontendLLBBridge, opts map[string]string) (retRes *frontend.Result, retErr error) { + c, err := llbBridgeToGatewayClient(ctx, llbBridge, opts, gf.workers.WorkerInfos()) + if err != nil { + return nil, err + } + + defer func() { + c.discard(retErr) + }() + + res, err := gf.f(ctx, c) + if err != nil { + return nil, err + } + + return c.toFrontendResult(res) +} diff --git a/vendor/github.com/moby/buildkit/frontend/gateway/gateway.go b/vendor/github.com/moby/buildkit/frontend/gateway/gateway.go new file mode 100644 index 0000000000000..7f6e2c1797c11 --- /dev/null +++ b/vendor/github.com/moby/buildkit/frontend/gateway/gateway.go @@ -0,0 +1,513 @@ +package gateway + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net" + "os" + "strings" + "sync" + "time" + + "github.com/docker/distribution/reference" + apitypes "github.com/moby/buildkit/api/types" + "github.com/moby/buildkit/cache" + "github.com/moby/buildkit/client/llb" + "github.com/moby/buildkit/executor" + "github.com/moby/buildkit/frontend" + pb "github.com/moby/buildkit/frontend/gateway/pb" + "github.com/moby/buildkit/identity" + "github.com/moby/buildkit/session" + "github.com/moby/buildkit/solver" + opspb "github.com/moby/buildkit/solver/pb" + "github.com/moby/buildkit/util/apicaps" + "github.com/moby/buildkit/util/tracing" + "github.com/moby/buildkit/worker" + specs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "golang.org/x/net/http2" + spb "google.golang.org/genproto/googleapis/rpc/status" + "google.golang.org/grpc" + "google.golang.org/grpc/health" + "google.golang.org/grpc/health/grpc_health_v1" + "google.golang.org/grpc/status" +) + +const ( + keySource = "source" + keyDevel = "gateway-devel" + exporterImageConfig = "containerimage.config" +) + +func NewGatewayFrontend(w frontend.WorkerInfos) frontend.Frontend { + return &gatewayFrontend{ + workers: w, + } +} + +type gatewayFrontend struct { + workers frontend.WorkerInfos +} + +func filterPrefix(opts map[string]string, pfx string) map[string]string { + m := map[string]string{} + for k, v := range opts { + if strings.HasPrefix(k, pfx) { + m[strings.TrimPrefix(k, pfx)] = v + } + } + return m +} + +func (gf *gatewayFrontend) Solve(ctx context.Context, llbBridge frontend.FrontendLLBBridge, opts map[string]string) (ret *frontend.Result, retErr error) { + source, ok := opts[keySource] + if !ok { + return nil, errors.Errorf("no source specified for gateway") + } + + sid := session.FromContext(ctx) + + _, isDevel := opts[keyDevel] + var img specs.Image + var rootFS cache.ImmutableRef + var readonly bool // TODO: try to switch to read-only by default. + + if isDevel { + devRes, err := llbBridge.Solve(session.NewContext(ctx, "gateway:"+sid), + frontend.SolveRequest{ + Frontend: source, + FrontendOpt: filterPrefix(opts, "gateway-"), + }) + if err != nil { + return nil, err + } + defer func() { + devRes.EachRef(func(ref solver.CachedResult) error { + return ref.Release(context.TODO()) + }) + }() + if devRes.Ref == nil { + return nil, errors.Errorf("development gateway didn't return default result") + } + workerRef, ok := devRes.Ref.Sys().(*worker.WorkerRef) + if !ok { + return nil, errors.Errorf("invalid ref: %T", devRes.Ref.Sys()) + } + rootFS = workerRef.ImmutableRef + config, ok := devRes.Metadata[exporterImageConfig] + if ok { + if err := json.Unmarshal(config, &img); err != nil { + return nil, err + } + } + } else { + sourceRef, err := reference.ParseNormalizedNamed(source) + if err != nil { + return nil, err + } + + dgst, config, err := llbBridge.ResolveImageConfig(ctx, reference.TagNameOnly(sourceRef).String(), nil) // TODO: + if err != nil { + return nil, err + } + + if err := json.Unmarshal(config, &img); err != nil { + return nil, err + } + + if dgst != "" { + sourceRef, err = reference.WithDigest(sourceRef, dgst) + if err != nil { + return nil, err + } + } + + src := llb.Image(sourceRef.String()) + + def, err := src.Marshal() + if err != nil { + return nil, err + } + + res, err := llbBridge.Solve(ctx, frontend.SolveRequest{ + Definition: def.ToPB(), + }) + if err != nil { + return nil, err + } + defer func() { + res.EachRef(func(ref solver.CachedResult) error { + return ref.Release(context.TODO()) + }) + }() + if res.Ref == nil { + return nil, errors.Errorf("gateway source didn't return default result") + + } + workerRef, ok := res.Ref.Sys().(*worker.WorkerRef) + if !ok { + return nil, errors.Errorf("invalid ref: %T", res.Ref.Sys()) + } + rootFS = workerRef.ImmutableRef + } + + lbf, err := newLLBBridgeForwarder(ctx, llbBridge, gf.workers) + defer lbf.conn.Close() + if err != nil { + return nil, err + } + + args := []string{"/run"} + env := []string{} + cwd := "/" + if img.Config.Env != nil { + env = img.Config.Env + } + if img.Config.Entrypoint != nil { + args = img.Config.Entrypoint + } + if img.Config.WorkingDir != "" { + cwd = img.Config.WorkingDir + } + i := 0 + for k, v := range opts { + env = append(env, fmt.Sprintf("BUILDKIT_FRONTEND_OPT_%d", i)+"="+k+"="+v) + i++ + } + + env = append(env, "BUILDKIT_SESSION_ID="+sid) + + dt, err := json.Marshal(gf.workers.WorkerInfos()) + if err != nil { + return nil, errors.Wrap(err, "failed to marshal workers array") + } + env = append(env, "BUILDKIT_WORKERS="+string(dt)) + + defer func() { + for _, r := range lbf.refs { + if retErr == nil && lbf.result != nil { + keep := false + lbf.result.EachRef(func(r2 solver.CachedResult) error { + if r == r2 { + keep = true + } + return nil + }) + if keep { + continue + } + } + r.Release(context.TODO()) + } + }() + + env = append(env, "BUILDKIT_EXPORTEDPRODUCT="+apicaps.ExportedProduct) + + err = llbBridge.Exec(ctx, executor.Meta{ + Env: env, + Args: args, + Cwd: cwd, + ReadonlyRootFS: readonly, + }, rootFS, lbf.Stdin, lbf.Stdout, os.Stderr) + + if lbf.err != nil { + return nil, lbf.err + } + + if err != nil { + return nil, err + } + + return lbf.result, nil +} + +func newLLBBridgeForwarder(ctx context.Context, llbBridge frontend.FrontendLLBBridge, workers frontend.WorkerInfos) (*llbBridgeForwarder, error) { + lbf := &llbBridgeForwarder{ + callCtx: ctx, + llbBridge: llbBridge, + refs: map[string]solver.CachedResult{}, + pipe: newPipe(), + workers: workers, + } + + server := grpc.NewServer() + grpc_health_v1.RegisterHealthServer(server, health.NewServer()) + pb.RegisterLLBBridgeServer(server, lbf) + + go serve(ctx, server, lbf.conn) + + return lbf, nil +} + +type pipe struct { + Stdin io.ReadCloser + Stdout io.WriteCloser + conn net.Conn +} + +func newPipe() *pipe { + pr1, pw1, _ := os.Pipe() + pr2, pw2, _ := os.Pipe() + return &pipe{ + Stdin: pr1, + Stdout: pw2, + conn: &conn{ + Reader: pr2, + Writer: pw1, + Closer: pw2, + }, + } +} + +type conn struct { + io.Reader + io.Writer + io.Closer +} + +func (s *conn) LocalAddr() net.Addr { + return dummyAddr{} +} +func (s *conn) RemoteAddr() net.Addr { + return dummyAddr{} +} +func (s *conn) SetDeadline(t time.Time) error { + return nil +} +func (s *conn) SetReadDeadline(t time.Time) error { + return nil +} +func (s *conn) SetWriteDeadline(t time.Time) error { + return nil +} + +type dummyAddr struct { +} + +func (d dummyAddr) Network() string { + return "pipe" +} + +func (d dummyAddr) String() string { + return "localhost" +} + +type llbBridgeForwarder struct { + mu sync.Mutex + callCtx context.Context + llbBridge frontend.FrontendLLBBridge + refs map[string]solver.CachedResult + // lastRef solver.CachedResult + // lastRefs map[string]solver.CachedResult + // err error + result *frontend.Result + err error + exporterAttr map[string][]byte + workers frontend.WorkerInfos + *pipe +} + +func (lbf *llbBridgeForwarder) ResolveImageConfig(ctx context.Context, req *pb.ResolveImageConfigRequest) (*pb.ResolveImageConfigResponse, error) { + ctx = tracing.ContextWithSpanFromContext(ctx, lbf.callCtx) + var platform *specs.Platform + if p := req.Platform; p != nil { + platform = &specs.Platform{ + OS: p.OS, + Architecture: p.Architecture, + Variant: p.Variant, + OSVersion: p.OSVersion, + OSFeatures: p.OSFeatures, + } + } + dgst, dt, err := lbf.llbBridge.ResolveImageConfig(ctx, req.Ref, platform) + if err != nil { + return nil, err + } + return &pb.ResolveImageConfigResponse{ + Digest: dgst, + Config: dt, + }, nil +} + +func (lbf *llbBridgeForwarder) Solve(ctx context.Context, req *pb.SolveRequest) (*pb.SolveResponse, error) { + ctx = tracing.ContextWithSpanFromContext(ctx, lbf.callCtx) + res, err := lbf.llbBridge.Solve(ctx, frontend.SolveRequest{ + Definition: req.Definition, + Frontend: req.Frontend, + FrontendOpt: req.FrontendOpt, + ImportCacheRefs: req.ImportCacheRefs, + }) + if err != nil { + return nil, err + } + + if len(res.Refs) > 0 && !req.AllowResultReturn { + // this should never happen because old client shouldn't make a map request + return nil, errors.Errorf("solve did not return default result") + } + + pbRes := &pb.Result{} + var defaultID string + + lbf.mu.Lock() + if res.Refs != nil { + ids := make(map[string]string, len(res.Refs)) + for k, ref := range res.Refs { + id := identity.NewID() + if ref == nil { + id = "" + } else { + lbf.refs[id] = ref + } + ids[k] = id + } + pbRes.Result = &pb.Result_Refs{Refs: &pb.RefMap{Refs: ids}} + } else { + id := identity.NewID() + if res.Ref == nil { + id = "" + } else { + lbf.refs[id] = res.Ref + } + defaultID = id + pbRes.Result = &pb.Result_Ref{Ref: id} + } + lbf.mu.Unlock() + + // compatibility mode for older clients + if req.Final { + exp := map[string][]byte{} + if err := json.Unmarshal(req.ExporterAttr, &exp); err != nil { + return nil, err + } + + for k, v := range res.Metadata { + exp[k] = v + } + + lbf.result = &frontend.Result{ + Ref: lbf.refs[defaultID], + Metadata: exp, + } + } + + resp := &pb.SolveResponse{ + Result: pbRes, + } + + if !req.AllowResultReturn { + resp.Ref = defaultID + } + + return resp, nil +} +func (lbf *llbBridgeForwarder) ReadFile(ctx context.Context, req *pb.ReadFileRequest) (*pb.ReadFileResponse, error) { + ctx = tracing.ContextWithSpanFromContext(ctx, lbf.callCtx) + lbf.mu.Lock() + ref, ok := lbf.refs[req.Ref] + lbf.mu.Unlock() + if !ok { + return nil, errors.Errorf("no such ref: %v", req.Ref) + } + if ref == nil { + return nil, errors.Wrapf(os.ErrNotExist, "%s no found", req.FilePath) + } + workerRef, ok := ref.Sys().(*worker.WorkerRef) + if !ok { + return nil, errors.Errorf("invalid ref: %T", ref.Sys()) + } + + newReq := cache.ReadRequest{ + Filename: req.FilePath, + } + if r := req.Range; r != nil { + newReq.Range = &cache.FileRange{ + Offset: int(r.Offset), + Length: int(r.Length), + } + } + + dt, err := cache.ReadFile(ctx, workerRef.ImmutableRef, newReq) + if err != nil { + return nil, err + } + + return &pb.ReadFileResponse{Data: dt}, nil +} + +func (lbf *llbBridgeForwarder) Ping(context.Context, *pb.PingRequest) (*pb.PongResponse, error) { + + workers := lbf.workers.WorkerInfos() + pbWorkers := make([]*apitypes.WorkerRecord, 0, len(workers)) + for _, w := range workers { + pbWorkers = append(pbWorkers, &apitypes.WorkerRecord{ + ID: w.ID, + Labels: w.Labels, + Platforms: opspb.PlatformsFromSpec(w.Platforms), + }) + } + + return &pb.PongResponse{ + FrontendAPICaps: pb.Caps.All(), + Workers: pbWorkers, + // TODO: add LLB info + }, nil +} + +func (lbf *llbBridgeForwarder) Return(ctx context.Context, in *pb.ReturnRequest) (*pb.ReturnResponse, error) { + if in.Error != nil { + lbf.err = status.ErrorProto(&spb.Status{ + Code: in.Error.Code, + Message: in.Error.Message, + // Details: in.Error.Details, + }) + } else { + lbf.result = &frontend.Result{ + Metadata: in.Result.Metadata, + } + + switch res := in.Result.Result.(type) { + case *pb.Result_Ref: + ref, err := lbf.convertRef(res.Ref) + if err != nil { + return nil, err + } + lbf.result.Ref = ref + case *pb.Result_Refs: + m := map[string]solver.CachedResult{} + for k, v := range res.Refs.Refs { + ref, err := lbf.convertRef(v) + if err != nil { + return nil, err + } + m[k] = ref + } + lbf.result.Refs = m + } + } + + return &pb.ReturnResponse{}, nil +} + +func (lbf *llbBridgeForwarder) convertRef(id string) (solver.CachedResult, error) { + if id == "" { + return nil, nil + } + r, ok := lbf.refs[id] + if !ok { + return nil, errors.Errorf("return reference %s not found", id) + } + return r, nil +} + +func serve(ctx context.Context, grpcServer *grpc.Server, conn net.Conn) { + go func() { + <-ctx.Done() + conn.Close() + }() + logrus.Debugf("serving grpc connection") + (&http2.Server{}).ServeConn(conn, &http2.ServeConnOpts{Handler: grpcServer}) +} diff --git a/vendor/github.com/moby/buildkit/frontend/gateway/pb/caps.go b/vendor/github.com/moby/buildkit/frontend/gateway/pb/caps.go new file mode 100644 index 0000000000000..b5e902b1aac26 --- /dev/null +++ b/vendor/github.com/moby/buildkit/frontend/gateway/pb/caps.go @@ -0,0 +1,64 @@ +package moby_buildkit_v1_frontend + +import "github.com/moby/buildkit/util/apicaps" + +var Caps apicaps.CapList + +// Every backwards or forwards non-compatible change needs to add a new capability row. +// By default new capabilities should be experimental. After merge a capability is +// considered immutable. After a capability is marked stable it should not be disabled. + +const ( + CapSolveBase apicaps.CapID = "solve.base" + CapSolveInlineReturn apicaps.CapID = "solve.inlinereturn" + CapResolveImage apicaps.CapID = "resolveimage" + CapReadFile apicaps.CapID = "readfile" + CapReturnResult apicaps.CapID = "return" + CapReturnMap apicaps.CapID = "returnmap" +) + +func init() { + + Caps.Init(apicaps.Cap{ + ID: CapSolveBase, + Enabled: true, + Status: apicaps.CapStatusExperimental, + }) + + Caps.Init(apicaps.Cap{ + ID: CapSolveInlineReturn, + Name: "inline return from solve", + Enabled: true, + Deprecated: true, + Status: apicaps.CapStatusExperimental, + }) + + Caps.Init(apicaps.Cap{ + ID: CapResolveImage, + Name: "resolve remote image config", + Enabled: true, + Status: apicaps.CapStatusExperimental, + }) + + Caps.Init(apicaps.Cap{ + ID: CapReadFile, + Name: "read static file", + Enabled: true, + Status: apicaps.CapStatusExperimental, + }) + + Caps.Init(apicaps.Cap{ + ID: CapReturnResult, + Name: "return solve result", + Enabled: true, + Status: apicaps.CapStatusExperimental, + }) + + Caps.Init(apicaps.Cap{ + ID: CapReturnMap, + Name: "return reference map", + Enabled: true, + Status: apicaps.CapStatusExperimental, + }) + +} diff --git a/vendor/github.com/moby/buildkit/frontend/gateway/pb/gateway.pb.go b/vendor/github.com/moby/buildkit/frontend/gateway/pb/gateway.pb.go new file mode 100644 index 0000000000000..27789c60bd605 --- /dev/null +++ b/vendor/github.com/moby/buildkit/frontend/gateway/pb/gateway.pb.go @@ -0,0 +1,3360 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: gateway.proto + +/* + Package moby_buildkit_v1_frontend is a generated protocol buffer package. + + It is generated from these files: + gateway.proto + + It has these top-level messages: + Result + RefMap + ReturnRequest + ReturnResponse + ResolveImageConfigRequest + ResolveImageConfigResponse + SolveRequest + SolveResponse + ReadFileRequest + FileRange + ReadFileResponse + PingRequest + PongResponse +*/ +package moby_buildkit_v1_frontend + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" +import _ "github.com/gogo/protobuf/gogoproto" +import google_rpc "github.com/gogo/googleapis/google/rpc" +import pb "github.com/moby/buildkit/solver/pb" +import moby_buildkit_v1_types "github.com/moby/buildkit/api/types" +import moby_buildkit_v1_apicaps "github.com/moby/buildkit/util/apicaps/pb" + +import github_com_opencontainers_go_digest "github.com/opencontainers/go-digest" + +import context "golang.org/x/net/context" +import grpc "google.golang.org/grpc" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +type Result struct { + // Types that are valid to be assigned to Result: + // *Result_Ref + // *Result_Refs + Result isResult_Result `protobuf_oneof:"result"` + Metadata map[string][]byte `protobuf:"bytes,10,rep,name=metadata" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (m *Result) Reset() { *m = Result{} } +func (m *Result) String() string { return proto.CompactTextString(m) } +func (*Result) ProtoMessage() {} +func (*Result) Descriptor() ([]byte, []int) { return fileDescriptorGateway, []int{0} } + +type isResult_Result interface { + isResult_Result() + MarshalTo([]byte) (int, error) + Size() int +} + +type Result_Ref struct { + Ref string `protobuf:"bytes,1,opt,name=ref,proto3,oneof"` +} +type Result_Refs struct { + Refs *RefMap `protobuf:"bytes,2,opt,name=refs,oneof"` +} + +func (*Result_Ref) isResult_Result() {} +func (*Result_Refs) isResult_Result() {} + +func (m *Result) GetResult() isResult_Result { + if m != nil { + return m.Result + } + return nil +} + +func (m *Result) GetRef() string { + if x, ok := m.GetResult().(*Result_Ref); ok { + return x.Ref + } + return "" +} + +func (m *Result) GetRefs() *RefMap { + if x, ok := m.GetResult().(*Result_Refs); ok { + return x.Refs + } + return nil +} + +func (m *Result) GetMetadata() map[string][]byte { + if m != nil { + return m.Metadata + } + return nil +} + +// XXX_OneofFuncs is for the internal use of the proto package. +func (*Result) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { + return _Result_OneofMarshaler, _Result_OneofUnmarshaler, _Result_OneofSizer, []interface{}{ + (*Result_Ref)(nil), + (*Result_Refs)(nil), + } +} + +func _Result_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { + m := msg.(*Result) + // result + switch x := m.Result.(type) { + case *Result_Ref: + _ = b.EncodeVarint(1<<3 | proto.WireBytes) + _ = b.EncodeStringBytes(x.Ref) + case *Result_Refs: + _ = b.EncodeVarint(2<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Refs); err != nil { + return err + } + case nil: + default: + return fmt.Errorf("Result.Result has unexpected type %T", x) + } + return nil +} + +func _Result_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { + m := msg.(*Result) + switch tag { + case 1: // result.ref + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + x, err := b.DecodeStringBytes() + m.Result = &Result_Ref{x} + return true, err + case 2: // result.refs + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(RefMap) + err := b.DecodeMessage(msg) + m.Result = &Result_Refs{msg} + return true, err + default: + return false, nil + } +} + +func _Result_OneofSizer(msg proto.Message) (n int) { + m := msg.(*Result) + // result + switch x := m.Result.(type) { + case *Result_Ref: + n += proto.SizeVarint(1<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(len(x.Ref))) + n += len(x.Ref) + case *Result_Refs: + s := proto.Size(x.Refs) + n += proto.SizeVarint(2<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case nil: + default: + panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) + } + return n +} + +type RefMap struct { + Refs map[string]string `protobuf:"bytes,1,rep,name=refs" json:"refs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (m *RefMap) Reset() { *m = RefMap{} } +func (m *RefMap) String() string { return proto.CompactTextString(m) } +func (*RefMap) ProtoMessage() {} +func (*RefMap) Descriptor() ([]byte, []int) { return fileDescriptorGateway, []int{1} } + +func (m *RefMap) GetRefs() map[string]string { + if m != nil { + return m.Refs + } + return nil +} + +type ReturnRequest struct { + Result *Result `protobuf:"bytes,1,opt,name=result" json:"result,omitempty"` + Error *google_rpc.Status `protobuf:"bytes,2,opt,name=error" json:"error,omitempty"` +} + +func (m *ReturnRequest) Reset() { *m = ReturnRequest{} } +func (m *ReturnRequest) String() string { return proto.CompactTextString(m) } +func (*ReturnRequest) ProtoMessage() {} +func (*ReturnRequest) Descriptor() ([]byte, []int) { return fileDescriptorGateway, []int{2} } + +func (m *ReturnRequest) GetResult() *Result { + if m != nil { + return m.Result + } + return nil +} + +func (m *ReturnRequest) GetError() *google_rpc.Status { + if m != nil { + return m.Error + } + return nil +} + +type ReturnResponse struct { +} + +func (m *ReturnResponse) Reset() { *m = ReturnResponse{} } +func (m *ReturnResponse) String() string { return proto.CompactTextString(m) } +func (*ReturnResponse) ProtoMessage() {} +func (*ReturnResponse) Descriptor() ([]byte, []int) { return fileDescriptorGateway, []int{3} } + +type ResolveImageConfigRequest struct { + Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"` + Platform *pb.Platform `protobuf:"bytes,2,opt,name=Platform" json:"Platform,omitempty"` +} + +func (m *ResolveImageConfigRequest) Reset() { *m = ResolveImageConfigRequest{} } +func (m *ResolveImageConfigRequest) String() string { return proto.CompactTextString(m) } +func (*ResolveImageConfigRequest) ProtoMessage() {} +func (*ResolveImageConfigRequest) Descriptor() ([]byte, []int) { return fileDescriptorGateway, []int{4} } + +func (m *ResolveImageConfigRequest) GetRef() string { + if m != nil { + return m.Ref + } + return "" +} + +func (m *ResolveImageConfigRequest) GetPlatform() *pb.Platform { + if m != nil { + return m.Platform + } + return nil +} + +type ResolveImageConfigResponse struct { + Digest github_com_opencontainers_go_digest.Digest `protobuf:"bytes,1,opt,name=Digest,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"Digest"` + Config []byte `protobuf:"bytes,2,opt,name=Config,proto3" json:"Config,omitempty"` +} + +func (m *ResolveImageConfigResponse) Reset() { *m = ResolveImageConfigResponse{} } +func (m *ResolveImageConfigResponse) String() string { return proto.CompactTextString(m) } +func (*ResolveImageConfigResponse) ProtoMessage() {} +func (*ResolveImageConfigResponse) Descriptor() ([]byte, []int) { + return fileDescriptorGateway, []int{5} +} + +func (m *ResolveImageConfigResponse) GetConfig() []byte { + if m != nil { + return m.Config + } + return nil +} + +type SolveRequest struct { + Definition *pb.Definition `protobuf:"bytes,1,opt,name=Definition" json:"Definition,omitempty"` + Frontend string `protobuf:"bytes,2,opt,name=Frontend,proto3" json:"Frontend,omitempty"` + FrontendOpt map[string]string `protobuf:"bytes,3,rep,name=FrontendOpt" json:"FrontendOpt,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + ImportCacheRefs []string `protobuf:"bytes,4,rep,name=ImportCacheRefs" json:"ImportCacheRefs,omitempty"` + AllowResultReturn bool `protobuf:"varint,5,opt,name=allowResultReturn,proto3" json:"allowResultReturn,omitempty"` + // apicaps.CapSolveInlineReturn deprecated + Final bool `protobuf:"varint,10,opt,name=Final,proto3" json:"Final,omitempty"` + ExporterAttr []byte `protobuf:"bytes,11,opt,name=ExporterAttr,proto3" json:"ExporterAttr,omitempty"` +} + +func (m *SolveRequest) Reset() { *m = SolveRequest{} } +func (m *SolveRequest) String() string { return proto.CompactTextString(m) } +func (*SolveRequest) ProtoMessage() {} +func (*SolveRequest) Descriptor() ([]byte, []int) { return fileDescriptorGateway, []int{6} } + +func (m *SolveRequest) GetDefinition() *pb.Definition { + if m != nil { + return m.Definition + } + return nil +} + +func (m *SolveRequest) GetFrontend() string { + if m != nil { + return m.Frontend + } + return "" +} + +func (m *SolveRequest) GetFrontendOpt() map[string]string { + if m != nil { + return m.FrontendOpt + } + return nil +} + +func (m *SolveRequest) GetImportCacheRefs() []string { + if m != nil { + return m.ImportCacheRefs + } + return nil +} + +func (m *SolveRequest) GetAllowResultReturn() bool { + if m != nil { + return m.AllowResultReturn + } + return false +} + +func (m *SolveRequest) GetFinal() bool { + if m != nil { + return m.Final + } + return false +} + +func (m *SolveRequest) GetExporterAttr() []byte { + if m != nil { + return m.ExporterAttr + } + return nil +} + +type SolveResponse struct { + // deprecated + Ref string `protobuf:"bytes,1,opt,name=ref,proto3" json:"ref,omitempty"` + // these fields are returned when allowMapReturn was set + Result *Result `protobuf:"bytes,3,opt,name=result" json:"result,omitempty"` +} + +func (m *SolveResponse) Reset() { *m = SolveResponse{} } +func (m *SolveResponse) String() string { return proto.CompactTextString(m) } +func (*SolveResponse) ProtoMessage() {} +func (*SolveResponse) Descriptor() ([]byte, []int) { return fileDescriptorGateway, []int{7} } + +func (m *SolveResponse) GetRef() string { + if m != nil { + return m.Ref + } + return "" +} + +func (m *SolveResponse) GetResult() *Result { + if m != nil { + return m.Result + } + return nil +} + +type ReadFileRequest struct { + Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"` + FilePath string `protobuf:"bytes,2,opt,name=FilePath,proto3" json:"FilePath,omitempty"` + Range *FileRange `protobuf:"bytes,3,opt,name=Range" json:"Range,omitempty"` +} + +func (m *ReadFileRequest) Reset() { *m = ReadFileRequest{} } +func (m *ReadFileRequest) String() string { return proto.CompactTextString(m) } +func (*ReadFileRequest) ProtoMessage() {} +func (*ReadFileRequest) Descriptor() ([]byte, []int) { return fileDescriptorGateway, []int{8} } + +func (m *ReadFileRequest) GetRef() string { + if m != nil { + return m.Ref + } + return "" +} + +func (m *ReadFileRequest) GetFilePath() string { + if m != nil { + return m.FilePath + } + return "" +} + +func (m *ReadFileRequest) GetRange() *FileRange { + if m != nil { + return m.Range + } + return nil +} + +type FileRange struct { + Offset int64 `protobuf:"varint,1,opt,name=Offset,proto3" json:"Offset,omitempty"` + Length int64 `protobuf:"varint,2,opt,name=Length,proto3" json:"Length,omitempty"` +} + +func (m *FileRange) Reset() { *m = FileRange{} } +func (m *FileRange) String() string { return proto.CompactTextString(m) } +func (*FileRange) ProtoMessage() {} +func (*FileRange) Descriptor() ([]byte, []int) { return fileDescriptorGateway, []int{9} } + +func (m *FileRange) GetOffset() int64 { + if m != nil { + return m.Offset + } + return 0 +} + +func (m *FileRange) GetLength() int64 { + if m != nil { + return m.Length + } + return 0 +} + +type ReadFileResponse struct { + Data []byte `protobuf:"bytes,1,opt,name=Data,proto3" json:"Data,omitempty"` +} + +func (m *ReadFileResponse) Reset() { *m = ReadFileResponse{} } +func (m *ReadFileResponse) String() string { return proto.CompactTextString(m) } +func (*ReadFileResponse) ProtoMessage() {} +func (*ReadFileResponse) Descriptor() ([]byte, []int) { return fileDescriptorGateway, []int{10} } + +func (m *ReadFileResponse) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +type PingRequest struct { +} + +func (m *PingRequest) Reset() { *m = PingRequest{} } +func (m *PingRequest) String() string { return proto.CompactTextString(m) } +func (*PingRequest) ProtoMessage() {} +func (*PingRequest) Descriptor() ([]byte, []int) { return fileDescriptorGateway, []int{11} } + +type PongResponse struct { + FrontendAPICaps []moby_buildkit_v1_apicaps.APICap `protobuf:"bytes,1,rep,name=FrontendAPICaps" json:"FrontendAPICaps"` + LLBCaps []moby_buildkit_v1_apicaps.APICap `protobuf:"bytes,2,rep,name=LLBCaps" json:"LLBCaps"` + Workers []*moby_buildkit_v1_types.WorkerRecord `protobuf:"bytes,3,rep,name=Workers" json:"Workers,omitempty"` +} + +func (m *PongResponse) Reset() { *m = PongResponse{} } +func (m *PongResponse) String() string { return proto.CompactTextString(m) } +func (*PongResponse) ProtoMessage() {} +func (*PongResponse) Descriptor() ([]byte, []int) { return fileDescriptorGateway, []int{12} } + +func (m *PongResponse) GetFrontendAPICaps() []moby_buildkit_v1_apicaps.APICap { + if m != nil { + return m.FrontendAPICaps + } + return nil +} + +func (m *PongResponse) GetLLBCaps() []moby_buildkit_v1_apicaps.APICap { + if m != nil { + return m.LLBCaps + } + return nil +} + +func (m *PongResponse) GetWorkers() []*moby_buildkit_v1_types.WorkerRecord { + if m != nil { + return m.Workers + } + return nil +} + +func init() { + proto.RegisterType((*Result)(nil), "moby.buildkit.v1.frontend.Result") + proto.RegisterType((*RefMap)(nil), "moby.buildkit.v1.frontend.RefMap") + proto.RegisterType((*ReturnRequest)(nil), "moby.buildkit.v1.frontend.ReturnRequest") + proto.RegisterType((*ReturnResponse)(nil), "moby.buildkit.v1.frontend.ReturnResponse") + proto.RegisterType((*ResolveImageConfigRequest)(nil), "moby.buildkit.v1.frontend.ResolveImageConfigRequest") + proto.RegisterType((*ResolveImageConfigResponse)(nil), "moby.buildkit.v1.frontend.ResolveImageConfigResponse") + proto.RegisterType((*SolveRequest)(nil), "moby.buildkit.v1.frontend.SolveRequest") + proto.RegisterType((*SolveResponse)(nil), "moby.buildkit.v1.frontend.SolveResponse") + proto.RegisterType((*ReadFileRequest)(nil), "moby.buildkit.v1.frontend.ReadFileRequest") + proto.RegisterType((*FileRange)(nil), "moby.buildkit.v1.frontend.FileRange") + proto.RegisterType((*ReadFileResponse)(nil), "moby.buildkit.v1.frontend.ReadFileResponse") + proto.RegisterType((*PingRequest)(nil), "moby.buildkit.v1.frontend.PingRequest") + proto.RegisterType((*PongResponse)(nil), "moby.buildkit.v1.frontend.PongResponse") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// Client API for LLBBridge service + +type LLBBridgeClient interface { + // apicaps:CapResolveImage + ResolveImageConfig(ctx context.Context, in *ResolveImageConfigRequest, opts ...grpc.CallOption) (*ResolveImageConfigResponse, error) + // apicaps:CapSolveBase + Solve(ctx context.Context, in *SolveRequest, opts ...grpc.CallOption) (*SolveResponse, error) + // apicaps:CapReadFile + ReadFile(ctx context.Context, in *ReadFileRequest, opts ...grpc.CallOption) (*ReadFileResponse, error) + Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PongResponse, error) + Return(ctx context.Context, in *ReturnRequest, opts ...grpc.CallOption) (*ReturnResponse, error) +} + +type lLBBridgeClient struct { + cc *grpc.ClientConn +} + +func NewLLBBridgeClient(cc *grpc.ClientConn) LLBBridgeClient { + return &lLBBridgeClient{cc} +} + +func (c *lLBBridgeClient) ResolveImageConfig(ctx context.Context, in *ResolveImageConfigRequest, opts ...grpc.CallOption) (*ResolveImageConfigResponse, error) { + out := new(ResolveImageConfigResponse) + err := grpc.Invoke(ctx, "/moby.buildkit.v1.frontend.LLBBridge/ResolveImageConfig", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *lLBBridgeClient) Solve(ctx context.Context, in *SolveRequest, opts ...grpc.CallOption) (*SolveResponse, error) { + out := new(SolveResponse) + err := grpc.Invoke(ctx, "/moby.buildkit.v1.frontend.LLBBridge/Solve", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *lLBBridgeClient) ReadFile(ctx context.Context, in *ReadFileRequest, opts ...grpc.CallOption) (*ReadFileResponse, error) { + out := new(ReadFileResponse) + err := grpc.Invoke(ctx, "/moby.buildkit.v1.frontend.LLBBridge/ReadFile", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *lLBBridgeClient) Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PongResponse, error) { + out := new(PongResponse) + err := grpc.Invoke(ctx, "/moby.buildkit.v1.frontend.LLBBridge/Ping", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *lLBBridgeClient) Return(ctx context.Context, in *ReturnRequest, opts ...grpc.CallOption) (*ReturnResponse, error) { + out := new(ReturnResponse) + err := grpc.Invoke(ctx, "/moby.buildkit.v1.frontend.LLBBridge/Return", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for LLBBridge service + +type LLBBridgeServer interface { + // apicaps:CapResolveImage + ResolveImageConfig(context.Context, *ResolveImageConfigRequest) (*ResolveImageConfigResponse, error) + // apicaps:CapSolveBase + Solve(context.Context, *SolveRequest) (*SolveResponse, error) + // apicaps:CapReadFile + ReadFile(context.Context, *ReadFileRequest) (*ReadFileResponse, error) + Ping(context.Context, *PingRequest) (*PongResponse, error) + Return(context.Context, *ReturnRequest) (*ReturnResponse, error) +} + +func RegisterLLBBridgeServer(s *grpc.Server, srv LLBBridgeServer) { + s.RegisterService(&_LLBBridge_serviceDesc, srv) +} + +func _LLBBridge_ResolveImageConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ResolveImageConfigRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LLBBridgeServer).ResolveImageConfig(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/moby.buildkit.v1.frontend.LLBBridge/ResolveImageConfig", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LLBBridgeServer).ResolveImageConfig(ctx, req.(*ResolveImageConfigRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _LLBBridge_Solve_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SolveRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LLBBridgeServer).Solve(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/moby.buildkit.v1.frontend.LLBBridge/Solve", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LLBBridgeServer).Solve(ctx, req.(*SolveRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _LLBBridge_ReadFile_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ReadFileRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LLBBridgeServer).ReadFile(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/moby.buildkit.v1.frontend.LLBBridge/ReadFile", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LLBBridgeServer).ReadFile(ctx, req.(*ReadFileRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _LLBBridge_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PingRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LLBBridgeServer).Ping(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/moby.buildkit.v1.frontend.LLBBridge/Ping", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LLBBridgeServer).Ping(ctx, req.(*PingRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _LLBBridge_Return_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ReturnRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LLBBridgeServer).Return(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/moby.buildkit.v1.frontend.LLBBridge/Return", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LLBBridgeServer).Return(ctx, req.(*ReturnRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _LLBBridge_serviceDesc = grpc.ServiceDesc{ + ServiceName: "moby.buildkit.v1.frontend.LLBBridge", + HandlerType: (*LLBBridgeServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ResolveImageConfig", + Handler: _LLBBridge_ResolveImageConfig_Handler, + }, + { + MethodName: "Solve", + Handler: _LLBBridge_Solve_Handler, + }, + { + MethodName: "ReadFile", + Handler: _LLBBridge_ReadFile_Handler, + }, + { + MethodName: "Ping", + Handler: _LLBBridge_Ping_Handler, + }, + { + MethodName: "Return", + Handler: _LLBBridge_Return_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "gateway.proto", +} + +func (m *Result) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Result) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Result != nil { + nn1, err := m.Result.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += nn1 + } + if len(m.Metadata) > 0 { + for k, _ := range m.Metadata { + dAtA[i] = 0x52 + i++ + v := m.Metadata[k] + byteSize := 0 + if len(v) > 0 { + byteSize = 1 + len(v) + sovGateway(uint64(len(v))) + } + mapSize := 1 + len(k) + sovGateway(uint64(len(k))) + byteSize + i = encodeVarintGateway(dAtA, i, uint64(mapSize)) + dAtA[i] = 0xa + i++ + i = encodeVarintGateway(dAtA, i, uint64(len(k))) + i += copy(dAtA[i:], k) + if len(v) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintGateway(dAtA, i, uint64(len(v))) + i += copy(dAtA[i:], v) + } + } + } + return i, nil +} + +func (m *Result_Ref) MarshalTo(dAtA []byte) (int, error) { + i := 0 + dAtA[i] = 0xa + i++ + i = encodeVarintGateway(dAtA, i, uint64(len(m.Ref))) + i += copy(dAtA[i:], m.Ref) + return i, nil +} +func (m *Result_Refs) MarshalTo(dAtA []byte) (int, error) { + i := 0 + if m.Refs != nil { + dAtA[i] = 0x12 + i++ + i = encodeVarintGateway(dAtA, i, uint64(m.Refs.Size())) + n2, err := m.Refs.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n2 + } + return i, nil +} +func (m *RefMap) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RefMap) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Refs) > 0 { + for k, _ := range m.Refs { + dAtA[i] = 0xa + i++ + v := m.Refs[k] + mapSize := 1 + len(k) + sovGateway(uint64(len(k))) + 1 + len(v) + sovGateway(uint64(len(v))) + i = encodeVarintGateway(dAtA, i, uint64(mapSize)) + dAtA[i] = 0xa + i++ + i = encodeVarintGateway(dAtA, i, uint64(len(k))) + i += copy(dAtA[i:], k) + dAtA[i] = 0x12 + i++ + i = encodeVarintGateway(dAtA, i, uint64(len(v))) + i += copy(dAtA[i:], v) + } + } + return i, nil +} + +func (m *ReturnRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ReturnRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Result != nil { + dAtA[i] = 0xa + i++ + i = encodeVarintGateway(dAtA, i, uint64(m.Result.Size())) + n3, err := m.Result.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n3 + } + if m.Error != nil { + dAtA[i] = 0x12 + i++ + i = encodeVarintGateway(dAtA, i, uint64(m.Error.Size())) + n4, err := m.Error.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n4 + } + return i, nil +} + +func (m *ReturnResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ReturnResponse) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + return i, nil +} + +func (m *ResolveImageConfigRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ResolveImageConfigRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Ref) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintGateway(dAtA, i, uint64(len(m.Ref))) + i += copy(dAtA[i:], m.Ref) + } + if m.Platform != nil { + dAtA[i] = 0x12 + i++ + i = encodeVarintGateway(dAtA, i, uint64(m.Platform.Size())) + n5, err := m.Platform.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n5 + } + return i, nil +} + +func (m *ResolveImageConfigResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ResolveImageConfigResponse) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Digest) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintGateway(dAtA, i, uint64(len(m.Digest))) + i += copy(dAtA[i:], m.Digest) + } + if len(m.Config) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintGateway(dAtA, i, uint64(len(m.Config))) + i += copy(dAtA[i:], m.Config) + } + return i, nil +} + +func (m *SolveRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SolveRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Definition != nil { + dAtA[i] = 0xa + i++ + i = encodeVarintGateway(dAtA, i, uint64(m.Definition.Size())) + n6, err := m.Definition.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n6 + } + if len(m.Frontend) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintGateway(dAtA, i, uint64(len(m.Frontend))) + i += copy(dAtA[i:], m.Frontend) + } + if len(m.FrontendOpt) > 0 { + for k, _ := range m.FrontendOpt { + dAtA[i] = 0x1a + i++ + v := m.FrontendOpt[k] + mapSize := 1 + len(k) + sovGateway(uint64(len(k))) + 1 + len(v) + sovGateway(uint64(len(v))) + i = encodeVarintGateway(dAtA, i, uint64(mapSize)) + dAtA[i] = 0xa + i++ + i = encodeVarintGateway(dAtA, i, uint64(len(k))) + i += copy(dAtA[i:], k) + dAtA[i] = 0x12 + i++ + i = encodeVarintGateway(dAtA, i, uint64(len(v))) + i += copy(dAtA[i:], v) + } + } + if len(m.ImportCacheRefs) > 0 { + for _, s := range m.ImportCacheRefs { + dAtA[i] = 0x22 + i++ + l = len(s) + for l >= 1<<7 { + dAtA[i] = uint8(uint64(l)&0x7f | 0x80) + l >>= 7 + i++ + } + dAtA[i] = uint8(l) + i++ + i += copy(dAtA[i:], s) + } + } + if m.AllowResultReturn { + dAtA[i] = 0x28 + i++ + if m.AllowResultReturn { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if m.Final { + dAtA[i] = 0x50 + i++ + if m.Final { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if len(m.ExporterAttr) > 0 { + dAtA[i] = 0x5a + i++ + i = encodeVarintGateway(dAtA, i, uint64(len(m.ExporterAttr))) + i += copy(dAtA[i:], m.ExporterAttr) + } + return i, nil +} + +func (m *SolveResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SolveResponse) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Ref) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintGateway(dAtA, i, uint64(len(m.Ref))) + i += copy(dAtA[i:], m.Ref) + } + if m.Result != nil { + dAtA[i] = 0x1a + i++ + i = encodeVarintGateway(dAtA, i, uint64(m.Result.Size())) + n7, err := m.Result.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n7 + } + return i, nil +} + +func (m *ReadFileRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ReadFileRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Ref) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintGateway(dAtA, i, uint64(len(m.Ref))) + i += copy(dAtA[i:], m.Ref) + } + if len(m.FilePath) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintGateway(dAtA, i, uint64(len(m.FilePath))) + i += copy(dAtA[i:], m.FilePath) + } + if m.Range != nil { + dAtA[i] = 0x1a + i++ + i = encodeVarintGateway(dAtA, i, uint64(m.Range.Size())) + n8, err := m.Range.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n8 + } + return i, nil +} + +func (m *FileRange) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *FileRange) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Offset != 0 { + dAtA[i] = 0x8 + i++ + i = encodeVarintGateway(dAtA, i, uint64(m.Offset)) + } + if m.Length != 0 { + dAtA[i] = 0x10 + i++ + i = encodeVarintGateway(dAtA, i, uint64(m.Length)) + } + return i, nil +} + +func (m *ReadFileResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ReadFileResponse) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Data) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintGateway(dAtA, i, uint64(len(m.Data))) + i += copy(dAtA[i:], m.Data) + } + return i, nil +} + +func (m *PingRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PingRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + return i, nil +} + +func (m *PongResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PongResponse) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.FrontendAPICaps) > 0 { + for _, msg := range m.FrontendAPICaps { + dAtA[i] = 0xa + i++ + i = encodeVarintGateway(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if len(m.LLBCaps) > 0 { + for _, msg := range m.LLBCaps { + dAtA[i] = 0x12 + i++ + i = encodeVarintGateway(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if len(m.Workers) > 0 { + for _, msg := range m.Workers { + dAtA[i] = 0x1a + i++ + i = encodeVarintGateway(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + return i, nil +} + +func encodeVarintGateway(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *Result) Size() (n int) { + var l int + _ = l + if m.Result != nil { + n += m.Result.Size() + } + if len(m.Metadata) > 0 { + for k, v := range m.Metadata { + _ = k + _ = v + l = 0 + if len(v) > 0 { + l = 1 + len(v) + sovGateway(uint64(len(v))) + } + mapEntrySize := 1 + len(k) + sovGateway(uint64(len(k))) + l + n += mapEntrySize + 1 + sovGateway(uint64(mapEntrySize)) + } + } + return n +} + +func (m *Result_Ref) Size() (n int) { + var l int + _ = l + l = len(m.Ref) + n += 1 + l + sovGateway(uint64(l)) + return n +} +func (m *Result_Refs) Size() (n int) { + var l int + _ = l + if m.Refs != nil { + l = m.Refs.Size() + n += 1 + l + sovGateway(uint64(l)) + } + return n +} +func (m *RefMap) Size() (n int) { + var l int + _ = l + if len(m.Refs) > 0 { + for k, v := range m.Refs { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovGateway(uint64(len(k))) + 1 + len(v) + sovGateway(uint64(len(v))) + n += mapEntrySize + 1 + sovGateway(uint64(mapEntrySize)) + } + } + return n +} + +func (m *ReturnRequest) Size() (n int) { + var l int + _ = l + if m.Result != nil { + l = m.Result.Size() + n += 1 + l + sovGateway(uint64(l)) + } + if m.Error != nil { + l = m.Error.Size() + n += 1 + l + sovGateway(uint64(l)) + } + return n +} + +func (m *ReturnResponse) Size() (n int) { + var l int + _ = l + return n +} + +func (m *ResolveImageConfigRequest) Size() (n int) { + var l int + _ = l + l = len(m.Ref) + if l > 0 { + n += 1 + l + sovGateway(uint64(l)) + } + if m.Platform != nil { + l = m.Platform.Size() + n += 1 + l + sovGateway(uint64(l)) + } + return n +} + +func (m *ResolveImageConfigResponse) Size() (n int) { + var l int + _ = l + l = len(m.Digest) + if l > 0 { + n += 1 + l + sovGateway(uint64(l)) + } + l = len(m.Config) + if l > 0 { + n += 1 + l + sovGateway(uint64(l)) + } + return n +} + +func (m *SolveRequest) Size() (n int) { + var l int + _ = l + if m.Definition != nil { + l = m.Definition.Size() + n += 1 + l + sovGateway(uint64(l)) + } + l = len(m.Frontend) + if l > 0 { + n += 1 + l + sovGateway(uint64(l)) + } + if len(m.FrontendOpt) > 0 { + for k, v := range m.FrontendOpt { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovGateway(uint64(len(k))) + 1 + len(v) + sovGateway(uint64(len(v))) + n += mapEntrySize + 1 + sovGateway(uint64(mapEntrySize)) + } + } + if len(m.ImportCacheRefs) > 0 { + for _, s := range m.ImportCacheRefs { + l = len(s) + n += 1 + l + sovGateway(uint64(l)) + } + } + if m.AllowResultReturn { + n += 2 + } + if m.Final { + n += 2 + } + l = len(m.ExporterAttr) + if l > 0 { + n += 1 + l + sovGateway(uint64(l)) + } + return n +} + +func (m *SolveResponse) Size() (n int) { + var l int + _ = l + l = len(m.Ref) + if l > 0 { + n += 1 + l + sovGateway(uint64(l)) + } + if m.Result != nil { + l = m.Result.Size() + n += 1 + l + sovGateway(uint64(l)) + } + return n +} + +func (m *ReadFileRequest) Size() (n int) { + var l int + _ = l + l = len(m.Ref) + if l > 0 { + n += 1 + l + sovGateway(uint64(l)) + } + l = len(m.FilePath) + if l > 0 { + n += 1 + l + sovGateway(uint64(l)) + } + if m.Range != nil { + l = m.Range.Size() + n += 1 + l + sovGateway(uint64(l)) + } + return n +} + +func (m *FileRange) Size() (n int) { + var l int + _ = l + if m.Offset != 0 { + n += 1 + sovGateway(uint64(m.Offset)) + } + if m.Length != 0 { + n += 1 + sovGateway(uint64(m.Length)) + } + return n +} + +func (m *ReadFileResponse) Size() (n int) { + var l int + _ = l + l = len(m.Data) + if l > 0 { + n += 1 + l + sovGateway(uint64(l)) + } + return n +} + +func (m *PingRequest) Size() (n int) { + var l int + _ = l + return n +} + +func (m *PongResponse) Size() (n int) { + var l int + _ = l + if len(m.FrontendAPICaps) > 0 { + for _, e := range m.FrontendAPICaps { + l = e.Size() + n += 1 + l + sovGateway(uint64(l)) + } + } + if len(m.LLBCaps) > 0 { + for _, e := range m.LLBCaps { + l = e.Size() + n += 1 + l + sovGateway(uint64(l)) + } + } + if len(m.Workers) > 0 { + for _, e := range m.Workers { + l = e.Size() + n += 1 + l + sovGateway(uint64(l)) + } + } + return n +} + +func sovGateway(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozGateway(x uint64) (n int) { + return sovGateway(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Result) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Result: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Result: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Ref", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Result = &Result_Ref{string(dAtA[iNdEx:postIndex])} + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Refs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &RefMap{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Result = &Result_Refs{v} + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Metadata == nil { + m.Metadata = make(map[string][]byte) + } + var mapkey string + mapvalue := []byte{} + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthGateway + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var mapbyteLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapbyteLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intMapbyteLen := int(mapbyteLen) + if intMapbyteLen < 0 { + return ErrInvalidLengthGateway + } + postbytesIndex := iNdEx + intMapbyteLen + if postbytesIndex > l { + return io.ErrUnexpectedEOF + } + mapvalue = make([]byte, mapbyteLen) + copy(mapvalue, dAtA[iNdEx:postbytesIndex]) + iNdEx = postbytesIndex + } else { + iNdEx = entryPreIndex + skippy, err := skipGateway(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGateway + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Metadata[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGateway(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGateway + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RefMap) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RefMap: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RefMap: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Refs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Refs == nil { + m.Refs = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthGateway + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthGateway + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipGateway(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGateway + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Refs[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGateway(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGateway + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ReturnRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ReturnRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ReturnRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Result", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Result == nil { + m.Result = &Result{} + } + if err := m.Result.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Error == nil { + m.Error = &google_rpc.Status{} + } + if err := m.Error.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGateway(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGateway + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ReturnResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ReturnResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ReturnResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipGateway(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGateway + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ResolveImageConfigRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ResolveImageConfigRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResolveImageConfigRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Ref", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Ref = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Platform", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Platform == nil { + m.Platform = &pb.Platform{} + } + if err := m.Platform.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGateway(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGateway + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ResolveImageConfigResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ResolveImageConfigResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResolveImageConfigResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Digest", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Digest = github_com_opencontainers_go_digest.Digest(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Config", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Config = append(m.Config[:0], dAtA[iNdEx:postIndex]...) + if m.Config == nil { + m.Config = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGateway(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGateway + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SolveRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SolveRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SolveRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Definition", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Definition == nil { + m.Definition = &pb.Definition{} + } + if err := m.Definition.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Frontend", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Frontend = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FrontendOpt", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.FrontendOpt == nil { + m.FrontendOpt = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthGateway + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthGateway + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipGateway(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGateway + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.FrontendOpt[mapkey] = mapvalue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ImportCacheRefs", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ImportCacheRefs = append(m.ImportCacheRefs, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowResultReturn", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.AllowResultReturn = bool(v != 0) + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Final", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Final = bool(v != 0) + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExporterAttr", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExporterAttr = append(m.ExporterAttr[:0], dAtA[iNdEx:postIndex]...) + if m.ExporterAttr == nil { + m.ExporterAttr = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGateway(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGateway + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SolveResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SolveResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SolveResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Ref", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Ref = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Result", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Result == nil { + m.Result = &Result{} + } + if err := m.Result.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGateway(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGateway + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ReadFileRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ReadFileRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ReadFileRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Ref", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Ref = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FilePath", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FilePath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Range", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Range == nil { + m.Range = &FileRange{} + } + if err := m.Range.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGateway(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGateway + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FileRange) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: FileRange: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FileRange: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Offset", wireType) + } + m.Offset = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Offset |= (int64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Length", wireType) + } + m.Length = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Length |= (int64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipGateway(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGateway + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ReadFileResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ReadFileResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ReadFileResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGateway(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGateway + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PingRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PingRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PingRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipGateway(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGateway + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PongResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PongResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PongResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FrontendAPICaps", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FrontendAPICaps = append(m.FrontendAPICaps, moby_buildkit_v1_apicaps.APICap{}) + if err := m.FrontendAPICaps[len(m.FrontendAPICaps)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LLBCaps", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.LLBCaps = append(m.LLBCaps, moby_buildkit_v1_apicaps.APICap{}) + if err := m.LLBCaps[len(m.LLBCaps)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Workers", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Workers = append(m.Workers, &moby_buildkit_v1_types.WorkerRecord{}) + if err := m.Workers[len(m.Workers)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGateway(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGateway + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGateway(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGateway + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGateway + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGateway + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthGateway + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGateway + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipGateway(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthGateway = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGateway = fmt.Errorf("proto: integer overflow") +) + +func init() { proto.RegisterFile("gateway.proto", fileDescriptorGateway) } + +var fileDescriptorGateway = []byte{ + // 969 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x56, 0x4b, 0x6f, 0xdb, 0x46, + 0x10, 0x0e, 0x4d, 0x49, 0x91, 0x46, 0x52, 0xac, 0x2e, 0x8a, 0x42, 0xe1, 0xc1, 0x51, 0x89, 0x22, + 0x65, 0xf3, 0x20, 0x51, 0xa5, 0x45, 0xd2, 0x04, 0x48, 0x1b, 0xd9, 0x31, 0xe2, 0x56, 0x41, 0x84, + 0xcd, 0xc1, 0x40, 0xd0, 0x1e, 0x56, 0xd2, 0x92, 0x26, 0x4c, 0x71, 0xd9, 0xe5, 0xca, 0xae, 0xd0, + 0x4b, 0xdb, 0x53, 0x7e, 0x5a, 0x8e, 0x3d, 0xf7, 0x10, 0x14, 0xbe, 0xf5, 0x5f, 0x14, 0xfb, 0xa0, + 0x4c, 0xbf, 0x64, 0xfb, 0xa4, 0x9d, 0xe5, 0x7c, 0x33, 0xdf, 0xce, 0x7c, 0xb3, 0x2b, 0x68, 0x47, + 0x44, 0xd0, 0x43, 0xb2, 0xf0, 0x33, 0xce, 0x04, 0x43, 0xb7, 0x67, 0x6c, 0xbc, 0xf0, 0xc7, 0xf3, + 0x38, 0x99, 0xee, 0xc7, 0xc2, 0x3f, 0xf8, 0xda, 0x0f, 0x39, 0x4b, 0x05, 0x4d, 0xa7, 0xce, 0xc3, + 0x28, 0x16, 0x7b, 0xf3, 0xb1, 0x3f, 0x61, 0xb3, 0x20, 0x62, 0x11, 0x0b, 0x14, 0x62, 0x3c, 0x0f, + 0x95, 0xa5, 0x0c, 0xb5, 0xd2, 0x91, 0x9c, 0xfe, 0x69, 0xf7, 0x88, 0xb1, 0x28, 0xa1, 0x24, 0x8b, + 0x73, 0xb3, 0x0c, 0x78, 0x36, 0x09, 0x72, 0x41, 0xc4, 0x3c, 0x37, 0x98, 0x07, 0x25, 0x8c, 0x24, + 0x12, 0x14, 0x44, 0x82, 0x9c, 0x25, 0x07, 0x94, 0x07, 0xd9, 0x38, 0x60, 0x59, 0xe1, 0x1d, 0x5c, + 0xe8, 0x4d, 0xb2, 0x38, 0x10, 0x8b, 0x8c, 0xe6, 0xc1, 0x21, 0xe3, 0xfb, 0x94, 0x1b, 0xc0, 0xa3, + 0x0b, 0x01, 0x73, 0x11, 0x27, 0x12, 0x35, 0x21, 0x59, 0x2e, 0x93, 0xc8, 0x5f, 0x0d, 0x72, 0xff, + 0xb3, 0xa0, 0x86, 0x69, 0x3e, 0x4f, 0x04, 0x42, 0x60, 0x73, 0x1a, 0x76, 0xad, 0x9e, 0xe5, 0x35, + 0x5e, 0xdd, 0xc0, 0xd2, 0x40, 0x8f, 0xa1, 0xc2, 0x69, 0x98, 0x77, 0xd7, 0x7a, 0x96, 0xd7, 0xec, + 0x7f, 0xee, 0x5f, 0x58, 0x3f, 0x1f, 0xd3, 0xf0, 0x35, 0xc9, 0x5e, 0xdd, 0xc0, 0x0a, 0x80, 0x7e, + 0x82, 0xfa, 0x8c, 0x0a, 0x32, 0x25, 0x82, 0x74, 0xa1, 0x67, 0x7b, 0xcd, 0x7e, 0xb0, 0x12, 0x2c, + 0x19, 0xf8, 0xaf, 0x0d, 0xe2, 0x65, 0x2a, 0xf8, 0x02, 0x2f, 0x03, 0x38, 0xcf, 0xa0, 0x7d, 0xe2, + 0x13, 0xea, 0x80, 0xbd, 0x4f, 0x17, 0x9a, 0x2a, 0x96, 0x4b, 0xf4, 0x29, 0x54, 0x0f, 0x48, 0x32, + 0xa7, 0x8a, 0x69, 0x0b, 0x6b, 0xe3, 0xe9, 0xda, 0x13, 0x6b, 0x50, 0x87, 0x1a, 0x57, 0xe1, 0xdd, + 0xbf, 0xd4, 0x59, 0x25, 0x4d, 0xf4, 0xbd, 0x39, 0x97, 0xa5, 0xa8, 0xdd, 0xbf, 0xf4, 0x5c, 0xf2, + 0x27, 0xd7, 0xb4, 0x14, 0xd0, 0x79, 0x0c, 0x8d, 0xe5, 0xd6, 0x65, 0x74, 0x1a, 0x25, 0x3a, 0xae, + 0x80, 0x36, 0xa6, 0x62, 0xce, 0x53, 0x4c, 0x7f, 0x9d, 0xd3, 0x5c, 0xa0, 0xef, 0x0a, 0x7e, 0x0a, + 0x7f, 0x59, 0x91, 0xa5, 0x23, 0x36, 0x00, 0xe4, 0x41, 0x95, 0x72, 0xce, 0xb8, 0x69, 0x0f, 0xf2, + 0xb5, 0xf2, 0x7c, 0x9e, 0x4d, 0xfc, 0xb7, 0x4a, 0x79, 0x58, 0x3b, 0xb8, 0x1d, 0xb8, 0x55, 0x64, + 0xcd, 0x33, 0x96, 0xe6, 0xd4, 0xdd, 0x85, 0xdb, 0x98, 0x2a, 0xdd, 0xed, 0xcc, 0x48, 0x44, 0x37, + 0x59, 0x1a, 0xc6, 0x51, 0xc1, 0xa9, 0x03, 0x36, 0x2e, 0xa4, 0x80, 0xe5, 0x12, 0x79, 0x50, 0x1f, + 0x25, 0x44, 0x84, 0x8c, 0xcf, 0x4c, 0xb6, 0x96, 0x9f, 0x8d, 0xfd, 0x62, 0x0f, 0x2f, 0xbf, 0xba, + 0x7f, 0x58, 0xe0, 0x9c, 0x17, 0x59, 0xe7, 0x45, 0x3f, 0x42, 0x6d, 0x2b, 0x8e, 0x68, 0xae, 0x8f, + 0xdb, 0x18, 0xf4, 0x3f, 0x7c, 0xbc, 0x73, 0xe3, 0x9f, 0x8f, 0x77, 0xee, 0x95, 0xd4, 0xcb, 0x32, + 0x9a, 0x4e, 0x58, 0x2a, 0x48, 0x9c, 0x52, 0x2e, 0xe7, 0xe9, 0xe1, 0x54, 0x41, 0x7c, 0x8d, 0xc4, + 0x26, 0x02, 0xfa, 0x0c, 0x6a, 0x3a, 0xba, 0xe9, 0xba, 0xb1, 0xdc, 0xf7, 0x36, 0xb4, 0xde, 0x4a, + 0x02, 0xc5, 0x79, 0x7c, 0x80, 0x2d, 0x1a, 0xc6, 0x69, 0x2c, 0x62, 0x96, 0x9a, 0x3a, 0xdf, 0x92, + 0xfc, 0x8f, 0x77, 0x71, 0xc9, 0x03, 0x39, 0x50, 0xdf, 0x36, 0x35, 0x37, 0x1d, 0x5c, 0xda, 0xe8, + 0x1d, 0x34, 0x8b, 0xf5, 0x9b, 0x4c, 0x74, 0x6d, 0xa5, 0xa0, 0x27, 0x2b, 0x9a, 0x56, 0x66, 0xe2, + 0x97, 0xa0, 0x5a, 0x4e, 0xe5, 0x60, 0xc8, 0x83, 0xf5, 0x9d, 0x59, 0xc6, 0xb8, 0xd8, 0x24, 0x93, + 0x3d, 0x2a, 0x05, 0xd6, 0xad, 0xf4, 0x6c, 0xaf, 0x81, 0x4f, 0x6f, 0xa3, 0x07, 0xf0, 0x09, 0x49, + 0x12, 0x76, 0x68, 0x14, 0xa1, 0x7a, 0xdb, 0xad, 0xf6, 0x2c, 0xaf, 0x8e, 0xcf, 0x7e, 0x90, 0x72, + 0xdc, 0x8e, 0x53, 0x92, 0x74, 0x41, 0x79, 0x68, 0x03, 0xb9, 0xd0, 0x7a, 0xf9, 0x9b, 0x0c, 0x4b, + 0xf9, 0x0b, 0x21, 0x78, 0xb7, 0xa9, 0x8a, 0x78, 0x62, 0xcf, 0x79, 0x0e, 0x9d, 0xd3, 0x94, 0xaf, + 0x25, 0xf7, 0x9f, 0xa1, 0x6d, 0xce, 0x6f, 0xfa, 0xdf, 0x29, 0xdd, 0x32, 0xfa, 0x8e, 0x39, 0x1e, + 0x00, 0xfb, 0x9a, 0x03, 0xe0, 0xfe, 0x0e, 0xeb, 0x98, 0x92, 0xe9, 0x76, 0x9c, 0xd0, 0x8b, 0xa5, + 0x2b, 0x9b, 0x19, 0x27, 0x74, 0x44, 0xc4, 0xde, 0xb2, 0x99, 0xc6, 0x46, 0x4f, 0xa1, 0x8a, 0x49, + 0x1a, 0x51, 0x93, 0xfa, 0x8b, 0x15, 0xa9, 0x55, 0x12, 0xe9, 0x8b, 0x35, 0xc4, 0x7d, 0x06, 0x8d, + 0xe5, 0x9e, 0x94, 0xe2, 0x9b, 0x30, 0xcc, 0xa9, 0x96, 0xb5, 0x8d, 0x8d, 0x25, 0xf7, 0x87, 0x34, + 0x8d, 0x4c, 0x6a, 0x1b, 0x1b, 0xcb, 0xbd, 0x0b, 0x9d, 0x63, 0xe6, 0xa6, 0x34, 0x08, 0x2a, 0x5b, + 0xf2, 0xbe, 0xb4, 0x54, 0x1f, 0xd4, 0xda, 0x6d, 0x43, 0x73, 0x14, 0xa7, 0xc5, 0x60, 0xba, 0x47, + 0x16, 0xb4, 0x46, 0x2c, 0x3d, 0x1e, 0xa7, 0x11, 0xac, 0x17, 0xfd, 0x79, 0x31, 0xda, 0xd9, 0x24, + 0x59, 0x71, 0xa7, 0xf5, 0xce, 0x1e, 0xc5, 0xbc, 0x00, 0xbe, 0x76, 0x1c, 0x54, 0xe4, 0xe4, 0xe1, + 0xd3, 0x70, 0xf4, 0x03, 0xdc, 0x1c, 0x0e, 0x07, 0x2a, 0xd2, 0xda, 0xb5, 0x22, 0x15, 0x30, 0xf4, + 0x1c, 0x6e, 0xee, 0xaa, 0x87, 0x29, 0x37, 0xd3, 0x71, 0x4e, 0x59, 0xd5, 0xfb, 0xe5, 0x6b, 0x37, + 0x4c, 0x27, 0x8c, 0x4f, 0x71, 0x01, 0xea, 0xbf, 0xaf, 0x40, 0x63, 0x38, 0x1c, 0x0c, 0x78, 0x3c, + 0x8d, 0x28, 0xfa, 0xd3, 0x02, 0x74, 0xf6, 0x3e, 0x41, 0xdf, 0xac, 0x56, 0xc9, 0xf9, 0x17, 0x9b, + 0xf3, 0xed, 0x35, 0x51, 0xa6, 0xca, 0xef, 0xa0, 0xaa, 0x54, 0x8c, 0xbe, 0xbc, 0xe2, 0x9c, 0x3b, + 0xde, 0xe5, 0x8e, 0x26, 0xf6, 0x04, 0xea, 0x85, 0x12, 0xd0, 0xbd, 0x95, 0xf4, 0x4e, 0x08, 0xdd, + 0xb9, 0x7f, 0x25, 0x5f, 0x93, 0x64, 0x17, 0x2a, 0x52, 0x46, 0xe8, 0xee, 0x0a, 0x50, 0x49, 0x67, + 0xce, 0xaa, 0x73, 0x9e, 0xd0, 0xdf, 0x2f, 0xf2, 0x49, 0x55, 0x77, 0x8c, 0xb7, 0x92, 0x4f, 0xe9, + 0xc5, 0x73, 0xbe, 0xba, 0x82, 0xa7, 0x0e, 0x3f, 0x68, 0x7d, 0x38, 0xda, 0xb0, 0xfe, 0x3e, 0xda, + 0xb0, 0xfe, 0x3d, 0xda, 0xb0, 0xc6, 0x35, 0xf5, 0x9f, 0xe5, 0xd1, 0xff, 0x01, 0x00, 0x00, 0xff, + 0xff, 0xb8, 0xcb, 0x8c, 0xfa, 0xd6, 0x09, 0x00, 0x00, +} diff --git a/vendor/github.com/moby/buildkit/frontend/gateway/pb/gateway.proto b/vendor/github.com/moby/buildkit/frontend/gateway/pb/gateway.proto new file mode 100644 index 0000000000000..8f40a335b09bb --- /dev/null +++ b/vendor/github.com/moby/buildkit/frontend/gateway/pb/gateway.proto @@ -0,0 +1,99 @@ +syntax = "proto3"; + +package moby.buildkit.v1.frontend; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/gogo/googleapis/google/rpc/status.proto"; +import "github.com/moby/buildkit/solver/pb/ops.proto"; +import "github.com/moby/buildkit/api/types/worker.proto"; +import "github.com/moby/buildkit/util/apicaps/pb/caps.proto"; + +option (gogoproto.sizer_all) = true; +option (gogoproto.marshaler_all) = true; +option (gogoproto.unmarshaler_all) = true; + +service LLBBridge { + // apicaps:CapResolveImage + rpc ResolveImageConfig(ResolveImageConfigRequest) returns (ResolveImageConfigResponse); + // apicaps:CapSolveBase + rpc Solve(SolveRequest) returns (SolveResponse); + // apicaps:CapReadFile + rpc ReadFile(ReadFileRequest) returns (ReadFileResponse); + rpc Ping(PingRequest) returns (PongResponse); + rpc Return(ReturnRequest) returns (ReturnResponse); +} + +message Result { + oneof result { + string ref = 1; + RefMap refs = 2; + } + map metadata = 10; +} + +message RefMap { + map refs = 1; +} + +message ReturnRequest { + Result result = 1; + google.rpc.Status error = 2; +} + +message ReturnResponse { +} + +message ResolveImageConfigRequest { + string Ref = 1; + pb.Platform Platform = 2; +} + +message ResolveImageConfigResponse { + string Digest = 1 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false]; + bytes Config = 2; +} + +message SolveRequest { + pb.Definition Definition = 1; + string Frontend = 2; + map FrontendOpt = 3; + repeated string ImportCacheRefs = 4; + bool allowResultReturn = 5; + + // apicaps.CapSolveInlineReturn deprecated + bool Final = 10; + bytes ExporterAttr = 11; +} + +message SolveResponse { + // deprecated + string ref = 1; // can be used by readfile request + // deprecated +/* bytes ExporterAttr = 2;*/ + + // these fields are returned when allowMapReturn was set + Result result = 3; +} + +message ReadFileRequest { + string Ref = 1; + string FilePath = 2; + FileRange Range = 3; +} + +message FileRange { + int64 Offset = 1; + int64 Length = 2; +} + +message ReadFileResponse { + bytes Data = 1; +} + +message PingRequest{ +} +message PongResponse{ + repeated moby.buildkit.v1.apicaps.APICap FrontendAPICaps = 1 [(gogoproto.nullable) = false]; + repeated moby.buildkit.v1.apicaps.APICap LLBCaps = 2 [(gogoproto.nullable) = false]; + repeated moby.buildkit.v1.types.WorkerRecord Workers = 3; +} \ No newline at end of file diff --git a/vendor/github.com/moby/buildkit/frontend/gateway/pb/generate.go b/vendor/github.com/moby/buildkit/frontend/gateway/pb/generate.go new file mode 100644 index 0000000000000..4ab07c6d4a75c --- /dev/null +++ b/vendor/github.com/moby/buildkit/frontend/gateway/pb/generate.go @@ -0,0 +1,3 @@ +package moby_buildkit_v1_frontend + +//go:generate protoc -I=. -I=../../../vendor/ -I=../../../../../../ --gogo_out=plugins=grpc:. gateway.proto diff --git a/vendor/github.com/moby/buildkit/frontend/result.go b/vendor/github.com/moby/buildkit/frontend/result.go new file mode 100644 index 0000000000000..37715de8a3f9f --- /dev/null +++ b/vendor/github.com/moby/buildkit/frontend/result.go @@ -0,0 +1,23 @@ +package frontend + +import "github.com/moby/buildkit/solver" + +type Result struct { + Ref solver.CachedResult + Refs map[string]solver.CachedResult + Metadata map[string][]byte +} + +func (r *Result) EachRef(fn func(solver.CachedResult) error) (err error) { + if r.Ref != nil { + err = fn(r.Ref) + } + for _, r := range r.Refs { + if r != nil { + if err1 := fn(r); err1 != nil && err == nil { + err = err1 + } + } + } + return err +} diff --git a/vendor/github.com/moby/buildkit/session/auth/auth.go b/vendor/github.com/moby/buildkit/session/auth/auth.go new file mode 100644 index 0000000000000..2b96a7cef1a33 --- /dev/null +++ b/vendor/github.com/moby/buildkit/session/auth/auth.go @@ -0,0 +1,26 @@ +package auth + +import ( + "context" + + "github.com/moby/buildkit/session" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func CredentialsFunc(ctx context.Context, c session.Caller) func(string) (string, string, error) { + return func(host string) (string, string, error) { + client := NewAuthClient(c.Conn()) + + resp, err := client.Credentials(ctx, &CredentialsRequest{ + Host: host, + }) + if err != nil { + if st, ok := status.FromError(err); ok && st.Code() == codes.Unimplemented { + return "", "", nil + } + return "", "", err + } + return resp.Username, resp.Secret, nil + } +} diff --git a/vendor/github.com/moby/buildkit/session/auth/auth.pb.go b/vendor/github.com/moby/buildkit/session/auth/auth.pb.go new file mode 100644 index 0000000000000..8993b85b96eda --- /dev/null +++ b/vendor/github.com/moby/buildkit/session/auth/auth.pb.go @@ -0,0 +1,673 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: auth.proto + +/* + Package auth is a generated protocol buffer package. + + It is generated from these files: + auth.proto + + It has these top-level messages: + CredentialsRequest + CredentialsResponse +*/ +package auth + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" + +import strings "strings" +import reflect "reflect" + +import context "golang.org/x/net/context" +import grpc "google.golang.org/grpc" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +type CredentialsRequest struct { + Host string `protobuf:"bytes,1,opt,name=Host,proto3" json:"Host,omitempty"` +} + +func (m *CredentialsRequest) Reset() { *m = CredentialsRequest{} } +func (*CredentialsRequest) ProtoMessage() {} +func (*CredentialsRequest) Descriptor() ([]byte, []int) { return fileDescriptorAuth, []int{0} } + +func (m *CredentialsRequest) GetHost() string { + if m != nil { + return m.Host + } + return "" +} + +type CredentialsResponse struct { + Username string `protobuf:"bytes,1,opt,name=Username,proto3" json:"Username,omitempty"` + Secret string `protobuf:"bytes,2,opt,name=Secret,proto3" json:"Secret,omitempty"` +} + +func (m *CredentialsResponse) Reset() { *m = CredentialsResponse{} } +func (*CredentialsResponse) ProtoMessage() {} +func (*CredentialsResponse) Descriptor() ([]byte, []int) { return fileDescriptorAuth, []int{1} } + +func (m *CredentialsResponse) GetUsername() string { + if m != nil { + return m.Username + } + return "" +} + +func (m *CredentialsResponse) GetSecret() string { + if m != nil { + return m.Secret + } + return "" +} + +func init() { + proto.RegisterType((*CredentialsRequest)(nil), "moby.filesync.v1.CredentialsRequest") + proto.RegisterType((*CredentialsResponse)(nil), "moby.filesync.v1.CredentialsResponse") +} +func (this *CredentialsRequest) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*CredentialsRequest) + if !ok { + that2, ok := that.(CredentialsRequest) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Host != that1.Host { + return false + } + return true +} +func (this *CredentialsResponse) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*CredentialsResponse) + if !ok { + that2, ok := that.(CredentialsResponse) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Username != that1.Username { + return false + } + if this.Secret != that1.Secret { + return false + } + return true +} +func (this *CredentialsRequest) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 5) + s = append(s, "&auth.CredentialsRequest{") + s = append(s, "Host: "+fmt.Sprintf("%#v", this.Host)+",\n") + s = append(s, "}") + return strings.Join(s, "") +} +func (this *CredentialsResponse) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 6) + s = append(s, "&auth.CredentialsResponse{") + s = append(s, "Username: "+fmt.Sprintf("%#v", this.Username)+",\n") + s = append(s, "Secret: "+fmt.Sprintf("%#v", this.Secret)+",\n") + s = append(s, "}") + return strings.Join(s, "") +} +func valueToGoStringAuth(v interface{}, typ string) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv) +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// Client API for Auth service + +type AuthClient interface { + Credentials(ctx context.Context, in *CredentialsRequest, opts ...grpc.CallOption) (*CredentialsResponse, error) +} + +type authClient struct { + cc *grpc.ClientConn +} + +func NewAuthClient(cc *grpc.ClientConn) AuthClient { + return &authClient{cc} +} + +func (c *authClient) Credentials(ctx context.Context, in *CredentialsRequest, opts ...grpc.CallOption) (*CredentialsResponse, error) { + out := new(CredentialsResponse) + err := grpc.Invoke(ctx, "/moby.filesync.v1.Auth/Credentials", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for Auth service + +type AuthServer interface { + Credentials(context.Context, *CredentialsRequest) (*CredentialsResponse, error) +} + +func RegisterAuthServer(s *grpc.Server, srv AuthServer) { + s.RegisterService(&_Auth_serviceDesc, srv) +} + +func _Auth_Credentials_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CredentialsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AuthServer).Credentials(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/moby.filesync.v1.Auth/Credentials", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AuthServer).Credentials(ctx, req.(*CredentialsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Auth_serviceDesc = grpc.ServiceDesc{ + ServiceName: "moby.filesync.v1.Auth", + HandlerType: (*AuthServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Credentials", + Handler: _Auth_Credentials_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "auth.proto", +} + +func (m *CredentialsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CredentialsRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Host) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintAuth(dAtA, i, uint64(len(m.Host))) + i += copy(dAtA[i:], m.Host) + } + return i, nil +} + +func (m *CredentialsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CredentialsResponse) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Username) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintAuth(dAtA, i, uint64(len(m.Username))) + i += copy(dAtA[i:], m.Username) + } + if len(m.Secret) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintAuth(dAtA, i, uint64(len(m.Secret))) + i += copy(dAtA[i:], m.Secret) + } + return i, nil +} + +func encodeVarintAuth(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *CredentialsRequest) Size() (n int) { + var l int + _ = l + l = len(m.Host) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + return n +} + +func (m *CredentialsResponse) Size() (n int) { + var l int + _ = l + l = len(m.Username) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + l = len(m.Secret) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + return n +} + +func sovAuth(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozAuth(x uint64) (n int) { + return sovAuth(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *CredentialsRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CredentialsRequest{`, + `Host:` + fmt.Sprintf("%v", this.Host) + `,`, + `}`, + }, "") + return s +} +func (this *CredentialsResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CredentialsResponse{`, + `Username:` + fmt.Sprintf("%v", this.Username) + `,`, + `Secret:` + fmt.Sprintf("%v", this.Secret) + `,`, + `}`, + }, "") + return s +} +func valueToStringAuth(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *CredentialsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CredentialsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CredentialsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Host", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Host = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAuth(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CredentialsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CredentialsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CredentialsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Username", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Username = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Secret", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Secret = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAuth(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipAuth(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuth + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuth + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuth + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthAuth + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuth + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipAuth(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthAuth = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowAuth = fmt.Errorf("proto: integer overflow") +) + +func init() { proto.RegisterFile("auth.proto", fileDescriptorAuth) } + +var fileDescriptorAuth = []byte{ + // 224 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4a, 0x2c, 0x2d, 0xc9, + 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0xc8, 0xcd, 0x4f, 0xaa, 0xd4, 0x4b, 0xcb, 0xcc, + 0x49, 0x2d, 0xae, 0xcc, 0x4b, 0xd6, 0x2b, 0x33, 0x54, 0xd2, 0xe0, 0x12, 0x72, 0x2e, 0x4a, 0x4d, + 0x49, 0xcd, 0x2b, 0xc9, 0x4c, 0xcc, 0x29, 0x0e, 0x4a, 0x2d, 0x2c, 0x4d, 0x2d, 0x2e, 0x11, 0x12, + 0xe2, 0x62, 0xf1, 0xc8, 0x2f, 0x2e, 0x91, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x02, 0xb3, 0x95, + 0x3c, 0xb9, 0x84, 0x51, 0x54, 0x16, 0x17, 0xe4, 0xe7, 0x15, 0xa7, 0x0a, 0x49, 0x71, 0x71, 0x84, + 0x16, 0xa7, 0x16, 0xe5, 0x25, 0xe6, 0xa6, 0x42, 0x95, 0xc3, 0xf9, 0x42, 0x62, 0x5c, 0x6c, 0xc1, + 0xa9, 0xc9, 0x45, 0xa9, 0x25, 0x12, 0x4c, 0x60, 0x19, 0x28, 0xcf, 0x28, 0x89, 0x8b, 0xc5, 0xb1, + 0xb4, 0x24, 0x43, 0x28, 0x8a, 0x8b, 0x1b, 0xc9, 0x48, 0x21, 0x15, 0x3d, 0x74, 0xe7, 0xe9, 0x61, + 0xba, 0x4d, 0x4a, 0x95, 0x80, 0x2a, 0x88, 0xbb, 0x9c, 0x8c, 0x2e, 0x3c, 0x94, 0x63, 0xb8, 0xf1, + 0x50, 0x8e, 0xe1, 0xc3, 0x43, 0x39, 0xc6, 0x86, 0x47, 0x72, 0x8c, 0x2b, 0x1e, 0xc9, 0x31, 0x9e, + 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x2f, 0x1e, 0xc9, 0x31, + 0x7c, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0x43, 0x14, 0x0b, 0x28, 0x90, 0x92, 0xd8, 0xc0, + 0xa1, 0x64, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0xaa, 0x73, 0xf3, 0xd5, 0x33, 0x01, 0x00, 0x00, +} diff --git a/vendor/github.com/moby/buildkit/session/auth/auth.proto b/vendor/github.com/moby/buildkit/session/auth/auth.proto new file mode 100644 index 0000000000000..593312747950b --- /dev/null +++ b/vendor/github.com/moby/buildkit/session/auth/auth.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +package moby.filesync.v1; + +option go_package = "auth"; + +service Auth{ + rpc Credentials(CredentialsRequest) returns (CredentialsResponse); +} + + +message CredentialsRequest { + string Host = 1; +} + +message CredentialsResponse { + string Username = 1; + string Secret = 2; +} diff --git a/vendor/github.com/moby/buildkit/session/auth/generate.go b/vendor/github.com/moby/buildkit/session/auth/generate.go new file mode 100644 index 0000000000000..687aa7cc0b5b6 --- /dev/null +++ b/vendor/github.com/moby/buildkit/session/auth/generate.go @@ -0,0 +1,3 @@ +package auth + +//go:generate protoc --gogoslick_out=plugins=grpc:. auth.proto diff --git a/vendor/github.com/moby/buildkit/session/filesync/diffcopy.go b/vendor/github.com/moby/buildkit/session/filesync/diffcopy.go index 7f8bf3c0dff82..d0f8e76df3abb 100644 --- a/vendor/github.com/moby/buildkit/session/filesync/diffcopy.go +++ b/vendor/github.com/moby/buildkit/session/filesync/diffcopy.go @@ -12,10 +12,11 @@ import ( "google.golang.org/grpc" ) -func sendDiffCopy(stream grpc.Stream, dir string, includes, excludes []string, progress progressCb, _map func(*fsutil.Stat) bool) error { +func sendDiffCopy(stream grpc.Stream, dir string, includes, excludes, followPaths []string, progress progressCb, _map func(*fsutil.Stat) bool) error { return fsutil.Send(stream.Context(), stream, dir, &fsutil.WalkOpt{ ExcludePatterns: excludes, IncludePatterns: includes, + FollowPaths: followPaths, Map: _map, }, progress) } diff --git a/vendor/github.com/moby/buildkit/session/filesync/filesync.go b/vendor/github.com/moby/buildkit/session/filesync/filesync.go index 232a696d734a4..ee2668f06bf05 100644 --- a/vendor/github.com/moby/buildkit/session/filesync/filesync.go +++ b/vendor/github.com/moby/buildkit/session/filesync/filesync.go @@ -18,6 +18,7 @@ const ( keyOverrideExcludes = "override-excludes" keyIncludePatterns = "include-patterns" keyExcludePatterns = "exclude-patterns" + keyFollowPaths = "followpaths" keyDirName = "dir-name" ) @@ -87,6 +88,8 @@ func (sp *fsSyncProvider) handle(method string, stream grpc.ServerStream) (retEr } includes := opts[keyIncludePatterns] + followPaths := opts[keyFollowPaths] + var progress progressCb if sp.p != nil { progress = sp.p @@ -98,7 +101,7 @@ func (sp *fsSyncProvider) handle(method string, stream grpc.ServerStream) (retEr doneCh = sp.doneCh sp.doneCh = nil } - err := pr.sendFn(stream, dir.Dir, includes, excludes, progress, dir.Map) + err := pr.sendFn(stream, dir.Dir, includes, excludes, followPaths, progress, dir.Map) if doneCh != nil { if err != nil { doneCh <- err @@ -117,7 +120,7 @@ type progressCb func(int, bool) type protocol struct { name string - sendFn func(stream grpc.Stream, srcDir string, includes, excludes []string, progress progressCb, _map func(*fsutil.Stat) bool) error + sendFn func(stream grpc.Stream, srcDir string, includes, excludes, followPaths []string, progress progressCb, _map func(*fsutil.Stat) bool) error recvFn func(stream grpc.Stream, destDir string, cu CacheUpdater, progress progressCb) error } @@ -142,6 +145,7 @@ type FSSendRequestOpt struct { Name string IncludePatterns []string ExcludePatterns []string + FollowPaths []string OverrideExcludes bool // deprecated: this is used by docker/cli for automatically loading .dockerignore from the directory DestDir string CacheUpdater CacheUpdater @@ -181,6 +185,10 @@ func FSSync(ctx context.Context, c session.Caller, opt FSSendRequestOpt) error { opts[keyExcludePatterns] = opt.ExcludePatterns } + if opt.FollowPaths != nil { + opts[keyFollowPaths] = opt.FollowPaths + } + opts[keyDirName] = []string{opt.Name} ctx, cancel := context.WithCancel(ctx) @@ -261,7 +269,7 @@ func CopyToCaller(ctx context.Context, srcPath string, c session.Caller, progres return err } - return sendDiffCopy(cc, srcPath, nil, nil, progress, nil) + return sendDiffCopy(cc, srcPath, nil, nil, nil, progress, nil) } func CopyFileWriter(ctx context.Context, c session.Caller) (io.WriteCloser, error) { diff --git a/vendor/github.com/moby/buildkit/session/grpchijack/dial.go b/vendor/github.com/moby/buildkit/session/grpchijack/dial.go new file mode 100644 index 0000000000000..151ab5498f3ab --- /dev/null +++ b/vendor/github.com/moby/buildkit/session/grpchijack/dial.go @@ -0,0 +1,156 @@ +package grpchijack + +import ( + "context" + "io" + "net" + "strings" + "sync" + "time" + + controlapi "github.com/moby/buildkit/api/services/control" + "github.com/moby/buildkit/session" + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" +) + +func Dialer(api controlapi.ControlClient) session.Dialer { + return func(ctx context.Context, proto string, meta map[string][]string) (net.Conn, error) { + + meta = lowerHeaders(meta) + + md := metadata.MD(meta) + + ctx = metadata.NewOutgoingContext(ctx, md) + + stream, err := api.Session(ctx) + if err != nil { + return nil, err + } + + c, _ := streamToConn(stream) + return c, nil + } +} + +func streamToConn(stream grpc.Stream) (net.Conn, <-chan struct{}) { + closeCh := make(chan struct{}) + c := &conn{stream: stream, buf: make([]byte, 32*1<<10), closeCh: closeCh} + return c, closeCh +} + +type conn struct { + stream grpc.Stream + buf []byte + lastBuf []byte + + closedOnce sync.Once + readMu sync.Mutex + err error + closeCh chan struct{} +} + +func (c *conn) Read(b []byte) (n int, err error) { + c.readMu.Lock() + defer c.readMu.Unlock() + + if c.lastBuf != nil { + n := copy(b, c.lastBuf) + c.lastBuf = c.lastBuf[n:] + if len(c.lastBuf) == 0 { + c.lastBuf = nil + } + return n, nil + } + m := new(controlapi.BytesMessage) + m.Data = c.buf + + if err := c.stream.RecvMsg(m); err != nil { + return 0, err + } + c.buf = m.Data[:cap(m.Data)] + + n = copy(b, m.Data) + if n < len(m.Data) { + c.lastBuf = m.Data[n:] + } + + return n, nil +} + +func (c *conn) Write(b []byte) (int, error) { + m := &controlapi.BytesMessage{Data: b} + if err := c.stream.SendMsg(m); err != nil { + return 0, err + } + return len(b), nil +} + +func (c *conn) Close() (err error) { + c.closedOnce.Do(func() { + defer func() { + close(c.closeCh) + }() + + if cs, ok := c.stream.(grpc.ClientStream); ok { + err = cs.CloseSend() + if err != nil { + return + } + } + + c.readMu.Lock() + for { + m := new(controlapi.BytesMessage) + m.Data = c.buf + err = c.stream.RecvMsg(m) + if err != nil { + if err != io.EOF { + return + } + err = nil + break + } + c.buf = m.Data[:cap(m.Data)] + c.lastBuf = append(c.lastBuf, c.buf...) + } + c.readMu.Unlock() + + }) + return nil +} + +func (c *conn) LocalAddr() net.Addr { + return dummyAddr{} +} +func (c *conn) RemoteAddr() net.Addr { + return dummyAddr{} +} +func (c *conn) SetDeadline(t time.Time) error { + return nil +} +func (c *conn) SetReadDeadline(t time.Time) error { + return nil +} +func (c *conn) SetWriteDeadline(t time.Time) error { + return nil +} + +type dummyAddr struct { +} + +func (d dummyAddr) Network() string { + return "tcp" +} + +func (d dummyAddr) String() string { + return "localhost" +} + +func lowerHeaders(in map[string][]string) map[string][]string { + out := map[string][]string{} + for k := range in { + out[strings.ToLower(k)] = in[k] + } + return out +} diff --git a/vendor/github.com/moby/buildkit/session/grpchijack/hijack.go b/vendor/github.com/moby/buildkit/session/grpchijack/hijack.go new file mode 100644 index 0000000000000..6e34b2164e9f3 --- /dev/null +++ b/vendor/github.com/moby/buildkit/session/grpchijack/hijack.go @@ -0,0 +1,14 @@ +package grpchijack + +import ( + "net" + + controlapi "github.com/moby/buildkit/api/services/control" + "google.golang.org/grpc/metadata" +) + +func Hijack(stream controlapi.Control_SessionServer) (net.Conn, <-chan struct{}, map[string][]string) { + md, _ := metadata.FromIncomingContext(stream.Context()) + c, closeCh := streamToConn(stream) + return c, closeCh, md +} diff --git a/vendor/github.com/moby/buildkit/snapshot/blobmapping/snapshotter.go b/vendor/github.com/moby/buildkit/snapshot/blobmapping/snapshotter.go new file mode 100644 index 0000000000000..e91241b7b648e --- /dev/null +++ b/vendor/github.com/moby/buildkit/snapshot/blobmapping/snapshotter.go @@ -0,0 +1,129 @@ +package blobmapping + +import ( + "context" + + "github.com/boltdb/bolt" + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/snapshots" + "github.com/moby/buildkit/cache/metadata" + "github.com/moby/buildkit/snapshot" + digest "github.com/opencontainers/go-digest" + "github.com/sirupsen/logrus" +) + +const blobKey = "blobmapping.blob" + +type Opt struct { + Content content.Store + Snapshotter snapshot.SnapshotterBase + MetadataStore *metadata.Store +} + +type Info struct { + snapshots.Info + Blob string +} + +type DiffPair struct { + Blobsum digest.Digest + DiffID digest.Digest +} + +// this snapshotter keeps an internal mapping between a snapshot and a blob + +type Snapshotter struct { + snapshot.SnapshotterBase + opt Opt +} + +func NewSnapshotter(opt Opt) snapshot.Snapshotter { + s := &Snapshotter{ + SnapshotterBase: opt.Snapshotter, + opt: opt, + } + + return s +} + +// Remove also removes a reference to a blob. If it is a last reference then it deletes it the blob as well +// Remove is not safe to be called concurrently +func (s *Snapshotter) Remove(ctx context.Context, key string) error { + _, blob, err := s.GetBlob(ctx, key) + if err != nil { + return err + } + + blobs, err := s.opt.MetadataStore.Search(index(blob)) + if err != nil { + return err + } + + if err := s.SnapshotterBase.Remove(ctx, key); err != nil { + return err + } + + if len(blobs) == 1 && blobs[0].ID() == key { // last snapshot + if err := s.opt.Content.Delete(ctx, blob); err != nil { + logrus.Errorf("failed to delete blob %v: %+v", blob, err) + } + } + return nil +} + +func (s *Snapshotter) Usage(ctx context.Context, key string) (snapshots.Usage, error) { + u, err := s.SnapshotterBase.Usage(ctx, key) + if err != nil { + return snapshots.Usage{}, err + } + _, blob, err := s.GetBlob(ctx, key) + if err != nil { + return u, err + } + if blob != "" { + info, err := s.opt.Content.Info(ctx, blob) + if err != nil { + return u, err + } + (&u).Add(snapshots.Usage{Size: info.Size, Inodes: 1}) + } + return u, nil +} + +func (s *Snapshotter) GetBlob(ctx context.Context, key string) (digest.Digest, digest.Digest, error) { + md, _ := s.opt.MetadataStore.Get(key) + v := md.Get(blobKey) + if v == nil { + return "", "", nil + } + var blob DiffPair + if err := v.Unmarshal(&blob); err != nil { + return "", "", err + } + return blob.DiffID, blob.Blobsum, nil +} + +// Validates that there is no blob associated with the snapshot. +// Checks that there is a blob in the content store. +// If same blob has already been set then this is a noop. +func (s *Snapshotter) SetBlob(ctx context.Context, key string, diffID, blobsum digest.Digest) error { + _, err := s.opt.Content.Info(ctx, blobsum) + if err != nil { + return err + } + md, _ := s.opt.MetadataStore.Get(key) + + v, err := metadata.NewValue(DiffPair{DiffID: diffID, Blobsum: blobsum}) + if err != nil { + return err + } + v.Index = index(blobsum) + + return md.Update(func(b *bolt.Bucket) error { + return md.SetValue(b, blobKey, v) + }) +} + +func index(blob digest.Digest) string { + return "blobmap::" + blob.String() +} diff --git a/vendor/github.com/moby/buildkit/snapshot/localmounter.go b/vendor/github.com/moby/buildkit/snapshot/localmounter.go new file mode 100644 index 0000000000000..18e2411cfc908 --- /dev/null +++ b/vendor/github.com/moby/buildkit/snapshot/localmounter.go @@ -0,0 +1,72 @@ +package snapshot + +import ( + "io/ioutil" + "os" + "sync" + + "github.com/containerd/containerd/mount" + "github.com/pkg/errors" +) + +type Mounter interface { + Mount() (string, error) + Unmount() error +} + +// LocalMounter is a helper for mounting mountfactory to temporary path. In +// addition it can mount binds without privileges +func LocalMounter(mountable Mountable) Mounter { + return &localMounter{mountable: mountable} +} + +// LocalMounterWithMounts is a helper for mounting to temporary path. In +// addition it can mount binds without privileges +func LocalMounterWithMounts(mounts []mount.Mount) Mounter { + return &localMounter{mounts: mounts} +} + +type localMounter struct { + mu sync.Mutex + mounts []mount.Mount + mountable Mountable + target string +} + +func (lm *localMounter) Mount() (string, error) { + lm.mu.Lock() + defer lm.mu.Unlock() + + if lm.mounts == nil { + mounts, err := lm.mountable.Mount() + if err != nil { + return "", err + } + lm.mounts = mounts + } + + if len(lm.mounts) == 1 && (lm.mounts[0].Type == "bind" || lm.mounts[0].Type == "rbind") { + ro := false + for _, opt := range lm.mounts[0].Options { + if opt == "ro" { + ro = true + break + } + } + if !ro { + return lm.mounts[0].Source, nil + } + } + + dir, err := ioutil.TempDir("", "buildkit-mount") + if err != nil { + return "", errors.Wrap(err, "failed to create temp dir") + } + + if err := mount.All(lm.mounts, dir); err != nil { + os.RemoveAll(dir) + return "", errors.Wrapf(err, "failed to mount %s: %+v", dir, lm.mounts) + } + lm.target = dir + return dir, nil +} diff --git a/vendor/github.com/moby/buildkit/snapshot/localmounter_unix.go b/vendor/github.com/moby/buildkit/snapshot/localmounter_unix.go new file mode 100644 index 0000000000000..c44e435e993dd --- /dev/null +++ b/vendor/github.com/moby/buildkit/snapshot/localmounter_unix.go @@ -0,0 +1,29 @@ +// +build !windows + +package snapshot + +import ( + "os" + "syscall" + + "github.com/containerd/containerd/mount" +) + +func (lm *localMounter) Unmount() error { + lm.mu.Lock() + defer lm.mu.Unlock() + + if lm.target != "" { + if err := mount.Unmount(lm.target, syscall.MNT_DETACH); err != nil { + return err + } + os.RemoveAll(lm.target) + lm.target = "" + } + + if lm.mountable != nil { + return lm.mountable.Release() + } + + return nil +} diff --git a/vendor/github.com/moby/buildkit/snapshot/localmounter_windows.go b/vendor/github.com/moby/buildkit/snapshot/localmounter_windows.go new file mode 100644 index 0000000000000..4e1287b0d801a --- /dev/null +++ b/vendor/github.com/moby/buildkit/snapshot/localmounter_windows.go @@ -0,0 +1,26 @@ +package snapshot + +import ( + "os" + + "github.com/containerd/containerd/mount" +) + +func (lm *localMounter) Unmount() error { + lm.mu.Lock() + defer lm.mu.Unlock() + + if lm.target != "" { + if err := mount.Unmount(lm.target, 0); err != nil { + return err + } + os.RemoveAll(lm.target) + lm.target = "" + } + + if lm.mountable != nil { + return lm.mountable.Release() + } + + return nil +} diff --git a/vendor/github.com/moby/buildkit/snapshot/snapshotter.go b/vendor/github.com/moby/buildkit/snapshot/snapshotter.go new file mode 100644 index 0000000000000..ad7fcaf2dcf4e --- /dev/null +++ b/vendor/github.com/moby/buildkit/snapshot/snapshotter.go @@ -0,0 +1,137 @@ +package snapshot + +import ( + "context" + "sync" + + "github.com/containerd/containerd/mount" + "github.com/containerd/containerd/snapshots" + digest "github.com/opencontainers/go-digest" +) + +type Mountable interface { + // ID() string + Mount() ([]mount.Mount, error) + Release() error +} + +type SnapshotterBase interface { + Mounts(ctx context.Context, key string) (Mountable, error) + Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) error + View(ctx context.Context, key, parent string, opts ...snapshots.Opt) (Mountable, error) + + Stat(ctx context.Context, key string) (snapshots.Info, error) + Update(ctx context.Context, info snapshots.Info, fieldpaths ...string) (snapshots.Info, error) + Usage(ctx context.Context, key string) (snapshots.Usage, error) + Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) error + Remove(ctx context.Context, key string) error + Walk(ctx context.Context, fn func(context.Context, snapshots.Info) error) error + Close() error +} + +// Snapshotter defines interface that any snapshot implementation should satisfy +type Snapshotter interface { + Blobmapper + SnapshotterBase +} + +type Blobmapper interface { + GetBlob(ctx context.Context, key string) (digest.Digest, digest.Digest, error) + SetBlob(ctx context.Context, key string, diffID, blob digest.Digest) error +} + +func FromContainerdSnapshotter(s snapshots.Snapshotter) SnapshotterBase { + return &fromContainerd{Snapshotter: s} +} + +type fromContainerd struct { + snapshots.Snapshotter +} + +func (s *fromContainerd) Mounts(ctx context.Context, key string) (Mountable, error) { + mounts, err := s.Snapshotter.Mounts(ctx, key) + if err != nil { + return nil, err + } + return &staticMountable{mounts}, nil +} +func (s *fromContainerd) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) error { + _, err := s.Snapshotter.Prepare(ctx, key, parent, opts...) + return err +} +func (s *fromContainerd) View(ctx context.Context, key, parent string, opts ...snapshots.Opt) (Mountable, error) { + mounts, err := s.Snapshotter.View(ctx, key, parent, opts...) + if err != nil { + return nil, err + } + return &staticMountable{mounts}, nil +} + +type staticMountable struct { + mounts []mount.Mount +} + +func (m *staticMountable) Mount() ([]mount.Mount, error) { + return m.mounts, nil +} + +func (cm *staticMountable) Release() error { + return nil +} + +// NewContainerdSnapshotter converts snapshotter to containerd snapshotter +func NewContainerdSnapshotter(s Snapshotter) (snapshots.Snapshotter, func() error) { + cs := &containerdSnapshotter{Snapshotter: s} + return cs, cs.release +} + +type containerdSnapshotter struct { + mu sync.Mutex + releasers []func() error + Snapshotter +} + +func (cs *containerdSnapshotter) release() error { + cs.mu.Lock() + defer cs.mu.Unlock() + var err error + for _, f := range cs.releasers { + if err1 := f(); err != nil && err == nil { + err = err1 + } + } + return err +} + +func (cs *containerdSnapshotter) returnMounts(mf Mountable) ([]mount.Mount, error) { + mounts, err := mf.Mount() + if err != nil { + return nil, err + } + cs.mu.Lock() + cs.releasers = append(cs.releasers, mf.Release) + cs.mu.Unlock() + return mounts, nil +} + +func (cs *containerdSnapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, error) { + mf, err := cs.Snapshotter.Mounts(ctx, key) + if err != nil { + return nil, err + } + return cs.returnMounts(mf) +} + +func (cs *containerdSnapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) { + if err := cs.Snapshotter.Prepare(ctx, key, parent, opts...); err != nil { + return nil, err + } + return cs.Mounts(ctx, key) +} +func (cs *containerdSnapshotter) View(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) { + mf, err := cs.Snapshotter.View(ctx, key, parent, opts...) + if err != nil { + return nil, err + } + return cs.returnMounts(mf) +} diff --git a/vendor/github.com/moby/buildkit/solver/boltdbcachestorage/storage.go b/vendor/github.com/moby/buildkit/solver/boltdbcachestorage/storage.go new file mode 100644 index 0000000000000..8f890449120c5 --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/boltdbcachestorage/storage.go @@ -0,0 +1,449 @@ +package boltdbcachestorage + +import ( + "bytes" + "encoding/json" + "fmt" + + "github.com/boltdb/bolt" + "github.com/moby/buildkit/solver" + digest "github.com/opencontainers/go-digest" + "github.com/pkg/errors" +) + +const ( + resultBucket = "_result" + linksBucket = "_links" + byResultBucket = "_byresult" + backlinksBucket = "_backlinks" +) + +type Store struct { + db *bolt.DB +} + +func NewStore(dbPath string) (*Store, error) { + db, err := bolt.Open(dbPath, 0600, nil) + if err != nil { + return nil, errors.Wrapf(err, "failed to open database file %s", dbPath) + } + if err := db.Update(func(tx *bolt.Tx) error { + for _, b := range []string{resultBucket, linksBucket, byResultBucket, backlinksBucket} { + if _, err := tx.CreateBucketIfNotExists([]byte(b)); err != nil { + return err + } + } + return nil + }); err != nil { + return nil, err + } + db.NoSync = true + return &Store{db: db}, nil +} + +func (s *Store) Exists(id string) bool { + exists := false + err := s.db.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(linksBucket)).Bucket([]byte(id)) + exists = b != nil + return nil + }) + if err != nil { + return false + } + return exists +} + +func (s *Store) Walk(fn func(id string) error) error { + ids := make([]string, 0) + if err := s.db.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(linksBucket)) + c := b.Cursor() + for k, v := c.First(); k != nil; k, v = c.Next() { + if v == nil { + ids = append(ids, string(k)) + } + } + return nil + }); err != nil { + return err + } + for _, id := range ids { + if err := fn(id); err != nil { + return err + } + } + return nil +} + +func (s *Store) WalkResults(id string, fn func(solver.CacheResult) error) error { + var list []solver.CacheResult + if err := s.db.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(resultBucket)) + if b == nil { + return nil + } + b = b.Bucket([]byte(id)) + if b == nil { + return nil + } + + return b.ForEach(func(k, v []byte) error { + var res solver.CacheResult + if err := json.Unmarshal(v, &res); err != nil { + return err + } + list = append(list, res) + return nil + }) + }); err != nil { + return err + } + for _, res := range list { + if err := fn(res); err != nil { + return err + } + } + return nil +} + +func (s *Store) Load(id string, resultID string) (solver.CacheResult, error) { + var res solver.CacheResult + if err := s.db.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(resultBucket)) + if b == nil { + return errors.WithStack(solver.ErrNotFound) + } + b = b.Bucket([]byte(id)) + if b == nil { + return errors.WithStack(solver.ErrNotFound) + } + + v := b.Get([]byte(resultID)) + if v == nil { + return errors.WithStack(solver.ErrNotFound) + } + + return json.Unmarshal(v, &res) + }); err != nil { + return solver.CacheResult{}, err + } + return res, nil +} + +func (s *Store) AddResult(id string, res solver.CacheResult) error { + return s.db.Update(func(tx *bolt.Tx) error { + b, err := tx.Bucket([]byte(resultBucket)).CreateBucketIfNotExists([]byte(id)) + if err != nil { + return err + } + dt, err := json.Marshal(res) + if err != nil { + return err + } + if err := b.Put([]byte(res.ID), dt); err != nil { + return err + } + b, err = tx.Bucket([]byte(byResultBucket)).CreateBucketIfNotExists([]byte(res.ID)) + if err != nil { + return err + } + if err := b.Put([]byte(id), []byte{}); err != nil { + return err + } + + return nil + }) +} + +func (s *Store) WalkIDsByResult(resultID string, fn func(string) error) error { + ids := map[string]struct{}{} + if err := s.db.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(byResultBucket)) + if b == nil { + return nil + } + b = b.Bucket([]byte(resultID)) + if b == nil { + return nil + } + return b.ForEach(func(k, v []byte) error { + ids[string(k)] = struct{}{} + return nil + }) + }); err != nil { + return err + } + for id := range ids { + if err := fn(id); err != nil { + return err + } + } + return nil +} + +func (s *Store) Release(resultID string) error { + return s.db.Update(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(byResultBucket)) + if b == nil { + return errors.WithStack(solver.ErrNotFound) + } + b = b.Bucket([]byte(resultID)) + if b == nil { + return errors.WithStack(solver.ErrNotFound) + } + if err := b.ForEach(func(k, v []byte) error { + return s.releaseHelper(tx, string(k), resultID) + }); err != nil { + return err + } + return nil + }) +} + +func (s *Store) releaseHelper(tx *bolt.Tx, id, resultID string) error { + results := tx.Bucket([]byte(resultBucket)).Bucket([]byte(id)) + if results == nil { + return nil + } + + if err := results.Delete([]byte(resultID)); err != nil { + return err + } + + ids := tx.Bucket([]byte(byResultBucket)) + + ids = ids.Bucket([]byte(resultID)) + if ids == nil { + return nil + } + + if err := ids.Delete([]byte(resultID)); err != nil { + return err + } + + if isEmptyBucket(ids) { + if err := tx.Bucket([]byte(byResultBucket)).DeleteBucket([]byte(resultID)); err != nil { + return err + } + } + + links := tx.Bucket([]byte(resultBucket)) + if results == nil { + return nil + } + links = links.Bucket([]byte(id)) + + return s.emptyBranchWithParents(tx, []byte(id)) +} + +func (s *Store) emptyBranchWithParents(tx *bolt.Tx, id []byte) error { + results := tx.Bucket([]byte(resultBucket)).Bucket(id) + if results == nil { + return nil + } + + isEmptyLinks := true + links := tx.Bucket([]byte(linksBucket)).Bucket(id) + if links != nil { + isEmptyLinks = isEmptyBucket(links) + } + + if !isEmptyBucket(results) || !isEmptyLinks { + return nil + } + + if backlinks := tx.Bucket([]byte(backlinksBucket)).Bucket(id); backlinks != nil { + if err := backlinks.ForEach(func(k, v []byte) error { + if subLinks := tx.Bucket([]byte(linksBucket)).Bucket(k); subLinks != nil { + if err := subLinks.ForEach(func(k, v []byte) error { + parts := bytes.Split(k, []byte("@")) + if len(parts) != 2 { + return errors.Errorf("invalid key %s", k) + } + if bytes.Equal(id, parts[1]) { + return subLinks.Delete(k) + } + return nil + }); err != nil { + return err + } + + if isEmptyBucket(subLinks) { + if err := tx.Bucket([]byte(linksBucket)).DeleteBucket(k); err != nil { + return err + } + } + } + return s.emptyBranchWithParents(tx, k) + }); err != nil { + return err + } + if err := tx.Bucket([]byte(backlinksBucket)).DeleteBucket(id); err != nil { + return err + } + } + return nil +} + +func (s *Store) AddLink(id string, link solver.CacheInfoLink, target string) error { + return s.db.Update(func(tx *bolt.Tx) error { + b, err := tx.Bucket([]byte(linksBucket)).CreateBucketIfNotExists([]byte(id)) + if err != nil { + return err + } + + dt, err := json.Marshal(link) + if err != nil { + return err + } + + if err := b.Put(bytes.Join([][]byte{dt, []byte(target)}, []byte("@")), []byte{}); err != nil { + return err + } + + b, err = tx.Bucket([]byte(backlinksBucket)).CreateBucketIfNotExists([]byte(target)) + if err != nil { + return err + } + + if err := b.Put([]byte(id), []byte{}); err != nil { + return err + } + + return nil + }) +} + +func (s *Store) WalkLinks(id string, link solver.CacheInfoLink, fn func(id string) error) error { + var links []string + if err := s.db.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(linksBucket)) + if b == nil { + return nil + } + b = b.Bucket([]byte(id)) + if b == nil { + return nil + } + + dt, err := json.Marshal(link) + if err != nil { + return err + } + index := bytes.Join([][]byte{dt, {}}, []byte("@")) + c := b.Cursor() + k, _ := c.Seek([]byte(index)) + for { + if k != nil && bytes.HasPrefix(k, index) { + target := bytes.TrimPrefix(k, index) + links = append(links, string(target)) + k, _ = c.Next() + } else { + break + } + } + + return nil + }); err != nil { + return err + } + for _, l := range links { + if err := fn(l); err != nil { + return err + } + } + return nil +} + +func (s *Store) HasLink(id string, link solver.CacheInfoLink, target string) bool { + var v bool + if err := s.db.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(linksBucket)) + if b == nil { + return nil + } + b = b.Bucket([]byte(id)) + if b == nil { + return nil + } + + dt, err := json.Marshal(link) + if err != nil { + return err + } + v = b.Get(bytes.Join([][]byte{dt, []byte(target)}, []byte("@"))) != nil + return nil + }); err != nil { + return false + } + return v +} + +func (s *Store) WalkBacklinks(id string, fn func(id string, link solver.CacheInfoLink) error) error { + var outIDs []string + var outLinks []solver.CacheInfoLink + + if err := s.db.View(func(tx *bolt.Tx) error { + links := tx.Bucket([]byte(linksBucket)) + if links == nil { + return nil + } + backLinks := tx.Bucket([]byte(backlinksBucket)) + if backLinks == nil { + return nil + } + b := backLinks.Bucket([]byte(id)) + if b == nil { + return nil + } + + if err := b.ForEach(func(bid, v []byte) error { + b = links.Bucket(bid) + if b == nil { + return nil + } + if err := b.ForEach(func(k, v []byte) error { + parts := bytes.Split(k, []byte("@")) + if len(parts) == 2 { + if string(parts[1]) != id { + return nil + } + var l solver.CacheInfoLink + if err := json.Unmarshal(parts[0], &l); err != nil { + return err + } + l.Digest = digest.FromBytes([]byte(fmt.Sprintf("%s@%d", l.Digest, l.Output))) + l.Output = 0 + outIDs = append(outIDs, string(bid)) + outLinks = append(outLinks, l) + } + return nil + }); err != nil { + return err + } + return nil + }); err != nil { + return err + } + + return nil + }); err != nil { + return err + } + + for i := range outIDs { + if err := fn(outIDs[i], outLinks[i]); err != nil { + return err + } + } + return nil +} + +func isEmptyBucket(b *bolt.Bucket) bool { + if b == nil { + return true + } + k, _ := b.Cursor().First() + return k == nil +} diff --git a/vendor/github.com/moby/buildkit/solver/cachekey.go b/vendor/github.com/moby/buildkit/solver/cachekey.go new file mode 100644 index 0000000000000..3749af0ab3ac1 --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/cachekey.go @@ -0,0 +1,66 @@ +package solver + +import ( + "sync" + + digest "github.com/opencontainers/go-digest" +) + +// NewCacheKey creates a new cache key for a specific output index +func NewCacheKey(dgst digest.Digest, output Index) *CacheKey { + return &CacheKey{ + ID: rootKey(dgst, output).String(), + digest: dgst, + output: output, + ids: map[*cacheManager]string{}, + } +} + +// CacheKeyWithSelector combines a cache key with an optional selector digest. +// Used to limit the matches for dependency cache key. +type CacheKeyWithSelector struct { + Selector digest.Digest + CacheKey ExportableCacheKey +} + +type CacheKey struct { + mu sync.RWMutex + + ID string + deps [][]CacheKeyWithSelector // only [][]*inMemoryCacheKey + digest digest.Digest + output Index + ids map[*cacheManager]string + + indexIDs []string +} + +func (ck *CacheKey) Deps() [][]CacheKeyWithSelector { + ck.mu.RLock() + defer ck.mu.RUnlock() + deps := make([][]CacheKeyWithSelector, len(ck.deps)) + for i := range ck.deps { + deps[i] = append([]CacheKeyWithSelector(nil), ck.deps[i]...) + } + return deps +} + +func (ck *CacheKey) Digest() digest.Digest { + return ck.digest +} +func (ck *CacheKey) Output() Index { + return ck.output +} + +func (ck *CacheKey) clone() *CacheKey { + nk := &CacheKey{ + ID: ck.ID, + digest: ck.digest, + output: ck.output, + ids: map[*cacheManager]string{}, + } + for cm, id := range ck.ids { + nk.ids[cm] = id + } + return nk +} diff --git a/vendor/github.com/moby/buildkit/solver/cachemanager.go b/vendor/github.com/moby/buildkit/solver/cachemanager.go new file mode 100644 index 0000000000000..12a923503d5e2 --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/cachemanager.go @@ -0,0 +1,270 @@ +package solver + +import ( + "context" + "fmt" + "sync" + + "github.com/moby/buildkit/identity" + digest "github.com/opencontainers/go-digest" +) + +type CacheID string + +func NewInMemoryCacheManager() CacheManager { + return NewCacheManager(identity.NewID(), NewInMemoryCacheStorage(), NewInMemoryResultStorage()) +} + +func NewCacheManager(id string, storage CacheKeyStorage, results CacheResultStorage) CacheManager { + cm := &cacheManager{ + id: id, + backend: storage, + results: results, + } + + storage.Walk(func(id string) error { + return storage.WalkResults(id, func(cr CacheResult) error { + if !results.Exists(cr.ID) { + storage.Release(cr.ID) + } + return nil + }) + }) + + return cm +} + +type cacheManager struct { + mu sync.RWMutex + id string + + backend CacheKeyStorage + results CacheResultStorage +} + +func (c *cacheManager) ID() string { + return c.id +} + +func (c *cacheManager) Query(deps []CacheKeyWithSelector, input Index, dgst digest.Digest, output Index) ([]*CacheKey, error) { + c.mu.RLock() + defer c.mu.RUnlock() + + type dep struct { + results map[string]struct{} + key CacheKeyWithSelector + } + + allDeps := make([]dep, 0, len(deps)) + for _, k := range deps { + allDeps = append(allDeps, dep{key: k, results: map[string]struct{}{}}) + } + + allRes := map[string]*CacheKey{} + for _, d := range allDeps { + if err := c.backend.WalkLinks(c.getID(d.key.CacheKey.CacheKey), CacheInfoLink{input, output, dgst, d.key.Selector}, func(id string) error { + d.results[id] = struct{}{} + if _, ok := allRes[id]; !ok { + allRes[id] = c.newKeyWithID(id, dgst, output) + } + return nil + }); err != nil { + return nil, err + } + } + + // link the results against the keys that didn't exist + for id, key := range allRes { + for _, d := range allDeps { + if _, ok := d.results[id]; !ok { + if err := c.backend.AddLink(c.getID(d.key.CacheKey.CacheKey), CacheInfoLink{ + Input: input, + Output: output, + Digest: dgst, + Selector: d.key.Selector, + }, c.getID(key)); err != nil { + return nil, err + } + } + } + } + + if len(deps) == 0 { + if !c.backend.Exists(rootKey(dgst, output).String()) { + return nil, nil + } + return []*CacheKey{c.newRootKey(dgst, output)}, nil + } + + keys := make([]*CacheKey, 0, len(deps)) + for _, k := range allRes { + keys = append(keys, k) + } + return keys, nil +} + +func (c *cacheManager) Records(ck *CacheKey) ([]*CacheRecord, error) { + outs := make([]*CacheRecord, 0) + if err := c.backend.WalkResults(c.getID(ck), func(r CacheResult) error { + if c.results.Exists(r.ID) { + outs = append(outs, &CacheRecord{ + ID: r.ID, + cacheManager: c, + key: ck, + CreatedAt: r.CreatedAt, + }) + } else { + c.backend.Release(r.ID) + } + return nil + }); err != nil { + return nil, err + } + return outs, nil +} + +func (c *cacheManager) Load(ctx context.Context, rec *CacheRecord) (Result, error) { + c.mu.RLock() + defer c.mu.RUnlock() + + res, err := c.backend.Load(c.getID(rec.key), rec.ID) + if err != nil { + return nil, err + } + + return c.results.Load(ctx, res) +} + +func (c *cacheManager) Save(k *CacheKey, r Result) (*ExportableCacheKey, error) { + c.mu.Lock() + defer c.mu.Unlock() + + res, err := c.results.Save(r) + if err != nil { + return nil, err + } + + if err := c.backend.AddResult(c.getID(k), res); err != nil { + return nil, err + } + + if err := c.ensurePersistentKey(k); err != nil { + return nil, err + } + + rec := &CacheRecord{ + ID: res.ID, + cacheManager: c, + key: k, + CreatedAt: res.CreatedAt, + } + + return &ExportableCacheKey{ + CacheKey: k, + Exporter: &exporter{k: k, record: rec}, + }, nil +} + +func newKey() *CacheKey { + return &CacheKey{ids: map[*cacheManager]string{}} +} + +func (c *cacheManager) newKeyWithID(id string, dgst digest.Digest, output Index) *CacheKey { + k := newKey() + k.digest = dgst + k.output = output + k.ID = id + k.ids[c] = id + return k +} + +func (c *cacheManager) newRootKey(dgst digest.Digest, output Index) *CacheKey { + return c.newKeyWithID(rootKey(dgst, output).String(), dgst, output) +} + +func (c *cacheManager) getID(k *CacheKey) string { + k.mu.Lock() + id, ok := k.ids[c] + if ok { + k.mu.Unlock() + return id + } + if len(k.deps) == 0 { + k.ids[c] = k.ID + k.mu.Unlock() + return k.ID + } + id = c.getIDFromDeps(k) + k.ids[c] = id + k.mu.Unlock() + return id +} + +func (c *cacheManager) ensurePersistentKey(k *CacheKey) error { + id := c.getID(k) + for i, deps := range k.Deps() { + for _, ck := range deps { + l := CacheInfoLink{ + Input: Index(i), + Output: Index(k.Output()), + Digest: k.Digest(), + Selector: ck.Selector, + } + ckID := c.getID(ck.CacheKey.CacheKey) + if !c.backend.HasLink(ckID, l, id) { + if err := c.ensurePersistentKey(ck.CacheKey.CacheKey); err != nil { + return err + } + if err := c.backend.AddLink(ckID, l, id); err != nil { + return err + } + } + } + } + return nil +} + +func (c *cacheManager) getIDFromDeps(k *CacheKey) string { + matches := map[string]struct{}{} + + for i, deps := range k.deps { + if i == 0 || len(matches) > 0 { + for _, ck := range deps { + m2 := make(map[string]struct{}) + if err := c.backend.WalkLinks(c.getID(ck.CacheKey.CacheKey), CacheInfoLink{ + Input: Index(i), + Output: Index(k.Output()), + Digest: k.Digest(), + Selector: ck.Selector, + }, func(id string) error { + if i == 0 { + matches[id] = struct{}{} + } else { + m2[id] = struct{}{} + } + return nil + }); err != nil { + matches = map[string]struct{}{} + break + } + if i != 0 { + for id := range matches { + if _, ok := m2[id]; !ok { + delete(matches, id) + } + } + } + } + } + } + + for k := range matches { + return k + } + + return identity.NewID() +} + +func rootKey(dgst digest.Digest, output Index) digest.Digest { + return digest.FromBytes([]byte(fmt.Sprintf("%s@%d", dgst, output))) +} diff --git a/vendor/github.com/moby/buildkit/solver/cachestorage.go b/vendor/github.com/moby/buildkit/solver/cachestorage.go new file mode 100644 index 0000000000000..65225f757b349 --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/cachestorage.go @@ -0,0 +1,51 @@ +package solver + +import ( + "context" + "time" + + digest "github.com/opencontainers/go-digest" + "github.com/pkg/errors" +) + +var ErrNotFound = errors.Errorf("not found") + +// CacheKeyStorage is interface for persisting cache metadata +type CacheKeyStorage interface { + Exists(id string) bool + Walk(fn func(id string) error) error + + WalkResults(id string, fn func(CacheResult) error) error + Load(id string, resultID string) (CacheResult, error) + AddResult(id string, res CacheResult) error + Release(resultID string) error + WalkIDsByResult(resultID string, fn func(string) error) error + + AddLink(id string, link CacheInfoLink, target string) error + WalkLinks(id string, link CacheInfoLink, fn func(id string) error) error + HasLink(id string, link CacheInfoLink, target string) bool + WalkBacklinks(id string, fn func(id string, link CacheInfoLink) error) error +} + +// CacheResult is a record for a single solve result +type CacheResult struct { + CreatedAt time.Time + ID string +} + +// CacheInfoLink is a link between two cache keys +type CacheInfoLink struct { + Input Index `json:"Input,omitempty"` + Output Index `json:"Output,omitempty"` + Digest digest.Digest `json:"Digest,omitempty"` + Selector digest.Digest `json:"Selector,omitempty"` +} + +// CacheResultStorage is interface for converting cache metadata result to +// actual solve result +type CacheResultStorage interface { + Save(Result) (CacheResult, error) + Load(ctx context.Context, res CacheResult) (Result, error) + LoadRemote(ctx context.Context, res CacheResult) (*Remote, error) + Exists(id string) bool +} diff --git a/vendor/github.com/moby/buildkit/solver/combinedcache.go b/vendor/github.com/moby/buildkit/solver/combinedcache.go new file mode 100644 index 0000000000000..b4205d3ed063e --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/combinedcache.go @@ -0,0 +1,124 @@ +package solver + +import ( + "context" + "strings" + "sync" + + digest "github.com/opencontainers/go-digest" + "github.com/pkg/errors" + "golang.org/x/sync/errgroup" +) + +func newCombinedCacheManager(cms []CacheManager, main CacheManager) CacheManager { + return &combinedCacheManager{cms: cms, main: main} +} + +type combinedCacheManager struct { + cms []CacheManager + main CacheManager + id string + idOnce sync.Once +} + +func (cm *combinedCacheManager) ID() string { + cm.idOnce.Do(func() { + ids := make([]string, len(cm.cms)) + for i, c := range cm.cms { + ids[i] = c.ID() + } + cm.id = digest.FromBytes([]byte(strings.Join(ids, ","))).String() + }) + return cm.id +} + +func (cm *combinedCacheManager) Query(inp []CacheKeyWithSelector, inputIndex Index, dgst digest.Digest, outputIndex Index) ([]*CacheKey, error) { + eg, _ := errgroup.WithContext(context.TODO()) + keys := make(map[string]*CacheKey, len(cm.cms)) + var mu sync.Mutex + for _, c := range cm.cms { + func(c CacheManager) { + eg.Go(func() error { + recs, err := c.Query(inp, inputIndex, dgst, outputIndex) + if err != nil { + return err + } + mu.Lock() + for _, r := range recs { + if _, ok := keys[r.ID]; !ok || c == cm.main { + keys[r.ID] = r + } + } + mu.Unlock() + return nil + }) + }(c) + } + + if err := eg.Wait(); err != nil { + return nil, err + } + + out := make([]*CacheKey, 0, len(keys)) + for _, k := range keys { + out = append(out, k) + } + return out, nil +} + +func (cm *combinedCacheManager) Load(ctx context.Context, rec *CacheRecord) (Result, error) { + res, err := rec.cacheManager.Load(ctx, rec) + if err != nil { + return nil, err + } + if _, err := cm.main.Save(rec.key, res); err != nil { + return nil, err + } + return res, nil +} + +func (cm *combinedCacheManager) Save(key *CacheKey, s Result) (*ExportableCacheKey, error) { + return cm.main.Save(key, s) +} + +func (cm *combinedCacheManager) Records(ck *CacheKey) ([]*CacheRecord, error) { + if len(ck.ids) == 0 { + return nil, errors.Errorf("no results") + } + + records := map[string]*CacheRecord{} + var mu sync.Mutex + + eg, _ := errgroup.WithContext(context.TODO()) + for c := range ck.ids { + func(c *cacheManager) { + eg.Go(func() error { + recs, err := c.Records(ck) + if err != nil { + return err + } + mu.Lock() + for _, rec := range recs { + if _, ok := records[rec.ID]; !ok || c == cm.main { + if c == cm.main { + rec.Priority = 1 + } + records[rec.ID] = rec + } + } + mu.Unlock() + return nil + }) + }(c) + } + + if err := eg.Wait(); err != nil { + return nil, err + } + + out := make([]*CacheRecord, 0, len(records)) + for _, rec := range records { + out = append(out, rec) + } + return out, nil +} diff --git a/vendor/github.com/moby/buildkit/solver/edge.go b/vendor/github.com/moby/buildkit/solver/edge.go new file mode 100644 index 0000000000000..1547cf4bd1f00 --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/edge.go @@ -0,0 +1,866 @@ +package solver + +import ( + "context" + "sync" + + "github.com/moby/buildkit/solver/internal/pipe" + digest "github.com/opencontainers/go-digest" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +type edgeStatusType int + +const ( + edgeStatusInitial edgeStatusType = iota + edgeStatusCacheFast + edgeStatusCacheSlow + edgeStatusComplete +) + +func (t edgeStatusType) String() string { + return []string{"initial", "cache-fast", "cache-slow", "complete"}[t] +} + +func newEdge(ed Edge, op activeOp, index *edgeIndex) *edge { + e := &edge{ + edge: ed, + op: op, + depRequests: map[pipe.Receiver]*dep{}, + keyMap: map[string]*CacheKey{}, + cacheRecords: map[string]*CacheRecord{}, + index: index, + } + return e +} + +type edge struct { + edge Edge + op activeOp + + edgeState + depRequests map[pipe.Receiver]*dep + deps []*dep + + cacheMapReq pipe.Receiver + cacheMapDone bool + cacheMapIndex int + cacheMapDigests []digest.Digest + execReq pipe.Receiver + err error + cacheRecords map[string]*CacheRecord + keyMap map[string]*CacheKey + + noCacheMatchPossible bool + allDepsCompletedCacheFast bool + allDepsCompletedCacheSlow bool + allDepsStateCacheSlow bool + allDepsCompleted bool + hasActiveOutgoing bool + + releaserCount int + keysDidChange bool + index *edgeIndex + + secondaryExporters []expDep +} + +// dep holds state for a dependant edge +type dep struct { + req pipe.Receiver + edgeState + index Index + keyMap map[string]*CacheKey + desiredState edgeStatusType + e *edge + slowCacheReq pipe.Receiver + slowCacheComplete bool + slowCacheFoundKey bool + slowCacheKey *ExportableCacheKey + err error +} + +// expDep holds secorndary exporter info for dependency +type expDep struct { + index int + cacheKey CacheKeyWithSelector +} + +func newDep(i Index) *dep { + return &dep{index: i, keyMap: map[string]*CacheKey{}} +} + +// edgePipe is a pipe for requests between two edges +type edgePipe struct { + *pipe.Pipe + From, Target *edge + mu sync.Mutex +} + +// edgeState hold basic mutable state info for an edge +type edgeState struct { + state edgeStatusType + result *SharedCachedResult + cacheMap *CacheMap + keys []ExportableCacheKey +} + +type edgeRequest struct { + desiredState edgeStatusType + currentState edgeState + currentKeys int +} + +// incrementReferenceCount increases the number of times release needs to be +// called to release the edge. Called on merging edges. +func (e *edge) incrementReferenceCount() { + e.releaserCount += 1 +} + +// release releases the edge resources +func (e *edge) release() { + if e.releaserCount > 0 { + e.releaserCount-- + return + } + e.index.Release(e) + if e.result != nil { + go e.result.Release(context.TODO()) + } +} + +// commitOptions returns parameters for the op execution +func (e *edge) commitOptions() ([]*CacheKey, []CachedResult) { + k := NewCacheKey(e.cacheMap.Digest, e.edge.Index) + if len(e.deps) == 0 { + keys := make([]*CacheKey, 0, len(e.cacheMapDigests)) + for _, dgst := range e.cacheMapDigests { + keys = append(keys, NewCacheKey(dgst, e.edge.Index)) + } + return keys, nil + } + + inputs := make([][]CacheKeyWithSelector, len(e.deps)) + results := make([]CachedResult, len(e.deps)) + for i, dep := range e.deps { + inputs[i] = append(inputs[i], CacheKeyWithSelector{CacheKey: dep.result.CacheKey(), Selector: e.cacheMap.Deps[i].Selector}) + if dep.slowCacheKey != nil { + inputs[i] = append(inputs[i], CacheKeyWithSelector{CacheKey: *dep.slowCacheKey}) + } + results[i] = dep.result + } + + k.deps = inputs + return []*CacheKey{k}, results +} + +// isComplete returns true if edge state is final and will never change +func (e *edge) isComplete() bool { + return e.err != nil || e.result != nil +} + +// finishIncoming finalizes the incoming pipe request +func (e *edge) finishIncoming(req pipe.Sender) { + err := e.err + if req.Request().Canceled && err == nil { + err = context.Canceled + } + if debugScheduler { + logrus.Debugf("finishIncoming %s %v %#v desired=%s", e.edge.Vertex.Name(), err, e.edgeState, req.Request().Payload.(*edgeRequest).desiredState) + } + req.Finalize(&e.edgeState, err) +} + +// updateIncoming updates the current value of incoming pipe request +func (e *edge) updateIncoming(req pipe.Sender) { + req.Update(&e.edgeState) +} + +// probeCache is called with unprocessed cache keys for dependency +// if the key could match the edge, the cacheRecords for dependency are filled +func (e *edge) probeCache(d *dep, depKeys []CacheKeyWithSelector) bool { + if len(depKeys) == 0 { + return false + } + if e.op.IgnoreCache() { + return false + } + keys, err := e.op.Cache().Query(depKeys, d.index, e.cacheMap.Digest, e.edge.Index) + if err != nil { + e.err = errors.Wrap(err, "error on cache query") + } + found := false + for _, k := range keys { + if _, ok := d.keyMap[k.ID]; !ok { + d.keyMap[k.ID] = k + found = true + } + } + return found +} + +// checkDepMatchPossible checks if any cache matches are possible past this point +func (e *edge) checkDepMatchPossible(dep *dep) { + depHasSlowCache := e.cacheMap.Deps[dep.index].ComputeDigestFunc != nil + if !e.noCacheMatchPossible && (((!dep.slowCacheFoundKey && dep.slowCacheComplete && depHasSlowCache) || (!depHasSlowCache && dep.state >= edgeStatusCacheSlow)) && len(dep.keys) == 0) { + e.noCacheMatchPossible = true + } +} + +// slowCacheFunc returns the result based cache func for dependency if it exists +func (e *edge) slowCacheFunc(dep *dep) ResultBasedCacheFunc { + if e.cacheMap == nil { + return nil + } + return e.cacheMap.Deps[int(dep.index)].ComputeDigestFunc +} + +// allDepsHaveKeys checks if all dependencies have at least one key. used for +// determining if there is enough data for combining cache key for edge +func (e *edge) allDepsHaveKeys() bool { + if e.cacheMap == nil { + return false + } + for _, d := range e.deps { + if len(d.keys) == 0 && d.slowCacheKey == nil && d.result == nil { + return false + } + } + return true +} + +// depKeys returns all current dependency cache keys +func (e *edge) currentIndexKey() *CacheKey { + if e.cacheMap == nil { + return nil + } + + keys := make([][]CacheKeyWithSelector, len(e.deps)) + for i, d := range e.deps { + if len(d.keys) == 0 && d.result == nil { + return nil + } + for _, k := range d.keys { + keys[i] = append(keys[i], CacheKeyWithSelector{Selector: e.cacheMap.Deps[i].Selector, CacheKey: k}) + } + if d.result != nil { + keys[i] = append(keys[i], CacheKeyWithSelector{Selector: e.cacheMap.Deps[i].Selector, CacheKey: d.result.CacheKey()}) + if d.slowCacheKey != nil { + keys[i] = append(keys[i], CacheKeyWithSelector{CacheKey: ExportableCacheKey{CacheKey: d.slowCacheKey.CacheKey, Exporter: &exporter{k: d.slowCacheKey.CacheKey}}}) + } + } + } + + k := NewCacheKey(e.cacheMap.Digest, e.edge.Index) + k.deps = keys + + return k +} + +// slow cache keys can be computed in 2 phases if there are multiple deps. +// first evaluate ones that didn't match any definition based keys +func (e *edge) skipPhase2SlowCache(dep *dep) bool { + isPhase1 := false + for _, dep := range e.deps { + if !dep.slowCacheComplete && e.slowCacheFunc(dep) != nil && len(dep.keyMap) == 0 { + isPhase1 = true + break + } + } + + if isPhase1 && !dep.slowCacheComplete && e.slowCacheFunc(dep) != nil && len(dep.keyMap) > 0 { + return true + } + return false +} + +func (e *edge) skipPhase2FastCache(dep *dep) bool { + isPhase1 := false + for _, dep := range e.deps { + if e.cacheMap == nil || len(dep.keyMap) == 0 && ((!dep.slowCacheComplete && e.slowCacheFunc(dep) != nil) || (dep.state < edgeStatusComplete && e.slowCacheFunc(dep) == nil)) { + isPhase1 = true + break + } + } + + if isPhase1 && len(dep.keyMap) > 0 { + return true + } + return false +} + +// unpark is called by the scheduler with incoming requests and updates for +// previous calls. +// To avoid deadlocks and resource leaks this function needs to follow +// following rules: +// 1) this function needs to return unclosed outgoing requests if some incoming +// requests were not completed +// 2) this function may not return outgoing requests if it has completed all +// incoming requests +func (e *edge) unpark(incoming []pipe.Sender, updates, allPipes []pipe.Receiver, f *pipeFactory) { + // process all incoming changes + depChanged := false + for _, upt := range updates { + if changed := e.processUpdate(upt); changed { + depChanged = true + } + } + + if depChanged { + // the dep responses had changes. need to reevaluate edge state + e.recalcCurrentState() + } + + desiredState, done := e.respondToIncoming(incoming, allPipes) + if done { + return + } + + // set up new outgoing requests if needed + if e.cacheMapReq == nil && (e.cacheMap == nil || len(e.cacheRecords) == 0) { + index := e.cacheMapIndex + e.cacheMapReq = f.NewFuncRequest(func(ctx context.Context) (interface{}, error) { + return e.op.CacheMap(ctx, index) + }) + } + + // execute op + if e.execReq == nil && desiredState == edgeStatusComplete { + if ok := e.execIfPossible(f); ok { + return + } + } + + if e.execReq == nil { + e.createInputRequests(desiredState, f) + } + +} + +func (e *edge) makeExportable(k *CacheKey, records []*CacheRecord) ExportableCacheKey { + return ExportableCacheKey{ + CacheKey: k, + Exporter: &exporter{k: k, records: records, override: e.edge.Vertex.Options().ExportCache}, + } +} + +// processUpdate is called by unpark for every updated pipe request +func (e *edge) processUpdate(upt pipe.Receiver) (depChanged bool) { + // response for cachemap request + if upt == e.cacheMapReq && upt.Status().Completed { + if err := upt.Status().Err; err != nil { + e.cacheMapReq = nil + if !upt.Status().Canceled && e.err == nil { + e.err = err + } + } else { + resp := upt.Status().Value.(*cacheMapResp) + e.cacheMap = resp.CacheMap + e.cacheMapDone = resp.complete + e.cacheMapIndex++ + if len(e.deps) == 0 { + e.cacheMapDigests = append(e.cacheMapDigests, e.cacheMap.Digest) + if !e.op.IgnoreCache() { + keys, err := e.op.Cache().Query(nil, 0, e.cacheMap.Digest, e.edge.Index) + if err != nil { + logrus.Error(errors.Wrap(err, "invalid query response")) // make the build fail for this error + } else { + for _, k := range keys { + records, err := e.op.Cache().Records(k) + if err != nil { + logrus.Errorf("error receiving cache records: %v", err) + continue + } + + for _, r := range records { + e.cacheRecords[r.ID] = r + } + + e.keys = append(e.keys, e.makeExportable(k, records)) + } + } + } + e.state = edgeStatusCacheSlow + } + if e.allDepsHaveKeys() { + e.keysDidChange = true + } + // probe keys that were loaded before cache map + for i, dep := range e.deps { + e.probeCache(dep, withSelector(dep.keys, e.cacheMap.Deps[i].Selector)) + e.checkDepMatchPossible(dep) + } + if !e.cacheMapDone { + e.cacheMapReq = nil + } + } + return true + } + + // response for exec request + if upt == e.execReq && upt.Status().Completed { + if err := upt.Status().Err; err != nil { + e.execReq = nil + if !upt.Status().Canceled && e.err == nil { + e.err = err + } + } else { + e.result = NewSharedCachedResult(upt.Status().Value.(CachedResult)) + e.state = edgeStatusComplete + } + return true + } + + // response for requests to dependencies + if dep, ok := e.depRequests[upt]; ok { // TODO: ignore canceled + if err := upt.Status().Err; !upt.Status().Canceled && upt.Status().Completed && err != nil { + if e.err == nil { + e.err = err + } + dep.err = err + } + + state := upt.Status().Value.(*edgeState) + + if len(dep.keys) < len(state.keys) { + newKeys := state.keys[len(dep.keys):] + if e.cacheMap != nil { + e.probeCache(dep, withSelector(newKeys, e.cacheMap.Deps[dep.index].Selector)) + if e.allDepsHaveKeys() { + e.keysDidChange = true + } + } + depChanged = true + } + if dep.state != edgeStatusComplete && state.state == edgeStatusComplete { + e.keysDidChange = true + } + + recheck := state.state != dep.state + + dep.edgeState = *state + + if recheck && e.cacheMap != nil { + e.checkDepMatchPossible(dep) + depChanged = true + } + + return + } + + // response for result based cache function + for i, dep := range e.deps { + if upt == dep.slowCacheReq && upt.Status().Completed { + if err := upt.Status().Err; err != nil { + dep.slowCacheReq = nil + if !upt.Status().Canceled && e.err == nil { + e.err = upt.Status().Err + } + } else if !dep.slowCacheComplete { + k := NewCacheKey(upt.Status().Value.(digest.Digest), -1) + dep.slowCacheKey = &ExportableCacheKey{CacheKey: k, Exporter: &exporter{k: k}} + slowKeyExp := CacheKeyWithSelector{CacheKey: *dep.slowCacheKey} + defKeyExp := CacheKeyWithSelector{CacheKey: dep.result.CacheKey(), Selector: e.cacheMap.Deps[i].Selector} + dep.slowCacheFoundKey = e.probeCache(dep, []CacheKeyWithSelector{slowKeyExp}) + + // connect def key to slow key + e.op.Cache().Query([]CacheKeyWithSelector{defKeyExp, slowKeyExp}, dep.index, e.cacheMap.Digest, e.edge.Index) + + dep.slowCacheComplete = true + e.keysDidChange = true + e.checkDepMatchPossible(dep) // not matching key here doesn't set nocachematch possible to true + } + return true + } + } + + return +} + +// recalcCurrentState is called by unpark to recompute internal state after +// the state of dependencies has changed +func (e *edge) recalcCurrentState() { + // TODO: fast pass to detect incomplete results + newKeys := map[string]*CacheKey{} + + for i, dep := range e.deps { + if i == 0 { + for id, k := range dep.keyMap { + if _, ok := e.keyMap[id]; ok { + continue + } + newKeys[id] = k + } + } else { + for id := range newKeys { + if _, ok := dep.keyMap[id]; !ok { + delete(newKeys, id) + } + } + } + if len(newKeys) == 0 { + break + } + } + + for _, r := range newKeys { + // TODO: add all deps automatically + mergedKey := r.clone() + mergedKey.deps = make([][]CacheKeyWithSelector, len(e.deps)) + for i, dep := range e.deps { + if dep.result != nil { + mergedKey.deps[i] = append(mergedKey.deps[i], CacheKeyWithSelector{Selector: e.cacheMap.Deps[i].Selector, CacheKey: dep.result.CacheKey()}) + if dep.slowCacheKey != nil { + mergedKey.deps[i] = append(mergedKey.deps[i], CacheKeyWithSelector{CacheKey: *dep.slowCacheKey}) + } + } else { + for _, k := range dep.keys { + mergedKey.deps[i] = append(mergedKey.deps[i], CacheKeyWithSelector{Selector: e.cacheMap.Deps[i].Selector, CacheKey: k}) + } + } + } + + records, err := e.op.Cache().Records(mergedKey) + if err != nil { + logrus.Errorf("error receiving cache records: %v", err) + continue + } + + for _, r := range records { + e.cacheRecords[r.ID] = r + } + + e.keys = append(e.keys, e.makeExportable(mergedKey, records)) + } + + // detect lower/upper bound for current state + allDepsCompletedCacheFast := e.cacheMap != nil + allDepsCompletedCacheSlow := e.cacheMap != nil + allDepsStateCacheSlow := true + allDepsCompleted := true + stLow := edgeStatusInitial // minimal possible state + stHigh := edgeStatusCacheSlow // maximum possible state + if e.cacheMap != nil { + for _, dep := range e.deps { + isSlowIncomplete := e.slowCacheFunc(dep) != nil && (dep.state == edgeStatusCacheSlow || (dep.state == edgeStatusComplete && !dep.slowCacheComplete)) + + if dep.state > stLow && len(dep.keyMap) == 0 && !isSlowIncomplete { + stLow = dep.state + if stLow > edgeStatusCacheSlow { + stLow = edgeStatusCacheSlow + } + } + effectiveState := dep.state + if dep.state == edgeStatusCacheSlow && isSlowIncomplete { + effectiveState = edgeStatusCacheFast + } + if dep.state == edgeStatusComplete && isSlowIncomplete { + effectiveState = edgeStatusCacheFast + } + if effectiveState < stHigh { + stHigh = effectiveState + } + if isSlowIncomplete || dep.state < edgeStatusComplete { + allDepsCompleted = false + } + if dep.state < edgeStatusCacheFast { + allDepsCompletedCacheFast = false + } + if isSlowIncomplete || dep.state < edgeStatusCacheSlow { + allDepsCompletedCacheSlow = false + } + if dep.state < edgeStatusCacheSlow && len(dep.keys) == 0 { + allDepsStateCacheSlow = false + } + } + if stLow > e.state { + e.state = stLow + } + if stHigh > e.state { + e.state = stHigh + } + if !e.cacheMapDone && len(e.keys) == 0 { + e.state = edgeStatusInitial + } + + e.allDepsCompletedCacheFast = e.cacheMapDone && allDepsCompletedCacheFast + e.allDepsCompletedCacheSlow = e.cacheMapDone && allDepsCompletedCacheSlow + e.allDepsStateCacheSlow = e.cacheMapDone && allDepsStateCacheSlow + e.allDepsCompleted = e.cacheMapDone && allDepsCompleted + } +} + +// respondToIncoming responds to all incoming requests. completing or +// updating them when possible +func (e *edge) respondToIncoming(incoming []pipe.Sender, allPipes []pipe.Receiver) (edgeStatusType, bool) { + // detect the result state for the requests + allIncomingCanComplete := true + desiredState := e.state + allCanceled := true + + // check incoming requests + // check if all requests can be either answered or canceled + if !e.isComplete() { + for _, req := range incoming { + if !req.Request().Canceled { + allCanceled = false + if r := req.Request().Payload.(*edgeRequest); desiredState < r.desiredState { + desiredState = r.desiredState + if e.hasActiveOutgoing || r.desiredState == edgeStatusComplete || r.currentKeys == len(e.keys) { + allIncomingCanComplete = false + } + } + } + } + } + + // do not set allIncomingCanComplete if active ongoing can modify the state + if !allCanceled && e.state < edgeStatusComplete && len(e.keys) == 0 && e.hasActiveOutgoing { + allIncomingCanComplete = false + } + + if debugScheduler { + logrus.Debugf("status state=%s cancomplete=%v hasouts=%v noPossibleCache=%v depsCacheFast=%v keys=%d cacheRecords=%d", e.state, allIncomingCanComplete, e.hasActiveOutgoing, e.noCacheMatchPossible, e.allDepsCompletedCacheFast, len(e.keys), len(e.cacheRecords)) + } + + if allIncomingCanComplete && e.hasActiveOutgoing { + // cancel all current requests + for _, p := range allPipes { + p.Cancel() + } + + // can close all but one requests + var leaveOpen pipe.Sender + for _, req := range incoming { + if !req.Request().Canceled { + leaveOpen = req + break + } + } + for _, req := range incoming { + if leaveOpen == nil || leaveOpen == req { + leaveOpen = req + continue + } + e.finishIncoming(req) + } + return desiredState, true + } + + // can complete, finish and return + if allIncomingCanComplete && !e.hasActiveOutgoing { + for _, req := range incoming { + e.finishIncoming(req) + } + return desiredState, true + } + + // update incoming based on current state + for _, req := range incoming { + r := req.Request().Payload.(*edgeRequest) + if req.Request().Canceled { + e.finishIncoming(req) + } else if !e.hasActiveOutgoing && e.state >= r.desiredState { + e.finishIncoming(req) + } else if !isEqualState(r.currentState, e.edgeState) && !req.Request().Canceled { + e.updateIncoming(req) + } + } + return desiredState, false +} + +// createInputRequests creates new requests for dependencies or async functions +// that need to complete to continue processing the edge +func (e *edge) createInputRequests(desiredState edgeStatusType, f *pipeFactory) { + // initialize deps state + if e.deps == nil { + e.depRequests = make(map[pipe.Receiver]*dep) + e.deps = make([]*dep, 0, len(e.edge.Vertex.Inputs())) + for i := range e.edge.Vertex.Inputs() { + e.deps = append(e.deps, newDep(Index(i))) + } + } + + // cycle all dependencies. set up outgoing requests if needed + for _, dep := range e.deps { + desiredStateDep := dep.state + + if e.noCacheMatchPossible { + desiredStateDep = edgeStatusComplete + } else if dep.state == edgeStatusInitial && desiredState > dep.state { + desiredStateDep = edgeStatusCacheFast + } else if dep.state == edgeStatusCacheFast && desiredState > dep.state { + // wait all deps to complete cache fast before continuing with slow cache + if (e.allDepsCompletedCacheFast && len(e.keys) == 0) || len(dep.keys) == 0 || e.allDepsHaveKeys() { + if !e.skipPhase2FastCache(dep) { + desiredStateDep = edgeStatusCacheSlow + } + } + } else if dep.state == edgeStatusCacheSlow && desiredState == edgeStatusComplete { + // if all deps have completed cache-slow or content based cache for input is available + if (len(dep.keys) == 0 || e.allDepsCompletedCacheSlow || (!e.skipPhase2FastCache(dep) && e.slowCacheFunc(dep) != nil)) && (len(e.cacheRecords) == 0) { + if len(dep.keys) == 0 || !e.skipPhase2SlowCache(dep) && e.allDepsStateCacheSlow { + desiredStateDep = edgeStatusComplete + } + } + } else if dep.state == edgeStatusCacheSlow && e.slowCacheFunc(dep) != nil && desiredState == edgeStatusCacheSlow { + if len(dep.keys) == 0 || !e.skipPhase2SlowCache(dep) && e.allDepsStateCacheSlow { + desiredStateDep = edgeStatusComplete + } + } + + // outgoing request is needed + if dep.state < desiredStateDep { + addNew := true + if dep.req != nil && !dep.req.Status().Completed { + if dep.req.Request().(*edgeRequest).desiredState != desiredStateDep { + dep.req.Cancel() + } else { + addNew = false + } + } + if addNew { + req := f.NewInputRequest(e.edge.Vertex.Inputs()[int(dep.index)], &edgeRequest{ + currentState: dep.edgeState, + desiredState: desiredStateDep, + currentKeys: len(dep.keys), + }) + e.depRequests[req] = dep + dep.req = req + } + } + // initialize function to compute cache key based on dependency result + if dep.state == edgeStatusComplete && dep.slowCacheReq == nil && e.slowCacheFunc(dep) != nil && e.cacheMap != nil { + fn := e.slowCacheFunc(dep) + res := dep.result + func(fn ResultBasedCacheFunc, res Result, index Index) { + dep.slowCacheReq = f.NewFuncRequest(func(ctx context.Context) (interface{}, error) { + return e.op.CalcSlowCache(ctx, index, fn, res) + }) + }(fn, res, dep.index) + } + } +} + +// execIfPossible creates a request for getting the edge result if there is +// enough state +func (e *edge) execIfPossible(f *pipeFactory) bool { + if len(e.cacheRecords) > 0 { + if e.keysDidChange { + e.postpone(f) + return true + } + e.execReq = f.NewFuncRequest(e.loadCache) + for req := range e.depRequests { + req.Cancel() + } + return true + } else if e.allDepsCompleted { + if e.keysDidChange { + e.postpone(f) + return true + } + e.execReq = f.NewFuncRequest(e.execOp) + return true + } + return false +} + +// postpone delays exec to next unpark invocation if we have unprocessed keys +func (e *edge) postpone(f *pipeFactory) { + f.NewFuncRequest(func(context.Context) (interface{}, error) { + return nil, nil + }) +} + +// loadCache creates a request to load edge result from cache +func (e *edge) loadCache(ctx context.Context) (interface{}, error) { + recs := make([]*CacheRecord, 0, len(e.cacheRecords)) + for _, r := range e.cacheRecords { + recs = append(recs, r) + } + + rec := getBestResult(recs) + + logrus.Debugf("load cache for %s with %s", e.edge.Vertex.Name(), rec.ID) + res, err := e.op.LoadCache(ctx, rec) + if err != nil { + return nil, err + } + + return NewCachedResult(res, ExportableCacheKey{CacheKey: rec.key, Exporter: &exporter{k: rec.key, record: rec, edge: e}}), nil +} + +// execOp creates a request to execute the vertex operation +func (e *edge) execOp(ctx context.Context) (interface{}, error) { + cacheKeys, inputs := e.commitOptions() + results, subExporters, err := e.op.Exec(ctx, toResultSlice(inputs)) + if err != nil { + return nil, err + } + + index := e.edge.Index + if len(results) <= int(index) { + return nil, errors.Errorf("invalid response from exec need %d index but %d results received", index, len(results)) + } + + res := results[int(index)] + + for i := range results { + if i != int(index) { + go results[i].Release(context.TODO()) + } + } + + var exporters []CacheExporter + + for _, cacheKey := range cacheKeys { + ck, err := e.op.Cache().Save(cacheKey, res) + if err != nil { + return nil, err + } + + if exp, ok := ck.Exporter.(*exporter); ok { + exp.edge = e + } + + exps := make([]CacheExporter, 0, len(subExporters)) + for _, exp := range subExporters { + exps = append(exps, exp.Exporter) + } + + exporters = append(exporters, ck.Exporter) + exporters = append(exporters, exps...) + } + + ck := &ExportableCacheKey{ + CacheKey: cacheKeys[0], + Exporter: &mergedExporter{exporters: exporters}, + } + + return NewCachedResult(res, *ck), nil +} + +func toResultSlice(cres []CachedResult) (out []Result) { + out = make([]Result, len(cres)) + for i := range cres { + out[i] = cres[i].(Result) + } + return out +} + +func isEqualState(s1, s2 edgeState) bool { + if s1.state != s2.state || s1.result != s2.result || s1.cacheMap != s2.cacheMap || len(s1.keys) != len(s2.keys) { + return false + } + return true +} + +func withSelector(keys []ExportableCacheKey, selector digest.Digest) []CacheKeyWithSelector { + out := make([]CacheKeyWithSelector, len(keys)) + for i, k := range keys { + out[i] = CacheKeyWithSelector{Selector: selector, CacheKey: k} + } + return out +} diff --git a/vendor/github.com/moby/buildkit/solver/exporter.go b/vendor/github.com/moby/buildkit/solver/exporter.go new file mode 100644 index 0000000000000..fa963f414a3e3 --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/exporter.go @@ -0,0 +1,208 @@ +package solver + +import ( + "context" + + digest "github.com/opencontainers/go-digest" +) + +type exporter struct { + k *CacheKey + records []*CacheRecord + record *CacheRecord + + res []CacheExporterRecord + edge *edge // for secondaryExporters + override *bool +} + +func addBacklinks(t CacheExporterTarget, rec CacheExporterRecord, cm *cacheManager, id string, bkm map[string]CacheExporterRecord) (CacheExporterRecord, error) { + if rec == nil { + var ok bool + rec, ok = bkm[id] + if ok { + return rec, nil + } + _ = ok + } + if err := cm.backend.WalkBacklinks(id, func(id string, link CacheInfoLink) error { + if rec == nil { + rec = t.Add(link.Digest) + } + r, ok := bkm[id] + if !ok { + var err error + r, err = addBacklinks(t, nil, cm, id, bkm) + if err != nil { + return err + } + } + rec.LinkFrom(r, int(link.Input), link.Selector.String()) + return nil + }); err != nil { + return nil, err + } + if rec == nil { + rec = t.Add(digest.Digest(id)) + } + bkm[id] = rec + return rec, nil +} + +type backlinkT struct{} + +var backlinkKey = backlinkT{} + +func (e *exporter) ExportTo(ctx context.Context, t CacheExporterTarget, opt CacheExportOpt) ([]CacheExporterRecord, error) { + var bkm map[string]CacheExporterRecord + + if bk := ctx.Value(backlinkKey); bk == nil { + bkm = map[string]CacheExporterRecord{} + ctx = context.WithValue(ctx, backlinkKey, bkm) + } else { + bkm = bk.(map[string]CacheExporterRecord) + } + + if t.Visited(e) { + return e.res, nil + } + + deps := e.k.Deps() + + type expr struct { + r CacheExporterRecord + selector digest.Digest + } + + rec := t.Add(rootKey(e.k.Digest(), e.k.Output())) + allRec := []CacheExporterRecord{rec} + + addRecord := true + + if e.override != nil { + addRecord = *e.override + } + + if e.record == nil && len(e.k.Deps()) > 0 { + e.record = getBestResult(e.records) + } + + var remote *Remote + if v := e.record; v != nil && len(e.k.Deps()) > 0 && addRecord { + cm := v.cacheManager + key := cm.getID(v.key) + res, err := cm.backend.Load(key, v.ID) + if err != nil { + return nil, err + } + + remote, err = cm.results.LoadRemote(ctx, res) + if err != nil { + return nil, err + } + + if remote == nil && opt.Mode != CacheExportModeRemoteOnly { + res, err := cm.results.Load(ctx, res) + if err != nil { + return nil, err + } + remote, err = opt.Convert(ctx, res) + if err != nil { + return nil, err + } + res.Release(context.TODO()) + } + + if remote != nil { + for _, rec := range allRec { + rec.AddResult(v.CreatedAt, remote) + } + } + } + + if remote != nil && opt.Mode == CacheExportModeMin { + opt.Mode = CacheExportModeRemoteOnly + } + + srcs := make([][]expr, len(deps)) + + for i, deps := range deps { + for _, dep := range deps { + recs, err := dep.CacheKey.Exporter.ExportTo(ctx, t, opt) + if err != nil { + return nil, nil + } + for _, r := range recs { + srcs[i] = append(srcs[i], expr{r: r, selector: dep.Selector}) + } + } + } + + if e.edge != nil { + for _, de := range e.edge.secondaryExporters { + recs, err := de.cacheKey.CacheKey.Exporter.ExportTo(ctx, t, opt) + if err != nil { + return nil, nil + } + for _, r := range recs { + srcs[de.index] = append(srcs[de.index], expr{r: r, selector: de.cacheKey.Selector}) + } + } + } + + for i, srcs := range srcs { + for _, src := range srcs { + rec.LinkFrom(src.r, i, src.selector.String()) + } + } + + for cm, id := range e.k.ids { + if _, err := addBacklinks(t, rec, cm, id, bkm); err != nil { + return nil, err + } + } + + if v := e.record; v != nil && len(deps) == 0 { + cm := v.cacheManager + key := cm.getID(v.key) + if err := cm.backend.WalkIDsByResult(v.ID, func(id string) error { + if id == key { + return nil + } + allRec = append(allRec, t.Add(digest.Digest(id))) + return nil + }); err != nil { + return nil, err + } + } + + e.res = allRec + t.Visit(e) + + return e.res, nil +} + +func getBestResult(records []*CacheRecord) *CacheRecord { + var rec *CacheRecord + for _, r := range records { + if rec == nil || rec.CreatedAt.Before(r.CreatedAt) || (rec.CreatedAt.Equal(r.CreatedAt) && rec.Priority < r.Priority) { + rec = r + } + } + return rec +} + +type mergedExporter struct { + exporters []CacheExporter +} + +func (e *mergedExporter) ExportTo(ctx context.Context, t CacheExporterTarget, opt CacheExportOpt) (er []CacheExporterRecord, err error) { + for _, e := range e.exporters { + r, err := e.ExportTo(ctx, t, opt) + if err != nil { + return nil, err + } + er = append(er, r...) + } + return +} diff --git a/vendor/github.com/moby/buildkit/solver/index.go b/vendor/github.com/moby/buildkit/solver/index.go new file mode 100644 index 0000000000000..1bb17d8944320 --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/index.go @@ -0,0 +1,243 @@ +package solver + +import ( + "sync" + + "github.com/moby/buildkit/identity" +) + +// edgeIndex is a synchronous map for detecting edge collisions. +type edgeIndex struct { + mu sync.Mutex + + items map[string]*indexItem + backRefs map[*edge]map[string]struct{} +} + +type indexItem struct { + edge *edge + links map[CacheInfoLink]map[string]struct{} + deps map[string]struct{} +} + +func newEdgeIndex() *edgeIndex { + return &edgeIndex{ + items: map[string]*indexItem{}, + backRefs: map[*edge]map[string]struct{}{}, + } +} + +func (ei *edgeIndex) Release(e *edge) { + ei.mu.Lock() + defer ei.mu.Unlock() + + for id := range ei.backRefs[e] { + ei.releaseEdge(id, e) + } + delete(ei.backRefs, e) +} + +func (ei *edgeIndex) releaseEdge(id string, e *edge) { + item, ok := ei.items[id] + if !ok { + return + } + + item.edge = nil + + if len(item.links) == 0 { + for d := range item.deps { + ei.releaseLink(d, id) + } + delete(ei.items, id) + } +} + +func (ei *edgeIndex) releaseLink(id, target string) { + item, ok := ei.items[id] + if !ok { + return + } + + for lid, links := range item.links { + for check := range links { + if check == target { + delete(links, check) + } + } + if len(links) == 0 { + delete(item.links, lid) + } + } + + if item.edge == nil && len(item.links) == 0 { + for d := range item.deps { + ei.releaseLink(d, id) + } + delete(ei.items, id) + } +} + +func (ei *edgeIndex) LoadOrStore(k *CacheKey, e *edge) *edge { + ei.mu.Lock() + defer ei.mu.Unlock() + + // get all current edges that match the cachekey + ids := ei.getAllMatches(k) + + var oldID string + var old *edge + + for _, id := range ids { + if item, ok := ei.items[id]; ok { + if item.edge != e { + oldID = id + old = item.edge + } + } + } + + if old != nil && !(!isIgnoreCache(old) && isIgnoreCache(e)) { + ei.enforceLinked(oldID, k) + return old + } + + id := identity.NewID() + if len(ids) > 0 { + id = ids[0] + } + + ei.enforceLinked(id, k) + + ei.items[id].edge = e + backRefs, ok := ei.backRefs[e] + if !ok { + backRefs = map[string]struct{}{} + ei.backRefs[e] = backRefs + } + backRefs[id] = struct{}{} + + return nil +} + +// enforceLinked adds links from current ID to all dep keys +func (er *edgeIndex) enforceLinked(id string, k *CacheKey) { + main, ok := er.items[id] + if !ok { + main = &indexItem{ + links: map[CacheInfoLink]map[string]struct{}{}, + deps: map[string]struct{}{}, + } + er.items[id] = main + } + + deps := k.Deps() + + for i, dd := range deps { + for _, d := range dd { + ck := d.CacheKey.CacheKey + er.enforceIndexID(ck) + ll := CacheInfoLink{Input: Index(i), Digest: k.Digest(), Output: k.Output(), Selector: d.Selector} + for _, ckID := range ck.indexIDs { + if item, ok := er.items[ckID]; ok { + links, ok := item.links[ll] + if !ok { + links = map[string]struct{}{} + item.links[ll] = links + } + links[id] = struct{}{} + main.deps[ckID] = struct{}{} + } + } + } + } +} + +func (ei *edgeIndex) enforceIndexID(k *CacheKey) { + if len(k.indexIDs) > 0 { + return + } + + matches := ei.getAllMatches(k) + + if len(matches) > 0 { + k.indexIDs = matches + } else { + k.indexIDs = []string{identity.NewID()} + } + + for _, id := range k.indexIDs { + ei.enforceLinked(id, k) + } +} + +func (ei *edgeIndex) getAllMatches(k *CacheKey) []string { + deps := k.Deps() + + if len(deps) == 0 { + return []string{rootKey(k.Digest(), k.Output()).String()} + } + + for _, dd := range deps { + for _, k := range dd { + ei.enforceIndexID(k.CacheKey.CacheKey) + } + } + + matches := map[string]struct{}{} + + for i, dd := range deps { + if i == 0 { + for _, d := range dd { + ll := CacheInfoLink{Input: Index(i), Digest: k.Digest(), Output: k.Output(), Selector: d.Selector} + for _, ckID := range d.CacheKey.CacheKey.indexIDs { + item, ok := ei.items[ckID] + if ok { + for l := range item.links[ll] { + matches[l] = struct{}{} + } + } + } + } + continue + } + + if len(matches) == 0 { + break + } + + for m := range matches { + found := false + for _, d := range dd { + ll := CacheInfoLink{Input: Index(i), Digest: k.Digest(), Output: k.Output(), Selector: d.Selector} + for _, ckID := range d.CacheKey.CacheKey.indexIDs { + if l, ok := ei.items[ckID].links[ll]; ok { + if _, ok := l[m]; ok { + found = true + break + } + } + } + } + + if !found { + delete(matches, m) + } + } + } + + out := make([]string, 0, len(matches)) + + for m := range matches { + out = append(out, m) + } + + return out +} + +func isIgnoreCache(e *edge) bool { + if e.edge.Vertex == nil { + return false + } + return e.edge.Vertex.Options().IgnoreCache +} diff --git a/vendor/github.com/moby/buildkit/solver/internal/pipe/pipe.go b/vendor/github.com/moby/buildkit/solver/internal/pipe/pipe.go new file mode 100644 index 0000000000000..e61a6b3465bbe --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/internal/pipe/pipe.go @@ -0,0 +1,197 @@ +package pipe + +import ( + "context" + "sync" + "sync/atomic" + + "github.com/pkg/errors" +) + +type channel struct { + OnSendCompletion func() + value atomic.Value + lastValue interface{} +} + +func (c *channel) Send(v interface{}) { + c.value.Store(v) + if c.OnSendCompletion != nil { + c.OnSendCompletion() + } +} + +func (c *channel) Receive() (interface{}, bool) { + v := c.value.Load() + if c.lastValue == v { + return nil, false + } + c.lastValue = v + return v, true +} + +type Pipe struct { + Sender Sender + Receiver Receiver + OnReceiveCompletion func() + OnSendCompletion func() +} + +type Request struct { + Payload interface{} + Canceled bool +} + +type Sender interface { + Request() Request + Update(v interface{}) + Finalize(v interface{}, err error) + Status() Status +} + +type Receiver interface { + Receive() bool + Cancel() + Status() Status + Request() interface{} +} + +type Status struct { + Canceled bool + Completed bool + Err error + Value interface{} +} + +func NewWithFunction(f func(context.Context) (interface{}, error)) (*Pipe, func()) { + p := New(Request{}) + + ctx, cancel := context.WithCancel(context.TODO()) + + p.OnReceiveCompletion = func() { + if req := p.Sender.Request(); req.Canceled { + cancel() + } + } + + return p, func() { + res, err := f(ctx) + if err != nil { + p.Sender.Finalize(nil, err) + return + } + p.Sender.Finalize(res, nil) + } +} + +func New(req Request) *Pipe { + cancelCh := &channel{} + roundTripCh := &channel{} + pw := &sender{ + req: req, + recvChannel: cancelCh, + sendChannel: roundTripCh, + } + pr := &receiver{ + req: req, + recvChannel: roundTripCh, + sendChannel: cancelCh, + } + + p := &Pipe{ + Sender: pw, + Receiver: pr, + } + + cancelCh.OnSendCompletion = func() { + v, ok := cancelCh.Receive() + if ok { + pw.setRequest(v.(Request)) + } + if p.OnReceiveCompletion != nil { + p.OnReceiveCompletion() + } + } + + roundTripCh.OnSendCompletion = func() { + if p.OnSendCompletion != nil { + p.OnSendCompletion() + } + } + + return p +} + +type sender struct { + status Status + req Request + recvChannel *channel + sendChannel *channel + mu sync.Mutex +} + +func (pw *sender) Status() Status { + return pw.status +} + +func (pw *sender) Request() Request { + pw.mu.Lock() + defer pw.mu.Unlock() + return pw.req +} + +func (pw *sender) setRequest(req Request) { + pw.mu.Lock() + defer pw.mu.Unlock() + pw.req = req +} + +func (pw *sender) Update(v interface{}) { + pw.status.Value = v + pw.sendChannel.Send(pw.status) +} + +func (pw *sender) Finalize(v interface{}, err error) { + if v != nil { + pw.status.Value = v + } + pw.status.Err = err + pw.status.Completed = true + if errors.Cause(err) == context.Canceled && pw.req.Canceled { + pw.status.Canceled = true + } + pw.sendChannel.Send(pw.status) +} + +type receiver struct { + status Status + req Request + recvChannel *channel + sendChannel *channel +} + +func (pr *receiver) Request() interface{} { + return pr.req.Payload +} + +func (pr *receiver) Receive() bool { + v, ok := pr.recvChannel.Receive() + if !ok { + return false + } + pr.status = v.(Status) + return true +} + +func (pr *receiver) Cancel() { + req := pr.req + if req.Canceled { + return + } + req.Canceled = true + pr.sendChannel.Send(req) +} + +func (pr *receiver) Status() Status { + return pr.status +} diff --git a/vendor/github.com/moby/buildkit/solver/jobs.go b/vendor/github.com/moby/buildkit/solver/jobs.go new file mode 100644 index 0000000000000..bce6c8b875084 --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/jobs.go @@ -0,0 +1,774 @@ +package solver + +import ( + "context" + "fmt" + "sync" + "time" + + "github.com/moby/buildkit/client" + "github.com/moby/buildkit/identity" + "github.com/moby/buildkit/session" + "github.com/moby/buildkit/util/flightcontrol" + "github.com/moby/buildkit/util/progress" + "github.com/moby/buildkit/util/tracing" + digest "github.com/opencontainers/go-digest" + "github.com/pkg/errors" +) + +// ResolveOpFunc finds an Op implementation for a Vertex +type ResolveOpFunc func(Vertex, Builder) (Op, error) + +type Builder interface { + Build(ctx context.Context, e Edge) (CachedResult, error) + Call(ctx context.Context, name string, fn func(ctx context.Context) error) error +} + +// Solver provides a shared graph of all the vertexes currently being +// processed. Every vertex that is being solved needs to be loaded into job +// first. Vertex operations are invoked and progress tracking happends through +// jobs. +type Solver struct { + mu sync.RWMutex + jobs map[string]*Job + actives map[digest.Digest]*state + opts SolverOpt + + updateCond *sync.Cond + s *scheduler + index *edgeIndex +} + +type state struct { + jobs map[*Job]struct{} + parents map[digest.Digest]struct{} + childVtx map[digest.Digest]struct{} + + mpw *progress.MultiWriter + allPw map[progress.Writer]struct{} + + vtx Vertex + clientVertex client.Vertex + + mu sync.Mutex + op *sharedOp + edges map[Index]*edge + opts SolverOpt + index *edgeIndex + + cache map[string]CacheManager + mainCache CacheManager + solver *Solver +} + +func (s *state) getSessionID() string { + // TODO: connect with sessionmanager to avoid getting dropped sessions + s.mu.Lock() + for j := range s.jobs { + if j.SessionID != "" { + s.mu.Unlock() + return j.SessionID + } + } + parents := map[digest.Digest]struct{}{} + for p := range s.parents { + parents[p] = struct{}{} + } + s.mu.Unlock() + + for p := range parents { + s.solver.mu.Lock() + pst, ok := s.solver.actives[p] + s.solver.mu.Unlock() + if ok { + if sessionID := pst.getSessionID(); sessionID != "" { + return sessionID + } + } + } + return "" +} + +func (s *state) builder() *subBuilder { + return &subBuilder{state: s} +} + +func (s *state) getEdge(index Index) *edge { + s.mu.Lock() + defer s.mu.Unlock() + if e, ok := s.edges[index]; ok { + return e + } + + if s.op == nil { + s.op = newSharedOp(s.opts.ResolveOpFunc, s.opts.DefaultCache, s) + } + + e := newEdge(Edge{Index: index, Vertex: s.vtx}, s.op, s.index) + s.edges[index] = e + return e +} + +func (s *state) setEdge(index Index, newEdge *edge) { + s.mu.Lock() + defer s.mu.Unlock() + e, ok := s.edges[index] + if ok { + if e == newEdge { + return + } + e.release() + } + + newEdge.incrementReferenceCount() + s.edges[index] = newEdge +} + +func (s *state) combinedCacheManager() CacheManager { + s.mu.Lock() + cms := make([]CacheManager, 0, len(s.cache)+1) + cms = append(cms, s.mainCache) + for _, cm := range s.cache { + cms = append(cms, cm) + } + s.mu.Unlock() + + if len(cms) == 1 { + return s.mainCache + } + + return newCombinedCacheManager(cms, s.mainCache) +} + +func (s *state) Release() { + for _, e := range s.edges { + e.release() + } + if s.op != nil { + s.op.release() + } +} + +type subBuilder struct { + *state + mu sync.Mutex + exporters []ExportableCacheKey +} + +func (sb *subBuilder) Build(ctx context.Context, e Edge) (CachedResult, error) { + res, err := sb.solver.subBuild(ctx, e, sb.vtx) + if err != nil { + return nil, err + } + sb.mu.Lock() + sb.exporters = append(sb.exporters, res.CacheKey()) + sb.mu.Unlock() + return res, nil +} + +func (sb *subBuilder) Call(ctx context.Context, name string, fn func(ctx context.Context) error) error { + ctx = progress.WithProgress(ctx, sb.mpw) + return inVertexContext(ctx, name, fn) +} + +type Job struct { + list *Solver + pr *progress.MultiReader + pw progress.Writer + + progressCloser func() + SessionID string +} + +type SolverOpt struct { + ResolveOpFunc ResolveOpFunc + DefaultCache CacheManager +} + +func NewSolver(opts SolverOpt) *Solver { + if opts.DefaultCache == nil { + opts.DefaultCache = NewInMemoryCacheManager() + } + jl := &Solver{ + jobs: make(map[string]*Job), + actives: make(map[digest.Digest]*state), + opts: opts, + index: newEdgeIndex(), + } + jl.s = newScheduler(jl) + jl.updateCond = sync.NewCond(jl.mu.RLocker()) + return jl +} + +func (jl *Solver) setEdge(e Edge, newEdge *edge) { + jl.mu.RLock() + defer jl.mu.RUnlock() + + st, ok := jl.actives[e.Vertex.Digest()] + if !ok { + return + } + + st.setEdge(e.Index, newEdge) +} + +func (jl *Solver) getEdge(e Edge) *edge { + jl.mu.RLock() + defer jl.mu.RUnlock() + + st, ok := jl.actives[e.Vertex.Digest()] + if !ok { + return nil + } + return st.getEdge(e.Index) +} + +func (jl *Solver) subBuild(ctx context.Context, e Edge, parent Vertex) (CachedResult, error) { + v, err := jl.load(e.Vertex, parent, nil) + if err != nil { + return nil, err + } + e.Vertex = v + return jl.s.build(ctx, e) +} + +func (jl *Solver) Close() { + jl.s.Stop() +} + +func (jl *Solver) load(v, parent Vertex, j *Job) (Vertex, error) { + jl.mu.Lock() + defer jl.mu.Unlock() + + cache := map[Vertex]Vertex{} + + return jl.loadUnlocked(v, parent, j, cache) +} + +func (jl *Solver) loadUnlocked(v, parent Vertex, j *Job, cache map[Vertex]Vertex) (Vertex, error) { + if v, ok := cache[v]; ok { + return v, nil + } + origVtx := v + + inputs := make([]Edge, len(v.Inputs())) + for i, e := range v.Inputs() { + v, err := jl.loadUnlocked(e.Vertex, parent, j, cache) + if err != nil { + return nil, err + } + inputs[i] = Edge{Index: e.Index, Vertex: v} + } + + dgst := v.Digest() + + dgstWithoutCache := digest.FromBytes([]byte(fmt.Sprintf("%s-ignorecache", dgst))) + + // if same vertex is already loaded without cache just use that + st, ok := jl.actives[dgstWithoutCache] + + if !ok { + st, ok = jl.actives[dgst] + + // !ignorecache merges with ignorecache but ignorecache doesn't merge with !ignorecache + if ok && !st.vtx.Options().IgnoreCache && v.Options().IgnoreCache { + dgst = dgstWithoutCache + } + + v = &vertexWithCacheOptions{ + Vertex: v, + dgst: dgst, + inputs: inputs, + } + + st, ok = jl.actives[dgst] + } + + if !ok { + st = &state{ + opts: jl.opts, + jobs: map[*Job]struct{}{}, + parents: map[digest.Digest]struct{}{}, + childVtx: map[digest.Digest]struct{}{}, + allPw: map[progress.Writer]struct{}{}, + mpw: progress.NewMultiWriter(progress.WithMetadata("vertex", dgst)), + vtx: v, + clientVertex: initClientVertex(v), + edges: map[Index]*edge{}, + index: jl.index, + mainCache: jl.opts.DefaultCache, + cache: map[string]CacheManager{}, + solver: jl, + } + jl.actives[dgst] = st + } + + st.mu.Lock() + for _, cache := range v.Options().CacheSources { + if cache.ID() != st.mainCache.ID() { + if _, ok := st.cache[cache.ID()]; !ok { + st.cache[cache.ID()] = cache + } + } + } + + if j != nil { + if _, ok := st.jobs[j]; !ok { + st.jobs[j] = struct{}{} + } + } + st.mu.Unlock() + + if parent != nil { + if _, ok := st.parents[parent.Digest()]; !ok { + st.parents[parent.Digest()] = struct{}{} + parentState, ok := jl.actives[parent.Digest()] + if !ok { + return nil, errors.Errorf("inactive parent %s", parent.Digest()) + } + parentState.childVtx[dgst] = struct{}{} + + for id, c := range parentState.cache { + st.cache[id] = c + } + } + } + + jl.connectProgressFromState(st, st) + cache[origVtx] = v + return v, nil +} + +func (jl *Solver) connectProgressFromState(target, src *state) { + for j := range src.jobs { + if _, ok := target.allPw[j.pw]; !ok { + target.mpw.Add(j.pw) + target.allPw[j.pw] = struct{}{} + j.pw.Write(target.clientVertex.Digest.String(), target.clientVertex) + } + } + for p := range src.parents { + jl.connectProgressFromState(target, jl.actives[p]) + } +} + +func (jl *Solver) NewJob(id string) (*Job, error) { + jl.mu.Lock() + defer jl.mu.Unlock() + + if _, ok := jl.jobs[id]; ok { + return nil, errors.Errorf("job ID %s exists", id) + } + + pr, ctx, progressCloser := progress.NewContext(context.Background()) + pw, _, _ := progress.FromContext(ctx) // TODO: expose progress.Pipe() + + j := &Job{ + list: jl, + pr: progress.NewMultiReader(pr), + pw: pw, + progressCloser: progressCloser, + } + jl.jobs[id] = j + + jl.updateCond.Broadcast() + + return j, nil +} + +func (jl *Solver) Get(id string) (*Job, error) { + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + go func() { + <-ctx.Done() + jl.updateCond.Broadcast() + }() + + jl.mu.RLock() + defer jl.mu.RUnlock() + for { + select { + case <-ctx.Done(): + return nil, errors.Errorf("no such job %s", id) + default: + } + j, ok := jl.jobs[id] + if !ok { + jl.updateCond.Wait() + continue + } + return j, nil + } +} + +// called with solver lock +func (jl *Solver) deleteIfUnreferenced(k digest.Digest, st *state) { + if len(st.jobs) == 0 && len(st.parents) == 0 { + for chKey := range st.childVtx { + chState := jl.actives[chKey] + delete(chState.parents, k) + jl.deleteIfUnreferenced(chKey, chState) + } + st.Release() + delete(jl.actives, k) + } +} + +func (j *Job) Build(ctx context.Context, e Edge) (CachedResult, error) { + v, err := j.list.load(e.Vertex, nil, j) + if err != nil { + return nil, err + } + e.Vertex = v + return j.list.s.build(ctx, e) +} + +func (j *Job) Discard() error { + defer j.progressCloser() + + j.list.mu.Lock() + defer j.list.mu.Unlock() + + j.pw.Close() + + for k, st := range j.list.actives { + if _, ok := st.jobs[j]; ok { + delete(st.jobs, j) + j.list.deleteIfUnreferenced(k, st) + } + if _, ok := st.allPw[j.pw]; ok { + delete(st.allPw, j.pw) + } + } + return nil +} + +func (j *Job) Call(ctx context.Context, name string, fn func(ctx context.Context) error) error { + ctx = progress.WithProgress(ctx, j.pw) + return inVertexContext(ctx, name, fn) +} + +type cacheMapResp struct { + *CacheMap + complete bool +} + +type activeOp interface { + CacheMap(context.Context, int) (*cacheMapResp, error) + LoadCache(ctx context.Context, rec *CacheRecord) (Result, error) + Exec(ctx context.Context, inputs []Result) (outputs []Result, exporters []ExportableCacheKey, err error) + IgnoreCache() bool + Cache() CacheManager + CalcSlowCache(context.Context, Index, ResultBasedCacheFunc, Result) (digest.Digest, error) +} + +func newSharedOp(resolver ResolveOpFunc, cacheManager CacheManager, st *state) *sharedOp { + so := &sharedOp{ + resolver: resolver, + st: st, + slowCacheRes: map[Index]digest.Digest{}, + slowCacheErr: map[Index]error{}, + } + return so +} + +type execRes struct { + execRes []*SharedResult + execExporters []ExportableCacheKey +} + +type sharedOp struct { + resolver ResolveOpFunc + st *state + g flightcontrol.Group + + opOnce sync.Once + op Op + subBuilder *subBuilder + err error + + execRes *execRes + execErr error + + cacheRes []*CacheMap + cacheDone bool + cacheErr error + + slowMu sync.Mutex + slowCacheRes map[Index]digest.Digest + slowCacheErr map[Index]error +} + +func (s *sharedOp) IgnoreCache() bool { + return s.st.vtx.Options().IgnoreCache +} + +func (s *sharedOp) Cache() CacheManager { + return s.st.combinedCacheManager() +} + +func (s *sharedOp) LoadCache(ctx context.Context, rec *CacheRecord) (Result, error) { + ctx = progress.WithProgress(ctx, s.st.mpw) + // no cache hit. start evaluating the node + span, ctx := tracing.StartSpan(ctx, "load cache: "+s.st.vtx.Name()) + notifyStarted(ctx, &s.st.clientVertex, true) + res, err := s.Cache().Load(ctx, rec) + tracing.FinishWithError(span, err) + notifyCompleted(ctx, &s.st.clientVertex, err, true) + return res, err +} + +func (s *sharedOp) CalcSlowCache(ctx context.Context, index Index, f ResultBasedCacheFunc, res Result) (digest.Digest, error) { + key, err := s.g.Do(ctx, fmt.Sprintf("slow-compute-%d", index), func(ctx context.Context) (interface{}, error) { + s.slowMu.Lock() + // TODO: add helpers for these stored values + if res := s.slowCacheRes[index]; res != "" { + s.slowMu.Unlock() + return res, nil + } + if err := s.slowCacheErr[index]; err != nil { + s.slowMu.Unlock() + return err, nil + } + s.slowMu.Unlock() + ctx = progress.WithProgress(ctx, s.st.mpw) + key, err := f(ctx, res) + complete := true + if err != nil { + canceled := false + select { + case <-ctx.Done(): + canceled = true + default: + } + if canceled && errors.Cause(err) == context.Canceled { + complete = false + } + } + s.slowMu.Lock() + defer s.slowMu.Unlock() + if complete { + if err == nil { + s.slowCacheRes[index] = key + } + s.slowCacheErr[index] = err + } + return key, err + }) + if err != nil { + return "", err + } + return key.(digest.Digest), nil +} + +func (s *sharedOp) CacheMap(ctx context.Context, index int) (*cacheMapResp, error) { + op, err := s.getOp() + if err != nil { + return nil, err + } + res, err := s.g.Do(ctx, "cachemap", func(ctx context.Context) (ret interface{}, retErr error) { + if s.cacheRes != nil && s.cacheDone || index < len(s.cacheRes) { + return s.cacheRes, nil + } + if s.cacheErr != nil { + return nil, s.cacheErr + } + ctx = progress.WithProgress(ctx, s.st.mpw) + ctx = session.NewContext(ctx, s.st.getSessionID()) + if len(s.st.vtx.Inputs()) == 0 { + // no cache hit. start evaluating the node + span, ctx := tracing.StartSpan(ctx, "cache request: "+s.st.vtx.Name()) + notifyStarted(ctx, &s.st.clientVertex, false) + defer func() { + tracing.FinishWithError(span, retErr) + notifyCompleted(ctx, &s.st.clientVertex, retErr, false) + }() + } + res, done, err := op.CacheMap(ctx, len(s.cacheRes)) + complete := true + if err != nil { + canceled := false + select { + case <-ctx.Done(): + canceled = true + default: + } + if canceled && errors.Cause(err) == context.Canceled { + complete = false + } + } + if complete { + if err == nil { + s.cacheRes = append(s.cacheRes, res) + s.cacheDone = done + } + s.cacheErr = err + } + return s.cacheRes, err + }) + if err != nil { + return nil, err + } + + if len(res.([]*CacheMap)) <= index { + return s.CacheMap(ctx, index) + } + + return &cacheMapResp{CacheMap: res.([]*CacheMap)[index], complete: s.cacheDone}, nil +} + +func (s *sharedOp) Exec(ctx context.Context, inputs []Result) (outputs []Result, exporters []ExportableCacheKey, err error) { + op, err := s.getOp() + if err != nil { + return nil, nil, err + } + res, err := s.g.Do(ctx, "exec", func(ctx context.Context) (ret interface{}, retErr error) { + if s.execRes != nil || s.execErr != nil { + return s.execRes, s.execErr + } + + ctx = progress.WithProgress(ctx, s.st.mpw) + ctx = session.NewContext(ctx, s.st.getSessionID()) + + // no cache hit. start evaluating the node + span, ctx := tracing.StartSpan(ctx, s.st.vtx.Name()) + notifyStarted(ctx, &s.st.clientVertex, false) + defer func() { + tracing.FinishWithError(span, retErr) + notifyCompleted(ctx, &s.st.clientVertex, retErr, false) + }() + + res, err := op.Exec(ctx, inputs) + complete := true + if err != nil { + canceled := false + select { + case <-ctx.Done(): + canceled = true + default: + } + if canceled && errors.Cause(err) == context.Canceled { + complete = false + } + } + if complete { + if res != nil { + var subExporters []ExportableCacheKey + s.subBuilder.mu.Lock() + if len(s.subBuilder.exporters) > 0 { + subExporters = append(subExporters, s.subBuilder.exporters...) + } + s.subBuilder.mu.Unlock() + + s.execRes = &execRes{execRes: wrapShared(res), execExporters: subExporters} + } + s.execErr = err + } + return s.execRes, err + }) + if err != nil { + return nil, nil, err + } + r := res.(*execRes) + return unwrapShared(r.execRes), r.execExporters, nil +} + +func (s *sharedOp) getOp() (Op, error) { + s.opOnce.Do(func() { + s.subBuilder = s.st.builder() + s.op, s.err = s.resolver(s.st.vtx, s.subBuilder) + }) + if s.err != nil { + return nil, s.err + } + return s.op, nil +} + +func (s *sharedOp) release() { + if s.execRes != nil { + for _, r := range s.execRes.execRes { + go r.Release(context.TODO()) + } + } +} + +func initClientVertex(v Vertex) client.Vertex { + inputDigests := make([]digest.Digest, 0, len(v.Inputs())) + for _, inp := range v.Inputs() { + inputDigests = append(inputDigests, inp.Vertex.Digest()) + } + return client.Vertex{ + Inputs: inputDigests, + Name: v.Name(), + Digest: v.Digest(), + } +} + +func wrapShared(inp []Result) []*SharedResult { + out := make([]*SharedResult, len(inp)) + for i, r := range inp { + out[i] = NewSharedResult(r) + } + return out +} + +func unwrapShared(inp []*SharedResult) []Result { + out := make([]Result, len(inp)) + for i, r := range inp { + out[i] = r.Clone() + } + return out +} + +type vertexWithCacheOptions struct { + Vertex + inputs []Edge + dgst digest.Digest +} + +func (v *vertexWithCacheOptions) Digest() digest.Digest { + return v.dgst +} + +func (v *vertexWithCacheOptions) Inputs() []Edge { + return v.inputs +} + +func notifyStarted(ctx context.Context, v *client.Vertex, cached bool) { + pw, _, _ := progress.FromContext(ctx) + defer pw.Close() + now := time.Now() + v.Started = &now + v.Completed = nil + v.Cached = cached + pw.Write(v.Digest.String(), *v) +} + +func notifyCompleted(ctx context.Context, v *client.Vertex, err error, cached bool) { + pw, _, _ := progress.FromContext(ctx) + defer pw.Close() + now := time.Now() + if v.Started == nil { + v.Started = &now + } + v.Completed = &now + v.Cached = cached + if err != nil { + v.Error = err.Error() + } + pw.Write(v.Digest.String(), *v) +} + +func inVertexContext(ctx context.Context, name string, f func(ctx context.Context) error) error { + v := client.Vertex{ + Digest: digest.FromBytes([]byte(identity.NewID())), + Name: name, + } + pw, _, ctx := progress.FromContext(ctx, progress.WithMetadata("vertex", v.Digest)) + notifyStarted(ctx, &v, false) + defer pw.Close() + err := f(ctx) + notifyCompleted(ctx, &v, err, false) + return err +} diff --git a/vendor/github.com/moby/buildkit/solver/llbsolver/bridge.go b/vendor/github.com/moby/buildkit/solver/llbsolver/bridge.go new file mode 100644 index 0000000000000..b568d3c8591af --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/llbsolver/bridge.go @@ -0,0 +1,191 @@ +package llbsolver + +import ( + "context" + "io" + "strings" + "sync" + + "github.com/docker/distribution/reference" + "github.com/moby/buildkit/cache" + "github.com/moby/buildkit/cache/remotecache" + "github.com/moby/buildkit/executor" + "github.com/moby/buildkit/frontend" + "github.com/moby/buildkit/solver" + "github.com/moby/buildkit/util/tracing" + "github.com/moby/buildkit/worker" + digest "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +type llbBridge struct { + builder solver.Builder + frontends map[string]frontend.Frontend + resolveWorker func() (worker.Worker, error) + resolveCacheImporter remotecache.ResolveCacheImporterFunc + cms map[string]solver.CacheManager + cmsMu sync.Mutex + platforms []specs.Platform +} + +func (b *llbBridge) Solve(ctx context.Context, req frontend.SolveRequest) (res *frontend.Result, err error) { + w, err := b.resolveWorker() + if err != nil { + return nil, err + } + var cms []solver.CacheManager + for _, ref := range req.ImportCacheRefs { + b.cmsMu.Lock() + var cm solver.CacheManager + if prevCm, ok := b.cms[ref]; !ok { + r, err := reference.ParseNormalizedNamed(ref) + if err != nil { + return nil, err + } + ref = reference.TagNameOnly(r).String() + func(ref string) { + cm = newLazyCacheManager(ref, func() (solver.CacheManager, error) { + var cmNew solver.CacheManager + if err := b.builder.Call(ctx, "importing cache manifest from "+ref, func(ctx context.Context) error { + if b.resolveCacheImporter == nil { + return errors.New("no cache importer is available") + } + typ := "" // TODO: support non-registry type + ci, desc, err := b.resolveCacheImporter(ctx, typ, ref) + if err != nil { + return err + } + cmNew, err = ci.Resolve(ctx, desc, ref, w) + return err + }); err != nil { + return nil, err + } + return cmNew, nil + }) + }(ref) + b.cms[ref] = cm + } else { + cm = prevCm + } + cms = append(cms, cm) + b.cmsMu.Unlock() + } + + if req.Definition != nil && req.Definition.Def != nil { + edge, err := Load(req.Definition, WithCacheSources(cms), RuntimePlatforms(b.platforms)) + if err != nil { + return nil, err + } + ref, err := b.builder.Build(ctx, edge) + if err != nil { + return nil, err + } + + res = &frontend.Result{Ref: ref} + } + if req.Frontend != "" { + f, ok := b.frontends[req.Frontend] + if !ok { + return nil, errors.Errorf("invalid frontend: %s", req.Frontend) + } + res, err = f.Solve(ctx, b, req.FrontendOpt) + if err != nil { + return nil, err + } + } else { + if req.Definition == nil || req.Definition.Def == nil { + return &frontend.Result{}, nil + } + } + + if err := res.EachRef(func(r solver.CachedResult) error { + wr, ok := r.Sys().(*worker.WorkerRef) + if !ok { + return errors.Errorf("invalid reference for exporting: %T", r.Sys()) + } + if wr.ImmutableRef != nil { + if err := wr.ImmutableRef.Finalize(ctx, false); err != nil { + return err + } + } + return nil + }); err != nil { + return nil, err + } + return +} + +func (s *llbBridge) Exec(ctx context.Context, meta executor.Meta, root cache.ImmutableRef, stdin io.ReadCloser, stdout, stderr io.WriteCloser) (err error) { + w, err := s.resolveWorker() + if err != nil { + return err + } + span, ctx := tracing.StartSpan(ctx, strings.Join(meta.Args, " ")) + err = w.Exec(ctx, meta, root, stdin, stdout, stderr) + tracing.FinishWithError(span, err) + return err +} + +func (s *llbBridge) ResolveImageConfig(ctx context.Context, ref string, platform *specs.Platform) (digest.Digest, []byte, error) { + w, err := s.resolveWorker() + if err != nil { + return "", nil, err + } + return w.ResolveImageConfig(ctx, ref, platform) +} + +type lazyCacheManager struct { + id string + main solver.CacheManager + + waitCh chan struct{} + err error +} + +func (lcm *lazyCacheManager) ID() string { + return lcm.id +} +func (lcm *lazyCacheManager) Query(inp []solver.CacheKeyWithSelector, inputIndex solver.Index, dgst digest.Digest, outputIndex solver.Index) ([]*solver.CacheKey, error) { + if err := lcm.wait(); err != nil { + return nil, err + } + return lcm.main.Query(inp, inputIndex, dgst, outputIndex) +} +func (lcm *lazyCacheManager) Records(ck *solver.CacheKey) ([]*solver.CacheRecord, error) { + if err := lcm.wait(); err != nil { + return nil, err + } + return lcm.main.Records(ck) +} +func (lcm *lazyCacheManager) Load(ctx context.Context, rec *solver.CacheRecord) (solver.Result, error) { + if err := lcm.wait(); err != nil { + return nil, err + } + return lcm.main.Load(ctx, rec) +} +func (lcm *lazyCacheManager) Save(key *solver.CacheKey, s solver.Result) (*solver.ExportableCacheKey, error) { + if err := lcm.wait(); err != nil { + return nil, err + } + return lcm.main.Save(key, s) +} + +func (lcm *lazyCacheManager) wait() error { + <-lcm.waitCh + return lcm.err +} + +func newLazyCacheManager(id string, fn func() (solver.CacheManager, error)) solver.CacheManager { + lcm := &lazyCacheManager{id: id, waitCh: make(chan struct{})} + go func() { + defer close(lcm.waitCh) + cm, err := fn() + if err != nil { + lcm.err = err + return + } + lcm.main = cm + }() + return lcm +} diff --git a/vendor/github.com/moby/buildkit/solver/llbsolver/ops/build.go b/vendor/github.com/moby/buildkit/solver/llbsolver/ops/build.go new file mode 100644 index 0000000000000..4b0300497b4fd --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/llbsolver/ops/build.go @@ -0,0 +1,132 @@ +package ops + +import ( + "context" + "encoding/json" + "os" + + "github.com/containerd/continuity/fs" + "github.com/moby/buildkit/client/llb" + "github.com/moby/buildkit/frontend" + "github.com/moby/buildkit/snapshot" + "github.com/moby/buildkit/solver" + "github.com/moby/buildkit/solver/pb" + "github.com/moby/buildkit/worker" + digest "github.com/opencontainers/go-digest" + "github.com/pkg/errors" +) + +const buildCacheType = "buildkit.build.v0" + +type buildOp struct { + op *pb.BuildOp + b frontend.FrontendLLBBridge + v solver.Vertex +} + +func NewBuildOp(v solver.Vertex, op *pb.Op_Build, b frontend.FrontendLLBBridge, _ worker.Worker) (solver.Op, error) { + return &buildOp{ + op: op.Build, + b: b, + v: v, + }, nil +} + +func (b *buildOp) CacheMap(ctx context.Context, index int) (*solver.CacheMap, bool, error) { + dt, err := json.Marshal(struct { + Type string + Exec *pb.BuildOp + }{ + Type: buildCacheType, + Exec: b.op, + }) + if err != nil { + return nil, false, err + } + + return &solver.CacheMap{ + Digest: digest.FromBytes(dt), + Deps: make([]struct { + Selector digest.Digest + ComputeDigestFunc solver.ResultBasedCacheFunc + }, len(b.v.Inputs())), + }, true, nil +} + +func (b *buildOp) Exec(ctx context.Context, inputs []solver.Result) (outputs []solver.Result, retErr error) { + if b.op.Builder != pb.LLBBuilder { + return nil, errors.Errorf("only LLB builder is currently allowed") + } + + builderInputs := b.op.Inputs + llbDef, ok := builderInputs[pb.LLBDefinitionInput] + if !ok { + return nil, errors.Errorf("no llb definition input %s found", pb.LLBDefinitionInput) + } + + i := int(llbDef.Input) + if i >= len(inputs) { + return nil, errors.Errorf("invalid index %v", i) // TODO: this should be validated before + } + inp := inputs[i] + + ref, ok := inp.Sys().(*worker.WorkerRef) + if !ok { + return nil, errors.Errorf("invalid reference for build %T", inp.Sys()) + } + + mount, err := ref.ImmutableRef.Mount(ctx, true) + if err != nil { + return nil, err + } + + lm := snapshot.LocalMounter(mount) + + root, err := lm.Mount() + if err != nil { + return nil, err + } + + defer func() { + if retErr != nil && lm != nil { + lm.Unmount() + } + }() + + fn := pb.LLBDefaultDefinitionFile + if override, ok := b.op.Attrs[pb.AttrLLBDefinitionFilename]; ok { + fn = override + } + + newfn, err := fs.RootPath(root, fn) + if err != nil { + return nil, errors.Wrapf(err, "working dir %s points to invalid target", fn) + } + + f, err := os.Open(newfn) + if err != nil { + return nil, errors.Wrapf(err, "failed to open %s", newfn) + } + + def, err := llb.ReadFrom(f) + if err != nil { + f.Close() + return nil, err + } + f.Close() + lm.Unmount() + lm = nil + + newRes, err := b.b.Solve(ctx, frontend.SolveRequest{ + Definition: def.ToPB(), + }) + if err != nil { + return nil, err + } + + for _, r := range newRes.Refs { + r.Release(context.TODO()) + } + + return []solver.Result{newRes.Ref}, err +} diff --git a/vendor/github.com/moby/buildkit/solver/llbsolver/ops/exec.go b/vendor/github.com/moby/buildkit/solver/llbsolver/ops/exec.go new file mode 100644 index 0000000000000..b9252275a9439 --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/llbsolver/ops/exec.go @@ -0,0 +1,535 @@ +package ops + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "os" + "path" + "runtime" + "sort" + "strings" + "sync" + "time" + + "github.com/boltdb/bolt" + "github.com/containerd/containerd/mount" + "github.com/docker/docker/pkg/locker" + "github.com/moby/buildkit/cache" + "github.com/moby/buildkit/cache/metadata" + "github.com/moby/buildkit/executor" + "github.com/moby/buildkit/snapshot" + "github.com/moby/buildkit/solver" + "github.com/moby/buildkit/solver/llbsolver" + "github.com/moby/buildkit/solver/pb" + "github.com/moby/buildkit/util/progress/logs" + "github.com/moby/buildkit/worker" + digest "github.com/opencontainers/go-digest" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +const execCacheType = "buildkit.exec.v0" + +type execOp struct { + op *pb.ExecOp + cm cache.Manager + md *metadata.Store + exec executor.Executor + w worker.Worker + numInputs int + + cacheMounts map[string]*cacheRefShare +} + +func NewExecOp(v solver.Vertex, op *pb.Op_Exec, cm cache.Manager, md *metadata.Store, exec executor.Executor, w worker.Worker) (solver.Op, error) { + return &execOp{ + op: op.Exec, + cm: cm, + md: md, + exec: exec, + numInputs: len(v.Inputs()), + w: w, + cacheMounts: map[string]*cacheRefShare{}, + }, nil +} + +func cloneExecOp(old *pb.ExecOp) pb.ExecOp { + n := *old + meta := *n.Meta + n.Meta = &meta + n.Mounts = nil + for i := range n.Mounts { + m := *n.Mounts[i] + n.Mounts = append(n.Mounts, &m) + } + return n +} + +func (e *execOp) CacheMap(ctx context.Context, index int) (*solver.CacheMap, bool, error) { + op := cloneExecOp(e.op) + for i := range op.Mounts { + op.Mounts[i].Selector = "" + } + op.Meta.ProxyEnv = nil + + dt, err := json.Marshal(struct { + Type string + Exec *pb.ExecOp + OS string + Arch string + }{ + Type: execCacheType, + Exec: &op, + OS: runtime.GOOS, + Arch: runtime.GOARCH, + }) + if err != nil { + return nil, false, err + } + + cm := &solver.CacheMap{ + Digest: digest.FromBytes(dt), + Deps: make([]struct { + Selector digest.Digest + ComputeDigestFunc solver.ResultBasedCacheFunc + }, e.numInputs), + } + + deps, err := e.getMountDeps() + if err != nil { + return nil, false, err + } + + for i, dep := range deps { + if len(dep.Selectors) != 0 { + dgsts := make([][]byte, 0, len(dep.Selectors)) + for _, p := range dep.Selectors { + dgsts = append(dgsts, []byte(p)) + } + cm.Deps[i].Selector = digest.FromBytes(bytes.Join(dgsts, []byte{0})) + } + if !dep.NoContentBasedHash { + cm.Deps[i].ComputeDigestFunc = llbsolver.NewContentHashFunc(dedupePaths(dep.Selectors)) + } + } + + return cm, true, nil +} + +func dedupePaths(inp []string) []string { + old := make(map[string]struct{}, len(inp)) + for _, p := range inp { + old[p] = struct{}{} + } + paths := make([]string, 0, len(old)) + for p1 := range old { + var skip bool + for p2 := range old { + if p1 != p2 && strings.HasPrefix(p1, p2) { + skip = true + break + } + } + if !skip { + paths = append(paths, p1) + } + } + sort.Slice(paths, func(i, j int) bool { + return paths[i] < paths[j] + }) + return paths +} + +type dep struct { + Selectors []string + NoContentBasedHash bool +} + +func (e *execOp) getMountDeps() ([]dep, error) { + deps := make([]dep, e.numInputs) + for _, m := range e.op.Mounts { + if m.Input == pb.Empty { + continue + } + if int(m.Input) >= len(deps) { + return nil, errors.Errorf("invalid mountinput %v", m) + } + + sel := m.Selector + if sel != "" { + sel = path.Join("/", sel) + deps[m.Input].Selectors = append(deps[m.Input].Selectors, sel) + } + + if !m.Readonly || m.Dest == pb.RootMount { // exclude read-only rootfs + deps[m.Input].NoContentBasedHash = true + } + } + return deps, nil +} + +func (e *execOp) getRefCacheDir(ctx context.Context, ref cache.ImmutableRef, id string, m *pb.Mount, sharing pb.CacheSharingOpt) (mref cache.MutableRef, err error) { + + key := "cache-dir:" + id + if ref != nil { + key += ":" + ref.ID() + } + + if ref, ok := e.cacheMounts[key]; ok { + return ref.clone(), nil + } + defer func() { + if err == nil { + share := &cacheRefShare{MutableRef: mref, refs: map[*cacheRef]struct{}{}} + e.cacheMounts[key] = share + mref = share.clone() + } + }() + + switch sharing { + case pb.CacheSharingOpt_SHARED: + return sharedCacheRefs.get(key, func() (cache.MutableRef, error) { + return e.getRefCacheDirNoCache(ctx, key, ref, id, m, false) + }) + case pb.CacheSharingOpt_PRIVATE: + return e.getRefCacheDirNoCache(ctx, key, ref, id, m, false) + case pb.CacheSharingOpt_LOCKED: + return e.getRefCacheDirNoCache(ctx, key, ref, id, m, true) + default: + return nil, errors.Errorf("invalid cache sharing option: %s", sharing.String()) + } + +} + +func (e *execOp) getRefCacheDirNoCache(ctx context.Context, key string, ref cache.ImmutableRef, id string, m *pb.Mount, block bool) (cache.MutableRef, error) { + makeMutable := func(cache.ImmutableRef) (cache.MutableRef, error) { + desc := fmt.Sprintf("cached mount %s from exec %s", m.Dest, strings.Join(e.op.Meta.Args, " ")) + return e.cm.New(ctx, ref, cache.WithDescription(desc), cache.CachePolicyRetain) + } + + cacheRefsLocker.Lock(key) + defer cacheRefsLocker.Unlock(key) + for { + sis, err := e.md.Search(key) + if err != nil { + return nil, err + } + locked := false + for _, si := range sis { + if mRef, err := e.cm.GetMutable(ctx, si.ID()); err == nil { + logrus.Debugf("reusing ref for cache dir: %s", mRef.ID()) + return mRef, nil + } else if errors.Cause(err) == cache.ErrLocked { + locked = true + } + } + if block && locked { + cacheRefsLocker.Unlock(key) + select { + case <-ctx.Done(): + cacheRefsLocker.Lock(key) + return nil, ctx.Err() + case <-time.After(100 * time.Millisecond): + cacheRefsLocker.Lock(key) + } + } else { + break + } + } + mRef, err := makeMutable(ref) + if err != nil { + return nil, err + } + + si, _ := e.md.Get(mRef.ID()) + v, err := metadata.NewValue(key) + if err != nil { + mRef.Release(context.TODO()) + return nil, err + } + v.Index = key + if err := si.Update(func(b *bolt.Bucket) error { + return si.SetValue(b, key, v) + }); err != nil { + mRef.Release(context.TODO()) + return nil, err + } + return mRef, nil +} + +func (e *execOp) Exec(ctx context.Context, inputs []solver.Result) ([]solver.Result, error) { + var mounts []executor.Mount + var root cache.Mountable + var readonlyRootFS bool + + var outputs []cache.Ref + + defer func() { + for _, o := range outputs { + if o != nil { + go o.Release(context.TODO()) + } + } + }() + + // loop over all mounts, fill in mounts, root and outputs + for _, m := range e.op.Mounts { + var mountable cache.Mountable + var ref cache.ImmutableRef + + if m.Dest == pb.RootMount && m.MountType != pb.MountType_BIND { + return nil, errors.Errorf("invalid mount type %s for %s", m.MountType.String(), m.Dest) + } + + // if mount is based on input validate and load it + if m.Input != pb.Empty { + if int(m.Input) > len(inputs) { + return nil, errors.Errorf("missing input %d", m.Input) + } + inp := inputs[int(m.Input)] + workerRef, ok := inp.Sys().(*worker.WorkerRef) + if !ok { + return nil, errors.Errorf("invalid reference for exec %T", inp.Sys()) + } + ref = workerRef.ImmutableRef + mountable = ref + } + + makeMutable := func(cache.ImmutableRef) (cache.MutableRef, error) { + desc := fmt.Sprintf("mount %s from exec %s", m.Dest, strings.Join(e.op.Meta.Args, " ")) + return e.cm.New(ctx, ref, cache.WithDescription(desc)) + } + + switch m.MountType { + case pb.MountType_BIND: + // if mount creates an output + if m.Output != pb.SkipOutput { + // it it is readonly and not root then output is the input + if m.Readonly && ref != nil && m.Dest != pb.RootMount { + outputs = append(outputs, ref.Clone()) + } else { + // otherwise output and mount is the mutable child + active, err := makeMutable(ref) + if err != nil { + return nil, err + } + outputs = append(outputs, active) + mountable = active + } + } else if ref == nil { + // this case is empty readonly scratch without output that is not really useful for anything but don't error + active, err := makeMutable(ref) + if err != nil { + return nil, err + } + defer active.Release(context.TODO()) + mountable = active + } + + case pb.MountType_CACHE: + if m.CacheOpt == nil { + return nil, errors.Errorf("missing cache mount options") + } + mRef, err := e.getRefCacheDir(ctx, ref, m.CacheOpt.ID, m, m.CacheOpt.Sharing) + if err != nil { + return nil, err + } + mountable = mRef + defer func() { + go mRef.Release(context.TODO()) + }() + if m.Output != pb.SkipOutput && ref != nil { + outputs = append(outputs, ref.Clone()) + } + + case pb.MountType_TMPFS: + mountable = newTmpfs() + + default: + return nil, errors.Errorf("mount type %s not implemented", m.MountType) + } + + // validate that there is a mount + if mountable == nil { + return nil, errors.Errorf("mount %s has no input", m.Dest) + } + + // if dest is root we need mutable ref even if there is no output + if m.Dest == pb.RootMount { + root = mountable + readonlyRootFS = m.Readonly + if m.Output == pb.SkipOutput && readonlyRootFS { + active, err := makeMutable(ref) + if err != nil { + return nil, err + } + defer func() { + go active.Release(context.TODO()) + }() + root = active + } + } else { + mounts = append(mounts, executor.Mount{Src: mountable, Dest: m.Dest, Readonly: m.Readonly, Selector: m.Selector}) + } + } + + // sort mounts so parents are mounted first + sort.Slice(mounts, func(i, j int) bool { + return mounts[i].Dest < mounts[j].Dest + }) + + meta := executor.Meta{ + Args: e.op.Meta.Args, + Env: e.op.Meta.Env, + Cwd: e.op.Meta.Cwd, + User: e.op.Meta.User, + ReadonlyRootFS: readonlyRootFS, + } + + if e.op.Meta.ProxyEnv != nil { + meta.Env = append(meta.Env, proxyEnvList(e.op.Meta.ProxyEnv)...) + } + + stdout, stderr := logs.NewLogStreams(ctx, os.Getenv("BUILDKIT_DEBUG_EXEC_OUTPUT") == "1") + defer stdout.Close() + defer stderr.Close() + + if err := e.exec.Exec(ctx, meta, root, mounts, nil, stdout, stderr); err != nil { + return nil, errors.Wrapf(err, "executor failed running %v", meta.Args) + } + + refs := []solver.Result{} + for i, out := range outputs { + if mutable, ok := out.(cache.MutableRef); ok { + ref, err := mutable.Commit(ctx) + if err != nil { + return nil, errors.Wrapf(err, "error committing %s", mutable.ID()) + } + refs = append(refs, worker.NewWorkerRefResult(ref, e.w)) + } else { + refs = append(refs, worker.NewWorkerRefResult(out.(cache.ImmutableRef), e.w)) + } + outputs[i] = nil + } + return refs, nil +} + +func proxyEnvList(p *pb.ProxyEnv) []string { + out := []string{} + if v := p.HttpProxy; v != "" { + out = append(out, "HTTP_PROXY="+v, "http_proxy="+v) + } + if v := p.HttpsProxy; v != "" { + out = append(out, "HTTPS_PROXY="+v, "https_proxy="+v) + } + if v := p.FtpProxy; v != "" { + out = append(out, "FTP_PROXY="+v, "ftp_proxy="+v) + } + if v := p.NoProxy; v != "" { + out = append(out, "NO_PROXY="+v, "no_proxy="+v) + } + return out +} + +func newTmpfs() cache.Mountable { + return &tmpfs{} +} + +type tmpfs struct { +} + +func (f *tmpfs) Mount(ctx context.Context, readonly bool) (snapshot.Mountable, error) { + return &tmpfsMount{readonly: readonly}, nil +} + +type tmpfsMount struct { + readonly bool +} + +func (m *tmpfsMount) Mount() ([]mount.Mount, error) { + opt := []string{"nosuid"} + if m.readonly { + opt = append(opt, "ro") + } + return []mount.Mount{{ + Type: "tmpfs", + Source: "tmpfs", + Options: opt, + }}, nil +} +func (m *tmpfsMount) Release() error { + return nil +} + +var cacheRefsLocker = locker.New() +var sharedCacheRefs = &cacheRefs{} + +type cacheRefs struct { + mu sync.Mutex + shares map[string]*cacheRefShare +} + +func (r *cacheRefs) get(key string, fn func() (cache.MutableRef, error)) (cache.MutableRef, error) { + r.mu.Lock() + defer r.mu.Unlock() + + if r.shares == nil { + r.shares = map[string]*cacheRefShare{} + } + + share, ok := r.shares[key] + if ok { + return share.clone(), nil + } + + mref, err := fn() + if err != nil { + return nil, err + } + + share = &cacheRefShare{MutableRef: mref, main: r, key: key, refs: map[*cacheRef]struct{}{}} + r.shares[key] = share + + return share.clone(), nil +} + +type cacheRefShare struct { + cache.MutableRef + mu sync.Mutex + refs map[*cacheRef]struct{} + main *cacheRefs + key string +} + +func (r *cacheRefShare) clone() cache.MutableRef { + cacheRef := &cacheRef{cacheRefShare: r} + r.mu.Lock() + r.refs[cacheRef] = struct{}{} + r.mu.Unlock() + return cacheRef +} + +func (r *cacheRefShare) release(ctx context.Context) error { + if r.main != nil { + r.main.mu.Lock() + defer r.main.mu.Unlock() + delete(r.main.shares, r.key) + } + return r.MutableRef.Release(ctx) +} + +type cacheRef struct { + *cacheRefShare +} + +func (r *cacheRef) Release(ctx context.Context) error { + r.mu.Lock() + defer r.mu.Unlock() + delete(r.refs, r) + if len(r.refs) == 0 { + return r.release(ctx) + } + return nil +} diff --git a/vendor/github.com/moby/buildkit/solver/llbsolver/ops/source.go b/vendor/github.com/moby/buildkit/solver/llbsolver/ops/source.go new file mode 100644 index 0000000000000..722861eeb4f07 --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/llbsolver/ops/source.go @@ -0,0 +1,78 @@ +package ops + +import ( + "context" + "sync" + + "github.com/moby/buildkit/solver" + "github.com/moby/buildkit/solver/pb" + "github.com/moby/buildkit/source" + "github.com/moby/buildkit/worker" + digest "github.com/opencontainers/go-digest" +) + +const sourceCacheType = "buildkit.source.v0" + +type sourceOp struct { + mu sync.Mutex + op *pb.Op_Source + platform *pb.Platform + sm *source.Manager + src source.SourceInstance + w worker.Worker +} + +func NewSourceOp(_ solver.Vertex, op *pb.Op_Source, platform *pb.Platform, sm *source.Manager, w worker.Worker) (solver.Op, error) { + return &sourceOp{ + op: op, + sm: sm, + w: w, + platform: platform, + }, nil +} + +func (s *sourceOp) instance(ctx context.Context) (source.SourceInstance, error) { + s.mu.Lock() + defer s.mu.Unlock() + if s.src != nil { + return s.src, nil + } + id, err := source.FromLLB(s.op, s.platform) + if err != nil { + return nil, err + } + src, err := s.sm.Resolve(ctx, id) + if err != nil { + return nil, err + } + s.src = src + return s.src, nil +} + +func (s *sourceOp) CacheMap(ctx context.Context, index int) (*solver.CacheMap, bool, error) { + src, err := s.instance(ctx) + if err != nil { + return nil, false, err + } + k, done, err := src.CacheKey(ctx, index) + if err != nil { + return nil, false, err + } + + return &solver.CacheMap{ + // TODO: add os/arch + Digest: digest.FromBytes([]byte(sourceCacheType + ":" + k)), + }, done, nil +} + +func (s *sourceOp) Exec(ctx context.Context, _ []solver.Result) (outputs []solver.Result, err error) { + src, err := s.instance(ctx) + if err != nil { + return nil, err + } + ref, err := src.Snapshot(ctx) + if err != nil { + return nil, err + } + return []solver.Result{worker.NewWorkerRefResult(ref, s.w)}, nil +} diff --git a/vendor/github.com/moby/buildkit/solver/llbsolver/result.go b/vendor/github.com/moby/buildkit/solver/llbsolver/result.go new file mode 100644 index 0000000000000..7049b86855f66 --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/llbsolver/result.go @@ -0,0 +1,152 @@ +package llbsolver + +import ( + "bytes" + "context" + "path" + "strings" + "time" + + "github.com/moby/buildkit/cache" + "github.com/moby/buildkit/cache/contenthash" + "github.com/moby/buildkit/solver" + "github.com/moby/buildkit/worker" + digest "github.com/opencontainers/go-digest" + "github.com/pkg/errors" + "golang.org/x/sync/errgroup" +) + +func NewContentHashFunc(selectors []string) solver.ResultBasedCacheFunc { + return func(ctx context.Context, res solver.Result) (digest.Digest, error) { + ref, ok := res.Sys().(*worker.WorkerRef) + if !ok { + return "", errors.Errorf("invalid reference: %T", res) + } + + if len(selectors) == 0 { + selectors = []string{""} + } + + dgsts := make([][]byte, len(selectors)) + + eg, ctx := errgroup.WithContext(ctx) + + for i, sel := range selectors { + // FIXME(tonistiigi): enabling this parallelization seems to create wrong results for some big inputs(like gobuild) + // func(i int) { + // eg.Go(func() error { + dgst, err := contenthash.Checksum(ctx, ref.ImmutableRef, path.Join("/", sel)) + if err != nil { + return "", err + } + dgsts[i] = []byte(dgst) + // return nil + // }) + // }(i) + } + + if err := eg.Wait(); err != nil { + return "", err + } + + return digest.FromBytes(bytes.Join(dgsts, []byte{0})), nil + } +} + +func newCacheResultStorage(wc *worker.Controller) solver.CacheResultStorage { + return &cacheResultStorage{ + wc: wc, + } +} + +type cacheResultStorage struct { + wc *worker.Controller +} + +func (s *cacheResultStorage) Save(res solver.Result) (solver.CacheResult, error) { + ref, ok := res.Sys().(*worker.WorkerRef) + if !ok { + return solver.CacheResult{}, errors.Errorf("invalid result: %T", res.Sys()) + } + if ref.ImmutableRef != nil { + if !cache.HasCachePolicyRetain(ref.ImmutableRef) { + if err := cache.CachePolicyRetain(ref.ImmutableRef); err != nil { + return solver.CacheResult{}, err + } + ref.ImmutableRef.Metadata().Commit() + } + } + return solver.CacheResult{ID: ref.ID(), CreatedAt: time.Now()}, nil +} +func (s *cacheResultStorage) Load(ctx context.Context, res solver.CacheResult) (solver.Result, error) { + return s.load(res.ID) +} + +func (s *cacheResultStorage) getWorkerRef(id string) (worker.Worker, string, error) { + workerID, refID, err := parseWorkerRef(id) + if err != nil { + return nil, "", err + } + w, err := s.wc.Get(workerID) + if err != nil { + return nil, "", err + } + return w, refID, nil +} + +func (s *cacheResultStorage) load(id string) (solver.Result, error) { + w, refID, err := s.getWorkerRef(id) + if err != nil { + return nil, err + } + if refID == "" { + return worker.NewWorkerRefResult(nil, w), nil + } + ref, err := w.LoadRef(refID) + if err != nil { + return nil, err + } + return worker.NewWorkerRefResult(ref, w), nil +} + +func (s *cacheResultStorage) LoadRemote(ctx context.Context, res solver.CacheResult) (*solver.Remote, error) { + w, refID, err := s.getWorkerRef(res.ID) + if err != nil { + return nil, err + } + ref, err := w.LoadRef(refID) + if err != nil { + return nil, err + } + defer ref.Release(context.TODO()) + remote, err := w.GetRemote(ctx, ref, false) + if err != nil { + return nil, nil // ignore error. loadRemote is best effort + } + return remote, nil +} +func (s *cacheResultStorage) Exists(id string) bool { + ref, err := s.load(id) + if err != nil { + return false + } + ref.Release(context.TODO()) + return true +} + +func parseWorkerRef(id string) (string, string, error) { + parts := strings.Split(id, "::") + if len(parts) != 2 { + return "", "", errors.Errorf("invalid workerref id: %s", id) + } + return parts[0], parts[1], nil +} + +func workerRefConverter(ctx context.Context, res solver.Result) (*solver.Remote, error) { + ref, ok := res.Sys().(*worker.WorkerRef) + if !ok { + return nil, errors.Errorf("invalid result: %T", res.Sys()) + } + + return ref.Worker.GetRemote(ctx, ref.ImmutableRef, true) +} diff --git a/vendor/github.com/moby/buildkit/solver/llbsolver/solver.go b/vendor/github.com/moby/buildkit/solver/llbsolver/solver.go new file mode 100644 index 0000000000000..2e27f5332f07d --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/llbsolver/solver.go @@ -0,0 +1,177 @@ +package llbsolver + +import ( + "context" + "time" + + "github.com/moby/buildkit/cache" + "github.com/moby/buildkit/cache/remotecache" + "github.com/moby/buildkit/client" + "github.com/moby/buildkit/exporter" + "github.com/moby/buildkit/frontend" + "github.com/moby/buildkit/session" + "github.com/moby/buildkit/solver" + "github.com/moby/buildkit/util/progress" + "github.com/moby/buildkit/worker" + specs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +type ExporterRequest struct { + Exporter exporter.ExporterInstance + CacheExporter remotecache.Exporter + CacheExportMode solver.CacheExportMode +} + +// ResolveWorkerFunc returns default worker for the temporary default non-distributed use cases +type ResolveWorkerFunc func() (worker.Worker, error) + +type Solver struct { + solver *solver.Solver + resolveWorker ResolveWorkerFunc + frontends map[string]frontend.Frontend + resolveCacheImporter remotecache.ResolveCacheImporterFunc + platforms []specs.Platform +} + +func New(wc *worker.Controller, f map[string]frontend.Frontend, cacheStore solver.CacheKeyStorage, resolveCI remotecache.ResolveCacheImporterFunc) (*Solver, error) { + s := &Solver{ + resolveWorker: defaultResolver(wc), + frontends: f, + resolveCacheImporter: resolveCI, + } + + results := newCacheResultStorage(wc) + + cache := solver.NewCacheManager("local", cacheStore, results) + + // executing is currently only allowed on default worker + w, err := wc.GetDefault() + if err != nil { + return nil, err + } + s.platforms = w.Platforms() + + s.solver = solver.NewSolver(solver.SolverOpt{ + ResolveOpFunc: s.resolver(), + DefaultCache: cache, + }) + return s, nil +} + +func (s *Solver) resolver() solver.ResolveOpFunc { + return func(v solver.Vertex, b solver.Builder) (solver.Op, error) { + w, err := s.resolveWorker() + if err != nil { + return nil, err + } + return w.ResolveOp(v, s.Bridge(b)) + } +} + +func (s *Solver) Bridge(b solver.Builder) frontend.FrontendLLBBridge { + return &llbBridge{ + builder: b, + frontends: s.frontends, + resolveWorker: s.resolveWorker, + resolveCacheImporter: s.resolveCacheImporter, + cms: map[string]solver.CacheManager{}, + platforms: s.platforms, + } +} + +func (s *Solver) Solve(ctx context.Context, id string, req frontend.SolveRequest, exp ExporterRequest) (*client.SolveResponse, error) { + j, err := s.solver.NewJob(id) + if err != nil { + return nil, err + } + + defer j.Discard() + + j.SessionID = session.FromContext(ctx) + + res, err := s.Bridge(j).Solve(ctx, req) + if err != nil { + return nil, err + } + + defer func() { + res.EachRef(func(ref solver.CachedResult) error { + go ref.Release(context.TODO()) + return nil + }) + }() + + var exporterResponse map[string]string + if exp := exp.Exporter; exp != nil { + var immutable cache.ImmutableRef + if res := res.Ref; res != nil { // FIXME(tonistiigi): + workerRef, ok := res.Sys().(*worker.WorkerRef) + if !ok { + return nil, errors.Errorf("invalid reference: %T", res.Sys()) + } + immutable = workerRef.ImmutableRef + } + + if err := j.Call(ctx, exp.Name(), func(ctx context.Context) error { + exporterResponse, err = exp.Export(ctx, immutable, res.Metadata) + return err + }); err != nil { + return nil, err + } + } + + if e := exp.CacheExporter; e != nil { + if err := j.Call(ctx, "exporting cache", func(ctx context.Context) error { + prepareDone := oneOffProgress(ctx, "preparing build cache for export") + if err := res.EachRef(func(res solver.CachedResult) error { + _, err := res.CacheKey().Exporter.ExportTo(ctx, e, solver.CacheExportOpt{ + Convert: workerRefConverter, + Mode: exp.CacheExportMode, + }) + return err + }); err != nil { + return prepareDone(err) + } + prepareDone(nil) + return e.Finalize(ctx) + }); err != nil { + return nil, err + } + } + + return &client.SolveResponse{ + ExporterResponse: exporterResponse, + }, nil +} + +func (s *Solver) Status(ctx context.Context, id string, statusChan chan *client.SolveStatus) error { + j, err := s.solver.Get(id) + if err != nil { + return err + } + return j.Status(ctx, statusChan) +} + +func defaultResolver(wc *worker.Controller) ResolveWorkerFunc { + return func() (worker.Worker, error) { + return wc.GetDefault() + } +} + +func oneOffProgress(ctx context.Context, id string) func(err error) error { + pw, _, _ := progress.FromContext(ctx) + now := time.Now() + st := progress.Status{ + Started: &now, + } + pw.Write(id, st) + return func(err error) error { + // TODO: set error on status + now := time.Now() + st.Completed = &now + pw.Write(id, st) + pw.Close() + return err + } +} diff --git a/vendor/github.com/moby/buildkit/solver/llbsolver/vertex.go b/vendor/github.com/moby/buildkit/solver/llbsolver/vertex.go new file mode 100644 index 0000000000000..bd04e1ed55f52 --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/llbsolver/vertex.go @@ -0,0 +1,185 @@ +package llbsolver + +import ( + "strings" + + "github.com/containerd/containerd/platforms" + "github.com/moby/buildkit/solver" + "github.com/moby/buildkit/solver/pb" + "github.com/moby/buildkit/source" + digest "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +type vertex struct { + sys interface{} + options solver.VertexOptions + inputs []solver.Edge + digest digest.Digest + name string +} + +func (v *vertex) Digest() digest.Digest { + return v.digest +} + +func (v *vertex) Sys() interface{} { + return v.sys +} + +func (v *vertex) Options() solver.VertexOptions { + return v.options +} + +func (v *vertex) Inputs() []solver.Edge { + return v.inputs +} + +func (v *vertex) Name() string { + return v.name +} + +type LoadOpt func(*pb.Op, *pb.OpMetadata, *solver.VertexOptions) error + +func WithCacheSources(cms []solver.CacheManager) LoadOpt { + return func(_ *pb.Op, _ *pb.OpMetadata, opt *solver.VertexOptions) error { + opt.CacheSources = cms + return nil + } +} + +func RuntimePlatforms(p []specs.Platform) LoadOpt { + var defaultPlatform *pb.Platform + pp := make([]specs.Platform, len(p)) + for i := range p { + pp[i] = platforms.Normalize(p[i]) + } + return func(op *pb.Op, _ *pb.OpMetadata, opt *solver.VertexOptions) error { + if op.Platform == nil { + if defaultPlatform == nil { + p := platforms.DefaultSpec() + defaultPlatform = &pb.Platform{ + OS: p.OS, + Architecture: p.Architecture, + } + } + op.Platform = defaultPlatform + } + if _, ok := op.Op.(*pb.Op_Exec); ok { + var found bool + for _, pp := range pp { + if pp.OS == op.Platform.OS && pp.Architecture == op.Platform.Architecture && pp.Variant == op.Platform.Variant { + found = true + break + } + } + if !found { + return errors.Errorf("runtime execution on platform %s not supported", platforms.Format(specs.Platform{OS: op.Platform.OS, Architecture: op.Platform.Architecture, Variant: op.Platform.Variant})) + } + } + return nil + } +} + +func Load(def *pb.Definition, opts ...LoadOpt) (solver.Edge, error) { + return loadLLB(def, func(dgst digest.Digest, pbOp *pb.Op, load func(digest.Digest) (solver.Vertex, error)) (solver.Vertex, error) { + opMetadata := def.Metadata[dgst] + vtx, err := newVertex(dgst, pbOp, &opMetadata, load, opts...) + if err != nil { + return nil, err + } + return vtx, nil + }) +} + +func newVertex(dgst digest.Digest, op *pb.Op, opMeta *pb.OpMetadata, load func(digest.Digest) (solver.Vertex, error), opts ...LoadOpt) (*vertex, error) { + opt := solver.VertexOptions{} + if opMeta != nil { + opt.IgnoreCache = opMeta.IgnoreCache + opt.Description = opMeta.Description + if opMeta.ExportCache != nil { + opt.ExportCache = &opMeta.ExportCache.Value + } + } + for _, fn := range opts { + if err := fn(op, opMeta, &opt); err != nil { + return nil, err + } + } + vtx := &vertex{sys: op, options: opt, digest: dgst, name: llbOpName(op)} + for _, in := range op.Inputs { + sub, err := load(in.Digest) + if err != nil { + return nil, err + } + vtx.inputs = append(vtx.inputs, solver.Edge{Index: solver.Index(in.Index), Vertex: sub}) + } + return vtx, nil +} + +// loadLLB loads LLB. +// fn is executed sequentially. +func loadLLB(def *pb.Definition, fn func(digest.Digest, *pb.Op, func(digest.Digest) (solver.Vertex, error)) (solver.Vertex, error)) (solver.Edge, error) { + if len(def.Def) == 0 { + return solver.Edge{}, errors.New("invalid empty definition") + } + + allOps := make(map[digest.Digest]*pb.Op) + + var dgst digest.Digest + + for _, dt := range def.Def { + var op pb.Op + if err := (&op).Unmarshal(dt); err != nil { + return solver.Edge{}, errors.Wrap(err, "failed to parse llb proto op") + } + dgst = digest.FromBytes(dt) + allOps[dgst] = &op + } + + lastOp := allOps[dgst] + delete(allOps, dgst) + dgst = lastOp.Inputs[0].Digest + + cache := make(map[digest.Digest]solver.Vertex) + + var rec func(dgst digest.Digest) (solver.Vertex, error) + rec = func(dgst digest.Digest) (solver.Vertex, error) { + if v, ok := cache[dgst]; ok { + return v, nil + } + v, err := fn(dgst, allOps[dgst], rec) + if err != nil { + return nil, err + } + cache[dgst] = v + return v, nil + } + + v, err := rec(dgst) + if err != nil { + return solver.Edge{}, err + } + return solver.Edge{Vertex: v, Index: solver.Index(lastOp.Inputs[0].Index)}, nil +} + +func llbOpName(op *pb.Op) string { + switch op := op.Op.(type) { + case *pb.Op_Source: + if id, err := source.FromLLB(op, nil); err == nil { + if id, ok := id.(*source.LocalIdentifier); ok { + if len(id.IncludePatterns) == 1 { + return op.Source.Identifier + " (" + id.IncludePatterns[0] + ")" + } + } + } + return op.Source.Identifier + case *pb.Op_Exec: + return strings.Join(op.Exec.Meta.Args, " ") + case *pb.Op_Build: + return "build" + default: + return "unknown" + } +} diff --git a/vendor/github.com/moby/buildkit/solver/memorycachestorage.go b/vendor/github.com/moby/buildkit/solver/memorycachestorage.go new file mode 100644 index 0000000000000..75c8abdeadce2 --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/memorycachestorage.go @@ -0,0 +1,307 @@ +package solver + +import ( + "context" + "sync" + "time" + + "github.com/pkg/errors" +) + +func NewInMemoryCacheStorage() CacheKeyStorage { + return &inMemoryStore{ + byID: map[string]*inMemoryKey{}, + byResult: map[string]map[string]struct{}{}, + } +} + +type inMemoryStore struct { + mu sync.RWMutex + byID map[string]*inMemoryKey + byResult map[string]map[string]struct{} +} + +type inMemoryKey struct { + id string + results map[string]CacheResult + links map[CacheInfoLink]map[string]struct{} + backlinks map[string]struct{} +} + +func (s *inMemoryStore) Exists(id string) bool { + s.mu.RLock() + defer s.mu.RUnlock() + if k, ok := s.byID[id]; ok { + return len(k.links) > 0 || len(k.results) > 0 + } + return false +} + +func newInMemoryKey(id string) *inMemoryKey { + return &inMemoryKey{ + results: map[string]CacheResult{}, + links: map[CacheInfoLink]map[string]struct{}{}, + backlinks: map[string]struct{}{}, + id: id, + } +} + +func (s *inMemoryStore) Walk(fn func(string) error) error { + s.mu.RLock() + ids := make([]string, 0, len(s.byID)) + for id := range s.byID { + ids = append(ids, id) + } + s.mu.RUnlock() + + for _, id := range ids { + if err := fn(id); err != nil { + return err + } + } + return nil +} + +func (s *inMemoryStore) WalkResults(id string, fn func(CacheResult) error) error { + s.mu.RLock() + + k, ok := s.byID[id] + if !ok { + s.mu.RUnlock() + return nil + } + copy := make([]CacheResult, 0, len(k.results)) + for _, res := range k.results { + copy = append(copy, res) + } + s.mu.RUnlock() + + for _, res := range copy { + if err := fn(res); err != nil { + return err + } + } + return nil +} + +func (s *inMemoryStore) Load(id string, resultID string) (CacheResult, error) { + s.mu.RLock() + defer s.mu.RUnlock() + k, ok := s.byID[id] + if !ok { + return CacheResult{}, errors.Wrapf(ErrNotFound, "no such key %s", id) + } + r, ok := k.results[resultID] + if !ok { + return CacheResult{}, errors.WithStack(ErrNotFound) + } + return r, nil +} + +func (s *inMemoryStore) AddResult(id string, res CacheResult) error { + s.mu.Lock() + defer s.mu.Unlock() + k, ok := s.byID[id] + if !ok { + k = newInMemoryKey(id) + s.byID[id] = k + } + k.results[res.ID] = res + m, ok := s.byResult[res.ID] + if !ok { + m = map[string]struct{}{} + s.byResult[res.ID] = m + } + m[id] = struct{}{} + return nil +} + +func (s *inMemoryStore) WalkIDsByResult(resultID string, fn func(string) error) error { + s.mu.Lock() + + ids := map[string]struct{}{} + for id := range s.byResult[resultID] { + ids[id] = struct{}{} + } + s.mu.Unlock() + + for id := range ids { + if err := fn(id); err != nil { + return err + } + } + + return nil +} + +func (s *inMemoryStore) Release(resultID string) error { + s.mu.Lock() + defer s.mu.Unlock() + + ids, ok := s.byResult[resultID] + if !ok { + return nil + } + + for id := range ids { + k, ok := s.byID[id] + if !ok { + continue + } + + delete(k.results, resultID) + delete(s.byResult[resultID], id) + if len(s.byResult[resultID]) == 0 { + delete(s.byResult, resultID) + } + + s.emptyBranchWithParents(k) + } + + return nil +} + +func (s *inMemoryStore) emptyBranchWithParents(k *inMemoryKey) { + if len(k.results) != 0 || len(k.links) != 0 { + return + } + for id := range k.backlinks { + p, ok := s.byID[id] + if !ok { + continue + } + for l := range p.links { + delete(p.links[l], k.id) + if len(p.links[l]) == 0 { + delete(p.links, l) + } + } + s.emptyBranchWithParents(p) + } + + delete(s.byID, k.id) +} + +func (s *inMemoryStore) AddLink(id string, link CacheInfoLink, target string) error { + s.mu.Lock() + defer s.mu.Unlock() + k, ok := s.byID[id] + if !ok { + k = newInMemoryKey(id) + s.byID[id] = k + } + k2, ok := s.byID[target] + if !ok { + k2 = newInMemoryKey(target) + s.byID[target] = k2 + } + m, ok := k.links[link] + if !ok { + m = map[string]struct{}{} + k.links[link] = m + } + + k2.backlinks[id] = struct{}{} + m[target] = struct{}{} + return nil +} + +func (s *inMemoryStore) WalkLinks(id string, link CacheInfoLink, fn func(id string) error) error { + s.mu.RLock() + k, ok := s.byID[id] + if !ok { + s.mu.RUnlock() + return nil + } + var links []string + for target := range k.links[link] { + links = append(links, target) + } + s.mu.RUnlock() + + for _, t := range links { + if err := fn(t); err != nil { + return err + } + } + return nil +} + +func (s *inMemoryStore) HasLink(id string, link CacheInfoLink, target string) bool { + s.mu.RLock() + defer s.mu.RUnlock() + if k, ok := s.byID[id]; ok { + if v, ok := k.links[link]; ok { + if _, ok := v[target]; ok { + return true + } + } + } + return false +} + +func (s *inMemoryStore) WalkBacklinks(id string, fn func(id string, link CacheInfoLink) error) error { + s.mu.RLock() + k, ok := s.byID[id] + if !ok { + s.mu.RUnlock() + return nil + } + var outIDs []string + var outLinks []CacheInfoLink + for bid := range k.backlinks { + b, ok := s.byID[bid] + if !ok { + continue + } + for l, m := range b.links { + if _, ok := m[id]; !ok { + continue + } + outIDs = append(outIDs, bid) + outLinks = append(outLinks, CacheInfoLink{ + Digest: rootKey(l.Digest, l.Output), + Input: l.Input, + Selector: l.Selector, + }) + } + } + s.mu.RUnlock() + + for i := range outIDs { + if err := fn(outIDs[i], outLinks[i]); err != nil { + return err + } + } + return nil +} + +func NewInMemoryResultStorage() CacheResultStorage { + return &inMemoryResultStore{m: &sync.Map{}} +} + +type inMemoryResultStore struct { + m *sync.Map +} + +func (s *inMemoryResultStore) Save(r Result) (CacheResult, error) { + s.m.Store(r.ID(), r) + return CacheResult{ID: r.ID(), CreatedAt: time.Now()}, nil +} + +func (s *inMemoryResultStore) Load(ctx context.Context, res CacheResult) (Result, error) { + v, ok := s.m.Load(res.ID) + if !ok { + return nil, errors.WithStack(ErrNotFound) + } + return v.(Result), nil +} + +func (s *inMemoryResultStore) LoadRemote(ctx context.Context, res CacheResult) (*Remote, error) { + return nil, nil +} + +func (s *inMemoryResultStore) Exists(id string) bool { + _, ok := s.m.Load(id) + return ok +} diff --git a/vendor/github.com/moby/buildkit/solver/pb/attr.go b/vendor/github.com/moby/buildkit/solver/pb/attr.go new file mode 100644 index 0000000000000..6f5f77ff516b3 --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/pb/attr.go @@ -0,0 +1,17 @@ +package pb + +const AttrKeepGitDir = "git.keepgitdir" +const AttrFullRemoteURL = "git.fullurl" +const AttrLocalSessionID = "local.session" +const AttrLocalUniqueID = "local.unique" +const AttrIncludePatterns = "local.includepattern" +const AttrFollowPaths = "local.followpaths" +const AttrExcludePatterns = "local.excludepatterns" +const AttrSharedKeyHint = "local.sharedkeyhint" +const AttrLLBDefinitionFilename = "llbbuild.filename" + +const AttrHTTPChecksum = "http.checksum" +const AttrHTTPFilename = "http.filename" +const AttrHTTPPerm = "http.perm" +const AttrHTTPUID = "http.uid" +const AttrHTTPGID = "http.gid" diff --git a/vendor/github.com/moby/buildkit/solver/pb/const.go b/vendor/github.com/moby/buildkit/solver/pb/const.go new file mode 100644 index 0000000000000..2cb9951082e83 --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/pb/const.go @@ -0,0 +1,12 @@ +package pb + +type InputIndex int64 +type OutputIndex int64 + +const RootMount = "/" +const SkipOutput OutputIndex = -1 +const Empty InputIndex = -1 +const LLBBuilder InputIndex = -1 + +const LLBDefinitionInput = "buildkit.llb.definition" +const LLBDefaultDefinitionFile = LLBDefinitionInput diff --git a/vendor/github.com/moby/buildkit/solver/pb/generate.go b/vendor/github.com/moby/buildkit/solver/pb/generate.go new file mode 100644 index 0000000000000..c31e148f2adf8 --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/pb/generate.go @@ -0,0 +1,3 @@ +package pb + +//go:generate protoc -I=. -I=../../vendor/ --gogofaster_out=. ops.proto diff --git a/vendor/github.com/moby/buildkit/solver/pb/ops.pb.go b/vendor/github.com/moby/buildkit/solver/pb/ops.pb.go new file mode 100644 index 0000000000000..881e87c34ad59 --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/pb/ops.pb.go @@ -0,0 +1,4960 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ops.proto + +/* + Package pb is a generated protocol buffer package. + + Package pb provides the protobuf definition of LLB: low-level builder instruction. + LLB is DAG-structured; Op represents a vertex, and Definition represents a graph. + + It is generated from these files: + ops.proto + + It has these top-level messages: + Op + Platform + Input + ExecOp + Meta + Mount + CacheOpt + CopyOp + CopySource + SourceOp + BuildOp + BuildInput + OpMetadata + ExportCache + ProxyEnv + WorkerConstraints + Definition +*/ +package pb + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" +import _ "github.com/gogo/protobuf/gogoproto" + +import github_com_opencontainers_go_digest "github.com/opencontainers/go-digest" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +// MountType defines a type of a mount from a supported set +type MountType int32 + +const ( + MountType_BIND MountType = 0 + MountType_SECRET MountType = 1 + MountType_SSH MountType = 2 + MountType_CACHE MountType = 3 + MountType_TMPFS MountType = 4 +) + +var MountType_name = map[int32]string{ + 0: "BIND", + 1: "SECRET", + 2: "SSH", + 3: "CACHE", + 4: "TMPFS", +} +var MountType_value = map[string]int32{ + "BIND": 0, + "SECRET": 1, + "SSH": 2, + "CACHE": 3, + "TMPFS": 4, +} + +func (x MountType) String() string { + return proto.EnumName(MountType_name, int32(x)) +} +func (MountType) EnumDescriptor() ([]byte, []int) { return fileDescriptorOps, []int{0} } + +// CacheSharingOpt defines different sharing modes for cache mount +type CacheSharingOpt int32 + +const ( + // SHARED cache mount can be used concurrently by multiple writers + CacheSharingOpt_SHARED CacheSharingOpt = 0 + // PRIVATE creates a new mount if there are multiple writers + CacheSharingOpt_PRIVATE CacheSharingOpt = 1 + // LOCKED pauses second writer until first one releases the mount + CacheSharingOpt_LOCKED CacheSharingOpt = 2 +) + +var CacheSharingOpt_name = map[int32]string{ + 0: "SHARED", + 1: "PRIVATE", + 2: "LOCKED", +} +var CacheSharingOpt_value = map[string]int32{ + "SHARED": 0, + "PRIVATE": 1, + "LOCKED": 2, +} + +func (x CacheSharingOpt) String() string { + return proto.EnumName(CacheSharingOpt_name, int32(x)) +} +func (CacheSharingOpt) EnumDescriptor() ([]byte, []int) { return fileDescriptorOps, []int{1} } + +// Op represents a vertex of the LLB DAG. +type Op struct { + // inputs is a set of input edges. + Inputs []*Input `protobuf:"bytes,1,rep,name=inputs" json:"inputs,omitempty"` + // Types that are valid to be assigned to Op: + // *Op_Exec + // *Op_Source + // *Op_Copy + // *Op_Build + Op isOp_Op `protobuf_oneof:"op"` + Platform *Platform `protobuf:"bytes,10,opt,name=platform" json:"platform,omitempty"` + Constraints *WorkerConstraints `protobuf:"bytes,11,opt,name=constraints" json:"constraints,omitempty"` +} + +func (m *Op) Reset() { *m = Op{} } +func (m *Op) String() string { return proto.CompactTextString(m) } +func (*Op) ProtoMessage() {} +func (*Op) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{0} } + +type isOp_Op interface { + isOp_Op() + MarshalTo([]byte) (int, error) + Size() int +} + +type Op_Exec struct { + Exec *ExecOp `protobuf:"bytes,2,opt,name=exec,oneof"` +} +type Op_Source struct { + Source *SourceOp `protobuf:"bytes,3,opt,name=source,oneof"` +} +type Op_Copy struct { + Copy *CopyOp `protobuf:"bytes,4,opt,name=copy,oneof"` +} +type Op_Build struct { + Build *BuildOp `protobuf:"bytes,5,opt,name=build,oneof"` +} + +func (*Op_Exec) isOp_Op() {} +func (*Op_Source) isOp_Op() {} +func (*Op_Copy) isOp_Op() {} +func (*Op_Build) isOp_Op() {} + +func (m *Op) GetOp() isOp_Op { + if m != nil { + return m.Op + } + return nil +} + +func (m *Op) GetInputs() []*Input { + if m != nil { + return m.Inputs + } + return nil +} + +func (m *Op) GetExec() *ExecOp { + if x, ok := m.GetOp().(*Op_Exec); ok { + return x.Exec + } + return nil +} + +func (m *Op) GetSource() *SourceOp { + if x, ok := m.GetOp().(*Op_Source); ok { + return x.Source + } + return nil +} + +func (m *Op) GetCopy() *CopyOp { + if x, ok := m.GetOp().(*Op_Copy); ok { + return x.Copy + } + return nil +} + +func (m *Op) GetBuild() *BuildOp { + if x, ok := m.GetOp().(*Op_Build); ok { + return x.Build + } + return nil +} + +func (m *Op) GetPlatform() *Platform { + if m != nil { + return m.Platform + } + return nil +} + +func (m *Op) GetConstraints() *WorkerConstraints { + if m != nil { + return m.Constraints + } + return nil +} + +// XXX_OneofFuncs is for the internal use of the proto package. +func (*Op) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { + return _Op_OneofMarshaler, _Op_OneofUnmarshaler, _Op_OneofSizer, []interface{}{ + (*Op_Exec)(nil), + (*Op_Source)(nil), + (*Op_Copy)(nil), + (*Op_Build)(nil), + } +} + +func _Op_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { + m := msg.(*Op) + // op + switch x := m.Op.(type) { + case *Op_Exec: + _ = b.EncodeVarint(2<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Exec); err != nil { + return err + } + case *Op_Source: + _ = b.EncodeVarint(3<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Source); err != nil { + return err + } + case *Op_Copy: + _ = b.EncodeVarint(4<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Copy); err != nil { + return err + } + case *Op_Build: + _ = b.EncodeVarint(5<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Build); err != nil { + return err + } + case nil: + default: + return fmt.Errorf("Op.Op has unexpected type %T", x) + } + return nil +} + +func _Op_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { + m := msg.(*Op) + switch tag { + case 2: // op.exec + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(ExecOp) + err := b.DecodeMessage(msg) + m.Op = &Op_Exec{msg} + return true, err + case 3: // op.source + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(SourceOp) + err := b.DecodeMessage(msg) + m.Op = &Op_Source{msg} + return true, err + case 4: // op.copy + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(CopyOp) + err := b.DecodeMessage(msg) + m.Op = &Op_Copy{msg} + return true, err + case 5: // op.build + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(BuildOp) + err := b.DecodeMessage(msg) + m.Op = &Op_Build{msg} + return true, err + default: + return false, nil + } +} + +func _Op_OneofSizer(msg proto.Message) (n int) { + m := msg.(*Op) + // op + switch x := m.Op.(type) { + case *Op_Exec: + s := proto.Size(x.Exec) + n += proto.SizeVarint(2<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Op_Source: + s := proto.Size(x.Source) + n += proto.SizeVarint(3<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Op_Copy: + s := proto.Size(x.Copy) + n += proto.SizeVarint(4<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Op_Build: + s := proto.Size(x.Build) + n += proto.SizeVarint(5<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case nil: + default: + panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) + } + return n +} + +// Platform is github.com/opencontainers/image-spec/specs-go/v1.Platform +type Platform struct { + Architecture string `protobuf:"bytes,1,opt,name=Architecture,proto3" json:"Architecture,omitempty"` + OS string `protobuf:"bytes,2,opt,name=OS,proto3" json:"OS,omitempty"` + Variant string `protobuf:"bytes,3,opt,name=Variant,proto3" json:"Variant,omitempty"` + OSVersion string `protobuf:"bytes,4,opt,name=OSVersion,proto3" json:"OSVersion,omitempty"` + OSFeatures []string `protobuf:"bytes,5,rep,name=OSFeatures" json:"OSFeatures,omitempty"` +} + +func (m *Platform) Reset() { *m = Platform{} } +func (m *Platform) String() string { return proto.CompactTextString(m) } +func (*Platform) ProtoMessage() {} +func (*Platform) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{1} } + +func (m *Platform) GetArchitecture() string { + if m != nil { + return m.Architecture + } + return "" +} + +func (m *Platform) GetOS() string { + if m != nil { + return m.OS + } + return "" +} + +func (m *Platform) GetVariant() string { + if m != nil { + return m.Variant + } + return "" +} + +func (m *Platform) GetOSVersion() string { + if m != nil { + return m.OSVersion + } + return "" +} + +func (m *Platform) GetOSFeatures() []string { + if m != nil { + return m.OSFeatures + } + return nil +} + +// Input represents an input edge for an Op. +type Input struct { + // digest of the marshaled input Op + Digest github_com_opencontainers_go_digest.Digest `protobuf:"bytes,1,opt,name=digest,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"digest"` + // output index of the input Op + Index OutputIndex `protobuf:"varint,2,opt,name=index,proto3,customtype=OutputIndex" json:"index"` +} + +func (m *Input) Reset() { *m = Input{} } +func (m *Input) String() string { return proto.CompactTextString(m) } +func (*Input) ProtoMessage() {} +func (*Input) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{2} } + +// ExecOp executes a command in a container. +type ExecOp struct { + Meta *Meta `protobuf:"bytes,1,opt,name=meta" json:"meta,omitempty"` + Mounts []*Mount `protobuf:"bytes,2,rep,name=mounts" json:"mounts,omitempty"` +} + +func (m *ExecOp) Reset() { *m = ExecOp{} } +func (m *ExecOp) String() string { return proto.CompactTextString(m) } +func (*ExecOp) ProtoMessage() {} +func (*ExecOp) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{3} } + +func (m *ExecOp) GetMeta() *Meta { + if m != nil { + return m.Meta + } + return nil +} + +func (m *ExecOp) GetMounts() []*Mount { + if m != nil { + return m.Mounts + } + return nil +} + +// Meta is a set of arguments for ExecOp. +// Meta is unrelated to LLB metadata. +// FIXME: rename (ExecContext? ExecArgs?) +type Meta struct { + Args []string `protobuf:"bytes,1,rep,name=args" json:"args,omitempty"` + Env []string `protobuf:"bytes,2,rep,name=env" json:"env,omitempty"` + Cwd string `protobuf:"bytes,3,opt,name=cwd,proto3" json:"cwd,omitempty"` + User string `protobuf:"bytes,4,opt,name=user,proto3" json:"user,omitempty"` + ProxyEnv *ProxyEnv `protobuf:"bytes,5,opt,name=proxy_env,json=proxyEnv" json:"proxy_env,omitempty"` +} + +func (m *Meta) Reset() { *m = Meta{} } +func (m *Meta) String() string { return proto.CompactTextString(m) } +func (*Meta) ProtoMessage() {} +func (*Meta) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{4} } + +func (m *Meta) GetArgs() []string { + if m != nil { + return m.Args + } + return nil +} + +func (m *Meta) GetEnv() []string { + if m != nil { + return m.Env + } + return nil +} + +func (m *Meta) GetCwd() string { + if m != nil { + return m.Cwd + } + return "" +} + +func (m *Meta) GetUser() string { + if m != nil { + return m.User + } + return "" +} + +func (m *Meta) GetProxyEnv() *ProxyEnv { + if m != nil { + return m.ProxyEnv + } + return nil +} + +// Mount specifies how to mount an input Op as a filesystem. +type Mount struct { + Input InputIndex `protobuf:"varint,1,opt,name=input,proto3,customtype=InputIndex" json:"input"` + Selector string `protobuf:"bytes,2,opt,name=selector,proto3" json:"selector,omitempty"` + Dest string `protobuf:"bytes,3,opt,name=dest,proto3" json:"dest,omitempty"` + Output OutputIndex `protobuf:"varint,4,opt,name=output,proto3,customtype=OutputIndex" json:"output"` + Readonly bool `protobuf:"varint,5,opt,name=readonly,proto3" json:"readonly,omitempty"` + MountType MountType `protobuf:"varint,6,opt,name=mountType,proto3,enum=pb.MountType" json:"mountType,omitempty"` + CacheOpt *CacheOpt `protobuf:"bytes,20,opt,name=cacheOpt" json:"cacheOpt,omitempty"` +} + +func (m *Mount) Reset() { *m = Mount{} } +func (m *Mount) String() string { return proto.CompactTextString(m) } +func (*Mount) ProtoMessage() {} +func (*Mount) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{5} } + +func (m *Mount) GetSelector() string { + if m != nil { + return m.Selector + } + return "" +} + +func (m *Mount) GetDest() string { + if m != nil { + return m.Dest + } + return "" +} + +func (m *Mount) GetReadonly() bool { + if m != nil { + return m.Readonly + } + return false +} + +func (m *Mount) GetMountType() MountType { + if m != nil { + return m.MountType + } + return MountType_BIND +} + +func (m *Mount) GetCacheOpt() *CacheOpt { + if m != nil { + return m.CacheOpt + } + return nil +} + +// CacheOpt defines options specific to cache mounts +type CacheOpt struct { + // ID is an optional namespace for the mount + ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` + // Sharing is the sharing mode for the mount + Sharing CacheSharingOpt `protobuf:"varint,2,opt,name=sharing,proto3,enum=pb.CacheSharingOpt" json:"sharing,omitempty"` +} + +func (m *CacheOpt) Reset() { *m = CacheOpt{} } +func (m *CacheOpt) String() string { return proto.CompactTextString(m) } +func (*CacheOpt) ProtoMessage() {} +func (*CacheOpt) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{6} } + +func (m *CacheOpt) GetID() string { + if m != nil { + return m.ID + } + return "" +} + +func (m *CacheOpt) GetSharing() CacheSharingOpt { + if m != nil { + return m.Sharing + } + return CacheSharingOpt_SHARED +} + +// CopyOp copies files across Ops. +type CopyOp struct { + Src []*CopySource `protobuf:"bytes,1,rep,name=src" json:"src,omitempty"` + Dest string `protobuf:"bytes,2,opt,name=dest,proto3" json:"dest,omitempty"` +} + +func (m *CopyOp) Reset() { *m = CopyOp{} } +func (m *CopyOp) String() string { return proto.CompactTextString(m) } +func (*CopyOp) ProtoMessage() {} +func (*CopyOp) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{7} } + +func (m *CopyOp) GetSrc() []*CopySource { + if m != nil { + return m.Src + } + return nil +} + +func (m *CopyOp) GetDest() string { + if m != nil { + return m.Dest + } + return "" +} + +// CopySource specifies a source for CopyOp. +type CopySource struct { + Input InputIndex `protobuf:"varint,1,opt,name=input,proto3,customtype=InputIndex" json:"input"` + Selector string `protobuf:"bytes,2,opt,name=selector,proto3" json:"selector,omitempty"` +} + +func (m *CopySource) Reset() { *m = CopySource{} } +func (m *CopySource) String() string { return proto.CompactTextString(m) } +func (*CopySource) ProtoMessage() {} +func (*CopySource) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{8} } + +func (m *CopySource) GetSelector() string { + if m != nil { + return m.Selector + } + return "" +} + +// SourceOp specifies a source such as build contexts and images. +type SourceOp struct { + // TODO: use source type or any type instead of URL protocol. + // identifier e.g. local://, docker-image://, git://, https://... + Identifier string `protobuf:"bytes,1,opt,name=identifier,proto3" json:"identifier,omitempty"` + // attrs are defined in attr.go + Attrs map[string]string `protobuf:"bytes,2,rep,name=attrs" json:"attrs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (m *SourceOp) Reset() { *m = SourceOp{} } +func (m *SourceOp) String() string { return proto.CompactTextString(m) } +func (*SourceOp) ProtoMessage() {} +func (*SourceOp) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{9} } + +func (m *SourceOp) GetIdentifier() string { + if m != nil { + return m.Identifier + } + return "" +} + +func (m *SourceOp) GetAttrs() map[string]string { + if m != nil { + return m.Attrs + } + return nil +} + +// BuildOp is used for nested build invocation. +// BuildOp is experimental and can break without backwards compatibility +type BuildOp struct { + Builder InputIndex `protobuf:"varint,1,opt,name=builder,proto3,customtype=InputIndex" json:"builder"` + Inputs map[string]*BuildInput `protobuf:"bytes,2,rep,name=inputs" json:"inputs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"` + Def *Definition `protobuf:"bytes,3,opt,name=def" json:"def,omitempty"` + Attrs map[string]string `protobuf:"bytes,4,rep,name=attrs" json:"attrs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (m *BuildOp) Reset() { *m = BuildOp{} } +func (m *BuildOp) String() string { return proto.CompactTextString(m) } +func (*BuildOp) ProtoMessage() {} +func (*BuildOp) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{10} } + +func (m *BuildOp) GetInputs() map[string]*BuildInput { + if m != nil { + return m.Inputs + } + return nil +} + +func (m *BuildOp) GetDef() *Definition { + if m != nil { + return m.Def + } + return nil +} + +func (m *BuildOp) GetAttrs() map[string]string { + if m != nil { + return m.Attrs + } + return nil +} + +// BuildInput is used for BuildOp. +type BuildInput struct { + Input InputIndex `protobuf:"varint,1,opt,name=input,proto3,customtype=InputIndex" json:"input"` +} + +func (m *BuildInput) Reset() { *m = BuildInput{} } +func (m *BuildInput) String() string { return proto.CompactTextString(m) } +func (*BuildInput) ProtoMessage() {} +func (*BuildInput) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{11} } + +// OpMetadata is a per-vertex metadata entry, which can be defined for arbitrary Op vertex and overridable on the run time. +type OpMetadata struct { + // ignore_cache specifies to ignore the cache for this Op. + IgnoreCache bool `protobuf:"varint,1,opt,name=ignore_cache,json=ignoreCache,proto3" json:"ignore_cache,omitempty"` + // Description can be used for keeping any text fields that builder doesn't parse + Description map[string]string `protobuf:"bytes,2,rep,name=description" json:"description,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // index 3 reserved for WorkerConstraint in previous versions + // WorkerConstraint worker_constraint = 3; + ExportCache *ExportCache `protobuf:"bytes,4,opt,name=export_cache,json=exportCache" json:"export_cache,omitempty"` +} + +func (m *OpMetadata) Reset() { *m = OpMetadata{} } +func (m *OpMetadata) String() string { return proto.CompactTextString(m) } +func (*OpMetadata) ProtoMessage() {} +func (*OpMetadata) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{12} } + +func (m *OpMetadata) GetIgnoreCache() bool { + if m != nil { + return m.IgnoreCache + } + return false +} + +func (m *OpMetadata) GetDescription() map[string]string { + if m != nil { + return m.Description + } + return nil +} + +func (m *OpMetadata) GetExportCache() *ExportCache { + if m != nil { + return m.ExportCache + } + return nil +} + +type ExportCache struct { + Value bool `protobuf:"varint,1,opt,name=Value,proto3" json:"Value,omitempty"` +} + +func (m *ExportCache) Reset() { *m = ExportCache{} } +func (m *ExportCache) String() string { return proto.CompactTextString(m) } +func (*ExportCache) ProtoMessage() {} +func (*ExportCache) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{13} } + +func (m *ExportCache) GetValue() bool { + if m != nil { + return m.Value + } + return false +} + +type ProxyEnv struct { + HttpProxy string `protobuf:"bytes,1,opt,name=http_proxy,json=httpProxy,proto3" json:"http_proxy,omitempty"` + HttpsProxy string `protobuf:"bytes,2,opt,name=https_proxy,json=httpsProxy,proto3" json:"https_proxy,omitempty"` + FtpProxy string `protobuf:"bytes,3,opt,name=ftp_proxy,json=ftpProxy,proto3" json:"ftp_proxy,omitempty"` + NoProxy string `protobuf:"bytes,4,opt,name=no_proxy,json=noProxy,proto3" json:"no_proxy,omitempty"` +} + +func (m *ProxyEnv) Reset() { *m = ProxyEnv{} } +func (m *ProxyEnv) String() string { return proto.CompactTextString(m) } +func (*ProxyEnv) ProtoMessage() {} +func (*ProxyEnv) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{14} } + +func (m *ProxyEnv) GetHttpProxy() string { + if m != nil { + return m.HttpProxy + } + return "" +} + +func (m *ProxyEnv) GetHttpsProxy() string { + if m != nil { + return m.HttpsProxy + } + return "" +} + +func (m *ProxyEnv) GetFtpProxy() string { + if m != nil { + return m.FtpProxy + } + return "" +} + +func (m *ProxyEnv) GetNoProxy() string { + if m != nil { + return m.NoProxy + } + return "" +} + +// WorkerConstraints defines conditions for the worker +type WorkerConstraints struct { + Filter []string `protobuf:"bytes,1,rep,name=filter" json:"filter,omitempty"` +} + +func (m *WorkerConstraints) Reset() { *m = WorkerConstraints{} } +func (m *WorkerConstraints) String() string { return proto.CompactTextString(m) } +func (*WorkerConstraints) ProtoMessage() {} +func (*WorkerConstraints) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{15} } + +func (m *WorkerConstraints) GetFilter() []string { + if m != nil { + return m.Filter + } + return nil +} + +// Definition is the LLB definition structure with per-vertex metadata entries +type Definition struct { + // def is a list of marshaled Op messages + Def [][]byte `protobuf:"bytes,1,rep,name=def" json:"def,omitempty"` + // metadata contains metadata for the each of the Op messages. + // A key must be an LLB op digest string. Currently, empty string is not expected as a key, but it may change in the future. + Metadata map[github_com_opencontainers_go_digest.Digest]OpMetadata `protobuf:"bytes,2,rep,name=metadata,castkey=github.com/opencontainers/go-digest.Digest" json:"metadata" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"` +} + +func (m *Definition) Reset() { *m = Definition{} } +func (m *Definition) String() string { return proto.CompactTextString(m) } +func (*Definition) ProtoMessage() {} +func (*Definition) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{16} } + +func (m *Definition) GetDef() [][]byte { + if m != nil { + return m.Def + } + return nil +} + +func (m *Definition) GetMetadata() map[github_com_opencontainers_go_digest.Digest]OpMetadata { + if m != nil { + return m.Metadata + } + return nil +} + +func init() { + proto.RegisterType((*Op)(nil), "pb.Op") + proto.RegisterType((*Platform)(nil), "pb.Platform") + proto.RegisterType((*Input)(nil), "pb.Input") + proto.RegisterType((*ExecOp)(nil), "pb.ExecOp") + proto.RegisterType((*Meta)(nil), "pb.Meta") + proto.RegisterType((*Mount)(nil), "pb.Mount") + proto.RegisterType((*CacheOpt)(nil), "pb.CacheOpt") + proto.RegisterType((*CopyOp)(nil), "pb.CopyOp") + proto.RegisterType((*CopySource)(nil), "pb.CopySource") + proto.RegisterType((*SourceOp)(nil), "pb.SourceOp") + proto.RegisterType((*BuildOp)(nil), "pb.BuildOp") + proto.RegisterType((*BuildInput)(nil), "pb.BuildInput") + proto.RegisterType((*OpMetadata)(nil), "pb.OpMetadata") + proto.RegisterType((*ExportCache)(nil), "pb.ExportCache") + proto.RegisterType((*ProxyEnv)(nil), "pb.ProxyEnv") + proto.RegisterType((*WorkerConstraints)(nil), "pb.WorkerConstraints") + proto.RegisterType((*Definition)(nil), "pb.Definition") + proto.RegisterEnum("pb.MountType", MountType_name, MountType_value) + proto.RegisterEnum("pb.CacheSharingOpt", CacheSharingOpt_name, CacheSharingOpt_value) +} +func (m *Op) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Op) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Inputs) > 0 { + for _, msg := range m.Inputs { + dAtA[i] = 0xa + i++ + i = encodeVarintOps(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if m.Op != nil { + nn1, err := m.Op.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += nn1 + } + if m.Platform != nil { + dAtA[i] = 0x52 + i++ + i = encodeVarintOps(dAtA, i, uint64(m.Platform.Size())) + n2, err := m.Platform.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n2 + } + if m.Constraints != nil { + dAtA[i] = 0x5a + i++ + i = encodeVarintOps(dAtA, i, uint64(m.Constraints.Size())) + n3, err := m.Constraints.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n3 + } + return i, nil +} + +func (m *Op_Exec) MarshalTo(dAtA []byte) (int, error) { + i := 0 + if m.Exec != nil { + dAtA[i] = 0x12 + i++ + i = encodeVarintOps(dAtA, i, uint64(m.Exec.Size())) + n4, err := m.Exec.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n4 + } + return i, nil +} +func (m *Op_Source) MarshalTo(dAtA []byte) (int, error) { + i := 0 + if m.Source != nil { + dAtA[i] = 0x1a + i++ + i = encodeVarintOps(dAtA, i, uint64(m.Source.Size())) + n5, err := m.Source.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n5 + } + return i, nil +} +func (m *Op_Copy) MarshalTo(dAtA []byte) (int, error) { + i := 0 + if m.Copy != nil { + dAtA[i] = 0x22 + i++ + i = encodeVarintOps(dAtA, i, uint64(m.Copy.Size())) + n6, err := m.Copy.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n6 + } + return i, nil +} +func (m *Op_Build) MarshalTo(dAtA []byte) (int, error) { + i := 0 + if m.Build != nil { + dAtA[i] = 0x2a + i++ + i = encodeVarintOps(dAtA, i, uint64(m.Build.Size())) + n7, err := m.Build.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n7 + } + return i, nil +} +func (m *Platform) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Platform) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Architecture) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintOps(dAtA, i, uint64(len(m.Architecture))) + i += copy(dAtA[i:], m.Architecture) + } + if len(m.OS) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintOps(dAtA, i, uint64(len(m.OS))) + i += copy(dAtA[i:], m.OS) + } + if len(m.Variant) > 0 { + dAtA[i] = 0x1a + i++ + i = encodeVarintOps(dAtA, i, uint64(len(m.Variant))) + i += copy(dAtA[i:], m.Variant) + } + if len(m.OSVersion) > 0 { + dAtA[i] = 0x22 + i++ + i = encodeVarintOps(dAtA, i, uint64(len(m.OSVersion))) + i += copy(dAtA[i:], m.OSVersion) + } + if len(m.OSFeatures) > 0 { + for _, s := range m.OSFeatures { + dAtA[i] = 0x2a + i++ + l = len(s) + for l >= 1<<7 { + dAtA[i] = uint8(uint64(l)&0x7f | 0x80) + l >>= 7 + i++ + } + dAtA[i] = uint8(l) + i++ + i += copy(dAtA[i:], s) + } + } + return i, nil +} + +func (m *Input) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Input) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Digest) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintOps(dAtA, i, uint64(len(m.Digest))) + i += copy(dAtA[i:], m.Digest) + } + if m.Index != 0 { + dAtA[i] = 0x10 + i++ + i = encodeVarintOps(dAtA, i, uint64(m.Index)) + } + return i, nil +} + +func (m *ExecOp) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExecOp) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Meta != nil { + dAtA[i] = 0xa + i++ + i = encodeVarintOps(dAtA, i, uint64(m.Meta.Size())) + n8, err := m.Meta.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n8 + } + if len(m.Mounts) > 0 { + for _, msg := range m.Mounts { + dAtA[i] = 0x12 + i++ + i = encodeVarintOps(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + return i, nil +} + +func (m *Meta) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Meta) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Args) > 0 { + for _, s := range m.Args { + dAtA[i] = 0xa + i++ + l = len(s) + for l >= 1<<7 { + dAtA[i] = uint8(uint64(l)&0x7f | 0x80) + l >>= 7 + i++ + } + dAtA[i] = uint8(l) + i++ + i += copy(dAtA[i:], s) + } + } + if len(m.Env) > 0 { + for _, s := range m.Env { + dAtA[i] = 0x12 + i++ + l = len(s) + for l >= 1<<7 { + dAtA[i] = uint8(uint64(l)&0x7f | 0x80) + l >>= 7 + i++ + } + dAtA[i] = uint8(l) + i++ + i += copy(dAtA[i:], s) + } + } + if len(m.Cwd) > 0 { + dAtA[i] = 0x1a + i++ + i = encodeVarintOps(dAtA, i, uint64(len(m.Cwd))) + i += copy(dAtA[i:], m.Cwd) + } + if len(m.User) > 0 { + dAtA[i] = 0x22 + i++ + i = encodeVarintOps(dAtA, i, uint64(len(m.User))) + i += copy(dAtA[i:], m.User) + } + if m.ProxyEnv != nil { + dAtA[i] = 0x2a + i++ + i = encodeVarintOps(dAtA, i, uint64(m.ProxyEnv.Size())) + n9, err := m.ProxyEnv.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n9 + } + return i, nil +} + +func (m *Mount) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Mount) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Input != 0 { + dAtA[i] = 0x8 + i++ + i = encodeVarintOps(dAtA, i, uint64(m.Input)) + } + if len(m.Selector) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintOps(dAtA, i, uint64(len(m.Selector))) + i += copy(dAtA[i:], m.Selector) + } + if len(m.Dest) > 0 { + dAtA[i] = 0x1a + i++ + i = encodeVarintOps(dAtA, i, uint64(len(m.Dest))) + i += copy(dAtA[i:], m.Dest) + } + if m.Output != 0 { + dAtA[i] = 0x20 + i++ + i = encodeVarintOps(dAtA, i, uint64(m.Output)) + } + if m.Readonly { + dAtA[i] = 0x28 + i++ + if m.Readonly { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if m.MountType != 0 { + dAtA[i] = 0x30 + i++ + i = encodeVarintOps(dAtA, i, uint64(m.MountType)) + } + if m.CacheOpt != nil { + dAtA[i] = 0xa2 + i++ + dAtA[i] = 0x1 + i++ + i = encodeVarintOps(dAtA, i, uint64(m.CacheOpt.Size())) + n10, err := m.CacheOpt.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n10 + } + return i, nil +} + +func (m *CacheOpt) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CacheOpt) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintOps(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + if m.Sharing != 0 { + dAtA[i] = 0x10 + i++ + i = encodeVarintOps(dAtA, i, uint64(m.Sharing)) + } + return i, nil +} + +func (m *CopyOp) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CopyOp) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Src) > 0 { + for _, msg := range m.Src { + dAtA[i] = 0xa + i++ + i = encodeVarintOps(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if len(m.Dest) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintOps(dAtA, i, uint64(len(m.Dest))) + i += copy(dAtA[i:], m.Dest) + } + return i, nil +} + +func (m *CopySource) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CopySource) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Input != 0 { + dAtA[i] = 0x8 + i++ + i = encodeVarintOps(dAtA, i, uint64(m.Input)) + } + if len(m.Selector) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintOps(dAtA, i, uint64(len(m.Selector))) + i += copy(dAtA[i:], m.Selector) + } + return i, nil +} + +func (m *SourceOp) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SourceOp) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Identifier) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintOps(dAtA, i, uint64(len(m.Identifier))) + i += copy(dAtA[i:], m.Identifier) + } + if len(m.Attrs) > 0 { + for k, _ := range m.Attrs { + dAtA[i] = 0x12 + i++ + v := m.Attrs[k] + mapSize := 1 + len(k) + sovOps(uint64(len(k))) + 1 + len(v) + sovOps(uint64(len(v))) + i = encodeVarintOps(dAtA, i, uint64(mapSize)) + dAtA[i] = 0xa + i++ + i = encodeVarintOps(dAtA, i, uint64(len(k))) + i += copy(dAtA[i:], k) + dAtA[i] = 0x12 + i++ + i = encodeVarintOps(dAtA, i, uint64(len(v))) + i += copy(dAtA[i:], v) + } + } + return i, nil +} + +func (m *BuildOp) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *BuildOp) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Builder != 0 { + dAtA[i] = 0x8 + i++ + i = encodeVarintOps(dAtA, i, uint64(m.Builder)) + } + if len(m.Inputs) > 0 { + for k, _ := range m.Inputs { + dAtA[i] = 0x12 + i++ + v := m.Inputs[k] + msgSize := 0 + if v != nil { + msgSize = v.Size() + msgSize += 1 + sovOps(uint64(msgSize)) + } + mapSize := 1 + len(k) + sovOps(uint64(len(k))) + msgSize + i = encodeVarintOps(dAtA, i, uint64(mapSize)) + dAtA[i] = 0xa + i++ + i = encodeVarintOps(dAtA, i, uint64(len(k))) + i += copy(dAtA[i:], k) + if v != nil { + dAtA[i] = 0x12 + i++ + i = encodeVarintOps(dAtA, i, uint64(v.Size())) + n11, err := v.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n11 + } + } + } + if m.Def != nil { + dAtA[i] = 0x1a + i++ + i = encodeVarintOps(dAtA, i, uint64(m.Def.Size())) + n12, err := m.Def.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n12 + } + if len(m.Attrs) > 0 { + for k, _ := range m.Attrs { + dAtA[i] = 0x22 + i++ + v := m.Attrs[k] + mapSize := 1 + len(k) + sovOps(uint64(len(k))) + 1 + len(v) + sovOps(uint64(len(v))) + i = encodeVarintOps(dAtA, i, uint64(mapSize)) + dAtA[i] = 0xa + i++ + i = encodeVarintOps(dAtA, i, uint64(len(k))) + i += copy(dAtA[i:], k) + dAtA[i] = 0x12 + i++ + i = encodeVarintOps(dAtA, i, uint64(len(v))) + i += copy(dAtA[i:], v) + } + } + return i, nil +} + +func (m *BuildInput) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *BuildInput) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Input != 0 { + dAtA[i] = 0x8 + i++ + i = encodeVarintOps(dAtA, i, uint64(m.Input)) + } + return i, nil +} + +func (m *OpMetadata) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *OpMetadata) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.IgnoreCache { + dAtA[i] = 0x8 + i++ + if m.IgnoreCache { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if len(m.Description) > 0 { + for k, _ := range m.Description { + dAtA[i] = 0x12 + i++ + v := m.Description[k] + mapSize := 1 + len(k) + sovOps(uint64(len(k))) + 1 + len(v) + sovOps(uint64(len(v))) + i = encodeVarintOps(dAtA, i, uint64(mapSize)) + dAtA[i] = 0xa + i++ + i = encodeVarintOps(dAtA, i, uint64(len(k))) + i += copy(dAtA[i:], k) + dAtA[i] = 0x12 + i++ + i = encodeVarintOps(dAtA, i, uint64(len(v))) + i += copy(dAtA[i:], v) + } + } + if m.ExportCache != nil { + dAtA[i] = 0x22 + i++ + i = encodeVarintOps(dAtA, i, uint64(m.ExportCache.Size())) + n13, err := m.ExportCache.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n13 + } + return i, nil +} + +func (m *ExportCache) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExportCache) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Value { + dAtA[i] = 0x8 + i++ + if m.Value { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + return i, nil +} + +func (m *ProxyEnv) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ProxyEnv) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.HttpProxy) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintOps(dAtA, i, uint64(len(m.HttpProxy))) + i += copy(dAtA[i:], m.HttpProxy) + } + if len(m.HttpsProxy) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintOps(dAtA, i, uint64(len(m.HttpsProxy))) + i += copy(dAtA[i:], m.HttpsProxy) + } + if len(m.FtpProxy) > 0 { + dAtA[i] = 0x1a + i++ + i = encodeVarintOps(dAtA, i, uint64(len(m.FtpProxy))) + i += copy(dAtA[i:], m.FtpProxy) + } + if len(m.NoProxy) > 0 { + dAtA[i] = 0x22 + i++ + i = encodeVarintOps(dAtA, i, uint64(len(m.NoProxy))) + i += copy(dAtA[i:], m.NoProxy) + } + return i, nil +} + +func (m *WorkerConstraints) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *WorkerConstraints) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Filter) > 0 { + for _, s := range m.Filter { + dAtA[i] = 0xa + i++ + l = len(s) + for l >= 1<<7 { + dAtA[i] = uint8(uint64(l)&0x7f | 0x80) + l >>= 7 + i++ + } + dAtA[i] = uint8(l) + i++ + i += copy(dAtA[i:], s) + } + } + return i, nil +} + +func (m *Definition) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Definition) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Def) > 0 { + for _, b := range m.Def { + dAtA[i] = 0xa + i++ + i = encodeVarintOps(dAtA, i, uint64(len(b))) + i += copy(dAtA[i:], b) + } + } + if len(m.Metadata) > 0 { + for k, _ := range m.Metadata { + dAtA[i] = 0x12 + i++ + v := m.Metadata[k] + msgSize := 0 + if (&v) != nil { + msgSize = (&v).Size() + msgSize += 1 + sovOps(uint64(msgSize)) + } + mapSize := 1 + len(k) + sovOps(uint64(len(k))) + msgSize + i = encodeVarintOps(dAtA, i, uint64(mapSize)) + dAtA[i] = 0xa + i++ + i = encodeVarintOps(dAtA, i, uint64(len(k))) + i += copy(dAtA[i:], k) + dAtA[i] = 0x12 + i++ + i = encodeVarintOps(dAtA, i, uint64((&v).Size())) + n14, err := (&v).MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n14 + } + } + return i, nil +} + +func encodeVarintOps(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *Op) Size() (n int) { + var l int + _ = l + if len(m.Inputs) > 0 { + for _, e := range m.Inputs { + l = e.Size() + n += 1 + l + sovOps(uint64(l)) + } + } + if m.Op != nil { + n += m.Op.Size() + } + if m.Platform != nil { + l = m.Platform.Size() + n += 1 + l + sovOps(uint64(l)) + } + if m.Constraints != nil { + l = m.Constraints.Size() + n += 1 + l + sovOps(uint64(l)) + } + return n +} + +func (m *Op_Exec) Size() (n int) { + var l int + _ = l + if m.Exec != nil { + l = m.Exec.Size() + n += 1 + l + sovOps(uint64(l)) + } + return n +} +func (m *Op_Source) Size() (n int) { + var l int + _ = l + if m.Source != nil { + l = m.Source.Size() + n += 1 + l + sovOps(uint64(l)) + } + return n +} +func (m *Op_Copy) Size() (n int) { + var l int + _ = l + if m.Copy != nil { + l = m.Copy.Size() + n += 1 + l + sovOps(uint64(l)) + } + return n +} +func (m *Op_Build) Size() (n int) { + var l int + _ = l + if m.Build != nil { + l = m.Build.Size() + n += 1 + l + sovOps(uint64(l)) + } + return n +} +func (m *Platform) Size() (n int) { + var l int + _ = l + l = len(m.Architecture) + if l > 0 { + n += 1 + l + sovOps(uint64(l)) + } + l = len(m.OS) + if l > 0 { + n += 1 + l + sovOps(uint64(l)) + } + l = len(m.Variant) + if l > 0 { + n += 1 + l + sovOps(uint64(l)) + } + l = len(m.OSVersion) + if l > 0 { + n += 1 + l + sovOps(uint64(l)) + } + if len(m.OSFeatures) > 0 { + for _, s := range m.OSFeatures { + l = len(s) + n += 1 + l + sovOps(uint64(l)) + } + } + return n +} + +func (m *Input) Size() (n int) { + var l int + _ = l + l = len(m.Digest) + if l > 0 { + n += 1 + l + sovOps(uint64(l)) + } + if m.Index != 0 { + n += 1 + sovOps(uint64(m.Index)) + } + return n +} + +func (m *ExecOp) Size() (n int) { + var l int + _ = l + if m.Meta != nil { + l = m.Meta.Size() + n += 1 + l + sovOps(uint64(l)) + } + if len(m.Mounts) > 0 { + for _, e := range m.Mounts { + l = e.Size() + n += 1 + l + sovOps(uint64(l)) + } + } + return n +} + +func (m *Meta) Size() (n int) { + var l int + _ = l + if len(m.Args) > 0 { + for _, s := range m.Args { + l = len(s) + n += 1 + l + sovOps(uint64(l)) + } + } + if len(m.Env) > 0 { + for _, s := range m.Env { + l = len(s) + n += 1 + l + sovOps(uint64(l)) + } + } + l = len(m.Cwd) + if l > 0 { + n += 1 + l + sovOps(uint64(l)) + } + l = len(m.User) + if l > 0 { + n += 1 + l + sovOps(uint64(l)) + } + if m.ProxyEnv != nil { + l = m.ProxyEnv.Size() + n += 1 + l + sovOps(uint64(l)) + } + return n +} + +func (m *Mount) Size() (n int) { + var l int + _ = l + if m.Input != 0 { + n += 1 + sovOps(uint64(m.Input)) + } + l = len(m.Selector) + if l > 0 { + n += 1 + l + sovOps(uint64(l)) + } + l = len(m.Dest) + if l > 0 { + n += 1 + l + sovOps(uint64(l)) + } + if m.Output != 0 { + n += 1 + sovOps(uint64(m.Output)) + } + if m.Readonly { + n += 2 + } + if m.MountType != 0 { + n += 1 + sovOps(uint64(m.MountType)) + } + if m.CacheOpt != nil { + l = m.CacheOpt.Size() + n += 2 + l + sovOps(uint64(l)) + } + return n +} + +func (m *CacheOpt) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovOps(uint64(l)) + } + if m.Sharing != 0 { + n += 1 + sovOps(uint64(m.Sharing)) + } + return n +} + +func (m *CopyOp) Size() (n int) { + var l int + _ = l + if len(m.Src) > 0 { + for _, e := range m.Src { + l = e.Size() + n += 1 + l + sovOps(uint64(l)) + } + } + l = len(m.Dest) + if l > 0 { + n += 1 + l + sovOps(uint64(l)) + } + return n +} + +func (m *CopySource) Size() (n int) { + var l int + _ = l + if m.Input != 0 { + n += 1 + sovOps(uint64(m.Input)) + } + l = len(m.Selector) + if l > 0 { + n += 1 + l + sovOps(uint64(l)) + } + return n +} + +func (m *SourceOp) Size() (n int) { + var l int + _ = l + l = len(m.Identifier) + if l > 0 { + n += 1 + l + sovOps(uint64(l)) + } + if len(m.Attrs) > 0 { + for k, v := range m.Attrs { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovOps(uint64(len(k))) + 1 + len(v) + sovOps(uint64(len(v))) + n += mapEntrySize + 1 + sovOps(uint64(mapEntrySize)) + } + } + return n +} + +func (m *BuildOp) Size() (n int) { + var l int + _ = l + if m.Builder != 0 { + n += 1 + sovOps(uint64(m.Builder)) + } + if len(m.Inputs) > 0 { + for k, v := range m.Inputs { + _ = k + _ = v + l = 0 + if v != nil { + l = v.Size() + l += 1 + sovOps(uint64(l)) + } + mapEntrySize := 1 + len(k) + sovOps(uint64(len(k))) + l + n += mapEntrySize + 1 + sovOps(uint64(mapEntrySize)) + } + } + if m.Def != nil { + l = m.Def.Size() + n += 1 + l + sovOps(uint64(l)) + } + if len(m.Attrs) > 0 { + for k, v := range m.Attrs { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovOps(uint64(len(k))) + 1 + len(v) + sovOps(uint64(len(v))) + n += mapEntrySize + 1 + sovOps(uint64(mapEntrySize)) + } + } + return n +} + +func (m *BuildInput) Size() (n int) { + var l int + _ = l + if m.Input != 0 { + n += 1 + sovOps(uint64(m.Input)) + } + return n +} + +func (m *OpMetadata) Size() (n int) { + var l int + _ = l + if m.IgnoreCache { + n += 2 + } + if len(m.Description) > 0 { + for k, v := range m.Description { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovOps(uint64(len(k))) + 1 + len(v) + sovOps(uint64(len(v))) + n += mapEntrySize + 1 + sovOps(uint64(mapEntrySize)) + } + } + if m.ExportCache != nil { + l = m.ExportCache.Size() + n += 1 + l + sovOps(uint64(l)) + } + return n +} + +func (m *ExportCache) Size() (n int) { + var l int + _ = l + if m.Value { + n += 2 + } + return n +} + +func (m *ProxyEnv) Size() (n int) { + var l int + _ = l + l = len(m.HttpProxy) + if l > 0 { + n += 1 + l + sovOps(uint64(l)) + } + l = len(m.HttpsProxy) + if l > 0 { + n += 1 + l + sovOps(uint64(l)) + } + l = len(m.FtpProxy) + if l > 0 { + n += 1 + l + sovOps(uint64(l)) + } + l = len(m.NoProxy) + if l > 0 { + n += 1 + l + sovOps(uint64(l)) + } + return n +} + +func (m *WorkerConstraints) Size() (n int) { + var l int + _ = l + if len(m.Filter) > 0 { + for _, s := range m.Filter { + l = len(s) + n += 1 + l + sovOps(uint64(l)) + } + } + return n +} + +func (m *Definition) Size() (n int) { + var l int + _ = l + if len(m.Def) > 0 { + for _, b := range m.Def { + l = len(b) + n += 1 + l + sovOps(uint64(l)) + } + } + if len(m.Metadata) > 0 { + for k, v := range m.Metadata { + _ = k + _ = v + l = v.Size() + mapEntrySize := 1 + len(k) + sovOps(uint64(len(k))) + 1 + l + sovOps(uint64(l)) + n += mapEntrySize + 1 + sovOps(uint64(mapEntrySize)) + } + } + return n +} + +func sovOps(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozOps(x uint64) (n int) { + return sovOps(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Op) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Op: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Op: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Inputs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Inputs = append(m.Inputs, &Input{}) + if err := m.Inputs[len(m.Inputs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Exec", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &ExecOp{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Op = &Op_Exec{v} + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Source", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &SourceOp{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Op = &Op_Source{v} + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Copy", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &CopyOp{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Op = &Op_Copy{v} + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Build", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &BuildOp{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Op = &Op_Build{v} + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Platform", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Platform == nil { + m.Platform = &Platform{} + } + if err := m.Platform.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Constraints", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Constraints == nil { + m.Constraints = &WorkerConstraints{} + } + if err := m.Constraints.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipOps(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthOps + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Platform) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Platform: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Platform: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Architecture", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Architecture = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OS", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OS = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Variant", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Variant = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OSVersion", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OSVersion = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OSFeatures", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OSFeatures = append(m.OSFeatures, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipOps(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthOps + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Input) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Input: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Input: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Digest", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Digest = github_com_opencontainers_go_digest.Digest(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType) + } + m.Index = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Index |= (OutputIndex(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipOps(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthOps + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExecOp) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExecOp: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExecOp: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Meta", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Meta == nil { + m.Meta = &Meta{} + } + if err := m.Meta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Mounts", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Mounts = append(m.Mounts, &Mount{}) + if err := m.Mounts[len(m.Mounts)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipOps(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthOps + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Meta) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Meta: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Meta: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Args = append(m.Args, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Env", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Env = append(m.Env, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Cwd", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Cwd = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field User", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.User = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProxyEnv", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ProxyEnv == nil { + m.ProxyEnv = &ProxyEnv{} + } + if err := m.ProxyEnv.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipOps(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthOps + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Mount) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Mount: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Mount: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Input", wireType) + } + m.Input = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Input |= (InputIndex(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Selector", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Selector = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Dest", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Dest = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Output", wireType) + } + m.Output = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Output |= (OutputIndex(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Readonly", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Readonly = bool(v != 0) + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MountType", wireType) + } + m.MountType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MountType |= (MountType(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 20: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CacheOpt", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.CacheOpt == nil { + m.CacheOpt = &CacheOpt{} + } + if err := m.CacheOpt.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipOps(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthOps + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CacheOpt) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CacheOpt: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CacheOpt: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sharing", wireType) + } + m.Sharing = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sharing |= (CacheSharingOpt(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipOps(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthOps + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CopyOp) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CopyOp: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CopyOp: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Src", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Src = append(m.Src, &CopySource{}) + if err := m.Src[len(m.Src)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Dest", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Dest = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipOps(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthOps + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CopySource) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CopySource: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CopySource: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Input", wireType) + } + m.Input = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Input |= (InputIndex(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Selector", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Selector = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipOps(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthOps + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SourceOp) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SourceOp: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SourceOp: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Identifier", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Identifier = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Attrs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Attrs == nil { + m.Attrs = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthOps + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthOps + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipOps(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthOps + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Attrs[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipOps(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthOps + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BuildOp) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: BuildOp: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BuildOp: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Builder", wireType) + } + m.Builder = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Builder |= (InputIndex(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Inputs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Inputs == nil { + m.Inputs = make(map[string]*BuildInput) + } + var mapkey string + var mapvalue *BuildInput + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthOps + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var mapmsglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapmsglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if mapmsglen < 0 { + return ErrInvalidLengthOps + } + postmsgIndex := iNdEx + mapmsglen + if mapmsglen < 0 { + return ErrInvalidLengthOps + } + if postmsgIndex > l { + return io.ErrUnexpectedEOF + } + mapvalue = &BuildInput{} + if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { + return err + } + iNdEx = postmsgIndex + } else { + iNdEx = entryPreIndex + skippy, err := skipOps(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthOps + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Inputs[mapkey] = mapvalue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Def", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Def == nil { + m.Def = &Definition{} + } + if err := m.Def.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Attrs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Attrs == nil { + m.Attrs = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthOps + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthOps + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipOps(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthOps + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Attrs[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipOps(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthOps + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BuildInput) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: BuildInput: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BuildInput: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Input", wireType) + } + m.Input = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Input |= (InputIndex(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipOps(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthOps + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *OpMetadata) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: OpMetadata: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: OpMetadata: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IgnoreCache", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.IgnoreCache = bool(v != 0) + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Description == nil { + m.Description = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthOps + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthOps + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipOps(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthOps + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Description[mapkey] = mapvalue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExportCache", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ExportCache == nil { + m.ExportCache = &ExportCache{} + } + if err := m.ExportCache.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipOps(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthOps + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExportCache) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExportCache: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExportCache: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Value = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipOps(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthOps + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ProxyEnv) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ProxyEnv: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ProxyEnv: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field HttpProxy", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.HttpProxy = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field HttpsProxy", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.HttpsProxy = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FtpProxy", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FtpProxy = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NoProxy", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NoProxy = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipOps(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthOps + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *WorkerConstraints) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: WorkerConstraints: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: WorkerConstraints: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Filter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Filter = append(m.Filter, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipOps(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthOps + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Definition) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Definition: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Definition: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Def", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Def = append(m.Def, make([]byte, postIndex-iNdEx)) + copy(m.Def[len(m.Def)-1], dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Metadata == nil { + m.Metadata = make(map[github_com_opencontainers_go_digest.Digest]OpMetadata) + } + var mapkey github_com_opencontainers_go_digest.Digest + mapvalue := &OpMetadata{} + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthOps + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = github_com_opencontainers_go_digest.Digest(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var mapmsglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapmsglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if mapmsglen < 0 { + return ErrInvalidLengthOps + } + postmsgIndex := iNdEx + mapmsglen + if mapmsglen < 0 { + return ErrInvalidLengthOps + } + if postmsgIndex > l { + return io.ErrUnexpectedEOF + } + mapvalue = &OpMetadata{} + if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { + return err + } + iNdEx = postmsgIndex + } else { + iNdEx = entryPreIndex + skippy, err := skipOps(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthOps + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Metadata[github_com_opencontainers_go_digest.Digest(mapkey)] = *mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipOps(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthOps + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipOps(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowOps + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowOps + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowOps + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthOps + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowOps + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipOps(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthOps = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowOps = fmt.Errorf("proto: integer overflow") +) + +func init() { proto.RegisterFile("ops.proto", fileDescriptorOps) } + +var fileDescriptorOps = []byte{ + // 1203 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0x4d, 0x8f, 0x1b, 0x45, + 0x13, 0xde, 0x19, 0x7f, 0xcd, 0xd4, 0x6c, 0x36, 0x7e, 0x3b, 0x79, 0x83, 0x59, 0xc2, 0xae, 0x99, + 0x20, 0xe4, 0x7c, 0xac, 0x57, 0x32, 0x52, 0x88, 0x38, 0x44, 0xac, 0x3f, 0xa2, 0x35, 0x21, 0x38, + 0x6a, 0xaf, 0x96, 0x63, 0x34, 0x1e, 0xb7, 0xbd, 0xa3, 0x78, 0xa7, 0x47, 0x3d, 0xed, 0xb0, 0x3e, + 0x80, 0x44, 0x7e, 0x01, 0x12, 0x12, 0x77, 0x7e, 0x08, 0xf7, 0x1c, 0xb9, 0xc2, 0x21, 0xa0, 0x20, + 0xf1, 0x3b, 0x50, 0x75, 0xb7, 0x67, 0x66, 0x93, 0x20, 0x25, 0x82, 0x93, 0xbb, 0xab, 0x9e, 0x7a, + 0xba, 0xea, 0xe9, 0x9a, 0x6a, 0x83, 0xcb, 0x93, 0xb4, 0x9d, 0x08, 0x2e, 0x39, 0xb1, 0x93, 0xc9, + 0xf6, 0xde, 0x3c, 0x92, 0x27, 0xcb, 0x49, 0x3b, 0xe4, 0xa7, 0xfb, 0x73, 0x3e, 0xe7, 0xfb, 0xca, + 0x35, 0x59, 0xce, 0xd4, 0x4e, 0x6d, 0xd4, 0x4a, 0x87, 0xf8, 0x3f, 0xd9, 0x60, 0x8f, 0x12, 0xf2, + 0x01, 0x54, 0xa3, 0x38, 0x59, 0xca, 0xb4, 0x61, 0x35, 0x4b, 0x2d, 0xaf, 0xe3, 0xb6, 0x93, 0x49, + 0x7b, 0x88, 0x16, 0x6a, 0x1c, 0xa4, 0x09, 0x65, 0x76, 0xc6, 0xc2, 0x86, 0xdd, 0xb4, 0x5a, 0x5e, + 0x07, 0x10, 0x30, 0x38, 0x63, 0xe1, 0x28, 0x39, 0xdc, 0xa0, 0xca, 0x43, 0x3e, 0x82, 0x6a, 0xca, + 0x97, 0x22, 0x64, 0x8d, 0x92, 0xc2, 0x6c, 0x22, 0x66, 0xac, 0x2c, 0x0a, 0x65, 0xbc, 0xc8, 0x14, + 0xf2, 0x64, 0xd5, 0x28, 0xe7, 0x4c, 0x3d, 0x9e, 0xac, 0x34, 0x13, 0x7a, 0xc8, 0x35, 0xa8, 0x4c, + 0x96, 0xd1, 0x62, 0xda, 0xa8, 0x28, 0x88, 0x87, 0x90, 0x2e, 0x1a, 0x14, 0x46, 0xfb, 0x48, 0x0b, + 0x9c, 0x64, 0x11, 0xc8, 0x19, 0x17, 0xa7, 0x0d, 0xc8, 0x0f, 0x7c, 0x68, 0x6c, 0x34, 0xf3, 0x92, + 0x4f, 0xc0, 0x0b, 0x79, 0x9c, 0x4a, 0x11, 0x44, 0xb1, 0x4c, 0x1b, 0x9e, 0x02, 0xff, 0x1f, 0xc1, + 0x5f, 0x71, 0xf1, 0x98, 0x89, 0x5e, 0xee, 0xa4, 0x45, 0x64, 0xb7, 0x0c, 0x36, 0x4f, 0xfc, 0x1f, + 0x2d, 0x70, 0xd6, 0xac, 0xc4, 0x87, 0xcd, 0x03, 0x11, 0x9e, 0x44, 0x92, 0x85, 0x72, 0x29, 0x58, + 0xc3, 0x6a, 0x5a, 0x2d, 0x97, 0x9e, 0xb3, 0x91, 0x2d, 0xb0, 0x47, 0x63, 0x25, 0x94, 0x4b, 0xed, + 0xd1, 0x98, 0x34, 0xa0, 0x76, 0x1c, 0x88, 0x28, 0x88, 0xa5, 0x52, 0xc6, 0xa5, 0xeb, 0x2d, 0xb9, + 0x0a, 0xee, 0x68, 0x7c, 0xcc, 0x44, 0x1a, 0xf1, 0x58, 0xe9, 0xe1, 0xd2, 0xdc, 0x40, 0x76, 0x00, + 0x46, 0xe3, 0x7b, 0x2c, 0x40, 0xd2, 0xb4, 0x51, 0x69, 0x96, 0x5a, 0x2e, 0x2d, 0x58, 0xfc, 0x6f, + 0xa1, 0xa2, 0xee, 0x88, 0x7c, 0x0e, 0xd5, 0x69, 0x34, 0x67, 0xa9, 0xd4, 0xe9, 0x74, 0x3b, 0xcf, + 0x9e, 0xef, 0x6e, 0xfc, 0xf6, 0x7c, 0xf7, 0x46, 0xa1, 0x19, 0x78, 0xc2, 0xe2, 0x90, 0xc7, 0x32, + 0x88, 0x62, 0x26, 0xd2, 0xfd, 0x39, 0xdf, 0xd3, 0x21, 0xed, 0xbe, 0xfa, 0xa1, 0x86, 0x81, 0x5c, + 0x87, 0x4a, 0x14, 0x4f, 0xd9, 0x99, 0xca, 0xbf, 0xd4, 0xbd, 0x64, 0xa8, 0xbc, 0xd1, 0x52, 0x26, + 0x4b, 0x39, 0x44, 0x17, 0xd5, 0x08, 0x7f, 0x08, 0x55, 0xdd, 0x02, 0xe4, 0x2a, 0x94, 0x4f, 0x99, + 0x0c, 0xd4, 0xf1, 0x5e, 0xc7, 0x41, 0x69, 0x1f, 0x30, 0x19, 0x50, 0x65, 0xc5, 0xee, 0x3a, 0xe5, + 0x4b, 0x94, 0xde, 0xce, 0xbb, 0xeb, 0x01, 0x5a, 0xa8, 0x71, 0xf8, 0xdf, 0x40, 0x19, 0x03, 0x08, + 0x81, 0x72, 0x20, 0xe6, 0xba, 0x0d, 0x5d, 0xaa, 0xd6, 0xa4, 0x0e, 0x25, 0x16, 0x3f, 0x51, 0xb1, + 0x2e, 0xc5, 0x25, 0x5a, 0xc2, 0xaf, 0xa7, 0x46, 0x4c, 0x5c, 0x62, 0xdc, 0x32, 0x65, 0xc2, 0x68, + 0xa8, 0xd6, 0xe4, 0x3a, 0xb8, 0x89, 0xe0, 0x67, 0xab, 0x47, 0x18, 0x5d, 0x29, 0x74, 0x08, 0x1a, + 0x07, 0xf1, 0x13, 0xea, 0x24, 0x66, 0xe5, 0x7f, 0x67, 0x43, 0x45, 0x25, 0x44, 0x5a, 0x58, 0x7e, + 0xb2, 0xd4, 0x4a, 0x96, 0xba, 0xc4, 0x94, 0x0f, 0x4a, 0xe8, 0xac, 0x7a, 0x14, 0x7d, 0x1b, 0x9c, + 0x94, 0x2d, 0x58, 0x28, 0xb9, 0x30, 0x77, 0x9d, 0xed, 0x31, 0x9d, 0x29, 0x5e, 0x87, 0xce, 0x50, + 0xad, 0xc9, 0x4d, 0xa8, 0x72, 0xa5, 0xa1, 0x4a, 0xf2, 0x1f, 0x94, 0x35, 0x10, 0x24, 0x17, 0x2c, + 0x98, 0xf2, 0x78, 0xb1, 0x52, 0xa9, 0x3b, 0x34, 0xdb, 0x93, 0x9b, 0xe0, 0x2a, 0xd5, 0x8e, 0x56, + 0x09, 0x6b, 0x54, 0x9b, 0x56, 0x6b, 0xab, 0x73, 0x21, 0x53, 0x14, 0x8d, 0x34, 0xf7, 0xe3, 0x57, + 0x12, 0x06, 0xe1, 0x09, 0x1b, 0x25, 0xb2, 0x71, 0x39, 0xd7, 0xa0, 0x67, 0x6c, 0x34, 0xf3, 0xfa, + 0x43, 0x70, 0xd6, 0x56, 0xec, 0xe0, 0x61, 0xdf, 0xf4, 0xb6, 0x3d, 0xec, 0x93, 0x3d, 0xa8, 0xa5, + 0x27, 0x81, 0x88, 0xe2, 0xb9, 0x2a, 0x75, 0xab, 0x73, 0x29, 0x23, 0x19, 0x6b, 0x3b, 0x72, 0xad, + 0x31, 0xfe, 0x5d, 0xa8, 0xea, 0x2f, 0x9a, 0x34, 0xa1, 0x94, 0x8a, 0xd0, 0x4c, 0x95, 0xad, 0xf5, + 0xa7, 0xae, 0x87, 0x02, 0x45, 0x57, 0x26, 0x95, 0x9d, 0x4b, 0xe5, 0x53, 0x80, 0x1c, 0xf6, 0xdf, + 0x5c, 0x89, 0xff, 0x83, 0x05, 0xce, 0x7a, 0x18, 0xe1, 0x97, 0x15, 0x4d, 0x59, 0x2c, 0xa3, 0x59, + 0xc4, 0x84, 0xa9, 0xb3, 0x60, 0x21, 0x7b, 0x50, 0x09, 0xa4, 0x14, 0xeb, 0x86, 0x7d, 0xa7, 0x38, + 0xc9, 0xda, 0x07, 0xe8, 0x19, 0xc4, 0x52, 0xac, 0xa8, 0x46, 0x6d, 0xdf, 0x01, 0xc8, 0x8d, 0xd8, + 0x9d, 0x8f, 0xd9, 0xca, 0xb0, 0xe2, 0x92, 0x5c, 0x86, 0xca, 0x93, 0x60, 0xb1, 0x64, 0x26, 0x29, + 0xbd, 0xf9, 0xd4, 0xbe, 0x63, 0xf9, 0x3f, 0xdb, 0x50, 0x33, 0x93, 0x8d, 0xdc, 0x82, 0x9a, 0x9a, + 0x6c, 0x26, 0xa3, 0xd7, 0x57, 0xba, 0x86, 0x90, 0xfd, 0x6c, 0x64, 0x17, 0x72, 0x34, 0x54, 0x7a, + 0x74, 0x9b, 0x1c, 0xf3, 0x01, 0x5e, 0x9a, 0xb2, 0x99, 0x99, 0xcd, 0xea, 0x2a, 0xfa, 0x6c, 0x16, + 0xc5, 0x91, 0x8c, 0x78, 0x4c, 0xd1, 0x45, 0x6e, 0xad, 0xab, 0x2e, 0x2b, 0xc6, 0x2b, 0x45, 0xc6, + 0x57, 0x8b, 0x1e, 0x82, 0x57, 0x38, 0xe6, 0x35, 0x55, 0x7f, 0x58, 0xac, 0xda, 0x1c, 0xa9, 0xe8, + 0xf4, 0xc3, 0x92, 0xab, 0xf0, 0x2f, 0xf4, 0xbb, 0x0d, 0x90, 0x53, 0xbe, 0x79, 0xa7, 0xf8, 0x7f, + 0x59, 0x00, 0xa3, 0x04, 0x47, 0xce, 0x34, 0x50, 0x13, 0x6a, 0x33, 0x9a, 0xc7, 0x5c, 0xb0, 0x47, + 0xea, 0x73, 0x50, 0xf1, 0x0e, 0xf5, 0xb4, 0x4d, 0xb5, 0x39, 0x39, 0x00, 0x6f, 0xca, 0xd2, 0x50, + 0x44, 0x09, 0x0a, 0x66, 0x44, 0xdf, 0xc5, 0x9a, 0x72, 0x9e, 0x76, 0x3f, 0x47, 0x68, 0xad, 0x8a, + 0x31, 0xa4, 0x03, 0x9b, 0xec, 0x2c, 0xe1, 0x42, 0x9a, 0x53, 0xf4, 0x03, 0x78, 0x51, 0x3f, 0xa5, + 0x68, 0x57, 0x27, 0x51, 0x8f, 0xe5, 0x9b, 0xed, 0xbb, 0x50, 0x7f, 0x99, 0xf4, 0xad, 0x04, 0xba, + 0x06, 0x5e, 0x81, 0x1b, 0x81, 0xc7, 0x0a, 0xa8, 0x2b, 0xd4, 0x1b, 0xff, 0x29, 0xbe, 0x70, 0x66, + 0x16, 0x92, 0xf7, 0x01, 0x4e, 0xa4, 0x4c, 0x1e, 0xa9, 0xe1, 0x68, 0x0e, 0x71, 0xd1, 0xa2, 0x10, + 0x64, 0x17, 0x3c, 0xdc, 0xa4, 0xc6, 0xaf, 0x0f, 0x54, 0x11, 0xa9, 0x06, 0xbc, 0x07, 0xee, 0x2c, + 0x0b, 0xd7, 0x03, 0xd0, 0x99, 0xad, 0xa3, 0xdf, 0x05, 0x27, 0xe6, 0xc6, 0xa7, 0x67, 0x75, 0x2d, + 0xe6, 0xca, 0xe5, 0xdf, 0x84, 0xff, 0xbd, 0xf2, 0x1c, 0x93, 0x2b, 0x50, 0x9d, 0x45, 0x0b, 0xa9, + 0x3e, 0x09, 0x1c, 0xff, 0x66, 0xe7, 0xff, 0x6a, 0x01, 0xe4, 0xed, 0x8b, 0x8a, 0x60, 0x6f, 0x23, + 0x66, 0x53, 0xf7, 0xf2, 0x02, 0x9c, 0x53, 0x73, 0x2b, 0xe6, 0xae, 0xae, 0x9e, 0x6f, 0xf9, 0xf6, + 0xfa, 0xd2, 0x94, 0xa6, 0xfa, 0xc9, 0x7c, 0xfa, 0xfb, 0x5b, 0x3d, 0x99, 0xd9, 0x09, 0xdb, 0xf7, + 0xe1, 0xc2, 0x39, 0xba, 0x37, 0xfc, 0x1a, 0xf2, 0xce, 0x29, 0x5c, 0xd9, 0x8d, 0xcf, 0xc0, 0xcd, + 0x46, 0x39, 0x71, 0xa0, 0xdc, 0x1d, 0x7e, 0xd9, 0xaf, 0x6f, 0x10, 0x80, 0xea, 0x78, 0xd0, 0xa3, + 0x83, 0xa3, 0xba, 0x45, 0x6a, 0x50, 0x1a, 0x8f, 0x0f, 0xeb, 0x36, 0x71, 0xa1, 0xd2, 0x3b, 0xe8, + 0x1d, 0x0e, 0xea, 0x25, 0x5c, 0x1e, 0x3d, 0x78, 0x78, 0x6f, 0x5c, 0x2f, 0xdf, 0xb8, 0x0d, 0x17, + 0x5f, 0x9a, 0xcd, 0x2a, 0xfa, 0xf0, 0x80, 0x0e, 0x90, 0xc9, 0x83, 0xda, 0x43, 0x3a, 0x3c, 0x3e, + 0x38, 0x1a, 0xd4, 0x2d, 0x74, 0x7c, 0x31, 0xea, 0xdd, 0x1f, 0xf4, 0xeb, 0x76, 0xb7, 0xfe, 0xec, + 0xc5, 0x8e, 0xf5, 0xcb, 0x8b, 0x1d, 0xeb, 0x8f, 0x17, 0x3b, 0xd6, 0xf7, 0x7f, 0xee, 0x6c, 0x4c, + 0xaa, 0xea, 0x6f, 0xe2, 0xc7, 0x7f, 0x07, 0x00, 0x00, 0xff, 0xff, 0x42, 0x80, 0x11, 0x1b, 0x66, + 0x0a, 0x00, 0x00, +} diff --git a/vendor/github.com/moby/buildkit/solver/pb/ops.proto b/vendor/github.com/moby/buildkit/solver/pb/ops.proto new file mode 100644 index 0000000000000..db44c5a4e0f2d --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/pb/ops.proto @@ -0,0 +1,165 @@ +syntax = "proto3"; + +// Package pb provides the protobuf definition of LLB: low-level builder instruction. +// LLB is DAG-structured; Op represents a vertex, and Definition represents a graph. +package pb; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; + +// Op represents a vertex of the LLB DAG. +message Op { + // inputs is a set of input edges. + repeated Input inputs = 1; + oneof op { + ExecOp exec = 2; + SourceOp source = 3; + CopyOp copy = 4; + BuildOp build = 5; + } + Platform platform = 10; + WorkerConstraints constraints = 11; +} + +// Platform is github.com/opencontainers/image-spec/specs-go/v1.Platform +message Platform { + string Architecture = 1; + string OS = 2; + string Variant = 3; + string OSVersion = 4; // unused + repeated string OSFeatures = 5; // unused +} + +// Input represents an input edge for an Op. +message Input { + // digest of the marshaled input Op + string digest = 1 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false]; + // output index of the input Op + int64 index = 2 [(gogoproto.customtype) = "OutputIndex", (gogoproto.nullable) = false]; +} + +// ExecOp executes a command in a container. +message ExecOp { + Meta meta = 1; + repeated Mount mounts = 2; +} + +// Meta is a set of arguments for ExecOp. +// Meta is unrelated to LLB metadata. +// FIXME: rename (ExecContext? ExecArgs?) +message Meta { + repeated string args = 1; + repeated string env = 2; + string cwd = 3; + string user = 4; + ProxyEnv proxy_env = 5; +} + +// Mount specifies how to mount an input Op as a filesystem. +message Mount { + int64 input = 1 [(gogoproto.customtype) = "InputIndex", (gogoproto.nullable) = false]; + string selector = 2; + string dest = 3; + int64 output = 4 [(gogoproto.customtype) = "OutputIndex", (gogoproto.nullable) = false]; + bool readonly = 5; + MountType mountType = 6; + CacheOpt cacheOpt = 20; +} + +// MountType defines a type of a mount from a supported set +enum MountType { + BIND = 0; + SECRET = 1; + SSH = 2; + CACHE = 3; + TMPFS = 4; +} + +// CacheOpt defines options specific to cache mounts +message CacheOpt { + // ID is an optional namespace for the mount + string ID = 1; + // Sharing is the sharing mode for the mount + CacheSharingOpt sharing = 2; +} + +// CacheSharingOpt defines different sharing modes for cache mount +enum CacheSharingOpt { + // SHARED cache mount can be used concurrently by multiple writers + SHARED = 0; + // PRIVATE creates a new mount if there are multiple writers + PRIVATE = 1; + // LOCKED pauses second writer until first one releases the mount + LOCKED = 2; +} + +// CopyOp copies files across Ops. +message CopyOp { + repeated CopySource src = 1; + string dest = 2; +} + +// CopySource specifies a source for CopyOp. +message CopySource { + int64 input = 1 [(gogoproto.customtype) = "InputIndex", (gogoproto.nullable) = false]; + string selector = 2; +} + +// SourceOp specifies a source such as build contexts and images. +message SourceOp { + // TODO: use source type or any type instead of URL protocol. + // identifier e.g. local://, docker-image://, git://, https://... + string identifier = 1; + // attrs are defined in attr.go + map attrs = 2; +} + +// BuildOp is used for nested build invocation. +// BuildOp is experimental and can break without backwards compatibility +message BuildOp { + int64 builder = 1 [(gogoproto.customtype) = "InputIndex", (gogoproto.nullable) = false]; + map inputs = 2; + Definition def = 3; + map attrs = 4; + // outputs +} + +// BuildInput is used for BuildOp. +message BuildInput { + int64 input = 1 [(gogoproto.customtype) = "InputIndex", (gogoproto.nullable) = false]; +} + +// OpMetadata is a per-vertex metadata entry, which can be defined for arbitrary Op vertex and overridable on the run time. +message OpMetadata { + // ignore_cache specifies to ignore the cache for this Op. + bool ignore_cache = 1; + // Description can be used for keeping any text fields that builder doesn't parse + map description = 2; + // index 3 reserved for WorkerConstraint in previous versions + // WorkerConstraint worker_constraint = 3; + ExportCache export_cache = 4; +} + +message ExportCache { + bool Value = 1; +} + +message ProxyEnv { + string http_proxy = 1; + string https_proxy = 2; + string ftp_proxy = 3; + string no_proxy = 4; +} + +// WorkerConstraints defines conditions for the worker +message WorkerConstraints { + repeated string filter = 1; // containerd-style filter +} + +// Definition is the LLB definition structure with per-vertex metadata entries +message Definition { + // def is a list of marshaled Op messages + repeated bytes def = 1; + // metadata contains metadata for the each of the Op messages. + // A key must be an LLB op digest string. Currently, empty string is not expected as a key, but it may change in the future. + map metadata = 2 [(gogoproto.castkey) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false]; +} diff --git a/vendor/github.com/moby/buildkit/solver/pb/platform.go b/vendor/github.com/moby/buildkit/solver/pb/platform.go new file mode 100644 index 0000000000000..a434aa7168821 --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/pb/platform.go @@ -0,0 +1,41 @@ +package pb + +import ( + specs "github.com/opencontainers/image-spec/specs-go/v1" +) + +func (p *Platform) Spec() specs.Platform { + return specs.Platform{ + OS: p.OS, + Architecture: p.Architecture, + Variant: p.Variant, + OSVersion: p.OSVersion, + OSFeatures: p.OSFeatures, + } +} + +func PlatformFromSpec(p specs.Platform) Platform { + return Platform{ + OS: p.OS, + Architecture: p.Architecture, + Variant: p.Variant, + OSVersion: p.OSVersion, + OSFeatures: p.OSFeatures, + } +} + +func ToSpecPlatforms(p []Platform) []specs.Platform { + out := make([]specs.Platform, 0, len(p)) + for _, pp := range p { + out = append(out, pp.Spec()) + } + return out +} + +func PlatformsFromSpec(p []specs.Platform) []Platform { + out := make([]Platform, 0, len(p)) + for _, pp := range p { + out = append(out, PlatformFromSpec(pp)) + } + return out +} diff --git a/vendor/github.com/moby/buildkit/solver/progress.go b/vendor/github.com/moby/buildkit/solver/progress.go new file mode 100644 index 0000000000000..14f3f5e0199c3 --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/progress.go @@ -0,0 +1,109 @@ +package solver + +import ( + "context" + "io" + "time" + + "github.com/moby/buildkit/client" + "github.com/moby/buildkit/util/progress" + digest "github.com/opencontainers/go-digest" + "github.com/sirupsen/logrus" +) + +func (j *Job) Status(ctx context.Context, ch chan *client.SolveStatus) error { + vs := &vertexStream{cache: map[digest.Digest]*client.Vertex{}} + pr := j.pr.Reader(ctx) + defer func() { + if enc := vs.encore(); len(enc) > 0 { + ch <- &client.SolveStatus{Vertexes: enc} + } + close(ch) + }() + + for { + p, err := pr.Read(ctx) + if err != nil { + if err == io.EOF { + return nil + } + return err + } + ss := &client.SolveStatus{} + for _, p := range p { + switch v := p.Sys.(type) { + case client.Vertex: + ss.Vertexes = append(ss.Vertexes, vs.append(v)...) + + case progress.Status: + vtx, ok := p.Meta("vertex") + if !ok { + logrus.Warnf("progress %s status without vertex info", p.ID) + continue + } + vs := &client.VertexStatus{ + ID: p.ID, + Vertex: vtx.(digest.Digest), + Name: v.Action, + Total: int64(v.Total), + Current: int64(v.Current), + Timestamp: p.Timestamp, + Started: v.Started, + Completed: v.Completed, + } + ss.Statuses = append(ss.Statuses, vs) + case client.VertexLog: + vtx, ok := p.Meta("vertex") + if !ok { + logrus.Warnf("progress %s log without vertex info", p.ID) + continue + } + v.Vertex = vtx.(digest.Digest) + v.Timestamp = p.Timestamp + ss.Logs = append(ss.Logs, &v) + } + } + select { + case <-ctx.Done(): + return ctx.Err() + case ch <- ss: + } + } +} + +type vertexStream struct { + cache map[digest.Digest]*client.Vertex +} + +func (vs *vertexStream) append(v client.Vertex) []*client.Vertex { + var out []*client.Vertex + vs.cache[v.Digest] = &v + if v.Started != nil { + for _, inp := range v.Inputs { + if inpv, ok := vs.cache[inp]; ok { + if !inpv.Cached && inpv.Completed == nil { + inpv.Cached = true + inpv.Started = v.Started + inpv.Completed = v.Started + out = append(out, vs.append(*inpv)...) + delete(vs.cache, inp) + } + } + } + } + vcopy := v + return append(out, &vcopy) +} + +func (vs *vertexStream) encore() []*client.Vertex { + var out []*client.Vertex + for _, v := range vs.cache { + if v.Started != nil && v.Completed == nil { + now := time.Now() + v.Completed = &now + v.Error = context.Canceled.Error() + out = append(out, v) + } + } + return out +} diff --git a/vendor/github.com/moby/buildkit/solver/result.go b/vendor/github.com/moby/buildkit/solver/result.go new file mode 100644 index 0000000000000..641cf14f039a9 --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/result.go @@ -0,0 +1,105 @@ +package solver + +import ( + "context" + "sync" + "sync/atomic" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// SharedResult is a result that can be cloned +type SharedResult struct { + mu sync.Mutex + main Result +} + +func NewSharedResult(main Result) *SharedResult { + return &SharedResult{main: main} +} + +func (r *SharedResult) Clone() Result { + r.mu.Lock() + defer r.mu.Unlock() + + r1, r2 := dup(r.main) + r.main = r1 + return r2 +} + +func (r *SharedResult) Release(ctx context.Context) error { + r.mu.Lock() + defer r.mu.Unlock() + return r.main.Release(ctx) +} + +func dup(res Result) (Result, Result) { + sem := int64(0) + return &splitResult{Result: res, sem: &sem}, &splitResult{Result: res, sem: &sem} +} + +type splitResult struct { + Result + released int64 + sem *int64 +} + +func (r *splitResult) Release(ctx context.Context) error { + if atomic.AddInt64(&r.released, 1) > 1 { + err := errors.Errorf("releasing already released reference") + logrus.Error(err) + return err + } + if atomic.AddInt64(r.sem, 1) == 2 { + return r.Result.Release(ctx) + } + return nil +} + +// NewCachedResult combines a result and cache key into cached result +func NewCachedResult(res Result, k ExportableCacheKey) CachedResult { + return &cachedResult{res, k} +} + +type cachedResult struct { + Result + k ExportableCacheKey +} + +func (cr *cachedResult) CacheKey() ExportableCacheKey { + return cr.k +} + +func NewSharedCachedResult(res CachedResult) *SharedCachedResult { + return &SharedCachedResult{ + SharedResult: NewSharedResult(res), + CachedResult: res, + } +} + +func (r *SharedCachedResult) Clone() CachedResult { + return &clonedCachedResult{Result: r.SharedResult.Clone(), cr: r.CachedResult} +} + +func (r *SharedCachedResult) Release(ctx context.Context) error { + return r.SharedResult.Release(ctx) +} + +type clonedCachedResult struct { + Result + cr CachedResult +} + +func (r *clonedCachedResult) ID() string { + return r.Result.ID() +} + +func (cr *clonedCachedResult) CacheKey() ExportableCacheKey { + return cr.cr.CacheKey() +} + +type SharedCachedResult struct { + *SharedResult + CachedResult +} diff --git a/vendor/github.com/moby/buildkit/solver/scheduler.go b/vendor/github.com/moby/buildkit/solver/scheduler.go new file mode 100644 index 0000000000000..7336f619e0eb8 --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/scheduler.go @@ -0,0 +1,396 @@ +package solver + +import ( + "context" + "sync" + + "github.com/moby/buildkit/solver/internal/pipe" + "github.com/moby/buildkit/util/cond" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +const debugScheduler = false // TODO: replace with logs in build trace + +func newScheduler(ef edgeFactory) *scheduler { + s := &scheduler{ + waitq: map[*edge]struct{}{}, + incoming: map[*edge][]*edgePipe{}, + outgoing: map[*edge][]*edgePipe{}, + + stopped: make(chan struct{}), + closed: make(chan struct{}), + + ef: ef, + } + s.cond = cond.NewStatefulCond(&s.mu) + + go s.loop() + + return s +} + +type dispatcher struct { + next *dispatcher + e *edge +} + +type scheduler struct { + cond *cond.StatefulCond + mu sync.Mutex + muQ sync.Mutex + + ef edgeFactory + + waitq map[*edge]struct{} + next *dispatcher + last *dispatcher + stopped chan struct{} + stoppedOnce sync.Once + closed chan struct{} + + incoming map[*edge][]*edgePipe + outgoing map[*edge][]*edgePipe +} + +func (s *scheduler) Stop() { + s.stoppedOnce.Do(func() { + close(s.stopped) + }) + <-s.closed +} + +func (s *scheduler) loop() { + defer func() { + close(s.closed) + }() + + go func() { + <-s.stopped + s.mu.Lock() + s.cond.Signal() + s.mu.Unlock() + }() + + s.mu.Lock() + for { + select { + case <-s.stopped: + s.mu.Unlock() + return + default: + } + s.muQ.Lock() + l := s.next + if l != nil { + if l == s.last { + s.last = nil + } + s.next = l.next + delete(s.waitq, l.e) + } + s.muQ.Unlock() + if l == nil { + s.cond.Wait() + continue + } + s.dispatch(l.e) + } +} + +// dispatch schedules an edge to be processed +func (s *scheduler) dispatch(e *edge) { + inc := make([]pipe.Sender, len(s.incoming[e])) + for i, p := range s.incoming[e] { + inc[i] = p.Sender + } + out := make([]pipe.Receiver, len(s.outgoing[e])) + for i, p := range s.outgoing[e] { + out[i] = p.Receiver + } + + e.hasActiveOutgoing = false + updates := []pipe.Receiver{} + for _, p := range out { + if ok := p.Receive(); ok { + updates = append(updates, p) + } + if !p.Status().Completed { + e.hasActiveOutgoing = true + } + } + + // unpark the edge + debugSchedulerPreUnpark(e, inc, updates, out) + e.unpark(inc, updates, out, &pipeFactory{s: s, e: e}) + debugSchedulerPostUnpark(e, inc) + + // set up new requests that didn't complete/were added by this run + openIncoming := make([]*edgePipe, 0, len(inc)) + for _, r := range s.incoming[e] { + if !r.Sender.Status().Completed { + openIncoming = append(openIncoming, r) + } + } + if len(openIncoming) > 0 { + s.incoming[e] = openIncoming + } else { + delete(s.incoming, e) + } + + openOutgoing := make([]*edgePipe, 0, len(out)) + for _, r := range s.outgoing[e] { + if !r.Receiver.Status().Completed { + openOutgoing = append(openOutgoing, r) + } + } + if len(openOutgoing) > 0 { + s.outgoing[e] = openOutgoing + } else { + delete(s.outgoing, e) + } + + // if keys changed there might be possiblity for merge with other edge + if e.keysDidChange { + if k := e.currentIndexKey(); k != nil { + // skip this if not at least 1 key per dep + origEdge := e.index.LoadOrStore(k, e) + if origEdge != nil { + logrus.Debugf("merging edge %s to %s\n", e.edge.Vertex.Name(), origEdge.edge.Vertex.Name()) + if s.mergeTo(origEdge, e) { + s.ef.setEdge(e.edge, origEdge) + } + } + } + e.keysDidChange = false + } + + // validation to avoid deadlocks/resource leaks: + // TODO: if these start showing up in error reports they can be changed + // to error the edge instead. They can only appear from algorithm bugs in + // unpark(), not for any external input. + if len(openIncoming) > 0 && len(openOutgoing) == 0 { + panic("invalid dispatch: return leaving incoming open") + } + if len(openIncoming) == 0 && len(openOutgoing) > 0 { + panic("invalid dispatch: return leaving outgoing open") + } +} + +// signal notifies that an edge needs to be processed again +func (s *scheduler) signal(e *edge) { + s.muQ.Lock() + if _, ok := s.waitq[e]; !ok { + d := &dispatcher{e: e} + if s.last == nil { + s.next = d + } else { + s.last.next = d + } + s.last = d + s.waitq[e] = struct{}{} + s.cond.Signal() + } + s.muQ.Unlock() +} + +// build evaluates edge into a result +func (s *scheduler) build(ctx context.Context, edge Edge) (CachedResult, error) { + s.mu.Lock() + e := s.ef.getEdge(edge) + if e == nil { + s.mu.Unlock() + return nil, errors.Errorf("invalid request %v for build", edge) + } + + wait := make(chan struct{}) + + var p *pipe.Pipe + p = s.newPipe(e, nil, pipe.Request{Payload: &edgeRequest{desiredState: edgeStatusComplete}}) + p.OnSendCompletion = func() { + p.Receiver.Receive() + if p.Receiver.Status().Completed { + close(wait) + } + } + s.mu.Unlock() + + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + go func() { + <-ctx.Done() + p.Receiver.Cancel() + }() + + <-wait + + if err := p.Receiver.Status().Err; err != nil { + return nil, err + } + return p.Receiver.Status().Value.(*edgeState).result.Clone(), nil +} + +// newPipe creates a new request pipe between two edges +func (s *scheduler) newPipe(target, from *edge, req pipe.Request) *pipe.Pipe { + p := &edgePipe{ + Pipe: pipe.New(req), + Target: target, + From: from, + } + + s.signal(target) + if from != nil { + p.OnSendCompletion = func() { + p.mu.Lock() + defer p.mu.Unlock() + s.signal(p.From) + } + s.outgoing[from] = append(s.outgoing[from], p) + } + s.incoming[target] = append(s.incoming[target], p) + p.OnReceiveCompletion = func() { + p.mu.Lock() + defer p.mu.Unlock() + s.signal(p.Target) + } + return p.Pipe +} + +// newRequestWithFunc creates a new request pipe that invokes a async function +func (s *scheduler) newRequestWithFunc(e *edge, f func(context.Context) (interface{}, error)) pipe.Receiver { + pp, start := pipe.NewWithFunction(f) + p := &edgePipe{ + Pipe: pp, + From: e, + } + p.OnSendCompletion = func() { + p.mu.Lock() + defer p.mu.Unlock() + s.signal(p.From) + } + s.outgoing[e] = append(s.outgoing[e], p) + go start() + return p.Receiver +} + +// mergeTo merges the state from one edge to another. source edge is discarded. +func (s *scheduler) mergeTo(target, src *edge) bool { + if !target.edge.Vertex.Options().IgnoreCache && src.edge.Vertex.Options().IgnoreCache { + return false + } + for _, inc := range s.incoming[src] { + inc.mu.Lock() + inc.Target = target + s.incoming[target] = append(s.incoming[target], inc) + inc.mu.Unlock() + } + + for _, out := range s.outgoing[src] { + out.mu.Lock() + out.From = target + s.outgoing[target] = append(s.outgoing[target], out) + out.mu.Unlock() + out.Receiver.Cancel() + } + + delete(s.incoming, src) + delete(s.outgoing, src) + s.signal(target) + + for i, d := range src.deps { + for _, k := range d.keys { + target.secondaryExporters = append(target.secondaryExporters, expDep{i, CacheKeyWithSelector{CacheKey: k, Selector: src.cacheMap.Deps[i].Selector}}) + } + if d.slowCacheKey != nil { + target.secondaryExporters = append(target.secondaryExporters, expDep{i, CacheKeyWithSelector{CacheKey: *d.slowCacheKey}}) + } + if d.result != nil { + target.secondaryExporters = append(target.secondaryExporters, expDep{i, CacheKeyWithSelector{CacheKey: d.result.CacheKey(), Selector: src.cacheMap.Deps[i].Selector}}) + } + } + + // TODO(tonistiigi): merge cache providers + + return true +} + +// edgeFactory allows access to the edges from a shared graph +type edgeFactory interface { + getEdge(Edge) *edge + setEdge(Edge, *edge) +} + +type pipeFactory struct { + e *edge + s *scheduler +} + +func (pf *pipeFactory) NewInputRequest(ee Edge, req *edgeRequest) pipe.Receiver { + target := pf.s.ef.getEdge(ee) + if target == nil { + panic("failed to get edge") // TODO: return errored pipe + } + p := pf.s.newPipe(target, pf.e, pipe.Request{Payload: req}) + if debugScheduler { + logrus.Debugf("> newPipe %s %p desiredState=%s", ee.Vertex.Name(), p, req.desiredState) + } + return p.Receiver +} + +func (pf *pipeFactory) NewFuncRequest(f func(context.Context) (interface{}, error)) pipe.Receiver { + p := pf.s.newRequestWithFunc(pf.e, f) + if debugScheduler { + logrus.Debugf("> newFunc %p", p) + } + return p +} + +func debugSchedulerPreUnpark(e *edge, inc []pipe.Sender, updates, allPipes []pipe.Receiver) { + if !debugScheduler { + return + } + logrus.Debugf(">> unpark %s req=%d upt=%d out=%d state=%s %s", e.edge.Vertex.Name(), len(inc), len(updates), len(allPipes), e.state, e.edge.Vertex.Digest()) + + for i, dep := range e.deps { + des := edgeStatusInitial + if dep.req != nil { + des = dep.req.Request().(*edgeRequest).desiredState + } + logrus.Debugf(":: dep%d %s state=%s des=%s keys=%s hasslowcache=%v", i, e.edge.Vertex.Inputs()[i].Vertex.Name(), dep.state, des, len(dep.keys), e.slowCacheFunc(dep) != nil) + } + + for i, in := range inc { + req := in.Request() + logrus.Debugf("> incoming-%d: %p dstate=%s canceled=%v", i, in, req.Payload.(*edgeRequest).desiredState, req.Canceled) + } + + for i, up := range updates { + if up == e.cacheMapReq { + logrus.Debugf("> update-%d: %p cacheMapReq complete=%v", i, up, up.Status().Completed) + } else if up == e.execReq { + logrus.Debugf("> update-%d: %p execReq complete=%v", i, up, up.Status().Completed) + } else { + st, ok := up.Status().Value.(*edgeState) + if ok { + index := -1 + if dep, ok := e.depRequests[up]; ok { + index = int(dep.index) + } + logrus.Debugf("> update-%d: %p input-%d keys=%d state=%s", i, up, index, len(st.keys), st.state) + } else { + logrus.Debugf("> update-%d: unknown", i) + } + } + } +} + +func debugSchedulerPostUnpark(e *edge, inc []pipe.Sender) { + if !debugScheduler { + return + } + for i, in := range inc { + logrus.Debugf("< incoming-%d: %p completed=%v", i, in, in.Status().Completed) + } + logrus.Debugf("<< unpark %s\n", e.edge.Vertex.Name()) +} diff --git a/vendor/github.com/moby/buildkit/solver/types.go b/vendor/github.com/moby/buildkit/solver/types.go new file mode 100644 index 0000000000000..5de320b40cbaf --- /dev/null +++ b/vendor/github.com/moby/buildkit/solver/types.go @@ -0,0 +1,168 @@ +package solver + +import ( + "context" + "time" + + "github.com/containerd/containerd/content" + digest "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" +) + +// Vertex is one node in the build graph +type Vertex interface { + // Digest is a content-addressable vertex identifier + Digest() digest.Digest + // Sys returns an internal value that is used to execute the vertex. Usually + // this is capured by the operation resolver method during solve. + Sys() interface{} + Options() VertexOptions + // Array of edges current vertex depends on. + Inputs() []Edge + Name() string +} + +// Index is a index value for output edge +type Index int + +// Edge is a path to a specific output of the vertex +type Edge struct { + Index Index + Vertex Vertex +} + +// VertexOptions has optional metadata for the vertex that is not contained in digest +type VertexOptions struct { + IgnoreCache bool + CacheSources []CacheManager + Description map[string]string // text values with no special meaning for solver + ExportCache *bool + // WorkerConstraint +} + +// Result is an abstract return value for a solve +type Result interface { + ID() string + Release(context.Context) error + Sys() interface{} +} + +// CachedResult is a result connected with its cache key +type CachedResult interface { + Result + CacheKey() ExportableCacheKey +} + +// CacheExportMode is the type for setting cache exporting modes +type CacheExportMode int + +const ( + // CacheExportModeMin exports a topmost allowed vertex and its dependencies + // that already have transferable layers + CacheExportModeMin CacheExportMode = iota + // CacheExportModeMax exports all possible non-root vertexes + CacheExportModeMax + // CacheExportModeRemoteOnly only exports vertexes that already have + // transferable layers + CacheExportModeRemoteOnly +) + +// CacheExportOpt defines options for exporting build cache +type CacheExportOpt struct { + // Convert can convert a build result to transferable object + Convert func(context.Context, Result) (*Remote, error) + // Mode defines a cache export algorithm + Mode CacheExportMode +} + +// CacheExporter can export the artifacts of the build chain +type CacheExporter interface { + ExportTo(ctx context.Context, t CacheExporterTarget, opt CacheExportOpt) ([]CacheExporterRecord, error) +} + +// CacheExporterTarget defines object capable of receiving exports +type CacheExporterTarget interface { + Add(dgst digest.Digest) CacheExporterRecord + Visit(interface{}) + Visited(interface{}) bool +} + +// CacheExporterRecord is a single object being exported +type CacheExporterRecord interface { + AddResult(createdAt time.Time, result *Remote) + LinkFrom(src CacheExporterRecord, index int, selector string) +} + +// Remote is a descriptor or a list of stacked descriptors that can be pulled +// from a content provider +// TODO: add closer to keep referenced data from getting deleted +type Remote struct { + Descriptors []ocispec.Descriptor + Provider content.Provider +} + +// CacheLink is a link between two cache records +type CacheLink struct { + Source digest.Digest `json:",omitempty"` + Input Index `json:",omitempty"` + Output Index `json:",omitempty"` + Base digest.Digest `json:",omitempty"` + Selector digest.Digest `json:",omitempty"` +} + +// Op is an implementation for running a vertex +type Op interface { + // CacheMap returns structure describing how the operation is cached. + // Currently only roots are allowed to return multiple cache maps per op. + CacheMap(context.Context, int) (*CacheMap, bool, error) + // Exec runs an operation given results from previous operations. + Exec(ctx context.Context, inputs []Result) (outputs []Result, err error) +} + +type ResultBasedCacheFunc func(context.Context, Result) (digest.Digest, error) + +type CacheMap struct { + // Digest is a base digest for operation that needs to be combined with + // inputs cache or selectors for dependencies. + Digest digest.Digest + Deps []struct { + // Optional digest that is merged with the cache key of the input + Selector digest.Digest + // Optional function that returns a digest for the input based on its + // return value + ComputeDigestFunc ResultBasedCacheFunc + } +} + +// ExportableCacheKey is a cache key connected with an exporter that can export +// a chain of cacherecords pointing to that key +type ExportableCacheKey struct { + *CacheKey + Exporter CacheExporter +} + +// CacheRecord is an identifier for loading in cache +type CacheRecord struct { + ID string + Size int + CreatedAt time.Time + Priority int + + cacheManager *cacheManager + key *CacheKey +} + +// CacheManager implements build cache backend +type CacheManager interface { + // ID is used to identify cache providers that are backed by same source + // to avoid duplicate calls to the same provider + ID() string + // Query searches for cache paths from one cache key to the output of a + // possible match. + Query(inp []CacheKeyWithSelector, inputIndex Index, dgst digest.Digest, outputIndex Index) ([]*CacheKey, error) + Records(ck *CacheKey) ([]*CacheRecord, error) + // Load pulls and returns the cached result + Load(ctx context.Context, rec *CacheRecord) (Result, error) + // Save saves a result based on a cache key + Save(key *CacheKey, s Result) (*ExportableCacheKey, error) +} diff --git a/vendor/github.com/moby/buildkit/source/git/gitsource.go b/vendor/github.com/moby/buildkit/source/git/gitsource.go new file mode 100644 index 0000000000000..e9b1e8c1af0c2 --- /dev/null +++ b/vendor/github.com/moby/buildkit/source/git/gitsource.go @@ -0,0 +1,406 @@ +package git + +import ( + "bytes" + "context" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "regexp" + "strings" + + "github.com/boltdb/bolt" + "github.com/docker/docker/pkg/locker" + "github.com/moby/buildkit/cache" + "github.com/moby/buildkit/cache/metadata" + "github.com/moby/buildkit/identity" + "github.com/moby/buildkit/snapshot" + "github.com/moby/buildkit/source" + "github.com/moby/buildkit/util/progress/logs" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +var validHex = regexp.MustCompile(`^[a-f0-9]{40}$`) + +type Opt struct { + CacheAccessor cache.Accessor + MetadataStore *metadata.Store +} + +type gitSource struct { + md *metadata.Store + cache cache.Accessor + locker *locker.Locker +} + +func NewSource(opt Opt) (source.Source, error) { + gs := &gitSource{ + md: opt.MetadataStore, + cache: opt.CacheAccessor, + locker: locker.New(), + } + + if err := exec.Command("git", "version").Run(); err != nil { + return nil, errors.Wrap(err, "failed to find git binary") + } + + return gs, nil +} + +func (gs *gitSource) ID() string { + return source.GitScheme +} + +// needs to be called with repo lock +func (gs *gitSource) mountRemote(ctx context.Context, remote string) (target string, release func(), retErr error) { + remoteKey := "git-remote::" + remote + + sis, err := gs.md.Search(remoteKey) + if err != nil { + return "", nil, errors.Wrapf(err, "failed to search metadata for %s", remote) + } + + var remoteRef cache.MutableRef + for _, si := range sis { + remoteRef, err = gs.cache.GetMutable(ctx, si.ID()) + if err != nil { + if cache.IsLocked(err) { + // should never really happen as no other function should access this metadata, but lets be graceful + logrus.Warnf("mutable ref for %s %s was locked: %v", remote, si.ID(), err) + continue + } + return "", nil, errors.Wrapf(err, "failed to get mutable ref for %s", remote) + } + break + } + + initializeRepo := false + if remoteRef == nil { + remoteRef, err = gs.cache.New(ctx, nil, cache.CachePolicyRetain, cache.WithDescription(fmt.Sprintf("shared git repo for %s", remote))) + if err != nil { + return "", nil, errors.Wrapf(err, "failed to create new mutable for %s", remote) + } + initializeRepo = true + } + + releaseRemoteRef := func() { + remoteRef.Release(context.TODO()) + } + + defer func() { + if retErr != nil && remoteRef != nil { + releaseRemoteRef() + } + }() + + mount, err := remoteRef.Mount(ctx, false) + if err != nil { + return "", nil, err + } + + lm := snapshot.LocalMounter(mount) + dir, err := lm.Mount() + if err != nil { + return "", nil, err + } + + defer func() { + if retErr != nil { + lm.Unmount() + } + }() + + if initializeRepo { + if _, err := gitWithinDir(ctx, dir, "", "init", "--bare"); err != nil { + return "", nil, errors.Wrapf(err, "failed to init repo at %s", dir) + } + + if _, err := gitWithinDir(ctx, dir, "", "remote", "add", "origin", remote); err != nil { + return "", nil, errors.Wrapf(err, "failed add origin repo at %s", dir) + } + + // same new remote metadata + si, _ := gs.md.Get(remoteRef.ID()) + v, err := metadata.NewValue(remoteKey) + v.Index = remoteKey + if err != nil { + return "", nil, err + } + + if err := si.Update(func(b *bolt.Bucket) error { + return si.SetValue(b, "git-remote", v) + }); err != nil { + return "", nil, err + } + } + return dir, func() { + lm.Unmount() + releaseRemoteRef() + }, nil +} + +type gitSourceHandler struct { + *gitSource + src source.GitIdentifier + cacheKey string +} + +func (gs *gitSource) Resolve(ctx context.Context, id source.Identifier) (source.SourceInstance, error) { + gitIdentifier, ok := id.(*source.GitIdentifier) + if !ok { + return nil, errors.Errorf("invalid git identifier %v", id) + } + + return &gitSourceHandler{ + src: *gitIdentifier, + gitSource: gs, + }, nil +} + +func (gs *gitSourceHandler) CacheKey(ctx context.Context, index int) (string, bool, error) { + remote := gs.src.Remote + ref := gs.src.Ref + if ref == "" { + ref = "master" + } + gs.locker.Lock(remote) + defer gs.locker.Unlock(remote) + + if isCommitSHA(ref) { + gs.cacheKey = ref + return ref, true, nil + } + + gitDir, unmountGitDir, err := gs.mountRemote(ctx, remote) + if err != nil { + return "", false, err + } + defer unmountGitDir() + + // TODO: should we assume that remote tag is immutable? add a timer? + + buf, err := gitWithinDir(ctx, gitDir, "", "ls-remote", "origin", ref) + if err != nil { + return "", false, errors.Wrapf(err, "failed to fetch remote %s", remote) + } + out := buf.String() + idx := strings.Index(out, "\t") + if idx == -1 { + return "", false, errors.Errorf("failed to find commit SHA from output: %s", string(out)) + } + + sha := string(out[:idx]) + if !isCommitSHA(sha) { + return "", false, errors.Errorf("invalid commit sha %q", sha) + } + gs.cacheKey = sha + return sha, true, nil +} + +func (gs *gitSourceHandler) Snapshot(ctx context.Context) (out cache.ImmutableRef, retErr error) { + ref := gs.src.Ref + if ref == "" { + ref = "master" + } + + cacheKey := gs.cacheKey + if cacheKey == "" { + var err error + cacheKey, _, err = gs.CacheKey(ctx, 0) + if err != nil { + return nil, err + } + } + + snapshotKey := "git-snapshot::" + cacheKey + ":" + gs.src.Subdir + gs.locker.Lock(snapshotKey) + defer gs.locker.Unlock(snapshotKey) + + sis, err := gs.md.Search(snapshotKey) + if err != nil { + return nil, errors.Wrapf(err, "failed to search metadata for %s", snapshotKey) + } + if len(sis) > 0 { + return gs.cache.Get(ctx, sis[0].ID()) + } + + gs.locker.Lock(gs.src.Remote) + defer gs.locker.Unlock(gs.src.Remote) + gitDir, unmountGitDir, err := gs.mountRemote(ctx, gs.src.Remote) + if err != nil { + return nil, err + } + defer unmountGitDir() + + doFetch := true + if isCommitSHA(ref) { + // skip fetch if commit already exists + if _, err := gitWithinDir(ctx, gitDir, "", "cat-file", "-e", ref+"^{commit}"); err == nil { + doFetch = false + } + } + + if doFetch { + args := []string{"fetch"} + if !isCommitSHA(ref) { // TODO: find a branch from ls-remote? + args = append(args, "--depth=1", "--no-tags") + } else { + if _, err := os.Lstat(filepath.Join(gitDir, "shallow")); err == nil { + args = append(args, "--unshallow") + } + } + args = append(args, "origin") + if !isCommitSHA(ref) { + args = append(args, ref+":tags/"+ref) + // local refs are needed so they would be advertised on next fetches + // TODO: is there a better way to do this? + } + if _, err := gitWithinDir(ctx, gitDir, "", args...); err != nil { + return nil, errors.Wrapf(err, "failed to fetch remote %s", gs.src.Remote) + } + } + + checkoutRef, err := gs.cache.New(ctx, nil, cache.WithDescription(fmt.Sprintf("git snapshot for %s#%s", gs.src.Remote, ref))) + if err != nil { + return nil, errors.Wrapf(err, "failed to create new mutable for %s", gs.src.Remote) + } + + defer func() { + if retErr != nil && checkoutRef != nil { + checkoutRef.Release(context.TODO()) + } + }() + + mount, err := checkoutRef.Mount(ctx, false) + if err != nil { + return nil, err + } + lm := snapshot.LocalMounter(mount) + checkoutDir, err := lm.Mount() + if err != nil { + return nil, err + } + defer func() { + if retErr != nil && lm != nil { + lm.Unmount() + } + }() + + if gs.src.KeepGitDir { + _, err = gitWithinDir(ctx, checkoutDir, "", "init") + if err != nil { + return nil, err + } + _, err = gitWithinDir(ctx, checkoutDir, "", "remote", "add", "origin", gitDir) + if err != nil { + return nil, err + } + pullref := ref + if isCommitSHA(ref) { + pullref = "refs/buildkit/" + identity.NewID() + _, err = gitWithinDir(ctx, gitDir, "", "update-ref", pullref, ref) + if err != nil { + return nil, err + } + } + _, err = gitWithinDir(ctx, checkoutDir, "", "fetch", "--depth=1", "origin", pullref) + if err != nil { + return nil, err + } + _, err = gitWithinDir(ctx, checkoutDir, checkoutDir, "checkout", "FETCH_HEAD") + if err != nil { + return nil, errors.Wrapf(err, "failed to checkout remote %s", gs.src.Remote) + } + gitDir = checkoutDir + } else { + _, err = gitWithinDir(ctx, gitDir, checkoutDir, "checkout", ref, "--", ".") + if err != nil { + return nil, errors.Wrapf(err, "failed to checkout remote %s", gs.src.Remote) + } + } + + _, err = gitWithinDir(ctx, gitDir, checkoutDir, "submodule", "update", "--init", "--recursive", "--depth=1") + if err != nil { + return nil, errors.Wrapf(err, "failed to update submodules for %s", gs.src.Remote) + } + + lm.Unmount() + lm = nil + + snap, err := checkoutRef.Commit(ctx) + if err != nil { + return nil, err + } + checkoutRef = nil + + defer func() { + if retErr != nil { + snap.Release(context.TODO()) + } + }() + + si, _ := gs.md.Get(snap.ID()) + v, err := metadata.NewValue(snapshotKey) + v.Index = snapshotKey + if err != nil { + return nil, err + } + if err := si.Update(func(b *bolt.Bucket) error { + return si.SetValue(b, "git-snapshot", v) + }); err != nil { + return nil, err + } + + return snap, nil +} + +func isCommitSHA(str string) bool { + return validHex.MatchString(str) +} + +func gitWithinDir(ctx context.Context, gitDir, workDir string, args ...string) (*bytes.Buffer, error) { + a := []string{"--git-dir", gitDir} + if workDir != "" { + a = append(a, "--work-tree", workDir) + } + return git(ctx, workDir, append(a, args...)...) +} + +func git(ctx context.Context, dir string, args ...string) (*bytes.Buffer, error) { + for { + stdout, stderr := logs.NewLogStreams(ctx, false) + defer stdout.Close() + defer stderr.Close() + cmd := exec.Command("git", args...) + cmd.Dir = dir // some commands like submodule require this + buf := bytes.NewBuffer(nil) + errbuf := bytes.NewBuffer(nil) + cmd.Stdout = io.MultiWriter(stdout, buf) + cmd.Stderr = io.MultiWriter(stderr, errbuf) + // remote git commands spawn helper processes that inherit FDs and don't + // handle parent death signal so exec.CommandContext can't be used + err := runProcessGroup(ctx, cmd) + if err != nil { + if strings.Contains(errbuf.String(), "--depth") || strings.Contains(errbuf.String(), "shallow") { + if newArgs := argsNoDepth(args); len(args) > len(newArgs) { + args = newArgs + continue + } + } + } + return buf, err + } +} + +func argsNoDepth(args []string) []string { + out := make([]string, 0, len(args)) + for _, a := range args { + if a != "--depth=1" { + out = append(out, a) + } + } + return out +} diff --git a/vendor/github.com/moby/buildkit/source/git/gitsource_unix.go b/vendor/github.com/moby/buildkit/source/git/gitsource_unix.go new file mode 100644 index 0000000000000..4d0e9d89d20a9 --- /dev/null +++ b/vendor/github.com/moby/buildkit/source/git/gitsource_unix.go @@ -0,0 +1,27 @@ +// +build !windows + +package git + +import ( + "context" + "os/exec" + "syscall" +) + +func runProcessGroup(ctx context.Context, cmd *exec.Cmd) error { + cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} + if err := cmd.Start(); err != nil { + return err + } + waitDone := make(chan struct{}) + go func() { + select { + case <-ctx.Done(): + syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL) + case <-waitDone: + } + }() + err := cmd.Wait() + close(waitDone) + return err +} diff --git a/vendor/github.com/moby/buildkit/source/git/gitsource_windows.go b/vendor/github.com/moby/buildkit/source/git/gitsource_windows.go new file mode 100644 index 0000000000000..3435c8f9eef46 --- /dev/null +++ b/vendor/github.com/moby/buildkit/source/git/gitsource_windows.go @@ -0,0 +1,23 @@ +// +build windows + +package git + +import ( + "context" + "os/exec" +) + +func runProcessGroup(ctx context.Context, cmd *exec.Cmd) error { + if err := cmd.Start(); err != nil { + return err + } + waitDone := make(chan struct{}) + go func() { + select { + case <-ctx.Done(): + cmd.Process.Kill() + case <-waitDone: + } + }() + return cmd.Wait() +} diff --git a/vendor/github.com/moby/buildkit/source/gitidentifier.go b/vendor/github.com/moby/buildkit/source/gitidentifier.go new file mode 100644 index 0000000000000..9f338343bf9ac --- /dev/null +++ b/vendor/github.com/moby/buildkit/source/gitidentifier.go @@ -0,0 +1,70 @@ +package source + +import ( + "net/url" + "strings" + + "github.com/pkg/errors" +) + +type GitIdentifier struct { + Remote string + Ref string + Subdir string + KeepGitDir bool +} + +func NewGitIdentifier(remoteURL string) (*GitIdentifier, error) { + repo := GitIdentifier{} + + if !isGitTransport(remoteURL) { + remoteURL = "https://" + remoteURL + } + + var fragment string + if strings.HasPrefix(remoteURL, "git@") { + // git@.. is not an URL, so cannot be parsed as URL + parts := strings.SplitN(remoteURL, "#", 2) + + repo.Remote = parts[0] + if len(parts) == 2 { + fragment = parts[1] + } + repo.Ref, repo.Subdir = getRefAndSubdir(fragment) + } else { + u, err := url.Parse(remoteURL) + if err != nil { + return nil, err + } + + repo.Ref, repo.Subdir = getRefAndSubdir(u.Fragment) + u.Fragment = "" + repo.Remote = u.String() + } + if repo.Subdir != "" { + return nil, errors.Errorf("subdir not supported yet") + } + return &repo, nil +} + +func (i *GitIdentifier) ID() string { + return "git" +} + +// isGitTransport returns true if the provided str is a git transport by inspecting +// the prefix of the string for known protocols used in git. +func isGitTransport(str string) bool { + return strings.HasPrefix(str, "http://") || strings.HasPrefix(str, "https://") || strings.HasPrefix(str, "git://") || strings.HasPrefix(str, "git@") +} + +func getRefAndSubdir(fragment string) (ref string, subdir string) { + refAndDir := strings.SplitN(fragment, ":", 2) + ref = "master" + if len(refAndDir[0]) != 0 { + ref = refAndDir[0] + } + if len(refAndDir) > 1 && len(refAndDir[1]) != 0 { + subdir = refAndDir[1] + } + return +} diff --git a/vendor/github.com/moby/buildkit/source/http/httpsource.go b/vendor/github.com/moby/buildkit/source/http/httpsource.go new file mode 100644 index 0000000000000..58c70400989ca --- /dev/null +++ b/vendor/github.com/moby/buildkit/source/http/httpsource.go @@ -0,0 +1,429 @@ +package http + +import ( + "context" + "crypto/sha256" + "encoding/json" + "fmt" + "io" + "mime" + "net/http" + "net/url" + "os" + "path" + "path/filepath" + "strings" + "time" + + "github.com/boltdb/bolt" + "github.com/docker/docker/pkg/locker" + "github.com/moby/buildkit/cache" + "github.com/moby/buildkit/cache/metadata" + "github.com/moby/buildkit/snapshot" + "github.com/moby/buildkit/source" + "github.com/moby/buildkit/util/tracing" + digest "github.com/opencontainers/go-digest" + "github.com/pkg/errors" +) + +type Opt struct { + CacheAccessor cache.Accessor + MetadataStore *metadata.Store + Transport http.RoundTripper +} + +type httpSource struct { + md *metadata.Store + cache cache.Accessor + locker *locker.Locker + client *http.Client +} + +func NewSource(opt Opt) (source.Source, error) { + transport := opt.Transport + if transport == nil { + transport = tracing.DefaultTransport + } + hs := &httpSource{ + md: opt.MetadataStore, + cache: opt.CacheAccessor, + locker: locker.New(), + client: &http.Client{ + Transport: transport, + }, + } + return hs, nil +} + +func (hs *httpSource) ID() string { + return source.HttpsScheme +} + +type httpSourceHandler struct { + *httpSource + src source.HttpIdentifier + refID string + cacheKey digest.Digest +} + +func (hs *httpSource) Resolve(ctx context.Context, id source.Identifier) (source.SourceInstance, error) { + httpIdentifier, ok := id.(*source.HttpIdentifier) + if !ok { + return nil, errors.Errorf("invalid http identifier %v", id) + } + + return &httpSourceHandler{ + src: *httpIdentifier, + httpSource: hs, + }, nil +} + +// urlHash is internal hash the etag is stored by that doesn't leak outside +// this package. +func (hs *httpSourceHandler) urlHash() (digest.Digest, error) { + dt, err := json.Marshal(struct { + Filename string + Perm, UID, GID int + }{ + Filename: getFileName(hs.src.URL, hs.src.Filename, nil), + Perm: hs.src.Perm, + UID: hs.src.UID, + GID: hs.src.GID, + }) + if err != nil { + return "", err + } + return digest.FromBytes(dt), nil +} + +func (hs *httpSourceHandler) formatCacheKey(filename string, dgst digest.Digest, lastModTime string) digest.Digest { + dt, err := json.Marshal(struct { + Filename string + Perm, UID, GID int + Checksum digest.Digest + LastModTime string `json:",omitempty"` + }{ + Filename: filename, + Perm: hs.src.Perm, + UID: hs.src.UID, + GID: hs.src.GID, + Checksum: dgst, + LastModTime: lastModTime, + }) + if err != nil { + return dgst + } + return digest.FromBytes(dt) +} + +func (hs *httpSourceHandler) CacheKey(ctx context.Context, index int) (string, bool, error) { + if hs.src.Checksum != "" { + hs.cacheKey = hs.src.Checksum + return hs.formatCacheKey(getFileName(hs.src.URL, hs.src.Filename, nil), hs.src.Checksum, "").String(), true, nil + } + + uh, err := hs.urlHash() + if err != nil { + return "", false, nil + } + + // look up metadata(previously stored headers) for that URL + sis, err := hs.md.Search(uh.String()) + if err != nil { + return "", false, errors.Wrapf(err, "failed to search metadata for %s", uh) + } + + req, err := http.NewRequest("GET", hs.src.URL, nil) + if err != nil { + return "", false, err + } + req = req.WithContext(ctx) + m := map[string]*metadata.StorageItem{} + + if len(sis) > 0 { + for _, si := range sis { + // if metaDigest := getMetaDigest(si); metaDigest == hs.formatCacheKey("") { + if etag := getETag(si); etag != "" { + if dgst := getChecksum(si); dgst != "" { + m[etag] = si + req.Header.Add("If-None-Match", etag) + } + } + // } + } + } + + resp, err := hs.client.Do(req) + if err != nil { + return "", false, err + } + if resp.StatusCode < 200 || resp.StatusCode >= 400 { + return "", false, errors.Errorf("invalid response status %d", resp.StatusCode) + } + if resp.StatusCode == http.StatusNotModified { + respETag := resp.Header.Get("ETag") + si, ok := m[respETag] + if !ok { + return "", false, errors.Errorf("invalid not-modified ETag: %v", respETag) + } + hs.refID = si.ID() + dgst := getChecksum(si) + if dgst == "" { + return "", false, errors.Errorf("invalid metadata change") + } + modTime := getModTime(si) + resp.Body.Close() + return hs.formatCacheKey(getFileName(hs.src.URL, hs.src.Filename, resp), dgst, modTime).String(), true, nil + } + + ref, dgst, err := hs.save(ctx, resp) + if err != nil { + return "", false, err + } + ref.Release(context.TODO()) + + hs.cacheKey = dgst + + return hs.formatCacheKey(getFileName(hs.src.URL, hs.src.Filename, resp), dgst, resp.Header.Get("Last-Modified")).String(), true, nil +} + +func (hs *httpSourceHandler) save(ctx context.Context, resp *http.Response) (ref cache.ImmutableRef, dgst digest.Digest, retErr error) { + newRef, err := hs.cache.New(ctx, nil, cache.CachePolicyRetain, cache.WithDescription(fmt.Sprintf("http url %s", hs.src.URL))) + if err != nil { + return nil, "", err + } + + releaseRef := func() { + newRef.Release(context.TODO()) + } + + defer func() { + if retErr != nil && newRef != nil { + releaseRef() + } + }() + + mount, err := newRef.Mount(ctx, false) + if err != nil { + return nil, "", err + } + + lm := snapshot.LocalMounter(mount) + dir, err := lm.Mount() + if err != nil { + return nil, "", err + } + + defer func() { + if retErr != nil && lm != nil { + lm.Unmount() + } + }() + perm := 0600 + if hs.src.Perm != 0 { + perm = hs.src.Perm + } + fp := filepath.Join(dir, getFileName(hs.src.URL, hs.src.Filename, resp)) + + f, err := os.OpenFile(fp, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(perm)) + if err != nil { + return nil, "", err + } + defer func() { + if f != nil { + f.Close() + } + }() + + h := sha256.New() + + if _, err := io.Copy(io.MultiWriter(f, h), resp.Body); err != nil { + return nil, "", err + } + + if err := f.Close(); err != nil { + return nil, "", err + } + f = nil + + if hs.src.UID != 0 || hs.src.GID != 0 { + if err := os.Chown(fp, hs.src.UID, hs.src.GID); err != nil { + return nil, "", err + } + } + + mTime := time.Unix(0, 0) + lastMod := resp.Header.Get("Last-Modified") + if lastMod != "" { + if parsedMTime, err := http.ParseTime(lastMod); err == nil { + mTime = parsedMTime + } + } + + if err := os.Chtimes(fp, mTime, mTime); err != nil { + return nil, "", err + } + + lm.Unmount() + lm = nil + + ref, err = newRef.Commit(ctx) + if err != nil { + return nil, "", err + } + newRef = nil + + hs.refID = ref.ID() + dgst = digest.NewDigest(digest.SHA256, h) + + if respETag := resp.Header.Get("ETag"); respETag != "" { + setETag(ref.Metadata(), respETag) + uh, err := hs.urlHash() + if err != nil { + return nil, "", err + } + setChecksum(ref.Metadata(), uh.String(), dgst) + if err := ref.Metadata().Commit(); err != nil { + return nil, "", err + } + } + + if modTime := resp.Header.Get("Last-Modified"); modTime != "" { + setModTime(ref.Metadata(), modTime) + } + + return ref, dgst, nil +} + +func (hs *httpSourceHandler) Snapshot(ctx context.Context) (cache.ImmutableRef, error) { + if hs.refID != "" { + ref, err := hs.cache.Get(ctx, hs.refID) + if err == nil { + return ref, nil + } + } + + req, err := http.NewRequest("GET", hs.src.URL, nil) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + + resp, err := hs.client.Do(req) + if err != nil { + return nil, err + } + + ref, dgst, err := hs.save(ctx, resp) + if err != nil { + return nil, err + } + if dgst != hs.cacheKey { + ref.Release(context.TODO()) + return nil, errors.Errorf("digest mismatch %s: %s", dgst, hs.cacheKey) + } + + return ref, nil +} + +const keyETag = "etag" +const keyChecksum = "http.checksum" +const keyModTime = "http.modtime" + +func setETag(si *metadata.StorageItem, s string) error { + v, err := metadata.NewValue(s) + if err != nil { + return errors.Wrap(err, "failed to create etag value") + } + si.Queue(func(b *bolt.Bucket) error { + return si.SetValue(b, keyETag, v) + }) + return nil +} + +func getETag(si *metadata.StorageItem) string { + v := si.Get(keyETag) + if v == nil { + return "" + } + var etag string + if err := v.Unmarshal(&etag); err != nil { + return "" + } + return etag +} + +func setModTime(si *metadata.StorageItem, s string) error { + v, err := metadata.NewValue(s) + if err != nil { + return errors.Wrap(err, "failed to create modtime value") + } + si.Queue(func(b *bolt.Bucket) error { + return si.SetValue(b, keyModTime, v) + }) + return nil +} + +func getModTime(si *metadata.StorageItem) string { + v := si.Get(keyModTime) + if v == nil { + return "" + } + var modTime string + if err := v.Unmarshal(&modTime); err != nil { + return "" + } + return modTime +} + +func setChecksum(si *metadata.StorageItem, url string, d digest.Digest) error { + v, err := metadata.NewValue(d) + if err != nil { + return errors.Wrap(err, "failed to create checksum value") + } + v.Index = url + si.Queue(func(b *bolt.Bucket) error { + return si.SetValue(b, keyChecksum, v) + }) + return nil +} + +func getChecksum(si *metadata.StorageItem) digest.Digest { + v := si.Get(keyChecksum) + if v == nil { + return "" + } + var dgstStr string + if err := v.Unmarshal(&dgstStr); err != nil { + return "" + } + dgst, err := digest.Parse(dgstStr) + if err != nil { + return "" + } + return dgst +} + +func getFileName(urlStr, manualFilename string, resp *http.Response) string { + if manualFilename != "" { + return manualFilename + } + if resp != nil { + if contentDisposition := resp.Header.Get("Content-Disposition"); contentDisposition != "" { + if _, params, err := mime.ParseMediaType(contentDisposition); err == nil { + if params["filename"] != "" && !strings.HasSuffix(params["filename"], "/") { + if filename := filepath.Base(filepath.FromSlash(params["filename"])); filename != "" { + return filename + } + } + } + } + } + u, err := url.Parse(urlStr) + if err == nil { + if base := path.Base(u.Path); base != "." && base != "/" { + return base + } + } + return "download" +} diff --git a/vendor/github.com/moby/buildkit/source/identifier.go b/vendor/github.com/moby/buildkit/source/identifier.go new file mode 100644 index 0000000000000..a4d9f5a01bd13 --- /dev/null +++ b/vendor/github.com/moby/buildkit/source/identifier.go @@ -0,0 +1,205 @@ +package source + +import ( + "encoding/json" + "strconv" + "strings" + + "github.com/containerd/containerd/reference" + "github.com/moby/buildkit/solver/pb" + digest "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +var ( + errInvalid = errors.New("invalid") + errNotFound = errors.New("not found") +) + +const ( + DockerImageScheme = "docker-image" + GitScheme = "git" + LocalScheme = "local" + HttpScheme = "http" + HttpsScheme = "https" +) + +type Identifier interface { + ID() string // until sources are in process this string comparison could be avoided +} + +func FromString(s string) (Identifier, error) { + // TODO: improve this + parts := strings.SplitN(s, "://", 2) + if len(parts) != 2 { + return nil, errors.Wrapf(errInvalid, "failed to parse %s", s) + } + + switch parts[0] { + case DockerImageScheme: + return NewImageIdentifier(parts[1]) + case GitScheme: + return NewGitIdentifier(parts[1]) + case LocalScheme: + return NewLocalIdentifier(parts[1]) + case HttpsScheme: + return NewHttpIdentifier(parts[1], true) + case HttpScheme: + return NewHttpIdentifier(parts[1], false) + default: + return nil, errors.Wrapf(errNotFound, "unknown schema %s", parts[0]) + } +} +func FromLLB(op *pb.Op_Source, platform *pb.Platform) (Identifier, error) { + id, err := FromString(op.Source.Identifier) + if err != nil { + return nil, err + } + if id, ok := id.(*ImageIdentifier); ok && platform != nil { + id.Platform = &specs.Platform{ + OS: platform.OS, + Architecture: platform.Architecture, + Variant: platform.Variant, + OSVersion: platform.OSVersion, + OSFeatures: platform.OSFeatures, + } + } + if id, ok := id.(*GitIdentifier); ok { + for k, v := range op.Source.Attrs { + switch k { + case pb.AttrKeepGitDir: + if v == "true" { + id.KeepGitDir = true + } + case pb.AttrFullRemoteURL: + id.Remote = v + } + } + } + if id, ok := id.(*LocalIdentifier); ok { + for k, v := range op.Source.Attrs { + switch k { + case pb.AttrLocalSessionID: + id.SessionID = v + if p := strings.SplitN(v, ":", 2); len(p) == 2 { + id.Name = p[0] + "-" + id.Name + id.SessionID = p[1] + } + case pb.AttrIncludePatterns: + var patterns []string + if err := json.Unmarshal([]byte(v), &patterns); err != nil { + return nil, err + } + id.IncludePatterns = patterns + case pb.AttrExcludePatterns: + var patterns []string + if err := json.Unmarshal([]byte(v), &patterns); err != nil { + return nil, err + } + id.ExcludePatterns = patterns + case pb.AttrFollowPaths: + var paths []string + if err := json.Unmarshal([]byte(v), &paths); err != nil { + return nil, err + } + id.FollowPaths = paths + case pb.AttrSharedKeyHint: + id.SharedKeyHint = v + } + } + } + if id, ok := id.(*HttpIdentifier); ok { + for k, v := range op.Source.Attrs { + switch k { + case pb.AttrHTTPChecksum: + dgst, err := digest.Parse(v) + if err != nil { + return nil, err + } + id.Checksum = dgst + case pb.AttrHTTPFilename: + id.Filename = v + case pb.AttrHTTPPerm: + i, err := strconv.ParseInt(v, 0, 64) + if err != nil { + return nil, err + } + id.Perm = int(i) + case pb.AttrHTTPUID: + i, err := strconv.ParseInt(v, 0, 64) + if err != nil { + return nil, err + } + id.UID = int(i) + case pb.AttrHTTPGID: + i, err := strconv.ParseInt(v, 0, 64) + if err != nil { + return nil, err + } + id.GID = int(i) + } + } + } + return id, nil +} + +type ImageIdentifier struct { + Reference reference.Spec + Platform *specs.Platform +} + +func NewImageIdentifier(str string) (*ImageIdentifier, error) { + ref, err := reference.Parse(str) + if err != nil { + return nil, errors.WithStack(err) + } + + if ref.Object == "" { + return nil, errors.WithStack(reference.ErrObjectRequired) + } + return &ImageIdentifier{Reference: ref}, nil +} + +func (_ *ImageIdentifier) ID() string { + return DockerImageScheme +} + +type LocalIdentifier struct { + Name string + SessionID string + IncludePatterns []string + ExcludePatterns []string + FollowPaths []string + SharedKeyHint string +} + +func NewLocalIdentifier(str string) (*LocalIdentifier, error) { + return &LocalIdentifier{Name: str}, nil +} + +func (*LocalIdentifier) ID() string { + return LocalScheme +} + +func NewHttpIdentifier(str string, tls bool) (*HttpIdentifier, error) { + proto := "https://" + if !tls { + proto = "http://" + } + return &HttpIdentifier{TLS: tls, URL: proto + str}, nil +} + +type HttpIdentifier struct { + TLS bool + URL string + Checksum digest.Digest + Filename string + Perm int + UID int + GID int +} + +func (_ *HttpIdentifier) ID() string { + return HttpsScheme +} diff --git a/vendor/github.com/moby/buildkit/source/local/local.go b/vendor/github.com/moby/buildkit/source/local/local.go new file mode 100644 index 0000000000000..1e9ab6a4a3cc1 --- /dev/null +++ b/vendor/github.com/moby/buildkit/source/local/local.go @@ -0,0 +1,249 @@ +package local + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "github.com/boltdb/bolt" + "github.com/moby/buildkit/cache" + "github.com/moby/buildkit/cache/contenthash" + "github.com/moby/buildkit/cache/metadata" + "github.com/moby/buildkit/session" + "github.com/moby/buildkit/session/filesync" + "github.com/moby/buildkit/snapshot" + "github.com/moby/buildkit/source" + "github.com/moby/buildkit/util/progress" + digest "github.com/opencontainers/go-digest" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/tonistiigi/fsutil" + "golang.org/x/time/rate" +) + +const keySharedKey = "local.sharedKey" + +type Opt struct { + SessionManager *session.Manager + CacheAccessor cache.Accessor + MetadataStore *metadata.Store +} + +func NewSource(opt Opt) (source.Source, error) { + ls := &localSource{ + sm: opt.SessionManager, + cm: opt.CacheAccessor, + md: opt.MetadataStore, + } + return ls, nil +} + +type localSource struct { + sm *session.Manager + cm cache.Accessor + md *metadata.Store +} + +func (ls *localSource) ID() string { + return source.LocalScheme +} + +func (ls *localSource) Resolve(ctx context.Context, id source.Identifier) (source.SourceInstance, error) { + localIdentifier, ok := id.(*source.LocalIdentifier) + if !ok { + return nil, errors.Errorf("invalid local identifier %v", id) + } + + return &localSourceHandler{ + src: *localIdentifier, + localSource: ls, + }, nil +} + +type localSourceHandler struct { + src source.LocalIdentifier + *localSource +} + +func (ls *localSourceHandler) CacheKey(ctx context.Context, index int) (string, bool, error) { + sessionID := ls.src.SessionID + + if sessionID == "" { + id := session.FromContext(ctx) + if id == "" { + return "", false, errors.New("could not access local files without session") + } + sessionID = id + } + dt, err := json.Marshal(struct { + SessionID string + IncludePatterns []string + ExcludePatterns []string + }{SessionID: sessionID, IncludePatterns: ls.src.IncludePatterns, ExcludePatterns: ls.src.ExcludePatterns}) + if err != nil { + return "", false, err + } + return "session:" + ls.src.Name + ":" + digest.FromBytes(dt).String(), true, nil +} + +func (ls *localSourceHandler) Snapshot(ctx context.Context) (out cache.ImmutableRef, retErr error) { + + id := session.FromContext(ctx) + if id == "" { + return nil, errors.New("could not access local files without session") + } + + timeoutCtx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + + caller, err := ls.sm.Get(timeoutCtx, id) + if err != nil { + return nil, err + } + + sharedKey := keySharedKey + ":" + ls.src.Name + ":" + ls.src.SharedKeyHint + ":" + caller.SharedKey() // TODO: replace caller.SharedKey() with source based hint from client(absolute-path+nodeid) + + var mutable cache.MutableRef + sis, err := ls.md.Search(sharedKey) + if err != nil { + return nil, err + } + for _, si := range sis { + if m, err := ls.cm.GetMutable(ctx, si.ID()); err == nil { + logrus.Debugf("reusing ref for local: %s", m.ID()) + mutable = m + break + } + } + + if mutable == nil { + m, err := ls.cm.New(ctx, nil, cache.CachePolicyRetain, cache.WithDescription(fmt.Sprintf("local source for %s", ls.src.Name))) + if err != nil { + return nil, err + } + mutable = m + logrus.Debugf("new ref for local: %s", mutable.ID()) + } + + defer func() { + if retErr != nil && mutable != nil { + go mutable.Release(context.TODO()) + } + }() + + mount, err := mutable.Mount(ctx, false) + if err != nil { + return nil, err + } + + lm := snapshot.LocalMounter(mount) + + dest, err := lm.Mount() + if err != nil { + return nil, err + } + + defer func() { + if retErr != nil && lm != nil { + lm.Unmount() + } + }() + + cc, err := contenthash.GetCacheContext(ctx, mutable.Metadata()) + if err != nil { + return nil, err + } + + opt := filesync.FSSendRequestOpt{ + Name: ls.src.Name, + IncludePatterns: ls.src.IncludePatterns, + ExcludePatterns: ls.src.ExcludePatterns, + FollowPaths: ls.src.FollowPaths, + OverrideExcludes: false, + DestDir: dest, + CacheUpdater: &cacheUpdater{cc}, + ProgressCb: newProgressHandler(ctx, "transferring "+ls.src.Name+":"), + } + + if err := filesync.FSSync(ctx, caller, opt); err != nil { + return nil, err + } + + if err := lm.Unmount(); err != nil { + return nil, err + } + lm = nil + + if err := contenthash.SetCacheContext(ctx, mutable.Metadata(), cc); err != nil { + return nil, err + } + + // skip storing snapshot by the shared key if it already exists + skipStoreSharedKey := false + si, _ := ls.md.Get(mutable.ID()) + if v := si.Get(keySharedKey); v != nil { + var str string + if err := v.Unmarshal(&str); err != nil { + return nil, err + } + skipStoreSharedKey = str == sharedKey + } + if !skipStoreSharedKey { + v, err := metadata.NewValue(sharedKey) + if err != nil { + return nil, err + } + v.Index = sharedKey + if err := si.Update(func(b *bolt.Bucket) error { + return si.SetValue(b, sharedKey, v) + }); err != nil { + return nil, err + } + logrus.Debugf("saved %s as %s", mutable.ID(), sharedKey) + } + + snap, err := mutable.Commit(ctx) + if err != nil { + return nil, err + } + + mutable = nil // avoid deferred cleanup + + return snap, nil +} + +func newProgressHandler(ctx context.Context, id string) func(int, bool) { + limiter := rate.NewLimiter(rate.Every(100*time.Millisecond), 1) + pw, _, _ := progress.FromContext(ctx) + now := time.Now() + st := progress.Status{ + Started: &now, + Action: "transferring", + } + pw.Write(id, st) + return func(s int, last bool) { + if last || limiter.Allow() { + st.Current = s + if last { + now := time.Now() + st.Completed = &now + } + pw.Write(id, st) + if last { + pw.Close() + } + } + } +} + +type cacheUpdater struct { + contenthash.CacheContext +} + +func (cu *cacheUpdater) MarkSupported(bool) { +} + +func (cu *cacheUpdater) ContentHasher() fsutil.ContentHasher { + return contenthash.NewFromStat +} diff --git a/vendor/github.com/moby/buildkit/source/manager.go b/vendor/github.com/moby/buildkit/source/manager.go new file mode 100644 index 0000000000000..e520b6c77c3d6 --- /dev/null +++ b/vendor/github.com/moby/buildkit/source/manager.go @@ -0,0 +1,48 @@ +package source + +import ( + "context" + "sync" + + "github.com/moby/buildkit/cache" + "github.com/pkg/errors" +) + +type Source interface { + ID() string + Resolve(ctx context.Context, id Identifier) (SourceInstance, error) +} + +type SourceInstance interface { + CacheKey(ctx context.Context, index int) (string, bool, error) + Snapshot(ctx context.Context) (cache.ImmutableRef, error) +} + +type Manager struct { + mu sync.Mutex + sources map[string]Source +} + +func NewManager() (*Manager, error) { + return &Manager{ + sources: make(map[string]Source), + }, nil +} + +func (sm *Manager) Register(src Source) { + sm.mu.Lock() + sm.sources[src.ID()] = src + sm.mu.Unlock() +} + +func (sm *Manager) Resolve(ctx context.Context, id Identifier) (SourceInstance, error) { + sm.mu.Lock() + src, ok := sm.sources[id.ID()] + sm.mu.Unlock() + + if !ok { + return nil, errors.Errorf("no handler for %s", id.ID()) + } + + return src.Resolve(ctx, id) +} diff --git a/vendor/github.com/moby/buildkit/util/apicaps/caps.go b/vendor/github.com/moby/buildkit/util/apicaps/caps.go new file mode 100644 index 0000000000000..2509b53b07f7b --- /dev/null +++ b/vendor/github.com/moby/buildkit/util/apicaps/caps.go @@ -0,0 +1,161 @@ +package apicaps + +import ( + "fmt" + "sort" + "strings" + + pb "github.com/moby/buildkit/util/apicaps/pb" + "github.com/pkg/errors" +) + +type PBCap = pb.APICap + +// ExportedProduct is the name of the product using this package. +// Users vendoring this library may override it to provide better versioning hints +// for their users (or set it with a flag to buildkitd). +var ExportedProduct string + +// CapStatus defines the stability properties of a capability +type CapStatus int + +const ( + // CapStatusStable refers to a capability that should never be changed in + // backwards incompatible manner unless there is a serious security issue. + CapStatusStable CapStatus = iota + // CapStatusExperimental refers to a capability that may be removed in the future. + // If incompatible changes are made the previous ID is disabled and new is added. + CapStatusExperimental + // CapStatusPrerelease is same as CapStatusExperimental that can be used for new + // features before they move to stable. + CapStatusPrerelease +) + +// CapID is type for capability identifier +type CapID string + +// Cap describes an API feature +type Cap struct { + ID CapID + Name string // readable name, may contain spaces but keep in one sentence + Status CapStatus + Enabled bool + Deprecated bool + SupportedHint map[string]string + DisabledReason string + DisabledReasonMsg string + DisabledAlternative string +} + +// CapList is a collection of capability definitions +type CapList struct { + m map[CapID]Cap +} + +// Init initializes definition for a new capability. +// Not safe to be called concurrently with other methods. +func (l *CapList) Init(cc ...Cap) { + if l.m == nil { + l.m = make(map[CapID]Cap, len(cc)) + } + for _, c := range cc { + l.m[c.ID] = c + } +} + +// All reports the configuration of all known capabilities +func (l *CapList) All() []pb.APICap { + out := make([]pb.APICap, 0, len(l.m)) + for _, c := range l.m { + out = append(out, pb.APICap{ + ID: string(c.ID), + Enabled: c.Enabled, + Deprecated: c.Deprecated, + DisabledReason: c.DisabledReason, + DisabledReasonMsg: c.DisabledReasonMsg, + DisabledAlternative: c.DisabledAlternative, + }) + } + sort.Slice(out, func(i, j int) bool { + return out[i].ID < out[j].ID + }) + return out +} + +// CapSet returns a CapSet for an capability configuration +func (l *CapList) CapSet(caps []pb.APICap) CapSet { + m := make(map[string]*pb.APICap, len(caps)) + for _, c := range caps { + if c.ID != "" { + m[c.ID] = &c + } + } + return CapSet{ + list: l, + set: m, + } +} + +// CapSet is a configuration for detecting supported capabilities +type CapSet struct { + list *CapList + set map[string]*pb.APICap +} + +// Supports returns an error if capability is not supported +func (s *CapSet) Supports(id CapID) error { + err := &CapError{ID: id} + c, ok := s.list.m[id] + if !ok { + return errors.WithStack(err) + } + err.Definition = &c + state, ok := s.set[string(id)] + if !ok { + return errors.WithStack(err) + } + err.State = state + if !state.Enabled { + return errors.WithStack(err) + } + return nil +} + +// CapError is an error for unsupported capability +type CapError struct { + ID CapID + Definition *Cap + State *pb.APICap +} + +func (e CapError) Error() string { + if e.Definition == nil { + return fmt.Sprintf("unknown API capability %s", e.ID) + } + typ := "" + if e.Definition.Status == CapStatusExperimental { + typ = "experimental " + } + if e.Definition.Status == CapStatusPrerelease { + typ = "prerelease " + } + name := "" + if e.Definition.Name != "" { + name = "(" + e.Definition.Name + ")" + } + b := &strings.Builder{} + fmt.Fprintf(b, "requested %sfeature %s %s", typ, e.ID, name) + if e.State == nil { + fmt.Fprint(b, " is not supported by build server") + if hint, ok := e.Definition.SupportedHint[ExportedProduct]; ok { + fmt.Fprintf(b, " (added in %s)", hint) + } + fmt.Fprintf(b, ", please update %s", ExportedProduct) + } else { + fmt.Fprint(b, " has been disabled on the build server") + if e.State.DisabledReasonMsg != "" { + fmt.Fprintf(b, ": %s", e.State.DisabledReasonMsg) + } + } + return b.String() +} diff --git a/vendor/github.com/moby/buildkit/util/apicaps/pb/caps.pb.go b/vendor/github.com/moby/buildkit/util/apicaps/pb/caps.pb.go new file mode 100644 index 0000000000000..9d4d48802427b --- /dev/null +++ b/vendor/github.com/moby/buildkit/util/apicaps/pb/caps.pb.go @@ -0,0 +1,535 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: caps.proto + +/* + Package moby_buildkit_v1_apicaps is a generated protocol buffer package. + + It is generated from these files: + caps.proto + + It has these top-level messages: + APICap +*/ +package moby_buildkit_v1_apicaps + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" +import _ "github.com/gogo/protobuf/gogoproto" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +// APICap defines a capability supported by the service +type APICap struct { + ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` + Enabled bool `protobuf:"varint,2,opt,name=Enabled,proto3" json:"Enabled,omitempty"` + Deprecated bool `protobuf:"varint,3,opt,name=Deprecated,proto3" json:"Deprecated,omitempty"` + DisabledReason string `protobuf:"bytes,4,opt,name=DisabledReason,proto3" json:"DisabledReason,omitempty"` + DisabledReasonMsg string `protobuf:"bytes,5,opt,name=DisabledReasonMsg,proto3" json:"DisabledReasonMsg,omitempty"` + DisabledAlternative string `protobuf:"bytes,6,opt,name=DisabledAlternative,proto3" json:"DisabledAlternative,omitempty"` +} + +func (m *APICap) Reset() { *m = APICap{} } +func (m *APICap) String() string { return proto.CompactTextString(m) } +func (*APICap) ProtoMessage() {} +func (*APICap) Descriptor() ([]byte, []int) { return fileDescriptorCaps, []int{0} } + +func (m *APICap) GetID() string { + if m != nil { + return m.ID + } + return "" +} + +func (m *APICap) GetEnabled() bool { + if m != nil { + return m.Enabled + } + return false +} + +func (m *APICap) GetDeprecated() bool { + if m != nil { + return m.Deprecated + } + return false +} + +func (m *APICap) GetDisabledReason() string { + if m != nil { + return m.DisabledReason + } + return "" +} + +func (m *APICap) GetDisabledReasonMsg() string { + if m != nil { + return m.DisabledReasonMsg + } + return "" +} + +func (m *APICap) GetDisabledAlternative() string { + if m != nil { + return m.DisabledAlternative + } + return "" +} + +func init() { + proto.RegisterType((*APICap)(nil), "moby.buildkit.v1.apicaps.APICap") +} +func (m *APICap) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *APICap) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintCaps(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + if m.Enabled { + dAtA[i] = 0x10 + i++ + if m.Enabled { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if m.Deprecated { + dAtA[i] = 0x18 + i++ + if m.Deprecated { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if len(m.DisabledReason) > 0 { + dAtA[i] = 0x22 + i++ + i = encodeVarintCaps(dAtA, i, uint64(len(m.DisabledReason))) + i += copy(dAtA[i:], m.DisabledReason) + } + if len(m.DisabledReasonMsg) > 0 { + dAtA[i] = 0x2a + i++ + i = encodeVarintCaps(dAtA, i, uint64(len(m.DisabledReasonMsg))) + i += copy(dAtA[i:], m.DisabledReasonMsg) + } + if len(m.DisabledAlternative) > 0 { + dAtA[i] = 0x32 + i++ + i = encodeVarintCaps(dAtA, i, uint64(len(m.DisabledAlternative))) + i += copy(dAtA[i:], m.DisabledAlternative) + } + return i, nil +} + +func encodeVarintCaps(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *APICap) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovCaps(uint64(l)) + } + if m.Enabled { + n += 2 + } + if m.Deprecated { + n += 2 + } + l = len(m.DisabledReason) + if l > 0 { + n += 1 + l + sovCaps(uint64(l)) + } + l = len(m.DisabledReasonMsg) + if l > 0 { + n += 1 + l + sovCaps(uint64(l)) + } + l = len(m.DisabledAlternative) + if l > 0 { + n += 1 + l + sovCaps(uint64(l)) + } + return n +} + +func sovCaps(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozCaps(x uint64) (n int) { + return sovCaps(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *APICap) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCaps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: APICap: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: APICap: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCaps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCaps + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Enabled", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCaps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Enabled = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Deprecated", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCaps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Deprecated = bool(v != 0) + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DisabledReason", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCaps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCaps + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DisabledReason = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DisabledReasonMsg", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCaps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCaps + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DisabledReasonMsg = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DisabledAlternative", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCaps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCaps + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DisabledAlternative = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCaps(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthCaps + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipCaps(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowCaps + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowCaps + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowCaps + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthCaps + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowCaps + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipCaps(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthCaps = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowCaps = fmt.Errorf("proto: integer overflow") +) + +func init() { proto.RegisterFile("caps.proto", fileDescriptorCaps) } + +var fileDescriptorCaps = []byte{ + // 236 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4a, 0x4e, 0x2c, 0x28, + 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0xc8, 0xcd, 0x4f, 0xaa, 0xd4, 0x4b, 0x2a, 0xcd, + 0xcc, 0x49, 0xc9, 0xce, 0x2c, 0xd1, 0x2b, 0x33, 0xd4, 0x4b, 0x2c, 0xc8, 0x04, 0xc9, 0x4b, 0xe9, + 0xa6, 0x67, 0x96, 0x64, 0x94, 0x26, 0xe9, 0x25, 0xe7, 0xe7, 0xea, 0xa7, 0xe7, 0xa7, 0xe7, 0xeb, + 0x83, 0x35, 0x24, 0x95, 0xa6, 0x81, 0x79, 0x60, 0x0e, 0x98, 0x05, 0x31, 0x48, 0xe9, 0x16, 0x23, + 0x17, 0x9b, 0x63, 0x80, 0xa7, 0x73, 0x62, 0x81, 0x10, 0x1f, 0x17, 0x93, 0xa7, 0x8b, 0x04, 0xa3, + 0x02, 0xa3, 0x06, 0x67, 0x10, 0x93, 0xa7, 0x8b, 0x90, 0x04, 0x17, 0xbb, 0x6b, 0x5e, 0x62, 0x52, + 0x4e, 0x6a, 0x8a, 0x04, 0x93, 0x02, 0xa3, 0x06, 0x47, 0x10, 0x8c, 0x2b, 0x24, 0xc7, 0xc5, 0xe5, + 0x92, 0x5a, 0x50, 0x94, 0x9a, 0x9c, 0x58, 0x92, 0x9a, 0x22, 0xc1, 0x0c, 0x96, 0x44, 0x12, 0x11, + 0x52, 0xe3, 0xe2, 0x73, 0xc9, 0x2c, 0x06, 0xab, 0x0d, 0x4a, 0x4d, 0x2c, 0xce, 0xcf, 0x93, 0x60, + 0x01, 0x9b, 0x8a, 0x26, 0x2a, 0xa4, 0xc3, 0x25, 0x88, 0x2a, 0xe2, 0x5b, 0x9c, 0x2e, 0xc1, 0x0a, + 0x56, 0x8a, 0x29, 0x21, 0x64, 0xc0, 0x25, 0x0c, 0x13, 0x74, 0xcc, 0x29, 0x49, 0x2d, 0xca, 0x4b, + 0x2c, 0xc9, 0x2c, 0x4b, 0x95, 0x60, 0x03, 0xab, 0xc7, 0x26, 0xe5, 0xc4, 0x73, 0xe2, 0x91, 0x1c, + 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x26, 0xb1, 0x81, 0x7d, 0x6c, 0x0c, 0x08, + 0x00, 0x00, 0xff, 0xff, 0x02, 0x2d, 0x9e, 0x91, 0x48, 0x01, 0x00, 0x00, +} diff --git a/vendor/github.com/moby/buildkit/util/apicaps/pb/caps.proto b/vendor/github.com/moby/buildkit/util/apicaps/pb/caps.proto new file mode 100644 index 0000000000000..1e8c06517c510 --- /dev/null +++ b/vendor/github.com/moby/buildkit/util/apicaps/pb/caps.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +package moby.buildkit.v1.apicaps; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; + +option (gogoproto.sizer_all) = true; +option (gogoproto.marshaler_all) = true; +option (gogoproto.unmarshaler_all) = true; + +// APICap defines a capability supported by the service +message APICap { + string ID = 1; + bool Enabled = 2; + bool Deprecated = 3; // Unused. May be used for warnings in the future + string DisabledReason = 4; // Reason key for detection code + string DisabledReasonMsg = 5; // Message to the user + string DisabledAlternative = 6; // Identifier that updated client could catch. +} \ No newline at end of file diff --git a/vendor/github.com/moby/buildkit/util/apicaps/pb/generate.go b/vendor/github.com/moby/buildkit/util/apicaps/pb/generate.go new file mode 100644 index 0000000000000..281dfabd64a22 --- /dev/null +++ b/vendor/github.com/moby/buildkit/util/apicaps/pb/generate.go @@ -0,0 +1,3 @@ +package moby_buildkit_v1_apicaps + +//go:generate protoc -I=. -I=../../../vendor/ -I=../../../../../../ --gogo_out=plugins=grpc:. caps.proto diff --git a/vendor/github.com/moby/buildkit/util/appdefaults/appdefaults_unix.go b/vendor/github.com/moby/buildkit/util/appdefaults/appdefaults_unix.go new file mode 100644 index 0000000000000..7b907ad32be0e --- /dev/null +++ b/vendor/github.com/moby/buildkit/util/appdefaults/appdefaults_unix.go @@ -0,0 +1,55 @@ +// +build !windows + +package appdefaults + +import ( + "os" + "path/filepath" + "strings" +) + +const ( + Address = "unix:///run/buildkit/buildkitd.sock" + Root = "/var/lib/buildkit" +) + +// UserAddress typically returns /run/user/$UID/buildkit/buildkitd.sock +func UserAddress() string { + // pam_systemd sets XDG_RUNTIME_DIR but not other dirs. + xdgRuntimeDir := os.Getenv("XDG_RUNTIME_DIR") + if xdgRuntimeDir != "" { + dirs := strings.Split(xdgRuntimeDir, ":") + return "unix://" + filepath.Join(dirs[0], "buildkit", "buildkitd.sock") + } + return Address +} + +// EnsureUserAddressDir sets sticky bit on XDG_RUNTIME_DIR if XDG_RUNTIME_DIR is set. +// See https://github.com/opencontainers/runc/issues/1694 +func EnsureUserAddressDir() error { + xdgRuntimeDir := os.Getenv("XDG_RUNTIME_DIR") + if xdgRuntimeDir != "" { + dirs := strings.Split(xdgRuntimeDir, ":") + dir := filepath.Join(dirs[0], "buildkit") + if err := os.MkdirAll(dir, 0700); err != nil { + return err + } + return os.Chmod(dir, 0700|os.ModeSticky) + } + return nil +} + +// UserRoot typically returns /home/$USER/.local/share/buildkit +func UserRoot() string { + // pam_systemd sets XDG_RUNTIME_DIR but not other dirs. + xdgDataHome := os.Getenv("XDG_DATA_HOME") + if xdgDataHome != "" { + dirs := strings.Split(xdgDataHome, ":") + return filepath.Join(dirs[0], "buildkit") + } + home := os.Getenv("HOME") + if home != "" { + return filepath.Join(home, ".local", "share", "buildkit") + } + return Root +} diff --git a/vendor/github.com/moby/buildkit/util/appdefaults/appdefaults_windows.go b/vendor/github.com/moby/buildkit/util/appdefaults/appdefaults_windows.go new file mode 100644 index 0000000000000..dbc96c809556f --- /dev/null +++ b/vendor/github.com/moby/buildkit/util/appdefaults/appdefaults_windows.go @@ -0,0 +1,18 @@ +package appdefaults + +const ( + Address = "npipe:////./pipe/buildkitd" + Root = ".buildstate" +) + +func UserAddress() string { + return Address +} + +func EnsureUserAddressDir() error { + return nil +} + +func UserRoot() string { + return Root +} diff --git a/vendor/github.com/moby/buildkit/util/cond/cond.go b/vendor/github.com/moby/buildkit/util/cond/cond.go new file mode 100644 index 0000000000000..c5e07aec9e3d6 --- /dev/null +++ b/vendor/github.com/moby/buildkit/util/cond/cond.go @@ -0,0 +1,40 @@ +package cond + +import ( + "sync" +) + +// NewStatefulCond returns a stateful version of sync.Cond . This cond will +// never block on `Wait()` if `Signal()` has been called after the `Wait()` last +// returned. This is useful for avoiding to take a lock on `cond.Locker` for +// signalling. +func NewStatefulCond(l sync.Locker) *StatefulCond { + sc := &StatefulCond{main: l} + sc.c = sync.NewCond(&sc.mu) + return sc +} + +type StatefulCond struct { + main sync.Locker + mu sync.Mutex + c *sync.Cond + signalled bool +} + +func (s *StatefulCond) Wait() { + s.main.Unlock() + s.mu.Lock() + if !s.signalled { + s.c.Wait() + } + s.signalled = false + s.mu.Unlock() + s.main.Lock() +} + +func (s *StatefulCond) Signal() { + s.mu.Lock() + s.signalled = true + s.c.Signal() + s.mu.Unlock() +} diff --git a/vendor/github.com/moby/buildkit/util/contentutil/buffer.go b/vendor/github.com/moby/buildkit/util/contentutil/buffer.go new file mode 100644 index 0000000000000..ac8c8baff36a5 --- /dev/null +++ b/vendor/github.com/moby/buildkit/util/contentutil/buffer.go @@ -0,0 +1,156 @@ +package contentutil + +import ( + "bytes" + "context" + "io/ioutil" + "sync" + "time" + + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/errdefs" + digest "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +// Buffer is a content provider and ingester that keeps data in memory +type Buffer interface { + content.Provider + content.Ingester +} + +// NewBuffer returns a new buffer +func NewBuffer() Buffer { + return &buffer{ + buffers: map[digest.Digest][]byte{}, + refs: map[string]struct{}{}, + } +} + +type buffer struct { + mu sync.Mutex + buffers map[digest.Digest][]byte + refs map[string]struct{} +} + +func (b *buffer) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) { + var wOpts content.WriterOpts + for _, opt := range opts { + if err := opt(&wOpts); err != nil { + return nil, err + } + } + b.mu.Lock() + if _, ok := b.refs[wOpts.Ref]; ok { + return nil, errors.Wrapf(errdefs.ErrUnavailable, "ref %s locked", wOpts.Ref) + } + b.mu.Unlock() + return &bufferedWriter{ + main: b, + digester: digest.Canonical.Digester(), + buffer: bytes.NewBuffer(nil), + expected: wOpts.Desc.Digest, + releaseRef: func() { + b.mu.Lock() + delete(b.refs, wOpts.Ref) + b.mu.Unlock() + }, + }, nil +} + +func (b *buffer) ReaderAt(ctx context.Context, desc ocispec.Descriptor) (content.ReaderAt, error) { + r, err := b.getBytesReader(ctx, desc.Digest) + if err != nil { + return nil, err + } + return &readerAt{Reader: r, Closer: ioutil.NopCloser(r), size: int64(r.Len())}, nil +} + +func (b *buffer) getBytesReader(ctx context.Context, dgst digest.Digest) (*bytes.Reader, error) { + b.mu.Lock() + defer b.mu.Unlock() + + if dt, ok := b.buffers[dgst]; ok { + return bytes.NewReader(dt), nil + } + + return nil, errors.Wrapf(errdefs.ErrNotFound, "content %v", dgst) +} + +func (b *buffer) addValue(k digest.Digest, dt []byte) { + b.mu.Lock() + defer b.mu.Unlock() + b.buffers[k] = dt +} + +type bufferedWriter struct { + main *buffer + ref string + offset int64 + total int64 + startedAt time.Time + updatedAt time.Time + buffer *bytes.Buffer + expected digest.Digest + digester digest.Digester + releaseRef func() +} + +func (w *bufferedWriter) Write(p []byte) (n int, err error) { + n, err = w.buffer.Write(p) + w.digester.Hash().Write(p[:n]) + w.offset += int64(len(p)) + w.updatedAt = time.Now() + return n, err +} + +func (w *bufferedWriter) Close() error { + if w.buffer != nil { + w.releaseRef() + w.buffer = nil + } + return nil +} + +func (w *bufferedWriter) Status() (content.Status, error) { + return content.Status{ + Ref: w.ref, + Offset: w.offset, + Total: w.total, + StartedAt: w.startedAt, + UpdatedAt: w.updatedAt, + }, nil +} + +func (w *bufferedWriter) Digest() digest.Digest { + return w.digester.Digest() +} + +func (w *bufferedWriter) Commit(ctx context.Context, size int64, expected digest.Digest, opt ...content.Opt) error { + if w.buffer == nil { + return errors.Errorf("can't commit already committed or closed") + } + if s := int64(w.buffer.Len()); size > 0 && size != s { + return errors.Errorf("unexpected commit size %d, expected %d", s, size) + } + dgst := w.digester.Digest() + if expected != "" && expected != dgst { + return errors.Errorf("unexpected digest: %v != %v", dgst, expected) + } + if w.expected != "" && w.expected != dgst { + return errors.Errorf("unexpected digest: %v != %v", dgst, w.expected) + } + w.main.addValue(dgst, w.buffer.Bytes()) + return w.Close() +} + +func (w *bufferedWriter) Truncate(size int64) error { + if size != 0 { + return errors.New("Truncate: unsupported size") + } + w.offset = 0 + w.digester.Hash().Reset() + w.buffer.Reset() + return nil +} diff --git a/vendor/github.com/moby/buildkit/util/contentutil/copy.go b/vendor/github.com/moby/buildkit/util/contentutil/copy.go new file mode 100644 index 0000000000000..a03f16e65e26f --- /dev/null +++ b/vendor/github.com/moby/buildkit/util/contentutil/copy.go @@ -0,0 +1,43 @@ +package contentutil + +import ( + "context" + "io" + + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/remotes" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" +) + +func Copy(ctx context.Context, ingester content.Ingester, provider content.Provider, desc ocispec.Descriptor) error { + if _, err := remotes.FetchHandler(ingester, &localFetcher{provider})(ctx, desc); err != nil { + return err + } + return nil +} + +type localFetcher struct { + content.Provider +} + +func (f *localFetcher) Fetch(ctx context.Context, desc ocispec.Descriptor) (io.ReadCloser, error) { + r, err := f.Provider.ReaderAt(ctx, desc) + if err != nil { + return nil, err + } + return &rc{ReaderAt: r}, nil +} + +type rc struct { + content.ReaderAt + offset int +} + +func (r *rc) Read(b []byte) (int, error) { + n, err := r.ReadAt(b, int64(r.offset)) + r.offset += n + if n > 0 && err == io.EOF { + err = nil + } + return n, err +} diff --git a/vendor/github.com/moby/buildkit/util/contentutil/fetcher.go b/vendor/github.com/moby/buildkit/util/contentutil/fetcher.go new file mode 100644 index 0000000000000..0c87e6476027a --- /dev/null +++ b/vendor/github.com/moby/buildkit/util/contentutil/fetcher.go @@ -0,0 +1,70 @@ +package contentutil + +import ( + "context" + "io" + + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/remotes" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +func FromFetcher(f remotes.Fetcher) content.Provider { + return &fetchedProvider{ + f: f, + } +} + +type fetchedProvider struct { + f remotes.Fetcher +} + +func (p *fetchedProvider) ReaderAt(ctx context.Context, desc ocispec.Descriptor) (content.ReaderAt, error) { + rc, err := p.f.Fetch(ctx, desc) + if err != nil { + return nil, err + } + + return &readerAt{Reader: rc, Closer: rc, size: desc.Size}, nil +} + +type readerAt struct { + io.Reader + io.Closer + size int64 + offset int64 +} + +func (r *readerAt) ReadAt(b []byte, off int64) (int, error) { + if ra, ok := r.Reader.(io.ReaderAt); ok { + return ra.ReadAt(b, off) + } + + if r.offset != off { + if seeker, ok := r.Reader.(io.Seeker); ok { + if _, err := seeker.Seek(off, io.SeekStart); err != nil { + return 0, err + } + r.offset = off + } else { + return 0, errors.Errorf("unsupported offset") + } + } + + var totalN int + for len(b) > 0 { + n, err := r.Reader.Read(b) + r.offset += int64(n) + totalN += n + b = b[n:] + if err != nil { + return totalN, err + } + } + return totalN, nil +} + +func (r *readerAt) Size() int64 { + return r.size +} diff --git a/vendor/github.com/moby/buildkit/util/contentutil/multiprovider.go b/vendor/github.com/moby/buildkit/util/contentutil/multiprovider.go new file mode 100644 index 0000000000000..fd635ad456967 --- /dev/null +++ b/vendor/github.com/moby/buildkit/util/contentutil/multiprovider.go @@ -0,0 +1,44 @@ +package contentutil + +import ( + "context" + "sync" + + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/errdefs" + digest "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +func NewMultiProvider(base content.Provider) *MultiProvider { + return &MultiProvider{ + base: base, + sub: map[digest.Digest]content.Provider{}, + } +} + +type MultiProvider struct { + mu sync.RWMutex + base content.Provider + sub map[digest.Digest]content.Provider +} + +func (mp *MultiProvider) ReaderAt(ctx context.Context, desc ocispec.Descriptor) (content.ReaderAt, error) { + mp.mu.RLock() + if p, ok := mp.sub[desc.Digest]; ok { + mp.mu.RUnlock() + return p.ReaderAt(ctx, desc) + } + mp.mu.RUnlock() + if mp.base == nil { + return nil, errors.Wrapf(errdefs.ErrNotFound, "content %v", desc.Digest) + } + return mp.base.ReaderAt(ctx, desc) +} + +func (mp *MultiProvider) Add(dgst digest.Digest, p content.Provider) { + mp.mu.Lock() + defer mp.mu.Unlock() + mp.sub[dgst] = p +} diff --git a/vendor/github.com/moby/buildkit/util/contentutil/pusher.go b/vendor/github.com/moby/buildkit/util/contentutil/pusher.go new file mode 100644 index 0000000000000..ab88128aa2add --- /dev/null +++ b/vendor/github.com/moby/buildkit/util/contentutil/pusher.go @@ -0,0 +1,58 @@ +package contentutil + +import ( + "context" + + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/remotes" + "github.com/pkg/errors" +) + +func FromPusher(p remotes.Pusher) content.Ingester { + return &pushingIngester{ + p: p, + } +} + +type pushingIngester struct { + p remotes.Pusher +} + +// Writer implements content.Ingester. desc.MediaType must be set for manifest blobs. +func (i *pushingIngester) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) { + var wOpts content.WriterOpts + for _, opt := range opts { + if err := opt(&wOpts); err != nil { + return nil, err + } + } + if wOpts.Ref == "" { + return nil, errors.Wrap(errdefs.ErrInvalidArgument, "ref must not be empty") + } + // pusher requires desc.MediaType to determine the PUT URL, especially for manifest blobs. + contentWriter, err := i.p.Push(ctx, wOpts.Desc) + if err != nil { + return nil, err + } + return &writer{ + Writer: contentWriter, + contentWriterRef: wOpts.Ref, + }, nil +} + +type writer struct { + content.Writer // returned from pusher.Push + contentWriterRef string // ref passed for Writer() +} + +func (w *writer) Status() (content.Status, error) { + st, err := w.Writer.Status() + if err != nil { + return st, err + } + if w.contentWriterRef != "" { + st.Ref = w.contentWriterRef + } + return st, nil +} diff --git a/vendor/github.com/moby/buildkit/util/flightcontrol/flightcontrol.go b/vendor/github.com/moby/buildkit/util/flightcontrol/flightcontrol.go new file mode 100644 index 0000000000000..e51824a4f3606 --- /dev/null +++ b/vendor/github.com/moby/buildkit/util/flightcontrol/flightcontrol.go @@ -0,0 +1,324 @@ +package flightcontrol + +import ( + "context" + "io" + "runtime" + "sort" + "sync" + "time" + + "github.com/moby/buildkit/util/progress" + "github.com/pkg/errors" +) + +// flightcontrol is like singleflight but with support for cancellation and +// nested progress reporting + +var errRetry = errors.Errorf("retry") + +type contextKeyT string + +var contextKey = contextKeyT("buildkit/util/flightcontrol.progress") + +type Group struct { + mu sync.Mutex // protects m + m map[string]*call // lazily initialized +} + +func (g *Group) Do(ctx context.Context, key string, fn func(ctx context.Context) (interface{}, error)) (v interface{}, err error) { + defer func() { + if errors.Cause(err) == errRetry { + runtime.Gosched() + v, err = g.Do(ctx, key, fn) + } + }() + g.mu.Lock() + if g.m == nil { + g.m = make(map[string]*call) + } + + if c, ok := g.m[key]; ok { // register 2nd waiter + g.mu.Unlock() + return c.wait(ctx) + } + + c := newCall(fn) + g.m[key] = c + go func() { + // cleanup after a caller has returned + <-c.ready + g.mu.Lock() + delete(g.m, key) + g.mu.Unlock() + }() + g.mu.Unlock() + return c.wait(ctx) +} + +type call struct { + mu sync.Mutex + result interface{} + err error + ready chan struct{} + + ctx *sharedContext + ctxs []context.Context + fn func(ctx context.Context) (interface{}, error) + once sync.Once + + closeProgressWriter func() + progressState *progressState + progressCtx context.Context +} + +func newCall(fn func(ctx context.Context) (interface{}, error)) *call { + c := &call{ + fn: fn, + ready: make(chan struct{}), + progressState: newProgressState(), + } + ctx := newContext(c) // newSharedContext + pr, pctx, closeProgressWriter := progress.NewContext(context.Background()) + + c.progressCtx = pctx + c.ctx = ctx + c.closeProgressWriter = closeProgressWriter + + go c.progressState.run(pr) // TODO: remove this, wrap writer instead + + return c +} + +func (c *call) run() { + defer c.closeProgressWriter() + v, err := c.fn(c.ctx) + c.mu.Lock() + c.result = v + c.err = err + c.mu.Unlock() + close(c.ready) +} + +func (c *call) wait(ctx context.Context) (v interface{}, err error) { + c.mu.Lock() + // detect case where caller has just returned, let it clean up before + select { + case <-c.ready: // could return if no error + c.mu.Unlock() + return nil, errRetry + default: + } + + pw, ok, ctx := progress.FromContext(ctx) + if ok { + c.progressState.add(pw) + } + c.ctxs = append(c.ctxs, ctx) + + c.mu.Unlock() + + go c.once.Do(c.run) + + select { + case <-ctx.Done(): + select { + case <-c.ctx.Done(): + // if this cancelled the last context, then wait for function to shut down + // and don't accept any more callers + <-c.ready + return c.result, c.err + default: + if ok { + c.progressState.close(pw) + } + return nil, ctx.Err() + } + case <-c.ready: + return c.result, c.err // shared not implemented yet + } +} + +func (c *call) Deadline() (deadline time.Time, ok bool) { + c.mu.Lock() + defer c.mu.Unlock() + for _, ctx := range c.ctxs { + select { + case <-ctx.Done(): + default: + dl, ok := ctx.Deadline() + if ok { + return dl, ok + } + } + } + return time.Time{}, false +} + +func (c *call) Done() <-chan struct{} { + c.mu.Lock() + c.ctx.signal() + c.mu.Unlock() + return c.ctx.done +} + +func (c *call) Err() error { + select { + case <-c.ctx.Done(): + return c.ctx.err + default: + return nil + } +} + +func (c *call) Value(key interface{}) interface{} { + if key == contextKey { + return c.progressState + } + c.mu.Lock() + defer c.mu.Unlock() + + ctx := c.progressCtx + select { + case <-ctx.Done(): + default: + if v := ctx.Value(key); v != nil { + return v + } + } + + if len(c.ctxs) > 0 { + ctx = c.ctxs[0] + select { + case <-ctx.Done(): + default: + if v := ctx.Value(key); v != nil { + return v + } + } + } + + return nil +} + +type sharedContext struct { + *call + done chan struct{} + err error +} + +func newContext(c *call) *sharedContext { + return &sharedContext{call: c, done: make(chan struct{})} +} + +// call with lock +func (c *sharedContext) signal() { + select { + case <-c.done: + default: + var err error + for _, ctx := range c.ctxs { + select { + case <-ctx.Done(): + err = ctx.Err() + default: + return + } + } + c.err = err + close(c.done) + } +} + +type rawProgressWriter interface { + WriteRawProgress(*progress.Progress) error + Close() error +} + +type progressState struct { + mu sync.Mutex + items map[string]*progress.Progress + writers []rawProgressWriter + done bool +} + +func newProgressState() *progressState { + return &progressState{ + items: make(map[string]*progress.Progress), + } +} + +func (ps *progressState) run(pr progress.Reader) { + for { + p, err := pr.Read(context.TODO()) + if err != nil { + if err == io.EOF { + ps.mu.Lock() + ps.done = true + ps.mu.Unlock() + for _, w := range ps.writers { + w.Close() + } + } + return + } + ps.mu.Lock() + for _, p := range p { + for _, w := range ps.writers { + w.WriteRawProgress(p) + } + ps.items[p.ID] = p + } + ps.mu.Unlock() + } +} + +func (ps *progressState) add(pw progress.Writer) { + rw, ok := pw.(rawProgressWriter) + if !ok { + return + } + ps.mu.Lock() + plist := make([]*progress.Progress, 0, len(ps.items)) + for _, p := range ps.items { + plist = append(plist, p) + } + sort.Slice(plist, func(i, j int) bool { + return plist[i].Timestamp.Before(plist[j].Timestamp) + }) + for _, p := range plist { + rw.WriteRawProgress(p) + } + if ps.done { + rw.Close() + } else { + ps.writers = append(ps.writers, rw) + } + ps.mu.Unlock() +} + +func (ps *progressState) close(pw progress.Writer) { + rw, ok := pw.(rawProgressWriter) + if !ok { + return + } + ps.mu.Lock() + for i, w := range ps.writers { + if w == rw { + w.Close() + ps.writers = append(ps.writers[:i], ps.writers[i+1:]...) + break + } + } + ps.mu.Unlock() +} + +func WriteProgress(ctx context.Context, pw progress.Writer) error { + v := ctx.Value(contextKey) + p, ok := v.(*progressState) + if !ok { + return errors.Errorf("invalid context not from flightcontrol") + } + p.add(pw) + return nil +} diff --git a/vendor/github.com/moby/buildkit/util/imageutil/config.go b/vendor/github.com/moby/buildkit/util/imageutil/config.go new file mode 100644 index 0000000000000..356ed53cea017 --- /dev/null +++ b/vendor/github.com/moby/buildkit/util/imageutil/config.go @@ -0,0 +1,159 @@ +package imageutil + +import ( + "context" + "encoding/json" + + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/images" + "github.com/containerd/containerd/platforms" + "github.com/containerd/containerd/reference" + "github.com/containerd/containerd/remotes" + digest "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +type IngesterProvider interface { + content.Ingester + content.Provider +} + +func Config(ctx context.Context, str string, resolver remotes.Resolver, ingester IngesterProvider, platform *specs.Platform) (digest.Digest, []byte, error) { + // TODO: fix containerd to take struct instead of string + platformStr := platforms.Default() + if platform != nil { + platformStr = platforms.Format(*platform) + } + ref, err := reference.Parse(str) + if err != nil { + return "", nil, errors.WithStack(err) + } + + desc := specs.Descriptor{ + Digest: ref.Digest(), + } + if desc.Digest != "" { + ra, err := ingester.ReaderAt(ctx, desc) + if err == nil { + desc.Size = ra.Size() + mt, err := DetectManifestMediaType(ra) + if err == nil { + desc.MediaType = mt + } + } + } + // use resolver if desc is incomplete + if desc.MediaType == "" { + _, desc, err = resolver.Resolve(ctx, ref.String()) + if err != nil { + return "", nil, err + } + } + + fetcher, err := resolver.Fetcher(ctx, ref.String()) + if err != nil { + return "", nil, err + } + + handlers := []images.Handler{ + remotes.FetchHandler(ingester, fetcher), + childrenConfigHandler(ingester, platformStr), + } + if err := images.Dispatch(ctx, images.Handlers(handlers...), desc); err != nil { + return "", nil, err + } + config, err := images.Config(ctx, ingester, desc, platformStr) + if err != nil { + return "", nil, err + } + + dt, err := content.ReadBlob(ctx, ingester, config) + if err != nil { + return "", nil, err + } + + return desc.Digest, dt, nil +} + +func childrenConfigHandler(provider content.Provider, platform string) images.HandlerFunc { + return func(ctx context.Context, desc specs.Descriptor) ([]specs.Descriptor, error) { + var descs []specs.Descriptor + switch desc.MediaType { + case images.MediaTypeDockerSchema2Manifest, specs.MediaTypeImageManifest: + p, err := content.ReadBlob(ctx, provider, desc) + if err != nil { + return nil, err + } + + // TODO(stevvooe): We just assume oci manifest, for now. There may be + // subtle differences from the docker version. + var manifest specs.Manifest + if err := json.Unmarshal(p, &manifest); err != nil { + return nil, err + } + + descs = append(descs, manifest.Config) + case images.MediaTypeDockerSchema2ManifestList, specs.MediaTypeImageIndex: + p, err := content.ReadBlob(ctx, provider, desc) + if err != nil { + return nil, err + } + + var index specs.Index + if err := json.Unmarshal(p, &index); err != nil { + return nil, err + } + + if platform != "" { + pf, err := platforms.Parse(platform) + if err != nil { + return nil, err + } + matcher := platforms.NewMatcher(pf) + + for _, d := range index.Manifests { + if d.Platform == nil || matcher.Match(*d.Platform) { + descs = append(descs, d) + } + } + } else { + descs = append(descs, index.Manifests...) + } + case images.MediaTypeDockerSchema2Config, specs.MediaTypeImageConfig: + // childless data types. + return nil, nil + default: + return nil, errors.Errorf("encountered unknown type %v; children may not be fetched", desc.MediaType) + } + + return descs, nil + } +} + +// specs.MediaTypeImageManifest, // TODO: detect schema1/manifest-list +func DetectManifestMediaType(ra content.ReaderAt) (string, error) { + // TODO: schema1 + + p := make([]byte, ra.Size()) + if _, err := ra.ReadAt(p, 0); err != nil { + return "", err + } + + var mfst struct { + 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 + } + return images.MediaTypeDockerSchema2ManifestList, nil +} diff --git a/vendor/github.com/moby/buildkit/util/progress/logs/logs.go b/vendor/github.com/moby/buildkit/util/progress/logs/logs.go new file mode 100644 index 0000000000000..54f6ff89657cf --- /dev/null +++ b/vendor/github.com/moby/buildkit/util/progress/logs/logs.go @@ -0,0 +1,53 @@ +package logs + +import ( + "context" + "io" + "os" + + "github.com/moby/buildkit/client" + "github.com/moby/buildkit/identity" + "github.com/moby/buildkit/util/progress" + "github.com/pkg/errors" +) + +func NewLogStreams(ctx context.Context, printOutput bool) (io.WriteCloser, io.WriteCloser) { + return newStreamWriter(ctx, 1, printOutput), newStreamWriter(ctx, 2, printOutput) +} + +func newStreamWriter(ctx context.Context, stream int, printOutput bool) io.WriteCloser { + pw, _, _ := progress.FromContext(ctx) + return &streamWriter{ + pw: pw, + stream: stream, + printOutput: printOutput, + } +} + +type streamWriter struct { + pw progress.Writer + stream int + printOutput bool +} + +func (sw *streamWriter) Write(dt []byte) (int, error) { + sw.pw.Write(identity.NewID(), client.VertexLog{ + Stream: sw.stream, + Data: append([]byte{}, dt...), + }) + if sw.printOutput { + switch sw.stream { + case 1: + return os.Stdout.Write(dt) + case 2: + return os.Stderr.Write(dt) + default: + return 0, errors.Errorf("invalid stream %d", sw.stream) + } + } + return len(dt), nil +} + +func (sw *streamWriter) Close() error { + return sw.pw.Close() +} diff --git a/vendor/github.com/moby/buildkit/util/progress/multireader.go b/vendor/github.com/moby/buildkit/util/progress/multireader.go new file mode 100644 index 0000000000000..2bd3f2ca8616c --- /dev/null +++ b/vendor/github.com/moby/buildkit/util/progress/multireader.go @@ -0,0 +1,77 @@ +package progress + +import ( + "context" + "io" + "sync" +) + +type MultiReader struct { + mu sync.Mutex + main Reader + initialized bool + done chan struct{} + writers map[*progressWriter]func() +} + +func NewMultiReader(pr Reader) *MultiReader { + mr := &MultiReader{ + main: pr, + writers: make(map[*progressWriter]func()), + done: make(chan struct{}), + } + return mr +} + +func (mr *MultiReader) Reader(ctx context.Context) Reader { + mr.mu.Lock() + defer mr.mu.Unlock() + + pr, ctx, closeWriter := NewContext(ctx) + pw, _, ctx := FromContext(ctx) + + w := pw.(*progressWriter) + mr.writers[w] = closeWriter + + go func() { + select { + case <-ctx.Done(): + case <-mr.done: + } + mr.mu.Lock() + defer mr.mu.Unlock() + delete(mr.writers, w) + }() + + if !mr.initialized { + go mr.handle() + mr.initialized = true + } + + return pr +} + +func (mr *MultiReader) handle() error { + for { + p, err := mr.main.Read(context.TODO()) + if err != nil { + if err == io.EOF { + mr.mu.Lock() + for w, c := range mr.writers { + w.Close() + c() + } + mr.mu.Unlock() + return nil + } + return err + } + mr.mu.Lock() + for _, p := range p { + for w := range mr.writers { + w.writeRawProgress(p) + } + } + mr.mu.Unlock() + } +} diff --git a/vendor/github.com/moby/buildkit/util/progress/multiwriter.go b/vendor/github.com/moby/buildkit/util/progress/multiwriter.go new file mode 100644 index 0000000000000..51989368ce5de --- /dev/null +++ b/vendor/github.com/moby/buildkit/util/progress/multiwriter.go @@ -0,0 +1,105 @@ +package progress + +import ( + "sort" + "sync" + "time" +) + +type rawProgressWriter interface { + WriteRawProgress(*Progress) error + Close() error +} + +type MultiWriter struct { + mu sync.Mutex + items []*Progress + writers map[rawProgressWriter]struct{} + done bool + meta map[string]interface{} +} + +func NewMultiWriter(opts ...WriterOption) *MultiWriter { + mw := &MultiWriter{ + writers: map[rawProgressWriter]struct{}{}, + meta: map[string]interface{}{}, + } + for _, o := range opts { + o(mw) + } + return mw +} + +func (ps *MultiWriter) Add(pw Writer) { + rw, ok := pw.(rawProgressWriter) + if !ok { + return + } + ps.mu.Lock() + plist := make([]*Progress, 0, len(ps.items)) + for _, p := range ps.items { + plist = append(plist, p) + } + sort.Slice(plist, func(i, j int) bool { + return plist[i].Timestamp.Before(plist[j].Timestamp) + }) + for _, p := range plist { + rw.WriteRawProgress(p) + } + ps.writers[rw] = struct{}{} + ps.mu.Unlock() +} + +func (ps *MultiWriter) Delete(pw Writer) { + rw, ok := pw.(rawProgressWriter) + if !ok { + return + } + + ps.mu.Lock() + delete(ps.writers, rw) + ps.mu.Unlock() +} + +func (ps *MultiWriter) Write(id string, v interface{}) error { + p := &Progress{ + ID: id, + Timestamp: time.Now(), + Sys: v, + meta: ps.meta, + } + return ps.WriteRawProgress(p) +} + +func (ps *MultiWriter) WriteRawProgress(p *Progress) error { + meta := p.meta + if len(ps.meta) > 0 { + meta = map[string]interface{}{} + for k, v := range p.meta { + meta[k] = v + } + for k, v := range ps.meta { + if _, ok := meta[k]; !ok { + meta[k] = v + } + } + } + p.meta = meta + return ps.writeRawProgress(p) +} + +func (ps *MultiWriter) writeRawProgress(p *Progress) error { + ps.mu.Lock() + defer ps.mu.Unlock() + ps.items = append(ps.items, p) + for w := range ps.writers { + if err := w.WriteRawProgress(p); err != nil { + return err + } + } + return nil +} + +func (ps *MultiWriter) Close() error { + return nil +} diff --git a/vendor/github.com/moby/buildkit/util/progress/progress.go b/vendor/github.com/moby/buildkit/util/progress/progress.go new file mode 100644 index 0000000000000..b802716bf7837 --- /dev/null +++ b/vendor/github.com/moby/buildkit/util/progress/progress.go @@ -0,0 +1,252 @@ +package progress + +import ( + "context" + "io" + "sort" + "sync" + "time" + + "github.com/pkg/errors" +) + +// Progress package provides utility functions for using the context to capture +// progress of a running function. All progress items written contain an ID +// that is used to collapse unread messages. + +type contextKeyT string + +var contextKey = contextKeyT("buildkit/util/progress") + +// FromContext returns a progress writer from a context. +func FromContext(ctx context.Context, opts ...WriterOption) (Writer, bool, context.Context) { + v := ctx.Value(contextKey) + pw, ok := v.(*progressWriter) + if !ok { + if pw, ok := v.(*MultiWriter); ok { + return pw, true, ctx + } + return &noOpWriter{}, false, ctx + } + pw = newWriter(pw) + for _, o := range opts { + o(pw) + } + ctx = context.WithValue(ctx, contextKey, pw) + return pw, true, ctx +} + +type WriterOption func(Writer) + +// NewContext returns a new context and a progress reader that captures all +// progress items writtern to this context. Last returned parameter is a closer +// function to signal that no new writes will happen to this context. +func NewContext(ctx context.Context) (Reader, context.Context, func()) { + pr, pw, cancel := pipe() + ctx = WithProgress(ctx, pw) + return pr, ctx, cancel +} + +func WithProgress(ctx context.Context, pw Writer) context.Context { + return context.WithValue(ctx, contextKey, pw) +} + +func WithMetadata(key string, val interface{}) WriterOption { + return func(w Writer) { + if pw, ok := w.(*progressWriter); ok { + pw.meta[key] = val + } + if pw, ok := w.(*MultiWriter); ok { + pw.meta[key] = val + } + } +} + +type Writer interface { + Write(id string, value interface{}) error + Close() error +} + +type Reader interface { + Read(context.Context) ([]*Progress, error) +} + +type Progress struct { + ID string + Timestamp time.Time + Sys interface{} + meta map[string]interface{} +} + +type Status struct { + Action string + Current int + Total int + Started *time.Time + Completed *time.Time +} + +type progressReader struct { + ctx context.Context + cond *sync.Cond + mu sync.Mutex + writers map[*progressWriter]struct{} + dirty map[string]*Progress +} + +func (pr *progressReader) Read(ctx context.Context) ([]*Progress, error) { + done := make(chan struct{}) + defer close(done) + go func() { + select { + case <-done: + case <-ctx.Done(): + pr.cond.Broadcast() + } + }() + pr.mu.Lock() + for { + select { + case <-ctx.Done(): + pr.mu.Unlock() + return nil, ctx.Err() + default: + } + dmap := pr.dirty + if len(dmap) == 0 { + select { + case <-pr.ctx.Done(): + if len(pr.writers) == 0 { + pr.mu.Unlock() + return nil, io.EOF + } + default: + } + pr.cond.Wait() + continue + } + pr.dirty = make(map[string]*Progress) + pr.mu.Unlock() + + out := make([]*Progress, 0, len(dmap)) + for _, p := range dmap { + out = append(out, p) + } + + sort.Slice(out, func(i, j int) bool { + return out[i].Timestamp.Before(out[j].Timestamp) + }) + + return out, nil + } +} + +func (pr *progressReader) append(pw *progressWriter) { + pr.mu.Lock() + defer pr.mu.Unlock() + + select { + case <-pr.ctx.Done(): + return + default: + pr.writers[pw] = struct{}{} + } +} + +func pipe() (*progressReader, *progressWriter, func()) { + ctx, cancel := context.WithCancel(context.Background()) + pr := &progressReader{ + ctx: ctx, + writers: make(map[*progressWriter]struct{}), + dirty: make(map[string]*Progress), + } + pr.cond = sync.NewCond(&pr.mu) + go func() { + <-ctx.Done() + pr.cond.Broadcast() + }() + pw := &progressWriter{ + reader: pr, + } + return pr, pw, cancel +} + +func newWriter(pw *progressWriter) *progressWriter { + meta := make(map[string]interface{}) + for k, v := range pw.meta { + meta[k] = v + } + pw = &progressWriter{ + reader: pw.reader, + meta: meta, + } + pw.reader.append(pw) + return pw +} + +type progressWriter struct { + done bool + reader *progressReader + meta map[string]interface{} +} + +func (pw *progressWriter) Write(id string, v interface{}) error { + if pw.done { + return errors.Errorf("writing %s to closed progress writer", id) + } + return pw.writeRawProgress(&Progress{ + ID: id, + Timestamp: time.Now(), + Sys: v, + meta: pw.meta, + }) +} + +func (pw *progressWriter) WriteRawProgress(p *Progress) error { + meta := p.meta + if len(pw.meta) > 0 { + meta = map[string]interface{}{} + for k, v := range p.meta { + meta[k] = v + } + for k, v := range pw.meta { + if _, ok := meta[k]; !ok { + meta[k] = v + } + } + } + p.meta = meta + return pw.writeRawProgress(p) +} + +func (pw *progressWriter) writeRawProgress(p *Progress) error { + pw.reader.mu.Lock() + pw.reader.dirty[p.ID] = p + pw.reader.cond.Broadcast() + pw.reader.mu.Unlock() + return nil +} + +func (pw *progressWriter) Close() error { + pw.reader.mu.Lock() + delete(pw.reader.writers, pw) + pw.reader.mu.Unlock() + pw.reader.cond.Broadcast() + pw.done = true + return nil +} + +func (p *Progress) Meta(key string) (interface{}, bool) { + v, ok := p.meta[key] + return v, ok +} + +type noOpWriter struct{} + +func (pw *noOpWriter) Write(_ string, _ interface{}) error { + return nil +} + +func (pw *noOpWriter) Close() error { + return nil +} diff --git a/vendor/github.com/moby/buildkit/util/rootless/specconv/specconv_linux.go b/vendor/github.com/moby/buildkit/util/rootless/specconv/specconv_linux.go new file mode 100644 index 0000000000000..1b90219f1dec4 --- /dev/null +++ b/vendor/github.com/moby/buildkit/util/rootless/specconv/specconv_linux.go @@ -0,0 +1,113 @@ +package specconv + +import ( + "os" + "sort" + "strings" + + "github.com/opencontainers/runc/libcontainer/system" + "github.com/opencontainers/runc/libcontainer/user" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" +) + +// ToRootless converts spec to be compatible with "rootless" runc. +// * Adds userns (Note: since we are already in userns, ideally we should not need to do this. runc-side issue is tracked at https://github.com/opencontainers/runc/issues/1837) +// * Fix up mount flags (same as above) +// * Replace /sys with bind-mount (FIXME: we don't need to do this if netns is unshared) +func ToRootless(spec *specs.Spec) error { + if !system.RunningInUserNS() { + return errors.New("needs to be in user namespace") + } + uidMap, err := user.CurrentProcessUIDMap() + if err != nil && !os.IsNotExist(err) { + return err + } + gidMap, err := user.CurrentProcessUIDMap() + if err != nil && !os.IsNotExist(err) { + return err + } + return toRootless(spec, uidMap, gidMap) +} + +// toRootless was forked from github.com/opencontainers/runc/libcontainer/specconv +func toRootless(spec *specs.Spec, uidMap, gidMap []user.IDMap) error { + if err := configureUserNS(spec, uidMap, gidMap); err != nil { + return err + } + if err := configureMounts(spec); err != nil { + return err + } + + // Remove cgroup settings. + spec.Linux.Resources = nil + spec.Linux.CgroupsPath = "" + return nil +} + +// configureUserNS add suserns and the current ID map to the spec. +// Since we are already in userns, ideally we should not need to add userns. +// However, currently rootless runc always requires userns to be added. +// https://github.com/opencontainers/runc/issues/1837 +func configureUserNS(spec *specs.Spec, uidMap, gidMap []user.IDMap) error { + spec.Linux.Namespaces = append(spec.Linux.Namespaces, specs.LinuxNamespace{ + Type: specs.UserNamespace, + }) + + sort.Slice(uidMap, func(i, j int) bool { return uidMap[i].ID < uidMap[j].ID }) + uNextContainerID := int64(0) + for _, u := range uidMap { + spec.Linux.UIDMappings = append(spec.Linux.UIDMappings, + specs.LinuxIDMapping{ + HostID: uint32(u.ID), + ContainerID: uint32(uNextContainerID), + Size: uint32(u.Count), + }) + uNextContainerID += int64(u.Count) + } + sort.Slice(gidMap, func(i, j int) bool { return gidMap[i].ID < gidMap[j].ID }) + gNextContainerID := int64(0) + for _, g := range gidMap { + spec.Linux.GIDMappings = append(spec.Linux.GIDMappings, + specs.LinuxIDMapping{ + HostID: uint32(g.ID), + ContainerID: uint32(gNextContainerID), + Size: uint32(g.Count), + }) + gNextContainerID += int64(g.Count) + } + return nil +} + +func configureMounts(spec *specs.Spec) error { + var mounts []specs.Mount + for _, mount := range spec.Mounts { + // Ignore all mounts that are under /sys, because we add /sys later. + if strings.HasPrefix(mount.Destination, "/sys") { + continue + } + + // Remove all gid= and uid= mappings. + // Since we are already in userns, ideally we should not need to do this. + // https://github.com/opencontainers/runc/issues/1837 + var options []string + for _, option := range mount.Options { + if !strings.HasPrefix(option, "gid=") && !strings.HasPrefix(option, "uid=") { + options = append(options, option) + } + } + mount.Options = options + mounts = append(mounts, mount) + } + + // Add the sysfs mount as an rbind, because we can't mount /sys unless we have netns. + // TODO: keep original /sys mount when we have netns. + mounts = append(mounts, specs.Mount{ + Source: "/sys", + Destination: "/sys", + Type: "none", + Options: []string{"rbind", "nosuid", "noexec", "nodev", "ro"}, + }) + spec.Mounts = mounts + return nil +} diff --git a/vendor/github.com/moby/buildkit/util/system/path_unix.go b/vendor/github.com/moby/buildkit/util/system/path_unix.go new file mode 100644 index 0000000000000..c607c4db09f20 --- /dev/null +++ b/vendor/github.com/moby/buildkit/util/system/path_unix.go @@ -0,0 +1,14 @@ +// +build !windows + +package system + +// DefaultPathEnv is unix style list of directories to search for +// executables. Each directory is separated from the next by a colon +// ':' character . +const DefaultPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + +// CheckSystemDriveAndRemoveDriveLetter verifies that a path, if it includes a drive letter, +// is the system drive. This is a no-op on Linux. +func CheckSystemDriveAndRemoveDriveLetter(path string) (string, error) { + return path, nil +} diff --git a/vendor/github.com/moby/buildkit/util/system/path_windows.go b/vendor/github.com/moby/buildkit/util/system/path_windows.go new file mode 100644 index 0000000000000..cbfe2c1576ceb --- /dev/null +++ b/vendor/github.com/moby/buildkit/util/system/path_windows.go @@ -0,0 +1,37 @@ +// +build windows + +package system + +import ( + "fmt" + "path/filepath" + "strings" +) + +// DefaultPathEnv is deliberately empty on Windows as the default path will be set by +// the container. Docker has no context of what the default path should be. +const DefaultPathEnv = "" + +// CheckSystemDriveAndRemoveDriveLetter verifies and manipulates a Windows path. +// This is used, for example, when validating a user provided path in docker cp. +// If a drive letter is supplied, it must be the system drive. The drive letter +// is always removed. Also, it translates it to OS semantics (IOW / to \). We +// need the path in this syntax so that it can ultimately be contatenated with +// a Windows long-path which doesn't support drive-letters. Examples: +// C: --> Fail +// C:\ --> \ +// a --> a +// /a --> \a +// d:\ --> Fail +func CheckSystemDriveAndRemoveDriveLetter(path string) (string, error) { + if len(path) == 2 && string(path[1]) == ":" { + return "", fmt.Errorf("No relative path specified in %q", path) + } + if !filepath.IsAbs(path) || len(path) < 2 { + return filepath.FromSlash(path), nil + } + if string(path[1]) == ":" && !strings.EqualFold(string(path[0]), "c") { + return "", fmt.Errorf("The specified path is not on the system drive (C:)") + } + return filepath.FromSlash(path[2:]), nil +} diff --git a/vendor/github.com/moby/buildkit/util/system/seccomp_linux.go b/vendor/github.com/moby/buildkit/util/system/seccomp_linux.go new file mode 100644 index 0000000000000..62afa03fef032 --- /dev/null +++ b/vendor/github.com/moby/buildkit/util/system/seccomp_linux.go @@ -0,0 +1,29 @@ +// +build linux,seccomp + +package system + +import ( + "sync" + + "golang.org/x/sys/unix" +) + +var seccompSupported bool +var seccompOnce sync.Once + +func SeccompSupported() bool { + seccompOnce.Do(func() { + seccompSupported = getSeccompSupported() + }) + return seccompSupported +} + +func getSeccompSupported() bool { + if err := unix.Prctl(unix.PR_GET_SECCOMP, 0, 0, 0, 0); err != unix.EINVAL { + // Make sure the kernel has CONFIG_SECCOMP_FILTER. + if err := unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0); err != unix.EINVAL { + return true + } + } + return false +} diff --git a/vendor/github.com/moby/buildkit/util/system/seccomp_nolinux.go b/vendor/github.com/moby/buildkit/util/system/seccomp_nolinux.go new file mode 100644 index 0000000000000..e348c379a903f --- /dev/null +++ b/vendor/github.com/moby/buildkit/util/system/seccomp_nolinux.go @@ -0,0 +1,7 @@ +// +build !linux,seccomp + +package system + +func SeccompSupported() bool { + return false +} diff --git a/vendor/github.com/moby/buildkit/util/system/seccomp_noseccomp.go b/vendor/github.com/moby/buildkit/util/system/seccomp_noseccomp.go new file mode 100644 index 0000000000000..84cfb7fa83987 --- /dev/null +++ b/vendor/github.com/moby/buildkit/util/system/seccomp_noseccomp.go @@ -0,0 +1,7 @@ +// +build !seccomp + +package system + +func SeccompSupported() bool { + return false +} diff --git a/vendor/github.com/moby/buildkit/util/tracing/tracing.go b/vendor/github.com/moby/buildkit/util/tracing/tracing.go new file mode 100644 index 0000000000000..6af2b8c55e392 --- /dev/null +++ b/vendor/github.com/moby/buildkit/util/tracing/tracing.go @@ -0,0 +1,109 @@ +package tracing + +import ( + "context" + "fmt" + "io" + "net/http" + + "github.com/opentracing-contrib/go-stdlib/nethttp" + opentracing "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/opentracing/opentracing-go/log" +) + +// StartSpan starts a new span as a child of the span in context. +// If there is no span in context then this is a no-op. +// The difference from opentracing.StartSpanFromContext is that this method +// does not depend on global tracer. +func StartSpan(ctx context.Context, operationName string, opts ...opentracing.StartSpanOption) (opentracing.Span, context.Context) { + parent := opentracing.SpanFromContext(ctx) + tracer := opentracing.Tracer(&opentracing.NoopTracer{}) + if parent != nil { + tracer = parent.Tracer() + opts = append(opts, opentracing.ChildOf(parent.Context())) + } + span := tracer.StartSpan(operationName, opts...) + if parent != nil { + return span, opentracing.ContextWithSpan(ctx, span) + } + return span, ctx +} + +// FinishWithError finalizes the span and sets the error if one is passed +func FinishWithError(span opentracing.Span, err error) { + if err != nil { + fields := []log.Field{ + log.String("event", "error"), + log.String("message", err.Error()), + } + if _, ok := err.(interface { + Cause() error + }); ok { + fields = append(fields, log.String("stack", fmt.Sprintf("%+v", err))) + } + span.LogFields(fields...) + ext.Error.Set(span, true) + } + span.Finish() +} + +// ContextWithSpanFromContext sets the tracing span of a context from other +// context if one is not already set. Alternative would be +// context.WithoutCancel() that would copy the context but reset ctx.Done +func ContextWithSpanFromContext(ctx, ctx2 context.Context) context.Context { + // if already is a span then noop + if span := opentracing.SpanFromContext(ctx); span != nil { + return ctx + } + if span := opentracing.SpanFromContext(ctx2); span != nil { + return opentracing.ContextWithSpan(ctx, span) + } + return ctx +} + +var DefaultTransport http.RoundTripper = &Transport{ + RoundTripper: &nethttp.Transport{RoundTripper: http.DefaultTransport}, +} + +var DefaultClient = &http.Client{ + Transport: DefaultTransport, +} + +type Transport struct { + http.RoundTripper +} + +func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { + span := opentracing.SpanFromContext(req.Context()) + if span == nil { // no tracer connected with either request or transport + return t.RoundTripper.RoundTrip(req) + } + + req, tracer := nethttp.TraceRequest(span.Tracer(), req) + + resp, err := t.RoundTripper.RoundTrip(req) + if err != nil { + tracer.Finish() + return resp, err + } + + if req.Method == "HEAD" { + tracer.Finish() + } else { + resp.Body = closeTracker{resp.Body, tracer.Finish} + } + + return resp, err +} + +type closeTracker struct { + io.ReadCloser + finish func() +} + +func (c closeTracker) Close() error { + err := c.ReadCloser.Close() + c.finish() + return err +} diff --git a/vendor/github.com/moby/buildkit/vendor.conf b/vendor/github.com/moby/buildkit/vendor.conf index 6f29ab57f0b5f..9bde31d7fd000 100644 --- a/vendor/github.com/moby/buildkit/vendor.conf +++ b/vendor/github.com/moby/buildkit/vendor.conf @@ -6,8 +6,8 @@ github.com/davecgh/go-spew v1.1.0 github.com/pmezard/go-difflib v1.0.0 golang.org/x/sys 314a259e304ff91bd6985da2a7149bbf91237993 -github.com/containerd/containerd e1428ef05460da40720d622c803262e6fc8d3477 -github.com/containerd/typeurl f6943554a7e7e88b3c14aad190bf05932da84788 +github.com/containerd/containerd b41633746ed4833f52c3c071e8edcfa2713e5677 +github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40 golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c github.com/sirupsen/logrus v1.0.0 google.golang.org/grpc v1.12.0 @@ -18,12 +18,12 @@ github.com/gogo/googleapis 08a7655d27152912db7aaf4f983275eaf8d128ef github.com/golang/protobuf v1.1.0 github.com/containerd/continuity d3c23511c1bf5851696cba83143d9cbcd666869b github.com/opencontainers/image-spec v1.0.1 -github.com/opencontainers/runc 0e561642f81e84ebd0b3afd6ec510c75a2ccb71b +github.com/opencontainers/runc ad0f5255060d36872be04de22f8731f38ef2d7b1 github.com/Microsoft/go-winio v0.4.7 github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c github.com/opencontainers/runtime-spec v1.0.1 github.com/containerd/go-runc f271fa2021de855d4d918dbef83c5fe19db1bdd5 -github.com/containerd/console cb7008ab3d8359b78c5f464cb7cf160107ad5925 +github.com/containerd/console 5d1b48d6114b8c9666f0c8b916f871af97b0a761 google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944 golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4 github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9 @@ -36,18 +36,17 @@ github.com/docker/go-units v0.3.1 github.com/google/shlex 6f45313302b9c56850fc17f99e40caebce98c716 golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631 -github.com/BurntSushi/locker a6e239ea1c69bff1cfdb20c4b73dadf52f784b6a github.com/docker/docker 71cd53e4a197b303c6ba086bd584ffd67a884281 github.com/pkg/profile 5b67d428864e92711fcbd2f8629456121a56d91f -github.com/tonistiigi/fsutil dc68c74458923f357474a9178bd198aa3ed11a5f +github.com/tonistiigi/fsutil 8abad97ee3969cdf5e9c367f46adba2c212b3ddb github.com/hashicorp/go-immutable-radix 826af9ccf0feeee615d546d69b11f8e98da8c8f1 git://github.com/tonistiigi/go-immutable-radix.git github.com/hashicorp/golang-lru a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4 github.com/mitchellh/hashstructure 2bca23e0e452137f789efbc8610126fd8b94f73b github.com/docker/go-connections 3ede32e2033de7505e6500d6c868c2b9ed9f169d github.com/docker/distribution 30578ca32960a4d368bf6db67b0a33c2a1f3dc6f -github.com/tonistiigi/units 29de085e9400559bd68aea2e7bc21566e7b8281d +github.com/tonistiigi/units 6950e57a87eaf136bbe44ef2ec8e75b9e3569de2 github.com/docker/cli 99576756eb3303b7af8102c502f21a912e3c1af6 https://github.com/tonistiigi/docker-cli.git github.com/docker/docker-credential-helpers d68f9aeca33f5fd3f08eeae5e9d175edf4e731d1 github.com/docker/libnetwork 822e5b59d346b7ad0735df2c8e445e9787320e67 @@ -61,8 +60,10 @@ github.com/uber/jaeger-lib c48167d9cae5887393dd5e61efd06a4a48b7fbb3 github.com/codahale/hdrhistogram f8ad88b59a584afeee9d334eff879b104439117b github.com/opentracing-contrib/go-stdlib b1a47cfbdd7543e70e9ef3e73d0802ad306cc1cc -github.com/opencontainers/selinux 74a747aeaf2d66097b6908f572794f49f07dda2c # used by dockerfile tests -github.com/gotestyourself/gotestyourself cf3a5ab914a2efa8bc838d09f5918c1d44d029 +gotest.tools v2.1.0 github.com/google/go-cmp v0.2.0 + +# used by rootless spec conv test +github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0 diff --git a/vendor/github.com/moby/buildkit/worker/filter.go b/vendor/github.com/moby/buildkit/worker/filter.go new file mode 100644 index 0000000000000..c94a6265f3812 --- /dev/null +++ b/vendor/github.com/moby/buildkit/worker/filter.go @@ -0,0 +1,33 @@ +package worker + +import ( + "strings" + + "github.com/containerd/containerd/filters" +) + +func adaptWorker(w Worker) filters.Adaptor { + return filters.AdapterFunc(func(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + case "id": + return w.ID(), len(w.ID()) > 0 + case "labels": + return checkMap(fieldpath[1:], w.Labels()) + } + + return "", false + }) +} + +func checkMap(fieldpath []string, m map[string]string) (string, bool) { + if len(m) == 0 { + return "", false + } + + value, ok := m[strings.Join(fieldpath, ".")] + return value, ok +} diff --git a/vendor/github.com/moby/buildkit/worker/result.go b/vendor/github.com/moby/buildkit/worker/result.go new file mode 100644 index 0000000000000..9aa6af4167489 --- /dev/null +++ b/vendor/github.com/moby/buildkit/worker/result.go @@ -0,0 +1,40 @@ +package worker + +import ( + "context" + + "github.com/moby/buildkit/cache" + "github.com/moby/buildkit/solver" +) + +func NewWorkerRefResult(ref cache.ImmutableRef, worker Worker) solver.Result { + return &workerRefResult{&WorkerRef{ImmutableRef: ref, Worker: worker}} +} + +type WorkerRef struct { + ImmutableRef cache.ImmutableRef + Worker Worker +} + +func (wr *WorkerRef) ID() string { + refID := "" + if wr.ImmutableRef != nil { + refID = wr.ImmutableRef.ID() + } + return wr.Worker.ID() + "::" + refID +} + +type workerRefResult struct { + *WorkerRef +} + +func (r *workerRefResult) Release(ctx context.Context) error { + if r.ImmutableRef == nil { + return nil + } + return r.ImmutableRef.Release(ctx) +} + +func (r *workerRefResult) Sys() interface{} { + return r.WorkerRef +} diff --git a/vendor/github.com/moby/buildkit/worker/worker.go b/vendor/github.com/moby/buildkit/worker/worker.go new file mode 100644 index 0000000000000..cd29d5ea667d6 --- /dev/null +++ b/vendor/github.com/moby/buildkit/worker/worker.go @@ -0,0 +1,41 @@ +package worker + +import ( + "context" + "io" + + "github.com/moby/buildkit/cache" + "github.com/moby/buildkit/client" + "github.com/moby/buildkit/executor" + "github.com/moby/buildkit/exporter" + "github.com/moby/buildkit/frontend" + "github.com/moby/buildkit/solver" + digest "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/image-spec/specs-go/v1" +) + +type Worker interface { + // ID needs to be unique in the cluster + ID() string + Labels() map[string]string + Platforms() []specs.Platform + LoadRef(id string) (cache.ImmutableRef, error) + // ResolveOp resolves Vertex.Sys() to Op implementation. + ResolveOp(v solver.Vertex, s frontend.FrontendLLBBridge) (solver.Op, error) + ResolveImageConfig(ctx context.Context, ref string, platform *specs.Platform) (digest.Digest, []byte, error) + // Exec is similar to executor.Exec but without []mount.Mount + Exec(ctx context.Context, meta executor.Meta, rootFS cache.ImmutableRef, stdin io.ReadCloser, stdout, stderr io.WriteCloser) error + DiskUsage(ctx context.Context, opt client.DiskUsageInfo) ([]*client.UsageInfo, error) + Exporter(name string) (exporter.Exporter, error) + Prune(ctx context.Context, ch chan client.UsageInfo) error + GetRemote(ctx context.Context, ref cache.ImmutableRef, createIfNeeded bool) (*solver.Remote, error) + FromRemote(ctx context.Context, remote *solver.Remote) (cache.ImmutableRef, error) +} + +// Pre-defined label keys +const ( + labelPrefix = "org.mobyproject.buildkit.worker." + LabelExecutor = labelPrefix + "executor" // "oci" or "containerd" + LabelSnapshotter = labelPrefix + "snapshotter" // containerd snapshotter name ("overlay", "native", ...) + LabelHostname = labelPrefix + "hostname" +) diff --git a/vendor/github.com/moby/buildkit/worker/workercontroller.go b/vendor/github.com/moby/buildkit/worker/workercontroller.go new file mode 100644 index 0000000000000..b07db8f599f41 --- /dev/null +++ b/vendor/github.com/moby/buildkit/worker/workercontroller.go @@ -0,0 +1,77 @@ +package worker + +import ( + "sync" + + "github.com/containerd/containerd/filters" + "github.com/moby/buildkit/client" + "github.com/pkg/errors" +) + +// Controller holds worker instances. +// Currently, only local workers are supported. +type Controller struct { + // TODO: define worker interface and support remote ones + workers sync.Map + defaultID string +} + +// Add adds a local worker +func (c *Controller) Add(w Worker) error { + c.workers.Store(w.ID(), w) + if c.defaultID == "" { + c.defaultID = w.ID() + } + return nil +} + +// List lists workers +func (c *Controller) List(filterStrings ...string) ([]Worker, error) { + filter, err := filters.ParseAll(filterStrings...) + if err != nil { + return nil, err + } + var workers []Worker + c.workers.Range(func(k, v interface{}) bool { + w := v.(Worker) + if filter.Match(adaptWorker(w)) { + workers = append(workers, w) + } + return true + }) + return workers, nil +} + +// GetDefault returns the default local worker +func (c *Controller) GetDefault() (Worker, error) { + if c.defaultID == "" { + return nil, errors.Errorf("no default worker") + } + return c.Get(c.defaultID) +} + +func (c *Controller) Get(id string) (Worker, error) { + v, ok := c.workers.Load(id) + if !ok { + return nil, errors.Errorf("worker %s not found", id) + } + return v.(Worker), nil +} + +// TODO: add Get(Constraint) (*Worker, error) + +func (c *Controller) WorkerInfos() []client.WorkerInfo { + workers, err := c.List() + if err != nil { + return nil + } + out := make([]client.WorkerInfo, 0, len(workers)) + for _, w := range workers { + out = append(out, client.WorkerInfo{ + ID: w.ID(), + Labels: w.Labels(), + Platforms: w.Platforms(), + }) + } + return out +} diff --git a/vendor/github.com/opencontainers/runc/libcontainer/configs/config.go b/vendor/github.com/opencontainers/runc/libcontainer/configs/config.go index 3cae4fd8d96c8..b1c4762fe20c7 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/configs/config.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/configs/config.go @@ -141,9 +141,10 @@ type Config struct { // OomScoreAdj specifies the adjustment to be made by the kernel when calculating oom scores // for a process. Valid values are between the range [-1000, '1000'], where processes with - // higher scores are preferred for being killed. + // higher scores are preferred for being killed. If it is unset then we don't touch the current + // value. // More information about kernel oom score calculation here: https://lwn.net/Articles/317814/ - OomScoreAdj int `json:"oom_score_adj"` + OomScoreAdj *int `json:"oom_score_adj,omitempty"` // UidMappings is an array of User ID mappings for User Namespaces UidMappings []IDMap `json:"uid_mappings"` diff --git a/vendor/github.com/opencontainers/runc/libcontainer/nsenter/nsexec.c b/vendor/github.com/opencontainers/runc/libcontainer/nsenter/nsexec.c index 2c69cee5d64e3..a4cd1399d9eb2 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/nsenter/nsexec.c +++ b/vendor/github.com/opencontainers/runc/libcontainer/nsenter/nsexec.c @@ -505,7 +505,8 @@ void join_namespaces(char *nslist) ns->fd = fd; ns->ns = nsflag(namespace); - strncpy(ns->path, path, PATH_MAX); + strncpy(ns->path, path, PATH_MAX - 1); + ns->path[PATH_MAX - 1] = '\0'; } while ((namespace = strtok_r(NULL, ",", &saveptr)) != NULL); /* @@ -678,17 +679,15 @@ void nsexec(void) /* * Enable setgroups(2) if we've been asked to. But we also * have to explicitly disable setgroups(2) if we're - * creating a rootless container (this is required since - * Linux 3.19). + * creating a rootless container for single-entry mapping. + * i.e. config.is_setgroup == false. + * (this is required since Linux 3.19). + * + * For rootless multi-entry mapping, config.is_setgroup shall be true and + * newuidmap/newgidmap shall be used. */ - if (config.is_rootless && config.is_setgroup) { - kill(child, SIGKILL); - bail("cannot allow setgroup in an unprivileged user namespace setup"); - } - if (config.is_setgroup) - update_setgroups(child, SETGROUPS_ALLOW); - if (config.is_rootless) + if (config.is_rootless && !config.is_setgroup) update_setgroups(child, SETGROUPS_DENY); /* Set up mappings. */ @@ -809,25 +808,30 @@ void nsexec(void) if (config.namespaces) join_namespaces(config.namespaces); - /* - * Unshare all of the namespaces. Now, it should be noted that this - * ordering might break in the future (especially with rootless - * containers). But for now, it's not possible to split this into - * CLONE_NEWUSER + [the rest] because of some RHEL SELinux issues. - * - * Note that we don't merge this with clone() because there were - * some old kernel versions where clone(CLONE_PARENT | CLONE_NEWPID) - * was broken, so we'll just do it the long way anyway. - */ - if (unshare(config.cloneflags) < 0) - bail("failed to unshare namespaces"); - /* * Deal with user namespaces first. They are quite special, as they * affect our ability to unshare other namespaces and are used as * context for privilege checks. + * + * We don't unshare all namespaces in one go. The reason for this + * is that, while the kernel documentation may claim otherwise, + * there are certain cases where unsharing all namespaces at once + * will result in namespace objects being owned incorrectly. + * Ideally we should just fix these kernel bugs, but it's better to + * be safe than sorry, and fix them separately. + * + * A specific case of this is that the SELinux label of the + * internal kern-mount that mqueue uses will be incorrect if the + * UTS namespace is cloned before the USER namespace is mapped. + * I've also heard of similar problems with the network namespace + * in some scenarios. This also mirrors how LXC deals with this + * problem. */ if (config.cloneflags & CLONE_NEWUSER) { + if (unshare(CLONE_NEWUSER) < 0) + bail("failed to unshare user namespace"); + config.cloneflags &= ~CLONE_NEWUSER; + /* * We don't have the privileges to do any mapping here (see the * clone_parent rant). So signal our parent to hook us up. @@ -853,8 +857,21 @@ void nsexec(void) if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) < 0) bail("failed to set process as dumpable"); } + + /* Become root in the namespace proper. */ + if (setresuid(0, 0, 0) < 0) + bail("failed to become root in user namespace"); } + /* + * Unshare all of the namespaces. Note that we don't merge this + * with clone() because there were some old kernel versions where + * clone(CLONE_PARENT | CLONE_NEWPID) was broken, so we'll just do + * it the long way. + */ + if (unshare(config.cloneflags) < 0) + bail("failed to unshare namespaces"); + /* * TODO: What about non-namespace clone flags that we're dropping here? * diff --git a/vendor/github.com/opencontainers/runc/libcontainer/system/linux.go b/vendor/github.com/opencontainers/runc/libcontainer/system/linux.go index 5f124cd8bbc72..a4ae8901acc06 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/system/linux.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/system/linux.go @@ -3,13 +3,12 @@ package system import ( - "bufio" - "fmt" "os" "os/exec" "syscall" // only for exec "unsafe" + "github.com/opencontainers/runc/libcontainer/user" "golang.org/x/sys/unix" ) @@ -102,34 +101,43 @@ func Setctty() error { } // RunningInUserNS detects whether we are currently running in a user namespace. -// Copied from github.com/lxc/lxd/shared/util.go +// Originally copied from github.com/lxc/lxd/shared/util.go func RunningInUserNS() bool { - file, err := os.Open("/proc/self/uid_map") + uidmap, err := user.CurrentProcessUIDMap() if err != nil { // This kernel-provided file only exists if user namespaces are supported return false } - defer file.Close() - - buf := bufio.NewReader(file) - l, _, err := buf.ReadLine() - if err != nil { - return false - } + return UIDMapInUserNS(uidmap) +} - line := string(l) - var a, b, c int64 - fmt.Sscanf(line, "%d %d %d", &a, &b, &c) +func UIDMapInUserNS(uidmap []user.IDMap) bool { /* * We assume we are in the initial user namespace if we have a full * range - 4294967295 uids starting at uid 0. */ - if a == 0 && b == 0 && c == 4294967295 { + if len(uidmap) == 1 && uidmap[0].ID == 0 && uidmap[0].ParentID == 0 && uidmap[0].Count == 4294967295 { return false } return true } +// GetParentNSeuid returns the euid within the parent user namespace +func GetParentNSeuid() int64 { + euid := int64(os.Geteuid()) + uidmap, err := user.CurrentProcessUIDMap() + if err != nil { + // This kernel-provided file only exists if user namespaces are supported + return euid + } + for _, um := range uidmap { + if um.ID <= euid && euid <= um.ID+um.Count-1 { + return um.ParentID + euid - um.ID + } + } + return euid +} + // SetSubreaper sets the value i as the subreaper setting for the calling process func SetSubreaper(i int) error { return unix.Prctl(PR_SET_CHILD_SUBREAPER, uintptr(i), 0, 0, 0) diff --git a/vendor/github.com/opencontainers/runc/libcontainer/system/unsupported.go b/vendor/github.com/opencontainers/runc/libcontainer/system/unsupported.go index e7cfd62b293c4..b94be74a6648b 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/system/unsupported.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/system/unsupported.go @@ -2,8 +2,26 @@ package system +import ( + "os" + + "github.com/opencontainers/runc/libcontainer/user" +) + // RunningInUserNS is a stub for non-Linux systems // Always returns false func RunningInUserNS() bool { return false } + +// UIDMapInUserNS is a stub for non-Linux systems +// Always returns false +func UIDMapInUserNS(uidmap []user.IDMap) bool { + return false +} + +// GetParentNSeuid returns the euid within the parent user namespace +// Always returns os.Geteuid on non-linux +func GetParentNSeuid() int { + return os.Geteuid() +} diff --git a/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go b/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go index c45e300411853..c1e634c949db8 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go @@ -114,3 +114,29 @@ func CurrentUser() (User, error) { func CurrentGroup() (Group, error) { return LookupGid(unix.Getgid()) } + +func CurrentUserSubUIDs() ([]SubID, error) { + u, err := CurrentUser() + if err != nil { + return nil, err + } + return ParseSubIDFileFilter("/etc/subuid", + func(entry SubID) bool { return entry.Name == u.Name }) +} + +func CurrentGroupSubGIDs() ([]SubID, error) { + g, err := CurrentGroup() + if err != nil { + return nil, err + } + return ParseSubIDFileFilter("/etc/subgid", + func(entry SubID) bool { return entry.Name == g.Name }) +} + +func CurrentProcessUIDMap() ([]IDMap, error) { + return ParseIDMapFile("/proc/self/uid_map") +} + +func CurrentProcessGIDMap() ([]IDMap, error) { + return ParseIDMapFile("/proc/self/gid_map") +} diff --git a/vendor/github.com/opencontainers/runc/libcontainer/user/user.go b/vendor/github.com/opencontainers/runc/libcontainer/user/user.go index 93414516caa8c..7b912bbf8b44f 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/user/user.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/user/user.go @@ -75,12 +75,29 @@ func groupFromOS(g *user.Group) (Group, error) { return newGroup, nil } +// SubID represents an entry in /etc/sub{u,g}id +type SubID struct { + Name string + SubID int64 + Count int64 +} + +// IDMap represents an entry in /proc/PID/{u,g}id_map +type IDMap struct { + ID int64 + ParentID int64 + Count int64 +} + func parseLine(line string, v ...interface{}) { - if line == "" { + parseParts(strings.Split(line, ":"), v...) +} + +func parseParts(parts []string, v ...interface{}) { + if len(parts) == 0 { return } - parts := strings.Split(line, ":") for i, p := range parts { // Ignore cases where we don't have enough fields to populate the arguments. // Some configuration files like to misbehave. @@ -96,6 +113,8 @@ func parseLine(line string, v ...interface{}) { case *int: // "numbers", with conversion errors ignored because of some misbehaving configuration files. *e, _ = strconv.Atoi(p) + case *int64: + *e, _ = strconv.ParseInt(p, 10, 64) case *[]string: // Comma-separated lists. if p != "" { @@ -105,7 +124,7 @@ func parseLine(line string, v ...interface{}) { } default: // Someone goof'd when writing code using this function. Scream so they can hear us. - panic(fmt.Sprintf("parseLine only accepts {*string, *int, *[]string} as arguments! %#v is not a pointer!", e)) + panic(fmt.Sprintf("parseLine only accepts {*string, *int, *int64, *[]string} as arguments! %#v is not a pointer!", e)) } } } @@ -479,3 +498,111 @@ func GetAdditionalGroupsPath(additionalGroups []string, groupPath string) ([]int } return GetAdditionalGroups(additionalGroups, group) } + +func ParseSubIDFile(path string) ([]SubID, error) { + subid, err := os.Open(path) + if err != nil { + return nil, err + } + defer subid.Close() + return ParseSubID(subid) +} + +func ParseSubID(subid io.Reader) ([]SubID, error) { + return ParseSubIDFilter(subid, nil) +} + +func ParseSubIDFileFilter(path string, filter func(SubID) bool) ([]SubID, error) { + subid, err := os.Open(path) + if err != nil { + return nil, err + } + defer subid.Close() + return ParseSubIDFilter(subid, filter) +} + +func ParseSubIDFilter(r io.Reader, filter func(SubID) bool) ([]SubID, error) { + if r == nil { + return nil, fmt.Errorf("nil source for subid-formatted data") + } + + var ( + s = bufio.NewScanner(r) + out = []SubID{} + ) + + for s.Scan() { + if err := s.Err(); err != nil { + return nil, err + } + + line := strings.TrimSpace(s.Text()) + if line == "" { + continue + } + + // see: man 5 subuid + p := SubID{} + parseLine(line, &p.Name, &p.SubID, &p.Count) + + if filter == nil || filter(p) { + out = append(out, p) + } + } + + return out, nil +} + +func ParseIDMapFile(path string) ([]IDMap, error) { + r, err := os.Open(path) + if err != nil { + return nil, err + } + defer r.Close() + return ParseIDMap(r) +} + +func ParseIDMap(r io.Reader) ([]IDMap, error) { + return ParseIDMapFilter(r, nil) +} + +func ParseIDMapFileFilter(path string, filter func(IDMap) bool) ([]IDMap, error) { + r, err := os.Open(path) + if err != nil { + return nil, err + } + defer r.Close() + return ParseIDMapFilter(r, filter) +} + +func ParseIDMapFilter(r io.Reader, filter func(IDMap) bool) ([]IDMap, error) { + if r == nil { + return nil, fmt.Errorf("nil source for idmap-formatted data") + } + + var ( + s = bufio.NewScanner(r) + out = []IDMap{} + ) + + for s.Scan() { + if err := s.Err(); err != nil { + return nil, err + } + + line := strings.TrimSpace(s.Text()) + if line == "" { + continue + } + + // see: man 7 user_namespaces + p := IDMap{} + parseParts(strings.Fields(line), &p.ID, &p.ParentID, &p.Count) + + if filter == nil || filter(p) { + out = append(out, p) + } + } + + return out, nil +} diff --git a/vendor/github.com/opencontainers/runtime-spec/README.md b/vendor/github.com/opencontainers/runtime-spec/README.md index f0ad86e672415..b40dba17d815c 100644 --- a/vendor/github.com/opencontainers/runtime-spec/README.md +++ b/vendor/github.com/opencontainers/runtime-spec/README.md @@ -22,12 +22,12 @@ To provide context for users the following section gives example use cases for e ### Application Bundle Builders Application bundle builders can create a [bundle](bundle.md) directory that includes all of the files required for launching an application as a container. -The bundle contains an OCI [configuration file](config.md) where the builder can specify host-independent details such as [which executable to launch](config.md#process) and host-specific settings such as [mount](config.md#mounts) locations, [hook](config.md#hooks) paths, Linux [namespaces](config-linux.md#namespaces) and [cgroups](config-linux.md#control-groups). +The bundle contains an OCI [configuration file](config.md) where the builder can specify host-independent details such as [which executable to launch](config.md#process) and host-specific settings such as [mount](config.md#mounts) locations, [hook](config.md#posix-platform-hooks) paths, Linux [namespaces](config-linux.md#namespaces) and [cgroups](config-linux.md#control-groups). Because the configuration includes host-specific settings, application bundle directories copied between two hosts may require configuration adjustments. ### Hook Developers -[Hook](config.md#hooks) developers can extend the functionality of an OCI-compliant runtime by hooking into a container's lifecycle with an external application. +[Hook](config.md#posix-platform-hooks) developers can extend the functionality of an OCI-compliant runtime by hooking into a container's lifecycle with an external application. Example use cases include sophisticated network configuration, volume garbage collection, etc. ### Runtime Developers @@ -54,7 +54,7 @@ When in doubt, start on the [mailing-list](#mailing-list). ### Meetings -The contributors and maintainers of all OCI projects have monthly meetings at 2:00 PM (USA Pacific) on the first Wednesday of every month. +The contributors and maintainers of all OCI projects have monthly meetings, which are usually at 2:00 PM (USA Pacific) on the first Wednesday of every month. There is an [iCalendar][rfc5545] format for the meetings [here](meeting.ics). Everyone is welcome to participate via [UberConference web][uberconference] or audio-only: +1 415 968 0849 (no PIN needed). An initial agenda will be posted to the [mailing list](#mailing-list) in the week before each meeting, and everyone is welcome to propose additional topics or suggest other agenda alterations there. diff --git a/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go b/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go index 71c9fa7731ceb..7781c53617359 100644 --- a/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go +++ b/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go @@ -25,6 +25,8 @@ type Spec struct { Solaris *Solaris `json:"solaris,omitempty" platform:"solaris"` // Windows is platform-specific configuration for Windows based containers. Windows *Windows `json:"windows,omitempty" platform:"windows"` + // VM specifies configuration for virtual-machine-based containers. + VM *VM `json:"vm,omitempty" platform:"vm"` } // Process contains information to start a specific application inside the container. @@ -194,10 +196,10 @@ const ( // LinuxIDMapping specifies UID/GID mappings type LinuxIDMapping struct { - // HostID is the starting UID/GID on the host to be mapped to 'ContainerID' - HostID uint32 `json:"hostID"` // ContainerID is the starting UID/GID in the container ContainerID uint32 `json:"containerID"` + // HostID is the starting UID/GID on the host to be mapped to 'ContainerID' + HostID uint32 `json:"hostID"` // Size is the number of IDs to be mapped Size uint32 `json:"size"` } @@ -320,6 +322,14 @@ type LinuxNetwork struct { Priorities []LinuxInterfacePriority `json:"priorities,omitempty"` } +// LinuxRdma for Linux cgroup 'rdma' resource management (Linux 4.11) +type LinuxRdma struct { + // Maximum number of HCA handles that can be opened. Default is "no limit". + HcaHandles *uint32 `json:"hcaHandles,omitempty"` + // Maximum number of HCA objects that can be created. Default is "no limit". + HcaObjects *uint32 `json:"hcaObjects,omitempty"` +} + // LinuxResources has container runtime resource constraints type LinuxResources struct { // Devices configures the device whitelist. @@ -336,6 +346,10 @@ type LinuxResources struct { HugepageLimits []LinuxHugepageLimit `json:"hugepageLimits,omitempty"` // Network restriction configuration Network *LinuxNetwork `json:"network,omitempty"` + // Rdma resource restriction configuration. + // Limits are a set of key value pairs that define RDMA resource limits, + // where the key is device name and value is resource limits. + Rdma map[string]LinuxRdma `json:"rdma,omitempty"` } // LinuxDevice represents the mknod information for a Linux special device file @@ -419,6 +433,8 @@ type SolarisAnet struct { type Windows struct { // LayerFolders contains a list of absolute paths to directories containing image layers. LayerFolders []string `json:"layerFolders"` + // Devices are the list of devices to be mapped into the container. + Devices []WindowsDevice `json:"devices,omitempty"` // Resources contains information for handling resource constraints for the container. Resources *WindowsResources `json:"resources,omitempty"` // CredentialSpec contains a JSON object describing a group Managed Service Account (gMSA) specification. @@ -433,6 +449,14 @@ type Windows struct { Network *WindowsNetwork `json:"network,omitempty"` } +// WindowsDevice represents information about a host device to be mapped into the container. +type WindowsDevice struct { + // Device identifier: interface class GUID, etc. + ID string `json:"id"` + // Device identifier type: "class", etc. + IDType string `json:"idType"` +} + // WindowsResources has container runtime resource constraints for containers running on Windows. type WindowsResources struct { // Memory restriction configuration. @@ -487,6 +511,42 @@ type WindowsHyperV struct { UtilityVMPath string `json:"utilityVMPath,omitempty"` } +// VM contains information for virtual-machine-based containers. +type VM struct { + // Hypervisor specifies hypervisor-related configuration for virtual-machine-based containers. + Hypervisor VMHypervisor `json:"hypervisor,omitempty"` + // Kernel specifies kernel-related configuration for virtual-machine-based containers. + Kernel VMKernel `json:"kernel"` + // Image specifies guest image related configuration for virtual-machine-based containers. + Image VMImage `json:"image,omitempty"` +} + +// VMHypervisor contains information about the hypervisor to use for a virtual machine. +type VMHypervisor struct { + // Path is the host path to the hypervisor used to manage the virtual machine. + Path string `json:"path"` + // Parameters specifies parameters to pass to the hypervisor. + Parameters string `json:"parameters,omitempty"` +} + +// VMKernel contains information about the kernel to use for a virtual machine. +type VMKernel struct { + // Path is the host path to the kernel used to boot the virtual machine. + Path string `json:"path"` + // Parameters specifies parameters to pass to the kernel. + Parameters string `json:"parameters,omitempty"` + // InitRD is the host path to an initial ramdisk to be used by the kernel. + InitRD string `json:"initrd,omitempty"` +} + +// VMImage contains information about the virtual machine root image. +type VMImage struct { + // Path is the host path to the root image that the VM kernel would boot into. + Path string `json:"path"` + // Format is the root image format type (e.g. "qcow2", "raw", "vhd", etc). + Format string `json:"format"` +} + // LinuxSeccomp represents syscall restrictions type LinuxSeccomp struct { DefaultAction LinuxSeccompAction `json:"defaultAction"` diff --git a/vendor/github.com/opencontainers/runtime-spec/specs-go/version.go b/vendor/github.com/opencontainers/runtime-spec/specs-go/version.go index ff0cb6a80e244..b920fc1b39d74 100644 --- a/vendor/github.com/opencontainers/runtime-spec/specs-go/version.go +++ b/vendor/github.com/opencontainers/runtime-spec/specs-go/version.go @@ -11,7 +11,7 @@ const ( VersionPatch = 1 // VersionDev indicates development branch. Releases will be empty string. - VersionDev = "" + VersionDev = "-dev" ) // Version is the specification version that the package types support. diff --git a/vendor/github.com/opentracing-contrib/go-stdlib/LICENSE b/vendor/github.com/opentracing-contrib/go-stdlib/LICENSE new file mode 100644 index 0000000000000..c259d1290717d --- /dev/null +++ b/vendor/github.com/opentracing-contrib/go-stdlib/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2016, opentracing-contrib +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of go-stdlib nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/opentracing-contrib/go-stdlib/README.md b/vendor/github.com/opentracing-contrib/go-stdlib/README.md new file mode 100644 index 0000000000000..139709c1461a5 --- /dev/null +++ b/vendor/github.com/opentracing-contrib/go-stdlib/README.md @@ -0,0 +1,18 @@ +# go-stdlib + +This repository contains OpenTracing instrumentation for packages in +the Go standard library. + +For documentation on the packages, +[check godoc](https://godoc.org/github.com/opentracing-contrib/go-stdlib/). + +**The APIs in the various packages are experimental and may change in +the future. You should vendor them to avoid spurious breakage.** + +## Packages + +Instrumentation is provided for the following packages, with the +following caveats: + +- **net/http**: Client and server instrumentation. *Only supported + with Go 1.7 and later.* diff --git a/vendor/github.com/opentracing-contrib/go-stdlib/nethttp/client.go b/vendor/github.com/opentracing-contrib/go-stdlib/nethttp/client.go new file mode 100644 index 0000000000000..8d33bcb63c0fe --- /dev/null +++ b/vendor/github.com/opentracing-contrib/go-stdlib/nethttp/client.go @@ -0,0 +1,301 @@ +// +build go1.7 + +package nethttp + +import ( + "context" + "io" + "net/http" + "net/http/httptrace" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/opentracing/opentracing-go/log" +) + +type contextKey int + +const ( + keyTracer contextKey = iota +) + +const defaultComponentName = "net/http" + +// Transport wraps a RoundTripper. If a request is being traced with +// Tracer, Transport will inject the current span into the headers, +// and set HTTP related tags on the span. +type Transport struct { + // The actual RoundTripper to use for the request. A nil + // RoundTripper defaults to http.DefaultTransport. + http.RoundTripper +} + +type clientOptions struct { + operationName string + componentName string + disableClientTrace bool +} + +// ClientOption contols the behavior of TraceRequest. +type ClientOption func(*clientOptions) + +// OperationName returns a ClientOption that sets the operation +// name for the client-side span. +func OperationName(operationName string) ClientOption { + return func(options *clientOptions) { + options.operationName = operationName + } +} + +// ComponentName returns a ClientOption that sets the component +// name for the client-side span. +func ComponentName(componentName string) ClientOption { + return func(options *clientOptions) { + options.componentName = componentName + } +} + +// ClientTrace returns a ClientOption that turns on or off +// extra instrumentation via httptrace.WithClientTrace. +func ClientTrace(enabled bool) ClientOption { + return func(options *clientOptions) { + options.disableClientTrace = !enabled + } +} + +// TraceRequest adds a ClientTracer to req, tracing the request and +// all requests caused due to redirects. When tracing requests this +// way you must also use Transport. +// +// Example: +// +// func AskGoogle(ctx context.Context) error { +// client := &http.Client{Transport: &nethttp.Transport{}} +// req, err := http.NewRequest("GET", "http://google.com", nil) +// if err != nil { +// return err +// } +// req = req.WithContext(ctx) // extend existing trace, if any +// +// req, ht := nethttp.TraceRequest(tracer, req) +// defer ht.Finish() +// +// res, err := client.Do(req) +// if err != nil { +// return err +// } +// res.Body.Close() +// return nil +// } +func TraceRequest(tr opentracing.Tracer, req *http.Request, options ...ClientOption) (*http.Request, *Tracer) { + opts := &clientOptions{} + for _, opt := range options { + opt(opts) + } + ht := &Tracer{tr: tr, opts: opts} + ctx := req.Context() + if !opts.disableClientTrace { + ctx = httptrace.WithClientTrace(ctx, ht.clientTrace()) + } + req = req.WithContext(context.WithValue(ctx, keyTracer, ht)) + return req, ht +} + +type closeTracker struct { + io.ReadCloser + sp opentracing.Span +} + +func (c closeTracker) Close() error { + err := c.ReadCloser.Close() + c.sp.LogFields(log.String("event", "ClosedBody")) + c.sp.Finish() + return err +} + +// RoundTrip implements the RoundTripper interface. +func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { + rt := t.RoundTripper + if rt == nil { + rt = http.DefaultTransport + } + tracer, ok := req.Context().Value(keyTracer).(*Tracer) + if !ok { + return rt.RoundTrip(req) + } + + tracer.start(req) + + ext.HTTPMethod.Set(tracer.sp, req.Method) + ext.HTTPUrl.Set(tracer.sp, req.URL.String()) + + carrier := opentracing.HTTPHeadersCarrier(req.Header) + tracer.sp.Tracer().Inject(tracer.sp.Context(), opentracing.HTTPHeaders, carrier) + resp, err := rt.RoundTrip(req) + + if err != nil { + tracer.sp.Finish() + return resp, err + } + ext.HTTPStatusCode.Set(tracer.sp, uint16(resp.StatusCode)) + if req.Method == "HEAD" { + tracer.sp.Finish() + } else { + resp.Body = closeTracker{resp.Body, tracer.sp} + } + return resp, nil +} + +// Tracer holds tracing details for one HTTP request. +type Tracer struct { + tr opentracing.Tracer + root opentracing.Span + sp opentracing.Span + opts *clientOptions +} + +func (h *Tracer) start(req *http.Request) opentracing.Span { + if h.root == nil { + parent := opentracing.SpanFromContext(req.Context()) + var spanctx opentracing.SpanContext + if parent != nil { + spanctx = parent.Context() + } + operationName := h.opts.operationName + if operationName == "" { + operationName = "HTTP Client" + } + root := h.tr.StartSpan(operationName, opentracing.ChildOf(spanctx)) + h.root = root + } + + ctx := h.root.Context() + h.sp = h.tr.StartSpan("HTTP "+req.Method, opentracing.ChildOf(ctx)) + ext.SpanKindRPCClient.Set(h.sp) + + componentName := h.opts.componentName + if componentName == "" { + componentName = defaultComponentName + } + ext.Component.Set(h.sp, componentName) + + return h.sp +} + +// Finish finishes the span of the traced request. +func (h *Tracer) Finish() { + if h.root != nil { + h.root.Finish() + } +} + +// Span returns the root span of the traced request. This function +// should only be called after the request has been executed. +func (h *Tracer) Span() opentracing.Span { + return h.root +} + +func (h *Tracer) clientTrace() *httptrace.ClientTrace { + return &httptrace.ClientTrace{ + GetConn: h.getConn, + GotConn: h.gotConn, + PutIdleConn: h.putIdleConn, + GotFirstResponseByte: h.gotFirstResponseByte, + Got100Continue: h.got100Continue, + DNSStart: h.dnsStart, + DNSDone: h.dnsDone, + ConnectStart: h.connectStart, + ConnectDone: h.connectDone, + WroteHeaders: h.wroteHeaders, + Wait100Continue: h.wait100Continue, + WroteRequest: h.wroteRequest, + } +} + +func (h *Tracer) getConn(hostPort string) { + ext.HTTPUrl.Set(h.sp, hostPort) + h.sp.LogFields(log.String("event", "GetConn")) +} + +func (h *Tracer) gotConn(info httptrace.GotConnInfo) { + h.sp.SetTag("net/http.reused", info.Reused) + h.sp.SetTag("net/http.was_idle", info.WasIdle) + h.sp.LogFields(log.String("event", "GotConn")) +} + +func (h *Tracer) putIdleConn(error) { + h.sp.LogFields(log.String("event", "PutIdleConn")) +} + +func (h *Tracer) gotFirstResponseByte() { + h.sp.LogFields(log.String("event", "GotFirstResponseByte")) +} + +func (h *Tracer) got100Continue() { + h.sp.LogFields(log.String("event", "Got100Continue")) +} + +func (h *Tracer) dnsStart(info httptrace.DNSStartInfo) { + h.sp.LogFields( + log.String("event", "DNSStart"), + log.String("host", info.Host), + ) +} + +func (h *Tracer) dnsDone(info httptrace.DNSDoneInfo) { + fields := []log.Field{log.String("event", "DNSDone")} + for _, addr := range info.Addrs { + fields = append(fields, log.String("addr", addr.String())) + } + if info.Err != nil { + fields = append(fields, log.Error(info.Err)) + } + h.sp.LogFields(fields...) +} + +func (h *Tracer) connectStart(network, addr string) { + h.sp.LogFields( + log.String("event", "ConnectStart"), + log.String("network", network), + log.String("addr", addr), + ) +} + +func (h *Tracer) connectDone(network, addr string, err error) { + if err != nil { + h.sp.LogFields( + log.String("message", "ConnectDone"), + log.String("network", network), + log.String("addr", addr), + log.String("event", "error"), + log.Error(err), + ) + } else { + h.sp.LogFields( + log.String("event", "ConnectDone"), + log.String("network", network), + log.String("addr", addr), + ) + } +} + +func (h *Tracer) wroteHeaders() { + h.sp.LogFields(log.String("event", "WroteHeaders")) +} + +func (h *Tracer) wait100Continue() { + h.sp.LogFields(log.String("event", "Wait100Continue")) +} + +func (h *Tracer) wroteRequest(info httptrace.WroteRequestInfo) { + if info.Err != nil { + h.sp.LogFields( + log.String("message", "WroteRequest"), + log.String("event", "error"), + log.Error(info.Err), + ) + ext.Error.Set(h.sp, true) + } else { + h.sp.LogFields(log.String("event", "WroteRequest")) + } +} diff --git a/vendor/github.com/opentracing-contrib/go-stdlib/nethttp/doc.go b/vendor/github.com/opentracing-contrib/go-stdlib/nethttp/doc.go new file mode 100644 index 0000000000000..c853ca643765e --- /dev/null +++ b/vendor/github.com/opentracing-contrib/go-stdlib/nethttp/doc.go @@ -0,0 +1,3 @@ +// Package nethttp provides OpenTracing instrumentation for the +// net/http package. +package nethttp diff --git a/vendor/github.com/opentracing-contrib/go-stdlib/nethttp/server.go b/vendor/github.com/opentracing-contrib/go-stdlib/nethttp/server.go new file mode 100644 index 0000000000000..2b31415e7e871 --- /dev/null +++ b/vendor/github.com/opentracing-contrib/go-stdlib/nethttp/server.go @@ -0,0 +1,96 @@ +// +build go1.7 + +package nethttp + +import ( + "net/http" + + opentracing "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" +) + +type statusCodeTracker struct { + http.ResponseWriter + status int +} + +func (w *statusCodeTracker) WriteHeader(status int) { + w.status = status + w.ResponseWriter.WriteHeader(status) +} + +type mwOptions struct { + opNameFunc func(r *http.Request) string + componentName string +} + +// MWOption controls the behavior of the Middleware. +type MWOption func(*mwOptions) + +// OperationNameFunc returns a MWOption that uses given function f +// to generate operation name for each server-side span. +func OperationNameFunc(f func(r *http.Request) string) MWOption { + return func(options *mwOptions) { + options.opNameFunc = f + } +} + +// MWComponentName returns a MWOption that sets the component name +// name for the server-side span. +func MWComponentName(componentName string) MWOption { + return func(options *mwOptions) { + options.componentName = componentName + } +} + +// Middleware wraps an http.Handler and traces incoming requests. +// Additionally, it adds the span to the request's context. +// +// By default, the operation name of the spans is set to "HTTP {method}". +// This can be overriden with options. +// +// Example: +// http.ListenAndServe("localhost:80", nethttp.Middleware(tracer, http.DefaultServeMux)) +// +// The options allow fine tuning the behavior of the middleware. +// +// Example: +// mw := nethttp.Middleware( +// tracer, +// http.DefaultServeMux, +// nethttp.OperationName(func(r *http.Request) string { +// return "HTTP " + r.Method + ":/api/customers" +// }), +// ) +func Middleware(tr opentracing.Tracer, h http.Handler, options ...MWOption) http.Handler { + opts := mwOptions{ + opNameFunc: func(r *http.Request) string { + return "HTTP " + r.Method + }, + } + for _, opt := range options { + opt(&opts) + } + fn := func(w http.ResponseWriter, r *http.Request) { + ctx, _ := tr.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header)) + sp := tr.StartSpan(opts.opNameFunc(r), ext.RPCServerOption(ctx)) + ext.HTTPMethod.Set(sp, r.Method) + ext.HTTPUrl.Set(sp, r.URL.String()) + + // set component name, use "net/http" if caller does not specify + componentName := opts.componentName + if componentName == "" { + componentName = defaultComponentName + } + ext.Component.Set(sp, componentName) + + w = &statusCodeTracker{w, 200} + r = r.WithContext(opentracing.ContextWithSpan(r.Context(), sp)) + + h.ServeHTTP(w, r) + + ext.HTTPStatusCode.Set(sp, uint16(w.(*statusCodeTracker).status)) + sp.Finish() + } + return http.HandlerFunc(fn) +} diff --git a/vendor/github.com/pmezard/go-difflib/README.md b/vendor/github.com/pmezard/go-difflib/README.md deleted file mode 100644 index e87f307ed44fe..0000000000000 --- a/vendor/github.com/pmezard/go-difflib/README.md +++ /dev/null @@ -1,50 +0,0 @@ -go-difflib -========== - -[![Build Status](https://travis-ci.org/pmezard/go-difflib.png?branch=master)](https://travis-ci.org/pmezard/go-difflib) -[![GoDoc](https://godoc.org/github.com/pmezard/go-difflib/difflib?status.svg)](https://godoc.org/github.com/pmezard/go-difflib/difflib) - -Go-difflib is a partial port of python 3 difflib package. Its main goal -was to make unified and context diff available in pure Go, mostly for -testing purposes. - -The following class and functions (and related tests) have be ported: - -* `SequenceMatcher` -* `unified_diff()` -* `context_diff()` - -## Installation - -```bash -$ go get github.com/pmezard/go-difflib/difflib -``` - -### Quick Start - -Diffs are configured with Unified (or ContextDiff) structures, and can -be output to an io.Writer or returned as a string. - -```Go -diff := UnifiedDiff{ - A: difflib.SplitLines("foo\nbar\n"), - B: difflib.SplitLines("foo\nbaz\n"), - FromFile: "Original", - ToFile: "Current", - Context: 3, -} -text, _ := GetUnifiedDiffString(diff) -fmt.Printf(text) -``` - -would output: - -``` ---- Original -+++ Current -@@ -1,3 +1,3 @@ - foo --bar -+baz -``` - diff --git a/vendor/github.com/stevvooe/ttrpc/codec.go b/vendor/github.com/stevvooe/ttrpc/codec.go deleted file mode 100644 index 7956a72220c50..0000000000000 --- a/vendor/github.com/stevvooe/ttrpc/codec.go +++ /dev/null @@ -1,26 +0,0 @@ -package ttrpc - -import ( - "github.com/gogo/protobuf/proto" - "github.com/pkg/errors" -) - -type codec struct{} - -func (c codec) Marshal(msg interface{}) ([]byte, error) { - switch v := msg.(type) { - case proto.Message: - return proto.Marshal(v) - default: - return nil, errors.Errorf("ttrpc: cannot marshal unknown type: %T", msg) - } -} - -func (c codec) Unmarshal(p []byte, msg interface{}) error { - switch v := msg.(type) { - case proto.Message: - return proto.Unmarshal(p, v) - default: - return errors.Errorf("ttrpc: cannot unmarshal into unknown type: %T", msg) - } -} diff --git a/vendor/github.com/stevvooe/ttrpc/config.go b/vendor/github.com/stevvooe/ttrpc/config.go deleted file mode 100644 index 23bc603a38dda..0000000000000 --- a/vendor/github.com/stevvooe/ttrpc/config.go +++ /dev/null @@ -1,23 +0,0 @@ -package ttrpc - -import "github.com/pkg/errors" - -type serverConfig struct { - handshaker Handshaker -} - -type ServerOpt func(*serverConfig) error - -// WithServerHandshaker can be passed to NewServer to ensure that the -// handshaker is called before every connection attempt. -// -// Only one handshaker is allowed per server. -func WithServerHandshaker(handshaker Handshaker) ServerOpt { - return func(c *serverConfig) error { - if c.handshaker != nil { - return errors.New("only one handshaker allowed per server") - } - c.handshaker = handshaker - return nil - } -} diff --git a/vendor/github.com/tonistiigi/fsutil/chtimes_nolinux.go b/vendor/github.com/tonistiigi/fsutil/chtimes_nolinux.go index 39bfdfee5b93b..cdd80ec9a73e4 100644 --- a/vendor/github.com/tonistiigi/fsutil/chtimes_nolinux.go +++ b/vendor/github.com/tonistiigi/fsutil/chtimes_nolinux.go @@ -9,5 +9,12 @@ import ( func chtimes(path string, un int64) error { mtime := time.Unix(0, un) + fi, err := os.Lstat(path) + if err != nil { + return err + } + if fi.Mode()&os.ModeSymlink != 0 { + return nil + } return os.Chtimes(path, mtime, mtime) } diff --git a/vendor/github.com/tonistiigi/fsutil/diff.go b/vendor/github.com/tonistiigi/fsutil/diff.go index 6125ef73af648..340a0e48a45c8 100644 --- a/vendor/github.com/tonistiigi/fsutil/diff.go +++ b/vendor/github.com/tonistiigi/fsutil/diff.go @@ -1,10 +1,9 @@ package fsutil import ( + "context" "hash" "os" - - "golang.org/x/net/context" ) type walkerFn func(ctx context.Context, pathC chan<- *currentPath) error diff --git a/vendor/github.com/tonistiigi/fsutil/diff_containerd.go b/vendor/github.com/tonistiigi/fsutil/diff_containerd.go index c7c9788e85e92..2722cef4aca46 100644 --- a/vendor/github.com/tonistiigi/fsutil/diff_containerd.go +++ b/vendor/github.com/tonistiigi/fsutil/diff_containerd.go @@ -1,10 +1,10 @@ package fsutil import ( + "context" "os" "strings" - "golang.org/x/net/context" "golang.org/x/sync/errgroup" ) diff --git a/vendor/github.com/tonistiigi/fsutil/diskwriter.go b/vendor/github.com/tonistiigi/fsutil/diskwriter.go index e2d034c75ea1b..aa1974f24320a 100644 --- a/vendor/github.com/tonistiigi/fsutil/diskwriter.go +++ b/vendor/github.com/tonistiigi/fsutil/diskwriter.go @@ -1,6 +1,7 @@ package fsutil import ( + "context" "hash" "io" "os" @@ -9,9 +10,8 @@ import ( "sync" "time" - digest "github.com/opencontainers/go-digest" + "github.com/opencontainers/go-digest" "github.com/pkg/errors" - "golang.org/x/net/context" "golang.org/x/sync/errgroup" ) @@ -80,9 +80,7 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er } }() - p = filepath.FromSlash(p) - - destPath := filepath.Join(dw.dest, p) + destPath := filepath.Join(dw.dest, filepath.FromSlash(p)) if kind == ChangeKindDelete { // todo: no need to validate if diff is trusted but is it always? @@ -102,8 +100,10 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er return errors.Errorf("%s invalid change without stat information", p) } + statCopy := *stat + if dw.filter != nil { - if ok := dw.filter(stat); !ok { + if ok := dw.filter(&statCopy); !ok { return nil } } @@ -122,7 +122,7 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er } if oldFi != nil && fi.IsDir() && oldFi.IsDir() { - if err := rewriteMetadata(destPath, stat); err != nil { + if err := rewriteMetadata(destPath, &statCopy); err != nil { return errors.Wrapf(err, "error setting dir metadata for %s", destPath) } return nil @@ -141,16 +141,16 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er return errors.Wrapf(err, "failed to create dir %s", newPath) } case fi.Mode()&os.ModeDevice != 0 || fi.Mode()&os.ModeNamedPipe != 0: - if err := handleTarTypeBlockCharFifo(newPath, stat); err != nil { + if err := handleTarTypeBlockCharFifo(newPath, &statCopy); err != nil { return errors.Wrapf(err, "failed to create device %s", newPath) } case fi.Mode()&os.ModeSymlink != 0: - if err := os.Symlink(stat.Linkname, newPath); err != nil { + if err := os.Symlink(statCopy.Linkname, newPath); err != nil { return errors.Wrapf(err, "failed to symlink %s", newPath) } - case stat.Linkname != "": - if err := os.Link(filepath.Join(dw.dest, stat.Linkname), newPath); err != nil { - return errors.Wrapf(err, "failed to link %s to %s", newPath, stat.Linkname) + case statCopy.Linkname != "": + if err := os.Link(filepath.Join(dw.dest, statCopy.Linkname), newPath); err != nil { + return errors.Wrapf(err, "failed to link %s to %s", newPath, statCopy.Linkname) } default: isRegularFile = true @@ -170,7 +170,7 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er } } - if err := rewriteMetadata(newPath, stat); err != nil { + if err := rewriteMetadata(newPath, &statCopy); err != nil { return errors.Wrapf(err, "error setting metadata for %s", newPath) } @@ -272,14 +272,27 @@ func (hw *hashedWriter) Digest() digest.Digest { } type lazyFileWriter struct { - dest string - ctx context.Context - f *os.File + dest string + ctx context.Context + f *os.File + fileMode *os.FileMode } func (lfw *lazyFileWriter) Write(dt []byte) (int, error) { if lfw.f == nil { file, err := os.OpenFile(lfw.dest, os.O_WRONLY, 0) //todo: windows + if os.IsPermission(err) { + // retry after chmod + fi, er := os.Stat(lfw.dest) + if er == nil { + mode := fi.Mode() + lfw.fileMode = &mode + er = os.Chmod(lfw.dest, mode|0222) + if er == nil { + file, err = os.OpenFile(lfw.dest, os.O_WRONLY, 0) + } + } + } if err != nil { return 0, errors.Wrapf(err, "failed to open %s", lfw.dest) } @@ -289,10 +302,14 @@ func (lfw *lazyFileWriter) Write(dt []byte) (int, error) { } func (lfw *lazyFileWriter) Close() error { + var err error if lfw.f != nil { - return lfw.f.Close() + err = lfw.f.Close() } - return nil + if err == nil && lfw.fileMode != nil { + err = os.Chmod(lfw.dest, *lfw.fileMode) + } + return err } func mkdev(major int64, minor int64) uint32 { diff --git a/vendor/github.com/tonistiigi/fsutil/followlinks.go b/vendor/github.com/tonistiigi/fsutil/followlinks.go new file mode 100644 index 0000000000000..ed4af6e816565 --- /dev/null +++ b/vendor/github.com/tonistiigi/fsutil/followlinks.go @@ -0,0 +1,150 @@ +package fsutil + +import ( + "io/ioutil" + "os" + "path/filepath" + "runtime" + "sort" + strings "strings" + + "github.com/pkg/errors" +) + +func FollowLinks(root string, paths []string) ([]string, error) { + r := &symlinkResolver{root: root, resolved: map[string]struct{}{}} + for _, p := range paths { + if err := r.append(p); err != nil { + return nil, err + } + } + res := make([]string, 0, len(r.resolved)) + for r := range r.resolved { + res = append(res, r) + } + sort.Strings(res) + return dedupePaths(res), nil +} + +type symlinkResolver struct { + root string + resolved map[string]struct{} +} + +func (r *symlinkResolver) append(p string) error { + p = filepath.Join(".", p) + current := "." + for { + parts := strings.SplitN(p, string(filepath.Separator), 2) + current = filepath.Join(current, parts[0]) + + targets, err := r.readSymlink(current, true) + if err != nil { + return err + } + + p = "" + if len(parts) == 2 { + p = parts[1] + } + + if p == "" || targets != nil { + if _, ok := r.resolved[current]; ok { + return nil + } + } + + if targets != nil { + r.resolved[current] = struct{}{} + for _, target := range targets { + if err := r.append(filepath.Join(target, p)); err != nil { + return err + } + } + return nil + } + + if p == "" { + r.resolved[current] = struct{}{} + return nil + } + } +} + +func (r *symlinkResolver) readSymlink(p string, allowWildcard bool) ([]string, error) { + realPath := filepath.Join(r.root, p) + base := filepath.Base(p) + if allowWildcard && containsWildcards(base) { + fis, err := ioutil.ReadDir(filepath.Dir(realPath)) + if err != nil { + if os.IsNotExist(err) { + return nil, nil + } + return nil, errors.Wrapf(err, "failed to read dir %s", filepath.Dir(realPath)) + } + var out []string + for _, f := range fis { + if ok, _ := filepath.Match(base, f.Name()); ok { + res, err := r.readSymlink(filepath.Join(filepath.Dir(p), f.Name()), false) + if err != nil { + return nil, err + } + out = append(out, res...) + } + } + return out, nil + } + + fi, err := os.Lstat(realPath) + if err != nil { + if os.IsNotExist(err) { + return nil, nil + } + return nil, errors.Wrapf(err, "failed to lstat %s", realPath) + } + if fi.Mode()&os.ModeSymlink == 0 { + return nil, nil + } + link, err := os.Readlink(realPath) + if err != nil { + return nil, errors.Wrapf(err, "failed to readlink %s", realPath) + } + link = filepath.Clean(link) + if filepath.IsAbs(link) { + return []string{link}, nil + } + return []string{ + filepath.Join(string(filepath.Separator), filepath.Join(filepath.Dir(p), link)), + }, nil +} + +func containsWildcards(name string) bool { + isWindows := runtime.GOOS == "windows" + for i := 0; i < len(name); i++ { + ch := name[i] + if ch == '\\' && !isWindows { + i++ + } else if ch == '*' || ch == '?' || ch == '[' { + return true + } + } + return false +} + +// dedupePaths expects input as a sorted list +func dedupePaths(in []string) []string { + out := make([]string, 0, len(in)) + var last string + for _, s := range in { + // if one of the paths is root there is no filter + if s == "." { + return nil + } + if strings.HasPrefix(s, last+string(filepath.Separator)) { + continue + } + out = append(out, s) + last = s + } + return out +} diff --git a/vendor/github.com/tonistiigi/fsutil/receive.go b/vendor/github.com/tonistiigi/fsutil/receive.go index 5867c984eeeb1..14ccb6c7bf275 100644 --- a/vendor/github.com/tonistiigi/fsutil/receive.go +++ b/vendor/github.com/tonistiigi/fsutil/receive.go @@ -1,12 +1,12 @@ package fsutil import ( + "context" "io" "os" "sync" "github.com/pkg/errors" - "golang.org/x/net/context" "golang.org/x/sync/errgroup" ) diff --git a/vendor/github.com/tonistiigi/fsutil/send.go b/vendor/github.com/tonistiigi/fsutil/send.go index d3cda0bb64697..61f6170a4c022 100644 --- a/vendor/github.com/tonistiigi/fsutil/send.go +++ b/vendor/github.com/tonistiigi/fsutil/send.go @@ -1,13 +1,13 @@ package fsutil import ( + "context" "io" "os" "path/filepath" "sync" "github.com/pkg/errors" - "golang.org/x/net/context" "golang.org/x/sync/errgroup" ) diff --git a/vendor/github.com/tonistiigi/fsutil/walker.go b/vendor/github.com/tonistiigi/fsutil/walker.go index d05a42dbed153..aa50991455424 100644 --- a/vendor/github.com/tonistiigi/fsutil/walker.go +++ b/vendor/github.com/tonistiigi/fsutil/walker.go @@ -1,6 +1,7 @@ package fsutil import ( + "context" "os" "path/filepath" "runtime" @@ -9,13 +10,15 @@ import ( "github.com/docker/docker/pkg/fileutils" "github.com/pkg/errors" - "golang.org/x/net/context" ) type WalkOpt struct { IncludePatterns []string ExcludePatterns []string - Map func(*Stat) bool + // FollowPaths contains symlinks that are resolved into include patterns + // before performing the fs walk + FollowPaths []string + Map func(*Stat) bool } func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) error { @@ -39,8 +42,25 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err } } + var includePatterns []string + if opt != nil && opt.IncludePatterns != nil { + includePatterns = make([]string, len(opt.IncludePatterns)) + for k := range opt.IncludePatterns { + includePatterns[k] = filepath.Clean(opt.IncludePatterns[k]) + } + } + if opt != nil && opt.FollowPaths != nil { + targets, err := FollowLinks(p, opt.FollowPaths) + if err != nil { + return err + } + if targets != nil { + includePatterns = append(includePatterns, targets...) + includePatterns = dedupePaths(includePatterns) + } + } + var lastIncludedDir string - var includePatternPrefixes []string seenFiles := make(map[uint64]string) return filepath.Walk(root, func(path string, fi os.FileInfo, err error) (retErr error) { @@ -66,34 +86,34 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err } if opt != nil { - if opt.IncludePatterns != nil { - if includePatternPrefixes == nil { - includePatternPrefixes = patternPrefixes(opt.IncludePatterns) - } - matched := false + if includePatterns != nil { + skip := false if lastIncludedDir != "" { if strings.HasPrefix(path, lastIncludedDir+string(filepath.Separator)) { - matched = true + skip = true } } - if !matched { - for _, p := range opt.IncludePatterns { - if m, _ := filepath.Match(p, path); m { + + if !skip { + matched := false + partial := true + for _, p := range includePatterns { + if ok, p := matchPrefix(p, path); ok { matched = true - break + if !p { + partial = false + break + } } } - if matched && fi.IsDir() { - lastIncludedDir = path - } - } - if !matched { - if !fi.IsDir() { - return nil - } else { - if noPossiblePrefixMatch(path, includePatternPrefixes) { + if !matched { + if fi.IsDir() { return filepath.SkipDir } + return nil + } + if !partial && fi.IsDir() { + lastIncludedDir = path } } } @@ -131,13 +151,13 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err stat := &Stat{ Path: path, Mode: uint32(fi.Mode()), - Size_: fi.Size(), ModTime: fi.ModTime().UnixNano(), } setUnixOpt(fi, stat, path, seenFiles) if !fi.IsDir() { + stat.Size_ = fi.Size() if fi.Mode()&os.ModeSymlink != 0 { link, err := os.Readlink(origpath) if err != nil { @@ -199,29 +219,28 @@ func (s *StatInfo) Sys() interface{} { return s.Stat } -func patternPrefixes(patterns []string) []string { - pfxs := make([]string, 0, len(patterns)) - for _, ptrn := range patterns { - idx := strings.IndexFunc(ptrn, func(ch rune) bool { - return ch == '*' || ch == '?' || ch == '[' || ch == '\\' - }) - if idx == -1 { - idx = len(ptrn) - } - pfxs = append(pfxs, ptrn[:idx]) +func matchPrefix(pattern, name string) (bool, bool) { + count := strings.Count(name, string(filepath.Separator)) + partial := false + if strings.Count(pattern, string(filepath.Separator)) > count { + pattern = trimUntilIndex(pattern, string(filepath.Separator), count) + partial = true } - return pfxs + m, _ := filepath.Match(pattern, name) + return m, partial } -func noPossiblePrefixMatch(p string, pfxs []string) bool { - for _, pfx := range pfxs { - chk := p - if len(pfx) < len(p) { - chk = p[:len(pfx)] - } - if strings.HasPrefix(pfx, chk) { - return false +func trimUntilIndex(str, sep string, count int) string { + s := str + i := 0 + c := 0 + for { + idx := strings.Index(s, sep) + s = s[idx+len(sep):] + i += idx + len(sep) + c++ + if c > count { + return str[:i-len(sep)] } } - return true } diff --git a/vendor/golang.org/x/crypto/cryptobyte/asn1.go b/vendor/golang.org/x/crypto/cryptobyte/asn1.go new file mode 100644 index 0000000000000..528b9bff671e5 --- /dev/null +++ b/vendor/golang.org/x/crypto/cryptobyte/asn1.go @@ -0,0 +1,751 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cryptobyte + +import ( + encoding_asn1 "encoding/asn1" + "fmt" + "math/big" + "reflect" + "time" + + "golang.org/x/crypto/cryptobyte/asn1" +) + +// This file contains ASN.1-related methods for String and Builder. + +// Builder + +// AddASN1Int64 appends a DER-encoded ASN.1 INTEGER. +func (b *Builder) AddASN1Int64(v int64) { + b.addASN1Signed(asn1.INTEGER, v) +} + +// AddASN1Int64WithTag appends a DER-encoded ASN.1 INTEGER with the +// given tag. +func (b *Builder) AddASN1Int64WithTag(v int64, tag asn1.Tag) { + b.addASN1Signed(tag, v) +} + +// AddASN1Enum appends a DER-encoded ASN.1 ENUMERATION. +func (b *Builder) AddASN1Enum(v int64) { + b.addASN1Signed(asn1.ENUM, v) +} + +func (b *Builder) addASN1Signed(tag asn1.Tag, v int64) { + b.AddASN1(tag, func(c *Builder) { + length := 1 + for i := v; i >= 0x80 || i < -0x80; i >>= 8 { + length++ + } + + for ; length > 0; length-- { + i := v >> uint((length-1)*8) & 0xff + c.AddUint8(uint8(i)) + } + }) +} + +// AddASN1Uint64 appends a DER-encoded ASN.1 INTEGER. +func (b *Builder) AddASN1Uint64(v uint64) { + b.AddASN1(asn1.INTEGER, func(c *Builder) { + length := 1 + for i := v; i >= 0x80; i >>= 8 { + length++ + } + + for ; length > 0; length-- { + i := v >> uint((length-1)*8) & 0xff + c.AddUint8(uint8(i)) + } + }) +} + +// AddASN1BigInt appends a DER-encoded ASN.1 INTEGER. +func (b *Builder) AddASN1BigInt(n *big.Int) { + if b.err != nil { + return + } + + b.AddASN1(asn1.INTEGER, func(c *Builder) { + if n.Sign() < 0 { + // A negative number has to be converted to two's-complement form. So we + // invert and subtract 1. If the most-significant-bit isn't set then + // we'll need to pad the beginning with 0xff in order to keep the number + // negative. + nMinus1 := new(big.Int).Neg(n) + nMinus1.Sub(nMinus1, bigOne) + bytes := nMinus1.Bytes() + for i := range bytes { + bytes[i] ^= 0xff + } + if bytes[0]&0x80 == 0 { + c.add(0xff) + } + c.add(bytes...) + } else if n.Sign() == 0 { + c.add(0) + } else { + bytes := n.Bytes() + if bytes[0]&0x80 != 0 { + c.add(0) + } + c.add(bytes...) + } + }) +} + +// AddASN1OctetString appends a DER-encoded ASN.1 OCTET STRING. +func (b *Builder) AddASN1OctetString(bytes []byte) { + b.AddASN1(asn1.OCTET_STRING, func(c *Builder) { + c.AddBytes(bytes) + }) +} + +const generalizedTimeFormatStr = "20060102150405Z0700" + +// AddASN1GeneralizedTime appends a DER-encoded ASN.1 GENERALIZEDTIME. +func (b *Builder) AddASN1GeneralizedTime(t time.Time) { + if t.Year() < 0 || t.Year() > 9999 { + b.err = fmt.Errorf("cryptobyte: cannot represent %v as a GeneralizedTime", t) + return + } + b.AddASN1(asn1.GeneralizedTime, func(c *Builder) { + c.AddBytes([]byte(t.Format(generalizedTimeFormatStr))) + }) +} + +// AddASN1BitString appends a DER-encoded ASN.1 BIT STRING. This does not +// support BIT STRINGs that are not a whole number of bytes. +func (b *Builder) AddASN1BitString(data []byte) { + b.AddASN1(asn1.BIT_STRING, func(b *Builder) { + b.AddUint8(0) + b.AddBytes(data) + }) +} + +func (b *Builder) addBase128Int(n int64) { + var length int + if n == 0 { + length = 1 + } else { + for i := n; i > 0; i >>= 7 { + length++ + } + } + + for i := length - 1; i >= 0; i-- { + o := byte(n >> uint(i*7)) + o &= 0x7f + if i != 0 { + o |= 0x80 + } + + b.add(o) + } +} + +func isValidOID(oid encoding_asn1.ObjectIdentifier) bool { + if len(oid) < 2 { + return false + } + + if oid[0] > 2 || (oid[0] <= 1 && oid[1] >= 40) { + return false + } + + for _, v := range oid { + if v < 0 { + return false + } + } + + return true +} + +func (b *Builder) AddASN1ObjectIdentifier(oid encoding_asn1.ObjectIdentifier) { + b.AddASN1(asn1.OBJECT_IDENTIFIER, func(b *Builder) { + if !isValidOID(oid) { + b.err = fmt.Errorf("cryptobyte: invalid OID: %v", oid) + return + } + + b.addBase128Int(int64(oid[0])*40 + int64(oid[1])) + for _, v := range oid[2:] { + b.addBase128Int(int64(v)) + } + }) +} + +func (b *Builder) AddASN1Boolean(v bool) { + b.AddASN1(asn1.BOOLEAN, func(b *Builder) { + if v { + b.AddUint8(0xff) + } else { + b.AddUint8(0) + } + }) +} + +func (b *Builder) AddASN1NULL() { + b.add(uint8(asn1.NULL), 0) +} + +// MarshalASN1 calls encoding_asn1.Marshal on its input and appends the result if +// successful or records an error if one occurred. +func (b *Builder) MarshalASN1(v interface{}) { + // NOTE(martinkr): This is somewhat of a hack to allow propagation of + // encoding_asn1.Marshal errors into Builder.err. N.B. if you call MarshalASN1 with a + // value embedded into a struct, its tag information is lost. + if b.err != nil { + return + } + bytes, err := encoding_asn1.Marshal(v) + if err != nil { + b.err = err + return + } + b.AddBytes(bytes) +} + +// AddASN1 appends an ASN.1 object. The object is prefixed with the given tag. +// Tags greater than 30 are not supported and result in an error (i.e. +// low-tag-number form only). The child builder passed to the +// BuilderContinuation can be used to build the content of the ASN.1 object. +func (b *Builder) AddASN1(tag asn1.Tag, f BuilderContinuation) { + if b.err != nil { + return + } + // Identifiers with the low five bits set indicate high-tag-number format + // (two or more octets), which we don't support. + if tag&0x1f == 0x1f { + b.err = fmt.Errorf("cryptobyte: high-tag number identifier octects not supported: 0x%x", tag) + return + } + b.AddUint8(uint8(tag)) + b.addLengthPrefixed(1, true, f) +} + +// String + +// ReadASN1Boolean decodes an ASN.1 INTEGER and converts it to a boolean +// representation into out and advances. It reports whether the read +// was successful. +func (s *String) ReadASN1Boolean(out *bool) bool { + var bytes String + if !s.ReadASN1(&bytes, asn1.INTEGER) || len(bytes) != 1 { + return false + } + + switch bytes[0] { + case 0: + *out = false + case 0xff: + *out = true + default: + return false + } + + return true +} + +var bigIntType = reflect.TypeOf((*big.Int)(nil)).Elem() + +// ReadASN1Integer decodes an ASN.1 INTEGER into out and advances. If out does +// not point to an integer or to a big.Int, it panics. It reports whether the +// read was successful. +func (s *String) ReadASN1Integer(out interface{}) bool { + if reflect.TypeOf(out).Kind() != reflect.Ptr { + panic("out is not a pointer") + } + switch reflect.ValueOf(out).Elem().Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + var i int64 + if !s.readASN1Int64(&i) || reflect.ValueOf(out).Elem().OverflowInt(i) { + return false + } + reflect.ValueOf(out).Elem().SetInt(i) + return true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + var u uint64 + if !s.readASN1Uint64(&u) || reflect.ValueOf(out).Elem().OverflowUint(u) { + return false + } + reflect.ValueOf(out).Elem().SetUint(u) + return true + case reflect.Struct: + if reflect.TypeOf(out).Elem() == bigIntType { + return s.readASN1BigInt(out.(*big.Int)) + } + } + panic("out does not point to an integer type") +} + +func checkASN1Integer(bytes []byte) bool { + if len(bytes) == 0 { + // An INTEGER is encoded with at least one octet. + return false + } + if len(bytes) == 1 { + return true + } + if bytes[0] == 0 && bytes[1]&0x80 == 0 || bytes[0] == 0xff && bytes[1]&0x80 == 0x80 { + // Value is not minimally encoded. + return false + } + return true +} + +var bigOne = big.NewInt(1) + +func (s *String) readASN1BigInt(out *big.Int) bool { + var bytes String + if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) { + return false + } + if bytes[0]&0x80 == 0x80 { + // Negative number. + neg := make([]byte, len(bytes)) + for i, b := range bytes { + neg[i] = ^b + } + out.SetBytes(neg) + out.Add(out, bigOne) + out.Neg(out) + } else { + out.SetBytes(bytes) + } + return true +} + +func (s *String) readASN1Int64(out *int64) bool { + var bytes String + if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) || !asn1Signed(out, bytes) { + return false + } + return true +} + +func asn1Signed(out *int64, n []byte) bool { + length := len(n) + if length > 8 { + return false + } + for i := 0; i < length; i++ { + *out <<= 8 + *out |= int64(n[i]) + } + // Shift up and down in order to sign extend the result. + *out <<= 64 - uint8(length)*8 + *out >>= 64 - uint8(length)*8 + return true +} + +func (s *String) readASN1Uint64(out *uint64) bool { + var bytes String + if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) || !asn1Unsigned(out, bytes) { + return false + } + return true +} + +func asn1Unsigned(out *uint64, n []byte) bool { + length := len(n) + if length > 9 || length == 9 && n[0] != 0 { + // Too large for uint64. + return false + } + if n[0]&0x80 != 0 { + // Negative number. + return false + } + for i := 0; i < length; i++ { + *out <<= 8 + *out |= uint64(n[i]) + } + return true +} + +// ReadASN1Int64WithTag decodes an ASN.1 INTEGER with the given tag into out +// and advances. It reports whether the read was successful and resulted in a +// value that can be represented in an int64. +func (s *String) ReadASN1Int64WithTag(out *int64, tag asn1.Tag) bool { + var bytes String + return s.ReadASN1(&bytes, tag) && checkASN1Integer(bytes) && asn1Signed(out, bytes) +} + +// ReadASN1Enum decodes an ASN.1 ENUMERATION into out and advances. It reports +// whether the read was successful. +func (s *String) ReadASN1Enum(out *int) bool { + var bytes String + var i int64 + if !s.ReadASN1(&bytes, asn1.ENUM) || !checkASN1Integer(bytes) || !asn1Signed(&i, bytes) { + return false + } + if int64(int(i)) != i { + return false + } + *out = int(i) + return true +} + +func (s *String) readBase128Int(out *int) bool { + ret := 0 + for i := 0; len(*s) > 0; i++ { + if i == 4 { + return false + } + ret <<= 7 + b := s.read(1)[0] + ret |= int(b & 0x7f) + if b&0x80 == 0 { + *out = ret + return true + } + } + return false // truncated +} + +// ReadASN1ObjectIdentifier decodes an ASN.1 OBJECT IDENTIFIER into out and +// advances. It reports whether the read was successful. +func (s *String) ReadASN1ObjectIdentifier(out *encoding_asn1.ObjectIdentifier) bool { + var bytes String + if !s.ReadASN1(&bytes, asn1.OBJECT_IDENTIFIER) || len(bytes) == 0 { + return false + } + + // In the worst case, we get two elements from the first byte (which is + // encoded differently) and then every varint is a single byte long. + components := make([]int, len(bytes)+1) + + // The first varint is 40*value1 + value2: + // According to this packing, value1 can take the values 0, 1 and 2 only. + // When value1 = 0 or value1 = 1, then value2 is <= 39. When value1 = 2, + // then there are no restrictions on value2. + var v int + if !bytes.readBase128Int(&v) { + return false + } + if v < 80 { + components[0] = v / 40 + components[1] = v % 40 + } else { + components[0] = 2 + components[1] = v - 80 + } + + i := 2 + for ; len(bytes) > 0; i++ { + if !bytes.readBase128Int(&v) { + return false + } + components[i] = v + } + *out = components[:i] + return true +} + +// ReadASN1GeneralizedTime decodes an ASN.1 GENERALIZEDTIME into out and +// advances. It reports whether the read was successful. +func (s *String) ReadASN1GeneralizedTime(out *time.Time) bool { + var bytes String + if !s.ReadASN1(&bytes, asn1.GeneralizedTime) { + return false + } + t := string(bytes) + res, err := time.Parse(generalizedTimeFormatStr, t) + if err != nil { + return false + } + if serialized := res.Format(generalizedTimeFormatStr); serialized != t { + return false + } + *out = res + return true +} + +// ReadASN1BitString decodes an ASN.1 BIT STRING into out and advances. +// It reports whether the read was successful. +func (s *String) ReadASN1BitString(out *encoding_asn1.BitString) bool { + var bytes String + if !s.ReadASN1(&bytes, asn1.BIT_STRING) || len(bytes) == 0 { + return false + } + + paddingBits := uint8(bytes[0]) + bytes = bytes[1:] + if paddingBits > 7 || + len(bytes) == 0 && paddingBits != 0 || + len(bytes) > 0 && bytes[len(bytes)-1]&(1< 4 || len(*s) < int(2+lenLen) { + return false + } + + lenBytes := String((*s)[2 : 2+lenLen]) + if !lenBytes.readUnsigned(&len32, int(lenLen)) { + return false + } + + // ITU-T X.690 section 10.1 (DER length forms) requires encoding the length + // with the minimum number of octets. + if len32 < 128 { + // Length should have used short-form encoding. + return false + } + if len32>>((lenLen-1)*8) == 0 { + // Leading octet is 0. Length should have been at least one byte shorter. + return false + } + + headerLen = 2 + uint32(lenLen) + if headerLen+len32 < len32 { + // Overflow. + return false + } + length = headerLen + len32 + } + + if uint32(int(length)) != length || !s.ReadBytes((*[]byte)(out), int(length)) { + return false + } + if skipHeader && !out.Skip(int(headerLen)) { + panic("cryptobyte: internal error") + } + + return true +} diff --git a/vendor/golang.org/x/crypto/cryptobyte/asn1/asn1.go b/vendor/golang.org/x/crypto/cryptobyte/asn1/asn1.go new file mode 100644 index 0000000000000..cda8e3edfd5ea --- /dev/null +++ b/vendor/golang.org/x/crypto/cryptobyte/asn1/asn1.go @@ -0,0 +1,46 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package asn1 contains supporting types for parsing and building ASN.1 +// messages with the cryptobyte package. +package asn1 // import "golang.org/x/crypto/cryptobyte/asn1" + +// Tag represents an ASN.1 identifier octet, consisting of a tag number +// (indicating a type) and class (such as context-specific or constructed). +// +// Methods in the cryptobyte package only support the low-tag-number form, i.e. +// a single identifier octet with bits 7-8 encoding the class and bits 1-6 +// encoding the tag number. +type Tag uint8 + +const ( + classConstructed = 0x20 + classContextSpecific = 0x80 +) + +// Constructed returns t with the constructed class bit set. +func (t Tag) Constructed() Tag { return t | classConstructed } + +// ContextSpecific returns t with the context-specific class bit set. +func (t Tag) ContextSpecific() Tag { return t | classContextSpecific } + +// The following is a list of standard tag and class combinations. +const ( + BOOLEAN = Tag(1) + INTEGER = Tag(2) + BIT_STRING = Tag(3) + OCTET_STRING = Tag(4) + NULL = Tag(5) + OBJECT_IDENTIFIER = Tag(6) + ENUM = Tag(10) + UTF8String = Tag(12) + SEQUENCE = Tag(16 | classConstructed) + SET = Tag(17 | classConstructed) + PrintableString = Tag(19) + T61String = Tag(20) + IA5String = Tag(22) + UTCTime = Tag(23) + GeneralizedTime = Tag(24) + GeneralString = Tag(27) +) diff --git a/vendor/golang.org/x/crypto/cryptobyte/builder.go b/vendor/golang.org/x/crypto/cryptobyte/builder.go new file mode 100644 index 0000000000000..29b4c7641279c --- /dev/null +++ b/vendor/golang.org/x/crypto/cryptobyte/builder.go @@ -0,0 +1,309 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cryptobyte + +import ( + "errors" + "fmt" +) + +// A Builder builds byte strings from fixed-length and length-prefixed values. +// Builders either allocate space as needed, or are ‘fixed’, which means that +// they write into a given buffer and produce an error if it's exhausted. +// +// The zero value is a usable Builder that allocates space as needed. +// +// Simple values are marshaled and appended to a Builder using methods on the +// Builder. Length-prefixed values are marshaled by providing a +// BuilderContinuation, which is a function that writes the inner contents of +// the value to a given Builder. See the documentation for BuilderContinuation +// for details. +type Builder struct { + err error + result []byte + fixedSize bool + child *Builder + offset int + pendingLenLen int + pendingIsASN1 bool + inContinuation *bool +} + +// NewBuilder creates a Builder that appends its output to the given buffer. +// Like append(), the slice will be reallocated if its capacity is exceeded. +// Use Bytes to get the final buffer. +func NewBuilder(buffer []byte) *Builder { + return &Builder{ + result: buffer, + } +} + +// NewFixedBuilder creates a Builder that appends its output into the given +// buffer. This builder does not reallocate the output buffer. Writes that +// would exceed the buffer's capacity are treated as an error. +func NewFixedBuilder(buffer []byte) *Builder { + return &Builder{ + result: buffer, + fixedSize: true, + } +} + +// Bytes returns the bytes written by the builder or an error if one has +// occurred during during building. +func (b *Builder) Bytes() ([]byte, error) { + if b.err != nil { + return nil, b.err + } + return b.result[b.offset:], nil +} + +// BytesOrPanic returns the bytes written by the builder or panics if an error +// has occurred during building. +func (b *Builder) BytesOrPanic() []byte { + if b.err != nil { + panic(b.err) + } + return b.result[b.offset:] +} + +// AddUint8 appends an 8-bit value to the byte string. +func (b *Builder) AddUint8(v uint8) { + b.add(byte(v)) +} + +// AddUint16 appends a big-endian, 16-bit value to the byte string. +func (b *Builder) AddUint16(v uint16) { + b.add(byte(v>>8), byte(v)) +} + +// AddUint24 appends a big-endian, 24-bit value to the byte string. The highest +// byte of the 32-bit input value is silently truncated. +func (b *Builder) AddUint24(v uint32) { + b.add(byte(v>>16), byte(v>>8), byte(v)) +} + +// AddUint32 appends a big-endian, 32-bit value to the byte string. +func (b *Builder) AddUint32(v uint32) { + b.add(byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) +} + +// AddBytes appends a sequence of bytes to the byte string. +func (b *Builder) AddBytes(v []byte) { + b.add(v...) +} + +// BuilderContinuation is continuation-passing interface for building +// length-prefixed byte sequences. Builder methods for length-prefixed +// sequences (AddUint8LengthPrefixed etc) will invoke the BuilderContinuation +// supplied to them. The child builder passed to the continuation can be used +// to build the content of the length-prefixed sequence. For example: +// +// parent := cryptobyte.NewBuilder() +// parent.AddUint8LengthPrefixed(func (child *Builder) { +// child.AddUint8(42) +// child.AddUint8LengthPrefixed(func (grandchild *Builder) { +// grandchild.AddUint8(5) +// }) +// }) +// +// It is an error to write more bytes to the child than allowed by the reserved +// length prefix. After the continuation returns, the child must be considered +// invalid, i.e. users must not store any copies or references of the child +// that outlive the continuation. +// +// If the continuation panics with a value of type BuildError then the inner +// error will be returned as the error from Bytes. If the child panics +// otherwise then Bytes will repanic with the same value. +type BuilderContinuation func(child *Builder) + +// BuildError wraps an error. If a BuilderContinuation panics with this value, +// the panic will be recovered and the inner error will be returned from +// Builder.Bytes. +type BuildError struct { + Err error +} + +// AddUint8LengthPrefixed adds a 8-bit length-prefixed byte sequence. +func (b *Builder) AddUint8LengthPrefixed(f BuilderContinuation) { + b.addLengthPrefixed(1, false, f) +} + +// AddUint16LengthPrefixed adds a big-endian, 16-bit length-prefixed byte sequence. +func (b *Builder) AddUint16LengthPrefixed(f BuilderContinuation) { + b.addLengthPrefixed(2, false, f) +} + +// AddUint24LengthPrefixed adds a big-endian, 24-bit length-prefixed byte sequence. +func (b *Builder) AddUint24LengthPrefixed(f BuilderContinuation) { + b.addLengthPrefixed(3, false, f) +} + +// AddUint32LengthPrefixed adds a big-endian, 32-bit length-prefixed byte sequence. +func (b *Builder) AddUint32LengthPrefixed(f BuilderContinuation) { + b.addLengthPrefixed(4, false, f) +} + +func (b *Builder) callContinuation(f BuilderContinuation, arg *Builder) { + if !*b.inContinuation { + *b.inContinuation = true + + defer func() { + *b.inContinuation = false + + r := recover() + if r == nil { + return + } + + if buildError, ok := r.(BuildError); ok { + b.err = buildError.Err + } else { + panic(r) + } + }() + } + + f(arg) +} + +func (b *Builder) addLengthPrefixed(lenLen int, isASN1 bool, f BuilderContinuation) { + // Subsequent writes can be ignored if the builder has encountered an error. + if b.err != nil { + return + } + + offset := len(b.result) + b.add(make([]byte, lenLen)...) + + if b.inContinuation == nil { + b.inContinuation = new(bool) + } + + b.child = &Builder{ + result: b.result, + fixedSize: b.fixedSize, + offset: offset, + pendingLenLen: lenLen, + pendingIsASN1: isASN1, + inContinuation: b.inContinuation, + } + + b.callContinuation(f, b.child) + b.flushChild() + if b.child != nil { + panic("cryptobyte: internal error") + } +} + +func (b *Builder) flushChild() { + if b.child == nil { + return + } + b.child.flushChild() + child := b.child + b.child = nil + + if child.err != nil { + b.err = child.err + return + } + + length := len(child.result) - child.pendingLenLen - child.offset + + if length < 0 { + panic("cryptobyte: internal error") // result unexpectedly shrunk + } + + if child.pendingIsASN1 { + // For ASN.1, we reserved a single byte for the length. If that turned out + // to be incorrect, we have to move the contents along in order to make + // space. + if child.pendingLenLen != 1 { + panic("cryptobyte: internal error") + } + var lenLen, lenByte uint8 + if int64(length) > 0xfffffffe { + b.err = errors.New("pending ASN.1 child too long") + return + } else if length > 0xffffff { + lenLen = 5 + lenByte = 0x80 | 4 + } else if length > 0xffff { + lenLen = 4 + lenByte = 0x80 | 3 + } else if length > 0xff { + lenLen = 3 + lenByte = 0x80 | 2 + } else if length > 0x7f { + lenLen = 2 + lenByte = 0x80 | 1 + } else { + lenLen = 1 + lenByte = uint8(length) + length = 0 + } + + // Insert the initial length byte, make space for successive length bytes, + // and adjust the offset. + child.result[child.offset] = lenByte + extraBytes := int(lenLen - 1) + if extraBytes != 0 { + child.add(make([]byte, extraBytes)...) + childStart := child.offset + child.pendingLenLen + copy(child.result[childStart+extraBytes:], child.result[childStart:]) + } + child.offset++ + child.pendingLenLen = extraBytes + } + + l := length + for i := child.pendingLenLen - 1; i >= 0; i-- { + child.result[child.offset+i] = uint8(l) + l >>= 8 + } + if l != 0 { + b.err = fmt.Errorf("cryptobyte: pending child length %d exceeds %d-byte length prefix", length, child.pendingLenLen) + return + } + + if !b.fixedSize { + b.result = child.result // In case child reallocated result. + } +} + +func (b *Builder) add(bytes ...byte) { + if b.err != nil { + return + } + if b.child != nil { + panic("attempted write while child is pending") + } + if len(b.result)+len(bytes) < len(bytes) { + b.err = errors.New("cryptobyte: length overflow") + } + if b.fixedSize && len(b.result)+len(bytes) > cap(b.result) { + b.err = errors.New("cryptobyte: Builder is exceeding its fixed-size buffer") + return + } + b.result = append(b.result, bytes...) +} + +// A MarshalingValue marshals itself into a Builder. +type MarshalingValue interface { + // Marshal is called by Builder.AddValue. It receives a pointer to a builder + // to marshal itself into. It may return an error that occurred during + // marshaling, such as unset or invalid values. + Marshal(b *Builder) error +} + +// AddValue calls Marshal on v, passing a pointer to the builder to append to. +// If Marshal returns an error, it is set on the Builder so that subsequent +// appends don't have an effect. +func (b *Builder) AddValue(v MarshalingValue) { + err := v.Marshal(b) + if err != nil { + b.err = err + } +} diff --git a/vendor/golang.org/x/crypto/cryptobyte/string.go b/vendor/golang.org/x/crypto/cryptobyte/string.go new file mode 100644 index 0000000000000..39bf98aeead81 --- /dev/null +++ b/vendor/golang.org/x/crypto/cryptobyte/string.go @@ -0,0 +1,166 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package cryptobyte contains types that help with parsing and constructing +// length-prefixed, binary messages, including ASN.1 DER. (The asn1 subpackage +// contains useful ASN.1 constants.) +// +// The String type is for parsing. It wraps a []byte slice and provides helper +// functions for consuming structures, value by value. +// +// The Builder type is for constructing messages. It providers helper functions +// for appending values and also for appending length-prefixed submessages – +// without having to worry about calculating the length prefix ahead of time. +// +// See the documentation and examples for the Builder and String types to get +// started. +package cryptobyte // import "golang.org/x/crypto/cryptobyte" + +// String represents a string of bytes. It provides methods for parsing +// fixed-length and length-prefixed values from it. +type String []byte + +// read advances a String by n bytes and returns them. If less than n bytes +// remain, it returns nil. +func (s *String) read(n int) []byte { + if len(*s) < n { + return nil + } + v := (*s)[:n] + *s = (*s)[n:] + return v +} + +// Skip advances the String by n byte and reports whether it was successful. +func (s *String) Skip(n int) bool { + return s.read(n) != nil +} + +// ReadUint8 decodes an 8-bit value into out and advances over it. +// It reports whether the read was successful. +func (s *String) ReadUint8(out *uint8) bool { + v := s.read(1) + if v == nil { + return false + } + *out = uint8(v[0]) + return true +} + +// ReadUint16 decodes a big-endian, 16-bit value into out and advances over it. +// It reports whether the read was successful. +func (s *String) ReadUint16(out *uint16) bool { + v := s.read(2) + if v == nil { + return false + } + *out = uint16(v[0])<<8 | uint16(v[1]) + return true +} + +// ReadUint24 decodes a big-endian, 24-bit value into out and advances over it. +// It reports whether the read was successful. +func (s *String) ReadUint24(out *uint32) bool { + v := s.read(3) + if v == nil { + return false + } + *out = uint32(v[0])<<16 | uint32(v[1])<<8 | uint32(v[2]) + return true +} + +// ReadUint32 decodes a big-endian, 32-bit value into out and advances over it. +// It reports whether the read was successful. +func (s *String) ReadUint32(out *uint32) bool { + v := s.read(4) + if v == nil { + return false + } + *out = uint32(v[0])<<24 | uint32(v[1])<<16 | uint32(v[2])<<8 | uint32(v[3]) + return true +} + +func (s *String) readUnsigned(out *uint32, length int) bool { + v := s.read(length) + if v == nil { + return false + } + var result uint32 + for i := 0; i < length; i++ { + result <<= 8 + result |= uint32(v[i]) + } + *out = result + return true +} + +func (s *String) readLengthPrefixed(lenLen int, outChild *String) bool { + lenBytes := s.read(lenLen) + if lenBytes == nil { + return false + } + var length uint32 + for _, b := range lenBytes { + length = length << 8 + length = length | uint32(b) + } + if int(length) < 0 { + // This currently cannot overflow because we read uint24 at most, but check + // anyway in case that changes in the future. + return false + } + v := s.read(int(length)) + if v == nil { + return false + } + *outChild = v + return true +} + +// ReadUint8LengthPrefixed reads the content of an 8-bit length-prefixed value +// into out and advances over it. It reports whether the read was successful. +func (s *String) ReadUint8LengthPrefixed(out *String) bool { + return s.readLengthPrefixed(1, out) +} + +// ReadUint16LengthPrefixed reads the content of a big-endian, 16-bit +// length-prefixed value into out and advances over it. It reports whether the +// read was successful. +func (s *String) ReadUint16LengthPrefixed(out *String) bool { + return s.readLengthPrefixed(2, out) +} + +// ReadUint24LengthPrefixed reads the content of a big-endian, 24-bit +// length-prefixed value into out and advances over it. It reports whether +// the read was successful. +func (s *String) ReadUint24LengthPrefixed(out *String) bool { + return s.readLengthPrefixed(3, out) +} + +// ReadBytes reads n bytes into out and advances over them. It reports +// whether the read was successful. +func (s *String) ReadBytes(out *[]byte, n int) bool { + v := s.read(n) + if v == nil { + return false + } + *out = v + return true +} + +// CopyBytes copies len(out) bytes into out and advances over them. It reports +// whether the copy operation was successful +func (s *String) CopyBytes(out []byte) bool { + n := len(out) + v := s.read(n) + if v == nil { + return false + } + return copy(out, v) == n +} + +// Empty reports whether the string does not contain any bytes. +func (s String) Empty() bool { + return len(s) == 0 +} diff --git a/vendor/golang.org/x/crypto/ocsp/ocsp.go b/vendor/golang.org/x/crypto/ocsp/ocsp.go new file mode 100644 index 0000000000000..5edc9c97c2034 --- /dev/null +++ b/vendor/golang.org/x/crypto/ocsp/ocsp.go @@ -0,0 +1,781 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package ocsp parses OCSP responses as specified in RFC 2560. OCSP responses +// are signed messages attesting to the validity of a certificate for a small +// period of time. This is used to manage revocation for X.509 certificates. +package ocsp // import "golang.org/x/crypto/ocsp" + +import ( + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + _ "crypto/sha1" + _ "crypto/sha256" + _ "crypto/sha512" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "errors" + "fmt" + "math/big" + "strconv" + "time" +) + +var idPKIXOCSPBasic = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 5, 5, 7, 48, 1, 1}) + +// ResponseStatus contains the result of an OCSP request. See +// https://tools.ietf.org/html/rfc6960#section-2.3 +type ResponseStatus int + +const ( + Success ResponseStatus = 0 + Malformed ResponseStatus = 1 + InternalError ResponseStatus = 2 + TryLater ResponseStatus = 3 + // Status code four is unused in OCSP. See + // https://tools.ietf.org/html/rfc6960#section-4.2.1 + SignatureRequired ResponseStatus = 5 + Unauthorized ResponseStatus = 6 +) + +func (r ResponseStatus) String() string { + switch r { + case Success: + return "success" + case Malformed: + return "malformed" + case InternalError: + return "internal error" + case TryLater: + return "try later" + case SignatureRequired: + return "signature required" + case Unauthorized: + return "unauthorized" + default: + return "unknown OCSP status: " + strconv.Itoa(int(r)) + } +} + +// ResponseError is an error that may be returned by ParseResponse to indicate +// that the response itself is an error, not just that its indicating that a +// certificate is revoked, unknown, etc. +type ResponseError struct { + Status ResponseStatus +} + +func (r ResponseError) Error() string { + return "ocsp: error from server: " + r.Status.String() +} + +// These are internal structures that reflect the ASN.1 structure of an OCSP +// response. See RFC 2560, section 4.2. + +type certID struct { + HashAlgorithm pkix.AlgorithmIdentifier + NameHash []byte + IssuerKeyHash []byte + SerialNumber *big.Int +} + +// https://tools.ietf.org/html/rfc2560#section-4.1.1 +type ocspRequest struct { + TBSRequest tbsRequest +} + +type tbsRequest struct { + Version int `asn1:"explicit,tag:0,default:0,optional"` + RequestorName pkix.RDNSequence `asn1:"explicit,tag:1,optional"` + RequestList []request +} + +type request struct { + Cert certID +} + +type responseASN1 struct { + Status asn1.Enumerated + Response responseBytes `asn1:"explicit,tag:0,optional"` +} + +type responseBytes struct { + ResponseType asn1.ObjectIdentifier + Response []byte +} + +type basicResponse struct { + TBSResponseData responseData + SignatureAlgorithm pkix.AlgorithmIdentifier + Signature asn1.BitString + Certificates []asn1.RawValue `asn1:"explicit,tag:0,optional"` +} + +type responseData struct { + Raw asn1.RawContent + Version int `asn1:"optional,default:0,explicit,tag:0"` + RawResponderID asn1.RawValue + ProducedAt time.Time `asn1:"generalized"` + Responses []singleResponse +} + +type singleResponse struct { + CertID certID + Good asn1.Flag `asn1:"tag:0,optional"` + Revoked revokedInfo `asn1:"tag:1,optional"` + Unknown asn1.Flag `asn1:"tag:2,optional"` + ThisUpdate time.Time `asn1:"generalized"` + NextUpdate time.Time `asn1:"generalized,explicit,tag:0,optional"` + SingleExtensions []pkix.Extension `asn1:"explicit,tag:1,optional"` +} + +type revokedInfo struct { + RevocationTime time.Time `asn1:"generalized"` + Reason asn1.Enumerated `asn1:"explicit,tag:0,optional"` +} + +var ( + oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2} + oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4} + oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} + oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} + oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} + oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} + oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} + oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 2} + oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1} + oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2} + oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3} + oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4} +) + +var hashOIDs = map[crypto.Hash]asn1.ObjectIdentifier{ + crypto.SHA1: asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26}), + crypto.SHA256: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 1}), + crypto.SHA384: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 2}), + crypto.SHA512: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 3}), +} + +// TODO(rlb): This is also from crypto/x509, so same comment as AGL's below +var signatureAlgorithmDetails = []struct { + algo x509.SignatureAlgorithm + oid asn1.ObjectIdentifier + pubKeyAlgo x509.PublicKeyAlgorithm + hash crypto.Hash +}{ + {x509.MD2WithRSA, oidSignatureMD2WithRSA, x509.RSA, crypto.Hash(0) /* no value for MD2 */}, + {x509.MD5WithRSA, oidSignatureMD5WithRSA, x509.RSA, crypto.MD5}, + {x509.SHA1WithRSA, oidSignatureSHA1WithRSA, x509.RSA, crypto.SHA1}, + {x509.SHA256WithRSA, oidSignatureSHA256WithRSA, x509.RSA, crypto.SHA256}, + {x509.SHA384WithRSA, oidSignatureSHA384WithRSA, x509.RSA, crypto.SHA384}, + {x509.SHA512WithRSA, oidSignatureSHA512WithRSA, x509.RSA, crypto.SHA512}, + {x509.DSAWithSHA1, oidSignatureDSAWithSHA1, x509.DSA, crypto.SHA1}, + {x509.DSAWithSHA256, oidSignatureDSAWithSHA256, x509.DSA, crypto.SHA256}, + {x509.ECDSAWithSHA1, oidSignatureECDSAWithSHA1, x509.ECDSA, crypto.SHA1}, + {x509.ECDSAWithSHA256, oidSignatureECDSAWithSHA256, x509.ECDSA, crypto.SHA256}, + {x509.ECDSAWithSHA384, oidSignatureECDSAWithSHA384, x509.ECDSA, crypto.SHA384}, + {x509.ECDSAWithSHA512, oidSignatureECDSAWithSHA512, x509.ECDSA, crypto.SHA512}, +} + +// TODO(rlb): This is also from crypto/x509, so same comment as AGL's below +func signingParamsForPublicKey(pub interface{}, requestedSigAlgo x509.SignatureAlgorithm) (hashFunc crypto.Hash, sigAlgo pkix.AlgorithmIdentifier, err error) { + var pubType x509.PublicKeyAlgorithm + + switch pub := pub.(type) { + case *rsa.PublicKey: + pubType = x509.RSA + hashFunc = crypto.SHA256 + sigAlgo.Algorithm = oidSignatureSHA256WithRSA + sigAlgo.Parameters = asn1.RawValue{ + Tag: 5, + } + + case *ecdsa.PublicKey: + pubType = x509.ECDSA + + switch pub.Curve { + case elliptic.P224(), elliptic.P256(): + hashFunc = crypto.SHA256 + sigAlgo.Algorithm = oidSignatureECDSAWithSHA256 + case elliptic.P384(): + hashFunc = crypto.SHA384 + sigAlgo.Algorithm = oidSignatureECDSAWithSHA384 + case elliptic.P521(): + hashFunc = crypto.SHA512 + sigAlgo.Algorithm = oidSignatureECDSAWithSHA512 + default: + err = errors.New("x509: unknown elliptic curve") + } + + default: + err = errors.New("x509: only RSA and ECDSA keys supported") + } + + if err != nil { + return + } + + if requestedSigAlgo == 0 { + return + } + + found := false + for _, details := range signatureAlgorithmDetails { + if details.algo == requestedSigAlgo { + if details.pubKeyAlgo != pubType { + err = errors.New("x509: requested SignatureAlgorithm does not match private key type") + return + } + sigAlgo.Algorithm, hashFunc = details.oid, details.hash + if hashFunc == 0 { + err = errors.New("x509: cannot sign with hash function requested") + return + } + found = true + break + } + } + + if !found { + err = errors.New("x509: unknown SignatureAlgorithm") + } + + return +} + +// TODO(agl): this is taken from crypto/x509 and so should probably be exported +// from crypto/x509 or crypto/x509/pkix. +func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) x509.SignatureAlgorithm { + for _, details := range signatureAlgorithmDetails { + if oid.Equal(details.oid) { + return details.algo + } + } + return x509.UnknownSignatureAlgorithm +} + +// TODO(rlb): This is not taken from crypto/x509, but it's of the same general form. +func getHashAlgorithmFromOID(target asn1.ObjectIdentifier) crypto.Hash { + for hash, oid := range hashOIDs { + if oid.Equal(target) { + return hash + } + } + return crypto.Hash(0) +} + +func getOIDFromHashAlgorithm(target crypto.Hash) asn1.ObjectIdentifier { + for hash, oid := range hashOIDs { + if hash == target { + return oid + } + } + return nil +} + +// This is the exposed reflection of the internal OCSP structures. + +// The status values that can be expressed in OCSP. See RFC 6960. +const ( + // Good means that the certificate is valid. + Good = iota + // Revoked means that the certificate has been deliberately revoked. + Revoked + // Unknown means that the OCSP responder doesn't know about the certificate. + Unknown + // ServerFailed is unused and was never used (see + // https://go-review.googlesource.com/#/c/18944). ParseResponse will + // return a ResponseError when an error response is parsed. + ServerFailed +) + +// The enumerated reasons for revoking a certificate. See RFC 5280. +const ( + Unspecified = 0 + KeyCompromise = 1 + CACompromise = 2 + AffiliationChanged = 3 + Superseded = 4 + CessationOfOperation = 5 + CertificateHold = 6 + + RemoveFromCRL = 8 + PrivilegeWithdrawn = 9 + AACompromise = 10 +) + +// Request represents an OCSP request. See RFC 6960. +type Request struct { + HashAlgorithm crypto.Hash + IssuerNameHash []byte + IssuerKeyHash []byte + SerialNumber *big.Int +} + +// Marshal marshals the OCSP request to ASN.1 DER encoded form. +func (req *Request) Marshal() ([]byte, error) { + hashAlg := getOIDFromHashAlgorithm(req.HashAlgorithm) + if hashAlg == nil { + return nil, errors.New("Unknown hash algorithm") + } + return asn1.Marshal(ocspRequest{ + tbsRequest{ + Version: 0, + RequestList: []request{ + { + Cert: certID{ + pkix.AlgorithmIdentifier{ + Algorithm: hashAlg, + Parameters: asn1.RawValue{Tag: 5 /* ASN.1 NULL */}, + }, + req.IssuerNameHash, + req.IssuerKeyHash, + req.SerialNumber, + }, + }, + }, + }, + }) +} + +// Response represents an OCSP response containing a single SingleResponse. See +// RFC 6960. +type Response struct { + // Status is one of {Good, Revoked, Unknown} + Status int + SerialNumber *big.Int + ProducedAt, ThisUpdate, NextUpdate, RevokedAt time.Time + RevocationReason int + Certificate *x509.Certificate + // TBSResponseData contains the raw bytes of the signed response. If + // Certificate is nil then this can be used to verify Signature. + TBSResponseData []byte + Signature []byte + SignatureAlgorithm x509.SignatureAlgorithm + + // IssuerHash is the hash used to compute the IssuerNameHash and IssuerKeyHash. + // Valid values are crypto.SHA1, crypto.SHA256, crypto.SHA384, and crypto.SHA512. + // If zero, the default is crypto.SHA1. + IssuerHash crypto.Hash + + // RawResponderName optionally contains the DER-encoded subject of the + // responder certificate. Exactly one of RawResponderName and + // ResponderKeyHash is set. + RawResponderName []byte + // ResponderKeyHash optionally contains the SHA-1 hash of the + // responder's public key. Exactly one of RawResponderName and + // ResponderKeyHash is set. + ResponderKeyHash []byte + + // Extensions contains raw X.509 extensions from the singleExtensions field + // of the OCSP response. When parsing certificates, this can be used to + // extract non-critical extensions that are not parsed by this package. When + // marshaling OCSP responses, the Extensions field is ignored, see + // ExtraExtensions. + Extensions []pkix.Extension + + // ExtraExtensions contains extensions to be copied, raw, into any marshaled + // OCSP response (in the singleExtensions field). Values override any + // extensions that would otherwise be produced based on the other fields. The + // ExtraExtensions field is not populated when parsing certificates, see + // Extensions. + ExtraExtensions []pkix.Extension +} + +// These are pre-serialized error responses for the various non-success codes +// defined by OCSP. The Unauthorized code in particular can be used by an OCSP +// responder that supports only pre-signed responses as a response to requests +// for certificates with unknown status. See RFC 5019. +var ( + MalformedRequestErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x01} + InternalErrorErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x02} + TryLaterErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x03} + SigRequredErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x05} + UnauthorizedErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x06} +) + +// CheckSignatureFrom checks that the signature in resp is a valid signature +// from issuer. This should only be used if resp.Certificate is nil. Otherwise, +// the OCSP response contained an intermediate certificate that created the +// signature. That signature is checked by ParseResponse and only +// resp.Certificate remains to be validated. +func (resp *Response) CheckSignatureFrom(issuer *x509.Certificate) error { + return issuer.CheckSignature(resp.SignatureAlgorithm, resp.TBSResponseData, resp.Signature) +} + +// ParseError results from an invalid OCSP response. +type ParseError string + +func (p ParseError) Error() string { + return string(p) +} + +// ParseRequest parses an OCSP request in DER form. It only supports +// requests for a single certificate. Signed requests are not supported. +// If a request includes a signature, it will result in a ParseError. +func ParseRequest(bytes []byte) (*Request, error) { + var req ocspRequest + rest, err := asn1.Unmarshal(bytes, &req) + if err != nil { + return nil, err + } + if len(rest) > 0 { + return nil, ParseError("trailing data in OCSP request") + } + + if len(req.TBSRequest.RequestList) == 0 { + return nil, ParseError("OCSP request contains no request body") + } + innerRequest := req.TBSRequest.RequestList[0] + + hashFunc := getHashAlgorithmFromOID(innerRequest.Cert.HashAlgorithm.Algorithm) + if hashFunc == crypto.Hash(0) { + return nil, ParseError("OCSP request uses unknown hash function") + } + + return &Request{ + HashAlgorithm: hashFunc, + IssuerNameHash: innerRequest.Cert.NameHash, + IssuerKeyHash: innerRequest.Cert.IssuerKeyHash, + SerialNumber: innerRequest.Cert.SerialNumber, + }, nil +} + +// ParseResponse parses an OCSP response in DER form. It only supports +// responses for a single certificate. If the response contains a certificate +// then the signature over the response is checked. If issuer is not nil then +// it will be used to validate the signature or embedded certificate. +// +// Invalid responses and parse failures will result in a ParseError. +// Error responses will result in a ResponseError. +func ParseResponse(bytes []byte, issuer *x509.Certificate) (*Response, error) { + return ParseResponseForCert(bytes, nil, issuer) +} + +// ParseResponseForCert parses an OCSP response in DER form and searches for a +// Response relating to cert. If such a Response is found and the OCSP response +// contains a certificate then the signature over the response is checked. If +// issuer is not nil then it will be used to validate the signature or embedded +// certificate. +// +// Invalid responses and parse failures will result in a ParseError. +// Error responses will result in a ResponseError. +func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Response, error) { + var resp responseASN1 + rest, err := asn1.Unmarshal(bytes, &resp) + if err != nil { + return nil, err + } + if len(rest) > 0 { + return nil, ParseError("trailing data in OCSP response") + } + + if status := ResponseStatus(resp.Status); status != Success { + return nil, ResponseError{status} + } + + if !resp.Response.ResponseType.Equal(idPKIXOCSPBasic) { + return nil, ParseError("bad OCSP response type") + } + + var basicResp basicResponse + rest, err = asn1.Unmarshal(resp.Response.Response, &basicResp) + if err != nil { + return nil, err + } + + if n := len(basicResp.TBSResponseData.Responses); n == 0 || cert == nil && n > 1 { + return nil, ParseError("OCSP response contains bad number of responses") + } + + var singleResp singleResponse + if cert == nil { + singleResp = basicResp.TBSResponseData.Responses[0] + } else { + match := false + for _, resp := range basicResp.TBSResponseData.Responses { + if cert.SerialNumber.Cmp(resp.CertID.SerialNumber) == 0 { + singleResp = resp + match = true + break + } + } + if !match { + return nil, ParseError("no response matching the supplied certificate") + } + } + + ret := &Response{ + TBSResponseData: basicResp.TBSResponseData.Raw, + Signature: basicResp.Signature.RightAlign(), + SignatureAlgorithm: getSignatureAlgorithmFromOID(basicResp.SignatureAlgorithm.Algorithm), + Extensions: singleResp.SingleExtensions, + SerialNumber: singleResp.CertID.SerialNumber, + ProducedAt: basicResp.TBSResponseData.ProducedAt, + ThisUpdate: singleResp.ThisUpdate, + NextUpdate: singleResp.NextUpdate, + } + + // Handle the ResponderID CHOICE tag. ResponderID can be flattened into + // TBSResponseData once https://go-review.googlesource.com/34503 has been + // released. + rawResponderID := basicResp.TBSResponseData.RawResponderID + switch rawResponderID.Tag { + case 1: // Name + var rdn pkix.RDNSequence + if rest, err := asn1.Unmarshal(rawResponderID.Bytes, &rdn); err != nil || len(rest) != 0 { + return nil, ParseError("invalid responder name") + } + ret.RawResponderName = rawResponderID.Bytes + case 2: // KeyHash + if rest, err := asn1.Unmarshal(rawResponderID.Bytes, &ret.ResponderKeyHash); err != nil || len(rest) != 0 { + return nil, ParseError("invalid responder key hash") + } + default: + return nil, ParseError("invalid responder id tag") + } + + if len(basicResp.Certificates) > 0 { + // Responders should only send a single certificate (if they + // send any) that connects the responder's certificate to the + // original issuer. We accept responses with multiple + // certificates due to a number responders sending them[1], but + // ignore all but the first. + // + // [1] https://github.com/golang/go/issues/21527 + ret.Certificate, err = x509.ParseCertificate(basicResp.Certificates[0].FullBytes) + if err != nil { + return nil, err + } + + if err := ret.CheckSignatureFrom(ret.Certificate); err != nil { + return nil, ParseError("bad signature on embedded certificate: " + err.Error()) + } + + if issuer != nil { + if err := issuer.CheckSignature(ret.Certificate.SignatureAlgorithm, ret.Certificate.RawTBSCertificate, ret.Certificate.Signature); err != nil { + return nil, ParseError("bad OCSP signature: " + err.Error()) + } + } + } else if issuer != nil { + if err := ret.CheckSignatureFrom(issuer); err != nil { + return nil, ParseError("bad OCSP signature: " + err.Error()) + } + } + + for _, ext := range singleResp.SingleExtensions { + if ext.Critical { + return nil, ParseError("unsupported critical extension") + } + } + + for h, oid := range hashOIDs { + if singleResp.CertID.HashAlgorithm.Algorithm.Equal(oid) { + ret.IssuerHash = h + break + } + } + if ret.IssuerHash == 0 { + return nil, ParseError("unsupported issuer hash algorithm") + } + + switch { + case bool(singleResp.Good): + ret.Status = Good + case bool(singleResp.Unknown): + ret.Status = Unknown + default: + ret.Status = Revoked + ret.RevokedAt = singleResp.Revoked.RevocationTime + ret.RevocationReason = int(singleResp.Revoked.Reason) + } + + return ret, nil +} + +// RequestOptions contains options for constructing OCSP requests. +type RequestOptions struct { + // Hash contains the hash function that should be used when + // constructing the OCSP request. If zero, SHA-1 will be used. + Hash crypto.Hash +} + +func (opts *RequestOptions) hash() crypto.Hash { + if opts == nil || opts.Hash == 0 { + // SHA-1 is nearly universally used in OCSP. + return crypto.SHA1 + } + return opts.Hash +} + +// CreateRequest returns a DER-encoded, OCSP request for the status of cert. If +// opts is nil then sensible defaults are used. +func CreateRequest(cert, issuer *x509.Certificate, opts *RequestOptions) ([]byte, error) { + hashFunc := opts.hash() + + // OCSP seems to be the only place where these raw hash identifiers are + // used. I took the following from + // http://msdn.microsoft.com/en-us/library/ff635603.aspx + _, ok := hashOIDs[hashFunc] + if !ok { + return nil, x509.ErrUnsupportedAlgorithm + } + + if !hashFunc.Available() { + return nil, x509.ErrUnsupportedAlgorithm + } + h := opts.hash().New() + + var publicKeyInfo struct { + Algorithm pkix.AlgorithmIdentifier + PublicKey asn1.BitString + } + if _, err := asn1.Unmarshal(issuer.RawSubjectPublicKeyInfo, &publicKeyInfo); err != nil { + return nil, err + } + + h.Write(publicKeyInfo.PublicKey.RightAlign()) + issuerKeyHash := h.Sum(nil) + + h.Reset() + h.Write(issuer.RawSubject) + issuerNameHash := h.Sum(nil) + + req := &Request{ + HashAlgorithm: hashFunc, + IssuerNameHash: issuerNameHash, + IssuerKeyHash: issuerKeyHash, + SerialNumber: cert.SerialNumber, + } + return req.Marshal() +} + +// CreateResponse returns a DER-encoded OCSP response with the specified contents. +// The fields in the response are populated as follows: +// +// The responder cert is used to populate the responder's name field, and the +// certificate itself is provided alongside the OCSP response signature. +// +// The issuer cert is used to puplate the IssuerNameHash and IssuerKeyHash fields. +// +// The template is used to populate the SerialNumber, Status, RevokedAt, +// RevocationReason, ThisUpdate, and NextUpdate fields. +// +// If template.IssuerHash is not set, SHA1 will be used. +// +// The ProducedAt date is automatically set to the current date, to the nearest minute. +func CreateResponse(issuer, responderCert *x509.Certificate, template Response, priv crypto.Signer) ([]byte, error) { + var publicKeyInfo struct { + Algorithm pkix.AlgorithmIdentifier + PublicKey asn1.BitString + } + if _, err := asn1.Unmarshal(issuer.RawSubjectPublicKeyInfo, &publicKeyInfo); err != nil { + return nil, err + } + + if template.IssuerHash == 0 { + template.IssuerHash = crypto.SHA1 + } + hashOID := getOIDFromHashAlgorithm(template.IssuerHash) + if hashOID == nil { + return nil, errors.New("unsupported issuer hash algorithm") + } + + if !template.IssuerHash.Available() { + return nil, fmt.Errorf("issuer hash algorithm %v not linked into binary", template.IssuerHash) + } + h := template.IssuerHash.New() + h.Write(publicKeyInfo.PublicKey.RightAlign()) + issuerKeyHash := h.Sum(nil) + + h.Reset() + h.Write(issuer.RawSubject) + issuerNameHash := h.Sum(nil) + + innerResponse := singleResponse{ + CertID: certID{ + HashAlgorithm: pkix.AlgorithmIdentifier{ + Algorithm: hashOID, + Parameters: asn1.RawValue{Tag: 5 /* ASN.1 NULL */}, + }, + NameHash: issuerNameHash, + IssuerKeyHash: issuerKeyHash, + SerialNumber: template.SerialNumber, + }, + ThisUpdate: template.ThisUpdate.UTC(), + NextUpdate: template.NextUpdate.UTC(), + SingleExtensions: template.ExtraExtensions, + } + + switch template.Status { + case Good: + innerResponse.Good = true + case Unknown: + innerResponse.Unknown = true + case Revoked: + innerResponse.Revoked = revokedInfo{ + RevocationTime: template.RevokedAt.UTC(), + Reason: asn1.Enumerated(template.RevocationReason), + } + } + + rawResponderID := asn1.RawValue{ + Class: 2, // context-specific + Tag: 1, // Name (explicit tag) + IsCompound: true, + Bytes: responderCert.RawSubject, + } + tbsResponseData := responseData{ + Version: 0, + RawResponderID: rawResponderID, + ProducedAt: time.Now().Truncate(time.Minute).UTC(), + Responses: []singleResponse{innerResponse}, + } + + tbsResponseDataDER, err := asn1.Marshal(tbsResponseData) + if err != nil { + return nil, err + } + + hashFunc, signatureAlgorithm, err := signingParamsForPublicKey(priv.Public(), template.SignatureAlgorithm) + if err != nil { + return nil, err + } + + responseHash := hashFunc.New() + responseHash.Write(tbsResponseDataDER) + signature, err := priv.Sign(rand.Reader, responseHash.Sum(nil), hashFunc) + if err != nil { + return nil, err + } + + response := basicResponse{ + TBSResponseData: tbsResponseData, + SignatureAlgorithm: signatureAlgorithm, + Signature: asn1.BitString{ + Bytes: signature, + BitLength: 8 * len(signature), + }, + } + if template.Certificate != nil { + response.Certificates = []asn1.RawValue{ + {FullBytes: template.Certificate.Raw}, + } + } + responseDER, err := asn1.Marshal(response) + if err != nil { + return nil, err + } + + return asn1.Marshal(responseASN1{ + Status: asn1.Enumerated(Success), + Response: responseBytes{ + ResponseType: idPKIXOCSPBasic, + Response: responseDER, + }, + }) +} diff --git a/vendor/gotest.tools/LICENSE b/vendor/gotest.tools/LICENSE new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/vendor/gotest.tools/LICENSE @@ -0,0 +1,202 @@ + + 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. diff --git a/vendor/gotest.tools/README.md b/vendor/gotest.tools/README.md new file mode 100644 index 0000000000000..f5df2043157f6 --- /dev/null +++ b/vendor/gotest.tools/README.md @@ -0,0 +1,31 @@ +# gotest.tools + +A collection of packages to augment `testing` and support common patterns. + +[![GoDoc](https://godoc.org/gotest.tools?status.svg)](https://godoc.org/gotest.tools) +[![CircleCI](https://circleci.com/gh/gotestyourself/gotestyourself/tree/master.svg?style=shield)](https://circleci.com/gh/gotestyourself/gotestyourself/tree/master) +[![Go Reportcard](https://goreportcard.com/badge/gotest.tools)](https://goreportcard.com/report/gotest.tools) + + +## Packages + +* [assert](http://godoc.org/gotest.tools/assert) - + compare values and fail the test when a comparison fails +* [env](http://godoc.org/gotest.tools/env) - + test code which uses environment variables +* [fs](http://godoc.org/gotest.tools/fs) - + create temporary files and compare a filesystem tree to an expected value +* [golden](http://godoc.org/gotest.tools/golden) - + compare large multi-line strings against values frozen in golden files +* [icmd](http://godoc.org/gotest.tools/icmd) - + execute binaries and test the output +* [poll](http://godoc.org/gotest.tools/poll) - + test asynchronous code by polling until a desired state is reached +* [skip](http://godoc.org/gotest.tools/skip) - + skip a test and print the source code of the condition used to skip the test + +## Related + +* [gotest.tools/gotestsum](https://github.com/gotestyourself/gotestsum) - go test runner with custom output +* [maxbrunsfeld/counterfeiter](https://github.com/maxbrunsfeld/counterfeiter) - generate fakes for interfaces +* [jonboulle/clockwork](https://github.com/jonboulle/clockwork) - a fake clock for testing code that uses `time` diff --git a/vendor/github.com/gotestyourself/gotestyourself/assert/assert.go b/vendor/gotest.tools/assert/assert.go similarity index 85% rename from vendor/github.com/gotestyourself/gotestyourself/assert/assert.go rename to vendor/gotest.tools/assert/assert.go index d2b05f41655ab..05d6635436f78 100644 --- a/vendor/github.com/gotestyourself/gotestyourself/assert/assert.go +++ b/vendor/gotest.tools/assert/assert.go @@ -8,7 +8,7 @@ comparison fails. The one difference is that Assert() will end the test executio immediately (using t.FailNow()) whereas Check() will fail the test (using t.Fail()), return the value of the comparison, then proceed with the rest of the test case. -Example Usage +Example usage The example below shows assert used with some common types. @@ -16,8 +16,8 @@ The example below shows assert used with some common types. import ( "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestEverything(t *testing.T) { @@ -49,12 +49,20 @@ The example below shows assert used with some common types. Comparisons -https://godoc.org/github.com/gotestyourself/gotestyourself/assert/cmp provides +Package https://godoc.org/gotest.tools/assert/cmp provides many common comparisons. Additional comparisons can be written to compare values in other ways. See the example Assert (CustomComparison). +Automated migration from testify + +gty-migrate-from-testify is a binary which can update source code which uses +testify assertions to use the assertions provided by this package. + +See http://bit.do/cmd-gty-migrate-from-testify. + + */ -package assert +package assert // import "gotest.tools/assert" import ( "fmt" @@ -62,9 +70,9 @@ import ( "go/token" gocmp "github.com/google/go-cmp/cmp" - "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/internal/format" - "github.com/gotestyourself/gotestyourself/internal/source" + "gotest.tools/assert/cmp" + "gotest.tools/internal/format" + "gotest.tools/internal/source" ) // BoolOrComparison can be a bool, or cmp.Comparison. See Assert() for usage. @@ -234,7 +242,17 @@ func NilError(t TestingT, err error, msgAndArgs ...interface{}) { } // Equal uses the == operator to assert two values are equal and fails the test -// if they are not equal. This is equivalent to Assert(t, cmp.Equal(x, y)). +// if they are not equal. +// +// If the comparison fails Equal will use the variable names for x and y as part +// of the failure message to identify the actual and expected values. +// +// If either x or y are a multi-line string the failure message will include a +// unified diff of the two values. If the values only differ by whitespace +// the unified diff will be augmented by replacing whitespace characters with +// visible characters to identify the whitespace difference. +// +// This is equivalent to Assert(t, cmp.Equal(x, y)). func Equal(t TestingT, x, y interface{}, msgAndArgs ...interface{}) { if ht, ok := t.(helperT); ok { ht.Helper() @@ -242,8 +260,12 @@ func Equal(t TestingT, x, y interface{}, msgAndArgs ...interface{}) { assert(t, t.FailNow, argsAfterT, cmp.Equal(x, y), msgAndArgs...) } -// DeepEqual uses https://github.com/google/go-cmp/cmp to assert two values -// are equal and fails the test if they are not equal. +// DeepEqual uses google/go-cmp (http://bit.do/go-cmp) to assert two values are +// equal and fails the test if they are not equal. +// +// Package https://godoc.org/gotest.tools/assert/opt provides some additional +// commonly used Options. +// // This is equivalent to Assert(t, cmp.DeepEqual(x, y)). func DeepEqual(t TestingT, x, y interface{}, opts ...gocmp.Option) { if ht, ok := t.(helperT); ok { @@ -276,7 +298,7 @@ func ErrorContains(t TestingT, err error, substring string, msgAndArgs ...interf // // Expected can be one of: // a func(error) bool which returns true if the error is the expected type, -// an instance of a struct of the expected type, +// an instance of (or a pointer to) a struct of the expected type, // a pointer to an interface the error is expected to implement, // a reflect.Type of the expected struct or interface. // diff --git a/vendor/github.com/gotestyourself/gotestyourself/assert/cmp/compare.go b/vendor/gotest.tools/assert/cmp/compare.go similarity index 90% rename from vendor/github.com/gotestyourself/gotestyourself/assert/cmp/compare.go rename to vendor/gotest.tools/assert/cmp/compare.go index 64a25f00b339b..ae03749e203b6 100644 --- a/vendor/github.com/gotestyourself/gotestyourself/assert/cmp/compare.go +++ b/vendor/gotest.tools/assert/cmp/compare.go @@ -1,5 +1,5 @@ /*Package cmp provides Comparisons for Assert and Check*/ -package cmp +package cmp // import "gotest.tools/assert/cmp" import ( "fmt" @@ -7,7 +7,7 @@ import ( "strings" "github.com/google/go-cmp/cmp" - "github.com/pmezard/go-difflib/difflib" + "gotest.tools/internal/format" ) // Comparison is a function which compares values and returns ResultSuccess if @@ -15,10 +15,12 @@ import ( // Result will contain a message about why it failed. type Comparison func() Result -// DeepEqual compares two values using https://godoc.org/github.com/google/go-cmp/cmp +// DeepEqual compares two values using google/go-cmp (http://bit.do/go-cmp) // and succeeds if the values are equal. // // The comparison can be customized using comparison Options. +// Package https://godoc.org/gotest.tools/assert/opt provides some additional +// commonly used Options. func DeepEqual(x, y interface{}, opts ...cmp.Option) Comparison { return func() (result Result) { defer func() { @@ -27,7 +29,10 @@ func DeepEqual(x, y interface{}, opts ...cmp.Option) Comparison { } }() diff := cmp.Diff(x, y, opts...) - return toResult(diff == "", "\n"+diff) + if diff == "" { + return ResultSuccess + } + return multiLineDiffResult(diff) } } @@ -53,14 +58,15 @@ func toResult(success bool, msg string) Result { return ResultFailure(msg) } -// Equal succeeds if x == y. +// Equal succeeds if x == y. See assert.Equal for full documentation. func Equal(x, y interface{}) Comparison { return func() Result { switch { case x == y: return ResultSuccess case isMultiLineStringCompare(x, y): - return multiLineStringDiffResult(x.(string), y.(string)) + diff := format.UnifiedDiff(format.DiffConfig{A: x.(string), B: y.(string)}) + return multiLineDiffResult(diff) } return ResultFailureTemplate(` {{- .Data.x}} ( @@ -86,15 +92,7 @@ func isMultiLineStringCompare(x, y interface{}) bool { return strings.Contains(strX, "\n") || strings.Contains(strY, "\n") } -func multiLineStringDiffResult(x, y string) Result { - diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ - A: difflib.SplitLines(x), - B: difflib.SplitLines(y), - Context: 3, - }) - if err != nil { - return ResultFailure(fmt.Sprintf("failed to diff: %s", err)) - } +func multiLineDiffResult(diff string) Result { return ResultFailureTemplate(` --- {{ with callArg 0 }}{{ formatNode . }}{{else}}←{{end}} +++ {{ with callArg 1 }}{{ formatNode . }}{{else}}→{{end}} @@ -242,7 +240,7 @@ func isNil(obj interface{}, msgFunc func(reflect.Value) string) Comparison { // // Expected can be one of: // a func(error) bool which returns true if the error is the expected type, -// an instance of a struct of the expected type, +// an instance of (or a pointer to) a struct of the expected type, // a pointer to an interface the error is expected to implement, // a reflect.Type of the expected struct or interface. func ErrorType(err error, expected interface{}) Comparison { @@ -261,7 +259,7 @@ func ErrorType(err error, expected interface{}) Comparison { expectedType := reflect.TypeOf(expected) switch { - case expectedType.Kind() == reflect.Struct: + case expectedType.Kind() == reflect.Struct, isPtrToStruct(expectedType): return cmpErrorTypeEqualType(err, expectedType) case isPtrToInterface(expectedType): return cmpErrorTypeImplementsType(err, expectedType.Elem()) @@ -308,3 +306,7 @@ func cmpErrorTypeImplementsType(err error, expectedType reflect.Type) Result { func isPtrToInterface(typ reflect.Type) bool { return typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Interface } + +func isPtrToStruct(typ reflect.Type) bool { + return typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Struct +} diff --git a/vendor/github.com/gotestyourself/gotestyourself/assert/cmp/result.go b/vendor/gotest.tools/assert/cmp/result.go similarity index 97% rename from vendor/github.com/gotestyourself/gotestyourself/assert/cmp/result.go rename to vendor/gotest.tools/assert/cmp/result.go index 99f39eeb2749e..7c3c37dd721a7 100644 --- a/vendor/github.com/gotestyourself/gotestyourself/assert/cmp/result.go +++ b/vendor/gotest.tools/assert/cmp/result.go @@ -6,7 +6,7 @@ import ( "go/ast" "text/template" - "github.com/gotestyourself/gotestyourself/internal/source" + "gotest.tools/internal/source" ) // Result of a Comparison. diff --git a/vendor/github.com/gotestyourself/gotestyourself/assert/result.go b/vendor/gotest.tools/assert/result.go similarity index 93% rename from vendor/github.com/gotestyourself/gotestyourself/assert/result.go rename to vendor/gotest.tools/assert/result.go index b685b7f3e5c13..3900264d0bfa1 100644 --- a/vendor/github.com/gotestyourself/gotestyourself/assert/result.go +++ b/vendor/gotest.tools/assert/result.go @@ -4,9 +4,9 @@ import ( "fmt" "go/ast" - "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/internal/format" - "github.com/gotestyourself/gotestyourself/internal/source" + "gotest.tools/assert/cmp" + "gotest.tools/internal/format" + "gotest.tools/internal/source" ) func runComparison( diff --git a/vendor/github.com/gotestyourself/gotestyourself/env/env.go b/vendor/gotest.tools/env/env.go similarity index 84% rename from vendor/github.com/gotestyourself/gotestyourself/env/env.go rename to vendor/gotest.tools/env/env.go index 700430f1dd131..ed0f189916133 100644 --- a/vendor/github.com/gotestyourself/gotestyourself/env/env.go +++ b/vendor/gotest.tools/env/env.go @@ -1,13 +1,14 @@ /*Package env provides functions to test code that read environment variables or the current working directory. */ -package env +package env // import "gotest.tools/env" import ( "os" "strings" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" + "gotest.tools/x/subtest" ) type helperT interface { @@ -23,7 +24,7 @@ func Patch(t assert.TestingT, key, value string) func() { } oldValue, ok := os.LookupEnv(key) assert.NilError(t, os.Setenv(key, value)) - return func() { + cleanup := func() { if ht, ok := t.(helperT); ok { ht.Helper() } @@ -33,6 +34,10 @@ func Patch(t assert.TestingT, key, value string) func() { } assert.NilError(t, os.Setenv(key, oldValue)) } + if tc, ok := t.(subtest.TestContext); ok { + tc.AddCleanup(cleanup) + } + return cleanup } // PatchAll sets the environment to env, and returns a function which will @@ -47,7 +52,7 @@ func PatchAll(t assert.TestingT, env map[string]string) func() { for key, value := range env { assert.NilError(t, os.Setenv(key, value), "setenv %s=%s", key, value) } - return func() { + cleanup := func() { if ht, ok := t.(helperT); ok { ht.Helper() } @@ -56,6 +61,10 @@ func PatchAll(t assert.TestingT, env map[string]string) func() { assert.NilError(t, os.Setenv(key, oldVal), "setenv %s=%s", key, oldVal) } } + if tc, ok := t.(subtest.TestContext); ok { + tc.AddCleanup(cleanup) + } + return cleanup } // ToMap takes a list of strings in the format returned by os.Environ() and @@ -89,10 +98,14 @@ func ChangeWorkingDir(t assert.TestingT, dir string) func() { cwd, err := os.Getwd() assert.NilError(t, err) assert.NilError(t, os.Chdir(dir)) - return func() { + cleanup := func() { if ht, ok := t.(helperT); ok { ht.Helper() } assert.NilError(t, os.Chdir(cwd)) } + if tc, ok := t.(subtest.TestContext); ok { + tc.AddCleanup(cleanup) + } + return cleanup } diff --git a/vendor/github.com/gotestyourself/gotestyourself/fs/file.go b/vendor/gotest.tools/fs/file.go similarity index 77% rename from vendor/github.com/gotestyourself/gotestyourself/fs/file.go rename to vendor/gotest.tools/fs/file.go index a2f731093b02d..8bf0188d2f0cd 100644 --- a/vendor/github.com/gotestyourself/gotestyourself/fs/file.go +++ b/vendor/gotest.tools/fs/file.go @@ -1,17 +1,20 @@ -/*Package fs provides tools for creating and working with temporary files and -directories. +/*Package fs provides tools for creating temporary files, and testing the +contents and structure of a directory. */ -package fs +package fs // import "gotest.tools/fs" import ( "io/ioutil" "os" "path/filepath" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" + "gotest.tools/x/subtest" ) -// Path objects return their filesystem path. Both File and Dir implement Path. +// Path objects return their filesystem path. Path may be implemented by a +// real filesystem object (such as File and Dir) or by a type which updates +// entries in a Manifest. type Path interface { Path() string Remove() @@ -45,6 +48,9 @@ func NewFile(t assert.TestingT, prefix string, ops ...PathOp) *File { for _, op := range ops { assert.NilError(t, op(file)) } + if tc, ok := t.(subtest.TestContext); ok { + tc.AddCleanup(file.Remove) + } return file } @@ -77,6 +83,9 @@ func NewDir(t assert.TestingT, prefix string, ops ...PathOp) *Dir { for _, op := range ops { assert.NilError(t, op(dir)) } + if tc, ok := t.(subtest.TestContext); ok { + tc.AddCleanup(dir.Remove) + } return dir } diff --git a/vendor/gotest.tools/fs/manifest.go b/vendor/gotest.tools/fs/manifest.go new file mode 100644 index 0000000000000..00976ef19c9b9 --- /dev/null +++ b/vendor/gotest.tools/fs/manifest.go @@ -0,0 +1,129 @@ +package fs + +import ( + "io" + "io/ioutil" + "os" + "path/filepath" + + "github.com/pkg/errors" + "gotest.tools/assert" +) + +// Manifest stores the expected structure and properties of files and directories +// in a filesystem. +type Manifest struct { + root *directory +} + +type resource struct { + mode os.FileMode + uid uint32 + gid uint32 +} + +type file struct { + resource + content io.ReadCloser +} + +func (f *file) Type() string { + return "file" +} + +type symlink struct { + resource + target string +} + +func (f *symlink) Type() string { + return "symlink" +} + +type directory struct { + resource + items map[string]dirEntry +} + +func (f *directory) Type() string { + return "directory" +} + +type dirEntry interface { + Type() string +} + +// ManifestFromDir creates a Manifest by reading the directory at path. The +// manifest stores the structure and properties of files in the directory. +// ManifestFromDir can be used with Equal to compare two directories. +func ManifestFromDir(t assert.TestingT, path string) Manifest { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + + manifest, err := manifestFromDir(path) + assert.NilError(t, err) + return manifest +} + +func manifestFromDir(path string) (Manifest, error) { + info, err := os.Stat(path) + switch { + case err != nil: + return Manifest{}, err + case !info.IsDir(): + return Manifest{}, errors.Errorf("path %s must be a directory", path) + } + + directory, err := newDirectory(path, info) + return Manifest{root: directory}, err +} + +func newDirectory(path string, info os.FileInfo) (*directory, error) { + items := make(map[string]dirEntry) + children, err := ioutil.ReadDir(path) + if err != nil { + return nil, err + } + for _, child := range children { + fullPath := filepath.Join(path, child.Name()) + items[child.Name()], err = getTypedResource(fullPath, child) + if err != nil { + return nil, err + } + } + + return &directory{ + resource: newResourceFromInfo(info), + items: items, + }, nil +} + +func getTypedResource(path string, info os.FileInfo) (dirEntry, error) { + switch { + case info.IsDir(): + return newDirectory(path, info) + case info.Mode()&os.ModeSymlink != 0: + return newSymlink(path, info) + // TODO: devices, pipes? + default: + return newFile(path, info) + } +} + +func newSymlink(path string, info os.FileInfo) (*symlink, error) { + target, err := os.Readlink(path) + return &symlink{ + resource: newResourceFromInfo(info), + target: target, + }, err +} + +func newFile(path string, info os.FileInfo) (*file, error) { + // TODO: defer file opening to reduce number of open FDs? + readCloser, err := os.Open(path) + return &file{ + resource: newResourceFromInfo(info), + content: readCloser, + }, err +} diff --git a/vendor/gotest.tools/fs/manifest_unix.go b/vendor/gotest.tools/fs/manifest_unix.go new file mode 100644 index 0000000000000..bba2fcd9df8df --- /dev/null +++ b/vendor/gotest.tools/fs/manifest_unix.go @@ -0,0 +1,30 @@ +// +build !windows + +package fs + +import ( + "os" + "syscall" +) + +const ( + defaultRootDirMode = os.ModeDir | 0700 + defaultSymlinkMode = os.ModeSymlink | 0777 +) + +func newResourceFromInfo(info os.FileInfo) resource { + statT := info.Sys().(*syscall.Stat_t) + return resource{ + mode: info.Mode(), + uid: statT.Uid, + gid: statT.Gid, + } +} + +func (p *filePath) SetMode(mode os.FileMode) { + p.file.mode = mode +} + +func (p *directoryPath) SetMode(mode os.FileMode) { + p.directory.mode = mode | os.ModeDir +} diff --git a/vendor/gotest.tools/fs/manifest_windows.go b/vendor/gotest.tools/fs/manifest_windows.go new file mode 100644 index 0000000000000..1c1a0936795dd --- /dev/null +++ b/vendor/gotest.tools/fs/manifest_windows.go @@ -0,0 +1,22 @@ +package fs + +import "os" + +const ( + defaultRootDirMode = os.ModeDir | 0777 + defaultSymlinkMode = os.ModeSymlink | 0666 +) + +func newResourceFromInfo(info os.FileInfo) resource { + return resource{mode: info.Mode()} +} + +func (p *filePath) SetMode(mode os.FileMode) { + bits := mode & 0600 + p.file.mode = bits + bits/010 + bits/0100 +} + +// TODO: is mode ignored on windows? +func (p *directoryPath) SetMode(mode os.FileMode) { + p.directory.mode = defaultRootDirMode +} diff --git a/vendor/github.com/gotestyourself/gotestyourself/fs/ops.go b/vendor/gotest.tools/fs/ops.go similarity index 61% rename from vendor/github.com/gotestyourself/gotestyourself/fs/ops.go rename to vendor/gotest.tools/fs/ops.go index bf9d2150b355e..ec9d11c1100be 100644 --- a/vendor/github.com/gotestyourself/gotestyourself/fs/ops.go +++ b/vendor/gotest.tools/fs/ops.go @@ -1,32 +1,73 @@ package fs import ( + "bytes" + "io" "io/ioutil" "os" "path/filepath" + "strings" "time" + + "github.com/pkg/errors" ) -// PathOp is a function which accepts a Path to perform some operation +const defaultFileMode = 0644 + +// PathOp is a function which accepts a Path and performs an operation on that +// path. When called with real filesystem objects (File or Dir) a PathOp modifies +// the filesystem at the path. When used with a Manifest object a PathOp updates +// the manifest to expect a value. type PathOp func(path Path) error +type manifestResource interface { + SetMode(mode os.FileMode) + SetUID(uid uint32) + SetGID(gid uint32) +} + +type manifestFile interface { + manifestResource + SetContent(content io.ReadCloser) +} + +type manifestDirectory interface { + manifestResource + AddSymlink(path, target string) error + AddFile(path string, ops ...PathOp) error + AddDirectory(path string, ops ...PathOp) error +} + // WithContent writes content to a file at Path func WithContent(content string) PathOp { return func(path Path) error { - return ioutil.WriteFile(path.Path(), []byte(content), 0644) + if m, ok := path.(manifestFile); ok { + m.SetContent(ioutil.NopCloser(strings.NewReader(content))) + return nil + } + return ioutil.WriteFile(path.Path(), []byte(content), defaultFileMode) } } // WithBytes write bytes to a file at Path func WithBytes(raw []byte) PathOp { return func(path Path) error { - return ioutil.WriteFile(path.Path(), raw, 0644) + if m, ok := path.(manifestFile); ok { + m.SetContent(ioutil.NopCloser(bytes.NewReader(raw))) + return nil + } + return ioutil.WriteFile(path.Path(), raw, defaultFileMode) } } // AsUser changes ownership of the file system object at Path func AsUser(uid, gid int) PathOp { return func(path Path) error { + if m, ok := path.(manifestResource); ok { + m.SetUID(uint32(uid)) + m.SetGID(uint32(gid)) + return nil + } return os.Chown(path.Path(), uid, gid) } } @@ -34,6 +75,11 @@ func AsUser(uid, gid int) PathOp { // WithFile creates a file in the directory at path with content func WithFile(filename, content string, ops ...PathOp) PathOp { return func(path Path) error { + if m, ok := path.(manifestDirectory); ok { + ops = append([]PathOp{WithContent(content), WithMode(defaultFileMode)}, ops...) + return m.AddFile(filename, ops...) + } + fullpath := filepath.Join(path.Path(), filepath.FromSlash(filename)) if err := createFile(fullpath, content); err != nil { return err @@ -43,12 +89,22 @@ func WithFile(filename, content string, ops ...PathOp) PathOp { } func createFile(fullpath string, content string) error { - return ioutil.WriteFile(fullpath, []byte(content), 0644) + return ioutil.WriteFile(fullpath, []byte(content), defaultFileMode) } // WithFiles creates all the files in the directory at path with their content func WithFiles(files map[string]string) PathOp { return func(path Path) error { + if m, ok := path.(manifestDirectory); ok { + for filename, content := range files { + // TODO: remove duplication with WithFile + if err := m.AddFile(filename, WithContent(content), WithMode(defaultFileMode)); err != nil { + return err + } + } + return nil + } + for filename, content := range files { fullpath := filepath.Join(path.Path(), filepath.FromSlash(filename)) if err := createFile(fullpath, content); err != nil { @@ -62,6 +118,9 @@ func WithFiles(files map[string]string) PathOp { // FromDir copies the directory tree from the source path into the new Dir func FromDir(source string) PathOp { return func(path Path) error { + if _, ok := path.(manifestDirectory); ok { + return errors.New("use manifest.FromDir") + } return copyDirectory(source, path.Path()) } } @@ -69,9 +128,15 @@ func FromDir(source string) PathOp { // WithDir creates a subdirectory in the directory at path. Additional PathOp // can be used to modify the subdirectory func WithDir(name string, ops ...PathOp) PathOp { + const defaultMode = 0755 return func(path Path) error { + if m, ok := path.(manifestDirectory); ok { + ops = append([]PathOp{WithMode(defaultMode)}, ops...) + return m.AddDirectory(name, ops...) + } + fullpath := filepath.Join(path.Path(), filepath.FromSlash(name)) - err := os.MkdirAll(fullpath, 0755) + err := os.MkdirAll(fullpath, defaultMode) if err != nil { return err } @@ -91,6 +156,10 @@ func applyPathOps(path Path, ops []PathOp) error { // WithMode sets the file mode on the directory or file at path func WithMode(mode os.FileMode) PathOp { return func(path Path) error { + if m, ok := path.(manifestResource); ok { + m.SetMode(mode) + return nil + } return os.Chmod(path.Path(), mode) } } @@ -112,6 +181,7 @@ func copyDirectory(source, dest string) error { } continue } + // TODO: handle symlinks if err := copyFile(sourcePath, destPath); err != nil { return err } @@ -134,6 +204,9 @@ func copyFile(source, dest string) error { // the other functions in this package. func WithSymlink(path, target string) PathOp { return func(root Path) error { + if v, ok := root.(manifestDirectory); ok { + return v.AddSymlink(path, target) + } return os.Symlink(filepath.Join(root.Path(), target), filepath.Join(root.Path(), path)) } } @@ -145,6 +218,9 @@ func WithSymlink(path, target string) PathOp { // the other functions in this package. func WithHardlink(path, target string) PathOp { return func(root Path) error { + if _, ok := root.(manifestDirectory); ok { + return errors.New("WithHardlink yet implemented for manifests") + } return os.Link(filepath.Join(root.Path(), target), filepath.Join(root.Path(), path)) } } @@ -153,6 +229,9 @@ func WithHardlink(path, target string) PathOp { // at path. func WithTimestamps(atime, mtime time.Time) PathOp { return func(root Path) error { + if _, ok := root.(manifestDirectory); ok { + return errors.New("WithTimestamp yet implemented for manifests") + } return os.Chtimes(root.Path(), atime, mtime) } } diff --git a/vendor/gotest.tools/fs/path.go b/vendor/gotest.tools/fs/path.go new file mode 100644 index 0000000000000..baf777f0dfcc4 --- /dev/null +++ b/vendor/gotest.tools/fs/path.go @@ -0,0 +1,151 @@ +package fs + +import ( + "bytes" + "io" + "io/ioutil" + "os" + + "gotest.tools/assert" +) + +// resourcePath is an adaptor for resources so they can be used as a Path +// with PathOps. +type resourcePath struct{} + +func (p *resourcePath) Path() string { + return "manifest: not a filesystem path" +} + +func (p *resourcePath) Remove() {} + +type filePath struct { + resourcePath + file *file +} + +func (p *filePath) SetContent(content io.ReadCloser) { + p.file.content = content +} + +func (p *filePath) SetUID(uid uint32) { + p.file.uid = uid +} + +func (p *filePath) SetGID(gid uint32) { + p.file.gid = gid +} + +type directoryPath struct { + resourcePath + directory *directory +} + +func (p *directoryPath) SetUID(uid uint32) { + p.directory.uid = uid +} + +func (p *directoryPath) SetGID(gid uint32) { + p.directory.gid = gid +} + +func (p *directoryPath) AddSymlink(path, target string) error { + p.directory.items[path] = &symlink{ + resource: newResource(defaultSymlinkMode), + target: target, + } + return nil +} + +func (p *directoryPath) AddFile(path string, ops ...PathOp) error { + newFile := &file{resource: newResource(0)} + p.directory.items[path] = newFile + exp := &filePath{file: newFile} + return applyPathOps(exp, ops) +} + +func (p *directoryPath) AddDirectory(path string, ops ...PathOp) error { + newDir := newDirectoryWithDefaults() + p.directory.items[path] = newDir + exp := &directoryPath{directory: newDir} + return applyPathOps(exp, ops) +} + +// Expected returns a Manifest with a directory structured created by ops. The +// PathOp operations are applied to the manifest as expectations of the +// filesystem structure and properties. +func Expected(t assert.TestingT, ops ...PathOp) Manifest { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + + newDir := newDirectoryWithDefaults() + e := &directoryPath{directory: newDir} + assert.NilError(t, applyPathOps(e, ops)) + return Manifest{root: newDir} +} + +func newDirectoryWithDefaults() *directory { + return &directory{ + resource: newResource(defaultRootDirMode), + items: make(map[string]dirEntry), + } +} + +func newResource(mode os.FileMode) resource { + return resource{ + mode: mode, + uid: currentUID(), + gid: currentGID(), + } +} + +func currentUID() uint32 { + return normalizeID(os.Getuid()) +} + +func currentGID() uint32 { + return normalizeID(os.Getgid()) +} + +func normalizeID(id int) uint32 { + // ids will be -1 on windows + if id < 0 { + return 0 + } + return uint32(id) +} + +var anyFileContent = ioutil.NopCloser(bytes.NewReader(nil)) + +// MatchAnyFileContent is a PathOp that updates a Manifest so that the file +// at path may contain any content. +func MatchAnyFileContent(path Path) error { + if m, ok := path.(*filePath); ok { + m.SetContent(anyFileContent) + } + return nil +} + +const anyFile = "*" + +// MatchExtraFiles is a PathOp that updates a Manifest to allow a directory +// to contain unspecified files. +func MatchExtraFiles(path Path) error { + if m, ok := path.(*directoryPath); ok { + m.AddFile(anyFile) + } + return nil +} + +// anyFileMode is represented by uint32_max +const anyFileMode os.FileMode = 4294967295 + +// MatchAnyFileMode is a PathOp that updates a Manifest so that the resource at path +// will match any file mode. +func MatchAnyFileMode(path Path) error { + if m, ok := path.(manifestResource); ok { + m.SetMode(anyFileMode) + } + return nil +} diff --git a/vendor/gotest.tools/fs/report.go b/vendor/gotest.tools/fs/report.go new file mode 100644 index 0000000000000..4de85a6ef7318 --- /dev/null +++ b/vendor/gotest.tools/fs/report.go @@ -0,0 +1,215 @@ +package fs + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" + + "gotest.tools/assert/cmp" + "gotest.tools/internal/format" +) + +// Equal compares a directory to the expected structured described by a manifest +// and returns success if they match. If they do not match the failure message +// will contain all the differences between the directory structure and the +// expected structure defined by the Manifest. +// +// Equal is a cmp.Comparison which can be used with assert.Assert(). +func Equal(path string, expected Manifest) cmp.Comparison { + return func() cmp.Result { + actual, err := manifestFromDir(path) + if err != nil { + return cmp.ResultFromError(err) + } + failures := eqDirectory(string(os.PathSeparator), expected.root, actual.root) + if len(failures) == 0 { + return cmp.ResultSuccess + } + msg := fmt.Sprintf("directory %s does not match expected:\n", path) + return cmp.ResultFailure(msg + formatFailures(failures)) + } +} + +type failure struct { + path string + problems []problem +} + +type problem string + +func notEqual(property string, x, y interface{}) problem { + return problem(fmt.Sprintf("%s: expected %s got %s", property, x, y)) +} + +func errProblem(reason string, err error) problem { + return problem(fmt.Sprintf("%s: %s", reason, err)) +} + +func existenceProblem(filename, reason string, args ...interface{}) problem { + return problem(filename + ": " + fmt.Sprintf(reason, args...)) +} + +func eqResource(x, y resource) []problem { + var p []problem + if x.uid != y.uid { + p = append(p, notEqual("uid", x.uid, y.uid)) + } + if x.gid != y.gid { + p = append(p, notEqual("gid", x.gid, y.gid)) + } + if x.mode != anyFileMode && x.mode != y.mode { + p = append(p, notEqual("mode", x.mode, y.mode)) + } + return p +} + +func eqFile(x, y *file) []problem { + p := eqResource(x.resource, y.resource) + + switch { + case x.content == nil: + p = append(p, existenceProblem("content", "expected content is nil")) + return p + case x.content == anyFileContent: + return p + case y.content == nil: + p = append(p, existenceProblem("content", "actual content is nil")) + return p + } + + xContent, xErr := ioutil.ReadAll(x.content) + defer x.content.Close() + yContent, yErr := ioutil.ReadAll(y.content) + defer y.content.Close() + + if xErr != nil { + p = append(p, errProblem("failed to read expected content", xErr)) + } + if yErr != nil { + p = append(p, errProblem("failed to read actual content", xErr)) + } + if xErr != nil || yErr != nil { + return p + } + + if !bytes.Equal(xContent, yContent) { + p = append(p, diffContent(xContent, yContent)) + } + return p +} + +func diffContent(x, y []byte) problem { + diff := format.UnifiedDiff(format.DiffConfig{ + A: string(x), + B: string(y), + From: "expected", + To: "actual", + }) + // Remove the trailing newline in the diff. A trailing newline is always + // added to a problem by formatFailures. + diff = strings.TrimSuffix(diff, "\n") + return problem("content:\n" + indent(diff, " ")) +} + +func indent(s, prefix string) string { + buf := new(bytes.Buffer) + lines := strings.SplitAfter(s, "\n") + for _, line := range lines { + buf.WriteString(prefix + line) + } + return buf.String() +} + +func eqSymlink(x, y *symlink) []problem { + p := eqResource(x.resource, y.resource) + if x.target != y.target { + p = append(p, notEqual("target", x.target, y.target)) + } + return p +} + +func eqDirectory(path string, x, y *directory) []failure { + p := eqResource(x.resource, y.resource) + var f []failure + + for _, name := range sortedKeys(x.items) { + if name == anyFile { + continue + } + xEntry := x.items[name] + yEntry, ok := y.items[name] + if !ok { + p = append(p, existenceProblem(name, "expected %s to exist", xEntry.Type())) + continue + } + + if xEntry.Type() != yEntry.Type() { + p = append(p, notEqual(name, xEntry.Type(), yEntry.Type())) + continue + } + + f = append(f, eqEntry(filepath.Join(path, name), xEntry, yEntry)...) + } + + if _, ok := x.items[anyFile]; !ok { + for _, name := range sortedKeys(y.items) { + if _, ok := x.items[name]; !ok { + yEntry := y.items[name] + p = append(p, existenceProblem(name, "unexpected %s", yEntry.Type())) + } + } + } + + if len(p) > 0 { + f = append(f, failure{path: path, problems: p}) + } + return f +} + +func sortedKeys(items map[string]dirEntry) []string { + var keys []string + for key := range items { + keys = append(keys, key) + } + sort.Strings(keys) + return keys +} + +// eqEntry assumes x and y to be the same type +func eqEntry(path string, x, y dirEntry) []failure { + resp := func(problems []problem) []failure { + if len(problems) == 0 { + return nil + } + return []failure{{path: path, problems: problems}} + } + + switch typed := x.(type) { + case *file: + return resp(eqFile(typed, y.(*file))) + case *symlink: + return resp(eqSymlink(typed, y.(*symlink))) + case *directory: + return eqDirectory(path, typed, y.(*directory)) + } + return nil +} + +func formatFailures(failures []failure) string { + sort.Slice(failures, func(i, j int) bool { + return failures[i].path < failures[j].path + }) + + buf := new(bytes.Buffer) + for _, failure := range failures { + buf.WriteString(failure.path + "\n") + for _, problem := range failure.problems { + buf.WriteString(" " + string(problem) + "\n") + } + } + return buf.String() +} diff --git a/vendor/github.com/gotestyourself/gotestyourself/icmd/command.go b/vendor/gotest.tools/icmd/command.go similarity index 98% rename from vendor/github.com/gotestyourself/gotestyourself/icmd/command.go rename to vendor/gotest.tools/icmd/command.go index 0066c51514ce9..901895991a956 100644 --- a/vendor/github.com/gotestyourself/gotestyourself/icmd/command.go +++ b/vendor/gotest.tools/icmd/command.go @@ -1,6 +1,6 @@ /*Package icmd executes binaries and provides convenient assertions for testing the results. */ -package icmd +package icmd // import "gotest.tools/icmd" import ( "bytes" @@ -11,8 +11,8 @@ import ( "sync" "time" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + "gotest.tools/assert/cmp" ) type helperT interface { @@ -228,6 +228,7 @@ func StartCmd(cmd Cmd) *Result { return result } +// TODO: support exec.CommandContext func buildCmd(cmd Cmd) *Result { var execCmd *exec.Cmd switch len(cmd.Command) { diff --git a/vendor/github.com/gotestyourself/gotestyourself/icmd/exitcode.go b/vendor/gotest.tools/icmd/exitcode.go similarity index 100% rename from vendor/github.com/gotestyourself/gotestyourself/icmd/exitcode.go rename to vendor/gotest.tools/icmd/exitcode.go diff --git a/vendor/github.com/gotestyourself/gotestyourself/icmd/ops.go b/vendor/gotest.tools/icmd/ops.go similarity index 100% rename from vendor/github.com/gotestyourself/gotestyourself/icmd/ops.go rename to vendor/gotest.tools/icmd/ops.go diff --git a/vendor/github.com/pmezard/go-difflib/LICENSE b/vendor/gotest.tools/internal/difflib/LICENSE similarity index 100% rename from vendor/github.com/pmezard/go-difflib/LICENSE rename to vendor/gotest.tools/internal/difflib/LICENSE diff --git a/vendor/github.com/pmezard/go-difflib/difflib/difflib.go b/vendor/gotest.tools/internal/difflib/difflib.go similarity index 56% rename from vendor/github.com/pmezard/go-difflib/difflib/difflib.go rename to vendor/gotest.tools/internal/difflib/difflib.go index 003e99fadb4f1..5efa99c1d4615 100644 --- a/vendor/github.com/pmezard/go-difflib/difflib/difflib.go +++ b/vendor/gotest.tools/internal/difflib/difflib.go @@ -1,27 +1,10 @@ -// Package difflib is a partial port of Python difflib module. -// -// It provides tools to compare sequences of strings and generate textual diffs. -// -// The following class and functions have been ported: -// -// - SequenceMatcher -// -// - unified_diff -// -// - context_diff -// -// Getting unified diffs was the main goal of the port. Keep in mind this code -// is mostly suitable to output text differences in a human friendly way, there -// are no guarantees generated diffs are consumable by patch(1). -package difflib +/* Package difflib is a partial port of Python difflib module. + +Original source: https://github.com/pmezard/go-difflib -import ( - "bufio" - "bytes" - "fmt" - "io" - "strings" -) +This file is trimmed to only the parts used by this repository. +*/ +package difflib // import "gotest.tools/internal/difflib" func min(a, b int) int { if a < b { @@ -37,13 +20,6 @@ func max(a, b int) int { return b } -func calculateRatio(matches, length int) float64 { - if length > 0 { - return 2.0 * float64(matches) / float64(length) - } - return 1.0 -} - type Match struct { A int B int @@ -103,14 +79,6 @@ func NewMatcher(a, b []string) *SequenceMatcher { return &m } -func NewMatcherWithJunk(a, b []string, autoJunk bool, - isJunk func(string) bool) *SequenceMatcher { - - m := SequenceMatcher{IsJunk: isJunk, autoJunk: autoJunk} - m.SetSeqs(a, b) - return &m -} - // Set two sequences to be compared. func (m *SequenceMatcher) SetSeqs(a, b []string) { m.SetSeq1(a) @@ -450,323 +418,3 @@ func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode { } return groups } - -// Return a measure of the sequences' similarity (float in [0,1]). -// -// Where T is the total number of elements in both sequences, and -// M is the number of matches, this is 2.0*M / T. -// Note that this is 1 if the sequences are identical, and 0 if -// they have nothing in common. -// -// .Ratio() is expensive to compute if you haven't already computed -// .GetMatchingBlocks() or .GetOpCodes(), in which case you may -// want to try .QuickRatio() or .RealQuickRation() first to get an -// upper bound. -func (m *SequenceMatcher) Ratio() float64 { - matches := 0 - for _, m := range m.GetMatchingBlocks() { - matches += m.Size - } - return calculateRatio(matches, len(m.a)+len(m.b)) -} - -// Return an upper bound on ratio() relatively quickly. -// -// This isn't defined beyond that it is an upper bound on .Ratio(), and -// is faster to compute. -func (m *SequenceMatcher) QuickRatio() float64 { - // viewing a and b as multisets, set matches to the cardinality - // of their intersection; this counts the number of matches - // without regard to order, so is clearly an upper bound - if m.fullBCount == nil { - m.fullBCount = map[string]int{} - for _, s := range m.b { - m.fullBCount[s] = m.fullBCount[s] + 1 - } - } - - // avail[x] is the number of times x appears in 'b' less the - // number of times we've seen it in 'a' so far ... kinda - avail := map[string]int{} - matches := 0 - for _, s := range m.a { - n, ok := avail[s] - if !ok { - n = m.fullBCount[s] - } - avail[s] = n - 1 - if n > 0 { - matches += 1 - } - } - return calculateRatio(matches, len(m.a)+len(m.b)) -} - -// Return an upper bound on ratio() very quickly. -// -// This isn't defined beyond that it is an upper bound on .Ratio(), and -// is faster to compute than either .Ratio() or .QuickRatio(). -func (m *SequenceMatcher) RealQuickRatio() float64 { - la, lb := len(m.a), len(m.b) - return calculateRatio(min(la, lb), la+lb) -} - -// Convert range to the "ed" format -func formatRangeUnified(start, stop int) string { - // Per the diff spec at http://www.unix.org/single_unix_specification/ - beginning := start + 1 // lines start numbering with one - length := stop - start - if length == 1 { - return fmt.Sprintf("%d", beginning) - } - if length == 0 { - beginning -= 1 // empty ranges begin at line just before the range - } - return fmt.Sprintf("%d,%d", beginning, length) -} - -// Unified diff parameters -type UnifiedDiff struct { - A []string // First sequence lines - FromFile string // First file name - FromDate string // First file time - B []string // Second sequence lines - ToFile string // Second file name - ToDate string // Second file time - Eol string // Headers end of line, defaults to LF - Context int // Number of context lines -} - -// Compare two sequences of lines; generate the delta as a unified diff. -// -// Unified diffs are a compact way of showing line changes and a few -// lines of context. The number of context lines is set by 'n' which -// defaults to three. -// -// By default, the diff control lines (those with ---, +++, or @@) are -// created with a trailing newline. This is helpful so that inputs -// created from file.readlines() result in diffs that are suitable for -// file.writelines() since both the inputs and outputs have trailing -// newlines. -// -// For inputs that do not have trailing newlines, set the lineterm -// argument to "" so that the output will be uniformly newline free. -// -// The unidiff format normally has a header for filenames and modification -// times. Any or all of these may be specified using strings for -// 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'. -// The modification times are normally expressed in the ISO 8601 format. -func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error { - buf := bufio.NewWriter(writer) - defer buf.Flush() - wf := func(format string, args ...interface{}) error { - _, err := buf.WriteString(fmt.Sprintf(format, args...)) - return err - } - ws := func(s string) error { - _, err := buf.WriteString(s) - return err - } - - if len(diff.Eol) == 0 { - diff.Eol = "\n" - } - - started := false - m := NewMatcher(diff.A, diff.B) - for _, g := range m.GetGroupedOpCodes(diff.Context) { - if !started { - started = true - fromDate := "" - if len(diff.FromDate) > 0 { - fromDate = "\t" + diff.FromDate - } - toDate := "" - if len(diff.ToDate) > 0 { - toDate = "\t" + diff.ToDate - } - if diff.FromFile != "" || diff.ToFile != "" { - err := wf("--- %s%s%s", diff.FromFile, fromDate, diff.Eol) - if err != nil { - return err - } - err = wf("+++ %s%s%s", diff.ToFile, toDate, diff.Eol) - if err != nil { - return err - } - } - } - first, last := g[0], g[len(g)-1] - range1 := formatRangeUnified(first.I1, last.I2) - range2 := formatRangeUnified(first.J1, last.J2) - if err := wf("@@ -%s +%s @@%s", range1, range2, diff.Eol); err != nil { - return err - } - for _, c := range g { - i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 - if c.Tag == 'e' { - for _, line := range diff.A[i1:i2] { - if err := ws(" " + line); err != nil { - return err - } - } - continue - } - if c.Tag == 'r' || c.Tag == 'd' { - for _, line := range diff.A[i1:i2] { - if err := ws("-" + line); err != nil { - return err - } - } - } - if c.Tag == 'r' || c.Tag == 'i' { - for _, line := range diff.B[j1:j2] { - if err := ws("+" + line); err != nil { - return err - } - } - } - } - } - return nil -} - -// Like WriteUnifiedDiff but returns the diff a string. -func GetUnifiedDiffString(diff UnifiedDiff) (string, error) { - w := &bytes.Buffer{} - err := WriteUnifiedDiff(w, diff) - return string(w.Bytes()), err -} - -// Convert range to the "ed" format. -func formatRangeContext(start, stop int) string { - // Per the diff spec at http://www.unix.org/single_unix_specification/ - beginning := start + 1 // lines start numbering with one - length := stop - start - if length == 0 { - beginning -= 1 // empty ranges begin at line just before the range - } - if length <= 1 { - return fmt.Sprintf("%d", beginning) - } - return fmt.Sprintf("%d,%d", beginning, beginning+length-1) -} - -type ContextDiff UnifiedDiff - -// Compare two sequences of lines; generate the delta as a context diff. -// -// Context diffs are a compact way of showing line changes and a few -// lines of context. The number of context lines is set by diff.Context -// which defaults to three. -// -// By default, the diff control lines (those with *** or ---) are -// created with a trailing newline. -// -// For inputs that do not have trailing newlines, set the diff.Eol -// argument to "" so that the output will be uniformly newline free. -// -// The context diff format normally has a header for filenames and -// modification times. Any or all of these may be specified using -// strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate. -// The modification times are normally expressed in the ISO 8601 format. -// If not specified, the strings default to blanks. -func WriteContextDiff(writer io.Writer, diff ContextDiff) error { - buf := bufio.NewWriter(writer) - defer buf.Flush() - var diffErr error - wf := func(format string, args ...interface{}) { - _, err := buf.WriteString(fmt.Sprintf(format, args...)) - if diffErr == nil && err != nil { - diffErr = err - } - } - ws := func(s string) { - _, err := buf.WriteString(s) - if diffErr == nil && err != nil { - diffErr = err - } - } - - if len(diff.Eol) == 0 { - diff.Eol = "\n" - } - - prefix := map[byte]string{ - 'i': "+ ", - 'd': "- ", - 'r': "! ", - 'e': " ", - } - - started := false - m := NewMatcher(diff.A, diff.B) - for _, g := range m.GetGroupedOpCodes(diff.Context) { - if !started { - started = true - fromDate := "" - if len(diff.FromDate) > 0 { - fromDate = "\t" + diff.FromDate - } - toDate := "" - if len(diff.ToDate) > 0 { - toDate = "\t" + diff.ToDate - } - if diff.FromFile != "" || diff.ToFile != "" { - wf("*** %s%s%s", diff.FromFile, fromDate, diff.Eol) - wf("--- %s%s%s", diff.ToFile, toDate, diff.Eol) - } - } - - first, last := g[0], g[len(g)-1] - ws("***************" + diff.Eol) - - range1 := formatRangeContext(first.I1, last.I2) - wf("*** %s ****%s", range1, diff.Eol) - for _, c := range g { - if c.Tag == 'r' || c.Tag == 'd' { - for _, cc := range g { - if cc.Tag == 'i' { - continue - } - for _, line := range diff.A[cc.I1:cc.I2] { - ws(prefix[cc.Tag] + line) - } - } - break - } - } - - range2 := formatRangeContext(first.J1, last.J2) - wf("--- %s ----%s", range2, diff.Eol) - for _, c := range g { - if c.Tag == 'r' || c.Tag == 'i' { - for _, cc := range g { - if cc.Tag == 'd' { - continue - } - for _, line := range diff.B[cc.J1:cc.J2] { - ws(prefix[cc.Tag] + line) - } - } - break - } - } - } - return diffErr -} - -// Like WriteContextDiff but returns the diff a string. -func GetContextDiffString(diff ContextDiff) (string, error) { - w := &bytes.Buffer{} - err := WriteContextDiff(w, diff) - return string(w.Bytes()), err -} - -// Split a string on "\n" while preserving them. The output can be used -// as input for UnifiedDiff and ContextDiff structures. -func SplitLines(s string) []string { - lines := strings.SplitAfter(s, "\n") - lines[len(lines)-1] += "\n" - return lines -} diff --git a/vendor/gotest.tools/internal/format/diff.go b/vendor/gotest.tools/internal/format/diff.go new file mode 100644 index 0000000000000..c938c97bec147 --- /dev/null +++ b/vendor/gotest.tools/internal/format/diff.go @@ -0,0 +1,161 @@ +package format + +import ( + "bytes" + "fmt" + "strings" + "unicode" + + "gotest.tools/internal/difflib" +) + +const ( + contextLines = 2 +) + +// DiffConfig for a unified diff +type DiffConfig struct { + A string + B string + From string + To string +} + +// UnifiedDiff is a modified version of difflib.WriteUnifiedDiff with better +// support for showing the whitespace differences. +func UnifiedDiff(conf DiffConfig) string { + a := strings.SplitAfter(conf.A, "\n") + b := strings.SplitAfter(conf.B, "\n") + groups := difflib.NewMatcher(a, b).GetGroupedOpCodes(contextLines) + if len(groups) == 0 { + return "" + } + + buf := new(bytes.Buffer) + writeFormat := func(format string, args ...interface{}) { + buf.WriteString(fmt.Sprintf(format, args...)) + } + writeLine := func(prefix string, s string) { + buf.WriteString(prefix + s) + } + if hasWhitespaceDiffLines(groups, a, b) { + writeLine = visibleWhitespaceLine(writeLine) + } + formatHeader(writeFormat, conf) + for _, group := range groups { + formatRangeLine(writeFormat, group) + for _, opCode := range group { + in, out := a[opCode.I1:opCode.I2], b[opCode.J1:opCode.J2] + switch opCode.Tag { + case 'e': + formatLines(writeLine, " ", in) + case 'r': + formatLines(writeLine, "-", in) + formatLines(writeLine, "+", out) + case 'd': + formatLines(writeLine, "-", in) + case 'i': + formatLines(writeLine, "+", out) + } + } + } + return buf.String() +} + +// hasWhitespaceDiffLines returns true if any diff groups is only different +// because of whitespace characters. +func hasWhitespaceDiffLines(groups [][]difflib.OpCode, a, b []string) bool { + for _, group := range groups { + in, out := new(bytes.Buffer), new(bytes.Buffer) + for _, opCode := range group { + if opCode.Tag == 'e' { + continue + } + for _, line := range a[opCode.I1:opCode.I2] { + in.WriteString(line) + } + for _, line := range b[opCode.J1:opCode.J2] { + out.WriteString(line) + } + } + if removeWhitespace(in.String()) == removeWhitespace(out.String()) { + return true + } + } + return false +} + +func removeWhitespace(s string) string { + var result []rune + for _, r := range s { + if !unicode.IsSpace(r) { + result = append(result, r) + } + } + return string(result) +} + +func visibleWhitespaceLine(ws func(string, string)) func(string, string) { + mapToVisibleSpace := func(r rune) rune { + switch r { + case '\n': + case ' ': + return '·' + case '\t': + return '▷' + case '\v': + return '▽' + case '\r': + return '↵' + case '\f': + return '↓' + default: + if unicode.IsSpace(r) { + return '�' + } + } + return r + } + return func(prefix, s string) { + ws(prefix, strings.Map(mapToVisibleSpace, s)) + } +} + +func formatHeader(wf func(string, ...interface{}), conf DiffConfig) { + if conf.From != "" || conf.To != "" { + wf("--- %s\n", conf.From) + wf("+++ %s\n", conf.To) + } +} + +func formatRangeLine(wf func(string, ...interface{}), group []difflib.OpCode) { + first, last := group[0], group[len(group)-1] + range1 := formatRangeUnified(first.I1, last.I2) + range2 := formatRangeUnified(first.J1, last.J2) + wf("@@ -%s +%s @@\n", range1, range2) +} + +// Convert range to the "ed" format +func formatRangeUnified(start, stop int) string { + // Per the diff spec at http://www.unix.org/single_unix_specification/ + beginning := start + 1 // lines start numbering with one + length := stop - start + if length == 1 { + return fmt.Sprintf("%d", beginning) + } + if length == 0 { + beginning-- // empty ranges begin at line just before the range + } + return fmt.Sprintf("%d,%d", beginning, length) +} + +func formatLines(writeLine func(string, string), prefix string, lines []string) { + for _, line := range lines { + writeLine(prefix, line) + } + // Add a newline if the last line is missing one so that the diff displays + // properly. + if !strings.HasSuffix(lines[len(lines)-1], "\n") { + writeLine("", "\n") + } +} diff --git a/vendor/github.com/gotestyourself/gotestyourself/internal/format/format.go b/vendor/gotest.tools/internal/format/format.go similarity index 91% rename from vendor/github.com/gotestyourself/gotestyourself/internal/format/format.go rename to vendor/gotest.tools/internal/format/format.go index 6961ffe6da6a8..8f6494f99421d 100644 --- a/vendor/github.com/gotestyourself/gotestyourself/internal/format/format.go +++ b/vendor/gotest.tools/internal/format/format.go @@ -1,4 +1,4 @@ -package format +package format // import "gotest.tools/internal/format" import "fmt" diff --git a/vendor/github.com/gotestyourself/gotestyourself/internal/source/source.go b/vendor/gotest.tools/internal/source/source.go similarity index 98% rename from vendor/github.com/gotestyourself/gotestyourself/internal/source/source.go rename to vendor/gotest.tools/internal/source/source.go index f71c5129b8762..a05933cc3380e 100644 --- a/vendor/github.com/gotestyourself/gotestyourself/internal/source/source.go +++ b/vendor/gotest.tools/internal/source/source.go @@ -1,4 +1,4 @@ -package source +package source // import "gotest.tools/internal/source" import ( "bytes" diff --git a/vendor/github.com/gotestyourself/gotestyourself/poll/poll.go b/vendor/gotest.tools/poll/poll.go similarity index 95% rename from vendor/github.com/gotestyourself/gotestyourself/poll/poll.go rename to vendor/gotest.tools/poll/poll.go index 42a42cc14943a..3e3dd7f58a37d 100644 --- a/vendor/github.com/gotestyourself/gotestyourself/poll/poll.go +++ b/vendor/gotest.tools/poll/poll.go @@ -1,6 +1,6 @@ /*Package poll provides tools for testing asynchronous code. */ -package poll +package poll // import "gotest.tools/poll" import ( "fmt" @@ -25,15 +25,15 @@ type helperT interface { // Settings are used to configure the behaviour of WaitOn type Settings struct { - // Timeout is the maximum time to wait for the condition. Defaults to 10s + // Timeout is the maximum time to wait for the condition. Defaults to 10s. Timeout time.Duration - // Delay is the time to sleep between checking the condition. Detaults to - // 1ms + // Delay is the time to sleep between checking the condition. Defaults to + // 100ms. Delay time.Duration } func defaultConfig() *Settings { - return &Settings{Timeout: 10 * time.Second, Delay: time.Millisecond} + return &Settings{Timeout: 10 * time.Second, Delay: 100 * time.Millisecond} } // SettingOp is a function which accepts and modifies Settings diff --git a/vendor/github.com/gotestyourself/gotestyourself/skip/skip.go b/vendor/gotest.tools/skip/skip.go similarity index 60% rename from vendor/github.com/gotestyourself/gotestyourself/skip/skip.go rename to vendor/gotest.tools/skip/skip.go index 46ab288819bff..4bfc9a11c2da1 100644 --- a/vendor/github.com/gotestyourself/gotestyourself/skip/skip.go +++ b/vendor/gotest.tools/skip/skip.go @@ -1,6 +1,7 @@ -/*Package skip provides functions for skipping based on a condition. - */ -package skip +/*Package skip provides functions for skipping a test and printing the source code +of the condition used to skip the test. +*/ +package skip // import "gotest.tools/skip" import ( "fmt" @@ -9,8 +10,8 @@ import ( "runtime" "strings" - "github.com/gotestyourself/gotestyourself/internal/format" - "github.com/gotestyourself/gotestyourself/internal/source" + "gotest.tools/internal/format" + "gotest.tools/internal/source" ) type skipT interface { @@ -25,9 +26,10 @@ type helperT interface { // BoolOrCheckFunc can be a bool or func() bool, other types will panic type BoolOrCheckFunc interface{} -// If skips the test if the check function returns true. The skip message will -// contain the name of the check function. Extra message text can be passed as a -// format string with args +// If the condition expression evaluates to true, or the condition function returns +// true, skip the test. +// The skip message will contain the source code of the expression. +// Extra message text can be passed as a format string with args func If(t skipT, condition BoolOrCheckFunc, msgAndArgs ...interface{}) { if ht, ok := t.(helperT); ok { ht.Helper() @@ -49,18 +51,6 @@ func getFunctionName(function func() bool) string { return strings.SplitN(path.Base(funcPath), ".", 2)[1] } -// IfCondition skips the test if the condition is true. The skip message will -// contain the source of the expression passed as the condition. Extra message -// text can be passed as a format string with args. -// -// Deprecated: Use If() which now accepts bool arguments -func IfCondition(t skipT, condition bool, msgAndArgs ...interface{}) { - if ht, ok := t.(helperT); ok { - ht.Helper() - } - ifCondition(t, condition, msgAndArgs...) -} - func ifCondition(t skipT, condition bool, msgAndArgs ...interface{}) { if ht, ok := t.(helperT); ok { ht.Helper() diff --git a/vendor/gotest.tools/x/subtest/context.go b/vendor/gotest.tools/x/subtest/context.go new file mode 100644 index 0000000000000..878bdebf14d89 --- /dev/null +++ b/vendor/gotest.tools/x/subtest/context.go @@ -0,0 +1,81 @@ +/*Package subtest provides a TestContext to subtests which handles cleanup, and +provides a testing.TB, and context.Context. + +This package was inspired by github.com/frankban/quicktest. +*/ +package subtest // import "gotest.tools/x/subtest" + +import ( + "context" + "testing" +) + +type testcase struct { + testing.TB + ctx context.Context + cleanupFuncs []cleanupFunc +} + +type cleanupFunc func() + +func (tc *testcase) Ctx() context.Context { + if tc.ctx == nil { + var cancel func() + tc.ctx, cancel = context.WithCancel(context.Background()) + tc.AddCleanup(cancel) + } + return tc.ctx +} + +// Cleanup runs all cleanup functions. Functions are run in the opposite order +// in which they were added. Cleanup is called automatically before Run exits. +func (tc *testcase) Cleanup() { + for _, f := range tc.cleanupFuncs { + // Defer all cleanup functions so they all run even if one calls + // t.FailNow() or panics. Deferring them also runs them in reverse order. + defer f() + } + tc.cleanupFuncs = nil +} + +func (tc *testcase) AddCleanup(f func()) { + tc.cleanupFuncs = append(tc.cleanupFuncs, f) +} + +func (tc *testcase) Parallel() { + tp, ok := tc.TB.(parallel) + if !ok { + panic("Parallel called with a testing.B") + } + tp.Parallel() +} + +type parallel interface { + Parallel() +} + +// Run a subtest. When subtest exits, every cleanup function added with +// TestContext.AddCleanup will be run. +func Run(t *testing.T, name string, subtest func(t TestContext)) bool { + return t.Run(name, func(t *testing.T) { + tc := &testcase{TB: t} + defer tc.Cleanup() + subtest(tc) + }) +} + +// TestContext provides a testing.TB and a context.Context for a test case. +type TestContext interface { + testing.TB + // AddCleanup function which will be run when before Run returns. + AddCleanup(f func()) + // Ctx returns a context for the test case. Multiple calls from the same subtest + // will return the same context. The context is cancelled when Run + // returns. + Ctx() context.Context + // Parallel calls t.Parallel on the testing.TB. Panics if testing.TB does + // not implement Parallel. + Parallel() +} + +var _ TestContext = &testcase{} diff --git a/volume/local/local_test.go b/volume/local/local_test.go index 541b8448aa0ab..4cb47ba045229 100644 --- a/volume/local/local_test.go +++ b/volume/local/local_test.go @@ -11,7 +11,7 @@ import ( "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/mount" - "github.com/gotestyourself/gotestyourself/skip" + "gotest.tools/skip" ) func TestGetAddress(t *testing.T) { diff --git a/volume/mounts/mounts.go b/volume/mounts/mounts.go index 8f255a5482944..177f4c3d97ff5 100644 --- a/volume/mounts/mounts.go +++ b/volume/mounts/mounts.go @@ -62,6 +62,12 @@ type MountPoint struct { // Sepc is a copy of the API request that created this mount. Spec mounttypes.Mount + // Some bind mounts should not be automatically created. + // (Some are auto-created for backwards-compatability) + // This is checked on the API but setting this here prevents race conditions. + // where a bind dir existed during validation was removed before reaching the setup code. + SkipMountpointCreation bool + // Track usage of this mountpoint // Specifically needed for containers which are running and calls to `docker cp` // because both these actions require mounting the volumes. @@ -90,6 +96,10 @@ func (m *MountPoint) Cleanup() error { // The, optional, checkFun parameter allows doing additional checking // before creating the source directory on the host. func (m *MountPoint) Setup(mountLabel string, rootIDs idtools.IDPair, checkFun func(m *MountPoint) error) (path string, err error) { + if m.SkipMountpointCreation { + return m.Source, nil + } + defer func() { if err != nil || !label.RelabelNeeded(m.Mode) { return @@ -140,6 +150,7 @@ func (m *MountPoint) Setup(mountLabel string, rootIDs idtools.IDPair, checkFun f return "", err } } + // idtools.MkdirAllNewAs() produces an error if m.Source exists and is a file (not a directory) // also, makes sure that if the directory is created, the correct remapped rootUID/rootGID will own it if err := idtools.MkdirAllAndChownNew(m.Source, 0755, rootIDs); err != nil { diff --git a/volume/service/db_test.go b/volume/service/db_test.go index 14ad87a514c3f..4cac9176b75d0 100644 --- a/volume/service/db_test.go +++ b/volume/service/db_test.go @@ -8,8 +8,8 @@ import ( "time" "github.com/boltdb/bolt" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestSetGetMeta(t *testing.T) { diff --git a/volume/service/restore_test.go b/volume/service/restore_test.go index d3c6c9f92ced1..95420d9586a55 100644 --- a/volume/service/restore_test.go +++ b/volume/service/restore_test.go @@ -10,7 +10,7 @@ import ( volumedrivers "github.com/docker/docker/volume/drivers" "github.com/docker/docker/volume/service/opts" volumetestutils "github.com/docker/docker/volume/testutils" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" ) func TestRestore(t *testing.T) { diff --git a/volume/service/service_linux_test.go b/volume/service/service_linux_test.go index 6c1f936b6abf8..ae70d7e2c5f99 100644 --- a/volume/service/service_linux_test.go +++ b/volume/service/service_linux_test.go @@ -13,8 +13,8 @@ import ( "github.com/docker/docker/volume/local" "github.com/docker/docker/volume/service/opts" "github.com/docker/docker/volume/testutils" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestLocalVolumeSize(t *testing.T) { diff --git a/volume/service/service_test.go b/volume/service/service_test.go index 3c4130eab5677..870d19f8a0abe 100644 --- a/volume/service/service_test.go +++ b/volume/service/service_test.go @@ -12,8 +12,8 @@ import ( volumedrivers "github.com/docker/docker/volume/drivers" "github.com/docker/docker/volume/service/opts" "github.com/docker/docker/volume/testutils" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestServiceCreate(t *testing.T) { diff --git a/volume/service/store_test.go b/volume/service/store_test.go index b6b0829952223..53345f318bb51 100644 --- a/volume/service/store_test.go +++ b/volume/service/store_test.go @@ -15,8 +15,8 @@ import ( "github.com/docker/docker/volume/service/opts" volumetestutils "github.com/docker/docker/volume/testutils" "github.com/google/go-cmp/cmp" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestCreate(t *testing.T) { diff --git a/volume/testutils/testutils.go b/volume/testutils/testutils.go index 5bb38e3f3311a..0a20a355160c2 100644 --- a/volume/testutils/testutils.go +++ b/volume/testutils/testutils.go @@ -41,11 +41,12 @@ func (NoopVolume) CreatedAt() (time.Time, error) { return time.Now(), nil } type FakeVolume struct { name string driverName string + createdAt time.Time } // NewFakeVolume creates a new fake volume for testing func NewFakeVolume(name string, driverName string) volume.Volume { - return FakeVolume{name: name, driverName: driverName} + return FakeVolume{name: name, driverName: driverName, createdAt: time.Now()} } // Name is the name of the volume @@ -69,7 +70,9 @@ func (FakeVolume) Status() map[string]interface{} { } // CreatedAt provides the time the volume (directory) was created at -func (FakeVolume) CreatedAt() (time.Time, error) { return time.Now(), nil } +func (f FakeVolume) CreatedAt() (time.Time, error) { + return f.createdAt, nil +} // FakeDriver is a driver that generates fake volumes type FakeDriver struct {