Skip to content

Commit 5097bb1

Browse files
authored
Merge pull request #126 from andyzhangx/NodeGetVolumeStats
feat: add NodeGetVolumeStats support
2 parents 5b2dda0 + 48406c1 commit 5097bb1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+11591
-13
lines changed

go.sum

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1222,11 +1222,13 @@ k8s.io/apiserver v0.20.0/go.mod h1:6gRIWiOkvGvQt12WTYmsiYoUyYW0FXSiMdNl4m+sxY8=
12221222
k8s.io/cli-runtime v0.20.0/go.mod h1:C5tewU1SC1t09D7pmkk83FT4lMAw+bvMDuRxA7f0t2s=
12231223
k8s.io/client-go v0.20.0 h1:Xlax8PKbZsjX4gFvNtt4F5MoJ1V5prDvCuoq9B7iax0=
12241224
k8s.io/client-go v0.20.0/go.mod h1:4KWh/g+Ocd8KkCwKF8vUNnmqgv+EVnQDK4MBF4oB5tY=
1225+
k8s.io/cloud-provider v0.20.0 h1:CVPQ66iyfNgeGomUq2jE/TWrfzE77bdCpemhFS8955U=
12251226
k8s.io/cloud-provider v0.20.0/go.mod h1:Lz/luSVD5BrHDDhtVdjFh0C2qQCRYdf0b9BHQ9L+bXc=
12261227
k8s.io/cluster-bootstrap v0.20.0/go.mod h1:6WZaNIBvcvL7MkPzSRKrZDIr4u+ePW2oIWoRsEFMjmE=
12271228
k8s.io/code-generator v0.20.0/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg=
12281229
k8s.io/component-base v0.20.0 h1:BXGL8iitIQD+0NgW49UsM7MraNUUGDU3FBmrfUAtmVQ=
12291230
k8s.io/component-base v0.20.0/go.mod h1:wKPj+RHnAr8LW2EIBIK7AxOHPde4gme2lzXwVSoRXeA=
1231+
k8s.io/component-helpers v0.20.0-alpha.2.0.20201114090304-7cb42b694587 h1:XV8zcD26n/hrYX129A4vR35MvgGPKKSVAQP38dif/HI=
12301232
k8s.io/component-helpers v0.20.0-alpha.2.0.20201114090304-7cb42b694587/go.mod h1:Fb+7xcdRsPnQNQnXkqMwptC6LsONZr10ris8YPJw1bk=
12311233
k8s.io/controller-manager v0.20.0-alpha.1.0.20201209052538-b2c380a1dc86 h1:22Cxrg/eu1vIlWan5HoJJzZw8/sAZpjlwMrs87uOdEQ=
12321234
k8s.io/controller-manager v0.20.0-alpha.1.0.20201209052538-b2c380a1dc86/go.mod h1:nPjStmyrmF6IE6bhIsSN+5A4nYp1ORzLENSAPp+tSzs=
@@ -1265,6 +1267,7 @@ k8s.io/kubernetes v1.21.0-alpha.0.0.20201210005053-f58c4d8cd725 h1:zDSrjDDKNWvXy
12651267
k8s.io/kubernetes v1.21.0-alpha.0.0.20201210005053-f58c4d8cd725/go.mod h1:/xrHGNfoQphtkhZvyd5bA1lRmz+QkDVmBZu+O8QMoek=
12661268
k8s.io/legacy-cloud-providers v0.20.0/go.mod h1:1jEkaU7h9+b1EYdfWDBvhFAr+QpRfUjQfK+dGhxPGfA=
12671269
k8s.io/metrics v0.20.0/go.mod h1:9yiRhfr8K8sjdj2EthQQE9WvpYDvsXIV3CjN4Ruq4Jw=
1270+
k8s.io/mount-utils v0.21.0-alpha.0 h1:IKn8GtClgNVIb801UbMpDI7F49D6KBXzSf5yTO5VZpo=
12681271
k8s.io/mount-utils v0.21.0-alpha.0/go.mod h1:Jv9NRZ5L2LF87A17GaGlArD+r3JAJdZFvo4XD1cG4Kc=
12691272
k8s.io/repo-infra v0.0.1-alpha.1/go.mod h1:wO1t9WaB99V80ljbeENTnayuEEwNZt7gECYh/CEyOJ8=
12701273
k8s.io/sample-apiserver v0.20.0/go.mod h1:tScvbz/BcUG46IOsu2YLt4EjBP7XeUuMzMbQt2tQYWw=

