engineyard 0.3.1 → 0.3.2
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.
- data/lib/engineyard.rb +2 -2
 - data/lib/engineyard/api.rb +19 -0
 - data/lib/engineyard/cli.rb +24 -12
 - data/lib/engineyard/cli/action/deploy.rb +112 -0
 - data/lib/engineyard/cli/api.rb +14 -1
 - data/lib/engineyard/error.rb +27 -0
 - data/lib/engineyard/model.rb +9 -0
 - data/lib/engineyard/{account → model}/api_struct.rb +8 -3
 - data/lib/engineyard/{account → model}/app.rb +3 -3
 - data/lib/engineyard/{account → model}/environment.rb +14 -7
 - data/lib/engineyard/model/instance.rb +91 -0
 - data/lib/engineyard/{account → model}/log.rb +1 -1
 - data/lib/engineyard/repo.rb +3 -3
 - data/spec/engineyard/{account → model}/api_struct_spec.rb +11 -7
 - data/spec/engineyard/model/environment_spec.rb +45 -0
 - data/spec/engineyard/model/instance_spec.rb +70 -0
 - data/spec/engineyard/repo_spec.rb +17 -14
 - data/spec/ey/deploy_spec.rb +95 -45
 - data/spec/ey/list_environments_spec.rb +11 -1
 - data/spec/ey/logs_spec.rb +18 -0
 - data/spec/ey/rebuild_spec.rb +21 -4
 - data/spec/ey/ssh_spec.rb +28 -3
 - data/spec/spec_helper.rb +37 -12
 - data/spec/support/fake_awsm.ru +139 -4
 - data/spec/support/helpers.rb +19 -8
 - metadata +31 -20
 - data/lib/engineyard/account.rb +0 -63
 - data/lib/engineyard/account/app_master.rb +0 -6
 - data/lib/engineyard/account/instance.rb +0 -6
 - data/lib/engineyard/action/deploy.rb +0 -138
 - data/lib/engineyard/action/rebuild.rb +0 -31
 - data/lib/engineyard/action/util.rb +0 -19
 - data/spec/engineyard/account/environment_spec.rb +0 -20
 - data/spec/engineyard/account_spec.rb +0 -18
 
    
        data/lib/engineyard.rb
    CHANGED
    
    | 
         @@ -1,12 +1,12 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module EY
         
     | 
| 
       2 
2 
     | 
    
         
             
              require 'engineyard/ruby_ext'
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
     | 
    
         
            -
              VERSION = "0.3. 
     | 
| 
      
 4 
     | 
    
         
            +
              VERSION = "0.3.2"
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
       6 
     | 
    
         
            -
              autoload :Account, 'engineyard/account'
         
     | 
| 
       7 
6 
     | 
    
         
             
              autoload :API,     'engineyard/api'
         
     | 
| 
       8 
7 
     | 
    
         
             
              autoload :Config,  'engineyard/config'
         
     | 
| 
       9 
8 
     | 
    
         
             
              autoload :Error,   'engineyard/error'
         
     | 
| 
      
 9 
     | 
    
         
            +
              autoload :Model,   'engineyard/model'
         
     | 
| 
       10 
10 
     | 
    
         
             
              autoload :Repo,    'engineyard/repo'
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
12 
     | 
    
         
             
              class UI
         
     | 
    
        data/lib/engineyard/api.rb
    CHANGED
    
    | 
         @@ -1,3 +1,5 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'engineyard/model'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module EY
         
     | 
| 
       2 
4 
     | 
    
         
             
              class API
         
     | 
| 
       3 
5 
     | 
    
         
             
                attr_reader :token
         
     | 
| 
         @@ -20,9 +22,26 @@ module EY 
     | 
|
| 
       20 
22 
     | 
    
         
             
                  self.class.request(url, opts)
         
     | 
| 
       21 
23 
     | 
    
         
             
                end
         
     | 
| 
       22 
24 
     | 
    
         | 
| 
      
 25 
     | 
    
         
            +
                def environments
         
     | 
| 
      
 26 
     | 
    
         
            +
                  @environments ||= EY::Model::Environment.from_array(request('/environments')["environments"], :api => self)
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                def apps
         
     | 
| 
      
 30 
     | 
    
         
            +
                  @apps ||= EY::Model::App.from_array(request('/apps')["apps"], :api => self)
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def environment_named(name, envs = self.environments)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  envs.find{|e| e.name == name }
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                def app_for_repo(repo)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  apps.find{|a| repo.urls.include?(a.repository_uri) }
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
       23 
41 
     | 
    
         
             
                class InvalidCredentials < EY::Error; end
         
     | 
| 
       24 
42 
     | 
    
         
             
                class RequestFailed < EY::Error; end
         
     | 
