bosh-bootstrap 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -42,6 +42,19 @@ class Bosh::Providers::AWS < Bosh::Providers::BaseProvider
42
42
  aws_compute_flavors.map { |fl| fl[:id] }
43
43
  end
44
44
 
45
+ # @return [String] provisions a new public IP address in target region
46
+ # TODO nil if none available
47
+ def provision_public_ip_address
48
+ address = fog_compute.addresses.create
49
+ address.public_ip
50
+ # TODO catch error and return nil
51
+ end
52
+
53
+ def associate_ip_address_with_server(ip_address, server)
54
+ address = fog_compute.addresses.get(ip_address)
55
+ address.server = server
56
+ end
57
+
45
58
  # Creates or reuses an AWS security group and opens ports.
46
59
  #
47
60
  # +security_group_name+ is the name to be created or reused
@@ -74,4 +87,26 @@ class Bosh::Providers::AWS < Bosh::Providers::BaseProvider
74
87
  def port_open?(ip_permissions, port)
75
88
  ip_permissions && ip_permissions.find {|ip| ip["fromPort"] <= port && ip["toPort"] >= port }
76
89
  end
90
+
91
+ def find_server_device(server, device)
92
+ server.volumes.all.find {|v| v.device == device}
93
+ end
94
+
95
+ def create_and_attach_volume(name, disk_size, server, device)
96
+ volume = fog_compute.volumes.create(
97
+ size: disk_size,
98
+ name: name,
99
+ description: '',
100
+ device: device,
101
+ availability_zone: server.availability_zone)
102
+ # TODO: the following works in fog 1.9.0+ (but which has a bug in bootstrap)
103
+ # https://github.com/fog/fog/issues/1516
104
+ #
105
+ # volume.wait_for { volume.status == 'available' }
106
+ # volume.attach(server.id, "/dev/vdc")
107
+ # volume.wait_for { volume.status == 'in-use' }
108
+ #
109
+ # Instead, using:
110
+ volume.server = server
111
+ end
77
112
  end
@@ -9,12 +9,7 @@ class Bosh::Providers::BaseProvider
9
9
  @fog_compute = fog_compute
10
10
  end
11
11
 
12
- # @return [String] provisions a new public IP address in target region
13
- # TODO nil if none available
14
- def provision_public_ip_address
15
- address = fog_compute.addresses.create
16
- address.public_ip
17
- # TODO catch error and return nil
12
+ def create_key_pair(key_pair_name)
13
+ fog_compute.key_pairs.create(:name => key_pair_name)
18
14
  end
19
-
20
15
  end
@@ -5,6 +5,19 @@ module Bosh; module Providers; end; end
5
5
  require "bosh/providers/base_provider"
6
6
 
7
7
  class Bosh::Providers::OpenStack < Bosh::Providers::BaseProvider
8
+ # @return [String] provisions a new public IP address in target region
9
+ # TODO nil if none available
10
+ def provision_public_ip_address
11
+ address = fog_compute.addresses.create
12
+ address.ip
13
+ # TODO catch error and return nil
14
+ end
15
+
16
+ def associate_ip_address_with_server(ip_address, server)
17
+ address = fog_compute.addresses.find { |a| a.ip == ip_address }
18
+ address.server = server
19
+ end
20
+
8
21
  # Creates or reuses an OpenStack security group and opens ports.
9
22
  #
10
23
  # +security_group_name+ is the name to be created or reused
@@ -15,13 +28,14 @@ class Bosh::Providers::OpenStack < Bosh::Providers::BaseProvider
15
28
  # https: 443
16
29
  # }
17
30
  def create_security_group(security_group_name, description, ports)
18
- unless sg = fog_compute.security_groups.get(security_group_name)
31
+ security_groups = fog_compute.security_groups
32
+ unless sg = security_groups.find { |s| s.name == security_group_name }
19
33
  sg = fog_compute.security_groups.create(name: security_group_name, description: description)
20
34
  puts "Created security group #{security_group_name}"
21
35
  else
22
36
  puts "Reusing security group #{security_group_name}"
23
37
  end
24
- ip_permissions = sg.ip_permissions
38
+ ip_permissions = sg.rules
25
39
  ports_opened = 0
