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.
- checksums.yaml +4 -4
- data/Gemfile +0 -7
- data/bin/ey-core +1 -1
- data/ey-core.gemspec +15 -1
- data/lib/ey-core/cli.rb +12 -114
- data/lib/ey-core/cli/accounts.rb +9 -0
- data/lib/ey-core/cli/applications.rb +16 -0
- data/lib/ey-core/cli/console.rb +14 -0
- data/lib/ey-core/cli/current_user.rb +8 -0
- data/lib/ey-core/cli/deploy.rb +65 -0
- data/lib/ey-core/cli/environments.rb +17 -0
- data/lib/ey-core/cli/errors.rb +7 -0
- data/lib/ey-core/cli/init.rb +11 -0
- data/lib/ey-core/cli/login.rb +26 -0
- data/lib/ey-core/cli/logout.rb +14 -0
- data/lib/ey-core/cli/logs.rb +40 -0
- data/lib/ey-core/cli/recipes.rb +92 -0
- data/lib/ey-core/cli/recipes/apply.rb +34 -0
- data/lib/ey-core/cli/recipes/download.rb +26 -0
- data/lib/ey-core/cli/recipes/upload.rb +27 -0
- data/lib/ey-core/cli/scp.rb +11 -0
- data/lib/ey-core/cli/servers.rb +19 -0
- data/lib/ey-core/cli/ssh.rb +94 -0
- data/lib/ey-core/cli/status.rb +20 -0
- data/lib/ey-core/cli/subcommand.rb +114 -0
- data/lib/ey-core/cli/timeout_deploy.rb +28 -0
- data/lib/ey-core/cli/version.rb +8 -0
- data/lib/ey-core/cli/web.rb +10 -0
- data/lib/ey-core/cli/web/disable.rb +23 -0
- data/lib/ey-core/cli/web/enable.rb +23 -0
- data/lib/ey-core/cli/web/restart.rb +23 -0
- data/lib/ey-core/cli/whoami.rb +4 -0
- data/lib/ey-core/client.rb +9 -0
- data/lib/ey-core/client/mock.rb +1 -0
- data/lib/ey-core/client/real.rb +4 -4
- data/lib/ey-core/collections/deployments.rb +8 -0
- data/lib/ey-core/models/account.rb +1 -0
- data/lib/ey-core/models/deployment.rb +23 -0
- data/lib/ey-core/models/environment.rb +37 -0
- data/lib/ey-core/requests/change_environment_maintenance.rb +38 -0
- data/lib/ey-core/requests/create_environment.rb +3 -0
- data/lib/ey-core/requests/deploy_environment_application.rb +17 -0
- data/lib/ey-core/requests/get_deployment.rb +19 -0
- data/lib/ey-core/requests/get_deployments.rb +29 -0
- data/lib/ey-core/requests/get_token_by_login.rb +30 -0
- data/lib/ey-core/requests/restart_environment_app_servers.rb +38 -0
- data/lib/ey-core/requests/timeout_deployment.rb +27 -0
- data/lib/ey-core/requests/upload_recipes_for_environment.rb +28 -0
- data/lib/ey-core/version.rb +1 -1
- data/spec/deployments_spec.rb +24 -0
- data/spec/tokens_spec.rb +23 -1
- metadata +228 -8
@@ -0,0 +1,92 @@
|
|
1
|
+
class Ey::Core::Cli::Recipes < Ey::Core::Cli::Subcommand
|
2
|
+
title "recipes"
|
3
|
+
summary "Chef specific commands"
|
4
|
+
|
5
|
+
Dir[File.dirname(__FILE__) + "/recipes/*.rb"].each { |file| load file }
|
6
|
+
|
7
|
+
Ey::Core::Cli::Recipes.descendants.each do |d|
|
8
|
+
mount d
|
9
|
+
end
|
10
|
+
|
11
|
+
def run_chef(type, environment)
|
12
|
+
request = environment.apply(type)
|
13
|
+
puts "Started #{type} chef run".green
|
14
|
+
request.wait_for { |r| r.ready? }
|
15
|
+
if request.successful
|
16
|
+
puts "#{type.capitalize} chef run completed".green
|
17
|
+
else
|
18
|
+
puts "#{type.capitalize} chef run failed".red
|
19
|
+
ap request
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def gzip(tarfile)
|
24
|
+
gz = StringIO.new("")
|
25
|
+
z = Zlib::GzipWriter.new(gz)
|
26
|
+
z.write tarfile.string
|
27
|
+
z.close # this is necessary!
|
28
|
+
|
29
|
+
# z was closed to write the gzip footer, so
|
30
|
+
# now we need a new StringIO
|
31
|
+
StringIO.new gz.string
|
32
|
+
end
|
33
|
+
|
34
|
+
def archive_directory(path)
|
35
|
+
tarfile = StringIO.new("")
|
36
|
+
Gem::Package::TarWriter.new(tarfile) do |tar|
|
37
|
+
Dir[File.join(path, "**/*")].each do |file|
|
38
|
+
mode = File.stat(file).mode
|
39
|
+
relative_file = "cookbooks/#{file.sub(/^#{Regexp::escape path}\/?/, '')}"
|
40
|
+
|
41
|
+
if File.directory?(file)
|
42
|
+
tar.mkdir relative_file, mode
|
43
|
+
else
|
44
|
+
tar.add_file relative_file, mode do |tf|
|
45
|
+
File.open(file, "rb") { |f| tf.write f.read }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
tarfile.rewind
|
52
|
+
gzip(tarfile)
|
53
|
+
end
|
54
|
+
|
55
|
+
def upload_recipes(environment, path="cookbooks/")
|
56
|
+
recipes_path = Pathname.new(path)
|
57
|
+
|
58
|
+
if recipes_path.exist? && recipes_path.to_s.match(/\.(tgz|tar\.gz)/)
|
59
|
+
environment.upload_recipes(recipes_path)
|
60
|
+
elsif recipes_path.exist?
|
61
|
+
environment.upload_recipes(archive_directory(path))
|
62
|
+
else
|
63
|
+
raise Ey::Core::Cli::RecipesNotFound, "Recipes file not found: #{recipes_path}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def ungzip(tarfile)
|
68
|
+
z = Zlib::GzipReader.new(tarfile)
|
69
|
+
unzipped = StringIO.new(z.read)
|
70
|
+
z.close
|
71
|
+
unzipped
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
def untar(io, destination)
|
76
|
+
Gem::Package::TarReader.new io do |tar|
|
77
|
+
tar.each do |tarfile|
|
78
|
+
destination_file = File.join destination, tarfile.full_name
|
79
|
+
|
80
|
+
if tarfile.directory?
|
81
|
+
FileUtils.mkdir_p destination_file
|
82
|
+
else
|
83
|
+
destination_directory = File.dirname(destination_file)
|
84
|
+
FileUtils.mkdir_p destination_directory unless File.directory?(destination_directory)
|
85
|
+
File.open destination_file, "wb" do |f|
|
86
|
+
f.print tarfile.read
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class Ey::Core::Cli::Recipes::Apply < Ey::Core::Cli::Recipes
|
2
|
+
title "apply"
|
3
|
+
summary "Apply changes to an environment"
|
4
|
+
option :account, short: "c", long: "account", description: "Name or id of account", argument: "account"
|
5
|
+
option :environment, short: "e", long: "environment", description: "Name or id of environment", argument: "environment"
|
6
|
+
|
7
|
+
switch :main, short: "m", long: "main", description: "Apply main recipes only"
|
8
|
+
switch :custom, long: "custom", description: "Apply custom recipes only"
|
9
|
+
switch :quick, short: "q", long: "quick", description: "Quick chef run"
|
10
|
+
switch :full, short: "f", long: "full", description: "Run main and custom chef"
|
11
|
+
|
12
|
+
def handle
|
13
|
+
operator, environment = core_operator_and_environment_for(options)
|
14
|
+
raise "Unable to find matching environment" unless environment
|
15
|
+
|
16
|
+
run_type = if switch_active?(:main)
|
17
|
+
"main"
|
18
|
+
elsif switch_active?(:custom)
|
19
|
+
"custom"
|
20
|
+
elsif switch_active?(:quick)
|
21
|
+
"quick"
|
22
|
+
elsif switch_active?(:full)
|
23
|
+
"main"
|
24
|
+
else
|
25
|
+
"main"
|
26
|
+
end
|
27
|
+
|
28
|
+
run_chef(run_type, environment)
|
29
|
+
|
30
|
+
if switch_active?(:full)
|
31
|
+
run_chef("custom", environment)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class Ey::Core::Cli::Recipes::Download < Ey::Core::Cli::Recipes
|
2
|
+
title "download"
|
3
|
+
summary "Download a copy of the custom chef recipes from this environment into the current directory"
|
4
|
+
description <<-DESC
|
5
|
+
The recipes will be unpacked into a directory called "cookbooks" in the
|
6
|
+
current directory. This is the opposite of 'recipes upload'.
|
7
|
+
|
8
|
+
If the cookbooks directory already exists, an error will be raised.
|
9
|
+
DESC
|
10
|
+
|
11
|
+
option :environment, short: "e", long: "environment", description: "Environment that will receive the recipes.", argument: "environment"
|
12
|
+
option :account, short: "c", long: "account", description: "Name of the account in which the environment can be found.", argument: "account"
|
13
|
+
|
14
|
+
def handle
|
15
|
+
if File.exist?("cookbooks")
|
16
|
+
raise Ey::Core::Clie::RecipesExist.new("Cannot download recipes, cookbooks directory already exists.")
|
17
|
+
end
|
18
|
+
|
19
|
+
operator, environment = core_operator_and_environment_for(options)
|
20
|
+
puts "Downloading recipes".green
|
21
|
+
recipes = environment.download_recipes
|
22
|
+
|
23
|
+
puts "Extracting recipes to 'cookbooks/'".green
|
24
|
+
untar(ungzip(recipes), './')
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class Ey::Core::Cli::Recipes::Upload < Ey::Core::Cli::Recipes
|
2
|
+
title "upload"
|
3
|
+
summary "Upload custom recipes to an environment"
|
4
|
+
option :environment, short: "e", long: "environment", description: "Environment that will receive the recipes.", argument: "environment"
|
5
|
+
option :account, short: "c", long: "account", description: "Name of the account in which the environment can be found.", argument: "account"
|
6
|
+
option :file, short: "f", long: "file", description: "Path to recipes", argument: "path"
|
7
|
+
|
8
|
+
switch :apply, short: "a", long: "apply", description: "Apply the recipes immediately after they are uploaded"
|
9
|
+
|
10
|
+
def handle
|
11
|
+
operator, environment = core_operator_and_environment_for(options)
|
12
|
+
path = option(:file) || "cookbooks/"
|
13
|
+
|
14
|
+
puts "Uploading custom recipes for #{environment.name}".green
|
15
|
+
begin
|
16
|
+
upload_recipes(environment, path)
|
17
|
+
puts "Uploading custom recipes complete".green
|
18
|
+
rescue => e
|
19
|
+
abort "There was a problem uploading the recipes".red
|
20
|
+
puts e.inspect
|
21
|
+
end
|
22
|
+
|
23
|
+
if switch_active?(:apply)
|
24
|
+
run_chef("custom", environment)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class Ey::Core::Cli::Scp < Ey::Core::Cli::Subcommand
|
2
|
+
title "scp"
|
3
|
+
summary "This command is deprecated"
|
4
|
+
description <<-DESC
|
5
|
+
The scp 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,19 @@
|
|
1
|
+
class Ey::Core::Cli::Servers < Ey::Core::Cli::Subcommand
|
2
|
+
title "servers"
|
3
|
+
summary "List servers you have access to"
|
4
|
+
option :account, short: 'c', long: 'account', description: 'Filter by account name or id', argument: 'Account'
|
5
|
+
option :environment, short: "-e", long: "environment", description: "Filter by environment.", argument: "environment"
|
6
|
+
|
7
|
+
def handle
|
8
|
+
servers = if option(:account)
|
9
|
+
account = core_account_for(options)
|
10
|
+
core_client.servers.all(account: account)
|
11
|
+
elsif environment = option(:environment)
|
12
|
+
(core_client.environments.get(environment) || core_client.environments.first(name: environment)).servers.all
|
13
|
+
else
|
14
|
+
core_client.servers.all
|
15
|
+
end
|
16
|
+
|
17
|
+
puts TablePrint::Printer.new(servers, [{id: {width: 10}}, :role, :provisioned_id]).table_print
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
class Ey::Core::Cli::Ssh < Ey::Core::Cli::Subcommand
|
2
|
+
title "ssh"
|
3
|
+
summary "Open an SSH session to the environment's application master"
|
4
|
+
option :account, short: "c", long: "account", description: "Name or id of account", argument: "account"
|
5
|
+
option :environment, short: "e", long: "environment", description: "Name or id of environment", argument: "environment"
|
6
|
+
option :server, short: 's', long: "server", description: "Specific server to ssh into. Id or amazon id (i-12345)", argument: "server"
|
7
|
+
option :utilities, long: "utilities", description: "Run command on the utility servers with the given names. Specify all to run the command on all utility servers.", argument: "'all,resque,redis,etc'"
|
8
|
+
option :command, long: "command", description: "Command to run", argument: "'command with args'"
|
9
|
+
option :shell, short: 's', long: "shell", description: "Run command in a shell other than bash", argument: "shell"
|
10
|
+
option :bind_address, long: "bind_address", description: "When no command is specified, pass -L to ssh", argument: "bind address"
|
11
|
+
|
12
|
+
switch :all, long: "all", description: "Run command on all servers"
|
13
|
+
switch :app_servers, long: "app_servers", description: "Run command on all application servers"
|
14
|
+
switch :db_servers, long: "db_servers", description: "Run command on all database servers"
|
15
|
+
switch :db_master, long: "db_master", description: "Run command on database master"
|
16
|
+
switch :db_slaves, long: "db_slaves", description: "Run command on database slaves"
|
17
|
+
switch :tty, short: 't', long: "tty", description: "Allocated a tty for the command"
|
18
|
+
|
19
|
+
def handle
|
20
|
+
operator, environment = core_operator_and_environment_for(options)
|
21
|
+
abort "Unable to find matching environment".red unless environment
|
22
|
+
|
23
|
+
cmd = option(:command)
|
24
|
+
ssh_opts = []
|
25
|
+
ssh_cmd = ["ssh"]
|
26
|
+
exits = []
|
27
|
+
user = environment.username
|
28
|
+
servers = []
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
if option(:command)
|
33
|
+
if shell = option(:shell)
|
34
|
+
cmd = Escape.shell_command([shell,'-lc',cmd])
|
35
|
+
end
|
36
|
+
|
37
|
+
if switch_active?(:tty)
|
38
|
+
ssh_opts << "-t"
|
39
|
+
elsif cmd.match(/sudo/)
|
40
|
+
puts "sudo commands often need a tty to run correctly. Use -t option to spawn a tty.".yellow
|
41
|
+
end
|
42
|
+
|
43
|
+
if switch_active?(:all)
|
44
|
+
servers += environment.servers.all.to_a
|
45
|
+
end
|
46
|
+
|
47
|
+
if switch_active?(:app_servers)
|
48
|
+
servers += (environment.servers.all(role: "app_master") + environment.servers.all(role: "app") + environment.servers.all(role: "solo")).to_a
|
49
|
+
end
|
50
|
+
|
51
|
+
if switch_active?(:db_servers)
|
52
|
+
servers += (environment.servers.all(role: "db_master") + environment.servers.all(role: "db_slave") + environment.servers.all(role: "solo")).to_a
|
53
|
+
end
|
54
|
+
|
55
|
+
if switch_active?(:db_master)
|
56
|
+
servers += (environment.servers.all(role: "db_master") + environment.servers.all(role: "solo")).to_a
|
57
|
+
end
|
58
|
+
|
59
|
+
if utils = option(:utilities)
|
60
|
+
if utils == 'all'
|
61
|
+
servers += environment.servers.all(role: "util").to_a
|
62
|
+
else
|
63
|
+
servers += environment.servers.all(role: "util", name: utils).to_a
|
64
|
+
end
|
65
|
+
end
|
66
|
+
else
|
67
|
+
if option(:bind_address)
|
68
|
+
ssh_opts += ["-L", option(:bind_address)]
|
69
|
+
end
|
70
|
+
|
71
|
+
if option(:server)
|
72
|
+
servers += [core_server_for(server: option[:server], operator: environment)]
|
73
|
+
else
|
74
|
+
servers += (environment.servers.all(role: "app_master") + environment.servers.all(role: "solo")).to_a
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
if servers.empty?
|
79
|
+
abort "Unable to find any matching servers. Aborting.".red
|
80
|
+
end
|
81
|
+
|
82
|
+
servers.each do |server|
|
83
|
+
host = server.public_hostname
|
84
|
+
name = server.name ? "#{server.role} (#{server.name})" : server.role
|
85
|
+
puts "\nConnecting to #{name} #{host}".green
|
86
|
+
sshcmd = Escape.shell_command((ssh_cmd + ["#{user}@#{host}"] + [cmd]).compact)
|
87
|
+
puts "Running command: #{sshcmd}".green
|
88
|
+
system sshcmd
|
89
|
+
exits << $?.exitstatus
|
90
|
+
end
|
91
|
+
|
92
|
+
exit exits.detect {|status| status != 0 } || 0
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Ey::Core::Cli::Status < Ey::Core::Cli::Subcommand
|
2
|
+
title "status"
|
3
|
+
summary "Show the deployment status of the app"
|
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 :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"
|
8
|
+
|
9
|
+
description <<-DESC
|
10
|
+
Show the current status of the most recent deployment of the specifed application and environment
|
11
|
+
DESC
|
12
|
+
|
13
|
+
def handle
|
14
|
+
operator, environment = core_operator_and_environment_for(self.options)
|
15
|
+
app = core_application_for(self.options)
|
16
|
+
deployments = core_client.deployments.all(environment_id: environment.id, application_id: app.id)
|
17
|
+
|
18
|
+
ap deployments.first
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
class Ey::Core::Cli::Subcommand < Belafonte::App
|
2
|
+
def self.descendants
|
3
|
+
ObjectSpace.each_object(Class).select { |klass| klass < self }
|
4
|
+
end
|
5
|
+
|
6
|
+
class << self
|
7
|
+
attr_accessor :core_file
|
8
|
+
|
9
|
+
def core_file
|
10
|
+
@core_file ||= File.expand_path("~/.ey-core")
|
11
|
+
end
|
12
|
+
|
13
|
+
def eyrc
|
14
|
+
@eyrc ||= File.expand_path("~/.eyrc")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def unauthenticated_core_client
|
19
|
+
@unauthenticated_core_client ||= Ey::Core::Client.new(token: nil, url: core_url)
|
20
|
+
end
|
21
|
+
|
22
|
+
def core_client
|
23
|
+
@core_client ||= Ey::Core::Client.new(url: core_url, config_file: self.class.core_file)
|
24
|
+
rescue RuntimeError => e
|
25
|
+
if legacy_token = e.message.match(/missing token/i) && eyrc_yaml["api_token"]
|
26
|
+
puts "Found legacy .eyrc token. Migrating to core file".green
|
27
|
+
write_core_yaml(legacy_token)
|
28
|
+
retry
|
29
|
+
elsif e.message.match(/missing token/i)
|
30
|
+
abort "Missing credentials: Run 'ey login' to retrieve your Engine Yard Cloud API token.".yellow
|
31
|
+
else
|
32
|
+
raise e
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def core_url
|
37
|
+
env_url = ENV["CORE_URL"] || ENV["CLOUD_URL"]
|
38
|
+
(env_url && File.join(env_url, '/')) || "https://api.engineyard.com/"
|
39
|
+
end
|
40
|
+
|
41
|
+
def current_accounts
|
42
|
+
core_client.users.current.accounts
|
43
|
+
end
|
44
|
+
|
45
|
+
def longest_length_by_name(collection)
|
46
|
+
collection.map(&:name).group_by(&:size).max.last.length
|
47
|
+
end
|
48
|
+
|
49
|
+
def write_core_yaml(token=nil)
|
50
|
+
core_yaml[core_url] = token if token
|
51
|
+
File.open(self.class.core_file, "w") { |f| f.puts core_yaml.to_yaml }
|
52
|
+
end
|
53
|
+
|
54
|
+
def eyrc_yaml
|
55
|
+
@eyrc_yaml ||= YAML.load_file(self.class.eyrc) || {}
|
56
|
+
rescue Errno::ENOENT => e # we don't really care if this doesn't exist
|
57
|
+
{}
|
58
|
+
end
|
59
|
+
|
60
|
+
def core_yaml
|
61
|
+
@core_yaml ||= YAML.load_file(self.class.core_file) || {}
|
62
|
+
rescue Errno::ENOENT => e
|
63
|
+
puts "Creating #{self.class.core_file}".yellow
|
64
|
+
FileUtils.touch(self.class.core_file)
|
65
|
+
retry
|
66
|
+
end
|
67
|
+
|
68
|
+
def core_account_for(options={})
|
69
|
+
@core_account ||= core_client.accounts.get(options[:account])
|
70
|
+
@core_account ||= core_client.users.current.accounts.first(name: options[:account])
|
71
|
+
end
|
72
|
+
|
73
|
+
def operator(options)
|
74
|
+
options[:account] ? core_account_for(options) : core_client
|
75
|
+
end
|
76
|
+
|
77
|
+
def core_operator_and_environment_for(options={})
|
78
|
+
operator = operator(options)
|
79
|
+
environment = operator.environments.get(options[:environment]) || operator.environments.first(name: options[:environment])
|
80
|
+
[operator, environment]
|
81
|
+
end
|
82
|
+
|
83
|
+
def core_environment_for(options={})
|
84
|
+
core_client.environments.get(options[:environment]) || core_client.environments.first(name: options[:environment])
|
85
|
+
end
|
86
|
+
|
87
|
+
def core_server_for(options={})
|
88
|
+
operator = options.fetch(:operator, core_client)
|
89
|
+
operator.servers.get(options[:server]) || operator.servers.first(provisioned_id: options[:server])
|
90
|
+
end
|
91
|
+
|
92
|
+
def core_application_for(options={})
|
93
|
+
return nil unless options[:app]
|
94
|
+
|
95
|
+
app = begin
|
96
|
+
Integer(options[:app])
|
97
|
+
rescue
|
98
|
+
options[:app]
|
99
|
+
end
|
100
|
+
|
101
|
+
actor = options[:environment].is_a?(Ey::Core::Client::Environment) ? options[:environment].account : operator(options)
|
102
|
+
|
103
|
+
if app.is_a?(Integer)
|
104
|
+
actor.applications.get(app)
|
105
|
+
else
|
106
|
+
applications = actor.applications.all(name: app)
|
107
|
+
if applications.count == 1
|
108
|
+
applications.first
|
109
|
+
else
|
110
|
+
raise Ey::Core::Cli::AmbiguousSearch.new("Found multiple applications that matched that search. Please be more specific by specifying the account, environment, and application name.")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|