Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(metrics-operator): add basicauth to prometheus provider #2154

Merged
merged 27 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7642e7c
feat(metrics-operator): first iteration on prometheus auth
RealAnna Sep 21, 2023
90d56f5
feat(metrics-operator): add unit tests
RealAnna Sep 21, 2023
d57d5d7
feat(metrics-operator): add test of header
RealAnna Sep 21, 2023
4a72956
feat(metrics-operator): use roundtripper from prometheus api
RealAnna Sep 22, 2023
7e2ca7e
feat(metrics-operator): fixed test
RealAnna Sep 22, 2023
34fcf45
feat(metrics-operator): revert change to ddprovider
RealAnna Sep 25, 2023
dc17e31
feat(metrics-operator): lint
RealAnna Sep 25, 2023
204a01a
feat(metrics-operator): lint
RealAnna Sep 25, 2023
8493362
feat(metrics-operator): add integration test
RealAnna Sep 25, 2023
c3982e7
feat(metrics-operator): add unit test
RealAnna Sep 25, 2023
3eeb2fe
feat(metrics-operator): add unit test
RealAnna Sep 25, 2023
906a528
fix: useless test setup
RealAnna Sep 25, 2023
198f7de
fix: removed dd special func for secrets
RealAnna Sep 25, 2023
bc72d94
fix: add unit test to make ondrej happy
RealAnna Sep 25, 2023
72a6ba0
Update metrics-operator/controllers/common/providers/prometheus/prome…
RealAnna Sep 26, 2023
febbd21
fix: embed getter in the struct
RealAnna Sep 26, 2023
b940271
Update metrics-operator/controllers/common/providers/prometheus/commo…
RealAnna Sep 26, 2023
4802061
Update metrics-operator/controllers/common/providers/prometheus/prome…
RealAnna Sep 26, 2023
19073cd
deps: update github.com/keptn/lifecycle-toolkit/klt-cert-manager dige…
renovate[bot] Sep 22, 2023
75c51c1
deps: update github.com/keptn/lifecycle-toolkit/klt-cert-manager dige…
renovate[bot] Sep 26, 2023
1ca1af0
feat(metrics-operator): add support for user-friendly duration string…
bacherfl Sep 27, 2023
e1b89a3
feat: add interface
RealAnna Sep 21, 2023
16954d3
feat: add interface
RealAnna Sep 27, 2023
40fa7d2
feat: add interface
RealAnna Sep 27, 2023
cff4bb2
feat: add fix for secrets
RealAnna Sep 27, 2023
9d4b69f
Update lifecycle-operator/go.mod
RealAnna Sep 27, 2023
3f2f736
feat: remove mod error
RealAnna Sep 27, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion metrics-operator/api/v1alpha3/keptnmetricsprovider_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,19 @@ func (p *KeptnMetricsProvider) HasSecretDefined() bool {
if p.Spec.SecretKeyRef == (corev1.SecretKeySelector{}) {
return false
}
if strings.TrimSpace(p.Spec.SecretKeyRef.Name) == "" || strings.TrimSpace(p.Spec.SecretKeyRef.Key) == "" {
//if the secret name exists the secret is defined
if strings.TrimSpace(p.Spec.SecretKeyRef.Name) == "" {
RealAnna marked this conversation as resolved.
Show resolved Hide resolved
return false
}
return true
}

func (p *KeptnMetricsProvider) HasSecretKeyDefined() bool {
if p.Spec.SecretKeyRef == (corev1.SecretKeySelector{}) {
return false
}
//if the secret name exists the secret is defined
if strings.TrimSpace(p.Spec.SecretKeyRef.Key) == "" {
return false
}
return true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"errors"
"fmt"
"strings"

metricsapi "github.com/keptn/lifecycle-toolkit/metrics-operator/api/v1alpha3"
corev1 "k8s.io/api/core/v1"
Expand All @@ -16,18 +15,8 @@ const apiKey, appKey = "DD_CLIENT_API_KEY", "DD_CLIENT_APP_KEY"

var ErrSecretKeyRefNotDefined = errors.New("the SecretKeyRef property with the DataDog API Key is missing")

func hasDDSecretDefined(spec metricsapi.KeptnMetricsProviderSpec) bool {
if spec.SecretKeyRef == (corev1.SecretKeySelector{}) {
return false
}
if strings.TrimSpace(spec.SecretKeyRef.Name) == "" {
return false
}
return true
}

func getDDSecret(ctx context.Context, provider metricsapi.KeptnMetricsProvider, k8sClient client.Client) (string, string, error) {
if !hasDDSecretDefined(provider.Spec) {
if !provider.HasSecretDefined() {
return "", "", ErrSecretKeyRefNotDefined
}
ddCredsSecret := &corev1.Secret{}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type Error struct {
}

func getDTSecret(ctx context.Context, provider metricsapi.KeptnMetricsProvider, k8sClient client.Client) (string, error) {
if !provider.HasSecretDefined() {
if !provider.HasSecretDefined() || !provider.HasSecretKeyDefined() {
return "", ErrSecretKeyRefNotDefined
}
dtCredsSecret := &corev1.Secret{}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,12 @@ import (
)

func TestGetSecret_NoKeyDefined(t *testing.T) {
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte(dtpayload))
require.Nil(t, err)
}))
defer svr.Close()

RealAnna marked this conversation as resolved.
Show resolved Hide resolved
fakeClient := fake.NewClient()

p := metricsapi.KeptnMetricsProvider{
Spec: metricsapi.KeptnMetricsProviderSpec{
TargetServer: svr.URL,
TargetServer: "svr.URL",
},
}
r, e := getDTSecret(context.TODO(), p, fakeClient)
Expand All @@ -31,16 +27,11 @@ func TestGetSecret_NoKeyDefined(t *testing.T) {
}

func TestGetSecret_NoSecret(t *testing.T) {
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte(dtpayload))
require.Nil(t, err)
}))
defer svr.Close()
fakeClient := fake.NewClient()

