Skip to content

Commit

Permalink
Merge pull request #1088 from ggriffiths/csi11_resize
Browse files Browse the repository at this point in the history
CSI 1.1 Volume Expand API implementation
  • Loading branch information
ggriffiths authored Apr 26, 2019
2 parents 933cec3 + e3b1620 commit 718a998
Show file tree
Hide file tree
Showing 12 changed files with 1,907 additions and 1,596 deletions.
74 changes: 74 additions & 0 deletions csi/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ func (s *OsdCsiServer) ControllerGetCapabilities(
},
}

// Resizing volumes supported
capExpandVolume := &csi.ControllerServiceCapability{
Type: &csi.ControllerServiceCapability_Rpc{
Rpc: &csi.ControllerServiceCapability_RPC{
Type: csi.ControllerServiceCapability_RPC_EXPAND_VOLUME,
},
},
}

// Creating and deleting snapshots
capCreateDeleteSnapshot := &csi.ControllerServiceCapability{
Type: &csi.ControllerServiceCapability_Rpc{
Expand All @@ -67,6 +76,7 @@ func (s *OsdCsiServer) ControllerGetCapabilities(
return &csi.ControllerGetCapabilitiesResponse{
Capabilities: []*csi.ControllerServiceCapability{
capCreateDeleteVolume,
capExpandVolume,
capCreateDeleteSnapshot,
},
}, nil
Expand Down Expand Up @@ -266,6 +276,17 @@ func (s *OsdCsiServer) CreateVolume(
return nil, status.Error(codes.InvalidArgument, e)
}

// Get parent ID from request: snapshot or volume
if req.GetVolumeContentSource() != nil {
if sourceSnap := req.GetVolumeContentSource().GetSnapshot(); sourceSnap != nil {
source.Parent = sourceSnap.SnapshotId
}

if sourceVol := req.GetVolumeContentSource().GetVolume(); sourceVol != nil {
source.Parent = sourceVol.VolumeId
}
}

// Get Size
if req.GetCapacityRange() != nil && req.GetCapacityRange().GetRequiredBytes() != 0 {
spec.Size = uint64(req.GetCapacityRange().GetRequiredBytes())
Expand Down Expand Up @@ -371,6 +392,59 @@ func (s *OsdCsiServer) DeleteVolume(
return &csi.DeleteVolumeResponse{}, nil
}

// ControllerExpandVolume is a CSI API which resizes a volume
func (s *OsdCsiServer) ControllerExpandVolume(
ctx context.Context,
req *csi.ControllerExpandVolumeRequest,
) (*csi.ControllerExpandVolumeResponse, error) {
if len(req.GetVolumeId()) == 0 {
return nil, status.Error(codes.InvalidArgument, "Volume id must be provided")
} else if req.GetCapacityRange() == nil {
return nil, status.Error(codes.InvalidArgument, "Capacity range must be provided")
} else if req.GetCapacityRange().GetRequiredBytes() < 0 || req.GetCapacityRange().GetLimitBytes() < 0 {
return nil, status.Error(codes.InvalidArgument, "Capacity ranges values cannot be negative")
}

// Get Size
spec := &api.VolumeSpecUpdate{}
newSize := uint64(req.GetCapacityRange().GetRequiredBytes())
spec.SizeOpt = &api.VolumeSpecUpdate_Size{
Size: newSize,
}

// Get grpc connection
conn, err := s.getConn()
if err != nil {
return nil, status.Errorf(
codes.Internal,
"Unable to connect to SDK server: %v", err)
}

// Get secret if any was passed
ctx = s.setupContextWithToken(ctx, req.GetSecrets())

// If the new size is greater than the current size, a volume update
// should be issued. Otherwise, no operation should occur.
volumes := api.NewOpenStorageVolumeClient(conn)

// Update volume with new size
_, err = volumes.Update(ctx, &api.SdkVolumeUpdateRequest{
VolumeId: req.GetVolumeId(),
Spec: spec,
})
if err != nil {
if err == kvdb.ErrNotFound {
return nil, status.Errorf(codes.NotFound, "Volume id %s not found", req.GetVolumeId())
}
return nil, status.Errorf(codes.Internal, "Failed to update volume size: %v", err)
}

return &csi.ControllerExpandVolumeResponse{
CapacityBytes: int64(newSize),
NodeExpansionRequired: false,
}, nil
}

func osdToCsiVolumeInfo(dest *csi.Volume, src *api.Volume) {
dest.VolumeId = src.GetId()
dest.CapacityBytes = int64(src.Spec.GetSize())
Expand Down
125 changes: 120 additions & 5 deletions csi/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1452,8 +1452,19 @@ func TestControllerCreateVolumeFromSnapshot(t *testing.T) {

// Setup mock functions
id := "myid"
snapID := id + "-snap"
gomock.InOrder(
//VolFromName name

// First check on parent
s.MockDriver().
EXPECT().
Enumerate(&api.VolumeLocator{
VolumeIds: []string{mockParentID},
}, nil).
Return([]*api.Volume{&api.Volume{Id: mockParentID}}, nil).
Times(1),

// VolFromName (name)
s.MockDriver().
EXPECT().
Inspect([]string{name}).
Expand All @@ -1462,20 +1473,49 @@ func TestControllerCreateVolumeFromSnapshot(t *testing.T) {

s.MockDriver().
EXPECT().
Enumerate(&api.VolumeLocator{Name: name}, nil).
Enumerate(gomock.Any(), nil).
Return(nil, fmt.Errorf("not found")).
Times(1),

//VolFromName parent
s.MockDriver().
EXPECT().
Create(gomock.Any(), gomock.Any(), gomock.Any()).
Return(id, nil).
Inspect(gomock.Any()).
Return(
[]*api.Volume{&api.Volume{
Id: mockParentID,
}}, nil).
Times(1),

// create
s.MockDriver().
EXPECT().
Snapshot(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
Return(snapID, nil).
Times(1),
s.MockDriver().
EXPECT().
Enumerate(&api.VolumeLocator{
VolumeIds: []string{id},
VolumeIds: []string{snapID},
}, nil).
Return([]*api.Volume{
&api.Volume{
Id: id,
Source: &api.Source{Parent: mockParentID},
},
}, nil).
Times(2),

s.MockDriver().
EXPECT().
Set(gomock.Any(), gomock.Any(), gomock.Any()).
Return(nil).
Times(1),

s.MockDriver().
EXPECT().
Enumerate(&api.VolumeLocator{
VolumeIds: []string{snapID},
}, nil).
Return([]*api.Volume{
&api.Volume{
Expand Down Expand Up @@ -1756,6 +1796,81 @@ func TestControllerDeleteVolume(t *testing.T) {
assert.Nil(t, err)
}

func TestControllerExpandVolumeBadParameter(t *testing.T) {
// Create server and client connection
s := newTestServer(t)
defer s.Stop()
c := csi.NewControllerClient(s.Conn())

_, err := c.ControllerExpandVolume(context.Background(), &csi.ControllerExpandVolumeRequest{})
assert.Error(t, err)
serverError, ok := status.FromError(err)
assert.True(t, ok)
assert.Equal(t, serverError.Code(), codes.InvalidArgument)
assert.Contains(t, serverError.Message(), "id must be provided")

_, err = c.ControllerExpandVolume(context.Background(), &csi.ControllerExpandVolumeRequest{
VolumeId: "id",
})
assert.Error(t, err)
serverError, ok = status.FromError(err)
assert.True(t, ok)
assert.Equal(t, serverError.Code(), codes.InvalidArgument)
assert.Contains(t, serverError.Message(), "Capacity range must be provided")

_, err = c.ControllerExpandVolume(context.Background(), &csi.ControllerExpandVolumeRequest{
VolumeId: "id",
CapacityRange: &csi.CapacityRange{
RequiredBytes: int64(-5),
},
})
assert.Error(t, err)
serverError, ok = status.FromError(err)
assert.True(t, ok)
assert.Equal(t, serverError.Code(), codes.InvalidArgument)
assert.Contains(t, serverError.Message(), "cannot be negative")

}

func TestControllerExpandVolume(t *testing.T) {
// Create server and client connection
s := newTestServer(t)
defer s.Stop()
c := csi.NewControllerClient(s.Conn())

myid := "myid"
gomock.InOrder(
s.MockDriver().
EXPECT().
Enumerate(&api.VolumeLocator{
VolumeIds: []string{myid},
}, nil).
Return([]*api.Volume{
&api.Volume{
Id: myid,
Spec: &api.VolumeSpec{
Size: uint64(50),
},
},
}, nil).
Times(1),
s.MockDriver().
EXPECT().
Set(gomock.Any(), gomock.Any(), gomock.Any()).
Return(nil).
Times(1),
)

_, err := c.ControllerExpandVolume(context.Background(), &csi.ControllerExpandVolumeRequest{
VolumeId: myid,
CapacityRange: &csi.CapacityRange{
RequiredBytes: int64(100),
},
Secrets: map[string]string{authsecrets.SecretTokenKey: systemUserToken},
})
assert.NoError(t, err)
}

func TestControllerCreateSnapshotBadParameters(t *testing.T) {
// Create server and client connection
s := newTestServer(t)
Expand Down
2 changes: 1 addition & 1 deletion csi/csi.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func NewOsdCsiServer(config *OsdCsiServerConfig) (grpcserver.Server, error) {

// Create server
gServer, err := grpcserver.New(&grpcserver.GrpcServerConfig{
Name: "CSI 1.0",
Name: "CSI 1.1",
Net: config.Net,
Address: config.Address,
})
Expand Down
7 changes: 6 additions & 1 deletion csi/csisanity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,13 @@ func TestCSISanity(t *testing.T) {
}

// Start CSI Sanity test
targetPath := "/tmp/mnt/csi"
sanity.Test(t, &sanity.Config{
Address: server.Address(),
TargetPath: "/mnt",
TargetPath: targetPath,
CreateTargetDir: func(p string) (string, error) {
os.MkdirAll(p+"/target", os.FileMode(0755))
return p, nil
},
})
}
7 changes: 7 additions & 0 deletions csi/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ func (s *OsdCsiServer) GetPluginCapabilities(
},
},
},
&csi.PluginCapability{
Type: &csi.PluginCapability_VolumeExpansion_{
VolumeExpansion: &csi.PluginCapability_VolumeExpansion{
Type: csi.PluginCapability_VolumeExpansion_ONLINE,
},
},
},
},
}, nil
}
Expand Down
Loading

0 comments on commit 718a998

Please sign in to comment.