diff --git a/.gitignore b/.gitignore index 0108070..3958215 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ Gemfile.lock InstalledFiles _yardoc coverage +coverage-features doc/ lib/bundler/man pkg diff --git a/Rakefile b/Rakefile index 188707e..d914b69 100755 --- a/Rakefile +++ b/Rakefile @@ -1,5 +1,6 @@ #!/usr/bin/env rake require "bundler/gem_tasks" +require 'cucumber/rake/task' namespace :spec do task :mocked do @@ -12,4 +13,6 @@ end task :spec => ["spec:mocked", "spec:unmocked"] -task default: "spec:mocked" +Cucumber::Rake::Task.new + +task default: ["spec:mocked", :cucumber] diff --git a/ey-core.gemspec b/ey-core.gemspec index 9612bfa..6806ceb 100644 --- a/ey-core.gemspec +++ b/ey-core.gemspec @@ -40,4 +40,7 @@ Gem::Specification.new do |gem| gem.add_development_dependency "rspec", "~> 3.0" gem.add_development_dependency "ffaker" gem.add_development_dependency "rake" + gem.add_development_dependency "aruba", "~> 0.11" + gem.add_development_dependency "cucumber", "~> 2.1" + gem.add_development_dependency "factis", "~> 1.0" end diff --git a/features/accounts.feature b/features/accounts.feature new file mode 100644 index 0000000..5fef4a9 --- /dev/null +++ b/features/accounts.feature @@ -0,0 +1,13 @@ +Feature: Accounts + In order to know what Engine Yard accounts I can access + As a User + I want to be able to list the accounts with which I'm associated + + Background: + Given I'm an Engine Yard user + And ey-core is configured with my cloud token + And I'm associated with several accounts + + Scenario: Listing my accounts + When I run `ey-core accounts` + Then I see the name and ID of each of my accounts diff --git a/features/applications.feature b/features/applications.feature new file mode 100644 index 0000000..f06c8c0 --- /dev/null +++ b/features/applications.feature @@ -0,0 +1,29 @@ +Feature: Applications + In order to determine what I can work with + As a User + I want to be able to list the applications that live in my accounts + + Background: + Given I'm an Engine Yard user + And ey-core is configured with my cloud token + And I have the following accounts: + | Account Name | + | one | + | two | + | three | + And each of my accounts has several applications + + Scenario: Listing all of my applications + When I run `ey-core applications` + Then I see the name and ID for all of my applications + + Scenario Outline: Listing applications for a specific account + When I run `ey-core applications one` + Then I see the applications in the one account + But I do not see applications from other accounts + + Examples: + | Account Flag | + | -a | + | --account | + diff --git a/features/current_user.feature b/features/current_user.feature new file mode 100644 index 0000000..3c5da1b --- /dev/null +++ b/features/current_user.feature @@ -0,0 +1,14 @@ +Feature: Current User + In order to ensure that I'm logged into the right account + As a User + I want to be able to see my user information + + Background: + Given I'm an Engine Yard user + And ey-core is configured with my cloud token + + Scenario: Getting the current user information + When I run `ey-core current_user` + Then I should see my user ID + And I should see my email address + And I should see my name diff --git a/features/deploy.feature b/features/deploy.feature new file mode 100644 index 0000000..e69de29 diff --git a/features/environments.feature b/features/environments.feature new file mode 100644 index 0000000..2673d9a --- /dev/null +++ b/features/environments.feature @@ -0,0 +1,30 @@ +Feature: Environments + In order to know what Engine Yard environments I can access + As a User + I want to be able to list the environments with which I'm associated + + Background: + Given I'm an Engine Yard user + And ey-core is configured with my cloud token + And I have the following accounts: + | Account Name | + | one | + | two | + | three | + And each of my accounts has several applications + And each of my applications has an environment + + Scenario: Listing all of my environments + When I run `ey-core environments` + Then I see the name and ID for all of my environments + + Scenario Outline: Listing environments for a specific account + When I run `ey-core environments one` + Then I see the environments in the one account + But I do not see environments from other accounts + + Examples: + | Account Flag | + | -a | + | --account | + diff --git a/features/init.feature b/features/init.feature new file mode 100644 index 0000000..bd06da7 --- /dev/null +++ b/features/init.feature @@ -0,0 +1,6 @@ +Feature: Init + This command is deprecated + + Scenario: Running init + When I run `ey-core init` + Then I am advised that this command has been deprecated diff --git a/features/login.feature b/features/login.feature new file mode 100644 index 0000000..c84a940 --- /dev/null +++ b/features/login.feature @@ -0,0 +1,6 @@ +Feature: Login + In order to interact with Engine Yard Cloud + As a User + I want to be able to log into the Cloud API + + diff --git a/features/logout.feature b/features/logout.feature new file mode 100644 index 0000000..e69de29 diff --git a/features/logs.feature b/features/logs.feature new file mode 100644 index 0000000..e69de29 diff --git a/features/recipes/apply.feature b/features/recipes/apply.feature new file mode 100644 index 0000000..97a7137 --- /dev/null +++ b/features/recipes/apply.feature @@ -0,0 +1,98 @@ +Feature: Recipes Apply + In order to keep my server configs up to date + As a User + I want to be able to apply config changes + + Background: + Given I'm an Engine Yard user + And ey-core is configured with my cloud token + And I have an account named ACME Inc + And ACME Inc has the following environments: + | Environment | + | coyote | + | roadrunner | + + Scenario: Applying changes to an environment (default behavior) + When I run `ey-core recipes apply coyote` + Then main recipes are applied to the coyote environment + But no changes are made to the roadrunner environment + + Scenario Outline: Applying main recipes + When I run `ey-core recipes apply
coyote` + Then main recipes are applied to the coyote environment + But no changes are made to the roadrunner environment + + Examples: + | Main Flag | + | -m | + | --main | + + Scenario Outline: Applying custom recipes + When I run `ey-core recipes apply coyote` + Then custom recipes are applied to the coyote environment + But no changes are made to the roadrunner environment + + Examples: + | Custom Flag | + | -c | + | --custom | + + Scenario Outline: Performing a quick chef run + When I run `ey-core recipes apply coyote` + Then a quick run is applied to the coyote environment + But no changes are made to the roadrunner environment + + Examples: + | Quick Flag | + | -q | + | --quick | + + Scenario Outline: Performing a full chef run + When I run `ey-core recipes apply coyote` + Then main recipes are applied to the coyote environment + And custom recipes are applied to the coyote environment + But no changes are made to the roadrunner environment + + Examples: + | Full Flag | + | -f | + | --full | + + Scenario Outline: Attempting to use more than one run type flag + When I run `ey-core recipes apply coyote` + Then I'm advised that only one run type flag may be used + And no changes are made to any environment + + Examples: + | Run Type Flags | + | -m -c -q -f | + | -m -c -q | + | -m -c -f | + | -m -q -f | + | -c -q -f | + | -m -c | + | -m -q | + | -m -f | + | -c -q | + | -c -f | + | -q -f | + + Scenario Outline: Applying changes to an environment on a specific account + Given I have an account named Wile E Enterprises + And Wile E Enterprises has the following environments: + | Environment | + | coyote | + | roadrunner | + + When I run `ey-core recipes apply coyote` + Then I am advised that my criteria matched several environments + And no changes are made to any environment + + When I run `ey-core recipes apply 'ACME Inc' coyote` + Then the main recipes are applied to the coyote environment for ACME Inc + But no changes are made to any other environment + + Examples: + | Account Flag | + | -a | + | --account | diff --git a/features/recipes/download.feature b/features/recipes/download.feature new file mode 100644 index 0000000..e69de29 diff --git a/features/recipes/step_definitions/apply_steps.rb b/features/recipes/step_definitions/apply_steps.rb new file mode 100644 index 0000000..d3febdd --- /dev/null +++ b/features/recipes/step_definitions/apply_steps.rb @@ -0,0 +1,70 @@ +Given %r(^I have an account named (.+)$) do |account_name| + known_accounts.push( + create_account( + client: client, + owner: current_user, + account: { + name: account_name + } + ) + ) +end + +Given %r(^(.+) has an application called (.+)$) do |account_name, app_name| + known_apps.push( + create_application( + account: account_named(account_name), + name: app_name + ) + ) +end + +Given %r(^(.+) has the following environments:$) do |account_name, environment_names| + + account = account_named(account_name) + app = account.applications.first + environment_names.hashes.each do |environment_hash| + known_environments.push( + create_environment( + account: account, + application: app, + environment: { + name: environment_hash['Environment'] + } + ) + ) + end +end + +Then %r(^(.+) recipes are applied to the coyote environment$) do |run_type| + expect(output_text).to match(/.*Started #{Regexp.escape(run_type)} chef run.*environment: coyote\).*/) +end + +Then %(no changes are made to the roadrunner environment) do + expect(output_text).not_to match(/.*chef run.*environment: roadrunner\).*/) +end + +Then %(a quick run is applied to the coyote environment) do + step %{quick recipes are applied to the coyote environment} +end + +Then %(I'm advised that only one run type flag may be used) do + expect(output_text).to match("Only one of --main, --custom, --quick, and --full may be specified.") +end + +Then %(no changes are made to any environment) do + expect(output_text).not_to match(/Started .* chef run.*\(account: .*/) +end + +Then %(I am advised that my criteria matched several environments) do + expect(output_text).to match(%{The criteria you've provided matches multiple environments. Please refine further with an account.}) +end + +Then %(the main recipes are applied to the coyote environment for ACME Inc) do + expect(output_text).to match(/Started main chef run.*\(account: ACME Inc, environment: coyote\)/) +end + +Then %(no changes are made to any other environment) do + runs = output_text.split("\n").select {|line| line =~ /Started .* chef run.*\(/} + expect(runs.count).to eql(1) +end diff --git a/features/recipes/upload.feature b/features/recipes/upload.feature new file mode 100644 index 0000000..e69de29 diff --git a/features/scp.feature b/features/scp.feature new file mode 100644 index 0000000..c358b37 --- /dev/null +++ b/features/scp.feature @@ -0,0 +1,6 @@ +Feature: Scp + This command is deprecated + + Scenario: Running init + When I run `ey-core init` + Then I am advised that this command has been deprecated diff --git a/features/servers.feature b/features/servers.feature new file mode 100644 index 0000000..bcf9118 --- /dev/null +++ b/features/servers.feature @@ -0,0 +1,56 @@ +Feature: Servers + In order to determine what servers I can access + As a User + I want to see a list of servers with which I'm associated + + Background: + Given I'm an Engine Yard user + And ey-core is configured with my cloud token + And I have the following accounts: + | Account Name | + | one | + | two | + | three | + And each of my accounts has several applications + And each of my applications has an environment + And each of my environments has a server + + Scenario: Listing all of my servers + When I run `ey-core servers` + Then I see the name, role, and provisioned ID for all of my servers + + Scenario Outline: Listing servers for a specific account + When I run `ey-core servers one` + Then I see the servers in the one account + But I do not see servers from other accounts + + Examples: + | Account Flag | + | -a | + | --account | + + Scenario Outline: Listing severs for a specific environment + When I run `ey-core servers one_1_env` + Then I see the servers in the one_1_env environment + But I do not see servers from other environments + + Examples: + | Environment Flag | + | -e | + | --environment | + + Scenario: Ambiguous environments + Given the two account has an environment named one_1_env with a server + When I run `ey-core servers -e one_1_env` + Then I do not see any servers + But I am advised that my filters yielded ambiguous results + + Scenario: Listing servers for a specific account and environment + When I run `ey-core servers -a one -e one_1_env` + Then I see the servers from the one_1_env environment in the one account + But I do not see any other servers + + Scenario: Account and environment filters down to no results + When I run `ey-core servers -a one -e two_1_env` + Then I do not see any servers + But I am advised that my filters matched no servers diff --git a/features/ssh.feature b/features/ssh.feature new file mode 100644 index 0000000..e69de29 diff --git a/features/status.feature b/features/status.feature new file mode 100644 index 0000000..ea7e716 --- /dev/null +++ b/features/status.feature @@ -0,0 +1,25 @@ +Feature: Status + In order to know if my app is healthy + As a User + I want to see the details of the app deployments + + Background: + Given I'm an Engine Yard user + And ey-core is configured with my cloud token + And I have an account + And my account has an application named super_app + And my application is associated with an environment named super_env + + Scenario: Getting the status with no deployments + When I run `ey-core status super_env super_app` + Then I see a message regarding my lack of deployments + + Scenario: Getting the status with one deployment + Given I've deployed the app + When I run `ey-core status super_env super_app` + Then I see the details for the deployment + + Scenario: Getting the status with more than one deployment + Given I've deployed the app twice + When I run `ey-core status super_env super_app` + Then I see the details for the most recent deployment diff --git a/features/step_definitions/accounts_steps.rb b/features/step_definitions/accounts_steps.rb new file mode 100644 index 0000000..9cbf0dd --- /dev/null +++ b/features/step_definitions/accounts_steps.rb @@ -0,0 +1,23 @@ +Given %(I'm an Engine Yard user) do + memorize_fact(:me, create_user(client: client)) + true +end + +Given %(ey-core is configured with my cloud token) do + add_config_option( + 'https://api.engineyard.com/' => current_user_hash['token'] + ) +end + +Given %(I'm associated with several accounts) do + account1 = create_account(client: client, owner: current_user) + account2 = create_account(client: client, owner: current_user) + memorize_fact(:accounts, [account1, account2]) +end + +Then %(I see the name and ID of each of my accounts) do + recall_fact(:accounts).each do |account| + expect(output_text).to include(account.id) + expect(output_text).to include(account.name) + end +end diff --git a/features/step_definitions/applications_steps.rb b/features/step_definitions/applications_steps.rb new file mode 100644 index 0000000..e5e51f9 --- /dev/null +++ b/features/step_definitions/applications_steps.rb @@ -0,0 +1,46 @@ +Given %r(^I have the following accounts:$) do |account_names| + account_names.hashes.each do |account_hash| + known_accounts.push( + create_account( + client: client, + owner: current_user, + account: { + name: account_hash['Account Name'] + } + ) + ) + end +end + +Given %(each of my accounts has several applications) do + known_accounts.each do |account| + known_apps.push( + create_application(account: account, name: "#{account.name}_1") + ) + + known_apps.push( + create_application(account: account, name: "#{account.name}_2") + ) + end +end + +Then %(I see the name and ID for all of my applications) do + known_apps.each do |app| + expect(output_text).to match(/#{Regexp.escape(app.id.to_s)}\s+\|\s+#{Regexp.escape(app.name)}/) + end +end + +Then %(I see the applications in the one account) do + account_named('one').applications.all.each do |app| + expect(output_text).to match(/#{Regexp.escape(app.id.to_s)}\s+\|\s+#{Regexp.escape(app.name)}/) + end +end + +Then %(I do not see applications from other accounts) do + two = account_named('two').applications.all.to_a + three = account_named('three').applications.all.to_a + + (two + three).each do |app| + expect(output_text).not_to match(/#{Regexp.escape(app.id.to_s)}\s+\|\s+#{Regexp.escape(app.name)}/) + end +end diff --git a/features/step_definitions/current_user_steps.rb b/features/step_definitions/current_user_steps.rb new file mode 100644 index 0000000..e71897b --- /dev/null +++ b/features/step_definitions/current_user_steps.rb @@ -0,0 +1,11 @@ +Then %(I should see my user ID) do + expect(output_text).to match(/#{Regexp.escape(current_user.id)}/) +end + +Then %(I should see my email address) do + expect(output_text).to match(/#{Regexp.escape(current_user.email)}/) +end + +Then %(I should see my name) do + expect(output_text).to match(/#{Regexp.escape(current_user.name)}/) +end diff --git a/features/step_definitions/deprecated_command_steps.rb b/features/step_definitions/deprecated_command_steps.rb new file mode 100644 index 0000000..20e53a1 --- /dev/null +++ b/features/step_definitions/deprecated_command_steps.rb @@ -0,0 +1,3 @@ +Then %(I am advised that this command has been deprecated) do + expect(output_text).to match(/.*This command is deprecated.*/) +end diff --git a/features/step_definitions/environments_steps.rb b/features/step_definitions/environments_steps.rb new file mode 100644 index 0000000..16d5b65 --- /dev/null +++ b/features/step_definitions/environments_steps.rb @@ -0,0 +1,37 @@ +Given %(each of my applications has an environment) do + known_accounts.each do |account| + account.applications.each do |app| + known_environments.push( + create_environment( + account: account, + application: app, + environment: { + name: "#{app.name}_env" + } + ) + ) + end + end +end + +Then %(I see the name and ID for all of my environments) do + known_environments.each do |environment| + expect(output_text).to match(/#{Regexp.escape(environment.id.to_s)}\s+\|\s+#{Regexp.escape(environment.name)}/) + end +end + +Then %(I see the environments in the one account) do + account_named('one').environments.all.each do |environment| + expect(output_text).to match(/#{Regexp.escape(environment.id.to_s)}\s+\|\s+#{Regexp.escape(environment.name)}/) + end +end + +Then %(I do not see environments from other accounts) do + two = account_named('two').environments.all.to_a + three = account_named('three').environments.all.to_a + + (two + three).each do |environment| + expect(output_text).not_to match(/#{Regexp.escape(environment.id.to_s)}\s+\|\s+#{Regexp.escape(environment.name)}/) + end + +end diff --git a/features/step_definitions/servers_steps.rb b/features/step_definitions/servers_steps.rb new file mode 100644 index 0000000..132635d --- /dev/null +++ b/features/step_definitions/servers_steps.rb @@ -0,0 +1,93 @@ +Given %(each of my environments has a server) do + # Each environment implicitly gets a solo server, so this is just setting + # up our known_servers collection + known_environments.each do |environment| + environment.servers.all.each do |server| + known_servers.push(server) + end + end +end + +Then %(I see the name, role, and provisioned ID for all of my servers) do + known_servers.each do |server| + expect(output_text).to match(/#{Regexp.escape(server.id.to_s)}\s+\|\s+#{Regexp.escape(server.role)}\s+\|\s+#{Regexp.escape(server.provisioned_id)}/) + end +end + +Then %(I see the servers in the one account) do + account_named('one').environments.all.each do |environment| + client.servers.all.to_a.select {|s| s.environment == environment}.each do |server| + expect(server).not_to be_nil + expect(output_text).to match(/#{Regexp.escape(server.id.to_s)}\s+\|\s+#{Regexp.escape(server.role)}\s+\|\s+#{Regexp.escape(server.provisioned_id)}/) + seen_servers.push(server) + end + end +end + +Then %(I do not see servers from other accounts) do + step %{I do not see any other servers} +end + +Then %(I see the servers in the one_1_env environment) do + environment_named('one_1_env').servers.all.to_a.each do |server| + expect(server).not_to be_nil + expect(output_text).to match(/#{Regexp.escape(server.id.to_s)}\s+\|\s+#{Regexp.escape(server.role)}\s+\|\s+#{Regexp.escape(server.provisioned_id)}/) + seen_servers.push(server) + end +end + +Then %(I do not see servers from other environments) do + step %{I do not see any other servers} +end + +Given %(the two account has an environment named one_1_env with a server) do + account_named('two').tap do |account| + account.applications.all.to_a.last.tap do |app| + known_environments.push( + create_environment( + account: account, + application: app, + environment: { + name: 'one_1_env' + } + ) + ) + end + end + + known_servers.push( + known_environments.last.servers.first + ) +end + +Then %(I do not see any servers) do + known_servers.each do |server| + expect(output_text).not_to match(/#{Regexp.escape(server.id.to_s)}\s+\|\s+#{Regexp.escape(server.role)}\s+\|\s+#{Regexp.escape(server.provisioned_id)}/) + end +end + +Then %(I am advised that my filters yielded ambiguous results) do + expect(output_text).to include("The criteria you've provided matches multiple environments. Please refine further with an account.") +end + +Then %(I see the servers from the one_1_env environment in the one account) do + account = account_named('one') + environment = account.environments.first(name: 'one_1_env') + + environment.servers.all.to_a.each do |server| + expect(server).not_to be_nil + expect(output_text).to match(/#{Regexp.escape(server.id.to_s)}\s+\|\s+#{Regexp.escape(server.role)}\s+\|\s+#{Regexp.escape(server.provisioned_id)}/) + seen_servers.push(server) + end +end + +Then %(I do not see any other servers) do + (known_servers - seen_servers).each do |server| + expect(output_text).not_to match(/#{Regexp.escape(server.id.to_s)}\s+\|\s+#{Regexp.escape(server.role)}\s+\|\s+#{Regexp.escape(server.provisioned_id)}/) + end +end + +Then %(I am advised that my filters matched no servers) do + expect(output_text). + to include('No servers were found that match your criteria.') +end diff --git a/features/step_definitions/status_steps.rb b/features/step_definitions/status_steps.rb new file mode 100644 index 0000000..cd1e3e0 --- /dev/null +++ b/features/step_definitions/status_steps.rb @@ -0,0 +1,99 @@ +def account + client.accounts.get(recall_fact(:account_id)) +end + +Given %(I have an account) do + memorize_fact(:account_id, create_account(client: client).id) +end + +def app + account.applications.get(recall_fact(:app_id)) +end + +Given %(my account has an application named super_app) do + memorize_fact( + :app_id, + create_application( + account: account, + name: 'super_app' + ).id + ) +end + +def environment + account.environments.get(recall_fact(:environment_id)) +end + +Given %(my application is associated with an environment named super_env) do + memorize_fact(:environment_id, create_environment(account: account, app: app, environment: {name: 'super_env'}).id) +end + +Then %(I see a message regarding my lack of deployments) do + expect(output_text).to include(%{We couldn't find a deployment matching the criteria you provided.}) +end + +def deployment + environment.deployments.get(recall_fact(:deployment_id)) +end + +def unweird_the_deployments + # TODO: Figure out why this is necessary :/ + app_deployment = client.data[:application_deployments].keys.sort.last + client.data[:application_deployments][app_deployment] = { + application_id: app.id, + environment_id: environment.id + } +end + +Given %(I've deployed the app) do + unweird_the_deployments + + # This doesn't actually emit anything, though it works in the specs + environment.deploy(app, ref: 'HEAD').resource! + memorize_fact( + :deployment_id, + environment.deployments.first.id + ) +end + +Then %(I see the details for the deployment) do + expect(output_text). + to match(/^.*:id.*=>.*#{Regexp.escape(deployment.id.to_s)}.*$/) +end + +def deployment_1 + environment.deployments.get(recall_fact(:deployment_1_id)) +end + +def deployment_2 + environment.deployments.get(recall_fact(:deployment_2_id)) +end + + +Given %(I've deployed the app twice) do + unweird_the_deployments + + environment.deploy(app, ref: 'HEAD').resource! + + memorize_fact( + :deployment_1_id, + environment.deployments.first.id + ) + + environment.deploy(app, ref: 'HEAD').resource! + + memorize_fact( + :deployment_2_id, + environment.deployments.sort {|a,b| a.id <=> b.id}.last.id + ) +end + +Then %(I see the details for the most recent deployment) do + expect(output_text). + to match(/^.*:id.*=>.*#{Regexp.escape(deployment_2.id.to_s)}.*$/) +end + +Then %(I do not see the details for older deployments) do + expect(output_text). + not_to match(/^.*:id.*=>.*#{Regexp.escape(deployment_1.id.to_s)}.*$/) +end diff --git a/features/step_definitions/version_steps.rb b/features/step_definitions/version_steps.rb new file mode 100644 index 0000000..b42a145 --- /dev/null +++ b/features/step_definitions/version_steps.rb @@ -0,0 +1,3 @@ +Then %(I see the current ey-core version) do + expect(output_text).to include(Ey::Core::VERSION) +end diff --git a/features/support/account_helpers.rb b/features/support/account_helpers.rb new file mode 100644 index 0000000..8c372ce --- /dev/null +++ b/features/support/account_helpers.rb @@ -0,0 +1,89 @@ +module AccountHelpers + def account_named(name) + client.accounts.first(name: name) + end + + def known_accounts + begin + recall_fact(:known_accounts) + rescue + memorize_fact(:known_accounts, []) + end + end + + def first_account + known_accounts.first.reload + end + + def last_account + known_accounts.last.reload + end + + def create_account(options={}) + creator = options[:creator] || create_client + client = options[:client] + + attributes = options[:account] || {} + attributes[:type] ||= "beta" # get around awsm billing requirements for tests + attributes[:name] ||= SecureRandom.hex(6) + + if client + attributes[:owner] ||= begin + client.users.current + rescue Ey::Core::Response::NotFound + end + end + attributes[:owner] ||= create_user(client: client) + + created_account = (client || creator).accounts.create!(attributes) + + if client + client.accounts.get!(created_account.identity) + else + created_account + end + end + + def create_user(options={}) + creator = options[:creator] || create_client + client = options[:client] + + attributes = options[:user] || {} + attributes[:name] ||= Faker::Name.name + attributes[:email] ||= Faker::Internet.email + + created_user = creator.users.create!(attributes) + + if client + client.users.get!(created_user.identity) + else + created_user + end + end + + def create_provider(options={}) + account = options[:account] || create_account(options) + + attributes = options[:provider] || {} + attributes[:type] ||= :aws + attributes[:provisioned_id] ||= SecureRandom.hex(8) + attributes[:credentials] ||= case attributes[:type] + when :aws then + { + :instance_aws_secret_id => SecureRandom.hex(6), + :instance_aws_secret_key => SecureRandom.hex(6), + :aws_secret_id => SecureRandom.hex(6), + :aws_secret_key => SecureRandom.hex(6), + :aws_login => Faker::Internet.email, + :aws_pass => SecureRandom.hex(6), + } + when :azure then + { + } + end + + account.providers.create!(attributes).resource! + end +end + +World(AccountHelpers) diff --git a/features/support/app_helpers.rb b/features/support/app_helpers.rb new file mode 100644 index 0000000..99821e2 --- /dev/null +++ b/features/support/app_helpers.rb @@ -0,0 +1,19 @@ +module AppHelpers + def known_apps + begin + recall_fact(:known_apps) + rescue + memorize_fact(:known_apps, []) + end + end + + def first_app + known_apps.first.reload + end + + def last_app + known_apps.last.reload + end +end + +World(AppHelpers) diff --git a/features/support/aruba.rb b/features/support/aruba.rb new file mode 100644 index 0000000..fb0a661 --- /dev/null +++ b/features/support/aruba.rb @@ -0,0 +1 @@ +require 'aruba/cucumber' diff --git a/features/support/boilerplate.rb b/features/support/boilerplate.rb new file mode 100644 index 0000000..8137cc3 --- /dev/null +++ b/features/support/boilerplate.rb @@ -0,0 +1 @@ +require 'faker' diff --git a/features/support/client_helpers.rb b/features/support/client_helpers.rb new file mode 100644 index 0000000..16fd1a1 --- /dev/null +++ b/features/support/client_helpers.rb @@ -0,0 +1,36 @@ +module ClientHelpers + def create_client(attributes={}) + token = if (user = attributes.delete(:user)) && Ey::Core::Client.mocking? + core = Ey::Core::Client::Mock.data.values.find { |c| c[:users][user.identity] } + core[:users][user.identity]["token"] + end + token ||= begin + token_dotfile = YAML.load_file(File.expand_path("/../../../.token"), __FILE__) rescue {} + ENV["CORE_TOKEN"] || token_dotfile[ENV["CORE_URL"]] || "a4bf6558da8c1051536d1596b8931ebd346aff0b" + end + + merged_attributes = attributes.merge(token: token, cache: true) + merged_attributes.merge!(logger: Logger.new(STDOUT)) if ENV['VERBOSE'] + + Ey::Core::Client.new(merged_attributes) + end + + def create_server_client(server, attributes={}) + unless core = Ey::Core::Client::Mock.data.values.find { |data| data[:servers][server.identity] } + raise "Failed to find server in mock data: #{server}" + end + + token = core[:servers][server.identity]["token"] + + merged_attributes = attributes.merge(token: token, cache: true) + merged_attributes.merge!(logger: Logger.new(STDOUT)) if ENV['VERBOSE'] + + Ey::Core::Client.new(merged_attributes) + end + + def create_unauthenticated_client + Ey::Core::Client.new(token: nil) + end +end + +World(ClientHelpers) diff --git a/features/support/config_file_helpers.rb b/features/support/config_file_helpers.rb new file mode 100644 index 0000000..dfe5177 --- /dev/null +++ b/features/support/config_file_helpers.rb @@ -0,0 +1,42 @@ +require 'yaml' + +module ConfigFileHelpers + def config_file_location + File.expand_path(File.join(aruba.current_directory, '.ey-core')) + end + + def write_config_file(hash = {}) + c = File.open(config_file_location, 'w') + c.write(hash.to_yaml) + c.close + end + + def read_config_file + begin + YAML.load_file(File.read(config_file_location)) + rescue + {} + end + end + + def nuke_config_file + FileUtils.rm_f(config_file_location) if File.exist?(config_file_location) + end + + def add_config_option(hash = {}) + current = read_config_file + write_config_file(current.merge(hash)) + end + + def remove_config_option(key) + current = read_config_file + current.delete(key) + write_config_file(current) + end +end + +World(ConfigFileHelpers) + +After do + nuke_config_file +end diff --git a/features/support/core.rb b/features/support/core.rb new file mode 100644 index 0000000..6512540 --- /dev/null +++ b/features/support/core.rb @@ -0,0 +1,19 @@ +module CoreHelpers + def client + begin + recall_fact(:client) + rescue + memorize_fact(:client, create_client) + end + end + + def current_user + client.users.current + end + + def current_user_hash + client.current_user + end +end + +World(CoreHelpers) diff --git a/features/support/deployment_helpers.rb b/features/support/deployment_helpers.rb new file mode 100644 index 0000000..e9575a4 --- /dev/null +++ b/features/support/deployment_helpers.rb @@ -0,0 +1,19 @@ +module DeploymentHelpers + def known_deployments + begin + recall_fact(:known_deployments) + rescue + memorize_fact(:known_deployments, []) + end + end + + def first_deployment + known_deployments.first.reload + end + + def last_deployment + known_deployments.last.reload + end +end + +World(DeploymentHelpers) diff --git a/features/support/env.rb b/features/support/env.rb new file mode 100644 index 0000000..5f1e9b8 --- /dev/null +++ b/features/support/env.rb @@ -0,0 +1,40 @@ +require 'simplecov' +SimpleCov.coverage_dir 'coverage-features' +SimpleCov.minimum_coverage 95 +SimpleCov.start do + add_filter '/spec/' + add_filter '/features/' + add_filter '/mock/' + add_group 'Libraries', 'lib' + add_group 'CLI', 'lib/ey-core/cli/' + add_group 'CLI Helpers', 'lib/ey-core/cli/helpers' +end + +require 'aruba/cucumber' +require 'factis/cucumber' +require 'ey-core' +require 'ey-core/version' +require 'ey-core/cli/main' + +EXE_DIR = File.expand_path(File.join(File.dirname(__FILE__), '/../../exe')) +LIB_DIR = File.join(File.expand_path(File.dirname(__FILE__)),'..','..','lib') + +Aruba.configure do |config| + config.command_search_paths = config.command_search_paths << EXE_DIR + config.home_directory = File.join(config.root_directory, config.working_directory) + config.command_launcher = :in_process + config.main_class = Ey::Core::Cli::Main +end + +Before do + # Using "announce" causes massive warnings on 1.9.2 + @puts = true + @original_rubylib = ENV['RUBYLIB'] + ENV['RUBYLIB'] = LIB_DIR + File::PATH_SEPARATOR + ENV['RUBYLIB'].to_s + #ENV['CORE_URL'] ||= "http://api-development.localdev.engineyard.com:9292" +end + +After do + ENV['RUBYLIB'] = @original_rubylib + #ENV.delete('CORE_URL') +end diff --git a/features/support/environment_helpers.rb b/features/support/environment_helpers.rb new file mode 100644 index 0000000..738745a --- /dev/null +++ b/features/support/environment_helpers.rb @@ -0,0 +1,23 @@ +module EnvironmentHelpers + def environment_named(name) + client.environments.first(name: name) + end + + def known_environments + begin + recall_fact(:known_environments) + rescue + memorize_fact(:known_environments, []) + end + end + + def first_environment + known_environments.first.reload + end + + def last_environment + known_environments.last.reload + end +end + +World(EnvironmentHelpers) diff --git a/features/support/fake_kernel.rb b/features/support/fake_kernel.rb new file mode 100644 index 0000000..4d4367d --- /dev/null +++ b/features/support/fake_kernel.rb @@ -0,0 +1,23 @@ +require 'aruba/processes/in_process' + +module Aruba + module Processes + class InProcess < BasicProcess + attr_reader :kernel + + class FakeKernel + def system(*args) + system_commands.push(args.join(' ')) + end + + def system_commands + @system_commands ||= [] + end + + def abort(msg) + exit(false) + end + end + end + end +end diff --git a/features/support/io.rb b/features/support/io.rb new file mode 100644 index 0000000..6e69c26 --- /dev/null +++ b/features/support/io.rb @@ -0,0 +1,5 @@ +After do + $stdout = STDOUT + $stdin = STDIN + $stderr = STDERR +end diff --git a/features/support/mock_api.rb b/features/support/mock_api.rb new file mode 100644 index 0000000..2b414cd --- /dev/null +++ b/features/support/mock_api.rb @@ -0,0 +1,21 @@ +require 'cucumber/rspec/doubles' + +# Set up the mocks. Ookla would approve. +Cistern.formatter = Cistern::Formatter::AwesomePrint +Ey::Core::Client.mock! +Ey::Core::Client::Mock.timeout = 0.1 +Ey::Core::Client::Mock.poll_interval = 0 +Ey::Core::Client::Real.timeout = 3 +Ey::Core::Client::Real.poll_interval = 0 + +Before do + # Reset the mocked API before every scenario + Ey::Core::Client::Mock.reset! + + # Stub out `#core_client` on all subcommands to ensure that they're using + # the mocked client. Otherwise, everything turns to calamity, because the + # mocked API is silly. + allow_any_instance_of(Ey::Core::Cli::Subcommand). + to receive(:core_client). + and_return(client) +end diff --git a/features/support/output_helpers.rb b/features/support/output_helpers.rb new file mode 100644 index 0000000..6a36f71 --- /dev/null +++ b/features/support/output_helpers.rb @@ -0,0 +1,7 @@ +module OutputHelpers + def output_text + last_command_started.output + end +end + +World(OutputHelpers) diff --git a/features/support/resource_helpers.rb b/features/support/resource_helpers.rb new file mode 100644 index 0000000..75aef53 --- /dev/null +++ b/features/support/resource_helpers.rb @@ -0,0 +1,189 @@ +module ResourceHelpers + def load_blueprint(options={}) + application = create_application(account: account) + database_service = create_database_service(provider: account.providers.first) + environment = create_environment(account: account, application: application, database_service: database_service, environment: {name: "environment#{SecureRandom.hex(4)}"}) + + [database_service, environment] + end + + def create_application(options={}) + account = options.delete(:account) || create_account(options) + options = Cistern::Hash.stringify_keys(options) + + options["name"] ||= "application#{SecureRandom.hex(4)}" + options["repository"] ||= "git://github.com/engineyard/todo.git" + options["type"] ||= "rails4" + + account.applications.create!(options) + end + + def create_server(client, options={}) + options = Cistern::Hash.stringify_keys(options) + + request = environment.servers.create( + "flavor" => "m3.medium", + "role" => "util", + ) + + request.resource! + end + + def create_cost(client, options={}) + account = options[:account] || create_account(client: client) + level = options[:level] || "summarized" + finality = options[:finality] || "estimated" + related_resource_type = options[:related_resource_type] || "account" + category = options[:category] || "non-server" + description = options[:description] || "AWS Other Services" + value = options[:value] || "1763" + environment = options[:environment] || nil + + client.data[:costs] << { + billing_month: "2015-07", + data_type: "cost", + level: level, + finality: finality, + related_resource_type: related_resource_type, + category: category, + units: "USD cents", + description: description, + value: value, + account: client.url_for("accounts/#{account.identity}"), + environment: environment + } + end + + def create_account_referral(client, options={}) + referred = options.delete(:referred) || create_account(client: client) + referrer = options.delete(:referrer) || create_account(client: client) + + account_referral_id = SecureRandom.uuid + referral = client.data[:account_referrals][account_referral_id] = { + "id" => account_referral_id, + "referrer" => client.url_for("accounts/#{referrer.identity}"), + "referred" => client.url_for("accounts/#{referred.identity}"), + } + + client.account_referrals.new(referral) + end + + def create_firewall(client, options={}) + provider = options.fetch(:provider) { create_provider(client: client) } + + firewall_params = options[:firewall] || {} + name = firewall_params.delete(:name) || SecureRandom.hex(6) + location = firewall_params.delete(:location) || "us-west-2" + + client.firewalls.create!( + :name => name, + :location => location, + :provider => provider, + ).resource! + end + + def create_database_service(options={}) + provider = options[:provider] || create_provider(options.merge(client: client)) + + database_service_params = Hashie::Mash.new( + :name => Faker::Name.first_name, + :provider => provider, + ).merge(options.fetch(:database_service, {})) + + database_server_params = Hashie::Mash.new( + :location => "us-west-2c", + :flavor => "db.m3.large", + :engine => "postgres", + :version => "9.3.5", + ).merge(options.fetch(:database_server, {})) + + client.database_services.create!(database_service_params.merge(database_server: database_server_params)).resource! + end + + def create_environment(options={}) + account = options[:account] || create_account(options) + + unless account.providers.first || options[:provider] + create_provider(account: account) + end + + environment = options[:environment] || {} + application = options[:application] || create_application(account: account) + database_service = options[:database_service] + configuration = Cistern::Hash.stringify_keys(options[:configuration] || {}) + configuration["type"] = "production-cluster" if configuration["type"] == "production" + configuration["type"] ||= "solo" + environment[:name] ||= options.fetch(:name, SecureRandom.hex(3)) + environment[:region] ||= "us-west-2" + + environment.merge!(application_id: application.id, account: account) + environment.merge!(database_service: database_service) if database_service + environment = client.environments.create!(environment) + + unless options[:boot] == false + request = environment.boot(configuration: configuration, application_id: application.id) + request.ready! + end + environment + end + + def create_provider_location(client, attributes={}) + attributes = Cistern::Hash.stringify_keys(attributes) + + if provider = attributes.delete("provider") + attributes["provider"] = client.url_for("/providers/#{provider.id}") + end + + attributes["id"] ||= client.uuid + client.data[:provider_locations][attributes["id"]] = attributes + + client.provider_locations.new(attributes) + end + + def create_server_event(client, attributes={}) + attributes = Cistern::Hash.stringify_keys(attributes) + + attributes.fetch("type") + + if server = attributes.delete("server") + attributes["server"] = client.url_for("/servers/#{server.id}") + end + + event_id = attributes["id"] ||= SecureRandom.uuid + + client.server_events.new( + client.data[:server_events][event_id] = attributes + ) + end + + def create_logical_database(options={}) + database_service = options.fetch(:database_service) { create_database_service(options) } + + database_service.databases.create!( + :name => SecureRandom.hex(6), + :username => "ey#{SecureRandom.hex(6)}", + :password => SecureRandom.hex(8), + ).resource! + end + + def create_untracked_server(options={}) + provider = options.fetch(:provider) { create_provider(options) } + + untracked_server = options[:untracked_server] || {} + + provisioner_id = untracked_server[:provisioner_id] || SecureRandom.uuid + location = untracked_server[:location] || "us-west-2b" + provisioned_id = untracked_server[:provisioned_id] || "i-#{SecureRandom.hex(4)}" + state = untracked_server[:state] || "found" + + client.untracked_servers.create( + :location => location, + :provider => provider, + :provisioned_id => provisioned_id, + :provisioner_id => provisioner_id, + :state => state, + ) + end +end + +World(ResourceHelpers) diff --git a/features/support/server_helpers.rb b/features/support/server_helpers.rb new file mode 100644 index 0000000..eb413de --- /dev/null +++ b/features/support/server_helpers.rb @@ -0,0 +1,27 @@ +module ServerHelpers + def known_servers + begin + recall_fact(:known_servers) + rescue + memorize_fact(:known_servers, []) + end + end + + def seen_servers + begin + recall_fact(:seen_servers) + rescue + memorize_fact(:seen_servers, []) + end + end + + def first_server + known_servers.first + end + + def last_server + known_servers.last + end +end + +World(ServerHelpers) diff --git a/features/timeout_deploy.feature b/features/timeout_deploy.feature new file mode 100644 index 0000000..e69de29 diff --git a/features/version.feature b/features/version.feature new file mode 100644 index 0000000..82ab33f --- /dev/null +++ b/features/version.feature @@ -0,0 +1,8 @@ +Feature: Version + In order to determine if I'm working with the most recent goodness + As a User + I want to know what version of ey-core I'm using + + Scenario: Displaying the version + When I run `ey-core version` + Then I see the current ey-core version diff --git a/features/web/disable.feature b/features/web/disable.feature new file mode 100644 index 0000000..e69de29 diff --git a/features/web/enable.feature b/features/web/enable.feature new file mode 100644 index 0000000..e69de29 diff --git a/features/web/restart.feature b/features/web/restart.feature new file mode 100644 index 0000000..e69de29 diff --git a/features/whoami.feature b/features/whoami.feature new file mode 100644 index 0000000..be5e8f4 --- /dev/null +++ b/features/whoami.feature @@ -0,0 +1,14 @@ +Feature: Whoami + In order to ensure that I'm logged into the right account + As a User + I want to be able to see my user information + + Background: + Given I'm an Engine Yard user + And ey-core is configured with my cloud token + + Scenario: Getting the current user information + When I run `ey-core whoami` + Then I should see my user ID + And I should see my email address + And I should see my name diff --git a/lib/ey-core/cli/applications.rb b/lib/ey-core/cli/applications.rb index 598e335..530aae2 100644 --- a/lib/ey-core/cli/applications.rb +++ b/lib/ey-core/cli/applications.rb @@ -8,7 +8,7 @@ class Applications < Subcommand summary "Retrieve a list of Engine Yard applications that you have access to." option :account, - short: 'c', + short: 'a', long: 'account', description: 'Filter by account name or id', argument: 'Account' diff --git a/lib/ey-core/cli/environments.rb b/lib/ey-core/cli/environments.rb index 9ca8685..824555a 100644 --- a/lib/ey-core/cli/environments.rb +++ b/lib/ey-core/cli/environments.rb @@ -8,7 +8,7 @@ class Environments < Subcommand summary "Retrieve a list of Engine Yard environments that you have access to." option :account, - short: 'c', + short: 'a', long: 'account', description: 'Filter by account name or id', argument: 'Account' @@ -27,7 +27,7 @@ def environments if option(:account) core_account.environments.all else - current_account.map(&:environments).flatten.sort_by(&:id) + current_accounts.map(&:environments).flatten.sort_by(&:id) end end end diff --git a/lib/ey-core/cli/helpers/chef.rb b/lib/ey-core/cli/helpers/chef.rb index cc1f34e..41a1b55 100644 --- a/lib/ey-core/cli/helpers/chef.rb +++ b/lib/ey-core/cli/helpers/chef.rb @@ -7,7 +7,9 @@ module Helpers module Chef def run_chef(type, environment) request = environment.apply(type) - puts "Started #{type} chef run".green + + puts "#{run_msg(type)} #{env_msg(environment)}" + request.wait_for { |r| r.ready? } if request.successful puts "#{type.capitalize} chef run completed".green @@ -17,6 +19,14 @@ def run_chef(type, environment) end end + def run_msg(type) + "Started #{type} chef run".green + end + + def env_msg(environment) + "(account: #{environment.account.name}, environment: #{environment.name})".yellow + end + def upload_recipes(environment, path="cookbooks/") recipes_path = Pathname.new(path) diff --git a/lib/ey-core/cli/helpers/core.rb b/lib/ey-core/cli/helpers/core.rb index 31872a8..89fa933 100644 --- a/lib/ey-core/cli/helpers/core.rb +++ b/lib/ey-core/cli/helpers/core.rb @@ -115,6 +115,21 @@ def eyrc_yaml {} end + def all_pages(resource, params={}) + params.delete(:per_page) + per_page = 100 + results = [] + page = 1 + + while resources = resource.all({page: page, per_page: per_page}.merge(params)) + results.concat(resources) + break if resources.size < per_page + page += 1 + end + + results + end + def self.included(base) base.extend(ClassMethods) end diff --git a/lib/ey-core/cli/recipes/apply.rb b/lib/ey-core/cli/recipes/apply.rb index d219043..68082a6 100644 --- a/lib/ey-core/cli/recipes/apply.rb +++ b/lib/ey-core/cli/recipes/apply.rb @@ -12,24 +12,18 @@ class Apply < Subcommand summary "Apply changes to an environment" option :account, - short: "c", + short: "a", long: "account", description: "Name or id of account", argument: "account" - option :environment, - short: "e", - long: "environment", - description: "Name or id of environment", - argument: "environment" - switch :main, short: "m", long: "main", description: "Apply main recipes only" switch :custom, - short: "u", + short: "c", long: "custom", description: "Apply custom recipes only" @@ -43,28 +37,59 @@ class Apply < Subcommand long: "full", description: "Run main and custom chef" + arg :environment + def handle validate_run_type_flags + abort_on_ambiguous_environment - operator, environment = core_operator_and_environment_for(options) - raise "Unable to find matching environment" unless environment - - run_chef(run_type, environment) + run_chef(run_type, applicable_environment) if switch_active?(:full) - run_chef("custom", environment) + run_chef("custom", applicable_environment) end end private def validate_run_type_flags if active_run_type_flags.length > 1 - kernel.abort( + abort( 'Only one of --main, --custom, --quick, and --full may be specified.' ) end end + def abort_on_ambiguous_environment + abort "The criteria you've provided matches multiple environments. Please refine further with an account." if possible_environments.length > 1 + end + + def environment_name + arg(:environment).first + end + + def environments_api + operator(options).environments + end + + def possible_environments + return @possible_environments if @possible_environments + + @possible_environments = [environments_api.get(environment_name)].compact + + if @possible_environments.empty? + @possible_environments = all_pages( + environments_api.all, + name: environment_name + ) + end + + @possible_environments + end + + def applicable_environment + possible_environments.first + end + def run_type secondary_run_types[active_run_type] || default_run_type end diff --git a/lib/ey-core/cli/servers.rb b/lib/ey-core/cli/servers.rb index e2bf19b..a4bbe71 100644 --- a/lib/ey-core/cli/servers.rb +++ b/lib/ey-core/cli/servers.rb @@ -20,21 +20,59 @@ class Servers < Subcommand argument: "environment" def handle - puts TablePrint::Printer. + abort_on_ambiguous_environment + + puts servers.empty? ? + "No servers were found that match your criteria." : + TablePrint::Printer. new(servers, [{id: {width: 10}}, :role, :provisioned_id]). table_print end private - def servers - if option(:account) - core_client.servers.all(account: core_account) - elsif environment = option(:environment) - (core_client.environments.get(environment) || core_client.environments.first(name: environment)).servers.all - else - core_client.servers.all + def environment_name + option(:environment) + end + + def abort_on_ambiguous_environment + if environment_name + abort "The criteria you've provided matches multiple environments. Please refine further with an account." if possible_environments.length > 1 end end + + def environments_api + core_client.environments + end + + def possible_environments + return @possible_environments if @possible_environments + + @possible_environments = [environments_api.get(environment_name)].compact + + if @possible_environments.empty? + @possible_environments = all_pages( + environments_api.all, + name: environment_name + ) + end + + @possible_environments + end + + def applicable_environment + possible_environments.first + end + + def servers + @servers ||= all_pages(core_client.servers, server_filters) + end + + def server_filters + filters = {} + filters[:account] = core_account.id if option(:account) + filters[:environment] = applicable_environment.id if environment_name + filters + end end end end diff --git a/lib/ey-core/cli/status.rb b/lib/ey-core/cli/status.rb index 0cd1ac6..06d7391 100644 --- a/lib/ey-core/cli/status.rb +++ b/lib/ey-core/cli/status.rb @@ -7,39 +7,57 @@ class Status < Subcommand title "status" summary "Show the deployment status of the app" description <<-DESC -Show the current status of the most recent deployment of the specifed application and environment -DESC +Given an environment name and an application name, show the status of the most recent deployment of that application on the environment in question. + +Optionally, one may also specify the account to use in the case that one has several accounts with identical environment/application names. - option :environment, - short: "e", - long: "environment", - description: "Name or id of the environment to deploy to.", - argument: "Environment" +If an account is specified, the deployment in question will come from the environment and application within that account. Otherwise, we use the first account available that matches for both the environment and the application. +DESC option :account, - short: 'c', + short: 'a', 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.', + description: 'Name or ID of the account to query', argument: 'Account name or id' - 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" + arg :environment + arg :app def handle - operator, environment = core_operator_and_environment_for(self.options) - deployments = core_client. - deployments. - all(environment_id: environment.id, application_id: app.id) + deployment = deployments.first - ap deployments.first + ap deployment ? deployment : no_deployments_found end private + def app_name + arg(:app).first + end + def app - core_application_for(options) + api.applications.first(name: app_name) + end + + def environment_name + arg(:environment).first + end + + def environment + @environment ||= api.environments.first(name: environment_name) + end + + def api + @api ||= self.operator(options) + end + + def deployments + @deployments ||= api. + deployments. + all(environment: environment.id, application: app.id) + end + + def no_deployments_found + "We couldn't find a deployment matching the criteria you provided." end end end diff --git a/spec/ey-core/cli/accounts_spec.rb b/spec/ey-core/cli/accounts_spec.rb deleted file mode 100644 index 0985b54..0000000 --- a/spec/ey-core/cli/accounts_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'spec_helper' -require 'ey-core/cli/accounts' - -describe Ey::Core::Cli::Accounts do - set_up_cli - - context 'ey-core recipes accounts' do - it 'lists my account ids' do - execute - - expect(standard_output).to match(/#{Regexp.escape(account.id)}/) - end - - it 'lists my account names' do - execute - - expect(standard_output).to match(/#{Regexp.escape(account.name)}/) - end - end -end diff --git a/spec/ey-core/cli/recipes/apply_spec.rb b/spec/ey-core/cli/recipes/apply_spec.rb deleted file mode 100644 index 7878642..0000000 --- a/spec/ey-core/cli/recipes/apply_spec.rb +++ /dev/null @@ -1,78 +0,0 @@ -require 'spec_helper' -require 'ey-core/cli/recipes/apply' - -describe Ey::Core::Cli::Recipes::Apply do - set_up_cli - - before(:each) do - allow_any_instance_of(described_class). - to receive(:run_chef). - with(any_args). - and_return(true) - end - - context 'ey-core recipes apply --main' do - arguments '--main' - - it 'performs a main chef run' do - expect(cli).to receive(:run_chef).with('main', environment) - - execute - expect(kernel.exit_status).to eql(0) - end - end - - context 'ey-core recipes apply --custom' do - arguments '--custom' - - it 'performs a custom chef run' do - expect(cli).to receive(:run_chef).with('custom', environment) - - execute - expect(kernel.exit_status).to eql(0) - end - end - - context 'ey-core recipes apply --quick' do - arguments '--quick' - - it 'performs a quick chef run' do - expect(cli).to receive(:run_chef).with('quick', environment) - - execute - expect(kernel.exit_status).to eql(0) - end - end - - context 'ey-core recipes apply --full' do - arguments '--full' - - it 'performs both a main and a custom chef run' do - expect(cli).to receive(:run_chef).with('main', environment) - expect(cli).to receive(:run_chef).with('custom', environment) - - execute - expect(kernel.exit_status).to eql(0) - end - end - - context 'attempting to use more than one run type flag' do - let(:run_type_flags) {['--main', '--custom', '--quick', '--full']} - let(:combinations) {run_type_flags.combination(2).to_a} - - it 'aborts with advice regarding the run type flags' do - expect(kernel). - to receive(:abort). - with('Only one of --main, --custom, --quick, and --full may be specified.'). - and_call_original. - exactly(combinations.length). - times - - combinations.each do |combination| - attempt = described_class.new(combination, stdin, stdout, stderr, kernel) - - expect(attempt.execute!).not_to eql(0) - end - end - end -end diff --git a/spec/ey-core/cli/recipes/download_spec.rb b/spec/ey-core/cli/recipes/download_spec.rb deleted file mode 100644 index e272a42..0000000 --- a/spec/ey-core/cli/recipes/download_spec.rb +++ /dev/null @@ -1,93 +0,0 @@ -require 'spec_helper' -require 'ey-core/cli/recipes/download' - -describe Ey::Core::Cli::Recipes::Download do - set_up_cli - - before(:each) do - allow_any_instance_of(described_class). - to receive(:untar). - with(any_args). - and_return(true) - - allow_any_instance_of(described_class). - to receive(:ungzip). - with(any_args). - and_return(true) - end - - context 'ey-core recipes download' do - context 'with an existing cookbooks directory' do - before(:each) do - allow(File).to receive(:exist?).with("cookbooks").and_return(true) - end - - it 'fails out, advising that the cookbooks already exist locally' do - status = execute - - expect(error_output). - to include( - 'Cannot download recipes, cookbooks directory already exists.' - ) - - expect(status).to eql(255) - end - end - - context 'with no cookbooks directory' do - before(:each) do - allow(File).to receive(:exist?).with("cookbooks").and_return(false) - - allow(environment). - to receive(:download_recipes). - and_return('cookbooks.tar.gz') - - allow(cli). - to receive(:ungzip) - - allow(cli). - to receive(:untar) - end - - it 'advises that the cookbooks are being downloaded' do - execute - - expect(standard_output).to include('Downloading recipes'.green) - end - - it 'downloads the cookbooks archive' do - expect(environment). - to receive(:download_recipes). - and_call_original - - execute - end - - it 'advises that the downloaded archive is being extracted' do - execute - - expect(standard_output). - to match(/Extracting recipes to 'cookbooks\/'/) - end - - it 'unarchives the downloaded archive' do - allow(environment).to receive(:download_recipes).and_return('cookbooks.tar.gz') - expect(cli). - to receive(:ungzip). - with('cookbooks.tar.gz'). - and_return('cookbooks.tar') - - expect(cli). - to receive(:untar). - with('cookbooks.tar', './'). - and_return(true) - - execute - end - - it 'exits cleanly' do - expect(execute).to eql(0) - end - end - end -end diff --git a/spec/ey-core/cli/recipes/upload_spec.rb b/spec/ey-core/cli/recipes/upload_spec.rb deleted file mode 100644 index a881452..0000000 --- a/spec/ey-core/cli/recipes/upload_spec.rb +++ /dev/null @@ -1,80 +0,0 @@ -require 'spec_helper' -require 'ey-core/cli/recipes/upload' - -describe Ey::Core::Cli::Recipes::Upload do - set_up_cli - - before(:each) do - allow_any_instance_of(described_class). - to receive(:run_chef). - with(any_args). - and_return(true) - - allow_any_instance_of(described_class). - to receive(:upload_recipes). - with(any_args). - and_return(true) - end - - context 'ey-core recipes upload' do - it 'advises that it is uploading recipes for the current environment' do - execute - - expect(standard_output). - to match(/Uploading custom recipes for /) - end - - it 'uploads the recipes' do - expect(cli).to receive(:upload_recipes).with(environment, 'cookbooks/') - - execute - end - - context 'upon uploading successfully' do - it 'advises that the upload completed' do - execute - - expect(standard_output). - to match(/Uploading custom recipes complete/) - end - end - - context 'upon failing to upload' do - before(:each) do - allow(cli). - to receive(:upload_recipes). - with(environment, 'cookbooks/'). - and_raise('big bada boom') - end - - it 'aborts, advising that the upload failed' do - status = execute - - expect(error_output). - to match(/There was a problem uploading the recipes/) - - expect(status).not_to eql(0) - end - end - end - - context 'ey-core recipes upload --apply' do - arguments '--apply' - - it 'performs a custom chef run' do - expect(cli).to receive(:run_chef).with('custom', environment) - - execute - end - end - - context 'ey-core recipes upload --path /some/path' do - arguments '--file /some/path' - - it 'uploads the recipes from the given path' do - expect(cli).to receive(:upload_recipes).with(environment, '/some/path') - - execute - end - end -end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 029c458..5afe930 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -7,9 +7,14 @@ Bundler.require(:default, :test) -require File.expand_path("../../lib/ey-core", __FILE__) +$:.unshift File.expand_path('../../lib', __FILE__) + Dir[File.expand_path("../{shared,support}/*.rb", __FILE__)].each{|f| require(f)} +require 'ey-core' +require 'ey-core/cli/main' + + RSpec.configure do |config| config.order = "random" config.after(:each) do