class Builderator::Interface::Packer

Generate packer.json

Attributes

packerfile[R]
security_group_id[R]

Public Class Methods

new(*_) click to toggle source
Calls superclass method
# File lib/builderator/interface/packer.rb, line 20
def initialize(*_)
  super

  docker_builders = Config.profile.current.packer.build.select do |_, builder|
    builder.to_h[:type] == 'docker'
  end

  @packerfile ||= {
    :builders => [],
    :provisioners => [],
    'post-processors' => []
  }.tap do |json|
    Config.profile.current.packer.build.each do |_, build|
      build_hash = build.to_hash.tap do |b|
        b[:tags] = Config.profile.current.tags unless Config.profile.current.tags.empty?
      end

      if build_hash[:type] == 'docker'
        raise 'The Docker builder requires a base image' unless build_hash.key?(:image)

        # The Docker builder requires one and only one of 'commit', 'discard', or 'export_path' set
        if build_hash.keys.select { |k| [:commit, :discard, :export_path].include?(k) }.length != 1
          raise 'The Docker builder requires one and only one of `commit`, `discard`, or `export_path` attributes to be defined'
        end
      end

      # If we specify encrypted boot, packer won't allow ami_users.
      # See: https://github.com/MYOB-Technology/packer/blob/509cd7dcf194beb6ca6d0c39057f7490fa630d78/builder/amazon/common/ami_config.go#L59-L61

      # A PR (https://github.com/mitchellh/packer/pull/4023) has been
      # submitted to resolve this issue but we shouldn't remove this
      # until a new Packer release with this feature.
      if build_hash.key?(:encrypt_boot)
        build_hash.delete(:ami_users)
      end

      ## Support is missing for several regions in some versions of Packer
      # Moving this functionality into a task until we can confirm that Packer
      # has full support again.
      build_hash.delete(:ami_regions)

      # This is not directly supported by Packer
      build_hash.delete(:tagging_role)

      # Use a security group that doesn't suck if user didn't specify.
      # Note that @security_group_id in this class will only ever be the one created here,
      # and will be nil if the user specified their own
      if build_hash.key?(:security_group_ids)
        puts "Using SecurityGroups #{build_hash[:security_group_ids]}"
      elsif build_hash.key?(:security_group_id)
        puts "Using SecurityGroup #{build_hash[:security_group_id]}"
      else
        @security_group_id = Util.get_security_group_id(build_hash[:region])
        build_hash[:security_group_id] = @security_group_id

        # Delete the security group created above when on builderator exit.
        # Note that for an unclean exit in which the instance was NOT terminated,
        # Amazon will refuse to delete the security group, as it is still attached
        # to an existing instance.  This is unfortunate, but is equivalent to packer's
        # default behavior except that now you'll get an exception from aws-sdk.
        at_exit { Util.remove_security_group(build_hash[:region], @security_group_id) }
      end

      json[:builders] << build_hash
    end

    # post_processors must be a List of Rashes.
    post_processors = Config.profile.current.packer.post_processors.to_a
    json['post-processors'] = post_processors.first unless post_processors.empty?
    json.delete('post-processors') if json['post-processors'].compact.empty?

    ## Initialize the staging directory unless using the docker builder
    json[:provisioners] << {
      :type => 'shell',
      :inline => "sudo mkdir -p #{Config.chef.staging_directory}/cache && "\
                 "sudo chown $(whoami) -R #{Config.chef.staging_directory}"
    } if docker_builders.empty?

    # Only add artifact provisioners if they're defined
    Config.profile.current.artifact.each do |_, artifact|
      json[:provisioners] << _artifact_provisioner(artifact)
    end unless Config.profile.current.artifact.attributes.empty?

    # Only add chef provisioners if they're defined
    unless Config.profile.current.chef.attributes.empty?
      # There are certain options (staging directory, run as sudo) that don't apply
      # to the docker builder.
      json[:provisioners] << if docker_builders.empty?
                               _chef_provisioner
                             else
                               _chef_provisioner_docker
                             end
    end

    # After adding the default provisioners, we add any additional ones to the provisioners array
    Config.profile.current.provisioner.each do |name, provisioner|
      json[:provisioners] << provisioner.attributes.tap { |p| p[:type] = name.to_s }
    end

    json.delete(:provisioners) if json[:provisioners].empty?
  end
end

Public Instance Methods

render() click to toggle source
# File lib/builderator/interface/packer.rb, line 123
def render
  JSON.pretty_generate(packerfile)
end

Private Instance Methods

_artifact_provisioner(artifact) click to toggle source

Upload artifacts to the build container

# File lib/builderator/interface/packer.rb, line 130
def _artifact_provisioner(artifact)
  {
      :type => 'file',
      :source => artifact.path,
      :destination => artifact.destination
  }
end
_chef_execute_command(sudo = true) click to toggle source
# File lib/builderator/interface/packer.rb, line 167
def _chef_execute_command(sudo = true)
  template = sudo ? 'sudo ' : ''
  "#{template}chef-solo --no-color -c #{Config.chef.staging_directory}/solo.rb -j #{Config.chef.staging_directory}/node.json"
end
_chef_install_command(sudo = true) click to toggle source
# File lib/builderator/interface/packer.rb, line 172
def _chef_install_command(sudo = true)
  template = sudo ? 'sudo ' : ''
  "curl -L https://www.chef.io/chef/install.sh | #{template}bash -s -- -v #{Config.chef.version}"
end
_chef_provisioner() click to toggle source
# File lib/builderator/interface/packer.rb, line 138
def _chef_provisioner
  _chef_provisioner_base.merge(
    :staging_directory => Config.chef.staging_directory,
    :install_command => _chef_install_command
  )
end
_chef_provisioner_base() click to toggle source
# File lib/builderator/interface/packer.rb, line 152
def _chef_provisioner_base
  {
    :type => 'chef-solo',
    :run_list => Config.profile.current.chef.run_list,
    :cookbook_paths => Config.local.cookbook_path,
    :data_bags_path => Config.local.data_bag_path,
    :environments_path => Config.local.environment_path,
    :chef_environment => Config.profile.current.chef.environment,
    :json => Config.profile.current.chef.node_attrs,
    :staging_directory => Config.chef.staging_directory,
    :execute_command => _chef_execute_command(true),
    :install_command => _chef_install_command(true)
  }
end
_chef_provisioner_docker() click to toggle source
# File lib/builderator/interface/packer.rb, line 145
def _chef_provisioner_docker
  _chef_provisioner_base.merge(
    :prevent_sudo => true,
    :install_command => _chef_install_command(false)
  )
end