Repository: openebs/node-disk-manager
Pull request #647 (2021-09-20 10:36:45)

fix(ndm-exporters): changed exporter service common label
Signed-off-by: Abhishek Agarwal <abhishek.agarwal@mayadata.io>

**Why is this PR required? What issue does it fix?**:
https://github.com/openebs/monitoring/issues/79

**What this PR does?**:
This PR removes:
1. Removes the `name` and `component` label from the ndm operator's exporter service yamls.
2. Add a new label(common for both the cluster as well as node exporters) in the ndm exporter services that will be used for monitoring. Label to added be added is- `app: openebs-ndm-exporter` in both the operator as well as helm deployment.

**Does this PR require any upgrade changes?**:
No

**If the changes in this PR are manually verified, list down the scenarios covered:**:

**Any additional information for your reviewer?** : 
_Mention if this PR is part of any design or a continuation of previous PRs_


**Checklist:**
- [x] Fixes https://github.com/openebs/monitoring/issues/79
- [x] PR Title follows the convention of  `<type>(<scope>): <subject>`
- [ ] Has the change log section been updated? 
- [ ] Commit has unit tests
- [ ] Commit has integration tests
- [ ] (Optional) Are upgrade changes included in this PR? If not, mention the issue/PR to track: 
- [ ] (Optional) If documentation changes are required, which issue on https://github.com/openebs/openebs-docs is used to track them: 

