Skip to content

Commit

Permalink
single data-ingest endpoint secret per namespace (namespace can not
Browse files Browse the repository at this point in the history
match two or more Dynakubes regarding app injection)

data-ingest url and token also injected as environment variables

init.sh secret is not recreated every few seconds
  • Loading branch information
aorcholski committed Nov 8, 2021
1 parent e88d9d4 commit 925d3b6
Show file tree
Hide file tree
Showing 13 changed files with 689 additions and 33 deletions.
3 changes: 2 additions & 1 deletion config/common/operator/clusterrole-operator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ rules:
- secrets
resourceNames:
- dynatrace-dynakube-config
- dynatrace-data-ingest-endpoint
verbs:
- get
- update
Expand Down Expand Up @@ -95,4 +96,4 @@ rules:
- customresourcedefinitions
verbs:
- list
- watch
- watch
1 change: 1 addition & 0 deletions config/common/webhook/clusterrole-webhook.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ rules:
- secrets
resourceNames:
- dynatrace-dynakube-config
- dynatrace-data-ingest-endpoint
verbs:
- get
- list
Expand Down
114 changes: 114 additions & 0 deletions controllers/dataingestendpointsecret/secret.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package dataingestendpointsecret

import (
"bytes"
"context"
"fmt"

dynatracev1beta1 "github.com/Dynatrace/dynatrace-operator/api/v1beta1"
"github.com/Dynatrace/dynatrace-operator/controllers/kubeobjects"
"github.com/Dynatrace/dynatrace-operator/dtclient"
"github.com/Dynatrace/dynatrace-operator/mapper"
"github.com/Dynatrace/dynatrace-operator/webhook"
"github.com/go-logr/logr"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)

const (
UrlSecretField = "DT_METRICS_INGEST_URL"
TokenSecretField = "DT_METRICS_INGEST_API_TOKEN"
configFile = "endpoint.properties"
)

// EndpointSecretGenerator manages the mint endpoint secret generation for the user namespaces.
type EndpointSecretGenerator struct {
client client.Client
apiReader client.Reader
logger logr.Logger
namespace string
}

func NewEndpointGenerator(client client.Client, apiReader client.Reader, ns string, logger logr.Logger) *EndpointSecretGenerator {
return &EndpointSecretGenerator{
client: client,
apiReader: apiReader,
namespace: ns,
logger: logger,
}
}

// GenerateForNamespace creates the data-ingest-endpoint secret for namespace while only having the name of the corresponding dynakube
// Used by the podInjection webhook in case the namespace lacks the secret.
func (g *EndpointSecretGenerator) GenerateForNamespace(ctx context.Context, dkName, targetNs string) (bool, error) {
g.logger.Info("Reconciling data-ingest endpoint secret for", "namespace", targetNs)
var dk dynatracev1beta1.DynaKube
if err := g.client.Get(ctx, client.ObjectKey{Name: dkName, Namespace: g.namespace}, &dk); err != nil {
return false, err
}

data, err := g.prepare(ctx, &dk)
if err != nil {
return false, err
}
return kubeobjects.CreateOrUpdateSecretIfNotExists(g.client, g.apiReader, webhook.SecretEndpointName, targetNs, data, corev1.SecretTypeOpaque, g.logger)
}

// GenerateForDynakube creates/updates the data-ingest-endpoint secret for EVERY namespace for the given dynakube.
// Used by the dynakube controller during reconcile.
func (g *EndpointSecretGenerator) GenerateForDynakube(ctx context.Context, dk *dynatracev1beta1.DynaKube) (bool, error) {
g.logger.Info("Reconciling data-ingest endpoint secret for", "dynakube", dk.Name)

data, err := g.prepare(ctx, dk)
if err != nil {
return false, err
}

anyUpdate := false
nsList, err := mapper.GetNamespacesForDynakube(ctx, g.apiReader, dk.Name)
if err != nil {
return false, err
}
for _, targetNs := range nsList {
if upd, err := kubeobjects.CreateOrUpdateSecretIfNotExists(g.client, g.apiReader, webhook.SecretEndpointName, targetNs.Name, data, corev1.SecretTypeOpaque, g.logger); err != nil {
return upd, err
} else if upd {
anyUpdate = true
}
}
g.logger.Info("Done updating data-ingest endpoint secrets")
return anyUpdate, nil
}

func (g *EndpointSecretGenerator) prepare(ctx context.Context, dk *dynatracev1beta1.DynaKube) (map[string][]byte, error) {
fields, err := g.PrepareFields(ctx, dk)
if err != nil {
return nil, errors.WithStack(err)
}

var endpointBuf bytes.Buffer
if _, err := endpointBuf.WriteString(fmt.Sprintf("%s=%s\n", UrlSecretField, fields[UrlSecretField])); err != nil {
return nil, errors.WithStack(err)
}
if _, err := endpointBuf.WriteString(fmt.Sprintf("%s=%s\n", TokenSecretField, fields[TokenSecretField])); err != nil {
return nil, errors.WithStack(err)
}

data := map[string][]byte{
configFile: endpointBuf.Bytes(),
}
return data, nil
}

func (g *EndpointSecretGenerator) PrepareFields(ctx context.Context, dk *dynatracev1beta1.DynaKube) (map[string]string, error) {
var tokens corev1.Secret
if err := g.client.Get(ctx, client.ObjectKey{Name: dk.Tokens(), Namespace: g.namespace}, &tokens); err != nil {
return nil, errors.WithMessage(err, "failed to query tokens")
}

return map[string]string{
UrlSecretField: fmt.Sprintf("%s/v2/metrics/ingest", dk.Spec.APIURL),
TokenSecretField: string(tokens.Data[dtclient.DynatraceDataIngestToken]),
}, nil
}
Loading

0 comments on commit 925d3b6

Please sign in to comment.