drupalcluster 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/drupalcluster +188 -0
- data/conf/drupalcluster.conf +22 -0
- data/drupalcluster.gemspec +22 -0
- data/lib/aws/drupal_aws.rb +114 -0
- data/lib/cli_framework.rb +54 -0
- data/lib/etc/stacks_json.rb +44 -0
- data/lib/etc/utilities.rb +67 -0
- data/lib/netting.rb +45 -0
- data/readme.md +24 -0
- metadata +93 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c8d457e4dcb27114f13fbef2af0d92279d17caf6
|
4
|
+
data.tar.gz: 993e01b7fc9bdfd7b11e083a9dd2a7d177199f03
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2c378072481dd82c976d31687bd69f47cbb2662327a5db72681928d0460ffd48b3966c0f3fc875b2f56a48bcc0c4afbfdd90efdebcdba636f3da36889c4e3825
|
7
|
+
data.tar.gz: c693336408ca3f4b2c3a66348400b1edc58b3c0cdb394fa58362f4f50d156ef2361b0c8420a89f471a608d83190d2b0da09da3ed490c8e2db5b3cf2e73c8c3ad
|
data/bin/drupalcluster
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
##
|
4
|
+
#
|
5
|
+
# drupalcluster is a command line tool to quickly
|
6
|
+
# deploy a Drupal hosting cluster of a scalable amount [2..5]
|
7
|
+
# of virtual webservers.
|
8
|
+
#
|
9
|
+
# !! AWS identity is required for this script !!
|
10
|
+
# Your AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY should be either
|
11
|
+
# environment variables, or set in ~/.aws/credentials.
|
12
|
+
#
|
13
|
+
# The configuration file contains additional details for the cluster,
|
14
|
+
# including an ssh KeyName that's needed to access the servers.
|
15
|
+
# The KeyName defaults to 'Drupal', easiest if it's precreated.
|
16
|
+
# $HOME/.drc/drupalcluster.conf
|
17
|
+
#
|
18
|
+
# This is a demo version only, builds Drupal on HTTP connection.
|
19
|
+
# Don't use it seriously.
|
20
|
+
# Especially, don't post personal/sensitive data on your Drupal site.
|
21
|
+
#
|
22
|
+
# ==== Commands
|
23
|
+
#
|
24
|
+
# create <name> -- Creates a Drupal hosting cluster
|
25
|
+
# check [<name>] -- Checks the status of creation/deletion
|
26
|
+
# delete <name> -- Deletes permanently the given cluster
|
27
|
+
# test <name|url> -- Sends a simple HTTP GET to the URL (of the given cluster)
|
28
|
+
# attack <instance> -- Permanently terminates the given server instance
|
29
|
+
# list -- Lists the recently created/deleted clusters.
|
30
|
+
#
|
31
|
+
|
32
|
+
require 'json'
|
33
|
+
require 'inifile'
|
34
|
+
|
35
|
+
require 'cli_framework'
|
36
|
+
require 'netting'
|
37
|
+
require 'aws/drupal_aws'
|
38
|
+
require 'etc/stacks_json'
|
39
|
+
require 'etc/utilities'
|
40
|
+
|
41
|
+
##
|
42
|
+
# Public methods of this class are just the CLI commands.
|
43
|
+
# Its private methods are separated into modules.
|
44
|
+
#
|
45
|
+
class Drupal_Clusting
|
46
|
+
|
47
|
+
include Stacks_json
|
48
|
+
include Utilities
|
49
|
+
|
50
|
+
def initialize
|
51
|
+
read_conf_file
|
52
|
+
read_stacks_json
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# Creates a Drupal cluster. You have to give it a name
|
57
|
+
# as parameter, and then a password for the DB admin.
|
58
|
+
# The process may take 10-15 minutes.
|
59
|
+
# If deployment was successful, the website URL and
|
60
|
+
# database details are displayed.
|
61
|
+
#
|
62
|
+
# ==== Example:
|
63
|
+
# drupalcluster create MySite
|
64
|
+
#
|
65
|
+
def create stack_name
|
66
|
+
@conf[:create][:stack_name] = stack_name
|
67
|
+
@conf[:create_params]["DBPassword"] ||= get_password
|
68
|
+
# generate key of name KeyName if not yet
|
69
|
+
puts "Creating drupal cluster '#{stack_name}', may take a while.."
|
70
|
+
puts "Ctrl-C will not stop the creation."
|
71
|
+
created = AMA::create_stack @conf
|
72
|
+
if created
|
73
|
+
display_outputs(created)
|
74
|
+
@stacks[stack_name] = created['WebsiteURL']
|
75
|
+
write_stacks_json
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
##
|
80
|
+
# Deletes the given cluster after confirmation.
|
81
|
+
#
|
82
|
+
# ==== Example:
|
83
|
+
# drupalcluster delete MySite
|
84
|
+
#
|
85
|
+
def delete stack_name
|
86
|
+
puts "No way back. All users and contents will be lost."
|
87
|
+
confirm
|
88
|
+
AMA::delete_stack stack_name
|
89
|
+
puts "Deletion initiated. Use following command to see if it is finished:"
|
90
|
+
puts " #{$PROGRAM_NAME} check #{stack_name}"
|
91
|
+
put_at_the_end stack_name
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# Tests the given site by a simple HTTP GET /.
|
96
|
+
# Either the cluster name or the Drupal site's base URL
|
97
|
+
# has to be specified as parameter.
|
98
|
+
#
|
99
|
+
# ==== Examples:
|
100
|
+
# drupalcluster test -- tests the last created/deleted site
|
101
|
+
# drupalcluster test MySite -- tests site called 'MySite'
|
102
|
+
# drupalcluster test http://xy.com/drupal -- tests given URL
|
103
|
+
#
|
104
|
+
def test stack=nil
|
105
|
+
stack_url = stack
|
106
|
+
if stack !~ /https?:|\.amazonaws\./
|
107
|
+
stack_name = stack or previous
|
108
|
+
stack_url = @stacks[stack_name]
|
109
|
+
end
|
110
|
+
die("An http[s] URL or a previously created stack name is required."
|
111
|
+
) unless stack_url
|
112
|
+
stack_url = stack_url.sub /\/drupal\/?/, ''
|
113
|
+
puts "Sending request to #{stack_url}"
|
114
|
+
http = Netting.new stack_url
|
115
|
+
res = http.get ''
|
116
|
+
puts "Result code is: #{res.code}" if res.respond_to? :code
|
117
|
+
end
|
118
|
+
|
119
|
+
##
|
120
|
+
# Gives status about creation/deletion of the given cluster.
|
121
|
+
# Possible results are:
|
122
|
+
# ongoing, created, failed, stack does not exist
|
123
|
+
#
|
124
|
+
# If the cluster is successfully created,
|
125
|
+
# check also provides its URL and database info
|
126
|
+
#
|
127
|
+
# Examples:
|
128
|
+
# drupalcluster check -- checks the last initiated creation/deletion
|
129
|
+
# drupalcluster check MySite -- checks processing of site called 'MySite'
|
130
|
+
#
|
131
|
+
def check stack_name=nil
|
132
|
+
stack_name = previous unless stack_name
|
133
|
+
puts "Checking stack #{stack_name}."
|
134
|
+
res = AMA::stack_status stack_name
|
135
|
+
puts res
|
136
|
+
if :created == res
|
137
|
+
outputs = AMA::info stack_name
|
138
|
+
display_outputs( outputs )
|
139
|
+
if @stacks[stack_name] != outputs['WebsiteURL']
|
140
|
+
@stacks[stack_name] = outputs['WebsiteURL']
|
141
|
+
write_stacks_json
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
##
|
147
|
+
# Terminate one of the Drupal servers, referred
|
148
|
+
# by its instance_id.
|
149
|
+
#
|
150
|
+
# The EC2 instance-id of the currently active server
|
151
|
+
# is displayed e.g. on the top of the PHP page
|
152
|
+
# that has the same URL but without "/drupal" at the end
|
153
|
+
#
|
154
|
+
# ==== Example:
|
155
|
+
# drupalcluster attack i-0a68ac21dd859867b
|
156
|
+
#
|
157
|
+
def attack instance_id
|
158
|
+
puts "We are about to _terminate_ server instance '#{instance_id}'."
|
159
|
+
confirm
|
160
|
+
AMA::terminate instance_id
|
161
|
+
puts "Terminating instance.."
|
162
|
+
end
|
163
|
+
|
164
|
+
##
|
165
|
+
# Lists the previously created stacks and their URL's.
|
166
|
+
# Takes no parameters.
|
167
|
+
#
|
168
|
+
# ==== Example:
|
169
|
+
# drupalcluster list
|
170
|
+
#
|
171
|
+
def list
|
172
|
+
@stacks.each_pair do |name, url|
|
173
|
+
puts "#{name}: #{url}"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
##
|
178
|
+
# Describes the given command.
|
179
|
+
#
|
180
|
+
def help cmd=nil
|
181
|
+
puts
|
182
|
+
puts get_comment_above( cmd, __FILE__ )
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
186
|
+
|
187
|
+
#
|
188
|
+
main(Drupal_Clusting)
|
@@ -0,0 +1,22 @@
|
|
1
|
+
[aws]
|
2
|
+
# not in use yet
|
3
|
+
region = eu-central-1
|
4
|
+
|
5
|
+
[create]
|
6
|
+
# template_url = https://s3.eu-central-1.amazonaws.com/drupalcluster/Drupal_in_VPC.yaml
|
7
|
+
template_url = https://s3.eu-central-1.amazonaws.com/drupalcluster/Drupal.yaml
|
8
|
+
disable_rollback = true
|
9
|
+
|
10
|
+
[create_params]
|
11
|
+
DBUser = test
|
12
|
+
# DBPassword = password
|
13
|
+
MultiAZDatabase = false
|
14
|
+
KeyName = Drupal
|
15
|
+
PuppetMasterIp = 172.31.44.226
|
16
|
+
|
17
|
+
HealthCheckInterval = 8
|
18
|
+
UnhealthyAfter = 4
|
19
|
+
|
20
|
+
[drupal]
|
21
|
+
# not in use yet
|
22
|
+
admin_user = admin
|
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
Gem::Specification.new do |s|
|
3
|
+
|
4
|
+
s.name = 'drupalcluster'
|
5
|
+
s.version = '0.1.1'
|
6
|
+
s.license = "Nonstandard"
|
7
|
+
s.date = '2017-11-03'
|
8
|
+
s.summary = "Create/delete/test a multiserver Drupal site in Amazon cloud."
|
9
|
+
s.description = File.new("readme.md").read
|
10
|
+
s.authors = ["Bertalan Pecsi"]
|
11
|
+
s.email = 'zellerede@gmail.com'
|
12
|
+
all_files = `git ls-files`.split $/
|
13
|
+
s.files = all_files.select {|fname| fname !~ /html/}
|
14
|
+
s.executables = ["drupalcluster"]
|
15
|
+
s.homepage = 'https://github.com/zellerede/drupal'
|
16
|
+
s.require_paths = ["bin",
|
17
|
+
"lib"]
|
18
|
+
|
19
|
+
s.add_runtime_dependency 'aws-sdk', '>= 2.7'
|
20
|
+
s.add_runtime_dependency 'inifile', '>= 2.8'
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
##
|
2
|
+
# The core functions of cloud manipulation, using aws-sdk
|
3
|
+
# Managing a drupal hosting cluster in Amazon's cloud
|
4
|
+
#
|
5
|
+
# ~/.aws/ identityfiles are assumed
|
6
|
+
#
|
7
|
+
require 'aws-sdk'
|
8
|
+
|
9
|
+
module AMA
|
10
|
+
extend self # used as a namespace
|
11
|
+
|
12
|
+
##
|
13
|
+
#
|
14
|
+
def create_stack conf
|
15
|
+
parameters = get_default_network_ids().update conf[:create_params]
|
16
|
+
paramlist = parameters.map {|k,v| {parameter_key: k.to_s, parameter_value: v.to_s}}
|
17
|
+
create_params = conf[:create].update({parameters: paramlist})
|
18
|
+
#puts create_params; return ###
|
19
|
+
cfn = Aws::CloudFormation::Client.new
|
20
|
+
begin
|
21
|
+
cfn.create_stack( create_params )
|
22
|
+
descr = cfn.wait_until(:stack_create_complete,
|
23
|
+
{stack_name: conf[:create][:stack_name]}, {
|
24
|
+
delay: 6,
|
25
|
+
max_attempts: 150, # 15 min
|
26
|
+
before_attempt: ->(*x) do
|
27
|
+
print "."
|
28
|
+
end
|
29
|
+
})
|
30
|
+
puts
|
31
|
+
rescue Aws::CloudFormation::Errors::ServiceError, \
|
32
|
+
Aws::Waiters::Errors::WaiterFailed, \
|
33
|
+
ArgumentError => e
|
34
|
+
puts e
|
35
|
+
return
|
36
|
+
end
|
37
|
+
shape_outputs( descr )
|
38
|
+
end
|
39
|
+
|
40
|
+
def delete_stack stack_name
|
41
|
+
begin
|
42
|
+
cfn = Aws::CloudFormation::Client.new
|
43
|
+
cfn.delete_stack({stack_name: stack_name})
|
44
|
+
rescue Aws::CloudFormation::Errors::ServiceError => e
|
45
|
+
puts e
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def stack_status stack_name
|
50
|
+
begin
|
51
|
+
cfn = Aws::CloudFormation::Client.new
|
52
|
+
events = cfn.describe_stack_events({stack_name: stack_name})
|
53
|
+
rescue Aws::CloudFormation::Errors::ServiceError, ArgumentError => e
|
54
|
+
puts e
|
55
|
+
return
|
56
|
+
end
|
57
|
+
last_event = events.stack_events.first
|
58
|
+
finished = ("AWS::CloudFormation::Stack" == last_event.resource_type)
|
59
|
+
finished &= (stack_name == last_event.logical_resource_id)
|
60
|
+
finished &= !("CREATE_IN_PROGRESS" == last_event.resource_status)
|
61
|
+
success = ("CREATE_COMPLETE" == last_event.resource_status)
|
62
|
+
finished ?
|
63
|
+
(success ? :created : :failed) :
|
64
|
+
:ongoing
|
65
|
+
end
|
66
|
+
|
67
|
+
def info stack_name
|
68
|
+
begin
|
69
|
+
cfn = Aws::CloudFormation::Client.new
|
70
|
+
descr = cfn.describe_stacks({stack_name: stack_name})
|
71
|
+
rescue Aws::CloudFormation::Errors::ServiceError, ArgumentError => e
|
72
|
+
puts e
|
73
|
+
return
|
74
|
+
end
|
75
|
+
shape_outputs( descr )
|
76
|
+
end
|
77
|
+
|
78
|
+
def terminate instance_id
|
79
|
+
begin
|
80
|
+
ec2 = Aws::EC2::Client.new
|
81
|
+
ec2.terminate_instances({ instance_ids: [instance_id] })
|
82
|
+
rescue Aws::EC2::Errors => e
|
83
|
+
puts " *** #{e}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
#
|
88
|
+
def get_default_network_ids
|
89
|
+
begin
|
90
|
+
ec2 = Aws::EC2::Client.new
|
91
|
+
descr = ec2.describe_vpcs
|
92
|
+
def_vpc = descr.vpcs.select {
|
93
|
+
|x| x.is_default } .first.vpc_id
|
94
|
+
descr = ec2.describe_subnets({ filters: [
|
95
|
+
{name: "vpc-id", values: [def_vpc]}
|
96
|
+
] })
|
97
|
+
rescue Aws::EC2::Errors => e
|
98
|
+
puts " *** #{e}"
|
99
|
+
return
|
100
|
+
end
|
101
|
+
subnets = descr.subnets.map &:subnet_id
|
102
|
+
{ VpcId: def_vpc,
|
103
|
+
SubnetA: subnets[0],
|
104
|
+
SubnetB: subnets[1] }
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
def shape_outputs( stacks_description )
|
109
|
+
stack = stacks_description.stacks.first
|
110
|
+
stack.outputs.map {|o| {o.output_key => o.output_value}
|
111
|
+
}.reduce {|hash1, hash2| hash1.update hash2}
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
#
|
3
|
+
|
4
|
+
require 'set'
|
5
|
+
|
6
|
+
##
|
7
|
+
# Gives the set of all public instance methods of the given class.
|
8
|
+
def methods_of cls
|
9
|
+
cls.public_instance_methods.to_set
|
10
|
+
end
|
11
|
+
|
12
|
+
##
|
13
|
+
# This is the main function of the CLI.
|
14
|
+
# Its parameter is an arbitrary class, whose public instance methods
|
15
|
+
# will be the CLI commands.
|
16
|
+
#
|
17
|
+
def main executorCls
|
18
|
+
executor = executorCls.new
|
19
|
+
cmds = (methods_of(executorCls) - methods_of(Object)).sort
|
20
|
+
cmds.map! {|x| x.to_s}
|
21
|
+
|
22
|
+
issued = ARGV.shift
|
23
|
+
if not cmds.include? issued
|
24
|
+
puts "No command '#{issued}' is known."
|
25
|
+
die "Please select from: #{cmds.join ', '}"
|
26
|
+
end
|
27
|
+
|
28
|
+
task = executorCls.public_instance_method(issued) .bind(executor)
|
29
|
+
begin
|
30
|
+
task.call *ARGV
|
31
|
+
rescue ArgumentError => e
|
32
|
+
puts "Try a different parameter set.." # show cmd help
|
33
|
+
puts e
|
34
|
+
rescue Interrupt
|
35
|
+
puts
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# Confirmation on STDIN
|
41
|
+
def confirm
|
42
|
+
printf "Are you sure? [Y/n] "
|
43
|
+
answer = STDIN.gets.strip
|
44
|
+
if ("Y" != answer)
|
45
|
+
die "Command ignored."
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# Displays the given +text+, if any, then quits.
|
51
|
+
def die text=""
|
52
|
+
puts text
|
53
|
+
exit
|
54
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
##
|
2
|
+
# This module is part of class Drupal_Clusting,
|
3
|
+
# handles the stack_name => stack_url connection
|
4
|
+
# by means of a json file and the hash @stack.
|
5
|
+
module Stacks_json
|
6
|
+
@@stacks_file = File.join ENV['HOME'], '.drc', '.drstacks.json'
|
7
|
+
|
8
|
+
private
|
9
|
+
##
|
10
|
+
# Gives back the last stack_name of the hash/json
|
11
|
+
def previous
|
12
|
+
name, url = @stacks.to_a.last
|
13
|
+
name
|
14
|
+
end
|
15
|
+
|
16
|
+
##
|
17
|
+
# Reads the json file into @stacks.
|
18
|
+
# In case of errors, it's quietly set to empty.
|
19
|
+
def read_stacks_json
|
20
|
+
@stacks = JSON.load File.new @@stacks_file
|
21
|
+
rescue SystemCallError # most probably: no such file yet
|
22
|
+
@stacks = {}
|
23
|
+
end
|
24
|
+
|
25
|
+
##
|
26
|
+
# Writes the hash @stacks into the json file.
|
27
|
+
# In case of errors, argumentless functionality won't work.
|
28
|
+
def write_stacks_json
|
29
|
+
File.open(@@stacks_file, 'w') { |f|
|
30
|
+
JSON.dump(@stacks, f)
|
31
|
+
}
|
32
|
+
rescue SystemCallError
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Effectively puts the given stack_name at the end
|
37
|
+
# of both the hash and the .json file.
|
38
|
+
def put_at_the_end stack_name
|
39
|
+
@stacks.delete stack_name
|
40
|
+
@stacks[stack_name] = nil
|
41
|
+
write_stacks_json
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'io/console'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
##
|
5
|
+
# This module contains further utility methods for Drupal_Clusting class
|
6
|
+
#
|
7
|
+
module Utilities
|
8
|
+
|
9
|
+
private
|
10
|
+
@@conf_default = File.join File.dirname(__FILE__), '..', '..', 'conf', 'drupalcluster.conf'
|
11
|
+
@@conf_file = File.join ENV['HOME'], '.drc', 'drupalcluster.conf'
|
12
|
+
##
|
13
|
+
# A password will be asked twice on command line, with noecho,
|
14
|
+
# until the repetition coincides the previous one.
|
15
|
+
def get_password for_what="DB"
|
16
|
+
matching = false
|
17
|
+
while not matching do
|
18
|
+
pwd = STDIN.getpass "Type a password for #{for_what}: "
|
19
|
+
pwd2 = STDIN.getpass "Repeat it: "
|
20
|
+
matching = (pwd == pwd2)
|
21
|
+
end
|
22
|
+
puts
|
23
|
+
pwd.rstrip
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# Reads the configuration file
|
28
|
+
def read_conf_file
|
29
|
+
if not File.exist? @@conf_file
|
30
|
+
FileUtils::mkdir_p(File.dirname @@conf_file)
|
31
|
+
FileUtils::copy @@conf_default, @@conf_file
|
32
|
+
end
|
33
|
+
begin
|
34
|
+
@conf = IniFile.load File.new @@conf_file
|
35
|
+
rescue SystemCallError => e
|
36
|
+
puts e
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# Display the creation outputs (basically any hash)
|
42
|
+
# by '<key>: <value>' lines
|
43
|
+
def display_outputs( stack_info )
|
44
|
+
stack_info.each_pair do |key, value|
|
45
|
+
puts "#{key}: #{value}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# Gets either the first comment from the specified source file,
|
51
|
+
# if +cmd+ is not defined. or the comment above the method
|
52
|
+
# named +cmd+. If comment/cmd is not found, returns "No help found".
|
53
|
+
def get_comment_above cmd=nil, file
|
54
|
+
# RDoc's parse is not too user friendly, so..
|
55
|
+
content = File.new(file).read
|
56
|
+
regex_str = '##\s*\n((\s*#(| .*)\n)+)'
|
57
|
+
regex_str += '\s*def\s+'+cmd+'\b' if cmd
|
58
|
+
regexp = Regexp.new regex_str
|
59
|
+
if content =~ regexp
|
60
|
+
comment_section = $1
|
61
|
+
comment_section.gsub /^\s*#( |$)/, ''
|
62
|
+
else
|
63
|
+
"No help found for #{cmd}."
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
data/lib/netting.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
##
|
4
|
+
#
|
5
|
+
# A wrapper around Net::HTTP::Post and Get methods.
|
6
|
+
#
|
7
|
+
class Netting
|
8
|
+
@@Timeout = 4 #sec
|
9
|
+
|
10
|
+
##
|
11
|
+
# A full URL has to be given, starting with http[s]:,
|
12
|
+
# ending in the port (if port is not the default)
|
13
|
+
def initialize url
|
14
|
+
@url = url
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# Sends HTTP POST, data as json
|
19
|
+
def post *stuff
|
20
|
+
send_req Net::HTTP::Post, *stuff
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# Sends HTTP GET and returns the answer
|
25
|
+
def get *stuff
|
26
|
+
send_req Net::HTTP::Get, *stuff
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
##
|
31
|
+
# Sends either a POST or a GET http message.
|
32
|
+
def send_req(req_type, action, data=nil)
|
33
|
+
uri = URI(@url + action)
|
34
|
+
req = req_type.new(uri, 'Content-Type' => 'application/json')
|
35
|
+
req.body = data
|
36
|
+
begin
|
37
|
+
response = Net::HTTP.start(uri.hostname, uri.port, {open_timeout: @@Timeout}) { |http|
|
38
|
+
http.request(req)
|
39
|
+
}
|
40
|
+
response
|
41
|
+
rescue Exception => e
|
42
|
+
puts " *** #{e.message}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/readme.md
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
+drupalcluster+ is a command line tool to quickly
|
2
|
+
deploy a Drupal hosting cluster of a scalable amount [2..5]
|
3
|
+
of virtual webservers.
|
4
|
+
|
5
|
+
!! AWS identity is required for this script !!
|
6
|
+
Your AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY should be either
|
7
|
+
environment variables, or set in ~/.aws/credentials.
|
8
|
+
|
9
|
+
The configuration file contains additional details for the cluster
|
10
|
+
/etc/drupalcluster/drupalcluster.conf
|
11
|
+
|
12
|
+
This is a demo version only, builds Drupal on HTTP connection.
|
13
|
+
Don't use it seriously.
|
14
|
+
Especially, don't post personal/sensitive data on your Drupal site.
|
15
|
+
|
16
|
+
==== Commands
|
17
|
+
|
18
|
+
create <name> -- Creates a Drupal hosting cluster
|
19
|
+
check [<name>] -- Checks the status of creation/deletion
|
20
|
+
delete <name> -- Deletes permanently the given cluster
|
21
|
+
test <name|url> -- Sends a simple HTTP GET to the URL (of the given cluster)
|
22
|
+
attack <instance> -- Permanently terminates the given server instance
|
23
|
+
list -- Lists the recently created/deleted clusters.
|
24
|
+
|
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: drupalcluster
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Bertalan Pecsi
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-11-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: aws-sdk
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.7'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: inifile
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.8'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.8'
|
41
|
+
description: "+drupalcluster+ is a command line tool to quickly\ndeploy a Drupal
|
42
|
+
hosting cluster of a scalable amount [2..5]\nof virtual webservers.\n\n!! AWS identity
|
43
|
+
is required for this script !!\nYour AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY should
|
44
|
+
be either\nenvironment variables, or set in ~/.aws/credentials.\n\nThe configuration
|
45
|
+
file contains additional details for the cluster\n /etc/drupalcluster/drupalcluster.conf\n\nThis
|
46
|
+
is a demo version only, builds Drupal on HTTP connection.\nDon't use it seriously.
|
47
|
+
\nEspecially, don't post personal/sensitive data on your Drupal site.\n\n==== Commands\n\n
|
48
|
+
\ create <name> -- Creates a Drupal hosting cluster\n check [<name>] --
|
49
|
+
Checks the status of creation/deletion \n delete <name> -- Deletes permanently
|
50
|
+
the given cluster\n test <name|url> -- Sends a simple HTTP GET to the URL (of
|
51
|
+
the given cluster)\n attack <instance> -- Permanently terminates the given server
|
52
|
+
instance\n list -- Lists the recently created/deleted clusters.\n\n"
|
53
|
+
email: zellerede@gmail.com
|
54
|
+
executables:
|
55
|
+
- drupalcluster
|
56
|
+
extensions: []
|
57
|
+
extra_rdoc_files: []
|
58
|
+
files:
|
59
|
+
- bin/drupalcluster
|
60
|
+
- conf/drupalcluster.conf
|
61
|
+
- drupalcluster.gemspec
|
62
|
+
- lib/aws/drupal_aws.rb
|
63
|
+
- lib/cli_framework.rb
|
64
|
+
- lib/etc/stacks_json.rb
|
65
|
+
- lib/etc/utilities.rb
|
66
|
+
- lib/netting.rb
|
67
|
+
- readme.md
|
68
|
+
homepage: https://github.com/zellerede/drupal
|
69
|
+
licenses:
|
70
|
+
- Nonstandard
|
71
|
+
metadata: {}
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
require_paths:
|
75
|
+
- bin
|
76
|
+
- lib
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
requirements: []
|
88
|
+
rubyforge_project:
|
89
|
+
rubygems_version: 2.5.1
|
90
|
+
signing_key:
|
91
|
+
specification_version: 4
|
92
|
+
summary: Create/delete/test a multiserver Drupal site in Amazon cloud.
|
93
|
+
test_files: []
|