codecov-commenter: # [Codecov](https://codecov.io/gh/openebs/node-disk-manager/pull/647?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=openebs) Report
> Merging [#647](https://codecov.io/gh/openebs/node-disk-manager/pull/647?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=openebs) (c4b10dc) into [develop](https://codecov.io/gh/openebs/node-disk-manager/commit/8472a707ce44a436f05c5a9a023c3d4f53d178c0?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=openebs) (8472a70) will **increase** coverage by `0.07%`.
> The diff coverage is `n/a`.

> :exclamation: Current head c4b10dc differs from pull request most recent head 8eefa36. Consider uploading reports for the commit 8eefa36 to get more accurate results
[![Impacted file tree graph](https://codecov.io/gh/openebs/node-disk-manager/pull/647/graphs/tree.svg?width=650&height=150&src=pr&token=dfeGKM3L1Z&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=openebs)](https://codecov.io/gh/openebs/node-disk-manager/pull/647?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=openebs)

```diff
@@             Coverage Diff             @@
##           develop     #647      +/-   ##
===========================================
+ Coverage    46.62%   46.70%   +0.07%     
===========================================
  Files           78       78              
  Lines         3820     3820              
===========================================
+ Hits          1781     1784       +3     
- Misses        1880     1882       +2     
+ Partials       159      154       -5     
```


| [Impacted Files](https://codecov.io/gh/openebs/node-disk-manager/pull/647?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=openebs) | Coverage Δ | |
|---|---|---|
| [cmd/ndm\_daemonset/probe/addhandler.go](https://codecov.io/gh/openebs/node-disk-manager/pull/647/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=openebs#diff-Y21kL25kbV9kYWVtb25zZXQvcHJvYmUvYWRkaGFuZGxlci5nbw==) | `75.42% <0.00%> (+1.02%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/openebs/node-disk-manager/pull/647?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=openebs).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=openebs)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/openebs/node-disk-manager/pull/647?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=openebs). Last update [8472a70...8eefa36](https://codecov.io/gh/openebs/node-disk-manager/pull/647?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=openebs). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=openebs).


Patch:
diff --git a/changelogs/unreleased/649-akhilerm b/changelogs/unreleased/649-akhilerm
new file mode 100644
index 00000000..a5c77cc9
--- /dev/null
+++ b/changelogs/unreleased/649-akhilerm
@@ -0,0 +1,1 @@
+mount /dev directory from host inside container so that wipefs gets reflected immediately on the host
diff --git a/cmd/ndm_daemonset/controller/blockdevice.go b/cmd/ndm_daemonset/controller/blockdevice.go
index 810665b1..d1627d46 100644
--- a/cmd/ndm_daemonset/controller/blockdevice.go
+++ b/cmd/ndm_daemonset/controller/blockdevice.go
@@ -17,8 +17,17 @@ limitations under the License.
 package controller
 
 import (
+	"fmt"
+	"regexp"
+	"strings"
+
+	"k8s.io/apimachinery/pkg/util/validation"
+	"k8s.io/client-go/util/jsonpath"
+	"k8s.io/klog"
+
 	apis "github.com/openebs/node-disk-manager/api/v1alpha1"
 	bd "github.com/openebs/node-disk-manager/blockdevice"
+
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
@@ -67,13 +76,116 @@ type FSInfo struct {
 
 // ToDevice convert deviceInfo struct to api.BlockDevice
 // type which will be pushed to etcd
-func (di *DeviceInfo) ToDevice() apis.BlockDevice {
+func (di *DeviceInfo) ToDevice(controller *Controller) (apis.BlockDevice, error) {
 	blockDevice := apis.BlockDevice{}
 	blockDevice.Spec = di.getDeviceSpec()
 	blockDevice.ObjectMeta = di.getObjectMeta()
 	blockDevice.TypeMeta = di.getTypeMeta()
 	blockDevice.Status = di.getStatus()
-	return blockDevice
+	err := addBdLabels(&blockDevice, controller)
+	if err != nil {
+		return blockDevice, fmt.Errorf("error in adding labels to the blockdevice: %v", err)
+	}
+	return blockDevice, nil
+}
+
+// addBdLabels add labels to block device that may be helpful for filtering the block device
+// based on some generic attributes like drive-type, model, vendor etc.
+func addBdLabels(bd *apis.BlockDevice, ctrl *Controller) error {
+	var JsonPathFields []string
+
+	// get the labels to be added from the configmap
+	if ctrl.NDMConfig != nil {
+		for _, metaConfig := range ctrl.NDMConfig.MetaConfigs {
+			if metaConfig.Key == deviceLabelsKey {
+				JsonPathFields = strings.Split(metaConfig.Type, ",")
+			}
+		}
+	}
+
+	if len(JsonPathFields) > 0 {
+		for _, jsonPath := range JsonPathFields {
+			// Parse jsonpath
+			fields, err := RelaxedJSONPathExpression(strings.TrimSpace(jsonPath))
+			if err != nil {
+				klog.Errorf("Error converting into a valid jsonpath expression: %+v", err)
+				return err
+			}
+
+			j := jsonpath.New(jsonPath)
+			if err := j.Parse(fields); err != nil {
+				klog.Errorf("Error parsing jsonpath: %s, error: %+v", fields, err)
+				return err
+			}
+
+			values, err := j.FindResults(bd)
+			if err != nil {
+				klog.Errorf("Error finding results for jsonpath: %s, error: %+v", fields, err)
+				return err
+			}
+
+			valueStrings := []string{}
+			var jsonPathFieldValue string
+
+			if len(values) > 0 || len(values[0]) > 0 {
+				for arrIx := range values {
+					for valIx := range values[arrIx] {
+						valueStrings = append(valueStrings, fmt.Sprintf("%v", values[arrIx][valIx].Interface()))
+					}
+				}
+
+				// convert the string array into a single string
+				jsonPathFieldValue = strings.Join(valueStrings, ",")
+				jsonPathFieldValue = strings.TrimSuffix(jsonPathFieldValue, ",")
+
+				// the above string formed should adhere to k8s label rules inorder for it to be
+				// used as a label value for blockdevice object.
+				// Check this link for more info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
+				errs := validation.IsValidLabelValue(jsonPathFieldValue)
+				if len(errs) > 0 {
+					return fmt.Errorf("invalid Label found. Error: {%s}", strings.Join(errs, ","))
+				}
+
+				jsonPathFields := strings.Split(jsonPath, ".")
+				if len(jsonPathFields) > 0 && jsonPathFields[len(jsonPathFields)-1] != "" && jsonPathFieldValue != "" {
+					bd.Labels[NDMLabelPrefix+jsonPathFields[len(jsonPathFields)-1]] = jsonPathFieldValue
+				}
+			}
+		}
+		klog.V(4).Infof("successfully added device labels")
+	}
+	return nil
+}
+
+// RelaxedJSONPathExpression attempts to be flexible with JSONPath expressions, it accepts:
+//   * metadata.name (no leading '.' or curly braces '{...}'
+//   * {metadata.name} (no leading '.')
+//   * .metadata.name (no curly braces '{...}')
+//   * {.metadata.name} (complete expression)
+// And transforms them all into a valid jsonpath expression:
+//   {.metadata.name}
+// NOTE: This code has been referenced from kubernetes kubectl github repo.
+//       Ref: https://github.com/kubernetes/kubectl/blob/caeb9274868c57d8a320014290cc7e3d1bcb9e46/pkg/cmd/get/customcolumn.go#L47
+func RelaxedJSONPathExpression(pathExpression string) (string, error) {
+	var jsonRegexp = regexp.MustCompile(`^\{\.?([^{}]+)\}$|^\.?([^{}]+)$`)
+
+	if len(pathExpression) == 0 {
+		return pathExpression, nil
+	}
+	submatches := jsonRegexp.FindStringSubmatch(pathExpression)
+	if submatches == nil {
+		return "", fmt.Errorf("unexpected path string, expected a 'name1.name2' or '.name1.name2' or '{name1.name2}' or '{.name1.name2}'")
+	}
+	if len(submatches) != 3 {
+		return "", fmt.Errorf("unexpected submatch list: %v", submatches)
+	}
+	var fieldSpec string
+	if len(submatches[1]) != 0 {
+		fieldSpec = submatches[1]
+	} else {
+		fieldSpec = submatches[2]
+	}
+	return fmt.Sprintf("{.%s}", fieldSpec), nil
 }
 
 // getObjectMeta returns ObjectMeta struct which contains
@@ -85,7 +197,14 @@ func (di *DeviceInfo) getObjectMeta() metav1.ObjectMeta {
 		Annotations: make(map[string]string),
 		Name:        di.UUID,
 	}
-	objectMeta.Labels[KubernetesHostNameLabel] = di.NodeAttributes[HostNameKey]
+	//objectMeta.Labels[KubernetesHostNameLabel] = di.NodeAttributes[HostNameKey]
+	for k, v := range di.NodeAttributes {
+		if k == HostNameKey {
+			objectMeta.Labels[KubernetesHostNameLabel] = v
+		} else {
+			objectMeta.Labels[k] = v
+		}
+	}
 	objectMeta.Labels[NDMDeviceTypeKey] = NDMDefaultDeviceType
 	objectMeta.Labels[NDMManagedKey] = TrueString
 	// adding custom labels
diff --git a/cmd/ndm_daemonset/controller/blockdevicestore.go b/cmd/ndm_daemonset/controller/blockdevicestore.go
index 91192b57..840742b4 100644
--- a/cmd/ndm_daemonset/controller/blockdevicestore.go
+++ b/cmd/ndm_daemonset/controller/blockdevicestore.go
@@ -22,12 +22,13 @@ import (
 	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/selection"
 
-	apis "github.com/openebs/node-disk-manager/api/v1alpha1"
-	"github.com/openebs/node-disk-manager/pkg/util"
 	"k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/klog"
 	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	apis "github.com/openebs/node-disk-manager/api/v1alpha1"
+	"github.com/openebs/node-disk-manager/pkg/util"
 )
 
 // CreateBlockDevice creates the BlockDevice resource in etcd
@@ -250,7 +251,11 @@ func (c *Controller) DeactivateStaleBlockDeviceResource(devices []string) {
 func (c *Controller) PushBlockDeviceResource(oldBlockDevice *apis.BlockDevice,
 	deviceDetails *DeviceInfo) error {
 	deviceDetails.NodeAttributes = c.NodeAttributes
-	deviceAPI := deviceDetails.ToDevice()
+	deviceAPI, err := deviceDetails.ToDevice(c)
+	if err != nil {
+		klog.Error("Failed to create a block device resource CR, Error: ", err)
+		return err
+	}
 	if oldBlockDevice != nil {
 		return c.UpdateBlockDevice(deviceAPI, oldBlockDevice)
 	}
diff --git a/cmd/ndm_daemonset/controller/controller.go b/cmd/ndm_daemonset/controller/controller.go
index ca5e17e2..09c20357 100644
--- a/cmd/ndm_daemonset/controller/controller.go
+++ b/cmd/ndm_daemonset/controller/controller.go
@@ -21,12 +21,10 @@ import (
 	"errors"
 	"fmt"
 	"os"
+	"strings"
 	"sync"
 	"time"
 
-	apis "github.com/openebs/node-disk-manager/api/v1alpha1"
-	"github.com/openebs/node-disk-manager/blockdevice"
-
 	v1 "k8s.io/api/core/v1"
 	"k8s.io/client-go/rest"
 	"k8s.io/klog"
@@ -34,6 +32,10 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client/config"
 	"sigs.k8s.io/controller-runtime/pkg/manager"
 	"sigs.k8s.io/controller-runtime/pkg/manager/signals"
+
+	apis "github.com/openebs/node-disk-manager/api/v1alpha1"
+	"github.com/openebs/node-disk-manager/blockdevice"
+	"github.com/openebs/node-disk-manager/pkg/util"
 )
 
 const (
@@ -49,6 +51,8 @@ const (
 	openEBSLabelPrefix = "openebs.io/"
 	// HostNameKey is the key for hostname
 	HostNameKey = "hostname"
+	// LabelTypeNode is the key present in an ENV variable(containing comma separated string value)
+	LabelTypeNode = "node-attributes"
 	// NodeNameKey is the node name label prefix
 	NodeNameKey = "nodename"
 	// KubernetesHostNameLabel is the hostname label used by k8s
@@ -70,11 +74,17 @@ const (
 	// NDMUnknown is constant for resource unknown status.
 	NDMUnknown = "Unknown"
 	// NDMDeviceTypeKey specifies the block device type
-	NDMDeviceTypeKey = "ndm.io/blockdevice-type"
+	NDMDeviceTypeKey = NDMLabelPrefix + "blockdevice-type"
 	// NDMManagedKey specifies blockdevice cr should be managed by ndm or not.
-	NDMManagedKey = "ndm.io/managed"
+	NDMManagedKey = NDMLabelPrefix + "managed"
+	// nodeLabelsKey is the meta config key name for adding node labels
+	nodeLabelsKey = "node-labels"
+	// deviceLabelsKey is the meta config key name for adding device labels
+	deviceLabelsKey = "device-labels"
+	// NDMLabelPrefix is the label prefix for ndm labels
+	NDMLabelPrefix = "ndm.io/"
 	// NDMZpoolName specifies the zpool name
-	NDMZpoolName = "ndm.io/zpool-name"
+	NDMZpoolName = NDMLabelPrefix + "zpool-name"
 )
 
 const (
@@ -189,16 +199,16 @@ func (c *Controller) setNodeAttributes() error {
 	}
 	c.NodeAttributes[NodeNameKey] = nodeName
 
-	// set the hostname label
-	if err = c.setHostName(); err != nil {
+	// set the node labels
+	if err = c.setNodeLabels(); err != nil {
 		return fmt.Errorf("unable to set node attributes:%v", err)
 	}
 	return nil
 }
 
-// setHostName set NodeAttribute field in Controller struct
+// setNodeLabels set NodeAttribute field in Controller struct
 // from the labels in node object
-func (c *Controller) setHostName() error {
+func (c *Controller) setNodeLabels() error {
 	nodeName := c.NodeAttributes[NodeNameKey]
 	// get the node object and fetch the hostname label from the
 	// node object
@@ -215,6 +225,33 @@ func (c *Controller) setHostName() error {
 	} else {
 		c.NodeAttributes[HostNameKey] = hostName
 	}
+
+	var labelPattern []string
+
+	// Get the list of node label patterns to be added from the configmap
+	if c.NDMConfig != nil {
+		for _, metaConfig := range c.NDMConfig.MetaConfigs {
+			if metaConfig.Key == nodeLabelsKey {
+				labelPattern = strings.Split(metaConfig.Pattern, ",")
+			}
+		}
+	}
+
+	if len(labelPattern) > 0 {
+		// Add only those node labels that matches the pattern specified in the
+		// node-labels meta config
+		for key, value := range node.Labels {
+			for _, pattern := range labelPattern {
+				if util.IsMatchRegex(pattern, key) {
+					if value != "" {
+						c.NodeAttributes[key] = value
+						break
+					}
+				}
+			}
+		}
+	}
+
 	return nil
 }
 
diff --git a/cmd/ndm_daemonset/controller/ndmconfig.go b/cmd/ndm_daemonset/controller/ndmconfig.go
index 9e128868..4a226332 100644
--- a/cmd/ndm_daemonset/controller/ndmconfig.go
+++ b/cmd/ndm_daemonset/controller/ndmconfig.go
@@ -36,6 +36,8 @@ type NodeDiskManagerConfig struct {
 	FilterConfigs []FilterConfig `json:"filterconfigs"` // FilterConfigs contains configs of Filters
 	// TagConfigs contains configs for tags
 	TagConfigs []TagConfig `json:"tagconfigs"`
+	// MetaConfig contains configs for device labels
+	MetaConfigs []MetaConfig `json:"metaconfigs"`
 }
 
 // ProbeConfig contains configs of Probe
@@ -61,6 +63,13 @@ type TagConfig struct {
 	TagName string `json:"tag"`
 }
 
+type MetaConfig struct {
+	Key     string `json:"key"`
+	Name    string `json:"name"`
+	Type    string `json:"type"`
+	Pattern string `json:"pattern"`
+}
+
 // SetNDMConfig sets config for probes and filters which user provides via configmap. If
 // no configmap present then ndm will load default config for each probes and filters.
 func (c *Controller) SetNDMConfig(opts NDMOptions) {
diff --git a/cmd/ndm_daemonset/controller/sparsefilegenerator.go b/cmd/ndm_daemonset/controller/sparsefilegenerator.go
index e985d395..218dbb8f 100644
--- a/cmd/ndm_daemonset/controller/sparsefilegenerator.go
+++ b/cmd/ndm_daemonset/controller/sparsefilegenerator.go
@@ -220,7 +220,13 @@ func (c *Controller) MarkSparseBlockDeviceStateActive(sparseFile string, sparseF
 
 	//If a BlockDevice CR already exits, update it. If not create a new one.
 	klog.Info("Updating the BlockDevice CR for Sparse file: ", BlockDeviceDetails.UUID)
-	err = c.CreateBlockDevice(BlockDeviceDetails.ToDevice())
+	bd, err := BlockDeviceDetails.ToDevice(c)
+	if err != nil {
+		klog.Error("Failed to create a block device resource CR, Error: ", err)
+		return
+	}
+
+	err = c.CreateBlockDevice(bd)
 	if err != nil {
 		klog.Error("Failed to create a block device resource in etcd, Error: ", err)
 	}
diff --git a/cmd/ndm_daemonset/probe/addhandler.go b/cmd/ndm_daemonset/probe/addhandler.go
index 60e4c21b..6be71716 100644
--- a/cmd/ndm_daemonset/probe/addhandler.go
+++ b/cmd/ndm_daemonset/probe/addhandler.go
@@ -22,6 +22,7 @@ import (
 	apis "github.com/openebs/node-disk-manager/api/v1alpha1"
 	"github.com/openebs/node-disk-manager/blockdevice"
 	"github.com/openebs/node-disk-manager/db/kubernetes"
+	"github.com/openebs/node-disk-manager/pkg/features"
 	"github.com/openebs/node-disk-manager/pkg/partition"
 
 	"k8s.io/apimachinery/pkg/api/errors"
@@ -111,18 +112,29 @@ func (pe *ProbeEvent) addBlockDevice(bd blockdevice.BlockDevice, bdAPIList *apis
 			len(bd.DependentDevices.Holders) > 0 {
 			klog.V(4).Infof("device: %s has holders/partitions. %+v", bd.DevPath, bd.DependentDevices)
 		} else {
-			klog.Infof("starting to create partition on device: %s", bd.DevPath)
 			d := partition.Disk{
 				DevPath:          bd.DevPath,
 				DiskSize:         bd.Capacity.Storage,
 				LogicalBlockSize: uint64(bd.DeviceAttributes.LogicalBlockSize),
 			}
-			if err := d.CreateSinglePartition(); err != nil {
-				klog.Errorf("error creating partition for %s, %v", bd.DevPath, err)
-				return err
+
+			if features.FeatureGates.IsEnabled(features.PartitionTableUUID) {
+				klog.Infof("starting to create partition table on device: %s", bd.DevPath)
+				if err := d.CreatePartitionTable(); err != nil {
+					klog.Errorf("error create partition table for %s, %v", bd.DevPath, err)
+					return err
+				}
+				klog.Infof("created new partition table in %s", bd.DevPath)
+				return ErrNeedRescan
+			} else {
+				klog.Infof("starting to create partition on device: %s", bd.DevPath)
+				if err := d.CreateSinglePartition(); err != nil {
+					klog.Errorf("error creating partition for %s, %v", bd.DevPath, err)
+					return err
+				}
+				klog.Infof("created new partition in %s", bd.DevPath)
+				return nil
 			}
-			klog.Infof("created new partition in %s", bd.DevPath)
-			return nil
 		}
 	} else {
 		bd.UUID = uuid
@@ -372,10 +384,14 @@ func (pe *ProbeEvent) deviceInUseByZFSLocalPV(bd blockdevice.BlockDevice, bdAPIL
 	bd.UUID = uuid
 
 	deviceInfo := pe.Controller.NewDeviceInfoFromBlockDevice(&bd)
-	bdAPI := deviceInfo.ToDevice()
+	bdAPI, err := deviceInfo.ToDevice(pe.Controller)
+	if err != nil {
+		klog.Error("Failed to create a block device resource CR, Error: ", err)
+		return true, err
+	}
 	bdAPI.Labels[kubernetes.BlockDeviceTagLabel] = string(blockdevice.ZFSLocalPV)
 
-	err := pe.Controller.CreateBlockDevice(bdAPI)
+	err = pe.Controller.CreateBlockDevice(bdAPI)
 	if err != nil {
 		klog.Errorf("unable to push %s (%s) to etcd", bd.UUID, bd.DevPath)
 		return false, err
@@ -582,11 +598,13 @@ func (pe *ProbeEvent) createOrUpdateWithPartitionUUID(bd blockdevice.BlockDevice
 // createOrUpdateWithAnnotation creates or updates a resource in etcd with given annotation.
 func (pe *ProbeEvent) createOrUpdateWithAnnotation(annotation map[string]string, bd blockdevice.BlockDevice, existingBD *apis.BlockDevice) error {
 	deviceInfo := pe.Controller.NewDeviceInfoFromBlockDevice(&bd)
-	bdAPI := deviceInfo.ToDevice()
-
+	bdAPI, err := deviceInfo.ToDevice(pe.Controller)
+	if err != nil {
+		klog.Error("Failed to create a block device resource CR, Error: ", err)
+		return err
+	}
 	bdAPI.Annotations = annotation
 
-	var err error
 	if existingBD != nil {
 		err = pe.Controller.UpdateBlockDevice(bdAPI, existingBD)
 	} else {
diff --git a/cmd/ndm_daemonset/probe/blkidprobe.go b/cmd/ndm_daemonset/probe/blkidprobe.go
new file mode 100644
index 00000000..ff845577
--- /dev/null
+++ b/cmd/ndm_daemonset/probe/blkidprobe.go
@@ -0,0 +1,74 @@
+/*
+Copyright 2020 The OpenEBS 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 probe
+
+import (
+	"github.com/openebs/node-disk-manager/blockdevice"
+	"github.com/openebs/node-disk-manager/cmd/ndm_daemonset/controller"
+	"github.com/openebs/node-disk-manager/pkg/blkid"
+	"k8s.io/klog"
+)
+
+const (
+	blkidProbePriority = 4
+)
+
+var (
+	blkidProbeState = defaultEnabled
+)
+
+type blkidProbe struct {
+}
+
+var blkidProbeRegister = func() {
+	// Get a controller object
+	ctrl := <-controller.ControllerBroadcastChannel
+	if ctrl == nil {
+		klog.Error("unable to configure custom tag probe")
+		return
+	}
+	probe := &blkidProbe{}
+
+	newRegisterProbe := &registerProbe{
+		priority:   blkidProbePriority,
+		name:       "blkid probe",
+		state:      blkidProbeState,
+		pi:         probe,
+		controller: ctrl,
+	}
+
+	newRegisterProbe.register()
+}
+
+func (bp *blkidProbe) Start() {}
+
+func (bp *blkidProbe) FillBlockDeviceDetails(bd *blockdevice.BlockDevice) {
+	di := &blkid.DeviceIdentifier{DevPath: bd.DevPath}
+
+	if len(bd.FSInfo.FileSystem) == 0 {
+		bd.FSInfo.FileSystem = di.GetOnDiskFileSystem()
+	}
+
+	// if the host is CentOS 7, the `libblkid` version on host is `2.23`,
+	// but the `PTUUID` tag was start to provide from `2.24`. This will cause
+	// the udev cache fetched from host udevd will not contain env `ID_PART_TABLE_UUID`.
+	// Fortunately, the `libblkid` version in our base container (ubuntu 16.04)
+	// is `2.27.1`, will provide `PTUUID` tag, so we should fetch `PTUUID` from `blkid`.
+	if len(bd.PartitionInfo.PartitionTableUUID) == 0 {
+		bd.PartitionInfo.PartitionTableUUID = di.GetPartitionUUID()
+	}
+}
diff --git a/cmd/ndm_daemonset/probe/changehandler.go b/cmd/ndm_daemonset/probe/changehandler.go
index 8b3f92cb..248a5585 100644
--- a/cmd/ndm_daemonset/probe/changehandler.go
+++ b/cmd/ndm_daemonset/probe/changehandler.go
@@ -64,12 +64,15 @@ func (pe *ProbeEvent) changeBlockDevice(bd *blockdevice.BlockDevice, requestedPr
 		klog.Infof("no changes in %s. Skipping update", bd.DevPath)
 		return nil
 	}
-
 	pe.addBlockDeviceToHierarchyCache(*bd)
 	if !pe.Controller.ApplyFilter(bd) {
 		return nil
 	}
-	apiBlockdevice := pe.Controller.NewDeviceInfoFromBlockDevice(bd).ToDevice()
+	apiBlockdevice, err := pe.Controller.NewDeviceInfoFromBlockDevice(bd).ToDevice(pe.Controller)
+	if err != nil {
+		klog.Error("Failed to create a block device resource CR, Error: ", err)
+		return err
+	}
 	apiBlockdevice.SetNamespace(pe.Controller.Namespace)
 	klog.Info("updating bd: ", apiBlockdevice.GetName())
 	return pe.Controller.UpdateBlockDevice(apiBlockdevice, nil)
diff --git a/cmd/ndm_daemonset/probe/eventhandler.go b/cmd/ndm_daemonset/probe/eventhandler.go
index ecaa719f..75320359 100644
--- a/cmd/ndm_daemonset/probe/eventhandler.go
+++ b/cmd/ndm_daemonset/probe/eventhandler.go
@@ -17,6 +17,8 @@ limitations under the License.
 package probe
 
 import (
+	"errors"
+
 	"github.com/openebs/node-disk-manager/blockdevice"
 	"github.com/openebs/node-disk-manager/cmd/ndm_daemonset/controller"
 	"github.com/openebs/node-disk-manager/pkg/features"
@@ -38,6 +40,10 @@ const (
 	ChangeEA EventAction = libudevwrapper.UDEV_ACTION_CHANGE
 )
 
+var (
+	ErrNeedRescan = errors.New("need rescan")
+)
+
 // ProbeEvent struct contain a copy of controller it will update disk resources
 type ProbeEvent struct {
 	Controller *controller.Controller
@@ -55,7 +61,7 @@ func (pe *ProbeEvent) addBlockDeviceEvent(msg controller.EventMessage) {
 
 	isGPTBasedUUIDEnabled := features.FeatureGates.IsEnabled(features.GPTBasedUUID)
 
-	isErrorDuringUpdate := false
+	isNeedRescan := false
 	erroredDevices := make([]string, 0)
 
 	// iterate through each block device and perform the add/update operation
@@ -82,9 +88,11 @@ func (pe *ProbeEvent) addBlockDeviceEvent(msg controller.EventMessage) {
 			}
 			err := pe.addBlockDevice(*device, bdAPIList)
 			if err != nil {
-				isErrorDuringUpdate = true
-				erroredDevices = append(erroredDevices, device.DevPath)
-				klog.Error(err)
+				isNeedRescan = true
+				if !errors.Is(err, ErrNeedRescan) {
+					erroredDevices = append(erroredDevices, device.DevPath)
+					klog.Error(err)
+				}
 			}
 		} else {
 			// if GPTBasedUUID is disabled and the device type is partition,
@@ -98,13 +106,13 @@ func (pe *ProbeEvent) addBlockDeviceEvent(msg controller.EventMessage) {
 			existingBlockDeviceResource := pe.Controller.GetExistingBlockDeviceResource(bdAPIList, deviceInfo.UUID)
 			err := pe.Controller.PushBlockDeviceResource(existingBlockDeviceResource, deviceInfo)
 			if err != nil {
-				isErrorDuringUpdate = true
+				isNeedRescan = true
 				klog.Error(err)
 			}
 		}
 	}
 
-	if isErrorDuringUpdate {
+	if isNeedRescan {
 		go Rescan(pe.Controller)
 	}
 }
diff --git a/cmd/ndm_daemonset/probe/probe.go b/cmd/ndm_daemonset/probe/probe.go
index 9bf9ff38..e13ba0c3 100644
--- a/cmd/ndm_daemonset/probe/probe.go
+++ b/cmd/ndm_daemonset/probe/probe.go
@@ -35,6 +35,7 @@ var RegisteredProbes = []func(){
 	sysfsProbeRegister,
 	usedbyProbeRegister,
 	customTagProbeRegister,
+	blkidProbeRegister,
 }
 
 type registerProbe struct {
diff --git a/cmd/ndm_daemonset/probe/udevprobe.go b/cmd/ndm_daemonset/probe/udevprobe.go
index aaf93b02..2ef30799 100644
--- a/cmd/ndm_daemonset/probe/udevprobe.go
+++ b/cmd/ndm_daemonset/probe/udevprobe.go
@@ -121,13 +121,13 @@ func (up *udevProbe) Start() {
 	go up.listen()
 	if features.FeatureGates.IsEnabled(features.ChangeDetection) {
 		up.udeveventSubscription = udevevent.Subscribe(
-		        udevevent.EventTypeAdd,
+			udevevent.EventTypeAdd,
 			udevevent.EventTypeRemove,
 			udevevent.EventTypeChange,
 		)
 	} else {
 		up.udeveventSubscription = udevevent.Subscribe(
-		        udevevent.EventTypeAdd,
+			udevevent.EventTypeAdd,
 			udevevent.EventTypeRemove,
 		)
 	}
diff --git a/cmd/ndm_daemonset/probe/uuid.go b/cmd/ndm_daemonset/probe/uuid.go
index 292f8a44..cd614b1b 100644
--- a/cmd/ndm_daemonset/probe/uuid.go
+++ b/cmd/ndm_daemonset/probe/uuid.go
@@ -20,6 +20,7 @@ import (
 	"os"
 
 	"github.com/openebs/node-disk-manager/blockdevice"
+	"github.com/openebs/node-disk-manager/pkg/features"
 	"github.com/openebs/node-disk-manager/pkg/util"
 
 	"k8s.io/klog"
@@ -84,6 +85,15 @@ func generateUUID(bd blockdevice.BlockDevice) (string, bool) {
 		klog.Infof("device(%s) has a filesystem, using filesystem UUID: %s", bd.DevPath, bd.FSInfo.FileSystemUUID)
 		uuidField = bd.FSInfo.FileSystemUUID
 		ok = true
+	case features.FeatureGates.IsEnabled(features.PartitionTableUUID) && len(bd.PartitionInfo.PartitionTableType) > 0:
+		if len(bd.PartitionInfo.PartitionTableUUID) == 0 {
+			klog.Errorf("device(%s) has a partition table, but can not get partition table uuid", bd.DevPath)
+			break
+		}
+
+		klog.Infof("device(%s) has a partition table, use partition table uuid: %s", bd.DevPath, bd.PartitionInfo.PartitionTableUUID)
+		uuidField = bd.PartitionInfo.PartitionTableUUID
+		ok = true
 	}
 
 	if ok {
diff --git a/deploy/helm/charts/Chart.yaml b/deploy/helm/charts/Chart.yaml
index be8bfcdd..35f05c08 100644
--- a/deploy/helm/charts/Chart.yaml
+++ b/deploy/helm/charts/Chart.yaml
@@ -1,7 +1,7 @@
 apiVersion: v2
 name: openebs-ndm
 description: Helm chart for OpenEBS Node Disk Manager - a Kubernetes native storage device management solution. For instructions on how to install, refer to https://openebs.github.io/node-disk-manager/.
-version: 1.7.1
+version: 1.7.2
 appVersion: 1.7.0
 icon: https://raw.githubusercontent.com/cncf/artwork/master/projects/openebs/icon/color/openebs-icon-color.png
 home: http://www.openebs.io/
diff --git a/deploy/helm/charts/README.md b/deploy/helm/charts/README.md
index 04a0b26d..fb146e49 100644
--- a/deploy/helm/charts/README.md
+++ b/deploy/helm/charts/README.md
@@ -42,6 +42,8 @@ The following table lists the configurable parameters of the OpenEBS NDM chart a
 | `ndm.probes.enableSeachest`             | Enable Seachest probe for NDM                 | `false`                                    |
 | `ndm.probes.enableUdevProbe`            | Enable Udev probe for NDM                     | `true`                                    |
 | `ndm.probes.enableSmartProbe`           | Enable Smart probe for NDM                    | `true`                                    |
+| `ndm.metaConfig.nodeLabelPattern`       | Config for adding node labels as BD labels    | `kubernetes.io*,beta.kubernetes.io*`      |
+| `ndm.metaConfig.deviceLabelTypes`       | Config for adding device attributes as BD labels | `.spec.details.vendor,.spec.details.model,.spec.details.driveType,.spec.filesystem.fsType`|
 | `ndmOperator.enabled`                   | Enable NDM Operator                           | `true`                                    |
 | `ndmOperator.replica`                   | Pod replica count for NDM operator            | `1`                                       |
 | `ndmOperator.upgradeStrategy`           | Update strategy NDM operator                  | `"Recreate"`                              |
diff --git a/deploy/helm/charts/templates/_helpers.tpl b/deploy/helm/charts/templates/_helpers.tpl
index ae929f51..c9755104 100644
--- a/deploy/helm/charts/templates/_helpers.tpl
+++ b/deploy/helm/charts/templates/_helpers.tpl
@@ -85,7 +85,7 @@ If release name contains chart name it will be used as a full name.
 {{- end }}
 
 {{- define "openebs-ndm.exporter.name" -}}
-{{- $ndmName := default .Chart.Name .Values.ndmExporter.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- $ndmName := .Chart.Name | trunc 63 | trimSuffix "-" }}
 {{- $componentName := "exporter" | trunc 63 | trimSuffix "-" }}
 {{- printf "%s-%s" $ndmName $componentName | trunc 63 | trimSuffix "-" }}
 {{- end }}
@@ -193,7 +193,7 @@ Create labels for ndm operator component
 Create match labels for ndm cluster exporter deployment
 */}}
 {{- define "openebs-ndm.cluster-exporter.matchLabels" -}}
-app: {{ template "openebs-ndm.cluster-exporter.name" . }}
+app: {{ template "openebs-ndm.exporter.name" . }}
 release: {{ .Release.Name }}
 component: {{ default (include "openebs-ndm.cluster-exporter.name" .) .Values.ndmExporter.clusterExporter.componentName }}
 {{- end -}}
@@ -202,7 +202,6 @@ component: {{ default (include "openebs-ndm.cluster-exporter.name" .) .Values.nd
 Create component labels for ndm cluster exporter component
 */}}
 {{- define "openebs-ndm.cluster-exporter.componentLabels" -}}
-name: {{ template "openebs-ndm.exporter.name" . }}
 openebs.io/component-name: {{ default (include "openebs-ndm.cluster-exporter.name" .) .Values.ndmExporter.clusterExporter.componentName }}
 {{- end -}}
 
@@ -220,7 +219,7 @@ Create labels for ndm cluster exporter component
 Create match labels for ndm node exporter deployment
 */}}
 {{- define "openebs-ndm.node-exporter.matchLabels" -}}
-app: {{ template "openebs-ndm.node-exporter.name" . }}
+app: {{ template "openebs-ndm.exporter.name" . }}
 release: {{ .Release.Name }}
 component: {{ default (include "openebs-ndm.node-exporter.name" .) .Values.ndmExporter.nodeExporter.componentName }}
 {{- end -}}
@@ -229,7 +228,6 @@ component: {{ default (include "openebs-ndm.node-exporter.name" .) .Values.ndmEx
 Create component labels for ndm node exporter component
 */}}
 {{- define "openebs-ndm.node-exporter.componentLabels" -}}
-name: {{ template "openebs-ndm.exporter.name" . }}
 openebs.io/component-name: {{ default (include "openebs-ndm.node-exporter.name" .) .Values.ndmExporter.nodeExporter.componentName }}
 {{- end -}}
 
diff --git a/deploy/helm/charts/templates/cluster-exporter-service.yaml b/deploy/helm/charts/templates/cluster-exporter-service.yaml
index be731fb3..719f6b4d 100644
--- a/deploy/helm/charts/templates/cluster-exporter-service.yaml
+++ b/deploy/helm/charts/templates/cluster-exporter-service.yaml
@@ -13,6 +13,6 @@ spec:
       targetPort: {{ .Values.ndmExporter.clusterExporter.metricsPort }}
   selector:
     {{- with .Values.ndmExporter.clusterExporter.podLabels }}
-      {{ toYaml . }}
-      {{- end }}
+    {{ toYaml . }}
+    {{- end }}
   {{- end }}
diff --git a/deploy/helm/charts/templates/cluster-exporter.yaml b/deploy/helm/charts/templates/cluster-exporter.yaml
index 52a0a6f8..e908696e 100644
--- a/deploy/helm/charts/templates/cluster-exporter.yaml
+++ b/deploy/helm/charts/templates/cluster-exporter.yaml
@@ -16,9 +16,9 @@ spec:
     metadata:
       labels:
         {{- include "openebs-ndm.cluster-exporter.labels" . | nindent 8 }}
-          {{- with .Values.ndmExporter.clusterExporter.podLabels }}
-          {{ toYaml . | nindent 8 }}
-          {{- end }}
+        {{- with .Values.ndmExporter.clusterExporter.podLabels }}
+        {{ toYaml . }}
+        {{- end }}
     spec:
       serviceAccountName: {{ template "openebs-ndm.serviceAccountName" . }}
       containers:
diff --git a/deploy/helm/charts/templates/configmap.yaml b/deploy/helm/charts/templates/configmap.yaml
index 337b0e59..99b814d5 100644
--- a/deploy/helm/charts/templates/configmap.yaml
+++ b/deploy/helm/charts/templates/configmap.yaml
@@ -36,3 +36,10 @@ data:
         state: {{ .Values.ndm.filters.enablePathFilter }}
         include: "{{ .Values.ndm.filters.includePaths }}"
         exclude: "{{ .Values.ndm.filters.excludePaths }}"
+    metaconfigs:
+      - key: node-labels
+        name: node labels
+        pattern: "{{ .Values.ndm.metaConfig.nodeLabelPattern }}"
+      - key: device-labels
+        name: device labels
+        type: "{{ .Values.ndm.metaConfig.deviceLabelTypes }}"
diff --git a/deploy/helm/charts/templates/daemonset.yaml b/deploy/helm/charts/templates/daemonset.yaml
index 1a733163..a6d90592 100644
--- a/deploy/helm/charts/templates/daemonset.yaml
+++ b/deploy/helm/charts/templates/daemonset.yaml
@@ -22,7 +22,7 @@ spec:
       labels:
         {{- include "openebs-ndm.labels" . | nindent 8 }}
         {{- with .Values.ndm.podLabels}}
-        {{ toYaml . | nindent 8 }}
+        {{ toYaml . }}
         {{- end}}
     spec:
       serviceAccountName: {{ template "openebs-ndm.serviceAccountName" . }}
diff --git a/deploy/helm/charts/templates/deployment.yaml b/deploy/helm/charts/templates/deployment.yaml
index 4a54dc77..3c2be99e 100644
--- a/deploy/helm/charts/templates/deployment.yaml
+++ b/deploy/helm/charts/templates/deployment.yaml
@@ -25,7 +25,7 @@ spec:
       labels:
         {{- include "openebs-ndm.operator.labels" . | nindent 8 }}
         {{- with .Values.ndmOperator.podLabels}}
-        {{ toYaml . | nindent 8 }}
+        {{ toYaml . }}
         {{- end}}
     spec:
       serviceAccountName: {{ template "openebs-ndm.serviceAccountName" . }}
diff --git a/deploy/helm/charts/templates/node-exporter-service.yaml b/deploy/helm/charts/templates/node-exporter-service.yaml
index fd4f8e11..026b77d8 100644
--- a/deploy/helm/charts/templates/node-exporter-service.yaml
+++ b/deploy/helm/charts/templates/node-exporter-service.yaml
@@ -13,6 +13,6 @@ spec:
       targetPort: {{ .Values.ndmExporter.nodeExporter.metricsPort }}
   selector:
     {{- with .Values.ndmExporter.nodeExporter.podLabels }}
-      {{ toYaml . }}
-      {{- end }}
+    {{ toYaml . }}
+    {{- end }}
   {{- end }}
diff --git a/deploy/helm/charts/templates/node-exporter.yaml b/deploy/helm/charts/templates/node-exporter.yaml
index e0082b14..b3307281 100644
--- a/deploy/helm/charts/templates/node-exporter.yaml
+++ b/deploy/helm/charts/templates/node-exporter.yaml
@@ -15,9 +15,9 @@ spec:
     metadata:
       labels:
         {{- include "openebs-ndm.node-exporter.labels" . | nindent 8 }}
-          {{- with .Values.ndmExporter.nodeExporter.podLabels }}
-          {{ toYaml . | nindent 8 }}
-          {{- end }}
+        {{- with .Values.ndmExporter.nodeExporter.podLabels }}
+        {{ toYaml . }}
+        {{- end }}
     spec:
       serviceAccountName: {{ template "openebs-ndm.serviceAccountName" . }}
       containers:
diff --git a/deploy/helm/charts/values.yaml b/deploy/helm/charts/values.yaml
index b8d79220..94029c5a 100644
--- a/deploy/helm/charts/values.yaml
+++ b/deploy/helm/charts/values.yaml
@@ -51,6 +51,9 @@ ndm:
     enableSeachest: false
     enableUdevProbe: true
     enableSmartProbe: true
+  metaConfig:
+    nodeLabelPattern: ""
+    deviceLabelTypes: ""
   healthCheck:
     initialDelaySeconds: 30
     periodSeconds: 60
diff --git a/deploy/ndm-operator.yaml b/deploy/ndm-operator.yaml
index dc6a0131..7fb502b5 100644
--- a/deploy/ndm-operator.yaml
+++ b/deploy/ndm-operator.yaml
@@ -436,6 +436,17 @@ data:
         state: true
         include: ""
         exclude: "/dev/loop,/dev/fd0,/dev/sr0,/dev/ram,/dev/md,/dev/dm-,/dev/rbd,/dev/zd"
+    # metconfig can be used to decorate the block device with different types of labels
+    # that are available on the node or come in a device properties.
+    # node labels - the node where bd is discovered. A whitlisted label prefixes
+    # attribute labels - a property of the BD can be added as a ndm label as ndm.io/<property>=<property-value>
+    metaconfigs:
+      - key: node-labels
+        name: node labels
+        pattern: ""
+      - key: device-labels
+        name: device labels
+        type: ""
 ---
 # Create NDM Service Account
 apiVersion: v1
@@ -518,6 +529,9 @@ spec:
         args:
         - -v=4
         - --feature-gates="GPTBasedUUID"
+        # Use partition table UUID instead of create single partition to get
+        # partition UUID. Require `GPTBasedUUID` to be enabled with.
+        # - --feature-gates="PartitionTableUUID"
         - --feature-gates="APIService"
         # Detect changes to device size, filesystem and mount-points without restart.
         #- --feature-gates="ChangeDetection"
@@ -711,8 +725,7 @@ metadata:
   name: ndm-cluster-exporter-service
   namespace: openebs
   labels:
-    name: openebs-ndm-exporter
-    component: openebs-ndm-cluster-exporter
+    app: openebs-ndm-exporter
 spec:
   clusterIP: None
   ports:
@@ -776,8 +789,7 @@ metadata:
   name: ndm-node-exporter-service
   namespace: openebs
   labels:
-    name: openebs-ndm-exporter
-    component: openebs-ndm-node-exporter
+    app: openebs-ndm-exporter
 spec:
   clusterIP: None
   ports:
diff --git a/deploy/yamls/ndm-cluster-exporter.yaml b/deploy/yamls/ndm-cluster-exporter.yaml
index 296daffd..fe9c2f62 100644
--- a/deploy/yamls/ndm-cluster-exporter.yaml
+++ b/deploy/yamls/ndm-cluster-exporter.yaml
@@ -51,8 +51,7 @@ metadata:
   name: ndm-cluster-exporter-service
   namespace: openebs
   labels:
-    name: openebs-ndm-exporter
-    component: openebs-ndm-cluster-exporter
+    app: openebs-ndm-exporter
 spec:
   clusterIP: None
   ports:
diff --git a/deploy/yamls/ndm-node-exporter.yaml b/deploy/yamls/ndm-node-exporter.yaml
index 54b12456..950038c5 100644
--- a/deploy/yamls/ndm-node-exporter.yaml
+++ b/deploy/yamls/ndm-node-exporter.yaml
@@ -52,8 +52,7 @@ metadata:
   name: ndm-node-exporter-service
   namespace: openebs
   labels:
-    name: openebs-ndm-exporter
-    component: openebs-ndm-node-exporter
+    app: openebs-ndm-exporter
 spec:
   clusterIP: None
   ports:
diff --git a/deploy/yamls/node-disk-manager-config.yaml b/deploy/yamls/node-disk-manager-config.yaml
index 0bd7bcdf..f78feed8 100644
--- a/deploy/yamls/node-disk-manager-config.yaml
+++ b/deploy/yamls/node-disk-manager-config.yaml
@@ -38,4 +38,15 @@ data:
         state: true
         include: ""
         exclude: "/dev/loop,/dev/fd0,/dev/sr0,/dev/ram,/dev/md,/dev/dm-,/dev/rbd,/dev/zd"
+    # metconfig can be used to decorate the block device with different types of labels
+    # that are available on the node or come in a device properties.
+    # node labels - the node where bd is discovered. A whitlisted label prefixes
+    # attribute labels - a property of the BD can be added as a ndm label as ndm.io/<property>=<property-value>
+    metaconfigs:
+      - key: node-labels
+        name: node labels
+        pattern: ""
+      - key: device-labels
+        name: device labels
+        type: ""
 ---
diff --git a/deploy/yamls/node-disk-manager.yaml b/deploy/yamls/node-disk-manager.yaml
index 150e883e..be10abf1 100644
--- a/deploy/yamls/node-disk-manager.yaml
+++ b/deploy/yamls/node-disk-manager.yaml
@@ -33,6 +33,9 @@ spec:
         args:
         - -v=4
         - --feature-gates="GPTBasedUUID"
+        # Use partition table UUID instead of create single partition to get
+        # partition UUID. Require `GPTBasedUUID` to be enabled with.
+        # - --feature-gates="PartitionTableUUID"
         - --feature-gates="APIService"
         # Detect changes to device size, filesystem and mount-points without restart.
         #- --feature-gates="ChangeDetection"
diff --git a/go.sum b/go.sum
index 318fdc5e..53054dd5 100644
--- a/go.sum
+++ b/go.sum
@@ -6,8 +6,6 @@ cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxK
 cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
 cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
 cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
-cloud.google.com/go v0.51.0 h1:PvKAVQWCtlGUSlZkGW3QLelKaWq7KYv/MW1EboG8bfM=
-cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw=
 cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
 cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
 cloud.google.com/go v0.54.0 h1:3ithwDMr7/3vpAMXiH+ZQnYbuIsh+OPhUPMFC9enmn0=
@@ -28,34 +26,18 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
 github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
 github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
 github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
-github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
-github.com/Azure/go-autorest/autorest v0.9.6 h1:5YWtOnckcudzIw8lPPBcWOnmIFWMtHci1ZWAZulMSx0=
-github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
 github.com/Azure/go-autorest/autorest v0.11.1 h1:eVvIXUKiTgv++6YnWb42DUA1YL7qDugnKP0HljexdnQ=
 github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
-github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
-github.com/Azure/go-autorest/autorest/adal v0.8.2 h1:O1X4oexUxnZCaEUGsvMnr8ZGj8HI37tNezwY4npRqA0=
-github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
 github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
 github.com/Azure/go-autorest/autorest/adal v0.9.5 h1:Y3bBUV4rTuxenJJs41HU3qmqsb+auo+a3Lz+PlJPpL0=
 github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
-github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
-github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM=
-github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
 github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
 github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
-github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
-github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
-github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc=
-github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
 github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
+github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=
 github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
-github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
-github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
 github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8Pcx+3oqrE=
 github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
-github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
-github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
 github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
 github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
@@ -63,22 +45,15 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
-github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
-github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
-github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
-github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
 github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
-github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
 github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
-github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@@ -86,7 +61,6 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
 github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
 github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
-github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
 github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
@@ -99,7 +73,6 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
 github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
-github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
 github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
@@ -113,13 +86,10 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
 github.com/diskfs/go-diskfs v1.1.1 h1:rMjLpaydtXGVZb7mdkRGK1+//30i76nKAit89zUzeaI=
 github.com/diskfs/go-diskfs v1.1.1/go.mod h1:afUPxxu+x1snp4aCY2bKR0CoZ/YFJewV3X2UEr2nPZE=
-github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
-github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
 github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
 github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
@@ -141,8 +111,6 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4
 github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
-github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -156,49 +124,13 @@ github.com/go-logr/logr v0.3.0 h1:q4c+kbcR0d5rSurhBR8dIgieOaYpXtsdTYfx22Cu6rs=
 github.com/go-logr/logr v0.3.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
 github.com/go-logr/zapr v0.2.0 h1:v6Ji8yBW77pva6NkJKQdHLAJKrIJKRHz0RXwPqCHSR4=
 github.com/go-logr/zapr v0.2.0/go.mod h1:qhKdvif7YF5GI9NWEpyxTSSBdGmzkNguibrdCNVPunU=
-github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
-github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
-github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
-github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
-github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
-github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
-github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
-github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
-github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
-github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
-github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
 github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
 github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
-github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
-github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
-github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
 github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
 github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
-github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
-github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
-github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
-github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
-github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk=
-github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
-github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
-github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
-github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
-github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
-github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
-github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
 github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
-github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
-github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
-github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
-github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
-github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
-github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
-github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
 github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
 github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
-github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
-github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
-github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
@@ -209,7 +141,6 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
 github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 h1:5ZkaAPbicIKTF2I64qf5Fh8Aa83Q/dnOafMYV0OMwjA=
 github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
 github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -229,7 +160,6 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
 github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
 github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
 github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
-github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
 github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
 github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
@@ -253,7 +183,6 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
 github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -264,7 +193,6 @@ github.com/googleapis/gnostic v0.5.1 h1:A8Yhf6EtqTv9RMsU6MQTyrtV1TjWlR6xU9BsZIwu
 github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
-github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
@@ -324,11 +252,7 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
 github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
@@ -378,7 +302,6 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
 github.com/onsi/gomega v1.10.2 h1:aY/nuoWlKJud2J6U0E3NWsjlg+0GtwXxgEqthRdzlcs=
 github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
-github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -407,7 +330,6 @@ github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB8
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
 github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
 github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
 github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
@@ -417,7 +339,6 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
-github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
@@ -431,8 +352,6 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B
 github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
 github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
-github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
-github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
 github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
 github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
@@ -441,7 +360,6 @@ github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
 github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
 github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -450,28 +368,19 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
-github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
-github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
 github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
-github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
-github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
-go.etcd.io/etcd v0.5.0-alpha.5.0.20200819165624-17cef6e3e9d5/go.mod h1:skWido08r9w6Lq/w70DO5XYIKMu4QFu1+4VsqLQuJy8=
 go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg=
-go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
-go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
-go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -494,14 +403,10 @@ go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE=
 golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -524,9 +429,9 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl
 golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE=
 golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
 golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
 golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
 golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
@@ -540,7 +445,6 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -548,11 +452,9 @@ golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73r
 golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -567,14 +469,11 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
-golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
 golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6 h1:pE8b58s1HRDMi8RDc79m0HISf9D4TzseP40cEA6IGfs=
 golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -594,7 +493,6 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h
 golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -621,8 +519,6 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4 h1:5/PjkGUjvEU5Gl6BxmvKRPpqo2uNMv4rcHBMwzk/st8=
-golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY=
 golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -630,7 +526,6 @@ golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fq
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
 golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -642,10 +537,8 @@ golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjTo
 golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
@@ -656,7 +549,6 @@ golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBn
 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
@@ -687,7 +579,6 @@ golang.org/x/tools v0.0.0-20200616133436-c1934b75d054 h1:HHeAlu5H9b71C+Fx0K+1dGg
 golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -728,17 +619,14 @@ google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4
 google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
 google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a h1:pOwg4OoaRYScjmR4LlLgdtnyoHYTSAVhhqe5uPdpII8=
 google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
-google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=
 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
 google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
@@ -750,7 +638,6 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
 google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
 google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
 google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
@@ -789,68 +676,44 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh
 honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
 honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-k8s.io/api v0.19.2 h1:q+/krnHWKsL7OBZg/rxnycsl9569Pud76UJ77MvKXms=
-k8s.io/api v0.19.2/go.mod h1:IQpK0zFQ1xc5iNIQPqzgoOwuFugaYHK4iCknlAQP9nI=
 k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo=
 k8s.io/api v0.20.2 h1:y/HR22XDZY3pniu9hIFDLpUCPq2w5eQ6aV/VFQ7uJMw=
 k8s.io/api v0.20.2/go.mod h1:d7n6Ehyzx+S+cE3VhTGfVNNqtGc/oL9DCdYYahlurV8=
-k8s.io/apiextensions-apiserver v0.19.2 h1:oG84UwiDsVDu7dlsGQs5GySmQHCzMhknfhFExJMz9tA=
-k8s.io/apiextensions-apiserver v0.19.2/go.mod h1:EYNjpqIAvNZe+svXVx9j4uBaVhTB4C94HkY3w058qcg=
 k8s.io/apiextensions-apiserver v0.20.1 h1:ZrXQeslal+6zKM/HjDXLzThlz/vPSxrfK3OqL8txgVQ=
 k8s.io/apiextensions-apiserver v0.20.1/go.mod h1:ntnrZV+6a3dB504qwC5PN/Yg9PBiDNt1EVqbW2kORVk=
-k8s.io/apimachinery v0.19.2 h1:5Gy9vQpAGTKHPVOh5c4plE274X8D/6cuEiTO2zve7tc=
-k8s.io/apimachinery v0.19.2/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
 k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
 k8s.io/apimachinery v0.20.2 h1:hFx6Sbt1oG0n6DZ+g4bFt5f6BoMkOjKWsQFu077M3Vg=
 k8s.io/apimachinery v0.20.2/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
-k8s.io/apiserver v0.19.2/go.mod h1:FreAq0bJ2vtZFj9Ago/X0oNGC51GfubKK/ViOKfVAOA=
 k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU=
-k8s.io/client-go v0.19.2 h1:gMJuU3xJZs86L1oQ99R4EViAADUPMHHtS9jFshasHSc=
-k8s.io/client-go v0.19.2/go.mod h1:S5wPhCqyDNAlzM9CnEdgTGV4OqhsW3jGO1UM1epwfJA=
 k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y=
 k8s.io/client-go v0.20.2 h1:uuf+iIAbfnCSw8IGAv/Rg0giM+2bOzHLOsbbrwrdhNQ=
 k8s.io/client-go v0.20.2/go.mod h1:kH5brqWqp7HDxUFKoEgiI4v8G1xzbe9giaCenUWJzgE=
-k8s.io/code-generator v0.19.2/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk=
 k8s.io/code-generator v0.20.1/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg=
-k8s.io/component-base v0.19.2 h1:jW5Y9RcZTb79liEhW3XDVTW7MuvEGP0tQZnfSX6/+gs=
-k8s.io/component-base v0.19.2/go.mod h1:g5LrsiTiabMLZ40AR6Hl45f088DevyGY+cCE2agEIVo=
 k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk=
 k8s.io/component-base v0.20.2 h1:LMmu5I0pLtwjpp5009KLuMGFqSc2S2isGw8t1hpYKLE=
 k8s.io/component-base v0.20.2/go.mod h1:pzFtCiwe/ASD0iV7ySMu8SYVJjCapNM9bjvk7ptpKh0=
 k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
-k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
 k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
 k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
 k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
 k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
-k8s.io/klog/v2 v2.2.0 h1:XRvcwJozkgZ1UQJmfMGpvRthQHOvihEhYtDfAaxMz/A=
 k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
 k8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ=
 k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
-k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6 h1:+WnxoVtG8TMiudHBSEtrVL1egv36TkkJm+bA8AxicmQ=
-k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
 k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd h1:sOHNzJIkytDF6qadMNKhhDRpc6ODik8lVC6nOur7B2c=
 k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
-k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
-k8s.io/utils v0.0.0-20200912215256-4140de9c8800 h1:9ZNvfPvVIEsp/T1ez4GQuzCcCTEQWhovSofhqR73A6g=
-k8s.io/utils v0.0.0-20200912215256-4140de9c8800/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
 k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
 k8s.io/utils v0.0.0-20210111153108-fddb29f9d009 h1:0T5IaWHO3sJTEmCP6mUlBvMukxPKUQWqiI/YuiBNMiQ=
 k8s.io/utils v0.0.0-20210111153108-fddb29f9d009/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
-sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.9/go.mod h1:dzAXnQbTRyDlZPJX2SUPEqvnB+j7AJjtlox7PEwigU0=
 sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
-sigs.k8s.io/controller-runtime v0.7.2 h1:gD2JZp0bBLLuvSRYVNvox+bRCz1UUUxKDjPUCb56Ukk=
-sigs.k8s.io/controller-runtime v0.7.2/go.mod h1:pJ3YBrJiAqMAZKi6UVGuE98ZrroV1p+pIhoHsMm9wdU=
 sigs.k8s.io/controller-runtime v0.8.2 h1:SBWmI0b3uzMIUD/BIXWNegrCeZmPJ503pOtwxY0LPHM=
 sigs.k8s.io/controller-runtime v0.8.2/go.mod h1:U/l+DUopBc1ecfRZ5aviA9JDmGFQKvLf5YkZNx2e0sU=
-sigs.k8s.io/structured-merge-diff/v4 v4.0.1 h1:YXTMot5Qz/X1iBRJhAt+vI+HVttY0WkSqqhKxQ0xVbA=
-sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
 sigs.k8s.io/structured-merge-diff/v4 v4.0.2 h1:YHQV7Dajm86OuqnIR6zAelnDWBRjo+YhYV9PmGrh1s8=
 sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
 sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
diff --git a/pkg/blkid/blkid.go b/pkg/blkid/blkid.go
index 596342f8..54b457de 100644
--- a/pkg/blkid/blkid.go
+++ b/pkg/blkid/blkid.go
@@ -1,3 +1,6 @@
+//go:build (linux && ignore) || cgo
+// +build linux,ignore cgo
+
 /*
 Copyright 2020 The OpenEBS Authors
 
@@ -14,8 +17,6 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-// +build linux, cgo
-
 package blkid
 
 /*
@@ -30,8 +31,9 @@ import (
 )
 
 const (
-	fsTypeIdentifier = "TYPE"
-	labelIdentifier  = "LABEL"
+	fsTypeIdentifier        = "TYPE"
+	labelIdentifier         = "LABEL"
+	partitionUUIDIdentifier = "PTUUID"
 )
 
 type DeviceIdentifier struct {
@@ -50,6 +52,12 @@ func (di *DeviceIdentifier) GetOnDiskLabel() string {
 	return di.GetTagValue(labelIdentifier)
 }
 
+// GetPartitionUUID returns the partition UUID present on the disk by reading from the disk
+// using libblkid
+func (di *DeviceIdentifier) GetPartitionUUID() string {
+	return di.GetTagValue(partitionUUIDIdentifier)
+}
+
 func (di *DeviceIdentifier) GetTagValue(tag string) string {
 	var blkidType *C.char
 	blkidType = C.CString(tag)
diff --git a/pkg/cleaner/jobcontroller.go b/pkg/cleaner/jobcontroller.go
index e391419d..be2df979 100644
--- a/pkg/cleaner/jobcontroller.go
+++ b/pkg/cleaner/jobcontroller.go
@@ -23,6 +23,7 @@ package cleaner
 import (
 	"context"
 	"fmt"
+
 	"github.com/openebs/node-disk-manager/api/v1alpha1"
 	"github.com/openebs/node-disk-manager/blockdevice"
 	"github.com/openebs/node-disk-manager/cmd/ndm_daemonset/controller"
@@ -116,14 +117,23 @@ func NewCleanupJob(bd *v1alpha1.BlockDevice, volMode VolumeMode, tolerations []v
 
 		jobContainer.Args = []string{args}
 
+		var volume v1.Volume
+		var volumeMount v1.VolumeMount
+
 		// in case of sparse disk, need to mount the sparse file directory
 		// and clear the sparse file
 		if bd.Spec.Details.DeviceType == blockdevice.SparseBlockDeviceType {
-			volume, volumeMount := getVolumeMounts(bd.Spec.Path, bd.Spec.Path, mountName)
-			jobContainer.VolumeMounts = []v1.VolumeMount{volumeMount}
-			podSpec.Volumes = []v1.Volume{volume}
+			volume, volumeMount = getVolumeMounts(bd.Spec.Path, bd.Spec.Path, mountName)
+		} else {
+			// for a non sparse disk, mount the /dev directory so that wipefs inside the container
+			// gets reflected outside the container also. When this is done a correspnding change
+			// event is also generated from udev
+			volume, volumeMount = getVolumeMounts("/dev", "/dev", mountName)
 		}
 
+		jobContainer.VolumeMounts = []v1.VolumeMount{volumeMount}
+		podSpec.Volumes = []v1.Volume{volume}
+
 	} else if volMode == VolumeModeFileSystem {
 		jobContainer.Command = []string{"/bin/sh", "-c"}
 		jobContainer.Args = []string{"find /tmp -mindepth 1 -maxdepth 1 -print0 | xargs -0 rm -rf"}
diff --git a/pkg/features/features.go b/pkg/features/features.go
index a7574977..a1809ad8 100644
--- a/pkg/features/features.go
+++ b/pkg/features/features.go
@@ -50,6 +50,12 @@ const (
 	// ChangeDetection is used to enable detecting changes to
 	// blockdevice size, filesystem, and mount-points.
 	ChangeDetection Feature = "ChangeDetection"
+
+	// PartitionTableUUID feature flag is used to enable use a
+	// partition table uuid instead of create partition described in
+	// https://github.com/openebs/node-disk-manager/issues/621 .
+	// This feature must enabled with GPTBasedUUID.
+	PartitionTableUUID Feature = "PartitionTableUUID"
 )
 
 // supportedFeatures is the list of supported features. This is used while parsing the
@@ -59,14 +65,22 @@ var supportedFeatures = []Feature{
 	APIService,
 	UseOSDisk,
 	ChangeDetection,
+	PartitionTableUUID,
 }
 
 // defaultFeatureGates is the default features that will be applied to the application
 var defaultFeatureGates = map[Feature]bool{
-	GPTBasedUUID:    true,
-	APIService:      false,
-	UseOSDisk:       false,
-	ChangeDetection: false,
+	GPTBasedUUID:       true,
+	APIService:         false,
+	UseOSDisk:          false,
+	ChangeDetection:    false,
+	PartitionTableUUID: false,
+}
+
+var featureDependencies = map[Feature][]Feature{
+	PartitionTableUUID: {
+		GPTBasedUUID,
+	},
 }
 
 // featureFlag is a map representing the flag and its state
@@ -124,9 +138,38 @@ func (fg featureFlag) SetFeatureFlag(features []string) error {
 		fg[f] = isEnabled
 	}
 
+	// We make sure features are only turned on if their dependencies are met
+	// We memoize values to avoid computing the same dependency twice
+	memoizedValues := make(featureFlag)
+	for feature := range fg {
+		_ = ValidateDependencies(feature, fg, memoizedValues)
+	}
+
 	for k, v := range fg {
 		klog.Infof("Feature gate: %s, state: %s", k, util.StateStatus(v))
 	}
 
 	return nil
 }
+
+// Ensures features are disabled if their dependencies are unmet
+// Returns true if a feature is enabled after validation
+func ValidateDependencies(feature Feature, flags featureFlag, memoizedValues featureFlag) bool {
+	if value, isMemoized := memoizedValues[feature]; isMemoized {
+		return value
+	}
+	if disabled := !flags[feature]; disabled {
+		return false
+	}
+	dependencies := featureDependencies[feature]
+	for _, dependency := range dependencies {
+		missingDependency := !ValidateDependencies(dependency, flags, memoizedValues)
+		if missingDependency {
+			flags[feature] = false
+			klog.Infof("Feature %v was set to false due to missing dependency %v", feature, dependency)
+			break
+		}
+	}
+	memoizedValues[feature] = flags[feature]
+	return flags[feature]
+}
diff --git a/pkg/partition/partition.go b/pkg/partition/partition.go
index 3e6149ad..f2c4a483 100644
--- a/pkg/partition/partition.go
+++ b/pkg/partition/partition.go
@@ -164,3 +164,40 @@ func (d *Disk) CreateSinglePartition() error {
 	klog.Infof("created a single partition on disk %s", d.DevPath)
 	return nil
 }
+
+// CreatePartitionTable create a GPT header on the disk
+func (d *Disk) CreatePartitionTable() error {
+	fd, err := diskfs.Open(d.DevPath)
+	if err != nil {
+		return fmt.Errorf("error opening disk fd for disk %s: %v", d.DevPath, err)
+	}
+	d.disk = fd
+
+	// check for any existing partition table on the disk
+	if _, err := d.disk.GetPartitionTable(); err == nil {
+		klog.Errorf("aborting partition creation, disk %s already contains a known partition table", d.DevPath)
+		return fmt.Errorf("disk %s contains a partition table, cannot create a new partition table", d.DevPath)
+	}
+
+	// check for any existing filesystem on the disk
+	deviceIdentifier := blkid.DeviceIdentifier{
+		DevPath: d.DevPath,
+	}
+	if fs := deviceIdentifier.GetOnDiskFileSystem(); len(fs) != 0 {
+		klog.Errorf("aborting partition creation, disk %s contains a known filesystem: %s", d.DevPath, fs)
+		return fmt.Errorf("disk %s contains a known filesyste: %s, cannot create a partition table", d.DevPath, fs)
+	}
+
+	err = d.createPartitionTable()
+	if err != nil {
+		klog.Error("partition table initialization failed")
+		return err
+	}
+
+	err = d.disk.Partition(d.table)
+	if err != nil {
+		return fmt.Errorf("unable to create/write partition table. %v", err)
+	}
+	klog.Infof("created partition table on disk %s", d.DevPath)
+	return nil
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 85d0e220..1320215c 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -13,8 +13,6 @@ github.com/Azure/go-autorest/autorest/date
 github.com/Azure/go-autorest/logger
 # github.com/Azure/go-autorest/tracing v0.6.0
 github.com/Azure/go-autorest/tracing
-# github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6
-## explicit
 # github.com/beorn7/perks v1.0.1
 github.com/beorn7/perks/quantile
 # github.com/cespare/xxhash/v2 v2.1.1
@@ -47,8 +45,6 @@ github.com/ghodss/yaml
 github.com/go-logr/logr
 # github.com/go-logr/zapr v0.2.0
 github.com/go-logr/zapr
-# github.com/go-openapi/validate v0.19.5
-## explicit
 # github.com/gogo/protobuf v1.3.1
 github.com/gogo/protobuf/proto
 github.com/gogo/protobuf/sortkeys
@@ -168,10 +164,6 @@ github.com/spf13/pflag
 # github.com/stretchr/testify v1.6.1
 ## explicit
 github.com/stretchr/testify/assert
-# github.com/ugorji/go v1.1.4
-## explicit
-# github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77
-## explicit
 # go.uber.org/atomic v1.6.0
 go.uber.org/atomic
 # go.uber.org/multierr v1.5.0


Test patch:
diff --git a/cmd/ndm_daemonset/controller/blockdevicestore_test.go b/cmd/ndm_daemonset/controller/blockdevicestore_test.go
index 8baeb780..907df0cf 100644
--- a/cmd/ndm_daemonset/controller/blockdevicestore_test.go
+++ b/cmd/ndm_daemonset/controller/blockdevicestore_test.go
@@ -19,9 +19,10 @@ package controller
 import (
 	"testing"
 
-	apis "github.com/openebs/node-disk-manager/api/v1alpha1"
 	"github.com/stretchr/testify/assert"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	apis "github.com/openebs/node-disk-manager/api/v1alpha1"
 )
 
 // mockEmptyDeviceCr returns BlockDevice object with minimum attributes it is used in unit test cases.
diff --git a/cmd/ndm_daemonset/controller/mockdata_test.go b/cmd/ndm_daemonset/controller/mockdata_test.go
index 6612f40d..13174a27 100644
--- a/cmd/ndm_daemonset/controller/mockdata_test.go
+++ b/cmd/ndm_daemonset/controller/mockdata_test.go
@@ -20,11 +20,12 @@ import (
 	"fmt"
 	"testing"
 
-	apis "github.com/openebs/node-disk-manager/api/v1alpha1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/client-go/kubernetes/scheme"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	ndmFakeClientset "sigs.k8s.io/controller-runtime/pkg/client/fake"
+
+	apis "github.com/openebs/node-disk-manager/api/v1alpha1"
 )
 
 const (
diff --git a/cmd/ndm_daemonset/probe/uuid_test.go b/cmd/ndm_daemonset/probe/uuid_test.go
index 14d0f448..3de64865 100644
--- a/cmd/ndm_daemonset/probe/uuid_test.go
+++ b/cmd/ndm_daemonset/probe/uuid_test.go
@@ -21,6 +21,7 @@ import (
 	"testing"
 
 	"github.com/openebs/node-disk-manager/blockdevice"
+	"github.com/openebs/node-disk-manager/pkg/features"
 	"github.com/openebs/node-disk-manager/pkg/util"
 	"github.com/stretchr/testify/assert"
 )
@@ -30,15 +31,30 @@ func TestGenerateUUID(t *testing.T) {
 	fakeSerial := "CT500MX500SSD1"
 	fakeFileSystemUUID := "149108ca-f404-4556-a263-04943e6cb0b3"
 	fakePartitionUUID := "065e2357-05"
+	fakePartitionTableUUID := "6f479331-dad4-4ccb-b146-5c359c55399b"
 	fakeLVM_DM_UUID := "LVM-j2xmqvbcVWBQK9Jdttte3CyeVTGgxtVV5VcCi3nxdwihZDxSquMOBaGL5eymBNvk"
 	fakeCRYPT_DM_UUID := "CRYPT-LUKS1-f4608c76343d4b5badaf6651d32f752b-backup"
 	loopDevicePath := "/dev/loop98"
 	hostName, _ := os.Hostname()
+	features.FeatureGates.SetFeatureFlag([]string{
+		"GPTBasedUUID=1",
+		"PartitionTableUUID=1",
+	})
 	tests := map[string]struct {
 		bd       blockdevice.BlockDevice
 		wantUUID string
 		wantOk   bool
 	}{
+		"debiceType-disk with PartitionTableUUID": {
+			bd: blockdevice.BlockDevice{
+				PartitionInfo: blockdevice.PartitionInformation{
+					PartitionTableType: "gpt",
+					PartitionTableUUID: fakePartitionTableUUID,
+				},
+			},
+			wantUUID: blockdevice.BlockDevicePrefix + util.Hash(fakePartitionTableUUID),
+			wantOk:   true,
+		},
 		"deviceType-disk with WWN": {
 			bd: blockdevice.BlockDevice{
 				DeviceAttributes: blockdevice.DeviceAttribute{
diff --git a/pkg/features/features_test.go b/pkg/features/features_test.go
index ef393445..35829150 100644
--- a/pkg/features/features_test.go
+++ b/pkg/features/features_test.go
@@ -142,3 +142,238 @@ func TestSetFeatureFlag(t *testing.T) {
 		})
 	}
 }
+
+func TestBasicFeatureDependencies(t *testing.T) {
+	firstFeature := Feature("FeatureGate1")
+	secondFeature := Feature("FeatureGate2")
+	featureDependsOnFirst := Feature("FeatureGateNeeds1")
+	featureDependsOnSecond := Feature("FeatureGateNeeds2")
+	featureDependsOnBoth := Feature("FeatureGateNeeds1And2")
+
+	supportedFeatures = []Feature{
+		firstFeature,
+		secondFeature,
+		featureDependsOnFirst,
+		featureDependsOnSecond,
+		featureDependsOnBoth,
+	}
+
+	featureDependencies = map[Feature][]Feature{
+		featureDependsOnFirst:  {firstFeature},
+		featureDependsOnSecond: {secondFeature},
+		featureDependsOnBoth:   {firstFeature, secondFeature},
+	}
+
+	type args struct {
+		features []string
+	}
+
+	tests := map[string]struct {
+		args args
+		want featureFlag
+	}{
+		"unmet dependency should fail": {
+			args: args{
+				features: []string{string(featureDependsOnFirst)},
+			},
+			want: map[Feature]bool{
+				featureDependsOnFirst: false,
+			},
+		},
+		"met dependency should succeed": {
+			args: args{
+				features: []string{string(firstFeature), string(featureDependsOnFirst)},
+			},
+			want: map[Feature]bool{
+				firstFeature:          true,
+				featureDependsOnFirst: true,
+			},
+		},
+		"one of two unmet dependency should fail": {
+			args: args{
+				features: []string{string(firstFeature), string(featureDependsOnBoth)},
+			},
+			want: map[Feature]bool{
+				firstFeature:         true,
+				featureDependsOnBoth: false,
+			},
+		},
+		"all dependencies met should succeed": {
+			args: args{
+				features: []string{
+					string(firstFeature),
+					string(secondFeature),
+					string(featureDependsOnBoth)},
+			},
+			want: map[Feature]bool{
+				firstFeature:         true,
+				secondFeature:        true,
+				featureDependsOnBoth: true,
+			},
+		},
+		"multiple features can have same dependencies": {
+			args: args{
+				features: []string{
+					string(firstFeature),
+					string(secondFeature),
+					string(featureDependsOnFirst),
+					string(featureDependsOnSecond),
+					string(featureDependsOnBoth)},
+			},
+			want: map[Feature]bool{
+				firstFeature:           true,
+				secondFeature:          true,
+				featureDependsOnFirst:  true,
+				featureDependsOnSecond: true,
+				featureDependsOnBoth:   true,
+			},
+		},
+	}
+
+	for name, test := range tests {
+		t.Run(name, func(t *testing.T) {
+			flags := make(featureFlag)
+			err := flags.SetFeatureFlag(test.args.features)
+			if err != nil {
+				t.Errorf("SetFeatureFlag() error = %v", err)
+				return
+			}
+			if !reflect.DeepEqual(flags, test.want) {
+				t.Errorf("SetFeatureFlag() got = %v, want %v", flags, test.want)
+			}
+		})
+	}
+
+}
+
+func TestNestedFeatureDependencies(t *testing.T) {
+	firstFeature := Feature("FeatureGate1")
+	secondFeature := Feature("FeatureGate2")
+	thirdFeatureNeedsBoth := Feature("FeatureGate3Needs1And2")
+	fourthFeatureNeedsThird := Feature("FeatureGate4Needs3")
+
+	supportedFeatures = []Feature{
+		firstFeature,
+		secondFeature,
+		thirdFeatureNeedsBoth,
+		fourthFeatureNeedsThird,
+	}
+
+	featureDependencies = map[Feature][]Feature{
+		thirdFeatureNeedsBoth:   {firstFeature, secondFeature},
+		fourthFeatureNeedsThird: {thirdFeatureNeedsBoth},
+	}
+
+	type args struct {
+		init   []string
+		action []string
+	}
+
+	type want struct {
+		initResults   featureFlag
+		actionResults featureFlag
+	}
+
+	tests := map[string]struct {
+		args args
+		want want
+	}{
+		"satisfied nested features should succeed": {
+			args: args{
+				init:   []string{string(firstFeature), string(secondFeature), string(thirdFeatureNeedsBoth)},
+				action: []string{string(fourthFeatureNeedsThird)},
+			},
+			want: want{
+				initResults: map[Feature]bool{
+					firstFeature:          true,
+					secondFeature:         true,
+					thirdFeatureNeedsBoth: true,
+				},
+				actionResults: map[Feature]bool{
+					firstFeature:            true,
+					secondFeature:           true,
+					thirdFeatureNeedsBoth:   true,
+					fourthFeatureNeedsThird: true,
+				},
+			},
+		},
+		"unsatisfied nested dependency should fail": {
+			args: args{
+				init:   []string{string(firstFeature)},
+				action: []string{string(thirdFeatureNeedsBoth), string(fourthFeatureNeedsThird)},
+			},
+			want: want{
+				initResults: map[Feature]bool{
+					firstFeature: true,
+				},
+				actionResults: map[Feature]bool{
+					firstFeature:            true,
+					thirdFeatureNeedsBoth:   false,
+					fourthFeatureNeedsThird: false,
+				},
+			},
+		},
+		"turning off dependency should turn off dependant": {
+			args: args{
+				init:   []string{string(firstFeature), string(secondFeature), string(thirdFeatureNeedsBoth)},
+				action: []string{string(firstFeature + "=false")},
+			},
+			want: want{
+				initResults: map[Feature]bool{
+					firstFeature:          true,
+					secondFeature:         true,
+					thirdFeatureNeedsBoth: true,
+				},
+				actionResults: map[Feature]bool{
+					firstFeature:          false,
+					secondFeature:         true,
+					thirdFeatureNeedsBoth: false,
+				},
+			},
+		},
+		"turning off dependency should turn off nested dependants": {
+			args: args{
+				init:   []string{string(firstFeature), string(secondFeature), string(thirdFeatureNeedsBoth), string(fourthFeatureNeedsThird)},
+				action: []string{string(firstFeature + "=false")},
+			},
+			want: want{
+				initResults: map[Feature]bool{
+					firstFeature:            true,
+					secondFeature:           true,
+					thirdFeatureNeedsBoth:   true,
+					fourthFeatureNeedsThird: true,
+				},
+				actionResults: map[Feature]bool{
+					firstFeature:            false,
+					secondFeature:           true,
+					thirdFeatureNeedsBoth:   false,
+					fourthFeatureNeedsThird: false,
+				},
+			},
+		},
+	}
+
+	for name, test := range tests {
+		t.Run(name, func(t *testing.T) {
+			flags := make(featureFlag)
+			initErr := flags.SetFeatureFlag(test.args.init)
+			if initErr != nil {
+				t.Errorf("SetFeatureFlag() error initializing test = %v", initErr)
+				return
+			}
+			if !reflect.DeepEqual(flags, test.want.initResults) {
+				t.Errorf("SetFeatureFlag() initializing test got = %v, want %v", flags, test.want)
+				return
+			}
+			err := flags.SetFeatureFlag(test.args.action)
+			if err != nil {
+				t.Errorf("SetFeatureFlag() error = %v", err)
+				return
+			}
+			if !reflect.DeepEqual(flags, test.want.actionResults) {
+				t.Errorf("SetFeatureFlag() got = %v, want %v", flags, test.want)
+			}
+		})
+	}
+
+}