mirror of
https://github.com/kubernetes-sigs/node-feature-discovery.git
synced 2024-12-15 17:50:49 +00:00
Merge pull request #1051 from AhmedGrati/feat-add-deny-label-ns-with-wildcard
feat: add deny-label-ns flag which supports wildcard
This commit is contained in:
commit
a92614c292
8 changed files with 107 additions and 34 deletions
|
@ -85,12 +85,15 @@ func main() {
|
||||||
func initFlags(flagset *flag.FlagSet) *master.Args {
|
func initFlags(flagset *flag.FlagSet) *master.Args {
|
||||||
args := &master.Args{
|
args := &master.Args{
|
||||||
LabelWhiteList: utils.RegexpVal{Regexp: *regexp.MustCompile("")},
|
LabelWhiteList: utils.RegexpVal{Regexp: *regexp.MustCompile("")},
|
||||||
|
DenyLabelNs: map[string]struct{}{"*.kubernetes.io": {}},
|
||||||
}
|
}
|
||||||
|
|
||||||
flagset.StringVar(&args.CaFile, "ca-file", "",
|
flagset.StringVar(&args.CaFile, "ca-file", "",
|
||||||
"Root certificate for verifying connections")
|
"Root certificate for verifying connections")
|
||||||
flagset.StringVar(&args.CertFile, "cert-file", "",
|
flagset.StringVar(&args.CertFile, "cert-file", "",
|
||||||
"Certificate used for authenticating connections")
|
"Certificate used for authenticating connections")
|
||||||
|
flagset.Var(&args.DenyLabelNs, "deny-label-ns",
|
||||||
|
"Comma separated list of denied label namespaces")
|
||||||
flagset.Var(&args.ExtraLabelNs, "extra-label-ns",
|
flagset.Var(&args.ExtraLabelNs, "extra-label-ns",
|
||||||
"Comma separated list of allowed extra label namespaces")
|
"Comma separated list of allowed extra label namespaces")
|
||||||
flagset.StringVar(&args.Instance, "instance", "",
|
flagset.StringVar(&args.Instance, "instance", "",
|
||||||
|
|
|
@ -86,6 +86,9 @@ spec:
|
||||||
{{- if .Values.master.extraLabelNs | empty | not }}
|
{{- if .Values.master.extraLabelNs | empty | not }}
|
||||||
- "-extra-label-ns={{- join "," .Values.master.extraLabelNs }}"
|
- "-extra-label-ns={{- join "," .Values.master.extraLabelNs }}"
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- if .Values.master.denyLabelNs | empty | not }}
|
||||||
|
- "-deny-label-ns={{- join "," .Values.master.denyLabelNs }}"
|
||||||
|
{{- end }}
|
||||||
{{- if .Values.master.resourceLabels | empty | not }}
|
{{- if .Values.master.resourceLabels | empty | not }}
|
||||||
- "-resource-labels={{- join "," .Values.master.resourceLabels }}"
|
- "-resource-labels={{- join "," .Values.master.resourceLabels }}"
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
|
@ -17,6 +17,7 @@ master:
|
||||||
port: 8080
|
port: 8080
|
||||||
instance:
|
instance:
|
||||||
featureApi:
|
featureApi:
|
||||||
|
denyLabelNs: []
|
||||||
extraLabelNs: []
|
extraLabelNs: []
|
||||||
resourceLabels: []
|
resourceLabels: []
|
||||||
crdController: null
|
crdController: null
|
||||||
|
|
|
@ -216,12 +216,10 @@ nfd-master -label-whitelist='.*cpuid\.'
|
||||||
### -extra-label-ns
|
### -extra-label-ns
|
||||||
|
|
||||||
The `-extra-label-ns` flag specifies a comma-separated list of allowed feature
|
The `-extra-label-ns` flag specifies a comma-separated list of allowed feature
|
||||||
label namespaces. By default, nfd-master only allows creating labels in the
|
label namespaces. This option can be used to allow
|
||||||
default `feature.node.kubernetes.io` and `profile.node.kubernetes.io` label
|
|
||||||
namespaces and their sub-namespaces (e.g. `vendor.feature.node.kubernetes.io`
|
|
||||||
and `sub.ns.profile.node.kubernetes.io`). This option can be used to allow
|
|
||||||
other vendor or application specific namespaces for custom labels from the
|
other vendor or application specific namespaces for custom labels from the
|
||||||
local and custom feature sources.
|
local and custom feature sources, even though these labels were denied using
|
||||||
|
the `deny-label-ns` flag.
|
||||||
|
|
||||||
The same namespace control and this flag applies Extended Resources (created
|
The same namespace control and this flag applies Extended Resources (created
|
||||||
with `-resource-labels`), too.
|
with `-resource-labels`), too.
|
||||||
|
@ -234,6 +232,28 @@ Example:
|
||||||
nfd-master -extra-label-ns=vendor-1.com,vendor-2.io
|
nfd-master -extra-label-ns=vendor-1.com,vendor-2.io
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### -deny-label-ns
|
||||||
|
|
||||||
|
The `-deny-label-ns` flag specifies a comma-separated list of excluded
|
||||||
|
label namespaces. By default, nfd-master allows creating labels in all
|
||||||
|
namespaces, excluding `kubernetes.io` namespace and its sub-namespaces
|
||||||
|
(i.e. `*.kubernetes.io`). However, you should note that
|
||||||
|
`kubernetes.io` and its sub-namespaces are always denied.
|
||||||
|
For example, `nfd-master -deny-label-ns=""` would still disallow
|
||||||
|
`kubernetes.io` and `*.kubernetes.io`.
|
||||||
|
This option can be used to exclude some vendors or application specific
|
||||||
|
namespaces.
|
||||||
|
Note that the namespaces `feature.node.kubernetes.io` and `profile.node.kubernetes.io`
|
||||||
|
and their sub-namespaces are always allowed and cannot be denied.
|
||||||
|
|
||||||
|
Default: *empty*
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nfd-master -deny-label-ns=*.vendor.com,vendor-2.io
|
||||||
|
```
|
||||||
|
|
||||||
### -resource-labels
|
### -resource-labels
|
||||||
|
|
||||||
The `-resource-labels` flag specifies a comma-separated list of features to be
|
The `-resource-labels` flag specifies a comma-separated list of features to be
|
||||||
|
|
|
@ -249,9 +249,6 @@ feature.node.kubernetes.io/my-feature.2: "myvalue"
|
||||||
my.namespace/my-feature.3: "456"
|
my.namespace/my-feature.3: "456"
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that in the example above `-extra-label-ns=my.namespace` must be specified
|
|
||||||
on the nfd-master command line.
|
|
||||||
|
|
||||||
### Hooks
|
### Hooks
|
||||||
|
|
||||||
**DEPRECATED** The `local` source executes hooks found in
|
**DEPRECATED** The `local` source executes hooks found in
|
||||||
|
@ -303,11 +300,7 @@ key-value pairs, separated by newlines:
|
||||||
|
|
||||||
The label value defaults to `true`, if not specified.
|
The label value defaults to `true`, if not specified.
|
||||||
|
|
||||||
Label namespace may be specified with `<namespace>/<name>[=<value>]`. The
|
Label namespace may be specified with `<namespace>/<name>[=<value>]`.
|
||||||
namespace must be explicitly allowed with the `-extra-label-ns` command line
|
|
||||||
flag of nfd-master if using something else than
|
|
||||||
`[<sub-ns>.]feature.node.kubernetes.io` or
|
|
||||||
`[<sub-ns>.]profile.node.kubernetes.io`.
|
|
||||||
|
|
||||||
### Mounts
|
### Mounts
|
||||||
|
|
||||||
|
@ -414,13 +407,13 @@ The namespace part (i.e. prefix) of the labels is controlled by nfd:
|
||||||
|
|
||||||
- All built-in labels use `feature.node.kubernetes.io`. This is also
|
- All built-in labels use `feature.node.kubernetes.io`. This is also
|
||||||
the default for user defined features that don't specify any namespace.
|
the default for user defined features that don't specify any namespace.
|
||||||
- User-defined labels are allowed to use:
|
- Namespaces may be excluded with the
|
||||||
- `feature.node.kubernetes.io` and `profile.node.kubernetes.io` plus their
|
[`-deny-label-ns`](../reference/master-commandline-reference.md#-deny-label-ns)
|
||||||
sub-namespaces (e.g. `vendor.profile.node.kubernetes.io` and
|
|
||||||
`sub.ns.profile.node.kubernetes.io`) by default
|
|
||||||
- Additional namespaces may be enabled with the
|
|
||||||
[`-extra-label-ns`](../reference/master-commandline-reference.md#-extra-label-ns)
|
|
||||||
command line flag of nfd-master
|
command line flag of nfd-master
|
||||||
|
- To allow specific namespaces that were denied, you can use
|
||||||
|
[`-extra-label-ns`](../reference/master-commandline-reference.md#-extra-label-ns)
|
||||||
|
command line flag of nfd-master.
|
||||||
|
e.g: `nfd-master -deny-label-ns="*" -extra-label-ns=example.com`
|
||||||
|
|
||||||
## Label rule format
|
## Label rule format
|
||||||
|
|
||||||
|
|
|
@ -352,16 +352,18 @@ func TestSetLabels(t *testing.T) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("When -extra-label-ns and -instance are specified", func() {
|
Convey("When -extra-label-ns, -deny-label-ns and -instance are specified", func() {
|
||||||
// In the gRPC request the label names may omit the default ns
|
// In the gRPC request the label names may omit the default ns
|
||||||
instance := "foo"
|
instance := "foo"
|
||||||
vendorFeatureLabel := "vendor." + nfdv1alpha1.FeatureLabelNs + "/feature-4"
|
vendorFeatureLabel := "vendor." + nfdv1alpha1.FeatureLabelNs + "/feature-4"
|
||||||
vendorProfileLabel := "vendor." + nfdv1alpha1.ProfileLabelNs + "/feature-5"
|
vendorProfileLabel := "vendor." + nfdv1alpha1.ProfileLabelNs + "/feature-5"
|
||||||
mockLabels := map[string]string{"feature-1": "val-1",
|
mockLabels := map[string]string{"feature-1": "val-1",
|
||||||
"valid.ns/feature-2": "val-2",
|
"valid.ns/feature-2": "val-2",
|
||||||
"invalid.ns/feature-3": "val-3",
|
"random.denied.ns/feature-3": "val-3",
|
||||||
vendorFeatureLabel: " val-4",
|
"kubernetes.io/feature-4": "val-4",
|
||||||
vendorProfileLabel: " val-5"}
|
"sub.ns.kubernetes.io/feature-5": "val-5",
|
||||||
|
vendorFeatureLabel: " val-6",
|
||||||
|
vendorProfileLabel: " val-7"}
|
||||||
expectedPatches := []apihelper.JsonPatch{
|
expectedPatches := []apihelper.JsonPatch{
|
||||||
apihelper.NewJsonPatch("add", "/metadata/annotations", instance+"."+nfdv1alpha1.WorkerVersionAnnotation, workerVer),
|
apihelper.NewJsonPatch("add", "/metadata/annotations", instance+"."+nfdv1alpha1.WorkerVersionAnnotation, workerVer),
|
||||||
apihelper.NewJsonPatch("add", "/metadata/annotations",
|
apihelper.NewJsonPatch("add", "/metadata/annotations",
|
||||||
|
@ -374,6 +376,8 @@ func TestSetLabels(t *testing.T) {
|
||||||
apihelper.NewJsonPatch("add", "/metadata/labels", vendorProfileLabel, mockLabels[vendorProfileLabel]),
|
apihelper.NewJsonPatch("add", "/metadata/labels", vendorProfileLabel, mockLabels[vendorProfileLabel]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mockMaster.deniedNs.normal = map[string]struct{}{"random.denied.ns": {}}
|
||||||
|
mockMaster.deniedNs.wildcard = map[string]struct{}{"kubernetes.io": {}}
|
||||||
mockMaster.args.ExtraLabelNs = map[string]struct{}{"valid.ns": {}}
|
mockMaster.args.ExtraLabelNs = map[string]struct{}{"valid.ns": {}}
|
||||||
mockMaster.args.Instance = instance
|
mockMaster.args.Instance = instance
|
||||||
mockHelper.On("GetClient").Return(mockClient, nil)
|
mockHelper.On("GetClient").Return(mockClient, nil)
|
||||||
|
|
|
@ -63,6 +63,7 @@ type Annotations map[string]string
|
||||||
type Args struct {
|
type Args struct {
|
||||||
CaFile string
|
CaFile string
|
||||||
CertFile string
|
CertFile string
|
||||||
|
DenyLabelNs utils.StringSetVal
|
||||||
ExtraLabelNs utils.StringSetVal
|
ExtraLabelNs utils.StringSetVal
|
||||||
Instance string
|
Instance string
|
||||||
KeyFile string
|
KeyFile string
|
||||||
|
@ -78,6 +79,11 @@ type Args struct {
|
||||||
ResourceLabels utils.StringSetVal
|
ResourceLabels utils.StringSetVal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type deniedNs struct {
|
||||||
|
normal utils.StringSetVal
|
||||||
|
wildcard utils.StringSetVal
|
||||||
|
}
|
||||||
|
|
||||||
type NfdMaster interface {
|
type NfdMaster interface {
|
||||||
Run() error
|
Run() error
|
||||||
Stop()
|
Stop()
|
||||||
|
@ -95,6 +101,7 @@ type nfdMaster struct {
|
||||||
ready chan bool
|
ready chan bool
|
||||||
apihelper apihelper.APIHelpers
|
apihelper apihelper.APIHelpers
|
||||||
kubeconfig *restclient.Config
|
kubeconfig *restclient.Config
|
||||||
|
deniedNs
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewNfdMaster creates a new NfdMaster server instance.
|
// NewNfdMaster creates a new NfdMaster server instance.
|
||||||
|
@ -126,6 +133,16 @@ func NewNfdMaster(args *Args) (NfdMaster, error) {
|
||||||
return nfd, fmt.Errorf("-ca-file needs to be specified alongside -cert-file and -key-file")
|
return nfd, fmt.Errorf("-ca-file needs to be specified alongside -cert-file and -key-file")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if args.DenyLabelNs == nil {
|
||||||
|
args.DenyLabelNs = make(utils.StringSetVal)
|
||||||
|
}
|
||||||
|
// Pre-process DenyLabelNS into 2 lists: one for normal ns, and the other for wildcard ns
|
||||||
|
normalDeniedNs, wildcardDeniedNs := preProcessDeniedNamespaces(args.DenyLabelNs)
|
||||||
|
nfd.deniedNs.normal = normalDeniedNs
|
||||||
|
nfd.deniedNs.wildcard = wildcardDeniedNs
|
||||||
|
// We forcibly deny kubernetes.io
|
||||||
|
nfd.deniedNs.normal["kubernetes.io"] = struct{}{}
|
||||||
|
nfd.deniedNs.wildcard[".kubernetes.io"] = struct{}{}
|
||||||
|
|
||||||
// Initialize Kubernetes API helpers
|
// Initialize Kubernetes API helpers
|
||||||
if !args.NoPublish {
|
if !args.NoPublish {
|
||||||
|
@ -392,7 +409,7 @@ func (m *nfdMaster) updateMasterNode() error {
|
||||||
// into extended resources. This function also handles proper namespacing of
|
// into extended resources. This function also handles proper namespacing of
|
||||||
// labels and ERs, i.e. adds the possibly missing default namespace for labels
|
// labels and ERs, i.e. adds the possibly missing default namespace for labels
|
||||||
// arriving through the gRPC API.
|
// arriving through the gRPC API.
|
||||||
func filterFeatureLabels(labels Labels, extraLabelNs map[string]struct{}, labelWhiteList regexp.Regexp, extendedResourceNames map[string]struct{}) (Labels, ExtendedResources) {
|
func (m *nfdMaster) filterFeatureLabels(labels Labels) (Labels, ExtendedResources) {
|
||||||
outLabels := Labels{}
|
outLabels := Labels{}
|
||||||
|
|
||||||
for label, value := range labels {
|
for label, value := range labels {
|
||||||
|
@ -404,15 +421,18 @@ func filterFeatureLabels(labels Labels, extraLabelNs map[string]struct{}, labelW
|
||||||
// Check label namespace, filter out if ns is not whitelisted
|
// Check label namespace, filter out if ns is not whitelisted
|
||||||
if ns != nfdv1alpha1.FeatureLabelNs && ns != nfdv1alpha1.ProfileLabelNs &&
|
if ns != nfdv1alpha1.FeatureLabelNs && ns != nfdv1alpha1.ProfileLabelNs &&
|
||||||
!strings.HasSuffix(ns, nfdv1alpha1.FeatureLabelSubNsSuffix) && !strings.HasSuffix(ns, nfdv1alpha1.ProfileLabelSubNsSuffix) {
|
!strings.HasSuffix(ns, nfdv1alpha1.FeatureLabelSubNsSuffix) && !strings.HasSuffix(ns, nfdv1alpha1.ProfileLabelSubNsSuffix) {
|
||||||
if _, ok := extraLabelNs[ns]; !ok {
|
// If the namespace is denied, and not present in the extraLabelNs, label will be ignored
|
||||||
|
if isNamespaceDenied(ns, m.deniedNs.wildcard, m.deniedNs.normal) {
|
||||||
|
if _, ok := m.args.ExtraLabelNs[ns]; !ok {
|
||||||
klog.Errorf("Namespace %q is not allowed. Ignoring label %q\n", ns, label)
|
klog.Errorf("Namespace %q is not allowed. Ignoring label %q\n", ns, label)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Skip if label doesn't match labelWhiteList
|
// Skip if label doesn't match labelWhiteList
|
||||||
if !labelWhiteList.MatchString(name) {
|
if !m.args.LabelWhiteList.Regexp.MatchString(name) {
|
||||||
klog.Errorf("%s (%s) does not match the whitelist (%s) and will not be published.", name, label, labelWhiteList.String())
|
klog.Errorf("%s (%s) does not match the whitelist (%s) and will not be published.", name, label, m.args.LabelWhiteList.Regexp.String())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
outLabels[label] = value
|
outLabels[label] = value
|
||||||
|
@ -420,7 +440,7 @@ func filterFeatureLabels(labels Labels, extraLabelNs map[string]struct{}, labelW
|
||||||
|
|
||||||
// Remove labels which are intended to be extended resources
|
// Remove labels which are intended to be extended resources
|
||||||
extendedResources := ExtendedResources{}
|
extendedResources := ExtendedResources{}
|
||||||
for extendedResourceName := range extendedResourceNames {
|
for extendedResourceName := range m.args.ResourceLabels {
|
||||||
// Add possibly missing default ns
|
// Add possibly missing default ns
|
||||||
extendedResourceName = addNs(extendedResourceName, nfdv1alpha1.FeatureLabelNs)
|
extendedResourceName = addNs(extendedResourceName, nfdv1alpha1.FeatureLabelNs)
|
||||||
if value, ok := outLabels[extendedResourceName]; ok {
|
if value, ok := outLabels[extendedResourceName]; ok {
|
||||||
|
@ -449,6 +469,20 @@ func verifyNodeName(cert *x509.Certificate, nodeName string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isNamespaceDenied(labelNs string, wildcardDeniedNs map[string]struct{}, normalDeniedNs map[string]struct{}) bool {
|
||||||
|
for deniedNs := range normalDeniedNs {
|
||||||
|
if labelNs == deniedNs {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for deniedNs := range wildcardDeniedNs {
|
||||||
|
if strings.HasSuffix(labelNs, deniedNs) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// SetLabels implements LabelerServer
|
// SetLabels implements LabelerServer
|
||||||
func (m *nfdMaster) SetLabels(c context.Context, r *pb.SetLabelsRequest) (*pb.SetLabelsReply, error) {
|
func (m *nfdMaster) SetLabels(c context.Context, r *pb.SetLabelsRequest) (*pb.SetLabelsReply, error) {
|
||||||
err := authorizeClient(c, m.args.VerifyNodeName, r.NodeName)
|
err := authorizeClient(c, m.args.VerifyNodeName, r.NodeName)
|
||||||
|
@ -583,7 +617,7 @@ func (m *nfdMaster) refreshNodeFeatures(cli *kubernetes.Clientset, nodeName stri
|
||||||
labels[k] = v
|
labels[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
labels, extendedResources := filterFeatureLabels(labels, m.args.ExtraLabelNs, m.args.LabelWhiteList.Regexp, m.args.ResourceLabels)
|
labels, extendedResources := m.filterFeatureLabels(labels)
|
||||||
|
|
||||||
var taints []corev1.Taint
|
var taints []corev1.Taint
|
||||||
if m.args.EnableTaints {
|
if m.args.EnableTaints {
|
||||||
|
@ -918,6 +952,22 @@ func stringToNsNames(cslist, ns string) []string {
|
||||||
return names
|
return names
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Seperate denied namespaces into two lists:
|
||||||
|
// one contains wildcard namespaces the other contains normal namespaces
|
||||||
|
func preProcessDeniedNamespaces(deniedNs map[string]struct{}) (normalDeniedNs map[string]struct{}, wildcardDeniedNs map[string]struct{}) {
|
||||||
|
normalDeniedNs = map[string]struct{}{}
|
||||||
|
wildcardDeniedNs = map[string]struct{}{}
|
||||||
|
for ns := range deniedNs {
|
||||||
|
if strings.HasPrefix(ns, "*") {
|
||||||
|
trimedNs := strings.TrimLeft(ns, "*")
|
||||||
|
wildcardDeniedNs[trimedNs] = struct{}{}
|
||||||
|
} else {
|
||||||
|
normalDeniedNs[ns] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (m *nfdMaster) instanceAnnotation(name string) string {
|
func (m *nfdMaster) instanceAnnotation(name string) string {
|
||||||
if m.args.Instance == "" {
|
if m.args.Instance == "" {
|
||||||
return name
|
return name
|
||||||
|
|
|
@ -76,8 +76,7 @@ func (a *StringSetVal) String() string {
|
||||||
if *a == nil {
|
if *a == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
vals := make([]string, 0, len(*a))
|
||||||
vals := make([]string, len(*a), 0)
|
|
||||||
for val := range *a {
|
for val := range *a {
|
||||||
vals = append(vals, val)
|
vals = append(vals, val)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue