ey-core 3.0.5 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -7
  3. data/bin/ey-core +1 -1
  4. data/ey-core.gemspec +15 -1
  5. data/lib/ey-core/cli.rb +12 -114
  6. data/lib/ey-core/cli/accounts.rb +9 -0
  7. data/lib/ey-core/cli/applications.rb +16 -0
  8. data/lib/ey-core/cli/console.rb +14 -0
  9. data/lib/ey-core/cli/current_user.rb +8 -0
  10. data/lib/ey-core/cli/deploy.rb +65 -0
  11. data/lib/ey-core/cli/environments.rb +17 -0
  12. data/lib/ey-core/cli/errors.rb +7 -0
  13. data/lib/ey-core/cli/init.rb +11 -0
  14. data/lib/ey-core/cli/login.rb +26 -0
  15. data/lib/ey-core/cli/logout.rb +14 -0
  16. data/lib/ey-core/cli/logs.rb +40 -0
  17. data/lib/ey-core/cli/recipes.rb +92 -0
  18. data/lib/ey-core/cli/recipes/apply.rb +34 -0
  19. data/lib/ey-core/cli/recipes/download.rb +26 -0
  20. data/lib/ey-core/cli/recipes/upload.rb +27 -0
  21. data/lib/ey-core/cli/scp.rb +11 -0
  22. data/lib/ey-core/cli/servers.rb +19 -0
  23. data/lib/ey-core/cli/ssh.rb +94 -0
  24. data/lib/ey-core/cli/status.rb +20 -0
  25. data/lib/ey-core/cli/subcommand.rb +114 -0
  26. data/lib/ey-core/cli/timeout_deploy.rb +28 -0
  27. data/lib/ey-core/cli/version.rb +8 -0
  28. data/lib/ey-core/cli/web.rb +10 -0
  29. data/lib/ey-core/cli/web/disable.rb +23 -0
  30. data/lib/ey-core/cli/web/enable.rb +23 -0
  31. data/lib/ey-core/cli/web/restart.rb +23 -0
  32. data/lib/ey-core/cli/whoami.rb +4 -0
  33. data/lib/ey-core/client.rb +9 -0
  34. data/lib/ey-core/client/mock.rb +1 -0
  35. data/lib/ey-core/client/real.rb +4 -4
  36. data/lib/ey-core/collections/deployments.rb +8 -0
  37. data/lib/ey-core/models/account.rb +1 -0
  38. data/lib/ey-core/models/deployment.rb +23 -0
  39. data/lib/ey-core/models/environment.rb +37 -0
  40. data/lib/ey-core/requests/change_environment_maintenance.rb +38 -0
  41. data/lib/ey-core/requests/create_environment.rb +3 -0
  42. data/lib/ey-core/requests/deploy_environment_application.rb +17 -0
  43. data/lib/ey-core/requests/get_deployment.rb +19 -0
  44. data/lib/ey-core/requests/get_deployments.rb +29 -0
  45. data/lib/ey-core/requests/get_token_by_login.rb +30 -0
  46. data/lib/ey-core/requests/restart_environment_app_servers.rb +38 -0
  47. data/lib/ey-core/requests/timeout_deployment.rb +27 -0
  48. data/lib/ey-core/requests/upload_recipes_for_environment.rb +28 -0
  49. data/lib/ey-core/version.rb +1 -1
  50. data/spec/deployments_spec.rb +24 -0
  51. data/spec/tokens_spec.rb +23 -1
  52. metadata +228 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1dc4da29698ad9bb948ca751e854c4efb134aab9
4
- data.tar.gz: 89e4e06dfeee04459768395059a892ce38865f2c
3
+ metadata.gz: fc84632655feed3d40115fdf1417d1a2e203877e
4
+ data.tar.gz: 92b68a9e1c740d0ad32148486f8d9516976722f8
5
5
  SHA512:
