virtcontainers: copy files form host to guest

Files are copied over gRPC and there is no limit in size of the files that
can be copied. Small files are copied using just one gRPC call while big files
are copied by parts.

Signed-off-by: Julio Montes <julio.montes@intel.com>
This commit is contained in:
Julio Montes 2018-12-18 14:45:38 -06:00
parent dcd48a9ca1
commit 62917621c2
7 changed files with 144 additions and 0 deletions

View File

@ -250,4 +250,7 @@ type agent interface {
// setGuestDateTime asks the agent to set guest time to the provided one
setGuestDateTime(time.Time) error
// copyFile copies file from host to container's rootfs
copyFile(src, dst string) error
}

View File

@ -1005,3 +1005,8 @@ func (h *hyper) setGuestDateTime(time.Time) error {
// hyperstart-agent does not support setGuestDateTime
return nil
}
func (h *hyper) copyFile(src, dst string) error {
// hyperstart-agent does not support copyFile
return nil
}

View File

@ -261,3 +261,11 @@ func TestHyperGetAgentUrl(t *testing.T) {
assert.Nil(err)
assert.Empty(url)
}
func TestHyperCopyFile(t *testing.T) {
assert := assert.New(t)
h := &hyper{}
err := h.copyFile("", "")
assert.Nil(err)
}

View File

@ -9,6 +9,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"regexp"
@ -34,6 +35,7 @@ import (
"github.com/sirupsen/logrus"
"github.com/vishvananda/netlink"
"golang.org/x/net/context"
"golang.org/x/sys/unix"
golangGrpc "google.golang.org/grpc"
"google.golang.org/grpc/codes"
grpcStatus "google.golang.org/grpc/status"
@ -63,6 +65,7 @@ var (
shmDir = "shm"
kataEphemeralDevType = "ephemeral"
ephemeralPath = filepath.Join(kataGuestSandboxDir, kataEphemeralDevType)
grpcMaxDataSize = int64(1024 * 1024)
)
// KataAgentConfig is a structure storing information needed
@ -1490,6 +1493,9 @@ func (k *kataAgent) installReqFunc(c *kataclient.AgentClient) {
k.reqHandlers["grpc.GuestDetailsRequest"] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) {
return k.client.GetGuestDetails(ctx, req.(*grpc.GuestDetailsRequest), opts...)
}
k.reqHandlers["grpc.CopyFileRequest"] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) {
return k.client.CopyFile(ctx, req.(*grpc.CopyFileRequest), opts...)
}
k.reqHandlers["grpc.SetGuestDateTimeRequest"] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) {
return k.client.SetGuestDateTime(ctx, req.(*grpc.SetGuestDateTimeRequest), opts...)
}
@ -1708,3 +1714,56 @@ func (k *kataAgent) convertToRoutes(aRoutes []*aTypes.Route) (routes []*types.Ro
return routes
}
func (k *kataAgent) copyFile(src, dst string) error {
var st unix.Stat_t
err := unix.Stat(src, &st)
if err != nil {
return fmt.Errorf("Could not get file %s information: %v", src, err)
}
b, err := ioutil.ReadFile(src)
if err != nil {
return fmt.Errorf("Could not read file %s: %v", src, err)
}
fileSize := int64(len(b))
k.Logger().WithFields(logrus.Fields{
"source": src,
"dest": dst,
}).Debugf("Copying file from host to guest")
cpReq := &grpc.CopyFileRequest{
Path: dst,
DirMode: uint32(dirMode),
FileMode: st.Mode,
FileSize: fileSize,
Uid: int32(st.Uid),
Gid: int32(st.Gid),
}
// Copy file by parts if it's needed
remainingBytes := fileSize
offset := int64(0)
for remainingBytes > 0 {
bytesToCopy := int64(len(b))
if bytesToCopy > grpcMaxDataSize {
bytesToCopy = grpcMaxDataSize
}
cpReq.Data = b[:bytesToCopy]
cpReq.Offset = offset
if _, err = k.sendReq(cpReq); err != nil {
return fmt.Errorf("Could not send CopyFile request: %v", err)
}
b = b[bytesToCopy:]
remainingBytes -= bytesToCopy
offset += grpcMaxDataSize
}
return nil
}

View File

@ -247,6 +247,10 @@ func (p *gRPCProxy) SetGuestDateTime(ctx context.Context, req *pb.SetGuestDateTi
return &gpb.Empty{}, nil
}
func (p *gRPCProxy) CopyFile(ctx context.Context, req *pb.CopyFileRequest) (*gpb.Empty, error) {
return &gpb.Empty{}, nil
}
func gRPCRegister(s *grpc.Server, srv interface{}) {
switch g := srv.(type) {
case *gRPCProxy:
@ -844,3 +848,55 @@ func TestKataGetAgentUrl(t *testing.T) {
assert.NotEmpty(url)
}
func TestKataCopyFile(t *testing.T) {
assert := assert.New(t)
impl := &gRPCProxy{}
proxy := mock.ProxyGRPCMock{
GRPCImplementer: impl,
GRPCRegister: gRPCRegister,
}
sockDir, err := testGenerateKataProxySockDir()
assert.NoError(err)
defer os.RemoveAll(sockDir)
testKataProxyURL := fmt.Sprintf(testKataProxyURLTempl, sockDir)
err = proxy.Start(testKataProxyURL)
assert.NoError(err)
defer proxy.Stop()
k := &kataAgent{
state: KataAgentState{
URL: testKataProxyURL,
},
}
err = k.copyFile("/abc/xyz/123", "/tmp")
assert.Error(err)
src, err := ioutil.TempFile("", "src")
assert.NoError(err)
defer os.Remove(src.Name())
data := []byte("abcdefghi123456789")
_, err = src.Write(data)
assert.NoError(err)
assert.NoError(src.Close())
dst, err := ioutil.TempFile("", "dst")
assert.NoError(err)
assert.NoError(dst.Close())
defer os.Remove(dst.Name())
orgGrpcMaxDataSize := grpcMaxDataSize
grpcMaxDataSize = 1
defer func() {
grpcMaxDataSize = orgGrpcMaxDataSize
}()
err = k.copyFile(src.Name(), dst.Name())
assert.NoError(err)
}

View File

@ -209,3 +209,8 @@ func (n *noopAgent) getGuestDetails(*grpc.GuestDetailsRequest) (*grpc.GuestDetai
func (n *noopAgent) setGuestDateTime(time.Time) error {
return nil
}
// copyFile is the Noop agent copy file. It does nothing.
func (n *noopAgent) copyFile(src, dst string) error {
return nil
}

View File

@ -272,3 +272,11 @@ func TestNoopGetAgentUrl(t *testing.T) {
assert.Nil(err)
assert.Empty(url)
}
func TestNoopCopyFile(t *testing.T) {
assert := assert.New(t)
n := &noopAgent{}
err := n.copyFile("", "")
assert.Nil(err)
}