Update cri to v1.11.1.
Signed-off-by: Lantao Liu <lantaol@google.com>
This commit is contained in:
parent
65839a47a8
commit
e9ad2d0481
@ -43,7 +43,7 @@ gotest.tools v2.1.0
|
|||||||
github.com/google/go-cmp v0.1.0
|
github.com/google/go-cmp v0.1.0
|
||||||
|
|
||||||
# cri dependencies
|
# cri dependencies
|
||||||
github.com/containerd/cri 661f3b0377db409fe0e5677115f02ce7b89fd17d https://github.com/dmcgowan/cri-containerd
|
github.com/containerd/cri v1.11.1
|
||||||
github.com/containerd/go-cni 5882530828ecf62032409b298a3e8b19e08b6534
|
github.com/containerd/go-cni 5882530828ecf62032409b298a3e8b19e08b6534
|
||||||
github.com/blang/semver v3.1.0
|
github.com/blang/semver v3.1.0
|
||||||
github.com/containernetworking/cni v0.6.0
|
github.com/containernetworking/cni v0.6.0
|
||||||
|
31
vendor/github.com/containerd/cri/pkg/config/config.go
generated
vendored
31
vendor/github.com/containerd/cri/pkg/config/config.go
generated
vendored
@ -37,6 +37,8 @@ type ContainerdConfig struct {
|
|||||||
DefaultRuntime Runtime `toml:"default_runtime" json:"defaultRuntime"`
|
DefaultRuntime Runtime `toml:"default_runtime" json:"defaultRuntime"`
|
||||||
// UntrustedWorkloadRuntime is a runtime to run untrusted workloads on it.
|
// UntrustedWorkloadRuntime is a runtime to run untrusted workloads on it.
|
||||||
UntrustedWorkloadRuntime Runtime `toml:"untrusted_workload_runtime" json:"untrustedWorkloadRuntime"`
|
UntrustedWorkloadRuntime Runtime `toml:"untrusted_workload_runtime" json:"untrustedWorkloadRuntime"`
|
||||||
|
// NoPivot disables pivot-root (linux only), required when running a container in a RamDisk with runc
|
||||||
|
NoPivot bool `toml:"no_pivot" json:"noPivot"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CniConfig contains toml config related to cni
|
// CniConfig contains toml config related to cni
|
||||||
@ -61,16 +63,32 @@ type CniConfig struct {
|
|||||||
// Mirror contains the config related to the registry mirror
|
// Mirror contains the config related to the registry mirror
|
||||||
type Mirror struct {
|
type Mirror struct {
|
||||||
// Endpoints are endpoints for a namespace. CRI plugin will try the endpoints
|
// Endpoints are endpoints for a namespace. CRI plugin will try the endpoints
|
||||||
// one by one until a working one is found.
|
// one by one until a working one is found. The endpoint must be a valid url
|
||||||
|
// with host specified.
|
||||||
Endpoints []string `toml:"endpoint" json:"endpoint"`
|
Endpoints []string `toml:"endpoint" json:"endpoint"`
|
||||||
// TODO (Abhi) We might need to add auth per namespace. Looks like
|
}
|
||||||
// image auth information is passed by kube itself.
|
|
||||||
|
// AuthConfig contains the config related to authentication to a specific registry
|
||||||
|
type AuthConfig struct {
|
||||||
|
// Username is the username to login the registry.
|
||||||
|
Username string `toml:"username" json:"username"`
|
||||||
|
// Password is the password to login the registry.
|
||||||
|
Password string `toml:"password" json:"password"`
|
||||||
|
// Auth is a base64 encoded string from the concatenation of the username,
|
||||||
|
// a colon, and the password.
|
||||||
|
Auth string `toml:"auth" json:"auth"`
|
||||||
|
// IdentityToken is used to authenticate the user and get
|
||||||
|
// an access token for the registry.
|
||||||
|
IdentityToken string `toml:"identitytoken" json:"identitytoken"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Registry is registry settings configured
|
// Registry is registry settings configured
|
||||||
type Registry struct {
|
type Registry struct {
|
||||||
// Mirrors are namespace to mirror mapping for all namespaces.
|
// Mirrors are namespace to mirror mapping for all namespaces.
|
||||||
Mirrors map[string]Mirror `toml:"mirrors" json:"mirrors"`
|
Mirrors map[string]Mirror `toml:"mirrors" json:"mirrors"`
|
||||||
|
// Auths are registry endpoint to auth config mapping. The registry endpoint must
|
||||||
|
// be a valid url with host specified.
|
||||||
|
Auths map[string]AuthConfig `toml:"auths" json:"auths"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PluginConfig contains toml config related to CRI plugin,
|
// PluginConfig contains toml config related to CRI plugin,
|
||||||
@ -81,7 +99,7 @@ type PluginConfig struct {
|
|||||||
// CniConfig contains config related to cni
|
// CniConfig contains config related to cni
|
||||||
CniConfig `toml:"cni" json:"cni"`
|
CniConfig `toml:"cni" json:"cni"`
|
||||||
// Registry contains config related to the registry
|
// Registry contains config related to the registry
|
||||||
Registry `toml:"registry" json:"registry"`
|
Registry Registry `toml:"registry" json:"registry"`
|
||||||
// StreamServerAddress is the ip address streaming server is listening on.
|
// StreamServerAddress is the ip address streaming server is listening on.
|
||||||
StreamServerAddress string `toml:"stream_server_address" json:"streamServerAddress"`
|
StreamServerAddress string `toml:"stream_server_address" json:"streamServerAddress"`
|
||||||
// StreamServerPort is the port streaming server is listening on.
|
// StreamServerPort is the port streaming server is listening on.
|
||||||
@ -132,9 +150,10 @@ func DefaultConfig() PluginConfig {
|
|||||||
Engine: "",
|
Engine: "",
|
||||||
Root: "",
|
Root: "",
|
||||||
},
|
},
|
||||||
|
NoPivot: false,
|
||||||
},
|
},
|
||||||
StreamServerAddress: "",
|
StreamServerAddress: "127.0.0.1",
|
||||||
StreamServerPort: "10010",
|
StreamServerPort: "0",
|
||||||
EnableSelinux: false,
|
EnableSelinux: false,
|
||||||
EnableTLSStreaming: false,
|
EnableTLSStreaming: false,
|
||||||
SandboxImage: "k8s.gcr.io/pause:3.1",
|
SandboxImage: "k8s.gcr.io/pause:3.1",
|
||||||
|
202
vendor/github.com/containerd/cri/pkg/containerd/resolver/auth.go
generated
vendored
202
vendor/github.com/containerd/cri/pkg/containerd/resolver/auth.go
generated
vendored
@ -1,202 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2018 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// The corresponding file is in containerd/remote/docker/
|
|
||||||
// This package can be removed once a more feasible and hollistic resolver
|
|
||||||
// is finalized in containerd.
|
|
||||||
|
|
||||||
package resolver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type authenticationScheme byte
|
|
||||||
|
|
||||||
const (
|
|
||||||
basicAuth authenticationScheme = 1 << iota // Defined in RFC 7617
|
|
||||||
digestAuth // Defined in RFC 7616
|
|
||||||
bearerAuth // Defined in RFC 6750
|
|
||||||
)
|
|
||||||
|
|
||||||
// challenge carries information from a WWW-Authenticate response header.
|
|
||||||
// See RFC 2617.
|
|
||||||
type challenge struct {
|
|
||||||
// scheme is the auth-scheme according to RFC 2617
|
|
||||||
scheme authenticationScheme
|
|
||||||
|
|
||||||
// parameters are the auth-params according to RFC 2617
|
|
||||||
parameters map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
type byScheme []challenge
|
|
||||||
|
|
||||||
func (bs byScheme) Len() int { return len(bs) }
|
|
||||||
func (bs byScheme) Swap(i, j int) { bs[i], bs[j] = bs[j], bs[i] }
|
|
||||||
|
|
||||||
// Sort in priority order: token > digest > basic
|
|
||||||
func (bs byScheme) Less(i, j int) bool { return bs[i].scheme > bs[j].scheme }
|
|
||||||
|
|
||||||
// Octet types from RFC 2616.
|
|
||||||
type octetType byte
|
|
||||||
|
|
||||||
var octetTypes [256]octetType
|
|
||||||
|
|
||||||
const (
|
|
||||||
isToken octetType = 1 << iota
|
|
||||||
isSpace
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// OCTET = <any 8-bit sequence of data>
|
|
||||||
// CHAR = <any US-ASCII character (octets 0 - 127)>
|
|
||||||
// CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
|
|
||||||
// CR = <US-ASCII CR, carriage return (13)>
|
|
||||||
// LF = <US-ASCII LF, linefeed (10)>
|
|
||||||
// SP = <US-ASCII SP, space (32)>
|
|
||||||
// HT = <US-ASCII HT, horizontal-tab (9)>
|
|
||||||
// <"> = <US-ASCII double-quote mark (34)>
|
|
||||||
// CRLF = CR LF
|
|
||||||
// LWS = [CRLF] 1*( SP | HT )
|
|
||||||
// TEXT = <any OCTET except CTLs, but including LWS>
|
|
||||||
// separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <">
|
|
||||||
// | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT
|
|
||||||
// token = 1*<any CHAR except CTLs or separators>
|
|
||||||
// qdtext = <any TEXT except <">>
|
|
||||||
|
|
||||||
for c := 0; c < 256; c++ {
|
|
||||||
var t octetType
|
|
||||||
isCtl := c <= 31 || c == 127
|
|
||||||
isChar := 0 <= c && c <= 127
|
|
||||||
isSeparator := strings.ContainsRune(" \t\"(),/:;<=>?@[]\\{}", rune(c))
|
|
||||||
if strings.ContainsRune(" \t\r\n", rune(c)) {
|
|
||||||
t |= isSpace
|
|
||||||
}
|
|
||||||
if isChar && !isCtl && !isSeparator {
|
|
||||||
t |= isToken
|
|
||||||
}
|
|
||||||
octetTypes[c] = t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseAuthHeader(header http.Header) []challenge {
|
|
||||||
challenges := []challenge{}
|
|
||||||
for _, h := range header[http.CanonicalHeaderKey("WWW-Authenticate")] {
|
|
||||||
v, p := parseValueAndParams(h)
|
|
||||||
var s authenticationScheme
|
|
||||||
switch v {
|
|
||||||
case "basic":
|
|
||||||
s = basicAuth
|
|
||||||
case "digest":
|
|
||||||
s = digestAuth
|
|
||||||
case "bearer":
|
|
||||||
s = bearerAuth
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
challenges = append(challenges, challenge{scheme: s, parameters: p})
|
|
||||||
}
|
|
||||||
sort.Stable(byScheme(challenges))
|
|
||||||
return challenges
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseValueAndParams(header string) (value string, params map[string]string) {
|
|
||||||
params = make(map[string]string)
|
|
||||||
value, s := expectToken(header)
|
|
||||||
if value == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
value = strings.ToLower(value)
|
|
||||||
for {
|
|
||||||
var pkey string
|
|
||||||
pkey, s = expectToken(skipSpace(s))
|
|
||||||
if pkey == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !strings.HasPrefix(s, "=") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var pvalue string
|
|
||||||
pvalue, s = expectTokenOrQuoted(s[1:])
|
|
||||||
if pvalue == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pkey = strings.ToLower(pkey)
|
|
||||||
params[pkey] = pvalue
|
|
||||||
s = skipSpace(s)
|
|
||||||
if !strings.HasPrefix(s, ",") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s = s[1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func skipSpace(s string) (rest string) {
|
|
||||||
i := 0
|
|
||||||
for ; i < len(s); i++ {
|
|
||||||
if octetTypes[s[i]]&isSpace == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s[i:]
|
|
||||||
}
|
|
||||||
|
|
||||||
func expectToken(s string) (token, rest string) {
|
|
||||||
i := 0
|
|
||||||
for ; i < len(s); i++ {
|
|
||||||
if octetTypes[s[i]]&isToken == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s[:i], s[i:]
|
|
||||||
}
|
|
||||||
|
|
||||||
func expectTokenOrQuoted(s string) (value string, rest string) {
|
|
||||||
if !strings.HasPrefix(s, "\"") {
|
|
||||||
return expectToken(s)
|
|
||||||
}
|
|
||||||
s = s[1:]
|
|
||||||
for i := 0; i < len(s); i++ {
|
|
||||||
switch s[i] {
|
|
||||||
case '"':
|
|
||||||
return s[:i], s[i+1:]
|
|
||||||
case '\\':
|
|
||||||
p := make([]byte, len(s)-1)
|
|
||||||
j := copy(p, s[:i])
|
|
||||||
escape := true
|
|
||||||
for i = i + 1; i < len(s); i++ {
|
|
||||||
b := s[i]
|
|
||||||
switch {
|
|
||||||
case escape:
|
|
||||||
escape = false
|
|
||||||
p[j] = b
|
|
||||||
j++
|
|
||||||
case b == '\\':
|
|
||||||
escape = true
|
|
||||||
case b == '"':
|
|
||||||
return string(p[:j]), s[i+1:]
|
|
||||||
default:
|
|
||||||
p[j] = b
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", ""
|
|
||||||
}
|
|
172
vendor/github.com/containerd/cri/pkg/containerd/resolver/fetcher.go
generated
vendored
172
vendor/github.com/containerd/cri/pkg/containerd/resolver/fetcher.go
generated
vendored
@ -1,172 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2018 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// The corresponding file is in containerd/remote/docker/
|
|
||||||
// This package can be removed once a more feasible and hollistic resolver
|
|
||||||
// is finalized in containerd.
|
|
||||||
|
|
||||||
package resolver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/containerd/containerd/errdefs"
|
|
||||||
"github.com/containerd/containerd/images"
|
|
||||||
"github.com/containerd/containerd/log"
|
|
||||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
type dockerFetcher struct {
|
|
||||||
*dockerBase
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r dockerFetcher) Fetch(ctx context.Context, desc ocispec.Descriptor) (io.ReadCloser, error) {
|
|
||||||
var bases []string
|
|
||||||
for _, b := range r.base {
|
|
||||||
bases = append(bases, b.String())
|
|
||||||
}
|
|
||||||
ctx = log.WithLogger(ctx, log.G(ctx).WithFields(
|
|
||||||
logrus.Fields{
|
|
||||||
"base": bases,
|
|
||||||
"digest": desc.Digest,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
|
|
||||||
urls, err := r.getV2URLPaths(ctx, desc)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, err = contextWithRepositoryScope(ctx, r.refspec, false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return newHTTPReadSeeker(desc.Size, func(offset int64) (io.ReadCloser, error) {
|
|
||||||
for _, u := range urls {
|
|
||||||
rc, err := r.open(ctx, u, desc.MediaType, offset)
|
|
||||||
if err != nil {
|
|
||||||
if errdefs.IsNotFound(err) {
|
|
||||||
continue // try one of the other urls.
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, errors.Wrapf(errdefs.ErrNotFound,
|
|
||||||
"could not fetch content descriptor %v (%v) from remote",
|
|
||||||
desc.Digest, desc.MediaType)
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r dockerFetcher) open(ctx context.Context, u, mediatype string, offset int64) (io.ReadCloser, error) {
|
|
||||||
req, err := http.NewRequest(http.MethodGet, u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Set("Accept", strings.Join([]string{mediatype, `*`}, ", "))
|
|
||||||
|
|
||||||
if offset > 0 {
|
|
||||||
// Note: "Accept-Ranges: bytes" cannot be trusted as some endpoints
|
|
||||||
// will return the header without supporting the range. The content
|
|
||||||
// range must always be checked.
|
|
||||||
req.Header.Set("Range", fmt.Sprintf("bytes=%d-", offset))
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := r.doRequestWithRetries(ctx, req, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.StatusCode > 299 {
|
|
||||||
// TODO(stevvooe): When doing a offset specific request, we should
|
|
||||||
// really distinguish between a 206 and a 200. In the case of 200, we
|
|
||||||
// can discard the bytes, hiding the seek behavior from the
|
|
||||||
// implementation.
|
|
||||||
|
|
||||||
resp.Body.Close()
|
|
||||||
if resp.StatusCode == http.StatusNotFound {
|
|
||||||
return nil, errors.Wrapf(errdefs.ErrNotFound, "content at %v not found", u)
|
|
||||||
}
|
|
||||||
return nil, errors.Errorf("unexpected status code %v: %v", u, resp.Status)
|
|
||||||
}
|
|
||||||
if offset > 0 {
|
|
||||||
cr := resp.Header.Get("content-range")
|
|
||||||
if cr != "" {
|
|
||||||
if !strings.HasPrefix(cr, fmt.Sprintf("bytes %d-", offset)) {
|
|
||||||
return nil, errors.Errorf("unhandled content range in response: %v", cr)
|
|
||||||
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// TODO: Should any cases where use of content range
|
|
||||||
// without the proper header be considerd?
|
|
||||||
// 206 responses?
|
|
||||||
|
|
||||||
// Discard up to offset
|
|
||||||
// Could use buffer pool here but this case should be rare
|
|
||||||
n, err := io.Copy(ioutil.Discard, io.LimitReader(resp.Body, offset))
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to discard to offset")
|
|
||||||
}
|
|
||||||
if n != offset {
|
|
||||||
return nil, errors.Errorf("unable to discard to offset")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return resp.Body, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getV2URLPaths generates the candidate urls paths for the object based on the
|
|
||||||
// set of hints and the provided object id. URLs are returned in the order of
|
|
||||||
// most to least likely succeed.
|
|
||||||
func (r *dockerFetcher) getV2URLPaths(ctx context.Context, desc ocispec.Descriptor) ([]string, error) {
|
|
||||||
var urls []string
|
|
||||||
|
|
||||||
if len(desc.URLs) > 0 {
|
|
||||||
// handle fetch via external urls.
|
|
||||||
for _, u := range desc.URLs {
|
|
||||||
log.G(ctx).WithField("url", u).Debug("adding alternative url")
|
|
||||||
urls = append(urls, u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch desc.MediaType {
|
|
||||||
case images.MediaTypeDockerSchema2Manifest, images.MediaTypeDockerSchema2ManifestList,
|
|
||||||
images.MediaTypeDockerSchema1Manifest,
|
|
||||||
ocispec.MediaTypeImageManifest, ocispec.MediaTypeImageIndex:
|
|
||||||
urls = append(urls, r.urls(path.Join("manifests", desc.Digest.String()))...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// always fallback to attempting to get the object out of the blobs store.
|
|
||||||
urls = append(urls, r.urls(path.Join("blobs", desc.Digest.String()))...)
|
|
||||||
|
|
||||||
return urls, nil
|
|
||||||
}
|
|
148
vendor/github.com/containerd/cri/pkg/containerd/resolver/httpreadseeker.go
generated
vendored
148
vendor/github.com/containerd/cri/pkg/containerd/resolver/httpreadseeker.go
generated
vendored
@ -1,148 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2018 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// The corresponding file is in containerd/remote/docker/.
|
|
||||||
// This package can be removed once a more feasible and hollistic resolver
|
|
||||||
// is finalized in containerd.
|
|
||||||
|
|
||||||
package resolver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
|
|
||||||
"github.com/containerd/containerd/errdefs"
|
|
||||||
"github.com/containerd/containerd/log"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
type httpReadSeeker struct {
|
|
||||||
size int64
|
|
||||||
offset int64
|
|
||||||
rc io.ReadCloser
|
|
||||||
open func(offset int64) (io.ReadCloser, error)
|
|
||||||
closed bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func newHTTPReadSeeker(size int64, open func(offset int64) (io.ReadCloser, error)) (io.ReadCloser, error) {
|
|
||||||
return &httpReadSeeker{
|
|
||||||
size: size,
|
|
||||||
open: open,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hrs *httpReadSeeker) Read(p []byte) (n int, err error) {
|
|
||||||
if hrs.closed {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
rd, err := hrs.reader()
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err = rd.Read(p)
|
|
||||||
hrs.offset += int64(n)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hrs *httpReadSeeker) Close() error {
|
|
||||||
if hrs.closed {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
hrs.closed = true
|
|
||||||
if hrs.rc != nil {
|
|
||||||
return hrs.rc.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hrs *httpReadSeeker) Seek(offset int64, whence int) (int64, error) {
|
|
||||||
if hrs.closed {
|
|
||||||
return 0, errors.Wrap(errdefs.ErrUnavailable, "Fetcher.Seek: closed")
|
|
||||||
}
|
|
||||||
|
|
||||||
abs := hrs.offset
|
|
||||||
switch whence {
|
|
||||||
case io.SeekStart:
|
|
||||||
abs = offset
|
|
||||||
case io.SeekCurrent:
|
|
||||||
abs += offset
|
|
||||||
case io.SeekEnd:
|
|
||||||
if hrs.size == -1 {
|
|
||||||
return 0, errors.Wrap(errdefs.ErrUnavailable, "Fetcher.Seek: unknown size, cannot seek from end")
|
|
||||||
}
|
|
||||||
abs = hrs.size + offset
|
|
||||||
default:
|
|
||||||
return 0, errors.Wrap(errdefs.ErrInvalidArgument, "Fetcher.Seek: invalid whence")
|
|
||||||
}
|
|
||||||
|
|
||||||
if abs < 0 {
|
|
||||||
return 0, errors.Wrapf(errdefs.ErrInvalidArgument, "Fetcher.Seek: negative offset")
|
|
||||||
}
|
|
||||||
|
|
||||||
if abs != hrs.offset {
|
|
||||||
if hrs.rc != nil {
|
|
||||||
if err := hrs.rc.Close(); err != nil {
|
|
||||||
log.L.WithError(err).Errorf("Fetcher.Seek: failed to close ReadCloser")
|
|
||||||
}
|
|
||||||
|
|
||||||
hrs.rc = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
hrs.offset = abs
|
|
||||||
}
|
|
||||||
|
|
||||||
return hrs.offset, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hrs *httpReadSeeker) reader() (io.Reader, error) {
|
|
||||||
if hrs.rc != nil {
|
|
||||||
return hrs.rc, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if hrs.size == -1 || hrs.offset < hrs.size {
|
|
||||||
// only try to reopen the body request if we are seeking to a value
|
|
||||||
// less than the actual size.
|
|
||||||
if hrs.open == nil {
|
|
||||||
return nil, errors.Wrapf(errdefs.ErrNotImplemented, "cannot open")
|
|
||||||
}
|
|
||||||
|
|
||||||
rc, err := hrs.open(hrs.offset)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrapf(err, "httpReaderSeeker: failed open")
|
|
||||||
}
|
|
||||||
|
|
||||||
if hrs.rc != nil {
|
|
||||||
if err := hrs.rc.Close(); err != nil {
|
|
||||||
log.L.WithError(err).Errorf("httpReadSeeker: failed to close ReadCloser")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hrs.rc = rc
|
|
||||||
} else {
|
|
||||||
// There is an edge case here where offset == size of the content. If
|
|
||||||
// we seek, we will probably get an error for content that cannot be
|
|
||||||
// sought (?). In that case, we should err on committing the content,
|
|
||||||
// as the length is already satisified but we just return the empty
|
|
||||||
// reader instead.
|
|
||||||
|
|
||||||
hrs.rc = ioutil.NopCloser(bytes.NewReader([]byte{}))
|
|
||||||
}
|
|
||||||
|
|
||||||
return hrs.rc, nil
|
|
||||||
}
|
|
622
vendor/github.com/containerd/cri/pkg/containerd/resolver/resolver.go
generated
vendored
622
vendor/github.com/containerd/cri/pkg/containerd/resolver/resolver.go
generated
vendored
@ -1,622 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2018 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 resolver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"path"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/containerd/containerd/images"
|
|
||||||
"github.com/containerd/containerd/log"
|
|
||||||
"github.com/containerd/containerd/reference"
|
|
||||||
"github.com/containerd/containerd/remotes"
|
|
||||||
digest "github.com/opencontainers/go-digest"
|
|
||||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"golang.org/x/net/context/ctxhttp"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This file is a modified copy of containerd/remote/docker/resolver.go.
|
|
||||||
// The changes carried over by this file includes support for image ref
|
|
||||||
// resolution and fetching image using multiple registry mirror urls. A new
|
|
||||||
// ResolverOption called Registry is added. Registry will contain a map
|
|
||||||
// of namespace relevant mirror urls. The client will use the ResolverOptions
|
|
||||||
// to set the urls associated with the namespace of the image reference.
|
|
||||||
// This package can be removed once a more feasible and hollistic resolver
|
|
||||||
// is finalized in containerd. The specific changes are made to the base
|
|
||||||
// function to calculate the base urls for the image location. urls() is
|
|
||||||
// added to fetch the mirror urls associated with the namespace of the image
|
|
||||||
// ResolverOptions are changed for client to set the namespace and mirror urls
|
|
||||||
// for to pull the image.
|
|
||||||
|
|
||||||
var (
|
|
||||||
// ErrNoToken is returned if a request is successful but the body does not
|
|
||||||
// contain an authorization token.
|
|
||||||
ErrNoToken = errors.New("authorization server did not include a token in the response")
|
|
||||||
|
|
||||||
// ErrInvalidAuthorization is used when credentials are passed to a server but
|
|
||||||
// those credentials are rejected.
|
|
||||||
ErrInvalidAuthorization = errors.New("authorization failed")
|
|
||||||
)
|
|
||||||
|
|
||||||
type containerdResolver struct {
|
|
||||||
credentials func(string) (string, string, error)
|
|
||||||
plainHTTP bool
|
|
||||||
client *http.Client
|
|
||||||
tracker StatusTracker
|
|
||||||
registry map[string][]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Options are used to configured a new Docker register resolver
|
|
||||||
type Options struct {
|
|
||||||
// Credentials provides username and secret given a host.
|
|
||||||
// If username is empty but a secret is given, that secret
|
|
||||||
// is interpretted as a long lived token.
|
|
||||||
Credentials func(string) (string, string, error)
|
|
||||||
|
|
||||||
// PlainHTTP specifies to use plain http and not https
|
|
||||||
PlainHTTP bool
|
|
||||||
|
|
||||||
// Client is the http client to used when making registry requests
|
|
||||||
Client *http.Client
|
|
||||||
|
|
||||||
// Tracker is used to track uploads to the registry. This is used
|
|
||||||
// since the registry does not have upload tracking and the existing
|
|
||||||
// mechanism for getting blob upload status is expensive.
|
|
||||||
Tracker StatusTracker
|
|
||||||
|
|
||||||
Registry map[string][]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewResolver returns a new resolver to a Docker registry
|
|
||||||
func NewResolver(options Options) remotes.Resolver {
|
|
||||||
tracker := options.Tracker
|
|
||||||
if tracker == nil {
|
|
||||||
tracker = NewInMemoryTracker()
|
|
||||||
}
|
|
||||||
|
|
||||||
return &containerdResolver{
|
|
||||||
credentials: options.Credentials,
|
|
||||||
plainHTTP: options.PlainHTTP,
|
|
||||||
client: options.Client,
|
|
||||||
tracker: tracker,
|
|
||||||
registry: options.Registry,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ remotes.Resolver = &containerdResolver{}
|
|
||||||
|
|
||||||
func (r *containerdResolver) Resolve(ctx context.Context, ref string) (string, ocispec.Descriptor, error) {
|
|
||||||
refspec, err := reference.Parse(ref)
|
|
||||||
if err != nil {
|
|
||||||
return "", ocispec.Descriptor{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if refspec.Object == "" {
|
|
||||||
return "", ocispec.Descriptor{}, reference.ErrObjectRequired
|
|
||||||
}
|
|
||||||
|
|
||||||
base, err := r.base(refspec)
|
|
||||||
if err != nil {
|
|
||||||
return "", ocispec.Descriptor{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
fetcher := dockerFetcher{
|
|
||||||
dockerBase: base,
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
urls []string
|
|
||||||
dgst = refspec.Digest()
|
|
||||||
)
|
|
||||||
|
|
||||||
if dgst != "" {
|
|
||||||
if err := dgst.Validate(); err != nil {
|
|
||||||
// need to fail here, since we can't actually resolve the invalid
|
|
||||||
// digest.
|
|
||||||
return "", ocispec.Descriptor{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// turns out, we have a valid digest, make a url.
|
|
||||||
urls = append(urls, fetcher.urls("manifests", dgst.String())...)
|
|
||||||
|
|
||||||
// fallback to blobs on not found.
|
|
||||||
urls = append(urls, fetcher.urls("blobs", dgst.String())...)
|
|
||||||
} else {
|
|
||||||
urls = append(urls, fetcher.urls("manifests", refspec.Object)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, err = contextWithRepositoryScope(ctx, refspec, false)
|
|
||||||
if err != nil {
|
|
||||||
return "", ocispec.Descriptor{}, err
|
|
||||||
}
|
|
||||||
for _, u := range urls {
|
|
||||||
log.G(ctx).WithFields(logrus.Fields{
|
|
||||||
"url": u,
|
|
||||||
}).Debug("Trying to fetch from url")
|
|
||||||
req, err := http.NewRequest(http.MethodHead, u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return "", ocispec.Descriptor{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// set headers for all the types we support for resolution.
|
|
||||||
req.Header.Set("Accept", strings.Join([]string{
|
|
||||||
images.MediaTypeDockerSchema2Manifest,
|
|
||||||
images.MediaTypeDockerSchema2ManifestList,
|
|
||||||
ocispec.MediaTypeImageManifest,
|
|
||||||
ocispec.MediaTypeImageIndex, "*"}, ", "))
|
|
||||||
|
|
||||||
log.G(ctx).Info("resolving")
|
|
||||||
resp, err := fetcher.doRequestWithRetries(ctx, req, nil)
|
|
||||||
if err != nil {
|
|
||||||
return "", ocispec.Descriptor{}, err
|
|
||||||
}
|
|
||||||
resp.Body.Close() // don't care about body contents.
|
|
||||||
|
|
||||||
if resp.StatusCode > 299 {
|
|
||||||
if resp.StatusCode == http.StatusNotFound {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return "", ocispec.Descriptor{}, errors.Errorf("unexpected status code %v: %v", u, resp.Status)
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is the only point at which we trust the registry. we use the
|
|
||||||
// content headers to assemble a descriptor for the name. when this becomes
|
|
||||||
// more robust, we mostly get this information from a secure trust store.
|
|
||||||
dgstHeader := digest.Digest(resp.Header.Get("Docker-Content-Digest"))
|
|
||||||
|
|
||||||
if dgstHeader != "" {
|
|
||||||
if err := dgstHeader.Validate(); err != nil {
|
|
||||||
return "", ocispec.Descriptor{}, errors.Wrapf(err, "%q in header not a valid digest", dgstHeader)
|
|
||||||
}
|
|
||||||
dgst = dgstHeader
|
|
||||||
}
|
|
||||||
|
|
||||||
if dgst == "" {
|
|
||||||
return "", ocispec.Descriptor{}, errors.Errorf("could not resolve digest for %v", ref)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
size int64
|
|
||||||
sizeHeader = resp.Header.Get("Content-Length")
|
|
||||||
)
|
|
||||||
|
|
||||||
size, err = strconv.ParseInt(sizeHeader, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
|
|
||||||
return "", ocispec.Descriptor{}, errors.Wrapf(err, "invalid size header: %q", sizeHeader)
|
|
||||||
}
|
|
||||||
if size < 0 {
|
|
||||||
return "", ocispec.Descriptor{}, errors.Errorf("%q in header not a valid size", sizeHeader)
|
|
||||||
}
|
|
||||||
|
|
||||||
desc := ocispec.Descriptor{
|
|
||||||
Digest: dgst,
|
|
||||||
MediaType: resp.Header.Get("Content-Type"), // need to strip disposition?
|
|
||||||
Size: size,
|
|
||||||
}
|
|
||||||
|
|
||||||
log.G(ctx).WithField("desc.digest", desc.Digest).Debug("resolved")
|
|
||||||
return ref, desc, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", ocispec.Descriptor{}, errors.Errorf("%v not found", ref)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *containerdResolver) Fetcher(ctx context.Context, ref string) (remotes.Fetcher, error) {
|
|
||||||
refspec, err := reference.Parse(ref)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
base, err := r.base(refspec)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return dockerFetcher{
|
|
||||||
dockerBase: base,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *containerdResolver) Pusher(ctx context.Context, ref string) (remotes.Pusher, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type dockerBase struct {
|
|
||||||
refspec reference.Spec
|
|
||||||
base []url.URL
|
|
||||||
|
|
||||||
client *http.Client
|
|
||||||
useBasic bool
|
|
||||||
username, secret string
|
|
||||||
token string
|
|
||||||
mu sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *containerdResolver) base(refspec reference.Spec) (*dockerBase, error) {
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
base []url.URL
|
|
||||||
username, secret string
|
|
||||||
)
|
|
||||||
|
|
||||||
host := refspec.Hostname()
|
|
||||||
prefix := strings.TrimPrefix(refspec.Locator, host+"/")
|
|
||||||
|
|
||||||
if urls, ok := r.registry[host]; ok {
|
|
||||||
urls, err := r.getV2Urls(urls, prefix)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to fetch v2 urls")
|
|
||||||
}
|
|
||||||
base = append(base, urls...)
|
|
||||||
} else if host == "docker.io" {
|
|
||||||
base = append(base, []url.URL{{Host: "registry-1.docker.io", Scheme: "https", Path: path.Join("/v2", prefix)}}...)
|
|
||||||
} else {
|
|
||||||
scheme := "https"
|
|
||||||
if r.plainHTTP || strings.HasPrefix(host, "localhost:") {
|
|
||||||
scheme = "http"
|
|
||||||
}
|
|
||||||
base = append(base, []url.URL{{Host: host, Scheme: scheme, Path: path.Join("/v2", prefix)}}...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.credentials != nil {
|
|
||||||
username, secret, err = r.credentials(base[0].Host)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &dockerBase{
|
|
||||||
refspec: refspec,
|
|
||||||
base: base,
|
|
||||||
client: r.client,
|
|
||||||
username: username,
|
|
||||||
secret: secret,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *dockerBase) getToken() string {
|
|
||||||
r.mu.Lock()
|
|
||||||
defer r.mu.Unlock()
|
|
||||||
|
|
||||||
return r.token
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *dockerBase) setToken(token string) bool {
|
|
||||||
r.mu.Lock()
|
|
||||||
defer r.mu.Unlock()
|
|
||||||
|
|
||||||
changed := r.token != token
|
|
||||||
r.token = token
|
|
||||||
|
|
||||||
return changed
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *dockerBase) urls(ps ...string) []string {
|
|
||||||
urls := []string{}
|
|
||||||
for _, url := range r.base {
|
|
||||||
url.Path = path.Join(url.Path, path.Join(ps...))
|
|
||||||
urls = append(urls, url.String())
|
|
||||||
}
|
|
||||||
return urls
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *dockerBase) authorize(req *http.Request) {
|
|
||||||
token := r.getToken()
|
|
||||||
if r.useBasic {
|
|
||||||
req.SetBasicAuth(r.username, r.secret)
|
|
||||||
} else if token != "" {
|
|
||||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *dockerBase) doRequest(ctx context.Context, req *http.Request) (*http.Response, error) {
|
|
||||||
ctx = log.WithLogger(ctx, log.G(ctx).WithField("url", req.URL.String()))
|
|
||||||
log.G(ctx).WithField("request.headers", req.Header).WithField("request.method", req.Method).Debug("do request")
|
|
||||||
r.authorize(req)
|
|
||||||
resp, err := ctxhttp.Do(ctx, r.client, req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to do request")
|
|
||||||
}
|
|
||||||
log.G(ctx).WithFields(logrus.Fields{
|
|
||||||
"status": resp.Status,
|
|
||||||
"response.headers": resp.Header,
|
|
||||||
}).Debug("fetch response received")
|
|
||||||
return resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *dockerBase) doRequestWithRetries(ctx context.Context, req *http.Request, responses []*http.Response) (*http.Response, error) {
|
|
||||||
resp, err := r.doRequest(ctx, req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
responses = append(responses, resp)
|
|
||||||
req, err = r.retryRequest(ctx, req, responses)
|
|
||||||
if err != nil {
|
|
||||||
resp.Body.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if req != nil {
|
|
||||||
resp.Body.Close()
|
|
||||||
return r.doRequestWithRetries(ctx, req, responses)
|
|
||||||
}
|
|
||||||
return resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *dockerBase) retryRequest(ctx context.Context, req *http.Request, responses []*http.Response) (*http.Request, error) {
|
|
||||||
if len(responses) > 5 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
last := responses[len(responses)-1]
|
|
||||||
if last.StatusCode == http.StatusUnauthorized {
|
|
||||||
log.G(ctx).WithField("header", last.Header.Get("WWW-Authenticate")).Debug("Unauthorized")
|
|
||||||
for _, c := range parseAuthHeader(last.Header) {
|
|
||||||
if c.scheme == bearerAuth {
|
|
||||||
if err := invalidAuthorization(c, responses); err != nil {
|
|
||||||
r.setToken("")
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := r.setTokenAuth(ctx, c.parameters); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return copyRequest(req)
|
|
||||||
} else if c.scheme == basicAuth {
|
|
||||||
if r.username != "" && r.secret != "" {
|
|
||||||
r.useBasic = true
|
|
||||||
}
|
|
||||||
return copyRequest(req)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
} else if last.StatusCode == http.StatusMethodNotAllowed && req.Method == http.MethodHead {
|
|
||||||
// Support registries which have not properly implemented the HEAD method for
|
|
||||||
// manifests endpoint
|
|
||||||
if strings.Contains(req.URL.Path, "/manifests/") {
|
|
||||||
// TODO: copy request?
|
|
||||||
req.Method = http.MethodGet
|
|
||||||
return copyRequest(req)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Handle 50x errors accounting for attempt history
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func invalidAuthorization(c challenge, responses []*http.Response) error {
|
|
||||||
errStr := c.parameters["error"]
|
|
||||||
if errStr == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
n := len(responses)
|
|
||||||
if n == 1 || (n > 1 && !sameRequest(responses[n-2].Request, responses[n-1].Request)) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.Wrapf(ErrInvalidAuthorization, "server message: %s", errStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sameRequest(r1, r2 *http.Request) bool {
|
|
||||||
if r1.Method != r2.Method {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if *r1.URL != *r2.URL {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyRequest(req *http.Request) (*http.Request, error) {
|
|
||||||
ireq := *req
|
|
||||||
if ireq.GetBody != nil {
|
|
||||||
var err error
|
|
||||||
ireq.Body, err = ireq.GetBody()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &ireq, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *dockerBase) setTokenAuth(ctx context.Context, params map[string]string) error {
|
|
||||||
realm, ok := params["realm"]
|
|
||||||
if !ok {
|
|
||||||
return errors.New("no realm specified for token auth challenge")
|
|
||||||
}
|
|
||||||
|
|
||||||
realmURL, err := url.Parse(realm)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "invalid token auth challenge realm")
|
|
||||||
}
|
|
||||||
|
|
||||||
to := tokenOptions{
|
|
||||||
realm: realmURL.String(),
|
|
||||||
service: params["service"],
|
|
||||||
}
|
|
||||||
|
|
||||||
to.scopes = getTokenScopes(ctx, params)
|
|
||||||
if len(to.scopes) == 0 {
|
|
||||||
return errors.New("no scope specified for token auth challenge")
|
|
||||||
}
|
|
||||||
|
|
||||||
var token string
|
|
||||||
if r.secret != "" {
|
|
||||||
// Credential information is provided, use oauth POST endpoint
|
|
||||||
token, err = r.fetchTokenWithOAuth(ctx, to)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to fetch oauth token")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Do request anonymously
|
|
||||||
token, err = r.fetchToken(ctx, to)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to fetch anonymous token")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
r.setToken(token)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type tokenOptions struct {
|
|
||||||
realm string
|
|
||||||
service string
|
|
||||||
scopes []string
|
|
||||||
}
|
|
||||||
|
|
||||||
type postTokenResponse struct {
|
|
||||||
AccessToken string `json:"access_token"`
|
|
||||||
RefreshToken string `json:"refresh_token"`
|
|
||||||
ExpiresIn int `json:"expires_in"`
|
|
||||||
IssuedAt time.Time `json:"issued_at"`
|
|
||||||
Scope string `json:"scope"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *dockerBase) fetchTokenWithOAuth(ctx context.Context, to tokenOptions) (string, error) {
|
|
||||||
form := url.Values{}
|
|
||||||
form.Set("scope", strings.Join(to.scopes, " "))
|
|
||||||
form.Set("service", to.service)
|
|
||||||
// TODO: Allow setting client_id
|
|
||||||
form.Set("client_id", "containerd-dist-tool")
|
|
||||||
|
|
||||||
if r.username == "" {
|
|
||||||
form.Set("grant_type", "refresh_token")
|
|
||||||
form.Set("refresh_token", r.secret)
|
|
||||||
} else {
|
|
||||||
form.Set("grant_type", "password")
|
|
||||||
form.Set("username", r.username)
|
|
||||||
form.Set("password", r.secret)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := ctxhttp.PostForm(ctx, r.client, to.realm, form)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
// Registries without support for POST may return 404 for POST /v2/token.
|
|
||||||
// As of September 2017, GCR is known to return 404.
|
|
||||||
if (resp.StatusCode == 405 && r.username != "") || resp.StatusCode == 404 || resp.StatusCode == 401 {
|
|
||||||
return r.fetchToken(ctx, to)
|
|
||||||
} else if resp.StatusCode < 200 || resp.StatusCode >= 400 {
|
|
||||||
b, _ := ioutil.ReadAll(io.LimitReader(resp.Body, 64000)) // 64KB
|
|
||||||
log.G(ctx).WithFields(logrus.Fields{
|
|
||||||
"status": resp.Status,
|
|
||||||
"body": string(b),
|
|
||||||
}).Debugf("token request failed")
|
|
||||||
// TODO: handle error body and write debug output
|
|
||||||
return "", errors.Errorf("unexpected status: %s", resp.Status)
|
|
||||||
}
|
|
||||||
|
|
||||||
decoder := json.NewDecoder(resp.Body)
|
|
||||||
|
|
||||||
var tr postTokenResponse
|
|
||||||
if err = decoder.Decode(&tr); err != nil {
|
|
||||||
return "", errors.Wrap(err, "unable to decode token response")
|
|
||||||
}
|
|
||||||
|
|
||||||
return tr.AccessToken, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type getTokenResponse struct {
|
|
||||||
Token string `json:"token"`
|
|
||||||
AccessToken string `json:"access_token"`
|
|
||||||
ExpiresIn int `json:"expires_in"`
|
|
||||||
IssuedAt time.Time `json:"issued_at"`
|
|
||||||
RefreshToken string `json:"refresh_token"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// fetchToken fetches a token using a GET request
|
|
||||||
func (r *dockerBase) fetchToken(ctx context.Context, to tokenOptions) (string, error) {
|
|
||||||
req, err := http.NewRequest("GET", to.realm, nil)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
reqParams := req.URL.Query()
|
|
||||||
|
|
||||||
if to.service != "" {
|
|
||||||
reqParams.Add("service", to.service)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, scope := range to.scopes {
|
|
||||||
reqParams.Add("scope", scope)
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.secret != "" {
|
|
||||||
req.SetBasicAuth(r.username, r.secret)
|
|
||||||
}
|
|
||||||
|
|
||||||
req.URL.RawQuery = reqParams.Encode()
|
|
||||||
|
|
||||||
resp, err := ctxhttp.Do(ctx, r.client, req)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode < 200 || resp.StatusCode >= 400 {
|
|
||||||
// TODO: handle error body and write debug output
|
|
||||||
return "", errors.Errorf("unexpected status: %s", resp.Status)
|
|
||||||
}
|
|
||||||
|
|
||||||
decoder := json.NewDecoder(resp.Body)
|
|
||||||
|
|
||||||
var tr getTokenResponse
|
|
||||||
if err = decoder.Decode(&tr); err != nil {
|
|
||||||
return "", errors.Wrap(err, "unable to decode token response")
|
|
||||||
}
|
|
||||||
|
|
||||||
// `access_token` is equivalent to `token` and if both are specified
|
|
||||||
// the choice is undefined. Canonicalize `access_token` by sticking
|
|
||||||
// things in `token`.
|
|
||||||
if tr.AccessToken != "" {
|
|
||||||
tr.Token = tr.AccessToken
|
|
||||||
}
|
|
||||||
|
|
||||||
if tr.Token == "" {
|
|
||||||
return "", ErrNoToken
|
|
||||||
}
|
|
||||||
|
|
||||||
return tr.Token, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *containerdResolver) getV2Urls(urls []string, imagePath string) ([]url.URL, error) {
|
|
||||||
v2Urls := []url.URL{}
|
|
||||||
for _, u := range urls {
|
|
||||||
v2Url, err := url.Parse(u)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrapf(err, "failed to parse url during getv2 urls: %+v", u)
|
|
||||||
}
|
|
||||||
v2Url.Path = path.Join("/v2", imagePath)
|
|
||||||
v2Urls = append(v2Urls, *v2Url)
|
|
||||||
}
|
|
||||||
return v2Urls, nil
|
|
||||||
}
|
|
80
vendor/github.com/containerd/cri/pkg/containerd/resolver/scope.go
generated
vendored
80
vendor/github.com/containerd/cri/pkg/containerd/resolver/scope.go
generated
vendored
@ -1,80 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2018 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// The corresponding file is in containerd/remote/docker/.
|
|
||||||
// This package can be removed once a more feasible and hollistic resolver
|
|
||||||
// is finalized in containerd.
|
|
||||||
|
|
||||||
package resolver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net/url"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/containerd/containerd/reference"
|
|
||||||
)
|
|
||||||
|
|
||||||
// repositoryScope returns a repository scope string such as "repository:foo/bar:pull"
|
|
||||||
// for "host/foo/bar:baz".
|
|
||||||
// When push is true, both pull and push are added to the scope.
|
|
||||||
func repositoryScope(refspec reference.Spec, push bool) (string, error) {
|
|
||||||
u, err := url.Parse("dummy://" + refspec.Locator)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
s := "repository:" + strings.TrimPrefix(u.Path, "/") + ":pull"
|
|
||||||
if push {
|
|
||||||
s += ",push"
|
|
||||||
}
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// tokenScopesKey is used for the key for context.WithValue().
|
|
||||||
// value: []string (e.g. {"registry:foo/bar:pull"})
|
|
||||||
type tokenScopesKey struct{}
|
|
||||||
|
|
||||||
// contextWithRepositoryScope returns a context with tokenScopesKey{} and the repository scope value.
|
|
||||||
func contextWithRepositoryScope(ctx context.Context, refspec reference.Spec, push bool) (context.Context, error) {
|
|
||||||
s, err := repositoryScope(refspec, push)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return context.WithValue(ctx, tokenScopesKey{}, []string{s}), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getTokenScopes returns deduplicated and sorted scopes from ctx.Value(tokenScopesKey{}) and params["scope"].
|
|
||||||
func getTokenScopes(ctx context.Context, params map[string]string) []string {
|
|
||||||
var scopes []string
|
|
||||||
if x := ctx.Value(tokenScopesKey{}); x != nil {
|
|
||||||
scopes = append(scopes, x.([]string)...)
|
|
||||||
}
|
|
||||||
if scope, ok := params["scope"]; ok {
|
|
||||||
for _, s := range scopes {
|
|
||||||
// Note: this comparison is unaware of the scope grammar (https://docs.docker.com/registry/spec/auth/scope/)
|
|
||||||
// So, "repository:foo/bar:pull,push" != "repository:foo/bar:push,pull", although semantically they are equal.
|
|
||||||
if s == scope {
|
|
||||||
// already appended
|
|
||||||
goto Sort
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scopes = append(scopes, scope)
|
|
||||||
}
|
|
||||||
Sort:
|
|
||||||
sort.Strings(scopes)
|
|
||||||
return scopes
|
|
||||||
}
|
|
71
vendor/github.com/containerd/cri/pkg/containerd/resolver/status.go
generated
vendored
71
vendor/github.com/containerd/cri/pkg/containerd/resolver/status.go
generated
vendored
@ -1,71 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2018 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// The corresponding file is in containerd/remote/docker/.
|
|
||||||
// This package can be removed once a more feasible and hollistic resolver
|
|
||||||
// is finalized in containerd.
|
|
||||||
|
|
||||||
package resolver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/containerd/containerd/content"
|
|
||||||
"github.com/containerd/containerd/errdefs"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Status of a content operation
|
|
||||||
type Status struct {
|
|
||||||
content.Status
|
|
||||||
|
|
||||||
// UploadUUID is used by the Docker registry to reference blob uploads
|
|
||||||
UploadUUID string
|
|
||||||
}
|
|
||||||
|
|
||||||
// StatusTracker to track status of operations
|
|
||||||
type StatusTracker interface {
|
|
||||||
GetStatus(string) (Status, error)
|
|
||||||
SetStatus(string, Status)
|
|
||||||
}
|
|
||||||
|
|
||||||
type memoryStatusTracker struct {
|
|
||||||
statuses map[string]Status
|
|
||||||
m sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewInMemoryTracker returns a StatusTracker that tracks content status in-memory
|
|
||||||
func NewInMemoryTracker() StatusTracker {
|
|
||||||
return &memoryStatusTracker{
|
|
||||||
statuses: map[string]Status{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *memoryStatusTracker) GetStatus(ref string) (Status, error) {
|
|
||||||
t.m.Lock()
|
|
||||||
defer t.m.Unlock()
|
|
||||||
status, ok := t.statuses[ref]
|
|
||||||
if !ok {
|
|
||||||
return Status{}, errors.Wrapf(errdefs.ErrNotFound, "status for ref %v", ref)
|
|
||||||
}
|
|
||||||
return status, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *memoryStatusTracker) SetStatus(ref string, status Status) {
|
|
||||||
t.m.Lock()
|
|
||||||
t.statuses[ref] = status
|
|
||||||
t.m.Unlock()
|
|
||||||
}
|
|
6
vendor/github.com/containerd/cri/pkg/server/container_start.go
generated
vendored
6
vendor/github.com/containerd/cri/pkg/server/container_start.go
generated
vendored
@ -108,7 +108,11 @@ func (c *criService) startContainer(ctx context.Context,
|
|||||||
return cntr.IO, nil
|
return cntr.IO, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
task, err := container.NewTask(ctx, ioCreation)
|
var taskOpts []containerd.NewTaskOpts
|
||||||
|
if c.config.NoPivot {
|
||||||
|
taskOpts = append(taskOpts, containerd.WithNoPivotRoot)
|
||||||
|
}
|
||||||
|
task, err := container.NewTask(ctx, ioCreation, taskOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to create containerd task")
|
return errors.Wrap(err, "failed to create containerd task")
|
||||||
}
|
}
|
||||||
|
10
vendor/github.com/containerd/cri/pkg/server/helpers.go
generated
vendored
10
vendor/github.com/containerd/cri/pkg/server/helpers.go
generated
vendored
@ -444,3 +444,13 @@ func getRuntimeConfigFromContainerInfo(c containers.Container) (criconfig.Runtim
|
|||||||
r.Root = runtimeOpts.RuntimeRoot
|
r.Root = runtimeOpts.RuntimeRoot
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// toRuntimeAuthConfig converts cri plugin auth config to runtime auth config.
|
||||||
|
func toRuntimeAuthConfig(a criconfig.AuthConfig) *runtime.AuthConfig {
|
||||||
|
return &runtime.AuthConfig{
|
||||||
|
Username: a.Username,
|
||||||
|
Password: a.Password,
|
||||||
|
Auth: a.Auth,
|
||||||
|
IdentityToken: a.IdentityToken,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
71
vendor/github.com/containerd/cri/pkg/server/image_pull.go
generated
vendored
71
vendor/github.com/containerd/cri/pkg/server/image_pull.go
generated
vendored
@ -19,18 +19,21 @@ package server
|
|||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containerd/containerd"
|
"github.com/containerd/containerd"
|
||||||
"github.com/containerd/containerd/errdefs"
|
"github.com/containerd/containerd/errdefs"
|
||||||
containerdimages "github.com/containerd/containerd/images"
|
containerdimages "github.com/containerd/containerd/images"
|
||||||
|
"github.com/containerd/containerd/reference"
|
||||||
|
"github.com/containerd/containerd/remotes"
|
||||||
|
"github.com/containerd/containerd/remotes/docker"
|
||||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||||
|
|
||||||
containerdresolver "github.com/containerd/cri/pkg/containerd/resolver"
|
|
||||||
imagestore "github.com/containerd/cri/pkg/store/image"
|
imagestore "github.com/containerd/cri/pkg/store/image"
|
||||||
"github.com/containerd/cri/pkg/util"
|
"github.com/containerd/cri/pkg/util"
|
||||||
)
|
)
|
||||||
@ -87,12 +90,7 @@ func (c *criService) PullImage(ctx context.Context, r *runtime.PullImageRequest)
|
|||||||
if ref != imageRef {
|
if ref != imageRef {
|
||||||
logrus.Debugf("PullImage using normalized image ref: %q", ref)
|
logrus.Debugf("PullImage using normalized image ref: %q", ref)
|
||||||
}
|
}
|
||||||
resolver := containerdresolver.NewResolver(containerdresolver.Options{
|
resolver, desc, err := c.getResolver(ctx, ref, c.credentials(r.GetAuth()))
|
||||||
Credentials: func(string) (string, string, error) { return ParseAuth(r.GetAuth()) },
|
|
||||||
Client: http.DefaultClient,
|
|
||||||
Registry: c.getResolverOptions(),
|
|
||||||
})
|
|
||||||
_, desc, err := resolver.Resolve(ctx, ref)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "failed to resolve image %q", ref)
|
return nil, errors.Wrapf(err, "failed to resolve image %q", ref)
|
||||||
}
|
}
|
||||||
@ -206,10 +204,59 @@ func (c *criService) createImageReference(ctx context.Context, name string, desc
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *criService) getResolverOptions() map[string][]string {
|
// credentials returns a credential function for docker resolver to use.
|
||||||
options := make(map[string][]string)
|
func (c *criService) credentials(auth *runtime.AuthConfig) func(string) (string, string, error) {
|
||||||
for ns, mirror := range c.config.Mirrors {
|
return func(host string) (string, string, error) {
|
||||||
options[ns] = append(options[ns], mirror.Endpoints...)
|
if auth == nil {
|
||||||
|
// Get default auth from config.
|
||||||
|
for h, ac := range c.config.Registry.Auths {
|
||||||
|
u, err := url.Parse(h)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", errors.Wrapf(err, "parse auth host %q", h)
|
||||||
}
|
}
|
||||||
return options
|
if u.Host == host {
|
||||||
|
auth = toRuntimeAuthConfig(ac)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ParseAuth(auth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getResolver tries registry mirrors and the default registry, and returns the resolver and descriptor
|
||||||
|
// from the first working registry.
|
||||||
|
func (c *criService) getResolver(ctx context.Context, ref string, cred func(string) (string, string, error)) (remotes.Resolver, imagespec.Descriptor, error) {
|
||||||
|
refspec, err := reference.Parse(ref)
|
||||||
|
if err != nil {
|
||||||
|
return nil, imagespec.Descriptor{}, errors.Wrap(err, "parse image reference")
|
||||||
|
}
|
||||||
|
// Try mirrors in order first, and then try default host name.
|
||||||
|
for _, e := range c.config.Registry.Mirrors[refspec.Hostname()].Endpoints {
|
||||||
|
u, err := url.Parse(e)
|
||||||
|
if err != nil {
|
||||||
|
return nil, imagespec.Descriptor{}, errors.Wrapf(err, "parse registry endpoint %q", e)
|
||||||
|
}
|
||||||
|
resolver := docker.NewResolver(docker.ResolverOptions{
|
||||||
|
Credentials: cred,
|
||||||
|
Client: http.DefaultClient,
|
||||||
|
Host: func(string) (string, error) { return u.Host, nil },
|
||||||
|
// By default use "https".
|
||||||
|
PlainHTTP: u.Scheme == "http",
|
||||||
|
})
|
||||||
|
_, desc, err := resolver.Resolve(ctx, ref)
|
||||||
|
if err == nil {
|
||||||
|
return resolver, desc, nil
|
||||||
|
}
|
||||||
|
// Continue to try next endpoint
|
||||||
|
}
|
||||||
|
resolver := docker.NewResolver(docker.ResolverOptions{
|
||||||
|
Credentials: cred,
|
||||||
|
Client: http.DefaultClient,
|
||||||
|
})
|
||||||
|
_, desc, err := resolver.Resolve(ctx, ref)
|
||||||
|
if err != nil {
|
||||||
|
return nil, imagespec.Descriptor{}, errors.Wrap(err, "no available registry endpoint")
|
||||||
|
}
|
||||||
|
return resolver, desc, nil
|
||||||
}
|
}
|
||||||
|
26
vendor/github.com/containerd/cri/pkg/server/sandbox_run.go
generated
vendored
26
vendor/github.com/containerd/cri/pkg/server/sandbox_run.go
generated
vendored
@ -293,8 +293,13 @@ func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandbox
|
|||||||
// Create sandbox task in containerd.
|
// Create sandbox task in containerd.
|
||||||
log.Tracef("Create sandbox container (id=%q, name=%q).",
|
log.Tracef("Create sandbox container (id=%q, name=%q).",
|
||||||
id, name)
|
id, name)
|
||||||
|
|
||||||
|
var taskOpts []containerd.NewTaskOpts
|
||||||
|
if c.config.NoPivot {
|
||||||
|
taskOpts = append(taskOpts, containerd.WithNoPivotRoot)
|
||||||
|
}
|
||||||
// We don't need stdio for sandbox container.
|
// We don't need stdio for sandbox container.
|
||||||
task, err := container.NewTask(ctx, containerdio.NullIO)
|
task, err := container.NewTask(ctx, containerdio.NullIO, taskOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return status, errors.Wrap(err, "failed to create containerd task")
|
return status, errors.Wrap(err, "failed to create containerd task")
|
||||||
}
|
}
|
||||||
@ -578,13 +583,10 @@ func untrustedWorkload(config *runtime.PodSandboxConfig) bool {
|
|||||||
return config.GetAnnotations()[annotations.UntrustedWorkload] == "true"
|
return config.GetAnnotations()[annotations.UntrustedWorkload] == "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
// hostPrivilegedSandbox returns true if the sandbox configuration
|
// hostAccessingSandbox returns true if the sandbox configuration
|
||||||
// requires additional host privileges for the sandbox.
|
// requires additional host access for the sandbox.
|
||||||
func hostPrivilegedSandbox(config *runtime.PodSandboxConfig) bool {
|
func hostAccessingSandbox(config *runtime.PodSandboxConfig) bool {
|
||||||
securityContext := config.GetLinux().GetSecurityContext()
|
securityContext := config.GetLinux().GetSecurityContext()
|
||||||
if securityContext.GetPrivileged() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
namespaceOptions := securityContext.GetNamespaceOptions()
|
namespaceOptions := securityContext.GetNamespaceOptions()
|
||||||
if namespaceOptions.GetNetwork() == runtime.NamespaceMode_NODE ||
|
if namespaceOptions.GetNetwork() == runtime.NamespaceMode_NODE ||
|
||||||
@ -602,9 +604,13 @@ func hostPrivilegedSandbox(config *runtime.PodSandboxConfig) bool {
|
|||||||
func (c *criService) getSandboxRuntime(config *runtime.PodSandboxConfig) (criconfig.Runtime, error) {
|
func (c *criService) getSandboxRuntime(config *runtime.PodSandboxConfig) (criconfig.Runtime, error) {
|
||||||
untrusted := false
|
untrusted := false
|
||||||
if untrustedWorkload(config) {
|
if untrustedWorkload(config) {
|
||||||
// TODO(random-liu): Figure out we should return error or not.
|
// If the untrusted workload is requesting access to the host/node, this request will fail.
|
||||||
if hostPrivilegedSandbox(config) {
|
//
|
||||||
return criconfig.Runtime{}, errors.New("untrusted workload with host privilege is not allowed")
|
// Note: If the workload is marked untrusted but requests privileged, this can be granted, as the
|
||||||
|
// runtime may support this. For example, in a virtual-machine isolated runtime, privileged
|
||||||
|
// is a supported option, granting the workload to access the entire guest VM instead of host.
|
||||||
|
if hostAccessingSandbox(config) {
|
||||||
|
return criconfig.Runtime{}, errors.New("untrusted workload with host access is not allowed")
|
||||||
}
|
}
|
||||||
untrusted = true
|
untrusted = true
|
||||||
}
|
}
|
||||||
|
18
vendor/github.com/containerd/cri/vendor.conf
generated
vendored
18
vendor/github.com/containerd/cri/vendor.conf
generated
vendored
@ -2,14 +2,15 @@ github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9
|
|||||||
github.com/blang/semver v3.1.0
|
github.com/blang/semver v3.1.0
|
||||||
github.com/boltdb/bolt e9cf4fae01b5a8ff89d0ec6b32f0d9c9f79aefdd
|
github.com/boltdb/bolt e9cf4fae01b5a8ff89d0ec6b32f0d9c9f79aefdd
|
||||||
github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
|
github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
|
||||||
github.com/containerd/cgroups fe281dd265766145e943a034aa41086474ea6130
|
github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2
|
||||||
github.com/containerd/console 9290d21dc56074581f619579c43d970b4514bc08
|
github.com/containerd/console 4d8a41f4ce5b9bae77c41786ea2458330f43f081
|
||||||
github.com/containerd/containerd 84bebdd91d347c99069d1705b7d4e6d6f746160c
|
github.com/containerd/containerd b9eeaa1ce83dd9970605ddbd0b35d4d3fa5f87bd
|
||||||
github.com/containerd/continuity d3c23511c1bf5851696cba83143d9cbcd666869b
|
github.com/containerd/continuity d3c23511c1bf5851696cba83143d9cbcd666869b
|
||||||
github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c
|
github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c
|
||||||
github.com/containerd/go-cni 5882530828ecf62032409b298a3e8b19e08b6534
|
github.com/containerd/go-cni 5882530828ecf62032409b298a3e8b19e08b6534
|
||||||
github.com/containerd/go-runc f271fa2021de855d4d918dbef83c5fe19db1bdd5
|
github.com/containerd/go-runc edcf3de1f4971445c42d61f20d506b30612aa031
|
||||||
github.com/containerd/typeurl f6943554a7e7e88b3c14aad190bf05932da84788
|
github.com/containerd/ttrpc 94dde388801693c54f88a6596f713b51a8b30b2d
|
||||||
|
github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40
|
||||||
github.com/containernetworking/cni v0.6.0
|
github.com/containernetworking/cni v0.6.0
|
||||||
github.com/containernetworking/plugins v0.7.0
|
github.com/containernetworking/plugins v0.7.0
|
||||||
github.com/coreos/go-systemd 48702e0da86bd25e76cfef347e2adeb434a0d0a6
|
github.com/coreos/go-systemd 48702e0da86bd25e76cfef347e2adeb434a0d0a6
|
||||||
@ -35,12 +36,12 @@ github.com/json-iterator/go f2b4162afba35581b6d4a50d3b8f34e33c144682
|
|||||||
github.com/matttproud/golang_protobuf_extensions v1.0.0
|
github.com/matttproud/golang_protobuf_extensions v1.0.0
|
||||||
github.com/Microsoft/go-winio v0.4.7
|
github.com/Microsoft/go-winio v0.4.7
|
||||||
github.com/Microsoft/hcsshim v0.6.11
|
github.com/Microsoft/hcsshim v0.6.11
|
||||||
github.com/modern-go/reflect2 05fbef0ca5da472bbf96c9322b84a53edc03c9fd
|
|
||||||
github.com/modern-go/concurrent 1.0.3
|
github.com/modern-go/concurrent 1.0.3
|
||||||
|
github.com/modern-go/reflect2 05fbef0ca5da472bbf96c9322b84a53edc03c9fd
|
||||||
github.com/opencontainers/go-digest c9281466c8b2f606084ac71339773efd177436e7
|
github.com/opencontainers/go-digest c9281466c8b2f606084ac71339773efd177436e7
|
||||||
github.com/opencontainers/image-spec v1.0.1
|
github.com/opencontainers/image-spec v1.0.1
|
||||||
github.com/opencontainers/runc 69663f0bd4b60df09991c08812a60108003fa340
|
github.com/opencontainers/runc 69663f0bd4b60df09991c08812a60108003fa340
|
||||||
github.com/opencontainers/runtime-spec v1.0.1
|
github.com/opencontainers/runtime-spec d810dbc60d8c5aeeb3d054bd1132fab2121968ce
|
||||||
github.com/opencontainers/runtime-tools v0.6.0
|
github.com/opencontainers/runtime-tools v0.6.0
|
||||||
github.com/opencontainers/selinux 4a2974bf1ee960774ffd517717f1f45325af0206
|
github.com/opencontainers/selinux 4a2974bf1ee960774ffd517717f1f45325af0206
|
||||||
github.com/pkg/errors v0.8.0
|
github.com/pkg/errors v0.8.0
|
||||||
@ -51,7 +52,6 @@ github.com/prometheus/common 89604d197083d4781071d3c65855d24ecfb0a563
|
|||||||
github.com/prometheus/procfs cb4147076ac75738c9a7d279075a253c0cc5acbd
|
github.com/prometheus/procfs cb4147076ac75738c9a7d279075a253c0cc5acbd
|
||||||
github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0
|
github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0
|
||||||
github.com/sirupsen/logrus v1.0.0
|
github.com/sirupsen/logrus v1.0.0
|
||||||
github.com/stevvooe/ttrpc d4528379866b0ce7e9d71f3eb96f0582fc374577
|
|
||||||
github.com/stretchr/testify v1.1.4
|
github.com/stretchr/testify v1.1.4
|
||||||
github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16
|
github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16
|
||||||
github.com/tchap/go-patricia 5ad6cdb7538b0097d5598c7e57f0a24072adf7dc
|
github.com/tchap/go-patricia 5ad6cdb7538b0097d5598c7e57f0a24072adf7dc
|
||||||
@ -62,7 +62,7 @@ github.com/xeipuuv/gojsonschema 1d523034197ff1f222f6429836dd36a2457a1874
|
|||||||
golang.org/x/crypto 49796115aa4b964c318aad4f3084fdb41e9aa067
|
golang.org/x/crypto 49796115aa4b964c318aad4f3084fdb41e9aa067
|
||||||
golang.org/x/net b3756b4b77d7b13260a0a2ec658753cf48922eac
|
golang.org/x/net b3756b4b77d7b13260a0a2ec658753cf48922eac
|
||||||
golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c
|
golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c
|
||||||
golang.org/x/sys 314a259e304ff91bd6985da2a7149bbf91237993 https://github.com/golang/sys
|
golang.org/x/sys 1b2967e3c290b7c545b3db0deeda16e9be4f98a2 https://github.com/golang/sys
|
||||||
golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4
|
golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4
|
||||||
golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631
|
golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631
|
||||||
google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
|
google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
|
||||||
|
Loading…
Reference in New Issue
Block a user