ashmckenzie/percheron

View on GitHub
lib/percheron/unit.rb

Summary

Maintainability
B
4 hrs
Test Coverage
require 'percheron/unit/image_helper'

module Percheron
  class Unit

    extend Forwardable
    extend ConfigDelegator
    include Unit::ImageHelper

    def_delegators :unit_config, :name, :pseudo_name, :docker_image
    def_config_item_with_default :unit_config, [], :env, :ports, :volumes, :pre_build_scripts,
                                 :post_start_scripts, :start_args, :dns
    def_config_item_with_default :unit_config, true, :startable

    def initialize(config, stack, unit_name)
      @config = config
      @stack = stack
      @unit_name = unit_name
      @unit_config = stack.unit_configs[unit_name] || Hashie::Mash.new({}) # FIXME
      self
    end

    def needed_unit_names
      unit_config.fetch('needed_unit_names', unit_config.fetch('dependant_unit_names', []))
    end

    def needed_units
      needed_unit_names.each_with_object({}) do |unit_name, all|
        all[unit_name] = stack.units[unit_name]
      end
    end

    def startable_needed_units
      needed_units.select { |_, unit| unit.startable? }
    end

    def metastore_key
      @metastore_key ||= '%s.units.%s' % [ stack.metastore_key, name ]
    end

    def id
      exists? ? info.id[0...12] : nil
    end

    def hostname
      unit_config.fetch('hostname', full_name)
    end

    def restart_policy
      @restart_policy ||= begin
        name = unit_config.fetch('restart_policy', 'always')
        max_retry_count = unit_config.fetch('restart_policy_retry_count', 0)
        { 'Name' => name, 'MaximumRetryCount' => max_retry_count }
      end
    end

    def privileged
      unit_config.fetch('privileged', false)
    end

    def full_name
      '%s_%s' % [ stack.name, name ]
    end

    def display_name
      '%s:%s' % [ stack.name, name ]
    end

    def pseudo_full_name
      '%s_%s' % [ stack.name, pseudo_name ]
    end

    def image
      Connection.perform(Docker::Image, :get, image_name)
    rescue Docker::Error::NotFoundError
      nil
    end

    def version
      @version ||= Semantic::Version.new(unit_config.version)
    end

    def built_version
      @built_version ||= Semantic::Version.new(built_image_version)
    end

    def exposed_ports
      ports.each_with_object({}) { |p, all| all[p.split(':')[1]] = {} }
    end

    def links
      startable_needed_units.map { |_, unit| '%s:%s' % [ unit.full_name, unit.full_name ] }
    end

    def container
      Connection.perform(Docker::Container, :get, full_name)
    rescue Docker::Error::NotFoundError, Excon::Errors::SocketError
      NullUnit.new
    end

    def labels
      { version: version.to_s, created_by: "Percheron #{Percheron::VERSION}" }
    end

    def ip
      exists? ? info.NetworkSettings.IPAddress : 'n/a'
    end

    def dockerfile
      return nil unless unit_config.dockerfile
      Pathname.new(File.expand_path(unit_config.dockerfile, config.file_base_path))
    end

    def update_dockerfile_md5!
      md5 = current_dockerfile_md5
      $logger.debug "Setting MD5 for '#{name}' unit to #{md5}"
      $metastore.set("#{metastore_key}.dockerfile_md5", md5)
    end

    def dockerfile_md5s_match?
      dockerfile_md5 == current_dockerfile_md5
    end

    def versions_match?
      version == built_version
    end

    def running?
      exists? && info.State.Running
    end

    def exists?
      !info.empty?
    end

    def buildable?
      !dockerfile.nil? && unit_config.docker_image.nil?
    end

    def valid?
      Validators::Unit.new(self).valid?
    end

    def pseudo?
      !pseudo_name.nil?
    end

    alias_method :startable?, :startable

    private

      attr_reader :config, :stack, :unit_config, :unit_name

      def current_dockerfile_md5
        dockerfile ? Digest::MD5.file(dockerfile).hexdigest : Digest::MD5.hexdigest(image_name)
      end

      def dockerfile_md5
        $metastore.get("#{metastore_key}.dockerfile_md5") || current_dockerfile_md5
      end

      def built_image_version
        (exists? && info.Config.Labels) ? info.Config.Labels.version : '0.0.0'
      end

      def info
        Hashie::Mash.new(container.info)
      end
  end
end