Vendor in latest containers/image

Add support for kpod login/logout

Signed-off-by: umohnani8 <umohnani@redhat.com>
This commit is contained in:
umohnani8 2017-10-10 10:11:06 -04:00
parent d664a58a6d
commit 5d48e1aca5
13 changed files with 192 additions and 51 deletions

View file

@ -4,7 +4,7 @@ k8s.io/apimachinery release-1.7 https://github.com/kubernetes/apimachinery
k8s.io/apiserver release-1.7 https://github.com/kubernetes/apiserver k8s.io/apiserver release-1.7 https://github.com/kubernetes/apiserver
# #
github.com/sirupsen/logrus v1.0.0 github.com/sirupsen/logrus v1.0.0
github.com/containers/image d17474f39dae1da15ab9ae033d57ebefcf62f77a github.com/containers/image 57b257d128d6075ea3287991ee408d24c7bd2758
github.com/docker/docker-credential-helpers d68f9aeca33f5fd3f08eeae5e9d175edf4e731d1 github.com/docker/docker-credential-helpers d68f9aeca33f5fd3f08eeae5e9d175edf4e731d1
github.com/ostreedev/ostree-go master github.com/ostreedev/ostree-go master
github.com/containers/storage 64bf27465d0d1edd89e7a4ce49866fea01145782 github.com/containers/storage 64bf27465d0d1edd89e7a4ce49866fea01145782

View file