6
- metadata.gz: 5bea713bee5a23c8e5371eac1c4138e4d6cc240fb9bf96ba4995c84e923c01ec1fbdc3fe0100827d4182578bca59feb894301d76d867482d25e7def36e8812ca
7
- data.tar.gz: af636f6e784f5371f79e9f9ddd300a171cfa76c6ee1ac976d81d0b163850e307a36c4063ef5fe642f8b4cd8278bab42a810c5b84aa262f88d0aa73529eebbd02
6
+ metadata.gz: 4e58ade18e6f14500f8f47446d20829777a438a44d89ed653a705d31edd5cabfbb5b8ec1955f7e798b4a1bbce6ce4c6cc4958148e32e82039fb948c39db54c7a
7
+ data.tar.gz: 6e24dfeaaa4781fa782f8cca1b6b44f0b95347c33a387c9a9a66c7a341ada2055eb88caa887f617062fd6d6bb4bf3e2d75b52b2e908b0fe3c669d4bc490054ee
data/Gemfile CHANGED
@@ -3,12 +3,6 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in ey-core.gemspec
4
4
  gemspec
5
5
 
6
- gem 'awesome_print', '~> 1.0'
7
- gem 'faye', '~> 1.1'
8
- gem 'oj'
9
- gem 'oj_mimic_json'
10
- gem 'pry-nav'
11
-
12
6
  group :doc do
13
7
  gem 'yard'
14
8
  gem 'redcarpet'
@@ -21,7 +15,6 @@ group :test do
21
15
  gem 'guard-rspec', '~> 4.2', require: false
22
16
  gem 'hashie'
23
17
  gem 'rack-test'
24
- gem 'rake'
25
18
  gem 'rspec', '~> 3.1'
26
19
  gem 'simplecov'
27
20
  gem 'timecop'
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
2
  require File.expand_path('../../lib/ey-core/cli', __FILE__)
3
3
 
4
- Ey::Core::Cli.new(ARGV).run
4
+ Ey::Core::Cli.new(ARGV).execute!
@@ -19,10 +19,24 @@ Gem::Specification.new do |gem|
19
19
  gem.licenses = ["MIT"]
20
20
 
21
21
  gem.add_dependency "addressable", "~> 2.2"
22
+ gem.add_dependency "awesome_print"
23
+ gem.add_dependency "belafonte"
22
24
  gem.add_dependency "cistern", "~> 0.12"
25
+ gem.add_dependency "colorize"
23
26
  gem.add_dependency "ey-hmac", "~> 2.0"
24
- gem.add_dependency "sshkey", "~> 1.6"
27
+ gem.add_dependency "escape"
25
28
  gem.add_dependency "faraday", "~> 0.9"
26
29
  gem.add_dependency "faraday_middleware", "~> 0.9"
30
+ gem.add_dependency "faye"
27
31
  gem.add_dependency "mime-types", "~> 2.99" #maintain ruby 1.9 compatibility
32
+ gem.add_dependency "oj"
33
+ gem.add_dependency "oj_mimic_json"
34
+ gem.add_dependency "pry"
35
+ gem.add_dependency "sshkey", "~> 1.6"
36
+ gem.add_dependency "table_print"
37
+
38
+ gem.add_development_dependency "pry-nav"
39
+ gem.add_development_dependency "rspec", "~> 3.0"
40
+ gem.add_development_dependency "ffaker"
41
+ gem.add_development_dependency "rake"
28
42
  end
@@ -3,124 +3,22 @@ require 'ostruct'
3
3
  require 'ey-core'
4
4
  require 'awesome_print'
5
5
  require 'pry'
6
+ require 'belafonte'
7
+ require 'table_print'
8
+ require 'rubygems/package'
9
+ require 'escape'
6
10
 
7
11
  Cistern.formatter = Cistern::Formatter::AwesomePrint
8
12
 
9
- class Ey::Core::Cli
10
13
 
