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  }