Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
144 changes: 144 additions & 0 deletions internal/controller/reconciler/generate_secrets_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
Copyright 2025.

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 reconciler

import (
"context"
"testing"
"unicode/utf8"

"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
ctrlClient "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"

apiv2 "github.com/wandb/operator/api/v2"
serverManifest "github.com/wandb/operator/pkg/wandb/manifest"
)
Comment on lines +19 to +37

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.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate target file and suite files =="
git ls-files | rg '(^|/)internal/controller/reconciler/generate_secrets_test\.go$|(^|/)suite_test\.go$|(^|/)go\.mod$'

echo
echo "== target file imports and test declarations =="
sed -n '1,220p' internal/controller/reconciler/generate_secrets_test.go

echo
echo "== test framework indicators =="
rg -n 'Describe|Context|It|When|Specify|testing\.|func Test|\bdRequire\(|\bgomega\b|Ginkgo|TestingT|ginkgo|suite_test\.go|envtest|testing\.B|func TestMain' -S .

echo
echo "== lint/test make targets and go.mod deps =="
if [ -f Makefile ]; then sed -n '1,220p' Makefile; fi
echo
[ -f go.mod ] && awk '/module |require \(/,/^)/' go.mod | sed -n '1,220p'

Repository: wandb/operator

Length of output: 50371


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== target file imports/declarations =="
sed -n '1,180p' internal/controller/reconciler/generate_secrets_test.go

echo
echo "== focused test framework indicators excluding vendored/crds =="
rg -n --glob '!pkg/vendored/**' --glob '!**/crds/**' 'func Test[A-Za-z0-9_]+|require\..+|\.Expect\(|Describe\(|Context\(|It\(|GinkgoT|Gomega|envtest|suite_test\.go' --glob '*_test.go' . | sed -n '1,240p'

echo
echo "== reconciler suite files =="
git ls-files 'internal/controller/**suite_test.go' 'internal/controller/**/*_test.go'

echo
echo "== Makefile relevant targets =="
[ -f Makefile ] && (sed -n '/^lint:/,/^test:/p' Makefile; sed -n '/^test:/,/^[a-zA-Z_][A-Za-z0-9_-]*:/p' Makefile)

echo
echo "== go.mod relevant deps =="
[ -f go.mod ] && awk '/^module |^require \(/,/^)/' go.mod | rg 'ginkgo|gomega|envtest|testify' || true

Repository: wandb/operator

Length of output: 42962


Use the required test framework and run validation before merge.

internal/controller/reconciler/generate_secrets_test.go uses testing, Testify, and fake.NewClientBuilder. The repository policy requires Ginkgo/Gomega specs attached to suite_test.go files with envtest.