pkg/nfs/nfs.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ type Driver struct {
3737
ns *NodeServer
3838
cap map[csi.VolumeCapability_AccessMode_Mode]bool
3939
cscap []*csi.ControllerServiceCapability
40+
nscap []*csi.NodeServiceCapability
4041
}
4142

4243
const (
@@ -81,6 +82,11 @@ func NewNFSdriver(nodeID, endpoint string, perm *uint32) *Driver {
8182
n.AddControllerServiceCapabilities([]csi.ControllerServiceCapability_RPC_Type{
8283
csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME,
8384
})
85+
86+
n.AddNodeServiceCapabilities([]csi.NodeServiceCapability_RPC_Type{
87+
csi.NodeServiceCapability_RPC_GET_VOLUME_STATS,
88+
csi.NodeServiceCapability_RPC_UNKNOWN,
89+
})
8490
return n
8591
}
8692

@@ -125,6 +131,15 @@ func (n *Driver) AddControllerServiceCapabilities(cl []csi.ControllerServiceCapa
125131
n.cscap = csc
126132
}
127133

134+
func (n *Driver) AddNodeServiceCapabilities(nl []csi.NodeServiceCapability_RPC_Type) {
135+
var nsc []*csi.NodeServiceCapability
136+
for _, n := range nl {
137+
glog.Infof("Enabling node service capability: %v", n.String())
138+
nsc = append(nsc, NewNodeServiceCapability(n))
139+
}
140+
n.nscap = nsc
141+
}
142+
128143
func IsCorruptedDir(dir string) bool {
129144
_, pathErr := mount.PathExists(dir)
130145
fmt.Printf("IsCorruptedDir(%s) returned with error: %v", dir, pathErr)

pkg/nfs/nfs_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,54 @@ func TestRun(t *testing.T) {
134134
t.Run(tc.name, tc.testFunc)
135135
}
136136
}
137+
138+
func TestNewControllerServiceCapability(t *testing.T) {
139+
tests := []struct {
140+
cap csi.ControllerServiceCapability_RPC_Type
141+
}{
142+
{
143+
cap: csi.ControllerServiceCapability_RPC_UNKNOWN,
144+
},
145+
{
146+
cap: csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME,
147+
},
148+
{
149+
cap: csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME,
150+
},
151+
{
152+
cap: csi.ControllerServiceCapability_RPC_LIST_VOLUMES,
153+
},
154+
{
155+
cap: csi.ControllerServiceCapability_RPC_GET_CAPACITY,
156+
},
157+
}
158+
for _, test := range tests {
159+
resp := NewControllerServiceCapability(test.cap)
160+
assert.NotNil(t, resp)
161+
assert.Equal(t, resp.XXX_sizecache, int32(0))
162+
}
163+
}
164+
165+
func TestNewNodeServiceCapability(t *testing.T) {
166+
tests := []struct {
167+
cap csi.NodeServiceCapability_RPC_Type
168+
}{
169+
{
170+
cap: csi.NodeServiceCapability_RPC_UNKNOWN,
171+
},
172+
{
173+
cap: csi.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME,
174+
},
175+
{
176+
cap: csi.NodeServiceCapability_RPC_GET_VOLUME_STATS,
177+
},
178+
{
179+
cap: csi.NodeServiceCapability_RPC_EXPAND_VOLUME,
180+
},
181+
}
182+
for _, test := range tests {
183+
resp := NewNodeServiceCapability(test.cap)
184+
assert.NotNil(t, resp)
185+
assert.Equal(t, resp.XXX_sizecache, int32(0))
186+
}
187+
}

pkg/nfs/nodeserver.go

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"golang.org/x/net/context"
2727
"google.golang.org/grpc/codes"
2828
"google.golang.org/grpc/status"
29+
"k8s.io/kubernetes/pkg/volume"
2930
"k8s.io/utils/mount"
3031
)
3132

