appscake 0.0.1 → 0.0.2
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.
- data/LICENSE.txt +45 -0
- data/README.txt +56 -0
- data/Rakefile +42 -0
- data/bin/appscake +164 -4
- data/certificates/cert-appscake.pem +25 -0
- data/certificates/pk-appscake.pem +27 -0
- data/lib/appscake_utils.rb +325 -0
- data/public/bootstrap/css/bootstrap-responsive.css +1058 -0
- data/public/bootstrap/css/bootstrap-responsive.min.css +9 -0
- data/public/bootstrap/css/bootstrap.css +5774 -0
- data/public/bootstrap/css/bootstrap.min.css +9 -0
- data/public/bootstrap/img/glyphicons-halflings-white.png +0 -0
- data/public/bootstrap/img/glyphicons-halflings.png +0 -0
- data/public/bootstrap/js/bootstrap.js +2027 -0
- data/public/bootstrap/js/bootstrap.min.js +6 -0
- data/public/css/bootstrap-responsive.css +1058 -0
- data/public/css/bootstrap-responsive.min.css +9 -0
- data/public/css/bootstrap.css +5774 -0
- data/public/css/bootstrap.min.css +16 -0
- data/public/img/glyphicons-halflings-white.png +0 -0
- data/public/img/glyphicons-halflings.png +0 -0
- data/public/js/appscake_validator.js +93 -0
- data/public/js/bootstrap.js +2027 -0
- data/public/js/bootstrap.min.js +6 -0
- data/public/js/jquery-1.7.1.min.js +4 -0
- data/public/js/jquery.validate.min.js +51 -0
- data/views/_common.erb +69 -0
- data/views/_ec2.erb +129 -0
- data/views/_virtual.erb +34 -0
- data/views/error.erb +29 -0
- data/views/index.erb +56 -0
- data/views/success.erb +24 -0
- data/views/view_log.erb +166 -0
- metadata +37 -5
data/LICENSE.txt
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
Software License Agreement (BSD License)
|
2
|
+
|
3
|
+
Copyright (c) 2008, Regents of the University of California
|
4
|
+
All rights reserved.
|
5
|
+
|
6
|
+
Redistribution and use of this software in source and binary forms, with or
|
7
|
+
without modification, are permitted provided that the following conditions
|
8
|
+
are met:
|
9
|
+
|
10
|
+
* Redistributions of source code must retain the above
|
11
|
+
copyright notice, this list of conditions and the
|
12
|
+
following disclaimer.
|
13
|
+
|
14
|
+
* Redistributions in binary form must reproduce the above
|
15
|
+
copyright notice, this list of conditions and the
|
16
|
+
following disclaimer in the documentation and/or other
|
17
|
+
materials provided with the distribution.
|
18
|
+
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
22
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
23
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
24
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
25
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
26
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
27
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
28
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
29
|
+
POSSIBILITY OF SUCH DAMAGE. USERS OF THIS SOFTWARE ACKNOWLEDGE THE POSSIBLE
|
30
|
+
PRESENCE OF OTHER OPEN SOURCE LICENSED MATERIAL, COPYRIGHTED MATERIAL OR
|
31
|
+
PATENTED MATERIAL IN THIS SOFTWARE, AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY
|
32
|
+
DISCOVERING IT MAY INFORM DR. CHANDRA KRINTZ AT THE UNIVERSITY OF CALIFORNIA,
|
33
|
+
SANTA BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY, WHICH
|
34
|
+
IN THE REGENTS’ DISCRETION MAY INCLUDE, WITHOUT LIMITATION, REPLACEMENT
|
35
|
+
OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO IDENTIFIED, OR WITHDRAWAL
|
36
|
+
OF THE CODE CAPABILITY TO THE EXTENT NEEDED TO COMPLY WITH ANY SUCH LICENSES
|
37
|
+
OR RIGHTS.
|
38
|
+
|
39
|
+
This software makes use of third party software libraries released under different
|
40
|
+
licenses. The table below lists all the contained libraries and the licenses under
|
41
|
+
which they are provided to you.
|
42
|
+
|
43
|
+
* Twitter Bootstrap - Apache Software License 2.0
|
44
|
+
* jQuery Validation Plugin - MIT License
|
45
|
+
|
data/README.txt
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
AppsCake - Web Frontend for AppScale Tools
|
2
|
+
==========================================
|
3
|
+
|
4
|
+
"AppsCake makes deploying AppScale a piece of cake"
|
5
|
+
|
6
|
+
AppsCake is a simple and lightweight web application that allows users to
|
7
|
+
interact with AppScale tools over the web. This way even those users who
|
8
|
+
are not familiar with general cloud principles or those who are not
|
9
|
+
comfortable with working with a traditional command line interface can
|
10
|
+
get started with deploying AppScale clouds and AppScale cloud applications.
|
11
|
+
|
12
|
+
AppsCake has been developed using the Ruby programming language and is
|
13
|
+
based on Sinatra. So far it has been tested in Xen and EC2 cloud
|
14
|
+
environments.
|
15
|
+
|
16
|
+
Prerequisites
|
17
|
+
=============
|
18
|
+
|
19
|
+
Following software is required to install and run AppsCake:
|
20
|
+
|
21
|
+
1. Ruby interpreter
|
22
|
+
2. Sinatra gem
|
23
|
+
3. AppScale tools gem (or alternatively you can install AppScale tools
|
24
|
+
binary distribution on your machine/VM and put them in the PATH)
|
25
|
+
|
26
|
+
Installation
|
27
|
+
============
|
28
|
+
|
29
|
+
There are 2 ways to install AppsCake:
|
30
|
+
|
31
|
+
1. Pull the source from the Github
|
32
|
+
- https://github.com/AppScale/appscake
|
33
|
+
|
34
|
+
2. Install the AppsCake gem
|
35
|
+
sudo gem install appscake
|
36
|
+
|
37
|
+
Running AppsCake
|
38
|
+
================
|
39
|
+
|
40
|
+
To launch AppsCake, simply go into the bin directory and execute the
|
41
|
+
'appscake' script:
|
42
|
+
|
43
|
+
./appscake
|
44
|
+
|
45
|
+
Once the server has started up, fire up your web browser and navigate
|
46
|
+
to https://<appscake-host>:8443
|
47
|
+
|
48
|
+
If you're installing AppsCake on an EC2 image make sure to modify your
|
49
|
+
security group so that it allows inbound traffic on the port 8443.
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
======================================================================
|
54
|
+
AppsCake is a project by the UCSB RACELab
|
55
|
+
http://appscake.cs.ucsb.edu
|
56
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/gempackagetask'
|
4
|
+
require 'rake/testtask'
|
5
|
+
|
6
|
+
|
7
|
+
# TODO(cgb): This probably should be moved into a Gemfile and out of this file.
|
8
|
+
spec = Gem::Specification.new do |s|
|
9
|
+
s.name = 'appscake'
|
10
|
+
s.version = '0.0.2'
|
11
|
+
|
12
|
+
s.summary = "A web interface to the AppScale command-line tools."
|
13
|
+
s.description = <<-EOF
|
14
|
+
AppsCake provides a pretty web interface that can be used to deploy
|
15
|
+
AppScale over machines in Xen, KVM, Amazon EC2, or Eucalyptus. In
|
16
|
+
short, it makes deploying AppScale a piece of cake!
|
17
|
+
EOF
|
18
|
+
|
19
|
+
s.author = "Hiranya Jayathilaka"
|
20
|
+
s.email = "appscale_community@googlegroups.com"
|
21
|
+
s.homepage = "http://appscale.cs.ucsb.edu"
|
22
|
+
|
23
|
+
s.executables = ["appscake"]
|
24
|
+
s.default_executable = 'appscake'
|
25
|
+
s.platform = Gem::Platform::RUBY
|
26
|
+
|
27
|
+
candidates = Dir.glob("**/*")
|
28
|
+
s.files = candidates.delete_if do |item|
|
29
|
+
item.include?(".git") || item.include?("rdoc") || item.include?("pkg")
|
30
|
+
end
|
31
|
+
s.require_path = "lib"
|
32
|
+
s.autorequire = "appscake_utils"
|
33
|
+
|
34
|
+
s.has_rdoc = false
|
35
|
+
s.add_dependency('net-ssh', '>= 2.6.0')
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
# responds to 'rake gem'
|
40
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
41
|
+
pkg.need_tar = true
|
42
|
+
end
|
data/bin/appscake
CHANGED
@@ -1,5 +1,165 @@
|
|
1
|
-
#!/bin/
|
2
|
-
#
|
3
|
-
#
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# Author: Hiranya Jayathilaka (hiranya@cs.ucsb.edu)
|
3
|
+
# AppsCake web interface for deploying and launching AppScale clouds
|
4
|
+
# AppsCake = Makes deploying AppScale a 'piece of cake'
|
4
5
|
|
5
|
-
|
6
|
+
require 'rubygems'
|
7
|
+
require 'sinatra/base'
|
8
|
+
require 'webrick'
|
9
|
+
require 'webrick/https'
|
10
|
+
require 'openssl'
|
11
|
+
|
12
|
+
CERT_DIR = File.expand_path(File.join(File.dirname(__FILE__), "..", "certificates"))
|
13
|
+
|
14
|
+
LIB_DIR = File.expand_path(File.join(File.dirname(__FILE__), "..", "lib"))
|
15
|
+
$:.unshift LIB_DIR
|
16
|
+
require 'appscake_utils'
|
17
|
+
|
18
|
+
puts "\nAppsCake - Makes deploying AppScale a 'piece of cake'!\n\n"
|
19
|
+
|
20
|
+
class AppsCake < Sinatra::Base
|
21
|
+
set :views, settings.root + '/../views'
|
22
|
+
set :public_folder, settings.root + '/../public'
|
23
|
+
|
24
|
+
get '/' do
|
25
|
+
erb :index
|
26
|
+
end
|
27
|
+
|
28
|
+
post '/virtual.do' do
|
29
|
+
if locked?
|
30
|
+
return report_error("Server Busy", "AppsCake is currently busy deploying a cloud." +
|
31
|
+
" Please try again later.")
|
32
|
+
end
|
33
|
+
|
34
|
+
status, yaml_result, yaml = validate_yaml(params[:ips])
|
35
|
+
if !status
|
36
|
+
return report_error("IP Configuration Error", yaml_result)
|
37
|
+
end
|
38
|
+
|
39
|
+
status,acc_result = validate_appscale_credentials(params[:virtual_user],
|
40
|
+
params[:virtual_pass], params[:virtual_pass2])
|
41
|
+
if !status
|
42
|
+
return report_error("AppScale Administrator Account Configuration Error", acc_result)
|
43
|
+
end
|
44
|
+
|
45
|
+
status,ssh_result = validate_ssh_credentials(params[:virtual_keyname], params[:root_password], yaml)
|
46
|
+
if !status
|
47
|
+
return report_error("AppScale SSH Configuration Error", ssh_result)
|
48
|
+
end
|
49
|
+
|
50
|
+
add_key_options = {
|
51
|
+
'ips' => yaml,
|
52
|
+
'keyname' => params[:virtual_keyname],
|
53
|
+
'auto' => true,
|
54
|
+
'root_password' => params[:root_password]
|
55
|
+
}
|
56
|
+
|
57
|
+
app_name = nil
|
58
|
+
file_location = nil
|
59
|
+
if !params[:target_app].nil? and params[:target_app] != '_none_'
|
60
|
+
puts params[:target_app]
|
61
|
+
app_name = params[:target_app]
|
62
|
+
file_location = File.join(File.dirname(__FILE__), "repository", params[:target_app])
|
63
|
+
end
|
64
|
+
|
65
|
+
run_instances_options = {
|
66
|
+
'ips' => yaml,
|
67
|
+
'keyname' => params[:virtual_keyname],
|
68
|
+
'file_location' => file_location,
|
69
|
+
'appname' => app_name,
|
70
|
+
'appengine' => 1,
|
71
|
+
'autoscale' => true,
|
72
|
+
'separate' => false,
|
73
|
+
'confirm' => false,
|
74
|
+
'table' => 'cassandra',
|
75
|
+
'infrastructure' => nil,
|
76
|
+
'admin_user' => params[:virtual_user],
|
77
|
+
'admin_pass' => params[:virtual_pass]
|
78
|
+
}
|
79
|
+
|
80
|
+
deploy_on_virtual_cluster(params, add_key_options, run_instances_options, yaml_result)
|
81
|
+
end
|
82
|
+
|
83
|
+
post '/iaas_ec2.do' do
|
84
|
+
status, result = validate_ec2_cluster_settings(params[:min], params[:max], params[:ami])
|
85
|
+
if !status
|
86
|
+
return report_error("Cluster Configuration Error", result)
|
87
|
+
end
|
88
|
+
|
89
|
+
status, result = validate_ec2_credentials(params[:username], params[:access_key],
|
90
|
+
params[:secret_key], params[:region])
|
91
|
+
if !status
|
92
|
+
return report_error("EC2 Security Configuration Error", result)
|
93
|
+
end
|
94
|
+
|
95
|
+
status, result = validate_ec2_certificate_uploads(params[:username], params[:private_key],
|
96
|
+
params[:cert])
|
97
|
+
if !status
|
98
|
+
return report_error("EC2 Security Configuration Error", result)
|
99
|
+
end
|
100
|
+
cert_timestamp = result
|
101
|
+
|
102
|
+
status,acc_result = validate_appscale_credentials(params[:ec2_user],
|
103
|
+
params[:ec2_pass], params[:ec2_pass2])
|
104
|
+
if !status
|
105
|
+
return report_error("AppScale Administrator Account Configuration Error", acc_result)
|
106
|
+
end
|
107
|
+
|
108
|
+
app_name = nil
|
109
|
+
file_location = nil
|
110
|
+
if !params[:target_app].nil? and params[:target_app] != '_none_'
|
111
|
+
app_name = params[:target_app]
|
112
|
+
file_location = File.join(File.dirname(__FILE__), "repository", params[:target_app])
|
113
|
+
end
|
114
|
+
|
115
|
+
run_instances_options = {
|
116
|
+
'keyname' => params[:ec2_keyname],
|
117
|
+
'group' => params[:ec2_keyname],
|
118
|
+
'file_location' => file_location,
|
119
|
+
'appname' => app_name,
|
120
|
+
'appengine' => 1,
|
121
|
+
'autoscale' => true,
|
122
|
+
'separate' => false,
|
123
|
+
'confirm' => false,
|
124
|
+
'table' => 'cassandra',
|
125
|
+
'infrastructure' => 'ec2',
|
126
|
+
'min_images' => params[:min].to_i,
|
127
|
+
'max_images' => params[:max].to_i,
|
128
|
+
'instance_type' => params[:instance_type],
|
129
|
+
'machine' => params[:ami],
|
130
|
+
'admin_user' => params[:ec2_user],
|
131
|
+
'admin_pass' => params[:ec2_pass],
|
132
|
+
}
|
133
|
+
deploy_on_ec2(params, run_instances_options, cert_timestamp)
|
134
|
+
end
|
135
|
+
|
136
|
+
get '/view_logs' do
|
137
|
+
timestamp = params[:ts]
|
138
|
+
if timestamp.nil? or timestamp.length == 0
|
139
|
+
return report_error("Invalid URL Request", "No timestamp information found in the request")
|
140
|
+
end
|
141
|
+
@timestamp = timestamp
|
142
|
+
erb :view_log
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
webrick_options = {
|
147
|
+
:Port => 8443,
|
148
|
+
:Logger => WEBrick::Log::new($stderr, WEBrick::Log::INFO),
|
149
|
+
:DocumentRoot => "/ruby/htdocs",
|
150
|
+
:SSLEnable => true,
|
151
|
+
:SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE,
|
152
|
+
:SSLCertificate => OpenSSL::X509::Certificate.new(
|
153
|
+
File.open(File.join(CERT_DIR, "cert-appscake.pem")).read),
|
154
|
+
:SSLPrivateKey => OpenSSL::PKey::RSA.new(
|
155
|
+
File.open(File.join(CERT_DIR, "pk-appscake.pem")).read),
|
156
|
+
:SSLCertName => [["CN", WEBrick::Utils::getservername]],
|
157
|
+
:app => AppsCake,
|
158
|
+
:server => 'webrick'
|
159
|
+
}
|
160
|
+
|
161
|
+
Rack::Server.start webrick_options
|
162
|
+
|
163
|
+
at_exit do
|
164
|
+
puts "Terminating AppsCake..."
|
165
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIELzCCAxegAwIBAgIJAOIyrR3Lx40pMA0GCSqGSIb3DQEBBQUAMIGtMQswCQYD
|
3
|
+
VQQGEwJVUzELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDVNhbnRhIEJhcmJhcmExGTAX
|
4
|
+
BgNVBAoMEFVDIFNhbnRhIEJhcmJhcmExJzAlBgNVBAsMHkRlcGFydG1lbnQgb2Yg
|
5
|
+
Q29tcHV0ZXIgU2NpZW5jZTERMA8GA1UEAwwIQXBwU2NhbGUxIjAgBgkqhkiG9w0B
|
6
|
+
CQEWE2hpcmFueWFAY3MudWNzYi5lZHUwHhcNMTIwOTI1MjEyOTIzWhcNMTIxMDI1
|
7
|
+
MjEyOTIzWjCBrTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRYwFAYDVQQHDA1T
|
8
|
+
YW50YSBCYXJiYXJhMRkwFwYDVQQKDBBVQyBTYW50YSBCYXJiYXJhMScwJQYDVQQL
|
9
|
+
DB5EZXBhcnRtZW50IG9mIENvbXB1dGVyIFNjaWVuY2UxETAPBgNVBAMMCEFwcFNj
|
10
|
+
YWxlMSIwIAYJKoZIhvcNAQkBFhNoaXJhbnlhQGNzLnVjc2IuZWR1MIIBIjANBgkq
|
11
|
+
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0uzoMSPlUZrn7eAnwVYT4D8DgkHEU+W5
|
12
|
+
BuHp5fby9Wj3/WerEr//ie61w29r1XPdF1KP2O+nj6LSk3AzluwEi03pJQBhWNmm
|
13
|
+
FKU8zavKwvRJtOYesNttwx6GDTQ/3cGSQpJ/5T9o+Xk/iOA9IFiOhCEH8L8OYoIb
|
14
|
+
SmmoR020cAOA7RpJ/T30DH7JNfDP4Q4BfyOimzKzpMz0w3scEJ3WAYz1LR0pc0MF
|
15
|
+
Kcz/NVYWouGO6l3S5kLANkXn7zWqH34dopOOSBWKqo9zEDVihlu0IzzZeJ4x7OSw
|
16
|
+
WlWH+DcPqvH8R7UCp0SooCMmrlniJp8AUMmHZ/p/vHZrwpQKWRnFbQIDAQABo1Aw
|
17
|
+
TjAdBgNVHQ4EFgQUbuGZ86sFEYgjUWClz2cr7NwIUHQwHwYDVR0jBBgwFoAUbuGZ
|
18
|
+
86sFEYgjUWClz2cr7NwIUHQwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOC
|
19
|
+
AQEAnVXP7n5Hf/mRQU0Bm7ZFcJ726hHG3Kt9b80zhBbBCI4ZBPR1HlWR8JclBINX
|
20
|
+
ES+/T0AoNU5C8XR5sJ6sFTL6VpYd2PyX/wfXnvzaejR5TyEv81LjK2PHjS/fFBMh
|
21
|
+
zjMxCM+ABWNZZEAdBkqvaFKL+pz73ges7pIBtxpT7PcEj3M1oTMZUUUlbEHv45HX
|
22
|
+
er3sUZb2rn3DztqZRdwzZUIdGOvP5h2jSJcNdD/3JqoROufHQbRWlGtMBYUfWCGf
|
23
|
+
Z7wOgs73OhMFBd+9Mvw4+s321yH8TBvGWH2vdQ7llg0ev4J7f+3p8hVEZN3IjlDL
|
24
|
+
cUOI1rlQiIJZCaQPaqlODOpvNg==
|
25
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,27 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
MIIEogIBAAKCAQEA0uzoMSPlUZrn7eAnwVYT4D8DgkHEU+W5BuHp5fby9Wj3/Wer
|
3
|
+
Er//ie61w29r1XPdF1KP2O+nj6LSk3AzluwEi03pJQBhWNmmFKU8zavKwvRJtOYe
|
4
|
+
sNttwx6GDTQ/3cGSQpJ/5T9o+Xk/iOA9IFiOhCEH8L8OYoIbSmmoR020cAOA7RpJ
|
5
|
+
/T30DH7JNfDP4Q4BfyOimzKzpMz0w3scEJ3WAYz1LR0pc0MFKcz/NVYWouGO6l3S
|
6
|
+
5kLANkXn7zWqH34dopOOSBWKqo9zEDVihlu0IzzZeJ4x7OSwWlWH+DcPqvH8R7UC
|
7
|
+
p0SooCMmrlniJp8AUMmHZ/p/vHZrwpQKWRnFbQIDAQABAoIBAEsFdHi1+cSSwld7
|
8
|
+
WOiNQziJcSgNWFU26h6mj9j5guUC1uHM064xmCRpQUEoCkS7lzHKbduNMh4GnbtP
|
9
|
+
NypA/ETIC1rbzcQaddX2B7BnoBDDbsvm5ZemFF5IJwnfQbAQP4NqNA9IBIBnPc/j
|
10
|
+
Yhp1JQud7AMXEXi8KhTHi9EAtGL6Vq/SsVmK63BosFK8PFyaZ/EYAIL7QTJQBdwW
|
11
|
+
DWjW5FUVBP0NY+iJXuEt1slV32Xhx3eSPk53L2CG9o2aMkT+dbx/mC13edYI7UU7
|
12
|
+
4bjq+F7Gxz93w+ZsRJ2YkciSHHk8qQ/FZrqpSr0GYul9eUSGkm8l8LZGNbzHsl8D
|
13
|
+
yAQrJUECgYEA7myy2+Ha0RG0icf001OPr+T3JGJK93R+VIC7LGF/rCuU6RLll7Nl
|
14
|
+
rYpMuYKvmiGaORpJYVc5zVwzeY5k90IyaVPa2xKtVo9tRfeNEaTaqlB6gTm+m5E3
|
15
|
+
iJFYym+G+KwuTuuhVVU9nV7KWODSBfLglEgOSpLTCGui0QN+qbY2ZVUCgYEA4nlF
|
16
|
+
s4NjQfUZiGGRbFKNqosVp2rgtFR95HXXRLjYTWtK/FS0ieMJyJUEFWJiNi0j9P+/
|
17
|
+
J9CmQy4r66MpqFooYHquMYRDukXCQ27KdfLe4UHM+bTQOAoCY7Tl/ds7mVHSp2cI
|
18
|
+
P/T2W3ehrxNPB2KNeU3OgJd9IPYNW/wwHotwX7kCgYBt7+stHmByZLKVkYDfbLll
|
19
|
+
hrM6sKQWpD2YI1+rIC3pqpLYQeFh6NOqiInGRG9KJ9JgIDHT04+QlMIbe8AsjvaF
|
20
|
+
wKe6ukr5Ddt6FqKSjyxQuhkyuvib7QLpUvPZLEHVKjeUJmxW1544kTvGbawKGCrb
|
21
|
+
1LnaQwdR66fArtbZ1G4SnQKBgCcHOynKdKqDMJk+Jy+BsoQ3X83wLzUkcmWSoTxo
|
22
|
+
lm4RFWUSu+IfTCpS89czkzU+5jlscWbNIDnnlQ4Qmjc3AkpOGgLShlFtgCLazu0w
|
23
|
+
o5QyIL7PmCpwHyVLoW7z/vtXDHRo3xUWg/YTUbu4GiBtrW/AJtwmPxwVCwxVE33Q
|
24
|
+
DdeRAoGABCXxlPDzGW5qeHDDGWD6FdnRo6MqFS5W72MZM13t9AXSkP0Mnfds5smV
|
25
|
+
q0ejip4UNuvgfckU+ZnECTV/gU++1ePICyyY+4ERmjZq2YaI67AtH8KYGCDhlM2L
|
26
|
+
XHdEbTcJKqBzsBKi22xAISaSvUpZ8UPFoaRQBYsAKeQB52DJLEk=
|
27
|
+
-----END RSA PRIVATE KEY-----
|
@@ -0,0 +1,325 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'net/ssh'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
tools_home = `which appscale-run-instances`
|
6
|
+
if tools_home.length == 0
|
7
|
+
# If AppScale-Tools are not installed on the local machine, use the Gem
|
8
|
+
require 'appscale-tools'
|
9
|
+
else
|
10
|
+
# Give priority to the the local copy of AppScale Tools
|
11
|
+
$:.unshift File.join(File.dirname(tools_home), "..", "lib")
|
12
|
+
require 'appscale_tools'
|
13
|
+
end
|
14
|
+
|
15
|
+
$mutex = Mutex.new
|
16
|
+
|
17
|
+
def report_error(title, msg)
|
18
|
+
@title = title
|
19
|
+
@error = msg
|
20
|
+
return erb :error
|
21
|
+
end
|
22
|
+
|
23
|
+
def validate_appscale_credentials(user, pass1, pass2)
|
24
|
+
if user.nil? or user.length == 0
|
25
|
+
return [false, "Administrator username not provided"]
|
26
|
+
elsif pass1.nil? or pass2.nil? or pass1.length == 0 or pass2.length == 0
|
27
|
+
return [false, "Administrator password not provided"]
|
28
|
+
elsif pass1 != pass2
|
29
|
+
return [false, "Password entries do not match"]
|
30
|
+
elsif pass1.length < 6
|
31
|
+
return [false, "Password must contain at least 6 characters"]
|
32
|
+
else
|
33
|
+
return [true, '']
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def validate_ssh_credentials(keyname, root_password, ips_yaml)
|
38
|
+
if keyname.nil? or keyname.length == 0
|
39
|
+
return [false, "AppScale key name not provided"]
|
40
|
+
elsif root_password.nil? or root_password == 0
|
41
|
+
return [false, "Root password for AppScale machines not provided"]
|
42
|
+
else
|
43
|
+
# Randomly pick a server and try to connect to it via SSH using the
|
44
|
+
# provided credentials. This will guard againt invalid root passwords
|
45
|
+
# entered by the user and any obvious network level issues which might
|
46
|
+
# prevent AppsCake from connecting to the specified machines. The test
|
47
|
+
# assumes that all hosts can be accessed using the same root password
|
48
|
+
# and they are on the same network (if one is reachable, then all are
|
49
|
+
# reachable) which is the case for most typical AppScale deployments.
|
50
|
+
# In scenarios where the above assumption is not the case (i.e. some
|
51
|
+
# machines are reachable and some are not) the test may pass, but the
|
52
|
+
# final deployment may fail. These runtime errors will go to the deployment
|
53
|
+
# logs generated by AppsCake per each deployment which can also be accessed
|
54
|
+
# over the web.
|
55
|
+
node_layout = NodeLayout.new(ips_yaml, {:database => "cassandra"})
|
56
|
+
ips = node_layout.nodes.collect { |node| node.id }
|
57
|
+
begin
|
58
|
+
Net::SSH.start(ips[0], 'root', :password => root_password, :timeout => 10) do |ssh|
|
59
|
+
ssh.exec('ls')
|
60
|
+
end
|
61
|
+
rescue Timeout::Error
|
62
|
+
return [false, "Connection timed out for #{ips[0]}"]
|
63
|
+
rescue Errno::EHOSTUNREACH
|
64
|
+
return [false, "Host unreachable error for #{ips[0]}"]
|
65
|
+
rescue Errno::ECONNREFUSED
|
66
|
+
return [false, "Connection refused for #{ips[0]}"]
|
67
|
+
rescue Net::SSH::AuthenticationFailed
|
68
|
+
return [false, "Authentication failed for #{ips[0]} - Please ensure that the specified" +
|
69
|
+
" root password is correct"]
|
70
|
+
rescue Exception => e
|
71
|
+
return [false, "Unexpected runtime error connecting to #{ips[0]}"]
|
72
|
+
end
|
73
|
+
return [true, '']
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def validate_yaml(yaml_str)
|
78
|
+
if yaml_str.nil? or yaml_str.length == 0
|
79
|
+
return [false, "ips.yaml configuration not provided"]
|
80
|
+
end
|
81
|
+
|
82
|
+
yaml = YAML.load(yaml_str)
|
83
|
+
critical_roles = %w[ appengine loadbalancer database login shadow zookeeper ]
|
84
|
+
aggregate_roles = {
|
85
|
+
'master' => %w[ shadow loadbalancer zookeeper login ],
|
86
|
+
'controller' => %w[ shadow loadbalancer zookeeper database login ],
|
87
|
+
'servers' => %w[ appengine database loadbalancer ]
|
88
|
+
}
|
89
|
+
optional_roles = %w[ open memcache ]
|
90
|
+
|
91
|
+
success_result = ''
|
92
|
+
yaml.each do |symbol, value|
|
93
|
+
role = symbol.to_s
|
94
|
+
if !critical_roles.include? role and !aggregate_roles.has_key? role and
|
95
|
+
!optional_roles.include? role
|
96
|
+
return [false, "Unknown AppScale server role: #{role}"]
|
97
|
+
else
|
98
|
+
critical_roles.delete_if { |r|
|
99
|
+
r == role or (aggregate_roles.has_key? role and aggregate_roles[role].include? r)
|
100
|
+
}
|
101
|
+
success_result += "<p>#{role}</p><ul>"
|
102
|
+
if value.kind_of?(Array)
|
103
|
+
value.each do |val|
|
104
|
+
success_result += "<li>#{val}</li>"
|
105
|
+
end
|
106
|
+
else
|
107
|
+
success_result += "<li>#{value}</li>"
|
108
|
+
end
|
109
|
+
success_result += '</ul>'
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
if critical_roles.length > 0
|
114
|
+
result = "Following required roles are not configured: "
|
115
|
+
critical_roles.each do |role|
|
116
|
+
result += "#{role}, "
|
117
|
+
end
|
118
|
+
return [false, result[0..-3]]
|
119
|
+
end
|
120
|
+
|
121
|
+
[true, success_result, yaml]
|
122
|
+
end
|
123
|
+
|
124
|
+
def validate_ec2_cluster_settings(min, max, ami)
|
125
|
+
if min.nil? or min.length == 0
|
126
|
+
return [false, "Minimum number of nodes unspecified"]
|
127
|
+
elsif max.nil? or max.length == 0
|
128
|
+
return [false, "Maximum number of nodes unspecified"]
|
129
|
+
elsif ami.nil? or ami.length == 0
|
130
|
+
return [false, "AMI ID not specified"]
|
131
|
+
elsif min.to_i <= 0
|
132
|
+
return [false, "Minimum number of nodes must be positive"]
|
133
|
+
elsif max.to_i < min.to_i
|
134
|
+
return [false, "Maximum number of nodes must not be smaller than the minimum numberof nodes"]
|
135
|
+
else
|
136
|
+
return [true, ""]
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def validate_ec2_credentials(username, access_key, secret_key, region)
|
141
|
+
if username.nil? or username.length == 0
|
142
|
+
return [false, "EC2 username not specified"]
|
143
|
+
elsif access_key.nil? or access_key.length == 0
|
144
|
+
return [false, "EC2 access key not specified"]
|
145
|
+
elsif secret_key.nil? or secret_key.length == 0
|
146
|
+
return [false, "EC2 secret key not specified"]
|
147
|
+
elsif region.nil? or region.length == 0
|
148
|
+
return [false, "EC2 region not specified"]
|
149
|
+
else
|
150
|
+
output = CommonFunctions::shell("ec2-describe-regions -O #{access_key} -W #{secret_key}")
|
151
|
+
if output.nil? or output.length == 0
|
152
|
+
return [false, "Unable to execute EC2 command line tools"]
|
153
|
+
elsif output.include?"AuthFailure"
|
154
|
+
return [false, "EC2 authentication failed. Invalid EC2 access key or/and secret key."]
|
155
|
+
elsif !output.include?region
|
156
|
+
return [false, "Invalid EC2 region. This region is not available for your account."]
|
157
|
+
end
|
158
|
+
end
|
159
|
+
[true, ""]
|
160
|
+
end
|
161
|
+
|
162
|
+
def validate_ec2_certificate_uploads(username, pk_upload, cert_upload)
|
163
|
+
if pk_upload.nil?
|
164
|
+
return [false, "Primary key not uploaded"]
|
165
|
+
elsif pk_upload[:type] != "application/x-x509-ca-cert" and
|
166
|
+
pk_upload[:type] != "application/x-pem-file"
|
167
|
+
return [false, "Invalid primary key format: #{pk_upload[:type]}"]
|
168
|
+
elsif cert_upload.nil?
|
169
|
+
return [false, "X509 certificate not uploaded"]
|
170
|
+
elsif cert_upload[:type] != "application/x-x509-ca-cert" and
|
171
|
+
cert_upload[:type] != "application/x-pem-file"
|
172
|
+
return [false, "Invalid certificate format: #{cert_upload[:type]}"]
|
173
|
+
else
|
174
|
+
timestamp = Time.now.to_i
|
175
|
+
File.open("certificates/#{username}_#{timestamp}_pk.pem", "w") do |f|
|
176
|
+
f.write(pk_upload[:tempfile].read)
|
177
|
+
end
|
178
|
+
File.open("certificates/#{username}_#{timestamp}_cert.pem", "w") do |f|
|
179
|
+
f.write(cert_upload[:tempfile].read)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
[true, timestamp]
|
183
|
+
end
|
184
|
+
|
185
|
+
def locked?
|
186
|
+
$mutex.synchronize do
|
187
|
+
return File.exists?('appscake.lock')
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def lock
|
192
|
+
$mutex.synchronize do
|
193
|
+
if !File.exists?('appscake.lock')
|
194
|
+
File.open('appscake.lock', 'w') do |file|
|
195
|
+
file.write('AppsCake.lock')
|
196
|
+
end
|
197
|
+
return true
|
198
|
+
else
|
199
|
+
return false
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def unlock
|
205
|
+
$mutex.synchronize do
|
206
|
+
if File.exists?('appscake.lock')
|
207
|
+
File.delete('appscake.lock')
|
208
|
+
return true
|
209
|
+
else
|
210
|
+
return false
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def deploy(options)
|
216
|
+
30.times do |i|
|
217
|
+
puts "Deploying..."
|
218
|
+
sleep(1)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def add_key(options)
|
223
|
+
puts "Generating RSA keys..."
|
224
|
+
end
|
225
|
+
|
226
|
+
def redirect_standard_io(timestamp)
|
227
|
+
begin
|
228
|
+
orig_stderr = $stderr.clone
|
229
|
+
orig_stdout = $stdout.clone
|
230
|
+
log_path = File.join(File.expand_path(File.dirname(__FILE__)), "logs")
|
231
|
+
$stderr.reopen File.new(File.join(log_path, "deploy-#{timestamp}.log"), "w")
|
232
|
+
$stderr.sync = true
|
233
|
+
$stdout.reopen File.new(File.join(log_path, "deploy-#{timestamp}.log"), "w")
|
234
|
+
$stdout.sync = true
|
235
|
+
retval = yield
|
236
|
+
rescue Exception => e
|
237
|
+
puts "[__ERROR__] Runtime error in deployment process: #{e.message}"
|
238
|
+
$stdout.reopen orig_stdout
|
239
|
+
$stderr.reopen orig_stderr
|
240
|
+
raise e
|
241
|
+
ensure
|
242
|
+
$stdout.reopen orig_stdout
|
243
|
+
$stderr.reopen orig_stderr
|
244
|
+
end
|
245
|
+
retval
|
246
|
+
end
|
247
|
+
|
248
|
+
def deploy_on_virtual_cluster(params, add_key_options, run_instances_options, success_msg)
|
249
|
+
if lock
|
250
|
+
begin
|
251
|
+
timestamp = Time.now.to_i
|
252
|
+
pid = fork do
|
253
|
+
begin
|
254
|
+
redirect_standard_io(timestamp) do
|
255
|
+
key_file = File.expand_path("~/.appscale/#{params[:keyname]}")
|
256
|
+
if File.exists?(key_file)
|
257
|
+
puts "AppScale key '#{params[:keyname]}' found on the disk. Reusing..."
|
258
|
+
else
|
259
|
+
puts "AppScale key '#{params[:keyname]}' not found on the disk. Generating..."
|
260
|
+
AppScaleTools.add_keypair(add_key_options)
|
261
|
+
end
|
262
|
+
AppScaleTools.run_instances(run_instances_options)
|
263
|
+
end
|
264
|
+
ensure
|
265
|
+
# If the fork was successful, the sub-process should release the lock
|
266
|
+
unlock
|
267
|
+
end
|
268
|
+
end
|
269
|
+
Process.detach(pid)
|
270
|
+
@timestamp = timestamp
|
271
|
+
@pid = pid
|
272
|
+
@html = success_msg
|
273
|
+
return erb :success
|
274
|
+
rescue Exception => e
|
275
|
+
# If something went wrong with the fork, release the lock immediately and return
|
276
|
+
unlock
|
277
|
+
return report_error("Unexpected Runtime Error", "Runtime error while executing" +
|
278
|
+
" appscale tools: #{e.message}")
|
279
|
+
end
|
280
|
+
else
|
281
|
+
return report_error("Server Busy", "AppsCake is currently busy deploying a cloud." +
|
282
|
+
" Please try again later.")
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def deploy_on_ec2(params, run_instances_options, cert_timestamp)
|
287
|
+
if lock
|
288
|
+
begin
|
289
|
+
timestamp = Time.now.to_i
|
290
|
+
pid = fork do
|
291
|
+
ENV['EC2_REGION'] = params[:region]
|
292
|
+
ENV['EC2_PRIVATE_KEY'] = File.join(File.dirname(__FILE__), "certificates",
|
293
|
+
"#{params[:username]}_#{cert_timestamp}_pk.pem")
|
294
|
+
ENV['EC2_CERT'] = File.join(File.dirname(__FILE__), "certificates",
|
295
|
+
"#{params[:username]}_#{cert_timestamp}_cert.pem")
|
296
|
+
ENV['EC2_ACCESS_KEY'] = params[:access_key]
|
297
|
+
ENV['EC2_SECRET_KEY'] = params[:secret_key]
|
298
|
+
ENV['EC2_JVM_ARGS'] = "-Djavax.net.ssl.trustStore=#{ENV['JAVA_HOME']}/lib/security/cacerts"
|
299
|
+
ENV['EC2_URL'] = "https://ec2.#{params[:region]}.amazonaws.com"
|
300
|
+
ENV['S3_URL'] = "https://s3.amazonaws.com:443"
|
301
|
+
begin
|
302
|
+
redirect_standard_io(timestamp) do
|
303
|
+
AppScaleTools.run_instances(run_instances_options)
|
304
|
+
end
|
305
|
+
ensure
|
306
|
+
# If the fork was successful, the sub-process should release the lock
|
307
|
+
unlock
|
308
|
+
end
|
309
|
+
end
|
310
|
+
Process.detach(pid)
|
311
|
+
@timestamp = timestamp
|
312
|
+
@pid = pid
|
313
|
+
@html = ""
|
314
|
+
return erb :success
|
315
|
+
rescue Exception => e
|
316
|
+
# If something went wrong with the fork, release the lock immediately and return
|
317
|
+
unlock
|
318
|
+
return report_error("Unexpected Runtime Error", "Runtime error while executing" +
|
319
|
+
" appscale tools: #{e.message}")
|
320
|
+
end
|
321
|
+
else
|
322
|
+
return report_error("Server Busy", "AppsCake is currently busy deploying a cloud." +
|
323
|
+
" Please try again later.")
|
324
|
+
end
|
325
|
+
end
|