mirror of
https://github.com/prometheus-operator/prometheus-operator.git
synced 2025-04-21 11:48:53 +00:00
Add tests for assetStore
Signed-off-by: Simon Pasquier <spasquie@redhat.com>
This commit is contained in:
parent
a0a1816f4c
commit
7ed47043ce
4 changed files with 885 additions and 231 deletions
pkg/prometheus
267
pkg/prometheus/asset_store.go
Normal file
267
pkg/prometheus/asset_store.go
Normal 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
|
||||
}
|
611
pkg/prometheus/asset_store_test.go
Normal file
611
pkg/prometheus/asset_store_test.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
|
@ -1820,216 +1819,3 @@ func (c *Operator) listMatchingNamespaces(selector labels.Selector) ([]string, e
|
|||
}
|
||||
return ns, nil
|
||||
}
|
||||
|
||||
type tlsAssetKey struct {
|
||||
from string
|
||||
ns string
|
||||
name string
|
||||
key string
|
||||
}
|
||||
|
||||
func tlsAssetKeyFromSecretSelector(ns string, sel *v1.SecretKeySelector) tlsAssetKey {
|
||||
return tlsAssetKey{
|
||||
from: "configmap",
|
||||
ns: ns,
|
||||
name: sel.Name,
|
||||
key: sel.Key,
|
||||
}
|
||||
}
|
||||
|
||||
func tlsAssetKeyFromConfigMapSelector(ns string, sel *v1.ConfigMapKeySelector) tlsAssetKey {
|
||||
return tlsAssetKey{
|
||||
from: "secret",
|
||||
ns: ns,
|
||||
name: sel.Name,
|
||||
key: sel.Key,
|
||||
}
|
||||
}
|
||||
|
||||
func (k tlsAssetKey) String() string {
|
||||
return fmt.Sprintf("%s_%s_%s_%s", k.from, k.ns, k.name, k.key)
|
||||
}
|
||||
|
||||
type assetStore struct {
|
||||
cmClient corev1client.ConfigMapsGetter
|
||||
sClient corev1client.SecretsGetter
|
||||
objStore cache.Store
|
||||
|
||||
tlsAssets map[tlsAssetKey]TLSAsset
|
||||
bearerTokenAssets map[string]BearerToken
|
||||
basicAuthAssets map[string]BasicAuthCredentials
|
||||
}
|
||||
|
||||
func newAssetStore(cmClient corev1client.ConfigMapsGetter, sClient corev1client.SecretsGetter) *assetStore {
|
||||
assetStore := &assetStore{
|
||||
cmClient: cmClient,
|
||||
sClient: sClient,
|
||||
tlsAssets: make(map[tlsAssetKey]TLSAsset),
|
||||
bearerTokenAssets: make(map[string]BearerToken),
|
||||
basicAuthAssets: make(map[string]BasicAuthCredentials),
|
||||
}
|
||||
|
||||
assetStore.objStore = cache.NewStore(assetStore.keyFunc)
|
||||
|
||||
return assetStore
|
||||
}
|
||||
|
||||
func (a *assetStore) keyFunc(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)
|
||||
}
|
||||
|
||||
func (a *assetStore) addTLSConfig(ns string, tlsConfig *monitoringv1.TLSConfig) error {
|
||||
if tlsConfig == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if tlsConfig.CA != (monitoringv1.SecretOrConfigMap{}) {
|
||||
switch {
|
||||
case tlsConfig.CA.Secret != nil:
|
||||
ca, err := a.getSecretKey(ns, *tlsConfig.CA.Secret)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get CA")
|
||||
}
|
||||
a.tlsAssets[tlsAssetKeyFromSecretSelector(ns, tlsConfig.CA.Secret)] = TLSAsset(ca)
|
||||
|
||||
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[tlsAssetKeyFromConfigMapSelector(ns, tlsConfig.CA.ConfigMap)] = TLSAsset(ca)
|
||||
}
|
||||
}
|
||||
|
||||
if tlsConfig.Cert != (monitoringv1.SecretOrConfigMap{}) {
|
||||
switch {
|
||||
case tlsConfig.Cert.Secret != nil:
|
||||
cert, err := a.getSecretKey(ns, *tlsConfig.Cert.Secret)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get cert")
|
||||
}
|
||||
a.tlsAssets[tlsAssetKeyFromSecretSelector(ns, tlsConfig.Cert.Secret)] = TLSAsset(cert)
|
||||
|
||||
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[tlsAssetKeyFromConfigMapSelector(ns, tlsConfig.Cert.ConfigMap)] = 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[tlsAssetKeyFromSecretSelector(ns, tlsConfig.KeySecret)] = TLSAsset(key)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ import (
|
|||
"github.com/go-kit/kit/log/level"
|
||||
"github.com/pkg/errors"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
v1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
|
||||
|
@ -75,11 +74,8 @@ func stringMapToMapSlice(m map[string]string) yaml.MapSlice {
|
|||
}
|
||||
|
||||
func addTLStoYaml(cfg yaml.MapSlice, namespace string, tls *v1.TLSConfig) yaml.MapSlice {
|
||||
pathForSecretSelector := func(sel *corev1.SecretKeySelector) string {
|
||||
return path.Join(tlsAssetsDir, tlsAssetKeyFromSecretSelector(namespace, sel).String())
|
||||
}
|
||||
pathForConfigMapSelector := func(sel *corev1.ConfigMapKeySelector) string {
|
||||
return path.Join(tlsAssetsDir, tlsAssetKeyFromConfigMapSelector(namespace, sel).String())
|
||||
pathForSelector := func(sel v1.SecretOrConfigMap) string {
|
||||
return path.Join(tlsAssetsDir, tlsAssetKeyFromSelector(namespace, sel).String())
|
||||
}
|
||||
if tls != nil {
|
||||
tlsConfig := yaml.MapSlice{
|
||||
|
@ -88,26 +84,20 @@ func addTLStoYaml(cfg yaml.MapSlice, namespace string, tls *v1.TLSConfig) yaml.M
|
|||
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: pathForSecretSelector(tls.CA.Secret)})
|
||||
}
|
||||
if tls.CA.ConfigMap != nil {
|
||||
tlsConfig = append(tlsConfig, yaml.MapItem{Key: "ca_file", Value: pathForConfigMapSelector(tls.CA.ConfigMap)})
|
||||
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: pathForSecretSelector(tls.Cert.Secret)})
|
||||
}
|
||||
if tls.Cert.ConfigMap != nil {
|
||||
tlsConfig = append(tlsConfig, yaml.MapItem{Key: "cert_file", Value: pathForConfigMapSelector(tls.Cert.ConfigMap)})
|
||||
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: pathForSecretSelector(tls.KeySecret)})
|
||||
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})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue