Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
internal: add sidecar service and client setup for volume group
Browse files Browse the repository at this point in the history
added sidecar service and client setup code for volume group
feature

Signed-off-by: Nikhil-Ladha <nikhilladha1999@gmail.com>
Nikhil-Ladha committed Jul 17, 2024
1 parent a4d5bd1 commit 63e7aec
Showing 5 changed files with 405 additions and 0 deletions.
51 changes: 51 additions & 0 deletions internal/client/fake/volumegroup-client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
Copyright 2024 The Kubernetes-CSI-Addons Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package fake

import "github.com/csi-addons/kubernetes-csi-addons/internal/proto"

// VolumeGroupClient to fake grouping operations.
type VolumeGroupClient struct {
// CreateVolumeGroupMock mocks CreateVolumeGroup RPC call.
CreateVolumeGroupMock func(volumeGroupName string, volumeIDs []string) (*proto.CreateVolumeGroupResponse, error)
// ModifyVolumeGroupMembershipMock mock ModifyVolumeGroupMembership RPC call.
ModifyVolumeGroupMembershipMock func(volumeGroupID string, volumeIDs []string) (*proto.ModifyVolumeGroupMembershipResponse, error)
// DeleteVolumeGroupMock mocks DeleteVolumeGroup RPC call.
DeleteVolumeGroupMock func(volumeGroupID string) (*proto.DeleteVolumeGroupResponse, error)
// ControllerGetVolumeGroupMock mocks ControllerGetVolumeGroup RPC call.
ControllerGetVolumeGroupMock func(volumeGroupID string) (*proto.ControllerGetVolumeGroupResponse, error)
}

// CreateVolumeGroup calls CreateVolumeGroupMock mock function.
func (vg *VolumeGroupClient) CreateVolumeGroup(volumeGroupName string, volumeIDs []string) (*proto.CreateVolumeGroupResponse, error) {
return vg.CreateVolumeGroupMock(volumeGroupName, volumeIDs)
}

// ModifyVolumeGroupMembership calls ModifyVolumeGroupMembership mock function.
func (vg *VolumeGroupClient) ModifyVolumeGroupMembership(volumeGroupID string, volumeIDs []string) (*proto.ModifyVolumeGroupMembershipResponse, error) {
return vg.ModifyVolumeGroupMembershipMock(volumeGroupID, volumeIDs)
}

// DeleteVolumeGroup calls DeleteVolumeGroup mock function.
func (vg *VolumeGroupClient) DeleteVolumeGroup(volumeGroupID string) (*proto.DeleteVolumeGroupResponse, error) {
return vg.DeleteVolumeGroupMock(volumeGroupID)
}

// ControllerGetVolumeGroup calls ControllerGetVolumeGroup mock function.
func (vg *VolumeGroupClient) ControllerGetVolumeGroup(volumeGroupID string) (*proto.ControllerGetVolumeGroupResponse, error) {
return vg.ControllerGetVolumeGroupMock(volumeGroupID)
}
99 changes: 99 additions & 0 deletions internal/client/volumegroup-client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
Copyright 2024 The Kubernetes-CSI-Addons Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package client

import (
"context"
"time"

"github.com/csi-addons/kubernetes-csi-addons/internal/proto"

"google.golang.org/grpc"
)

type volumeGroupClient struct {
client proto.ControllerClient
timeout time.Duration
}

// VolumeGroup holds the methods required for volume grouping.
type VolumeGroup interface {
// CreateVolumeGroup RPC call to create a volume group.
CreateVolumeGroup(volumeGroupName string, volumeIDs []string) (*proto.CreateVolumeGroupResponse, error)
// ModifyVolumeGroupMembership RPC call to modify the volume group.
ModifyVolumeGroupMembership(volumeGroupID string, volumeIDs []string) (*proto.ModifyVolumeGroupMembershipResponse, error)
// DeleteVolumeGroup RPC call to delete the volume group.
DeleteVolumeGroup(volumeGroupID string) (*proto.DeleteVolumeGroupResponse, error)
// ControllerGetVolumeGroup RPC call to fetch the volume group.
ControllerGetVolumeGroup(volumeGroupID string) (*proto.ControllerGetVolumeGroupResponse, error)
}

