rightscale_provisioner 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +13 -0
- data/README.md +62 -0
- data/Rakefile +3 -0
- data/config/Gemfile +4 -0
- data/config/Vagrantfile +79 -0
- data/config/rightscale_provisioner.cfg.example +6 -0
- data/lib/rightscale_provisioner.rb +165 -0
- data/lib/rightscale_provisioner/api15.rb +201 -0
- data/lib/rightscale_provisioner/version.rb +3 -0
- data/rightscale_provisoner.gemspec +25 -0
- data/spec/api15_spec.rb +158 -0
- data/spec/rightscale_provisioner_spec.rb +27 -0
- data/spec/spec_helper.rb +10 -0
- metadata +120 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
Njc1ZWQ0ODcxNWQyMWQxOGYzMTZiOWE1M2NhNGVhZjUyN2Y2YTllMg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ODEwMjBiMmU2ZmVjNjk3ZWJmNWEyYjRiZTYyOTZjYzI2ZWFjZWY4Mw==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ZDczNDBjMWJhMTI5Nzg1YjkxYjU1NTdiNGNlYzUwYmFhMTc4ZDlhYzI2Yzgx
|
10
|
+
OTcxNDEwNjg5YzI1MTY4ZTI2NDk1ZWEyZTI5YWRhODYwMGY1MzNhYjhiZGE3
|
11
|
+
ODdhNmYzNzhkMjM2ZTAyYTU2MzQzZmNlZDRlNWQ2Y2JkM2RlMzY=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
OTlmMzdhOGYyMDVlMmRjZDFhZDZmODY4ZWNkNDBmODk1NzY3NDU4YzEzZTIz
|
14
|
+
NmU1N2I5MTZjNGVkZDczY2EwNmJiMzk2ZjE2Y2RhZGUxNmIzYTBkMWNiZjM4
|
15
|
+
NGRiODU5ZTBiYzZkZGQwYTQzZjg4OWFiZjU3MjljNDg4OTU3YWE=
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright (C) 2013 caryp
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# RightscaleProvisoner
|
2
|
+
|
3
|
+
Provision your vagrant boxes using the RightScale ServerTemplates.
|
4
|
+
|
5
|
+
## Requirement
|
6
|
+
|
7
|
+
You must have VirtualBox installed, ruby 1.9 and the bundler gem.
|
8
|
+
|
9
|
+
NOTE: this is the initial release and does not yet support the new vagrant 1.1+ plugin interface. This currently only works with Vagrant 1.0.x.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Copy the configuration file into your home dir and secure it:
|
14
|
+
|
15
|
+
> cp config/rightscale_provisioner.cfg.example ~/.rightscale_provisioner.cfg
|
16
|
+
> chmod 600 ~/.rightscale_provisioner.cfg
|
17
|
+
|
18
|
+
Then edit ```~/.rightscale_provisioner.cfg``` and add your RightScale dashboard credentials.
|
19
|
+
|
20
|
+
Copy the ```config/Vagrantfile``` and ```config/Gemfile``` into your project
|
21
|
+
directory:
|
22
|
+
|
23
|
+
> mkdir ~/my_dev_project
|
24
|
+
> cp config/Gemfile ~/my_dev_project
|
25
|
+
> cp config/Vagrantfile ~/my_dev_project
|
26
|
+
|
27
|
+
Then edit ```~/my_dev_project/Vagrantfile``` to point to the ServerTemplate you
|
28
|
+
want and setup any inputs.
|
29
|
+
|
30
|
+
## Usage
|
31
|
+
|
32
|
+
Launch a local VM:
|
33
|
+
|
34
|
+
> cd ~/my_dev_project
|
35
|
+
> bundle install
|
36
|
+
> bundle exec vagrant up
|
37
|
+
|
38
|
+
Once your vagrant VM is provisioned, you can login to your VM using:
|
39
|
+
|
40
|
+
> bundle exec vagrant ssh
|
41
|
+
|
42
|
+
Shutdown your VM:
|
43
|
+
|
44
|
+
> bundle exec vagrant destroy
|
45
|
+
|
46
|
+
## Bugs and Known Limitations
|
47
|
+
* Cannot have duplicate server names in account -- should only search for servers within specified deployment
|
48
|
+
* does not yet select correct MCI on the ST. Your default MCI must have blueskies cloud settings in it. Add support for multi_cloud_image_name to fix.
|
49
|
+
* does not terminate servers in dashboard or do any cleanup -- you must login and manually delete servers.
|
50
|
+
|
51
|
+
## Contributing
|
52
|
+
|
53
|
+
1. Fork it
|
54
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
55
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
56
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
57
|
+
5. Create new Pull Request
|
58
|
+
|
59
|
+
# Authors
|
60
|
+
|
61
|
+
Author:: caryp (<cary@rightscale.com>)
|
62
|
+
Author:: sumner (<sumner@rightscale.com>)
|
data/Rakefile
ADDED
data/config/Gemfile
ADDED
data/config/Vagrantfile
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'rightscale_provisioner'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
user_secrets_json = File.read(ENV['HOME']+'/.rightscale_provisioner.cfg')
|
5
|
+
user_secrets = JSON.parse(user_secrets_json)
|
6
|
+
|
7
|
+
Vagrant::Config.run do |config|
|
8
|
+
config.vm.host_name = "rightscale-provisioner-example"
|
9
|
+
|
10
|
+
config.vm.box = "RightImage_Ubuntu_12.04_x64_v5.8.8_Vagrant_Dev9_8557fee0fd2d0d"
|
11
|
+
config.vm.box_url = "https://rightscale-vagrant-dev.s3.amazonaws.com/virtualbox/ubuntu/12.04/RightImage_Ubuntu_12.04_x64_v5.8.8_Vagrant_Dev9_8557fee0fd2d0d.box"
|
12
|
+
|
13
|
+
# Boot with a GUI so you can see the screen. (Default is headless)
|
14
|
+
# config.vm.boot_mode = :gui
|
15
|
+
|
16
|
+
# Assign this VM to a host-only network IP, allowing you to access it
|
17
|
+
# via the IP. Host-only networks can talk to the host machine as well as
|
18
|
+
# any other machines on the same network, but cannot be accessed (through this
|
19
|
+
# network interface) by any external networks.
|
20
|
+
config.vm.network :hostonly, "33.33.33.10"
|
21
|
+
|
22
|
+
# Assign this VM to a bridged network, allowing you to connect directly to a
|
23
|
+
# network using the host's network device. This makes the VM appear as another
|
24
|
+
# physical device on your network.
|
25
|
+
|
26
|
+
# config.vm.network :bridged
|
27
|
+
|
28
|
+
# Forward a port from the guest to the host, which allows for outside
|
29
|
+
# computers to access the VM, whereas host only networking does not.
|
30
|
+
# config.vm.forward_port 3000, 3000
|
31
|
+
|
32
|
+
# Share an additional folder to the guest VM. The first argument is
|
33
|
+
# an identifier, the second is the path on the guest to mount the
|
34
|
+
# folder, and the third is the path on the host to the actual folder.
|
35
|
+
|
36
|
+
# Shared folder for the developer_tool.json file. see README.
|
37
|
+
config.vm.share_folder "v-data", "/home/webapps", "."
|
38
|
+
|
39
|
+
config.ssh.max_tries = 40
|
40
|
+
config.ssh.timeout = 120
|
41
|
+
|
42
|
+
config.vm.provision RightscaleProvisoner::RightscaleProvisoner do |rightscale|
|
43
|
+
|
44
|
+
rightscale.email = user_secrets['email']
|
45
|
+
rightscale.password = user_secrets['password']
|
46
|
+
rightscale.account_id = user_secrets['account_id']
|
47
|
+
rightscale.api_url = user_secrets['api_url'] # default: https://www.rightscale.com
|
48
|
+
|
49
|
+
# rightscale.cloud_name = "BlueSkies"
|
50
|
+
rightscale.servertemplate = "PHP App Server (v13.2)"
|
51
|
+
rightscale.deployment_name = "Vagrant Deployment Test" #unique name!
|
52
|
+
rightscale.server_name = "Vagrant Server Test" #unique name!
|
53
|
+
|
54
|
+
rightscale.server_inputs = {
|
55
|
+
|
56
|
+
# database connection
|
57
|
+
"app/database_name" => "text:app_test",
|
58
|
+
"db/dns/master/fqdn" => "text:www.mydomain.com",
|
59
|
+
|
60
|
+
# application code respository
|
61
|
+
"repo/default/account" => "text:github_user@mail.com",
|
62
|
+
"repo/default/credential" => "cred:github_private_key",
|
63
|
+
"repo/default/repository" => "text:git://github.com/rightscale/examples.git",
|
64
|
+
"repo/default/revision" => "text:unified_php",
|
65
|
+
|
66
|
+
# open up port 8000
|
67
|
+
"sys_firewall/rule/enable" => "text:enable",
|
68
|
+
"sys_firewall/rule/port" => "text:8000",
|
69
|
+
"sys_firewall/rule/ip_address" => "text:any",
|
70
|
+
"sys_firewall/rule/protocol" => "text:tcp"
|
71
|
+
}
|
72
|
+
|
73
|
+
# scripts to run after boot list is completed
|
74
|
+
rightscale.operational_scripts = [
|
75
|
+
"sys_firewall::setup_rule"
|
76
|
+
]
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
require "vagrant"
|
2
|
+
require "vagrant/errors"
|
3
|
+
|
4
|
+
require "rightscale_provisioner/version"
|
5
|
+
require "rightscale_provisioner/api15"
|
6
|
+
|
7
|
+
module RightscaleProvisoner
|
8
|
+
class RightscaleProvisoner < Vagrant::Provisioners::Base
|
9
|
+
|
10
|
+
VAGRANT_CLOUD_NAME = "BlueSkies"
|
11
|
+
|
12
|
+
class Config < Vagrant::Config::Base
|
13
|
+
attr_accessor :email
|
14
|
+
attr_accessor :password
|
15
|
+
attr_accessor :account_id
|
16
|
+
attr_accessor :api_url
|
17
|
+
attr_accessor :servertemplate # name or id
|
18
|
+
attr_accessor :deployment_name
|
19
|
+
attr_accessor :multi_cloud_image_name
|
20
|
+
attr_accessor :cloud_name
|
21
|
+
attr_accessor :server_name
|
22
|
+
attr_accessor :server_inputs
|
23
|
+
attr_accessor :operational_scripts
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.config_class
|
27
|
+
Config
|
28
|
+
end
|
29
|
+
|
30
|
+
class RightScaleError < Vagrant::Errors::VagrantError
|
31
|
+
# overrider i18n support for now
|
32
|
+
def translate_error(opts)
|
33
|
+
return nil if !opts[:_key]
|
34
|
+
opts[:_key]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def prepare
|
39
|
+
# open RightScale API connection
|
40
|
+
@conn = API15.new
|
41
|
+
@conn.connection(config.email, config.password, config.account_id, config.api_url)
|
42
|
+
|
43
|
+
# fail if the requested cloud is not registered with RightScale account
|
44
|
+
config.cloud_name ||= VAGRANT_CLOUD_NAME
|
45
|
+
@cloud = @conn.find_cloud_by_name(config.cloud_name)
|
46
|
+
raise "ERROR: cannot find a cloud named: '#{config.cloud_name}'. " +
|
47
|
+
"Please check the spelling of the 'cloud_name' parameter in " +
|
48
|
+
"your Vagrant file and verify the cloud is registered with " +
|
49
|
+
"your RightScale account?" unless @cloud
|
50
|
+
|
51
|
+
if config.operational_scripts
|
52
|
+
raise "ERROR: operational_scripts must be an array of strings" unless config.operational_scripts.kind_of?(Array)
|
53
|
+
end
|
54
|
+
|
55
|
+
# check for existing deployment and server in RightScale account
|
56
|
+
@deployment = @conn.find_deployment_by_name(config.deployment_name)
|
57
|
+
puts "Deployment '#{config.deployment_name}' #{@deployment ? "found." : "not found."}"
|
58
|
+
@server = @conn.find_server_by_name(config.server_name) if @deployment
|
59
|
+
puts "Server '#{config.server_name}' #{@server ? "found." : "not found."}"
|
60
|
+
|
61
|
+
# XXX: fails if the server is not running -- fix me!
|
62
|
+
# if @server
|
63
|
+
# # verify existing server is on the cloud we are requesting, if not fail.
|
64
|
+
# config.cloud_name ||= VAGRANT_CLOUD_NAME
|
65
|
+
# actual_cloud_name = @conn.server_cloud_name(@server)
|
66
|
+
# raise "ERROR: the server is in the '#{actual_cloud_name}' cloud, " +
|
67
|
+
# "and not in the requested '#{config.cloud_name}' cloud.\n" +
|
68
|
+
# "Please delete the server or pick and new server name." if config.cloud_name != actual_cloud_name
|
69
|
+
# end
|
70
|
+
|
71
|
+
unless @deployment && @server
|
72
|
+
# we need to create a server, can we find the servertemplate?
|
73
|
+
@servertemplate = @conn.find_servertemplate(config.servertemplate)
|
74
|
+
raise "ERROR: cannot find ServerTemplate '#{config.servertemplate}'. Did you import it?\n" +
|
75
|
+
"Visit http://bit.ly/VnOiA7 for more info.\n\n" unless @servertemplate
|
76
|
+
# can we find the MCI?
|
77
|
+
#TODO: @mci = @conn.find_multicloudimage_by_name(@servertemplate, config.multi_cloud_image_name)
|
78
|
+
end
|
79
|
+
|
80
|
+
# create deployment and server as needed
|
81
|
+
unless @deployment
|
82
|
+
@deployment = @conn.create_deployment(config.deployment_name)
|
83
|
+
puts "Created deployment."
|
84
|
+
end
|
85
|
+
|
86
|
+
unless @server
|
87
|
+
@server = @conn.create_server(@deployment, @servertemplate, @mci, @cloud, config.server_name)
|
88
|
+
puts "Created server."
|
89
|
+
end
|
90
|
+
|
91
|
+
unless @conn.is_provisioned?(@server)
|
92
|
+
|
93
|
+
# setup any inputs
|
94
|
+
@conn.set_server_inputs(@server, config.server_inputs) if config.server_inputs
|
95
|
+
|
96
|
+
# launch server
|
97
|
+
puts "Launching server..."
|
98
|
+
@server = @conn.launch_server(@server, config.server_inputs)
|
99
|
+
@conn.server_wait_for_state(@server, "booting")
|
100
|
+
end
|
101
|
+
|
102
|
+
if config.cloud_name == VAGRANT_CLOUD_NAME
|
103
|
+
# Vagrant box: grab "Data request URL" from UserData
|
104
|
+
user_data = @server.current_instance.show(:view => "full").user_data
|
105
|
+
puts user_data.inspect
|
106
|
+
@data_request_url = @conn.data_request_url(user_data)
|
107
|
+
puts "Data Request URL: #{@data_request_url}"
|
108
|
+
else
|
109
|
+
@conn.server_wait_for_state(config.server_name, "operational", 30)
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
def provision!
|
115
|
+
if config.cloud_name.nil? || config.cloud_name == VAGRANT_CLOUD_NAME
|
116
|
+
# check for rightimage
|
117
|
+
begin
|
118
|
+
env[:vm].channel.execute("which rs_connect") # TODO: figure out how to test for failure
|
119
|
+
rescue Exception => e
|
120
|
+
msg = "ERROR: This vagrant box does not have RightLink properly installed. Cannot continue."
|
121
|
+
raise RightScaleError.new(msg)
|
122
|
+
end
|
123
|
+
|
124
|
+
# run rs_connect
|
125
|
+
enroll_cmd = "sudo rs_connect --attach #{@data_request_url} --force"
|
126
|
+
puts "Running #{enroll_cmd}"
|
127
|
+
puts env[:vm].channel.execute(enroll_cmd)
|
128
|
+
|
129
|
+
if config.operational_scripts
|
130
|
+
# wait for operational
|
131
|
+
@conn.server_wait_for_state(@server, "operational", 30)
|
132
|
+
|
133
|
+
# run operational scripts
|
134
|
+
puts "Running operational recipes..." if config.operational_scripts
|
135
|
+
config.operational_scripts.each do |script_name|
|
136
|
+
|
137
|
+
# run recipe or rightscript
|
138
|
+
exec = script_name.include?("::") ? "rs_run_recipe" : "rs_run_rightscript"
|
139
|
+
cmd = "sudo #{exec} --name #{script_name}"
|
140
|
+
puts " #{cmd}"
|
141
|
+
puts env[:vm].channel.execute(cmd)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
else
|
146
|
+
puts "RightScale provisioning server on config.cloud_name cloud..."
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def cleanup
|
151
|
+
begin
|
152
|
+
if @server
|
153
|
+
# server terminate
|
154
|
+
@conn.terminate_server(@server)
|
155
|
+
# server delete
|
156
|
+
@conn.destroy_server(@server)
|
157
|
+
end
|
158
|
+
rescue Exception => e
|
159
|
+
puts "WARNING: unable to cleanup server."
|
160
|
+
puts "Message: #{e.message}"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
module RightscaleProvisoner
|
2
|
+
class API15
|
3
|
+
|
4
|
+
attr_reader :client
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
require "right_api_client"
|
8
|
+
end
|
9
|
+
|
10
|
+
def connection(email, password, account_id, api_url = nil)
|
11
|
+
begin
|
12
|
+
args = { :email => email, :password => password, :account_id => account_id }
|
13
|
+
@url = api_url
|
14
|
+
args[:api_url] = @url if @url
|
15
|
+
@connection ||= RightApi::Client.new(args)
|
16
|
+
@client = @connection
|
17
|
+
rescue Exception => e
|
18
|
+
args.delete(:password) # don't log password
|
19
|
+
puts "ERROR: could not connect to RightScale API. Params: #{args.inspect}"
|
20
|
+
puts e.message
|
21
|
+
puts e.backtrace
|
22
|
+
raise e
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def user_data
|
27
|
+
@user_data ||= @server.show.current_instance(:view=>"extended").show.user_data
|
28
|
+
end
|
29
|
+
|
30
|
+
def data_request_url(userdata)
|
31
|
+
data_hash = {}
|
32
|
+
entry = userdata.split('&').select { |entry| entry =~ /RS_rn_auth/i }
|
33
|
+
raise "ERROR: user data token not found. " +
|
34
|
+
"Does your MCI have a provides:rs_agent_type=right_link tag?" unless entry
|
35
|
+
token = entry.first.split('=')[1]
|
36
|
+
"#{@url}/servers/data_injection_payload/#{token}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def find_server_by_name(name)
|
40
|
+
server_list = @connection.servers.index(:filter => ["name==#{name}"])
|
41
|
+
raise "More than one server with the name of '#{name}'. " +
|
42
|
+
"Please fix via the RightScale dashboard and retry." if server_list.size > 1
|
43
|
+
server_list.first
|
44
|
+
end
|
45
|
+
|
46
|
+
def find_deployment_by_name(name)
|
47
|
+
deployment = nil
|
48
|
+
deployments_list = @connection.deployments.index(:filter => ["name==#{name}"])
|
49
|
+
raise "More than one deployment with the name of '#{name}'. " +
|
50
|
+
"Please fix via the RightScale dashboard and retry." if deployments_list.size > 1
|
51
|
+
deployment = deployments_list.first unless deployments_list.empty?
|
52
|
+
deployment
|
53
|
+
end
|
54
|
+
|
55
|
+
# returns:: String if cloud is found, nil if not found
|
56
|
+
def find_cloud_by_name(name)
|
57
|
+
cloud = nil
|
58
|
+
cloud_list = @connection.clouds.index(:filter => ["name==#{name}"])
|
59
|
+
raise "More than one cloud with the name of '#{name}'. " +
|
60
|
+
"Please fix via the RightScale dashboard and retry." if cloud_list.size > 1
|
61
|
+
cloud = cloud_list.first unless cloud_list.empty?
|
62
|
+
cloud
|
63
|
+
end
|
64
|
+
|
65
|
+
def find_mci_by_name(server_template, mci_name)
|
66
|
+
mci = nil
|
67
|
+
mci_list = @connection.mcis.index(:filter => ["name==#{name}"])
|
68
|
+
raise "More than one MultiCloud image with the name of '#{name}'. " +
|
69
|
+
"Please fix via the RightScale dashboard and retry." if mci_list.size > 1
|
70
|
+
mci = mci_list.first unless mci_list.empty?
|
71
|
+
mci
|
72
|
+
end
|
73
|
+
|
74
|
+
def find_servertemplate(name_or_id)
|
75
|
+
server_template = nil; id = nil; name = nil
|
76
|
+
|
77
|
+
# detect if user passed in a name or an id
|
78
|
+
# there is probably a cleaner way to do this, but I am lazy ATM.
|
79
|
+
begin
|
80
|
+
id = Integer(name_or_id)
|
81
|
+
rescue Exception => e
|
82
|
+
name = name_or_id # Cannot be case to integer, assume a name was passed
|
83
|
+
end
|
84
|
+
|
85
|
+
if name
|
86
|
+
# find ServerTemplate by name
|
87
|
+
st_list = @connection.server_templates.index(:filter => ["name==#{name}"])
|
88
|
+
num_matching_sts = 0
|
89
|
+
st_list.each do |st|
|
90
|
+
if st.name == name
|
91
|
+
server_template = st
|
92
|
+
num_matching_sts += 1
|
93
|
+
end
|
94
|
+
end
|
95
|
+
raise "ERROR: Unable to find ServerTemplate with the name of '#{name}' found " unless server_template
|
96
|
+
raise "ERROR: More than one ServerTemplate with the name of '#{name}' found " +
|
97
|
+
"in account. Please fix via the RightScale dashboard and retry." if num_matching_sts > 1
|
98
|
+
|
99
|
+
else
|
100
|
+
# find ServerTemplate by id
|
101
|
+
server_template = @connection.server_templates.index(:id => id)
|
102
|
+
end
|
103
|
+
|
104
|
+
server_template
|
105
|
+
end
|
106
|
+
|
107
|
+
def create_deployment(name)
|
108
|
+
@connection.deployments.create(:deployment => { :name => name, :decription => "Created by the Vagrant"})
|
109
|
+
end
|
110
|
+
|
111
|
+
def destroy_deployment(deployment)
|
112
|
+
deployment.destroy
|
113
|
+
end
|
114
|
+
|
115
|
+
def create_server(deployment, server_template, mci, cloud, name)
|
116
|
+
|
117
|
+
#TODO: mci param not used yet
|
118
|
+
|
119
|
+
# check params
|
120
|
+
unless st_href = server_template.show.href
|
121
|
+
raise "ERROR: ServerTemplate parameter not initialized properly"
|
122
|
+
end
|
123
|
+
|
124
|
+
unless d_href = deployment.show.href
|
125
|
+
raise "ERROR: Deployment parameter not initialized properly"
|
126
|
+
end
|
127
|
+
|
128
|
+
unless c_href = cloud.show.href
|
129
|
+
raise "ERROR: Deployment parameter not initialized properly"
|
130
|
+
end
|
131
|
+
|
132
|
+
# create server in deployment using specfied ST
|
133
|
+
server =
|
134
|
+
@connection.servers.create({
|
135
|
+
:server => {
|
136
|
+
:name => name,
|
137
|
+
:decription => "Created by the Vagrant",
|
138
|
+
:deployment_href => d_href,
|
139
|
+
:instance => {
|
140
|
+
:cloud_href => c_href,
|
141
|
+
:server_template_href => st_href
|
142
|
+
}
|
143
|
+
}
|
144
|
+
})
|
145
|
+
end
|
146
|
+
|
147
|
+
def is_provisioned?(server)
|
148
|
+
server.show.api_methods.include?(:current_instance)
|
149
|
+
end
|
150
|
+
|
151
|
+
# @param(Hash) inputs Hash input name/value pairs i.e. { :name => "text:dummy"}
|
152
|
+
def launch_server(server, inputs = { :name => "text:dummy"})
|
153
|
+
server_name = server.show.name
|
154
|
+
server.launch(inputs) # TODO: parse inputs from Vagrantfile
|
155
|
+
# XXX: need to create a new server object after launch -- why? API bug?
|
156
|
+
find_server_by_name(server_name)
|
157
|
+
end
|
158
|
+
|
159
|
+
def terminate_server(server)
|
160
|
+
server.terminate
|
161
|
+
end
|
162
|
+
|
163
|
+
# Only use this *before* you launch the server
|
164
|
+
def set_server_inputs(server, inputs)
|
165
|
+
server.show.next_instance.show.inputs.multi_update({"inputs" => inputs})
|
166
|
+
end
|
167
|
+
|
168
|
+
def server_wait_for_state(server, state, delay = 1)
|
169
|
+
while instance_from_server(server).show.state != state
|
170
|
+
puts "Waiting for instance to be in #{state} state..."
|
171
|
+
sleep delay
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def server_cloud_name(server)
|
176
|
+
instance = instance_from_server(server)
|
177
|
+
cloud = cloud_from_instance(instance)
|
178
|
+
cloud.show.name
|
179
|
+
end
|
180
|
+
|
181
|
+
private
|
182
|
+
|
183
|
+
def server_state(server)
|
184
|
+
instance_from_server(server)
|
185
|
+
end
|
186
|
+
|
187
|
+
def instance_from_server(server)
|
188
|
+
server_data = server.show
|
189
|
+
if is_provisioned?(server)
|
190
|
+
server_data.current_instance
|
191
|
+
else
|
192
|
+
server_data.next_instance
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def cloud_from_instance(instance)
|
197
|
+
instance.show.cloud
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
201
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'rightscale_provisioner/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "rightscale_provisioner"
|
8
|
+
gem.version = RightscaleProvisoner::VERSION
|
9
|
+
gem.authors = ["caryp", "sumner"]
|
10
|
+
gem.email = ["cary@rightscale.com", "sumner@rightscale.com"]
|
11
|
+
gem.description = %q{Enables the provisioning of local VMs using RightScale}
|
12
|
+
gem.summary = %q{RightScale provisioner for Vagrant}
|
13
|
+
gem.homepage = "http://github.com/caryp/rightscale_provisioner"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency('right_api_client', '>= 1.5.9')
|
21
|
+
gem.add_dependency('vagrant', '~> 1.0.0')
|
22
|
+
|
23
|
+
gem.add_development_dependency('rspec', '~> 2.5')
|
24
|
+
gem.add_development_dependency('rake', '~> 10.0.3')
|
25
|
+
end
|
data/spec/api15_spec.rb
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'json'
|
3
|
+
require 'right_api_client'
|
4
|
+
|
5
|
+
describe "API15 object" do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@api = RightscaleProvisoner::API15.new()
|
9
|
+
apiStub = double("RightApi::Client")
|
10
|
+
RightApi::Client.should_receive(:new).and_return(apiStub)
|
11
|
+
@api.connection("someemail", "somepasswd", "someaccountid", "https://my.rightscale.com")
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should find deployment by name" do
|
15
|
+
deploymentsStub = stub("deployments", :index => [ :name => "my_fake_deployment" ])
|
16
|
+
@api.instance_variable_get("@connection").should_receive(:deployments).and_return(deploymentsStub)
|
17
|
+
@api.find_deployment_by_name("my_fake_deployment")
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should raise error if deployment not found by name" do
|
21
|
+
deploymentsStub = stub("deployments", :index => nil)
|
22
|
+
@api.instance_variable_get("@connection").should_receive(:deployments).and_return(deploymentsStub)
|
23
|
+
lambda{@api.find_deployment_by_name("my_fake_deployment")}.should raise_error
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should raise error if multiple deployments found by name" do
|
27
|
+
deploymentsStub = stub("deployments", :index => [ {:name => "my_fake_deployment"}, {:name => "my_fake_deployment2"} ])
|
28
|
+
@api.instance_variable_get("@connection").should_receive(:deployments).and_return(deploymentsStub)
|
29
|
+
lambda{@api.find_deployment_by_name("my_fake_deployment")}.should raise_error
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should find server by name" do
|
33
|
+
serversStub = stub("servers", :index => [ :name => "my_fake_server" ])
|
34
|
+
@api.instance_variable_get("@connection").should_receive(:servers).and_return(serversStub)
|
35
|
+
@api.find_server_by_name("my_fake_server")
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should raise error if multiple servers found by name" do
|
39
|
+
serversStub = stub("servers", :index => [ {:name => "my_fake_server"}, {:name => "my_fake_server2"} ])
|
40
|
+
@api.instance_variable_get("@connection").should_receive(:servers).and_return(serversStub)
|
41
|
+
lambda{@api.find_server_by_name("my_fake_server")}.should raise_error
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should find MCI by name" do
|
45
|
+
pending ("TODO: add support for multi_cloud_image_name")
|
46
|
+
mcisStub = stub("mcis", :index => [ :name => "my_fake_mci" ])
|
47
|
+
@api.instance_variable_get("@connection").should_receive(:mcis).and_return(mcisStub)
|
48
|
+
@api.find_mci_by_name("my_fake_mci")
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should raise error if multiple MCI found by name" do
|
52
|
+
pending ("TODO: add support for multi_cloud_image_name")
|
53
|
+
mcisStub = stub("mcis", :index => [ {:name => "my_fake_mci"}, {:name => "my_fake_mci2"} ])
|
54
|
+
@api.instance_variable_get("@connection").should_receive(:mcis).and_return(mcisStub)
|
55
|
+
lambda{@api.find_mci_by_name("my_fake_mci")}.should raise_error
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should find servertemplate by name" do
|
59
|
+
servertemplatesStub = stub("servertemplates", :index => [ stub("servertemplate", :name => "my_fake_servertemplate") ])
|
60
|
+
@api.instance_variable_get("@connection").should_receive(:server_templates).and_return(servertemplatesStub)
|
61
|
+
@api.find_servertemplate("my_fake_servertemplate")
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should raise error if no servertemplates found by name" do
|
65
|
+
servertemplatesStub = stub("servertemplates", :index => [])
|
66
|
+
@api.instance_variable_get("@connection").should_receive(:server_templates).and_return(servertemplatesStub)
|
67
|
+
lambda{@api.find_servertemplate("my_fake_servertemplate")}.should raise_error
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should raise error if multiple servertemplates found by name" do
|
71
|
+
servertemplatesStub = stub("servertemplates", :index => [ stub("servertemplate", :name => "my_fake_servertemplate"), stub("servertemplate", :name => "my_fake_servertemplate") ])
|
72
|
+
@api.instance_variable_get("@connection").should_receive(:server_templates).and_return(servertemplatesStub)
|
73
|
+
lambda{@api.find_servertemplate("my_fake_servertemplate")}.should raise_error
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should find servertemplate by id" do
|
77
|
+
servertemplatesStub = stub("servertemplates", :index => [ :name => "my_fake_servertemplate" ])
|
78
|
+
@api.instance_variable_get("@connection").should_receive(:server_templates).and_return(servertemplatesStub)
|
79
|
+
@api.find_servertemplate(1234)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should create deployment" do
|
83
|
+
deploymentsStub = stub("deployments", :create => [ {:name => "my_fake_deployment"} ])
|
84
|
+
@api.instance_variable_get("@connection").should_receive(:deployments).and_return(deploymentsStub)
|
85
|
+
deploymentsStub.should_receive(:create)
|
86
|
+
@api.create_deployment("my_deployment")
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should create server" do
|
90
|
+
dStub = stub("deployment", :href => "/some/fake/path")
|
91
|
+
dsStub = stub("deployments", :show => dStub)
|
92
|
+
@api.should_receive(:create_deployment).and_return(dsStub)
|
93
|
+
deployment = @api.create_deployment("my_deployment")
|
94
|
+
|
95
|
+
stStub = stub("servertemplate", :href => "/some/fake/path", :show => "")
|
96
|
+
stsStub = stub("servertemplates", :show => stStub)
|
97
|
+
@api.should_receive(:find_servertemplate).and_return(stsStub)
|
98
|
+
server_template = @api.find_servertemplate(1234)
|
99
|
+
|
100
|
+
cStub = stub("cloud", :href => "/some/fake/path")
|
101
|
+
csStub = stub("clouds", :show => cStub)
|
102
|
+
@api.should_receive(:find_cloud_by_name).and_return(csStub)
|
103
|
+
cloud = @api.find_cloud_by_name(1234)
|
104
|
+
|
105
|
+
serversStub = stub("servers", :create => [ :name => "my_fake_server" ])
|
106
|
+
@api.instance_variable_get("@connection").should_receive(:servers).and_return(serversStub)
|
107
|
+
@api.create_server(deployment, server_template, nil, cloud, "my_fake_server")
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should launch server with inputs" do
|
111
|
+
serverStub = stub("server", :name => "foo")
|
112
|
+
serversStub = stub("servers", :launch => true, :show => serverStub, :index => [ :name => "my_fake_server" ])
|
113
|
+
@api.should_receive(:create_server).and_return(serversStub)
|
114
|
+
server = @api.create_server("foo", "bar", "my_fake_server")
|
115
|
+
@api.instance_variable_get("@connection").should_receive(:servers).and_return(serversStub)
|
116
|
+
@api.launch_server(server, [ {:name => "input1", :value => 1} ])
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should launch server without inputs" do
|
120
|
+
serverStub = stub("server", :name => "foo")
|
121
|
+
serversStub = stub("servers", :launch => true, :show => serverStub, :index => [ :name => "my_fake_server" ])
|
122
|
+
@api.should_receive(:create_server).and_return(serversStub)
|
123
|
+
server = @api.create_server("foo", "bar", "my_fake_server")
|
124
|
+
@api.instance_variable_get("@connection").should_receive(:servers).and_return(serversStub)
|
125
|
+
@api.launch_server(server)
|
126
|
+
end
|
127
|
+
|
128
|
+
it "returns data_request_url for instance" do
|
129
|
+
@user_data = "RS_rn_url=amqp://b915586461:278a854748@orange2-broker.test.rightscale.com/right_net&RS_rn_id=4985249009&RS_server=orange2-moo.test.rightscale.com&RS_rn_auth=d98106775832c174ffd55bd7b7cb175077574adf&RS_token=b233a57d1d24f27bd8650d0f9b6bfd54&RS_sketchy=sketchy1-145.rightscale.com&RS_rn_host=:0"
|
130
|
+
@request_data_url = "https://my.rightscale.com/servers/data_injection_payload/d98106775832c174ffd55bd7b7cb175077574adf"
|
131
|
+
|
132
|
+
@api.data_request_url(@user_data).should == @request_data_url
|
133
|
+
end
|
134
|
+
|
135
|
+
it "waits for state to change from booting state" do
|
136
|
+
pending "TODO"
|
137
|
+
end
|
138
|
+
|
139
|
+
it "fails if the server's cloud is not the requested cloud" do
|
140
|
+
pending "TODO"
|
141
|
+
end
|
142
|
+
|
143
|
+
it "sets inputs on the next instance" do
|
144
|
+
pending "TODO"
|
145
|
+
end
|
146
|
+
|
147
|
+
it "terminates a server" do
|
148
|
+
pending "TODO"
|
149
|
+
serverStub = stub("server", :name => "foo")
|
150
|
+
# serversStub = stub("servers", :launch => true, :show => serverStub, :index => [ :name => "my_fake_server" ])
|
151
|
+
# @api.should_receive(:create_server).and_return(serversStub)
|
152
|
+
# server = @api.create_server("foo", "bar", "my_fake_server")
|
153
|
+
# @api.instance_variable_get("@connection").should_receive(:servers).and_return(serversStub)
|
154
|
+
@api.terminate_server(serverStub)
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'json'
|
3
|
+
require 'right_api_client'
|
4
|
+
|
5
|
+
describe "RightccaleProvisioner object" do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
# @api = RightscaleProvisoner::API15.new()
|
9
|
+
# apiStub = double("RightApi::Client")
|
10
|
+
# RightApi::Client.should_receive(:new).and_return(apiStub)
|
11
|
+
# @api.connection("someemail", "somepasswd", "someaccountid", "https://my.rightscale.com")
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should find deployment by name" do
|
15
|
+
pending "TODO"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should fail if cloud is not found" do
|
19
|
+
pending "TODO"
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should fail if ServerTemplate is not found" do
|
23
|
+
pending "TODO"
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rightscale_provisioner
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- caryp
|
8
|
+
- sumner
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-06-04 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: right_api_client
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ! '>='
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 1.5.9
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ! '>='
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 1.5.9
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: vagrant
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ~>
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 1.0.0
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ~>
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 1.0.0
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rspec
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ~>
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '2.5'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ~>
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '2.5'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: rake
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ~>
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 10.0.3
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 10.0.3
|
70
|
+
description: Enables the provisioning of local VMs using RightScale
|
71
|
+
email:
|
72
|
+
- cary@rightscale.com
|
73
|
+
- sumner@rightscale.com
|
74
|
+
executables: []
|
75
|
+
extensions: []
|
76
|
+
extra_rdoc_files: []
|
77
|
+
files:
|
78
|
+
- .gitignore
|
79
|
+
- Gemfile
|
80
|
+
- LICENSE.txt
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- config/Gemfile
|
84
|
+
- config/Vagrantfile
|
85
|
+
- config/rightscale_provisioner.cfg.example
|
86
|
+
- lib/rightscale_provisioner.rb
|
87
|
+
- lib/rightscale_provisioner/api15.rb
|
88
|
+
- lib/rightscale_provisioner/version.rb
|
89
|
+
- rightscale_provisoner.gemspec
|
90
|
+
- spec/api15_spec.rb
|
91
|
+
- spec/rightscale_provisioner_spec.rb
|
92
|
+
- spec/spec_helper.rb
|
93
|
+
homepage: http://github.com/caryp/rightscale_provisioner
|
94
|
+
licenses: []
|
95
|
+
metadata: {}
|
96
|
+
post_install_message:
|
97
|
+
rdoc_options: []
|
98
|
+
require_paths:
|
99
|
+
- lib
|
100
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ! '>='
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
requirements: []
|
111
|
+
rubyforge_project:
|
112
|
+
rubygems_version: 2.0.3
|
113
|
+
signing_key:
|
114
|
+
specification_version: 4
|
115
|
+
summary: RightScale provisioner for Vagrant
|
116
|
+
test_files:
|
117
|
+
- spec/api15_spec.rb
|
118
|
+
- spec/rightscale_provisioner_spec.rb
|
119
|
+
- spec/spec_helper.rb
|
120
|
+
has_rdoc:
|