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 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: []