Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 9 additions & 7 deletions cmd/hydration-controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,15 @@ func main() {
klog.Fatalf("Failed to register the OTLP metrics exporter: %v", err)
}

defer func() {
shutdownCtx, cancel := context.WithTimeout(context.Background(), kmetrics.ShutdownTimeout)
defer cancel()
if err := oce.Shutdown(shutdownCtx); err != nil {
klog.Fatalf("Unable to stop the OTLP metrics exporter: %v", err)
}
}()
if oce != nil {
defer func() {
shutdownCtx, cancel := context.WithTimeout(context.Background(), kmetrics.ShutdownTimeout)
defer cancel()
if err := oce.Shutdown(shutdownCtx); err != nil {
klog.Fatalf("Unable to stop the OTLP metrics exporter: %v", err)
}
}()
}

absRepoRootDir, err := cmpath.AbsoluteOS(*repoRootDir)
if err != nil {
Expand Down
64 changes: 36 additions & 28 deletions cmd/reconciler-manager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,26 +161,30 @@ func main() {
})
setupLog.Info("RootSync controller registration scheduled")

otelCredentialProvider := &auth.CachingCredentialProvider{
Scopes: traceapi.DefaultAuthScopes(),
}
if os.Getenv("DISABLE_MONITORING") != "true" {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notice that the CRD API uses positive naming (spec.monitoring.enabled: false) while the internal environment variable uses negative opt-out naming (DISABLE_MONITORING=true). While spec.monitoring.enabled follows K8s API conventions and DISABLE_MONITORING=true provides backward-compatible defaults when unset, the difference might confuse contributors. Could we add a short code comment in pkg/reconcilermanager/controllers/util.go explaining this naming rationale?

otelCredentialProvider := &auth.CachingCredentialProvider{
Scopes: traceapi.DefaultAuthScopes(),
}

otel := controllers.NewOtelReconciler(mgr.GetClient(),
logger.WithName("controllers").WithName("Otel"),
otelCredentialProvider)
if err := otel.Register(mgr); err != nil {
setupLog.Error(err, "failed to register controller", "controller", "Otel")
os.Exit(1)
}
setupLog.Info("Otel controller registration successful")
otel := controllers.NewOtelReconciler(mgr.GetClient(),
logger.WithName("controllers").WithName("Otel"),
otelCredentialProvider)
if err := otel.Register(mgr); err != nil {
setupLog.Error(err, "failed to register controller", "controller", "Otel")
os.Exit(1)
}
setupLog.Info("Otel controller registration successful")

otelSA := controllers.NewOtelSAReconciler(*clusterName, mgr.GetClient(),
logger.WithName("controllers").WithName(controllers.OtelSALoggerName))
if err := otelSA.Register(mgr); err != nil {
setupLog.Error(err, "failed to register controller", "controller", "OtelSA")
os.Exit(1)
otelSA := controllers.NewOtelSAReconciler(*clusterName, mgr.GetClient(),
logger.WithName("controllers").WithName(controllers.OtelSALoggerName))
if err := otelSA.Register(mgr); err != nil {
setupLog.Error(err, "failed to register controller", "controller", "OtelSA")
os.Exit(1)
}
setupLog.Info("OtelSA controller registration successful")
} else {
setupLog.Info("Otel and OtelSA controller registration skipped (DISABLE_MONITORING=true)")
}
setupLog.Info("OtelSA controller registration successful")

// Register the OTLP metrics exporter and metrics instruments
ctx := context.Background()
Expand All @@ -190,24 +194,28 @@ func main() {
os.Exit(1)
}

defer func() {
shutdownCtx, cancel := context.WithTimeout(context.Background(), metrics.ShutdownTimeout)
defer cancel()
if err := oce.Shutdown(shutdownCtx); err != nil {
setupLog.Error(err, "failed to stop the OTLP metrics exporter")
}
}()
if oce != nil {
defer func() {
shutdownCtx, cancel := context.WithTimeout(context.Background(), metrics.ShutdownTimeout)
defer cancel()
if err := oce.Shutdown(shutdownCtx); err != nil {
setupLog.Error(err, "failed to stop the OTLP metrics exporter")
}
}()
}

// +kubebuilder:scaffold:builder

setupLog.Info("starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
setupLog.Error(err, "problem running manager")
// os.Exit(1) does not run deferred functions so explicitly stopping the OTLP metrics exporter.
shutdownCtx, cancel := context.WithTimeout(context.Background(), metrics.ShutdownTimeout)
defer cancel()
if err := oce.Shutdown(shutdownCtx); err != nil {
setupLog.Error(err, "failed to stop the OTLP metrics exporter")
if oce != nil {
shutdownCtx, cancel := context.WithTimeout(context.Background(), metrics.ShutdownTimeout)
defer cancel()
if err := oce.Shutdown(shutdownCtx); err != nil {
setupLog.Error(err, "failed to stop the OTLP metrics exporter")
}
}
os.Exit(1)
}
Expand Down
16 changes: 9 additions & 7 deletions cmd/reconciler/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,13 +148,15 @@ func main() {
klog.Fatalf("Failed to register the OTLP metrics exporter: %v", err)
}

defer func() {
shutdownCtx, cancel := context.WithTimeout(context.Background(), ocmetrics.ShutdownTimeout)
defer cancel()
if err := oce.Shutdown(shutdownCtx); err != nil {
klog.Fatalf("Unable to stop the OTLP metrics exporter: %v", err)
}
}()
if oce != nil {
defer func() {
shutdownCtx, cancel := context.WithTimeout(context.Background(), ocmetrics.ShutdownTimeout)
defer cancel()
if err := oce.Shutdown(shutdownCtx); err != nil {
klog.Fatalf("Unable to stop the OTLP metrics exporter: %v", err)
}
}()
}

absRepoRoot, err := cmpath.AbsoluteOS(*repoRootDir)
if err != nil {
Expand Down
205 changes: 205 additions & 0 deletions e2e/testcases/disable_monitoring_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
// Copyright 2024 Google LLC
//
// 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 e2e

import (
"fmt"
"testing"

appsv1 "k8s.io/api/apps/v1"
"k8s.io/utils/ptr"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/GoogleContainerTools/config-sync/e2e/nomostest"
testmetrics "github.com/GoogleContainerTools/config-sync/e2e/nomostest/metrics"
"github.com/GoogleContainerTools/config-sync/e2e/nomostest/ntopts"
nomostesting "github.com/GoogleContainerTools/config-sync/e2e/nomostest/testing"
"github.com/GoogleContainerTools/config-sync/e2e/nomostest/testpredicates"
"github.com/GoogleContainerTools/config-sync/e2e/nomostest/testwatcher"
"github.com/GoogleContainerTools/config-sync/pkg/api/configsync"
"github.com/GoogleContainerTools/config-sync/pkg/api/configsync/v1beta1"
"github.com/GoogleContainerTools/config-sync/pkg/core"
"github.com/GoogleContainerTools/config-sync/pkg/core/k8sobjects"
"github.com/GoogleContainerTools/config-sync/pkg/kinds"
"github.com/GoogleContainerTools/config-sync/pkg/metrics"
"github.com/GoogleContainerTools/config-sync/pkg/reconcilermanager"
)

func deploymentMissingVolume(volumeName string) testpredicates.Predicate {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest moving the predicates to e2e/nomostest/testpredicates/predicates.go

return func(o client.Object) error {
if o == nil {
return testpredicates.ErrObjectNotFound
}
d, ok := o.(*appsv1.Deployment)
if !ok {
return testpredicates.WrongTypeErr(o, d)
}
for _, volume := range d.Spec.Template.Spec.Volumes {
if volume.Name == volumeName {
return fmt.Errorf("Deployment %s should not have volume %s", core.ObjectNamespacedName(o), volumeName)
}
}
return nil
}
}

func deploymentHasVolume(volumeName string) testpredicates.Predicate {
return func(o client.Object) error {
if o == nil {
return testpredicates.ErrObjectNotFound
}
d, ok := o.(*appsv1.Deployment)
if !ok {
return testpredicates.WrongTypeErr(o, d)
}
for _, volume := range d.Spec.Template.Spec.Volumes {
if volume.Name == volumeName {
return nil
}
}
return fmt.Errorf("Deployment %s should have volume %s", core.ObjectNamespacedName(o), volumeName)
}
}

func TestDisableMonitoringRootSync(t *testing.T) {
rootSyncID := nomostest.DefaultRootSyncID
nt := nomostest.New(t, nomostesting.OverrideAPI,
ntopts.SyncWithGitSource(rootSyncID, ntopts.Unstructured))

rootReconcilerName := core.RootReconcilerObjectKey(rootSyncID.Name)
rootSyncV1 := k8sobjects.RootSyncObjectV1Beta1(configsync.RootSyncName)

nt.Must(nt.WatchForAllSyncs())

// validate initial monitoring enabled (default state)
nt.Must(nt.Watcher.WatchObject(kinds.Deployment(),
rootReconcilerName.Name, rootReconcilerName.Namespace,
testwatcher.WatchPredicates(
testpredicates.DeploymentHasContainer(metrics.OtelAgentName),
deploymentHasVolume("otel-agent-config-reconciler-vol"),
testpredicates.DeploymentMissingEnvVar(reconcilermanager.Reconciler, "DISABLE_MONITORING"),
),
))
err := nomostest.ValidateStandardMetricsForRootSync(nt, testmetrics.Summary{Sync: rootSyncID.ObjectKey})
if err != nil {
t.Fatalf("Expected standard metrics to be present when monitoring is enabled, but got: %v", err)
}

// disable monitoring
nt.MustMergePatch(rootSyncV1, `{"spec": {"monitoring": {"enabled": false}}}`)

nt.Must(nt.Watcher.WatchObject(kinds.Deployment(),
rootReconcilerName.Name, rootReconcilerName.Namespace,
testwatcher.WatchPredicates(
testpredicates.DeploymentMissingContainer(metrics.OtelAgentName),
deploymentMissingVolume("otel-agent-config-reconciler-vol"),
testpredicates.DeploymentHasEnvVar(reconcilermanager.Reconciler, "DISABLE_MONITORING", "true"),
),
))
Comment thread
cowsking marked this conversation as resolved.
nt.Must(nt.WatchForAllSyncs())

err = nomostest.ValidateStandardMetricsForRootSync(nt, testmetrics.Summary{Sync: rootSyncID.ObjectKey})
if err == nil {
t.Fatal("Expected an error when validating metrics for RootSync with disabled monitoring, but got nil")
}

// re-enable monitoring
nt.MustMergePatch(rootSyncV1, `{"spec": {"monitoring": {"enabled": true}}}`)

nt.Must(nt.Watcher.WatchObject(kinds.Deployment(),
rootReconcilerName.Name, rootReconcilerName.Namespace,
testwatcher.WatchPredicates(
testpredicates.DeploymentHasContainer(metrics.OtelAgentName),
deploymentHasVolume("otel-agent-config-reconciler-vol"),
testpredicates.DeploymentMissingEnvVar(reconcilermanager.Reconciler, "DISABLE_MONITORING"),
),
))
nt.Must(nt.WatchForAllSyncs())

err = nomostest.ValidateStandardMetricsForRootSync(nt, testmetrics.Summary{Sync: rootSyncID.ObjectKey})
if err != nil {
t.Fatalf("Expected standard metrics to be present after re-enabling monitoring, but got: %v", err)
}
}
Comment thread
cowsking marked this conversation as resolved.

func TestDisableMonitoringRepoSync(t *testing.T) {
repoSyncID := core.RepoSyncID(configsync.RepoSyncName, frontendNamespace)
nt := nomostest.New(t, nomostesting.OverrideAPI,
ntopts.SyncWithGitSource(nomostest.DefaultRootSyncID, ntopts.Unstructured),
ntopts.SyncWithGitSource(repoSyncID))
rootSyncGitRepo := nt.SyncSourceGitReadWriteRepository(nomostest.DefaultRootSyncID)
repoSyncKey := repoSyncID.ObjectKey

frontendReconcilerNN := core.NsReconcilerObjectKey(repoSyncID.Namespace, repoSyncID.Name)
repoSyncFrontend := nomostest.RepoSyncObjectV1Beta1FromNonRootRepo(nt, repoSyncKey)

nt.Must(nt.WatchForAllSyncs())

// Verify ns-reconciler-frontend uses the default monitoring state (enabled)
nt.Must(nt.Watcher.WatchObject(kinds.Deployment(),
frontendReconcilerNN.Name, frontendReconcilerNN.Namespace,
testwatcher.WatchPredicates(
testpredicates.DeploymentHasContainer(metrics.OtelAgentName),
deploymentHasVolume("otel-agent-config-reconciler-vol"),
testpredicates.DeploymentMissingEnvVar(reconcilermanager.Reconciler, "DISABLE_MONITORING"),
),
))
err := nomostest.ValidateStandardMetricsForRepoSync(nt, testmetrics.Summary{Sync: repoSyncID.ObjectKey})
if err != nil {
t.Fatalf("Expected standard metrics to be present when monitoring is enabled, but got: %v", err)
}

// Disable monitoring
repoSyncFrontend.Spec.Monitoring = &v1beta1.MonitoringSpec{Enabled: ptr.To(false)}
nt.Must(rootSyncGitRepo.Add(nomostest.StructuredNSPath(repoSyncID.Namespace, repoSyncID.Name), repoSyncFrontend))
nt.Must(rootSyncGitRepo.CommitAndPush("Disable monitoring of frontend Reposync"))

// validate override and make sure otel-agent is missing
nt.Must(nt.Watcher.WatchObject(kinds.Deployment(),
frontendReconcilerNN.Name, frontendReconcilerNN.Namespace,
testwatcher.WatchPredicates(
testpredicates.DeploymentMissingContainer(metrics.OtelAgentName),
deploymentMissingVolume("otel-agent-config-reconciler-vol"),
testpredicates.DeploymentHasEnvVar(reconcilermanager.Reconciler, "DISABLE_MONITORING", "true"),
),
))
nt.Must(nt.WatchForAllSyncs())

err = nomostest.ValidateStandardMetricsForRepoSync(nt, testmetrics.Summary{Sync: repoSyncID.ObjectKey})
if err == nil {
t.Fatal("Expected an error when validating metrics for RepoSync with disabled monitoring, but got nil")
}

// Re-enable monitoring
repoSyncFrontend.Spec.Monitoring = &v1beta1.MonitoringSpec{Enabled: ptr.To(true)}
nt.Must(rootSyncGitRepo.Add(nomostest.StructuredNSPath(repoSyncID.Namespace, repoSyncID.Name), repoSyncFrontend))
nt.Must(rootSyncGitRepo.CommitAndPush("Re-enable monitoring of frontend RepoSync"))

// validate container comes back
nt.Must(nt.Watcher.WatchObject(kinds.Deployment(),
frontendReconcilerNN.Name, frontendReconcilerNN.Namespace,
testwatcher.WatchPredicates(
testpredicates.DeploymentHasContainer(metrics.OtelAgentName),
deploymentHasVolume("otel-agent-config-reconciler-vol"),
testpredicates.DeploymentMissingEnvVar(reconcilermanager.Reconciler, "DISABLE_MONITORING"),
),
))
nt.Must(nt.WatchForAllSyncs())

err = nomostest.ValidateStandardMetricsForRepoSync(nt, testmetrics.Summary{Sync: repoSyncID.ObjectKey})
if err != nil {
t.Fatalf("Expected standard metrics to be present after re-enabling monitoring, but got: %v", err)
}
}
16 changes: 16 additions & 0 deletions manifests/reposync-crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,14 @@ spec:
- chart
- repo
type: object
monitoring:
description: monitoring defines the monitoring configuration.
properties:
enabled:
default: true
description: enabled controls whether metrics exporting is enabled.
type: boolean
type: object
oci:
description: oci contains configuration specific to importing resources
from an OCI package.
Expand Down Expand Up @@ -1409,6 +1417,14 @@ spec:
- chart
- repo
type: object
monitoring:
description: monitoring defines the monitoring configuration.
properties:
enabled:
default: true
description: enabled controls whether metrics exporting is enabled.
type: boolean
type: object
oci:
description: oci contains configuration specific to importing resources
from an OCI package.
Expand Down
16 changes: 16 additions & 0 deletions manifests/rootsync-crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,14 @@ spec:
- chart
- repo
type: object
monitoring:
description: monitoring defines the monitoring configuration.
properties:
enabled:
default: true
description: enabled controls whether metrics exporting is enabled.
type: boolean
type: object
oci:
description: oci contains configuration specific to importing resources
from an OCI package.
Expand Down Expand Up @@ -1482,6 +1490,14 @@ spec:
- chart
- repo
type: object
monitoring:
description: monitoring defines the monitoring configuration.
properties:
enabled:
default: true
description: enabled controls whether metrics exporting is enabled.
type: boolean
type: object
oci:
description: oci contains configuration specific to importing resources
from an OCI package.
Expand Down
Loading