@ -581,7 +581,7 @@ func (ic *imageCopier) copyBlobFromStream(srcStream io.Reader, srcInfo types.Blo
bar.ShowPercent = false bar.ShowPercent = false
bar.Start() bar.Start()
destStream = bar.NewProxyReader(destStream) destStream = bar.NewProxyReader(destStream)
defer fmt.Fprint(ic.reportWriter, "\n") defer bar.Finish()
// === Send a copy of the original, uncompressed, stream, to a separate path if necessary. // === Send a copy of the original, uncompressed, stream, to a separate path if necessary.
var originalLayerReader io.Reader // DO NOT USE this other than to drain the input if no other consumer in the pipeline has done so. var originalLayerReader io.Reader // DO NOT USE this other than to drain the input if no other consumer in the pipeline has done so.

View file

@ -7,7 +7,6 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"net"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
@ -16,9 +15,9 @@ import (
"github.com/containers/image/docker/reference" "github.com/containers/image/docker/reference"
"github.com/containers/image/pkg/docker/config" "github.com/containers/image/pkg/docker/config"
"github.com/containers/image/pkg/tlsclientconfig"
"github.com/containers/image/types" "github.com/containers/image/types"
"github.com/docker/distribution/registry/client" "github.com/docker/distribution/registry/client"
"github.com/docker/go-connections/sockets"
"github.com/docker/go-connections/tlsconfig" "github.com/docker/go-connections/tlsconfig"
"github.com/opencontainers/go-digest" "github.com/opencontainers/go-digest"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -110,27 +109,7 @@ func serverDefault() *tls.Config {
} }
} }
func newTransport() *http.Transport { // dockerCertDir returns a path to a directory to be consumed by tlsclientconfig.SetupCertificates() depending on ctx and hostPort.
direct := &net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}
tr := &http.Transport{
Proxy: http.ProxyFromEnvironment,
Dial: direct.Dial,
TLSHandshakeTimeout: 10 * time.Second,
// TODO(dmcgowan): Call close idle connections when complete and use keep alive
DisableKeepAlives: true,
}
proxyDialer, err := sockets.DialerFromEnvironment(direct)
if err == nil {
tr.Dial = proxyDialer.Dial
}
return tr
}
// dockerCertDir returns a path to a directory to be consumed by setupCertificates() depending on ctx and hostPort.
func dockerCertDir(ctx *types.SystemContext, hostPort string) string { func dockerCertDir(ctx *types.SystemContext, hostPort string) string {
if ctx != nil && ctx.DockerCertPath != "" { if ctx != nil && ctx.DockerCertPath != "" {
return ctx.DockerCertPath return ctx.DockerCertPath
@ -232,7 +211,7 @@ func newDockerClientWithDetails(ctx *types.SystemContext, registry, username, pa
if registry == dockerHostname { if registry == dockerHostname {
registry = dockerRegistry registry = dockerRegistry
} }
tr := newTransport() tr := tlsclientconfig.NewTransport()
tr.TLSClientConfig = serverDefault() tr.TLSClientConfig = serverDefault()
// It is undefined whether the host[:port] string for dockerHostname should be dockerHostname or dockerRegistry, // It is undefined whether the host[:port] string for dockerHostname should be dockerHostname or dockerRegistry,
@ -241,7 +220,7 @@ func newDockerClientWithDetails(ctx *types.SystemContext, registry, username, pa
// generally the UI hides the existence of the different dockerRegistry. But note that this behavior is // generally the UI hides the existence of the different dockerRegistry. But note that this behavior is
// undocumented and may change if docker/docker changes. // undocumented and may change if docker/docker changes.
certDir := dockerCertDir(ctx, hostName) certDir := dockerCertDir(ctx, hostName)
if err := setupCertificates(certDir, tr.TLSClientConfig); err != nil { if err := tlsclientconfig.SetupCertificates(certDir, tr.TLSClientConfig); err != nil {
return nil, err return nil, err
} }
@ -396,7 +375,7 @@ func (c *dockerClient) getBearerToken(ctx context.Context, realm, service, scope
if c.username != "" && c.password != "" { if c.username != "" && c.password != "" {
authReq.SetBasicAuth(c.username, c.password) authReq.SetBasicAuth(c.username, c.password)
} }
tr := newTransport() tr := tlsclientconfig.NewTransport()
// TODO(runcom): insecure for now to contact the external token service // TODO(runcom): insecure for now to contact the external token service
tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
client := &http.Client{Transport: tr} client := &http.Client{Transport: tr}

View file

@ -140,6 +140,7 @@ func (s *dockerImageSource) getExternalBlob(urls []string) (io.ReadCloser, int64
logrus.Debug(err) logrus.Debug(err)
continue continue
} }
break
} }
} }
if resp.Body != nil && err == nil { if resp.Body != nil && err == nil {

View file

@ -109,7 +109,7 @@ func (m *manifestOCI1) OCIConfig() (*imgspecv1.Image, error) {
func (m *manifestOCI1) LayerInfos() []types.BlobInfo { func (m *manifestOCI1) LayerInfos() []types.BlobInfo {
blobs := []types.BlobInfo{} blobs := []types.BlobInfo{}
for _, layer := range m.LayersDescriptors { for _, layer := range m.LayersDescriptors {
blobs = append(blobs, types.BlobInfo{Digest: layer.Digest, Size: layer.Size, Annotations: layer.Annotations}) blobs = append(blobs, types.BlobInfo{Digest: layer.Digest, Size: layer.Size, Annotations: layer.Annotations, URLs: layer.URLs})
} }
return blobs return blobs
} }
@ -160,6 +160,7 @@ func (m *manifestOCI1) UpdatedImage(options types.ManifestUpdateOptions) (types.
copy.LayersDescriptors[i].Digest = info.Digest copy.LayersDescriptors[i].Digest = info.Digest
copy.LayersDescriptors[i].Size = info.Size copy.LayersDescriptors[i].Size = info.Size
copy.LayersDescriptors[i].Annotations = info.Annotations copy.LayersDescriptors[i].Annotations = info.Annotations
copy.LayersDescriptors[i].URLs = info.URLs
} }
} }
// Ignore options.EmbeddedDockerReference: it may be set when converting from schema1, but we really don't care. // Ignore options.EmbeddedDockerReference: it may be set when converting from schema1, but we really don't care.

View file

@ -106,11 +106,7 @@ func (d *ociArchiveImageDestination) Commit() error {
src := d.tempDirRef.tempDirectory src := d.tempDirRef.tempDirectory
// path to save tarred up file // path to save tarred up file
dst := d.ref.resolvedFile dst := d.ref.resolvedFile
if err := tarDirectory(src, dst); err != nil { return tarDirectory(src, dst)
return err
}
return nil
} }
// tar converts the directory at src and saves it to dst // tar converts the directory at src and saves it to dst

View file

@ -66,7 +66,7 @@ func (d *ociImageDestination) ShouldCompressLayers() bool {
// AcceptsForeignLayerURLs returns false iff foreign layers in manifest should be actually // AcceptsForeignLayerURLs returns false iff foreign layers in manifest should be actually
// uploaded to the image destination, true otherwise. // uploaded to the image destination, true otherwise.
func (d *ociImageDestination) AcceptsForeignLayerURLs() bool { func (d *ociImageDestination) AcceptsForeignLayerURLs() bool {
return false return true
} }
// MustMatchRuntimeOS returns true iff the destination can store only images targeted for the current runtime OS. False otherwise. // MustMatchRuntimeOS returns true iff the destination can store only images targeted for the current runtime OS. False otherwise.

View file

@ -4,25 +4,43 @@ import (
"context" "context"
"io" "io"
"io/ioutil" "io/ioutil"
"net/http"
"os" "os"
"strconv"
"github.com/containers/image/pkg/tlsclientconfig"
"github.com/containers/image/types" "github.com/containers/image/types"
"github.com/docker/go-connections/tlsconfig"
"github.com/opencontainers/go-digest" "github.com/opencontainers/go-digest"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
) )
type ociImageSource struct { type ociImageSource struct {
ref ociReference ref ociReference
descriptor imgspecv1.Descriptor descriptor imgspecv1.Descriptor
client *http.Client
} }
// newImageSource returns an ImageSource for reading from an existing directory. // newImageSource returns an ImageSource for reading from an existing directory.
func newImageSource(ref ociReference) (types.ImageSource, error) { func newImageSource(ctx *types.SystemContext, ref ociReference) (types.ImageSource, error) {
tr := tlsclientconfig.NewTransport()
tr.TLSClientConfig = tlsconfig.ServerDefault()
if ctx != nil && ctx.OCICertPath != "" {
if err := tlsclientconfig.SetupCertificates(ctx.OCICertPath, tr.TLSClientConfig); err != nil {
return nil, err
}
tr.TLSClientConfig.InsecureSkipVerify = ctx.OCIInsecureSkipTLSVerify
}
client := &http.Client{}
client.Transport = tr
descriptor, err := ref.getManifestDescriptor() descriptor, err := ref.getManifestDescriptor()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &ociImageSource{ref: ref, descriptor: descriptor}, nil return &ociImageSource{ref: ref, descriptor: descriptor, client: client}, nil
} }
// Reference returns the reference used to set up this source. // Reference returns the reference used to set up this source.
@ -70,6 +88,10 @@ func (s *ociImageSource) GetTargetManifest(digest digest.Digest) ([]byte, string
// GetBlob returns a stream for the specified blob, and the blob's size. // GetBlob returns a stream for the specified blob, and the blob's size.
func (s *ociImageSource) GetBlob(info types.BlobInfo) (io.ReadCloser, int64, error) { func (s *ociImageSource) GetBlob(info types.BlobInfo) (io.ReadCloser, int64, error) {
if len(info.URLs) != 0 {
return s.getExternalBlob(info.URLs)
}
path, err := s.ref.blobPath(info.Digest) path, err := s.ref.blobPath(info.Digest)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
@ -89,3 +111,32 @@ func (s *ociImageSource) GetBlob(info types.BlobInfo) (io.ReadCloser, int64, err
func (s *ociImageSource) GetSignatures(context.Context) ([][]byte, error) { func (s *ociImageSource) GetSignatures(context.Context) ([][]byte, error) {
return [][]byte{}, nil return [][]byte{}, nil
} }
func (s *ociImageSource) getExternalBlob(urls []string) (io.ReadCloser, int64, error) {
errWrap := errors.New("failed fetching external blob from all urls")
for _, url := range urls {
resp, err := s.client.Get(url)
if err != nil {
errWrap = errors.Wrapf(errWrap, "fetching %s failed %s", url, err.Error())
continue
}
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
errWrap = errors.Wrapf(errWrap, "fetching %s failed, response code not 200", url)
continue
}
return resp.Body, getBlobSize(resp), nil
}
return nil, 0, errWrap
}
func getBlobSize(resp *http.Response) int64 {
size, err := strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 64)
if err != nil {
size = -1
}
return size
}

View file

@ -182,7 +182,7 @@ func (ref ociReference) PolicyConfigurationNamespaces() []string {
// NOTE: If any kind of signature verification should happen, build an UnparsedImage from the value returned by NewImageSource, // NOTE: If any kind of signature verification should happen, build an UnparsedImage from the value returned by NewImageSource,
// verify that UnparsedImage, and convert it into a real Image via image.FromUnparsedImage. // verify that UnparsedImage, and convert it into a real Image via image.FromUnparsedImage.
func (ref ociReference) NewImage(ctx *types.SystemContext) (types.Image, error) { func (ref ociReference) NewImage(ctx *types.SystemContext) (types.Image, error) {
src, err := newImageSource(ref) src, err := newImageSource(ctx, ref)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -244,7 +244,7 @@ func LoadManifestDescriptor(imgRef types.ImageReference) (imgspecv1.Descriptor,
// NewImageSource returns a types.ImageSource for this reference. // NewImageSource returns a types.ImageSource for this reference.
// The caller must call .Close() on the returned ImageSource. // The caller must call .Close() on the returned ImageSource.
func (ref ociReference) NewImageSource(ctx *types.SystemContext) (types.ImageSource, error) { func (ref ociReference) NewImageSource(ctx *types.SystemContext) (types.ImageSource, error) {
return newImageSource(ref) return newImageSource(ctx, ref)
} }
// NewImageDestination returns a types.ImageDestination for this reference. // NewImageDestination returns a types.ImageDestination for this reference.

View file

@ -46,6 +46,7 @@ type ostreeImageDestination struct {
schema manifestSchema schema manifestSchema
tmpDirPath string tmpDirPath string
blobs map[string]*blobToImport blobs map[string]*blobToImport
digest digest.Digest
} }
// newImageDestination returns an ImageDestination for writing to an existing ostree. // newImageDestination returns an ImageDestination for writing to an existing ostree.
@ -54,7 +55,7 @@ func newImageDestination(ref ostreeReference, tmpDirPath string) (types.ImageDes
if err := ensureDirectoryExists(tmpDirPath); err != nil { if err := ensureDirectoryExists(tmpDirPath); err != nil {
return nil, err return nil, err
} }
return &ostreeImageDestination{ref, "", manifestSchema{}, tmpDirPath, map[string]*blobToImport{}}, nil return &ostreeImageDestination{ref, "", manifestSchema{}, tmpDirPath, map[string]*blobToImport{}, ""}, nil
} }
// Reference returns the reference used to set up this destination. Note that this should directly correspond to user's intent, // Reference returns the reference used to set up this destination. Note that this should directly correspond to user's intent,
@ -238,10 +239,10 @@ func (d *ostreeImageDestination) ReapplyBlob(info types.BlobInfo) (types.BlobInf
// FIXME? This should also receive a MIME type if known, to differentiate between schema versions. // FIXME? This should also receive a MIME type if known, to differentiate between schema versions.
// If the destination is in principle available, refuses this manifest type (e.g. it does not recognize the schema), // If the destination is in principle available, refuses this manifest type (e.g. it does not recognize the schema),
// but may accept a different manifest type, the returned error must be an ManifestTypeRejectedError. // but may accept a different manifest type, the returned error must be an ManifestTypeRejectedError.
func (d *ostreeImageDestination) PutManifest(manifest []byte) error { func (d *ostreeImageDestination) PutManifest(manifestBlob []byte) error {
d.manifest = string(manifest) d.manifest = string(manifestBlob)
if err := json.Unmarshal(manifest, &d.schema); err != nil { if err := json.Unmarshal(manifestBlob, &d.schema); err != nil {
return err return err
} }
@ -250,7 +251,13 @@ func (d *ostreeImageDestination) PutManifest(manifest []byte) error {
return err return err
} }
return ioutil.WriteFile(manifestPath, manifest, 0644) digest, err := manifest.Digest(manifestBlob)
if err != nil {
return err
}
d.digest = digest
return ioutil.WriteFile(manifestPath, manifestBlob, 0644)
} }
func (d *ostreeImageDestination) PutSignatures(signatures [][]byte) error { func (d *ostreeImageDestination) PutSignatures(signatures [][]byte) error {
@ -304,7 +311,7 @@ func (d *ostreeImageDestination) Commit() error {
manifestPath := filepath.Join(d.tmpDirPath, "manifest") manifestPath := filepath.Join(d.tmpDirPath, "manifest")
metadata := []string{fmt.Sprintf("docker.manifest=%s", string(d.manifest))} metadata := []string{fmt.Sprintf("docker.manifest=%s", string(d.manifest)), fmt.Sprintf("docker.digest=%s", string(d.digest))}
err = d.ostreeCommit(repo, fmt.Sprintf("ociimage/%s", d.ref.branchName), manifestPath, metadata) err = d.ostreeCommit(repo, fmt.Sprintf("ociimage/%s", d.ref.branchName), manifestPath, metadata)
_, err = repo.CommitTransaction() _, err = repo.CommitTransaction()

View file

@ -0,0 +1,102 @@
package tlsclientconfig
import (
"crypto/tls"
"io/ioutil"
"net"
"net/http"
"os"
"path/filepath"
"strings"
"time"
"github.com/docker/go-connections/sockets"
"github.com/docker/go-connections/tlsconfig"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
// SetupCertificates opens all .crt, .cert, and .key files in dir and appends / loads certs and key pairs as appropriate to tlsc
func SetupCertificates(dir string, tlsc *tls.Config) error {
logrus.Debugf("Looking for TLS certificates and private keys in %s", dir)
fs, err := ioutil.ReadDir(dir)
if err != nil {
if os.IsNotExist(err) {
return nil
}
if os.IsPermission(err) {
logrus.Debugf("Skipping scan of %s due to permission error: %v", dir, err)
return nil
}
return err
}
for _, f := range fs {
fullPath := filepath.Join(dir, f.Name())
if strings.HasSuffix(f.Name(), ".crt") {
systemPool, err := tlsconfig.SystemCertPool()
if err != nil {
return errors.Wrap(err, "unable to get system cert pool")
}
tlsc.RootCAs = systemPool
logrus.Debugf(" crt: %s", fullPath)
data, err := ioutil.ReadFile(fullPath)
if err != nil {
return err
}
tlsc.RootCAs.AppendCertsFromPEM(data)
}
if strings.HasSuffix(f.Name(), ".cert") {
certName := f.Name()
keyName := certName[:len(certName)-5] + ".key"
logrus.Debugf(" cert: %s", fullPath)
if !hasFile(fs, keyName) {
return errors.Errorf("missing key %s for client certificate %s. Note that CA certificates should use the extension .crt", keyName, certName)
}
cert, err := tls.LoadX509KeyPair(filepath.Join(dir, certName), filepath.Join(dir, keyName))
if err != nil {
return err
}
tlsc.Certificates = append(tlsc.Certificates, cert)
}
if strings.HasSuffix(f.Name(), ".key") {
keyName := f.Name()
certName := keyName[:len(keyName)-4] + ".cert"
logrus.Debugf(" key: %s", fullPath)
if !hasFile(fs, certName) {
return errors.Errorf("missing client certificate %s for key %s", certName, keyName)
}
}
}
return nil
}
func hasFile(files []os.FileInfo, name string) bool {
for _, f := range files {
if f.Name() == name {
return true
}
}
return false
}
// NewTransport Creates a default transport
func NewTransport() *http.Transport {
direct := &net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}
tr := &http.Transport{
Proxy: http.ProxyFromEnvironment,
Dial: direct.Dial,
TLSHandshakeTimeout: 10 * time.Second,
// TODO(dmcgowan): Call close idle connections when complete and use keep alive
DisableKeepAlives: true,
}
proxyDialer, err := sockets.DialerFromEnvironment(direct)
if err == nil {
tr.Dial = proxyDialer.Dial
}
return tr
}

View file

@ -180,13 +180,9 @@ func (s *untrustedSignature) strictUnmarshalJSON(data []byte) error {
} }
s.UntrustedDockerManifestDigest = digest.Digest(digestString) s.UntrustedDockerManifestDigest = digest.Digest(digestString)
if err := paranoidUnmarshalJSONObjectExactFields(identity, map[string]interface{}{ return paranoidUnmarshalJSONObjectExactFields(identity, map[string]interface{}{
"docker-reference": &s.UntrustedDockerReference, "docker-reference": &s.UntrustedDockerReference,
}); err != nil { })
return err
}
return nil
} }
// Sign formats the signature and returns a blob signed using mech and keyIdentity // Sign formats the signature and returns a blob signed using mech and keyIdentity

View file

@ -307,6 +307,14 @@ type SystemContext struct {
// If not "", overrides the default path for the authentication file // If not "", overrides the default path for the authentication file
AuthFilePath string AuthFilePath string
// === OCI.Transport overrides ===
// If not "", a directory containing a CA certificate (ending with ".crt"),
// a client certificate (ending with ".cert") and a client ceritificate key
// (ending with ".key") used when downloading OCI image layers.
OCICertPath string
// Allow downloading OCI image layers over HTTP, or HTTPS with failed TLS verification. Note that this does not affect other TLS connections.
OCIInsecureSkipTLSVerify bool
// === docker.Transport overrides === // === docker.Transport overrides ===
// If not "", a directory containing a CA certificate (ending with ".crt"), // If not "", a directory containing a CA certificate (ending with ".crt"),
// a client certificate (ending with ".cert") and a client ceritificate key // a client certificate (ending with ".cert") and a client ceritificate key