26
40
  ports.each do |name, port|
27
41
  unless port_open?(ip_permissions, port)
@@ -35,6 +49,21 @@ class Bosh::Providers::OpenStack < Bosh::Providers::BaseProvider
35
49
  end
36
50
 
37
51
  def port_open?(ip_permissions, port)
38
- ip_permissions && ip_permissions.find {|ip| ip["fromPort"] <= port && ip["toPort"] >= port }
52
+ ip_permissions && ip_permissions.find {|ip| ip["from_port"] <= port && ip["to_port"] >= port }
53
+ end
54
+
55
+ def find_server_device(server, device)
56
+ va = fog_compute.get_server_volumes(server.id).body['volumeAttachments']
57
+ va.find { |v| v["device"] == device }
58
+ end
59
+
60
+ def create_and_attach_volume(name, disk_size, server, device)
61
+ volume = fog_compute.volumes.create(:name => name,
62
+ :description => "",
63
+ :size => disk_size,
64
+ :availability_zone => server.availability_zone)
65
+ volume.wait_for { volume.status == 'available' }
66
+ volume.attach(server.id, device)
67
+ volume.wait_for { volume.status == 'in-use' }
39
68
  end
40
69
  end
@@ -79,6 +79,13 @@ module Bosh::Bootstrap
79
79
  else
80
80
  confirm "No specific region/data center for #{settings.fog_credentials.provider}"
81
81
  end
82
+
83
+ unless settings["network_label"]
84
+ choose_provider_network_label
85
+ end
86
+ if settings["network_label"]
87
+ confirm "Using #{settings.fog_credentials.provider} network labelled #{settings['network_label']}"
88
+ end
82
89
  end
83
90
 
84
91
  def deploy_stage_2_bosh_configuration
@@ -101,6 +108,12 @@ module Bosh::Bootstrap
101
108
  end
102
109
  confirm "After BOSH is created, your username will be #{settings.bosh_username}"
103
110
 
111
+ unless settings[:bosh_resources_cloud_properties]
112
+ settings[:bosh_resources_cloud_properties] = bosh_resources_cloud_properties
113
+ save_settings!
114
+ end
115
+ confirm "Micro BOSH instance type will be #{settings[:bosh_resources_cloud_properties]["instance_type"]}"
116
+
104
117
  unless settings[:bosh]
105
118
  say "Defaulting to 16Gb persistent disk for BOSH"
106
119
  password = settings.bosh_password # FIXME dual use of password?
@@ -109,6 +122,7 @@ module Bosh::Bootstrap
109
122
  settings[:bosh][:persistent_disk] = 16384
110
123
  save_settings!
111
124
  end
125
+
112
126
  unless settings[:bosh]["ip_address"]
113
127
  say "Acquiring IP address for micro BOSH..."
114
128
  ip_address = acquire_ip_address
@@ -409,7 +423,8 @@ module Bosh::Bootstrap
409
423
  "openstack_username" => profile[:openstack_username],
410
424
  "openstack_api_key" => profile[:openstack_api_key],
411
425
  "openstack_tenant" => profile[:openstack_tenant],
412
- "openstack_auth_url" => profile[:openstack_auth_url]
426
+ "openstack_auth_url" => profile[:openstack_auth_url],
427
+ "openstack_region" => profile[:openstack_region]
413
428
  }
414
429
  end
415
430
  end
@@ -431,7 +446,6 @@ module Bosh::Bootstrap
431
446
  settings[:fog_credentials][key] = value
432
447
  end
433
448
  setup_bosh_cloud_properties
434
- settings[:bosh_resources_cloud_properties] = bosh_resources_cloud_properties
435
449
  settings[:bosh_provider] = settings.bosh_cloud_properties.keys.first # aws, vsphere...
436
450
  save_settings!
437
451
  end
@@ -502,13 +516,24 @@ module Bosh::Bootstrap
502
516
  if aws?
503
517
  {"instance_type" => "m1.medium"}
504
518
  elsif openstack?
505
- # TODO: Ask for instance type
506
- {"instance_type" => "m1.medium"}
519
+ {"instance_type" => choose_bosh_openstack_flavor}
507
520
  else
