Module: Middleman::AWSDeploy::InvalidateCloudfront

Defined in:
lib/middleman-aws-deploy/cloudfront-invalidate.rb

Defined Under Namespace

Modules: Options Classes: Opts

Class Method Summary collapse

Class Method Details

.optionsObject



22
23
24
# File 'lib/middleman-aws-deploy/cloudfront-invalidate.rb', line 22

def options
  @@options
end

.registered(app, opts_data = {}) {|@@options| ... } ⇒ Object Also known as: included

Yields:



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/middleman-aws-deploy/cloudfront-invalidate.rb', line 26

def registered(app, opts_data = {}, &block)
  @@options = Opts.new(opts_data)
  yield @@options if block_given?

  app.send :include, Options
  app.after_build do
    puts "== Invalidating CloudFront"
    
    # you can only invalidate in batches of 1000
    def invalidate(files, pos=0)
      paths, count = "", 0

      escaped_files = []
      files[pos...(pos+CF_BATCH_SIZE)].each do |key|
        count += 1
        escaped_files << (escaped_file = URI.escape(key.to_s))
        paths += "<Path>#{escaped_file}</Path>"
      end

      return if count < 1

      paths = "<Paths><Quantity>#{count}</Quantity><Items>#{paths}</Items></Paths>"

      digest = HMAC::SHA1.new(aws_cfi_opts.secret_access_key)
      digest << date = Time.now.utc.strftime("%a, %d %b %Y %H:%M:%S %Z")

      uri = URI.parse("https://cloudfront.amazonaws.com/2012-07-01/distribution/#{aws_cfi_opts.distribution_id}/invalidation")
      header = {
        'x-amz-date' => date,
        'Content-Type' => 'text/xml',
        'Authorization' => "AWS %s:%s" %
          [aws_cfi_opts.access_key_id, Base64.encode64(digest.digest)]
      }
      req = Net::HTTP::Post.new(uri.path)
      req.initialize_http_header(header)
      body = "<InvalidationBatch xmlns=\"http://cloudfront.amazonaws.com/doc/2012-07-01/\">#{paths}<CallerReference>ref_#{Time.now.utc.to_i}</CallerReference></InvalidationBatch>"
      req.body = body

      http = Net::HTTP.new(uri.host, uri.port)
      http.use_ssl = true
      http.verify_mode = OpenSSL::SSL::VERIFY_NONE
      res = http.request(req)
      if res.code == '201'
        puts "CloudFront reloading #{count} paths"
      else
        $stderr.puts "CloudFront Invalidate failed with error #{res.code}"
        $stderr.puts res.body
        $stderr.puts escaped_files.join("\n")
      end
      return if res.code == 400

      if count == CF_BATCH_SIZE
        invalidate(files, pos+count)
      end
    end

    def cf_files
      files = Dir["./build/**/.*", "./build/**/*"]
        .reject{|f| File.directory?(f) }.map{|f| f.sub(/^(?:\.\/)?build\//, '/') }
      # if :directory_indexes is active, we must invalidate both files and dirs
      # doing this 100% of the time, it would be nice if we could find of
      files += files.map{|f| f.gsub(/\/index\.html$/, '/') }
      files.uniq!
    end

    invalidate(cf_files)
  end
end