cloudformation-tool 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/LICENSE +339 -0
- data/README.md +204 -0
- data/bin/cftool +9 -0
- data/lib/cloud_formation_tool.rb +94 -0
- data/lib/cloud_formation_tool/cli.rb +5 -0
- data/lib/cloud_formation_tool/cli/compile.rb +19 -0
- data/lib/cloud_formation_tool/cli/create.rb +50 -0
- data/lib/cloud_formation_tool/cli/delete.rb +18 -0
- data/lib/cloud_formation_tool/cli/list_stacks.rb +15 -0
- data/lib/cloud_formation_tool/cli/main.rb +31 -0
- data/lib/cloud_formation_tool/cli/monitor.rb +28 -0
- data/lib/cloud_formation_tool/cli/parameters.rb +20 -0
- data/lib/cloud_formation_tool/cli/servers.rb +30 -0
- data/lib/cloud_formation_tool/cli/status.rb +18 -0
- data/lib/cloud_formation_tool/cloud_formation.rb +153 -0
- data/lib/cloud_formation_tool/cloud_formation/lambda_code.rb +41 -0
- data/lib/cloud_formation_tool/cloud_formation/stack.rb +152 -0
- data/lib/cloud_formation_tool/cloud_init.rb +86 -0
- data/lib/cloud_formation_tool/errors.rb +11 -0
- data/lib/cloud_formation_tool/storable.rb +41 -0
- data/lib/cloud_formation_tool/version.rb +3 -0
- metadata +109 -0
| @@ -0,0 +1,41 @@ | |
| 1 | 
            +
            require 'net/http'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module CloudFormationTool
         | 
| 4 | 
            +
              class CloudFormation
         | 
| 5 | 
            +
                
         | 
| 6 | 
            +
                class LambdaCode
         | 
| 7 | 
            +
                  include Storable
         | 
| 8 | 
            +
                  
         | 
| 9 | 
            +
                  def initialize(url)
         | 
| 10 | 
            +
                    log "Downloading Lambda code from #{url}"
         | 
| 11 | 
            +
                    res = fetch(url)
         | 
| 12 | 
            +
                    
         | 
| 13 | 
            +
                    @s3_url = URI(upload(make_filename(url.split('.').last), res.body, res['content-type']))
         | 
| 14 | 
            +
                    log "uploaded Lambda function to #{@s3_url}"
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
                  
         | 
| 17 | 
            +
                  def fetch(uri_str, limit = 10)
         | 
| 18 | 
            +
                    raise ArgumentError, 'too many HTTP redirects' if limit == 0
         | 
| 19 | 
            +
                    response = Net::HTTP.get_response(URI(uri_str))
         | 
| 20 | 
            +
                    case response
         | 
| 21 | 
            +
                    when Net::HTTPSuccess then
         | 
| 22 | 
            +
                      response
         | 
| 23 | 
            +
                    when Net::HTTPRedirection then
         | 
| 24 | 
            +
                      location = response['location']
         | 
| 25 | 
            +
                      log "redirected to #{location}"
         | 
| 26 | 
            +
                      fetch(location, limit - 1)
         | 
| 27 | 
            +
                    else
         | 
| 28 | 
            +
                      raise AppError, "Error downloading #{url}: #{response.value}"
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                  
         | 
| 32 | 
            +
                  def to_cloudformation
         | 
| 33 | 
            +
                    {
         | 
| 34 | 
            +
                      'S3Bucket' => @s3_url.hostname.split('.').first,
         | 
| 35 | 
            +
                      'S3Key' => @s3_url.path[1..-1]
         | 
| 36 | 
            +
                    }
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
            end
         | 
| @@ -0,0 +1,152 @@ | |
| 1 | 
            +
            module CloudFormationTool
         | 
| 2 | 
            +
              class CloudFormation
         | 
| 3 | 
            +
                
         | 
| 4 | 
            +
                class Stack
         | 
| 5 | 
            +
                  include Enumerable
         | 
| 6 | 
            +
                  include Storable
         | 
| 7 | 
            +
                  include CloudFormationTool
         | 
| 8 | 
            +
                  
         | 
| 9 | 
            +
                  attr_reader :name
         | 
| 10 | 
            +
                  
         | 
| 11 | 
            +
                  def initialize(name)
         | 
| 12 | 
            +
                    @name = name
         | 
| 13 | 
            +
                    @seenev = Set.new
         | 