508
521
  raise "implement #bosh_resources_cloud_properties for #{settings.fog_credentials.provider}"
509
522
  end
510
523
  end
511
524
 
525
+ def choose_bosh_openstack_flavor
526
+ say ""
527
+ hl.choose do |menu|
528
+ menu.prompt = "Choose Micro BOSH instance type: "
529
+ fog_compute.flavors.each do |flavor|
530
+ menu.choice(flavor.name) do
531
+ return flavor.name
532
+ end
533
+ end
534
+ end
535
+ end
536
+
512
537
  # Ask user to provide region information (URI)
513
538
  # or choose from a known list of regions (e.g. AWS)
514
539
  # Return true if region selected (@region_code is set)
@@ -517,8 +542,8 @@ module Bosh::Bootstrap
517
542
  if aws?
518
543
  choose_aws_region
519
544
  else
520
- settings["region_code"] = nil
521
- false
545
+ return false if settings.has_key?("region_code")
546
+ prompt_openstack_region
522
547
  end
523
548
  end
524
549
 
@@ -543,6 +568,29 @@ module Bosh::Bootstrap
543
568
  true
544
569
  end
545
570
 
571
+ def prompt_openstack_region
572
+ default_region = settings.fog_credentials.openstack_region
573
+ region = hl.ask("OpenStack Region (optional): ") { |q| q.default = default_region }
574
+ settings[:region_code] = region.strip == "" ? nil : region
575
+ return false unless settings[:region_code]
576
+
577
+ settings["fog_credentials"]["openstack_region"] = settings[:region_code]
578
+ settings["bosh_cloud_properties"]["openstack"]["region"] = settings[:region_code]
579
+ save_settings!
580
+ reset_fog_compute
581
+ true
582
+ end
583
+
584
+ def choose_provider_network_label
585
+ if openstack?
586
+ prompt_openstack_network_label
587
+ end
588
+ end
589
+
590
+ def prompt_openstack_network_label
591
+ settings[:network_label] = hl.ask("OpenStack private network label: ") { |q| q.default = "private" }
592
+ end
593
+
546
594
  # Creates a security group.
547
595
  # Also sets up the bosh_cloud_properties for the remote server
548
596
  #
@@ -574,19 +622,7 @@ module Bosh::Bootstrap
574
622
  save_settings!
575
623
  end
576
624
 
577
- # Creates a key pair.
578
- def create_key_pair(key_pair_name)
579
- if aws?
580
- create_aws_key_pair(key_pair_name)
581
- elsif openstack?
582
- create_openstack_key_pair(key_pair_name)
583
- else
584
- raise "implement #create_key_pair for #{settings.fog_credentials.provider}"
585
- end
586
- end
587
-
588
- # Creates an AWS key pair, and stores the private key
589
- # in settings manifest.
625
+ # Creates a key pair, and stores the private key in settings manifest.
590
626
  # Also sets up the bosh_cloud_properties for the remote server
591
627
  # to have the .pem key installed.
592
628
  #
@@ -594,48 +630,30 @@ module Bosh::Bootstrap
594
630
  # * bosh_key_pair.name
595
631
  # * bosh_key_pair.private_key
596
632
  # * bosh_key_pair.fingerprint
633
+ # For AWS:
597
634
  # * bosh_cloud_properties.aws.default_key_name
598
635
  # * bosh_cloud_properties.aws.ec2_private_key
