class SimplyGenius::Atmos::Ipc

Public Class Methods

new(sock_dir=Dir.tmpdir) click to toggle source
# File lib/simplygenius/atmos/ipc.rb, line 11
def initialize(sock_dir=Dir.tmpdir)
  @sock_dir = sock_dir
end

Public Instance Methods

generate_client_script() click to toggle source
# File lib/simplygenius/atmos/ipc.rb, line 40
      def generate_client_script
        script_file = File.join(@sock_dir, 'atmos_ipc.rb')
        File.write(script_file, <<~EOF
          #!/usr/bin/env ruby
          require 'socket'
          UNIXSocket.open('#{@socket_path}') {|c| c.puts(ARGV[0] || $stdin.read); puts c.gets }
        EOF
        )
        FileUtils.chmod('+x', script_file)
        return script_file
      end
listen(&block) click to toggle source
# File lib/simplygenius/atmos/ipc.rb, line 15
def listen(&block)
  raise "Already listening" if @server

  begin
    @socket_path = File.join(@sock_dir, 'atmos-ipc')
    FileUtils.rm_f(@socket_path)
    @server = UNIXServer.open(@socket_path)
  rescue ArgumentError => e
    if e.message =~ /too long unix socket path/ && @sock_dir != Dir.tmpdir
      logger.warn("Using tmp for ipc socket as path too long: #{@socket_path}")
      @sock_dir = Dir.tmpdir
      retry
    end
  end

  begin
    thread = Thread.new { run }
    block.call(@socket_path)
  ensure
    @server.close
    FileUtils.rm_f(@socket_path)
    @server = nil
  end
end

Private Instance Methods

close() click to toggle source
# File lib/simplygenius/atmos/ipc.rb, line 92
def close
  @server.close if @server rescue nil
end
dispatch(msg) click to toggle source
# File lib/simplygenius/atmos/ipc.rb, line 109
def dispatch(msg)
  response = {}
  action = load_action(msg[:action])
  if action.nil?
    response[:error] = "Unsupported ipc action: #{msg.to_hash.inspect}"
    logger.warn(response[:error])
  else
    begin
      response = action.new().execute(**msg)
    rescue => e
      response[:error] = "Failure while executing ipc action: #{e.message}"
      logger.log_exception(e, "Failure while executing ipc action")
    end
  end
  return response
end
load_action(name) click to toggle source
# File lib/simplygenius/atmos/ipc.rb, line 96
def load_action(name)
  action = nil
  logger.debug("Loading ipc action: #{name}")
  begin
    require "simplygenius/atmos/ipc_actions/#{name}"
    action = "SimplyGenius::Atmos::IpcActions::#{name.camelize}".constantize
    logger.debug("Loaded ipc action #{name}")
  rescue LoadError, NameError => e
    logger.log_exception(e, "Failed to load ipc action")
  end
  return action
end
respond(sock, response) click to toggle source
# File lib/simplygenius/atmos/ipc.rb, line 126
def respond(sock, response)
  msg = JSON.generate(response)
  logger.debug("Sending ipc response: #{msg.inspect}")
  sock.puts(msg)
  sock.flush
end
run() click to toggle source
# File lib/simplygenius/atmos/ipc.rb, line 54
def run
  logger.debug("Starting ipc thread")
  begin
    while @server && sock = @server.accept
      logger.debug("An ipc client connected")
      line = sock.gets
      logger.debug("Got ipc message: #{line.inspect}")
      response = {}

      begin
        msg = JSON.parse(line)
        msg = Hashie.symbolize_keys(msg)

        # enabled by default if enabled is not set (e.g. from provisioner local-exec)
        enabled = msg[:enabled].nil? ? true : ["true", "1"].include?(msg[:enabled].to_s)

        if enabled
          logger.debug("Dispatching IPC action")
          response = dispatch(msg)
        else
          response[:message] = "IPC action is not enabled"
          logger.debug(response[:error])
        end
      rescue => e
        logger.log_exception(e, "Failed to parse ipc message")
        response[:error] = "Failed to parse ipc message #{e.message}"
      end

      respond(sock, response)
      sock.close
    end
  rescue IOError, EOFError, Errno::EBADF
    nil
  rescue Exception => e
    logger.log_exception(e, "Ipc failure")
  end
end