Skip to content
This repository
Browse code

Add OpenStack Deployer

Change-Id: I1591f6063ba02c04249422c191b77a2d019d9491
  • Loading branch information...
commit 6dcee09c2453a3f515503898c5ecba9c8807c11a 1 parent 72c1099
Ferran Rodenas frodenas authored

Showing 26 changed files with 793 additions and 1 deletion. Show diff stats Hide diff stats

  1. +20 0 deployer/Gemfile.lock
  2. +1 0  deployer/bosh_deployer.gemspec
  3. +50 0 deployer/config/openstack_defaults.yml
  4. +1 1  deployer/lib/deployer/instance_manager.rb
  5. +256 0 deployer/lib/deployer/instance_manager/openstack.rb
  6. +131 0 deployer/spec/assets/apply_spec_openstack.yml
  7. 0  deployer/spec/assets/openstack_registry
  8. +24 0 deployer/spec/assets/test-bootstrap-config-openstack.yml
  9. +93 0 deployer/spec/unit/openstack/config_spec.rb
  10. +217 0 deployer/spec/unit/openstack/instance_manager_spec.rb
  11. BIN  deployer/vendor/cache/bosh_openstack_cpi-0.0.2.gem
  12. BIN  deployer/vendor/cache/excon-0.14.3.gem
  13. BIN  deployer/vendor/cache/fog-1.4.0.gem
  14. BIN  deployer/vendor/cache/formatador-0.2.3.gem
  15. BIN  deployer/vendor/cache/httparty-0.8.1.gem
  16. BIN  deployer/vendor/cache/httparty-0.8.3.gem
  17. BIN  deployer/vendor/cache/json-1.6.6.gem
  18. BIN  deployer/vendor/cache/json-1.7.3.gem
  19. BIN  deployer/vendor/cache/multi_xml-0.4.2.gem
  20. BIN  deployer/vendor/cache/multi_xml-0.5.1.gem
  21. BIN  deployer/vendor/cache/nokogiri-1.5.2.gem
  22. BIN  deployer/vendor/cache/nokogiri-1.5.5.gem
  23. BIN  deployer/vendor/cache/sequel-3.33.0.gem
  24. BIN  deployer/vendor/cache/sequel-3.37.0.gem
  25. BIN  deployer/vendor/cache/sqlite3-1.3.5.gem
  26. BIN  deployer/vendor/cache/sqlite3-1.3.6.gem
20 deployer/Gemfile.lock
@@ -6,6 +6,7 @@ PATH
6 6 bosh_aws_cpi (~> 0.6.0)
7 7 bosh_common (~> 0.4.0)
8 8 bosh_cpi (~> 0.4.2)
  9 + bosh_openstack_cpi (~> 0.0.2)
9 10 bosh_vsphere_cpi (~> 0.4.9)
10 11 sqlite3 (~> 1.3.3)
11 12
@@ -52,6 +53,13 @@ GEM
52 53 bosh_common (0.4.0)
53 54 bosh_cpi (0.4.3)
54 55 bosh_common
  56 + bosh_openstack_cpi (0.0.2)
  57 + bosh_common (>= 0.4.0)
  58 + bosh_cpi (>= 0.4.3)
  59 + fog (>= 1.4.0)
  60 + httpclient (>= 2.2.0)
  61 + uuidtools (>= 2.1.2)
  62 + yajl-ruby (>= 0.8.2)
55 63 bosh_vsphere_cpi (0.4.9)
56 64 bosh_common
57 65 bosh_cpi (>= 0.4.2)
@@ -63,6 +71,18 @@ GEM
63 71 builder (>= 2.1.2)
64 72 columnize (0.3.6)
65 73 diff-lcs (1.1.3)
  74 + excon (0.14.3)
  75 + fog (1.4.0)
  76 + builder
  77 + excon (~> 0.14.0)
  78 + formatador (~> 0.2.0)
  79 + mime-types
  80 + multi_json (~> 1.0)
  81 + net-scp (~> 1.0.4)
  82 + net-ssh (>= 2.1.3)
  83 + nokogiri (~> 1.5.0)
  84 + ruby-hmac
  85 + formatador (0.2.3)
66 86 highline (1.6.11)
67 87 httparty (0.8.3)
68 88 multi_json (~> 1.0)
1  deployer/bosh_deployer.gemspec
@@ -17,6 +17,7 @@ Gem::Specification.new do |s|
17 17 s.add_dependency "bosh_cpi", "~>0.4.2"
18 18 s.add_dependency "bosh_vsphere_cpi", "~>0.4.9"
19 19 s.add_dependency "bosh_aws_cpi", "~>0.6.0"
  20 + s.add_dependency "bosh_openstack_cpi", "~>0.0.2"
