Skip to content

Commit 286fe15

Browse files
committed
Merge commit '1b9fcff04095ae5c49ce642279be94cf0a20323d' into 2.5-into-develop
no conflicts
2 parents d4fa9c8 + 1b9fcff commit 286fe15

37 files changed

+717
-188
lines changed

acceptancetests/assess_deploy_lxd_profile_bundle.py

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@
1919
add_basic_testing_arguments,
2020
configure_logging,
2121
JujuAssertionError,
22+
is_subordinate,
23+
subordinate_machines_from_app_info,
24+
application_machines_from_app_info,
25+
align_machine_profiles,
2226
)
2327
from jujupy.wait_condition import (
24-
AgentsIdle,
2528
WaitForLXDProfilesConditions,
2629
)
2730

@@ -61,31 +64,15 @@ def assess_profile_machines(client):
6164
if 'charm-profile' in info:
6265
charm_profile = info['charm-profile']
6366
if charm_profile:
64-
machines = application_machines(info)
67+
if is_subordinate(info):
68+
machines = subordinate_machines_from_app_info(info, apps)
69+
else:
70+
machines = application_machines_from_app_info(info)
6571
machine_profiles.append((charm_profile, machines))
6672
if len(machine_profiles) > 0:
6773
aligned_machine_profiles = align_machine_profiles(machine_profiles)
6874
client.wait_for(WaitForLXDProfilesConditions(aligned_machine_profiles))
6975

70-
def application_machines(app_data):
71-
"""Get all the machines used to host the given application."""
72-
machines = [unit_data['machine'] for unit_data in
73-
app_data['units'].values()]
74-
return machines
75-
76-
def align_machine_profiles(machine_profiles):
77-
result = {}
78-
for items in machine_profiles:
79-
charm_profile = items[0]
80-
if charm_profile in result:
81-
# drop duplicates using set difference
82-
a = set(result[charm_profile])
83-
b = set(items[1])
84-
result[charm_profile].extend(b.difference(a))
85-
else:
86-
result[charm_profile] = list(items[1])
87-
return result
88-
8976
def parse_args(argv):
9077
parser = argparse.ArgumentParser(description="Test juju lxd profile bundle deploys.")
9178
parser.add_argument(
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#!/usr/bin/env python
2+
3+
""" Assess upgrading charms with lxd-profiles in different deployment scenarios.
4+
"""
5+
6+
import argparse
7+
import logging
8+
import os
9+
import sys
10+
11+
from deploy_stack import (
12+
BootstrapManager,
13+
)
14+
from jujucharm import (
15+
local_charm_path,
16+
)
17+
from utility import (
18+
add_basic_testing_arguments,
19+
configure_logging,
20+
JujuAssertionError,
21+
is_subordinate,
22+
subordinate_machines_from_app_info,
23+
application_machines_from_app_info,
24+
align_machine_profiles,
25+
)
26+
from jujupy.wait_condition import (
27+
WaitForLXDProfilesConditions,
28+
)
29+
30+
__metaclass__ = type
31+
32+
log = logging.getLogger("assess_lxdprofile_charm")
33+
34+
default_bundle = 'bundles-lxd-profile-upgrade.yaml'
35+
36+
def deploy_bundle(client):
37+
"""Deploy the given charm bundle
38+
:param client: Jujupy ModelClient object
39+
"""
40+
bundle = local_charm_path(
41+
charm=default_bundle,
42+
juju_ver=client.version,
43+
repository=os.environ['JUJU_REPOSITORY']
44+
)
45+
_, primary = client.deploy(bundle)
46+
client.wait_for(primary)
47+
48+
def upgrade_charm(client):
49+
client.upgrade_charm("lxd-profile", resvision='1')
50+
51+
def assess_profile_machines(client):
52+
"""Assess the machines
53+
"""
54+
# Ensure we wait for everything to start before checking the profiles,
55+
# that way we can ensure that things have been installed.
56+
client.wait_for_started()
57+
58+
machine_profiles = []
59+
status = client.get_status()
60+
apps = status.get_applications()
61+
for _, info in apps.items():
62+
if 'charm-profile' in info:
63+
charm_profile = info['charm-profile']
64+
if charm_profile:
65+
if is_subordinate(info):
66+
machines = subordinate_machines_from_app_info(info, apps)
67+
else:
68+
machines = application_machines_from_app_info(info)
69+
machine_profiles.append((charm_profile, machines))
70+
if len(machine_profiles) > 0:
71+
aligned_machine_profiles = align_machine_profiles(machine_profiles)
72+
client.wait_for(WaitForLXDProfilesConditions(aligned_machine_profiles))
73+
74+
def parse_args(argv):
75+
parser = argparse.ArgumentParser(description="Test juju lxd profile bundle deploys.")
76+
add_basic_testing_arguments(parser)
77+
return parser.parse_args(argv)
78+
79+
def main(argv=None):
80+
args = parse_args(argv)
81+
configure_logging(args.verbose)
82+
bs_manager = BootstrapManager.from_args(args)
83+
with bs_manager.booted_context(args.upload_tools):
84+
client = bs_manager.client
85+
86+
deploy_bundle(client)
87+
assess_profile_machines(client)
88+
upgrade_charm(client)
89+
assess_profile_machines(client)
90+
91+
if __name__ == '__main__':
92+
sys.exit(main())

acceptancetests/jujupy/client.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1299,10 +1299,12 @@ def wait_for_resource(self, resource_id, service_or_unit, timeout=60):
12991299
'ResourceId: {} Service or Unit: {} Timeout: {}'.format(
13001300
resource_id, service_or_unit, timeout))
13011301

