1
0
Fork 0
mirror of https://github.com/prometheus-operator/prometheus-operator.git synced 2025-04-21 11:48:53 +00:00

Merge pull request from simonpasquier/fix-3327

pkg/prometheus: skip invalid service monitors
This commit is contained in:
Sergiusz Urbaniak 2020-08-31 16:56:45 +02:00 committed by GitHub
commit 909fc64585
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 961 additions and 356 deletions

View file

@ -0,0 +1,267 @@
// Copyright 2020 The prometheus-operator 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 prometheus
import (
"context"
"fmt"
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
"github.com/pkg/errors"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/tools/cache"
)
// tlsAssetKey is a key for a TLS asset.
type tlsAssetKey struct {
from string
ns string
name string
key string
}
// tlsAssetKeyFromSecretSelector returns a tlsAssetKey struct from a secret key selector.
func tlsAssetKeyFromSecretSelector(ns string, sel *v1.SecretKeySelector) tlsAssetKey {
return tlsAssetKeyFromSelector(
ns,
monitoringv1.SecretOrConfigMap{
Secret: sel,
},
)
}
// tlsAssetKeyFromSelector returns a tlsAssetKey struct from a secret or configmap key selector.
func tlsAssetKeyFromSelector(ns string, sel monitoringv1.SecretOrConfigMap) tlsAssetKey {
if sel.Secret != nil {
return tlsAssetKey{
from: "secret",
ns: ns,
name: sel.Secret.Name,
key: sel.Secret.Key,
}
}
return tlsAssetKey{
from: "configmap",
ns: ns,
name: sel.ConfigMap.Name,
key: sel.ConfigMap.Key,
}
}
// String implements the fmt.Stringer interface.
func (k tlsAssetKey) String() string {
return fmt.Sprintf("%s_%s_%s_%s", k.from, k.ns, k.name, k.key)
}
// assetStore is a store that fetches and caches TLS materials, bearer tokens
// and auth credentials from configmaps and secrets.
// Data can be referenced directly from a Prometheus object or indirectly (for
// instance via ServiceMonitor). In practice a new store is created and used by
// each reconciliation loop.
//
// assetStore doesn't support concurrent access.
type assetStore struct {
cmClient corev1client.ConfigMapsGetter
sClient corev1client.SecretsGetter
objStore cache.Store
tlsAssets map[tlsAssetKey]TLSAsset
bearerTokenAssets map[string]BearerToken
basicAuthAssets map[string]BasicAuthCredentials
}
// newAssetStore returns an empty assetStore.
func newAssetStore(cmClient corev1client.ConfigMapsGetter, sClient corev1client.SecretsGetter) *assetStore {
return &assetStore{
cmClient: cmClient,
sClient: sClient,
tlsAssets: make(map[tlsAssetKey]TLSAsset),
bearerTokenAssets: make(map[string]BearerToken),
basicAuthAssets: make(map[string]BasicAuthCredentials),
objStore: cache.NewStore(assetKeyFunc),
}
}
func assetKeyFunc(obj interface{}) (string, error) {
switch v := obj.(type) {
case *v1.ConfigMap:
return fmt.Sprintf("0/%s/%s", v.GetNamespace(), v.GetName()), nil
case *v1.Secret:
return fmt.Sprintf("1/%s/%s", v.GetNamespace(), v.GetName()), nil
}
return "", errors.Errorf("unsupported type: %T", obj)
}
// addTLSConfig processes the given *TLSConfig and adds the referenced CA, certifcate and key to the store.
func (a *assetStore) addTLSConfig(ns string, tlsConfig *monitoringv1.TLSConfig) error {
if tlsConfig == nil {
return nil
}
if tlsConfig.CA != (monitoringv1.SecretOrConfigMap{}) {
var (
ca string
err error
)
switch {
case tlsConfig.CA.Secret != nil:
ca, err = a.getSecretKey(ns, *tlsConfig.CA.Secret)
case tlsConfig.CA.ConfigMap != nil:
ca, err = a.getConfigMapKey(ns, *tlsConfig.CA.ConfigMap)
}
if err != nil {
return errors.Wrap(err, "failed to get CA")
}
a.tlsAssets[tlsAssetKeyFromSelector(ns, tlsConfig.CA)] = TLSAsset(ca)
}
if tlsConfig.Cert != (monitoringv1.SecretOrConfigMap{}) {
var (
cert string
err error
)
switch {
case tlsConfig.Cert.Secret != nil:
cert, err = a.getSecretKey(ns, *tlsConfig.Cert.Secret)
case tlsConfig.Cert.ConfigMap != nil:
cert, err = a.getConfigMapKey(ns, *tlsConfig.Cert.ConfigMap)
}
if err != nil {
return errors.Wrap(err, "failed to get cert")
}
a.tlsAssets[tlsAssetKeyFromSelector(ns, tlsConfig.Cert)] = TLSAsset(cert)
}
if tlsConfig.KeySecret != nil {
key, err := a.getSecretKey(ns, *tlsConfig.KeySecret)
if err != nil {
return errors.Wrap(err, "failed to get key")
}
a.tlsAssets[tlsAssetKeyFromSelector(ns, monitoringv1.SecretOrConfigMap{Secret: tlsConfig.KeySecret})] = TLSAsset(key)
}
return nil
}
// addTLSConfig processes the given *BasicAuth and adds the referenced credentials to the store.
func (a *assetStore) addBasicAuth(ns string, ba *monitoringv1.BasicAuth, key string) error {
if ba == nil {
return nil
}
username, err := a.getSecretKey(ns, ba.Username)
if err != nil {
return errors.Wrap(err, "failed to get basic auth username")
}
password, err := a.getSecretKey(ns, ba.Password)
if err != nil {
return errors.Wrap(err, "failed to get basic auth password")
}
a.basicAuthAssets[key] = BasicAuthCredentials{
username: username,
password: password,
}
return nil
}
// addTLSConfig processes the given SecretKeySelector and adds the referenced data to the store.
func (a *assetStore) addBearerToken(ns string, sel v1.SecretKeySelector, key string) error {
if sel.Name == "" {
return nil
}
bearerToken, err := a.getSecretKey(ns, sel)
if err != nil {
return errors.Wrap(err, "failed to get bearer token")
}
a.bearerTokenAssets[key] = BearerToken(bearerToken)
return nil
}
func (a *assetStore) getConfigMapKey(namespace string, sel v1.ConfigMapKeySelector) (string, error) {
obj, exists, err := a.objStore.Get(&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: sel.Name,
Namespace: namespace,
},
})
if err != nil {
return "", errors.Wrapf(err, "unexpected store error when getting configmap %q", sel.Name)
}
if !exists {
cm, err := a.cmClient.ConfigMaps(namespace).Get(context.TODO(), sel.Name, metav1.GetOptions{})
if err != nil {
return "", errors.Wrapf(err, "unable to get configmap %q", sel.Name)
}
if err = a.objStore.Add(cm); err != nil {
return "", errors.Wrapf(err, "unexpected store error when adding configmap %q", sel.Name)
}
obj = cm
}
cm := obj.(*v1.ConfigMap)
if _, found := cm.Data[sel.Key]; !found {
return "", errors.Errorf("key %q in configmap %q not found", sel.Key, sel.Name)
}
return cm.Data[sel.Key], nil
}
func (a *assetStore) getSecretKey(namespace string, sel v1.SecretKeySelector) (string, error) {
obj, exists, err := a.objStore.Get(&v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: sel.Name,
Namespace: namespace,
},
})
if err != nil {
return "", errors.Wrapf(err, "unexpected store error when getting secret %q", sel.Name)
}
if !exists {
secret, err := a.sClient.Secrets(namespace).Get(context.TODO(), sel.Name, metav1.GetOptions{})
if err != nil {
return "", errors.Wrapf(err, "unable to get secret %q", sel.Name)
}
if err = a.objStore.Add(secret); err != nil {
return "", errors.Wrapf(err, "unexpected store error when adding secret %q", sel.Name)
}
obj = secret
}
secret := obj.(*v1.Secret)
if _, found := secret.Data[sel.Key]; !found {
return "", errors.Errorf("key %q in secret %q not found", sel.Key, sel.Name)
}
return string(secret.Data[sel.Key]), nil
}