20 21 s.add_dependency "agent_client", "~>0.1.1"
21 22 s.add_dependency "sqlite3", "~>1.3.3"
22 23 end
50 deployer/config/openstack_defaults.yml
... ... @@ -0,0 +1,50 @@
  1 +---
  2 +name:
  3 +
  4 +logging:
  5 + level: INFO
  6 +
  7 +dir:
  8 +
  9 +network:
  10 + type: dynamic
  11 + cloud_properties: {}
  12 +
  13 +env:
  14 + bosh:
  15 + password:
  16 +
  17 +resources:
  18 + persistent_disk: 4096
  19 + cloud_properties:
  20 + instance_type: m1.small
  21 + availability_zone:
  22 +
  23 +cloud:
  24 + plugin: openstack
  25 + properties:
  26 + openstack:
  27 + auth_url:
  28 + username:
  29 + api_key:
  30 + tenant:
  31 + default_key_name:
  32 + default_security_groups: []
  33 + ssh_user: vcap
  34 + registry:
  35 + endpoint: http://admin:admin@localhost:25889
  36 + user: admin
  37 + password: admin
  38 + stemcell:
  39 + kernel_id:
  40 + disk: 4096
  41 + agent:
  42 + ntp: []
  43 + blobstore:
  44 + plugin: local
  45 + properties:
  46 + blobstore_path: /var/vcap/micro_bosh/data/cache
  47 + mbus:
  48 +
  49 +apply_spec:
  50 + properties: {}
2  deployer/lib/deployer/instance_manager.rb
@@ -350,7 +350,7 @@ def update_spec(spec)
350 350 properties["director"]["name"] = Config.name
351 351 end
352 352
353   - %w{blobstore postgres director redis nats aws_registry}.each do |service|
  353 + %w{blobstore postgres director redis nats aws_registry openstack_registry}.each do |service|
