Skip to content

Commit e401688

Browse files
committed
If a charm specifies a min-juju-version of 2.8.0, then we no longer provision
a PV for charm storage.
1 parent 5b38ba8 commit e401688

File tree

17 files changed

+567
-211
lines changed

17 files changed

+567
-211
lines changed

api/caasoperatorprovisioner/client.go

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -107,27 +107,39 @@ type OperatorProvisioningInfo struct {
107107
Version version.Number
108108
APIAddresses []string
109109
Tags map[string]string
110-
CharmStorage storage.KubernetesFilesystemParams
110+
CharmStorage *storage.KubernetesFilesystemParams
111111
}
112112

113-
// OperatorProvisioningInfo returns the info needed to provision an operator.
114-
func (c *Client) OperatorProvisioningInfo() (OperatorProvisioningInfo, error) {
115-
var result params.OperatorProvisioningInfo
116-
if err := c.facade.FacadeCall("OperatorProvisioningInfo", nil, &result); err != nil {
113+
// OperatorProvisioningInfo returns the info needed to provision an operator for an application.
114+
func (c *Client) OperatorProvisioningInfo(applicationName string) (OperatorProvisioningInfo, error) {
115+
args := params.Entities{[]params.Entity{
116+
{Tag: names.NewApplicationTag(applicationName).String()},
117+
}}
118+
var result params.OperatorProvisioningInfoResults
119+
if err := c.facade.FacadeCall("OperatorProvisioningInfo", args, &result); err != nil {
117120
return OperatorProvisioningInfo{}, err
118121
}
119-
info := OperatorProvisioningInfo{
120-
ImagePath: result.ImagePath,
121-
Version: result.Version,
122-
APIAddresses: result.APIAddresses,
123-
Tags: result.Tags,
124-
CharmStorage: filesystemFromParams(result.CharmStorage),
125-
}
126-
return info, nil
122+
if len(result.Results) != 1 {
123+
return OperatorProvisioningInfo{}, errors.Errorf("expected one result, got %d", len(result.Results))
124+
}
125+
info := result.Results[0]
126+
if err := info.Error; err != nil {
127+
return OperatorProvisioningInfo{}, errors.Trace(err)
128+
}
129+
return OperatorProvisioningInfo{
130+
ImagePath: info.ImagePath,
131+
Version: info.Version,
132+
APIAddresses: info.APIAddresses,
133+
Tags: info.Tags,
134+
CharmStorage: filesystemFromParams(info.CharmStorage),
135+
}, nil
127136
}
128137

129-
func filesystemFromParams(in params.KubernetesFilesystemParams) storage.KubernetesFilesystemParams {
130-
return storage.KubernetesFilesystemParams{
138+
func filesystemFromParams(in *params.KubernetesFilesystemParams) *storage.KubernetesFilesystemParams {
139+
if in == nil {
140+
return nil
141+
}
142+
return &storage.KubernetesFilesystemParams{
131143
StorageName: in.StorageName,
132144
Provider: storage.ProviderType(in.Provider),
133145
Size: in.Size,

api/caasoperatorprovisioner/client_test.go

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -155,37 +155,38 @@ func (s *provisionerSuite) TestLifeCount(c *gc.C) {
155155
c.Check(err, gc.ErrorMatches, `expected 1 result, got 2`)
156156
}
157157

158-
func (s *provisionerSuite) OperatorProvisioningInfo(c *gc.C) {
158+
func (s *provisionerSuite) TestOperatorProvisioningInfo(c *gc.C) {
159159
vers := version.MustParse("2.99.0")
160160
client := newClient(func(objType string, version int, id, request string, a, result interface{}) error {
161161
c.Check(objType, gc.Equals, "CAASOperatorProvisioner")
162162
c.Check(id, gc.Equals, "")
163163
c.Assert(request, gc.Equals, "OperatorProvisioningInfo")
164-
c.Assert(a, gc.IsNil)
165-
c.Assert(result, gc.FitsTypeOf, &params.OperatorProvisioningInfo{})
166-
*(result.(*params.OperatorProvisioningInfo)) = params.OperatorProvisioningInfo{
167-
ImagePath: "juju-operator-image",
168-
Version: vers,
169-
APIAddresses: []string{"10.0.0.1:1"},
170-
Tags: map[string]string{"foo": "bar"},
171-
CharmStorage: params.KubernetesFilesystemParams{
172-
Size: 10,
173-
Provider: "kubernetes",
174-
StorageName: "stor",
175-
Tags: map[string]string{"model": "model-tag"},
176-
Attributes: map[string]interface{}{"key": "value"},
177-
},
178-
}
164+
c.Assert(a, jc.DeepEquals, params.Entities{Entities: []params.Entity{{"application-gitlab"}}})
165+
c.Assert(result, gc.FitsTypeOf, &params.OperatorProvisioningInfoResults{})
166+
*(result.(*params.OperatorProvisioningInfoResults)) = params.OperatorProvisioningInfoResults{
167+
Results: []params.OperatorProvisioningInfo{{
168+
ImagePath: "juju-operator-image",
169+
Version: vers,
170+
APIAddresses: []string{"10.0.0.1:1"},
171+
Tags: map[string]string{"foo": "bar"},
172+
CharmStorage: &params.KubernetesFilesystemParams{
173+
Size: 10,
174+
Provider: "kubernetes",
175+
StorageName: "stor",
176+
Tags: map[string]string{"model": "model-tag"},
177+
Attributes: map[string]interface{}{"key": "value"},
178+
},
179+
}}}
179180
return nil
180181
})
181-
info, err := client.OperatorProvisioningInfo()
182+
info, err := client.OperatorProvisioningInfo("gitlab")
182183
c.Assert(err, jc.ErrorIsNil)
183184
c.Assert(info, jc.DeepEquals, caasoperatorprovisioner.OperatorProvisioningInfo{
184185
ImagePath: "juju-operator-image",
185186
Version: vers,
186187
APIAddresses: []string{"10.0.0.1:1"},
187188
Tags: map[string]string{"foo": "bar"},
188-
CharmStorage: storage.KubernetesFilesystemParams{
189+
CharmStorage: &storage.KubernetesFilesystemParams{
189190
Size: 10,
190191
Provider: "kubernetes",
191192
StorageName: "stor",
@@ -195,6 +196,22 @@ func (s *provisionerSuite) OperatorProvisioningInfo(c *gc.C) {
195196
})
196197
}
197198

199+
func (s *provisionerSuite) TestOperatorProvisioningInfoArity(c *gc.C) {
200+
client := newClient(func(objType string, version int, id, request string, a, result interface{}) error {
201+
c.Check(objType, gc.Equals, "CAASOperatorProvisioner")
202+
c.Check(id, gc.Equals, "")
203+
c.Assert(request, gc.Equals, "OperatorProvisioningInfo")
204+
c.Assert(a, jc.DeepEquals, params.Entities{Entities: []params.Entity{{"application-gitlab"}}})
205+
c.Assert(result, gc.FitsTypeOf, &params.OperatorProvisioningInfoResults{})
206+
*(result.(*params.OperatorProvisioningInfoResults)) = params.OperatorProvisioningInfoResults{
207+
Results: []params.OperatorProvisioningInfo{{}, {}},
208+
}
209+
return nil
210+
})
211+
_, err := client.OperatorProvisioningInfo("gitlab")
212+
c.Assert(err, gc.ErrorMatches, "expected one result, got 2")
213+
}
214+
198215
func (s *provisionerSuite) TestIssueOperatorCertificate(c *gc.C) {
199216
client := newClient(func(objType string, version int, id, request string, a, result interface{}) error {
200217
c.Check(objType, gc.Equals, "CAASOperatorProvisioner")

api/client.go

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818

1919
"github.com/gorilla/websocket"
2020
"github.com/juju/errors"
21+
jujuversion "github.com/juju/juju/version"
2122
"github.com/juju/version"
2223
"gopkg.in/juju/charm.v6"
2324
csparams "gopkg.in/juju/charmrepo.v4/csclient/params"
@@ -420,17 +421,6 @@ func (c *Client) UploadCharm(curl *charm.URL, content io.ReadSeeker) (*charm.URL
420421
return curl, nil
421422
}
422423

423-
type minJujuVersionErr struct {
424-
*errors.Err
425-
}
426-
427-
func minVersionError(minver, jujuver version.Number) error {
428-
err := errors.NewErr("charm's min version (%s) is higher than this juju model's version (%s)",
429-
minver, jujuver)
430-
err.SetLocation(1)
431-
return minJujuVersionErr{&err}
432-
}
433-
434424
func (c *Client) validateCharmVersion(ch charm.Charm) error {
435425
minver := ch.Meta().MinJujuVersion
436426
if minver != version.Zero {
@@ -439,9 +429,7 @@ func (c *Client) validateCharmVersion(ch charm.Charm) error {
439429
return errors.Trace(err)
440430
}
441431

442-
if minver.Compare(agentver) > 0 {
443-
return minVersionError(minver, agentver)
444-
}
432+
return jujuversion.CheckJujuMinVersion(minver, agentver)
445433
}
446434
return nil
447435
}

apiserver/facades/controller/caasoperatorprovisioner/mock_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package caasoperatorprovisioner_test
66
import (
77
"github.com/juju/errors"
88
"github.com/juju/testing"
9+
"gopkg.in/juju/charm.v6"
910
"gopkg.in/juju/names.v3"
1011
"gopkg.in/tomb.v2"
1112

@@ -62,6 +63,14 @@ func (st *mockState) APIHostPortsForAgents() ([]network.SpaceHostPorts, error) {
6263
}, nil
6364
}
6465

66+
func (st *mockState) Application(appName string) (caasoperatorprovisioner.Application, error) {
67+
st.MethodCall(st, "Application", appName)
68+
if appName != "gitlab" {
69+
return nil, errors.NotFoundf("app %v", appName)
70+
}
71+
return st.app, nil
72+
}
73+
6574
func (st *mockState) Model() (caasoperatorprovisioner.Model, error) {
6675
st.MethodCall(st, "Model")
6776
if err := st.NextErr(); err != nil {
@@ -119,6 +128,7 @@ type mockApplication struct {
119128
state.Authenticator
120129
tag names.Tag
121130
password string
131+
charm caasoperatorprovisioner.Charm
122132
}
123133

124134
func (m *mockApplication) Tag() names.Tag {
@@ -134,6 +144,18 @@ func (a *mockApplication) Life() state.Life {
134144
return state.Alive
135145
}
136146

147+
func (a *mockApplication) Charm() (caasoperatorprovisioner.Charm, bool, error) {
148+
return a.charm, false, nil
149+
}
150+
151+
type mockCharm struct {
152+
meta *charm.Meta
153+
}
154+
155+
func (ch *mockCharm) Meta() *charm.Meta {
156+
return ch.meta
157+
}
158+
137159
type mockWatcher struct {
138160
testing.Stub
139161
tomb.Tomb

apiserver/facades/controller/caasoperatorprovisioner/provisioner.go

Lines changed: 68 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88

99
"github.com/juju/errors"
10+
"github.com/juju/loggo"
1011
"gopkg.in/juju/names.v3"
1112

1213
"github.com/juju/juju/apiserver/common"
@@ -26,6 +27,8 @@ import (
2627
"github.com/juju/juju/version"
2728
)
2829

30+
var logger = loggo.GetLogger("juju.apiserver.caasoperatorprovisioner")
31+
2932
type API struct {
3033
*common.PasswordChanger
3134
*common.LifeGetter
@@ -96,59 +99,93 @@ func (a *API) WatchApplications() (params.StringsWatchResult, error) {
9699
}
97100

98101
// OperatorProvisioningInfo returns the info needed to provision an operator.
99-
func (a *API) OperatorProvisioningInfo() (params.OperatorProvisioningInfo, error) {
102+
func (a *API) OperatorProvisioningInfo(args params.Entities) (params.OperatorProvisioningInfoResults, error) {
103+
var result params.OperatorProvisioningInfoResults
100104
cfg, err := a.state.ControllerConfig()
101105
if err != nil {
102-
return params.OperatorProvisioningInfo{}, err
106+
return result, err
103107
}
104108

105109
model, err := a.state.Model()
106110
if err != nil {
107-
return params.OperatorProvisioningInfo{}, errors.Trace(err)
111+
return result, errors.Trace(err)
108112
}
109113
modelConfig, err := model.ModelConfig()
110114
if err != nil {
111-
return params.OperatorProvisioningInfo{}, errors.Trace(err)
115+
return result, errors.Trace(err)
112116
}
113117

114118
vers, ok := modelConfig.AgentVersion()
115119
if !ok {
116-
return params.OperatorProvisioningInfo{}, errors.NewNotValid(nil,
120+
return result, errors.NewNotValid(nil,
117121
fmt.Sprintf("agent version is missing in model config %q", modelConfig.Name()),
118122
)
119123
}
120124

125+
resourceTags := tags.ResourceTags(
126+
names.NewModelTag(model.UUID()),
127+
names.NewControllerTag(cfg.ControllerUUID()),
128+
modelConfig,
129+
)
130+
121131
imagePath := podcfg.GetJujuOCIImagePath(cfg, vers.ToPatch(), version.OfficialBuild)
122-
storageClassName, _ := modelConfig.AllAttrs()[provider.OperatorStorageKey].(string)
123-
if storageClassName == "" {
124-
return params.OperatorProvisioningInfo{}, errors.New("no operator storage class defined")
125-
}
126-
charmStorageParams, err := CharmStorageParams(cfg.ControllerUUID(), storageClassName, modelConfig, "", a.storagePoolManager, a.registry)
127-
if err != nil {
128-
return params.OperatorProvisioningInfo{}, errors.Annotatef(err, "getting operator storage parameters")
129-
}
130132
apiAddresses, err := a.APIAddresses()
131133
if err == nil && apiAddresses.Error != nil {
132134
err = apiAddresses.Error
133135
}
134136
if err != nil {
135-
return params.OperatorProvisioningInfo{}, errors.Annotatef(err, "getting api addresses")
137+
return result, errors.Annotatef(err, "getting api addresses")
136138
}
137139

138-
resourceTags := tags.ResourceTags(
139-
names.NewModelTag(model.UUID()),
140-
names.NewControllerTag(cfg.ControllerUUID()),
141-
modelConfig,
142-
)
143-
charmStorageParams.Tags = resourceTags
144-
145-
return params.OperatorProvisioningInfo{
146-
ImagePath: imagePath,
147-
Version: vers,
148-
APIAddresses: apiAddresses.Result,
149-
CharmStorage: charmStorageParams,
150-
Tags: resourceTags,
151-
}, nil
140+
oneProvisioningInfo := func(storageRequired bool) params.OperatorProvisioningInfo {
141+
var charmStorageParams *params.KubernetesFilesystemParams
142+
storageClassName, _ := modelConfig.AllAttrs()[provider.OperatorStorageKey].(string)
143+
if storageRequired {
144+
if storageClassName == "" {
145+
return params.OperatorProvisioningInfo{
146+
Error: common.ServerError(errors.New("no operator storage defined")),
147+
}
148+
} else {
149+
charmStorageParams, err = CharmStorageParams(cfg.ControllerUUID(), storageClassName, modelConfig, "", a.storagePoolManager, a.registry)
150+
if err != nil {
151+
return params.OperatorProvisioningInfo{
152+
Error: common.ServerError(errors.Annotatef(err, "getting operator storage parameters")),
153+
}
154+
}
155+
charmStorageParams.Tags = resourceTags
156+
}
157+
}
158+
return params.OperatorProvisioningInfo{
159+
ImagePath: imagePath,
160+
Version: vers,
161+
APIAddresses: apiAddresses.Result,
162+
CharmStorage: charmStorageParams,
163+
Tags: resourceTags,
164+
}
165+
}
166+
result.Results = make([]params.OperatorProvisioningInfo, len(args.Entities))
167+
for i, entity := range args.Entities {
168+
appName, err := names.ParseApplicationTag(entity.Tag)
169+
if err != nil {
170+
result.Results[i].Error = common.ServerError(err)
171+
continue
172+
}
173+
app, err := a.state.Application(appName.Id())
174+
if err != nil {
175+
result.Results[i].Error = common.ServerError(err)
176+
continue
177+
}
178+
ch, _, err := app.Charm()
179+
if err != nil {
180+
result.Results[i].Error = common.ServerError(err)
181+
continue
182+
}
183+
needStorage := provider.RequireOperatorStorage(ch.Meta().MinJujuVersion)
184+
logger.Debugf("application %s has min-juju-version=%v, so charm storage is %v",
185+
appName.String(), ch.Meta().MinJujuVersion, needStorage)
186+
result.Results[i] = oneProvisioningInfo(needStorage)
187+
}
188+
return result, nil
152189
}
153190

154191
// IssueOperatorCertificate issues an x509 certificate for use by the specified application operator.
@@ -199,7 +236,7 @@ func CharmStorageParams(
199236
poolName string,
200237
poolManager poolmanager.PoolManager,
201238
registry storage.ProviderRegistry,
202-
) (params.KubernetesFilesystemParams, error) {
239+
) (*params.KubernetesFilesystemParams, error) {
203240
// The defaults here are for operator storage.
204241
// Workload storage will override these elsewhere.
205242
var size uint64 = 1024
@@ -209,7 +246,7 @@ func CharmStorageParams(
209246
modelCfg,
210247
)
211248

212-
result := params.KubernetesFilesystemParams{
249+
result := &params.KubernetesFilesystemParams{
213250
StorageName: "charm",
214251
Size: size,
215252
Provider: string(provider.K8s_ProviderType),
@@ -232,7 +269,7 @@ func CharmStorageParams(
232269

233270
providerType, attrs, err := poolStorageProvider(poolManager, registry, maybePoolName)
234271
if err != nil && (!errors.IsNotFound(err) || poolName != "") {
235-
return params.KubernetesFilesystemParams{}, errors.Trace(err)
272+
return nil, errors.Trace(err)
236273
}
237274
if err == nil {
238275
result.Provider = string(providerType)

0 commit comments

Comments
 (0)