| 14 | 
            +
                    @watch_timeouts = 0
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
                  
         | 
| 17 | 
            +
                  def delete
         | 
| 18 | 
            +
                    awscf.delete_stack  stack_name: @name
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
                  
         | 
| 21 | 
            +
                  def exist?
         | 
| 22 | 
            +
                    begin
         | 
| 23 | 
            +
                      awscf.describe_stacks stack_name: name
         | 
| 24 | 
            +
                      true
         | 
| 25 | 
            +
                    rescue Aws::CloudFormation::Errors::ValidationError => e
         | 
| 26 | 
            +
                      false
         | 
| 27 | 
            +
                    end
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
                  
         | 
| 30 | 
            +
                  def update(url, filepath, params = {})
         | 
| 31 | 
            +
                    log "Updating existing stack '#{name}' from '#{filepath}' params #{params.inspect}"
         | 
| 32 | 
            +
                    resp = awscf.update_stack({
         | 
| 33 | 
            +
                      stack_name: @name,
         | 
| 34 | 
            +
                      template_url: url,
         | 
| 35 | 
            +
                      capabilities: %w(CAPABILITY_IAM CAPABILITY_NAMED_IAM),
         | 
| 36 | 
            +
                      parameters: params.collect do |k,v|
         | 
| 37 | 
            +
                        {
         | 
| 38 | 
            +
                          parameter_key: k.to_s,
         | 
| 39 | 
            +
                          parameter_value: v.to_s,
         | 
| 40 | 
            +
                          use_previous_value: false,
         | 
| 41 | 
            +
                        }
         | 
| 42 | 
            +
                      end
         | 
| 43 | 
            +
                    })
         | 
| 44 | 
            +
                    resp.stack_id
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
                  
         | 
| 47 | 
            +
                  def create(template, params = {})
         | 
| 48 | 
            +
                    tmpl = CloudFormation.parse(template).to_yaml
         | 
| 49 | 
            +
                    url = upload(make_filename('yaml'), tmpl)
         | 
| 50 | 
            +
                    return update(url, template, params) if exist?
         | 
| 51 | 
            +
                    log "Creating stack '#{name}' from '#{template}' params #{params.inspect}"
         | 
| 52 | 
            +
                    resp = awscf.create_stack({
         | 
| 53 | 
            +
                      stack_name: @name,
         | 
| 54 | 
            +
                      template_url: url,
         | 
| 55 | 
            +
                      capabilities: %w(CAPABILITY_IAM CAPABILITY_NAMED_IAM),
         | 
| 56 | 
            +
                      on_failure: "DO_NOTHING", ##"ROLLBACK",
         | 
| 57 | 
            +
                      parameters: params.collect do |k,v|
         | 
| 58 | 
            +
                        {
         | 
| 59 | 
            +
                          parameter_key: k.to_s,
         | 
| 60 | 
            +
                          parameter_value: v.to_s,
         | 
| 61 | 
            +
                          use_previous_value: false,
         | 
| 62 | 
            +
                        }
         | 
| 63 | 
            +
                      end
         | 
| 64 | 
            +
                    })
         | 
| 65 | 
            +
                    resp.stack_id
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
                  
         | 
| 68 | 
            +
                  def resources
         | 
| 69 | 
            +
                    begin
         | 
| 70 | 
            +
                      resp = awscf.describe_stack_resources  stack_name: @name
         | 
| 71 | 
            +
                      resp.stack_resources
         | 
| 72 | 
            +
                    rescue Aws::CloudFormation::Errors::ValidationError => e
         | 
| 73 | 
            +
                      raise Errors::AppError, "Failed to get resources: #{e.message}"
         | 
| 74 | 
            +
                    end
         | 
| 75 | 
            +
                  end
         | 
| 76 | 
            +
                  
         | 
| 77 | 
            +
                  def asgroups
         | 
| 78 | 
            +
                    resources.select do |res|
         | 
| 79 | 
            +
                      res.resource_type == 'AWS::AutoScaling::AutoScalingGroup'
         | 
| 80 | 
            +
                    end
         | 
| 81 | 
            +
                  end
         | 
| 82 | 
            +
                  
         | 
| 83 | 
            +
                  def see_events
         | 
| 84 | 
            +
                    each { |e| @seenev << e.event_id }
         | 
| 85 | 
            +
                  end
         | 
| 86 | 
            +
                  
         | 