354 354 next unless properties[service]
355 355 properties[service]["address"] = service_ip
356 356
256 deployer/lib/deployer/instance_manager/openstack.rb
... ... @@ -0,0 +1,256 @@
  1 +# Copyright (c) 2009-2012 VMware, Inc.
  2 +
  3 +require 'net/ssh'
  4 +
  5 +module Bosh::Deployer
  6 + class InstanceManager
  7 +
  8 + class Openstack < InstanceManager
  9 +
  10 + def update_spec(spec)
  11 + spec = super(spec)
  12 + properties = spec["properties"]
  13 +
  14 + properties["openstack"] =
  15 + Config.spec_properties["openstack"] ||
  16 + Config.cloud_options["properties"]["openstack"].dup
  17 +
  18 + properties["openstack"]["registry"] = Config.cloud_options["properties"]["registry"]
  19 + properties["openstack"]["stemcell"] = Config.cloud_options["properties"]["stemcell"]
  20 +
  21 + spec.delete("networks")
  22 +
  23 + spec
  24 + end
  25 +
  26 + def configure
  27 + properties = Config.cloud_options["properties"]
  28 + @ssh_user = properties["openstack"]["ssh_user"]
  29 + @ssh_port = properties["openstack"]["ssh_port"] || 22
  30 + @ssh_wait = properties["openstack"]["ssh_wait"] || 60
  31 +
  32 + key = properties["openstack"]["private_key"]
  33 + unless key
  34 + raise ConfigError, "Missing properties.openstack.private_key"
  35 + end
  36 + @ssh_key = File.expand_path(key)
  37 + unless File.exists?(@ssh_key)
  38 + raise ConfigError, "properties.openstack.private_key '#{key}' does not exist"
  39 + end
  40 +
  41 + uri = URI.parse(properties["registry"]["endpoint"])
  42 + user, password = uri.userinfo.split(":", 2)
  43 + @registry_port = uri.port
  44 +
  45 + @registry_db = Tempfile.new("openstack_registry_db")
  46 + @registry_db_url = "sqlite://#{@registry_db.path}"
  47 +
  48 + registry_config = {
  49 + "logfile" => "./openstack_registry.log",
  50 + "http" => {
  51 + "port" => uri.port,
  52 + "user" => user,
  53 + "password" => password
  54 + },
  55 + "db" => {
  56 + "database" => @registry_db_url
  57 + },
  58 + "openstack" => properties["openstack"]
  59 + }
  60 +
  61 + @registry_config = Tempfile.new("openstack_registry_yml")
  62 + @registry_config.write(YAML.dump(registry_config))
  63 + @registry_config.close
  64 + end
  65 +
  66 + def start
  67 + configure()
  68 +
  69 + Sequel.connect(@registry_db_url) do |db|
  70 + migrate(db)
  71 + servers = @deployments["openstack_servers"]
  72 + db[:openstack_servers].insert_multiple(servers) if servers
  73 + end
  74 +
  75 + unless has_openstack_registry?
  76 + raise "openstack_registry command not found - " +
  77 + "run 'gem install bosh_openstack_registry'"
  78 + end
  79 +
  80 + cmd = "openstack_registry -c #{@registry_config.path}"
  81 +
  82 + @registry_pid = spawn(cmd)
  83 +
  84 + 5.times do
  85 + sleep 0.5
  86 + if Process.waitpid(@registry_pid, Process::WNOHANG)
  87 + raise Error, "`#{cmd}` failed, exit status=#{$?.exitstatus}"
  88 + end
  89 + end
  90 +
  91 + timeout_time = Time.now.to_f + (60 * 5)
  92 + http_client = HTTPClient.new()
  93 + begin
  94 + http_client.head("http://127.0.0.1:#{@registry_port}")
  95 + sleep 0.5
  96 + rescue URI::Error, SocketError, Errno::ECONNREFUSED => e
  97 + if timeout_time - Time.now.to_f > 0
  98 + retry
  99 + else
  100 + raise "Cannot access openstack_registry: #{e.message}"
  101 + end
  102 + end
  103 + logger.info("openstack_registry is ready on port #{@registry_port}")
  104 + ensure
  105 + @registry_config.unlink if @registry_config
  106 + end
  107 +
  108 + def stop
  109 + if @registry_pid && process_exists?(@registry_pid)
  110 + Process.kill("INT", @registry_pid)
  111 + Process.waitpid(@registry_pid)
  112 + end
  113 +
  114 + return unless @registry_db_url
  115 +
  116 + Sequel.connect(@registry_db_url) do |db|
  117 + @deployments["openstack_servers"] = db[:openstack_servers].map {|row| row}
  118 + end
  119 +
  120 + save_state
  121 + @registry_db.unlink if @registry_db
  122 + end
  123 +
  124 + def wait_until_agent_ready
  125 + tunnel(@registry_port)
  126 + super
  127 + end
  128 +
  129 + def discover_bosh_ip
  130 + if exists?
  131 + server = cloud.openstack.servers.get(state.vm_cid)
  132 + ip = server.public_ip_address
  133 + ip = server.private_ip_address if ip.nil? || ip.empty?
  134 + if ip.nil? || ip.empty?
  135 + raise "Unable to discover bosh ip"
  136 + else
  137 + if ip["addr"] != Config.bosh_ip
  138 + Config.bosh_ip = ip["addr"]
  139 + logger.info("discovered bosh ip=#{Config.bosh_ip}")
  140 + end
  141 + end
  142 + end
  143 +
  144 + super
  145 + end
  146 +
  147 + def service_ip
  148 + ip = cloud.openstack.servers.get(state.vm_cid).private_ip_address
  149 + ip["addr"] unless ip.nil? || ip.empty?
  150 + end
  151 +
  152 + # @return [Integer] size in MiB
  153 + def disk_size(cid)
  154 + # OpenStack stores disk size in GiB but we work with MiB
  155 + cloud.openstack.volumes.get(cid).size * 1024
  156 + end
  157 +
  158 + def persistent_disk_changed?
  159 + # since OpenStack stores disk size in GiB and we use MiB there
  160 + # is a risk of conversion errors which lead to an unnecessary
  161 + # disk migration, so we need to do a double conversion
  162 + # here to avoid that
  163 + requested = (Config.resources['persistent_disk'] / 1024.0).ceil * 1024
  164 + requested != disk_size(state.disk_cid)
  165 + end
  166 +
  167 + private
  168 +
  169 + # TODO this code is similar to has_stemcell_copy?
  170 + # move the two into bosh_common later
  171 + def has_openstack_registry?(path=ENV['PATH'])
  172 + path.split(":").each do |dir|
  173 + return true if File.exist?(File.join(dir, "openstack_registry"))
  174 + end
  175 + false
  176 + end
  177 +
  178 + def migrate(db)
  179 + db.create_table :openstack_servers do
  180 + primary_key :id
  181 + column :server_id, :text, :unique => true, :null => false
  182 + column :settings, :text, :null => false
  183 + end
  184 + end
  185 +
  186 + def process_exists?(pid)
  187 + begin
  188 + Process.kill(0, pid)
  189 + rescue Errno::ESRCH
  190 + false
  191 + end
  192 + end
  193 +
  194 + def socket_readable?(ip, port)
  195 + socket = TCPSocket.new(ip, port)
  196 + if IO.select([socket], nil, nil, 5)
  197 + logger.debug("tcp socket #{ip}:#{port} is readable")
  198 + yield
  199 + true
  200 + else
  201 + false
  202 + end
  203 + rescue SocketError => e
  204 + logger.debug("tcp socket #{ip}:#{port} SocketError: #{e.inspect}")
  205 + sleep 1
  206 + false
  207 + rescue SystemCallError => e
  208 + logger.debug("tcp socket #{ip}:#{port} SystemCallError: #{e.inspect}")
  209 + sleep 1
  210 + false
  211 + ensure
  212 + socket.close if socket
  213 + end
  214 +
  215 + def tunnel(port)
  216 + return if @session
  217 +
  218 + ip = discover_bosh_ip
  219 +
  220 + loop until socket_readable?(ip, @ssh_port) do
  221 + #sshd is up, sleep while host keys are generated
  222 + sleep @ssh_wait
  223 + end
  224 +
  225 + lo = "127.0.0.1"
  226 + cmd = "ssh -R #{port}:#{lo}:#{port} #{@ssh_user}@#{ip}"
  227 +
  228 + logger.info("Preparing for ssh tunnel: #{cmd}")
  229 + loop do
  230 + begin
  231 + @session = Net::SSH.start(ip, @ssh_user, :keys => [@ssh_key],
  232 + :paranoid => false)
  233 + logger.debug("ssh #{@ssh_user}@#{ip}: ESTABLISHED")
  234 + break
  235 + rescue => e
  236 + logger.debug("ssh start #{@ssh_user}@#{ip} failed: #{e.inspect}")
  237 + sleep 1
  238 + end
  239 + end
  240 +
  241 + @session.forward.remote(port, lo, port)
  242 + logger.info("`#{cmd}` started: OK")
  243 +
  244 + Thread.new do
  245 + begin
  246 + @session.loop { true }
  247 + rescue IOError => e
  248 + logger.debug("`#{cmd}` terminated: #{e.inspect}")
  249 + @session = nil
  250 + end
  251 + end
  252 + end
  253 +
  254 + end
  255 + end
  256 +end
