github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/secrets/backend_test.go (about) 1 // Copyright 2022 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package secrets_test 5 6 import ( 7 "github.com/juju/collections/set" 8 "github.com/juju/errors" 9 "github.com/juju/testing" 10 jc "github.com/juju/testing/checkers" 11 "go.uber.org/mock/gomock" 12 gc "gopkg.in/check.v1" 13 14 coresecrets "github.com/juju/juju/core/secrets" 15 "github.com/juju/juju/secrets" 16 "github.com/juju/juju/secrets/mocks" 17 "github.com/juju/juju/secrets/provider" 18 ) 19 20 type backendSuite struct { 21 testing.IsolationSuite 22 } 23 24 var _ = gc.Suite(&backendSuite{}) 25 26 func (s *backendSuite) TestSaveContent(c *gc.C) { 27 ctrl := gomock.NewController(c) 28 defer ctrl.Finish() 29 30 jujuapi := mocks.NewMockJujuAPIClient(ctrl) 31 backend := mocks.NewMockSecretsBackend(ctrl) 32 33 backends := set.NewStrings("somebackend1", "somebackend2") 34 s.PatchValue(&secrets.GetBackend, func(cfg *provider.ModelBackendConfig) (provider.SecretsBackend, error) { 35 c.Assert(backends.Contains(cfg.BackendType), jc.IsTrue) 36 return backend, nil 37 }) 38 39 client, err := secrets.NewClient(jujuapi) 40 c.Assert(err, jc.ErrorIsNil) 41 42 jujuapi.EXPECT().GetSecretBackendConfig(nil).Return(&provider.ModelBackendConfigInfo{ 43 ActiveID: "backend-id2", 44 Configs: map[string]provider.ModelBackendConfig{ 45 "backend-id1": { 46 ControllerUUID: "controller-uuid1", 47 ModelUUID: "model-uuid1", 48 ModelName: "model1", 49 BackendConfig: provider.BackendConfig{BackendType: "somebackend1"}, 50 }, 51 "backend-id2": { 52 ControllerUUID: "controller-uuid2", 53 ModelUUID: "model-uuid2", 54 ModelName: "model2", 55 BackendConfig: provider.BackendConfig{BackendType: "somebackend2"}, 56 }, 57 }, 58 }, nil) 59 60 uri := coresecrets.NewURI() 61 secretValue := coresecrets.NewSecretValue(map[string]string{"foo": "bar"}) 62 backend.EXPECT().SaveContent(gomock.Any(), uri, 666, secretValue).Return("rev-id", nil) 63 64 val, err := client.SaveContent(uri, 666, secretValue) 65 c.Assert(err, jc.ErrorIsNil) 66 c.Assert(val, jc.DeepEquals, coresecrets.ValueRef{ 67 BackendID: "backend-id2", 68 RevisionID: "rev-id", 69 }) 70 } 71 72 func (s *backendSuite) TestGetContent(c *gc.C) { 73 ctrl := gomock.NewController(c) 74 defer ctrl.Finish() 75 76 jujuapi := mocks.NewMockJujuAPIClient(ctrl) 77 backend := mocks.NewMockSecretsBackend(ctrl) 78 79 backends := set.NewStrings("somebackend1", "somebackend2") 80 s.PatchValue(&secrets.GetBackend, func(cfg *provider.ModelBackendConfig) (provider.SecretsBackend, error) { 81 c.Assert(backends.Contains(cfg.BackendType), jc.IsTrue) 82 return backend, nil 83 }) 84 85 client, err := secrets.NewClient(jujuapi) 86 c.Assert(err, jc.ErrorIsNil) 87 88 uri := coresecrets.NewURI() 89 jujuapi.EXPECT().GetContentInfo(uri, "label", true, false).Return(&secrets.ContentParams{ 90 ValueRef: &coresecrets.ValueRef{ 91 BackendID: "backend-id1", 92 RevisionID: "rev-id", 93 }, 94 }, &provider.ModelBackendConfig{ 95 ControllerUUID: "controller-uuid1", 96 ModelUUID: "model-uuid1", 97 ModelName: "model1", 98 BackendConfig: provider.BackendConfig{BackendType: "somebackend1"}, 99 }, false, nil) 100 secretValue := coresecrets.NewSecretValue(map[string]string{"foo": "bar"}) 101 backend.EXPECT().GetContent(gomock.Any(), "rev-id").Return(secretValue, nil) 102 103 val, err := client.GetContent(uri, "label", true, false) 104 c.Assert(err, jc.ErrorIsNil) 105 c.Assert(val, jc.DeepEquals, secretValue) 106 } 107 108 func (s *backendSuite) TestGetContentSecretDrained(c *gc.C) { 109 ctrl := gomock.NewController(c) 110 defer ctrl.Finish() 111 112 jujuapi := mocks.NewMockJujuAPIClient(ctrl) 113 backend := mocks.NewMockSecretsBackend(ctrl) 114 115 backends := set.NewStrings("somebackend1", "somebackend2", "somebackend3") 116 s.PatchValue(&secrets.GetBackend, func(cfg *provider.ModelBackendConfig) (provider.SecretsBackend, error) { 117 c.Assert(backends.Contains(cfg.BackendType), jc.IsTrue) 118 return backend, nil 119 }) 120 121 client, err := secrets.NewClient(jujuapi) 122 c.Assert(err, jc.ErrorIsNil) 123 124 uri := coresecrets.NewURI() 125 secretValue := coresecrets.NewSecretValue(map[string]string{"foo": "bar"}) 126 gomock.InOrder( 127 jujuapi.EXPECT().GetContentInfo(uri, "label", true, false).Return(&secrets.ContentParams{ 128 ValueRef: &coresecrets.ValueRef{ 129 BackendID: "backend-id1", 130 RevisionID: "rev-id", 131 }, 132 }, &provider.ModelBackendConfig{ 133 ControllerUUID: "controller-uuid1", 134 ModelUUID: "model-uuid1", 135 ModelName: "model1", 136 BackendConfig: provider.BackendConfig{BackendType: "somebackend1"}, 137 }, true, nil), 138 139 // First not found - we try again with the active backend. 140 backend.EXPECT().GetContent(gomock.Any(), "rev-id").Return(nil, errors.NotFoundf("")), 141 jujuapi.EXPECT().GetContentInfo(uri, "label", true, false).Return(&secrets.ContentParams{ 142 ValueRef: &coresecrets.ValueRef{ 143 BackendID: "backend-id2", 144 RevisionID: "rev-id2", 145 }, 146 }, &provider.ModelBackendConfig{ 147 ControllerUUID: "controller-uuid2", 148 ModelUUID: "model-uuid2", 149 ModelName: "model2", 150 BackendConfig: provider.BackendConfig{BackendType: "somebackend2"}, 151 }, true, nil), 152 153 // Second not found - refresh backend config. 154 backend.EXPECT().GetContent(gomock.Any(), "rev-id2").Return(nil, errors.NotFoundf("")), 155 156 // Third time lucky. 157 jujuapi.EXPECT().GetContentInfo(uri, "label", true, false).Return(&secrets.ContentParams{ 158 ValueRef: &coresecrets.ValueRef{ 159 BackendID: "backend-id3", 160 RevisionID: "rev-id3", 161 }, 162 }, &provider.ModelBackendConfig{ 163 ControllerUUID: "controller-uuid3", 164 ModelUUID: "model-uuid3", 165 ModelName: "model3", 166 BackendConfig: provider.BackendConfig{BackendType: "somebackend3"}, 167 }, false, nil), 168 backend.EXPECT().GetContent(gomock.Any(), "rev-id3").Return(secretValue, nil), 169 ) 170 171 val, err := client.GetContent(uri, "label", true, false) 172 c.Assert(err, jc.ErrorIsNil) 173 c.Assert(val, jc.DeepEquals, secretValue) 174 } 175 176 func (s *backendSuite) TestDeleteContent(c *gc.C) { 177 ctrl := gomock.NewController(c) 178 defer ctrl.Finish() 179 180 jujuapi := mocks.NewMockJujuAPIClient(ctrl) 181 backend := mocks.NewMockSecretsBackend(ctrl) 182 183 backends := set.NewStrings("somebackend1", "somebackend2") 184 s.PatchValue(&secrets.GetBackend, func(cfg *provider.ModelBackendConfig) (provider.SecretsBackend, error) { 185 c.Assert(backends.Contains(cfg.BackendType), jc.IsTrue) 186 return backend, nil 187 }) 188 189 client, err := secrets.NewClient(jujuapi) 190 c.Assert(err, jc.ErrorIsNil) 191 192 uri := coresecrets.NewURI() 193 jujuapi.EXPECT().GetRevisionContentInfo(uri, 666, true).Return(&secrets.ContentParams{ 194 ValueRef: &coresecrets.ValueRef{ 195 BackendID: "backend-id2", 196 RevisionID: "rev-id", 197 }, 198 }, &provider.ModelBackendConfig{ 199 ControllerUUID: "controller-uuid2", 200 ModelUUID: "model-uuid2", 201 ModelName: "model2", 202 BackendConfig: provider.BackendConfig{BackendType: "somebackend2"}, 203 }, false, nil) 204 205 backend.EXPECT().DeleteContent(gomock.Any(), "rev-id").Return(nil) 206 207 err = client.DeleteContent(uri, 666) 208 c.Assert(err, jc.ErrorIsNil) 209 } 210 211 func (s *backendSuite) TestDeleteContentDrained(c *gc.C) { 212 ctrl := gomock.NewController(c) 213 defer ctrl.Finish() 214 215 jujuapi := mocks.NewMockJujuAPIClient(ctrl) 216 backend := mocks.NewMockSecretsBackend(ctrl) 217 218 backends := set.NewStrings("somebackend1", "somebackend2", "somebackend3") 219 s.PatchValue(&secrets.GetBackend, func(cfg *provider.ModelBackendConfig) (provider.SecretsBackend, error) { 220 c.Assert(backends.Contains(cfg.BackendType), jc.IsTrue) 221 return backend, nil 222 }) 223 224 client, err := secrets.NewClient(jujuapi) 225 c.Assert(err, jc.ErrorIsNil) 226 227 uri := coresecrets.NewURI() 228 gomock.InOrder( 229 jujuapi.EXPECT().GetRevisionContentInfo(uri, 666, true).Return(&secrets.ContentParams{ 230 ValueRef: &coresecrets.ValueRef{ 231 BackendID: "backend-id1", 232 RevisionID: "rev-id", 233 }, 234 }, &provider.ModelBackendConfig{ 235 ControllerUUID: "controller-uuid1", 236 ModelUUID: "model-uuid1", 237 ModelName: "model1", 238 BackendConfig: provider.BackendConfig{BackendType: "somebackend1"}, 239 }, true, nil), 240 backend.EXPECT().DeleteContent(gomock.Any(), "rev-id").Return(errors.NotFoundf("")), 241 242 // Second not found - refresh backend config. 243 jujuapi.EXPECT().GetRevisionContentInfo(uri, 666, true).Return(&secrets.ContentParams{ 244 ValueRef: &coresecrets.ValueRef{ 245 BackendID: "backend-id2", 246 RevisionID: "rev-id2", 247 }, 248 }, &provider.ModelBackendConfig{ 249 ControllerUUID: "controller-uuid2", 250 ModelUUID: "model-uuid2", 251 ModelName: "model2", 252 BackendConfig: provider.BackendConfig{BackendType: "somebackend2"}, 253 }, true, nil), 254 backend.EXPECT().DeleteContent(gomock.Any(), "rev-id2").Return(errors.NotFoundf("")), 255 256 // Third time lucky. 257 jujuapi.EXPECT().GetRevisionContentInfo(uri, 666, true).Return(&secrets.ContentParams{ 258 ValueRef: &coresecrets.ValueRef{ 259 BackendID: "backend-id3", 260 RevisionID: "rev-id3", 261 }, 262 }, &provider.ModelBackendConfig{ 263 ControllerUUID: "controller-uuid2", 264 ModelUUID: "model-uuid2", 265 ModelName: "model2", 266 BackendConfig: provider.BackendConfig{BackendType: "somebackend2"}, 267 }, false, nil), 268 backend.EXPECT().DeleteContent(gomock.Any(), "rev-id3").Return(nil), 269 ) 270 271 err = client.DeleteContent(uri, 666) 272 c.Assert(err, jc.ErrorIsNil) 273 } 274 275 func (s *backendSuite) TestGetBackend(c *gc.C) { 276 ctrl := gomock.NewController(c) 277 defer ctrl.Finish() 278 279 jujuapi := mocks.NewMockJujuAPIClient(ctrl) 280 backend := mocks.NewMockSecretsBackend(ctrl) 281 282 backends := set.NewStrings("somebackend1", "somebackend2", "somebackend3") 283 called := 0 284 s.PatchValue(&secrets.GetBackend, func(cfg *provider.ModelBackendConfig) (provider.SecretsBackend, error) { 285 c.Assert(backends.Contains(cfg.BackendType), jc.IsTrue) 286 called++ 287 if called == 1 { 288 c.Assert(cfg.BackendType, gc.Equals, "somebackend2") 289 } else { 290 c.Assert(cfg.BackendType, gc.Equals, "somebackend1") 291 } 292 return backend, nil 293 }) 294 295 client, err := secrets.NewClient(jujuapi) 296 c.Assert(err, jc.ErrorIsNil) 297 backendID := "backend-id1" 298 299 gomock.InOrder( 300 jujuapi.EXPECT().GetSecretBackendConfig(nil).Return(&provider.ModelBackendConfigInfo{ 301 ActiveID: "backend-id2", 302 Configs: map[string]provider.ModelBackendConfig{ 303 "backend-id1": { 304 ControllerUUID: "controller-uuid1", 305 ModelUUID: "model-uuid1", 306 ModelName: "model1", 307 BackendConfig: provider.BackendConfig{BackendType: "somebackend1"}, 308 }, 309 "backend-id2": { 310 ControllerUUID: "controller-uuid2", 311 ModelUUID: "model-uuid2", 312 ModelName: "model2", 313 BackendConfig: provider.BackendConfig{BackendType: "somebackend2"}, 314 }, 315 }, 316 }, nil), 317 jujuapi.EXPECT().GetSecretBackendConfig(&backendID).Return(&provider.ModelBackendConfigInfo{ 318 ActiveID: "backend-id2", 319 Configs: map[string]provider.ModelBackendConfig{ 320 "backend-id1": { 321 ControllerUUID: "controller-uuid1", 322 ModelUUID: "model-uuid1", 323 ModelName: "model1", 324 BackendConfig: provider.BackendConfig{BackendType: "somebackend1"}, 325 }, 326 "backend-id2": { 327 ControllerUUID: "controller-uuid2", 328 ModelUUID: "model-uuid2", 329 ModelName: "model2", 330 BackendConfig: provider.BackendConfig{BackendType: "somebackend2"}, 331 }, 332 }, 333 }, nil), 334 jujuapi.EXPECT().GetBackendConfigForDrain(&backendID).Return( 335 &provider.ModelBackendConfig{ 336 ControllerUUID: "controller-uuid1", 337 ModelUUID: "model-uuid1", 338 ModelName: "model1", 339 BackendConfig: provider.BackendConfig{BackendType: "somebackend1"}, 340 }, "backend-id1", nil, 341 ), 342 ) 343 result, activeBackendID, err := client.GetBackend(nil, false) 344 c.Assert(err, jc.ErrorIsNil) 345 c.Assert(activeBackendID, gc.Equals, "backend-id2") 346 c.Assert(result, gc.Equals, backend) 347 348 result, activeBackendID, err = client.GetBackend(&backendID, false) 349 c.Assert(err, jc.ErrorIsNil) 350 c.Assert(activeBackendID, gc.Equals, "backend-id2") 351 c.Assert(result, gc.Equals, backend) 352 353 result, activeBackendID, err = client.GetBackend(&backendID, true) 354 c.Assert(err, jc.ErrorIsNil) 355 c.Assert(activeBackendID, gc.Equals, "backend-id1") 356 c.Assert(result, gc.Equals, backend) 357 } 358 359 func (s *backendSuite) TestGetRevisionContent(c *gc.C) { 360 ctrl := gomock.NewController(c) 361 defer ctrl.Finish() 362 363 jujuapi := mocks.NewMockJujuAPIClient(ctrl) 364 backend := mocks.NewMockSecretsBackend(ctrl) 365 366 backends := set.NewStrings("somebackend1", "somebackend2", "somebackend3") 367 s.PatchValue(&secrets.GetBackend, func(cfg *provider.ModelBackendConfig) (provider.SecretsBackend, error) { 368 c.Assert(backends.Contains(cfg.BackendType), jc.IsTrue) 369 return backend, nil 370 }) 371 372 client, err := secrets.NewClient(jujuapi) 373 c.Assert(err, jc.ErrorIsNil) 374 375 uri := coresecrets.NewURI() 376 secretValue := coresecrets.NewSecretValue(map[string]string{"foo": "bar"}) 377 gomock.InOrder( 378 jujuapi.EXPECT().GetRevisionContentInfo(uri, 666, false).Return(&secrets.ContentParams{ 379 ValueRef: &coresecrets.ValueRef{ 380 BackendID: "backend-id2", 381 RevisionID: "rev-id", 382 }, 383 }, &provider.ModelBackendConfig{ 384 ControllerUUID: "controller-uuid1", 385 ModelUUID: "model-uuid2", 386 ModelName: "model2", 387 BackendConfig: provider.BackendConfig{BackendType: "somebackend2"}, 388 }, false, nil), 389 jujuapi.EXPECT().GetSecretBackendConfig(ptr("backend-id2")).Return(&provider.ModelBackendConfigInfo{ 390 ActiveID: "backend-id2", 391 Configs: map[string]provider.ModelBackendConfig{ 392 "backend-id1": { 393 ControllerUUID: "controller-uuid1", 394 ModelUUID: "model-uuid1", 395 ModelName: "model1", 396 BackendConfig: provider.BackendConfig{BackendType: "somebackend1"}, 397 }, 398 "backend-id2": { 399 ControllerUUID: "controller-uuid1", 400 ModelUUID: "model-uuid2", 401 ModelName: "model2", 402 BackendConfig: provider.BackendConfig{BackendType: "somebackend2"}, 403 }, 404 }, 405 }, nil), 406 backend.EXPECT().GetContent(gomock.Any(), "rev-id").Return(secretValue, nil), 407 ) 408 409 val, err := client.GetRevisionContent(uri, 666) 410 c.Assert(err, jc.ErrorIsNil) 411 c.Assert(val, gc.Equals, secretValue) 412 } 413 414 func ptr[T any](v T) *T { 415 return &v 416 }