drupalcluster 0.1.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 +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: []
|