131 deployer/spec/assets/apply_spec_openstack.yml
... ... @@ -0,0 +1,131 @@
  1 +---
  2 +deployment: micro
  3 +release:
  4 + name: micro
  5 + version: 9
  6 +properties:
  7 + micro: true
  8 + domain: vcap.me
  9 + env:
  10 + networks:
  11 + apps: local
  12 + management: local
  13 + nats:
  14 + user: nats
  15 + password: nats
  16 + address: 127.0.0.1
  17 + port: 4222
  18 + redis:
  19 + address: 127.0.0.1
  20 + port: 25255
  21 + password: redis
  22 + postgres:
  23 + user: postgres
  24 + password: postgres
  25 + address: 127.0.0.1
  26 + port: 5432
  27 + database: bosh
  28 + blobstore:
  29 + address: 127.0.0.1
  30 + backend_port: 25251
  31 + port: 25250
  32 + director:
  33 + user: director
  34 + password: director
  35 + agent:
  36 + user: agent
  37 + password: agent
  38 + director:
  39 + address: 127.0.0.1
  40 + name: micro
  41 + port: 25555
  42 + openstack_registry:
  43 + http:
  44 + port: 25778
  45 + user: admin
  46 + password: admin
  47 + db:
  48 + database: postgres://postgres:postgres@localhost/bosh
  49 + max_connections: 32
  50 + pool_timeout: 10
  51 + openstack:
  52 + auth_url: http://127.0.0.1:5000/v2.0/tokens
  53 + username: foo-key
  54 + api_key: foo-secret
  55 + tenant: foo-key
  56 + hm:
  57 + http:
  58 + port: 25923
  59 + user: hm
  60 + password: hm
  61 + loglevel: info
  62 + director_account:
  63 + user: admin
  64 + password: admin
  65 + intervals:
  66 + poll_director: 60
  67 + poll_grace_period: 30
  68 + log_stats: 300
  69 + analyze_agents: 60
  70 + agent_timeout: 180
  71 + rogue_agent_alert: 180
  72 +index: 0
  73 +packages:
  74 + ruby:
  75 + name: ruby
  76 + version: 0.1-dev
  77 + sha1: ecdbdf4dedcf4e521763e7a2d6c0b9194e5c5ed0
  78 + blobstore_id: d0c7a54f-184f-4e5e-b863-35d06bbae678
  79 + nats:
  80 + name: nats
  81 + version: 0.1-dev
  82 + sha1: ebd7b3d3bdf4590de9f5e4f4eadc819b34f56901
  83 + blobstore_id: c15172b6-965e-49bf-bea6-2e4322a7dc02
  84 + redis:
  85 + name: redis
  86 + version: 0.1-dev
  87 + sha1: 105e225be4569e8a4b9ab9dae92263f72fe906f2
  88 + blobstore_id: 4c8aac83-4f5d-41ef-85f7-d371c779d7f1
  89 + libpq:
  90 + name: libpq
  91 + version: 0.1-dev
  92 + sha1: 13e95edc074553c02b558bdaf91ee8ba976b3c4a
  93 + blobstore_id: 50dd33cd-66a1-4fc6-b304-f1e1ea7c1d5a
  94 + postgres:
  95 + name: postgres
  96 + version: 0.1-dev
  97 + sha1: d8b620fe4ef8076cc404af213909f3108572c308
  98 + blobstore_id: e17ef61e-12f5-4bf5-99f7-b26d32911238
  99 + blobstore:
  100 + name: blobstore
  101 + version: 0.1-dev
  102 + sha1: 73f5451871a9625d49636cd806e96483c3cc83f4
  103 + blobstore_id: 82709f56-d487-458f-9d96-0f5254cae09e
  104 + nginx:
  105 + name: nginx
  106 + version: 0.1-dev
  107 + sha1: 7b1fd58b4981aad05a630a387b7d858c6ea3c75b
  108 + blobstore_id: 1611cb73-da3b-4dea-bcb1-80c217c96b22
  109 + director:
  110 + name: director
  111 + version: 0.2-dev
  112 + sha1: f7d99e12ac65c4529db0b14d3b71e996bc2bc671
  113 + blobstore_id: bdeff591-daaa-4a6b-a452-c37db8e913c0
  114 + openstack_registry:
  115 + name: openstack_registry
  116 + version: 0.1-dev
  117 + sha1: 4c73b808faff4bbb959aeca9bb84ebe4a140d62b
  118 + blobstore_id: 4082220e-acf6-427c-b0fb-56c8b47aae0c
  119 + health_monitor:
  120 + name: health_monitor
  121 + version: 0.1-dev
  122 + sha1: 25bc4ef890649a446f33a667667b9df8038b012c
  123 + blobstore_id: 71250e09-a863-4b9c-8d59-831e37490584
  124 +configuration_hash: {}
  125 +
  126 +job:
  127 + name: micro
  128 + version: 0.3-dev
  129 + sha1: 6df1470e27be9e80cc476823d0a71b1790f2620b
  130 + template: micro
  131 + blobstore_id: a44be400-a055-4670-926d-dfae70d3218d
