kiel 0.0

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/lib/kiel.rb ADDED
@@ -0,0 +1,249 @@
1
+ require 'rake'
2
+ require 'kiel/scm/git'
3
+ require 'kiel/cloud/aws'
4
+
5
+ # Kiel tries to make the task to create cloud images easier by braking the whole installation into smaller, reproducible tasks.
6
+ # Each step is versioned by a version control system like git or subversion. Each installation step is described by a file
7
+ # containing a capistrano script. Kiel assumes that there is a specific order in which the steps have to be executed.
8
+ #
9
+ # The purpose of splitting the installation of a machine image into smaller tasks is to save time when debugging the
10
+ # installation and save time, when little changes have to be made to the installation.
11
+ #
12
+ # If one step fails, all subsequent installation steps might fail too. But when one step succeeds, that step can be
13
+ # used as base for all subsequence steps.
14
+ #
15
+ # License:: Distributes under the MIT license
16
+
17
+ module Kiel
18
+ #--
19
+ RECOGNIZED_STEP_OPTIONS = [ :name, :task, :scm_name, :setup_name, :description ]
20
+ DEFAULT_OPTIONS = {}
21
+ RECOGNIZED_OPTIONS = [ :scm, :cloud, :setup, :base_image, :root_dir ]
22
+
23
+ class Implementation
24
+ def initialize defaults
25
+ @defaults = defaults.dup
26
+ end
27
+
28
+ # this getters defer the construction of expensive devices to the latest moment possible
29
+ def scm
30
+ @defaults[ :scm ] ||= SCM::Git.new
31
+ end
32
+
33
+ def cloud
34
+ @defaults[ :cloud ] ||= Cloud::AWS.new
35
+ end
36
+
37
+ def setup
38
+ @defaults[ :setup ] ||= Setup::Capistrano.new
39
+ end
40
+
41
+ def expand_path file_name
42
+ @defaults[ :root_dir ] ||= Dir.pwd
43
+
44
+ if file_name.kind_of? Array
45
+ file_name.collect { |f| File.expand_path f, @defaults[ :root_dir ] }
46
+ else
47
+ File.expand_path file_name, @defaults[ :root_dir ]
48
+ end
49
+ end
50
+
51
+ def build_tags step, steps
52
+ steps.inject( { 'image_type' => step[ :name ].to_s, step[ :name ].to_s => step[ :version ].to_s } ) do | t, s |
53
+ t.merge s[ :name ].to_s => s[ :version ].to_s
54
+ end
55
+ end
56
+
57
+ def initial_image_id step, base_steps
58
+ if base_steps.empty?
59
+ unless @defaults.key? :base_image
60
+ raise ArgumentError, "no :base_image given. Need to know what the base image of the very first image to produce should be"
61
+ end
62
+ { :id => @defaults[ :base_image ] }
63
+ else
64
+ step, *steps = *base_steps
65
+ { :tags => build_tags( step, steps ) }
66
+ end
67
+ end
68
+
69
+ def create_task step, steps
70
+ task = Rake::Task::define_task( step[ :task ] => steps.collect{ | s | s[ :task ] } ) do | task, arguments |
71
+ tags = build_tags step, steps
72
+
73
+ if cloud.exists? tags
74
+ puts "image \'#{step[ :name ]}\' already up to date and exists:"
75
+ tags.each{ | key, value | puts "\'#{key}\' => \'#{value}\'" }
76
+ else
77
+ puts "starting instance for: \'#{step[ :name ]}\'"
78
+ instance = cloud.start_instance initial_image_id( step, steps )
79
+ puts "instance for: \'#{step[ :name ]}\' started."
80
+
81
+ begin
82
+ dns_name = cloud.dns_name instance
83
+ expand_step = step.dup.merge( setup_name: expand_path( step[ :setup_name ] ) )
84
+
85
+ puts "excuting installation for: \'#{step[ :name ]}\'"
86
+ setup.execute expand_step, dns_name
87
+ puts "installation for: \'#{step[ :name ]}\' done."
88
+
89
+ puts "storing image for: \'#{step[ :name ]}\'"
90
+ cloud.store_image instance, tags
91
+ puts "image for: \'#{step[ :name ]}\' stored"
92
+ rescue
93
+ cloud.stop_instance instance
94
+ raise
95
+ end
96
+ end
97
+ end
98
+
99
+ task.add_description( step[ :description ] ) if step.key? :description
100
+ task
101
+ end
102
+
103
+ def add_versions steps
104
+ steps.collect() do | step |
105
+ name = step[ :scm_name ] == '*' ? '*' : expand_path( step[ :scm_name ] )
106
+ step.merge( version: scm.version( name ) )
107
+ end
108
+ end
109
+
110
+ private :setup, :cloud, :scm, :initial_image_id, :build_tags
111
+ end
112
+
113
+ # checks that all keys in options are valid
114
+ def self.check_options options
115
+ options.each_key{ | key |
116
+ raise ArgumentError, "Unrecognized option: \'#{key}\'" unless RECOGNIZED_OPTIONS.include? key
117
+ }
118
+ end
119
+
120
+ def self.expand_steps steps
121
+ raise ArgumentError, "no steps given" if steps.empty?
122
+
123
+ # convert all steps to hashes and check the given parameters
124
+ steps = steps.collect do | s; step |
125
+ if s.respond_to? :to_hash
126
+ step = s.to_hash.dup
127
+ step.each { | key, value |
128
+ raise ArgumentError, "unrecognized step option: \'#{key}\'" unless RECOGNIZED_STEP_OPTIONS.include? key
129
+ }
130
+
131
+ raise ArgumentError, "every step have to have at least a name" unless step.key? :name
132
+
133
+ step
134
+ elsif s.respond_to? :to_s
135
+ { :name => s.to_s }
136
+ else
137
+ raise ArgumentError, "a step have to be a string, symbol or a hash"
138
+ end
139
+ end
140
+
141
+ steps.first[ :scm_name ] = '*' unless steps.first.key? :scm_name
142
+
143
+ # merge in defaults
144
+ steps = steps.collect do | step |
145
+ {
146
+ :task => step[ :name ],
147
+ :scm_name => "#{step[ :name ]}.rb"
148
+ }.merge step
149
+ end.collect do | step |
150
+ {
151
+ :setup_name => step[ :scm_name ] == '*' ? "#{step[ :name ]}.rb" : step[ :scm_name ]
152
+ }.merge step
153
+ end
154
+
155
+ steps
156
+ end
157
+
158
+ # defines the +steps+ necessary to build an image by constructing Rake::Tasks that depend on each other. The
159
+ # dependencies are defined in the order the steps are given. Every step depends on all other steps following
160
+ # that step in the list of given +steps+.
161
+ #
162
+ # Each step produces a new machine image, by starting a server in the cloud with the previous image,
163
+ # adding a defined set of installation instructions and than saving the resulting image for the next step.
164
+ #
165
+ # Every step is defined by hash with the following keys:
166
+ #
167
+ # :name:: The name of the step. The name is used to name a tag in the resulting image. The value of the tag is
168
+ # the source code version of the sources of that step. By default +name+ is expanded to +name.rb+ in the
169
+ # current directory.
170
+ #
171
+ # :task:: The name of the Rake::Task to be created for that step. If not given, the +name+ is used.
172
+ #
173
+ # :scm_name:: The name that is passed to the source code management to determine the version of the description
174
+ # of the step. If not given, +name+ is expanded to +name.rb+ in the current directory. For the first
175
+ # element this defaults to '*', which is a special notation for the latest version of the repository.
176
+ #
177
+ # :setup_name:: The name of the script to be executed. This defaults to :scm_name if given and not '*' or to
178
+ # :name + '.rb'
179
+ #
180
+ # :description:: Optional description for the task to be created.
181
+ #
182
+ # A step can be given by just one string or symbol, both following lines will result in the same images created.
183
+ # Kiel::image [ :stepA, :stepB ]
184
+ # Kiel::image [ { :name => 'stepA', :task => 'stepA', :scm_name => '*', :setup_name ='stepA.rb' },
185
+ # { :name => 'stepB', :task => 'stepB', :scm_name => 'stepB.rb' } ]
186
+ #
187
+ # +options+ is a set of configurations that can be used to override global options set by Kiel::set_defaults.
188
+ # Possible options are:
189
+ #
190
+ # :scm:: An instance of the +source code management+ used to retrieve version informations. By default this will
191
+ # be an instance of +Kiel::SCM::Git+.
192
+ #
193
+ # :setup:: An instance of the device used to execute steps to execute the installations steps. By default this will
194
+ # be an instance of +Kiel::Setup::Capistrano+.
195
+ #
196
+ # :cloud:: An instance of the cloud provider to lookup images and to access cloud instances. By default this will
197
+ # be an instance of +Kiel::Cloud::AWS+
198
+ #
199
+ # :base_image:: A cloud image id that is used as base for the very first step. This is the right most argument in
200
+ # the list of +steps+.
201
+ #
202
+ # :root_dir:: Root directory, where all file names are bassed on. If the options is not given, the current directory is used
203
+ #
204
+ # Example:
205
+ # Kiel::image [ 'application', 'base' ], setup: Kiel::Setup::Capistrano.new, base_image: 'ami-6d555119'
206
+ #
207
+ # Will assume that every new version in the repository should lead to a new image based on an base image. The
208
+ # layout of the base image is defined by base.rb and the base images is only recreated when the version of base.rb
209
+ # changes. The base image is build by starting a cloud image with the id 'ami-6d555119'. To setup the base-image,
210
+ # base.rb is executed by a newly created Kiel::Setup::Capistrano instance. The resulting base image will be stored
211
+ # with the tags:
212
+ # { 'image_type' => 'base', 'base' => '<version of base.rb>' }.
213
+ #
214
+ # An application image is build by starting a cloud server with the base image and executing the steps provided by
215
+ # application.rb. The application image is then stored with the following tags:
216
+ # { 'iamge_type' => 'application', 'application' => '<version of the overall repository>, 'base' => '<version of base.rb>' }.
217
+ def self.image steps, options = {}
218
+ check_options( options )
219
+
220
+ implemenation = Implementation.new defaults().merge( options )
221
+ steps = expand_steps steps
222
+ steps = implemenation.add_versions steps
223
+
224
+ while !steps.empty? do
225
+ step, *steps = *steps
226
+
227
+ implemenation.create_task step.dup, steps.dup
228
+ end
229
+ end
230
+
231
+ private_class_method :expand_steps, :check_options
232
+
233
+ # set the global defaults that are applied to Kiel::image
234
+ def self.set_defaults defs
235
+ check_options defs
236
+ @@defaults ||= DEFAULT_OPTIONS.dup
237
+ @@defaults.merge! defs
238
+ end
239
+
240
+ def self.reset_defaults
241
+ @@defaults = nil
242
+ end
243
+
244
+ # returns the global defaults that are applied to every call to Kiel::image
245
+ def self.defaults
246
+ @@defaults ||= DEFAULT_OPTIONS.dup
247
+ @@defaults
248
+ end
249
+ end
@@ -0,0 +1,163 @@
1
+ require 'digest/sha1'
2
+
3
+ module Kiel
4
+ module Cloud
5
+ INSTANCE_STARTUP_TIMEOUT = 120
6
+ RECOGNIZED_OPTIONS = [ :region, :credentials, :instance, :start_options ]
7
+
8
+ # Implements the connection to the Amazon Web Services (AWS). The current implementation works for one
9
+ # configured region. The default server is a small EC2 instance.
10
+ class AWS
11
+ # The contructor takes the following configuration options:
12
+ #
13
+ # :region:: A string naming the region to be used. If no region is given, the default region is used.
14
+ #
15
+ # :credentials:: A hash containing the fields 'access_key_id' and 'secret_access_key' with the credential
16
+ # information to your amazon account.
17
+ #
18
+ # :instance:: An instance of the AWS::EC2. If that instance is given, no +credentials:+ should be given.
19
+ # Kiel::Cloud::AWS will instead use this instance.
20
+ #
21
+ # :start_options:: Options that are applied to EC2::InstanceCollection.create (siehe aws-sdk for more
22
+ # details). The aws_tests.rb uses the :key_name and :security_groups options to set the
23
+ # name of the used ssh key and a security group, where ssh is enabled.
24
+ def initialize options = {}
25
+ require 'aws/ec2'
26
+
27
+ options.each_key do | key |
28
+ raise ArgumentError, "unrecognized option \'#{key}\'" unless RECOGNIZED_OPTIONS.include? key
29
+ end
30
+
31
+ @ec2 = options[ :instance ]
32
+ @start_options = options[ :start_options ] || {}
33
+
34
+ if @ec2
35
+ puts "\'credentials\' ignored as an instance was given too" if options.key? :credentials
36
+ else
37
+ ::AWS.config( options[ :credentials ] )
38
+
39
+ @ec2 = ::AWS::EC2.new
40
+ @ec2 = @ec2.regions[ options[ :region ] ] if options.key? :region
41
+ end
42
+ end
43
+
44
+ def all_images_by_tags tags
45
+ images = @ec2.images.with_owner('self').tagged( tags.first.first )
46
+
47
+ images = images.select do | image |
48
+ image_tags = image.tags.to_h
49
+ image_tags.merge( tags ) == image_tags
50
+ end
51
+
52
+ images
53
+ end
54
+
55
+ def image_by_tags tags
56
+ images = all_images_by_tags tags
57
+
58
+ raise "#{images.size} are tagged with the given tags: #{tags.inspect}" if images.size > 1
59
+ images.size == 1 ? images.first : nil
60
+ end
61
+
62
+ def wait_for_ec2 instance
63
+ puts "waiting for EC2 instance to start."
64
+ sleep_count = INSTANCE_STARTUP_TIMEOUT
65
+ while instance.status == :pending and sleep_count != 0 do
66
+ sleep 1
67
+ sleep_count = sleep_count - 1
68
+ end
69
+ end
70
+
71
+ def wait_for_image image
72
+ image_state = :pending
73
+ while image_state == :pending do
74
+ begin
75
+ image_state = image.state
76
+ rescue => e
77
+ puts "err: #{e.inspect}"
78
+ end
79
+
80
+ sleep 1
81
+ STDOUT.write '.'
82
+ end
83
+ puts ''
84
+ end
85
+
86
+ private :image_by_tags, :wait_for_ec2, :wait_for_image
87
+
88
+ # starts a server instance in the cloud, returning a handle to that instance.
89
+ # the image is either named by an image id +:id => 'image_id'+ or by a set of tags that match for
90
+ # just one image +:tags => { 'image_type' => 'application', 'base' => '34' }+
91
+ def start_instance image_name
92
+ options = @start_options.merge( if image_name.key?( :id )
93
+ { image_id: image_name[ :id ] }
94
+ else
95
+ tags = image_name[ :tags ]
96
+ image = image_by_tags tags
97
+ raise RuntimeError, "no image with tags: \'#{tags}\' found to start an instance" unless image
98
+
99
+ { image_id: image.id }
100
+ end )
101
+
102
+ instance = @ec2.instances.create( options )
103
+
104
+ begin
105
+ wait_for_ec2 instance
106
+ puts "ec2 instance \'#{instance.dns_name}\' started."
107
+ rescue
108
+ instance.terminate
109
+ raise
110
+ end
111
+
112
+ instance
113
+ end
114
+
115
+ # store the given +instance+ and add the hash of +tags+ to the image.
116
+ def store_image instance, tags
117
+ begin
118
+
119
+ puts "waiting 2 minutes before starting to take the image..."
120
+ sleep 120
121
+ puts "creating images..."
122
+
123
+ image = @ec2.images.create(
124
+ :instance_id => instance.id,
125
+ :no_reboot => true,
126
+ :description => "automaticaly created #{tags[ 'image_type' ]} image",
127
+ :name => "#{tags[ 'image_type' ]} #{Digest::SHA1.hexdigest tags.inspect}" )
128
+
129
+ wait_for_image image
130
+
131
+ tags.each do | key, value |
132
+ image.add_tag( key, :value => value )
133
+ end
134
+ ensure
135
+ stop_instance instance
136
+ end
137
+ end
138
+
139
+ # stops the given instance.
140
+ def stop_instance instance
141
+ begin
142
+ instance.terminate
143
+ rescue
144
+ end
145
+ end
146
+
147
+ # returns true, if an image with the given tags exists
148
+ def exists? tags
149
+ raise ArgumentError, "AWS.exists? with empty tags" if tags.empty?
150
+
151
+ image_by_tags tags
152
+ end
153
+
154
+ def dns_name instance
155
+ instance.dns_name
156
+ end
157
+
158
+ # deletes the given image
159
+ def delete_image image_name
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,76 @@
1
+
2
+ module Kiel
3
+ module Cloud
4
+
5
+ # Implementation of the Cloud access-Interface
6
+ # The implementation assumes that a cloud provider provides machine images, used to start machines,
7
+ # that this images can a not unique set of tags and a unique id. Where the id is provided by the cloud provider
8
+ # tags are provided by the user. Kiel used the tags to store version informations to an image and uses this
9
+ # set of tags to identify an image.
10
+ #
11
+ # The implementation assumes that a cloud provider allows to start a server with a machine image as parameter
12
+ # and that the resulting instance has a public dns_name to be reachable.
13
+ class Mock
14
+ # +existing_images+ simulates an initial set of existing machine images, +dns_names+ provides a set of
15
+ # names that are assigned to newly created cloud instances.
16
+ def initialize existing_images = [], dns_names = []
17
+ @names = dns_names.dup
18
+ @calls = []
19
+ @images = [ existing_images.dup ].flatten
20
+ @running = []
21
+ @next_instance = 0
22
+ end
23
+
24
+ # starts a server instance in the cloud, returning a handle to that instance.
25
+ # the image is either named by an image id +:id => 'image_id'+ or by a set of tags that match for
26
+ # just one image +:tags => { 'image_type' => 'application', 'base' => '34' }+
27
+ def start_instance image_name
28
+ raise ArgumentError, "image_name must contain exactly one identification" unless image_name.size == 1
29
+
30
+ @running << @next_instance
31
+ @next_instance += 1
32
+ @running.last
33
+ end
34
+
35
+ # store the given +instance+ under the given +image_name+ and add the hash of +tags+ to the image.
36
+ def store_image instance, tags
37
+ image = { id: instance, tags: tags }
38
+ @calls << { func: :store_image, args: image }
39
+ @images << image
40
+ stop_instance instance
41
+ end
42
+
43
+ # stops the given instance.
44
+ def stop_instance instance
45
+ unless @running.delete( instance )
46
+ raise RuntimeError, "there is no instance #{instance} running"
47
+ end
48
+ end
49
+
50
+ # returns true, if an image with the given tags exists
51
+ def exists? tags
52
+ @images.detect { | image | image[ :tags ] == tags }
53
+ end
54
+
55
+ # returns the dns name from an instance
56
+ def dns_name instance
57
+ "#{instance}"
58
+ end
59
+
60
+ #--
61
+ def calls
62
+ @calls
63
+ end
64
+
65
+ #--
66
+ def running_instances
67
+ @running
68
+ end
69
+
70
+ #--
71
+ def stored_images
72
+ @images
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,21 @@
1
+ require 'digest/sha1'
2
+
3
+ module Kiel
4
+ module SCM
5
+ class Git
6
+ def single_version file
7
+ result = `git rev-list --max-count 1 HEAD #{file}`
8
+ result.gsub( /\n$/, '' )
9
+ end
10
+
11
+ private :single_version
12
+
13
+ def version file
14
+ files = [ file == '*' ? '' : file ].flatten
15
+ return single_version( files.first ) if files.size == 1
16
+
17
+ files.sort.inject( '' ) { | sum, file | Digest::SHA1.hexdigest sum + single_version(file) }
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,24 @@
1
+
2
+ module Kiel
3
+ module SCM
4
+
5
+ # A mock, implementing the interface to a source code managment system
6
+ class Mock
7
+ def initialize versions = {}
8
+ @versions = versions
9
+ end
10
+
11
+ # returns the latest version of the given file
12
+ def version step
13
+ step = step.to_s
14
+ raise RuntimeError, "no mocked version for step \'#{step}\'" unless @versions.key? step
15
+ @versions[ step ]
16
+ end
17
+
18
+ #--
19
+ def versions hash
20
+ @versions = hash
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,46 @@
1
+ require 'yaml'
2
+
3
+ module Kiel
4
+ module Setup
5
+ # The Capistrano Setup component executes one task out of a given script. The name of the script is by default
6
+ # the name of step + '.rb' in the +:root_dir+ given to Kiel::image. The task to be executed is 'deploy:step'.
7
+ # The tags that will be applied to the resulting image are passed to the script as global constant ::TAGS
8
+ class Capistrano
9
+
10
+ # +options+ are passed directly to Capistrano:
11
+ # options.each { | key, value |
12
+ # set key, value
13
+ # }
14
+ # so every options that have to be set in every script should be set by using this options.
15
+ def initialize options = {}
16
+ @options = options
17
+ end
18
+
19
+ # step contains the whole step informations at least :setup_name contains the script to be executed,
20
+ # :tags contains the tags that will be added to the images after setup, :version contains the version of the
21
+ # steps associated scm_name's file version.
22
+ def execute step, dns_server
23
+ options = { script: step[ :setup_name ], tags: step[ :tags ], version: step[ :version ],
24
+ name: step[ :name ].to_s, capistrano_options: @options, capistrano_server: dns_server }
25
+
26
+ file = IO.popen( ['cap', '-f', File.expand_path( '../capistrano_executer.rb', __FILE__ ), step[ :name ].to_s ], 'r+' )
27
+
28
+ file.write YAML::dump( options )
29
+ file.close_write
30
+
31
+ last_line = ''
32
+ begin
33
+ begin
34
+ text = file.readpartial 1024
35
+ STDOUT.write text
36
+ last_line += text
37
+ last_line = last_line.split("\n").last
38
+ end until text.empty?
39
+ rescue EOFError
40
+ end
41
+
42
+ raise "Error while executing #{step[ :setup_name ]}" if last_line =~ /\+-\+-\+-\+ERORR\+-\+-\+-\+/
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,42 @@
1
+ require 'yaml'
2
+
3
+ module Kiel
4
+ OPTIONS = YAML.load STDIN
5
+ TAGS = OPTIONS[ :tags ]
6
+ end
7
+
8
+ Kiel::OPTIONS[ :capistrano_options ].each do | key, value |
9
+ set key, value
10
+ end
11
+
12
+ role :server, Kiel::OPTIONS[ :capistrano_server ]
13
+
14
+ load Kiel::OPTIONS[ :script ]
15
+
16
+ task Kiel::OPTIONS[ :name ] do
17
+ begin
18
+ retry_count = 10
19
+
20
+ begin
21
+ deploy.step
22
+ rescue Capistrano::ConnectionError => ex
23
+ raise unless ex.message =~ /Errno::ECONNREFUSED|Errno::ETIMEDOUT/
24
+
25
+ puts "Connection failed"
26
+ retry_count -= 1
27
+
28
+ if retry_count == 0
29
+ puts 'giving up...'
30
+ raise
31
+ end
32
+
33
+ puts 'retrying...'
34
+ sleep 15
35
+ retry
36
+ end
37
+ rescue Exception => ex
38
+ puts ex.message
39
+ puts ex.backtrace.join("\n")
40
+ puts '+-+-+-+ERORR+-+-+-+'
41
+ end
42
+ end
@@ -0,0 +1,14 @@
1
+ module Kiel
2
+ module Setup
3
+ class Mock
4
+ def execute step, server
5
+ @steps ||= []
6
+ @steps << step[ :setup_name ]
7
+ end
8
+
9
+ def executed_steps
10
+ @steps || []
11
+ end
12
+ end
13
+ end
14
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kiel
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.0'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Torsten Robitzki
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-30 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Helper to build rake task to build cloud images step by step for easier
15
+ debugging and testing
16
+ email: gemmaster@robitzki.de
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - lib/kiel/cloud/aws.rb
22
+ - lib/kiel/cloud/mock.rb
23
+ - lib/kiel/scm/git.rb
24
+ - lib/kiel/scm/mock.rb
25
+ - lib/kiel/setup/capistrano.rb
26
+ - lib/kiel/setup/capistrano_executer.rb
27
+ - lib/kiel/setup/mock.rb
28
+ - lib/kiel.rb
29
+ homepage: https://github.com/TorstenRobitzki/Kiel
30
+ licenses:
31
+ - MIT
32
+ post_install_message:
33
+ rdoc_options: []
34
+ require_paths:
35
+ - lib
36
+ required_ruby_version: !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ! '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ required_rubygems_version: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ requirements: []
49
+ rubyforge_project:
50
+ rubygems_version: 1.8.24
51
+ signing_key:
52
+ specification_version: 3
53
+ summary: Building cloud images step by step
54
+ test_files: []