1302-
def upgrade_charm(self, service, charm_path=None):
1302+
def upgrade_charm(self, service, charm_path=None, resvision=None):
13031303
args = (service,)
13041304
if charm_path is not None:
13051305
args = args + ('--path', charm_path)
1306+
if resvision is not None:
1307+
args = args + ('--revision', resvision)
13061308
self.juju('upgrade-charm', args)
13071309

13081310
def remove_service(self, service):
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
series: bionic
2+
applications:
3+
lxd-profile:
4+
charm: cs:~juju-qa/bionic/lxd-profile-without-devices-0
5+
num_units: 4
6+
to:
7+
- "0"
8+
- "1"
9+
- "2"
10+
- "3"
11+
lxd-profile-subordinate:
12+
charm: cs:~juju-qa/bionic/lxd-profile-subordinate-1
13+
ubuntu:
14+
charm: cs:~jameinel/ubuntu-lite
15+
num_units: 4
16+
to:
17+
- "0"
18+
- "1"
19+
- "2"
20+
- "3"
21+
machines:
22+
"0": {}
23+
"1": {}
24+
"2": {}
25+
"3": {}
26+
relations:
27+
- - lxd-profile:juju-info
28+
- lxd-profile-subordinate:juju-info

acceptancetests/utility.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,3 +474,47 @@ def list_models(client):
474474
log.error('Failed to list current models due to error: {}'.format(e))
475475
raise e
476476
return json.loads(raw)
477+
478+
def is_subordinate(app_data):
479+
return (not 'unit' in app_data) and ('subordinate-to' in app_data)
480+
481+
def application_machines_from_app_info(app_data):
482+
"""Get all the machines used to host the given application from the
483+
application info in status.
484+
485+
:param app_data: application info from status
486+
"""
487+
machines = [unit_data['machine'] for unit_data in
488+
app_data['units'].values()]
489+
return machines
490+
491+
def subordinate_machines_from_app_info(app_data, apps):
492+
"""Get the subordinate machines from a given application from the
493+
application info in status.
494+
495+
:param app_data: application info from status
496+
"""
497+
machines = []
498+
for sub_name in app_data['subordinate-to']:
499+
for app_name, prim_app_data in apps.items():
500+
if sub_name == app_name:
501+
machines.extend(application_machines_from_app_info(prim_app_data))
502+
return machines
503+
504+
def align_machine_profiles(machine_profiles):
505+
"""Align machine profiles will create a dict from a list of machine
506+
ensuring that the machines are unique to each charm profile name.
507+
508+
:param machine_profiles: is a list of machine profiles tuple
509+
"""
510+
result = {}
511+
for items in machine_profiles:
512+
charm_profile = items[0]
513+
if charm_profile in result:
514+
# drop duplicates using set difference
515+
a = set(result[charm_profile])
516+
b = set(items[1])
517+
result[charm_profile].extend(b.difference(a))
518+
else:
519+
result[charm_profile] = list(items[1])
520+
return result