0  deployer/spec/assets/openstack_registry
No changes.
24 deployer/spec/assets/test-bootstrap-config-openstack.yml
... ... @@ -0,0 +1,24 @@
  1 +---
  2 +name: bosh-bootstrap-openstack
  3 +
  4 +network:
  5 + type: dynamic
  6 +
  7 +env:
  8 + bosh:
  9 + password: $6$salt$password
  10 +
  11 +cloud:
  12 + plugin: openstack
  13 + properties:
  14 + openstack:
  15 + auth_url: http://127.0.0.1:5000/v2.0/tokens
  16 + username: foo-key
  17 + api_key: foo-secret
  18 + tenant: foo-key
  19 + default_key_name: foo-key
  20 + default_security_groups: ["quick-start-1"]
  21 + registry:
  22 + endpoint: http://admin:admin@10.176.194.184:25695
  23 + user: admin
  24 + password: admin
93 deployer/spec/unit/openstack/config_spec.rb
... ... @@ -0,0 +1,93 @@
  1 +# Copyright (c) 2009-2012 VMware, Inc.
  2 +
  3 +require File.expand_path("../../../spec_helper", __FILE__)
  4 +require 'fog'
  5 +
  6 +describe Bosh::Deployer::Config do
  7 + before(:each) do
  8 + @dir = Dir.mktmpdir("bdc_spec")
  9 + end
  10 +
  11 + after(:each) do
  12 + FileUtils.remove_entry_secure @dir
  13 + end
  14 +
  15 + it "configure should fail without cloud properties" do
  16 + lambda {
  17 + Bosh::Deployer::Config.configure({"dir" => @dir})
  18 + }.should raise_error(Bosh::Deployer::ConfigError)
  19 + end
  20 +
  21 + it "should default agent properties" do
  22 + config = YAML.load_file(spec_asset("test-bootstrap-config-openstack.yml"))
  23 + config["dir"] = @dir
  24 + Bosh::Deployer::Config.configure(config)
  25 +
  26 + properties = Bosh::Deployer::Config.cloud_options["properties"]
  27 + properties["agent"].should be_kind_of(Hash)
  28 + properties["agent"]["mbus"].start_with?("http://").should be_true
  29 + properties["agent"]["blobstore"].should be_kind_of(Hash)
  30 + end
  31 +
  32 + it "should map network properties" do
  33 + config = YAML.load_file(spec_asset("test-bootstrap-config-openstack.yml"))
  34 + config["dir"] = @dir
  35 + Bosh::Deployer::Config.configure(config)
  36 +
  37 + networks = Bosh::Deployer::Config.networks
  38 + networks.should be_kind_of(Hash)
  39 +
  40 + net = networks['bosh']
  41 + net.should be_kind_of(Hash)
  42 + ['cloud_properties', 'type'].each do |key|
  43 + net[key].should_not be_nil
  44 + end
  45 + end
  46 +
  47 + it "should default vm env properties" do
  48 + env = Bosh::Deployer::Config.env
  49 + env.should be_kind_of(Hash)
  50 + env.should have_key("bosh")
  51 + env["bosh"].should be_kind_of(Hash)
  52 + env["bosh"]["password"].should_not be_nil
  53 + env["bosh"]["password"].should be_kind_of(String)
  54 + env["bosh"]["password"].should == "$6$salt$password"
  55 + end
  56 +
  57 + it "should contain default vm resource properties" do
  58 + Bosh::Deployer::Config.configure({"dir" => @dir, "cloud" => { "plugin" => "openstack" }})
  59 + resources = Bosh::Deployer::Config.resources
  60 + resources.should be_kind_of(Hash)
  61 +
  62 + resources['persistent_disk'].should be_kind_of(Integer)
  63 +
  64 + cloud_properties = resources['cloud_properties']
  65 + cloud_properties.should be_kind_of(Hash)
  66 +
  67 + ['instance_type'].each do |key|
  68 + cloud_properties[key].should_not be_nil
  69 + end
  70 + end
  71 +
  72 + it "should configure agent using mbus property" do
  73 + config = YAML.load_file(spec_asset("test-bootstrap-config-openstack.yml"))
  74 + config["dir"] = @dir
  75 + Bosh::Deployer::Config.configure(config)
  76 + agent = Bosh::Deployer::Config.agent
  77 + agent.should be_kind_of(Bosh::Agent::HTTPClient)
  78 + end
  79 +
  80 + it "should have openstack and registry object access" do
  81 + config = YAML.load_file(spec_asset("test-bootstrap-config-openstack.yml"))
  82 + config["dir"] = @dir
  83 + Bosh::Deployer::Config.configure(config)
  84 + openstack = double(Fog::Compute)
  85 + Fog::Compute.stub(:new).and_return(openstack)
  86 + glance = double(Fog::Image)
  87 + Fog::Image.stub(:new).and_return(glance)
  88 + cloud = Bosh::Deployer::Config.cloud
  89 + cloud.respond_to?(:openstack).should be_true
  90 + cloud.respond_to?(:registry).should be_true
  91 + cloud.registry.should be_kind_of(Bosh::OpenStackCloud::RegistryClient)
  92 + end
  93 +end
