Skip to content

Commit

Permalink
Add API calls and expose state settings functions.
Browse files Browse the repository at this point in the history
Expose the ability to update and delete storage pools on the api.
  • Loading branch information
Veebers committed Jan 28, 2019
1 parent 9f549a7 commit b1c6f99
Show file tree
Hide file tree
Showing 10 changed files with 249 additions and 9 deletions.
20 changes: 16 additions & 4 deletions apiserver/facades/client/storage/base_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ type baseStorageSuite struct {
resources *common.Resources
authorizer apiservertesting.FakeAuthorizer

api *storage.APIv4
apiCaas *storage.APIv4
api *storage.APIv5
apiCaas *storage.APIv5
apiv3 *storage.APIv3
storageAccessor *mockStorageAccessor
state *mockState
Expand Down Expand Up @@ -68,8 +68,8 @@ func (s *baseStorageSuite) SetUpTest(c *gc.C) {

s.callContext = context.NewCloudCallContext()
var err error
s.api, err = storage.NewAPIv4(s.state, state.ModelTypeIAAS, s.storageAccessor, s.registry, s.poolManager, s.resources, s.authorizer, s.callContext)
s.apiCaas, err = storage.NewAPIv4(s.state, state.ModelTypeCAAS, s.storageAccessor, s.registry, s.poolManager, s.resources, s.authorizer, s.callContext)
s.api, err = storage.NewAPIv5(s.state, state.ModelTypeIAAS, s.storageAccessor, s.registry, s.poolManager, s.resources, s.authorizer, s.callContext)
s.apiCaas, err = storage.NewAPIv5(s.state, state.ModelTypeCAAS, s.storageAccessor, s.registry, s.poolManager, s.resources, s.authorizer, s.callContext)
c.Assert(err, jc.ErrorIsNil)
s.apiv3, err = storage.NewAPIv3(s.state, state.ModelTypeIAAS, s.storageAccessor, s.registry, s.poolManager, s.resources, s.authorizer, s.callContext)
c.Assert(err, jc.ErrorIsNil)
Expand Down Expand Up @@ -357,5 +357,17 @@ func (s *baseStorageSuite) constructPoolManager() *mockPoolManager {
}
return result, nil
},
updatePool: func(name string, attrs map[string]interface{}) error {
if p, ok := s.pools[name]; ok {
updatedAttr := p.Attrs()
for k, v := range attrs {
updatedAttr[k] = v
}
newPool, err := jujustorage.NewConfig(name, p.Provider(), updatedAttr)
s.pools[name] = newPool
return err
}
return errors.NotFoundf("mock pool manager: get pool %v", name)
},
}
}
10 changes: 5 additions & 5 deletions apiserver/facades/client/storage/export_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Copyright 2015 Canonical Ltd.
// Copyright 2015, 2019 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package storage

var (
ValidatePoolListFilter = (*APIv4).validatePoolListFilter
ValidateNameCriteria = (*APIv4).validateNameCriteria
ValidateProviderCriteria = (*APIv4).validateProviderCriteria
EnsureStoragePoolFilter = (*APIv4).ensureStoragePoolFilter
ValidatePoolListFilter = (*APIv5).validatePoolListFilter
ValidateNameCriteria = (*APIv5).validateNameCriteria
ValidateProviderCriteria = (*APIv5).validateProviderCriteria
EnsureStoragePoolFilter = (*APIv5).ensureStoragePoolFilter
)

