2016-07-19 18:53:57 +00:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
2017-04-10 13:00:24 +00:00
|
|
|
"encoding/base64"
|
|
|
|
"strings"
|
|
|
|
|
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.
|
|
|
|
func (s *Server) PullImage(ctx context.Context, req *pb.PullImageRequest) (*pb.PullImageResponse, error) {
|
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
|
|
|
|
err error
|
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
|
|
|
|
}
|
2017-01-17 11:21:32 +00:00
|
|
|
resp := &pb.PullImageResponse{
|
2017-07-20 08:01:23 +00:00
|
|
|
ImageRef: pulled,
|
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
|
|
|
|
}
|