217 deployer/spec/unit/openstack/instance_manager_spec.rb
... ... @@ -0,0 +1,217 @@
  1 +# Copyright (c) 2009-2012 VMware, Inc.
  2 +
  3 +require File.expand_path("../../../spec_helper", __FILE__)
  4 +
  5 +BOSH_STEMCELL_TGZ ||= "bosh-instance-1.0.tgz"
  6 +
  7 +describe Bosh::Deployer::InstanceManager do
  8 + before(:each) do
  9 + @dir = Dir.mktmpdir("bdim_spec")
  10 + @config = YAML.load_file(spec_asset("test-bootstrap-config-openstack.yml"))
  11 + @config["dir"] = @dir
  12 + @config["name"] = "spec-#{UUIDTools::UUID.random_create.to_s}"
  13 + @config["logging"] = { "file" => "#{@dir}/bmim.log" }
  14 + @deployer = Bosh::Deployer::InstanceManager.create(@config)
  15 + @cloud = mock("cloud")
  16 + @openstack = mock("openstack")
  17 + @cloud.stub(:openstack).and_return(@openstack)
  18 + Bosh::Deployer::Config.stub!(:cloud).and_return(@cloud)
  19 + @agent = mock("agent")
  20 + Bosh::Deployer::Config.stub!(:agent).and_return(@agent)
  21 + end
  22 +
  23 + after(:each) do
  24 + @deployer.state.destroy
  25 + FileUtils.remove_entry_secure @dir
  26 + end
  27 +
  28 + def load_deployment
  29 + @deployer.send(:load_deployments)["instances"].select { |d| d[:name] == @deployer.state.name }.first
  30 + end
  31 +
  32 + def discover_bosh_ip(ip, id)
  33 + server = mock("server")
  34 + servers = mock("servers")
  35 + @openstack.should_receive(:servers).and_return(servers)
  36 + servers.should_receive(:get).with(id).and_return(server)
  37 + server.should_receive(:public_ip_address).and_return(ip)
  38 + end
  39 +
  40 + it "should update the apply spec" do
  41 + dummy_ip = "1.2.3.4"
  42 + @deployer.stub!(:service_ip).and_return(dummy_ip)
  43 + spec = YAML.load_file(spec_asset("apply_spec_openstack.yml"))
  44 + @deployer.update_spec(spec)
  45 + props = spec["properties"]
  46 +
  47 + %w{blobstore postgres director redis nats openstack_registry}.each do |service|
  48 + ip = props[service]["address"]
  49 + ip.should_not be_nil
  50 + ip.should_not == "127.0.0.1"
  51 + ip.should == dummy_ip
  52 + end
  53 + end
  54 +
  55 + it "should apply" do
  56 + @deployer.stub!(:service_ip).and_return("10.0.0.10")
  57 + spec = YAML.load_file(spec_asset("apply_spec_openstack.yml"))
  58 + updated_spec = @deployer.update_spec(spec.dup)
  59 + @agent.should_receive(:run_task).with(:stop)
  60 + @agent.should_receive(:run_task).with(:apply, updated_spec)
  61 + @agent.should_receive(:run_task).with(:start)
  62 + @deployer.apply(spec)
  63 + end
  64 +
  65 + it "should not populate disk model" do
  66 + disk_model = @deployer.disk_model
  67 + disk_model.should == nil
  68 + end
  69 +
  70 + it "should create a Bosh instance" do
  71 + @deployer.stub!(:service_ip).and_return("10.0.0.10")
  72 + spec = YAML.load_file(spec_asset("apply_spec_openstack.yml"))
  73 + @deployer.stub!(:run_command)
  74 + @deployer.stub!(:wait_until_agent_ready)
  75 + @deployer.stub!(:wait_until_director_ready)
  76 + @deployer.stub!(:load_apply_spec).and_return(spec)
  77 + @deployer.stub!(:load_stemcell_manifest).and_return({"cloud_properties" => {}})
  78 +
  79 + @deployer.state.uuid.should_not be_nil
  80 +
  81 + @deployer.state.stemcell_cid.should be_nil
  82 + @deployer.state.vm_cid.should be_nil
  83 +
  84 + @cloud.should_receive(:create_stemcell).and_return("SC-CID-CREATE")
  85 + @cloud.should_receive(:create_vm).and_return("VM-CID-CREATE")
  86 + @cloud.should_receive(:create_disk).and_return("DISK-CID-CREATE")
  87 + @cloud.should_receive(:attach_disk).with("VM-CID-CREATE", "DISK-CID-CREATE")
  88 + @agent.should_receive(:run_task).with(:mount_disk, "DISK-CID-CREATE").and_return({})
  89 + @agent.should_receive(:run_task).with(:stop)
  90 + @agent.should_receive(:run_task).with(:apply, spec)
  91 + @agent.should_receive(:run_task).with(:start)
  92 +
  93 + discover_bosh_ip("10.0.0.1", "VM-CID-CREATE")
  94 + @deployer.create(BOSH_STEMCELL_TGZ)
  95 +
  96 + @deployer.state.stemcell_cid.should == "SC-CID-CREATE"
  97 + @deployer.state.vm_cid.should == "VM-CID-CREATE"
  98 + @deployer.state.disk_cid.should == "DISK-CID-CREATE"
  99 + load_deployment.should == @deployer.state.values
  100 +
  101 + @deployer.renderer.total.should == @deployer.renderer.index
  102 + end
  103 +
  104 + it "should destroy a Bosh instance" do
  105 + disk_cid = "33"
  106 + @deployer.state.disk_cid = disk_cid
  107 + @deployer.state.stemcell_cid = "SC-CID-DESTROY"
  108 + @deployer.state.stemcell_name = @deployer.state.stemcell_cid
  109 +
  110 + @deployer.state.vm_cid = "VM-CID-DESTROY"
  111 +
  112 + @agent.should_receive(:list_disk).and_return([disk_cid])
  113 + @agent.should_receive(:run_task).with(:stop)
  114 + @agent.should_receive(:run_task).with(:unmount_disk, disk_cid).and_return({})
  115 + @cloud.should_receive(:detach_disk).with("VM-CID-DESTROY", disk_cid)
  116 + @cloud.should_receive(:delete_disk).with(disk_cid)
  117 + @cloud.should_receive(:delete_vm).with("VM-CID-DESTROY")
  118 +
  119 + @deployer.destroy
  120 +
  121 + @deployer.state.stemcell_cid.should be_nil
  122 + @deployer.state.stemcell_name.should be_nil
  123 + @deployer.state.vm_cid.should be_nil
  124 + @deployer.state.disk_cid.should be_nil
  125 +
  126 + load_deployment.should == @deployer.state.values
  127 +
  128 + @deployer.renderer.total.should == @deployer.renderer.index
  129 + end
  130 +
  131 + it "should update a Bosh instance" do
  132 + @deployer.stub!(:service_ip).and_return("10.0.0.10")
  133 + spec = YAML.load_file(spec_asset("apply_spec_openstack.yml"))
  134 + disk_cid = "22"
  135 + @deployer.stub!(:run_command)
  136 + @deployer.stub!(:wait_until_agent_ready)
  137 + @deployer.stub!(:wait_until_director_ready)
  138 + @deployer.stub!(:load_apply_spec).and_return(spec)
  139 + @deployer.stub!(:load_stemcell_manifest).and_return({"cloud_properties" => {}})
  140 + @deployer.stub!(:persistent_disk_changed?).and_return(false)
  141 +
  142 + @deployer.state.stemcell_cid = "SC-CID-UPDATE"
  143 + @deployer.state.vm_cid = "VM-CID-UPDATE"
  144 + @deployer.state.disk_cid = disk_cid
  145 +
  146 + @agent.should_receive(:run_task).with(:stop)
  147 + @agent.should_receive(:run_task).with(:unmount_disk, disk_cid).and_return({})
  148 + @cloud.should_receive(:detach_disk).with("VM-CID-UPDATE", disk_cid)
  149 + @cloud.should_receive(:delete_vm).with("VM-CID-UPDATE")
  150 + @cloud.should_receive(:delete_stemcell).with("SC-CID-UPDATE")
  151 + @cloud.should_receive(:create_stemcell).and_return("SC-CID")
  152 + @cloud.should_receive(:create_vm).and_return("VM-CID")
  153 + @cloud.should_receive(:attach_disk).with("VM-CID", disk_cid)
  154 + @agent.should_receive(:run_task).with(:mount_disk, disk_cid).and_return({})
  155 + @agent.should_receive(:list_disk).and_return([disk_cid])
  156 + @agent.should_receive(:run_task).with(:stop)
  157 + @agent.should_receive(:run_task).with(:apply, spec)
  158 + @agent.should_receive(:run_task).with(:start)
  159 +
  160 + discover_bosh_ip("10.0.0.2", "VM-CID")
  161 + @deployer.update(BOSH_STEMCELL_TGZ)
  162 +
  163 + @deployer.state.stemcell_cid.should == "SC-CID"
  164 + @deployer.state.vm_cid.should == "VM-CID"
  165 + @deployer.state.disk_cid.should == disk_cid
  166 +
  167 + load_deployment.should == @deployer.state.values
  168 + end
  169 +
  170 + it "should fail to create a Bosh instance if stemcell CID exists" do
  171 + @deployer.state.stemcell_cid = "SC-CID"
  172 +
  173 + lambda {
  174 + @deployer.create(BOSH_STEMCELL_TGZ)
  175 + }.should raise_error(Bosh::Deployer::ConfigError)
  176 + end
  177 +
  178 + it "should fail to create a Bosh instance if VM CID exists" do
  179 + @deployer.state.vm_cid = "VM-CID"
  180 +
  181 + lambda {
  182 + @deployer.create(BOSH_STEMCELL_TGZ)
  183 + }.should raise_error(Bosh::Deployer::ConfigError)
  184 + end
  185 +
  186 + it "should fail to destroy a Bosh instance unless stemcell CID exists" do
  187 + @deployer.state.vm_cid = "VM-CID"
  188 + @agent.should_receive(:run_task).with(:stop)
  189 + @cloud.should_receive(:delete_vm).with("VM-CID")
  190 + lambda {
  191 + @deployer.destroy
  192 + }.should raise_error(Bosh::Deployer::ConfigError)
  193 + end
  194 +
  195 + it "should fail to destroy a Bosh instance unless VM CID exists" do
  196 + @deployer.state.stemcell_cid = "SC-CID"
  197 + @agent.should_receive(:run_task).with(:stop)
  198 + lambda {
  199 + @deployer.destroy
  200 + }.should raise_error(Bosh::Deployer::ConfigError)
  201 + end
  202 +
  203 + require 'deployer/instance_manager/openstack'
  204 +
  205 + internal_to Bosh::Deployer::InstanceManager::Openstack do
  206 + it "should not find openstack_registry" do
  207 + path = "/usr/bin:/bin"
  208 + @deployer.has_openstack_registry?(path).should be_false
  209 + end
  210 +
  211 + it "should find find openstack_registry" do
  212 + path = ENV['PATH']
  213 + path += ":#{File.dirname(spec_asset('openstack_registry'))}"
  214 + @deployer.has_openstack_registry?(path).should be_true
  215 + end
  216 + end
  217 +end