| 
       25 
43 
     | 
    
         | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
       26 
45 
     | 
    
         
             
                def self.request(path, opts={})
         
     | 
| 
       27 
46 
     | 
    
         
             
                  require 'rest_client'
         
     | 
| 
       28 
47 
     | 
    
         
             
                  require 'json'
         
     | 
    
        data/lib/engineyard/cli.rb
    CHANGED
    
    | 
         @@ -24,8 +24,8 @@ module EY 
     | 
|
| 
       24 
24 
     | 
    
         
             
                method_option :install_eysd, :type => :boolean, :aliases => %(-s),
         
     | 
| 
       25 
25 
     | 
    
         
             
                  :desc => "Force remote install of eysd"
         
     | 
| 
       26 
26 
     | 
    
         
             
                def deploy(env_name = nil, branch = nil)
         
     | 
| 
       27 
     | 
    
         
            -
                  require 'engineyard/action/deploy'
         
     | 
| 
       28 
     | 
    
         
            -
                  EY::Action::Deploy.call(env_name, branch, options)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  require 'engineyard/cli/action/deploy'
         
     | 
| 
      
 28 
     | 
    
         
            +
                  EY::CLI::Action::Deploy.call(env_name, branch, options)
         
     | 
| 
       29 
29 
     | 
    
         
             
                end
         
     | 
| 
       30 
30 
     | 
    
         | 
| 
       31 
31 
     | 
    
         | 
| 
         @@ -47,15 +47,27 @@ module EY 
     | 
|
| 
       47 
47 
     | 
    
         | 
| 
       48 
48 
     | 
    
         
             
                desc "rebuild [ENV]", "Rebuild environment (ensure configuration is up-to-date)"
         
     | 
| 
       49 
49 
     | 
    
         
             
                def rebuild(name = nil)
         
     | 
| 
       50 
     | 
    
         
            -
                   
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
      
 50 
     | 
    
         
            +
                  env = if name
         
     | 
| 
      
 51 
     | 
    
         
            +
                          env = api.environment_named(name) or raise NoEnvironmentError.new(name)
         
     | 
| 
      
 52 
     | 
    
         
            +
                        end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  unless env
         
     | 
| 
      
 55 
     | 
    
         
            +
                    repo = Repo.new
         
     | 
| 
      
 56 
     | 
    
         
            +
                    app = api.app_for_repo(repo) or raise NoAppError.new(repo)
         
     | 
| 
      
 57 
     | 
    
         
            +
                    env = app.one_and_only_environment or raise EnvironmentError, "Unable to determine a single environment for the current application (found #{app.environments.size} environments)"
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  EY.ui.debug("Rebuilding #{env.name}")
         
     | 
| 
      
 61 
     | 
    
         
            +
                  env.rebuild
         
     | 
| 
       52 
62 
     | 
    
         
             
                end
         
     | 
| 
       53 
63 
     | 
    
         | 
| 
       54 
64 
     | 
    
         
             
                desc "ssh ENV", "Open an ssh session to the environment's application server"
         
     | 
| 
       55 
65 
     | 
    
         
             
                def ssh(name)
         
     | 
| 
       56 
     | 
    
         
            -
                  env =  
     | 
| 
       57 
     | 
    
         
            -
                  if env
         
     | 
| 
      
 66 
     | 
    
         
            +
                  env = api.environment_named(name)
         
     | 
| 
      
 67 
     | 
    
         
            +
                  if env && env.app_master
         
     | 
| 
       58 
68 
     | 
    
         
             
                    Kernel.exec "ssh", "#{env.username}@#{env.app_master.public_hostname}", *ARGV[2..-1]
         
     | 
| 
      
 69 
     | 
    
         
            +
                  elsif env
         
     | 
| 
      
 70 
     | 
    
         
            +
                    raise NoAppMaster.new(env.name)
         
     | 
| 
       59 
71 
     | 
    
         
             
                  else
         
     | 
| 
       60 
72 
     | 
    
         
             
                    EY.ui.warn %|Could not find an environment named "#{name}"|
         
     | 
| 
       61 
73 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -80,7 +92,7 @@ module EY 
     | 
|
| 
       80 
92 
     | 
    
         | 
| 
       81 
93 
     | 
    
         
             
                desc "upload_recipes ENV", "Upload custom chef recipes from the current directory to ENV"
         
     | 
| 
       82 
94 
     | 
    
         
             
                def upload_recipes(name)
         
     | 
| 
       83 
     | 
    
         
            -
                  if  
     | 
| 
      
 95 
     | 
    
         
            +
                  if env_named(name).upload_recipes
         
     | 
| 
       84 
96 
     | 
    
         
             
                    EY.ui.say "Recipes uploaded successfully"
         
     | 
