class RightImageTools::S3HtmlIndexer

Public Class Methods

new(bucket, id=nil, key=nil) click to toggle source
# File lib/s3_html_indexer.rb, line 10
def initialize(bucket, id=nil, key=nil)
  @s3 = RightAws::S3.new(id, key)
  @dev_bucket = bucket.downcase.include?("dev")
  
  # If the bucket holds base images.
  @base_bucket = bucket.downcase.include?("rightimage-base")

  # If the bucket holds reports for the public clouds, ec2, Google, or Azure.
  @public_bucket = bucket.downcase =~ /ec2|google|azure/

  @cloudstack_bucket = bucket.downcase.include?("cloudstack")
  @bucket = @s3.bucket(bucket)
  @keys = @bucket.keys
  @auto_hash = Hash.new{ |h,k| h[k] = Hash.new &h.default_proc }
end

Public Instance Methods

to_html(filename) click to toggle source
# File lib/s3_html_indexer.rb, line 26
def to_html(filename)
  # Create hash hierarchy from bucket list
  #@keys.sort! {|x,y| ver(x.full_name) <=> ver(y.full_name) }  #TODO: sort by rightlink version
  @keys.each do |path| 
    
    next unless is_public?(path)

    path_key = path.full_name
    # Ignore index.html and report_viewer.html .
    next if path_key =~ /index.html/
    next if path_key =~ /report_viewer.html/
    path_key.gsub!('xen/','xenserver/')
    path_key.gsub!(/(vmware|esxi)\/(vmware|esxi)\//,'esxi/')
    path_key.gsub!('vmware/','esxi/')

    #if path_key.split("/").size != 5
    #  puts "WARNING: Skipping malformed path: #{path}"
    #  next
    #end

    # Skip files if they are too small to be an image.
    # Except for mega cloud buckets that collect reports.
    if path.size.to_i < 1024*1024*100 and not @public_bucket
      puts "WARNING: Skipping #{path}: it appears to be too small (<100 MB) to be an image"
      next
    end
    
    sub = @auto_hash
    path_key.split( "/" ).each do |dir| 
      sub = sub[dir] 
    end
    
    # Retrieve image and report links duple.
    links = fix_public_endpoint(path.public_link)

    sub[:link] = links[0]
    sub[:report] = links[1]
    sub[:size] = path.size
    sub[:date] = path.last_modified.strftime("%m/%d/%Y")
    sub[:md5sum] = path.e_tag.gsub(/\"/, "")
    
  end

  # make a non-auto hash copy
  dirs = Hash.new
  dirs = dirs.merge(@auto_hash)

  # visualize hierarchy
  dirs.each do |k,v|
    @file = File.open(filename, "w")
    output("<html>")
    add_style
    display_dirs(k,v)
    output("</html>")
    puts "Output written to #{@file.path}"
    @file.close
  end
end
upload_index(filename) click to toggle source
# File lib/s3_html_indexer.rb, line 85
def upload_index(filename)
  #
  if File.exists?("index.html")
    @bucket.put("index.html", File.open(filename), {}, 'public-read')
    File.delete("index.html")

    # Upload amalgamated report_viewer.html with index.html.
    Dir.chdir(File.join(File.dirname(File.expand_path(__FILE__)), "..", "ui", "build"))
    @bucket.put("report_viewer.html", File.open("report_viewer.html"), {}, 'public-read')
  else
    # Upload index.html for empty buckets.
    Dir.chdir(File.join(File.dirname(File.expand_path(__FILE__)), "..", "ui", "build"))
    @bucket.put("index.html", File.open("empty_bucket_index.html"), {}, 'public-read')
    puts "Uploaded empty bucket index.html."
  end
end

Private Instance Methods

add_header(str, level) click to toggle source
# File lib/s3_html_indexer.rb, line 179
def add_header(str, level)
  case level
  when 0
    # bucket name
    output("<div id='header'><img alt='Embedded Image' width='140' height='19' src='#{encoded_logo}' /></div>")
    output("<div class='bucket common'>#{str.gsub(/rightscale-/,"").upcase}")
    output("<br/><br/>")
    output("<div class='disclaimer code common'>#{disclaimer}</div>")
    output("<br/>")
    output("</div>")
  when 1
    # Hypervisor
    output("<div class='hypervisor common'>")
    # Include "Hypervisor" in subheader if not base image bucket.
    if @base_bucket
      output("<h#{level-1}>#{str.upcase}</h#{level-1}>\n")          
    else
      output("<h#{level-1}>#{str.upcase} Hypervisor</h#{level-1}>\n")
    end
    output("</div>")
    output("<div class='platform common'>")
    output("<table style='padding:none; size:90%'>")
    output("<tr style='border: solid 1px black;'>")
    output("<th style='text-align:left'>Image</th>")
    # Leave 'Notes' header empty.
    output("<th></th>")
    if not @public_bucket
      output("<th>Size</th>")
      output("<th>Date</th>")
      output("<th>MD5</th>")
    else
      output("<th>Date</th>")
    end
    output("</tr>")
  when 2
    return
    # OS
  when 3
    return
    # OS Version
  else
    return
    # image
    # output("<div class='platform'>")
  end
end
add_style() click to toggle source
# File lib/s3_html_indexer.rb, line 291
def add_style
  output "<style>"
  output( "body {min-width: 600px; margin:0; padding:0; font-size:70.5%; /* font-family:'Lucida Sans', Verdana, Arial, sans-serif; */ font-family:'Lucida Grande','Lucida Sans Unicode','Lucida Sans',Verdana,lucida,sans-serif; background:#FFF; height:100%}
html {height:100%}
a img {border:none}
a {color:#1e4a7e}
table { width:90% }
th { text-align:center }

#header {
    background:#235186; 
    padding:2px 20px; 
    position:relative; 
    height:25px;
}

#header #logo {
    margin-top:3px; 
}

.common {
  font-size: 16px;
  margin-left:auto;
  margin-right:auto;
  font-weight: bold;
    padding:2px 20px; 
    position:relative; 
}

.bucket {
  font-size: 24px;
  color: #F5F2A9;
    background:#235186; 
    padding:10px 30px 20px; 
}
   
.version {
  color: #F5F2A9;
    background:#235186; 
}

.hypervisor {
  color: #235186;
    background:#F5F2A9; 
}

.platform {
    background: #FFFFFF;
    width:90%;
    margin-left: auto ;
  margin-right: auto ;
  position: relative;
}

.disclaimer {
    background: #FFFFFF;
    width:90%;
    margin-left: auto ;
  margin-right: auto ;
  position: relative;
  border: 2px solid black;
  font-weight: normal;
}

.code {
    font-family:'Courier New', monospace;
    color: #000000;
    position:relative;
    left:10px;
}
")
  output "</style>"
end
disclaimer() click to toggle source

Refer to “Base Images” or “RightImages” based on bucket.

# File lib/s3_html_indexer.rb, line 164
def disclaimer
  if @dev_bucket
    prod_bucket = @bucket.name.sub("-dev","")
    disclaimer = "The following "
    disclaimer <<  (@base_bucket ? "Base Images" : "RightImages")
    disclaimer << " are currently in RightScale development and testing.  They are not supported by RightScale.  For "
    disclaimer <<  (@base_bucket ? "Base Images" : "RightImages")
    disclaimer << " that RightScale supports, please see our <a href='http://#{prod_bucket}.s3.amazonaws.com/index.html'>production images</a>."
  else
    disclaimer = "The following "
    disclaimer <<  (@base_bucket ? "Base Images" : "RightImages")
    disclaimer << " are tested and supported by RightScale for general use.  For further information regarding use of these images with RightScale, please contact your RightScale account manager or sign up for an account at <a href='http://www.rightscale.com'>www.rightscale.com</a>."
  end
end
display_dirs(k,v,level=0) click to toggle source
# File lib/s3_html_indexer.rb, line 125
def display_dirs(k,v,level=0)
  return unless v
  unless v.keys.include?(:link)
    add_header(k, level)
    v.sort_by {|k2, v2| k2}.each do |k2, v2|
      display_dirs(k2, v2, level+1)
    end
    add_footer(k, level)
  else
    output("<tr class='code'>")
    # Link to images stored on S3 bucket.
    if not @public_bucket
      output("<td><a href='#{v[:link]}'>#{k}</a></td>")
    # Otherwise, the bucket only collects reports.
    else
      # Remove '.js.' extension in public cloud buckets.
      kp = k.gsub(/\.js$/,"")
      output("<td>#{kp}</td>")
    end
    # If bucket does not have report key, do not add "notes" link.
    if not v[:report].empty?
      output("<td><a href='#{v[:report]}'>notes</a></td>")
    else
      # Empty 'notes' cell.
      output("<td></td>")
    end
    # Hide fields for public cloud images.
    if not @public_bucket
      output("<td>#{v[:size].to_i/(1024*1024)}MB</td>")
      output("<td>#{v[:date]}</td>")
      output("<td>#{v[:md5sum]}</td>")
    else
      output("<td>#{v[:date]}</td>")
    end
    output("</tr>")
  end
end
fix_public_endpoint(link) click to toggle source

s3.amazonaws.com/rightscale-cloudstack/foo to rightscale-cloudstack.s3.amazonaws.com

# File lib/s3_html_indexer.rb, line 248
def fix_public_endpoint(link)
  link = link.dup
  # http apparently more reliable than https when importing cloudstack images (w-4839)
  if @cloudstack_bucket
    link.sub!("https","http")
    link.sub!(":443","")
  end
  link_ary = link.split("/")
  bucket = link_ary[3]
  host = link_ary[2]
  link_ary[2] = "#{bucket}.#{host}"
  link_ary.delete_at(3)
  new_link = link_ary.join("/")

  # Relative link to report.
  link_ary.slice!(1..2)


  

  #!!! Change the (usually double) compressed image extension to ".js".
  #if not @public_bucket
  #  link_ary[-1].gsub!(/\.[^.]+\.[^.]+$/,'.js')
  #end

  # If report doesn't exist, return empty string.
  # Relative url begins at link_ary[1].
  if not RightAws::S3::Key.create(@bucket,link_ary.slice(1..-1).join("/").gsub!(/\.[^.]+\.[^.]+$/,'.js')).exists?
   return [new_link , '']
  end

  # Replace "https://" with image viewer prefix.
  link_ary[0] = 'report_viewer.html?image='
  report_link = link_ary.join("/")

  # Return image link and report link.
  return [new_link , report_link]
end
is_public?(key) click to toggle source
# File lib/s3_html_indexer.rb, line 107
    def is_public?(key)
      key.refresh    
#      puts "Key: #{key.name}"
      is_public = false
      key.grantees.each do |g|
#        puts "grantee:#{g.name} perms:#{g.perms}"
        if g.name.to_s == "AllUsers" && g.perms.to_s == "READ"
          is_public = true
        end
      end
      if is_public
        puts "PUBLIC: #{key.name}"
      else
        puts "NOT PUBLIC: #{key.name}"
      end
      return is_public
    end
output(str) click to toggle source
# File lib/s3_html_indexer.rb, line 287
def output(str)
  @file.write "#{str}\n" if @file
end
ver(key) click to toggle source
# File lib/s3_html_indexer.rb, line 103
def ver(key)
  key.match(/v[0-9]+\.[0-9]+\.[0-9]+/).to_s
end