BIN  deployer/vendor/cache/bosh_openstack_cpi-0.0.2.gem
Binary file not shown
BIN  deployer/vendor/cache/excon-0.14.3.gem
Binary file not shown
BIN  deployer/vendor/cache/fog-1.4.0.gem
Binary file not shown
BIN  deployer/vendor/cache/formatador-0.2.3.gem
Binary file not shown
BIN  deployer/vendor/cache/httparty-0.8.1.gem
Binary file not shown
BIN  deployer/vendor/cache/httparty-0.8.3.gem
Binary file not shown
BIN  deployer/vendor/cache/json-1.6.6.gem
Binary file not shown
BIN  deployer/vendor/cache/json-1.7.3.gem
Binary file not shown
BIN  deployer/vendor/cache/multi_xml-0.4.2.gem
Binary file not shown
BIN  deployer/vendor/cache/multi_xml-0.5.1.gem
Binary file not shown
BIN  deployer/vendor/cache/nokogiri-1.5.2.gem
Binary file not shown
BIN  deployer/vendor/cache/nokogiri-1.5.5.gem
Binary file not shown
BIN  deployer/vendor/cache/sequel-3.33.0.gem
Binary file not shown
BIN  deployer/vendor/cache/sequel-3.37.0.gem
Binary file not shown
BIN  deployer/vendor/cache/sqlite3-1.3.5.gem
Binary file not shown
BIN  deployer/vendor/cache/sqlite3-1.3.6.gem
Binary file not shown

Git Notes

review

Code-Review+2: Martin Englund <menglund@vmware.com>
Verified+1: CI Master <cf-ci@rbcon.com>
Submitted-by: Ferran Rodenas <frodenas@gmail.com>
Submitted-at: Wed, 01 Aug 2012 12:56:54 +0000
Reviewed-on: http://reviews.cloudfoundry.org/7822
Project: bosh
Branch: refs/heads/master

0 comments on commit 6dcee09

Please sign in to comment.
Something went wrong with that request. Please try again.