Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add volume expansion support #87

Merged
merged 2 commits into from
Aug 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions examples/csi-storageclass.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ metadata:
provisioner: hostpath.csi.k8s.io
reclaimPolicy: Delete
volumeBindingMode: Immediate
allowVolumeExpansion: true
37 changes: 37 additions & 0 deletions pkg/hostpath/controllerserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ func NewControllerServer(ephemeral bool) *controllerServer {
csi.ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT,
csi.ControllerServiceCapability_RPC_LIST_SNAPSHOTS,
csi.ControllerServiceCapability_RPC_CLONE_VOLUME,
csi.ControllerServiceCapability_RPC_EXPAND_VOLUME,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deployment spec should be updated to add the external-resizer sidecar

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to release a new version of the driver before updating the manifest, otherwise we'll break the deployment script.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created #90 for that.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, how does this break the deployment script? The pull jobs running in this PR should be testing a custom built image of the hostpath driver that includes your change.

And before we tag a new image, when the CI jobs run, it will run the older version of the driver that doesn't implement expand volume. Does that break the resizer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's correct, the resizer fails to start because the version of driver specified in the manifest doesn't implement expand volume.

}),
}
}
Expand Down Expand Up @@ -457,6 +458,42 @@ func (cs *controllerServer) ListSnapshots(ctx context.Context, req *csi.ListSnap
}, nil
}

func (cs *controllerServer) ControllerExpandVolume(ctx context.Context, req *csi.ControllerExpandVolumeRequest) (*csi.ControllerExpandVolumeResponse, error) {

volID := req.GetVolumeId()
if len(volID) == 0 {
return nil, status.Error(codes.InvalidArgument, "Volume ID missing in request")
}

capRange := req.GetCapacityRange()
if capRange == nil {
return nil, status.Error(codes.InvalidArgument, "Capacity range not provided")
}

capacity := int64(capRange.GetRequiredBytes())
if capacity >= maxStorageCapacity {
return nil, status.Errorf(codes.OutOfRange, "Requested capacity %d exceeds maximum allowed %d", capacity, maxStorageCapacity)
}

exVol, err := getVolumeByID(volID)
if err != nil {
// Assume not found error
return nil, status.Errorf(codes.NotFound, "Could not get volume %s: %v", volID, err)
}

if exVol.VolSize < capacity {
exVol.VolSize = capacity
if err := updateHostpathVolume(volID, exVol); err != nil {
return nil, status.Errorf(codes.Internal, "Could not update volume %s: %v", volID, err)
}
}

return &csi.ControllerExpandVolumeResponse{
CapacityBytes: exVol.VolSize,
NodeExpansionRequired: true,
}, nil
}

func convertSnapshot(snap hostPathSnapshot) *csi.ListSnapshotsResponse {
entries := []*csi.ListSnapshotsResponse_Entry{
{
Expand Down
12 changes: 12 additions & 0 deletions pkg/hostpath/hostpath.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,18 @@ func createHostpathVolume(volID, name string, cap int64, volAccessType accessTyp
return &hostpathVol, nil
}

// updateVolume updates the existing hostpath volume.
func updateHostpathVolume(volID string, volume hostPathVolume) error {
glog.V(4).Infof("updating hostpath volume: %s", volID)

if _, err := getVolumeByID(volID); err != nil {
return err
}

hostPathVolumes[volID] = volume
return nil
}

// deleteVolume deletes the directory for the hostpath volume.
func deleteHostpathVolume(volID string) error {
glog.V(4).Infof("deleting hostpath volume: %s", volID)
Expand Down
47 changes: 47 additions & 0 deletions pkg/hostpath/nodeserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,10 +277,57 @@ func (ns *nodeServer) NodeGetCapabilities(ctx context.Context, req *csi.NodeGetC
},
},
},
{
Type: &csi.NodeServiceCapability_Rpc{
Rpc: &csi.NodeServiceCapability_RPC{
Type: csi.NodeServiceCapability_RPC_EXPAND_VOLUME,
},
},
},
},
}, nil
}

func (ns *nodeServer) NodeGetVolumeStats(ctx context.Context, in *csi.NodeGetVolumeStatsRequest) (*csi.NodeGetVolumeStatsResponse, error) {
return nil, status.Error(codes.Unimplemented, "")
}

// NodeExpandVolume is only implemented so the driver can be used for e2e testing.
func (ns *nodeServer) NodeExpandVolume(ctx context.Context, req *csi.NodeExpandVolumeRequest) (*csi.NodeExpandVolumeResponse, error) {

volID := req.GetVolumeId()
if len(volID) == 0 {
return nil, status.Error(codes.InvalidArgument, "Volume ID not provided")
}

vol, err := getVolumeByID(volID)
if err != nil {
// Assume not found error
return nil, status.Errorf(codes.NotFound, "Could not get volume %s: %v", volID, err)
}

volPath := req.GetVolumePath()
if len(volPath) == 0 {
return nil, status.Error(codes.InvalidArgument, "Volume path not provided")
}

info, err := os.Stat(volPath)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "Could not get file information from %s: %v", volPath, err)
}

switch m := info.Mode(); {
case m.IsDir():
if vol.VolAccessType != mountAccess {
return nil, status.Errorf(codes.InvalidArgument, "Volume %s is not a directory", volID)
}
case m&os.ModeDevice != 0:
if vol.VolAccessType != blockAccess {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The block and character access are two different types right?

return nil, status.Errorf(codes.InvalidArgument, "Volume %s is not a block device", volID)
}
default:
return nil, status.Errorf(codes.InvalidArgument, "Volume %s is invalid", volID)
}

return &csi.NodeExpandVolumeResponse{}, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should also verify if provided volume_path was correct. This can be done by - comparing supplied volume_path and target_path associated with the volume.

Loading