Skip to content
6 changes: 1 addition & 5 deletions pkg/clients/dynatrace/settings/kspm.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,5 @@ func (c *Client) CreateKSPMSetting(ctx context.Context, monitoredEntity string,
return "", fmt.Errorf("create kspm setting: %w", err)
}

if len(response) != 1 {
return "", tooManyEntriesError(len(response))
}

return response[0].ObjectID, nil
return getObjectID(response)
}
2 changes: 1 addition & 1 deletion pkg/clients/dynatrace/settings/kspm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func TestCreateKSPMSetting(t *testing.T) {

client := NewClient(apiClient)
objectID, err := client.CreateKSPMSetting(ctx, "scope-1", true)
require.ErrorAs(t, err, new(tooManyEntriesError))
require.ErrorAs(t, err, new(notSingleEntryError))
assert.Empty(t, objectID)
})
}
6 changes: 1 addition & 5 deletions pkg/clients/dynatrace/settings/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,7 @@ func (c *Client) performCreateOrUpdateKubernetesSetting(ctx context.Context, bod
return "", fmt.Errorf("create kubernetes setting: %w", err)
}

if len(response) != 1 {
return "", tooManyEntriesError(len(response))
}

return response[0].ObjectID, nil
return getObjectID(response)
}

func v1KubernetesObjectBody(clusterLabel, kubeSystemUUID, scope string) []postObjectsBody[kubernetesObjectValue] {
Expand Down
2 changes: 1 addition & 1 deletion pkg/clients/dynatrace/settings/kubernetes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func TestCreateOrUpdateKubernetesSetting(t *testing.T) {

client := NewClient(apiClient)
_, err := client.CreateOrUpdateKubernetesSetting(ctx, "label-1", "uuid-1", "scope-1")
require.ErrorAs(t, err, new(tooManyEntriesError))
require.ErrorAs(t, err, new(notSingleEntryError))
})
}

Expand Down
6 changes: 1 addition & 5 deletions pkg/clients/dynatrace/settings/logmonitoring.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,7 @@ func (c *Client) CreateLogMonitoringSetting(ctx context.Context, scope, clusterN
return "", fmt.Errorf("create logmonitoring setting: %w", err)
}

if len(response) != 1 {
return "", tooManyEntriesError(len(response))
}

return response[0].ObjectID, nil
return getObjectID(response)
}

func mapIngestRuleMatchers(input []logmonitoring.IngestRuleMatchers) []ingestRuleMatchers {
Expand Down
2 changes: 1 addition & 1 deletion pkg/clients/dynatrace/settings/logmonitoring_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func TestCreateLogMonitoringSetting(t *testing.T) {

client := NewClient(apiClient)
objectID, err := client.CreateLogMonitoringSetting(ctx, "scope-1", "cluster-1", nil)
require.ErrorAs(t, err, new(tooManyEntriesError))
require.ErrorAs(t, err, new(notSingleEntryError))
assert.Empty(t, objectID)
})
}
Expand Down
15 changes: 13 additions & 2 deletions pkg/clients/dynatrace/settings/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,20 @@ func newPostObjectsBody[T any](schemaID, schemaVersion, scope string, value T) [
}
}

type tooManyEntriesError int
// getObjectID gives back the ID of the first element of the post response.
// If there are 0 or multiple entries, it will error.
// We only create (post) Settings if they do not exist yet, so receiving back not exactly one object is a cause for alarm.
func getObjectID(response []postObjectsResponse) (string, error) {
if len(response) != 1 {
return "", notSingleEntryError(len(response))
}

return response[0].ObjectID, nil
}

type notSingleEntryError int