@@ -135,21 +136,74 @@ func (ns *NodeServer) NodeGetInfo(ctx context.Context, req *csi.NodeGetInfoReque
135136
// NodeGetCapabilities return the capabilities of the Node plugin
136137
func (ns *NodeServer) NodeGetCapabilities(ctx context.Context, req *csi.NodeGetCapabilitiesRequest) (*csi.NodeGetCapabilitiesResponse, error) {
137138
return &csi.NodeGetCapabilitiesResponse{
138-
Capabilities: []*csi.NodeServiceCapability{
139-
{
140-
Type: &csi.NodeServiceCapability_Rpc{
141-
Rpc: &csi.NodeServiceCapability_RPC{
142-
Type: csi.NodeServiceCapability_RPC_UNKNOWN,
143-
},
144-
},
145-
},
146-
},
139+
Capabilities: ns.Driver.nscap,
147140
}, nil
148141
}
149142

150143
// NodeGetVolumeStats get volume stats
151-
func (ns *NodeServer) NodeGetVolumeStats(ctx context.Context, in *csi.NodeGetVolumeStatsRequest) (*csi.NodeGetVolumeStatsResponse, error) {
152-
return nil, status.Error(codes.Unimplemented, "")
144+
func (ns *NodeServer) NodeGetVolumeStats(ctx context.Context, req *csi.NodeGetVolumeStatsRequest) (*csi.NodeGetVolumeStatsResponse, error) {
145+
if len(req.VolumeId) == 0 {
146+
return nil, status.Error(codes.InvalidArgument, "NodeGetVolumeStats volume ID was empty")
147+
}
148+
if len(req.VolumePath) == 0 {
149+
return nil, status.Error(codes.InvalidArgument, "NodeGetVolumeStats volume path was empty")
150+
}
151+
152+
_, err := os.Stat(req.VolumePath)
153+
if err != nil {
154+
if os.IsNotExist(err) {
155+
return nil, status.Errorf(codes.NotFound, "path %s does not exist", req.VolumePath)
156+
}
157+
return nil, status.Errorf(codes.Internal, "failed to stat file %s: %v", req.VolumePath, err)
158+
}
159+
160+
volumeMetrics, err := volume.NewMetricsStatFS(req.VolumePath).GetMetrics()
161+
if err != nil {
162+
return nil, status.Errorf(codes.Internal, "failed to get metrics: %v", err)
163+
}
164+
165+
available, ok := volumeMetrics.Available.AsInt64()
166+
if !ok {
167+
return nil, status.Errorf(codes.Internal, "failed to transform volume available size(%v)", volumeMetrics.Available)
168+
}
169+
capacity, ok := volumeMetrics.Capacity.AsInt64()
170+
if !ok {
171+
return nil, status.Errorf(codes.Internal, "failed to transform volume capacity size(%v)", volumeMetrics.Capacity)
172+
}
173+
used, ok := volumeMetrics.Used.AsInt64()
174+
if !ok {
175+
return nil, status.Errorf(codes.Internal, "failed to transform volume used size(%v)", volumeMetrics.Used)
176+
}
177+
178+
inodesFree, ok := volumeMetrics.InodesFree.AsInt64()
179+
if !ok {
180+
return nil, status.Errorf(codes.Internal, "failed to transform disk inodes free(%v)", volumeMetrics.InodesFree)
181+
}
182+
inodes, ok := volumeMetrics.Inodes.AsInt64()
183+
if !ok {
184+
return nil, status.Errorf(codes.Internal, "failed to transform disk inodes(%v)", volumeMetrics.Inodes)
185+
}
186+
inodesUsed, ok := volumeMetrics.InodesUsed.AsInt64()
187+
if !ok {
188+
return nil, status.Errorf(codes.Internal, "failed to transform disk inodes used(%v)", volumeMetrics.InodesUsed)
189+
}
190+
191+
return &csi.NodeGetVolumeStatsResponse{
192+
Usage: []*csi.VolumeUsage{
193+
{
194+
Unit: csi.VolumeUsage_BYTES,
195+
Available: available,
196+
Total: capacity,
197+
Used: used,
198+
},
199+
{
200+
Unit: csi.VolumeUsage_INODES,
201+
Available: inodesFree,
202+
Total: inodes,
203+
Used: inodesUsed,
204+
},
205+
},
206+
}, nil
153207
}
154208

155209
// NodeUnstageVolume unstage volume

pkg/nfs/nodeserver_test.go

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ import (
3030
"google.golang.org/grpc/status"
3131
)
3232

33+
const (
34+
targetTest = "./target_test"
35+
)
36+
3337
func TestNodePublishVolume(t *testing.T) {
3438
volumeCap := csi.VolumeCapability_AccessMode{Mode: csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER}
3539
alreadyMountedTarget := testutil.GetWorkDirPath("false_is_likely_exist_target", t)
@@ -166,7 +170,6 @@ func TestNodeUnpublishVolume(t *testing.T) {
166170
}
167171

168172
func TestNodeGetInfo(t *testing.T) {
169-
170173
ns, err := getTestNodeServer()
171174
if err != nil {
172175
t.Fatalf(err.Error())
@@ -180,7 +183,6 @@ func TestNodeGetInfo(t *testing.T) {
180183
}
181184

182185
func TestNodeGetCapabilities(t *testing.T) {
183-
184186
ns, err := getTestNodeServer()
185187
if err != nil {
186188
t.Fatalf(err.Error())
@@ -192,6 +194,11 @@ func TestNodeGetCapabilities(t *testing.T) {
192194
},
193195
}
194196

197+
capList := []*csi.NodeServiceCapability{{
198+
Type: capType,
199+
}}
200+
ns.Driver.nscap = capList
201+
195202
// Test valid request
196203
req := csi.NodeGetCapabilitiesRequest{}
197204
resp, err := ns.NodeGetCapabilities(context.Background(), &req)
@@ -211,3 +218,53 @@ func getTestNodeServer() (NodeServer, error) {
211218
mounter: mounter,
212219
}, nil
213220
}
221+
222+
func TestNodeGetVolumeStats(t *testing.T) {
223+
nonexistedPath := "/not/a/real/directory"
224+
fakePath := "/tmp/fake-volume-path"
225+
226+
tests := []struct {
227+
desc string
228+
req csi.NodeGetVolumeStatsRequest
229+
expectedErr error
230+
}{
231+
{
232+
desc: "[Error] Volume ID missing",
233+
req: csi.NodeGetVolumeStatsRequest{VolumePath: targetTest},
234+
expectedErr: status.Error(codes.InvalidArgument, "NodeGetVolumeStats volume ID was empty"),
235+
},
236+
{
237+
desc: "[Error] VolumePath missing",
238+
req: csi.NodeGetVolumeStatsRequest{VolumeId: "vol_1"},
239+
expectedErr: status.Error(codes.InvalidArgument, "NodeGetVolumeStats volume path was empty"),
240+
},
241+
{
242+
desc: "[Error] Incorrect volume path",
243+
req: csi.NodeGetVolumeStatsRequest{VolumePath: nonexistedPath, VolumeId: "vol_1"},
244+
expectedErr: status.Errorf(codes.NotFound, "path /not/a/real/directory does not exist"),
245+
},
246+
{
247+
desc: "[Success] Standard success",
248+
req: csi.NodeGetVolumeStatsRequest{VolumePath: fakePath, VolumeId: "vol_1"},
249+
expectedErr: nil,
250+
},
251+
}
252+
253+
// Setup
254+
_ = makeDir(fakePath)
255+
ns, err := getTestNodeServer()
256+
if err != nil {
257+
t.Fatalf(err.Error())
258+
}
259+
260+
for _, test := range tests {
261+
_, err := ns.NodeGetVolumeStats(context.Background(), &test.req)
262+
if !reflect.DeepEqual(err, test.expectedErr) {
263+
t.Errorf("desc: %v, expected error: %v, actual error: %v", test.desc, test.expectedErr, err)
264+
}
265+
}
266+
267+
// Clean up
268+
err = os.RemoveAll(fakePath)
269+
assert.NoError(t, err)
270+
}

pkg/nfs/utils.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,16 @@ func NewControllerServiceCapability(cap csi.ControllerServiceCapability_RPC_Type
4949
}
5050
}
5151

52+
func NewNodeServiceCapability(cap csi.NodeServiceCapability_RPC_Type) *csi.NodeServiceCapability {
53+
return &csi.NodeServiceCapability{
54+
Type: &csi.NodeServiceCapability_Rpc{
55+
Rpc: &csi.NodeServiceCapability_RPC{
56+
Type: cap,
57+
},
58+
},
59+
}
60+
}
61+
5262
func ParseEndpoint(ep string) (string, string, error) {
5363
if strings.HasPrefix(strings.ToLower(ep), "unix://") || strings.HasPrefix(strings.ToLower(ep), "tcp://") {
5464
s := strings.SplitN(ep, "://", 2)

test/integration/run-test.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ echo "publish volume test:"
7070
"$CSC_BIN" node publish --endpoint "$endpoint" --cap "$cap" --vol-context "$params" --target-path "$target_path" "$volumeid"
7171
sleep 2
7272

73+
echo "node stats test:"
74+
csc node stats --endpoint "$endpoint" "$volumeid:$target_path:$staging_target_path"
75+
sleep 2
76+
7377
echo "unpublish volume test:"
7478
"$CSC_BIN" node unpublish --endpoint "$endpoint" --target-path "$target_path" "$volumeid"
7579
sleep 2

vendor/k8s.io/cloud-provider/CONTRIBUTING.md

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)