| 
       85 
97 
     | 
    
         
             
                  else
         
     | 
| 
       86 
98 
     | 
    
         
             
                    EY.ui.error "Recipes upload failed"
         
     | 
| 
         @@ -94,8 +106,8 @@ module EY 
     | 
|
| 
       94 
106 
     | 
    
         
             
                map ["-v", "--version"] => :version
         
     | 
| 
       95 
107 
     | 
    
         | 
| 
       96 
108 
     | 
    
         
             
                private
         
     | 
| 
       97 
     | 
    
         
            -
                def  
     | 
| 
       98 
     | 
    
         
            -
                  @ 
     | 
| 
      
 109 
     | 
    
         
            +
                def api
         
     | 
| 
      
 110 
     | 
    
         
            +
                  @api ||= EY::CLI::API.new
         
     | 
| 
       99 
111 
     | 
    
         
             
                end
         
     | 
| 
       100 
112 
     | 
    
         | 
| 
       101 
113 
     | 
    
         
             
                def repo
         
     | 
| 
         @@ -103,7 +115,7 @@ module EY 
     | 
|
| 
       103 
115 
     | 
    
         
             
                end
         
     | 
| 
       104 
116 
     | 
    
         | 
| 
       105 
117 
     | 
    
         
             
                def env_named(env_name)
         
     | 
| 
       106 
     | 
    
         
            -
                  env =  
     | 
| 
      
 118 
     | 
    
         
            +
                  env = api.environment_named(env_name)
         
     | 
| 
       107 
119 
     | 
    
         | 
| 
       108 
120 
     | 
    
         
             
                  if env.nil?
         
     | 
| 
       109 
121 
     | 
    
         
             
                    raise EnvironmentError, "Environment '#{env_name}' can't be found\n" +
         
     | 
| 
         @@ -114,10 +126,10 @@ module EY 
     | 
|
| 
       114 
126 
     | 
    
         
             
                end
         
     | 
| 
       115 
127 
     | 
    
         | 
| 
       116 
128 
     | 
    
         
             
                def app_and_envs(all_envs = false)
         
     | 
| 
       117 
     | 
    
         
            -
                  app =  
     | 
| 
      
 129 
     | 
    
         
            +
                  app = api.app_for_repo(repo)
         
     | 
| 
       118 
130 
     | 
    
         | 
| 
       119 
131 
     | 
    
         
             
                  if all_envs || !app
         
     | 
| 
       120 
     | 
    
         
            -
                    envs =  
     | 
| 
      
 132 
     | 
    
         
            +
                    envs = api.environments
         
     | 
| 
       121 
133 
     | 
    
         
             
                    EY.ui.warn(NoAppError.new(repo).message) unless app || all_envs
         
     | 
| 
       122 
134 
     | 
    
         
             
                    [nil, envs]
         
     | 
| 
       123 
135 
     | 
    
         
             
                  else
         
     | 
| 
         @@ -0,0 +1,112 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module EY
         
     | 
| 
      
 2 
     | 
    
         
            +
              class CLI
         
     | 
| 
      
 3 
     | 
    
         
            +
                module Action
         
     | 
| 
      
 4 
     | 
    
         
            +
                  class Deploy
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                    EYSD_VERSION = "~>0.3.0"
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                    def self.call(env_name, branch, options)
         
     | 
| 
      
 9 
     | 
    
         
            +
                      env_name ||= EY.config.default_environment
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                      app = fetch_app
         
     | 
| 
      
 12 
     | 
    
         
            +
                      env = fetch_environment(env_name, app)
         
     | 
| 
      
 13 
     | 
    
         
            +
                      branch = fetch_branch(env.name, branch, options[:force])
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                      running = env.app_master && env.app_master.status == "running"
         
     | 
| 
      
 16 
     | 
    
         
            +
                      raise EnvironmentError, "No running instances for environment #{env.name}\nStart one at #{EY.config.endpoint}" unless running
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                      master   = env.app_master
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                      EY.ui.info "Connecting to the server..."
         
     | 
| 
      
 21 
     | 
    
         
            +
                      ensure_eysd_present(master, options[:install_eysd])
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                      EY.ui.info "Running deploy for '#{env.name}' on server..."
         
     | 
| 
      
 24 
     | 
    
         
            +
                      deployed = master.deploy!(app, branch, options[:migrate], env.config)
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                      if deployed
         
     | 
| 
      
 27 
     | 
    
         
            +
                        EY.ui.info "Deploy complete"
         
     | 
| 
      
 28 
     | 
    
         
            +
                      else
         
     | 
| 
      
 29 
     | 
    
         
            +
                        raise EY::Error, "Deploy failed"
         
     | 