| 87 | 
            +
                  def monitor(start_time = nil)
         | 
| 88 | 
            +
                    done = false
         | 
| 89 | 
            +
                    begin
         | 
| 90 | 
            +
                      until done
         | 
| 91 | 
            +
                        reverse_each do |ev|
         | 
| 92 | 
            +
                          next if @seenev.add?(ev.event_id).nil?
         | 
| 93 | 
            +
                          text = "#{ev.timestamp.strftime "%Y-%m-%d %H:%M:%S"}| " + %w(
         | 
| 94 | 
            +
                            resource_type:40
         | 
| 95 | 
            +
                            logical_resource_id:38
         | 
| 96 | 
            +
                            resource_status
         | 
| 97 | 
            +
                          ).collect { |field|
         | 
| 98 | 
            +
                            (name,size) = field.split(":")
         | 
| 99 | 
            +
                            size ||= 1
         | 
| 100 | 
            +
                            ev.send(name.to_sym).ljust(size.to_i, ' ')
         | 
| 101 | 
            +
                          }.join("  ")
         | 
| 102 | 
            +
                          text += " " + ev.resource_status_reason if ev.resource_status =~ /_FAILED/
         | 
| 103 | 
            +
                          if start_time.nil? or start_time < ev.timestamp
         | 
| 104 | 
            +
                            puts text
         | 
| 105 | 
            +
                          end
         | 
| 106 | 
            +
                          done = (ev.resource_type == "AWS::CloudFormation::Stack" and ev.resource_status =~ /(_COMPLETE|_FAILED)$/)
         | 
| 107 | 
            +
                        end
         | 
| 108 | 
            +
                      end
         | 
| 109 | 
            +
                    rescue CloudFormationTool::Errors::StackDoesNotExistError => e
         | 
| 110 | 
            +
                      puts "Stack #{name} does not exist"
         | 
| 111 | 
            +
                    end
         | 
| 112 | 
            +
                  end
         | 
| 113 | 
            +
                  
         | 
| 114 | 
            +
                  def each
         | 
| 115 | 
            +
                    token = nil
         | 
| 116 | 
            +
                    sleep(if @_last_poll_time.nil?
         | 
| 117 | 
            +
                      0
         | 
| 118 | 
            +
                      else
         | 
| 119 | 
            +
                        diff = Time.now - @_last_poll_time
         | 
| 120 | 
            +
                        if diff < 1
         | 
| 121 | 
            +
                          diff
         | 
| 122 | 
            +
                        else
         | 
| 123 | 
            +
                          0
         | 
| 124 | 
            +
                        end
         | 
| 125 | 
            +
                      end)
         | 
| 126 | 
            +
                    begin
         | 
| 127 | 
            +
                      resp = awscf.describe_stack_events stack_name: name, next_token: token
         | 
| 128 | 
            +
                      @watch_timeouts = 0
         | 
| 129 | 
            +
                      resp.stack_events.each do |ev|
         | 
| 130 | 
            +
                        yield ev
         | 
| 131 | 
            +
                      end
         | 
| 132 | 
            +
                    rescue Aws::CloudFormation::Errors::Throttling => e
         | 
| 133 | 
            +
                      sleep 1
         | 
| 134 | 
            +
                      retry
         | 
| 135 | 
            +
                    rescue Seahorse::Client::NetworkingError => e # we get this when there's a timeout
         | 
| 136 | 
            +
                      if (@watch_timeouts += 1) > 5
         | 
| 137 | 
            +
                        raise AppError, "Too many timeouts!"
         | 
| 138 | 
            +
                      else
         | 
| 139 | 
            +
                        retry
         | 
| 140 | 
            +
                      end
         | 
| 141 | 
            +
                    rescue Aws::CloudFormation::Errors::ValidationError => e
         | 
| 142 | 
            +
                      if e.message =~ /does not exist/
         | 
| 143 | 
            +
                        raise CloudFormationTool::Errors::StackDoesNotExistError, "Stack does not exist"
         | 
| 144 | 
            +
                      else
         | 
| 145 | 
            +
                        raise e
         | 
| 146 | 
            +
                      end
         | 
| 147 | 
            +
                    end
         | 
| 148 | 
            +
                  end
         | 
| 149 | 
            +
                end
         | 
| 150 | 
            +
             | 
| 151 | 
            +
              end
         | 
| 152 | 
            +
            end
         | 