11
- def self.stdout
12
- @stdout ||= STDOUT
13
- end
14
-
15
- def self.say(*args)
16
- self.stdout.puts(*args)
17
- end
18
-
19
- attr_reader :options, :action, :resource, :client
20
-
21
- def initialize(args=ARGV)
22
- @action, @options = parse(args)
23
- end
24
-
25
- def parse(args)
26
- options = {}
27
-
28
- action, resource_name, resource_id = OptionParser.new do |opts|
29
- opts.banner = "Usage: ey-core ACTION RESOURCE RESOURCE_ID [options]"
30
-
31
- opts.separator ""
32
- opts.separator "Specific options:"
33
-
34
- opts.on("-t", "--token [TOKEN]",
35
- "Use specific token. Defaults to core url entry in ~/.ey-core yaml file") do |token|
36
- options[:token] = token
37
- end
38
-
39
- opts.on("-u", "--url [URL]",
40
- "Use specific core URL. Defaults to 'https://api.engineyard.com'") do |url|
41
- options[:url] = url
42
- end
43
-
44
- opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
45
- options[:logger] = v ? Logger.new(STDOUT) : Logger.new(nil)
46
- end
47
-
48
- opts.on("-e", "--execute-command [COMMAND]", "Execute Command") do |c|
49
- @execute_command = c
50
- end
51
-
52
- opts.separator ""
53
- opts.separator "Common options:"
54
-
55
- # No argument, shows at tail. This will print an options summary.
56
- # Try it and see!
57
- opts.on_tail("-h", "--help", "Show this message") do
58
- puts opts
59
- exit
60
- end
61
-
62
- # Another typical switch to print the version.
63
- opts.on_tail("--version", "Show version") do
64
- puts Ey::Core::VERSION
65
- exit
66
- end
67
- end.parse!(args)
68
-
69
- set_client(options)
70
-
71
- set_resource(action, resource_name, resource_id) if resource_name && resource_id
72
-
73
- [action, (options.to_hash || {})]
74
- end
75
-
76
- def run
77
- public_send(action)
78
- end
79
-
80
- def console
81
- if @execute_command
82
- @client.instance_eval(@execute_command)
83
- else
84
- Pry.config.prompt = proc { |obj, nest_level, _| "ey-core:> " }
85
- @client.pry
86
- end
87
- end
88
-
89
- def show
90
- Ey::Core::Cli.say(resource.ai)
91
- end
92
-
93
- def destroy
94
- response = resource.destroy!
95
-
96
- if response.is_a?(Ey::Core::Client::Request)
97
- response.wait_for!(response.service.timeout, response.service.poll_interval) do |request|
98
- Ey::Core::Cli.say "Waiting ... #{Time.now}"
99
- Ey::Core::Cli.say(response.ai) unless request.ready?
100
- request.ready?
101
- end
102
- end
103
-
104
- Ey::Core::Cli.say(response.ai)
105
- end
106
-
107
- def current_user
108
- @current_user ||= @client.users.current
109
- end
110
-
111
- def set_client(options)
112
- @client ||= Ey::Core::Client.new(options)
113
- end
114
-
115
- def set_resource(action, resource_name, resource_id)
116
- action = 'show' if action == 'console'
117
- action || raise(ArgumentError.new("Missing action"))
118
- Ey::Core::Client.models.find { |m,_| m.to_s == resource_name } || raise(ArgumentError.new("Unknown resource: #{resource_name}"))
119
- %w[show destroy].include?(action) || raise(ArgumentError.new("Unknown action: #{action}"))
14
+ class Ey::Core::Cli < Belafonte::App
15
+ title "Engineyard CLI"
16
+ summary "Successor to the engineyard gem"
120
17
 
121
- # TODO: irregular inflections and all that
122
- collection = "#{resource_name.to_s}s"
18
+ require_relative "cli/subcommand"
19
+ Dir[File.dirname(__FILE__) + '/cli/*.rb'].each {|file| load file }
123
20
 
124
- @resource = client.public_send(collection).get!(resource_id)
21
+ Ey::Core::Cli::Subcommand.descendants.each do |d|
22
+ mount d
125
23
  end
