diff --git a/cmd/client/main.go b/cmd/client/main.go index 74445d46..f129984b 100644 --- a/cmd/client/main.go +++ b/cmd/client/main.go @@ -32,6 +32,7 @@ func main() { app.Commands = []cli.Command{ runtimeVersionCommand, + pullImageCommand, } if err := app.Run(os.Args); err != nil { @@ -39,6 +40,35 @@ func main() { } } +func PullImage(client pb.ImageServiceClient, image string) error { + _, err := client.PullImage(context.Background(), &pb.PullImageRequest{Image: &pb.ImageSpec{Image: &image}}) + if err != nil { + return err + } + return nil +} + +// try this with ./ocic pullimage docker://busybox +var pullImageCommand = cli.Command{ + Name: "pullimage", + Usage: "pull an image", + Action: func(context *cli.Context) error { + // Set up a connection to the server. + conn, err := grpc.Dial(address, grpc.WithInsecure()) + if err != nil { + return fmt.Errorf("Failed to connect: %v", err) + } + defer conn.Close() + client := pb.NewImageServiceClient(conn) + + err = PullImage(client, context.Args().Get(0)) + if err != nil { + return fmt.Errorf("pulling image failed: %v", err) + } + return nil + }, +} + var runtimeVersionCommand = cli.Command{ Name: "runtimeversion", Usage: "get runtime version information", diff --git a/server/image.go b/server/image.go index ababd42e..21c45508 100644 --- a/server/image.go +++ b/server/image.go @@ -1,23 +1,95 @@ package server import ( + "errors" + "os" + + "github.com/containers/image/directory" + "github.com/containers/image/image" + "github.com/containers/image/transports" pb "github.com/kubernetes/kubernetes/pkg/kubelet/api/v1alpha1/runtime" "golang.org/x/net/context" ) // ListImages lists existing images. func (s *Server) ListImages(ctx context.Context, req *pb.ListImagesRequest) (*pb.ListImagesResponse, error) { + // TODO + // containers/storage will take care of this by looking inside /var/lib/ocid/images + // and listing images. return nil, nil } // ImageStatus returns the status of the image. func (s *Server) ImageStatus(ctx context.Context, req *pb.ImageStatusRequest) (*pb.ImageStatusResponse, error) { + // TODO + // containers/storage will take care of this by looking inside /var/lib/ocid/images + // and getting the image status return nil, nil } // PullImage pulls a image with authentication config. func (s *Server) PullImage(ctx context.Context, req *pb.PullImageRequest) (*pb.PullImageResponse, error) { - return nil, nil + img := req.GetImage().GetImage() + if img == "" { + return nil, errors.New("got empty imagespec name") + } + + // TODO(runcom): deal with AuthConfig in req.GetAuth() + + // TODO(mrunalp,runcom): why do we need the SandboxConfig here? + // how do we pull in a specified sandbox? + tr, err := transports.ParseImageName(img) + if err != nil { + return nil, err + } + // TODO(runcom): figure out the ImageContext story in containers/image instead of passing ("", true) + src, err := tr.NewImageSource("", true) + if err != nil { + return nil, err + } + i := image.FromSource(src, nil) + blobs, err := i.BlobDigests() + if err != nil { + return nil, err + } + // TODO: make sure this dir exists? + if err := os.Mkdir("/var/lib/ocid/images/"+tr.StringWithinTransport(), 0755); err != nil { + return nil, err + } + dir, err := directory.NewReference("/var/lib/ocid/images/" + tr.StringWithinTransport()) + if err != nil { + return nil, err + } + // TODO(runcom): figure out the ImageContext story in containers/image instead of passing ("", true) + dest, err := dir.NewImageDestination("", true) + if err != nil { + return nil, err + } + // save blobs (layer + config for docker v2s2, layers only for docker v2s1 [the config is in the manifest]) + for _, b := range blobs { + // TODO(runcom,nalin): we need do-then-commit to later purge on error + r, _, err := src.GetBlob(b) + if err != nil { + return nil, err + } + if err := dest.PutBlob(b, r); err != nil { + r.Close() + return nil, err + } + r.Close() + } + // save manifest + m, _, err := i.Manifest() + if err != nil { + return nil, err + } + if err := dest.PutManifest(m); err != nil { + return nil, err + } + + // TODO: what else do we need here? (Signatures when the story isn't just pulling from docker://) + + return &pb.PullImageResponse{}, nil } // RemoveImage removes the image.