mirror of
https://github.com/enix/x509-certificate-exporter.git
synced 2026-02-14 10:00:00 +00:00
feat: include or exclude namespaces to watch based on their labels
This commit is contained in:
committed by
Paul Laffitte
parent
5bc2da352f
commit
fc47f9b1ab
@@ -69,6 +69,12 @@ func main() {
|
||||
kubeExcludeNamespaces := stringArrayFlag{}
|
||||
getopt.FlagLong(&kubeExcludeNamespaces, "exclude-namespace", 0, "removes the given kube namespace from the watch list (applied after --include-namespace)")
|
||||
|
||||
kubeIncludeNamespaceLabels := stringArrayFlag{}
|
||||
getopt.FlagLong(&kubeIncludeNamespaceLabels, "include-namespace-label", 0, "add the kube namespaces with the given label (or label value if specified) to the watch list (when used, all namespaces are excluded by default)")
|
||||
|
||||
kubeExcludeNamespaceLabels := stringArrayFlag{}
|
||||
getopt.FlagLong(&kubeExcludeNamespaceLabels, "exclude-namespace-label", 0, "removes the kube namespaces with the given label (or label value if specified) from the watch list (applied after --include-namespace-label)")
|
||||
|
||||
kubeIncludeLabels := stringArrayFlag{}
|
||||
getopt.FlagLong(&kubeIncludeLabels, "include-label", 0, "add the kube secrets with the given label (or label value if specified) to the watch list (when used, all secrets are excluded by default)")
|
||||
|
||||
@@ -128,23 +134,25 @@ func main() {
|
||||
}
|
||||
|
||||
exporter := internal.Exporter{
|
||||
ListenAddress: *listenAddress,
|
||||
SystemdSocket: *systemdSocket,
|
||||
ConfigFile: *configFile,
|
||||
Files: files,
|
||||
Directories: directories,
|
||||
YAMLs: yamls,
|
||||
YAMLPaths: internal.DefaultYamlPaths,
|
||||
TrimPathComponents: *trimPathComponents,
|
||||
MaxCacheDuration: time.Duration(maxCacheDuration),
|
||||
ExposeRelativeMetrics: *exposeRelativeMetrics,
|
||||
ExposeErrorMetrics: *exposeErrorMetrics,
|
||||
KubeSecretTypes: kubeSecretTypes,
|
||||
ConfigMapKeys: kubeConfigMapKeys,
|
||||
KubeIncludeNamespaces: kubeIncludeNamespaces,
|
||||
KubeExcludeNamespaces: kubeExcludeNamespaces,
|
||||
KubeIncludeLabels: kubeIncludeLabels,
|
||||
KubeExcludeLabels: kubeExcludeLabels,
|
||||
ListenAddress: *listenAddress,
|
||||
SystemdSocket: *systemdSocket,
|
||||
ConfigFile: *configFile,
|
||||
Files: files,
|
||||
Directories: directories,
|
||||
YAMLs: yamls,
|
||||
YAMLPaths: internal.DefaultYamlPaths,
|
||||
TrimPathComponents: *trimPathComponents,
|
||||
MaxCacheDuration: time.Duration(maxCacheDuration),
|
||||
ExposeRelativeMetrics: *exposeRelativeMetrics,
|
||||
ExposeErrorMetrics: *exposeErrorMetrics,
|
||||
KubeSecretTypes: kubeSecretTypes,
|
||||
ConfigMapKeys: kubeConfigMapKeys,
|
||||
KubeIncludeNamespaces: kubeIncludeNamespaces,
|
||||
KubeExcludeNamespaces: kubeExcludeNamespaces,
|
||||
KubeIncludeNamespaceLabels: kubeIncludeNamespaceLabels,
|
||||
KubeExcludeNamespaceLabels: kubeExcludeNamespaceLabels,
|
||||
KubeIncludeLabels: kubeIncludeLabels,
|
||||
KubeExcludeLabels: kubeExcludeLabels,
|
||||
}
|
||||
|
||||
if getopt.Lookup("expose-labels").Seen() {
|
||||
|
||||
@@ -114,6 +114,12 @@ spec:
|
||||
{{- range .Values.secretsExporter.excludeNamespaces }}
|
||||
- --exclude-namespace={{ . | trim }}
|
||||
{{- end }}
|
||||
{{- range .Values.secretsExporter.includeNamespaceLabels }}
|
||||
- --include-namespace-label={{ . | trim }}
|
||||
{{- end }}
|
||||
{{- range .Values.secretsExporter.excludeNamespaceLabels }}
|
||||
- --exclude-namespace-label={{ . | trim }}
|
||||
{{- end }}
|
||||
{{- range .Values.secretsExporter.includeLabels }}
|
||||
- --include-label={{ . | trim }}
|
||||
{{- end }}
|
||||
|
||||
@@ -122,6 +122,10 @@ secretsExporter:
|
||||
includeNamespaces: []
|
||||
# -- Exclude namespaces from being scanned by the TLS Secrets exporter (evaluated after `includeNamespaces`)
|
||||
excludeNamespaces: []
|
||||
# -- Only watch namespaces having these labels (all namespaces if empty). Items can be keys such as `my-label` or also require a value with syntax `my-label=my-value`.
|
||||
includeNamespaceLabels: []
|
||||
# -- Exclude namespaces having these labels. Items can be keys such as `my-label` or also require a value with syntax `my-label=my-value`.
|
||||
excludeNamespaceLabels: []
|
||||
# -- Only watch TLS Secrets having these labels (all secrets if empty). Items can be keys such as `my-label` or also require a value with syntax `my-label=my-value`.
|
||||
includeLabels: []
|
||||
# -- Exclude TLS Secrets having these labels. Items can be keys such as `my-label` or also require a value with syntax `my-label=my-value`.
|
||||
|
||||
@@ -27,24 +27,26 @@ import (
|
||||
|
||||
// Exporter : Configuration (from command-line)
|
||||
type Exporter struct {
|
||||
ListenAddress string
|
||||
SystemdSocket bool
|
||||
ConfigFile string
|
||||
Files []string
|
||||
Directories []string
|
||||
YAMLs []string
|
||||
YAMLPaths []YAMLCertRef
|
||||
TrimPathComponents int
|
||||
MaxCacheDuration time.Duration
|
||||
ExposeRelativeMetrics bool
|
||||
ExposeErrorMetrics bool
|
||||
ExposeLabels []string
|
||||
ConfigMapKeys []string
|
||||
KubeSecretTypes []KubeSecretType
|
||||
KubeIncludeNamespaces []string
|
||||
KubeExcludeNamespaces []string
|
||||
KubeIncludeLabels []string
|
||||
KubeExcludeLabels []string
|
||||
ListenAddress string
|
||||
SystemdSocket bool
|
||||
ConfigFile string
|
||||
Files []string
|
||||
Directories []string
|
||||
YAMLs []string
|
||||
YAMLPaths []YAMLCertRef
|
||||
TrimPathComponents int
|
||||
MaxCacheDuration time.Duration
|
||||
ExposeRelativeMetrics bool
|
||||
ExposeErrorMetrics bool
|
||||
ExposeLabels []string
|
||||
ConfigMapKeys []string
|
||||
KubeSecretTypes []KubeSecretType
|
||||
KubeIncludeNamespaces []string
|
||||
KubeExcludeNamespaces []string
|
||||
KubeIncludeNamespaceLabels []string
|
||||
KubeExcludeNamespaceLabels []string
|
||||
KubeIncludeLabels []string
|
||||
KubeExcludeLabels []string
|
||||
|
||||
kubeClient *kubernetes.Clientset
|
||||
listener net.Listener
|
||||
|
||||
@@ -109,36 +109,72 @@ func (exporter *Exporter) parseAllKubeObjects() ([]*certificateRef, []error) {
|
||||
}
|
||||
|
||||
func (exporter *Exporter) listNamespacesToWatch() ([]string, error) {
|
||||
includedNamespaces := exporter.KubeIncludeNamespaces
|
||||
|
||||
if len(includedNamespaces) < 1 {
|
||||
allNamespaces, err := exporter.kubeClient.CoreV1().Namespaces().List(context.Background(), metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, ns := range allNamespaces.Items {
|
||||
includedNamespaces = append(includedNamespaces, ns.Name)
|
||||
}
|
||||
_, includedLabelsWithValue := exporter.prepareLabelFilters(exporter.KubeIncludeNamespaceLabels)
|
||||
labelSelector := metav1.LabelSelector{MatchLabels: includedLabelsWithValue}
|
||||
namespaces, err := exporter.kubeClient.CoreV1().Namespaces().List(context.Background(), metav1.ListOptions{
|
||||
LabelSelector: labels.Set(labelSelector.MatchLabels).String(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
namespaces := []string{}
|
||||
for _, includeNs := range includedNamespaces {
|
||||
found := false
|
||||
return exporter.filterNamespaces(namespaces.Items), nil
|
||||
}
|
||||
|
||||
func (exporter *Exporter) filterNamespaces(namespaces []v1.Namespace) []string {
|
||||
filteredNamespaces := []*v1.Namespace{}
|
||||
for _, namespace := range namespaces {
|
||||
found := false
|
||||
for _, includeNs := range exporter.KubeIncludeNamespaces {
|
||||
if namespace.Name == includeNs {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(exporter.KubeIncludeNamespaces) > 0 && !found {
|
||||
continue
|
||||
}
|
||||
|
||||
found = false
|
||||
for _, excludeNs := range exporter.KubeExcludeNamespaces {
|
||||
if includeNs == excludeNs {
|
||||
if namespace.Name == excludeNs {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
namespaces = append(namespaces, includeNs)
|
||||
filteredNamespaces = append(filteredNamespaces, &namespace)
|
||||
}
|
||||
}
|
||||
|
||||
return namespaces, nil
|
||||
includedLabels, _ := exporter.prepareLabelFilters(exporter.KubeIncludeNamespaceLabels)
|
||||
excludedLabels, excludedLabelsWithValue := exporter.prepareLabelFilters(exporter.KubeExcludeNamespaceLabels)
|
||||
filteredNamespaces = filterObjects(filteredNamespaces, includedLabels, excludedLabels, excludedLabelsWithValue)
|
||||
|
||||
namespacesNames := []string{}
|
||||
for _, namespace := range filteredNamespaces {
|
||||
namespacesNames = append(namespacesNames, namespace.Name)
|
||||
}
|
||||
|
||||
return namespacesNames
|
||||
}
|
||||
|
||||
func (exporter *Exporter) prepareLabelFilters(labels []string) ([]string, map[string]string) {
|
||||
labelsWithValue := map[string]string{}
|
||||
labelsWithoutValue := []string{}
|
||||
|
||||
for _, label := range labels {
|
||||
parts := strings.Split(label, "=")
|
||||
if len(parts) < 2 {
|
||||
labelsWithoutValue = append(labelsWithoutValue, label)
|
||||
} else {
|
||||
labelsWithValue[parts[0]] = parts[1]
|
||||
}
|
||||
}
|
||||
|
||||
return labelsWithoutValue, labelsWithValue
|
||||
}
|
||||
|
||||
func (exporter *Exporter) getWatchedConfigMaps(namespace string) ([]v1.ConfigMap, error) {
|
||||
@@ -163,28 +199,7 @@ func (exporter *Exporter) getWatchedSecrets(namespace string) ([]v1.Secret, erro
|
||||
return cachedSecrets.([]v1.Secret), nil
|
||||
}
|
||||
|
||||
includedLabelsWithValue := map[string]string{}
|
||||
includedLabelsWithoutValue := []string{}
|
||||
for _, label := range exporter.KubeIncludeLabels {
|
||||
parts := strings.Split(label, "=")
|
||||
if len(parts) < 2 {
|
||||
includedLabelsWithoutValue = append(includedLabelsWithoutValue, label)
|
||||
} else {
|
||||
includedLabelsWithValue[parts[0]] = parts[1]
|
||||
}
|
||||
}
|
||||
|
||||
excludedLabelsWithValue := map[string]string{}
|
||||
excludedLabelsWithoutValue := []string{}
|
||||
for _, label := range exporter.KubeExcludeLabels {
|
||||
parts := strings.Split(label, "=")
|
||||
if len(parts) < 2 {
|
||||
excludedLabelsWithoutValue = append(excludedLabelsWithoutValue, label)
|
||||
} else {
|
||||
excludedLabelsWithValue[parts[0]] = parts[1]
|
||||
}
|
||||
}
|
||||
|
||||
_, includedLabelsWithValue := exporter.prepareLabelFilters(exporter.KubeIncludeLabels)
|
||||
labelSelector := metav1.LabelSelector{MatchLabels: includedLabelsWithValue}
|
||||
secrets, err := exporter.kubeClient.CoreV1().Secrets(namespace).List(context.Background(), metav1.ListOptions{
|
||||
LabelSelector: labels.Set(labelSelector.MatchLabels).String(),
|
||||
@@ -193,7 +208,7 @@ func (exporter *Exporter) getWatchedSecrets(namespace string) ([]v1.Secret, erro
|
||||
return nil, err
|
||||
}
|
||||
|
||||
filteredSecrets, err := exporter.filterSecrets(secrets.Items, includedLabelsWithoutValue, excludedLabelsWithoutValue, excludedLabelsWithValue)
|
||||
filteredSecrets, err := exporter.filterSecrets(secrets.Items)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -209,8 +224,11 @@ func (exporter *Exporter) getWatchedSecrets(namespace string) ([]v1.Secret, erro
|
||||
return shrinkedSecrets, nil
|
||||
}
|
||||
|
||||
func (exporter *Exporter) filterSecrets(secrets []v1.Secret, includedLabels, excludedLabels []string, excludedLabelsWithValue map[string]string) ([]v1.Secret, error) {
|
||||
filteredSecrets := []v1.Secret{}
|
||||
func (exporter *Exporter) filterSecrets(secrets []v1.Secret) ([]v1.Secret, error) {
|
||||
filteredSecrets := []*v1.Secret{}
|
||||
|
||||
includedLabels, _ := exporter.prepareLabelFilters(exporter.KubeIncludeLabels)
|
||||
excludedLabels, excludedLabelsWithValue := exporter.prepareLabelFilters(exporter.KubeExcludeLabels)
|
||||
|
||||
for _, secret := range secrets {
|
||||
hasIncludedType, err := exporter.checkHasIncludedType(&secret)
|
||||
@@ -222,41 +240,15 @@ func (exporter *Exporter) filterSecrets(secrets []v1.Secret, includedLabels, exc
|
||||
continue
|
||||
}
|
||||
|
||||
validKeyCount := 0
|
||||
for _, expectedKey := range includedLabels {
|
||||
for key := range secret.GetLabels() {
|
||||
if key == expectedKey {
|
||||
validKeyCount++
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
forbiddenKeyCount := 0
|
||||
for _, forbiddenKey := range excludedLabels {
|
||||
for key := range secret.GetLabels() {
|
||||
if key == forbiddenKey {
|
||||
forbiddenKeyCount++
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for forbiddenKey, forbiddenValue := range excludedLabelsWithValue {
|
||||
for key, value := range secret.GetLabels() {
|
||||
if key == forbiddenKey && value == forbiddenValue {
|
||||
forbiddenKeyCount++
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if validKeyCount >= len(includedLabels) && forbiddenKeyCount == 0 {
|
||||
filteredSecrets = append(filteredSecrets, secret)
|
||||
}
|
||||
filteredSecrets = append(filteredSecrets, &secret)
|
||||
}
|
||||
|
||||
return filteredSecrets, nil
|
||||
filteredSecrets = filterObjects(filteredSecrets, includedLabels, excludedLabels, excludedLabelsWithValue)
|
||||
for i, filteredSecret := range filteredSecrets {
|
||||
secrets[i] = *filteredSecret
|
||||
}
|
||||
|
||||
return secrets[:len(filteredSecrets)], nil
|
||||
}
|
||||
|
||||
func (exporter *Exporter) checkHasIncludedType(secret *v1.Secret) (bool, error) {
|
||||
@@ -334,3 +326,44 @@ func getKubeClient(config *rest.Config) (*kubernetes.Clientset, error) {
|
||||
|
||||
return kubeClient, nil
|
||||
}
|
||||
|
||||
func filterObjects[T metav1.Object](objects []T, includedLabels []string, excludedLabels []string, excludedLabelsWithValue map[string]string) []T {
|
||||
filteredObjects := []T{}
|
||||
|
||||
for _, object := range objects {
|
||||
validKeyCount := 0
|
||||
for _, expectedKey := range includedLabels {
|
||||
for key := range object.GetLabels() {
|
||||
if key == expectedKey {
|
||||
validKeyCount++
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
forbiddenKeyCount := 0
|
||||
for _, forbiddenKey := range excludedLabels {
|
||||
for key := range object.GetLabels() {
|
||||
if key == forbiddenKey {
|
||||
forbiddenKeyCount++
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for forbiddenKey, forbiddenValue := range excludedLabelsWithValue {
|
||||
for key, value := range object.GetLabels() {
|
||||
if key == forbiddenKey && value == forbiddenValue {
|
||||
forbiddenKeyCount++
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if validKeyCount >= len(includedLabels) && forbiddenKeyCount == 0 {
|
||||
filteredObjects = append(filteredObjects, object)
|
||||
}
|
||||
}
|
||||
|
||||
return filteredObjects
|
||||
}
|
||||
|
||||
@@ -88,202 +88,144 @@ func TestMain(m *testing.M) {
|
||||
os.Exit(status)
|
||||
}
|
||||
|
||||
func TestKubeAllSecrets(t *testing.T) {
|
||||
testRequestKube(t, &Exporter{}, func(m []model.MetricFamily) {
|
||||
checkMetricsCount(t, m, 21)
|
||||
metrics := getMetricsForName(m, "x509_read_errors")
|
||||
assert.Equal(t, 1., metrics[0].GetGauge().GetValue())
|
||||
})
|
||||
}
|
||||
|
||||
func TestKubeIncludeNamespace(t *testing.T) {
|
||||
testRequestKube(t, &Exporter{
|
||||
KubeIncludeNamespaces: []string{"default"},
|
||||
}, func(m []model.MetricFamily) {
|
||||
checkMetricsCount(t, m, 11)
|
||||
})
|
||||
}
|
||||
|
||||
func TestKubeIncludeMultipleNamespaces(t *testing.T) {
|
||||
testRequestKube(t, &Exporter{
|
||||
KubeIncludeNamespaces: []string{"default", "kube-system"},
|
||||
}, func(m []model.MetricFamily) {
|
||||
checkMetricsCount(t, m, 21)
|
||||
})
|
||||
}
|
||||
|
||||
func TestKubeExcludeNamespace(t *testing.T) {
|
||||
testRequestKube(t, &Exporter{
|
||||
KubeExcludeNamespaces: []string{"default"},
|
||||
}, func(m []model.MetricFamily) {
|
||||
checkMetricsCount(t, m, 10)
|
||||
})
|
||||
}
|
||||
|
||||
func TestKubeExcludeMultipleNamespaces(t *testing.T) {
|
||||
testRequestKube(t, &Exporter{
|
||||
KubeExcludeNamespaces: []string{"default", "kube-system"},
|
||||
}, func(m []model.MetricFamily) {
|
||||
checkMetricsCount(t, m, 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestKubeIncludeExcludeNamespaceMix(t *testing.T) {
|
||||
testRequestKube(t, &Exporter{
|
||||
KubeIncludeNamespaces: []string{"default"},
|
||||
KubeExcludeNamespaces: []string{"default"},
|
||||
}, func(m []model.MetricFamily) {
|
||||
checkMetricsCount(t, m, 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestKubeIncludeExcludeNamespaceMix2(t *testing.T) {
|
||||
testRequestKube(t, &Exporter{
|
||||
KubeIncludeNamespaces: []string{"default", "kube-system"},
|
||||
KubeExcludeNamespaces: []string{"kube-system"},
|
||||
}, func(m []model.MetricFamily) {
|
||||
checkMetricsCount(t, m, 11)
|
||||
})
|
||||
}
|
||||
|
||||
func TestKubeIncludeExistingLabelWithoutValue(t *testing.T) {
|
||||
testRequestKube(t, &Exporter{
|
||||
KubeIncludeLabels: []string{"test"},
|
||||
}, func(m []model.MetricFamily) {
|
||||
checkMetricsCount(t, m, 20)
|
||||
})
|
||||
}
|
||||
|
||||
func TestKubeIncludeNonExistingLabelWithoutValue(t *testing.T) {
|
||||
testRequestKube(t, &Exporter{
|
||||
KubeIncludeLabels: []string{"xxxx"},
|
||||
}, func(m []model.MetricFamily) {
|
||||
checkMetricsCount(t, m, 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestKubeIncludeExistingLabelWithValue(t *testing.T) {
|
||||
testRequestKube(t, &Exporter{
|
||||
KubeIncludeLabels: []string{"aze=abc"},
|
||||
}, func(m []model.MetricFamily) {
|
||||
checkMetricsCount(t, m, 20)
|
||||
})
|
||||
}
|
||||
|
||||
func TestKubeIncludeNonExistingLabelWithValue(t *testing.T) {
|
||||
testRequestKube(t, &Exporter{
|
||||
KubeIncludeLabels: []string{"xxx=xxx"},
|
||||
}, func(m []model.MetricFamily) {
|
||||
checkMetricsCount(t, m, 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestKubeIncludeExistingLabelWithNonExistingValue(t *testing.T) {
|
||||
testRequestKube(t, &Exporter{
|
||||
KubeIncludeLabels: []string{"aze=xxx"},
|
||||
}, func(m []model.MetricFamily) {
|
||||
checkMetricsCount(t, m, 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestKubeExcludeExistingLabelWithoutValue(t *testing.T) {
|
||||
testRequestKube(t, &Exporter{
|
||||
KubeExcludeLabels: []string{"test"},
|
||||
}, func(m []model.MetricFamily) {
|
||||
checkMetricsCount(t, m, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestKubeExcludeNonExistingLabelWithoutValue(t *testing.T) {
|
||||
testRequestKube(t, &Exporter{
|
||||
KubeExcludeLabels: []string{"xxxx"},
|
||||
}, func(m []model.MetricFamily) {
|
||||
checkMetricsCount(t, m, 21)
|
||||
})
|
||||
}
|
||||
|
||||
func TestKubeExcludeExistingLabelWithValue(t *testing.T) {
|
||||
testRequestKube(t, &Exporter{
|
||||
KubeExcludeLabels: []string{"aze=abc"},
|
||||
}, func(m []model.MetricFamily) {
|
||||
checkMetricsCount(t, m, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestKubeExcludeNonExistingLabelWithValue(t *testing.T) {
|
||||
testRequestKube(t, &Exporter{
|
||||
KubeExcludeLabels: []string{"xxx=xxx"},
|
||||
}, func(m []model.MetricFamily) {
|
||||
checkMetricsCount(t, m, 21)
|
||||
})
|
||||
}
|
||||
|
||||
func TestKubeExcludeExistingLabelWithNonExistingValue(t *testing.T) {
|
||||
testRequestKube(t, &Exporter{
|
||||
KubeExcludeLabels: []string{"aze=xxx"},
|
||||
}, func(m []model.MetricFamily) {
|
||||
checkMetricsCount(t, m, 21)
|
||||
})
|
||||
}
|
||||
|
||||
func TestKubeIncludeExcludeLabelMix(t *testing.T) {
|
||||
testRequestKube(t, &Exporter{
|
||||
KubeIncludeLabels: []string{"aze=abc"},
|
||||
KubeExcludeLabels: []string{"aze=abc"},
|
||||
}, func(m []model.MetricFamily) {
|
||||
checkMetricsCount(t, m, 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestKubeIncludeExcludeLabelMix2(t *testing.T) {
|
||||
testRequestKube(t, &Exporter{
|
||||
KubeIncludeLabels: []string{"test"},
|
||||
KubeExcludeLabels: []string{"index=0"},
|
||||
}, func(m []model.MetricFamily) {
|
||||
checkMetricsCount(t, m, 18)
|
||||
})
|
||||
}
|
||||
|
||||
func TestKubeIncludeExcludeLabelMix3(t *testing.T) {
|
||||
testRequestKube(t, &Exporter{
|
||||
KubeIncludeLabels: []string{"test"},
|
||||
KubeExcludeLabels: []string{"xxxxx"},
|
||||
}, func(m []model.MetricFamily) {
|
||||
checkMetricsCount(t, m, 20)
|
||||
})
|
||||
}
|
||||
|
||||
func TestKubeIncludeExcludeLabelMix4(t *testing.T) {
|
||||
testRequestKube(t, &Exporter{
|
||||
KubeIncludeLabels: []string{"index=0", "test"},
|
||||
KubeExcludeLabels: []string{"index=1"},
|
||||
}, func(m []model.MetricFamily) {
|
||||
checkMetricsCount(t, m, 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestKubeCustomSecret(t *testing.T) {
|
||||
testRequestKube(t, &Exporter{
|
||||
KubeSecretTypes: []KubeSecretType{
|
||||
{Type: "istio.io/cert-and-key", Regexp: regexp.MustCompile(`cert-chain\.pem`)},
|
||||
{Type: "istio.io/cert-and-key", Regexp: regexp.MustCompile(`root-cert\.pem`)},
|
||||
func TestKubeNamespaceAndSecretsFiltering(t *testing.T) {
|
||||
tests := []struct {
|
||||
Name string
|
||||
Exporter Exporter
|
||||
MetricCount int
|
||||
AdditionnalCheck func(m []model.MetricFamily)
|
||||
}{
|
||||
{
|
||||
Name: "All secrets (no filtering)",
|
||||
MetricCount: 21,
|
||||
AdditionnalCheck: func(m []model.MetricFamily) {
|
||||
metrics := getMetricsForName(m, "x509_read_errors")
|
||||
assert.Equal(t, 1., metrics[0].GetGauge().GetValue())
|
||||
},
|
||||
}, {
|
||||
Name: "Include existing label without value",
|
||||
Exporter: Exporter{
|
||||
KubeIncludeLabels: []string{"test"},
|
||||
},
|
||||
MetricCount: 20,
|
||||
}, {
|
||||
Name: "Include non-existing label without value",
|
||||
Exporter: Exporter{
|
||||
KubeIncludeLabels: []string{"xxxx"},
|
||||
},
|
||||
MetricCount: 0,
|
||||
}, {
|
||||
Name: "Include existing label with value",
|
||||
Exporter: Exporter{
|
||||
KubeIncludeLabels: []string{"aze=abc"},
|
||||
},
|
||||
MetricCount: 20,
|
||||
}, {
|
||||
Name: "Include non-existing label with value",
|
||||
Exporter: Exporter{
|
||||
KubeIncludeLabels: []string{"xxx=xxx"},
|
||||
},
|
||||
MetricCount: 0,
|
||||
}, {
|
||||
Name: "Include existing label with non-existing value",
|
||||
Exporter: Exporter{
|
||||
KubeIncludeLabels: []string{"aze=xxx"},
|
||||
},
|
||||
MetricCount: 0,
|
||||
}, {
|
||||
Name: "Exclude existing label without value",
|
||||
Exporter: Exporter{
|
||||
KubeExcludeLabels: []string{"test"},
|
||||
},
|
||||
MetricCount: 1,
|
||||
}, {
|
||||
Name: "Exclude non-existing label without value",
|
||||
Exporter: Exporter{
|
||||
KubeExcludeLabels: []string{"xxxx"},
|
||||
},
|
||||
MetricCount: 21,
|
||||
}, {
|
||||
Name: "Exclude existing label with value",
|
||||
Exporter: Exporter{
|
||||
KubeExcludeLabels: []string{"aze=abc"},
|
||||
},
|
||||
MetricCount: 1,
|
||||
}, {
|
||||
Name: "Exclude non-existing label with value",
|
||||
Exporter: Exporter{
|
||||
KubeExcludeLabels: []string{"xxx=xxx"},
|
||||
},
|
||||
MetricCount: 21,
|
||||
}, {
|
||||
Name: "Exclude existing label with non-existing value",
|
||||
Exporter: Exporter{
|
||||
KubeExcludeLabels: []string{"aze=xxx"},
|
||||
},
|
||||
MetricCount: 21,
|
||||
}, {
|
||||
Name: "Include and exclude label mix",
|
||||
Exporter: Exporter{
|
||||
KubeIncludeLabels: []string{"aze=abc"},
|
||||
KubeExcludeLabels: []string{"aze=abc"},
|
||||
},
|
||||
MetricCount: 0,
|
||||
}, {
|
||||
Name: "Include and exclude label mix 2",
|
||||
Exporter: Exporter{
|
||||
KubeIncludeLabels: []string{"test"},
|
||||
KubeExcludeLabels: []string{"index=0"},
|
||||
},
|
||||
MetricCount: 18,
|
||||
}, {
|
||||
Name: "Include and exclude label mix 3",
|
||||
Exporter: Exporter{
|
||||
KubeIncludeLabels: []string{"test"},
|
||||
KubeExcludeLabels: []string{"xxxxx"},
|
||||
},
|
||||
MetricCount: 20,
|
||||
}, {
|
||||
Name: "Include and exclude label mix 4",
|
||||
Exporter: Exporter{
|
||||
KubeIncludeLabels: []string{"index=0", "test"},
|
||||
KubeExcludeLabels: []string{"index=1"},
|
||||
},
|
||||
MetricCount: 2,
|
||||
}, {
|
||||
Name: "Custom secret",
|
||||
Exporter: Exporter{
|
||||
KubeSecretTypes: []KubeSecretType{
|
||||
{Type: "istio.io/cert-and-key", Regexp: regexp.MustCompile(`cert-chain\.pem`)},
|
||||
{Type: "istio.io/cert-and-key", Regexp: regexp.MustCompile(`root-cert\.pem`)},
|
||||
},
|
||||
},
|
||||
MetricCount: 2,
|
||||
AdditionnalCheck: func(m []model.MetricFamily) {
|
||||
metric := getMetricsForName(m, "x509_cert_expired")
|
||||
assert.Len(t, metric, 2)
|
||||
checkLabels(t, metric[0].GetLabel(), "k8s/default/test-custom-type", true, 15)
|
||||
checkLabels(t, metric[1].GetLabel(), "k8s/default/test-custom-type", true, 15)
|
||||
},
|
||||
}, {
|
||||
Name: "Metric labels",
|
||||
Exporter: Exporter{
|
||||
KubeIncludeNamespaces: []string{"default"},
|
||||
KubeIncludeLabels: []string{"index=0"},
|
||||
},
|
||||
MetricCount: 1,
|
||||
AdditionnalCheck: func(m []model.MetricFamily) {
|
||||
metric := getMetricsForName(m, "x509_cert_expired")[0]
|
||||
checkLabels(t, metric.GetLabel(), "k8s/default/test-default-0.crt", true, 15)
|
||||
},
|
||||
},
|
||||
}, func(m []model.MetricFamily) {
|
||||
metric := getMetricsForName(m, "x509_cert_expired")
|
||||
assert.Len(t, metric, 2)
|
||||
checkLabels(t, metric[0].GetLabel(), "k8s/default/test-custom-type", true, 15)
|
||||
checkLabels(t, metric[1].GetLabel(), "k8s/default/test-custom-type", true, 15)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestKubeMetricLabels(t *testing.T) {
|
||||
testRequestKube(t, &Exporter{
|
||||
KubeIncludeNamespaces: []string{"default"},
|
||||
KubeIncludeLabels: []string{"index=0"},
|
||||
}, func(m []model.MetricFamily) {
|
||||
metric := getMetricsForName(m, "x509_cert_expired")[0]
|
||||
checkLabels(t, metric.GetLabel(), "k8s/default/test-default-0.crt", true, 15)
|
||||
})
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.Name, func(t *testing.T) {
|
||||
testRequestKube(t, &tt.Exporter, func(m []model.MetricFamily) {
|
||||
checkMetricsCount(t, m, tt.MetricCount)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestKubeNamespaceListFailure(t *testing.T) {
|
||||
@@ -374,6 +316,107 @@ func TestKubeConnectionFromInsideFailure(t *testing.T) {
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestExporterFilterNamespaces(t *testing.T) {
|
||||
tests := []struct {
|
||||
Name string
|
||||
Exporter Exporter
|
||||
ExpectedNamespaces []string
|
||||
}{
|
||||
{
|
||||
Name: "All namespaces (no filtering)",
|
||||
ExpectedNamespaces: []string{"default", "kube-system", "x509-exporter"},
|
||||
}, {
|
||||
Name: "Include namespace",
|
||||
Exporter: Exporter{
|
||||
KubeIncludeNamespaces: []string{"default"},
|
||||
},
|
||||
ExpectedNamespaces: []string{"default"},
|
||||
}, {
|
||||
Name: "Include multiple namespaces",
|
||||
Exporter: Exporter{
|
||||
KubeIncludeNamespaces: []string{"default", "kube-system"},
|
||||
},
|
||||
ExpectedNamespaces: []string{"default", "kube-system"},
|
||||
}, {
|
||||
Name: "Exclude namespace",
|
||||
Exporter: Exporter{
|
||||
KubeExcludeNamespaces: []string{"default"},
|
||||
},
|
||||
ExpectedNamespaces: []string{"kube-system", "x509-exporter"},
|
||||
}, {
|
||||
Name: "Exclude multiple namespaces",
|
||||
Exporter: Exporter{
|
||||
KubeExcludeNamespaces: []string{"default", "kube-system"},
|
||||
},
|
||||
ExpectedNamespaces: []string{"x509-exporter"},
|
||||
}, {
|
||||
Name: "Include and exclude namespace mix",
|
||||
Exporter: Exporter{
|
||||
KubeIncludeNamespaces: []string{"default"},
|
||||
KubeExcludeNamespaces: []string{"default"},
|
||||
},
|
||||
ExpectedNamespaces: []string{},
|
||||
}, {
|
||||
Name: "Include and exclude namespace mix 2",
|
||||
Exporter: Exporter{
|
||||
KubeIncludeNamespaces: []string{"default", "kube-system"},
|
||||
KubeExcludeNamespaces: []string{"kube-system"},
|
||||
},
|
||||
ExpectedNamespaces: []string{"default"},
|
||||
}, {
|
||||
Name: "Exlucde labels",
|
||||
Exporter: Exporter{
|
||||
KubeExcludeNamespaceLabels: []string{"foo"},
|
||||
},
|
||||
ExpectedNamespaces: []string{"kube-system", "x509-exporter"},
|
||||
},
|
||||
{
|
||||
Name: "Exclude labels with value",
|
||||
Exporter: Exporter{
|
||||
KubeExcludeNamespaceLabels: []string{"group=foo"},
|
||||
},
|
||||
ExpectedNamespaces: []string{"x509-exporter"},
|
||||
},
|
||||
{
|
||||
Name: "Include namespaces and exclude labels with value",
|
||||
Exporter: Exporter{
|
||||
KubeIncludeNamespaces: []string{"default", "kube-system"},
|
||||
KubeExcludeNamespaceLabels: []string{"foo=bar"},
|
||||
},
|
||||
ExpectedNamespaces: []string{"kube-system"},
|
||||
},
|
||||
}
|
||||
|
||||
namespaces := []v1.Namespace{
|
||||
{ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "default",
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
"group": "foo",
|
||||
},
|
||||
}},
|
||||
{ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "kube-system",
|
||||
Labels: map[string]string{
|
||||
"group": "foo",
|
||||
},
|
||||
}},
|
||||
{ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "x509-exporter",
|
||||
Labels: map[string]string{
|
||||
"group": "bar",
|
||||
},
|
||||
}},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.Name, func(t *testing.T) {
|
||||
filteredNamespaces := tt.Exporter.filterNamespaces(namespaces)
|
||||
assert.Equal(t, tt.ExpectedNamespaces, filteredNamespaces)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testRequestKube(t *testing.T, e *Exporter, f func(metrics []model.MetricFamily)) {
|
||||
e.kubeClient = sharedKubeClient
|
||||
testRequest(t, e, f)
|
||||
|
||||
Reference in New Issue
Block a user