Skip to content

Commit 0efe492

Browse files
[k8s] improve kubernetes_secrets provider secret logging (#6841) (#7006)
* feat: improve kubernetes_secrets provider secret logging * feat: improve readability by replacing Info with Infof * fix: categorise properly the type of change * feat: more logging readability improvements * feat: improve readability by replacing "%s" with %q (cherry picked from commit 0ae5d35) Co-authored-by: Panos Koutsovasilis <panos.koutsovasilis@elastic.co>
1 parent 8c8ba4b commit 0efe492

File tree

3 files changed

+127
-15
lines changed

3 files changed

+127
-15
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Kind can be one of:
2+
# - breaking-change: a change to previously-documented behavior
3+
# - deprecation: functionality that is being removed in a later release
4+
# - bug-fix: fixes a problem in a previous version
5+
# - enhancement: extends functionality but does not break or fix existing behavior
6+
# - feature: new functionality
7+
# - known-issue: problems that we are aware of in a given version
8+
# - security: impacts on the security of a product or a user’s deployment.
9+
# - upgrade: important information for someone upgrading from a prior version
10+
# - other: does not fit into any of the other categories
11+
kind: enhancement
12+
13+
# Change summary; a 80ish characters long description of the change.
14+
summary: Improve kubernetes_secrets provider secret logging
15+
16+
# Long description; in case the summary is not enough to describe the change
17+
# this field accommodate a description without length limits.
18+
# NOTE: This field will be rendered only for breaking-change and known-issue kinds at the moment.
19+
#description:
20+
21+
# Affected component; usually one of "elastic-agent", "fleet-server", "filebeat", "metricbeat", "auditbeat", "all", etc.
22+
component: "elastic-agent"
23+
24+
# PR URL; optional; the PR number that added the changeset.
25+
# If not present is automatically filled by the tooling finding the PR where this changelog fragment has been added.
26+
# NOTE: the tooling supports backports, so it's able to fill the original PR number instead of the backport PR number.
27+
# Please provide it if you are adding a fragment for a different PR.
28+
pr: https://github.com/elastic/elastic-agent/pull/6841
29+
30+
# Issue URL; optional; the GitHub issue related to this changeset (either closes or is part of).
31+
# If not present is automatically filled by the tooling with the issue linked to the PR number.
32+
issue: https://github.com/elastic/elastic-agent/issues/6187

internal/pkg/composable/providers/kubernetessecrets/kubernetes_secrets.go

+23-13
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ func (p *contextProviderK8SSecrets) Fetch(key string) (string, bool) {
136136
return "", false
137137
}
138138
if len(tokens) != 4 {
139-
p.logger.Warn("Invalid secret key format: ", key, ". Secrets should be of the format kubernetes_secrets.namespace.secret_name.value")
139+
p.logger.Warnf(`Invalid secret key format: %q. Secrets should be of the format kubernetes_secrets.namespace.secret_name.value`, key)
140140
return "", false
141141
}
142142

@@ -151,7 +151,8 @@ func (p *contextProviderK8SSecrets) Fetch(key string) (string, bool) {
151151

152152
if p.config.DisableCache {
153153
// cache disabled - fetch secret from the API
154-
return p.fetchFromAPI(ctx, secretName, secretNamespace, secretKey)
154+
val, _, ok := p.fetchFromAPI(ctx, secretName, secretNamespace, secretKey)
155+
return val, ok
155156
}
156157

157158
// cache enabled
@@ -162,7 +163,7 @@ func (p *contextProviderK8SSecrets) Fetch(key string) (string, bool) {
162163
}
163164

164165
// cache miss - fetch secret from the API
165-
apiSecretValue, apiExists := p.fetchFromAPI(ctx, secretName, secretNamespace, secretKey)
166+
apiSecretValue, apiSecretResourceVersion, apiExists := p.fetchFromAPI(ctx, secretName, secretNamespace, secretKey)
166167
now := time.Now()
167168
sd = secret{
168169
name: secretName,
@@ -175,11 +176,13 @@ func (p *contextProviderK8SSecrets) Fetch(key string) (string, bool) {
175176
p.store.AddConditionally(key, sd, true, func(existing secret, exists bool) bool {
176177
if !exists {
177178
// no existing secret in the cache thus add it
179+
p.logger.Infof(`Fetch: %q inserted. Resource Version of secret: %q`, key, apiSecretResourceVersion)
178180
return true
179181
}
180182
if existing.value != apiSecretValue && !existing.apiFetchTime.After(now) {
181183
// there is an existing secret in the cache but its value has changed since the last time
182184
// it was fetched from the API thus update it
185+
p.logger.Infof(`Fetch: %q updated. Resource Version of secret: %q`, key, apiSecretResourceVersion)
183186
return true
184187
}
185188
// there is an existing secret in the cache, and it points already to the latest value
@@ -199,10 +202,13 @@ func (p *contextProviderK8SSecrets) refreshCache(ctx context.Context, comm corec
199202
case <-ctx.Done():
200203
return
201204
case <-timer.C:
205+
p.logger.Info("Cache: refresh started")
202206
hasUpdate := p.updateSecrets(ctx)
203207
if hasUpdate {
204-
p.logger.Info("Secrets cache was updated, the agent will be notified.")
208+
p.logger.Info("Cache: refresh ended with updates, agent will be notified")
205209
comm.Signal()
210+
} else {
211+
p.logger.Info("Cache: refresh ended without updates")
206212
}
207213
timer.Reset(p.config.RefreshInterval)
208214
}
@@ -220,11 +226,12 @@ func (p *contextProviderK8SSecrets) updateSecrets(ctx context.Context) bool {
220226
sd, exists := p.store.Get(key, false)
221227
if !exists {
222228
// this item has expired thus mark that the cache has updates and continue
229+
p.logger.Infof(`Cache: %q expired`, key)
223230
hasUpdates = true
224231
continue
225232
}
226233

227-
apiSecretValue, apiExists := p.fetchFromAPI(ctx, sd.name, sd.namespace, sd.key)
234+
apiSecretValue, apiResourceVersion, apiExists := p.fetchFromAPI(ctx, sd.name, sd.namespace, sd.key)
228235
now := time.Now()
229236
sd = secret{
230237
name: sd.name,
@@ -247,6 +254,7 @@ func (p *contextProviderK8SSecrets) updateSecrets(ctx context.Context) bool {
247254
// the secret value has changed and the above fetchFromAPI is more recent thus
248255
// add it and mark that the cache has updates
249256
hasUpdates = true
257+
p.logger.Infof(`Cache: %q updated. Resource Version of secret: %q`, key, apiResourceVersion)
250258
return true
251259
}
252260
// the secret value has not changed
@@ -258,30 +266,32 @@ func (p *contextProviderK8SSecrets) updateSecrets(ctx context.Context) bool {
258266
}
259267

260268
// fetchFromAPI fetches the secret value from the API
261-
func (p *contextProviderK8SSecrets) fetchFromAPI(ctx context.Context, secretName string, secretNamespace string, secretKey string) (string, bool) {
269+
func (p *contextProviderK8SSecrets) fetchFromAPI(ctx context.Context, secretName string, secretNamespace string, secretKey string) (string, string, bool) {
262270
ctx, cancel := context.WithTimeout(ctx, p.config.RequestTimeout)
263271
defer cancel()
264272

265273
p.clientMtx.RLock()
266274
if p.client == nil {
267275
// k8s client is nil most probably due to an error at p.Run
268276
p.clientMtx.RUnlock()
269-
return "", false
277+
p.logger.Warnf(`Could not retrieve secret %q at namespace %q because k8s client is nil`, secretName, secretNamespace)
278+
return "", "", false
270279
}
271280
c := p.client
272281
p.clientMtx.RUnlock()
273282

274283
si := c.CoreV1().Secrets(secretNamespace)
275284
secret, err := si.Get(ctx, secretName, metav1.GetOptions{})
276285
if err != nil {
277-
p.logger.Warn("Could not retrieve secret ", secretName, " at namespace ", secretNamespace, ": ", err.Error())
278-
return "", false
286+
p.logger.Warnf(`Could not retrieve secret %q at namespace %q: %s`, secretName, secretNamespace, err.Error())
287+
return "", "", false
279288
}
280289

281-
if _, ok := secret.Data[secretKey]; !ok {
282-
p.logger.Warn("Could not retrieve value of key ", secretKey, " for secret ", secretName, " at namespace ", secretNamespace)
283-
return "", false
290+
secretData, ok := secret.Data[secretKey]
291+
if !ok {
292+
p.logger.Warnf(`Could not retrieve value of key %q for secret %q at namespace %q because it does not exist`, secretKey, secretName, secretNamespace)
293+
return "", "", false
284294
}
285295

286-
return string(secret.Data[secretKey]), true
296+
return string(secretData), secret.GetResourceVersion(), true
287297
}

internal/pkg/composable/providers/kubernetessecrets/kubernetes_secrets_test.go

+72-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
"testing"
1313
"time"
1414

15+
"github.com/stretchr/testify/assert"
16+
1517
"github.com/stretchr/testify/mock"
1618
"github.com/stretchr/testify/require"
1719
v1 "k8s.io/api/core/v1"
@@ -936,6 +938,69 @@ func Test_Config(t *testing.T) {
936938
}
937939
}
938940

941+
func Test_FetchFromAPI(t *testing.T) {
942+
for _, tc := range []struct {
943+
name string
944+
k8sClient k8sclient.Interface
945+
secretName string
946+
secretNamespace string
947+
secretKey string
948+
expectedValue string
949+
expectedResourceVersion string
950+
expectedOK bool
951+
}{
952+
{
953+
name: "k8s client is nil",
954+
k8sClient: nil,
955+
},
956+
{
957+
name: "secret not found",
958+
k8sClient: k8sfake.NewClientset(),
959+
secretName: "secret_name",
960+
secretNamespace: "secret_namespace",
961+
secretKey: "secret_key",
962+
},
963+
{
964+
name: "key in secret not found",
965+
k8sClient: k8sfake.NewClientset(
966+
buildK8SSecret("secret_namespace", "secret_name", "secret_key", "secret_value"),
967+
),
968+
secretName: "secret_name",
969+
secretNamespace: "secret_namespace",
970+
secretKey: "secret_key_not_found",
971+
},
972+
{
973+
name: "key in secret not found",
974+
k8sClient: k8sfake.NewClientset(
975+
buildK8SSecretWithResourceVersion("secret_namespace", "secret_name", "secret_key", "secret_value", "100000"),
976+
),
977+
secretName: "secret_name",
978+
secretNamespace: "secret_namespace",
979+
secretKey: "secret_key",
980+
expectedValue: "secret_value",
981+
expectedResourceVersion: "100000",
982+
expectedOK: true,
983+
},
984+
} {
985+
t.Run(tc.name, func(t *testing.T) {
986+
ctx, cancel := context.WithCancel(context.Background())
987+
defer cancel()
988+
989+
provider := &contextProviderK8SSecrets{
990+
logger: logp.NewLogger("test_k8s_secrets"),
991+
config: defaultConfig(),
992+
client: tc.k8sClient,
993+
clientMtx: sync.RWMutex{},
994+
}
995+
996+
val, resourceVersion, ok := provider.fetchFromAPI(ctx, tc.secretName, tc.secretNamespace, tc.secretKey)
997+
assert.Equal(t, tc.expectedValue, val)
998+
assert.Equal(t, tc.expectedOK, ok)
999+
assert.Equal(t, tc.expectedResourceVersion, resourceVersion)
1000+
})
1001+
}
1002+
}
1003+
9391004
type secretTestDataBuilder struct {
9401005
namespace string
9411006
name string
@@ -977,14 +1042,19 @@ func buildCacheEntryKey(e *cacheEntry) string {
9771042
}
9781043

9791044
func buildK8SSecret(namespace string, name string, key string, value string) *v1.Secret {
1045+
return buildK8SSecretWithResourceVersion(namespace, name, key, value, "1")
1046+
}
1047+
1048+
func buildK8SSecretWithResourceVersion(namespace string, name string, key string, value string, resourceVersion string) *v1.Secret {
9801049
return &v1.Secret{
9811050
TypeMeta: metav1.TypeMeta{
9821051
Kind: "Secret",
9831052
APIVersion: "apps/v1beta1",
9841053
},
9851054
ObjectMeta: metav1.ObjectMeta{
986-
Name: name,
987-
Namespace: namespace,
1055+
Name: name,
1056+
Namespace: namespace,
1057+
ResourceVersion: resourceVersion,
9881058
},
9891059
Data: map[string][]byte{
9901060
key: []byte(value),

0 commit comments

Comments
 (0)