// NewReplicationClient returns VolumeGroup interface which has the RPC
// calls for grouping.
func NewVolumeGroupClient(cc *grpc.ClientConn, timeout time.Duration) VolumeGroup {
return &volumeGroupClient{client: proto.NewControllerClient(cc), timeout: timeout}
}

func (vg *volumeGroupClient) CreateVolumeGroup(volumeGroupName string, volumeIDs []string) (*proto.CreateVolumeGroupResponse, error) {
req := &proto.CreateVolumeGroupRequest{
Name: volumeGroupName,
VolumeIds: volumeIDs,
}

createCtx, cancel := context.WithTimeout(context.Background(), vg.timeout)
defer cancel()
resp, err := vg.client.CreateVolumeGroup(createCtx, req)

return resp, err
}

func (vg *volumeGroupClient) ModifyVolumeGroupMembership(volumeGroupID string, volumeIDs []string) (*proto.ModifyVolumeGroupMembershipResponse, error) {
req := &proto.ModifyVolumeGroupMembershipRequest{
VolumeGroupId: volumeGroupID,
VolumeIds: volumeIDs,
}

createCtx, cancel := context.WithTimeout(context.Background(), vg.timeout)
defer cancel()
resp, err := vg.client.ModifyVolumeGroupMembership(createCtx, req)

return resp, err
}

func (vg *volumeGroupClient) DeleteVolumeGroup(volumeGroupID string) (*proto.DeleteVolumeGroupResponse, error) {
req := &proto.DeleteVolumeGroupRequest{
VolumeGroupId: volumeGroupID,
}

createCtx, cancel := context.WithTimeout(context.Background(), vg.timeout)
defer cancel()
resp, err := vg.client.DeleteVolumeGroup(createCtx, req)

return resp, err
}

func (vg *volumeGroupClient) ControllerGetVolumeGroup(volumeGroupID string) (*proto.ControllerGetVolumeGroupResponse, error) {
req := &proto.ControllerGetVolumeGroupRequest{
VolumeGroupId: volumeGroupID,
}

createCtx, cancel := context.WithTimeout(context.Background(), vg.timeout)
defer cancel()
resp, err := vg.client.ControllerGetVolumeGroup(createCtx, req)

return resp, err
}
125 changes: 125 additions & 0 deletions internal/client/volumegroup-client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
Copyright 2024 The Kubernetes-CSI-Addons Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package client

import (
"errors"
"testing"

"github.com/csi-addons/kubernetes-csi-addons/internal/client/fake"
"github.com/csi-addons/kubernetes-csi-addons/internal/proto"

"github.com/stretchr/testify/assert"
)

func TestCreateVolumeGroup(t *testing.T) {
t.Parallel()
mockedCreateVolumeGroup := &fake.VolumeGroupClient{
CreateVolumeGroupMock: func(volumeGroupName string, volumeIDs []string) (*proto.CreateVolumeGroupResponse, error) {
return &proto.CreateVolumeGroupResponse{}, nil
},
}
client := mockedCreateVolumeGroup
resp, err := client.CreateVolumeGroup("", nil)
assert.Equal(t, &proto.CreateVolumeGroupResponse{}, resp)
assert.Nil(t, err)

// return error
mockedCreateVolumeGroup = &fake.VolumeGroupClient{
CreateVolumeGroupMock: func(volumeGroupName string, volumeIDs []string) (*proto.CreateVolumeGroupResponse, error) {
return nil, errors.New("failed to create volume group")
},
}
client = mockedCreateVolumeGroup
resp, err = client.CreateVolumeGroup("", nil)
assert.Nil(t, resp)
assert.NotNil(t, err)
}