| 
      
 30 
     | 
    
         
            +
                      end
         
     | 
| 
      
 31 
     | 
    
         
            +
                    end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                    private
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                    def self.api
         
     | 
| 
      
 36 
     | 
    
         
            +
                      @api ||= EY::CLI::API.new
         
     | 
| 
      
 37 
     | 
    
         
            +
                    end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                    def self.repo
         
     | 
| 
      
 40 
     | 
    
         
            +
                      @repo ||= EY::Repo.new
         
     | 
| 
      
 41 
     | 
    
         
            +
                    end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                    def self.fetch_app
         
     | 
| 
      
 44 
     | 
    
         
            +
                      app = api.app_for_repo(repo)
         
     | 
| 
      
 45 
     | 
    
         
            +
                      raise NoAppError.new(repo) unless app
         
     | 
| 
      
 46 
     | 
    
         
            +
                      app
         
     | 
| 
      
 47 
     | 
    
         
            +
                    end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                    def self.fetch_environment(env_name, app)
         
     | 
| 
      
 50 
     | 
    
         
            +
                      # if the name's not specified and there's not exactly one
         
     | 
| 
      
 51 
     | 
    
         
            +
                      # environment, we can't figure out which environment to deploy
         
     | 
| 
      
 52 
     | 
    
         
            +
                      raise DeployArgumentError if !env_name && app.environments.size != 1
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                      env = if env_name
         
     | 
| 
      
 55 
     | 
    
         
            +
                              api.environment_named(env_name, app.environments)
         
     | 
| 
      
 56 
     | 
    
         
            +
                            else
         
     | 
| 
      
 57 
     | 
    
         
            +
                              app.environments.first
         
     | 
| 
      
 58 
     | 
    
         
            +
                            end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                      # the environment exists, but doesn't have this app
         
     | 
| 
      
 61 
     | 
    
         
            +
                      if !env && api.environment_named(env_name)
         
     | 
| 
      
 62 
     | 
    
         
            +
                        raise EnvironmentError, "Environment '#{env_name}' doesn't run this application\nYou can add it at #{EY.config.endpoint}"
         
     | 
| 
      
 63 
     | 
    
         
            +
                      end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                      if !env
         
     | 
| 
      
 66 
     | 
    
         
            +
                        raise NoEnvironmentError.new(env_name)
         
     | 
| 
      
 67 
     | 
    
         
            +
                      end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                      env
         
     | 
| 
      
 70 
     | 
    
         
            +
                    end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                    def self.fetch_branch(env_name, user_specified_branch, force)
         
     | 
| 
      
 73 
     | 
    
         
            +
                      default_branch = EY.config.default_branch(env_name)
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                      branch = if user_specified_branch
         
     | 
| 
      
 76 
     | 
    
         
            +
                                 if default_branch && (user_specified_branch != default_branch) && !force
         
     | 
| 
      
 77 
     | 
    
         
            +
                                   raise BranchMismatch.new(default_branch, user_specified_branch)
         
     | 
| 
      
 78 
     | 
    
         
            +
                                 end
         
     | 
| 
      
 79 
     | 
    
         
            +
                                 user_specified_branch
         
     | 
| 
      
 80 
     | 
    
         
            +
                               else
         
     | 
| 
      
 81 
     | 
    
         
            +
                                 default_branch || repo.current_branch
         
     | 
| 
      
 82 
     | 
    
         
            +
                               end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                      raise DeployArgumentError unless branch
         
     | 
| 
      
 85 
     | 
    
         
            +
                      branch
         
     | 
| 
      
 86 
     | 
    
         
            +
                    end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                    def self.ensure_eysd_present(instance, install_eysd)
         
     | 
| 
      
 89 
     | 
    
         
            +
                      eysd_status = instance.ey_deploy_check
         
     | 
| 
      
 90 
     | 
    
         
            +
                      case eysd_status
         
     | 
| 
      
 91 
     | 
    
         
            +
                      when :ssh_failed
         
     | 
| 
      
 92 
     | 
    
         
            +
                        raise EnvironmentError, "SSH connection to #{instance.hostname} failed"
         
     | 
| 
      
 93 
     | 
    
         
            +
                      when :eysd_missing
         
     | 
| 
      
 94 
     | 
    
         
            +
                        EY.ui.warn "Instance does not have server-side component installed"
         
     | 
| 
      
 95 
     | 
    
         
            +
                        EY.ui.info "Installing server-side component..."
         
     | 
| 
      
 96 
     | 
    
         
            +
                        instance.install_ey_deploy!
         
     | 
| 
      
 97 
     | 
    
         
            +
                      when :too_new
         
     | 
| 
      
 98 
     | 
    
         
            +
                        raise EnvironmentError, "server-side component too new; please upgrade your copy of the engineyard gem."
         
     | 