type (
Expand Down
5 changes: 5 additions & 0 deletions apiserver/facades/client/storage/mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type mockPoolManager struct {
createPool func(name string, providerType jujustorage.ProviderType, attrs map[string]interface{}) (*jujustorage.Config, error)
deletePool func(name string) error
listPools func() ([]*jujustorage.Config, error)
updatePool func(name string, attrs map[string]interface{}) error
}

func (m *mockPoolManager) Get(name string) (*jujustorage.Config, error) {
Expand All @@ -38,6 +39,10 @@ func (m *mockPoolManager) List() ([]*jujustorage.Config, error) {
return m.listPools()
}

func (m *mockPoolManager) Update(name string, attrs map[string]interface{}) error {
return m.updatePool(name, attrs)
}

type mockStorageAccessor struct {
storageInstance func(names.StorageTag) (state.StorageInstance, error)
allStorageInstances func() ([]state.StorageInstance, error)
Expand Down
53 changes: 53 additions & 0 deletions apiserver/facades/client/storage/pooldelete_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright 2019 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package storage_test

import (
"fmt"

jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"

storage "github.com/juju/juju/storage"
"github.com/juju/juju/storage/provider"
)

type poolDeleteSuite struct {
baseStorageSuite
}

var _ = gc.Suite(&poolDeleteSuite{})

func (s *poolDeleteSuite) createPools(c *gc.C, num int) {
var err error
for i := 0; i < num; i++ {
poolName := fmt.Sprintf("%v%v", tstName, i)
s.baseStorageSuite.pools[poolName], err =
storage.NewConfig(poolName, provider.LoopProviderType, map[string]interface{}{"zip": "zap"})
c.Assert(err, jc.ErrorIsNil)
}
}

func (s *poolDeleteSuite) TestDeletePool(c *gc.C) {
s.createPools(c, 1)
poolName := fmt.Sprintf("%v%v", tstName, 0)

err := s.api.DeletePool(poolName)
c.Assert(err, jc.ErrorIsNil)

pools, err := s.poolManager.List()
c.Assert(err, jc.ErrorIsNil)
c.Assert(pools, gc.HasLen, 0)
}

func (s *poolDeleteSuite) TestDeleteErrorNotExists(c *gc.C) {
poolName := fmt.Sprintf("%v%v", tstName, 0)

err := s.api.DeletePool(poolName)
c.Assert(err, jc.ErrorIsNil)

pools, err := s.poolManager.List()
c.Assert(err, jc.ErrorIsNil)
c.Assert(pools, gc.HasLen, 0)
}
63 changes: 63 additions & 0 deletions apiserver/facades/client/storage/poolupdate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright 2019 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package storage_test

import (
"fmt"

"github.com/juju/errors"
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"

"github.com/juju/juju/apiserver/params"
storage "github.com/juju/juju/storage"
"github.com/juju/juju/storage/provider"
)

type poolUpdateSuite struct {
baseStorageSuite
}

var _ = gc.Suite(&poolUpdateSuite{})

func (s *poolUpdateSuite) createPools(c *gc.C, num int) {
var err error
for i := 0; i < num; i++ {
poolName := fmt.Sprintf("%v%v", tstName, i)
s.baseStorageSuite.pools[poolName], err =
storage.NewConfig(poolName, provider.LoopProviderType, map[string]interface{}{"zip": "zap"})
c.Assert(err, jc.ErrorIsNil)
}
}

func (s *poolUpdateSuite) TestUpdatePool(c *gc.C) {
s.createPools(c, 1)
poolName := fmt.Sprintf("%v%v", tstName, 0)
newAttrs := map[string]interface{}{
"foo1": "bar1",
"zip": "zoom",
}

err := s.api.UpdatePool(params.StoragePool{
Name: poolName,
Attrs: newAttrs,
})
c.Assert(err, jc.ErrorIsNil)

expected, err := storage.NewConfig(poolName, provider.LoopProviderType, newAttrs)
c.Assert(err, jc.ErrorIsNil)

pools, err := s.poolManager.List()
c.Assert(err, jc.ErrorIsNil)
c.Assert(pools, gc.HasLen, 1)
c.Assert(pools[0], gc.DeepEquals, expected)
}

func (s *poolUpdateSuite) TestUpdatePoolError(c *gc.C) {
poolName := fmt.Sprintf("%v%v", tstName, 0)
err := s.api.UpdatePool(params.StoragePool{
Name: poolName,
})
c.Assert(err, jc.Satisfies, errors.IsNotFound)
}
41 changes: 41 additions & 0 deletions apiserver/facades/client/storage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,29 @@ type APIv4 struct {
*APIv3
}

// APIv5 implements the storage v5 API.
type APIv5 struct {
*APIv4
}

// NewAPIv5 returns a new storage v5 API facade.
func NewAPIv5(
backend backend,
modelType state.ModelType,
storageAccess storageAccess,
registry storage.ProviderRegistry,
pm poolmanager.PoolManager,
resources facade.Resources,
authorizer facade.Authorizer,
callContext context.ProviderCallContext,
) (*APIv5, error) {
apiv4, err := NewAPIv4(backend, modelType, storageAccess, registry, pm, resources, authorizer, callContext)
if err != nil {
return nil, err
}
return &APIv5{apiv4}, nil
}

// NewAPIv4 returns a new storage v4 API facade.
func NewAPIv4(
backend backend,
Expand Down Expand Up @@ -1111,3 +1134,21 @@ func (a *APIv4) importFilesystem(

// Destroy was dropped in V4, replaced with Remove.
func (*APIv4) Destroy(_, _ struct{}) {}

// DeletePool deletes the named pool
func (a *APIv5) DeletePool(poolName string) error {
if err := a.checkCanWrite(); err != nil {
return errors.Trace(err)
}
err := a.poolManager.Delete(poolName)
return err
}

// UpdatePool deletes the named pool
func (a *APIv5) UpdatePool(p params.StoragePool) error {
if err := a.checkCanWrite(); err != nil {
return errors.Trace(err)
}
err := a.poolManager.Update(p.Name, p.Attrs)
return err
}
16 changes: 16 additions & 0 deletions state/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,17 @@ func removeSettingsOp(collection, key string) txn.Op {
}
}

// updateSettings updates the Settings for key.
func updateSettings(db Database, collection, key string, values map[string]interface{}) error {
existing, err := readSettings(db, collection, key)
if err != nil {
return errors.Trace(err)
}
existing.Update(values)
_, err = existing.Write()
return errors.Trace(err)
}

// listSettings returns all the settings with the specified key prefix.
func listSettings(backend modelBackend, collection, keyPrefix string) (map[string]map[string]interface{}, error) {
settings, closer := backend.db().GetRawCollection(collection)
Expand Down Expand Up @@ -484,6 +495,11 @@ func (s *StateSettings) RemoveSettings(key string) error {
return removeSettings(s.backend.db(), s.collection, key)
}

// UpdateSettings exposes updateSettings on state for use outside the state package.
func (s *StateSettings) UpdateSettings(key string, settings map[string]interface{}) error {
return updateSettings(s.backend.db(), s.collection, key, settings)
}

// ListSettings exposes listSettings on state for use outside the state package.
func (s *StateSettings) ListSettings(keyPrefix string) (map[string]map[string]interface{}, error) {
return listSettings(s.backend, s.collection, keyPrefix)
Expand Down
29 changes: 29 additions & 0 deletions state/settings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,35 @@ func (s *SettingsSuite) TestList(c *gc.C) {
})
}

func (s *SettingsSuite) TestUpdateSettings(c *gc.C) {
_, err := s.createSettings(s.key, map[string]interface{}{"foo1": "bar1", "foo2": "bar2"})
c.Assert(err, jc.ErrorIsNil)
options := map[string]interface{}{"alpha": "beta", "foo2": "zap100"}
err = updateSettings(s.state.db(), s.collection, s.key, options)
c.Assert(err, jc.ErrorIsNil)

// Check MongoDB state.
var mgoData struct {
Settings settingsMap
}
settings, closer := s.state.db().GetCollection(settingsC)
defer closer()
err = settings.FindId(s.key).One(&mgoData)
c.Assert(err, jc.ErrorIsNil)
c.Assert(
map[string]interface{}(mgoData.Settings),
gc.DeepEquals,
map[string]interface{}{
"foo1": "bar1", "alpha": "beta", "foo2": "zap100",
})
}

func (s *SettingsSuite) TestUpdateSettingsErrorsNotFound(c *gc.C) {
options := map[string]interface{}{"alpha": "beta", "foo2": "zap100"}
err := updateSettings(s.state.db(), s.collection, s.key, options)
c.Assert(err, jc.Satisfies, errors.IsNotFound)
}

func (s *SettingsSuite) TestUpdatingInterfaceSliceValue(c *gc.C) {
// When storing config values that are coerced from schemas as
// List(Something), the value will always be a []interface{}. Make
Expand Down
13 changes: 13 additions & 0 deletions storage/poolmanager/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ type PoolManager interface {
// Delete removes the pool with name from state.
Delete(name string) error

// Update updates existing pool configuration with new or changed values
Update(name string, attrs map[string]interface{}) error

// Get returns the pool with name from state.
Get(name string) (*storage.Config, error)

Expand All @@ -28,6 +31,7 @@ type PoolManager interface {

type SettingsManager interface {
CreateSettings(key string, settings map[string]interface{}) error
UpdateSettings(key string, settings map[string]interface{}) error
ReadSettings(key string) (map[string]interface{}, error)
RemoveSettings(key string) error
ListSettings(keyPrefix string) (map[string]map[string]interface{}, error)
Expand All @@ -48,6 +52,15 @@ func (m MemSettings) CreateSettings(key string, settings map[string]interface{})
return nil
}

// UpdateSettings is part of the SettingsManager interface.
func (m MemSettings) UpdateSettings(key string, settings map[string]interface{}) error {
if _, ok := m.Settings[key]; !ok {
return errors.NotFoundf("settings with key %q", key)
}
m.Settings[key] = settings
return nil
}

// ReadSettings is part of the SettingsManager interface.
func (m MemSettings) ReadSettings(key string) (map[string]interface{}, error) {
settings, ok := m.Settings[key]
Expand Down
8 changes: 8 additions & 0 deletions storage/poolmanager/poolmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ func (pm *poolManager) Delete(name string) error {
return errors.Annotatef(err, "deleting pool %q", name)
}

// Update is defined on PoolManager interface.
func (pm *poolManager) Update(name string, attrs map[string]interface{}) error {
if name == "" {
return MissingNameError
}
return pm.settings.UpdateSettings(globalKey(name), attrs)
}

// Get is defined on PoolManager interface.
func (pm *poolManager) Get(name string) (*storage.Config, error) {
settings, err := pm.settings.ReadSettings(globalKey(name))
Expand Down

0 comments on commit b1c6f99

Please sign in to comment.