p := metricsapi.KeptnMetricsProvider{
Spec: metricsapi.KeptnMetricsProviderSpec{
TargetServer: svr.URL,
TargetServer: "svr.URL",
},
}
r, e := getDTSecret(context.TODO(), p, fakeClient)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package prometheus

import (
"context"
"errors"
"net/http"

metricsapi "github.com/keptn/lifecycle-toolkit/metrics-operator/api/v1alpha3"
promapi "github.com/prometheus/client_golang/api"
"github.com/prometheus/common/config"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)

const secretKeyUserName = "user"
const secretKeyPassword = "password"

var ErrSecretKeyRefNotDefined = errors.New("the SecretKeyRef property with the Prometheus API Key is missing")
var ErrInvalidSecretFormat = errors.New("secret key does not contain user and password")

type SecretData struct {
User string `json:"user"`
Password config.Secret `json:"password"`
}

//go:generate moq -pkg fake -skip-ensure -out ./fake/roundtripper_mock.go . IRoundTripper
type IRoundTripper interface {
GetRoundTripper(context.Context, metricsapi.KeptnMetricsProvider, client.Client) (http.RoundTripper, error)
}

type RoundTripperRetriever struct {
}

func (r RoundTripperRetriever) GetRoundTripper(ctx context.Context, provider metricsapi.KeptnMetricsProvider, k8sClient client.Client) (http.RoundTripper, error) {
secret, err := getPrometheusSecret(ctx, provider, k8sClient)
if err != nil {
if errors.Is(err, ErrSecretKeyRefNotDefined) {
return promapi.DefaultRoundTripper, nil
}
return nil, err
}
return config.NewBasicAuthRoundTripper(secret.User, secret.Password, "", promapi.DefaultRoundTripper), nil
}

