Skip to content

Commit 1cfbe9f

Browse files
Merge pull request #1747 from JoaoBraveCoding/2114721
Bug 2114721: Adds telemeter token hash to Deployment annotation
2 parents a8a9a85 + 768b402 commit 1cfbe9f

File tree

6 files changed

+193
-6
lines changed

6 files changed

+193
-6
lines changed

pkg/manifests/manifests.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package manifests
1616

1717
import (
1818
"crypto/md5"
19+
"crypto/sha256"
1920
"crypto/tls"
2021
"encoding/base64"
2122
"encoding/json"
@@ -3513,12 +3514,18 @@ func (f *Factory) TelemeterClientPrometheusRule() (*monv1.PrometheusRule, error)
35133514
// TelemeterClientDeployment generates a new Deployment for Telemeter client.
35143515
// If the passed ConfigMap is not empty it mounts the Trusted CA Bundle as a VolumeMount to
35153516
// /etc/pki/ca-trust/extracted/pem/ location.
3516-
func (f *Factory) TelemeterClientDeployment(proxyCABundleCM *v1.ConfigMap) (*appsv1.Deployment, error) {
3517+
func (f *Factory) TelemeterClientDeployment(proxyCABundleCM *v1.ConfigMap, s *v1.Secret) (*appsv1.Deployment, error) {
35173518
d, err := f.NewDeployment(f.assets.MustNewAssetReader(TelemeterClientDeployment))
35183519
if err != nil {
35193520
return nil, err
35203521
}
35213522

3523+
// Set annotation on deployment to trigger redeployments
3524+
if s != nil {
3525+
hash := sha256.New()
3526+
d.Spec.Template.Annotations["telemeter-token-hash"] = string(hash.Sum(s.Data["token"]))
3527+
}
3528+
35223529
for i, container := range d.Spec.Template.Spec.Containers {
35233530
switch container.Name {
35243531
case "telemeter-client":
@@ -3602,7 +3609,6 @@ func (f *Factory) TelemeterClientServiceAccount() (*v1.ServiceAccount, error) {
36023609
return s, nil
36033610
}
36043611

3605-
// TelemeterClientSecret generates a new Secret for Telemeter client.
36063612
func (f *Factory) TelemeterClientSecret() (*v1.Secret, error) {
36073613
s, err := f.NewSecret(f.assets.MustNewAssetReader(TelemeterClientSecret))
36083614
if err != nil {

pkg/manifests/manifests_test.go

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package manifests
1616

1717
import (
1818
"context"
19+
"crypto/sha256"
1920
"errors"
2021
"fmt"
2122
"net/url"
@@ -628,7 +629,7 @@ func TestUnconfiguredManifests(t *testing.T) {
628629
t.Fatal(err)
629630
}
630631

631-
_, err = f.TelemeterClientDeployment(nil)
632+
_, err = f.TelemeterClientDeployment(nil, nil)
632633
if err != nil {
633634
t.Fatal(err)
634635
}
@@ -3125,7 +3126,7 @@ func TestTelemeterConfiguration(t *testing.T) {
31253126
t.Fatal(err)
31263127
}
31273128
f := NewFactory("openshift-monitoring", "openshift-user-workload-monitoring", c, defaultInfrastructureReader(), &fakeProxyReader{}, NewAssets(assetsPath), &APIServerConfig{}, &configv1.Console{})
3128-
d, err := f.TelemeterClientDeployment(&v1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "foo"}})
3129+
d, err := f.TelemeterClientDeployment(&v1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}, &v1.Secret{Data: map[string][]byte{"token": []byte("test")}})
31293130
if err != nil {
31303131
t.Fatal(err)
31313132
}
@@ -3148,6 +3149,15 @@ func TestTelemeterConfiguration(t *testing.T) {
31483149
}
31493150
}
31503151

3152+
hash := sha256.New()
3153+
expectedTokenHash := string(hash.Sum([]byte("test")))
3154+
3155+
if tokenHash, ok := d.Spec.Template.Annotations["telemeter-token-hash"]; !ok {
3156+
t.Fatalf("telemeter-token-hash annotation not set in telemeter-client deployment")
3157+
} else if expectedTokenHash != tokenHash {
3158+
t.Fatalf("incorrect token hash on telemeter-token-hash annotation, \n got %s, \nwant %s", tokenHash, expectedTokenHash)
3159+
}
3160+
31513161
expectedKubeRbacProxyTLSCipherSuitesArg := fmt.Sprintf("%s%s",
31523162
KubeRbacProxyTLSCipherSuitesFlag,
31533163
strings.Join(crypto.OpenSSLToIANACipherSuites(APIServerDefaultTLSCiphers), ","))
@@ -3163,6 +3173,87 @@ func TestTelemeterConfiguration(t *testing.T) {
31633173
}
31643174
}
31653175

3176+
func TestTelemeterClientSecret(t *testing.T) {
3177+
for _, tc := range []struct {
3178+
name string
3179+
config string
3180+
existingData map[string][]byte
3181+
expectedData map[string][]byte
3182+
updateToSaltExpected bool
3183+
}{
3184+
{
3185+
name: "No existing secret",
3186+
config: `telemeterClient:
3187+
token: mySecretToken
3188+
`,
3189+
existingData: map[string][]byte{},
3190+
expectedData: map[string][]byte{
3191+
"token": []byte("mySecretToken"),
3192+
},
3193+
updateToSaltExpected: true,
3194+
},
3195+
{
3196+
name: "Existing secret, salt gets deleted",
3197+
config: `telemeterClient:
3198+
token: mySecretToken
3199+
`,
3200+
existingData: map[string][]byte{
3201+
"token": []byte("mySecretToken"),
3202+
},
3203+
expectedData: map[string][]byte{
3204+
"token": []byte("mySecretToken"),
3205+
},
3206+
updateToSaltExpected: true,
3207+
},
3208+
{
3209+
name: "Existing secret, secret changes",
3210+
config: `telemeterClient:
3211+
token: myNewSecretToken
3212+
`,
3213+
existingData: map[string][]byte{
3214+
"token": []byte("mySecretToken"),
3215+
"salt": []byte("1234456789ABCDEF"),
3216+
},
3217+
expectedData: map[string][]byte{
3218+
"token": []byte("myNewSecretToken"),
3219+
},
3220+
updateToSaltExpected: true,
3221+
},
3222+
} {
3223+
t.Run(tc.name, func(t *testing.T) {
3224+
c, err := NewConfigFromString(tc.config)
3225+
if err != nil {
3226+
t.Fatal(err)
3227+
}
3228+
c.UserWorkloadConfiguration = NewDefaultUserWorkloadMonitoringConfig()
3229+
f := NewFactory("openshift-monitoring", "openshift-user-workload-monitoring", c, defaultInfrastructureReader(), &fakeProxyReader{}, NewAssets(assetsPath), &APIServerConfig{}, &configv1.Console{})
3230+
generatedS, err := f.TelemeterClientSecret()
3231+
if err != nil {
3232+
t.Fatal(err)
3233+
}
3234+
byteT, exists := generatedS.Data["token"]
3235+
newToken := string(byteT)
3236+
if !exists {
3237+
t.Fatalf("generated TelemeterClientSecret does not contain a token")
3238+
}
3239+
byteS, exists := generatedS.Data["salt"]
3240+
newSalt := string(byteS)
3241+
if !exists {
3242+
t.Fatalf("generated TelemeterClientSecret does not contain a salt")
3243+
}
3244+
if string(tc.expectedData["token"]) != newToken {
3245+
t.Fatalf("generated token is different from expected, expected %s, got %s", tc.expectedData["token"], newToken)
3246+
}
3247+
if tc.updateToSaltExpected && string(tc.existingData["salt"]) == newSalt {
3248+
t.Fatalf("generated salt remain the same expected it to be different, got %s", newSalt)
3249+
} else if !tc.updateToSaltExpected && string(tc.expectedData["salt"]) != newSalt {
3250+
t.Fatalf("generated salt is different from expected, expected %s, got %s", tc.expectedData["salt"], newSalt)
3251+
}
3252+
})
3253+
}
3254+
3255+
}
3256+
31663257
func TestThanosRulerConfiguration(t *testing.T) {
31673258
c, err := NewConfigFromString(``)
31683259
uwc, err := NewUserConfigFromString(`thanosRuler:

pkg/tasks/telemeter.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"github.com/openshift/cluster-monitoring-operator/pkg/client"
2222
"github.com/openshift/cluster-monitoring-operator/pkg/manifests"
2323
"github.com/openshift/cluster-monitoring-operator/pkg/promqlgen"
24+
apierrors "k8s.io/apimachinery/pkg/api/errors"
2425

2526
"github.com/pkg/errors"
2627
)
@@ -117,6 +118,14 @@ func (t *TelemeterClientTask) create(ctx context.Context) error {
117118
return errors.Wrap(err, "initializing Telemeter client Secret failed")
118119
}
119120

121+
oldS, err := t.client.GetSecret(ctx, s.Namespace, s.Name)
122+
if err != nil && !apierrors.IsNotFound(err) {
123+
return errors.Wrap(err, "getting Telemeter Client Secret failed")
124+
}
125+
if oldS != nil && string(oldS.Data["token"]) == t.config.ClusterMonitoringConfiguration.TelemeterClientConfig.Token {
126+
s.Data = oldS.Data
127+
}
128+
120129
err = t.client.CreateOrUpdateSecret(ctx, s)
121130
if err != nil {
122131
return errors.Wrap(err, "reconciling Telemeter client Secret failed")
@@ -149,7 +158,7 @@ func (t *TelemeterClientTask) create(ctx context.Context) error {
149158
return errors.Wrap(err, "syncing Telemeter client CA bundle ConfigMap failed")
150159
}
151160

152-
dep, err := t.factory.TelemeterClientDeployment(trustedCA)
161+
dep, err := t.factory.TelemeterClientDeployment(trustedCA, s)
153162
if err != nil {
154163
return errors.Wrap(err, "initializing Telemeter client Deployment failed")
155164
}
@@ -180,7 +189,7 @@ func (t *TelemeterClientTask) create(ctx context.Context) error {
180189
}
181190

182191
func (t *TelemeterClientTask) destroy(ctx context.Context) error {
183-
dep, err := t.factory.TelemeterClientDeployment(nil)
192+
dep, err := t.factory.TelemeterClientDeployment(nil, nil)
184193
if err != nil {
185194
return errors.Wrap(err, "initializing Telemeter client Deployment failed")
186195
}

test/e2e/config_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,50 @@ func TestClusterMonitorTelemeterClientConfig(t *testing.T) {
453453
}
454454
}
455455

456+
func TestTelemeterClientSecret(t *testing.T) {
457+
for _, tc := range []struct {
458+
name string
459+
oldC string
460+
newC string
461+
tokenChanged bool
462+
}{
463+
{
464+
name: "Existing Secret",
465+
oldC: `telemeterClient:
466+
token: mySecretToken
467+
`,
468+
newC: `telemeterClient:
469+
token: mySecretToken
470+
`,
471+
tokenChanged: false,
472+
},
473+
{
474+
name: "Existing Secret, new token",
475+
oldC: `telemeterClient:
476+
token: mySecretToken
477+
`,
478+
newC: `telemeterClient:
479+
token: myNewSecretToken
480+
`,
481+
tokenChanged: true,
482+
},
483+
} {
484+
485+
t.Run(tc.name, func(t *testing.T) {
486+
f.MustCreateOrUpdateConfigMap(t, configMapWithData(t, tc.oldC))
487+
oldS := f.MustGetSecret(t, "telemeter-client", f.Ns)
488+
f.MustCreateOrUpdateConfigMap(t, configMapWithData(t, tc.newC))
489+
if tc.tokenChanged {
490+
f.AssertValueInSecretNotEquals(oldS.GetName(), oldS.GetNamespace(), "token", string(oldS.Data["token"]))
491+
f.AssertValueInSecretNotEquals(oldS.GetName(), oldS.GetNamespace(), "salt", string(oldS.Data["salt"]))
492+
return
493+
}
494+
f.AssertValueInSecretEquals(oldS.GetName(), oldS.GetNamespace(), "token", string(oldS.Data["token"]))
495+
f.AssertValueInSecretEquals(oldS.GetName(), oldS.GetNamespace(), "salt", string(oldS.Data["salt"]))
496+
})
497+
}
498+
}
499+
456500
func TestClusterMonitorK8sPromAdapterConfig(t *testing.T) {
457501
const (
458502
deploymentName = "prometheus-adapter"

test/e2e/framework/assertions.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,24 @@ func (f *Framework) AssertValueInConfigMapNotEquals(name, namespace, key, compar
434434
}
435435
}
436436

437+
func (f *Framework) AssertValueInSecretEquals(name, namespace, key, compareWith string) func(t *testing.T) {
438+
return func(t *testing.T) {
439+
s := f.MustGetSecret(t, name, namespace)
440+
if string(s.Data[key]) != compareWith {
441+
t.Fatalf("wanted value %s for key %s but got %s", compareWith, key, string(s.Data[key]))
442+
}
443+
}
444+
}
445+
446+
func (f *Framework) AssertValueInSecretNotEquals(name, namespace, key, compareWith string) func(t *testing.T) {
447+
return func(t *testing.T) {
448+
s := f.MustGetSecret(t, name, namespace)
449+
if string(s.Data[key]) == compareWith {
450+
t.Fatalf("did not want value %s for key %s", compareWith, key)
451+
}
452+
}
453+
}
454+
437455
type getResourceFunc func() (metav1.Object, error)
438456

439457
func assertResourceExists(t *testing.T, getResource getResourceFunc) {

test/e2e/framework/helpers.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,25 @@ func (f *Framework) MustGetConfigMap(t *testing.T, name, namespace string) *v1.C
6060
return clusterCm
6161
}
6262

63+
// MustGetSecret `name` from `namespace` within 5 minutes or fail
64+
func (f *Framework) MustGetSecret(t *testing.T, name, namespace string) *v1.Secret {
65+
t.Helper()
66+
var secret *v1.Secret
67+
err := wait.Poll(time.Second, 5*time.Minute, func() (bool, error) {
68+
s, err := f.KubeClient.CoreV1().Secrets(namespace).Get(ctx, name, metav1.GetOptions{})
69+
if err != nil {
70+
return false, nil
71+
}
72+
73+
secret = s
74+
return true, nil
75+
})
76+
if err != nil {
77+
t.Fatalf("failed to get secret %s in namespace %s - %s", name, namespace, err.Error())
78+
}
79+
return secret
80+
}
81+
6382
// MustGetStatefulSet `name` from `namespace` within 5 minutes or fail
6483
func (f *Framework) MustGetStatefulSet(t *testing.T, name, namespace string) *appsv1.StatefulSet {
6584
t.Helper()

0 commit comments

Comments
 (0)