phase 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/Makefile +22 -0
- data/README.md +29 -0
- data/Rakefile +2 -0
- data/VERSION +1 -0
- data/bin/phase +5 -0
- data/lib/phase/adapters/aws.rb +28 -0
- data/lib/phase/cli/all.rb +10 -0
- data/lib/phase/cli/command.rb +18 -0
- data/lib/phase/cli/env.rb +0 -0
- data/lib/phase/cli/keys.rb +175 -0
- data/lib/phase/cli/logs.rb +21 -0
- data/lib/phase/cli/mosh.rb +23 -0
- data/lib/phase/cli/ssh.rb +66 -0
- data/lib/phase/cli/status.rb +99 -0
- data/lib/phase/cli/utils/loggable.rb +21 -0
- data/lib/phase/cli.rb +16 -0
- data/lib/phase/configuration.rb +41 -0
- data/lib/phase/dsl.rb +28 -0
- data/lib/phase/keys/key.rb +91 -0
- data/lib/phase/ssh/backend.rb +32 -0
- data/lib/phase/ssh/command.rb +16 -0
- data/lib/phase/ssh/coordinator.rb +13 -0
- data/lib/phase/version.rb +3 -0
- data/lib/phase.rb +35 -0
- data/phase.gemspec +33 -0
- metadata +229 -0
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA1:
         | 
| 3 | 
            +
              metadata.gz: 07ea8a370b008d8b134f8c30ba2c7887d6ac74e8
         | 
| 4 | 
            +
              data.tar.gz: 8f26a343ffdb04f29c852815ed1ecbe9a8344515
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: 41deb6339e385541f62e66efd4977dabe1d8283c76218dd8e3f7fdd118a9c144cbcf40f7628b037a7b0c597c7094d7cc9c104fff164a18c1a1bd3f18166d9bde
         | 
| 7 | 
            +
              data.tar.gz: ef485f51fa20e53b096b50360d8dbc31bda58140434aba2674699b853de01cb90fe4965e7e56b2b2bae6bc57f0e43ecaede75ad119ea8bfa0fb9d68f23aadd1c
         | 
    
        data/.gitignore
    ADDED
    
    | @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            *.gem
         | 
| 2 | 
            +
            *.rbc
         | 
| 3 | 
            +
            .bundle
         | 
| 4 | 
            +
            .config
         | 
| 5 | 
            +
            .yardoc
         | 
| 6 | 
            +
            Gemfile.lock
         | 
| 7 | 
            +
            InstalledFiles
         | 
| 8 | 
            +
            _yardoc
         | 
| 9 | 
            +
            coverage
         | 
| 10 | 
            +
            doc/
         | 
| 11 | 
            +
            lib/bundler/man
         | 
| 12 | 
            +
            pkg
         | 
| 13 | 
            +
            rdoc
         | 
| 14 | 
            +
            spec/reports
         | 
| 15 | 
            +
            test/tmp
         | 
| 16 | 
            +
            test/version_tmp
         | 
| 17 | 
            +
            tmp
         | 
| 18 | 
            +
            *.bundle
         | 
| 19 | 
            +
            *.so
         | 
| 20 | 
            +
            *.o
         | 
| 21 | 
            +
            *.a
         | 
| 22 | 
            +
            mkmf.log
         | 
    
        data/Gemfile
    ADDED
    
    
    
        data/LICENSE.txt
    ADDED
    
    | @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            Copyright (c) 2014 Orca Health, Inc.
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            MIT License
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            Permission is hereby granted, free of charge, to any person obtaining
         | 
| 6 | 
            +
            a copy of this software and associated documentation files (the
         | 
| 7 | 
            +
            "Software"), to deal in the Software without restriction, including
         | 
| 8 | 
            +
            without limitation the rights to use, copy, modify, merge, publish,
         | 
| 9 | 
            +
            distribute, sublicense, and/or sell copies of the Software, and to
         | 
| 10 | 
            +
            permit persons to whom the Software is furnished to do so, subject to
         | 
| 11 | 
            +
            the following conditions:
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            The above copyright notice and this permission notice shall be
         | 
| 14 | 
            +
            included in all copies or substantial portions of the Software.
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         | 
| 17 | 
            +
            EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         | 
| 18 | 
            +
            MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         | 
| 19 | 
            +
            NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
         | 
| 20 | 
            +
            LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         | 
| 21 | 
            +
            OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         | 
| 22 | 
            +
            WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         | 
    
        data/Makefile
    ADDED
    
    | @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            VERSION=$(shell cat VERSION)
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            .PHONY: default all clean install
         | 
| 4 | 
            +
            default: clean all
         | 
| 5 | 
            +
            all: build install alias
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            build: lib/phase/version.rb
         | 
| 8 | 
            +
            	gem build phase.gemspec
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            lib/phase/version.rb:
         | 
| 11 | 
            +
            	mkdir -p $(@D)
         | 
| 12 | 
            +
            	@echo 'module Phase\n	VERSION = "$(VERSION)"\nend' > $@
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            install:
         | 
| 15 | 
            +
            	gem install phase-$(VERSION).gem --no-rdoc --no-ri
         | 
| 16 | 
            +
            	rbenv rehash
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            alias:
         | 
| 19 | 
            +
            	alias phase=/Users/piers/.rbenv/shims/phase
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            clean:
         | 
| 22 | 
            +
            	rm -rf lib/phase/version.rb
         | 
    
        data/README.md
    ADDED
    
    | @@ -0,0 +1,29 @@ | |
