Class: Lono::Template::Upload

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

Instance Method Summary collapse

Methods included from AwsServices

#s3

Constructor Details

#initialize(options = {}) ⇒ Upload

Returns a new instance of Upload.



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

def initialize(options={})
  @options = options
  @checksums = {}
end

Instance Method Details

#ensure_s3_setup!Object

nice warning if the s3 path not found



116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/lono/template/upload.rb', line 116

def ensure_s3_setup!
  return if @options[:noop]

  settings = Lono::Settings.new
  if settings.s3_path
    @s3_full_path = settings.s3_path
  else
    say "Unable to upload templates to s3 because you have not configured the s3.path option in lono settings.yml.".colorize(:red)
    say "Please configure  settings.yml with s3.path.  Refer to http://lono.cloud/docs/settings/ for more help.".colorize(:red)
    exit 1
  end
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.



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

def load_checksums!
  return if @options[:noop]

  prefix = "#{s3_path}/#{Lono.env}" # s3://s3-bucket-and-path-from-settings/prod
  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/cloudformation-templates/prod/my-template.yml
    #    key = cloudformation-templates/prod/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/docker.yml s3_path = s3://boltops-stag/cloudformation-templates/stag/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/docker.yml => cloudformation-templates/stag/docker.yml
  pretty_path = path.sub(/^\.\//, '')
  key = "#{s3_path}/#{Lono.env}/#{pretty_path.sub(/^output\//,'')}"
  @checksums[key]
end

#runObject



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

def run
  ensure_s3_setup!
  load_checksums!

  paths = Dir.glob("#{Lono.root}/output/**/*")
  paths.reject { |p| p =~ %r{output/params} }.
        select { |p| File.file?(p) }.each do |path|
    upload(path)
  end
  say "Templates uploaded to s3."
end

#s3_bucketObject

Example:

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


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

def s3_bucket
  return '' if @options[:noop] # to get spec passing
  @s3_full_path.sub('s3://','').split('/').first
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}/#{s3_path}/#{Lono.env}/#{template_path}"
end

#s3_pathObject

Example:

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


110
111
112
113
# File 'lib/lono/template/upload.rb', line 110

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

#say(message) ⇒ Object



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

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 = "#{s3_path}/#{Lono.env}/#{pretty_path.sub(/^output\//,'')}"
  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/docker.yml to s3://boltops-stag/cloudformation-templates/stag/docker.yml
  # Uploaded: output/ecs/private.yml to s3://boltops-stag/cloudformation-templates/stag/ecs/private.yml
  message = "Uploaded: #{pretty_path} to #{s3_full_path}".colorize(:green)
  message = "NOOP: #{message}" if @options[:noop]
  say message
end