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
11 changes: 5 additions & 6 deletions pkg/console/operator/sync_v400.go
Original file line number Diff line number Diff line change
Expand Up @@ -485,14 +485,13 @@ func (co *consoleOperator) GetTelemetryConfiguration(ctx context.Context, operat
if err != nil {
return telemetryConfig, err
}
if !telemeterClientIsAvailable {
telemetryConfig["TELEMETER_CLIENT_DISABLED"] = "true"
return telemetryConfig, nil
}
telemetryConfig["TELEMETER_CLIENT_DISABLED"] = fmt.Sprintf("%t", !telemeterClientIsAvailable)

accessToken, err := telemetry.GetAccessToken(co.configNSSecretLister)
var accessToken string
accessToken, err = telemetry.GetAccessToken(co.configNSSecretLister)
if err != nil {
return nil, err
klog.V(4).Infof("telemetry config: failed to get access token, proceeding without organization metadata: %v", err)
accessToken = ""
}
organizationID, accountMail, refreshCache := telemetry.GetOrganizationMeta(telemetryConfig, co.trackables.organizationID, co.trackables.accountMail, clusterID, accessToken)
// cache fetched ORGANIZATION_ID and ACCOUNT_MAIL
Expand Down
253 changes: 253 additions & 0 deletions pkg/console/operator/sync_v400_test.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
package operator

import (
"context"
"encoding/json"
"sort"
"testing"

"github.com/go-test/deep"

configv1 "github.com/openshift/api/config/v1"
operatorv1 "github.com/openshift/api/operator/v1"
configlistersv1 "github.com/openshift/client-go/config/listers/config/v1"
appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
appsv1listers "k8s.io/client-go/listers/apps/v1"
corev1listers "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/tools/cache"

"github.com/openshift/console-operator/pkg/api"
"github.com/openshift/console-operator/pkg/console/telemetry"
)

func TestGetNodeComputeEnvironments(t *testing.T) {
Expand Down Expand Up @@ -286,3 +296,246 @@ func TestDeploymentProgressingByGeneration(t *testing.T) {
})
}
}

func newIndexer(keyFunc cache.KeyFunc) cache.Indexer {
return cache.NewIndexer(keyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
}

func newTestConsoleOperator(t *testing.T, telemeterAvailable bool, pullSecretHasCloudAuth bool) *consoleOperator {
t.Helper()

// telemetry-config ConfigMap in operator namespace
telemetryConfigCM := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: telemetry.TelemetryConfigMapName,
Namespace: api.OpenShiftConsoleOperatorNamespace,
},
Data: map[string]string{
"SEGMENT_API_HOST": "https://segment.example.com",
"SEGMENT_JS_HOST": "https://segment-js.example.com",
"SEGMENT_PUBLIC_API_KEY": "test-key",
},
}
operatorNSCMIndexer := newIndexer(cache.MetaNamespaceKeyFunc)
if err := operatorNSCMIndexer.Add(telemetryConfigCM); err != nil {
t.Fatalf("failed to add telemetry configmap to indexer: %v", err)
}

// ClusterVersion
cv := &configv1.ClusterVersion{
ObjectMeta: metav1.ObjectMeta{
Name: api.VersionResourceName,
},
Spec: configv1.ClusterVersionSpec{
ClusterID: "test-cluster-id",
},
}
cvIndexer := newIndexer(func(obj interface{}) (string, error) {
meta := obj.(metav1.ObjectMetaAccessor).GetObjectMeta()
return meta.GetName(), nil
})
if err := cvIndexer.Add(cv); err != nil {
t.Fatalf("failed to add clusterversion to indexer: %v", err)
}

// telemeter-client deployment
var replicas int32
if telemeterAvailable {
replicas = 1
}
telemeterDeploy := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: telemetry.TelemeterClientDeploymentName,
Namespace: telemetry.TelemeterClientDeploymentNamespace,
},
Status: appsv1.DeploymentStatus{
AvailableReplicas: replicas,
},
}
deployIndexer := newIndexer(cache.MetaNamespaceKeyFunc)
if err := deployIndexer.Add(telemeterDeploy); err != nil {
t.Fatalf("failed to add telemeter deployment to indexer: %v", err)
}

// pull-secret
pullSecret := &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: telemetry.PullSecretName,
Namespace: api.OpenShiftConfigNamespace,
},
Data: map[string][]byte{},
}
if pullSecretHasCloudAuth {
dockerConfig := telemetry.DockerConfig{
Auths: map[string]telemetry.DockerAuthEntry{
"cloud.openshift.com": {Auth: "test-token"},
},
}
configBytes, err := json.Marshal(dockerConfig)
if err != nil {
t.Fatalf("failed to marshal docker config: %v", err)
}
pullSecret.Data[".dockerconfigjson"] = configBytes
} else {
dockerConfig := telemetry.DockerConfig{
Auths: map[string]telemetry.DockerAuthEntry{
"quay.io": {Auth: "quay-token"},
},
}
configBytes, err := json.Marshal(dockerConfig)
if err != nil {
t.Fatalf("failed to marshal docker config: %v", err)
}
pullSecret.Data[".dockerconfigjson"] = configBytes
}
secretIndexer := newIndexer(cache.MetaNamespaceKeyFunc)
if err := secretIndexer.Add(pullSecret); err != nil {
t.Fatalf("failed to add pull-secret to indexer: %v", err)
}

