2016-07-19 18:53:57 +00:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
2017-04-10 13:00:24 +00:00
|
|
|
"encoding/base64"
|
|
|
|
"strings"
|
2017-11-09 10:10:35 +00:00
|
|
|
"time"
|
2017-04-10 13:00:24 +00:00
|
|
|
|
2016-10-18 14:48:33 +00:00
|
|
|
"github.com/containers/image/copy"
|
2017-04-10 13:00:24 +00:00
|
|
|
"github.com/containers/image/types"
|
2017-11-09 18:15:56 +00:00
|
|
|
"github.com/kubernetes-incubator/cri-o/pkg/storage"
|
2017-08-05 11:40:46 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
2016-07-19 18:53:57 +00:00
|
|
|
"golang.org/x/net/context"
|
2017-08-04 11:13:19 +00:00
|
|
|
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
2016-07-19 18:53:57 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// PullImage pulls a image with authentication config.
|
2017-11-09 10:10:35 +00:00
|
|
|
func (s *Server) PullImage(ctx context.Context, req *pb.PullImageRequest) (resp *pb.PullImageResponse, err error) {
|
|
|
|
const operation = "pull_image"
|
|
|
|
defer func() {
|
|
|
|
recordOperation(operation, time.Now())
|
|
|
|
recordError(operation, err)
|
|
|
|
}()
|
|
|
|
|
2016-10-18 14:48:33 +00:00
|
|
|
logrus.Debugf("PullImageRequest: %+v", req)
|
|
|
|
// TODO: what else do we need here? (Signatures when the story isn't just pulling from docker://)
|
|
|
|
image := ""
|
|
|
|
img := req.GetImage()
|
|
|
|
if img != nil {
|
2017-02-03 14:41:28 +00:00
|
|
|
image = img.Image
|
2016-07-20 08:42:55 +00:00
|
|
|
}
|
2017-06-07 17:54:02 +00:00
|
|
|
|
2017-04-10 13:00:24 +00:00
|
|
|
var (
|
2017-07-20 08:01:23 +00:00
|
|
|
images []string
|
|
|
|
pulled string
|
2017-04-10 13:00:24 +00:00
|
|
|
)
|
2017-07-20 08:01:23 +00:00
|
|
|
images, err = s.StorageImageServer().ResolveNames(image)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for _, img := range images {
|
|
|
|
var (
|
|
|
|
username string
|
|
|
|
password string
|
|
|
|
)
|
|
|
|
if req.GetAuth() != nil {
|
|
|
|
username = req.GetAuth().Username
|
|
|
|
password = req.GetAuth().Password
|
|
|
|
if req.GetAuth().Auth != "" {
|
|
|
|
username, password, err = decodeDockerAuth(req.GetAuth().Auth)
|
|
|
|
if err != nil {
|
|
|
|
logrus.Debugf("error decoding authentication for image %s: %v", img, err)
|
|
|
|
continue
|
|
|
|
}
|
2017-04-10 13:00:24 +00:00
|
|
|
}
|
|
|
|
}
|
2017-07-20 08:01:23 +00:00
|
|
|
options := ©.Options{
|
|
|
|
SourceCtx: &types.SystemContext{},
|
|
|
|
}
|
2017-08-15 02:15:01 +00:00
|
|
|
// Specifying a username indicates the user intends to send authentication to the registry.
|
2017-07-20 08:01:23 +00:00
|
|
|
if username != "" {
|
|
|
|
options.SourceCtx = &types.SystemContext{
|
|
|
|
DockerAuthConfig: &types.DockerAuthConfig{
|
|
|
|
Username: username,
|
|
|
|
Password: password,
|
|
|
|
},
|
|
|
|
}
|
2017-04-10 13:00:24 +00:00
|
|
|
}
|
2017-06-07 17:54:02 +00:00
|
|
|
|
2017-07-20 08:01:23 +00:00
|
|
|
var canPull bool
|
|
|
|
canPull, err = s.StorageImageServer().CanPull(img, options)
|
|
|
|
if err != nil && !canPull {
|
|
|
|
logrus.Debugf("error checking image %s: %v", img, err)
|
|
|
|
continue
|
|
|
|
}
|
2017-06-07 17:54:02 +00:00
|
|
|
|
2017-07-20 08:01:23 +00:00
|
|
|
// let's be smart, docker doesn't repull if image already exists.
|
2017-11-09 18:15:56 +00:00
|
|
|
var storedImage *storage.ImageResult
|
|
|
|
storedImage, err = s.StorageImageServer().ImageStatus(s.ImageContext(), img)
|
2017-07-20 08:01:23 +00:00
|
|
|
if err == nil {
|
2017-11-09 18:15:56 +00:00
|
|
|
tmpImg, err := s.StorageImageServer().PrepareImage(s.ImageContext(), img, options)
|
|
|
|
if err == nil {
|
|
|
|
tmpImgConfigDigest := tmpImg.ConfigInfo().Digest
|
|
|
|
if tmpImgConfigDigest.String() == "" {
|
|
|
|
// this means we are playing with a schema1 image, in which
|
|
|
|
// case, we're going to repull the image in any case
|
|
|
|
logrus.Debugf("image config digest is empty, re-pulling image")
|
|
|
|
} else if tmpImgConfigDigest.String() == storedImage.ConfigDigest.String() {
|
|
|
|
logrus.Debugf("image %s already in store, skipping pull", img)
|
|
|
|
pulled = img
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
logrus.Debugf("image in store has different ID, re-pulling %s", img)
|
2017-07-20 08:01:23 +00:00
|
|
|
}
|
2017-06-07 17:54:02 +00:00
|
|
|
|
2017-07-20 08:01:23 +00:00
|
|
|
_, err = s.StorageImageServer().PullImage(s.ImageContext(), img, options)
|
|
|
|
if err != nil {
|
|
|
|
logrus.Debugf("error pulling image %s: %v", img, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
pulled = img
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if pulled == "" && err != nil {
|
2016-07-20 08:42:55 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
Return image references from the storage package
The image's canonical reference is a name with a digest of the image's
manifest, so in imageService.ImageStatus() and
imageService.ListImages(), divide the image's name list into tagged and
digested values, and if we have names, add canonical versions.
In Server.ContainerStatus(), return the image name as it was given to us
as the image, and the image digested reference as the image reference.
In Server.ListImages(), be sure to only return tagged names in the
RepoTags field. In Server.ImageStatus(), also return canonical
references in the RepoDigests field.
In Server.PullImage(), be sure that we consistently return the same
image reference for an image, whether we ended up pulling it or not.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2017-07-12 16:41:38 +00:00
|
|
|
status, err := s.StorageImageServer().ImageStatus(s.ImageContext(), pulled)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
imageRef := status.ID
|
|
|
|
if len(status.RepoDigests) > 0 {
|
|
|
|
imageRef = status.RepoDigests[0]
|
|
|
|
}
|
2017-11-09 10:10:35 +00:00
|
|
|
resp = &pb.PullImageResponse{
|
Return image references from the storage package
The image's canonical reference is a name with a digest of the image's
manifest, so in imageService.ImageStatus() and
imageService.ListImages(), divide the image's name list into tagged and
digested values, and if we have names, add canonical versions.
In Server.ContainerStatus(), return the image name as it was given to us
as the image, and the image digested reference as the image reference.
In Server.ListImages(), be sure to only return tagged names in the
RepoTags field. In Server.ImageStatus(), also return canonical
references in the RepoDigests field.
In Server.PullImage(), be sure that we consistently return the same
image reference for an image, whether we ended up pulling it or not.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2017-07-12 16:41:38 +00:00
|
|
|
ImageRef: imageRef,
|
2017-01-17 11:21:32 +00:00
|
|
|
}
|
2016-10-18 14:48:33 +00:00
|
|
|
logrus.Debugf("PullImageResponse: %+v", resp)
|
|
|
|
return resp, nil
|
2016-07-19 18:53:57 +00:00
|
|
|
}
|
2017-04-10 13:00:24 +00:00
|
|
|
|
|
|
|
func decodeDockerAuth(s string) (string, string, error) {
|
|
|
|
decoded, err := base64.StdEncoding.DecodeString(s)
|
|
|
|
if err != nil {
|
|
|
|
return "", "", err
|
|
|
|
}
|
|
|
|
parts := strings.SplitN(string(decoded), ":", 2)
|
|
|
|
if len(parts) != 2 {
|
|
|
|
// if it's invalid just skip, as docker does
|
|
|
|
return "", "", nil
|
|
|
|
}
|
|
|
|
user := parts[0]
|
|
|
|
password := strings.Trim(parts[1], "\x00")
|
|
|
|
return user, password, nil
|
|
|
|
}
|