View file

@ -0,0 +1,611 @@
// Copyright 2020 The prometheus-operator 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 prometheus
import (
"fmt"
"testing"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
)
func TestAddBearerToken(t *testing.T) {
c := fake.NewSimpleClientset(
&v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "secret",
Namespace: "ns1",
},
Data: map[string][]byte{
"key1": []byte("val1"),
},
},
)
for i, tc := range []struct {
ns string
selectedName string
selectedKey string
err bool
expected string
}{
{
ns: "ns1",
selectedName: "secret",
selectedKey: "key1",
expected: "val1",
},
// Wrong namespace.
{
ns: "ns2",
selectedName: "secret",
selectedKey: "key1",
err: true,
},
// Wrong name.
{
ns: "ns1",
selectedName: "secreet",
selectedKey: "key1",
err: true,
},
// Wrong key.
{
ns: "ns1",
selectedName: "secret",
selectedKey: "key2",
err: true,
},
} {
t.Run("", func(t *testing.T) {
store := newAssetStore(c.CoreV1(), c.CoreV1())
sel := v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: tc.selectedName,
},
Key: tc.selectedKey,
}
key := fmt.Sprintf("basicauth/%d", i)
err := store.addBearerToken(tc.ns, sel, key)
if tc.err {
if err == nil {
t.Fatal("expecting error, got no error")
}
return
}
if err != nil {
t.Fatalf("expecting no error, got %q", err)
}
s, found := store.bearerTokenAssets[key]
if !found {
t.Fatalf("expecting to find key %q but got nothing", key)
}
if string(s) != tc.expected {
t.Fatalf("expecting %q, got %q", tc.expected, s)
}
})
}
}
func TestAddBasicAuth(t *testing.T) {
c := fake.NewSimpleClientset(
&v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "secret",
Namespace: "ns1",
},
Data: map[string][]byte{
"key1": []byte("val1"),
"key2": []byte("val2"),
},
},
)
for i, tc := range []struct {
ns string
selectedUserName string
selectedUserKey string
selectedPasswordName string
selectedPasswordKey string
err bool
expectedUser string
expectedPassword string
}{
{
ns: "ns1",
selectedUserName: "secret",
selectedUserKey: "key1",
selectedPasswordName: "secret",
selectedPasswordKey: "key2",
expectedUser: "val1",
expectedPassword: "val2",
},
// Wrong namespace.
{
ns: "ns2",
selectedUserName: "secret",
selectedUserKey: "key1",
selectedPasswordName: "secret",
selectedPasswordKey: "key2",
err: true,
},
// Wrong name for username selector.
{
ns: "ns1",
selectedUserName: "secreet",
selectedUserKey: "key1",
selectedPasswordName: "secret",
selectedPasswordKey: "key2",
err: true,
},
// Wrong key for username selector.
{
ns: "ns1",
selectedUserName: "secret",
selectedUserKey: "key3",
selectedPasswordName: "secret",
selectedPasswordKey: "key2",
err: true,
},
// Wrong name for password selector.
{
ns: "ns1",
selectedUserName: "secret",
selectedUserKey: "key1",
selectedPasswordName: "secreet",
selectedPasswordKey: "key2",
err: true,
},
// Wrong key for password selector.
{
ns: "ns1",
selectedUserName: "secret",
selectedUserKey: "key1",
selectedPasswordName: "secret",
selectedPasswordKey: "key3",
err: true,
},
} {
t.Run("", func(t *testing.T) {
store := newAssetStore(c.CoreV1(), c.CoreV1())
basicAuth := &monitoringv1.BasicAuth{
Username: v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: tc.selectedUserName,
},
Key: tc.selectedUserKey,
},
Password: v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: tc.selectedPasswordName,
},
Key: tc.selectedPasswordKey,
},
}
key := fmt.Sprintf("basicauth/%d", i)
err := store.addBasicAuth(tc.ns, basicAuth, key)
if tc.err {
if err == nil {
t.Fatal("expecting error, got no error")
}
return
}
if err != nil {
t.Fatalf("expecting no error, got %q", err)
}
s, found := store.basicAuthAssets[key]
if !found {
t.Fatalf("expecting to find key %q but got nothing", key)
}
if s.username != tc.expectedUser {
t.Fatalf("expecting username %q, got %q", tc.expectedUser, s)
}
if s.password != tc.expectedPassword {
t.Fatalf("expecting password %q, got %q", tc.expectedPassword, s)
}
})
}
}
func TestAddTLSConfig(t *testing.T) {
c := fake.NewSimpleClientset(
&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "cm",
Namespace: "ns1",
},
Data: map[string]string{
"key1": "val1",
"key2": "val2",
"key3": "val3",
},
},
&v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "secret",
Namespace: "ns1",
},
Data: map[string][]byte{
"key4": []byte("val4"),
"key5": []byte("val5"),
"key6": []byte("val6"),
},
},
)
for _, tc := range []struct {
ns string
tlsConfig *monitoringv1.TLSConfig
err bool
expectedCA string
expectedCert string
expectedKey string
}{
{
// CA, cert and key in secret.
ns: "ns1",
tlsConfig: &monitoringv1.TLSConfig{
CA: monitoringv1.SecretOrConfigMap{
Secret: &v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "secret",
},
Key: "key4",
},
},
Cert: monitoringv1.SecretOrConfigMap{
Secret: &v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "secret",
},
Key: "key5",
},
},
KeySecret: &v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "secret",
},
Key: "key6",
},
},
expectedCA: "val4",
expectedCert: "val5",
expectedKey: "val6",
},
{
// CA in configmap, cert and key in secret.
ns: "ns1",
tlsConfig: &monitoringv1.TLSConfig{
CA: monitoringv1.SecretOrConfigMap{
ConfigMap: &v1.ConfigMapKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "cm",
},
Key: "key1",
},
},
Cert: monitoringv1.SecretOrConfigMap{
Secret: &v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "secret",
},
Key: "key5",
},
},
KeySecret: &v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "secret",
},
Key: "key6",
},
},
expectedCA: "val1",
expectedCert: "val5",
expectedKey: "val6",
},
{
// CA and cert in configmap, key in secret.
ns: "ns1",
tlsConfig: &monitoringv1.TLSConfig{
CA: monitoringv1.SecretOrConfigMap{
ConfigMap: &v1.ConfigMapKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "cm",
},
Key: "key1",
},
},
Cert: monitoringv1.SecretOrConfigMap{
ConfigMap: &v1.ConfigMapKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "cm",
},
Key: "key2",
},
},
KeySecret: &v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "secret",
},
Key: "key6",
},
},
expectedCA: "val1",
expectedCert: "val2",
expectedKey: "val6",
},
{
// Wrong namespace.
ns: "ns2",
tlsConfig: &monitoringv1.TLSConfig{
CA: monitoringv1.SecretOrConfigMap{
ConfigMap: &v1.ConfigMapKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "cm",
},
Key: "key1",
},
},
Cert: monitoringv1.SecretOrConfigMap{
ConfigMap: &v1.ConfigMapKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "cm",
},
Key: "key2",
},
},
KeySecret: &v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "secret",
},
Key: "key6",
},
},
err: true,
},
{
// Wrong configmap selector for CA.
ns: "ns1",
tlsConfig: &monitoringv1.TLSConfig{
CA: monitoringv1.SecretOrConfigMap{
ConfigMap: &v1.ConfigMapKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "cm",
},
Key: "key4",
},
},
Cert: monitoringv1.SecretOrConfigMap{
ConfigMap: &v1.ConfigMapKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "cm",
},
Key: "key2",
},
},
KeySecret: &v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "secret",
},
Key: "key6",
},
},
err: true,
},
{
// Wrong secret selector for CA.
ns: "ns1",
tlsConfig: &monitoringv1.TLSConfig{
CA: monitoringv1.SecretOrConfigMap{
Secret: &v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "secret",
},
Key: "key1",
},
},
Cert: monitoringv1.SecretOrConfigMap{
ConfigMap: &v1.ConfigMapKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "cm",
},
Key: "key2",
},
},
KeySecret: &v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "secret",
},
Key: "key6",
},
},
err: true,
},
{
// Wrong configmap selector for cert.
ns: "ns1",
tlsConfig: &monitoringv1.TLSConfig{
CA: monitoringv1.SecretOrConfigMap{
ConfigMap: &v1.ConfigMapKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "cm",
},
Key: "key1",
},
},
Cert: monitoringv1.SecretOrConfigMap{
ConfigMap: &v1.ConfigMapKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "cm",
},
Key: "key4",
},
},
KeySecret: &v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "secret",
},
Key: "key6",
},
},
err: true,
},
{
// Wrong secret selector for cert.
ns: "ns1",
tlsConfig: &monitoringv1.TLSConfig{
CA: monitoringv1.SecretOrConfigMap{
ConfigMap: &v1.ConfigMapKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "cm",
},
Key: "key1",
},
},
Cert: monitoringv1.SecretOrConfigMap{
Secret: &v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "secret",
},
Key: "key2",
},
},
KeySecret: &v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "secret",
},
Key: "key6",
},
},
err: true,
},
{
// Wrong key selector.
ns: "ns1",
tlsConfig: &monitoringv1.TLSConfig{
CA: monitoringv1.SecretOrConfigMap{
Secret: &v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "secret",
},
Key: "key4",
},
},
Cert: monitoringv1.SecretOrConfigMap{
Secret: &v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "secret",
},
Key: "key5",
},
},
KeySecret: &v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "secret",
},
Key: "key7",
},
},
err: true,
},
} {
t.Run("", func(t *testing.T) {
store := newAssetStore(c.CoreV1(), c.CoreV1())
err := store.addTLSConfig(tc.ns, tc.tlsConfig)
if tc.err {
if err == nil {
t.Fatal("expecting error, got no error")
}
return
}
if err != nil {
t.Fatalf("expecting no error, got %q", err)
}
key := tlsAssetKeyFromSelector(tc.ns, tc.tlsConfig.CA)
ca, found := store.tlsAssets[key]
if !found {
t.Fatalf("expecting to find key %q but got nothing", key)
}
if string(ca) != tc.expectedCA {
t.Fatalf("expecting CA %q, got %q", tc.expectedCA, ca)
}
key = tlsAssetKeyFromSelector(tc.ns, tc.tlsConfig.Cert)
cert, found := store.tlsAssets[key]
if !found {
t.Fatalf("expecting to find key %q but got nothing", key)
}
if string(cert) != tc.expectedCert {
t.Fatalf("expecting cert %q, got %q", tc.expectedCert, ca)
}
key = tlsAssetKeyFromSecretSelector(tc.ns, tc.tlsConfig.KeySecret)
k, found := store.tlsAssets[key]
if !found {
t.Fatalf("expecting to find key %q but got nothing", key)
}
if string(k) != tc.expectedKey {
t.Fatalf("expecting cert key %q, got %q", tc.expectedCert, ca)
}
})
}
}