126
- end # class Ey::Core::Cli
24
+ end
@@ -0,0 +1,9 @@
1
+ class Ey::Core::Cli::Accounts < Ey::Core::Cli::Subcommand
2
+ title "accounts"
3
+ summary "Retrieve a list of Engine Yard accounts that you have access to."
4
+
5
+ def handle
6
+ table_data = TablePrint::Printer.new(current_accounts, [{id: {width: 36}}, :name])
7
+ puts table_data.table_print
8
+ end
9
+ end
@@ -0,0 +1,16 @@
1
+ class Ey::Core::Cli::Applications < Ey::Core::Cli::Subcommand
2
+ title "applications"
3
+ summary "Retrieve a list of Engine Yard applications that you have access to."
4
+ option :account, short: 'c', long: 'account', description: 'Filter by account name or id', argument: 'Account'
5
+
6
+ def handle
7
+ applications = if option(:account)
8
+ core_account_for(options).applications.all
9
+ else
10
+ current_accounts.map(&:applications).flatten.sort_by(&:id)
11
+ end
12
+
13
+ table_data = TablePrint::Printer.new(applications, [{id: {width: 10}}, :name])
14
+ puts table_data.table_print
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ class Ey::Core::Cli::Console < Ey::Core::Cli::Subcommand
2
+ title "console"
3
+ summary "Start an interactive console"
4
+ option :execute_command, short: "e", long: "command", description: "Command to execute", argument: "command"
5
+
6
+ def handle
7
+ if command = option(:execute_command)
8
+ core_client.instance_eval(command)
9
+ else
10
+ Pry.config.prompt = proc { |obj, nest_level, _| "ey-core:> " }
11
+ core_client.pry
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ class Ey::Core::Cli::CurrentUser < Ey::Core::Cli::Subcommand
2
+ title "current_user"
3
+ summary "Print the current user information"
4
+
5
+ def handle
6
+ ap core_client.users.current
7
+ end
8
+ end
@@ -0,0 +1,65 @@
1
+ class Ey::Core::Cli::Deploy < Ey::Core::Cli::Subcommand
2
+ title "deploy"
3
+ summary "Deploy your application"
4
+
5
+ option :environment, short: "e", long: "environment", description: "Name or id of the environment to deploy to.", argument: "Environment"
6
+ option :account, short: 'c', long: 'account', description: 'Name or ID of the account that the environment resides in. If no account is specified, the app will deploy to the first environment that meets the criteria, in the accounts you have access to.', argument: 'Account name or id'
7
+ option :ref, short: "r", long: "ref", description: "A git reference to deploy.", argument: "ref"
8
+ option :migrate, short: "m", long: "migrate", description: "The migration command to run. This option has a 50 character limit.", argument: "migrate"
9
+ option :app, short: "a", long: "app", description: "Application name or ID to deploy. If :account is not specified, this will be the first app that matches the criteria in the accounts you have access to.", argument: "app"
10
+
11
+ switch :stream, long: "stream", description: "Stream deploy output to this console."
12
+ switch :verbose, short: "v", long: "verbose", description: "Stream deploy output to this console. Alias to stream for backwards compatibility."
13
+ switch :no_migrate, long: "no-migrate", description: "Skip migration."
14
+
15
+ def handle
16
+ %w(environment app).each { |option| raise "--#{option} is required" unless options[option.to_sym] }
17
+ operator, environment = core_operator_and_environment_for(self.options)
18
+ if operator.is_a?(Ey::Core::Client::Account)
19
+ abort <<-EOF
20
+ Found account #{operator.name} but requested account #{option(:account)}.
21
+ Use the ID of the account instead of the name.
22
+ This can be retrieved by running "ey accounts".
23
+ EOF
24
+ .red unless operator.name == option(:account) || operator.id == option(:account)
25
+ end
26
+
27
+ unless environment
28
+ abort "Unable to locate environment #{option[:environment]} in #{operator.name}".red
29
+ end
30
+
31
+ unless option(:account)
32
+ self.options.merge!(environment: environment)
33
+ end
34
+
35
+ app = core_application_for(self.options)
36
+
37
+ deploy_options = {}
38
+ deploy_options.merge!(ref: option(:ref)) if option(:ref)
39
+ deploy_options.merge!(migrate_command: option(:migrate)) if option(:migrate)
40
+ deploy_options.merge!(migrate_command: '') if switch_active?(:no_migrate)
41
+ request = environment.deploy(app, deploy_options)
42
+
43
+ puts <<-EOF
44
+ Deploy started to environment: #{environment.name} with application: #{app.name}
45
+ Request ID: #{request.id}
46
+ EOF
47
+ if switch_active?(:stream) || switch_active?(:verbose)
48
+ request.subscribe { |m| print m["message"] if m.is_a?(Hash) }
49
+ puts "" # fix console output from stream
50
+ else
51
+ request.wait_for { |r| r.ready? } # dont raise from ready!
52
+ end
53
+
54
+ if request.successful
55
+ puts "Deploy successful!".green
56
+ else
57
+ abort <<-EOF
58
+ Deploy failed!
59
+ Request output:
60
+ #{request.message}
61
+ EOF
62
+ .red
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,17 @@
1
+ class Ey::Core::Cli::Environments < Ey::Core::Cli::Subcommand
2
+ title "environments"
3
+ summary "Retrieve a list of Engine Yard environments that you have access to."
4
+ option :account, short: 'c', long: 'account', description: 'Filter by account name or id', argument: 'Account'
5
+
6
+ def handle
7
+ environments = if option(:account)
8
+ core_account_for(options).environments.all
9
+ else
10
+ current_accounts.map(&:environments).flatten.sort_by(&:id)
11
+ end
12
+
13
+
14
+ table_data = TablePrint::Printer.new(environments, [{id: {width: 10}}, :name])
15
+ puts table_data.table_print
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ class Ey::Core::Cli::Errors
2
+ ::Ey::Core::Cli::RecipesNotFound = Class.new(ArgumentError)
3
+ ::Ey::Core::Cli::NoCommand = Class.new(ArgumentError)
4
+ ::Ey::Core::Cli::NoRepository = Class.new(ArgumentError)
5
+ ::Ey::Core::Cli::RecipesExist = Class.new(ArgumentError)
6
+ ::Ey::Core::Cli::AmbiguousSearch = Class.new(ArgumentError)
7
+ end
@@ -0,0 +1,11 @@
1
+ class Ey::Core::Cli::Init < Ey::Core::Cli::Subcommand
2
+ title "init"
3
+ summary "Deprecated"
4
+ description <<-DESC
5
+ The init command has been deprecated. We apologize for any inconvenience.
6
+ DESC
7
+
8
+ def handle
9
+ abort "This command is deprecated".red
10
+ end
11
+ end
@@ -0,0 +1,26 @@
1
+ class Ey::Core::Cli::Login < Ey::Core::Cli::Subcommand
2
+ title "login"
3
+ summary "Retrieve API token from Engine Yard Cloud"
4
+
5
+ def handle
6
+ email = ENV["EMAIL"] || ask("Email:")
7
+ password = ENV["PASSWORD"] || ask("Password:", echo: false)
8
+
9
+ token = unauthenticated_core_client.get_api_token(email, password).body["api_token"]
10
+
11
+ existing_token = core_yaml[core_url]
12
+ write_token = if existing_token && existing_token != token
13
+ puts "New token does not match existing token. Overwriting".yellow
14
+ true
15
+ elsif existing_token == token
16
+ puts "Token already exists".green
17
+ false
18
+ else
19
+ puts "Writing token".green
20
+ true
21
+ end
22
+ write_core_yaml(token) if write_token
23
+ rescue Ey::Core::Response::Unauthorized
24
+ abort "Invalid email or password".yellow
25
+ end
26
+ end
@@ -0,0 +1,14 @@
1
+ class Ey::Core::Cli::Logout < Ey::Core::Cli::Subcommand
2
+ title "logout"
3
+ summary "Remove your Engine Yard API token"
4
+
5
+ def handle
6
+ if core_yaml[core_url]
7
+ core_yaml.delete(core_url)
8
+ write_core_yaml
9
+ puts "Successfully removed API token from credentials file".green
10
+ else
11
+ puts "No API token found".yellow
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,40 @@
1
+ class Ey::Core::Cli::Logs < Ey::Core::Cli::Subcommand
2
+ title "logs"
3
+ summary "Retrieve the latest logs for an environment"
4
+ description <<-DESC
5
+ Displays Engine Yard configuration logs for all servers in the environment. If
6
+ recipes were uploaded to the environment & run, their logs will also be
7
+ displayed beneath the main configuration logs.
8
+ DESC
9
+
10
+ option :environment, short: "e", long: "environment", description: "Name or id of the environment to deploy to.", argument: "Environment"
11
+ option :account, short: 'c', long: 'account', description: 'Name or ID of the account that the environment resides in. If no account is specified, the app will deploy to the first environment that meets the criteria, in the accounts you have access to.', argument: 'Account name or id'
12
+ option :server, short: 's', long: 'server', description: "Only retrieve logs for the specified server", argument: "id or amazon_id"
13
+
14
+ def handle
15
+ operator, environment = core_operator_and_environment_for(options)
16
+ abort "Unable to find matching environment".red unless environment
17
+
18
+ servers = if option(:server)
19
+ [environment.servers.get(option(:server))] || environment.servers.all(provisioned_id: option(:server))
20
+ else
21
+ environment.servers.all
22
+ end
23
+
24
+ abort "No servers found".red if servers.empty?
25
+
26
+ servers.each do |server|
27
+ name = server.name ? "#{server.name} (#{server.role})" : server.role
28
+
29
+ if log = server.latest_main_log
30
+ puts "Main logs for #{name}:".green
31
+ puts log.contents
32
+ end
33
+
34
+ if log = server.latest_custom_log
35
+ puts "Custom logs for #{name}:".green
36
+ puts log.contents
37
+ end
38
+ end
39
+ end
40
+ end