func TestDeleteVolumeGroup(t *testing.T) {
t.Parallel()
mockedDeleteVolumeGroup := &fake.VolumeGroupClient{
DeleteVolumeGroupMock: func(volumeGroupID string) (*proto.DeleteVolumeGroupResponse, error) {
return &proto.DeleteVolumeGroupResponse{}, nil
},
}
client := mockedDeleteVolumeGroup
resp, err := client.DeleteVolumeGroup("")
assert.Equal(t, &proto.DeleteVolumeGroupResponse{}, resp)
assert.Nil(t, err)

// return error
mockedDeleteVolumeGroup = &fake.VolumeGroupClient{
DeleteVolumeGroupMock: func(volumeGroupID string) (*proto.DeleteVolumeGroupResponse, error) {
return nil, errors.New("failed to delete volume group")
},
}
client = mockedDeleteVolumeGroup
resp, err = client.DeleteVolumeGroup("")
assert.Nil(t, resp)
assert.NotNil(t, err)
}

func TestModifyVolumeGroupMembership(t *testing.T) {
t.Parallel()
// return success response
mockedModifyVolumeGroup := &fake.VolumeGroupClient{
ModifyVolumeGroupMembershipMock: func(volumeGroupID string, volumeIDs []string) (*proto.ModifyVolumeGroupMembershipResponse, error) {
return &proto.ModifyVolumeGroupMembershipResponse{}, nil
},
}
client := mockedModifyVolumeGroup
resp, err := client.ModifyVolumeGroupMembership("", nil)
assert.Equal(t, &proto.ModifyVolumeGroupMembershipResponse{}, resp)
assert.Nil(t, err)

// return error
mockedModifyVolumeGroup = &fake.VolumeGroupClient{
ModifyVolumeGroupMembershipMock: func(volumeGroupID string, volumeIDs []string) (*proto.ModifyVolumeGroupMembershipResponse, error) {
return nil, errors.New("failed to modify volume group")
},
}
client = mockedModifyVolumeGroup
resp, err = client.ModifyVolumeGroupMembership("", nil)
assert.Nil(t, resp)
assert.NotNil(t, err)
}

func TestControllerGetVolumeGroup(t *testing.T) {
t.Parallel()
// return success response
mockedGetVolumeGroup := &fake.VolumeGroupClient{
ControllerGetVolumeGroupMock: func(volumeGroupID string) (*proto.ControllerGetVolumeGroupResponse, error) {
return &proto.ControllerGetVolumeGroupResponse{}, nil
},
}
client := mockedGetVolumeGroup
resp, err := client.ControllerGetVolumeGroup("")
assert.Equal(t, &proto.ControllerGetVolumeGroupResponse{}, resp)
assert.Nil(t, err)

// return error
mockedGetVolumeGroup = &fake.VolumeGroupClient{
ControllerGetVolumeGroupMock: func(volumeGroupID string) (*proto.ControllerGetVolumeGroupResponse, error) {
return nil, errors.New("failed to get volume group")
},
}
client = mockedGetVolumeGroup
resp, err = client.ControllerGetVolumeGroup("")
assert.Nil(t, resp)
assert.NotNil(t, err)
}
129 changes: 129 additions & 0 deletions internal/sidecar/service/volumegroup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
Copyright 2024 The Kubernetes-CSI-Addons Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package service

import (
"context"

"github.com/csi-addons/kubernetes-csi-addons/internal/proto"

"google.golang.org/grpc"
"k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
)

// VolumeGroupServer struct of sidecar with supported methods of proto
// volumegroup server spec and also containing volumegroup
// controller client to csi driver.
type VolumeGroupServer struct {
proto.UnimplementedControllerServer
controllerClient proto.ControllerClient
kubeClient *kubernetes.Clientset
}

// NewVolumeGroupServer creates a new VolumeGroupServer which handles the proto.VolumeGroup
// Service requests.
func NewVolumeGroupServer(c *grpc.ClientConn, kc *kubernetes.Clientset) *VolumeGroupServer {
return &VolumeGroupServer{
controllerClient: proto.NewControllerClient(c),
kubeClient: kc,
}
}

// RegisterService registers service with the server.
func (vg *VolumeGroupServer) RegisterService(server grpc.ServiceRegistrar) {
proto.RegisterControllerServer(server, vg)
}