| 1 | 
            +
            # Phase
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            TODO: Write a gem description
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            ## Installation
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            Add this line to your application's Gemfile:
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                gem 'phase'
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            And then execute:
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                $ bundle
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            Or install it yourself as:
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                $ gem install phase
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            ## Usage
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            TODO: Write usage instructions here
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            ## Contributing
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            1. Fork it ( https://github.com/[my-github-username]/phase/fork )
         | 
| 26 | 
            +
            2. Create your feature branch (`git checkout -b my-new-feature`)
         | 
| 27 | 
            +
            3. Commit your changes (`git commit -am 'Add some feature'`)
         | 
| 28 | 
            +
            4. Push to the branch (`git push origin my-new-feature`)
         | 
| 29 | 
            +
            5. Create a new Pull Request
         | 
    
        data/Rakefile
    ADDED
    
    
    
        data/VERSION
    ADDED
    
    | @@ -0,0 +1 @@ | |
| 1 | 
            +
            0.0.1
         | 
    
        data/bin/phase
    ADDED
    
    
| @@ -0,0 +1,28 @@ | |
| 1 | 
            +
            module Phase
         | 
| 2 | 
            +
              module Adapters
         | 
| 3 | 
            +
                class AWS
         | 
| 4 | 
            +
                  # require 'capistrano/all'
         | 
| 5 | 
            +
                  require "fog/aws"
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                  def find_servers(options = {})
         | 
| 8 | 
            +
                    query = {}
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                    if options[:role]
         | 
| 11 | 
            +
                      query["tag:Role"] = options[:role]
         | 
| 12 | 
            +
                    end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    ec2.servers.all(query).map do |h|
         | 
| 15 | 
            +
                      {
         | 
| 16 | 
            +
                        hostname: h.dns_name,
         | 
| 17 | 
            +
                        user: "orca"
         | 
| 18 | 
            +
                      }
         | 
| 19 | 
            +
                    end
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  def ec2
         | 
| 23 | 
            +
                    @ec2 ||= ::Fog::Compute::AWS.new(region: ::Phase.config.aws_region)
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
            end
         | 
| 
            File without changes
         | 
| @@ -0,0 +1,175 @@ | |
| 1 | 
            +
            require 'json'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Phase
         | 
| 4 | 
            +
              module Commands
         | 
| 5 | 
            +
                class Keys < Command
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                  command :keys do |c|
         | 
| 8 | 
            +
                    c.syntax = "phase keys [--grant email_address] [--revoke email_address [--delete]] [--role bastion_role]"
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                    c.option "--grant email_address", String, "Add access for user with email_address."
         | 
| 11 | 
            +
                    c.option "--revoke email_address", String, "Revoke access for user with email_address."
         | 
| 12 | 
            +
                    c.option "--delete", "Delete key entry permanently. Defaults to false (comments-out instead)."
         | 
| 13 | 
            +
                    c.option "--role bastion_role", String, "Value of 'Role' tag for bastion hosts. Defaults to 'ssh'."
         | 
| 14 | 
            +
                    c.option "--list", "Default action. Lists email addresses and public keys of users with access."
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    c.description = "Adds or removes access by email address and public key on bastion servers."
         | 
| 17 | 
            +
                    c.action do |args, options|
         | 
| 18 | 
            +
                      options.default role: "ssh", delete: false
         | 
| 19 | 
            +
                      new(args, options).run
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  LOCKFILE_NAME = "phase.keys.lock"
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  def run
         | 
| 26 | 
            +
                    log "backing up existing keys..."
         | 
| 27 | 
            +
                    authorized_keys.backup!
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                    if email = options.grant
         | 
| 30 | 
            +
                      add_key(email)
         | 
| 31 | 
            +
                    elsif email = options.revoke
         | 
| 32 | 
            +
                      remove_key(email, options.delete)
         | 
| 33 | 
            +
                    else
         | 
| 34 | 
            +
                      print_keys_list
         | 
| 35 | 
            +
                    end
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  private
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    def authorized_keys
         | 
| 41 | 
            +
                      @authorized_keys ||= AuthorizedKeys.new(bastions)
         | 
| 42 | 
            +
                    end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    def bastions
         | 
| 45 | 
            +
                      @bastions ||= begin
         | 
| 46 | 
            +
                        bastions = find_hosts(role: options.role)
         | 
| 47 | 
            +
                        fail "No bastions found with role '#{ role }'." unless bastions.any?
         | 
| 48 | 
            +
                        bastions
         | 
| 49 | 
            +
                      end
         | 
| 50 | 
            +
                    end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                    def print_keys_list
         | 
| 53 | 
            +
                      table = ::Terminal::Table.new({
         | 
| 54 | 
            +
                        title: "Users with access",
         | 
| 55 | 
            +
                        headings: ["Email address", "Status", "Created at", "Updated at", "Public key"]
         | 
| 56 | 
            +
                      })
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                      authorized_keys.each do |_, auth_key|
         | 
| 59 | 
            +
                        # Allow administrative keys to be hidden.
         | 
| 60 | 
            +
                        next if auth_key.status == "IGNORE"
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                        cells = [
         | 
| 63 | 
            +
                          auth_key.email,
         | 
| 64 | 
            +
                          auth_key.status,
         | 
| 65 | 
            +
                          auth_key.created_at,
         | 
| 66 | 
            +
                          auth_key.updated_at,
         | 
| 67 | 
            +
                          auth_key.key_fragment
         | 
| 68 | 
            +
                        ].map do |cell|
         | 
| 69 | 
            +
                          cell.to_s.send( auth_key.active? ? :green : :red )
         | 
| 70 | 
            +
                        end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                        table << cells
         | 
| 73 | 
            +
                      end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                      puts table
         | 
| 76 | 
            +
                    end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                    def add_key(email)
         | 
| 79 | 
            +
                      if found_key = authorized_keys[email]
         | 
| 80 | 
            +
                        found_key.activate
         | 
| 81 | 
            +
                      else
         | 
| 82 | 
            +
                        key_text = ask("Enter public key:")
         | 
| 83 | 
            +
                        authorized_keys.add( Keys::Key.new(email: email, key: key_text) )
         | 
| 84 | 
            +
                      end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                      authorized_keys.persist!
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                      print_keys_list
         | 
| 89 | 
            +
                    end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                    def remove_key(email, delete)
         | 
| 92 | 
            +
                      if found_key = authorized_keys[email]
         | 
| 93 | 
            +
                        if delete
         | 
| 94 | 
            +
                          authorized_keys.remove(found_key)
         | 
| 95 | 
            +
                        else
         | 
| 96 | 
            +
                          found_key.deactivate
         | 
| 97 | 
            +
                        end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                        authorized_keys.persist!
         | 
| 100 | 
            +
                      else
         | 
| 101 | 
            +
                        fail "No key found."
         | 
| 102 | 
            +
                      end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                      print_keys_list
         | 
| 105 | 
            +
                    end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
             | 
| 108 | 
            +
                    class AuthorizedKeys
         | 
| 109 | 
            +
                      extend ::Forwardable
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                      attr_reader :bastions
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                      def initialize(bastions)
         | 
| 114 | 
            +
                        @bastions = bastions
         | 
| 115 | 
            +
                        @hash = Hash.new
         | 
| 116 | 
            +
                        fetch_keys
         | 
| 117 | 
            +
                      end
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                      def fetch_keys
         | 
| 120 | 
            +
                        lines = raw_keys.lines.map(&:chomp).reject(&:empty?)
         | 
| 121 | 
            +
                        idx = 0
         | 
| 122 | 
            +
                        while idx < lines.count
         | 
| 123 | 
            +
                          attrs_line = lines[idx]
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                          unless attrs_line.match(/\A### phase-key/)
         | 
| 126 | 
            +
                            idx += 1
         | 
| 127 | 
            +
                            next
         | 
| 128 | 
            +
                          end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                          key_line = lines[idx + 1]
         | 
| 131 | 
            +
                          add( Keys::Key.parse(attrs_line, key_line) )
         | 
| 132 | 
            +
                          idx += 2
         | 
| 133 | 
            +
                        end
         | 
| 134 | 
            +
                      end
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                      def add(key)
         | 
| 137 | 
            +
                        self[key.email] = key
         | 
| 138 | 
            +
                      end
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                      def remove(key)
         | 
| 141 | 
            +
                        delete(key.email)
         | 
| 142 | 
            +
                      end
         | 
| 143 | 
            +
             | 
| 144 | 
            +
                      def to_s
         | 
| 145 | 
            +
                        reduce("") do |out, (_, key)|
         | 
| 146 | 
            +
                          out << key.to_s
         | 
| 147 | 
            +
                        end
         | 
| 148 | 
            +
                      end
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                      def persist!
         | 
| 151 | 
            +
                        log "writing new keys..."
         | 
| 152 | 
            +
                        bastions.exec("echo '#{to_s}' > ~/.ssh/authorized_keys")
         | 
| 153 | 
            +
                      end
         | 
| 154 | 
            +
             | 
| 155 | 
            +
                      def backup!
         | 
| 156 | 
            +
                        File.open( File.expand_path("~/.phase-keys"), "w" ) { |file| file << raw_keys }
         | 
| 157 | 
            +
                      end
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                      private
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                        def raw_keys
         | 
| 162 | 
            +
                          @raw_keys ||= bastions.exec("cat ~/.ssh/authorized_keys").stdout
         | 
| 163 | 
            +
                        end
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                        def reset!
         | 
| 166 | 
            +
                          @raw_keys = nil
         | 
| 167 | 
            +
                          @all = nil
         | 
| 168 | 
            +
                        end
         | 
| 169 | 
            +
             | 
| 170 | 
            +
                      def_delegators :@hash, *(::Hash.instance_methods - AuthorizedKeys.instance_methods)
         | 
| 171 | 
            +
                    end
         | 
| 172 | 
            +
             | 
| 173 | 
            +
                end
         | 
| 174 | 
            +
              end
         | 
| 175 | 
            +
            end
         | 
| @@ -0,0 +1,21 @@ | |
| 1 | 
            +
            module Phase
         | 
| 2 | 
            +
              module Commands
         | 
| 3 | 
            +
                class Logs < Command
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                  command :logs do |c|
         | 
| 6 | 
            +
                    c.syntax = "phase logs [--tail]"
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                    c.option "-t", "--tail", "Stream logs."
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                    c.description = "."
         | 
| 11 | 
            +
                    c.action do |args, options|
         | 
| 12 | 
            +
                      new(args, options).run
         | 
| 13 | 
            +
                    end
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  def run
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
            end
         | 
| @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            module Phase
         | 
| 2 | 
            +
              module Commands
         | 
| 3 | 
            +
                class Mosh < SSH
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                  command :mosh do |c|
         | 
| 6 | 
            +
                    c.syntax = "phase mosh [-i instance_id] [-n instance_name] [-r instance_role] [-u user] [-c conn_str] [username@instance_name|instance_id]"
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                    c.option "-i", "--id instance_id", String, "Connects to the instance with this ID."
         | 
| 9 | 
            +
                    c.option "-n", "--name instance_name", String, "Connects to the instance with this 'Name' tag."
         | 
| 10 | 
            +
                    c.option "-r", "--role instance_role", String, "Connects to an instance with this 'Role' tag. Default is 'ssh'."
         | 
| 11 | 
            +
                    c.option "-u", "--user username", String, "Remote username to connect with."
         | 
| 12 | 
            +
                    c.option "-c", "--conn conn_str", String, "Invokes conn_str to establish terminal session (e.g. --conn='ssh -i key.pem')."
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    c.description = "Connects to the the specified instance via mosh."
         | 
| 15 | 
            +
                    c.action do |args, options|
         | 
| 16 | 
            +
                      options.default role: "ssh", conn: "mosh --ssh='ssh -A'"
         | 
| 17 | 
            +
                      new(args, options).run
         | 
| 18 | 
            +
                    end
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
            end
         | 
| @@ -0,0 +1,66 @@ | |
| 1 | 
            +
            module Phase
         | 
| 2 | 
            +
              module Commands
         | 
| 3 | 
            +
                class SSH < Command
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                  command :ssh do |c|
         | 
| 6 | 
            +
                    c.syntax = "phase ssh [-i instance_id] [-n instance_name] [-r instance_role] [-u user] [-c conn_str] [username@instance_name|instance_id]"
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                    c.option "-i", "--id instance_id", String, "Connects to the instance with this ID."
         | 
| 9 | 
            +
                    c.option "-n", "--name instance_name", String, "Connects to the instance with this 'Name' tag."
         | 
| 10 | 
            +
                    c.option "-r", "--role instance_role", String, "Connects to an instance with this 'Role' tag. Default is 'ssh'."
         | 
| 11 | 
            +
                    c.option "-u", "--user username", String, "Remote username to connect with."
         | 
| 12 | 
            +
                    c.option "-c", "--conn conn_str", String, "Invokes conn_str to establish terminal session (e.g. --conn 'ssh -i key.pem')."
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    c.description = "Connects to the the specified instance via SSH."
         | 
| 15 | 
            +
                    c.action do |args, options|
         | 
| 16 | 
            +
                      options.default role: "ssh", conn: "ssh -A"
         | 
| 17 | 
            +
                      new(args, options).run
         | 
| 18 | 
            +
                    end
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  attr_accessor :username, :instance
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  def run
         | 
| 24 | 
            +
                    parse_connection_string
         | 
| 25 | 
            +
                    log "connecting to instance #{ instance.id }..."
         | 
| 26 | 
            +
                    exec "#{ options.conn } #{ username }@#{ instance.dns_name }"
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  private
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                    def parse_connection_string
         | 
| 32 | 
            +
                      # Handle "connection string" style parameter.
         | 
| 33 | 
            +
                      if conn = args.first
         | 
| 34 | 
            +
                        @username, str = conn.split("@")
         | 
| 35 | 
            +
                        fail "Malformed parameter: username@[instance-name|instance-id]." if @username.nil? || str.nil?
         | 
| 36 | 
            +
                        str.match(/i-[0-9a-f]+/i) ? options.id = str : options.name = str
         | 
| 37 | 
            +
                      end
         | 
| 38 | 
            +
                    end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    def username
         | 
| 41 | 
            +
                      @username ||= begin
         | 
| 42 | 
            +
                        user = options.user
         | 
| 43 | 
            +
                        fail "Must specify username with -u or 'username@[instance-name|instance-id]' parameter." if user.nil?
         | 
| 44 | 
            +
                        user
         | 
| 45 | 
            +
                      end
         | 
| 46 | 
            +
                    end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                    def instance
         | 
| 49 | 
            +
                      @instance ||= begin
         | 
| 50 | 
            +
                        if options.id
         | 
| 51 | 
            +
                          instance = ec2.servers.all("instance-id" => options.id).first
         | 
| 52 | 
            +
                        elsif options.name
         | 
| 53 | 
            +
                          instance = ec2.servers.all("tag:Name" => options.name).first
         | 
| 54 | 
            +
                        else
         | 
| 55 | 
            +
                          instance = ec2.servers.all("tag:Role" => options.role).first
         | 
| 56 | 
            +
                        end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                        fail "no instance found." if instance.nil?
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                        instance
         | 
| 61 | 
            +
                      end
         | 
| 62 | 
            +
                    end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
              end
         | 
| 66 | 
            +
            end
         | 
| @@ -0,0 +1,99 @@ | |
| 1 | 
            +
            module Phase
         | 
| 2 | 
            +
              module Commands
         | 
| 3 | 
            +
                class Status < Command
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                  command :status do |c|
         | 
| 6 | 
            +
                    c.syntax = "phase status"
         | 
| 7 | 
            +
                    c.description = "Prints the current status of configured VPCs, subnets, and EC2 instances."
         | 
| 8 | 
            +
                    c.action do |args, options|
         | 
| 9 | 
            +
                      new(args, options).run
         | 
| 10 | 
            +
                    end
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  def run
         | 
| 14 | 
            +
                    @vpcs    = ec2.vpcs
         | 
| 15 | 
            +
                    @subnets = ec2.subnets
         | 
| 16 | 
            +
                    @servers = ec2.servers
         | 
| 17 | 
            +
                    @elbs    = elb.load_balancers
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    print_vpc_tables
         | 
| 20 | 
            +
                    print_servers_table
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  def print_vpc_tables
         | 
| 24 | 
            +
                    @vpcs.each do |vpc|
         | 
| 25 | 
            +
                      table = ::Terminal::Table.new(title: "VPC Status")
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                      add_section_headers(table, ["VPC ID", "Name", "State", "CIDR Block", "Tenancy"])
         | 
| 28 | 
            +
                      color = vpc.state == "available" ? :green : :light_red
         | 
| 29 | 
            +
                      add_row(table, color, [
         | 
| 30 | 
            +
                        vpc.id,
         | 
| 31 | 
            +
                        vpc.tags["Name"] || vpc.tags["name"],
         | 
| 32 | 
            +
                        vpc.state,
         | 
| 33 | 
            +
                        vpc.cidr_block,
         | 
| 34 | 
            +
                        vpc.tenancy
         | 
| 35 | 
            +
                      ])
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                      subnets = @subnets.select do |subnet|
         | 
| 38 | 
            +
                        subnet.vpc_id == vpc.id
         | 
| 39 | 
            +
                      end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                      return unless subnets.any?
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                      add_section_headers(table, ["Subnet ID", "Name", "State", "CIDR Block", "Availability Zone"])
         | 
| 44 | 
            +
                      subnets.each do |subnet|
         | 
| 45 | 
            +
                        color = subnet.ready? ? :green : :light_red
         | 
| 46 | 
            +
                        add_row(table, color, [
         | 
| 47 | 
            +
                          subnet.subnet_id,
         | 
| 48 | 
            +
                          subnet.tag_set["Name"] || subnet.tag_set["name"],
         | 
| 49 | 
            +
                          subnet.state,
         | 
| 50 | 
            +
                          subnet.cidr_block,
         | 
| 51 | 
            +
                          subnet.availability_zone
         | 
| 52 | 
            +
                        ])
         | 
| 53 | 
            +
                      end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                      puts table
         | 
| 56 | 
            +
                    end
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  def print_servers_table
         | 
| 60 | 
            +
                    table  = ::Terminal::Table.new(title: "Instances")
         | 
| 61 | 
            +
                    groups = @servers.group_by(&:subnet_id)
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                    add_section_headers(table, ["ID", "Name", "Type", "State", "Public IP", "Private IP", "Subnet Name"])
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                    groups.each_pair do |subnet_id, servers|
         | 
| 66 | 
            +
                      servers.each do |server|
         | 
| 67 | 
            +
                        color = server.ready? ? :green : :light_red
         | 
| 68 | 
            +
                        subnet = @subnets.find { |s| s.subnet_id == subnet_id }
         | 
| 69 | 
            +
                        subnet_name = subnet.tag_set["Name"] || subnet.tag_set["name"] if subnet
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                        add_row(table, color, [
         | 
| 72 | 
            +
                          server.id,
         | 
| 73 | 
            +
                          server.tags["Name"] || server.tags["name"],
         | 
| 74 | 
            +
                          server.flavor_id,
         | 
| 75 | 
            +
                          server.state,
         | 
| 76 | 
            +
                          server.public_ip_address,
         | 
| 77 | 
            +
                          server.private_ip_address,
         | 
| 78 | 
            +
                          subnet_name
         | 
| 79 | 
            +
                        ])
         | 
| 80 | 
            +
                      end
         | 
| 81 | 
            +
                    end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                    puts table
         | 
| 84 | 
            +
                  end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                  private
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                    def add_row(table, color_method, values)
         | 
| 89 | 
            +
                      table << values.map { |v| (v || "").send(color_method) }
         | 
| 90 | 
            +
                    end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                    def add_section_headers(table, headers)
         | 
| 93 | 
            +
                      table.add_separator if table.number_of_columns > 0
         | 
| 94 | 
            +
                      table << headers
         | 
| 95 | 
            +
                      table.add_separator
         | 
| 96 | 
            +
                    end
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
              end
         | 
| 99 | 
            +
            end
         | 
| @@ -0,0 +1,21 @@ | |
| 1 | 
            +
            module Phase
         | 
| 2 | 
            +
              module CLI
         | 
| 3 | 
            +
                module Utils
         | 
| 4 | 
            +
                  module Loggable
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                    def log(str)
         | 
| 7 | 
            +
                      out = "[phase]".green
         | 
| 8 | 
            +
                      out << " #{ str }"
         | 
| 9 | 
            +
                      puts out
         | 
| 10 | 
            +
                    end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                    def fail!(str)
         | 
| 13 | 
            +
                      out = "[phase]".red
         | 
| 14 | 
            +
                      out << " #{ str }"
         | 
| 15 | 
            +
                      abort out
         | 
| 16 | 
            +
                    end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
            end
         | 
    
        data/lib/phase/cli.rb
    ADDED
    
    | @@ -0,0 +1,16 @@ | |
| 1 | 
            +
            require "commander"
         | 
| 2 | 
            +
            require "phase/cli/all"
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Phase
         | 
| 5 | 
            +
              class CLI
         | 
| 6 | 
            +
                include ::Commander::Methods
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                def run
         | 
| 9 | 
            +
                  program :name, "Phase"
         | 
| 10 | 
            +
                  program :version, ::Phase::VERSION
         | 
| 11 | 
            +
                  program :description, "Phase controller."
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  run!
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
            end
         | 
| @@ -0,0 +1,41 @@ | |
| 1 | 
            +
            module Phase
         | 
| 2 | 
            +
              class Configuration
         | 
| 3 | 
            +
             | 
| 4 | 
            +
                attr_accessor :use_bastions,
         | 
| 5 | 
            +
                              :bastion_role,
         | 
| 6 | 
            +
                              :bastion_user,
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                              :aws_region
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def initialize
         | 
| 11 | 
            +
                  @use_bastions = false
         | 
| 12 | 
            +
                  @bastion_role = nil
         | 
| 13 | 
            +
                  @bastion_user = nil
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  @aws_region   = "us-east-1"
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  ::SSHKit.config.backend = SSH::Backend
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  configure_from_yml if defined?(::Rails) && yml_present?
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                def configure_from_yml
         | 
| 23 | 
            +
                  yml_config = ::YAML.load_file(yml_path) || {}
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  @use_bastions = yml_config[:use_bastions] if yml_config.has_key(:use_bastions)
         | 
| 26 | 
            +
                  @bastion_role = yml_config[:bastion_role] if yml_config.has_key(:bastion_role)
         | 
| 27 | 
            +
                  @bastion_user = yml_config[:bastion_user] if yml_config.has_key(:bastion_user)
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  @aws_region = yml_config[:aws_region] if yml_config.has_key(:aws_region)
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                def yml_present?
         | 
| 33 | 
            +
                  File.exists?(yml_path)
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                def yml_path
         | 
| 37 | 
            +
                  # ::Rails.root.join("config", "phase.yml")
         | 
| 38 | 
            +
                  ""
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
            end
         | 
    
        data/lib/phase/dsl.rb
    ADDED
    
    | @@ -0,0 +1,28 @@ | |
| 1 | 
            +
            module Phase
         | 
| 2 | 
            +
              module DSL
         | 
| 3 | 
            +
             | 
| 4 | 
            +
                # def on_role(role_name, options = {}, &block)
         | 
| 5 | 
            +
                #   destination_ips = []
         | 
| 6 | 
            +
                #   on(destination_ips, options, &block)
         | 
| 7 | 
            +
                # end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def on(destination_ips, options = {}, &block)
         | 
| 10 | 
            +
                  bastion_host = ["orca@54.165.207.98"]
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  coordinator = SSH::Coordinator.new(bastion_host)
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  destination_ips.each do |ip|
         | 
| 15 | 
            +
                    coordinator.each(options) do
         | 
| 16 | 
            +
                      on_remote_host(ip) { instance_exec(&block) }
         | 
| 17 | 
            +
                    end
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                def run_locally(&block)
         | 
| 22 | 
            +
                  ::SSHKit::Backend::Local.new(&block).run
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
            end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            include ::Phase::DSL
         | 
| @@ -0,0 +1,91 @@ | |
| 1 | 
            +
            module Phase
         | 
| 2 | 
            +
              module Keys
         | 
| 3 | 
            +
                class Key
         | 
| 4 | 
            +
                  attr_accessor :email, :key, :created_at, :updated_at, :status
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  PATTERN_KEY_LINE   = /ssh-rsa/
         | 
| 7 | 
            +
                  PATTERN_ATTRS_LINE = /\A### phase-key (.*)/
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  def self.parse(attrs_line, key_line)
         | 
| 10 | 
            +
                    if attrs_line.match(PATTERN_ATTRS_LINE)
         | 
| 11 | 
            +
                      attrs = JSON.parse($1)
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                      new_key = new(
         | 
| 14 | 
            +
                        email:      attrs["email"],
         | 
| 15 | 
            +
                        status:     attrs["status"],
         | 
| 16 | 
            +
                        created_at: attrs["created_at"],
         | 
| 17 | 
            +
                        updated_at: attrs["updated_at"]
         | 
| 18 | 
            +
                      )
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                      if key_line.match(PATTERN_KEY_LINE)
         | 
| 21 | 
            +
                        new_key.key = key_line.chomp
         | 
| 22 | 
            +
                      end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                      new_key
         | 
| 25 | 
            +
                    end
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  def initialize(attrs = {})
         | 
| 29 | 
            +
                    self.email      = attrs.fetch(:email)
         | 
| 30 | 
            +
                    self.key        = attrs.fetch(:key, nil)
         | 
| 31 | 
            +
                    self.status     = attrs.fetch(:status, "active")
         | 
| 32 | 
            +
                    self.created_at = attrs.fetch(:created_at, Time.now)
         | 
| 33 | 
            +
                    self.updated_at = attrs.fetch(:updated_at, Time.now)
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  def key=(text)
         | 
| 37 | 
            +
                    if text && !text.match(/\A(# )?ssh-rsa .+/)
         | 
| 38 | 
            +
                      raise ArgumentError, "Invalid key."
         | 
| 39 | 
            +
                    end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                    @key = text
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  def key_fragment
         | 
| 45 | 
            +
                    "..." + uncomment(self.key).split[1][-20..-1] if self.key
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  def attributes
         | 
| 49 | 
            +
                    {
         | 
| 50 | 
            +
                      email:      self.email,
         | 
| 51 | 
            +
                      status:     self.status,
         | 
| 52 | 
            +
                      created_at: self.created_at,
         | 
| 53 | 
            +
                      updated_at: self.updated_at
         | 
| 54 | 
            +
                    }
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  def touch
         | 
| 58 | 
            +
                    self.updated_at = Time.now
         | 
| 59 | 
            +
                  end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  def valid?
         | 
| 62 | 
            +
                    !!self.email && !!self.key
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  def active?
         | 
| 66 | 
            +
                    self.status == "active"
         | 
| 67 | 
            +
                  end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                  def activate
         | 
| 70 | 
            +
                    self.status = "active"
         | 
| 71 | 
            +
                    self.key = uncomment(self.key)
         | 
| 72 | 
            +
                  end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                  def deactivate
         | 
| 75 | 
            +
                    self.status = "inactive"
         | 
| 76 | 
            +
                    self.key = comment(self.key)
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                  def comment(key); "# #{key}"; end
         | 
| 80 | 
            +
                  def uncomment(key); self.key.sub(/\A# /, ""); end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                  def to_s
         | 
| 83 | 
            +
                    escape( ["", "### phase-key #{ ::JSON.dump(attributes) }", self.key, ""].join("\n") )
         | 
| 84 | 
            +
                  end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                  def escape(text)
         | 
| 87 | 
            +
                    text.gsub("'", "\'")
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
                end
         | 
| 90 | 
            +
              end
         | 
| 91 | 
            +
            end
         | 
| @@ -0,0 +1,32 @@ | |
| 1 | 
            +
            module Phase
         | 
| 2 | 
            +
              module SSH
         | 
| 3 | 
            +
                class Backend < ::SSHKit::Backend::Netssh
         | 
| 4 | 
            +
                  include ::SSHKit::CommandHelper
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  def initialize(*args)
         | 
| 7 | 
            +
                    # BUG: Backend::Netssh doesn't assign @pool when subclassed.
         | 
| 8 | 
            +
                    self.class.pool = ::SSHKit::Backend::ConnectionPool.new
         | 
| 9 | 
            +
                    super
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  def on_remote_host(remote_host, &block)
         | 
| 13 | 
            +
                    @remote_host = remote_host
         | 
| 14 | 
            +
                    yield
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  private
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    def command(*args)
         | 
| 20 | 
            +
                      options = args.extract_options!
         | 
| 21 | 
            +
                      SSH::Command.new(*[ *args, options.merge({
         | 
| 22 | 
            +
                        in: @pwd.nil? ? nil : File.join(@pwd),
         | 
| 23 | 
            +
                        env: @env,
         | 
| 24 | 
            +
                        host: @host,
         | 
| 25 | 
            +
                        user: @user,
         | 
| 26 | 
            +
                        group: @group,
         | 
| 27 | 
            +
                        remote_host: @remote_host
         | 
| 28 | 
            +
                      }) ])
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
            end
         | 
| @@ -0,0 +1,16 @@ | |
| 1 | 
            +
            module Phase
         | 
| 2 | 
            +
              module SSH
         | 
| 3 | 
            +
                class Command < ::SSHKit::Command
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                  def on_remote_host(&block)
         | 
| 6 | 
            +
                    return yield unless options[:remote_host]
         | 
| 7 | 
            +
                    "ssh #{ options[:remote_host] } -- %s" % yield
         | 
| 8 | 
            +
                  end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  def to_command
         | 
| 11 | 
            +
                    on_remote_host { super }
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
            end
         | 
| @@ -0,0 +1,13 @@ | |
| 1 | 
            +
            module Phase
         | 
| 2 | 
            +
              module SSH
         | 
| 3 | 
            +
                class Coordinator < ::SSHKit::Coordinator
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                  private
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                    # Prevents Coordinator from uniqifing @raw_hosts.
         | 
| 8 | 
            +
                    def resolve_hosts
         | 
| 9 | 
            +
                      @raw_hosts.map { |rh| rh.is_a?(::SSHKit::Host) ? rh : ::SSHKit::Host.new(rh) }
         | 
| 10 | 
            +
                    end
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
            end
         | 
    
        data/lib/phase.rb
    ADDED
    
    | @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            require "terminal-table"
         | 
| 2 | 
            +
            require "active_support"
         | 
| 3 | 
            +
            require "progressbar"
         | 
| 4 | 
            +
            require "colorize"
         | 
| 5 | 
            +
            require "fog"
         | 
| 6 | 
            +
            require "sshkit"
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            require "dotenv"
         | 
| 9 | 
            +
            ::Dotenv.load if defined?(::Dotenv)
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            require "phase/adapters/aws"
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            require "phase/ssh/backend"
         | 
| 14 | 
            +
            require "phase/ssh/command"
         | 
| 15 | 
            +
            require "phase/ssh/coordinator"
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            require "phase/configuration"
         | 
| 18 | 
            +
            require "phase/version"
         | 
| 19 | 
            +
             | 
| 20 | 
            +
             | 
| 21 | 
            +
            module Phase
         | 
| 22 | 
            +
              class << self
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                def config
         | 
| 25 | 
            +
                  @@config ||= Configuration.new
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                def reset_config!
         | 
| 29 | 
            +
                  @@config = nil
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
              config
         | 
| 35 | 
            +
            end
         | 
    
        data/phase.gemspec
    ADDED
    
    | @@ -0,0 +1,33 @@ | |
| 1 | 
            +
            # coding: utf-8
         | 
| 2 | 
            +
            lib = File.expand_path('../lib', __FILE__)
         | 
| 3 | 
            +
            $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
         | 
| 4 | 
            +
            require 'phase/version'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            Gem::Specification.new do |spec|
         | 
| 7 | 
            +
              spec.name          = "phase"
         | 
| 8 | 
            +
              spec.version       = Phase::VERSION
         | 
| 9 | 
            +
              spec.authors       = ["Piers Mainwaring", "Orca Health, Inc."]
         | 
| 10 | 
            +
              spec.email         = ["piers@impossibly.org"]
         | 
| 11 | 
            +
              spec.summary       = "Provides a simple API for managing cloud instances running in a multi-subnet network."
         | 
| 12 | 
            +
              spec.description   = ""
         | 
| 13 | 
            +
              spec.homepage      = "https://github.com/piersadrian/phase"
         | 
| 14 | 
            +
              spec.license       = "MIT"
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              spec.files         = `git ls-files -z`.split("\x0")
         | 
| 17 | 
            +
              spec.executables   = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
         | 
| 18 | 
            +
              spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
         | 
| 19 | 
            +
              spec.require_paths = ["lib"]
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              spec.add_runtime_dependency 'commander',      '~> 4.2'
         | 
| 22 | 
            +
              spec.add_runtime_dependency 'terminal-table', '~> 1.4'
         | 
| 23 | 
            +
              spec.add_runtime_dependency 'progressbar',    '~> 0.21'
         | 
| 24 | 
            +
              spec.add_runtime_dependency 'activesupport',  '~> 4'
         | 
| 25 | 
            +
              spec.add_runtime_dependency 'fog',            '~> 1.23'
         | 
| 26 | 
            +
              spec.add_runtime_dependency 'capistrano',     '~> 3.2'
         | 
| 27 | 
            +
              spec.add_runtime_dependency 'mina',           '~> 0.3'
         | 
| 28 | 
            +
              spec.add_runtime_dependency 'colorize',       '~> 0.7'
         | 
| 29 | 
            +
              spec.add_runtime_dependency 'dotenv',         '~> 0.11'
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              spec.add_development_dependency "bundler", "~> 1.6"
         | 
| 32 | 
            +
              spec.add_development_dependency "rake",    "~> 10.1"
         | 
| 33 | 
            +
            end
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,229 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: phase
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 0.0.1
         | 
| 5 | 
            +
            platform: ruby
         | 
| 6 | 
            +
            authors:
         | 
| 7 | 
            +
            - Piers Mainwaring
         | 
| 8 | 
            +
            - Orca Health, Inc.
         | 
| 9 | 
            +
            autorequire: 
         | 
| 10 | 
            +
            bindir: bin
         | 
| 11 | 
            +
            cert_chain: []
         | 
| 12 | 
            +
            date: 2014-12-15 00:00:00.000000000 Z
         | 
| 13 | 
            +
            dependencies:
         | 
| 14 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 15 | 
            +
              name: commander
         | 
| 16 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 17 | 
            +
                requirements:
         | 
| 18 | 
            +
                - - "~>"
         | 
| 19 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 20 | 
            +
                    version: '4.2'
         | 
| 21 | 
            +
              type: :runtime
         | 
| 22 | 
            +
              prerelease: false
         | 
| 23 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 24 | 
            +
                requirements:
         | 
| 25 | 
            +
                - - "~>"
         | 
| 26 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 27 | 
            +
                    version: '4.2'
         | 
| 28 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 29 | 
            +
              name: terminal-table
         | 
| 30 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 31 | 
            +
                requirements:
         | 
| 32 | 
            +
                - - "~>"
         | 
| 33 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 34 | 
            +
                    version: '1.4'
         | 
| 35 | 
            +
              type: :runtime
         | 
| 36 | 
            +
              prerelease: false
         | 
| 37 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 38 | 
            +
                requirements:
         | 
| 39 | 
            +
                - - "~>"
         | 
| 40 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 41 | 
            +
                    version: '1.4'
         | 
| 42 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 43 | 
            +
              name: progressbar
         | 
| 44 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 45 | 
            +
                requirements:
         | 
| 46 | 
            +
                - - "~>"
         | 
| 47 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 48 | 
            +
                    version: '0.21'
         | 
| 49 | 
            +
              type: :runtime
         | 
| 50 | 
            +
              prerelease: false
         | 
| 51 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 52 | 
            +
                requirements:
         | 
| 53 | 
            +
                - - "~>"
         | 
| 54 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 55 | 
            +
                    version: '0.21'
         | 
| 56 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 57 | 
            +
              name: activesupport
         | 
| 58 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 59 | 
            +
                requirements:
         | 
| 60 | 
            +
                - - "~>"
         | 
| 61 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 62 | 
            +
                    version: '4'
         | 
| 63 | 
            +
              type: :runtime
         | 
| 64 | 
            +
              prerelease: false
         | 
| 65 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 66 | 
            +
                requirements:
         | 
| 67 | 
            +
                - - "~>"
         | 
| 68 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 69 | 
            +
                    version: '4'
         | 
| 70 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 71 | 
            +
              name: fog
         | 
| 72 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 73 | 
            +
                requirements:
         | 
| 74 | 
            +
                - - "~>"
         | 
| 75 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 76 | 
            +
                    version: '1.23'
         | 
| 77 | 
            +
              type: :runtime
         | 
| 78 | 
            +
              prerelease: false
         | 
| 79 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 80 | 
            +
                requirements:
         | 
| 81 | 
            +
                - - "~>"
         | 
| 82 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 83 | 
            +
                    version: '1.23'
         | 
| 84 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 85 | 
            +
              name: capistrano
         | 
| 86 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 87 | 
            +
                requirements:
         | 
| 88 | 
            +
                - - "~>"
         | 
| 89 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 90 | 
            +
                    version: '3.2'
         | 
| 91 | 
            +
              type: :runtime
         | 
| 92 | 
            +
              prerelease: false
         | 
| 93 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 94 | 
            +
                requirements:
         | 
| 95 | 
            +
                - - "~>"
         | 
| 96 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 97 | 
            +
                    version: '3.2'
         | 
| 98 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 99 | 
            +
              name: mina
         | 
| 100 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 101 | 
            +
                requirements:
         | 
| 102 | 
            +
                - - "~>"
         | 
| 103 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 104 | 
            +
                    version: '0.3'
         | 
| 105 | 
            +
              type: :runtime
         | 
| 106 | 
            +
              prerelease: false
         | 
| 107 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 108 | 
            +
                requirements:
         | 
| 109 | 
            +
                - - "~>"
         | 
| 110 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 111 | 
            +
                    version: '0.3'
         | 
| 112 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 113 | 
            +
              name: colorize
         | 
| 114 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 115 | 
            +
                requirements:
         | 
| 116 | 
            +
                - - "~>"
         | 
| 117 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 118 | 
            +
                    version: '0.7'
         | 
| 119 | 
            +
              type: :runtime
         | 
| 120 | 
            +
              prerelease: false
         | 
| 121 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 122 | 
            +
                requirements:
         | 
| 123 | 
            +
                - - "~>"
         | 
| 124 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 125 | 
            +
                    version: '0.7'
         | 
| 126 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 127 | 
            +
              name: dotenv
         | 
| 128 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 129 | 
            +
                requirements:
         | 
| 130 | 
            +
                - - "~>"
         | 
| 131 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 132 | 
            +
                    version: '0.11'
         | 
| 133 | 
            +
              type: :runtime
         | 
| 134 | 
            +
              prerelease: false
         | 
| 135 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 136 | 
            +
                requirements:
         | 
| 137 | 
            +
                - - "~>"
         | 
| 138 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 139 | 
            +
                    version: '0.11'
         | 
| 140 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 141 | 
            +
              name: bundler
         | 
| 142 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 143 | 
            +
                requirements:
         | 
| 144 | 
            +
                - - "~>"
         | 
| 145 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 146 | 
            +
                    version: '1.6'
         | 
| 147 | 
            +
              type: :development
         | 
| 148 | 
            +
              prerelease: false
         | 
| 149 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 150 | 
            +
                requirements:
         | 
| 151 | 
            +
                - - "~>"
         | 
| 152 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 153 | 
            +
                    version: '1.6'
         | 
| 154 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 155 | 
            +
              name: rake
         | 
| 156 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 157 | 
            +
                requirements:
         | 
| 158 | 
            +
                - - "~>"
         | 
| 159 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 160 | 
            +
                    version: '10.1'
         | 
| 161 | 
            +
              type: :development
         | 
| 162 | 
            +
              prerelease: false
         | 
| 163 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 164 | 
            +
                requirements:
         | 
| 165 | 
            +
                - - "~>"
         | 
| 166 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 167 | 
            +
                    version: '10.1'
         | 
| 168 | 
            +
            description: ''
         | 
| 169 | 
            +
            email:
         | 
| 170 | 
            +
            - piers@impossibly.org
         | 
| 171 | 
            +
            executables:
         | 
| 172 | 
            +
            - phase
         | 
| 173 | 
            +
            extensions: []
         | 
| 174 | 
            +
            extra_rdoc_files: []
         | 
| 175 | 
            +
            files:
         | 
| 176 | 
            +
            - ".gitignore"
         | 
| 177 | 
            +
            - Gemfile
         | 
| 178 | 
            +
            - LICENSE.txt
         | 
| 179 | 
            +
            - Makefile
         | 
| 180 | 
            +
            - README.md
         | 
| 181 | 
            +
            - Rakefile
         | 
| 182 | 
            +
            - VERSION
         | 
| 183 | 
            +
            - bin/phase
         | 
| 184 | 
            +
            - lib/phase.rb
         | 
| 185 | 
            +
            - lib/phase/adapters/aws.rb
         | 
| 186 | 
            +
            - lib/phase/cli.rb
         | 
| 187 | 
            +
            - lib/phase/cli/all.rb
         | 
| 188 | 
            +
            - lib/phase/cli/command.rb
         | 
| 189 | 
            +
            - lib/phase/cli/env.rb
         | 
| 190 | 
            +
            - lib/phase/cli/keys.rb
         | 
| 191 | 
            +
            - lib/phase/cli/logs.rb
         | 
| 192 | 
            +
            - lib/phase/cli/mosh.rb
         | 
| 193 | 
            +
            - lib/phase/cli/ssh.rb
         | 
| 194 | 
            +
            - lib/phase/cli/status.rb
         | 
| 195 | 
            +
            - lib/phase/cli/utils/loggable.rb
         | 
| 196 | 
            +
            - lib/phase/configuration.rb
         | 
| 197 | 
            +
            - lib/phase/dsl.rb
         | 
| 198 | 
            +
            - lib/phase/keys/key.rb
         | 
| 199 | 
            +
            - lib/phase/ssh/backend.rb
         | 
| 200 | 
            +
            - lib/phase/ssh/command.rb
         | 
| 201 | 
            +
            - lib/phase/ssh/coordinator.rb
         | 
| 202 | 
            +
            - lib/phase/version.rb
         | 
| 203 | 
            +
            - phase.gemspec
         | 
| 204 | 
            +
            homepage: https://github.com/piersadrian/phase
         | 
| 205 | 
            +
            licenses:
         | 
| 206 | 
            +
            - MIT
         | 
| 207 | 
            +
            metadata: {}
         | 
| 208 | 
            +
            post_install_message: 
         | 
| 209 | 
            +
            rdoc_options: []
         | 
| 210 | 
            +
            require_paths:
         | 
| 211 | 
            +
            - lib
         | 
| 212 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 213 | 
            +
              requirements:
         | 
| 214 | 
            +
              - - ">="
         | 
| 215 | 
            +
                - !ruby/object:Gem::Version
         | 
| 216 | 
            +
                  version: '0'
         | 
| 217 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 218 | 
            +
              requirements:
         | 
| 219 | 
            +
              - - ">="
         | 
| 220 | 
            +
                - !ruby/object:Gem::Version
         | 
| 221 | 
            +
                  version: '0'
         | 
| 222 | 
            +
            requirements: []
         | 
| 223 | 
            +
            rubyforge_project: 
         | 
| 224 | 
            +
            rubygems_version: 2.2.2
         | 
| 225 | 
            +
            signing_key: 
         | 
| 226 | 
            +
            specification_version: 4
         | 
| 227 | 
            +
            summary: Provides a simple API for managing cloud instances running in a multi-subnet
         | 
| 228 | 
            +
              network.
         | 
| 229 | 
            +
            test_files: []
         |