Class: Lono::Template::Upload

Inherits:
Object
  • Object
show all
Includes:
AwsService
Defined in:
lib/lono/template/upload.rb

Instance Method Summary collapse

Methods included from AwsService

#s3

Constructor Details

#initialize(options = {}) ⇒ Upload

Returns a new instance of Upload.



9
10
11
12
13
# File 'lib/lono/template/upload.rb', line 9

def initialize(options={})
  @options = options
  @checksums = {}
  @prefix = "#{folder_key}/#{Lono.env}/templates" # s3://s3-bucket/folder/development/templates
end

Instance Method Details

#ensure_s3_setup!Object

nice warning if the s3 path not found



140
141
142
143
144
145
146
147
# File 'lib/lono/template/upload.rb', line 140

def ensure_s3_setup!
  return if @options[:noop]
  return if s3_folder

  say "Unable to upload templates to s3 because you have not configured the s3_folder option in lono settings.yml.".colorize(:red)
  say "Please configure settings.yml with s3_folder.  For more help: http://lono.cloud/docs/settings/".colorize(:red)
  exit 1
end

#folder_keyObject

The folder_key is the s3_folder setting with the s3 bucket.

Example:

s3_bucket('s3://mybucket/templates/storage/path')
=> templates/storage/path


128
129
130
131
132
# File 'lib/lono/template/upload.rb', line 128

def folder_key
  return nil if @options[:noop] # to get spec passing
  return nil unless s3_folder
  s3_folder.sub('s3://','').split('/')[1..-1].join('/')
end

#load_checksums!Object

Read existing files on s3 to grab their md5 checksum. We do this so we can see if we should avoid re-uploading the s3 child template entirely. If we upload a new child template that does not change AWS CloudFormation is not smart enough to know that it not has changed. I think all AWS CloudFormation does is check if the file’s timestamp.

Thought this would result in better AWS Change Set info but AWS still reports child stacks being changed even though they should not be reported. Leaving this s3 checksum in for now.



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/lono/template/upload.rb', line 36

def load_checksums!
  return if @options[:noop]

  resp = s3.list_objects(bucket: s3_bucket, prefix: @prefix)
  resp.contents.each do |object|
    # key does not include the bucket name
    #    full path = s3://my-bucket/s3_folder/templates/production/my-template.yml
    #    key = s3_folder/templates/production/my-template.yml
    # etag is the checksum as long as the file is not a multi-part file upload
    # it has extra double quotes wrapped around it.
    #    etag = "\"9cb437490cee2cc96101baf326e5ca81\""
    @checksums[object.key] = strip_surrounding_quotes(object.etag)
  end
  @checksums
end

#remote_checksum(path) ⇒ Object

path = ./output/templates/docker.yml s3_folder = s3://boltops-dev/s3_folder/templates/development/docker.yml



87
88
89
90
91
92
93
# File 'lib/lono/template/upload.rb', line 87

def remote_checksum(path)
  # first convert the local path to the path format that is stored in @checksums keys
  # ./output/templates/docker.yml => s3_folder/templates/development/docker.yml
  pretty_path = path.sub(/^\.\//, '')
  key = "#{@prefix}/#{pretty_path.sub(%r{output/templates/},'')}"
  @checksums[key]
end

#runObject



15
16
17
18
19
20
21
22
23
24
25
# File 'lib/lono/template/upload.rb', line 15

def run
  ensure_s3_setup!
  load_checksums!

  say "Uploading CloudFormation templates..."
  paths = Dir.glob("#{Lono.config.output_path}/templates/**/*")
  paths.select { |p| File.file?(p) }.each do |path|
    upload(path)
  end
  say "Templates uploaded to s3."
end

#s3_bucketObject

Parse the s3_folder setting and remove the folder portion to leave the “s3_bucket” portion Example:

s3_bucket('s3://mybucket/templates/storage/path')
=> mybucket


117
118
119
120
121
# File 'lib/lono/template/upload.rb', line 117

def s3_bucket
  return nil if @options[:noop] # to get spec passing
  return nil unless s3_folder
  s3_folder.sub('s3://','').split('/').first
end

#s3_folderObject



134
135
136
137
# File 'lib/lono/template/upload.rb', line 134

def s3_folder
  settings = Lono::Setting.new
  settings.s3_folder
end

#s3_https_url(template_path) ⇒ Object



96
97
98
99
# File 'lib/lono/template/upload.rb', line 96

def s3_https_url(template_path)
  ensure_s3_setup!
  "https://s3.amazonaws.com/#{s3_bucket}/#{@prefix}/#{template_path}"
end

#s3_presigned_url(template_output_path) ⇒ Object

used for cfn/base.rb def set_template_body!(params)



102
103
104
105
106
# File 'lib/lono/template/upload.rb', line 102

def s3_presigned_url(template_output_path)
  template_path = template_output_path.sub('output/templates/','')
  key = "#{@prefix}/#{template_path}"
  s3_presigner.presigned_url(:get_object, bucket: s3_bucket, key: key)
end

#s3_presignerObject



108
109
110
# File 'lib/lono/template/upload.rb', line 108

def s3_presigner
  @signer ||= Aws::S3::Presigner.new
end

#say(message) ⇒ Object



149
150
151
# File 'lib/lono/template/upload.rb', line 149

def say(message)
  puts message unless @options[:quiet]
end

#strip_surrounding_quotes(string) ⇒ Object



52
53
54
# File 'lib/lono/template/upload.rb', line 52

def strip_surrounding_quotes(string)
  string.sub(/^"/,'').sub(/"$/,'')
end

#upload(path) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/lono/template/upload.rb', line 56

def upload(path)
  pretty_path = path.sub(/^\.\//, '')
  key = "#{@prefix}/#{pretty_path.sub(%r{output/templates/},'')}"
  s3_full_path = "s3://#{s3_bucket}/#{key}"

  local_checksum = Digest::MD5.hexdigest(IO.read(path))
  remote_checksum = remote_checksum(path)
  if local_checksum == remote_checksum
    say("Not modified: #{pretty_path} to #{s3_full_path}".colorize(:yellow)) unless @options[:noop]
    return # do not upload unless the checksum has changed
  end

  resp = s3.put_object(
    body: IO.read(path),
    bucket: s3_bucket,
    key: key,
    storage_class: "REDUCED_REDUNDANCY"
  ) unless @options[:noop]

  # Example output:
  # Uploaded: output/templates/docker.yml to s3://boltops-dev/s3_folder/templates/development/docker.yml
  # Uploaded: output/templates/ecs/private.yml to s3://boltops-dev/s3_folder/templates/development/ecs/private.yml
  message = "Uploaded: #{pretty_path} to #{s3_full_path}".colorize(:green)
  message = "NOOP: #{message}" if @options[:noop]
  say message
end