| 
      
 99 
     | 
    
         
            +
                      when :too_old
         
     | 
| 
      
 100 
     | 
    
         
            +
                        EY.ui.info "Upgrading server-side component..."
         
     | 
| 
      
 101 
     | 
    
         
            +
                        instance.upgrade_ey_deploy!
         
     | 
| 
      
 102 
     | 
    
         
            +
                      when :ok
         
     | 
| 
      
 103 
     | 
    
         
            +
                        # no action needed
         
     | 
| 
      
 104 
     | 
    
         
            +
                      else
         
     | 
| 
      
 105 
     | 
    
         
            +
                        raise EY::Error, "Internal error: Unexpected status from Instance#ey_deploy_check; got #{eysd_status.inspect}"
         
     | 
| 
      
 106 
     | 
    
         
            +
                      end
         
     | 
| 
      
 107 
     | 
    
         
            +
                    end
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                  end
         
     | 
| 
      
 110 
     | 
    
         
            +
                end
         
     | 
| 
      
 111 
     | 
    
         
            +
              end
         
     | 
| 
      
 112 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/engineyard/cli/api.rb
    CHANGED
    
    | 
         @@ -23,6 +23,10 @@ module EY 
     | 
|
| 
       23 
23 
     | 
    
         
             
                    @token = self.class.fetch_token
         
     | 
| 
       24 
24 
     | 
    
         
             
                  end
         
     | 
| 
       25 
25 
     | 
    
         | 
| 
      
 26 
     | 
    
         
            +
                  def environment_named(env_name, envs = self.environments)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    super || find_environment_by_unambiguous_substring(env_name, envs)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
       26 
30 
     | 
    
         
             
                  def self.fetch_token
         
     | 
| 
       27 
31 
     | 
    
         
             
                    EY.ui.warn("The engineyard gem is prerelease software. Please do not use")
         
     | 
| 
       28 
32 
     | 
    
         
             
                    EY.ui.warn("this tool to deploy to mission-critical environments, yet.")
         
     | 
| 
         @@ -37,6 +41,15 @@ module EY 
     | 
|
| 
       37 
41 
     | 
    
         
             
                    end
         
     | 
| 
       38 
42 
     | 
    
         
             
                  end
         
     | 
| 
       39 
43 
     | 
    
         | 
| 
      
 44 
     | 
    
         
            +
                  private
         
     | 
| 
      
 45 
     | 
    
         
            +
                  def find_environment_by_unambiguous_substring(env_name, envs)
         
     | 
| 
      
 46 
     | 
    
         
            +
                    candidates = envs.find_all{|e| e.name[env_name] }
         
     | 
| 
      
 47 
     | 
    
         
            +
                    if candidates.size > 1
         
     | 
| 
      
 48 
     | 
    
         
            +
                      raise AmbiguousEnvironmentName.new(env_name, candidates.map {|e| e.name})
         
     | 
| 
      
 49 
     | 
    
         
            +
                    end
         
     | 
| 
      
 50 
     | 
    
         
            +
                    candidates.first
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
       40 
53 
     | 
    
         
             
                end
         
     | 
| 
       41 
54 
     | 
    
         
             
              end
         
     | 
| 
       42 
     | 
    
         
            -
            end
         
     | 
| 
      
 55 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/engineyard/error.rb
    CHANGED
    
    | 
         @@ -1,6 +1,12 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module EY
         
     | 
| 
       2 
2 
     | 
    
         
             
              class Error < RuntimeError; end
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
      
 4 
     | 
    
         
            +
              class NoRemotesError < EY::Error
         
     | 
| 
      
 5 
     | 
    
         
            +
                def initialize(path)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  super "fatal: No git remotes found in #{path}"
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
              end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
       4 
10 
     | 
    
         
             
              class NoAppError < Error
         
     | 
| 
       5 
11 
     | 
    
         
             
                def initialize(repo)
         
     | 
| 
       6 
12 
     | 
    
         
             
                  @repo = repo
         
     | 
| 
         @@ -14,9 +20,30 @@ module EY 
     | 
|
| 
       14 
20 
     | 
    
         
             
                end
         
     | 
| 
       15 
21 
     | 
    
         
             
              end
         
     | 
| 
       16 
22 
     | 
    
         | 
| 
      
 23 
     | 
    
         
            +
              class NoAppMaster < EY::Error
         
     | 
| 
      
 24 
     | 
    
         
            +
                def initialize(env_name)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  @env_name = env_name
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def message
         
     | 
| 
      
 29 
     | 
    
         
            +
                  "The environment '#{@env_name}' does not have a master instance."
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
              end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
       17 
