diff --git a/virtcontainers/agent.go b/virtcontainers/agent.go index 322cd0f471..7811e06d55 100644 --- a/virtcontainers/agent.go +++ b/virtcontainers/agent.go @@ -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 } diff --git a/virtcontainers/hyperstart_agent.go b/virtcontainers/hyperstart_agent.go index 67aff379f3..1ed48489a7 100644 --- a/virtcontainers/hyperstart_agent.go +++ b/virtcontainers/hyperstart_agent.go @@ -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 +} diff --git a/virtcontainers/hyperstart_agent_test.go b/virtcontainers/hyperstart_agent_test.go index c17f997c25..23a3ef0cdb 100644 --- a/virtcontainers/hyperstart_agent_test.go +++ b/virtcontainers/hyperstart_agent_test.go @@ -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) +} diff --git a/virtcontainers/kata_agent.go b/virtcontainers/kata_agent.go index 333a2d7426..f60a0ee86d 100644 --- a/virtcontainers/kata_agent.go +++ b/virtcontainers/kata_agent.go @@ -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 +} diff --git a/virtcontainers/kata_agent_test.go b/virtcontainers/kata_agent_test.go index b65b4086d5..3b268141b7 100644 --- a/virtcontainers/kata_agent_test.go +++ b/virtcontainers/kata_agent_test.go @@ -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) +} diff --git a/virtcontainers/noop_agent.go b/virtcontainers/noop_agent.go index faaacd0ebc..8406aa78d9 100644 --- a/virtcontainers/noop_agent.go +++ b/virtcontainers/noop_agent.go @@ -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 +} diff --git a/virtcontainers/noop_agent_test.go b/virtcontainers/noop_agent_test.go index 163e443226..feae80110b 100644 --- a/virtcontainers/noop_agent_test.go +++ b/virtcontainers/noop_agent_test.go @@ -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) +}