drupalcluster 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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: []