Skip to content

Commit 6a968c5

Browse files
authored
Merge pull request kubernetes#130904 from serathius/watchcache-corrupt
In TestListCorruptObject corrupt the object in etcd instead of changing encryption key
2 parents 94d6638 + 506e4fe commit 6a968c5

File tree

1 file changed

+55
-60
lines changed

1 file changed

+55
-60
lines changed

test/integration/controlplane/transformation/secrets_transformation_test.go

Lines changed: 55 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"testing"
3131
"time"
3232

33+
clientv3 "go.etcd.io/etcd/client/v3"
3334
rbacv1 "k8s.io/api/rbac/v1"
3435
apierrors "k8s.io/apimachinery/pkg/api/errors"
3536
"k8s.io/apimachinery/pkg/api/meta"
@@ -47,6 +48,7 @@ import (
4748
"k8s.io/client-go/tools/cache"
4849
featuregatetesting "k8s.io/component-base/featuregate/testing"
4950
"k8s.io/kubernetes/test/integration/authutil"
51+
"k8s.io/kubernetes/test/integration/framework"
5052
"k8s.io/utils/ptr"
5153
)
5254

@@ -431,19 +433,26 @@ func TestListCorruptObjects(t *testing.T) {
431433
// secrets that are created before encryption breaks
432434
secrets []string
433435
// whether encryption broke after the config change
434-
encryptionBrokenFn func(t *testing.T, got apierrors.APIStatus) bool
436+
encryptionBrokenFn func(t *testing.T, got apierrors.APIStatus)
435437
// what we expect for LIST on the corrupt objects after encryption has broken
436438
listAfter verifier
437439
}{
438440
{
439441
secrets: secrets,
440442
featureEnabled: true,
441-
encryptionBrokenFn: func(t *testing.T, got apierrors.APIStatus) bool {
442-
// the new encryption config does not have the old key, so reading of resources
443-
// created before the encryption change will fail with 'no matching prefix found'
444-
return got.Status().Reason == metav1.StatusReasonInternalError &&
445-
strings.Contains(got.Status().Message, "Internal error occurred: StorageError: corrupt object") &&
446-
strings.Contains(got.Status().Message, "data from the storage is not transformable revision=0: no matching prefix found")
443+
encryptionBrokenFn: func(t *testing.T, got apierrors.APIStatus) {
444+
status := got.Status()
445+
if status.Reason != metav1.StatusReasonInternalError {
446+
t.Errorf("Invalid reason, got: %q, want: %q", status.Reason, metav1.StatusReasonInternalError)
447+
}
448+
corruptObjectMsg := "Internal error occurred: StorageError: corrupt object"
449+
if !strings.Contains(status.Message, corruptObjectMsg) {
450+
t.Errorf("Message should include %q, but got: %q", corruptObjectMsg, status.Message)
451+
}
452+
messageAuthenticationFailedMsg := "data from the storage is not transformable revision=0: cipher: message authentication failed"
453+
if !strings.Contains(status.Message, messageAuthenticationFailedMsg) {
454+
t.Errorf("Message should include %q, but got: %q", messageAuthenticationFailedMsg, status.Message)
455+
}
447456
},
448457
listAfter: wantAPIStatusError{
449458
reason: metav1.StatusReasonStoreReadError,
@@ -482,11 +491,15 @@ func TestListCorruptObjects(t *testing.T) {
482491
{
483492
secrets: secrets,
484493
featureEnabled: false,
485-
encryptionBrokenFn: func(t *testing.T, got apierrors.APIStatus) bool {
486-
// the new encryption config does not have the old key, so reading of resources
487-
// created before the encryption change will fail with 'no matching prefix found'
488-
return got.Status().Reason == metav1.StatusReasonInternalError &&
489-
strings.Contains(got.Status().Message, "Internal error occurred: no matching prefix found")
494+
encryptionBrokenFn: func(t *testing.T, got apierrors.APIStatus) {
495+
status := got.Status()
496+
if status.Reason != metav1.StatusReasonInternalError {
497+
t.Errorf("Invalid reason, got: %q, want: %q", status.Reason, metav1.StatusReasonInternalError)
498+
}
499+
noMatchingPrefixFoundMsg := "Internal error occurred: cipher: message authentication failed"
500+
if !strings.Contains(status.Message, noMatchingPrefixFoundMsg) {
501+
t.Errorf("Message should include %q, but got: %q", noMatchingPrefixFoundMsg, status.Message)
502+
}
490503
},
491504
listAfter: wantAPIStatusError{
492505
reason: metav1.StatusReasonInternalError,
@@ -497,12 +510,13 @@ func TestListCorruptObjects(t *testing.T) {
497510
for _, tc := range tests {
498511
t.Run(fmt.Sprintf("%s/%t", string(genericfeatures.AllowUnsafeMalformedObjectDeletion), tc.featureEnabled), func(t *testing.T) {
499512
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.AllowUnsafeMalformedObjectDeletion, tc.featureEnabled)
500-
501-
test, err := newTransformTest(t, transformTestConfig{transformerConfigYAML: aesGCMConfigYAML, reload: true})
513+
storageConfig := framework.SharedEtcd()
514+
test, err := newTransformTest(t, transformTestConfig{transformerConfigYAML: aesGCMConfigYAML, reload: true, storageConfig: storageConfig})
502515
if err != nil {
503516
t.Fatalf("failed to setup test for envelop %s, error was %v", aesGCMPrefix, err)
504517
}
505518
defer test.cleanUp()
519+
ctx := context.Background()
506520

507521
// a) create a number of secrets in the test namespace
508522
for _, name := range tc.secrets {
@@ -523,64 +537,45 @@ func TestListCorruptObjects(t *testing.T) {
523537

524538
// c) override the config and break decryption of the old resources,
525539
// the secret created in step a will be undecryptable
526-
encryptionConf := filepath.Join(test.configDir, encryptionConfigFileName)
527-
body, _ := ioutil.ReadFile(encryptionConf)
528-
t.Logf("file before write: %s", body)
529-
// we replace the existing key with a new key from a different provider
530-
if err := os.WriteFile(encryptionConf, []byte(aesCBCConfigYAML), 0o644); err != nil {
531-
t.Fatalf("failed to write encryption config that's going to make decryption fail")
540+
client, err := clientv3.New(clientv3.Config{Endpoints: storageConfig.Transport.ServerList})
541+
if err != nil {
542+
t.Fatal(err)
532543
}
533-
body, _ = ioutil.ReadFile(encryptionConf)
534-
t.Logf("file after write: %s", body)
535-
536-
// d) wait for the breaking changes to take effect
537-
testCtx, cancel := context.WithCancel(context.Background())
538-
defer cancel()
539-
// TODO: dynamic encryption config reload takes about 1m, so can't use
540-
// wait.ForeverTestTimeout just yet, investigate and reduce the reload time.
541-
err = wait.PollUntilContextTimeout(testCtx, 1*time.Second, 2*time.Minute, true, func(ctx context.Context) (done bool, err error) {
542-
_, err = test.restClient.CoreV1().Secrets(testNamespace).Get(ctx, tc.secrets[0], metav1.GetOptions{})
543-
544+
defer func() {
545+
err := client.Close()
544546
if err != nil {
545-
t.Logf("get returned error: %#v message: %s", err, err.Error())
546-
}
547-
548-
var got apierrors.APIStatus
549-
if !errors.As(err, &got) {
550-
return false, nil
551-
}
552-
if done := tc.encryptionBrokenFn(t, got); done {
553-
return true, nil
547+
t.Fatal(err)
554548
}
555-
return false, nil
556-
})
549+
}()
550+
resp, err := client.Get(ctx, "/"+storageConfig.Prefix+"/secrets/", clientv3.WithPrefix())
557551
if err != nil {
558-
t.Fatalf("encryption never broke: %v", err)
552+
t.Fatal(err)
553+
}
554+
if len(resp.Kvs) != len(tc.secrets) {
555+
t.Fatalf("Expected %d number of keys, got: %d", len(tc.secrets), len(resp.Kvs))
556+
}
557+
for _, kv := range resp.Kvs {
558+
// Remove last byte
559+
_, err = client.Put(ctx, string(kv.Key), string(kv.Value)[:len(kv.Value)-1])
560+
if err != nil {
561+
t.Fatal(err)
562+
}
559563
}
560564

561-
// TODO: ConsistentListFromCache feature returns the list of objects
562-
// from cache even though these objects are not readable from the
563-
// store after encryption has broken; to work around this issue, let's
564-
// create a new secret and retrieve it from the store to get a more
565-
// recent ResourceVersion and invoke the list with:
566-
// ResourceVersionMatch: Exact
567-
newSecretName := "new-a"
568-
_, err = test.createSecret(newSecretName, testNamespace)
565+
_, err = test.restClient.CoreV1().Secrets(testNamespace).Get(ctx, tc.secrets[0], metav1.GetOptions{})
569566
if err != nil {
570-
t.Fatalf("expected no error while creating the new secret, but got: %d", err)
567+
t.Logf("get returned error: %#v message: %s", err, err.Error())
571568
}
572-
newSecret, err := test.restClient.CoreV1().Secrets(testNamespace).Get(context.Background(), newSecretName, metav1.GetOptions{})
573-
if err != nil {
574-
t.Fatalf("expected no error getting the new secret, but got: %d", err)
569+
570+
var got apierrors.APIStatus
571+
if !errors.As(err, &got) {
572+
t.Fatalf("encryption never broke: %v", err)
575573
}
574+
tc.encryptionBrokenFn(t, got)
576575

577576
// e) list should return expected error
578-
_, err = test.restClient.CoreV1().Secrets(testNamespace).List(context.Background(), metav1.ListOptions{
579-
ResourceVersion: newSecret.ResourceVersion,
580-
ResourceVersionMatch: metav1.ResourceVersionMatchExact,
581-
})
577+
_, err = test.restClient.CoreV1().Secrets(testNamespace).List(ctx, metav1.ListOptions{})
582578
tc.listAfter.verify(t, err)
583-
584579
})
585580
}
586581
}

0 commit comments

Comments
 (0)