33 
     | 
    
         
             
              class EnvironmentError < EY::Error
         
     | 
| 
       18 
34 
     | 
    
         
             
              end
         
     | 
| 
       19 
35 
     | 
    
         | 
| 
      
 36 
     | 
    
         
            +
              class AmbiguousEnvironmentName < EY::Error
         
     | 
| 
      
 37 
     | 
    
         
            +
                def initialize(name, matches)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  @name, @matches = name, matches
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                def message
         
     | 
| 
      
 42 
     | 
    
         
            +
                  pretty_names = @matches.map {|x| "'#{x}'"}.join(', ')
         
     | 
| 
      
 43 
     | 
    
         
            +
                  "The name '#{@name}' is ambiguous; it matches all of the following environment names: #{pretty_names}.\nPlease use a longer, unambiguous substring or the entire environment name."
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
              end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
       20 
47 
     | 
    
         
             
              class NoEnvironmentError < EY::Error
         
     | 
| 
       21 
48 
     | 
    
         
             
                def initialize(env_name=nil)
         
     | 
| 
       22 
49 
     | 
    
         
             
                  @env_name = env_name
         
     | 
| 
         @@ -0,0 +1,9 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module EY
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Model
         
     | 
| 
      
 3 
     | 
    
         
            +
                autoload :ApiStruct,   'engineyard/model/api_struct'
         
     | 
| 
      
 4 
     | 
    
         
            +
                autoload :App,         'engineyard/model/app'
         
     | 
| 
      
 5 
     | 
    
         
            +
                autoload :Environment, 'engineyard/model/environment'
         
     | 
| 
      
 6 
     | 
    
         
            +
                autoload :Log,         'engineyard/model/log'
         
     | 
| 
      
 7 
     | 
    
         
            +
                autoload :Instance,    'engineyard/model/instance'
         
     | 
| 
      
 8 
     | 
    
         
            +
              end
         
     | 
| 
      
 9 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -1,13 +1,13 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module EY
         
     | 
| 
       2 
     | 
    
         
            -
               
     | 
| 
      
 2 
     | 
    
         
            +
              module Model
         
     | 
| 
       3 
3 
     | 
    
         
             
                class ApiStruct < Struct
         
     | 
| 
       4 
4 
     | 
    
         
             
                  def self.new(*args, &block)
         
     | 
| 
       5 
5 
     | 
    
         
             
                    super(*args) do |*block_args|
         
     | 
| 
       6 