// CreateVolumeGroup calls CSI-Addons CreateVolumeGroup service.
func (vg *VolumeGroupServer) CreateVolumeGroup(
ctx context.Context,
req *proto.CreateVolumeGroupRequest) (*proto.CreateVolumeGroupResponse, error) {
// Get the VolumeGroup name and volumeIds from the request
vgReq := &proto.CreateVolumeGroupRequest{
Name: req.GetName(),
VolumeIds: req.GetVolumeIds(),
}

vgResp, err := vg.controllerClient.CreateVolumeGroup(ctx, vgReq)
if err != nil {
klog.Errorf("Failed to create volume group: %v", err)
return nil, err
}

return &proto.CreateVolumeGroupResponse{
VolumeGroupId: vgResp.GetVolumeGroupId(),
}, nil
}

// ModifyVolumeGroupMembership calls CSI-Addons ModifyVolumeGroupMembership service.
func (vg *VolumeGroupServer) ModifyVolumeGroupMembership(
ctx context.Context,
req *proto.ModifyVolumeGroupMembershipRequest) (*proto.ModifyVolumeGroupMembershipResponse, error) {
// Get the volumeGroup Id and volumeIds from the request
vgReq := &proto.ModifyVolumeGroupMembershipRequest{
VolumeGroupId: req.GetVolumeGroupId(),
VolumeIds: req.GetVolumeIds(),
}

vgResp, err := vg.controllerClient.ModifyVolumeGroupMembership(ctx, vgReq)
if err != nil {
klog.Errorf("Failed to modify volume group: %v", err)
return nil, err
}

return &proto.ModifyVolumeGroupMembershipResponse{
VolumeGroupId: vgResp.GetVolumeGroupId(),
}, nil
}

// DeleteVolumeGroup calls CSI-Addons DeleteVolumeGroup service.
func (vg *VolumeGroupServer) DeleteVolumeGroup(
ctx context.Context,
req *proto.DeleteVolumeGroupRequest) (*proto.DeleteVolumeGroupResponse, error) {
// Get the volumeGroup Id from the request
vgReq := &proto.DeleteVolumeGroupRequest{
VolumeGroupId: req.GetVolumeGroupId(),
}

_, err := vg.controllerClient.DeleteVolumeGroup(ctx, vgReq)
if err != nil {
klog.Errorf("Failed to delete volume group: %v", err)
return nil, err
}

return &proto.DeleteVolumeGroupResponse{}, nil
}

// ControllerGetVolumeGroup calls CSI-Addons ControllerGetVolumeGroup service.
func (vg *VolumeGroupServer) ControllerGetVolumeGroup(
ctx context.Context,
req *proto.ControllerGetVolumeGroupRequest) (*proto.ControllerGetVolumeGroupResponse, error) {
// Get the volumeGroup Id from the request
vgReq := &proto.ControllerGetVolumeGroupRequest{
VolumeGroupId: req.GetVolumeGroupId(),
}

vgResp, err := vg.controllerClient.ControllerGetVolumeGroup(ctx, vgReq)
if err != nil {
klog.Errorf("Failed to get volume group: %v", err)
return nil, err
}

return &proto.ControllerGetVolumeGroupResponse{
VolumeGroupId: vgResp.GetVolumeGroupId(),
}, nil
}
1 change: 1 addition & 0 deletions sidecar/main.go
Original file line number Diff line number Diff line change
@@ -114,6 +114,7 @@ func main() {
sidecarServer.RegisterService(service.NewReclaimSpaceServer(csiClient.GetGRPCClient(), kubeClient, *stagingPath))
sidecarServer.RegisterService(service.NewNetworkFenceServer(csiClient.GetGRPCClient(), kubeClient))
sidecarServer.RegisterService(service.NewReplicationServer(csiClient.GetGRPCClient(), kubeClient))
sidecarServer.RegisterService(service.NewVolumeGroupServer(csiClient.GetGRPCClient(), kubeClient))

isController, err := csiClient.HasControllerService()
if err != nil {

0 comments on commit 63e7aec

Please sign in to comment.