vendor: update c/image to handle text/plain from registries
Signed-off-by: Antonio Murdaca <runcom@redhat.com>
This commit is contained in:
parent
6de90e046a
commit
d551ef4523
10 changed files with 241 additions and 47 deletions
|
@ -12,7 +12,7 @@ github.com/gregjones/httpcache 787624de3eb7bd915c329cba748687a3b22666a6
|
||||||
github.com/json-iterator/go 1.0.0
|
github.com/json-iterator/go 1.0.0
|
||||||
github.com/peterbourgon/diskv v2.0.1
|
github.com/peterbourgon/diskv v2.0.1
|
||||||
github.com/sirupsen/logrus v1.0.0
|
github.com/sirupsen/logrus v1.0.0
|
||||||
github.com/containers/image 3d0304a02154dddc8f97cc833aa0861cea5e9ade
|
github.com/containers/image 701221f0891d76aeac3f25912e6bb9f84e88de1c
|
||||||
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 0d32dfce498e06c132c60dac945081bf44c22464
|
github.com/containers/storage 0d32dfce498e06c132c60dac945081bf44c22464
|
||||||
|
|
5
vendor/github.com/containers/image/copy/manifest.go
generated
vendored
5
vendor/github.com/containers/image/copy/manifest.go
generated
vendored
|
@ -46,6 +46,11 @@ func (ic *imageCopier) determineManifestConversion(destSupportedManifestMIMEType
|
||||||
if err != nil { // This should have been cached?!
|
if err != nil { // This should have been cached?!
|
||||||
return "", nil, errors.Wrap(err, "Error reading manifest")
|
return "", nil, errors.Wrap(err, "Error reading manifest")
|
||||||
}
|
}
|
||||||
|
normalizedSrcType := manifest.NormalizedMIMEType(srcType)
|
||||||
|
if srcType != normalizedSrcType {
|
||||||
|
logrus.Debugf("Source manifest MIME type %s, treating it as %s", srcType, normalizedSrcType)
|
||||||
|
srcType = normalizedSrcType
|
||||||
|
}
|
||||||
|
|
||||||
if forceManifestMIMEType != "" {
|
if forceManifestMIMEType != "" {
|
||||||
destSupportedManifestMIMETypes = []string{forceManifestMIMEType}
|
destSupportedManifestMIMETypes = []string{forceManifestMIMEType}
|
||||||
|
|
2
vendor/github.com/containers/image/directory/directory_dest.go
generated
vendored
2
vendor/github.com/containers/image/directory/directory_dest.go
generated
vendored
|
@ -70,7 +70,7 @@ func newImageDestination(ref dirReference, compress bool) (types.ImageDestinatio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// create version file
|
// create version file
|
||||||
err = ioutil.WriteFile(d.ref.versionPath(), []byte(version), 0755)
|
err = ioutil.WriteFile(d.ref.versionPath(), []byte(version), 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "error creating version file %q", d.ref.versionPath())
|
return nil, errors.Wrapf(err, "error creating version file %q", d.ref.versionPath())
|
||||||
}
|
}
|
||||||
|
|
174
vendor/github.com/containers/image/docker/docker_client.go
generated
vendored
174
vendor/github.com/containers/image/docker/docker_client.go
generated
vendored
|
@ -8,7 +8,10 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -25,10 +28,9 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
dockerHostname = "docker.io"
|
dockerHostname = "docker.io"
|
||||||
|
dockerV1Hostname = "index.docker.io"
|
||||||
dockerRegistry = "registry-1.docker.io"
|
dockerRegistry = "registry-1.docker.io"
|
||||||
|
|
||||||
systemPerHostCertDirPath = "/etc/docker/certs.d"
|
|
||||||
|
|
||||||
resolvedPingV2URL = "%s://%s/v2/"
|
resolvedPingV2URL = "%s://%s/v2/"
|
||||||
resolvedPingV1URL = "%s://%s/v1/_ping"
|
resolvedPingV1URL = "%s://%s/v1/_ping"
|
||||||
tagsPath = "/v2/%s/tags/list"
|
tagsPath = "/v2/%s/tags/list"
|
||||||
|
@ -49,6 +51,7 @@ var (
|
||||||
ErrV1NotSupported = errors.New("can't talk to a V1 docker registry")
|
ErrV1NotSupported = errors.New("can't talk to a V1 docker registry")
|
||||||
// ErrUnauthorizedForCredentials is returned when the status code returned is 401
|
// ErrUnauthorizedForCredentials is returned when the status code returned is 401
|
||||||
ErrUnauthorizedForCredentials = errors.New("unable to retrieve auth token: invalid username/password")
|
ErrUnauthorizedForCredentials = errors.New("unable to retrieve auth token: invalid username/password")
|
||||||
|
systemPerHostCertDirPaths = [2]string{"/etc/containers/certs.d", "/etc/docker/certs.d"}
|
||||||
)
|
)
|
||||||
|
|
||||||
// extensionSignature and extensionSignatureList come from github.com/openshift/origin/pkg/dockerregistry/server/signaturedispatcher.go:
|
// extensionSignature and extensionSignatureList come from github.com/openshift/origin/pkg/dockerregistry/server/signaturedispatcher.go:
|
||||||
|
@ -67,6 +70,7 @@ type extensionSignatureList struct {
|
||||||
|
|
||||||
type bearerToken struct {
|
type bearerToken struct {
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
ExpiresIn int `json:"expires_in"`
|
ExpiresIn int `json:"expires_in"`
|
||||||
IssuedAt time.Time `json:"issued_at"`
|
IssuedAt time.Time `json:"issued_at"`
|
||||||
}
|
}
|
||||||
|
@ -96,6 +100,24 @@ type authScope struct {
|
||||||
actions string
|
actions string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newBearerTokenFromJSONBlob(blob []byte) (*bearerToken, error) {
|
||||||
|
token := new(bearerToken)
|
||||||
|
if err := json.Unmarshal(blob, &token); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if token.Token == "" {
|
||||||
|
token.Token = token.AccessToken
|
||||||
|
}
|
||||||
|
if token.ExpiresIn < minimumTokenLifetimeSeconds {
|
||||||
|
token.ExpiresIn = minimumTokenLifetimeSeconds
|
||||||
|
logrus.Debugf("Increasing token expiration to: %d seconds", token.ExpiresIn)
|
||||||
|
}
|
||||||
|
if token.IssuedAt.IsZero() {
|
||||||
|
token.IssuedAt = time.Now().UTC()
|
||||||
|
}
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
||||||
// this is cloned from docker/go-connections because upstream docker has changed
|
// this is cloned from docker/go-connections because upstream docker has changed
|
||||||
// it and make deps here fails otherwise.
|
// it and make deps here fails otherwise.
|
||||||
// We'll drop this once we upgrade to docker 1.13.x deps.
|
// We'll drop this once we upgrade to docker 1.13.x deps.
|
||||||
|
@ -109,19 +131,42 @@ func serverDefault() *tls.Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
// dockerCertDir returns a path to a directory to be consumed by tlsclientconfig.SetupCertificates() depending on ctx and hostPort.
|
// dockerCertDir returns a path to a directory to be consumed by tlsclientconfig.SetupCertificates() depending on ctx and hostPort.
|
||||||
func dockerCertDir(ctx *types.SystemContext, hostPort string) string {
|
func dockerCertDir(ctx *types.SystemContext, hostPort string) (string, error) {
|
||||||
if ctx != nil && ctx.DockerCertPath != "" {
|
if ctx != nil && ctx.DockerCertPath != "" {
|
||||||
return ctx.DockerCertPath
|
return ctx.DockerCertPath, nil
|
||||||
}
|
}
|
||||||
var hostCertDir string
|
|
||||||
if ctx != nil && ctx.DockerPerHostCertDirPath != "" {
|
if ctx != nil && ctx.DockerPerHostCertDirPath != "" {
|
||||||
hostCertDir = ctx.DockerPerHostCertDirPath
|
return filepath.Join(ctx.DockerPerHostCertDirPath, hostPort), nil
|
||||||
} else if ctx != nil && ctx.RootForImplicitAbsolutePaths != "" {
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
hostCertDir string
|
||||||
|
fullCertDirPath string
|
||||||
|
)
|
||||||
|
for _, systemPerHostCertDirPath := range systemPerHostCertDirPaths {
|
||||||
|
if ctx != nil && ctx.RootForImplicitAbsolutePaths != "" {
|
||||||
hostCertDir = filepath.Join(ctx.RootForImplicitAbsolutePaths, systemPerHostCertDirPath)
|
hostCertDir = filepath.Join(ctx.RootForImplicitAbsolutePaths, systemPerHostCertDirPath)
|
||||||
} else {
|
} else {
|
||||||
hostCertDir = systemPerHostCertDirPath
|
hostCertDir = systemPerHostCertDirPath
|
||||||
}
|
}
|
||||||
return filepath.Join(hostCertDir, hostPort)
|
|
||||||
|
fullCertDirPath = filepath.Join(hostCertDir, hostPort)
|
||||||
|
_, err := os.Stat(fullCertDirPath)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if os.IsPermission(err) {
|
||||||
|
logrus.Debugf("error accessing certs directory due to permissions: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fullCertDirPath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// newDockerClientFromRef returns a new dockerClient instance for refHostname (a host a specified in the Docker image reference, not canonicalized to dockerRegistry)
|
// newDockerClientFromRef returns a new dockerClient instance for refHostname (a host a specified in the Docker image reference, not canonicalized to dockerRegistry)
|
||||||
|
@ -155,7 +200,10 @@ func newDockerClientWithDetails(ctx *types.SystemContext, registry, username, pa
|
||||||
// dockerHostname here, because it is more symmetrical to read the configuration in that case as well, and because
|
// dockerHostname here, because it is more symmetrical to read the configuration in that case as well, and because
|
||||||
// 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, err := dockerCertDir(ctx, hostName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if err := tlsclientconfig.SetupCertificates(certDir, tr.TLSClientConfig); err != nil {
|
if err := tlsclientconfig.SetupCertificates(certDir, tr.TLSClientConfig); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -202,6 +250,100 @@ func CheckAuth(ctx context.Context, sCtx *types.SystemContext, username, passwor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SearchResult holds the information of each matching image
|
||||||
|
// It matches the output returned by the v1 endpoint
|
||||||
|
type SearchResult struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
// StarCount states the number of stars the image has
|
||||||
|
StarCount int `json:"star_count"`
|
||||||
|
IsTrusted bool `json:"is_trusted"`
|
||||||
|
// IsAutomated states whether the image is an automated build
|
||||||
|
IsAutomated bool `json:"is_automated"`
|
||||||
|
// IsOfficial states whether the image is an official build
|
||||||
|
IsOfficial bool `json:"is_official"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchRegistry queries a registry for images that contain "image" in their name
|
||||||
|
// The limit is the max number of results desired
|
||||||
|
// Note: The limit value doesn't work with all registries
|
||||||
|
// for example registry.access.redhat.com returns all the results without limiting it to the limit value
|
||||||
|
func SearchRegistry(ctx context.Context, sCtx *types.SystemContext, registry, image string, limit int) ([]SearchResult, error) {
|
||||||
|
type V2Results struct {
|
||||||
|
// Repositories holds the results returned by the /v2/_catalog endpoint
|
||||||
|
Repositories []string `json:"repositories"`
|
||||||
|
}
|
||||||
|
type V1Results struct {
|
||||||
|
// Results holds the results returned by the /v1/search endpoint
|
||||||
|
Results []SearchResult `json:"results"`
|
||||||
|
}
|
||||||
|
v2Res := &V2Results{}
|
||||||
|
v1Res := &V1Results{}
|
||||||
|
|
||||||
|
// The /v2/_catalog endpoint has been disabled for docker.io therefore the call made to that endpoint will fail
|
||||||
|
// So using the v1 hostname for docker.io for simplicity of implementation and the fact that it returns search results
|
||||||
|
if registry == dockerHostname {
|
||||||
|
registry = dockerV1Hostname
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := newDockerClientWithDetails(sCtx, registry, "", "", "", nil, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error creating new docker client")
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf("trying to talk to v2 search endpoint\n")
|
||||||
|
resp, err := client.makeRequest(ctx, "GET", "/v2/_catalog", nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Debugf("error getting search results from v2 endpoint %q: %v", registry, err)
|
||||||
|
} else {
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
logrus.Debugf("error getting search results from v2 endpoint %q, status code %q", registry, resp.StatusCode)
|
||||||
|
} else {
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(v2Res); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
searchRes := []SearchResult{}
|
||||||
|
for _, repo := range v2Res.Repositories {
|
||||||
|
if strings.Contains(repo, image) {
|
||||||
|
res := SearchResult{
|
||||||
|
Name: repo,
|
||||||
|
}
|
||||||
|
searchRes = append(searchRes, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return searchRes, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set up the query values for the v1 endpoint
|
||||||
|
u := url.URL{
|
||||||
|
Path: "/v1/search",
|
||||||
|
}
|
||||||
|
q := u.Query()
|
||||||
|
q.Set("q", image)
|
||||||
|
q.Set("n", strconv.Itoa(limit))
|
||||||
|
u.RawQuery = q.Encode()
|
||||||
|
|
||||||
|
logrus.Debugf("trying to talk to v1 search endpoint\n")
|
||||||
|
resp, err = client.makeRequest(ctx, "GET", u.String(), nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Debugf("error getting search results from v1 endpoint %q: %v", registry, err)
|
||||||
|
} else {
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
logrus.Debugf("error getting search results from v1 endpoint %q, status code %q", registry, resp.StatusCode)
|
||||||
|
} else {
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(v1Res); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return v1Res.Results, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.Wrapf(err, "couldn't search registry %q", registry)
|
||||||
|
}
|
||||||
|
|
||||||
// makeRequest creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client.
|
// makeRequest creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client.
|
||||||
// The host name and schema is taken from the client or autodetected, and the path is relative to it, i.e. the path usually starts with /v2/.
|
// The host name and schema is taken from the client or autodetected, and the path is relative to it, i.e. the path usually starts with /v2/.
|
||||||
func (c *dockerClient) makeRequest(ctx context.Context, method, path string, headers map[string][]string, stream io.Reader) (*http.Response, error) {
|
func (c *dockerClient) makeRequest(ctx context.Context, method, path string, headers map[string][]string, stream io.Reader) (*http.Response, error) {
|
||||||
|
@ -332,18 +474,8 @@ func (c *dockerClient) getBearerToken(ctx context.Context, realm, service, scope
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var token bearerToken
|
|
||||||
if err := json.Unmarshal(tokenBlob, &token); err != nil {
|
return newBearerTokenFromJSONBlob(tokenBlob)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if token.ExpiresIn < minimumTokenLifetimeSeconds {
|
|
||||||
token.ExpiresIn = minimumTokenLifetimeSeconds
|
|
||||||
logrus.Debugf("Increasing token expiration to: %d seconds", token.ExpiresIn)
|
|
||||||
}
|
|
||||||
if token.IssuedAt.IsZero() {
|
|
||||||
token.IssuedAt = time.Now().UTC()
|
|
||||||
}
|
|
||||||
return &token, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// detectProperties detects various properties of the registry.
|
// detectProperties detects various properties of the registry.
|
||||||
|
|
8
vendor/github.com/containers/image/docker/docker_image_dest.go
generated
vendored
8
vendor/github.com/containers/image/docker/docker_image_dest.go
generated
vendored
|
@ -131,7 +131,7 @@ func (d *dockerImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobI
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode != http.StatusAccepted {
|
if res.StatusCode != http.StatusAccepted {
|
||||||
logrus.Debugf("Error initiating layer upload, response %#v", *res)
|
logrus.Debugf("Error initiating layer upload, response %#v", *res)
|
||||||
return types.BlobInfo{}, errors.Errorf("Error initiating layer upload to %s, status %d", uploadPath, res.StatusCode)
|
return types.BlobInfo{}, errors.Wrapf(client.HandleErrorResponse(res), "Error initiating layer upload to %s", uploadPath)
|
||||||
}
|
}
|
||||||
uploadLocation, err := res.Location()
|
uploadLocation, err := res.Location()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -167,7 +167,7 @@ func (d *dockerImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobI
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode != http.StatusCreated {
|
if res.StatusCode != http.StatusCreated {
|
||||||
logrus.Debugf("Error uploading layer, response %#v", *res)
|
logrus.Debugf("Error uploading layer, response %#v", *res)
|
||||||
return types.BlobInfo{}, errors.Errorf("Error uploading layer to %s, status %d", uploadLocation, res.StatusCode)
|
return types.BlobInfo{}, errors.Wrapf(client.HandleErrorResponse(res), "Error uploading layer to %s", uploadLocation)
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Debugf("Upload of layer %s complete", computedDigest)
|
logrus.Debugf("Upload of layer %s complete", computedDigest)
|
||||||
|
@ -196,7 +196,7 @@ func (d *dockerImageDestination) HasBlob(info types.BlobInfo) (bool, int64, erro
|
||||||
return true, getBlobSize(res), nil
|
return true, getBlobSize(res), nil
|
||||||
case http.StatusUnauthorized:
|
case http.StatusUnauthorized:
|
||||||
logrus.Debugf("... not authorized")
|
logrus.Debugf("... not authorized")
|
||||||
return false, -1, errors.Errorf("not authorized to read from destination repository %s", reference.Path(d.ref.ref))
|
return false, -1, client.HandleErrorResponse(res)
|
||||||
case http.StatusNotFound:
|
case http.StatusNotFound:
|
||||||
logrus.Debugf("... not present")
|
logrus.Debugf("... not present")
|
||||||
return false, -1, nil
|
return false, -1, nil
|
||||||
|
@ -447,7 +447,7 @@ sigExists:
|
||||||
logrus.Debugf("Error body %s", string(body))
|
logrus.Debugf("Error body %s", string(body))
|
||||||
}
|
}
|
||||||
logrus.Debugf("Error uploading signature, status %d, %#v", res.StatusCode, res)
|
logrus.Debugf("Error uploading signature, status %d, %#v", res.StatusCode, res)
|
||||||
return errors.Errorf("Error uploading signature to %s, status %d", path, res.StatusCode)
|
return errors.Wrapf(client.HandleErrorResponse(res), "Error uploading signature to %s", path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
vendor/github.com/containers/image/image/docker_schema1.go
generated
vendored
2
vendor/github.com/containers/image/image/docker_schema1.go
generated
vendored
|
@ -95,7 +95,7 @@ func (m *manifestSchema1) imageInspectInfo() (*types.ImageInspectInfo, error) {
|
||||||
// This is a horribly specific interface, but computing InformationOnly.LayerDiffIDs can be very expensive to compute
|
// This is a horribly specific interface, but computing InformationOnly.LayerDiffIDs can be very expensive to compute
|
||||||
// (most importantly it forces us to download the full layers even if they are already present at the destination).
|
// (most importantly it forces us to download the full layers even if they are already present at the destination).
|
||||||
func (m *manifestSchema1) UpdatedImageNeedsLayerDiffIDs(options types.ManifestUpdateOptions) bool {
|
func (m *manifestSchema1) UpdatedImageNeedsLayerDiffIDs(options types.ManifestUpdateOptions) bool {
|
||||||
return options.ManifestMIMEType == manifest.DockerV2Schema2MediaType
|
return (options.ManifestMIMEType == manifest.DockerV2Schema2MediaType || options.ManifestMIMEType == imgspecv1.MediaTypeImageManifest)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdatedImage returns a types.Image modified according to options.
|
// UpdatedImage returns a types.Image modified according to options.
|
||||||
|
|
10
vendor/github.com/containers/image/image/oci.go
generated
vendored
10
vendor/github.com/containers/image/image/oci.go
generated
vendored
|
@ -149,6 +149,16 @@ func (m *manifestOCI1) UpdatedImage(options types.ManifestUpdateOptions) (types.
|
||||||
|
|
||||||
switch options.ManifestMIMEType {
|
switch options.ManifestMIMEType {
|
||||||
case "": // No conversion, OK
|
case "": // No conversion, OK
|
||||||
|
case manifest.DockerV2Schema1MediaType, manifest.DockerV2Schema1SignedMediaType:
|
||||||
|
// We can't directly convert to V1, but we can transitively convert via a V2 image
|
||||||
|
m2, err := copy.convertToManifestSchema2()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m2.UpdatedImage(types.ManifestUpdateOptions{
|
||||||
|
ManifestMIMEType: options.ManifestMIMEType,
|
||||||
|
InformationOnly: options.InformationOnly,
|
||||||
|
})
|
||||||
case manifest.DockerV2Schema2MediaType:
|
case manifest.DockerV2Schema2MediaType:
|
||||||
return copy.convertToManifestSchema2()
|
return copy.convertToManifestSchema2()
|
||||||
default:
|
default:
|
||||||
|
|
63
vendor/github.com/containers/image/ostree/ostree_dest.go
generated
vendored
63
vendor/github.com/containers/image/ostree/ostree_dest.go
generated
vendored
|
@ -14,25 +14,31 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/containers/image/manifest"
|
"github.com/containers/image/manifest"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/containers/storage/pkg/archive"
|
"github.com/containers/storage/pkg/archive"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
|
selinux "github.com/opencontainers/selinux/go-selinux"
|
||||||
"github.com/ostreedev/ostree-go/pkg/otbuiltin"
|
"github.com/ostreedev/ostree-go/pkg/otbuiltin"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/vbatts/tar-split/tar/asm"
|
"github.com/vbatts/tar-split/tar/asm"
|
||||||
"github.com/vbatts/tar-split/tar/storage"
|
"github.com/vbatts/tar-split/tar/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
// #cgo pkg-config: glib-2.0 gobject-2.0 ostree-1
|
// #cgo pkg-config: glib-2.0 gobject-2.0 ostree-1 libselinux
|
||||||
// #include <glib.h>
|
// #include <glib.h>
|
||||||
// #include <glib-object.h>
|
// #include <glib-object.h>
|
||||||
// #include <gio/gio.h>
|
// #include <gio/gio.h>
|
||||||
// #include <stdlib.h>
|
// #include <stdlib.h>
|
||||||
// #include <ostree.h>
|
// #include <ostree.h>
|
||||||
// #include <gio/ginputstream.h>
|
// #include <gio/ginputstream.h>
|
||||||
|
// #include <selinux/selinux.h>
|
||||||
|
// #include <selinux/label.h>
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
type blobToImport struct {
|
type blobToImport struct {
|
||||||
|
@ -150,7 +156,7 @@ func (d *ostreeImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobI
|
||||||
return types.BlobInfo{Digest: computedDigest, Size: size}, nil
|
return types.BlobInfo{Digest: computedDigest, Size: size}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func fixFiles(dir string, usermode bool) error {
|
func fixFiles(selinuxHnd *C.struct_selabel_handle, root string, dir string, usermode bool) error {
|
||||||
entries, err := ioutil.ReadDir(dir)
|
entries, err := ioutil.ReadDir(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -164,13 +170,43 @@ func fixFiles(dir string, usermode bool) error {
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if selinuxHnd != nil {
|
||||||
|
relPath, err := filepath.Rel(root, fullpath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Handle /exports/hostfs as a special case. Files under this directory are copied to the host,
|
||||||
|
// thus we benefit from maintaining the same SELinux label they would have on the host as we could
|
||||||
|
// use hard links instead of copying the files.
|
||||||
|
relPath = fmt.Sprintf("/%s", strings.TrimPrefix(relPath, "exports/hostfs/"))
|
||||||
|
|
||||||
|
relPathC := C.CString(relPath)
|
||||||
|
defer C.free(unsafe.Pointer(relPathC))
|
||||||
|
var context *C.char
|
||||||
|
|
||||||
|
res, err := C.selabel_lookup_raw(selinuxHnd, &context, relPathC, C.int(info.Mode()&os.ModePerm))
|
||||||
|
if int(res) < 0 && err != syscall.ENOENT {
|
||||||
|
return errors.Wrapf(err, "cannot selabel_lookup_raw %s", relPath)
|
||||||
|
}
|
||||||
|
if int(res) == 0 {
|
||||||
|
defer C.freecon(context)
|
||||||
|
fullpathC := C.CString(fullpath)
|
||||||
|
defer C.free(unsafe.Pointer(fullpathC))
|
||||||
|
res, err = C.lsetfilecon_raw(fullpathC, context)
|
||||||
|
if int(res) < 0 {
|
||||||
|
return errors.Wrapf(err, "cannot setfilecon_raw %s", fullpath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if info.IsDir() {
|
if info.IsDir() {
|
||||||
if usermode {
|
if usermode {
|
||||||
if err := os.Chmod(fullpath, info.Mode()|0700); err != nil {
|
if err := os.Chmod(fullpath, info.Mode()|0700); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = fixFiles(fullpath, usermode)
|
err = fixFiles(selinuxHnd, root, fullpath, usermode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -205,7 +241,7 @@ func generateTarSplitMetadata(output *bytes.Buffer, file string) error {
|
||||||
}
|
}
|
||||||
defer stream.Close()
|
defer stream.Close()
|
||||||
|
|
||||||
gzReader, err := gzip.NewReader(stream)
|
gzReader, err := archive.DecompressStream(stream)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -223,7 +259,7 @@ func generateTarSplitMetadata(output *bytes.Buffer, file string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *ostreeImageDestination) importBlob(repo *otbuiltin.Repo, blob *blobToImport) error {
|
func (d *ostreeImageDestination) importBlob(selinuxHnd *C.struct_selabel_handle, repo *otbuiltin.Repo, blob *blobToImport) error {
|
||||||
ostreeBranch := fmt.Sprintf("ociimage/%s", blob.Digest.Hex())
|
ostreeBranch := fmt.Sprintf("ociimage/%s", blob.Digest.Hex())
|
||||||
destinationPath := filepath.Join(d.tmpDirPath, blob.Digest.Hex(), "root")
|
destinationPath := filepath.Join(d.tmpDirPath, blob.Digest.Hex(), "root")
|
||||||
if err := ensureDirectoryExists(destinationPath); err != nil {
|
if err := ensureDirectoryExists(destinationPath); err != nil {
|
||||||
|
@ -243,7 +279,7 @@ func (d *ostreeImageDestination) importBlob(repo *otbuiltin.Repo, blob *blobToIm
|
||||||
if err := archive.UntarPath(blob.BlobPath, destinationPath); err != nil {
|
if err := archive.UntarPath(blob.BlobPath, destinationPath); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := fixFiles(destinationPath, false); err != nil {
|
if err := fixFiles(selinuxHnd, destinationPath, destinationPath, false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -252,7 +288,7 @@ func (d *ostreeImageDestination) importBlob(repo *otbuiltin.Repo, blob *blobToIm
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := fixFiles(destinationPath, true); err != nil {
|
if err := fixFiles(selinuxHnd, destinationPath, destinationPath, true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -348,6 +384,17 @@ func (d *ostreeImageDestination) Commit() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var selinuxHnd *C.struct_selabel_handle
|
||||||
|
|
||||||
|
if os.Getuid() == 0 && selinux.GetEnabled() {
|
||||||
|
selinuxHnd, err = C.selabel_open(C.SELABEL_CTX_FILE, nil, 0)
|
||||||
|
if selinuxHnd == nil {
|
||||||
|
return errors.Wrapf(err, "cannot open the SELinux DB")
|
||||||
|
}
|
||||||
|
|
||||||
|
defer C.selabel_close(selinuxHnd)
|
||||||
|
}
|
||||||
|
|
||||||
checkLayer := func(hash string) error {
|
checkLayer := func(hash string) error {
|
||||||
blob := d.blobs[hash]
|
blob := d.blobs[hash]
|
||||||
// if the blob is not present in d.blobs then it is already stored in OSTree,
|
// if the blob is not present in d.blobs then it is already stored in OSTree,
|
||||||
|
@ -355,7 +402,7 @@ func (d *ostreeImageDestination) Commit() error {
|
||||||
if blob == nil {
|
if blob == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
err := d.importBlob(repo, blob)
|
err := d.importBlob(selinuxHnd, repo, blob)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
4
vendor/github.com/containers/image/storage/storage_reference.go
generated
vendored
4
vendor/github.com/containers/image/storage/storage_reference.go
generated
vendored
|
@ -71,8 +71,8 @@ func (s *storageReference) resolveImage() (*storage.Image, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if s.id == "" {
|
if s.id == "" {
|
||||||
logrus.Errorf("reference %q does not resolve to an image ID", s.StringWithinTransport())
|
logrus.Debugf("reference %q does not resolve to an image ID", s.StringWithinTransport())
|
||||||
return nil, ErrNoSuchImage
|
return nil, errors.Wrapf(ErrNoSuchImage, "reference %q does not resolve to an image ID", s.StringWithinTransport())
|
||||||
}
|
}
|
||||||
img, err := s.transport.store.Image(s.id)
|
img, err := s.transport.store.Image(s.id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
2
vendor/github.com/containers/image/tarball/tarball_reference.go
generated
vendored
2
vendor/github.com/containers/image/tarball/tarball_reference.go
generated
vendored
|
@ -89,5 +89,5 @@ func (r *tarballReference) DeleteImage(ctx *types.SystemContext) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *tarballReference) NewImageDestination(ctx *types.SystemContext) (types.ImageDestination, error) {
|
func (r *tarballReference) NewImageDestination(ctx *types.SystemContext) (types.ImageDestination, error) {
|
||||||
return nil, fmt.Errorf("destination not implemented yet")
|
return nil, fmt.Errorf(`"tarball:" locations can only be read from, not written to`)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue