rightscale_provisioner 0.0.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.
- 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:
|