6 
     | 
    
         
             
                      block.call(*block_args) if block
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
     | 
    
         
            -
                      def self.from_array(array,  
     | 
| 
      
 8 
     | 
    
         
            +
                      def self.from_array(array, common_values = {})
         
     | 
| 
       9 
9 
     | 
    
         
             
                        array.map do |values|
         
     | 
| 
       10 
     | 
    
         
            -
                          from_hash(values.merge( 
     | 
| 
      
 10 
     | 
    
         
            +
                          from_hash(values.merge(common_values))
         
     | 
| 
       11 
11 
     | 
    
         
             
                        end if array
         
     | 
| 
       12 
12 
     | 
    
         
             
                      end
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
         @@ -20,6 +20,11 @@ module EY 
     | 
|
| 
       20 
20 
     | 
    
         | 
| 
       21 
21 
     | 
    
         
             
                    end
         
     | 
| 
       22 
22 
     | 
    
         
             
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  def api_get(uri, options = {})
         
     | 
| 
      
 25 
     | 
    
         
            +
                    api.request(uri, options.merge(:method => :get))
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
       23 
28 
     | 
    
         
             
                end
         
     | 
| 
       24 
29 
     | 
    
         
             
              end
         
     | 
| 
       25 
30 
     | 
    
         
             
            end
         
     | 
| 
         @@ -1,10 +1,10 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module EY
         
     | 
| 
       2 
     | 
    
         
            -
               
     | 
| 
       3 
     | 
    
         
            -
                class App < ApiStruct.new(:name, :repository_uri, :environments, : 
     | 
| 
      
 2 
     | 
    
         
            +
              module Model
         
     | 
| 
      
 3 
     | 
    
         
            +
                class App < ApiStruct.new(:name, :repository_uri, :environments, :api)
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
                  def self.from_hash(hash)
         
     | 
| 
       6 
6 
     | 
    
         
             
                    super.tap do |app|
         
     | 
| 
       7 
     | 
    
         
            -
                      app.environments = Environment.from_array(app.environments, app. 
     | 
| 
      
 7 
     | 
    
         
            +
                      app.environments = Environment.from_array(app.environments, :api => app.api)
         
     | 
| 
       8 
8 
     | 
    
         
             
                    end
         
     | 
| 
       9 
9 
     | 
    
         
             
                  end
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
         @@ -1,24 +1,31 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module EY
         
     | 
| 
       2 
     | 
    
         
            -
               
     | 
| 
       3 
     | 
    
         
            -
                class Environment < ApiStruct.new(:id, :name, :instances_count, :apps, :app_master, :username, : 
     | 
| 
      
 2 
     | 
    
         
            +
              module Model
         
     | 
| 
      
 3 
     | 
    
         
            +
                class Environment < ApiStruct.new(:id, :name, :instances_count, :apps, :app_master, :username, :api)
         
     | 
| 
       4 
4 
     | 
    
         
             
                  def self.from_hash(hash)
         
     | 
| 
       5 
5 
     | 
    
         
             
                    super.tap do |env|
         
     | 
| 
       6 
6 
     | 
    
         
             
                      env.username = hash['ssh_username']
         
     | 
| 
       7 
     | 
    
         
            -
                      env.apps = App.from_array(env.apps, env. 
     | 
| 
       8 
     | 
    
         
            -
                      env.app_master =  
     | 
| 
      
 7 
     | 
    
         
            +
                      env.apps = App.from_array(env.apps, :api => env.api)
         
     | 
| 
      
 8 
     | 
    
         
            +
                      env.app_master = Instance.from_hash(env.app_master.merge(:environment => env)) if env.app_master
         
     | 
| 
       9 
9 
     | 
    
         
             
                    end
         
     | 
| 
       10 
10 
     | 
    
         
             
                  end
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
12 
     | 
    
         
             
                  def logs
         
     | 
| 
       13 
     | 
    
         
            -
                     
     | 
| 
      
 13 
     | 
    
         
            +
                    Log.from_array(api_get("/environments/#{id}/logs")["logs"])
         
     | 
| 
       14 
14 
     | 
    
         
             
                  end
         
     | 
| 
       15 
15 
     | 
    
         | 
| 
       16 
16 
     | 
    
         
             
                  def instances
         
     | 
| 
       17 
     | 
    
         
            -
                     
     | 
| 
      
 17 
     | 
    
         
            +
                    Instance.from_array(api_get("/environments/#{id}/instances")["instances"], :environment => self)
         
     | 
| 
       18 
18 
     | 
    
         
             
                  end
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
       20 
20 
     | 
    
         
             
                  def rebuild
         
     | 
| 
       21 
     | 
    
         
            -
                     
     | 
| 
      
 21 
     | 
    
         
            +
                    api.request("/environments/#{id}/rebuild", :method => :put)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  def upload_recipes(file_to_upload = recipe_file)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    api.request("/environments/#{id}/recipes",
         
     | 
| 
      
 26 
     | 
    
         
            +
                      :method => :post,
         
     | 
| 
      
 27 
     | 
    
         
            +
                      :params => {:file => file_to_upload}
         
     | 
| 
      
 28 
     | 
    
         
            +
                      )
         
     | 
| 
       22 
29 
     | 
    
         
             
                  end
         
     | 
| 
       23 
30 
     | 
    
         | 
| 
       24 
31 
     | 
    
         
             
                  def recipe_file
         
     | 
| 
         @@ -0,0 +1,91 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'escape'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module EY
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Model
         
     | 
| 
      
 5 
     | 
    
         
            +
                class Instance < ApiStruct.new(:id, :role, :status, :amazon_id, :public_hostname, :environment)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  EYSD_VERSION = "~>0.3.2"
         
     | 
| 
      
 7 
     | 
    
         
            +
                  CHECK_SCRIPT = <<-SCRIPT
         
     | 
| 
      
 8 
     | 
    
         
            +
            require "rubygems"
         
     | 
| 
      
 9 
     | 
    
         
            +
            requirement = Gem::Requirement.new("#{EYSD_VERSION}")
         
     | 
| 
      
 10 
     | 
    
         
            +
            required_version = requirement.requirements.last.last # thanks thanks rubygems rubygems
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            # this will be a ["name-version", Gem::Specification] two-element array if present, nil otherwise
         
     | 
| 
      
 13 
     | 
    
         
            +
            ey_deploy_geminfo = Gem.source_index.find{ |(name,_)| name =~ /^ey-deploy-\\\d/ }
         
     | 
| 
      
 14 
     | 
    
         
            +
            exit(104) unless ey_deploy_geminfo
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            current_version = ey_deploy_geminfo.last.version
         
     | 
| 
      
 17 
     | 
    
         
            +
            exit(0) if requirement.satisfied_by?(current_version)
         
     | 
| 
      
 18 
     | 
    
         
            +
            exit(70) if required_version > current_version
         
     | 
| 
      
 19 
     | 
    
         
            +
            exit(17) # required_version < current_version
         
     | 
| 
      
 20 
     | 
    
         
            +
                  SCRIPT
         
     | 
| 
      
 21 
     | 
    
         
            +
                  EXIT_STATUS = Hash.new { |h,k| raise EY::Error, "ey-deploy version checker exited with unknown status code #{k}" }
         
     | 
| 
      
 22 
     | 
    
         
            +
                  EXIT_STATUS.merge!({
         
     | 
| 
      
 23 
     | 
    
         
            +
                    255 => :ssh_failed,
         
     | 
| 
      
 24 
     | 
    
         
            +
                    104 => :eysd_missing,
         
     | 
| 
      
 25 
     | 
    
         
            +
                    70  => :too_old,
         
     | 
| 
      
 26 
     | 
    
         
            +
                    17  => :too_new,
         
     | 
| 
      
 27 
     | 
    
         
            +
                    0   => :ok,
         
     | 
| 
      
 28 
     | 
    
         
            +
                  })
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  alias :hostname :public_hostname
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  def deploy!(app, ref, migration_command=nil, extra_configuration=nil)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    deploy_cmd = [eysd_path, 'deploy', '--app', app.name, '--branch', ref]
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                    if extra_configuration
         
     | 
| 
      
 36 
     | 
    
         
            +
                      deploy_cmd << '--config' << extra_configuration.to_json
         
     | 
| 
      
 37 
     | 
    
         
            +
                    end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                    if migration_command
         
     | 
| 
      
 40 
     | 
    
         
            +
                      deploy_cmd << "--migrate" << migration_command
         
     | 
| 
      
 41 
     | 
    
         
            +
                    end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                    ssh Escape.shell_command(deploy_cmd)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                  def ey_deploy_check
         
     | 
| 
      
 47 
     | 
    
         
            +
                    require 'base64'
         
     | 
| 
      
 48 
     | 
    
         
            +
                    encoded_script = Base64.encode64(CHECK_SCRIPT).gsub(/\n/, '')
         
     | 
| 
      
 49 
     | 
    
         
            +
                    ssh "#{ruby_path} -r base64 -e \"eval Base64.decode64(ARGV[0])\" #{encoded_script}", false
         
     | 
| 
      
 50 
     | 
    
         
            +
                    EXIT_STATUS[$?.exitstatus]
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  def install_ey_deploy!
         
     | 
| 
      
 54 
     | 
    
         
            +
                    ssh(Escape.shell_command(['sudo', gem_path, 'install', 'ey-deploy', '-v', EYSD_VERSION]))
         
     | 
| 
      
 55 
     | 
    
         
            +
                  end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                  def upgrade_ey_deploy!
         
     | 
| 
      
 58 
     | 
    
         
            +
                    ssh "sudo #{gem_path} uninstall -a -x ey-deploy"
         
     | 
| 
      
 59 
     | 
    
         
            +
                    install_ey_deploy!
         
     | 
| 
      
 60 
     | 
    
         
            +
                  end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                private
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                  def ssh(remote_command, output = true)
         
     | 
| 
      
 65 
     | 
    
         
            +
                    user = environment.username
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                    cmd = Escape.shell_command(%w[ssh -o StrictHostKeyChecking=no -q] << "#{user}@#{hostname}" << remote_command)
         
     | 
| 
      
 68 
     | 
    
         
            +
                    cmd << " > /dev/null" unless output
         
     | 
| 
      
 69 
     | 
    
         
            +
                    output ? puts(cmd) : EY.ui.debug(cmd)
         
     | 
| 
      
 70 
     | 
    
         
            +
                    unless ENV["NO_SSH"]
         
     | 
| 
      
 71 
     | 
    
         
            +
                      system cmd
         
     | 
| 
      
 72 
     | 
    
         
            +
                    else
         
     | 
| 
      
 73 
     | 
    
         
            +
                      true
         
     | 
| 
      
 74 
     | 
    
         
            +
                    end
         
     | 
| 
      
 75 
     | 
    
         
            +
                  end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                  def eysd_path
         
     | 
| 
      
 78 
     | 
    
         
            +
                    "/usr/local/ey_resin/ruby/bin/eysd"
         
     | 
| 
      
 79 
     | 
    
         
            +
                  end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                  def gem_path
         
     | 
| 
      
 82 
     | 
    
         
            +
                    "/usr/local/ey_resin/ruby/bin/gem"
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                  def ruby_path
         
     | 
| 
      
 86 
     | 
    
         
            +
                    "/usr/local/ey_resin/ruby/bin/ruby"
         
     | 
| 
      
 87 
     | 
    
         
            +
                  end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                end
         
     | 
| 
      
 90 
     | 
    
         
            +
              end
         
     | 
| 
      
 91 
     | 
    
         
            +
            end
         
     |