func getPrometheusSecret(ctx context.Context, provider metricsapi.KeptnMetricsProvider, k8sClient client.Client) (*SecretData, error) {
if !provider.HasSecretDefined() {
return nil, ErrSecretKeyRefNotDefined
}
secret := &corev1.Secret{}
if err := k8sClient.Get(ctx, types.NamespacedName{Name: provider.Spec.SecretKeyRef.Name, Namespace: provider.Namespace}, secret); err != nil {
return nil, err
}

var secretData SecretData
user, ok := secret.Data[secretKeyUserName]
pw, yes := secret.Data[secretKeyPassword]
if !ok || !yes {
return nil, ErrInvalidSecretFormat
}
secretData.User = string(user)
secretData.Password = config.Secret(pw)
return &secretData, nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
package prometheus

import (
"context"
"net/http"
"net/http/httptest"
"reflect"
"strings"
"testing"

metricsapi "github.com/keptn/lifecycle-toolkit/metrics-operator/api/v1alpha3"
"github.com/keptn/lifecycle-toolkit/metrics-operator/controllers/common/fake"
promapi "github.com/prometheus/client_golang/api"
"github.com/prometheus/common/config"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)

const prometheusPayload = "test"

func TestGetSecret_NoKeyDefined(t *testing.T) {

fakeClient := fake.NewClient()

p := metricsapi.KeptnMetricsProvider{
Spec: metricsapi.KeptnMetricsProviderSpec{
TargetServer: "svr.URL",
},
}
r1, e := getPrometheusSecret(context.TODO(), p, fakeClient)
require.NotNil(t, e)
require.ErrorIs(t, ErrSecretKeyRefNotDefined, e)
require.Empty(t, r1)

}

func TestGetSecret_NoSecretDefined(t *testing.T) {

secretName := "testSecret"

fakeClient := fake.NewClient()

b := true
p := metricsapi.KeptnMetricsProvider{
Spec: metricsapi.KeptnMetricsProviderSpec{
SecretKeyRef: v1.SecretKeySelector{
Key: "apiKey",
LocalObjectReference: v1.LocalObjectReference{
Name: secretName,
},
Optional: &b,
},
TargetServer: "svr",
},
}
r1, e := getPrometheusSecret(context.TODO(), p, fakeClient)
require.NotNil(t, e)
t.Log(e.Error())
require.True(t, k8serrors.IsNotFound(e))
require.Empty(t, r1)

}

func TestGetSecret_HappyPath(t *testing.T) {
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte(prometheusPayload))
require.Nil(t, err)
}))
defer svr.Close()

secretName := "mySecret"
apiToken := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Namespace: "default",
},
Data: map[string][]byte{
"user": []byte("myuser"),
"password": []byte("mytoken"),
},
}
fakeClient := fake.NewClient(apiToken)

p := metricsapi.KeptnMetricsProvider{
ObjectMeta: metav1.ObjectMeta{Namespace: "default"},
Spec: metricsapi.KeptnMetricsProviderSpec{
SecretKeyRef: v1.SecretKeySelector{
Key: "login",
LocalObjectReference: v1.LocalObjectReference{
Name: secretName,
},
},
TargetServer: svr.URL,
},
}
r1, e := getPrometheusSecret(context.TODO(), p, fakeClient)
require.Nil(t, e)
require.Equal(t, "myuser", r1.User)
require.Equal(t, config.Secret("mytoken"), r1.Password)

}

func Test_GetRoundtripper(t *testing.T) {
goodsecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
Namespace: "default",
},
Data: map[string][]byte{
"user": []byte("myuser"),
"password": []byte("mytoken"),
},
}
tests := []struct {
name string
provider metricsapi.KeptnMetricsProvider
k8sClient client.Client
want http.RoundTripper
wantErr bool
errorStr string
}{
{
name: "TestSuccess",
provider: metricsapi.KeptnMetricsProvider{
ObjectMeta: metav1.ObjectMeta{Namespace: "default"},
Spec: metricsapi.KeptnMetricsProviderSpec{
Type: "",
TargetServer: "",
SecretKeyRef: v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "test",
},
Key: "",
Optional: nil,
},
},
},
k8sClient: fake.NewClient(goodsecret),
want: config.NewBasicAuthRoundTripper("myuser", "mytoken", "", promapi.DefaultRoundTripper),
wantErr: false,
},
{
name: "TestSecretNotDefined",
provider: metricsapi.KeptnMetricsProvider{},
k8sClient: fake.NewClient(),
want: promapi.DefaultRoundTripper,
wantErr: false,
},
{
name: "TestErrorFromGetPrometheusSecretNotExists",
provider: metricsapi.KeptnMetricsProvider{
ObjectMeta: metav1.ObjectMeta{Namespace: "default"},
Spec: metricsapi.KeptnMetricsProviderSpec{
Type: "",
TargetServer: "",
SecretKeyRef: v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "test",
},
Key: "",
Optional: nil,
},
},
},
k8sClient: fake.NewClient(),
want: nil,
wantErr: true,
errorStr: "not found",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := RoundTripperRetriever{}.GetRoundTripper(context.TODO(), tt.provider, tt.k8sClient)
t.Log(err)
if (err != nil) != tt.wantErr {
t.Errorf("getRoundtripper() error = %v, wantErr %v", err, tt.wantErr)
return
}
if tt.errorStr != "" && !strings.Contains(err.Error(), tt.errorStr) {
t.Errorf("getRoundtripper() error = %s, wantErr %s", err.Error(), tt.errorStr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("getRoundtripper() got = %v, want %v", got, tt.want)
}
})
}
}
Loading
Loading