| @@ -0,0 +1,86 @@ | |
| 1 | 
            +
            require 'zlib'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module CloudFormationTool
         | 
| 4 | 
            +
              class CloudInit
         | 
| 5 | 
            +
                include Storable
         | 
| 6 | 
            +
                
         | 
| 7 | 
            +
                attr_reader :path
         | 
| 8 | 
            +
                
         | 
| 9 | 
            +
                def initialize(path)
         | 
| 10 | 
            +
                  @path = path
         | 
| 11 | 
            +
                  log "Loading #{path}"
         | 
| 12 | 
            +
                  begin
         | 
| 13 | 
            +
                    @initfile = YAML.load(File.read(path)).to_h
         | 
| 14 | 
            +
                  rescue Errno::ENOENT => e
         | 
| 15 | 
            +
                    raise AppError.new("Error reading #{@path}: " + e.message)
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
              
         | 
| 19 | 
            +
                def compile
         | 
| 20 | 
            +
                  basepath = File.dirname(path)
         | 
| 21 | 
            +
                  basepath = "." if basepath.empty?
         | 
| 22 | 
            +
                  @initfile['write_files'] = (@initfile['write_files'] || []).collect do |file|
         | 
| 23 | 
            +
                    if file['file']
         | 
| 24 | 
            +
                      begin
         | 
| 25 | 
            +
                        read_file_content(basepath + "/" + file.delete('file'), file)
         | 
| 26 | 
            +
                      rescue Errno::EISDIR => e
         | 
| 27 | 
            +
                        raise AppError, "#{path} - error loading embedded file for #{file['path']}: " + e.message
         | 
| 28 | 
            +
                      rescue Errno::ENOENT => e
         | 
| 29 | 
            +
                        raise AppError, "#{path} - error loading embedded file for #{file['path']}: " + e.message
         | 
| 30 | 
            +
                      end
         | 
| 31 | 
            +
                    else
         | 
| 32 | 
            +
                      file
         | 
| 33 | 
            +
                    end
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
                  @initfile['write_files'] += (@initfile.delete('write_directories') || []).collect do |directory|
         | 
| 36 | 
            +
                    realdir = "#{basepath}/#{directory['source']}"
         | 
| 37 | 
            +
                    raise AppError.new("Cloud-init file #{path} references missing directory #{realdir}") unless File.exist? realdir
         | 
| 38 | 
            +
                    read_dir_files(realdir, directory['target'])
         | 
| 39 | 
            +
                  end.flatten
         | 
| 40 | 
            +
                  "#cloud-config\n" + @initfile.to_yaml
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
                
         | 
| 43 | 
            +
                def encode
         | 
| 44 | 
            +
                  yamlout = compile
         | 
| 45 | 
            +
                  if yamlout.size > 16384 # max AWS EC2 user data size - try compressing it
         | 
| 46 | 
            +
                    yamlout = Zlib::Deflate.new(nil, 31).deflate(yamlout, Zlib::FINISH) # 31 is the magic word to have deflate create a gzip compatible header
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
                  if yamlout.size > 16384 # still to big, we should upload to S3 and create an include file
         | 
| 49 | 
            +
                    url = upload  make_filename('init'),
         | 
| 50 | 
            +
                                  yamlout, 'text/cloud-config'
         | 
| 51 | 
            +
                    log "Wrote cloud config to #{url}"
         | 
| 52 | 
            +
                    [ "#include", url ].join "\n"  
         | 
| 53 | 
            +
                  else
         | 
| 54 | 
            +
                    yamlout
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
                
         | 
| 58 | 
            +
                def read_file_content(filepath, spec)
         | 
| 59 | 
            +
                  spec['encoding'] = 'base64'
         | 
| 60 | 
            +
                  spec['content'] = Base64.strict_encode64(File.read(filepath))
         | 
| 61 | 
            +
                  spec
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
                
         | 
| 64 | 
            +
                def read_dir_files(source, target)
         | 
| 65 | 
            +
                  Dir.entries(source).select do |entry|
         | 
| 66 | 
            +
                    entry !~ /^\.{1,2}$/
         | 
| 67 | 
            +
                  end.collect do |entry|
         | 
| 68 | 
            +
                    path = source + "/" + entry
         | 
| 69 | 
            +
                    targetpath = target + "/" + entry
         | 
| 70 | 
            +
                    if File.directory? path
         | 
| 71 | 
            +
                      read_dir_files(path, targetpath)
         | 