Also run make lint and make test and include their results before merge.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/controller/reconciler/generate_secrets_test.go` around lines 19 -
34, Rewrite the tests in generate_secrets_test.go from testing/Testify and
fake.NewClientBuilder to the repository’s Ginkgo/Gomega spec style, attaching
them to the appropriate suite_test.go and using envtest. Remove the direct
testing, Testify, and fake-client dependencies where no longer needed, then run
make lint and make test and report their results before merging.

Source: Coding guidelines


func newGenerateSecretsFixture(
t *testing.T,
seed ...ctrlClient.Object,
) (ctrlClient.Client, *apiv2.WeightsAndBiases) {
t.Helper()
scheme := runtime.NewScheme()
require.NoError(t, corev1.AddToScheme(scheme))
require.NoError(t, apiv2.AddToScheme(scheme))

wandb := &apiv2.WeightsAndBiases{
TypeMeta: metav1.TypeMeta{APIVersion: "apps.wandb.com/v2", Kind: "WeightsAndBiases"},
ObjectMeta: metav1.ObjectMeta{Name: "wandb", Namespace: "default"},
}
objects := append([]ctrlClient.Object{wandb}, seed...)
client := fake.NewClientBuilder().
WithScheme(scheme).
WithStatusSubresource(&apiv2.WeightsAndBiases{}).
WithObjects(objects...).
Build()
return client, wandb
}

// effectiveSecretValue returns the value under key. The fake client does not
// fold StringData into Data, so prefer StringData then fall back to Data.
func effectiveSecretValue(sec *corev1.Secret, key string) string {
if v, ok := sec.StringData[key]; ok {
return v
}
return string(sec.Data[key])
}

func weaveWorkerAuthManifest() serverManifest.Manifest {
return serverManifest.Manifest{
GeneratedSecrets: []serverManifest.GeneratedSecret{
{Name: "weave-worker-auth", Length: 32, CharacterType: "password", UseExactName: true},
},
}
}

// TestGenerateSecrets_RegeneratesNonUTF8AdoptedSecret: an adopted non-UTF-8
// token must be overwritten with a UTF-8-safe one.
func TestGenerateSecrets_RegeneratesNonUTF8AdoptedSecret(t *testing.T) {
invalid := []byte{0xff, 0xfe, 0xfd, 0x00, 0x80}
require.False(t, utf8.Valid(invalid), "test precondition: bytes must be invalid UTF-8")

seeded := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{Name: "weave-worker-auth", Namespace: "default"},
Type: corev1.SecretTypeOpaque,
Data: map[string][]byte{"key": invalid},
}
client, wandb := newGenerateSecretsFixture(t, seeded)

_, err := generateSecrets(context.Background(), client, wandb, weaveWorkerAuthManifest())
require.NoError(t, err)

var sec corev1.Secret
require.NoError(t, client.Get(context.Background(),
types.NamespacedName{Name: "weave-worker-auth", Namespace: "default"}, &sec))

value := effectiveSecretValue(&sec, "key")
require.NotEmpty(t, value)
require.True(t, utf8.ValidString(value), "regenerated token must be valid UTF-8")
require.NotEqual(t, string(invalid), value, "the non-UTF-8 token must be replaced")

sel, ok := wandb.Status.GeneratedSecrets["weave-worker-auth"]
require.True(t, ok)
require.Equal(t, "weave-worker-auth", sel.Name)
require.Equal(t, "key", sel.Key)
}

// TestGenerateSecrets_LeavesValidExistingValueUntouched: a valid adopted token
// is preserved (no needless rotation).
func TestGenerateSecrets_LeavesValidExistingValueUntouched(t *testing.T) {
valid := []byte("already-valid-token-123")
seeded := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{Name: "weave-worker-auth", Namespace: "default"},
Type: corev1.SecretTypeOpaque,
Data: map[string][]byte{"key": valid},
}
client, wandb := newGenerateSecretsFixture(t, seeded)

_, err := generateSecrets(context.Background(), client, wandb, weaveWorkerAuthManifest())
require.NoError(t, err)

var sec corev1.Secret
require.NoError(t, client.Get(context.Background(),
types.NamespacedName{Name: "weave-worker-auth", Namespace: "default"}, &sec))

require.Equal(t, valid, sec.Data["key"], "valid existing value must not be overwritten")
require.NotContains(t, sec.StringData, "key", "no regeneration should have occurred")
}

// TestGenerateSecrets_CreatesMissingSecretWithUTF8Token: fresh secrets hold a
// UTF-8-safe token.
func TestGenerateSecrets_CreatesMissingSecretWithUTF8Token(t *testing.T) {
client, wandb := newGenerateSecretsFixture(t)

_, err := generateSecrets(context.Background(), client, wandb, weaveWorkerAuthManifest())
require.NoError(t, err)

var sec corev1.Secret
require.NoError(t, client.Get(context.Background(),
types.NamespacedName{Name: "weave-worker-auth", Namespace: "default"}, &sec))

value := effectiveSecretValue(&sec, "key")
require.NotEmpty(t, value)
require.True(t, utf8.ValidString(value))
require.Len(t, value, 32)
}
12 changes: 9 additions & 3 deletions internal/controller/reconciler/reconcile_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"net/url"
"strings"
"time"
"unicode/utf8"

"github.com/samber/lo"
apiv2 "github.com/wandb/operator/api/v2"
Expand Down Expand Up @@ -1460,12 +1461,17 @@ func generateSecrets(ctx context.Context, client ctrlClient.Client, wandb *apiv2
return ctrl.Result{}, err
}
} else {
// Secret exists. Ensure it has the expected key; do not overwrite existing value.
if sec.Data == nil || (sec.Data != nil && sec.Data[keyName] == nil && sec.StringData == nil) {
// Secret exists. Ensure it has a usable key; do not overwrite a
// valid existing value.
existing, hasKey := sec.Data[keyName]
needsValue := !hasKey && sec.StringData == nil
// An adopted v1 secret can hold a non-UTF-8 token that breaks
// container creation as a string env var; regenerate it.
invalidUTF8 := hasKey && !utf8.Valid(existing)
if needsValue || invalidUTF8 {
if sec.StringData == nil {
sec.StringData = map[string]string{}
}
// Generate a value only if missing
valueLen := gs.Length
if valueLen <= 0 {
valueLen = 32
Expand Down
Loading