github.com/thiagoyeds/go-cloud@v0.26.0/runtimevar/gcpsecretmanager/gcpsecretmanager_test.go (about) 1 // Copyright 2020 The Go Cloud Development Kit Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package gcpsecretmanager 16 17 import ( 18 "context" 19 "errors" 20 "fmt" 21 "testing" 22 23 secretmanager "cloud.google.com/go/secretmanager/apiv1" 24 "gocloud.dev/internal/gcerr" 25 "gocloud.dev/internal/testing/setup" 26 "gocloud.dev/runtimevar" 27 "gocloud.dev/runtimevar/driver" 28 "gocloud.dev/runtimevar/drivertest" 29 "google.golang.org/api/option" 30 secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1" 31 "google.golang.org/grpc/codes" 32 "google.golang.org/grpc/status" 33 ) 34 35 // This constant records the project used for the last --record. 36 // If you want to use --record mode, 37 // 1. Update this constant to your GCP project ID. 38 // 2. Ensure that the "Secret Manager API" is enabled for your project. 39 // TODO(issue #300): Use Terraform to get this. 40 const projectID = "go-cloud-test-216917" 41 42 func secretKey(secretID string) string { 43 return "projects/" + projectID + "/secrets/" + secretID 44 } 45 46 type harness struct { 47 client *secretmanager.Client 48 closer func() 49 } 50 51 func newHarness(t *testing.T) (drivertest.Harness, error) { 52 ctx := context.Background() 53 conn, done := setup.NewGCPgRPCConn(ctx, t, "secretmanager.googleapis.com:443", "runtimevar") 54 55 client, err := secretmanager.NewClient(ctx, option.WithGRPCConn(conn)) 56 if err != nil { 57 return nil, err 58 } 59 60 return &harness{ 61 client: client, 62 closer: func() { 63 _ = client.Close() 64 done() 65 }, 66 }, nil 67 } 68 69 func (h *harness) MakeWatcher(_ context.Context, name string, decoder *runtimevar.Decoder) (driver.Watcher, error) { 70 return newWatcher(h.client, secretKey(name), decoder, nil) 71 } 72 73 func (h *harness) CreateVariable(ctx context.Context, name string, val []byte) error { 74 _, err := h.client.CreateSecret(ctx, &secretmanagerpb.CreateSecretRequest{ 75 Parent: "projects/" + projectID, 76 SecretId: name, 77 Secret: &secretmanagerpb.Secret{ 78 Replication: &secretmanagerpb.Replication{ 79 Replication: &secretmanagerpb.Replication_Automatic_{ 80 Automatic: &secretmanagerpb.Replication_Automatic{}, 81 }, 82 }, 83 Labels: map[string]string{ 84 "project": "runtimevar", 85 }, 86 }, 87 }) 88 if err != nil { 89 return err 90 } 91 92 // Add initial secret version. 93 _, err = h.client.AddSecretVersion(ctx, &secretmanagerpb.AddSecretVersionRequest{ 94 Parent: secretKey(name), 95 Payload: &secretmanagerpb.SecretPayload{Data: val}, 96 }) 97 98 return err 99 } 100 101 func (h *harness) UpdateVariable(ctx context.Context, name string, val []byte) error { 102 _, err := h.client.AddSecretVersion(ctx, &secretmanagerpb.AddSecretVersionRequest{ 103 Parent: secretKey(name), 104 Payload: &secretmanagerpb.SecretPayload{Data: val}, 105 }) 106 107 return err 108 } 109 110 func (h *harness) DeleteVariable(ctx context.Context, name string) error { 111 return h.client.DeleteSecret(ctx, &secretmanagerpb.DeleteSecretRequest{Name: secretKey(name)}) 112 } 113 114 func (h *harness) Close() { 115 h.closer() 116 } 117 118 func (h *harness) Mutable() bool { return true } 119 120 func TestConformance(t *testing.T) { 121 drivertest.RunConformanceTests(t, newHarness, []drivertest.AsTest{verifyAs{}}) 122 } 123 124 type verifyAs struct{} 125 126 func (verifyAs) Name() string { 127 return "verify As" 128 } 129 130 func (verifyAs) SnapshotCheck(s *runtimevar.Snapshot) error { 131 var v *secretmanagerpb.AccessSecretVersionResponse 132 if !s.As(&v) { 133 return errors.New("Snapshot.As failed") 134 } 135 return nil 136 } 137 138 func (verifyAs) ErrorCheck(v *runtimevar.Variable, err error) error { 139 var s *status.Status 140 if !v.ErrorAs(err, &s) { 141 return errors.New("runtimevar.ErrorAs failed") 142 } 143 return nil 144 } 145 146 // Secretmanager-specific tests. 147 148 func TestEquivalentError(t *testing.T) { 149 tests := []struct { 150 Err1, Err2 error 151 Want bool 152 }{ 153 {Err1: errors.New("not grpc"), Err2: errors.New("not grpc"), Want: true}, 154 {Err1: errors.New("not grpc"), Err2: errors.New("not grpc but different")}, 155 {Err1: errors.New("not grpc"), Err2: status.Errorf(codes.Internal, "fail")}, 156 {Err1: status.Errorf(codes.Internal, "fail"), Err2: status.Errorf(codes.InvalidArgument, "fail")}, 157 {Err1: status.Errorf(codes.Internal, "fail"), Err2: status.Errorf(codes.Internal, "fail"), Want: true}, 158 } 159 160 for _, test := range tests { 161 got := equivalentError(test.Err1, test.Err2) 162 if got != test.Want { 163 t.Errorf("%v vs %v: got %v want %v", test.Err1, test.Err2, got, test.Want) 164 } 165 } 166 } 167 168 func TestNoConnectionError(t *testing.T) { 169 ctx := context.Background() 170 creds, err := setup.FakeGCPCredentials(ctx) 171 if err != nil { 172 t.Fatal(err) 173 } 174 175 // Connect to the Secret Manager service. 176 client, cleanup, err := Dial(ctx, creds.TokenSource) 177 if err != nil { 178 t.Fatal(err) 179 } 180 defer cleanup() 181 182 key := SecretKey("gcp-project-id", "secret-name") 183 v, err := OpenVariable(client, key, nil, nil) 184 if err != nil { 185 t.Fatal(err) 186 } 187 defer func() { 188 if err := v.Close(); err != nil { 189 t.Error(err) 190 } 191 }() 192 193 _, err = v.Watch(ctx) 194 if err == nil { 195 t.Error("got nil want error") 196 } 197 } 198 199 func TestOpenVariable(t *testing.T) { 200 cleanup := setup.FakeGCPDefaultCredentials(t) 201 defer cleanup() 202 203 tests := []struct { 204 URL string 205 WantErr bool 206 }{ 207 // OK. 208 {"gcpsecretmanager://projects/myproject/secrets/mysecret", false}, 209 // OK, hierarchical key name. 210 {"gcpsecretmanager://projects/myproject/secrets/mysecret2", false}, 211 // OK, setting decoder. 212 {"gcpsecretmanager://projects/myproject/secrets/mysecret?decoder=string", false}, 213 // Missing projects prefix. 214 {"gcpsecretmanager://project/myproject/secrets/mysecret", true}, 215 // Missing project. 216 {"gcpsecretmanager://projects//secrets/mysecret", true}, 217 // Missing configs. 218 {"gcpsecretmanager://projects/myproject/mysecret", true}, 219 // Missing secretID with trailing slash. 220 {"gcpsecretmanager://projects/myproject/secrets/", true}, 221 // Missing secretID. 222 {"gcpsecretmanager://projects/myproject/secrets", true}, 223 // Invalid decoder. 224 {"gcpsecretmanager://projects/myproject/secrets/mysecret?decoder=notadecoder", true}, 225 // Invalid param. 226 {"gcpsecretmanager://projects/myproject/secrets/mysecret?param=value", true}, 227 } 228 229 ctx := context.Background() 230 for _, test := range tests { 231 if err := openVariable(ctx, test.URL); (err != nil) != test.WantErr { 232 t.Errorf("%s: got error %v, want error %v", test.URL, err, test.WantErr) 233 } 234 } 235 } 236 237 func openVariable(ctx context.Context, URL string) (err error) { 238 var v *runtimevar.Variable 239 v, err = runtimevar.OpenVariable(ctx, URL) 240 defer func() { 241 if v == nil { 242 return 243 } 244 245 if closeErr := v.Close(); closeErr != nil { 246 if grpcErr, ok := closeErr.(*gcerr.Error); ok && grpcErr.Code != gcerr.Canceled { 247 err = fmt.Errorf("close failed: %v. prev error: %v", closeErr, err) 248 } 249 } 250 }() 251 252 return err 253 }