agent/agentbootstrap/bootstrap.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,16 @@ func InitializeState(
106106
cloudCredentials := make(map[names.CloudCredentialTag]cloud.Credential)
107107
var cloudCredentialTag names.CloudCredentialTag
108108
if args.ControllerCloudCredential != nil && args.ControllerCloudCredentialName != "" {
109-
cloudCredentialTag = names.NewCloudCredentialTag(fmt.Sprintf(
109+
id := fmt.Sprintf(
110110
"%s/%s/%s",
111111
args.ControllerCloud.Name,
112112
adminUser.Id(),
113113
args.ControllerCloudCredentialName,
114-
))
114+
)
115+
if !names.IsValidCloudCredential(id) {
116+
return nil, nil, errors.NotValidf("cloud credential ID %q", id)
117+
}
118+
cloudCredentialTag = names.NewCloudCredentialTag(id)
115119
cloudCredentials[cloudCredentialTag] = *args.ControllerCloudCredential
116120
}
117121

api/credentialvalidator/credentialvalidator.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ func (c *Facade) ModelCredential() (base.StoredCredential, bool, error) {
5959
// WatchCredential provides a notify watcher that is responsive to changes
6060
// to a given cloud credential.
6161
func (c *Facade) WatchCredential(credentialID string) (watcher.NotifyWatcher, error) {
62+
if !names.IsValidCloudCredential(credentialID) {
63+
return nil, errors.NotValidf("cloud credential ID %q", credentialID)
64+
}
6265
in := names.NewCloudCredentialTag(credentialID).String()
6366
var result params.NotifyWatchResult
6467
err := c.facade.FacadeCall("WatchCredential", params.Entity{in}, &result)

apiserver/facades/client/application/application.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -520,15 +520,6 @@ func deployApplication(
520520
if cons.Pool == "" && *defaultStorageClass == "" {
521521
return errors.Errorf("storage pool for %q must be specified since there's no cluster default storage class", storageName)
522522
}
523-
if cons.Pool != "" {
524-
sp, err := storagePoolManager.Get(cons.Pool)
525-
if err != nil {
526-
return errors.Trace(err)
527-
}
528-
if sp.Provider() != k8s.K8s_ProviderType {
529-
return errors.Errorf("invalid storage provider type %q for %q", sp.Provider(), storageName)
530-
}
531-
}
532523
}
533524
}
534525

apiserver/facades/client/application/application_unit_test.go

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -653,23 +653,6 @@ func (s *ApplicationSuite) TestDeployCAASModelDefaultStorageClass(c *gc.C) {
653653
c.Assert(result.Results[0].Error, gc.IsNil)
654654
}
655655

656-
func (s *ApplicationSuite) TestDeployCAASModelWrongStorageType(c *gc.C) {
657-
application.SetModelType(s.api, state.ModelTypeCAAS)
658-
args := params.ApplicationsDeploy{
659-
Applications: []params.ApplicationDeploy{{
660-
ApplicationName: "foo",
661-
CharmURL: "local:foo-0",
662-
NumUnits: 1,
663-
Storage: map[string]storage.Constraints{
664-
"database": {Pool: "db"},
665-
},
666-
}},
667-
}
668-
result, err := s.api.Deploy(args)
669-
c.Assert(err, jc.ErrorIsNil)
670-
c.Assert(result.OneError(), gc.ErrorMatches, `invalid storage provider type "rootfs" for "database"`)
671-
}
672-
673656
func (s *ApplicationSuite) TestAddUnits(c *gc.C) {
674657
results, err := s.api.AddUnits(params.AddApplicationUnits{
675658
ApplicationName: "postgresql",

apiserver/facades/client/cloud/cloud.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,10 @@ func (api *CloudAPI) UserCredentials(args params.UserClouds) (params.StringsResu
460460
}
461461
out := make([]string, 0, len(cloudCredentials))
462462
for tagId := range cloudCredentials {
463+
if !names.IsValidCloudCredential(tagId) {
464+
results.Results[i].Error = common.ServerError(errors.NotValidf("cloud credential ID %q", tagId))
465+
continue
466+
}
463467
out = append(out, names.NewCloudCredentialTag(tagId).String())
464468
}
465469
results.Results[i].Result = out
@@ -1086,6 +1090,12 @@ func (api *CloudAPI) CredentialContents(args params.CloudCredentialArgs) (params
10861090
result = make([]params.CredentialContentResult, len(args.Credentials))
10871091
for i, given := range args.Credentials {
10881092
id := credId(given.CloudName, given.CredentialName)
1093+
if !names.IsValidCloudCredential(id) {
1094+
result[i] = params.CredentialContentResult{
1095+
Error: common.ServerError(errors.NotValidf("cloud credential ID %q", id)),
1096+
}
1097+
continue
1098+
}
10891099
tag := names.NewCloudCredentialTag(id)
10901100
credential, err := api.backend.CloudCredential(tag)
10911101
if err != nil {

apiserver/facades/controller/caasunitprovisioner/mock_test.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ type mockStorage struct {
259259
storageFilesystems map[names.StorageTag]names.FilesystemTag
260260
storageVolumes map[names.StorageTag]names.VolumeTag
261261
storageAttachments map[names.UnitTag]names.StorageTag
262+
backingVolume names.VolumeTag
262263
}
263264

264265
func (m *mockStorage) StorageInstance(tag names.StorageTag) (state.StorageInstance, error) {
@@ -273,7 +274,7 @@ func (m *mockStorage) AllFilesystems() ([]state.Filesystem, error) {
273274
m.MethodCall(m, "AllFilesystems")
274275
var result []state.Filesystem
275276
for _, fsTag := range m.storageFilesystems {
276-
result = append(result, &mockFilesystem{Stub: &m.Stub, tag: fsTag})
277+
result = append(result, &mockFilesystem{Stub: &m.Stub, tag: fsTag, volTag: m.backingVolume})
277278
}
278279
return result, nil
279280
}
@@ -295,7 +296,7 @@ func (m *mockStorage) DestroyVolume(tag names.VolumeTag) (err error) {
295296

296297
func (m *mockStorage) Filesystem(fsTag names.FilesystemTag) (state.Filesystem, error) {
297298
m.MethodCall(m, "Filesystem", fsTag)
298-
return &mockFilesystem{Stub: &m.Stub, tag: fsTag}, nil
299+
return &mockFilesystem{Stub: &m.Stub, tag: fsTag, volTag: m.backingVolume}, nil
299300
}
300301

301302
func (m *mockStorage) FilesystemAttachment(hostTag names.Tag, fsTag names.FilesystemTag) (state.FilesystemAttachment, error) {
@@ -304,7 +305,7 @@ func (m *mockStorage) FilesystemAttachment(hostTag names.Tag, fsTag names.Filesy
304305
}
305306

306307
func (m *mockStorage) StorageInstanceFilesystem(tag names.StorageTag) (state.Filesystem, error) {
307-
return &mockFilesystem{Stub: &m.Stub, tag: m.storageFilesystems[tag]}, nil
308+
return &mockFilesystem{Stub: &m.Stub, tag: m.storageFilesystems[tag], volTag: m.backingVolume}, nil
308309
}
309310

310311
func (m *mockStorage) UnitStorageAttachments(unit names.UnitTag) ([]state.StorageAttachment, error) {
@@ -400,7 +401,8 @@ func (a *mockStorageAttachment) StorageInstance() names.StorageTag {
400401
type mockFilesystem struct {
401402
*testing.Stub
402403
state.Filesystem
403-
tag names.FilesystemTag
404+
tag names.FilesystemTag
405+
volTag names.VolumeTag
404406
}
405407

406408
func (f *mockFilesystem) Tag() names.Tag {
@@ -412,7 +414,10 @@ func (f *mockFilesystem) FilesystemTag() names.FilesystemTag {
412414
}
413415

414416
func (f *mockFilesystem) Volume() (names.VolumeTag, error) {
415-
return names.NewVolumeTag("66"), nil
417+
if f.volTag.Id() == "" {
418+
return f.volTag, state.ErrNoBackingVolume
419+
}
420+
return f.volTag, nil
416421
}
417422

418423
func (f *mockFilesystem) Params() (state.FilesystemParams, bool) {

0 commit comments

Comments
 (0)