From 8f7df3557a453fc6c77752592404128818c6acef Mon Sep 17 00:00:00 2001 From: Dennis Walters Date: Sun, 29 May 2016 05:03:15 -0400 Subject: [PATCH 01/26] Beating my head against the wall Will need to talk to Eugene soon to figure out how to properly mock this stuff for acceptance tests, because using the same tricks that were used with the Ronin cuke suite has so far bore no fruit. --- ey-core.gemspec | 3 + features/accounts.feature | 13 ++ features/login.feature | 6 + features/step_definitions/accounts_steps.rb | 30 ++++ features/support/account_helpers.rb | 69 +++++++ features/support/aruba.rb | 1 + features/support/boilerplate.rb | 1 + features/support/client_helpers.rb | 36 ++++ features/support/config_file_helpers.rb | 42 +++++ features/support/core.rb | 19 ++ features/support/env.rb | 40 +++++ features/support/fake_kernel.rb | 23 +++ features/support/io.rb | 5 + features/support/mock_api.rb | 13 ++ features/support/output_helpers.rb | 7 + features/support/resource_helpers.rb | 189 ++++++++++++++++++++ lib/ey-core/cli/accounts.rb | 4 + spec/spec_helper.rb | 7 +- 18 files changed, 507 insertions(+), 1 deletion(-) create mode 100644 features/accounts.feature create mode 100644 features/login.feature create mode 100644 features/step_definitions/accounts_steps.rb create mode 100644 features/support/account_helpers.rb create mode 100644 features/support/aruba.rb create mode 100644 features/support/boilerplate.rb create mode 100644 features/support/client_helpers.rb create mode 100644 features/support/config_file_helpers.rb create mode 100644 features/support/core.rb create mode 100644 features/support/env.rb create mode 100644 features/support/fake_kernel.rb create mode 100644 features/support/io.rb create mode 100644 features/support/mock_api.rb create mode 100644 features/support/output_helpers.rb create mode 100644 features/support/resource_helpers.rb 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/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/step_definitions/accounts_steps.rb b/features/step_definitions/accounts_steps.rb new file mode 100644 index 0000000..9c2f5f3 --- /dev/null +++ b/features/step_definitions/accounts_steps.rb @@ -0,0 +1,30 @@ +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( + ENV['CORE_URL'] => 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]) + + asdf = client.instance_eval {@current_user} + puts "there are #{client.accounts.all.length} accounts" + puts "accounts: #{client.accounts.all.map(&:id)}" + puts "current user == '#{current_user.id}'" + puts "current user hash == '#{current_user_hash}'" +end + +Then %(I see the name and ID of each of my accounts) do + puts "all output == '#{output_text}'" + 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/support/account_helpers.rb b/features/support/account_helpers.rb new file mode 100644 index 0000000..adbfc4b --- /dev/null +++ b/features/support/account_helpers.rb @@ -0,0 +1,69 @@ +module AccountHelpers + 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/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/env.rb b/features/support/env.rb new file mode 100644 index 0000000..3c6ae55 --- /dev/null +++ b/features/support/env.rb @@ -0,0 +1,40 @@ +require 'simplecov' +SimpleCov.coverage_dir 'feature-coverage' +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/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..b5668d9 --- /dev/null +++ b/features/support/mock_api.rb @@ -0,0 +1,13 @@ +Before do + 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 + + Ey::Core::Client::Mock.reset! +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/lib/ey-core/cli/accounts.rb b/lib/ey-core/cli/accounts.rb index 1526a6b..7b01c88 100644 --- a/lib/ey-core/cli/accounts.rb +++ b/lib/ey-core/cli/accounts.rb @@ -8,6 +8,10 @@ class Accounts < Subcommand summary "Retrieve a list of Engine Yard accounts that you have access to." def handle + puts "current user in app == '#{core_client.users.current.attributes}'" + puts "current user accounts: '#{core_client.users.current.accounts.all.length}'" + puts "user count == #{core_client.users.all.length}" + puts "client data == '#{core_client.data}'" table_data = TablePrint::Printer.new(current_accounts, [{id: {width: 36}}, :name]) puts table_data.table_print end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2187aaf..ab08fbf 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -5,9 +5,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 From 1e09ca62c4c2aedbc9ec1dcaa0e900ef70a25d0b Mon Sep 17 00:00:00 2001 From: Dennis Walters Date: Mon, 30 May 2016 16:17:48 -0400 Subject: [PATCH 02/26] ey-core accounts feature --- features/step_definitions/accounts_steps.rb | 7 ------- features/support/core.rb | 11 ++++++----- features/support/env.rb | 2 +- features/support/mock_api.rb | 16 ++++++++++++++++ lib/ey-core/cli/accounts.rb | 4 ---- 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/features/step_definitions/accounts_steps.rb b/features/step_definitions/accounts_steps.rb index 9c2f5f3..337096f 100644 --- a/features/step_definitions/accounts_steps.rb +++ b/features/step_definitions/accounts_steps.rb @@ -13,16 +13,9 @@ account1 = create_account(client: client, owner: current_user) account2 = create_account(client: client, owner: current_user) memorize_fact(:accounts, [account1, account2]) - - asdf = client.instance_eval {@current_user} - puts "there are #{client.accounts.all.length} accounts" - puts "accounts: #{client.accounts.all.map(&:id)}" - puts "current user == '#{current_user.id}'" - puts "current user hash == '#{current_user_hash}'" end Then %(I see the name and ID of each of my accounts) do - puts "all output == '#{output_text}'" recall_fact(:accounts).each do |account| expect(output_text).to include(account.id) expect(output_text).to include(account.name) diff --git a/features/support/core.rb b/features/support/core.rb index 6512540..03a121f 100644 --- a/features/support/core.rb +++ b/features/support/core.rb @@ -1,10 +1,11 @@ module CoreHelpers def client - begin - recall_fact(:client) - rescue - memorize_fact(:client, create_client) - end + #begin + #recall_fact(:client) + #rescue + #memorize_fact(:client, create_client) + #end + MOCK_CLIENT end def current_user diff --git a/features/support/env.rb b/features/support/env.rb index 3c6ae55..481705f 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -1,5 +1,5 @@ require 'simplecov' -SimpleCov.coverage_dir 'feature-coverage' +SimpleCov.coverage_dir 'features-coverage' SimpleCov.minimum_coverage 95 SimpleCov.start do add_filter '/spec/' diff --git a/features/support/mock_api.rb b/features/support/mock_api.rb index b5668d9..0493e39 100644 --- a/features/support/mock_api.rb +++ b/features/support/mock_api.rb @@ -1,3 +1,17 @@ +module Ey + module Core + module Cli + module Helpers + module Core + def core_client + MOCK_CLIENT + end + end + end + end + end +end + Before do Cistern.formatter = Cistern::Formatter::AwesomePrint @@ -10,4 +24,6 @@ Ey::Core::Client::Real.poll_interval = 0 Ey::Core::Client::Mock.reset! + + MOCK_CLIENT = create_client end diff --git a/lib/ey-core/cli/accounts.rb b/lib/ey-core/cli/accounts.rb index 7b01c88..1526a6b 100644 --- a/lib/ey-core/cli/accounts.rb +++ b/lib/ey-core/cli/accounts.rb @@ -8,10 +8,6 @@ class Accounts < Subcommand summary "Retrieve a list of Engine Yard accounts that you have access to." def handle - puts "current user in app == '#{core_client.users.current.attributes}'" - puts "current user accounts: '#{core_client.users.current.accounts.all.length}'" - puts "user count == #{core_client.users.all.length}" - puts "client data == '#{core_client.data}'" table_data = TablePrint::Printer.new(current_accounts, [{id: {width: 36}}, :name]) puts table_data.table_print end From 7e46226c33ddbc60b61a10e56553ec03718e3055 Mon Sep 17 00:00:00 2001 From: Dennis Walters Date: Mon, 30 May 2016 16:29:20 -0400 Subject: [PATCH 03/26] Stubbed CLI features Most of the feature files present are empty, but `accounts` and `version` have been fleshed out. --- features/applications.feature | 0 features/current_user.feature | 0 features/deploy.feature | 0 features/environments.feature | 0 features/init.feature | 0 features/logout.feature | 0 features/logs.feature | 0 features/recipes/apply.feature | 0 features/recipes/download.feature | 0 features/recipes/upload.feature | 0 features/scp.feature | 0 features/servers.feature | 0 features/ssh.feature | 0 features/status.feature | 0 features/step_definitions/version_steps.rb | 3 +++ features/support/env.rb | 2 +- features/timeout_deploy.feature | 0 features/version.feature | 8 ++++++++ features/web/disable.feature | 0 features/web/enable.feature | 0 features/web/restart.feature | 0 features/whoami.feature | 0 22 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 features/applications.feature create mode 100644 features/current_user.feature create mode 100644 features/deploy.feature create mode 100644 features/environments.feature create mode 100644 features/init.feature create mode 100644 features/logout.feature create mode 100644 features/logs.feature create mode 100644 features/recipes/apply.feature create mode 100644 features/recipes/download.feature create mode 100644 features/recipes/upload.feature create mode 100644 features/scp.feature create mode 100644 features/servers.feature create mode 100644 features/ssh.feature create mode 100644 features/status.feature create mode 100644 features/step_definitions/version_steps.rb create mode 100644 features/timeout_deploy.feature create mode 100644 features/version.feature create mode 100644 features/web/disable.feature create mode 100644 features/web/enable.feature create mode 100644 features/web/restart.feature create mode 100644 features/whoami.feature diff --git a/features/applications.feature b/features/applications.feature new file mode 100644 index 0000000..e69de29 diff --git a/features/current_user.feature b/features/current_user.feature new file mode 100644 index 0000000..e69de29 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..e69de29 diff --git a/features/init.feature b/features/init.feature new file mode 100644 index 0000000..e69de29 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..e69de29 diff --git a/features/recipes/download.feature b/features/recipes/download.feature new file mode 100644 index 0000000..e69de29 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..e69de29 diff --git a/features/servers.feature b/features/servers.feature new file mode 100644 index 0000000..e69de29 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..e69de29 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/env.rb b/features/support/env.rb index 481705f..c4c65b6 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -1,5 +1,5 @@ require 'simplecov' -SimpleCov.coverage_dir 'features-coverage' +SimpleCov.coverage_dir 'coverage-features' SimpleCov.minimum_coverage 95 SimpleCov.start do add_filter '/spec/' 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..e69de29 From 4f1feb95a8baddb1f4e304b066314da9af8cc050 Mon Sep 17 00:00:00 2001 From: Dennis Walters Date: Mon, 30 May 2016 22:52:35 -0400 Subject: [PATCH 04/26] Described behavior for `ey-core status` * Switched from optional env/app to required env/app * Wrote the basic cuke for the `status` command Unrelated, found and fixed a typo that I left in the `environment` command. --- features/status.feature | 25 ++++++ features/step_definitions/accounts_steps.rb | 2 +- features/step_definitions/status_steps.rb | 99 +++++++++++++++++++++ features/support/account_helpers.rb | 16 ++++ features/support/app_helpers.rb | 19 ++++ features/support/deployment_helpers.rb | 19 ++++ features/support/env.rb | 6 +- features/support/environment_helpers.rb | 19 ++++ lib/ey-core/cli/environments.rb | 2 +- lib/ey-core/cli/status.rb | 59 +++++++----- 10 files changed, 241 insertions(+), 25 deletions(-) create mode 100644 features/step_definitions/status_steps.rb create mode 100644 features/support/app_helpers.rb create mode 100644 features/support/deployment_helpers.rb create mode 100644 features/support/environment_helpers.rb diff --git a/features/status.feature b/features/status.feature index e69de29..ea7e716 100644 --- a/features/status.feature +++ 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 index 337096f..9cbf0dd 100644 --- a/features/step_definitions/accounts_steps.rb +++ b/features/step_definitions/accounts_steps.rb @@ -5,7 +5,7 @@ Given %(ey-core is configured with my cloud token) do add_config_option( - ENV['CORE_URL'] => current_user_hash['token'] + 'https://api.engineyard.com/' => current_user_hash['token'] ) 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/support/account_helpers.rb b/features/support/account_helpers.rb index adbfc4b..e76c9af 100644 --- a/features/support/account_helpers.rb +++ b/features/support/account_helpers.rb @@ -1,4 +1,20 @@ module AccountHelpers + 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] 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/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 index c4c65b6..5f1e9b8 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -6,7 +6,7 @@ add_filter '/features/' add_filter '/mock/' add_group 'Libraries', 'lib' - add_group 'CLI', 'lib/ey-core/cli' + add_group 'CLI', 'lib/ey-core/cli/' add_group 'CLI Helpers', 'lib/ey-core/cli/helpers' end @@ -31,10 +31,10 @@ @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" + #ENV['CORE_URL'] ||= "http://api-development.localdev.engineyard.com:9292" end After do ENV['RUBYLIB'] = @original_rubylib - ENV.delete('CORE_URL') + #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..f99914d --- /dev/null +++ b/features/support/environment_helpers.rb @@ -0,0 +1,19 @@ +module EnvironmentHelpers + 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/lib/ey-core/cli/environments.rb b/lib/ey-core/cli/environments.rb index 9ca8685..8089a19 100644 --- a/lib/ey-core/cli/environments.rb +++ b/lib/ey-core/cli/environments.rb @@ -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/status.rb b/lib/ey-core/cli/status.rb index 0cd1ac6..cced093 100644 --- a/lib/ey-core/cli/status.rb +++ b/lib/ey-core/cli/status.rb @@ -7,39 +7,58 @@ 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 ||= environment. + deployments. + all. + select {|deployment| deployment.application == app} + end + + def no_deployments_found + "We couldn't find a deployment matching the criteria you provided." end end end From 6f23a7ee389b3de9897c437655a0a43a93713692 Mon Sep 17 00:00:00 2001 From: Dennis Walters Date: Tue, 31 May 2016 11:19:49 -0400 Subject: [PATCH 05/26] Better core client mock for cukes * The "mock api" cuke support file now turns on mocking globally * The mock api also uses proper method stubbing to ensure that the mocked API that we've set up is used by the CLI commands * Added the cucumber task to the default rake run --- Rakefile | 5 +- features/support/core.rb | 11 ++- features/support/mock_api.rb | 40 ++++------ spec/ey-core/cli/accounts_spec.rb | 20 ----- spec/ey-core/cli/recipes/apply_spec.rb | 78 ------------------- spec/ey-core/cli/recipes/download_spec.rb | 93 ----------------------- spec/ey-core/cli/recipes/upload_spec.rb | 80 ------------------- 7 files changed, 25 insertions(+), 302 deletions(-) delete mode 100644 spec/ey-core/cli/accounts_spec.rb delete mode 100644 spec/ey-core/cli/recipes/apply_spec.rb delete mode 100644 spec/ey-core/cli/recipes/download_spec.rb delete mode 100644 spec/ey-core/cli/recipes/upload_spec.rb 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/features/support/core.rb b/features/support/core.rb index 03a121f..6512540 100644 --- a/features/support/core.rb +++ b/features/support/core.rb @@ -1,11 +1,10 @@ module CoreHelpers def client - #begin - #recall_fact(:client) - #rescue - #memorize_fact(:client, create_client) - #end - MOCK_CLIENT + begin + recall_fact(:client) + rescue + memorize_fact(:client, create_client) + end end def current_user diff --git a/features/support/mock_api.rb b/features/support/mock_api.rb index 0493e39..2b414cd 100644 --- a/features/support/mock_api.rb +++ b/features/support/mock_api.rb @@ -1,29 +1,21 @@ -module Ey - module Core - module Cli - module Helpers - module Core - def core_client - MOCK_CLIENT - end - end - end - end - end -end - -Before do - Cistern.formatter = Cistern::Formatter::AwesomePrint +require 'cucumber/rspec/doubles' - 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 +# 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! - MOCK_CLIENT = create_client + # 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/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 From 3c6cdf100fa6f65908e6e479165d00724dc642fd Mon Sep 17 00:00:00 2001 From: Dennis Walters Date: Tue, 31 May 2016 11:29:16 -0400 Subject: [PATCH 06/26] Added cuke for applications command --- features/applications.feature | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/features/applications.feature b/features/applications.feature index e69de29..4c794f2 100644 --- a/features/applications.feature +++ 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 name and ID for all applications in the one account + But I do not see applications that belong to other accounts + + Examples: + | Account Flag | + | -a | + | --account | + From 51f9fa8834e9219c5e580eebd8e7f00c400290ee Mon Sep 17 00:00:00 2001 From: Dennis Walters Date: Tue, 31 May 2016 11:58:42 -0400 Subject: [PATCH 07/26] Simplified language in the applications cuke --- features/applications.feature | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/applications.feature b/features/applications.feature index 4c794f2..f06c8c0 100644 --- a/features/applications.feature +++ b/features/applications.feature @@ -19,8 +19,8 @@ Feature: Applications Scenario Outline: Listing applications for a specific account When I run `ey-core applications one` - Then I see the name and ID for all applications in the one account - But I do not see applications that belong to other accounts + Then I see the applications in the one account + But I do not see applications from other accounts Examples: | Account Flag | From 9e2725eab49f097ea435def3cbd0a20f1d76053b Mon Sep 17 00:00:00 2001 From: Dennis Walters Date: Tue, 31 May 2016 11:59:00 -0400 Subject: [PATCH 08/26] Added step definitions for applications --- .../step_definitions/applications_steps.rb | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 features/step_definitions/applications_steps.rb diff --git a/features/step_definitions/applications_steps.rb b/features/step_definitions/applications_steps.rb new file mode 100644 index 0000000..6f07bc7 --- /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 + client.accounts.first(name: '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 = client.accounts.first(name: 'two').applications.all.to_a + three = client.accounts.first(name: '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 From c899809ae750778a2b87cf7298a14096f71eaeb4 Mon Sep 17 00:00:00 2001 From: Dennis Walters Date: Tue, 31 May 2016 11:59:10 -0400 Subject: [PATCH 09/26] Changed -c to -a in applications command So far as I can tell, there's no real reason that we shouldn't be using -a across the board for the "account" option. --- lib/ey-core/cli/applications.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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' From 4420421fdc6fb3f4ef4a5df5dfab254500fb5afa Mon Sep 17 00:00:00 2001 From: Dennis Walters Date: Tue, 31 May 2016 12:00:44 -0400 Subject: [PATCH 10/26] Added coverage-features to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) 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 From a72b346c4cf644312fef5253b0ac8ec32ab98722 Mon Sep 17 00:00:00 2001 From: Dennis Walters Date: Tue, 31 May 2016 12:06:02 -0400 Subject: [PATCH 11/26] Cuke for current_user --- features/current_user.feature | 14 ++++++++++++++ features/step_definitions/current_user_steps.rb | 11 +++++++++++ 2 files changed, 25 insertions(+) create mode 100644 features/step_definitions/current_user_steps.rb diff --git a/features/current_user.feature b/features/current_user.feature index e69de29..3c5da1b 100644 --- a/features/current_user.feature +++ 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/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 From 3e1f082b2e2b9445085a02d935f8a4077d6f80e3 Mon Sep 17 00:00:00 2001 From: Dennis Walters Date: Tue, 31 May 2016 13:20:10 -0400 Subject: [PATCH 12/26] Aded cuke and steps for environments --- features/environments.feature | 30 +++++++++++++++ .../step_definitions/applications_steps.rb | 6 +-- .../step_definitions/environments_steps.rb | 37 +++++++++++++++++++ features/support/account_helpers.rb | 4 ++ 4 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 features/step_definitions/environments_steps.rb diff --git a/features/environments.feature b/features/environments.feature index e69de29..2673d9a 100644 --- a/features/environments.feature +++ 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/step_definitions/applications_steps.rb b/features/step_definitions/applications_steps.rb index 6f07bc7..e5e51f9 100644 --- a/features/step_definitions/applications_steps.rb +++ b/features/step_definitions/applications_steps.rb @@ -31,14 +31,14 @@ end Then %(I see the applications in the one account) do - client.accounts.first(name: 'one').applications.all.each do |app| + 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 = client.accounts.first(name: 'two').applications.all.to_a - three = client.accounts.first(name: 'three').applications.all.to_a + 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)}/) 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/support/account_helpers.rb b/features/support/account_helpers.rb index e76c9af..8c372ce 100644 --- a/features/support/account_helpers.rb +++ b/features/support/account_helpers.rb @@ -1,4 +1,8 @@ module AccountHelpers + def account_named(name) + client.accounts.first(name: name) + end + def known_accounts begin recall_fact(:known_accounts) From 27321c7fb31d94642afb9a02fe9276bd872b682c Mon Sep 17 00:00:00 2001 From: Dennis Walters Date: Tue, 31 May 2016 13:21:53 -0400 Subject: [PATCH 13/26] Changed environments account flag to -a --- lib/ey-core/cli/environments.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ey-core/cli/environments.rb b/lib/ey-core/cli/environments.rb index 8089a19..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' From 889bc2312b4e677e84b04656e1de44dc39fe1462 Mon Sep 17 00:00:00 2001 From: Dennis Walters Date: Wed, 1 Jun 2016 10:46:44 -0400 Subject: [PATCH 14/26] Added cuke and steps for servers command --- features/servers.feature | 56 +++++++++++++ features/step_definitions/servers_steps.rb | 93 ++++++++++++++++++++++ features/support/environment_helpers.rb | 4 + features/support/server_helpers.rb | 27 +++++++ 4 files changed, 180 insertions(+) create mode 100644 features/step_definitions/servers_steps.rb create mode 100644 features/support/server_helpers.rb diff --git a/features/servers.feature b/features/servers.feature index e69de29..bcf9118 100644 --- a/features/servers.feature +++ 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/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/support/environment_helpers.rb b/features/support/environment_helpers.rb index f99914d..738745a 100644 --- a/features/support/environment_helpers.rb +++ b/features/support/environment_helpers.rb @@ -1,4 +1,8 @@ module EnvironmentHelpers + def environment_named(name) + client.environments.first(name: name) + end + def known_environments begin recall_fact(:known_environments) 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) From 42ca69f020e9adf8d45e847a1d4461b30b493da2 Mon Sep 17 00:00:00 2001 From: Dennis Walters Date: Wed, 1 Jun 2016 10:47:07 -0400 Subject: [PATCH 15/26] Made servers command match specified behavior --- lib/ey-core/cli/servers.rb | 59 +++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/lib/ey-core/cli/servers.rb b/lib/ey-core/cli/servers.rb index e2bf19b..a8eed9d 100644 --- a/lib/ey-core/cli/servers.rb +++ b/lib/ey-core/cli/servers.rb @@ -20,19 +20,64 @@ 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 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 possible_environments + return @possible_environments if @possible_environments + + @possible_environments = [core_client.environments.get(environment_name)].compact + + @possible_environments = core_client.environments.all(name: environment_name).to_a if @possible_environments.empty? + + @possible_environments + end + + def applicable_environment + core_client.environments.get(option(:environment)) || + core_client.environments.first(name: option(:environment)) + end + 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 + @servers ||= AccountFilter.filter( + EnvironmentFilter.filter( + core_client.servers.all, + applicable_environment + ), + core_account + ) + end + + module AccountFilter + def self.filter(servers, account) + return servers unless account + + servers.select {|server| server.account == account} + end + end + + module EnvironmentFilter + def self.filter(servers, environment) + return servers unless environment + + servers.select {|server| server.environment == environment} end end end From 3b0e0ce84d7eec6842fab0990e56d4b846902687 Mon Sep 17 00:00:00 2001 From: Dennis Walters Date: Wed, 1 Jun 2016 11:53:51 -0400 Subject: [PATCH 16/26] copied current_user cuke to whoami cuke --- features/whoami.feature | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/features/whoami.feature b/features/whoami.feature index e69de29..be5e8f4 100644 --- a/features/whoami.feature +++ 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 From 3a6c4866a6461460ebffb8a6cf0007230374971d Mon Sep 17 00:00:00 2001 From: Dennis Walters Date: Wed, 1 Jun 2016 13:04:02 -0400 Subject: [PATCH 17/26] Ensure that `servers` lists all applicable servers The real client, unless told to do otherwise, only returns 20 results when one does `client.some_resource.all`. This uses the `all_pages` helper to retrive absolutely all resources matching the supplied filters. --- lib/ey-core/cli/helpers/core.rb | 15 +++++++++++++++ lib/ey-core/cli/servers.rb | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) 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/servers.rb b/lib/ey-core/cli/servers.rb index a8eed9d..f4a0e54 100644 --- a/lib/ey-core/cli/servers.rb +++ b/lib/ey-core/cli/servers.rb @@ -45,7 +45,7 @@ def possible_environments @possible_environments = [core_client.environments.get(environment_name)].compact - @possible_environments = core_client.environments.all(name: environment_name).to_a if @possible_environments.empty? + @possible_environments = all_pages(core_client.environments.all, name: environment_name).to_a if @possible_environments.empty? @possible_environments end @@ -58,7 +58,7 @@ def applicable_environment def servers @servers ||= AccountFilter.filter( EnvironmentFilter.filter( - core_client.servers.all, + all_pages(core_client.servers.all), applicable_environment ), core_account From d7bfa1ad66e240ee625b0a4ff6ab72794bedbf25 Mon Sep 17 00:00:00 2001 From: Dennis Walters Date: Wed, 1 Jun 2016 13:55:41 -0400 Subject: [PATCH 18/26] Cuked out the two deprecated commands --- features/init.feature | 6 ++++++ features/scp.feature | 6 ++++++ features/step_definitions/deprecated_command_steps.rb | 3 +++ 3 files changed, 15 insertions(+) create mode 100644 features/step_definitions/deprecated_command_steps.rb diff --git a/features/init.feature b/features/init.feature index e69de29..bd06da7 100644 --- a/features/init.feature +++ 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/scp.feature b/features/scp.feature index e69de29..c358b37 100644 --- a/features/scp.feature +++ 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/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 From 5e4f53bb81c44548991b8567a11869b3f359263a Mon Sep 17 00:00:00 2001 From: Dennis Walters Date: Wed, 1 Jun 2016 14:20:46 -0400 Subject: [PATCH 19/26] Simplified servers command Since the mock query interface for servers has been updated and fixed, we can do this without loading ALL OF THE SERVERS --- lib/ey-core/cli/servers.rb | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/lib/ey-core/cli/servers.rb b/lib/ey-core/cli/servers.rb index f4a0e54..2fe6315 100644 --- a/lib/ey-core/cli/servers.rb +++ b/lib/ey-core/cli/servers.rb @@ -51,34 +51,18 @@ def possible_environments end def applicable_environment - core_client.environments.get(option(:environment)) || - core_client.environments.first(name: option(:environment)) + possible_environments.first end def servers - @servers ||= AccountFilter.filter( - EnvironmentFilter.filter( - all_pages(core_client.servers.all), - applicable_environment - ), - core_account - ) + @servers ||= all_pages(core_client.servers, server_filters) end - module AccountFilter - def self.filter(servers, account) - return servers unless account - - servers.select {|server| server.account == account} - end - end - - module EnvironmentFilter - def self.filter(servers, environment) - return servers unless environment - - servers.select {|server| server.environment == environment} - end + def server_filters + filters = {} + filters[:account] = core_account if option(:account) + filters[:environment] = applicable_environment if environment_name + filters end end end From 8aac2dca98065d543d1a752deb3f552d9e4c4bc0 Mon Sep 17 00:00:00 2001 From: Dennis Walters Date: Wed, 1 Jun 2016 14:26:01 -0400 Subject: [PATCH 20/26] Critic tweaks for server command --- lib/ey-core/cli/servers.rb | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/ey-core/cli/servers.rb b/lib/ey-core/cli/servers.rb index 2fe6315..8027eb0 100644 --- a/lib/ey-core/cli/servers.rb +++ b/lib/ey-core/cli/servers.rb @@ -40,12 +40,21 @@ def abort_on_ambiguous_environment end end + def environments_api + core_client.environments + end + def possible_environments return @possible_environments if @possible_environments - @possible_environments = [core_client.environments.get(environment_name)].compact + @possible_environments = [environments_api.get(environment_name)].compact - @possible_environments = all_pages(core_client.environments.all, name: environment_name).to_a if @possible_environments.empty? + if @possible_environments.empty? + @possible_environments = all_pages( + environments_api.all, + name: environment_name + ) + end @possible_environments end From f893d6a6da6ef813bfd8983e6ef14999d198e519 Mon Sep 17 00:00:00 2001 From: Dennis Walters Date: Wed, 1 Jun 2016 15:24:13 -0400 Subject: [PATCH 21/26] filter attributes must be IDs --- lib/ey-core/cli/servers.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ey-core/cli/servers.rb b/lib/ey-core/cli/servers.rb index 8027eb0..a4bbe71 100644 --- a/lib/ey-core/cli/servers.rb +++ b/lib/ey-core/cli/servers.rb @@ -69,8 +69,8 @@ def servers def server_filters filters = {} - filters[:account] = core_account if option(:account) - filters[:environment] = applicable_environment if environment_name + filters[:account] = core_account.id if option(:account) + filters[:environment] = applicable_environment.id if environment_name filters end end From bc8d53406cbd79c6b062bbeb08a8f0a3e10583f2 Mon Sep 17 00:00:00 2001 From: Dennis Walters Date: Wed, 1 Jun 2016 15:24:38 -0400 Subject: [PATCH 22/26] Simplified the status command --- lib/ey-core/cli/status.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/ey-core/cli/status.rb b/lib/ey-core/cli/status.rb index cced093..06d7391 100644 --- a/lib/ey-core/cli/status.rb +++ b/lib/ey-core/cli/status.rb @@ -51,10 +51,9 @@ def api end def deployments - @deployments ||= environment. + @deployments ||= api. deployments. - all. - select {|deployment| deployment.application == app} + all(environment: environment.id, application: app.id) end def no_deployments_found From 06067861cccfdcc440c2b10513ad791d6ecca551 Mon Sep 17 00:00:00 2001 From: Dennis Walters Date: Mon, 6 Jun 2016 13:16:32 -0400 Subject: [PATCH 23/26] Added cuke for recipes apply --- features/recipes/apply.feature | 100 +++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/features/recipes/apply.feature b/features/recipes/apply.feature index e69de29..def172f 100644 --- a/features/recipes/apply.feature +++ b/features/recipes/apply.feature @@ -0,0 +1,100 @@ +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 an application called anvil_drop + And anvil_drop 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 an application called painted_tunnel + And painted_tunnel 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 | From 304323ed7ffa39366d2954dee8a38d6356cd87e1 Mon Sep 17 00:00:00 2001 From: Dennis Walters Date: Mon, 6 Jun 2016 14:30:00 -0400 Subject: [PATCH 24/26] Cleaned up recipe apply cuke and added steps --- features/recipes/apply.feature | 6 +- .../recipes/step_definitions/apply_steps.rb | 70 +++++++++++++++++++ 2 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 features/recipes/step_definitions/apply_steps.rb diff --git a/features/recipes/apply.feature b/features/recipes/apply.feature index def172f..97a7137 100644 --- a/features/recipes/apply.feature +++ b/features/recipes/apply.feature @@ -7,8 +7,7 @@ Feature: Recipes Apply 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 an application called anvil_drop - And anvil_drop has the following environments: + And ACME Inc has the following environments: | Environment | | coyote | | roadrunner | @@ -80,8 +79,7 @@ Feature: Recipes Apply 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 an application called painted_tunnel - And painted_tunnel has the following environments: + And Wile E Enterprises has the following environments: | Environment | | coyote | | roadrunner | 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 From 34df3c8875959e05ce0eb5c22d4c6f0721230982 Mon Sep 17 00:00:00 2001 From: Dennis Walters Date: Mon, 6 Jun 2016 14:30:21 -0400 Subject: [PATCH 25/26] Chef run output more specific When the CLI starts a chef run, it now also specifies the account and environment on which it's performing said run. This helps us with outside testing, and it also provides a better user experience. --- lib/ey-core/cli/helpers/chef.rb | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) 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) From f285c25553962d5347ef72be262d735b4c664471 Mon Sep 17 00:00:00 2001 From: Dennis Walters Date: Mon, 6 Jun 2016 14:31:23 -0400 Subject: [PATCH 26/26] Modified `recipes apply` behavior to match cuke * environment name/id is required, so it was converted from an option to an argument * The account short flag is now -a * The custom short flag is now -c * Informative messages are provided for both ambiguous environment matches as well as multiple run type flags used --- lib/ey-core/cli/recipes/apply.rb | 53 +++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 14 deletions(-) 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