diff --git a/go.mod b/go.mod index 7fd95595..a8fcb728 100644 --- a/go.mod +++ b/go.mod @@ -68,6 +68,7 @@ require ( github.com/docker/docker v20.10.20+incompatible // indirect github.com/docker/docker-credential-helpers v0.7.0 // indirect github.com/emicklei/go-restful/v3 v3.8.0 // indirect + github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/fatih/color v1.13.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-chi/chi v4.1.2+incompatible // indirect diff --git a/go.sum b/go.sum index 36484aab..627efb5b 100644 --- a/go.sum +++ b/go.sum @@ -211,6 +211,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw= github.com/facebookgo/limitgroup v0.0.0-20150612190941-6abd8d71ec01 h1:IeaD1VDVBPlx3viJT9Md8if8IxxJnO+x0JCGb054heg= github.com/facebookgo/muster v0.0.0-20150708232844-fd3d7953fd52 h1:a4DFiKFJiDRGFD1qIcqGLX/WlUMD9dyLSLDt+9QZgt8= diff --git a/pkg/secret/secret.go b/pkg/secret/secret.go index 6e37276d..f1007c0e 100644 --- a/pkg/secret/secret.go +++ b/pkg/secret/secret.go @@ -43,15 +43,15 @@ func ReconcileSecret(ctx context.Context, name, ns string, data map[string][]byt update := false for k := range data { if bytes.Compare(data[k], existingSecret.Data[k]) != 0 { - logging.FromContext(ctx).Infof("%s missing or different than expected, updating", k) + logging.FromContext(ctx).Infof("secret key %q missing or different than expected, updating", k) + existingSecret.Data[k] = data[k] update = true } } if update { - existingSecret.Data = data _, err = nsSecret.Update(ctx, existingSecret, metav1.UpdateOptions{}) if err != nil { - return fmt.Errorf("failed to udpate secret %s/%s: %w", ns, name, err) + return fmt.Errorf("failed to update secret %s/%s: %w", ns, name, err) } logging.FromContext(ctx).Infof("Updated secret %s/%s", ns, name) } diff --git a/pkg/secret/secret_test.go b/pkg/secret/secret_test.go new file mode 100644 index 00000000..0ddf6eea --- /dev/null +++ b/pkg/secret/secret_test.go @@ -0,0 +1,93 @@ +// Copyright 2022 The Sigstore Authors +// +// 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 secret + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + v1 "k8s.io/api/core/v1" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/fake" +) + +const ( + ns = "test-namespace" + name = "test-secret" +) + +func secret(existing map[string][]byte) *v1.Secret { + return &v1.Secret{ObjectMeta: meta_v1.ObjectMeta{Namespace: ns, Name: name}, Data: existing} +} + +func TestUpdateSecret(t *testing.T) { + var tests = []struct { + testName string + existing map[string][]byte + in map[string][]byte + want map[string][]byte + }{ + { + testName: "non-existing-creates", + existing: nil, + in: map[string][]byte{"foo": []byte("foo-value")}, + want: map[string][]byte{"foo": []byte("foo-value")}, + }, + { + testName: "empty-update-no-changes", + existing: map[string][]byte{"foo": []byte("foo-value")}, + in: map[string][]byte{}, + want: map[string][]byte{"foo": []byte("foo-value")}, + }, + { + testName: "update-field", + existing: map[string][]byte{"foo": []byte("foo-value")}, + in: map[string][]byte{"foo": []byte("new-foo")}, + want: map[string][]byte{"foo": []byte("new-foo")}, + }, + { + testName: "add-field", + existing: map[string][]byte{"foo": []byte("foo-value")}, + in: map[string][]byte{"bar": []byte("bar-value")}, + want: map[string][]byte{"foo": []byte("foo-value"), "bar": []byte("bar-value")}, + }, + } + + for _, tt := range tests { + t.Run(tt.testName, func(t *testing.T) { + objs := []runtime.Object{} + if tt.existing != nil { + objs = append(objs, secret(tt.existing)) + } + client := fake.NewSimpleClientset(objs...) + err := ReconcileSecret(context.Background(), name, ns, tt.in, client.CoreV1().Secrets(ns)) + if err != nil { + t.Errorf("Unexpected error updating: %s", err) + return + } + actual, err := client.CoreV1().Secrets(ns).Get(context.Background(), name, meta_v1.GetOptions{}) + if err != nil { + t.Errorf("Unexpected error getting: %s", err) + return + } + if diff := cmp.Diff(actual.Data, tt.want); diff != "" { + t.Errorf("%T differ (-got, +want): %s", tt.want, diff) + return + } + }) + } +}