Skip to content

Commit 7e0ba59

Browse files
author
sai chaithanya
committed
feat(nfs-server): add support to configure resource requirements on NFS Server (#92)
This PR adds support to configure resource requirements[requests & limits] for NFS Server via NFS StorageClass. Values specified in StorageClass annotation will be propagated to NFS Server deployment. **Sample NFS SC YAML**: ```yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: openebs-rwx annotations: openebs.io/cas-type: nfsrwx cas.openebs.io/config: | - name: NFSServerType value: "kernel" - name: BackendStorageClass value: "openebs-hostpath" # NFSServerResourceRequests defines the resource requests for NFS Server - name: NFSServerResourceRequests value: |- memory: 50Mi cpu: 50m # NFSServerResourceLimits defines the resource limits for NFS Server - name: NFSServerResourceLimits value: |- memory: 100Mi cpu: 100m provisioner: openebs.io/nfsrwx reclaimPolicy: Delete ``` Signed-off-by: mittachaitu <[email protected]>
1 parent f482996 commit 7e0ba59

File tree

9 files changed

+710
-3
lines changed

9 files changed

+710
-3
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ license-check:
195195
.PHONY: sanity-test
196196
sanity-test: sanity-test
197197
@echo "--> Running sanity test";
198-
go test -v -timeout 30m ./tests/...
198+
go test -v -timeout 60m ./tests/...
199199

200200
.PHONY: push
201201
push:

deploy/kubectl/openebs-nfs-provisioner.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,16 @@ metadata:
188188
value: "kernel"
189189
- name: BackendStorageClass
190190
value: "openebs-hostpath"
191+
# NFSServerResourceRequests defines the resource requests for NFS Server
192+
#- name: NFSServerResourceRequests
193+
# value: |-
194+
# memory: 50Mi
195+
# cpu: 50m
196+
# NFSServerResourceLimits defines the resource limits for NFS Server
197+
#- name: NFSServerResourceLimits
198+
# value: |-
199+
# memory: 100Mi
200+
# cpu: 100m
191201
# LeaseTime defines the renewl period(in seconds) for client state
192202
#- name: LeaseTime
193203
# value: 30

go.mod

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,19 @@ replace (
3030
)
3131

3232
require (
33+
github.com/cloudflare/cfssl v1.6.0 // indirect
34+
github.com/cloudflare/go-metrics v0.0.0-20151117154305-6a9aea36fb41 // indirect
3335
github.com/ghodss/yaml v1.0.0
36+
github.com/google/go-cmp v0.4.0
3437
github.com/onsi/ginkgo v1.12.0
3538
github.com/onsi/gomega v1.9.0
3639
github.com/openebs/maya v1.12.1
3740
github.com/pkg/errors v0.9.1
38-
github.com/prometheus/client_golang v1.0.0
41+
github.com/prometheus/client_golang v1.9.0
3942
github.com/spf13/cobra v1.1.1
4043
github.com/spf13/pflag v1.0.5
4144
github.com/stretchr/testify v1.4.0
45+
github.com/zmap/zlint v0.0.0-20190806154020-fd021b4cfbeb // indirect
4246
k8s.io/api v0.17.3
4347
k8s.io/apimachinery v0.17.3
4448
k8s.io/client-go v11.0.0+incompatible

go.sum

Lines changed: 229 additions & 0 deletions
Large diffs are not rendered by default.

provisioner/config.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"strconv"
2222
"strings"
2323

24+
"github.com/ghodss/yaml"
2425
mconfig "github.com/openebs/maya/pkg/apis/openebs.io/v1alpha1"
2526
cast "github.com/openebs/maya/pkg/castemplate/v1alpha1"
2627
"github.com/openebs/maya/pkg/util"
@@ -58,6 +59,12 @@ const (
5859

5960
// FSGroupID defines the permissions of nfs share volume
6061
FSGroupID = "FSGID"
62+
63+
// NFSServerResourceRequests holds key name that represent NFS Resource Requests
64+
NFSServerResourceRequests = "NFSServerResourceRequests"
65+
66+
// NFSServerResourceLimits holds key name that represent NFS Resource Limits
67+
NFSServerResourceLimits = "NFSServerResourceLimits"
6168
)
6269

6370
const (
@@ -192,6 +199,35 @@ func (c *VolumeConfig) GetFSGroupID() (*int64, error) {
192199
return &fsGIDInt, nil
193200
}
194201

202+
// GetNFSServerResourceRequirements fetches the resource(cpu & memory) request &
203+
// limits for NFS server from StorageClass only if specified
204+
func (c *VolumeConfig) GetNFSServerResourceRequirements() (*v1.ResourceRequirements, error) {
205+
var err error
206+
resourceRequirements := &v1.ResourceRequirements{}
207+
resourceRequirements.Requests, err = c.getResourceList(NFSServerResourceRequests)
208+
if err != nil {
209+
return nil, err
210+
}
211+
212+
resourceRequirements.Limits, err = c.getResourceList(NFSServerResourceLimits)
213+
if err != nil {
214+
return nil, err
215+
}
216+
return resourceRequirements, nil
217+
}
218+
219+
// getResourceList is a utility function to extract resource list
220+
// and convert from map[string]interface{} to proper Go struct
221+
func (c *VolumeConfig) getResourceList(key string) (v1.ResourceList, error) {
222+
var resourceList v1.ResourceList
223+
dataStr := c.getValue(key)
224+
err := yaml.Unmarshal([]byte(dataStr), &resourceList)
225+
if err != nil {
226+
return nil, errors.Wrapf(err, "failed to marshal data %s", dataStr)
227+
}
228+
return resourceList, nil
229+
}
230+
195231
//getValue is a utility function to extract the value
196232
// of the `key` from the ConfigMap object - which is
197233
// map[string]interface{map[string][string]}

provisioner/config_test.go

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/*
2+
Copyright 2021 The OpenEBS Authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
package provisioner
17+
18+
import (
19+
"reflect"
20+
"testing"
21+
22+
"github.com/ghodss/yaml"
23+
"github.com/google/go-cmp/cmp"
24+
corev1 "k8s.io/api/core/v1"
25+
"k8s.io/apimachinery/pkg/api/resource"
26+
)
27+
28+
func TestGetResourceList(t *testing.T) {
29+
tests := map[string]struct {
30+
volumeConfig *VolumeConfig
31+
key string
32+
expectedResourceList corev1.ResourceList
33+
isErrExpected bool
34+
}{
35+
"When NFS resource requests has only memory field": {
36+
volumeConfig: &VolumeConfig{
37+
options: map[string]interface{}{
38+
NFSServerResourceRequests: map[string]string{
39+
"value": func() string {
40+
resourceList := make(map[corev1.ResourceName]resource.Quantity)
41+
resourceList[corev1.ResourceMemory] = resource.MustParse("500M")
42+
data, err := yaml.Marshal(resourceList)
43+
if err != nil {
44+
t.Errorf("failed to convert to YAML error %v", err)
45+
}
46+
return string(data)
47+
}(),
48+
},
49+
},
50+
},
51+
key: NFSServerResourceRequests,
52+
expectedResourceList: map[corev1.ResourceName]resource.Quantity{
53+
corev1.ResourceMemory: resource.MustParse("500M"),
54+
},
55+
},
56+
"When NFS resource requests has both memory and cpu": {
57+
volumeConfig: &VolumeConfig{
58+
options: map[string]interface{}{
59+
NFSServerResourceRequests: map[string]string{
60+
"value": func() string {
61+
resourceList := make(map[corev1.ResourceName]resource.Quantity)
62+
resourceList[corev1.ResourceMemory] = resource.MustParse("500M")
63+
resourceList[corev1.ResourceCPU] = resource.MustParse("500m")
64+
data, err := yaml.Marshal(resourceList)
65+
if err != nil {
66+
t.Errorf("failed to convert to YAML error %v", err)
67+
}
68+
return string(data)
69+
}(),
70+
},
71+
},
72+
},
73+
key: NFSServerResourceRequests,
74+
expectedResourceList: map[corev1.ResourceName]resource.Quantity{
75+
corev1.ResourceMemory: resource.MustParse("500M"),
76+
corev1.ResourceCPU: resource.MustParse("500m"),
77+
},
78+
},
79+
"When NFS resource limits has only memory specified": {
80+
volumeConfig: &VolumeConfig{
81+
options: map[string]interface{}{
82+
"NFSResourceLimits": map[string]string{
83+
"value": func() string {
84+
resourceList := make(map[corev1.ResourceName]resource.Quantity)
85+
resourceList[corev1.ResourceMemory] = resource.MustParse("150Mi")
86+
data, err := yaml.Marshal(resourceList)
87+
if err != nil {
88+
t.Errorf("failed to convert to YAML error %v", err)
89+
}
90+
return string(data)
91+
}(),
92+
},
93+
},
94+
},
95+
key: "NFSResourceLimits",
96+
expectedResourceList: map[corev1.ResourceName]resource.Quantity{
97+
corev1.ResourceMemory: resource.MustParse("150Mi"),
98+
},
99+
},
100+
"When NFS resource limits has both memory & cpu specified": {
101+
volumeConfig: &VolumeConfig{
102+
options: map[string]interface{}{
103+
"NFSResourceLimits": map[string]string{
104+
"value": func() string {
105+
resourceList := make(map[corev1.ResourceName]resource.Quantity)
106+
resourceList[corev1.ResourceMemory] = resource.MustParse("150Mi")
107+
resourceList[corev1.ResourceCPU] = resource.MustParse("150m")
108+
data, err := yaml.Marshal(resourceList)
109+
if err != nil {
110+
t.Errorf("failed to convert to YAML error %v", err)
111+
}
112+
return string(data)
113+
}(),
114+
},
115+
},
116+
},
117+
key: "NFSResourceLimits",
118+
expectedResourceList: map[corev1.ResourceName]resource.Quantity{
119+
corev1.ResourceMemory: resource.MustParse("150Mi"),
120+
corev1.ResourceCPU: resource.MustParse("150m"),
121+
},
122+
},
123+
"When NFS resource limits are specified but requesting for requests": {
124+
volumeConfig: &VolumeConfig{
125+
options: map[string]interface{}{
126+
"NFSResourceLimits": map[string]string{
127+
"value": func() string {
128+
resourceList := make(map[corev1.ResourceName]resource.Quantity)
129+
resourceList[corev1.ResourceMemory] = resource.MustParse("150Mi")
130+
resourceList[corev1.ResourceCPU] = resource.MustParse("150m")
131+
data, err := yaml.Marshal(resourceList)
132+
if err != nil {
133+
t.Errorf("failed to convert to YAML error %v", err)
134+
}
135+
return string(data)
136+
}(),
137+
},
138+
},
139+
},
140+
key: NFSServerResourceRequests,
141+
expectedResourceList: nil,
142+
},
143+
"When invalid NFS resource requests are specified error should occur": {
144+
volumeConfig: &VolumeConfig{
145+
options: map[string]interface{}{
146+
NFSServerResourceRequests: map[string]string{
147+
"value": `memory: 50Ci`,
148+
},
149+
},
150+
},
151+
key: NFSServerResourceRequests,
152+
isErrExpected: true,
153+
},
154+
}
155+
156+
for name, test := range tests {
157+
gotOutput, err := test.volumeConfig.getResourceList(test.key)
158+
if test.isErrExpected && err == nil {
159+
t.Errorf("%q test failed expected error to occur but got nil", name)
160+
}
161+
if !test.isErrExpected && err != nil {
162+
t.Errorf("%q test failed expected error not to occur but got %v", name, err)
163+
}
164+
if !test.isErrExpected {
165+
if !reflect.DeepEqual(test.expectedResourceList, gotOutput) {
166+
t.Errorf("%q test has following diff %s", name, cmp.Diff(test.expectedResourceList, gotOutput))
167+
}
168+
}
169+
}
170+
}

provisioner/helper_kernel_nfs_server.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ type KernelNFSServerOptions struct {
9292
// fsGID defines the filesystem group ID if set then nfs share
9393
// volume permissions will be updated by OR'ing with rw-rw----
9494
fsGroup *int64
95+
96+
// resources defines the request & limits of NFS server
97+
// This will be populated from NFS StorageClass. If not
98+
// specified resource limits will not be applied on NFS
99+
// Server container
100+
resources *corev1.ResourceRequirements
95101
}
96102

97103
// validate checks that the required fields to create NFS Server
@@ -192,6 +198,7 @@ func (p *Provisioner) deleteBackendPVC(nfsServerOpts *KernelNFSServerOptions) er
192198

193199
// createDeployment creates a new NFS Server Deployment for a given NFS PVC
194200
func (p *Provisioner) createDeployment(nfsServerOpts *KernelNFSServerOptions) error {
201+
var resourceRequirements corev1.ResourceRequirements
195202
klog.V(4).Infof("Creating Deployment")
196203
if err := nfsServerOpts.validate(); err != nil {
197204
return err
@@ -218,6 +225,9 @@ func (p *Provisioner) createDeployment(nfsServerOpts *KernelNFSServerOptions) er
218225
nfsDeployLabelSelector := map[string]string{
219226
"openebs.io/nfs-server": deployName,
220227
}
228+
if nfsServerOpts.resources != nil {
229+
resourceRequirements = *nfsServerOpts.resources
230+
}
221231

222232
//TODO
223233
secContext := true
@@ -281,7 +291,8 @@ func (p *Provisioner) createDeployment(nfsServerOpts *KernelNFSServerOptions) er
281291
MountPath: "/nfsshare",
282292
},
283293
},
284-
),
294+
).
295+
WithResources(&resourceRequirements),
285296
).
286297
WithVolumeBuilders(
287298
volume.NewBuilder().

provisioner/provisioner_kernel_nfs_server.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ func (p *Provisioner) ProvisionKernalNFSServer(opts pvController.ProvisionOption
5555
return nil, err
5656
}
5757

58+
resources, err := volumeConfig.GetNFSServerResourceRequirements()
59+
if err != nil {
60+
klog.Errorf("Failed to get NFS server resource requirements(requests & limits) error: %s", err.Error())
61+
return nil, err
62+
}
63+
5864
//Extract the details to create a NFS Server
5965
nfsServerOpts := &KernelNFSServerOptions{
6066
pvName: name,
@@ -68,6 +74,7 @@ func (p *Provisioner) ProvisionKernalNFSServer(opts pvController.ProvisionOption
6874
pvcName: pvc.Name,
6975
pvcNamespace: pvc.Namespace,
7076
pvcUID: string(pvc.UID),
77+
resources: resources,
7178
}
7279

7380
nfsService, err := p.getNFSServerAddress(nfsServerOpts)

0 commit comments

Comments
 (0)