func (num tooManyEntriesError) Error() string {
func (num notSingleEntryError) Error() string {
return fmt.Sprintf("response is not containing exactly one entry, got %d entries", int(num))
}

Expand Down
6 changes: 3 additions & 3 deletions pkg/controllers/dynakube/kspm/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ import (
type Reconciler struct {
tokenReconciler *token.Reconciler
daemonSetReconciler *daemonset.Reconciler
settingsReconciler *settings.Reconciler
settingsReconciler *kspmsettings.Reconciler
}

func NewReconciler(client client.Client, apiReader client.Reader) *Reconciler {
return &Reconciler{
tokenReconciler: token.NewReconciler(client, apiReader),
daemonSetReconciler: daemonset.NewReconciler(client, apiReader),
settingsReconciler: settings.NewReconciler(),
settingsReconciler: kspmsettings.NewReconciler(),
}
}

Expand All @@ -35,7 +35,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, dtc dtsettings.APIClient, dk

err = r.settingsReconciler.Reconcile(ctx, dtc, dk)
if err != nil {
log.Info("failed to reconcile Dynatrace KSPM DaemonSet")
log.Info("failed to reconcile KSPM Settings")

return err
}
Expand Down
44 changes: 21 additions & 23 deletions pkg/controllers/dynakube/kspm/settings/conditions.go
Original file line number Diff line number Diff line change
@@ -1,44 +1,32 @@
package settings
package kspmsettings

import (
"fmt"

"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
alreadyExistReason = "AlreadyExist"
skippedReason = "Skipped"
errorReason = "Error"
createdReason = "Created"
existsReason = "Exists"
outdatedReason = "Outdated"
skippedReason = "Skipped"
errorReason = "Error"

ConditionType = "KSPMSettings"
conditionType = "KSPMSettings"
)

func setCreatedCondition(conditions *[]metav1.Condition, datasetPipelineEnabled bool) {
condition := metav1.Condition{
Type: ConditionType,
Status: metav1.ConditionTrue,
Reason: createdReason,
Message: fmt.Sprintf("KSPM settings have been created. configurationDatasetPipelineEnable: %t", datasetPipelineEnabled),
}
_ = meta.SetStatusCondition(conditions, condition)
}

func setAlreadyExistsCondition(conditions *[]metav1.Condition) {
func setExistsCondition(conditions *[]metav1.Condition) {
condition := metav1.Condition{
Type: ConditionType,
Type: conditionType,
Status: metav1.ConditionTrue,
Reason: alreadyExistReason,
Reason: existsReason,
Message: "KSPM settings already exist, will not create new ones.",
}
_ = meta.SetStatusCondition(conditions, condition)
}

func setErrorCondition(conditions *[]metav1.Condition, message string) {
condition := metav1.Condition{
Type: ConditionType,
Type: conditionType,
Status: metav1.ConditionFalse,
Reason: errorReason,
Message: "KSPM settings creation was skipped: " + message,
Expand All @@ -48,10 +36,20 @@ func setErrorCondition(conditions *[]metav1.Condition, message string) {

func setSkippedCondition(conditions *[]metav1.Condition, message string) {
condition := metav1.Condition{
Type: ConditionType,
Type: conditionType,
Status: metav1.ConditionFalse,
Reason: skippedReason,
Message: "KSPM settings creation was skipped: " + message,
}
_ = meta.SetStatusCondition(conditions, condition)
}

func setOutdatedCondition(conditions *[]metav1.Condition) {
condition := metav1.Condition{
Type: conditionType,
Status: metav1.ConditionFalse,
Reason: outdatedReason,
Message: "Condition outdated",
}
_ = meta.SetStatusCondition(conditions, condition)
}
14 changes: 8 additions & 6 deletions pkg/controllers/dynakube/kspm/settings/reconciler.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package settings
package kspmsettings

import (
"context"
Expand Down Expand Up @@ -31,15 +31,17 @@ func NewReconciler() *Reconciler {
func (r *Reconciler) Reconcile(ctx context.Context, dtc dtsettings.APIClient, dk *dynakube.DynaKube) error {
// Kubernetes Monitoring is REQUIRED for KSPM, so it is ok to just check for this.
if !dk.ActiveGate().IsKubernetesMonitoringEnabled() {
_ = meta.RemoveStatusCondition(dk.Conditions(), ConditionType)
_ = meta.RemoveStatusCondition(dk.Conditions(), conditionType)

return nil
}

if !k8sconditions.IsOutdated(r.timeProvider, dk, ConditionType) {
if !k8sconditions.IsOutdated(r.timeProvider, dk, conditionType) {
return nil
}

setOutdatedCondition(dk.Conditions()) // needed so the timestamp updates, will never actually show up in the status

hasReadScope := k8sconditions.IsOptionalScopeAvailable(dk, dtclient.ConditionTypeAPITokenSettingsRead)
hasWriteScope := k8sconditions.IsOptionalScopeAvailable(dk, dtclient.ConditionTypeAPITokenSettingsWrite)

Expand All @@ -54,7 +56,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, dtc dtsettings.APIClient, dk

if len(missingScopes) > 0 {
message := strings.Join(missingScopes, ", ") + " scope(s) missing: cannot query existing kspm monitoring setting and/or safely create new one."
k8sconditions.SetOptionalScopeMissing(dk.Conditions(), ConditionType, message)
k8sconditions.SetOptionalScopeMissing(dk.Conditions(), conditionType, message)
log.Info(message)

return nil
Expand Down Expand Up @@ -92,7 +94,7 @@ func (r *Reconciler) checkKSPMSettings(ctx context.Context, dtc dtsettings.APICl
if kspmSettings.TotalCount > 0 {
log.Info("there are already settings", "settings", kspmSettings)

setAlreadyExistsCondition(dk.Conditions())
setExistsCondition(dk.Conditions())

return nil
}
Expand All @@ -106,7 +108,7 @@ func (r *Reconciler) checkKSPMSettings(ctx context.Context, dtc dtsettings.APICl
return err
}

setCreatedCondition(dk.Conditions(), datasetPipelineEnabled)
setExistsCondition(dk.Conditions())
log.Info("kspm setting created", "settings", objectID, "configurationDatasetPipelineEnabled", datasetPipelineEnabled)

return nil
Expand Down
Loading