| 72 | 
            +
                    else
         | 
| 73 | 
            +
                      [ read_file_content(path, {
         | 
| 74 | 
            +
                        'path' => targetpath,
         | 
| 75 | 
            +
                        'permissions' => (("%o" % File.stat(path).mode)[-4..-1])
         | 
| 76 | 
            +
                      }) ]
         | 
| 77 | 
            +
                    end
         | 
| 78 | 
            +
                  end.flatten
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
                
         | 
| 81 | 
            +
                def to_base64
         | 
| 82 | 
            +
                  Base64.encode64(encode)
         | 
| 83 | 
            +
                end
         | 
| 84 | 
            +
                
         | 
| 85 | 
            +
              end
         | 
| 86 | 
            +
            end
         | 
| @@ -0,0 +1,41 @@ | |
| 1 | 
            +
            require 'digest'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module CloudFormationTool
         | 
| 4 | 
            +
              module Storable
         | 
| 5 | 
            +
                include CloudFormationTool
         | 
| 6 | 
            +
                
         | 
| 7 | 
            +
                def make_filename(ext = '')
         | 
| 8 | 
            +
                  base = "#{File.basename(Dir.pwd)}-#{Time.now.strftime("%Y%m%d%H%M%S")}"
         | 
| 9 | 
            +
                  if ext.empty?
         | 
| 10 | 
            +
                    base
         | 
| 11 | 
            +
                  else
         | 
| 12 | 
            +
                    "#{base}.#{ext}"
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
                
         | 
| 16 | 
            +
                def upload(path, content, mime_type = 'text/yaml')
         | 
| 17 | 
            +
                  md5 = Digest::MD5.hexdigest content
         | 
| 18 | 
            +
                  prefix = "#{md5[0]}/#{md5[1..2]}/#{md5}"
         | 
| 19 | 
            +
                  b = Aws::S3::Bucket.new(s3_bucket_name(region), client: awss3(region))
         | 
| 20 | 
            +
                  # return early if we already have a copy of this object stored.
         | 
| 21 | 
            +
                  # if this object was previously uploaded, we use its URLs (and not, for example,
         | 
| 22 | 
            +
                  # do a local copy to the requested path) because this way cloudformation can see
         | 
| 23 | 
            +
                  # that the updated template is exactly the same as the old one and will not force
         | 
| 24 | 
            +
                  # an unneeded update.
         | 
| 25 | 
            +
                  o = b.objects(prefix: "cf-compiled/#{prefix}/").first
         | 
| 26 | 
            +
                  if o.nil?
         | 
| 27 | 
            +
                    # no such luck, we need to actually upload the file
         | 
| 28 | 
            +
                    o = b.object("cf-compiled/#{prefix}/#{path}")
         | 
| 29 | 
            +
                    o.put acl: 'public-read',
         | 
| 30 | 
            +
                          body: content,
         | 
| 31 | 
            +
                          content_disposition: 'attachment',
         | 
| 32 | 
            +
                          content_encoding: 'gzip',
         | 
| 33 | 
            +
                          content_type: mime_type,
         | 
| 34 | 
            +
                          storage_class: 'REDUCED_REDUNDANCY'
         | 
| 35 | 
            +
                  else
         | 
| 36 | 
            +
                    log "re-using cached object"
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
                  o.public_url
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
            end
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,109 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: cloudformation-tool
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 0.1.1
         | 
| 5 | 
            +
            platform: ruby
         | 
| 6 | 
            +
            authors:
         | 
| 7 | 
            +
            - Oded Arbel
         | 
| 8 | 
            +
            autorequire: 
         | 
| 9 | 
            +
            bindir: bin
         | 
| 10 | 
            +
            cert_chain: []
         | 
| 11 | 
            +
            date: 2011-03-10 00:00:00.000000000 Z
         | 
| 12 | 
            +
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: clamp
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - "~>"
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: '1'
         | 
| 20 | 
            +
              type: :runtime
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - "~>"
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: '1'
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            +
              name: aws-sdk
         | 
| 29 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 | 
            +
                requirements:
         | 
| 31 | 
            +
                - - "~>"
         | 
| 32 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            +
                    version: '2'
         | 
| 34 | 
            +
              type: :runtime
         | 
| 35 | 
            +
              prerelease: false
         | 
| 36 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 | 
            +
                requirements:
         | 
| 38 | 
            +
                - - "~>"
         | 