return &consoleOperator{
operatorNSConfigMapLister: corev1listers.NewConfigMapLister(operatorNSCMIndexer),
clusterVersionLister: configlistersv1.NewClusterVersionLister(cvIndexer),
monitoringDeploymentLister: appsv1listers.NewDeploymentLister(deployIndexer),
configNSSecretLister: corev1listers.NewSecretLister(secretIndexer),
}
}

func TestGetTelemetryConfiguration_StableKeySet(t *testing.T) {
tests := []struct {
name string
telemeterAvailable bool
hasCloudAuth bool
expectDisabledVal string
}{
{
name: "telemeter available with cloud auth",
telemeterAvailable: true,
hasCloudAuth: true,
expectDisabledVal: "false",
},
{
name: "telemeter unavailable with cloud auth",
telemeterAvailable: false,
hasCloudAuth: true,
expectDisabledVal: "true",
},
{
name: "telemeter unavailable without cloud auth (disconnected)",
telemeterAvailable: false,
hasCloudAuth: false,
expectDisabledVal: "true",
},
{
name: "telemeter available without cloud auth (disconnected)",
telemeterAvailable: true,
hasCloudAuth: false,
expectDisabledVal: "false",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
co := newTestConsoleOperator(t, tt.telemeterAvailable, tt.hasCloudAuth)
operatorConfig := &operatorv1.Console{
ObjectMeta: metav1.ObjectMeta{Name: "cluster"},
}

config, err := co.GetTelemetryConfiguration(context.Background(), operatorConfig)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

if _, ok := config["CLUSTER_ID"]; !ok {
t.Error("expected CLUSTER_ID key to be present")
}
if _, ok := config["ORGANIZATION_ID"]; !ok {
t.Error("expected ORGANIZATION_ID key to always be present")
}
if _, ok := config["ACCOUNT_MAIL"]; !ok {
t.Error("expected ACCOUNT_MAIL key to always be present")
}

disabledVal, ok := config["TELEMETER_CLIENT_DISABLED"]
if !ok {
t.Error("expected TELEMETER_CLIENT_DISABLED key to always be present")
}
if disabledVal != tt.expectDisabledVal {
t.Errorf("expected TELEMETER_CLIENT_DISABLED=%q, got %q", tt.expectDisabledVal, disabledVal)
}
})
}
}

func TestGetTelemetryConfiguration_KeySetStableAcrossAvailabilityChange(t *testing.T) {
operatorConfig := &operatorv1.Console{
ObjectMeta: metav1.ObjectMeta{Name: "cluster"},
}

coUnavailable := newTestConsoleOperator(t, false, true)
configUnavailable, err := coUnavailable.GetTelemetryConfiguration(context.Background(), operatorConfig)
if err != nil {
t.Fatalf("unexpected error (unavailable): %v", err)
}

coAvailable := newTestConsoleOperator(t, true, true)
configAvailable, err := coAvailable.GetTelemetryConfiguration(context.Background(), operatorConfig)
if err != nil {
t.Fatalf("unexpected error (available): %v", err)
}

keysUnavailable := sortedKeys(configUnavailable)
keysAvailable := sortedKeys(configAvailable)

// All keys must be present in both states — no key-set difference allowed.
sharedKeys := []string{"CLUSTER_ID", "ORGANIZATION_ID", "ACCOUNT_MAIL", "TELEMETER_CLIENT_DISABLED", "SEGMENT_API_HOST", "SEGMENT_JS_HOST", "SEGMENT_PUBLIC_API_KEY"}
for _, key := range sharedKeys {
if _, ok := configUnavailable[key]; !ok {
t.Errorf("key %q missing from unavailable config, keys present: %v", key, keysUnavailable)
}
if _, ok := configAvailable[key]; !ok {
t.Errorf("key %q missing from available config, keys present: %v", key, keysAvailable)
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

if len(keysUnavailable) != len(keysAvailable) {
t.Fatalf("key set sizes differ: unavailable=%v, available=%v", keysUnavailable, keysAvailable)
}
for i := range keysUnavailable {
if keysUnavailable[i] != keysAvailable[i] {
t.Errorf("key set mismatch at index %d: unavailable has %q, available has %q", i, keysUnavailable[i], keysAvailable[i])
}
}
}

func sortedKeys(m map[string]string) []string {
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
return keys
}

// Verify that GetAccessToken failure does not cause GetTelemetryConfiguration to error
func TestGetTelemetryConfiguration_DisconnectedClusterNoError(t *testing.T) {
co := newTestConsoleOperator(t, false, false)
operatorConfig := &operatorv1.Console{
ObjectMeta: metav1.ObjectMeta{Name: "cluster"},
}

config, err := co.GetTelemetryConfiguration(context.Background(), operatorConfig)
if err != nil {
t.Fatalf("expected no error on disconnected cluster, got: %v", err)
}

if config["TELEMETER_CLIENT_DISABLED"] != "true" {
t.Error("expected TELEMETER_CLIENT_DISABLED=true on disconnected cluster")
}
if _, ok := config["ORGANIZATION_ID"]; !ok {
t.Error("expected ORGANIZATION_ID key to be present even on disconnected cluster")
}
if _, ok := config["ACCOUNT_MAIL"]; !ok {
t.Error("expected ACCOUNT_MAIL key to be present even on disconnected cluster")
}
}