View file

@ -45,7 +45,6 @@ import (
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/kubernetes"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
@ -1207,11 +1206,13 @@ func (c *Operator) sync(key string) error {
return err
}
if err := c.createOrUpdateConfigurationSecret(p, ruleConfigMapNames); err != nil {
assetStore := newAssetStore(c.kclient.CoreV1(), c.kclient.CoreV1())
if err := c.createOrUpdateConfigurationSecret(p, ruleConfigMapNames, assetStore); err != nil {
return errors.Wrap(err, "creating config failed")
}
if err := c.createOrUpdateTLSAssetSecret(p); err != nil {
if err := c.createOrUpdateTLSAssetSecret(p, assetStore); err != nil {
return errors.Wrap(err, "creating tls asset secret failed")
}
@ -1414,93 +1415,6 @@ func (c *Operator) loadAdditionalScrapeConfigsSecret(additionalScrapeConfigs *v1
return nil, nil
}
func extractCredKey(secret *v1.Secret, sel v1.SecretKeySelector, cred string) (string, error) {
if s, ok := secret.Data[sel.Key]; ok {
return string(s), nil
}
return "", fmt.Errorf("secret %s key %q in secret %q not found", cred, sel.Key, sel.Name)
}
func getCredFromSecret(c corev1client.SecretInterface, sel v1.SecretKeySelector, cred string, cacheKey string, cache map[string]*v1.Secret) (_ string, err error) {
var s *v1.Secret
var ok bool
if s, ok = cache[cacheKey]; !ok {
if s, err = c.Get(context.TODO(), sel.Name, metav1.GetOptions{}); err != nil {
return "", fmt.Errorf("unable to fetch %s secret %q: %s", cred, sel.Name, err)
}
cache[cacheKey] = s
}
return extractCredKey(s, sel, cred)
}
func getCredFromConfigMap(c corev1client.ConfigMapInterface, sel v1.ConfigMapKeySelector, cred string, cacheKey string, cache map[string]*v1.ConfigMap) (_ string, err error) {
var s *v1.ConfigMap
var ok bool
if s, ok = cache[cacheKey]; !ok {
if s, err = c.Get(context.TODO(), sel.Name, metav1.GetOptions{}); err != nil {
return "", fmt.Errorf("unable to fetch %s configmap %q: %s", cred, sel.Name, err)
}
cache[cacheKey] = s
}
if a, ok := s.Data[sel.Key]; ok {
return string(a), nil
}
return "", fmt.Errorf("config %s key %q in configmap %q not found", cred, sel.Key, sel.Name)
}
func loadBasicAuthSecretFromAPI(basicAuth *monitoringv1.BasicAuth, c corev1client.CoreV1Interface, ns string, cache map[string]*v1.Secret) (BasicAuthCredentials, error) {
var username string
var password string
var err error
sClient := c.Secrets(ns)
if username, err = getCredFromSecret(sClient, basicAuth.Username, "username", ns+"/"+basicAuth.Username.Name, cache); err != nil {
return BasicAuthCredentials{}, err
}
if password, err = getCredFromSecret(sClient, basicAuth.Password, "password", ns+"/"+basicAuth.Password.Name, cache); err != nil {
return BasicAuthCredentials{}, err
}
return BasicAuthCredentials{username: username, password: password}, nil
}
func loadBasicAuthSecret(basicAuth *monitoringv1.BasicAuth, s *v1.SecretList) (BasicAuthCredentials, error) {
var username string
var password string
var err error
for _, secret := range s.Items {
if secret.Name == basicAuth.Username.Name {
if username, err = extractCredKey(&secret, basicAuth.Username, "username"); err != nil {
return BasicAuthCredentials{}, err
}
}
if secret.Name == basicAuth.Password.Name {
if password, err = extractCredKey(&secret, basicAuth.Password, "password"); err != nil {
return BasicAuthCredentials{}, err
}
}
if username != "" && password != "" {
break
}
}
if username == "" && password == "" {
return BasicAuthCredentials{}, fmt.Errorf("basic auth username and password secret not found")
}
return BasicAuthCredentials{username: username, password: password}, nil
}
func gzipConfig(buf *bytes.Buffer, conf []byte) error {
w := gzip.NewWriter(buf)
defer w.Close()
@ -1510,222 +1424,7 @@ func gzipConfig(buf *bytes.Buffer, conf []byte) error {
return nil
}
func (c *Operator) loadBasicAuthSecrets(
mons map[string]*monitoringv1.ServiceMonitor,
remoteReads []monitoringv1.RemoteReadSpec,
remoteWrites []monitoringv1.RemoteWriteSpec,
apiserverConfig *monitoringv1.APIServerConfig,
SecretsInPromNS *v1.SecretList,
) (map[string]BasicAuthCredentials, error) {
secrets := map[string]BasicAuthCredentials{}
nsSecretCache := make(map[string]*v1.Secret)
for _, mon := range mons {
for i, ep := range mon.Spec.Endpoints {
if ep.BasicAuth != nil {
credentials, err := loadBasicAuthSecretFromAPI(ep.BasicAuth, c.kclient.CoreV1(), mon.Namespace, nsSecretCache)
if err != nil {
return nil, fmt.Errorf("could not generate basicAuth for servicemonitor %s. %s", mon.Name, err)
}
secrets[fmt.Sprintf("serviceMonitor/%s/%s/%d", mon.Namespace, mon.Name, i)] = credentials
}
}
}
for i, remote := range remoteReads {
if remote.BasicAuth != nil {
credentials, err := loadBasicAuthSecret(remote.BasicAuth, SecretsInPromNS)
if err != nil {
return nil, fmt.Errorf("could not generate basicAuth for remote_read config %d. %s", i, err)
}
secrets[fmt.Sprintf("remoteRead/%d", i)] = credentials
}
}
for i, remote := range remoteWrites {
if remote.BasicAuth != nil {
credentials, err := loadBasicAuthSecret(remote.BasicAuth, SecretsInPromNS)
if err != nil {
return nil, fmt.Errorf("could not generate basicAuth for remote_write config %d. %s", i, err)
}
secrets[fmt.Sprintf("remoteWrite/%d", i)] = credentials
}
}
// load apiserver basic auth secret
if apiserverConfig != nil && apiserverConfig.BasicAuth != nil {
credentials, err := loadBasicAuthSecret(apiserverConfig.BasicAuth, SecretsInPromNS)
if err != nil {
return nil, fmt.Errorf("could not generate basicAuth for apiserver config. %s", err)
}
secrets["apiserver"] = credentials
}
return secrets, nil
}
func (c *Operator) loadBearerTokensFromSecrets(mons map[string]*monitoringv1.ServiceMonitor) (map[string]BearerToken, error) {
tokens := map[string]BearerToken{}
nsSecretCache := make(map[string]*v1.Secret)
for _, mon := range mons {
for i, ep := range mon.Spec.Endpoints {
if ep.BearerTokenSecret.Name == "" {
continue
}
sClient := c.kclient.CoreV1().Secrets(mon.Namespace)
token, err := getCredFromSecret(
sClient,
ep.BearerTokenSecret,
"bearertoken",
mon.Namespace+"/"+ep.BearerTokenSecret.Name,
nsSecretCache,
)
if err != nil {
return nil, fmt.Errorf(
"failed to extract endpoint bearertoken for servicemonitor %v from secret %v in namespace %v",
mon.Name, ep.BearerTokenSecret.Name, mon.Namespace,
)
}
tokens[fmt.Sprintf("serviceMonitor/%s/%s/%d", mon.Namespace, mon.Name, i)] = BearerToken(token)
}
}
return tokens, nil
}
func (c *Operator) loadTLSAssetsFromTLSConfigList(ns string, tlsConfigs []*monitoringv1.TLSConfig) (map[string]TLSAsset, error) {
assets := map[string]TLSAsset{}
for _, tls := range tlsConfigs {
if tls == nil {
continue
}
prefix := ns + "/"
secretSelectors := map[string]*v1.SecretKeySelector{}
configMapSelectors := map[string]*v1.ConfigMapKeySelector{}
if tls.CA != (monitoringv1.SecretOrConfigMap{}) {
switch {
case tls.CA.Secret != nil:
secretSelectors[prefix+tls.CA.Secret.Name+"/"+tls.CA.Secret.Key] = tls.CA.Secret
case tls.CA.ConfigMap != nil:
configMapSelectors[prefix+tls.CA.ConfigMap.Name+"/"+tls.CA.ConfigMap.Key] = tls.CA.ConfigMap
}
}
if tls.Cert != (monitoringv1.SecretOrConfigMap{}) {
switch {
case tls.Cert.Secret != nil:
secretSelectors[prefix+tls.Cert.Secret.Name+"/"+tls.Cert.Secret.Key] = tls.Cert.Secret
case tls.Cert.ConfigMap != nil:
configMapSelectors[prefix+tls.Cert.ConfigMap.Name+"/"+tls.Cert.ConfigMap.Key] = tls.Cert.ConfigMap
}
}
if tls.KeySecret != nil {
secretSelectors[prefix+tls.KeySecret.Name+"/"+tls.KeySecret.Key] = tls.KeySecret
}
for key, selector := range secretSelectors {
sClient := c.kclient.CoreV1().Secrets(ns)
asset, err := getCredFromSecret(
sClient,
*selector,
"tls config",
key,
make(map[string]*v1.Secret),
)
if err != nil {
return nil, fmt.Errorf(
"failed to extract endpoint tls asset for servicemonitor %v from secret %v and key %v in namespace %v",
ns, selector.Name, selector.Key, ns,
)
}
assets[fmt.Sprintf(
"%v_%v_%v",
ns,
selector.Name,
selector.Key,
)] = TLSAsset(asset)
}
for key, selector := range configMapSelectors {
sClient := c.kclient.CoreV1().ConfigMaps(ns)
asset, err := getCredFromConfigMap(
sClient,
*selector,
"tls config",
key,
make(map[string]*v1.ConfigMap),
)
if err != nil {
return nil, fmt.Errorf(
"failed to extract endpoint tls asset for servicemonitor %v from configmap %v and key %v in namespace %v",
ns, selector.Name, selector.Key, ns,
)
}
assets[fmt.Sprintf(
"%v_%v_%v",
ns,
selector.Name,
selector.Key,
)] = TLSAsset(asset)
}
}
return assets, nil
}
func (c *Operator) loadTLSAssets(p *monitoringv1.Prometheus) (map[string]TLSAsset, error) {
smAssets := map[string]TLSAsset{}
promAssets := map[string]TLSAsset{}
assets := map[string]TLSAsset{}
smEndpoingTLSConfig := []*monitoringv1.TLSConfig{}
promRwTLSConfig := []*monitoringv1.TLSConfig{}
mons, err := c.selectServiceMonitors(p)
if err != nil {
return nil, errors.Wrap(err, "selecting ServiceMonitors failed")
}
for _, mon := range mons {
for _, ep := range mon.Spec.Endpoints {
smEndpoingTLSConfig = append(smEndpoingTLSConfig, ep.TLSConfig)
}
smAssets, err = c.loadTLSAssetsFromTLSConfigList(mon.Namespace, smEndpoingTLSConfig)
if err != nil {
return nil, err
}
}
for _, rw := range p.Spec.RemoteWrite {
promRwTLSConfig = append(promRwTLSConfig, rw.TLSConfig)
}
promAssets, err = c.loadTLSAssetsFromTLSConfigList(p.ObjectMeta.Namespace, promRwTLSConfig)
if err != nil {
return nil, err
}
for k, v := range smAssets {
assets[k] = v
}
for k, v := range promAssets {
assets[k] = v
}
return assets, nil
}
func (c *Operator) createOrUpdateConfigurationSecret(p *monitoringv1.Prometheus, ruleConfigMapNames []string) error {
func (c *Operator) createOrUpdateConfigurationSecret(p *monitoringv1.Prometheus, ruleConfigMapNames []string, store *assetStore) error {
// If no service or pod monitor selectors are configured, the user wants to
// manage configuration themselves. Do create an empty Secret if it doesn't
// exist.
@ -1751,7 +1450,7 @@ func (c *Operator) createOrUpdateConfigurationSecret(p *monitoringv1.Prometheus,
return nil
}
smons, err := c.selectServiceMonitors(p)
smons, err := c.selectServiceMonitors(p, store)
if err != nil {
return errors.Wrap(err, "selecting ServiceMonitors failed")
}
@ -1772,14 +1471,22 @@ func (c *Operator) createOrUpdateConfigurationSecret(p *monitoringv1.Prometheus,
return err
}
basicAuthSecrets, err := c.loadBasicAuthSecrets(smons, p.Spec.RemoteRead, p.Spec.RemoteWrite, p.Spec.APIServerConfig, SecretsInPromNS)
if err != nil {
return err
for i, remote := range p.Spec.RemoteRead {
if err := store.addBasicAuth(p.GetNamespace(), remote.BasicAuth, fmt.Sprintf("remoteRead/%d", i)); err != nil {
return errors.Wrapf(err, "remote read %d", i)
}
}
bearerTokens, err := c.loadBearerTokensFromSecrets(smons)
if err != nil {
return err
for i, remote := range p.Spec.RemoteWrite {
if err := store.addBasicAuth(p.GetNamespace(), remote.BasicAuth, fmt.Sprintf("remoteWrite/%d", i)); err != nil {
return errors.Wrapf(err, "remote write %d", i)
}
}
if p.Spec.APIServerConfig != nil {
if err := store.addBasicAuth(p.GetNamespace(), p.Spec.APIServerConfig.BasicAuth, "apiserver"); err != nil {
return errors.Wrap(err, "apiserver config")
}
}
additionalScrapeConfigs, err := c.loadAdditionalScrapeConfigsSecret(p.Spec.AdditionalScrapeConfigs, SecretsInPromNS)
@ -1801,8 +1508,8 @@ func (c *Operator) createOrUpdateConfigurationSecret(p *monitoringv1.Prometheus,
smons,
pmons,
bmons,
basicAuthSecrets,
bearerTokens,
store.basicAuthAssets,
store.bearerTokenAssets,
additionalScrapeConfigs,
additionalAlertRelabelConfigs,
additionalAlertManagerConfigs,
@ -1850,15 +1557,10 @@ func (c *Operator) createOrUpdateConfigurationSecret(p *monitoringv1.Prometheus,
return err
}
func (c *Operator) createOrUpdateTLSAssetSecret(p *monitoringv1.Prometheus) error {
func (c *Operator) createOrUpdateTLSAssetSecret(p *monitoringv1.Prometheus, store *assetStore) error {
boolTrue := true
sClient := c.kclient.CoreV1().Secrets(p.Namespace)
tlsAssets, err := c.loadTLSAssets(p)
if err != nil {
return err
}
tlsAssetsSecret := &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: tlsAssetsSecretName(p.Name),
@ -1877,11 +1579,17 @@ func (c *Operator) createOrUpdateTLSAssetSecret(p *monitoringv1.Prometheus) erro
Data: map[string][]byte{},
}
for key, asset := range tlsAssets {
tlsAssetsSecret.Data[key] = []byte(asset)
for i, rw := range p.Spec.RemoteWrite {
if err := store.addTLSConfig(p.GetNamespace(), rw.TLSConfig); err != nil {
return errors.Wrapf(err, "remote write %d", i)
}
}
_, err = sClient.Get(context.TODO(), tlsAssetsSecret.Name, metav1.GetOptions{})
for key, asset := range store.tlsAssets {
tlsAssetsSecret.Data[key.String()] = []byte(asset)
}
_, err := sClient.Get(context.TODO(), tlsAssetsSecret.Name, metav1.GetOptions{})
if err != nil {
if !apierrors.IsNotFound(err) {
return errors.Wrapf(
@ -1906,10 +1614,10 @@ func (c *Operator) createOrUpdateTLSAssetSecret(p *monitoringv1.Prometheus) erro
return nil
}
func (c *Operator) selectServiceMonitors(p *monitoringv1.Prometheus) (map[string]*monitoringv1.ServiceMonitor, error) {
func (c *Operator) selectServiceMonitors(p *monitoringv1.Prometheus, store *assetStore) (map[string]*monitoringv1.ServiceMonitor, error) {
namespaces := []string{}
// Selectors (<namespace>/<name>) might overlap. Deduplicate them along the keyFunc.
res := make(map[string]*monitoringv1.ServiceMonitor)
serviceMonitors := make(map[string]*monitoringv1.ServiceMonitor)
servMonSelector, err := metav1.LabelSelectorAsSelector(p.Spec.ServiceMonitorSelector)
if err != nil {
@ -1937,35 +1645,58 @@ func (c *Operator) selectServiceMonitors(p *monitoringv1.Prometheus) (map[string
cache.ListAllByNamespace(c.smonInf.GetIndexer(), ns, servMonSelector, func(obj interface{}) {
k, ok := c.keyFunc(obj)
if ok {
res[k] = obj.(*monitoringv1.ServiceMonitor)
serviceMonitors[k] = obj.(*monitoringv1.ServiceMonitor)
}
})
}
// If denied by Prometheus spec, filter out all service monitors that access
// the file system.
if p.Spec.ArbitraryFSAccessThroughSMs.Deny {
for namespaceAndName, sm := range res {
for _, endpoint := range sm.Spec.Endpoints {
if err := testForArbitraryFSAccess(endpoint); err != nil {
delete(res, namespaceAndName)
level.Warn(c.logger).Log(
"msg", "skipping servicemonitor",
"error", err.Error(),
"servicemonitor", namespaceAndName,
"namespace", p.Namespace,
"prometheus", p.Name,
)
res := make(map[string]*monitoringv1.ServiceMonitor, len(serviceMonitors))
for namespaceAndName, sm := range serviceMonitors {
var err error
for i, endpoint := range sm.Spec.Endpoints {
// If denied by Prometheus spec, filter out all service monitors that access
// the file system.
if p.Spec.ArbitraryFSAccessThroughSMs.Deny {
if err = testForArbitraryFSAccess(endpoint); err != nil {
break
}
}
smKey := fmt.Sprintf("serviceMonitor/%s/%s/%d", sm.GetNamespace(), sm.GetName(), i)
if err = store.addBearerToken(sm.GetNamespace(), endpoint.BearerTokenSecret, smKey); err != nil {
break
}
if err = store.addBasicAuth(sm.GetNamespace(), endpoint.BasicAuth, smKey); err != nil {
break
}
if err = store.addTLSConfig(sm.GetNamespace(), endpoint.TLSConfig); err != nil {
break
}
}
if err != nil {
level.Warn(c.logger).Log(
"msg", "skipping servicemonitor",
"error", err.Error(),
"servicemonitor", namespaceAndName,
"namespace", p.Namespace,
"prometheus", p.Name,
)
continue
}
res[namespaceAndName] = sm
}
serviceMonitors := []string{}
smKeys := []string{}
for k := range res {
serviceMonitors = append(serviceMonitors, k)
smKeys = append(smKeys, k)
}
level.Debug(c.logger).Log("msg", "selected ServiceMonitors", "servicemonitors", strings.Join(serviceMonitors, ","), "namespace", p.Namespace, "prometheus", p.Name)
level.Debug(c.logger).Log("msg", "selected ServiceMonitors", "servicemonitors", strings.Join(smKeys, ","), "namespace", p.Namespace, "prometheus", p.Name)
return res, nil
}

View file

@ -74,34 +74,30 @@ func stringMapToMapSlice(m map[string]string) yaml.MapSlice {
}
func addTLStoYaml(cfg yaml.MapSlice, namespace string, tls *v1.TLSConfig) yaml.MapSlice {
pathForSelector := func(sel v1.SecretOrConfigMap) string {
return path.Join(tlsAssetsDir, tlsAssetKeyFromSelector(namespace, sel).String())
}
if tls != nil {
pathPrefix := path.Join(tlsAssetsDir, namespace)
tlsConfig := yaml.MapSlice{
{Key: "insecure_skip_verify", Value: tls.InsecureSkipVerify},
}
if tls.CAFile != "" {
tlsConfig = append(tlsConfig, yaml.MapItem{Key: "ca_file", Value: tls.CAFile})
}
if tls.CA.Secret != nil {
tlsConfig = append(tlsConfig, yaml.MapItem{Key: "ca_file", Value: pathPrefix + "_" + tls.CA.Secret.Name + "_" + tls.CA.Secret.Key})
}
if tls.CA.ConfigMap != nil {
tlsConfig = append(tlsConfig, yaml.MapItem{Key: "ca_file", Value: pathPrefix + "_" + tls.CA.ConfigMap.Name + "_" + tls.CA.ConfigMap.Key})
if tls.CA.Secret != nil || tls.CA.ConfigMap != nil {
tlsConfig = append(tlsConfig, yaml.MapItem{Key: "ca_file", Value: pathForSelector(tls.CA)})
}
if tls.CertFile != "" {
tlsConfig = append(tlsConfig, yaml.MapItem{Key: "cert_file", Value: tls.CertFile})
}
if tls.Cert.Secret != nil {
tlsConfig = append(tlsConfig, yaml.MapItem{Key: "cert_file", Value: pathPrefix + "_" + tls.Cert.Secret.Name + "_" + tls.Cert.Secret.Key})
}
if tls.Cert.ConfigMap != nil {
tlsConfig = append(tlsConfig, yaml.MapItem{Key: "cert_file", Value: pathPrefix + "_" + tls.Cert.ConfigMap.Name + "_" + tls.Cert.ConfigMap.Key})
if tls.Cert.Secret != nil || tls.Cert.ConfigMap != nil {
tlsConfig = append(tlsConfig, yaml.MapItem{Key: "cert_file", Value: pathForSelector(tls.Cert)})
}
if tls.KeyFile != "" {
tlsConfig = append(tlsConfig, yaml.MapItem{Key: "key_file", Value: tls.KeyFile})
}
if tls.KeySecret != nil {
tlsConfig = append(tlsConfig, yaml.MapItem{Key: "key_file", Value: pathPrefix + "_" + tls.KeySecret.Name + "_" + tls.KeySecret.Key})
tlsConfig = append(tlsConfig, yaml.MapItem{Key: "key_file", Value: pathForSelector(v1.SecretOrConfigMap{Secret: tls.KeySecret})})
}
if tls.ServerName != "" {
tlsConfig = append(tlsConfig, yaml.MapItem{Key: "server_name", Value: tls.ServerName})