| 39 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            +
                    version: '2'
         | 
| 41 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 42 | 
            +
              name: autoloaded
         | 
| 43 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 44 | 
            +
                requirements:
         | 
| 45 | 
            +
                - - "~>"
         | 
| 46 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 47 | 
            +
                    version: '2'
         | 
| 48 | 
            +
              type: :runtime
         | 
| 49 | 
            +
              prerelease: false
         | 
| 50 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 51 | 
            +
                requirements:
         | 
| 52 | 
            +
                - - "~>"
         | 
| 53 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 54 | 
            +
                    version: '2'
         | 
| 55 | 
            +
            description: 
         | 
| 56 | 
            +
            email: oded.arbel@greenfieldtech.net
         | 
| 57 | 
            +
            executables:
         | 
| 58 | 
            +
            - cftool
         | 
| 59 | 
            +
            extensions: []
         | 
| 60 | 
            +
            extra_rdoc_files:
         | 
| 61 | 
            +
            - LICENSE
         | 
| 62 | 
            +
            - README.md
         | 
| 63 | 
            +
            files:
         | 
| 64 | 
            +
            - LICENSE
         | 
| 65 | 
            +
            - README.md
         | 
| 66 | 
            +
            - bin/cftool
         | 
| 67 | 
            +
            - lib/cloud_formation_tool.rb
         | 
| 68 | 
            +
            - lib/cloud_formation_tool/cli.rb
         | 
| 69 | 
            +
            - lib/cloud_formation_tool/cli/compile.rb
         | 
| 70 | 
            +
            - lib/cloud_formation_tool/cli/create.rb
         | 
| 71 | 
            +
            - lib/cloud_formation_tool/cli/delete.rb
         | 
| 72 | 
            +
            - lib/cloud_formation_tool/cli/list_stacks.rb
         | 
| 73 | 
            +
            - lib/cloud_formation_tool/cli/main.rb
         | 
| 74 | 
            +
            - lib/cloud_formation_tool/cli/monitor.rb
         | 
| 75 | 
            +
            - lib/cloud_formation_tool/cli/parameters.rb
         | 
| 76 | 
            +
            - lib/cloud_formation_tool/cli/servers.rb
         | 
| 77 | 
            +
            - lib/cloud_formation_tool/cli/status.rb
         | 
| 78 | 
            +
            - lib/cloud_formation_tool/cloud_formation.rb
         | 
| 79 | 
            +
            - lib/cloud_formation_tool/cloud_formation/lambda_code.rb
         | 
| 80 | 
            +
            - lib/cloud_formation_tool/cloud_formation/stack.rb
         | 
| 81 | 
            +
            - lib/cloud_formation_tool/cloud_init.rb
         | 
| 82 | 
            +
            - lib/cloud_formation_tool/errors.rb
         | 
| 83 | 
            +
            - lib/cloud_formation_tool/storable.rb
         | 
| 84 | 
            +
            - lib/cloud_formation_tool/version.rb
         | 
| 85 | 
            +
            homepage: http://github.com/GreenfieldTech/cloudformation-tool
         | 
| 86 | 
            +
            licenses:
         | 
| 87 | 
            +
            - GPL-2.0
         | 
| 88 | 
            +
            metadata: {}
         | 
| 89 | 
            +
            post_install_message: 
         | 
| 90 | 
            +
            rdoc_options: []
         | 
| 91 | 
            +
            require_paths:
         | 
| 92 | 
            +
            - lib
         | 
| 93 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 94 | 
            +
              requirements:
         | 
| 95 | 
            +
              - - ">="
         | 
| 96 | 
            +
                - !ruby/object:Gem::Version
         | 
| 97 | 
            +
                  version: '0'
         | 
| 98 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 99 | 
            +
              requirements:
         | 
| 100 | 
            +
              - - ">="
         | 
| 101 | 
            +
                - !ruby/object:Gem::Version
         | 
| 102 | 
            +
                  version: '0'
         | 
| 103 | 
            +
            requirements: []
         | 
| 104 | 
            +
            rubyforge_project: 
         | 
| 105 | 
            +
            rubygems_version: 2.4.5
         | 
| 106 | 
            +
            signing_key: 
         | 
| 107 | 
            +
            specification_version: 4
         | 
| 108 | 
            +
            summary: A pre-compiler tool for CloudFormation YAML templates
         | 
| 109 | 
            +
            test_files: []
         |