Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

k8s operators as deployed as statefulsets and use storage correctly #9120

Merged
merged 1 commit into from Aug 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 20 additions & 6 deletions api/caasoperatorprovisioner/client.go
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/juju/juju/apiserver/params"
"github.com/juju/juju/core/life"
"github.com/juju/juju/core/watcher"
"github.com/juju/juju/storage"
)

// Client allows access to the CAAS operator provisioner API endpoint.
Expand Down Expand Up @@ -105,8 +106,9 @@ func (c *Client) Life(appName string) (life.Value, error) {

// OperatorProvisioningInfo holds the info needed to provision an operator.
type OperatorProvisioningInfo struct {
ImagePath string
Version version.Number
ImagePath string
Version version.Number
CharmStorage storage.KubernetesFilesystemParams
}

// OperatorProvisioningInfo returns the info needed to provision an operator.
Expand All @@ -115,8 +117,20 @@ func (c *Client) OperatorProvisioningInfo() (OperatorProvisioningInfo, error) {
if err := c.facade.FacadeCall("OperatorProvisioningInfo", nil, &result); err != nil {
return OperatorProvisioningInfo{}, err
}
return OperatorProvisioningInfo{
ImagePath: result.ImagePath,
Version: result.Version,
}, nil
info := OperatorProvisioningInfo{
ImagePath: result.ImagePath,
Version: result.Version,
CharmStorage: filesystemFromParams(result.CharmStorage),
}
return info, nil
}

func filesystemFromParams(in params.KubernetesFilesystemParams) storage.KubernetesFilesystemParams {
return storage.KubernetesFilesystemParams{
StorageName: in.StorageName,
Provider: storage.ProviderType(in.Provider),
Size: in.Size,
Attributes: in.Attributes,
ResourceTags: in.Tags,
}
}
15 changes: 15 additions & 0 deletions api/caasoperatorprovisioner/client_test.go
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/juju/juju/api/caasoperatorprovisioner"
"github.com/juju/juju/apiserver/params"
"github.com/juju/juju/core/life"
"github.com/juju/juju/storage"
)

type provisionerSuite struct {
Expand Down Expand Up @@ -165,6 +166,13 @@ func (s *provisionerSuite) OperatorProvisioningInfo(c *gc.C) {
*(result.(*params.OperatorProvisioningInfo)) = params.OperatorProvisioningInfo{
ImagePath: "juju-operator-image",
Version: vers,
CharmStorage: params.KubernetesFilesystemParams{
Size: 10,
Provider: "kubernetes",
StorageName: "stor",
Tags: map[string]string{"model": "mode-tag"},
Attributes: map[string]interface{}{"key": "value"},
},
}
return nil
})
Expand All @@ -173,5 +181,12 @@ func (s *provisionerSuite) OperatorProvisioningInfo(c *gc.C) {
c.Assert(info, jc.DeepEquals, caasoperatorprovisioner.OperatorProvisioningInfo{
ImagePath: "juju-operator-image",
Version: vers,
CharmStorage: storage.KubernetesFilesystemParams{
Size: 10,
Provider: "kubernetes",
StorageName: "stor",
ResourceTags: map[string]string{"model": "mode-tag"},
Attributes: map[string]interface{}{"key": "value"},
},
})
}
26 changes: 26 additions & 0 deletions apiserver/facades/controller/caasoperatorprovisioner/mock_test.go
Expand Up @@ -11,9 +11,12 @@ import (

"github.com/juju/juju/apiserver/common"
apiservertesting "github.com/juju/juju/apiserver/testing"
"github.com/juju/juju/caas/kubernetes/provider"
"github.com/juju/juju/controller"
"github.com/juju/juju/network"
"github.com/juju/juju/state"
"github.com/juju/juju/storage"
"github.com/juju/juju/storage/poolmanager"
coretesting "github.com/juju/juju/testing"
)

Expand Down Expand Up @@ -59,6 +62,29 @@ func (st *mockState) WatchAPIHostPortsForAgents() state.NotifyWatcher {
return apiservertesting.NewFakeNotifyWatcher()
}

type mockStorageProviderRegistry struct {
testing.Stub
storage.ProviderRegistry
}

func (m *mockStorageProviderRegistry) StorageProvider(providerType storage.ProviderType) (storage.Provider, error) {
m.MethodCall(m, "StorageProvider", providerType)
return nil, errors.NotSupportedf("StorageProvider")
}

type mockStoragePoolManager struct {
testing.Stub
poolmanager.PoolManager
}

func (m *mockStoragePoolManager) Get(name string) (*storage.Config, error) {
m.MethodCall(m, "Get", name)
if err := m.NextErr(); err != nil {
return nil, err
}
return storage.NewConfig(name, provider.K8s_ProviderType, map[string]interface{}{"foo": "bar"})
}

