Skip to content

Commit

Permalink
feat(spc): add capability to specify allowed BD tags on SPC (#1748)
Browse files Browse the repository at this point in the history
* feat(spc): add capability to specify allowed BD tags on SPC

This commit adds the capability to specify allowed BD tags on SPC via
annotations. The annotation key is `openebs.io/allowed-bd-tags` and the
allowed value is comma separated strings.
For example: "fast,slow" is a possible value and in this case SPC provisioner
will reject BDs that have a BD tag (`openebs.io/block-device-tag`) present on
it and the value is other than fast or slow.

Signed-off-by: Ashutosh Kumar <ashutosh.kumar@mayadata.io>
  • Loading branch information
Ashutosh Kumar authored and prateekpandey14 committed Sep 11, 2020
1 parent a591d30 commit 50c5f20
Show file tree
Hide file tree
Showing 8 changed files with 469 additions and 11 deletions.
1 change: 0 additions & 1 deletion cmd/maya-apiserver/app/server/restore_endpoint_v1alpha1.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,6 @@ func updateRestoreStatus(clientset versioned.Interface, rst v1alpha1.CStorRestor

func createVolumeForRestore(r *v1alpha1.CStorRestore) (*v1alpha1.CASVolume, error) {
vol := &v1alpha1.CASVolume{}

vol.Name = r.Spec.VolumeName
vol.Labels = map[string]string{
string(v1alpha1.StorageClassKey): r.Spec.StorageClass,
Expand Down
57 changes: 53 additions & 4 deletions pkg/algorithm/nodeselect/v1alpha1/select_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,21 @@ import (
bdc "github.com/openebs/maya/pkg/blockdeviceclaim/v1alpha1"
env "github.com/openebs/maya/pkg/env/v1alpha1"
spcv1alpha1 "github.com/openebs/maya/pkg/storagepoolclaim/v1alpha1"
util "github.com/openebs/maya/pkg/util"
volume "github.com/openebs/maya/pkg/volume"
"github.com/openebs/maya/pkg/util"
"github.com/openebs/maya/pkg/volume"
"github.com/pkg/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog"
"strings"
)

const (
// CStorBDTagAnnotationKey is the annotation key for SPC allowed BD tags
CStorBDTagAnnotationKey = "openebs.io/allowed-bd-tags"
// BlockDeviceTagLabelKey is the key to fetch tag of a block
// device.
// For more info : https://github.com/openebs/node-disk-manager/pull/400
BlockDeviceTagLabelKey = "openebs.io/block-device-tag"
)

// BDDetails holds the claimed block device details
Expand Down Expand Up @@ -273,7 +283,7 @@ func (ac *Config) getBlockDevice() (*ndmapis.BlockDeviceList, error) {
if err != nil {
return nil, err
}
filterList := []string{blockdevice.FilterNonInactive}
filterList := []string{blockdevice.FilterNonInactive, blockdevice.FilterNotAllowedBDTag}

if diskType == string(apis.TypeSparseCPV) {
filterList = append(filterList, blockdevice.FilterSparseDevices)
Expand All @@ -285,7 +295,12 @@ func (ac *Config) getBlockDevice() (*ndmapis.BlockDeviceList, error) {
filterList = append(filterList, blockdevice.FilterNonFSType, blockdevice.FilterNonRelesedDevices)
}

bdl = bdList.Filter(filterList...)
allowedBDTags := getAllowedTagMap(ac.Spc.GetAnnotations())
filterOptions := &blockdevice.FilterOptions{
AllowedBDTags: allowedBDTags,
}

bdl = bdList.Filter(filterOptions, filterList...)
if len(bdl.Items) == 0 {
return nil, errors.Errorf("type {%s} devices are not available to provision pools in %s mode", diskType, ProvisioningType(ac.Spc))
}
Expand Down Expand Up @@ -355,6 +370,7 @@ func (ac *Config) ClaimBlockDevice(nodeBDs *nodeBlockDevice, spc *apis.StoragePo
continue
}
capacity := volume.ByteCount(bdObj.Spec.Capacity.Storage)

//TODO: Move below code to some function
newBDCObj, err := bdc.NewBuilder().
WithName(bdcName).
Expand All @@ -366,6 +382,16 @@ func (ac *Config) ClaimBlockDevice(nodeBDs *nodeBlockDevice, spc *apis.StoragePo
WithOwnerReference(spc).
WithFinalizer(spcv1alpha1.SPCFinalizer).
Build()

value, ok := bdObj.Labels[BlockDeviceTagLabelKey]

if ok {
ls := &metav1.LabelSelector{
MatchLabels: map[string]string{BlockDeviceTagLabelKey: value},
}
newBDCObj.Object.Spec.Selector = ls
}

if err != nil {
return nil, errors.Wrapf(err, "failed to build block device claim for bd {%s}", bdName)
}
Expand All @@ -388,3 +414,26 @@ func (ac *Config) ClaimBlockDevice(nodeBDs *nodeBlockDevice, spc *apis.StoragePo
}
return nodeClaimedBDs, nil
}

// getAllowedTagMap returns a map of the allowed BD tags
// Example :
// If the CSPC annotation is passed and following is the BD tag annotation
//
// cstor.openebs.io/allowed-bd-tags:fast,slow
//
// Then, a map {"fast":true,"slow":true} is returned.
func getAllowedTagMap(cspcAnnotation map[string]string) map[string]bool {
allowedTagsMap := make(map[string]bool)
allowedTags := cspcAnnotation[CStorBDTagAnnotationKey]
if strings.TrimSpace(allowedTags) == "" {
return allowedTagsMap
}
allowedTagsList := strings.Split(allowedTags, ",")
for _, v := range allowedTagsList {
if strings.TrimSpace(v) == "" {
continue
}
allowedTagsMap[v] = true
}
return allowedTagsMap
}
83 changes: 83 additions & 0 deletions pkg/algorithm/nodeselect/v1alpha1/select_node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package v1alpha1

import (
"reflect"
"strconv"
"testing"

Expand Down Expand Up @@ -547,3 +548,85 @@ func TestNodeBlockDeviceAlloter(t *testing.T) {
})
}
}

func Test_getAllowedTagMap(t *testing.T) {
type args struct {
cspcAnnotation map[string]string
}
tests := []struct {
name string
args args
want map[string]bool
}{
{
name: "Test case #1",
args: args{
cspcAnnotation: map[string]string{CStorBDTagAnnotationKey: "fast,slow"},
},
want: map[string]bool{"fast": true, "slow": true},
},

{
name: "Test case #2",
args: args{
cspcAnnotation: map[string]string{CStorBDTagAnnotationKey: "fast,slow"},
},
want: map[string]bool{"slow": true, "fast": true},
},

{
name: "Test case #3 -- Nil Annotations",
args: args{
cspcAnnotation: nil,
},
want: map[string]bool{},
},

{
name: "Test case #4 -- No BD tag Annotations",
args: args{
cspcAnnotation: map[string]string{"some-other-annotation-key": "awesome-openebs"},
},
want: map[string]bool{},
},

{
name: "Test case #5 -- Improper format 1",
args: args{
cspcAnnotation: map[string]string{CStorBDTagAnnotationKey: ",fast,slow,,"},
},
want: map[string]bool{"fast": true, "slow": true},
},

{
name: "Test case #6 -- Improper format 2",
args: args{
cspcAnnotation: map[string]string{CStorBDTagAnnotationKey: ",fast,slow"},
},
want: map[string]bool{"fast": true, "slow": true},
},

{
name: "Test case #7 -- Improper format 2",
args: args{
cspcAnnotation: map[string]string{CStorBDTagAnnotationKey: ",fast,,slow"},
},
want: map[string]bool{"fast": true, "slow": true},
},

{
name: "Test case #7 -- Improper format 2",
args: args{
cspcAnnotation: map[string]string{CStorBDTagAnnotationKey: "this is improper"},
},
want: map[string]bool{"this is improper": true},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := getAllowedTagMap(tt.args.cspcAnnotation); !reflect.DeepEqual(got, tt.want) {
t.Errorf("getAllowedTagMap() = %v, want %v", got, tt.want)
}
})
}
}
40 changes: 38 additions & 2 deletions pkg/blockdevice/v1alpha1/blockdevice.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
ndm "github.com/openebs/maya/pkg/apis/openebs.io/ndm/v1alpha1"
apis "github.com/openebs/maya/pkg/apis/openebs.io/v1alpha1"
bdc_v1alpha1 "github.com/openebs/maya/pkg/blockdeviceclaim/v1alpha1"
errors "github.com/pkg/errors"
"github.com/pkg/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"

Expand All @@ -40,8 +40,16 @@ const (
FilterNonSparseDevices = "filterNonSparseDevices"
InActiveStatus = "Inactive"
FilterNonRelesedDevices = "filterNonRelesedDevices"
FilterNotAllowedBDTag = "filterNotAllowedBDTag"

// BlockDeviceTagLabelKey is the key to fetch tag of a block
// device.
// For more info : https://github.com/openebs/node-disk-manager/pull/400
BlockDeviceTagLabelKey = "openebs.io/block-device-tag"
)

var bdFilterOptions FilterOptions

// KubernetesClient is the kubernetes client which will implement block device actions/behaviours
type KubernetesClient struct {
// Kubeclientset is a standard kubernetes clientset
Expand Down Expand Up @@ -111,6 +119,7 @@ var filterOptionFuncMap = map[string]filterOptionFunc{
FilterSparseDevices: filterSparseDevices,
FilterNonSparseDevices: filterNonSparseDevices,
FilterNonRelesedDevices: filterNonRelesedDevices,
FilterNotAllowedBDTag: filterNotAllowedBDTag,
}

// predicateFailedError returns the predicate error which is provided to this function as an argument
Expand Down Expand Up @@ -185,8 +194,12 @@ func checkName(db *BlockDevice) (string, bool) {
return "", true
}

type FilterOptions struct {
AllowedBDTags map[string]bool
}

// Filter adds filters on which the blockdevice has to be filtered
func (bdl *BlockDeviceList) Filter(predicateKeys ...string) *BlockDeviceList {
func (bdl *BlockDeviceList) Filter(filterOps *FilterOptions, predicateKeys ...string) *BlockDeviceList {
// Initialize filtered block device list
filteredBlockDeviceList := &BlockDeviceList{
BlockDeviceList: &ndm.BlockDeviceList{},
Expand All @@ -199,6 +212,9 @@ func (bdl *BlockDeviceList) Filter(predicateKeys ...string) *BlockDeviceList {
}
filteredBlockDeviceList = bdl
for _, key := range predicateKeys {
if key == FilterNotAllowedBDTag {
bdFilterOptions.AllowedBDTags = filterOps.AllowedBDTags
}
filteredBlockDeviceList = filterOptionFuncMap[key](filteredBlockDeviceList)
}
return filteredBlockDeviceList
Expand Down Expand Up @@ -326,6 +342,26 @@ func filterNonRelesedDevices(originalList *BlockDeviceList) *BlockDeviceList {
return filteredList
}

func filterNotAllowedBDTag(originalList *BlockDeviceList) *BlockDeviceList {
filteredList := &BlockDeviceList{
BlockDeviceList: &ndm.BlockDeviceList{},
errs: nil,
}
for _, device := range originalList.Items {
value, ok := device.Labels[BlockDeviceTagLabelKey]
bdTag := strings.TrimSpace(value)
if ok {
if bdTag == "" || !bdFilterOptions.AllowedBDTags[bdTag] {
continue
}
filteredList.Items = append(filteredList.Items, device)
} else {
filteredList.Items = append(filteredList.Items, device)
}
}
return filteredList
}

// Hasitems checks whether the BlockDeviceList contains BlockDevices
func (bdl *BlockDeviceList) Hasitems() (string, bool) {
if bdl == nil || bdl.BlockDeviceList == nil || bdl.Items == nil {
Expand Down
Loading

0 comments on commit 50c5f20

Please sign in to comment.