599
- def create_aws_key_pair(key_pair_name)
600
- unless fog_compute.key_pairs.get(key_pair_name)
601
- say "creating key pair #{key_pair_name}..."
602
- kp = fog_compute.key_pairs.create(:name => key_pair_name)
603
- settings[:bosh_key_pair] = {}
604
- settings[:bosh_key_pair][:name] = key_pair_name
605
- settings[:bosh_key_pair][:private_key] = kp.private_key
606
- settings[:bosh_key_pair][:fingerprint] = kp.fingerprint
607
- settings["bosh_cloud_properties"]["aws"]["default_key_name"] = key_pair_name
608
- settings["bosh_cloud_properties"]["aws"]["ec2_private_key"] = "/home/vcap/.ssh/#{key_pair_name}.pem"
609
- save_settings!
610
- else
611
- error "AWS key pair '#{key_pair_name}' already exists. Rename BOSH or delete old key pair manually and re-run CLI."
612
- end
613
- end
614
-
615
- # Creates an OpenStack key pair, and stores the private key
616
- # in settings manifest.
617
- # Also sets up the bosh_cloud_properties for the remote server
618
- # to have the .pem key installed.
619
- #
620
- # Adds settings:
621
- # * bosh_key_pair.name
622
- # * bosh_key_pair.private_key
623
- # * bosh_key_pair.fingerprint
636
+ # For OpenStack:
624
637
  # * bosh_cloud_properties.openstack.default_key_name
625
- # * bosh_cloud_properties.openstack.ec2_private_key
626
- def create_openstack_key_pair(key_pair_name)
638
+ # * bosh_cloud_properties.openstack.private_key
639
+ def create_key_pair(key_pair_name)
627
640
  unless fog_compute.key_pairs.get(key_pair_name)
628
641
  say "creating key pair #{key_pair_name}..."
629
- kp = fog_compute.key_pairs.create(:name => key_pair_name)
642
+ kp = provider.create_key_pair(key_pair_name)
630
643
  settings[:bosh_key_pair] = {}
631
644
  settings[:bosh_key_pair][:name] = key_pair_name
632
645
  settings[:bosh_key_pair][:private_key] = kp.private_key
633
646
  settings[:bosh_key_pair][:fingerprint] = kp.fingerprint
634
- settings["bosh_cloud_properties"]["openstack"]["default_key_name"] = key_pair_name
635
- settings["bosh_cloud_properties"]["openstack"]["private_key"] = "/home/vcap/.ssh/#{key_pair_name}.pem"
647
+ if aws?
648
+ settings["bosh_cloud_properties"]["aws"]["default_key_name"] = key_pair_name
649
+ settings["bosh_cloud_properties"]["aws"]["ec2_private_key"] = "/home/vcap/.ssh/#{key_pair_name}.pem"
650
+ elsif openstack?
651
+ settings["bosh_cloud_properties"]["openstack"]["default_key_name"] = key_pair_name
652
+ settings["bosh_cloud_properties"]["openstack"]["private_key"] = "/home/vcap/.ssh/#{key_pair_name}.pem"
653
+ end
636
654
  save_settings!
637
655
  else
638
- error "OpenStack key pair '#{key_pair_name}' already exists. Rename BOSH or delete old key pair manually and re-run CLI."
656
+ error "Key pair '#{key_pair_name}' already exists. Rename BOSH or delete old key pair manually and re-run CLI."
639
657
  end
640
658
  end
641
659
 
@@ -733,51 +751,52 @@ module Bosh::Bootstrap
733
751
  end
734
752
  confirm "Using key pair #{key_pair.name} for Inception VM"
735
753
 
736
- # make sure port 22 is open in the default security group
737
- security_group = fog_compute.security_groups.find { |sg| sg.name == 'default' }
738
- authorized = security_group.rules.detect do |ip_permission|
739
- ip_permission['ip_range'].first && ip_permission['ip_range']['cidr'] == '0.0.0.0/0' &&
740
- ip_permission['from_port'] == 22 &&
741
- ip_permission['ip_protocol'] == 'tcp' &&
742
- ip_permission['to_port'] == 22
743
- end
744
- unless authorized
745
- security_group.create_security_group_rule(22, 22)
746
- end
747
- confirm "Inception VM port 22 open"
748
-
749
754
  unless settings["inception"] && settings["inception"]["server_id"]
750
755
  username = "ubuntu"
751
756
  say "Provisioning server for inception VM..."
752
- settings["inception"] = {}
757
+ settings["inception"] ||= {}
753
758
 
754
759
  # Select OpenStack flavor
760
+ if settings["inception"]["flavor_id"]
761
+ inception_flavor = fog_compute.flavors.find { |f| f.id == settings["inception"]["flavor_id"] }
762
+ settings["inception"]["flavor_id"] = nil if inception_flavor.nil?
763
+ end
755
764
  unless settings["inception"]["flavor_id"]