type mockApplication struct {
state.Authenticator
tag names.Tag
Expand Down
Expand Up @@ -6,12 +6,19 @@ package caasoperatorprovisioner
import (
"fmt"

"github.com/juju/errors"
"gopkg.in/juju/names.v2"

"github.com/juju/juju/apiserver/common"
"github.com/juju/juju/apiserver/common/storagecommon"
"github.com/juju/juju/apiserver/facade"
"github.com/juju/juju/apiserver/params"
"github.com/juju/juju/caas"
"github.com/juju/juju/state"
"github.com/juju/juju/state/stateenvirons"
"github.com/juju/juju/state/watcher"
"github.com/juju/juju/storage"
"github.com/juju/juju/storage/poolmanager"
"github.com/juju/juju/version"
)

Expand All @@ -23,22 +30,34 @@ type API struct {
auth facade.Authorizer
resources facade.Resources

state CAASOperatorProvisionerState
state CAASOperatorProvisionerState
storageProviderRegistry storage.ProviderRegistry
storagePoolManager poolmanager.PoolManager
}

// NewStateCAASOperatorProvisionerAPI provides the signature required for facade registration.
func NewStateCAASOperatorProvisionerAPI(ctx facade.Context) (*API, error) {

authorizer := ctx.Auth()
resources := ctx.Resources()
return NewCAASOperatorProvisionerAPI(resources, authorizer, ctx.State())

broker, err := stateenvirons.GetNewCAASBrokerFunc(caas.New)(ctx.State())
if err != nil {
return nil, errors.Annotate(err, "getting caas client")
}
registry := stateenvirons.NewStorageProviderRegistry(broker)
pm := poolmanager.New(state.NewStateSettings(ctx.State()), registry)

return NewCAASOperatorProvisionerAPI(resources, authorizer, ctx.State(), registry, pm)
}

// NewCAASOperatorProvisionerAPI returns a new CAAS operator provisioner API facade.
func NewCAASOperatorProvisionerAPI(
resources facade.Resources,
authorizer facade.Authorizer,
st CAASOperatorProvisionerState,
storageProviderRegistry storage.ProviderRegistry,
storagePoolManager poolmanager.PoolManager,
) (*API, error) {
if !authorizer.AuthController() {
return nil, common.ErrPerm
Expand All @@ -50,6 +69,8 @@ func NewCAASOperatorProvisionerAPI(
auth: authorizer,
resources: resources,
state: st,
storageProviderRegistry: storageProviderRegistry,
storagePoolManager: storagePoolManager,
}, nil
}

Expand Down Expand Up @@ -80,9 +101,41 @@ func (a *API) OperatorProvisioningInfo() (params.OperatorProvisioningInfo, error
vers.Build = 0
imagePath = fmt.Sprintf("%s/caas-jujud-operator:%s", "jujusolutions", vers.String())
}
charmStorageParams, err := charmStorageParams(a.storagePoolManager, a.storageProviderRegistry)
if err != nil {
return params.OperatorProvisioningInfo{}, errors.Annotatef(err, "getting operator storage parameters")
}

return params.OperatorProvisioningInfo{
ImagePath: imagePath,
Version: version.Current,
ImagePath: imagePath,
Version: version.Current,
CharmStorage: charmStorageParams,
}, nil
}

func charmStorageParams(
poolManager poolmanager.PoolManager,
registry storage.ProviderRegistry,
) (params.KubernetesFilesystemParams, error) {

// TODO(caas) - make these configurable via model config
var pool = "operator-storage"
var size uint64 = 1024

result := params.KubernetesFilesystemParams{
Size: size,
}

providerType, cfg, err := storagecommon.StoragePoolConfig(pool, poolManager, registry)
if err != nil && !errors.IsNotFound(err) {
return params.KubernetesFilesystemParams{}, errors.Trace(err)
}
// No storage pool so we'll just return the size of storage.
if err != nil {
return result, nil
}

result.Provider = string(providerType)
result.Attributes = cfg.Attrs()
return result, nil
}
Expand Up @@ -6,6 +6,7 @@ package caasoperatorprovisioner_test
import (
"fmt"

"github.com/juju/errors"
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
"gopkg.in/juju/names.v2"
Expand All @@ -24,10 +25,12 @@ var _ = gc.Suite(&CAASProvisionerSuite{})
type CAASProvisionerSuite struct {
coretesting.BaseSuite

resources *common.Resources
authorizer *apiservertesting.FakeAuthorizer
api *caasoperatorprovisioner.API
st *mockState
resources *common.Resources
authorizer *apiservertesting.FakeAuthorizer
api *caasoperatorprovisioner.API
st *mockState
storageProviderRegistry *mockStorageProviderRegistry
storagePoolManager *mockStoragePoolManager
}

func (s *CAASProvisionerSuite) SetUpTest(c *gc.C) {
Expand All @@ -42,7 +45,9 @@ func (s *CAASProvisionerSuite) SetUpTest(c *gc.C) {
}

s.st = newMockState()
api, err := caasoperatorprovisioner.NewCAASOperatorProvisionerAPI(s.resources, s.authorizer, s.st)
s.storageProviderRegistry = &mockStorageProviderRegistry{}
s.storagePoolManager = &mockStoragePoolManager{}
api, err := caasoperatorprovisioner.NewCAASOperatorProvisionerAPI(s.resources, s.authorizer, s.st, s.storageProviderRegistry, s.storagePoolManager)
c.Assert(err, jc.ErrorIsNil)
s.api = api
}
Expand All @@ -51,7 +56,7 @@ func (s *CAASProvisionerSuite) TestPermission(c *gc.C) {
s.authorizer = &apiservertesting.FakeAuthorizer{
Tag: names.NewMachineTag("0"),
}
_, err := caasoperatorprovisioner.NewCAASOperatorProvisionerAPI(s.resources, s.authorizer, s.st)
_, err := caasoperatorprovisioner.NewCAASOperatorProvisionerAPI(s.resources, s.authorizer, s.st, s.storageProviderRegistry, s.storagePoolManager)
c.Assert(err, gc.ErrorMatches, "permission denied")
}

Expand Down Expand Up @@ -122,6 +127,11 @@ func (s *CAASProvisionerSuite) TestOperatorProvisioningInfoDefault(c *gc.C) {
c.Assert(result, jc.DeepEquals, params.OperatorProvisioningInfo{
ImagePath: fmt.Sprintf("jujusolutions/caas-jujud-operator:%s", version.Current.String()),
Version: version.Current,
CharmStorage: params.KubernetesFilesystemParams{
Size: uint64(1024),
Provider: "kubernetes",
Attributes: map[string]interface{}{"foo": "bar"},
},
})
}

Expand All @@ -132,6 +142,25 @@ func (s *CAASProvisionerSuite) TestOperatorProvisioningInfo(c *gc.C) {
c.Assert(result, jc.DeepEquals, params.OperatorProvisioningInfo{
ImagePath: s.st.operatorImage,
Version: version.Current,
CharmStorage: params.KubernetesFilesystemParams{
Size: uint64(1024),
Provider: "kubernetes",
Attributes: map[string]interface{}{"foo": "bar"},
},
})
}

func (s *CAASProvisionerSuite) TestOperatorProvisioningInfoNoStoragePool(c *gc.C) {
s.storagePoolManager.SetErrors(errors.NotFoundf("pool"))
s.st.operatorImage = "jujusolutions/caas-jujud-operator"
result, err := s.api.OperatorProvisioningInfo()
c.Assert(err, jc.ErrorIsNil)
c.Assert(result, jc.DeepEquals, params.OperatorProvisioningInfo{
ImagePath: s.st.operatorImage,
Version: version.Current,
CharmStorage: params.KubernetesFilesystemParams{
Size: uint64(1024),
},
})
}

Expand Down
5 changes: 3 additions & 2 deletions apiserver/params/params.go
Expand Up @@ -481,8 +481,9 @@ type ConfigResult struct {

// OperatorProvisioningInfo holds info need to provision an operator.
type OperatorProvisioningInfo struct {
ImagePath string `json:"image-path"`
Version version.Number `json:"version"`
ImagePath string `json:"image-path"`
Version version.Number `json:"version"`
CharmStorage KubernetesFilesystemParams `json:"charm-storage"`
}

// PublicAddress holds parameters for the PublicAddress call.
Expand Down
22 changes: 22 additions & 0 deletions caas/broker.go
Expand Up @@ -174,6 +174,24 @@ type Unit struct {
FilesystemInfo []FilesystemInfo
}

// CharmStorageParams defines parameters used to create storage
// for operators to use for charm state.
type CharmStorageParams struct {
// Size is the minimum size of the filesystem in MiB.
Size uint64

// The provider type for this filesystem.
Provider storage.ProviderType

// Attributes is a set of provider-specific options for storage creation,
// as defined in a storage pool.
Attributes map[string]interface{}

// ResourceTags is a set of tags to set on the created filesystem, if the
// storage provider supports tags.
ResourceTags map[string]string
}

// OperatorConfig is the config to use when creating an operator.
type OperatorConfig struct {
// OperatorImagePath is the docker registry URL for the image.
Expand All @@ -182,6 +200,10 @@ type OperatorConfig struct {
// Version is the Juju version of the operator image.
Version version.Number

// CharmStorage defines parameters used to create storage
// for operators to use for charm state.
CharmStorage CharmStorageParams

// AgentConf is the contents of the agent.conf file.
AgentConf []byte
}