Skip to content

Commit

Permalink
[ACM-8543] Tag Observatorium CR with a hash of all the config it uses (
Browse files Browse the repository at this point in the history
…#1329)

* Tag Observatorium CR with a hash of all the config it uses

This should make the CR's hash change. As the CR's hash is used to annotate the Observatorium API pods, these will be rolled out and pick up any new value from the configmaps or secrets.

Signed-off-by: Douglas Camata <[email protected]>

* Skip linting false positives

Signed-off-by: Douglas Camata <[email protected]>

* Fix MCO controller test

Signed-off-by: Douglas Camata <[email protected]>

* Fix another MCO controller test

Signed-off-by: Douglas Camata <[email protected]>

* Refactor Observatorium test

Signed-off-by: Douglas Camata <[email protected]>

* Accept case when Obs API config files and secrets aren't found

They might not yet have been synced by the Observatorium Operator.

Signed-off-by: Douglas Camata <[email protected]>

* Run goimports everywhere

Signed-off-by: Douglas Camata <[email protected]>

* Attempt to fix nolint tags for SonarCLoud

Signed-off-by: Douglas Camata <[email protected]>

* Goimports again

Signed-off-by: Douglas Camata <[email protected]>

* Try another syntax to get SonarCloud to recognize nolint clauses

Signed-off-by: Douglas Camata <[email protected]>

* Make SonarCloud happy

Signed-off-by: Douglas Camata <[email protected]>

* Make SonarCloud happy

Signed-off-by: Douglas Camata <[email protected]>

* Make SonarCloud happy

Signed-off-by: Douglas Camata <[email protected]>

---------

Signed-off-by: Douglas Camata <[email protected]>
  • Loading branch information
douglascamata authored Feb 16, 2024
1 parent f7769ea commit 2622434
Show file tree
Hide file tree
Showing 3 changed files with 203 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,13 @@ import (

mchv1 "github.com/stolostron/multiclusterhub-operator/api/v1"

addonv1alpha1 "open-cluster-management.io/api/addon/v1alpha1"
clusterv1 "open-cluster-management.io/api/cluster/v1"

mcoshared "github.com/stolostron/multicluster-observability-operator/operators/multiclusterobservability/api/shared"
mcov1beta2 "github.com/stolostron/multicluster-observability-operator/operators/multiclusterobservability/api/v1beta2"
"github.com/stolostron/multicluster-observability-operator/operators/multiclusterobservability/pkg/config"
"github.com/stolostron/multicluster-observability-operator/operators/multiclusterobservability/pkg/rendering/templates"
addonv1alpha1 "open-cluster-management.io/api/addon/v1alpha1"
clusterv1 "open-cluster-management.io/api/cluster/v1"
)

func init() {
Expand All @@ -58,7 +59,7 @@ func setupTest(t *testing.T) func() {
manifestsPath := path.Join(wd, "../../manifests")
os.Setenv("TEMPLATES_PATH", testManifestsPath)
templates.ResetTemplates()
//clean up the manifest path if left over from previous test
// clean up the manifest path if left over from previous test
if fi, err := os.Lstat(testManifestsPath); err == nil && fi.Mode()&os.ModeSymlink != 0 {
if err = os.Remove(testManifestsPath); err != nil {
t.Logf("Failed to delete symlink(%s) for the test manifests: (%v)", testManifestsPath, err)
Expand Down Expand Up @@ -309,7 +310,7 @@ func TestMultiClusterMonitoringCRUpdate(t *testing.T) {
clientCACerts := newTestCert(config.ClientCACerts, namespace)
grafanaCert := newTestCert(config.GrafanaCerts, namespace)
serverCert := newTestCert(config.ServerCerts, namespace)
//byo case for proxy
// byo case for proxy
proxyRouteBYOCACerts := newTestCert(config.ProxyRouteBYOCAName, namespace)
proxyRouteBYOCert := newTestCert(config.ProxyRouteBYOCERTName, namespace)
// byo case for the alertmanager route
Expand All @@ -319,6 +320,7 @@ func TestMultiClusterMonitoringCRUpdate(t *testing.T) {

objs := []runtime.Object{mco, svc, serverCACerts, clientCACerts, proxyRouteBYOCACerts, grafanaCert, serverCert,
testAmRouteBYOCaSecret, testAmRouteBYOCertSecret, proxyRouteBYOCert, clustermgmtAddon}

// Create a fake client to mock API calls.
cl := fake.NewClientBuilder().WithRuntimeObjects(objs...).Build()

Expand All @@ -339,10 +341,10 @@ func TestMultiClusterMonitoringCRUpdate(t *testing.T) {
if err != nil {
t.Fatalf("reconcile: (%v)", err)
}
//wait for update status
// wait for update status
time.Sleep(1 * time.Second)

//verify openshiftcluster monitoring label is set to true in namespace
// verify openshiftcluster monitoring label is set to true in namespace
updatedNS := &corev1.Namespace{}
err = cl.Get(context.TODO(), types.NamespacedName{
Name: namespace,
Expand Down Expand Up @@ -398,7 +400,7 @@ func TestMultiClusterMonitoringCRUpdate(t *testing.T) {
if err != nil {
t.Fatalf("reconcile: (%v)", err)
}
//wait for update status
// wait for update status
time.Sleep(1 * time.Second)

updatedObjectStoreSecret := &corev1.Secret{}
Expand Down Expand Up @@ -434,7 +436,7 @@ func TestMultiClusterMonitoringCRUpdate(t *testing.T) {
if err != nil {
t.Fatalf("reconcile: (%v)", err)
}
//wait for update status
// wait for update status
time.Sleep(1 * time.Second)

updatedConfigmap := &corev1.ConfigMap{}
Expand Down Expand Up @@ -473,7 +475,7 @@ func TestMultiClusterMonitoringCRUpdate(t *testing.T) {
if err != nil {
t.Fatalf("reconcile: (%v)", err)
}
//wait for update status
// wait for update status
time.Sleep(1 * time.Second)

updatedMCO = &mcov1beta2.MultiClusterObservability{}
Expand Down Expand Up @@ -509,7 +511,7 @@ func TestMultiClusterMonitoringCRUpdate(t *testing.T) {
t.Fatalf("reconcile: (%v)", err)
}
}
//wait for update status
// wait for update status
time.Sleep(1 * time.Second)

updatedMCO = &mcov1beta2.MultiClusterObservability{}
Expand Down Expand Up @@ -552,7 +554,7 @@ func TestMultiClusterMonitoringCRUpdate(t *testing.T) {
if err != nil {
t.Fatalf("reconcile: (%v)", err)
}
//wait for update status
// wait for update status
time.Sleep(1 * time.Second)

updatedMCO = &mcov1beta2.MultiClusterObservability{}
Expand Down Expand Up @@ -586,7 +588,7 @@ func TestMultiClusterMonitoringCRUpdate(t *testing.T) {
if err != nil {
t.Fatalf("reconcile: (%v)", err)
}
//wait for update status
// wait for update status
time.Sleep(1 * time.Second)

updatedMCO = &mcov1beta2.MultiClusterObservability{}
Expand Down Expand Up @@ -626,7 +628,7 @@ func TestMultiClusterMonitoringCRUpdate(t *testing.T) {
if err != nil {
t.Fatalf("reconcile: (%v)", err)
}
//wait for update status
// wait for update status
time.Sleep(1 * time.Second)

updatedMCO = &mcov1beta2.MultiClusterObservability{}
Expand All @@ -640,7 +642,7 @@ func TestMultiClusterMonitoringCRUpdate(t *testing.T) {
t.Errorf("Failed to get correct MCO status, expect Ready")
}

//Test finalizer
// Test finalizer
mco.ObjectMeta.DeletionTimestamp = &metav1.Time{Time: time.Now()}
mco.ObjectMeta.Finalizers = []string{resFinalizer, "test-finalizerr"}
mco.ObjectMeta.ResourceVersion = updatedMCO.ObjectMeta.ResourceVersion
Expand Down Expand Up @@ -738,7 +740,7 @@ func TestImageReplaceForMCO(t *testing.T) {
t.Fatalf("reconcile: (%v)", err)
}

//wait for update status
// wait for update status
time.Sleep(1 * time.Second)

expectedDeploymentNames := []string{
Expand Down Expand Up @@ -811,7 +813,7 @@ func TestImageReplaceForMCO(t *testing.T) {

// stop update status routine
stopStatusUpdate <- struct{}{}
//wait for update status
// wait for update status
time.Sleep(1 * time.Second)
}

Expand Down Expand Up @@ -1013,7 +1015,7 @@ func TestPrometheusRulesRemovedFromOpenshiftMonitoringNamespace(t *testing.T) {
Name: "acm-observability-alert-rules",
Namespace: "openshift-monitoring",
},
//Sample rules
// Sample rules
Spec: monitoringv1.PrometheusRuleSpec{
Groups: []monitoringv1.RuleGroup{
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ package multiclusterobservability
import (
"bytes"
"context"

// The import of crypto/md5 below is not for cryptographic use. It is used to hash the contents of files to track
// changes and thus it's not a security issue.
// nolint:gosec
"crypto/md5" // #nosec G401 G501
"encoding/hex"
"errors"
"fmt"
"os"
Expand Down Expand Up @@ -49,6 +55,58 @@ const (
endpointsRestartLabel = "endpoints/time-restarted"
)

// Fetch contents of the secrets: observability-server-certs, observability-client-ca-certs,
// observability-observatorium-api.
// Fetch contents of the configmap: observability-observatorium-api.
// Concatenate all of the above and hash their contents.
// If any of the secrets or configmaps aren't found, an empty struct of the respective type is used for the hash.
func hashObservatoriumCRConfig(cl client.Client) (string, error) {
secretsToQuery := []metav1.ObjectMeta{
{Name: mcoconfig.ServerCerts, Namespace: mcoconfig.GetDefaultNamespace()},
{Name: mcoconfig.ClientCACerts, Namespace: mcoconfig.GetDefaultNamespace()},
{Name: mcoconfig.GetOperandNamePrefix() + mcoconfig.ObservatoriumAPI, Namespace: mcoconfig.GetDefaultNamespace()},
}
configMapToQuery := metav1.ObjectMeta{
Name: mcoconfig.GetOperandNamePrefix() + mcoconfig.ObservatoriumAPI, Namespace: mcoconfig.GetDefaultNamespace(),
}

// The usage of crypto/md5 below is not for cryptographic use. It is used to hash the contents of files to track
// changes and thus it's not a security issue.
// nolint:gosec
hasher := md5.New() // #nosec G401 G501
for _, secret := range secretsToQuery {
resultSecret := &v1.Secret{}
err := cl.Get(context.TODO(), types.NamespacedName{
Name: secret.Name,
Namespace: secret.Namespace,
}, resultSecret)
if err != nil && !k8serrors.IsNotFound(err) {
return "", err
}
secretData, err := yaml.Marshal(resultSecret.Data)
if err != nil {
return "", err
}
hasher.Write(secretData)
}

resultConfigMap := &v1.ConfigMap{}
err := cl.Get(context.TODO(), types.NamespacedName{
Name: configMapToQuery.Name,
Namespace: configMapToQuery.Namespace,
}, resultConfigMap)
if err != nil && !k8serrors.IsNotFound(err) {
return "", err
}
configMapData, err := yaml.Marshal(resultConfigMap.Data)
if err != nil {
return "", err
}
hasher.Write(configMapData)

return hex.EncodeToString(hasher.Sum(nil)), nil
}

// GenerateObservatoriumCR returns Observatorium cr defined in MultiClusterObservability
func GenerateObservatoriumCR(
cl client.Client, scheme *runtime.Scheme,
Expand All @@ -75,6 +133,13 @@ func GenerateObservatoriumCR(
if err != nil {
return &ctrl.Result{}, err
}

hash, err := hashObservatoriumCRConfig(cl)
if err != nil {
return &ctrl.Result{}, err
}
labels["config-hash"] = hash

observatoriumCR := &obsv1alpha1.Observatorium{
ObjectMeta: metav1.ObjectMeta{
Name: mcoconfig.GetOperandName(mcoconfig.Observatorium),
Expand Down Expand Up @@ -439,7 +504,7 @@ func newAPISpec(c client.Client, mco *mcov1beta2.MultiClusterObservability) (obs
if !mcoconfig.WithoutResourcesRequests(mco.GetAnnotations()) {
apiSpec.Resources = mcoconfig.GetResources(mcoconfig.ObservatoriumAPI, mco.Spec.AdvancedConfig)
}
//set the default observatorium components' image
// set the default observatorium components' image
apiSpec.Image = mcoconfig.DefaultImgRepository + "/" + mcoconfig.ObservatoriumAPIImgName +
":" + mcoconfig.DefaultImgTagSuffix
replace, image := mcoconfig.ReplaceImage(mco.Annotations, apiSpec.Image, mcoconfig.ObservatoriumAPIImgName)
Expand Down Expand Up @@ -601,8 +666,8 @@ func newRuleSpec(mco *mcov1beta2.MultiClusterObservability, scSelected string) o
mco.Spec.StorageConfig.RuleStorageSize,
scSelected)

//configure alertmanager in ruler
//ruleSpec.AlertmanagerURLs = []string{mcoconfig.AlertmanagerURL}
// configure alertmanager in ruler
// ruleSpec.AlertmanagerURLs = []string{mcoconfig.AlertmanagerURL}
ruleSpec.AlertmanagerConfigFile = obsv1alpha1.AlertmanagerConfigFile{
Name: mcoconfig.AlertmanagersDefaultConfigMapName,
Key: mcoconfig.AlertmanagersDefaultConfigFileKey,
Expand Down Expand Up @@ -826,8 +891,8 @@ func newReceiverControllerSpec(mco *mcov1beta2.MultiClusterObservability) obsv1a

func newCompactSpec(mco *mcov1beta2.MultiClusterObservability, scSelected string) obsv1alpha1.CompactSpec {
compactSpec := obsv1alpha1.CompactSpec{}
//Compactor, generally, does not need to be highly available.
//Compactions are needed from time to time, only when new blocks appear.
// Compactor, generally, does not need to be highly available.
// Compactions are needed from time to time, only when new blocks appear.
compactSpec.Replicas = &mcoconfig.Replicas1
if !mcoconfig.WithoutResourcesRequests(mco.GetAnnotations()) {
compactSpec.Resources = mcoconfig.GetResources(mcoconfig.ThanosCompact, mco.Spec.AdvancedConfig)
Expand Down
Loading

0 comments on commit 2622434

Please sign in to comment.