36
38
"github.com/juju/juju/environs"
37
39
"github.com/juju/juju/environs/bootstrap"
38
40
"github.com/juju/juju/environs/config"
39
"github.com/juju/juju/environs/configstore"
40
41
"github.com/juju/juju/environs/filestorage"
41
42
"github.com/juju/juju/environs/imagemetadata"
42
43
imagetesting "github.com/juju/juju/environs/imagemetadata/testing"
53
54
"github.com/juju/juju/network"
54
55
"github.com/juju/juju/provider/common"
55
56
"github.com/juju/juju/provider/openstack"
57
"github.com/juju/juju/status"
56
58
"github.com/juju/juju/storage/provider/registry"
57
59
coretesting "github.com/juju/juju/testing"
58
"github.com/juju/juju/version"
60
jujuversion "github.com/juju/juju/version"
61
63
type ProviderSuite struct {
180
182
// Set credentials to use when bootstrapping. Must be done after
181
183
// starting server to get the auth URL.
182
args := prepareForBootstrapParams(nil, s.cred)
183
s.CloudRegion = args.CloudRegion
184
s.CloudEndpoint = args.CloudEndpoint
185
s.Credential = args.Credentials
184
s.Credential = makeCredential(s.cred)
185
s.CloudEndpoint = s.cred.URL
186
s.CloudRegion = s.cred.Region
187
188
s.LiveTests.SetUpSuite(c)
188
189
openstack.UseTestImageData(openstack.ImageMetadataStorage(s.Env), s.cred)
238
239
// Set credentials to use when bootstrapping. Must be done after
239
240
// starting server to get the auth URL.
240
args := prepareForBootstrapParams(nil, s.cred)
241
s.CloudRegion = args.CloudRegion
242
s.CloudEndpoint = args.CloudEndpoint
243
s.Credential = args.Credentials
241
s.Credential = makeCredential(s.cred)
242
s.CloudEndpoint = s.cred.URL
243
s.CloudRegion = s.cred.Region
245
245
cl := client.NewClient(s.cred, identity.AuthUserPass, nil)
246
246
err := cl.Authenticate()
252
252
"image-metadata-url": containerURL + "/juju-dist-test",
253
253
"auth-url": s.cred.URL,
255
s.PatchValue(&version.Current, coretesting.FakeVersionNumber)
255
s.PatchValue(&jujuversion.Current, coretesting.FakeVersionNumber)
256
256
s.Tests.SetUpTest(c)
257
257
// For testing, we create a storage instance to which is uploaded tools and image metadata.
258
258
s.env = s.Prepare(c)
280
280
func (s *localServerSuite) TestBootstrap(c *gc.C) {
281
281
// Tests uses Prepare, so destroy first.
282
err := environs.Destroy(s.env.Config().Name(), s.env, s.ConfigStore, s.ControllerStore)
282
err := environs.Destroy(s.env.Config().Name(), s.env, s.ControllerStore)
283
283
c.Assert(err, jc.ErrorIsNil)
284
284
s.Tests.TestBootstrap(c)
287
287
func (s *localServerSuite) TestStartStop(c *gc.C) {
288
288
// Tests uses Prepare, so destroy first.
289
err := environs.Destroy(s.env.Config().Name(), s.env, s.ConfigStore, s.ControllerStore)
289
err := environs.Destroy(s.env.Config().Name(), s.env, s.ControllerStore)
290
290
c.Assert(err, jc.ErrorIsNil)
291
291
s.Tests.TestStartStop(c)
307
err := environs.Destroy(s.env.Config().Name(), s.env, s.ConfigStore, s.ControllerStore)
307
err := environs.Destroy(s.env.Config().Name(), s.env, s.ControllerStore)
308
308
c.Assert(err, jc.ErrorIsNil)
310
310
// Create a config that matches s.TestConfig but with use-floating-ip set to true
407
err := environs.Destroy(s.env.Config().Name(), s.env, s.ConfigStore, s.ControllerStore)
407
err := environs.Destroy(s.env.Config().Name(), s.env, s.ControllerStore)
408
408
c.Assert(err, jc.ErrorIsNil)
410
cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{
411
"use-floating-ip": false,
413
c.Assert(err, jc.ErrorIsNil)
410
attrs := s.TestConfig.Merge(coretesting.Attrs{"use-floating-ip": false})
414
411
env, err := environs.Prepare(
415
envtesting.BootstrapContext(c), s.ConfigStore,
412
envtesting.BootstrapContext(c),
416
413
s.ControllerStore,
417
cfg.Name(), prepareForBootstrapParams(cfg, s.cred),
414
prepareParams(attrs, s.cred),
419
416
c.Assert(err, jc.ErrorIsNil)
420
417
err = bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{})
427
424
func (s *localServerSuite) TestStartInstanceHardwareCharacteristics(c *gc.C) {
428
425
// Ensure amd64 tools are available, to ensure an amd64 image.
429
426
amd64Version := version.Binary{
430
Number: version.Current,
427
Number: jujuversion.Current,
431
428
Arch: arch.AMD64,
433
430
for _, series := range series.SupportedSeries() {
436
433
c, s.toolsMetadataStorage, s.env.Config().AgentStream(), s.env.Config().AgentStream(), amd64Version)
439
err := environs.Destroy(s.env.Config().Name(), s.env, s.ConfigStore, s.ControllerStore)
436
err := environs.Destroy(s.env.Config().Name(), s.env, s.ControllerStore)
440
437
c.Assert(err, jc.ErrorIsNil)
442
439
env := s.Prepare(c)
532
529
// Openstack now has three security groups for the server, the default
533
530
// group, one group for the entire environment, and another for the
535
eUUID, _ := env.Config().UUID()
532
eUUID := env.Config().UUID()
536
533
assertSecurityGroups(c, env, []string{"default", fmt.Sprintf("juju-%v", eUUID), fmt.Sprintf("juju-%v-%v", eUUID, instanceName)})
537
534
err = env.StopInstances(inst.Id())
538
535
c.Assert(err, jc.ErrorIsNil)
562
559
c.Assert(err, jc.ErrorIsNil)
563
560
instanceName := "100"
564
561
inst, _ := testing.AssertStartInstance(c, env, instanceName)
565
eUUID, _ := env.Config().UUID()
562
eUUID := env.Config().UUID()
566
563
allSecurityGroups := []string{"default", fmt.Sprintf("juju-%v", eUUID), fmt.Sprintf("juju-%v-%v", eUUID, instanceName)}
567
564
assertSecurityGroups(c, env, allSecurityGroups)
568
565
err = env.StopInstances(inst.Id())
578
575
c.Assert(err, jc.ErrorIsNil)
579
576
instanceName := "100"
580
577
testing.AssertStartInstance(c, env, instanceName)
581
eUUID, _ := env.Config().UUID()
578
eUUID := env.Config().UUID()
582
579
allSecurityGroups := []string{"default", fmt.Sprintf("juju-%v", eUUID), fmt.Sprintf("juju-%v-%v", eUUID, instanceName)}
583
580
assertSecurityGroups(c, env, allSecurityGroups)
584
581
err = env.Destroy()
594
591
c.Assert(err, jc.ErrorIsNil)
595
592
instanceName := "100"
596
593
testing.AssertStartInstance(c, env, instanceName)
597
eUUID, _ := env.Config().UUID()
594
eUUID := env.Config().UUID()
598
595
allSecurityGroups := []string{"default", fmt.Sprintf("juju-%v", eUUID), fmt.Sprintf("juju-%v-global", eUUID)}
599
596
assertSecurityGroups(c, env, allSecurityGroups)
600
597
err = env.Destroy()
652
649
func (s *localServerSuite) TestInstanceStatus(c *gc.C) {
653
650
// goose's test service always returns ACTIVE state.
654
651
inst, _ := testing.AssertStartInstance(c, s.env, "100")
655
c.Assert(inst.Status(), gc.Equals, nova.StatusActive)
652
c.Assert(inst.Status().Status, gc.Equals, status.StatusRunning)
656
653
err := s.env.StopInstances(inst.Id())
657
654
c.Assert(err, jc.ErrorIsNil)
763
760
c.Assert(err, jc.ErrorIsNil)
764
761
c.Assert(instances, gc.HasLen, 1)
765
c.Assert(instances[0].Status(), gc.Equals, nova.StatusBuildSpawning)
762
c.Assert(instances[0].Status().Message, gc.Equals, nova.StatusBuildSpawning)
768
765
func (s *localServerSuite) TestInstancesShutoffSuspended(c *gc.C) {
796
793
c.Assert(err, jc.ErrorIsNil)
797
794
c.Assert(instances, gc.HasLen, 2)
798
c.Assert(instances[0].Status(), gc.Equals, nova.StatusShutoff)
799
c.Assert(instances[1].Status(), gc.Equals, nova.StatusSuspended)
795
c.Assert(instances[0].Status().Message, gc.Equals, nova.StatusShutoff)
796
c.Assert(instances[1].Status().Message, gc.Equals, nova.StatusSuspended)
802
799
func (s *localServerSuite) TestInstancesErrorResponse(c *gc.C) {
895
892
s.PatchValue(openstack.MakeServiceURL, func(client.AuthenticatingClient, string, []string) (string, error) {
896
893
return "", errors.New("cannae do it captain")
895
env := s.Open(c, s.env.Config())
899
896
sources, err := environs.ImageMetadataSources(env)
900
897
c.Assert(err, jc.ErrorIsNil)
901
898
c.Assert(sources, gc.HasLen, 3)
909
906
func (s *localServerSuite) TestGetToolsMetadataSources(c *gc.C) {
910
907
s.PatchValue(&tools.DefaultBaseURL, "")
909
env := s.Open(c, s.env.Config())
913
910
sources, err := tools.GetMetadataSources(env)
914
911
c.Assert(err, jc.ErrorIsNil)
915
912
c.Assert(sources, gc.HasLen, 2)
929
926
func (s *localServerSuite) TestSupportedArchitectures(c *gc.C) {
927
env := s.Open(c, s.env.Config())
931
928
a, err := env.SupportedArchitectures()
932
929
c.Assert(err, jc.ErrorIsNil)
933
930
c.Assert(a, jc.SameContents, []string{"amd64", "i386", "ppc64el"})
936
933
func (s *localServerSuite) TestSupportsNetworking(c *gc.C) {
934
env := s.Open(c, s.env.Config())
938
935
_, ok := environs.SupportsNetworking(env)
939
936
c.Assert(ok, jc.IsFalse)
942
939
func (s *localServerSuite) TestFindImageBadDefaultImage(c *gc.C) {
943
940
imagetesting.PatchOfficialDataSources(&s.CleanupSuite, "")
941
env := s.Open(c, s.env.Config())
946
943
// An error occurs if no suitable image is found.
947
944
_, err := openstack.FindInstanceSpec(env, "saucy", "amd64", "mem=1G", nil)
951
948
func (s *localServerSuite) TestConstraintsValidator(c *gc.C) {
949
env := s.Open(c, s.env.Config())
953
950
validator, err := env.ConstraintsValidator()
954
951
c.Assert(err, jc.ErrorIsNil)
955
cons := constraints.MustParse("arch=amd64 cpu-power=10")
952
cons := constraints.MustParse("arch=amd64 cpu-power=10 virt-type=lxd")
956
953
unsupported, err := validator.Validate(cons)
957
954
c.Assert(err, jc.ErrorIsNil)
958
955
c.Assert(unsupported, jc.SameContents, []string{"cpu-power"})
961
958
func (s *localServerSuite) TestConstraintsValidatorVocab(c *gc.C) {
959
env := s.Open(c, s.env.Config())
963
960
validator, err := env.ConstraintsValidator()
964
961
c.Assert(err, jc.ErrorIsNil)
965
962
cons := constraints.MustParse("arch=arm64")
968
965
cons = constraints.MustParse("instance-type=foo")
969
966
_, err = validator.Validate(cons)
970
967
c.Assert(err, gc.ErrorMatches, "invalid constraint value: instance-type=foo\nvalid values are:.*")
969
cons = constraints.MustParse("virt-type=foo")
970
_, err = validator.Validate(cons)
971
c.Assert(err, gc.ErrorMatches, regexp.QuoteMeta("invalid constraint value: virt-type=foo\nvalid values are: [kvm lxd]"))
973
974
func (s *localServerSuite) TestConstraintsMerge(c *gc.C) {
975
env := s.Open(c, s.env.Config())
975
976
validator, err := env.ConstraintsValidator()
976
977
c.Assert(err, jc.ErrorIsNil)
977
978
consA := constraints.MustParse("arch=amd64 mem=1G root-disk=10G")
996
997
c.Assert(spec.InstanceType.Name, gc.Equals, "m1.tiny")
1000
func (s *localServerSuite) TestFindInstanceImageConstraintHypervisor(c *gc.C) {
1001
testVirtType := "qemu"
1002
env := s.Open(c, s.env.Config())
1003
imageMetadata := []*imagemetadata.ImageMetadata{{
1006
VirtType: testVirtType,
1009
spec, err := openstack.FindInstanceSpec(
1010
env, coretesting.FakeDefaultSeries, "amd64", "virt-type="+testVirtType,
1013
c.Assert(err, jc.ErrorIsNil)
1014
c.Assert(spec.InstanceType.VirtType, gc.NotNil)
1015
c.Assert(*spec.InstanceType.VirtType, gc.Equals, testVirtType)
1016
c.Assert(spec.InstanceType.Name, gc.Equals, "m1.small")
1019
func (s *localServerSuite) TestFindInstanceImageWithHypervisorNoConstraint(c *gc.C) {
1020
testVirtType := "qemu"
1021
env := s.Open(c, s.env.Config())
1022
imageMetadata := []*imagemetadata.ImageMetadata{{
1025
VirtType: testVirtType,
1028
spec, err := openstack.FindInstanceSpec(
1029
env, coretesting.FakeDefaultSeries, "amd64", "",
1032
c.Assert(err, jc.ErrorIsNil)
1033
c.Assert(spec.InstanceType.VirtType, gc.NotNil)
1034
c.Assert(*spec.InstanceType.VirtType, gc.Equals, testVirtType)
1035
c.Assert(spec.InstanceType.Name, gc.Equals, "m1.small")
1038
func (s *localServerSuite) TestFindInstanceNoConstraint(c *gc.C) {
1039
env := s.Open(c, s.env.Config())
1040
imageMetadata := []*imagemetadata.ImageMetadata{{
1045
spec, err := openstack.FindInstanceSpec(
1046
env, coretesting.FakeDefaultSeries, "amd64", "",
1049
c.Assert(err, jc.ErrorIsNil)
1050
c.Assert(spec.InstanceType.VirtType, gc.IsNil)
1051
c.Assert(spec.InstanceType.Name, gc.Equals, "m1.small")
999
1054
func (s *localServerSuite) TestFindImageInvalidInstanceConstraint(c *gc.C) {
1055
env := s.Open(c, s.env.Config())
1001
1056
imageMetadata := []*imagemetadata.ImageMetadata{{
1002
1057
Id: "image-id",
1053
1108
func (s *localServerSuite) TestValidateImageMetadata(c *gc.C) {
1109
env := s.Open(c, s.env.Config())
1055
1110
params, err := env.(simplestreams.MetadataValidator).MetadataLookupParams("some-region")
1056
1111
c.Assert(err, jc.ErrorIsNil)
1057
1112
params.Sources, err = environs.ImageMetadataSources(env)
1067
1122
return simplestreams.NewURLDataSource("my datasource", "bar", false, simplestreams.CUSTOM_CLOUD_DATA, false), nil
1069
1124
environs.RegisterUserImageDataSourceFunc("my func", src)
1125
env := s.Open(c, s.env.Config())
1071
1126
sources, err := environs.ImageMetadataSources(env)
1072
1127
c.Assert(err, jc.ErrorIsNil)
1073
1128
var sourceIds []string
1172
1227
attrs := s.createConfigAttrs(c)
1173
1228
c.Assert(attrs["auth-url"].(string)[:8], gc.Equals, "https://")
1174
cfg, err := config.New(config.NoDefaults, attrs)
1175
c.Assert(err, jc.ErrorIsNil)
1176
1230
s.env, err = environs.Prepare(
1177
envtesting.BootstrapContext(c), configstore.NewMem(),
1231
envtesting.BootstrapContext(c),
1178
1232
jujuclienttesting.NewMemStore(),
1179
cfg.Name(), prepareForBootstrapParams(cfg, s.cred),
1233
prepareParams(attrs, s.cred),
1181
1235
c.Assert(err, jc.ErrorIsNil)
1182
1236
s.attrs = s.env.Config().AllAttrs()
1717
1771
assertMetadata(extraKey, extraValue)
1720
func prepareForBootstrapParams(cfg *config.Config, cred *identity.Credentials) environs.PrepareForBootstrapParams {
1721
return environs.PrepareForBootstrapParams{
1723
Credentials: cloud.NewCredential(
1724
cloud.UserPassAuthType,
1726
"username": cred.User,
1727
"password": cred.Secrets,
1728
"tenant-name": cred.TenantName,
1731
CloudEndpoint: cred.URL,
1732
CloudRegion: cred.Region,
1774
func prepareParams(attrs map[string]interface{}, cred *identity.Credentials) environs.PrepareParams {
1775
return environs.PrepareParams{
1777
ControllerName: attrs["name"].(string),
1778
Credential: makeCredential(cred),
1779
CloudName: "openstack",
1780
CloudEndpoint: cred.URL,
1781
CloudRegion: cred.Region,
1785
func makeCredential(cred *identity.Credentials) cloud.Credential {
1786
return cloud.NewCredential(
1787
cloud.UserPassAuthType,
1789
"username": cred.User,
1790
"password": cred.Secrets,
1791
"tenant-name": cred.TenantName,
1736
1796
// noSwiftSuite contains tests that run against an OpenStack service double
1737
1797
// that lacks Swift.
1738
1798
type noSwiftSuite struct {
1768
1828
"agent-version": coretesting.FakeVersionNumber.String(),
1769
1829
"authorized-keys": "fakekey",
1771
s.PatchValue(&version.Current, coretesting.FakeVersionNumber)
1831
s.PatchValue(&jujuversion.Current, coretesting.FakeVersionNumber)
1772
1832
// Serve fake tools and image metadata using "filestorage",
1773
1833
// rather than Swift as the rest of the tests do.
1774
1834
storageDir := c.MkDir()
1786
1846
openstack.UseTestImageData(imageStorage, s.cred)
1787
1847
imagetesting.PatchOfficialDataSources(&s.CleanupSuite, storageDir)
1789
cfg, err := config.New(config.NoDefaults, attrs)
1790
c.Assert(err, jc.ErrorIsNil)
1791
configStore := configstore.NewMem()
1792
1849
env, err := environs.Prepare(
1793
envtesting.BootstrapContext(c), configStore,
1850
envtesting.BootstrapContext(c),
1794
1851
jujuclienttesting.NewMemStore(),
1795
cfg.Name(), prepareForBootstrapParams(cfg, s.cred),
1852
prepareParams(attrs, s.cred),
1797
1854
c.Assert(err, jc.ErrorIsNil)
1817
1874
func newNovaOnlyOpenstackService(mux *http.ServeMux, cred *identity.Credentials, auth identity.AuthMode) *novaservice.Nova {
1818
var identityService identityservice.IdentityService
1875
var identityService, fallbackService identityservice.IdentityService
1819
1876
if auth == identity.AuthKeyPair {
1820
1877
identityService = identityservice.NewKeyPair()
1822
1879
identityService = identityservice.NewUserPass()
1880
fallbackService = identityservice.NewV3UserPass()
1824
1882
userInfo := identityService.AddUser(cred.User, cred.Secrets, cred.TenantName)
1825
1883
if cred.TenantName == "" {
1826
1884
panic("Openstack service double requires a tenant to be specified.")
1828
novaService := novaservice.New(cred.URL, "v2", userInfo.TenantId, cred.Region, identityService)
1886
novaService := novaservice.New(cred.URL, "v2", userInfo.TenantId, cred.Region, identityService, fallbackService)
1829
1887
identityService.SetupHTTP(mux)
1830
1888
novaService.SetupHTTP(mux)
1831
1889
return novaService