756
765
  say ""
757
766
  hl.choose do |menu|
758
767
  menu.prompt = "Choose OpenStack flavor: "
759
768
  fog_compute.flavors.each do |flavor|
760
769
  menu.choice(flavor.name) do
761
- settings["inception"]["flavor_id"] = flavor.id
770
+ inception_flavor = flavor
771
+ settings["inception"]["flavor_id"] = inception_flavor.id
762
772
  save_settings!
763
773
  end
764
774
  end
765
775
  end
766
776
  end
777
+ say ""
778
+ confirm "Using flavor #{inception_flavor.name} for Inception VM"
767
779
 
768
780
  # Select OpenStack image
781
+ if settings["inception"]["image_id"]
782
+ inception_image = fog_compute.images.find { |i| i.id == settings["inception"]["image_id"] }
783
+ settings["inception"]["image_id"] = nil if inception_image.nil?
784
+ end
769
785
  unless settings["inception"]["image_id"]
770
786
  say ""
771
787
  hl.choose do |menu|
772
788
  menu.prompt = "Choose OpenStack image (Ubuntu): "
773
789
  fog_compute.images.each do |image|
774
790
  menu.choice(image.name) do
775
- settings["inception"]["image_id"] = image.id
791
+ inception_image = image
792
+ settings["inception"]["image_id"] = inception_image.id
776
793
  save_settings!
777
794
  end
778
795
  end
779
796
  end
780
797
  end
798
+ say ""
799
+ confirm "Using image #{inception_image.name} for Inception VM"
781
800
 
782
801
  # Boot OpenStack server
783
802
  server = fog_compute.servers.create(
@@ -785,8 +804,8 @@ module Bosh::Bootstrap
785
804
  :key_name => key_pair.name,
786
805
  :public_key_path => public_key_path,
787
806
  :private_key_path => private_key_path,
788
- :flavor_ref => settings["inception"]["flavor_id"],
789
- :image_ref => settings["inception"]["image_id"],
807
+ :flavor_ref => inception_flavor.id,
808
+ :image_ref => inception_image.id,
790
809
  :username => username
791
810
  )
792
811
  unless server
@@ -810,6 +829,19 @@ module Bosh::Bootstrap
810
829
  save_settings!
811
830
  end
812
831
 
832
+ # TODO: Hack
833
+ unless server.public_ip_address
834
+ server.addresses["public"] = [settings["inception"]["ip_address"]]
835
+ end
836
+ unless server.public_key_path
837
+ server.public_key_path = public_key_path
838
+ end
839
+ unless server.private_key_path
840
+ server.private_key_path = private_key_path
841
+ end
842
+ server.username = settings["inception"]["username"]
843
+ Fog.wait_for(60) { server.sshable? }
844
+
813
845
  unless settings["inception"]["disk_size"]
814
846
  disk_size = DEFAULT_INCEPTION_VOLUME_SIZE # Gb
815
847
  device = "/dev/vdc"
@@ -818,44 +850,6 @@ module Bosh::Bootstrap
818
850
  settings["inception"]["disk_size"] = disk_size
819
851
  settings["inception"]["disk_device"] = device
820
852
  save_settings!
821
-
822
- # TODO use provision_and_mount_volume
823
-
824
- disk_size = 16 # Gb
825
- va = fog_compute.get_server_volumes(server.id).body['volumeAttachments']
826
- unless vol = va.find { |v| v["device"] == "/dev/vdc" }
827
- say "Provisioning #{disk_size}Gb persistent disk for inception VM..."
828
- volume = fog_compute.volumes.create(:name => "Inception Disk",
829
- :description => "",
830
- :size => disk_size,
831
- :availability_zone => server.availability_zone)
832
- volume.wait_for { volume.status == 'available' }
833
- volume.attach(server.id, "/dev/vdc")
834
- volume.wait_for { volume.status == 'in-use' }
835
- end
836
-
837
- # Format and mount the volume
838
- # TODO: Hack
839
- unless server.public_ip_address
840
- server.addresses["public"] = [settings["inception"]["ip_address"]]
841
- end
842
- unless server.public_key_path
843
- server.public_key_path = public_key_path
844
- end
845
- unless server.private_key_path
846
- server.private_key_path = private_key_path
847
- end
848
- server.username = settings["inception"]["username"]
849
- server.sshable?
850
-
851
- say "Mounting persistent disk as volume on inception VM..."
852
- # TODO if any of these ssh calls fail; retry
853
- server.ssh(['sudo mkfs.ext4 /dev/vdc -F'])
854
- server.ssh(['sudo mkdir -p /var/vcap/store'])
855
- server.ssh(['sudo mount /dev/vdc /var/vcap/store'])
856
-
857
- settings["inception"]["disk_size"] = disk_size
858
- save_settings!
859
853
  end
860
854
 
861
855
  # settings["inception"]["host"] is used externally to determine
@@ -884,8 +878,7 @@ module Bosh::Bootstrap
884
878
  end
885
879
 
886
880
  def associate_ip_address_with_server(ip_address, server)
887
- address = fog_compute.addresses.get(ip_address)
888
- address.server = server
881
+ provider.associate_ip_address_with_server(ip_address, server)
889
882
  server.reload
890
883
  end
891
884
 
@@ -894,23 +887,9 @@ module Bosh::Bootstrap
894
887
  #
895
888
  # Requires that we can SSH into +server+.
896
889
  def provision_and_mount_volume(server, disk_size, device)
897
- unless volume = server.volumes.all.find {|v| v.device == device}
890
+ unless provider.find_server_device(server, device)
898
891
  say "Provisioning #{disk_size}Gb persistent disk for inception VM..."
899
- volume = fog_compute.volumes.create(
900
- size: disk_size,
901
- name: "Inception Disk",
902
- description: '',
903
- device: "/dev/sdi",
904
- availability_zone: server.availability_zone)
905
- # TODO: the following works in fog 1.9.0+ (but which has a bug in bootstrap)
906
- # https://github.com/fog/fog/issues/1516
907
- #
908
- # volume.wait_for { volume.status == 'available' }
909
- # volume.attach(server.id, "/dev/vdc")
910
- # volume.wait_for { volume.status == 'in-use' }
911
- #
912
- # Instead, using:
913
- volume.server = server
892
+ provider.create_and_attach_volume("Inception Disk", disk_size, server, device)
914
893
  end
915
894
 
916
895
  # Format and mount the volume
@@ -79,7 +79,7 @@ module Bosh::Bootstrap::Stages
79
79
  # ec2_private_key: /home/vcap/.ssh/#{key_name}.pem
80
80
  cloud_properties = settings.bosh_cloud_properties
81
81
 
82
- {
82
+ manifest = {
83
83
  "name" => name,
84
84
  "env" => { "bosh" => {"password" => salted_password}},
85
85
  "logging" => { "level" => "DEBUG" },
@@ -101,7 +101,20 @@ module Bosh::Bootstrap::Stages
101
101
  "#{cloud_plugin.downcase}_registry" => { "address" => ipaddress }
102
102
  }
103
103
  }
104
- }.to_yaml.gsub(/\s![^ ]+$/, '')
104
+ }
105
+
106
+ # Openstack settings
107
+ if cloud_plugin.downcase == "openstack"
108
+ # Delete OpenStack registry IP address
109
+ manifest["apply_spec"]["properties"].delete("openstack_registry")
110
+
111
+ # OpenStack private network label
112
+ if settings.network_label
113
+ manifest["network"]["label"] = settings.network_label
114
+ end
115
+ end
116
+
117
+ manifest.to_yaml.gsub(/\s![^ ]+$/, '')
105
118
 
106
119
  # /![^ ]+\s/ removes object notation from the YAML which appears to cause problems when being interpretted by the
107
120
  # Ruby running on the inception vm. A before and after example would look like;
@@ -1,5 +1,5 @@
1
1
  module Bosh
2
2
  module Bootstrap
3
- VERSION = "0.5.0"
3
+ VERSION = "0.5.1"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bosh-bootstrap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-01-27 00:00:00.000000000 Z
12
+ date: 2013-01-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: thor