From 9ed27fc334b404c20b914b8e5365c6f3e050179c Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Thu, 26 Sep 2013 14:03:58 -0400 Subject: [PATCH] Origin UI 72 - Memberhip --- autocomplete/rhc_bash | 384 ++++++++++++------ {features => cucumber}/README.md | 14 +- {features => cucumber}/application.feature | 0 {features => cucumber}/cartridge.feature | 0 {features => cucumber}/client.feature | 0 {features => cucumber}/domain.feature | 0 {features => cucumber}/env.feature | 0 .../geared_application.feature | 0 {features => cucumber}/lib/rhc_helper.rb | 0 {features => cucumber}/lib/rhc_helper/api.rb | 0 {features => cucumber}/lib/rhc_helper/app.rb | 0 .../lib/rhc_helper/cartridge.rb | 0 .../lib/rhc_helper/commandify.rb | 0 .../lib/rhc_helper/domain.rb | 0 {features => cucumber}/lib/rhc_helper/env.rb | 0 .../lib/rhc_helper/httpify.rb | 0 .../lib/rhc_helper/loggable.rb | 0 .../lib/rhc_helper/persistable.rb | 0 .../lib/rhc_helper/runnable.rb | 0 .../lib/rhc_helper/sshkey.rb | 0 .../multiple_cartridge.feature | 0 .../scaled_application.feature | 0 {features => cucumber}/sshkey.feature | 0 .../step_definitions/application_steps.rb | 0 .../step_definitions/cartridge_steps.rb | 0 .../step_definitions/client_steps.rb | 0 .../step_definitions/domain_steps.rb | 0 .../step_definitions/env_steps.rb | 0 .../step_definitions/sshkey_steps.rb | 0 {features => cucumber}/support/assumptions.rb | 0 .../support/before_hooks.rb | 0 {features => cucumber}/support/env.rb | 0 {features => cucumber}/support/key1 | 0 {features => cucumber}/support/key1.pub | 0 {features => cucumber}/support/key2 | 0 {features => cucumber}/support/key2.pub | 0 {features => cucumber}/support/key3.pub | 0 .../support/platform_support.rb | 0 {features => cucumber}/verify.feature | 0 features/core_feature.rb | 141 +++++++ features/domains_feature.rb | 45 ++ features/members_feature.rb | 131 ++++++ lib/rhc/auth/token_store.rb | 2 + lib/rhc/commands.rb | 59 +-- lib/rhc/commands/account.rb | 7 +- lib/rhc/commands/alias.rb | 25 +- lib/rhc/commands/app.rb | 98 ++--- lib/rhc/commands/authorization.rb | 2 +- lib/rhc/commands/base.rb | 17 +- lib/rhc/commands/cartridge.rb | 50 +-- lib/rhc/commands/domain.rb | 118 ++++-- lib/rhc/commands/env.rb | 26 +- lib/rhc/commands/git_clone.rb | 5 +- lib/rhc/commands/member.rb | 148 +++++++ lib/rhc/commands/port_forward.rb | 5 +- lib/rhc/commands/snapshot.rb | 27 +- lib/rhc/commands/ssh.rb | 19 +- lib/rhc/commands/tail.rb | 5 +- lib/rhc/commands/threaddump.rb | 5 +- lib/rhc/config.rb | 1 + lib/rhc/context_helper.rb | 106 ++++- lib/rhc/core_ext.rb | 6 + lib/rhc/coverage_helper.rb | 2 +- lib/rhc/exceptions.rb | 12 + lib/rhc/git_helpers.rb | 11 +- lib/rhc/helpers.rb | 29 +- lib/rhc/output_helpers.rb | 7 +- lib/rhc/rest.rb | 1 + lib/rhc/rest/application.rb | 14 +- lib/rhc/rest/attributes.rb | 4 + lib/rhc/rest/base.rb | 3 +- lib/rhc/rest/cartridge.rb | 4 + lib/rhc/rest/client.rb | 64 +-- lib/rhc/rest/domain.rb | 25 +- lib/rhc/rest/membership.rb | 105 +++++ lib/rhc/rest/mock.rb | 47 ++- lib/rhc/rest/user.rb | 6 +- .../usage_templates/command_syntax_help.erb | 2 + spec/coverage_helper.rb | 40 +- spec/direct_execution_helper.rb | 242 +++++++++++ spec/rhc/command_spec.rb | 64 ++- spec/rhc/commands/account_spec.rb | 3 +- spec/rhc/commands/alias_spec.rb | 7 + spec/rhc/commands/app_spec.rb | 151 +++---- spec/rhc/commands/cartridge_spec.rb | 120 +++--- spec/rhc/commands/domain_spec.rb | 157 +++++-- spec/rhc/commands/member_spec.rb | 228 +++++++++++ spec/rhc/commands/port_forward_spec.rb | 2 +- spec/rhc/commands/snapshot_spec.rb | 40 +- spec/rhc/commands/ssh_spec.rb | 13 + spec/rhc/commands/tail_spec.rb | 2 +- spec/rhc/helpers_spec.rb | 9 + spec/rhc/rest_client_spec.rb | 68 ++++ spec/rhc/rest_spec.rb | 2 +- tasks/cucumber.rake | 17 +- 95 files changed, 2284 insertions(+), 663 deletions(-) rename {features => cucumber}/README.md (94%) rename {features => cucumber}/application.feature (100%) rename {features => cucumber}/cartridge.feature (100%) rename {features => cucumber}/client.feature (100%) rename {features => cucumber}/domain.feature (100%) rename {features => cucumber}/env.feature (100%) rename {features => cucumber}/geared_application.feature (100%) rename {features => cucumber}/lib/rhc_helper.rb (100%) rename {features => cucumber}/lib/rhc_helper/api.rb (100%) rename {features => cucumber}/lib/rhc_helper/app.rb (100%) rename {features => cucumber}/lib/rhc_helper/cartridge.rb (100%) rename {features => cucumber}/lib/rhc_helper/commandify.rb (100%) rename {features => cucumber}/lib/rhc_helper/domain.rb (100%) rename {features => cucumber}/lib/rhc_helper/env.rb (100%) rename {features => cucumber}/lib/rhc_helper/httpify.rb (100%) rename {features => cucumber}/lib/rhc_helper/loggable.rb (100%) rename {features => cucumber}/lib/rhc_helper/persistable.rb (100%) rename {features => cucumber}/lib/rhc_helper/runnable.rb (100%) rename {features => cucumber}/lib/rhc_helper/sshkey.rb (100%) rename {features => cucumber}/multiple_cartridge.feature (100%) rename {features => cucumber}/scaled_application.feature (100%) rename {features => cucumber}/sshkey.feature (100%) rename {features => cucumber}/step_definitions/application_steps.rb (100%) rename {features => cucumber}/step_definitions/cartridge_steps.rb (100%) rename {features => cucumber}/step_definitions/client_steps.rb (100%) rename {features => cucumber}/step_definitions/domain_steps.rb (100%) rename {features => cucumber}/step_definitions/env_steps.rb (100%) rename {features => cucumber}/step_definitions/sshkey_steps.rb (100%) rename {features => cucumber}/support/assumptions.rb (100%) rename {features => cucumber}/support/before_hooks.rb (100%) rename {features => cucumber}/support/env.rb (100%) rename {features => cucumber}/support/key1 (100%) rename {features => cucumber}/support/key1.pub (100%) rename {features => cucumber}/support/key2 (100%) rename {features => cucumber}/support/key2.pub (100%) rename {features => cucumber}/support/key3.pub (100%) rename {features => cucumber}/support/platform_support.rb (100%) rename {features => cucumber}/verify.feature (100%) create mode 100644 features/core_feature.rb create mode 100644 features/domains_feature.rb create mode 100644 features/members_feature.rb create mode 100644 lib/rhc/commands/member.rb create mode 100644 lib/rhc/rest/membership.rb create mode 100644 spec/direct_execution_helper.rb create mode 100644 spec/rhc/commands/member_spec.rb diff --git a/autocomplete/rhc_bash b/autocomplete/rhc_bash index d88d00e60..1c1bfd21d 100644 --- a/autocomplete/rhc_bash +++ b/autocomplete/rhc_bash @@ -10,9 +10,9 @@ _rhc() if [[ "$cur" == -* ]]; then opts="--always-prefix --clean --config --debug --insecure --limit --mock --noprompt --password --raw --rhlogin --server --ssl-ca-file --ssl-client-cert-file --ssl-version --timeout --token" elif [ -z $cur ]; then - opts="account alias alias-add alias-delete-cert alias-list alias-remove alias-update-cert app app-create app-delete app-force-stop app-reload app-restart app-show app-start app-stop app-tidy apps authorization authorization-add authorization-delete authorization-delete-all authorization-list cartridge-add cartridge-list cartridge-reload cartridge-remove cartridge-restart cartridge-scale cartridge-show cartridge-start cartridge-status cartridge-stop cartridge-storage domain domain-create domain-delete domain-list domain-show domain-update env env-list env-set env-show env-unset git-clone logout port-forward server setup snapshot snapshot-restore snapshot-save ssh sshkey sshkey-add sshkey-list sshkey-remove sshkey-show tail threaddump" + opts="account alias alias-add alias-delete-cert alias-list alias-remove alias-update-cert app app-create app-delete app-force-stop app-reload app-restart app-show app-start app-stop app-tidy apps authorization authorization-add authorization-delete authorization-delete-all authorization-list cartridge cartridge-add cartridge-list cartridge-reload cartridge-remove cartridge-restart cartridge-scale cartridge-show cartridge-start cartridge-status cartridge-stop cartridge-storage domain domain-configure domain-create domain-delete domain-leave domain-list domain-rename domain-show env env-list env-set env-show env-unset git-clone logout member member-add member-list member-remove port-forward server setup snapshot snapshot-restore snapshot-save ssh sshkey sshkey-add sshkey-list sshkey-remove sshkey-show tail threaddump" else - opts="account account-logout add-alias add-authorization add-cartridge add-sshkey alias alias-add alias-delete-cert alias-list alias-remove alias-update-cert aliases app app-create app-delete app-env app-force-stop app-reload app-restart app-show app-snapshot app-ssh app-start app-stop app-tidy apps authorization authorization-add authorization-delete authorization-delete-all authorization-list authorizations cartridge cartridge-add cartridge-list cartridge-reload cartridge-remove cartridge-restart cartridge-scale cartridge-show cartridge-start cartridge-status cartridge-stop cartridge-storage cartridges create-app create-domain delete-all-authorization delete-app delete-authorization delete-cert-alias delete-domain domain domain-create domain-delete domain-list domain-show domain-update domains env env-add env-list env-remove env-set env-show env-unset force-stop-app git-clone list-alias list-authorization list-cartridge list-domain list-env list-sshkey logout port-forward reload-app reload-cartridge remove-alias remove-cartridge remove-sshkey restart-app restart-cartridge restore-snapshot save-snapshot scale-cartridge server set-env setup show-app show-cartridge show-domain show-env show-sshkey snapshot snapshot-restore snapshot-save ssh sshkey sshkey-add sshkey-list sshkey-remove sshkey-show start-app start-cartridge status-cartridge stop-app stop-cartridge storage-cartridge tail threaddump tidy-app unset-env update-cert-alias update-domain" + opts="account account-logout add-alias add-authorization add-cartridge add-member add-sshkey alias alias-add alias-delete-cert alias-list alias-remove alias-update-cert aliases app app-create app-delete app-env app-force-stop app-reload app-restart app-show app-snapshot app-ssh app-start app-stop app-tidy apps authorization authorization-add authorization-delete authorization-delete-all authorization-list authorizations cartridge cartridge-add cartridge-list cartridge-reload cartridge-remove cartridge-restart cartridge-scale cartridge-show cartridge-start cartridge-status cartridge-stop cartridge-storage cartridges configure-domain create-app create-domain delete-all-authorization delete-app delete-authorization delete-cert-alias delete-domain domain domain-configure domain-create domain-delete domain-leave domain-list domain-rename domain-show domains env env-add env-list env-remove env-set env-show env-unset force-stop-app git-clone leave-domain list-alias list-authorization list-cartridge list-domain list-env list-member list-sshkey logout member member-add member-list member-remove members port-forward reload-app reload-cartridge remove-alias remove-cartridge remove-member remove-sshkey rename-domain restart-app restart-cartridge restore-snapshot save-snapshot scale-cartridge server set-env setup show-app show-cartridge show-domain show-env show-sshkey snapshot snapshot-restore snapshot-save ssh sshkey sshkey-add sshkey-list sshkey-remove sshkey-show start-app start-cartridge status-cartridge stop-app stop-cartridge storage-cartridge tail threaddump tidy-app unset-env update-cert-alias" fi else prev="${COMP_WORDS[@]:0:COMP_CWORD}" @@ -46,7 +46,7 @@ _rhc() "rhc add-alias") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -62,7 +62,15 @@ _rhc() "rhc add-cartridge") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --env --namespace" + opts="--app --application-id --cartridge --env --namespace" + else + opts="" + fi + ;; + + "rhc add-member") + if [[ "$cur" == -* ]]; then + opts="--ids --namespace --role" else opts="" fi @@ -86,7 +94,7 @@ _rhc() "rhc alias add") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -94,7 +102,7 @@ _rhc() "rhc alias delete-cert") if [[ "$cur" == -* ]]; then - opts="--app --confirm --namespace" + opts="--app --application-id --confirm --namespace" else opts="" fi @@ -102,7 +110,7 @@ _rhc() "rhc alias list") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -110,7 +118,7 @@ _rhc() "rhc alias remove") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -118,7 +126,7 @@ _rhc() "rhc alias update-cert") if [[ "$cur" == -* ]]; then - opts="--app --certificate --namespace --passphrase --private-key" + opts="--app --application-id --certificate --namespace --passphrase --private-key" else opts="" fi @@ -126,7 +134,7 @@ _rhc() "rhc alias-add") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -134,7 +142,7 @@ _rhc() "rhc alias-delete-cert") if [[ "$cur" == -* ]]; then - opts="--app --confirm --namespace" + opts="--app --application-id --confirm --namespace" else opts="" fi @@ -142,7 +150,7 @@ _rhc() "rhc alias-list") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -150,7 +158,7 @@ _rhc() "rhc alias-remove") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -158,7 +166,7 @@ _rhc() "rhc alias-update-cert") if [[ "$cur" == -* ]]; then - opts="--app --certificate --namespace --passphrase --private-key" + opts="--app --application-id --certificate --namespace --passphrase --private-key" else opts="" fi @@ -166,7 +174,7 @@ _rhc() "rhc aliases") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -182,7 +190,7 @@ _rhc() "rhc app create") if [[ "$cur" == -* ]]; then - opts="--app --dns --enable-jenkins --env --from-code --gear-size --git --namespace --no-dns --no-git --no-keys --nogit --repo --scaling --type" + opts="--app --dns --enable-jenkins --env --from-code --gear-size --git --namespace --no-dns --no-git --no-keys --repo --scaling --type" else opts="" fi @@ -190,7 +198,7 @@ _rhc() "rhc app delete") if [[ "$cur" == -* ]]; then - opts="--app --bypass --confirm --namespace" + opts="--app --application-id --confirm --namespace" else opts="" fi @@ -198,7 +206,7 @@ _rhc() "rhc app env") if [[ "$cur" == -* ]]; then - opts="--app --confirm --env --namespace" + opts="--app --application-id --confirm --env --namespace" else opts="" fi @@ -206,7 +214,7 @@ _rhc() "rhc app force-stop") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -214,7 +222,7 @@ _rhc() "rhc app reload") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -222,7 +230,7 @@ _rhc() "rhc app restart") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -230,7 +238,7 @@ _rhc() "rhc app show") if [[ "$cur" == -* ]]; then - opts="--app --gears --namespace --state" + opts="--app --application-id --gears --namespace --state" else opts="" fi @@ -238,7 +246,7 @@ _rhc() "rhc app snapshot") if [[ "$cur" == -* ]]; then - opts="--app --filepath --namespace" + opts="--app --application-id --filepath --namespace" else opts="" fi @@ -246,7 +254,7 @@ _rhc() "rhc app ssh") if [[ "$cur" == -* ]]; then - opts="--app --gears --limit --namespace --raw --ssh" + opts="--app --application-id --gears --limit --namespace --raw --ssh" else opts="" fi @@ -254,7 +262,7 @@ _rhc() "rhc app start") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -262,7 +270,7 @@ _rhc() "rhc app stop") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -270,7 +278,7 @@ _rhc() "rhc app tidy") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -278,7 +286,7 @@ _rhc() "rhc app-create") if [[ "$cur" == -* ]]; then - opts="--app --dns --enable-jenkins --env --from-code --gear-size --git --namespace --no-dns --no-git --no-keys --nogit --repo --scaling --type" + opts="--app --dns --enable-jenkins --env --from-code --gear-size --git --namespace --no-dns --no-git --no-keys --repo --scaling --type" else opts="" fi @@ -286,7 +294,7 @@ _rhc() "rhc app-delete") if [[ "$cur" == -* ]]; then - opts="--app --bypass --confirm --namespace" + opts="--app --application-id --confirm --namespace" else opts="" fi @@ -294,7 +302,7 @@ _rhc() "rhc app-env") if [[ "$cur" == -* ]]; then - opts="--app --confirm --env --namespace" + opts="--app --application-id --confirm --env --namespace" else opts="" fi @@ -302,7 +310,7 @@ _rhc() "rhc app-force-stop") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -310,7 +318,7 @@ _rhc() "rhc app-reload") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -318,7 +326,7 @@ _rhc() "rhc app-restart") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -326,7 +334,7 @@ _rhc() "rhc app-show") if [[ "$cur" == -* ]]; then - opts="--app --gears --namespace --state" + opts="--app --application-id --gears --namespace --state" else opts="" fi @@ -334,7 +342,7 @@ _rhc() "rhc app-snapshot") if [[ "$cur" == -* ]]; then - opts="--app --filepath --namespace" + opts="--app --application-id --filepath --namespace" else opts="" fi @@ -342,7 +350,7 @@ _rhc() "rhc app-ssh") if [[ "$cur" == -* ]]; then - opts="--app --gears --limit --namespace --raw --ssh" + opts="--app --application-id --gears --limit --namespace --raw --ssh" else opts="" fi @@ -350,7 +358,7 @@ _rhc() "rhc app-start") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -358,7 +366,7 @@ _rhc() "rhc app-stop") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -366,7 +374,7 @@ _rhc() "rhc app-tidy") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -470,7 +478,7 @@ _rhc() "rhc cartridge add") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --env --namespace" + opts="--app --application-id --cartridge --env --namespace" else opts="" fi @@ -486,7 +494,7 @@ _rhc() "rhc cartridge reload") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --namespace" + opts="--app --application-id --cartridge --namespace" else opts="" fi @@ -494,7 +502,7 @@ _rhc() "rhc cartridge remove") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --confirm --namespace" + opts="--app --application-id --cartridge --confirm --namespace" else opts="" fi @@ -502,7 +510,7 @@ _rhc() "rhc cartridge restart") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --namespace" + opts="--app --application-id --cartridge --namespace" else opts="" fi @@ -510,7 +518,7 @@ _rhc() "rhc cartridge scale") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --max --min --namespace" + opts="--app --application-id --cartridge --max --min --namespace" else opts="" fi @@ -518,7 +526,7 @@ _rhc() "rhc cartridge show") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --namespace" + opts="--app --application-id --cartridge --namespace" else opts="" fi @@ -526,7 +534,7 @@ _rhc() "rhc cartridge start") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --namespace" + opts="--app --application-id --cartridge --namespace" else opts="" fi @@ -534,7 +542,7 @@ _rhc() "rhc cartridge status") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --namespace" + opts="--app --application-id --cartridge --namespace" else opts="" fi @@ -542,7 +550,7 @@ _rhc() "rhc cartridge stop") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --namespace" + opts="--app --application-id --cartridge --namespace" else opts="" fi @@ -550,7 +558,7 @@ _rhc() "rhc cartridge storage") if [[ "$cur" == -* ]]; then - opts="--add --app --cartridge --force --namespace --remove --set --show" + opts="--add --app --application-id --cartridge --force --namespace --remove --set --show" else opts="" fi @@ -558,7 +566,7 @@ _rhc() "rhc cartridge-add") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --env --namespace" + opts="--app --application-id --cartridge --env --namespace" else opts="" fi @@ -574,7 +582,7 @@ _rhc() "rhc cartridge-reload") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --namespace" + opts="--app --application-id --cartridge --namespace" else opts="" fi @@ -582,7 +590,7 @@ _rhc() "rhc cartridge-remove") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --confirm --namespace" + opts="--app --application-id --cartridge --confirm --namespace" else opts="" fi @@ -590,7 +598,7 @@ _rhc() "rhc cartridge-restart") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --namespace" + opts="--app --application-id --cartridge --namespace" else opts="" fi @@ -598,7 +606,7 @@ _rhc() "rhc cartridge-scale") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --max --min --namespace" + opts="--app --application-id --cartridge --max --min --namespace" else opts="" fi @@ -606,7 +614,7 @@ _rhc() "rhc cartridge-show") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --namespace" + opts="--app --application-id --cartridge --namespace" else opts="" fi @@ -614,7 +622,7 @@ _rhc() "rhc cartridge-start") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --namespace" + opts="--app --application-id --cartridge --namespace" else opts="" fi @@ -622,7 +630,7 @@ _rhc() "rhc cartridge-status") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --namespace" + opts="--app --application-id --cartridge --namespace" else opts="" fi @@ -630,7 +638,7 @@ _rhc() "rhc cartridge-stop") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --namespace" + opts="--app --application-id --cartridge --namespace" else opts="" fi @@ -638,7 +646,7 @@ _rhc() "rhc cartridge-storage") if [[ "$cur" == -* ]]; then - opts="--add --app --cartridge --force --namespace --remove --set --show" + opts="--add --app --application-id --cartridge --force --namespace --remove --set --show" else opts="" fi @@ -652,9 +660,17 @@ _rhc() fi ;; + "rhc configure-domain") + if [[ "$cur" == -* ]]; then + opts="--allowed-gear-sizes --namespace --no-allowed-gear-sizes" + else + opts="" + fi + ;; + "rhc create-app") if [[ "$cur" == -* ]]; then - opts="--app --dns --enable-jenkins --env --from-code --gear-size --git --namespace --no-dns --no-git --no-keys --nogit --repo --scaling --type" + opts="--app --dns --enable-jenkins --env --from-code --gear-size --git --namespace --no-dns --no-git --no-keys --repo --scaling --type" else opts="" fi @@ -662,7 +678,7 @@ _rhc() "rhc create-domain") if [[ "$cur" == -* ]]; then - opts="--namespace" + opts="--allowed-gear-sizes --namespace --no-allowed-gear-sizes" else opts="" fi @@ -678,7 +694,7 @@ _rhc() "rhc delete-app") if [[ "$cur" == -* ]]; then - opts="--app --bypass --confirm --namespace" + opts="--app --application-id --confirm --namespace" else opts="" fi @@ -694,7 +710,7 @@ _rhc() "rhc delete-cert-alias") if [[ "$cur" == -* ]]; then - opts="--app --confirm --namespace" + opts="--app --application-id --confirm --namespace" else opts="" fi @@ -712,13 +728,21 @@ _rhc() if [[ "$cur" == -* ]]; then opts="" else - opts="create update show list delete" + opts="create rename configure show list delete leave" + fi + ;; + + "rhc domain configure") + if [[ "$cur" == -* ]]; then + opts="--allowed-gear-sizes --namespace --no-allowed-gear-sizes" + else + opts="" fi ;; "rhc domain create") if [[ "$cur" == -* ]]; then - opts="--namespace" + opts="--allowed-gear-sizes --namespace --no-allowed-gear-sizes" else opts="" fi @@ -732,6 +756,14 @@ _rhc() fi ;; + "rhc domain leave") + if [[ "$cur" == -* ]]; then + opts="--namespace" + else + opts="" + fi + ;; + "rhc domain list") if [[ "$cur" == -* ]]; then opts="--mine" @@ -740,7 +772,7 @@ _rhc() fi ;; - "rhc domain show") + "rhc domain rename") if [[ "$cur" == -* ]]; then opts="--namespace" else @@ -748,7 +780,7 @@ _rhc() fi ;; - "rhc domain update") + "rhc domain show") if [[ "$cur" == -* ]]; then opts="--namespace" else @@ -756,9 +788,17 @@ _rhc() fi ;; + "rhc domain-configure") + if [[ "$cur" == -* ]]; then + opts="--allowed-gear-sizes --namespace --no-allowed-gear-sizes" + else + opts="" + fi + ;; + "rhc domain-create") if [[ "$cur" == -* ]]; then - opts="--namespace" + opts="--allowed-gear-sizes --namespace --no-allowed-gear-sizes" else opts="" fi @@ -772,6 +812,14 @@ _rhc() fi ;; + "rhc domain-leave") + if [[ "$cur" == -* ]]; then + opts="--namespace" + else + opts="" + fi + ;; + "rhc domain-list") if [[ "$cur" == -* ]]; then opts="--mine" @@ -780,7 +828,7 @@ _rhc() fi ;; - "rhc domain-show") + "rhc domain-rename") if [[ "$cur" == -* ]]; then opts="--namespace" else @@ -788,7 +836,7 @@ _rhc() fi ;; - "rhc domain-update") + "rhc domain-show") if [[ "$cur" == -* ]]; then opts="--namespace" else @@ -814,7 +862,7 @@ _rhc() "rhc env add") if [[ "$cur" == -* ]]; then - opts="--app --confirm --env --namespace" + opts="--app --application-id --confirm --env --namespace" else opts="" fi @@ -822,7 +870,7 @@ _rhc() "rhc env list") if [[ "$cur" == -* ]]; then - opts="--app --namespace --quotes --table" + opts="--app --application-id --namespace --quotes --table" else opts="" fi @@ -830,7 +878,7 @@ _rhc() "rhc env remove") if [[ "$cur" == -* ]]; then - opts="--app --confirm --env --namespace" + opts="--app --application-id --confirm --env --namespace" else opts="" fi @@ -838,7 +886,7 @@ _rhc() "rhc env set") if [[ "$cur" == -* ]]; then - opts="--app --confirm --env --namespace" + opts="--app --application-id --confirm --env --namespace" else opts="" fi @@ -846,7 +894,7 @@ _rhc() "rhc env show") if [[ "$cur" == -* ]]; then - opts="--app --env --namespace --quotes --table" + opts="--app --application-id --env --namespace --quotes --table" else opts="" fi @@ -854,7 +902,7 @@ _rhc() "rhc env unset") if [[ "$cur" == -* ]]; then - opts="--app --confirm --env --namespace" + opts="--app --application-id --confirm --env --namespace" else opts="" fi @@ -862,7 +910,7 @@ _rhc() "rhc env-add") if [[ "$cur" == -* ]]; then - opts="--app --confirm --env --namespace" + opts="--app --application-id --confirm --env --namespace" else opts="" fi @@ -870,7 +918,7 @@ _rhc() "rhc env-list") if [[ "$cur" == -* ]]; then - opts="--app --namespace --quotes --table" + opts="--app --application-id --namespace --quotes --table" else opts="" fi @@ -878,7 +926,7 @@ _rhc() "rhc env-remove") if [[ "$cur" == -* ]]; then - opts="--app --confirm --env --namespace" + opts="--app --application-id --confirm --env --namespace" else opts="" fi @@ -886,7 +934,7 @@ _rhc() "rhc env-set") if [[ "$cur" == -* ]]; then - opts="--app --confirm --env --namespace" + opts="--app --application-id --confirm --env --namespace" else opts="" fi @@ -894,7 +942,7 @@ _rhc() "rhc env-show") if [[ "$cur" == -* ]]; then - opts="--app --env --namespace --quotes --table" + opts="--app --application-id --env --namespace --quotes --table" else opts="" fi @@ -902,7 +950,7 @@ _rhc() "rhc env-unset") if [[ "$cur" == -* ]]; then - opts="--app --confirm --env --namespace" + opts="--app --application-id --confirm --env --namespace" else opts="" fi @@ -910,7 +958,7 @@ _rhc() "rhc force-stop-app") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -918,7 +966,15 @@ _rhc() "rhc git-clone") if [[ "$cur" == -* ]]; then - opts="--app --namespace --repo" + opts="--app --application-id --namespace --repo" + else + opts="" + fi + ;; + + "rhc leave-domain") + if [[ "$cur" == -* ]]; then + opts="--namespace" else opts="" fi @@ -926,7 +982,7 @@ _rhc() "rhc list-alias") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -958,7 +1014,15 @@ _rhc() "rhc list-env") if [[ "$cur" == -* ]]; then - opts="--app --namespace --quotes --table" + opts="--app --application-id --namespace --quotes --table" + else + opts="" + fi + ;; + + "rhc list-member") + if [[ "$cur" == -* ]]; then + opts="--app --ids --namespace --target" else opts="" fi @@ -980,9 +1044,73 @@ _rhc() fi ;; + "rhc member") + if [[ "$cur" == -* ]]; then + opts="" + else + opts="list add remove" + fi + ;; + + "rhc member add") + if [[ "$cur" == -* ]]; then + opts="--ids --namespace --role" + else + opts="" + fi + ;; + + "rhc member list") + if [[ "$cur" == -* ]]; then + opts="--app --ids --namespace --target" + else + opts="" + fi + ;; + + "rhc member remove") + if [[ "$cur" == -* ]]; then + opts="--all --ids --namespace" + else + opts="" + fi + ;; + + "rhc member-add") + if [[ "$cur" == -* ]]; then + opts="--ids --namespace --role" + else + opts="" + fi + ;; + + "rhc member-list") + if [[ "$cur" == -* ]]; then + opts="--app --ids --namespace --target" + else + opts="" + fi + ;; + + "rhc member-remove") + if [[ "$cur" == -* ]]; then + opts="--all --ids --namespace" + else + opts="" + fi + ;; + + "rhc members") + if [[ "$cur" == -* ]]; then + opts="--app --ids --namespace --target" + else + opts="" + fi + ;; + "rhc port-forward") if [[ "$cur" == -* ]]; then - opts="--app --gear --namespace" + opts="--app --application-id --gear --namespace" else opts="" fi @@ -990,7 +1118,7 @@ _rhc() "rhc reload-app") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -998,7 +1126,7 @@ _rhc() "rhc reload-cartridge") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --namespace" + opts="--app --application-id --cartridge --namespace" else opts="" fi @@ -1006,7 +1134,7 @@ _rhc() "rhc remove-alias") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -1014,7 +1142,15 @@ _rhc() "rhc remove-cartridge") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --confirm --namespace" + opts="--app --application-id --cartridge --confirm --namespace" + else + opts="" + fi + ;; + + "rhc remove-member") + if [[ "$cur" == -* ]]; then + opts="--all --ids --namespace" else opts="" fi @@ -1028,9 +1164,17 @@ _rhc() fi ;; + "rhc rename-domain") + if [[ "$cur" == -* ]]; then + opts="--namespace" + else + opts="" + fi + ;; + "rhc restart-app") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -1038,7 +1182,7 @@ _rhc() "rhc restart-cartridge") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --namespace" + opts="--app --application-id --cartridge --namespace" else opts="" fi @@ -1046,7 +1190,7 @@ _rhc() "rhc restore-snapshot") if [[ "$cur" == -* ]]; then - opts="--app --filepath --namespace" + opts="--app --application-id --filepath --namespace" else opts="" fi @@ -1054,7 +1198,7 @@ _rhc() "rhc save-snapshot") if [[ "$cur" == -* ]]; then - opts="--app --filepath --namespace" + opts="--app --application-id --filepath --namespace" else opts="" fi @@ -1062,7 +1206,7 @@ _rhc() "rhc scale-cartridge") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --max --min --namespace" + opts="--app --application-id --cartridge --max --min --namespace" else opts="" fi @@ -1078,7 +1222,7 @@ _rhc() "rhc set-env") if [[ "$cur" == -* ]]; then - opts="--app --confirm --env --namespace" + opts="--app --application-id --confirm --env --namespace" else opts="" fi @@ -1094,7 +1238,7 @@ _rhc() "rhc show-app") if [[ "$cur" == -* ]]; then - opts="--app --gears --namespace --state" + opts="--app --application-id --gears --namespace --state" else opts="" fi @@ -1102,7 +1246,7 @@ _rhc() "rhc show-cartridge") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --namespace" + opts="--app --application-id --cartridge --namespace" else opts="" fi @@ -1118,7 +1262,7 @@ _rhc() "rhc show-env") if [[ "$cur" == -* ]]; then - opts="--app --env --namespace --quotes --table" + opts="--app --application-id --env --namespace --quotes --table" else opts="" fi @@ -1142,7 +1286,7 @@ _rhc() "rhc snapshot restore") if [[ "$cur" == -* ]]; then - opts="--app --filepath --namespace" + opts="--app --application-id --filepath --namespace" else opts="" fi @@ -1150,7 +1294,7 @@ _rhc() "rhc snapshot save") if [[ "$cur" == -* ]]; then - opts="--app --filepath --namespace" + opts="--app --application-id --filepath --namespace" else opts="" fi @@ -1158,7 +1302,7 @@ _rhc() "rhc snapshot-restore") if [[ "$cur" == -* ]]; then - opts="--app --filepath --namespace" + opts="--app --application-id --filepath --namespace" else opts="" fi @@ -1166,7 +1310,7 @@ _rhc() "rhc snapshot-save") if [[ "$cur" == -* ]]; then - opts="--app --filepath --namespace" + opts="--app --application-id --filepath --namespace" else opts="" fi @@ -1174,7 +1318,7 @@ _rhc() "rhc ssh") if [[ "$cur" == -* ]]; then - opts="--app --gears --limit --namespace --raw --ssh" + opts="--app --application-id --gears --limit --namespace --raw --ssh" else opts="" fi @@ -1254,7 +1398,7 @@ _rhc() "rhc start-app") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -1262,7 +1406,7 @@ _rhc() "rhc start-cartridge") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --namespace" + opts="--app --application-id --cartridge --namespace" else opts="" fi @@ -1270,7 +1414,7 @@ _rhc() "rhc status-cartridge") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --namespace" + opts="--app --application-id --cartridge --namespace" else opts="" fi @@ -1278,7 +1422,7 @@ _rhc() "rhc stop-app") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -1286,7 +1430,7 @@ _rhc() "rhc stop-cartridge") if [[ "$cur" == -* ]]; then - opts="--app --cartridge --namespace" + opts="--app --application-id --cartridge --namespace" else opts="" fi @@ -1294,7 +1438,7 @@ _rhc() "rhc storage-cartridge") if [[ "$cur" == -* ]]; then - opts="--add --app --cartridge --force --namespace --remove --set --show" + opts="--add --app --application-id --cartridge --force --namespace --remove --set --show" else opts="" fi @@ -1302,7 +1446,7 @@ _rhc() "rhc tail") if [[ "$cur" == -* ]]; then - opts="--app --files --gear --namespace --opts" + opts="--app --application-id --files --gear --namespace --opts" else opts="" fi @@ -1310,7 +1454,7 @@ _rhc() "rhc threaddump") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -1318,7 +1462,7 @@ _rhc() "rhc tidy-app") if [[ "$cur" == -* ]]; then - opts="--app --namespace" + opts="--app --application-id --namespace" else opts="" fi @@ -1326,7 +1470,7 @@ _rhc() "rhc unset-env") if [[ "$cur" == -* ]]; then - opts="--app --confirm --env --namespace" + opts="--app --application-id --confirm --env --namespace" else opts="" fi @@ -1334,15 +1478,7 @@ _rhc() "rhc update-cert-alias") if [[ "$cur" == -* ]]; then - opts="--app --certificate --namespace --passphrase --private-key" - else - opts="" - fi - ;; - - "rhc update-domain") - if [[ "$cur" == -* ]]; then - opts="--namespace" + opts="--app --application-id --certificate --namespace --passphrase --private-key" else opts="" fi diff --git a/features/README.md b/cucumber/README.md similarity index 94% rename from features/README.md rename to cucumber/README.md index b3ba891a2..b3ba54177 100644 --- a/features/README.md +++ b/cucumber/README.md @@ -1,3 +1,7 @@ +DEPRECATED +========= +Cucumber is being phased out - please add RSpec style features to the features/* directory + Overview ============== @@ -14,7 +18,7 @@ Usage Run from the base directory with ``` - bundle exec rake features + bundle exec rake cucummber ``` At the very least, you will probably want to specify `RHC_SERVER` (or @@ -46,7 +50,7 @@ proxy. For instance: GIT_SSH ------- -This is automatically set in `features/support/env.rb` but can be +This is automatically set in `cucumber/support/env.rb` but can be overridden if desired. This environment variable will be used by any `git` or `ssh` commands. @@ -110,7 +114,7 @@ reporting. To run the test, simply run ``` - bundle exec rake features + bundle exec rake cucumber ``` 1. Running the tests directly via `cucumber` gives you some more @@ -122,9 +126,9 @@ here](https://github.com/cucumber/cucumber/wiki/Running-Features). ``` # This runs all scenarios with the @application tag that also do not # have the @init tag - cucumber features -t @application -t ~@init + cucumber cucumber -t @application -t ~@init # This runs the scenario starting at a specific line in the file - cucumber features/application.feature:42 + cucumber cucumber/application.feature:42 ``` Developing tests diff --git a/features/application.feature b/cucumber/application.feature similarity index 100% rename from features/application.feature rename to cucumber/application.feature diff --git a/features/cartridge.feature b/cucumber/cartridge.feature similarity index 100% rename from features/cartridge.feature rename to cucumber/cartridge.feature diff --git a/features/client.feature b/cucumber/client.feature similarity index 100% rename from features/client.feature rename to cucumber/client.feature diff --git a/features/domain.feature b/cucumber/domain.feature similarity index 100% rename from features/domain.feature rename to cucumber/domain.feature diff --git a/features/env.feature b/cucumber/env.feature similarity index 100% rename from features/env.feature rename to cucumber/env.feature diff --git a/features/geared_application.feature b/cucumber/geared_application.feature similarity index 100% rename from features/geared_application.feature rename to cucumber/geared_application.feature diff --git a/features/lib/rhc_helper.rb b/cucumber/lib/rhc_helper.rb similarity index 100% rename from features/lib/rhc_helper.rb rename to cucumber/lib/rhc_helper.rb diff --git a/features/lib/rhc_helper/api.rb b/cucumber/lib/rhc_helper/api.rb similarity index 100% rename from features/lib/rhc_helper/api.rb rename to cucumber/lib/rhc_helper/api.rb diff --git a/features/lib/rhc_helper/app.rb b/cucumber/lib/rhc_helper/app.rb similarity index 100% rename from features/lib/rhc_helper/app.rb rename to cucumber/lib/rhc_helper/app.rb diff --git a/features/lib/rhc_helper/cartridge.rb b/cucumber/lib/rhc_helper/cartridge.rb similarity index 100% rename from features/lib/rhc_helper/cartridge.rb rename to cucumber/lib/rhc_helper/cartridge.rb diff --git a/features/lib/rhc_helper/commandify.rb b/cucumber/lib/rhc_helper/commandify.rb similarity index 100% rename from features/lib/rhc_helper/commandify.rb rename to cucumber/lib/rhc_helper/commandify.rb diff --git a/features/lib/rhc_helper/domain.rb b/cucumber/lib/rhc_helper/domain.rb similarity index 100% rename from features/lib/rhc_helper/domain.rb rename to cucumber/lib/rhc_helper/domain.rb diff --git a/features/lib/rhc_helper/env.rb b/cucumber/lib/rhc_helper/env.rb similarity index 100% rename from features/lib/rhc_helper/env.rb rename to cucumber/lib/rhc_helper/env.rb diff --git a/features/lib/rhc_helper/httpify.rb b/cucumber/lib/rhc_helper/httpify.rb similarity index 100% rename from features/lib/rhc_helper/httpify.rb rename to cucumber/lib/rhc_helper/httpify.rb diff --git a/features/lib/rhc_helper/loggable.rb b/cucumber/lib/rhc_helper/loggable.rb similarity index 100% rename from features/lib/rhc_helper/loggable.rb rename to cucumber/lib/rhc_helper/loggable.rb diff --git a/features/lib/rhc_helper/persistable.rb b/cucumber/lib/rhc_helper/persistable.rb similarity index 100% rename from features/lib/rhc_helper/persistable.rb rename to cucumber/lib/rhc_helper/persistable.rb diff --git a/features/lib/rhc_helper/runnable.rb b/cucumber/lib/rhc_helper/runnable.rb similarity index 100% rename from features/lib/rhc_helper/runnable.rb rename to cucumber/lib/rhc_helper/runnable.rb diff --git a/features/lib/rhc_helper/sshkey.rb b/cucumber/lib/rhc_helper/sshkey.rb similarity index 100% rename from features/lib/rhc_helper/sshkey.rb rename to cucumber/lib/rhc_helper/sshkey.rb diff --git a/features/multiple_cartridge.feature b/cucumber/multiple_cartridge.feature similarity index 100% rename from features/multiple_cartridge.feature rename to cucumber/multiple_cartridge.feature diff --git a/features/scaled_application.feature b/cucumber/scaled_application.feature similarity index 100% rename from features/scaled_application.feature rename to cucumber/scaled_application.feature diff --git a/features/sshkey.feature b/cucumber/sshkey.feature similarity index 100% rename from features/sshkey.feature rename to cucumber/sshkey.feature diff --git a/features/step_definitions/application_steps.rb b/cucumber/step_definitions/application_steps.rb similarity index 100% rename from features/step_definitions/application_steps.rb rename to cucumber/step_definitions/application_steps.rb diff --git a/features/step_definitions/cartridge_steps.rb b/cucumber/step_definitions/cartridge_steps.rb similarity index 100% rename from features/step_definitions/cartridge_steps.rb rename to cucumber/step_definitions/cartridge_steps.rb diff --git a/features/step_definitions/client_steps.rb b/cucumber/step_definitions/client_steps.rb similarity index 100% rename from features/step_definitions/client_steps.rb rename to cucumber/step_definitions/client_steps.rb diff --git a/features/step_definitions/domain_steps.rb b/cucumber/step_definitions/domain_steps.rb similarity index 100% rename from features/step_definitions/domain_steps.rb rename to cucumber/step_definitions/domain_steps.rb diff --git a/features/step_definitions/env_steps.rb b/cucumber/step_definitions/env_steps.rb similarity index 100% rename from features/step_definitions/env_steps.rb rename to cucumber/step_definitions/env_steps.rb diff --git a/features/step_definitions/sshkey_steps.rb b/cucumber/step_definitions/sshkey_steps.rb similarity index 100% rename from features/step_definitions/sshkey_steps.rb rename to cucumber/step_definitions/sshkey_steps.rb diff --git a/features/support/assumptions.rb b/cucumber/support/assumptions.rb similarity index 100% rename from features/support/assumptions.rb rename to cucumber/support/assumptions.rb diff --git a/features/support/before_hooks.rb b/cucumber/support/before_hooks.rb similarity index 100% rename from features/support/before_hooks.rb rename to cucumber/support/before_hooks.rb diff --git a/features/support/env.rb b/cucumber/support/env.rb similarity index 100% rename from features/support/env.rb rename to cucumber/support/env.rb diff --git a/features/support/key1 b/cucumber/support/key1 similarity index 100% rename from features/support/key1 rename to cucumber/support/key1 diff --git a/features/support/key1.pub b/cucumber/support/key1.pub similarity index 100% rename from features/support/key1.pub rename to cucumber/support/key1.pub diff --git a/features/support/key2 b/cucumber/support/key2 similarity index 100% rename from features/support/key2 rename to cucumber/support/key2 diff --git a/features/support/key2.pub b/cucumber/support/key2.pub similarity index 100% rename from features/support/key2.pub rename to cucumber/support/key2.pub diff --git a/features/support/key3.pub b/cucumber/support/key3.pub similarity index 100% rename from features/support/key3.pub rename to cucumber/support/key3.pub diff --git a/features/support/platform_support.rb b/cucumber/support/platform_support.rb similarity index 100% rename from features/support/platform_support.rb rename to cucumber/support/platform_support.rb diff --git a/features/verify.feature b/cucumber/verify.feature similarity index 100% rename from features/verify.feature rename to cucumber/verify.feature diff --git a/features/core_feature.rb b/features/core_feature.rb new file mode 100644 index 000000000..02e4dc5b5 --- /dev/null +++ b/features/core_feature.rb @@ -0,0 +1,141 @@ +require 'spec_helper' +require 'direct_execution_helper' + +describe "rhc core scenarios" do + + it "reports a version" do + r = rhc '--version' + r.status.should == 0 + r.stdout.should match /rhc \d+\.\d+\.\d+\b/ + end + + it "displays help" do + r = rhc 'help' + r.status.should == 0 + r.stdout.should match "Command line interface for OpenShift" + r.stdout.should match "Usage: rhc" + r.stdout.should match "Getting started" + r.stdout.should match "See 'rhc help options' for a list" + end + + context "with a clean configuration" do + before{ use_clean_config } + + it "walks through a configuration" do + r = rhc :setup, :with => setup_args + r.stdout.should match 'OpenShift Client Tools' + r.stdout.should match 'Checking for git ...' + r.stdout.should match 'Checking for applications ...' + r.stdout.should match 'Your client tools are now configured.' + r.status.should == 0 + + r = rhc :account + r.stdout.should match "on #{ENV['RHC_SERVER']}" + r.stdout.should match 'Gears' + r.stdout.should match 'Plan' + end + + it "starts the wizard on default invocation" do + r = rhc + r.stdout.should match "OpenShift Client Tools" + end + end + + context "when creating an app" do + when_running 'create-app', 'test1', a_web_cartridge + before{ no_applications(/^test1/) } + it "returns the proper info and is in the rest api" do + status.should == 0 + output.should match "Your application 'test1' is now available" + output.should match /Gear Size: .*default/ + output.should match /Scaling: .*no/ + output.should match %r(URL: .*http://test1-) + output.should match "Cloned to" + + apps = client.applications + apps.should_not be_empty + apps.should include{ |app| app.name == 'test1' } + end + end + + context "with an existing app" do + before(:all) do + standard_config + @app = has_an_application + end + + let(:app){ @app } + + it "should show app state" do + r = rhc 'app-show', app.name, '--state' + r.status.should == 0 + r.stdout.should match "Cartridge #{a_web_cartridge} is started" + end + + it "should stop and start the app" do + r = rhc 'stop-app', app.name + r.status.should == 0 + r.stdout.should match "#{app.name} stopped" + r = rhc 'start-app', app.name + r.status.should == 0 + r.stdout.should match "#{app.name} started" + end + + it "should show gear status" do + r = rhc 'app-show', app.name, '--gears' + r.status.should == 0 + r.stdout.lines.to_a.length.should == 3 + r.stdout.should match app.ssh_string + app.cartridges.map(&:name).each do |c| + r.stdout.should match c + end + r.stdout.should match "started" + end + + it "should show gear ssh strings" do + r = rhc 'app-show', app.name, '--gears', 'ssh' + r.status.should == 0 + r.stdout.lines.to_a.length.should == 1 + r.stdout.chomp.should == app.ssh_string + end + + context "when the app is cloned" do + before(:all) do + rhc('git-clone', @app.name).status.should == 0 + Dir.exists?(@app.name).should be_true + Dir.chdir @app.name + end + let(:git_config){ `git config --list` } + + it "will set Git config values" do + git_config.should match "rhc.app-id=#{app.id}" + git_config.should match "rhc.app-name=#{app.name}" + git_config.should match "rhc.domain-name=#{app.domain_name}" + end + + it "will infer the current app from the git repository" do + r = rhc 'show-app' + r.stdout.should match app.name + r.stdout.should match app.uuid + r.stdout.should match app.ssh_string + r.stdout.should match app.app_url + (app.cartridges.map(&:name) + app.cartridges.map(&:display_name)).each{ |n| r.stdout.should match n } + r.status.should == 0 + end + + it "will fetch the quotas from the app" do + r = rhc 'show-app', '--gears', 'quota' + r.stdout.chomp.lines.count.should == (app.gear_count + 2) + app.cartridges.map(&:name).each{ |n| r.stdout.should match n } + app.cartridges.map(&:gear_storage).each{ |n| r.stdout.should match(RHC::Helpers.human_size(n)) } + r.status.should == 0 + end + + it "will ssh to the app and run a command" do + r = rhc 'ssh', '--ssh', ENV['GIT_SSH'], 'echo $OPENSHIFT_APP_NAME' + r.stdout.should match app.name + r.status.should == 0 + end + end + end +end diff --git a/features/domains_feature.rb b/features/domains_feature.rb new file mode 100644 index 000000000..15309a59c --- /dev/null +++ b/features/domains_feature.rb @@ -0,0 +1,45 @@ +require 'spec_helper' +require 'direct_execution_helper' + +describe "rhc domain scenarios" do + context "with an existing domain" do + before(:all) do + standard_config + @domain = has_a_domain + end + let(:domain){ @domain } + + it "should display the domain configuration" do + r = rhc 'configure-domain', domain.id + r.status.should == 0 + r.stdout.should match "Allowed Gear Sizes:\s+#{domain.allowed_gear_sizes.join(", ")}" + end + + it "should change the domain configuration" do + r = rhc 'configure-domain', domain.id, '--no-allowed-gear-sizes' + r.status.should == 0 + r.stdout.should match "Allowed Gear Sizes:\s+$" + client.reset.find_domain(domain.id).allowed_gear_sizes.should == [] + + all_sizes = client.user.capabilities.gear_sizes + r = rhc 'configure-domain', domain.id, '--allowed-gear-sizes', all_sizes.join(',') + r.status.should == 0 + r.stdout.should match "Allowed Gear Sizes:\s+#{all_sizes.join(', ')}$" + client.reset.find_domain(domain.id).allowed_gear_sizes.should == all_sizes + end + + it "should reject invalid gear size configuration changes" do + all_sizes = client.user.capabilities.gear_sizes + + r = rhc 'configure-domain', domain.id, '--allowed-gear-sizes', '_not_a_size_' + r.status.should_not == 1 + r.stdout.should match "Updating domain configuration.*The following gear sizes are invalid: _not_a_size_" + client.reset.find_domain(domain.id).allowed_gear_sizes.should == all_sizes + + r = rhc 'configure-domain', domain.id, '--allowed-gear-sizes' + r.status.should_not == 1 + r.stdout.should match "invalid option: Provide a comma delimited .* --allowed-gear-sizes" + client.reset.find_domain(domain.id).allowed_gear_sizes.should == all_sizes + end + end +end \ No newline at end of file diff --git a/features/members_feature.rb b/features/members_feature.rb new file mode 100644 index 000000000..dce05687d --- /dev/null +++ b/features/members_feature.rb @@ -0,0 +1,131 @@ +require 'spec_helper' +require 'direct_execution_helper' + +describe "rhc member scenarios" do + context "with an existing domain" do + before(:all) do + standard_config + @domain = has_a_domain + end + + let(:domain){ @domain } + + context "with no users" do + before{ no_members(domain) } + + it "should not show members in the domain" do + r = rhc 'show-domain', domain.id + r.status.should == 0 + r.stdout.should_not match "Members:" + r.stdout.should match "owned by #{domain.owner}" + end + + it "should prevent leaving the domain for the owner" do + r = rhc 'leave-domain', domain.id + r.status.should_not == 1 + r.stdout.should match "Leaving domain.*You are the owner of this domain and cannot leave" + end + + it "should add and remove a member" do + user = other_users.keys.take(1).first + r = rhc 'add-member', user, '-n', domain.id + r.status.should == 0 + r.stdout.should match "Adding 1 editor to domain" + r.stdout.should match "done" + client.find_domain(domain.id).members.any?{ |m| m.id == other_users[user].id && m.editor? }.should be_true + + r = rhc 'show-domain', domain.id + r.status.should == 0 + r.stdout.should match "Members:" + r.stdout.should match "#{user} \\(edit\\)" + + r = rhc 'remove-member', user, '-n', domain.id + r.status.should == 0 + r.stdout.should match "Removing 1 member from domain" + client.find_domain(domain.id).members.none?{ |m| m.id == other_users[user].id }.should be_true + end + + it "should add and remove two members" do + user1, user2 = other_users.keys.take(2) + r = rhc 'add-member', user1, user2, '-n', domain.id + r.status.should == 0 + r.stdout.should match "Adding 2 editors to domain" + r.stdout.should match "done" + members = client.find_domain(domain.id).members + members.any?{ |m| m.id == other_users[user1].id && m.editor? }.should be_true + members.any?{ |m| m.id == other_users[user2].id && m.editor? }.should be_true + + r = rhc 'show-domain', domain.id + r.status.should == 0 + r.stdout.should match "Members:" + r.stdout.should match "#{user1} \\(edit\\)" + r.stdout.should match "#{user2} \\(edit\\)" + + r = rhc 'remove-member', user1, user2, '-n', domain.id + r.status.should == 0 + r.stdout.should match "Removing 2 members from domain" + client.find_domain(domain.id).members.none?{ |m| m.id == other_users[user1].id }.should be_true + client.find_domain(domain.id).members.none?{ |m| m.id == other_users[user2].id }.should be_true + end + + it "should add a view and an admin member. and allow users to leave the domain" do + user1, user2 = other_users.keys.take(2) + + r = rhc 'add-member', user1, '--role', 'admin', '-n', domain.id + r.status.should == 0 + r.stdout.should match "Adding 1 administrator to domain" + r.stdout.should match "done" + client.find_domain(domain.id).members.any?{ |m| m.id == other_users[user1].id && m.admin? }.should be_true + + r = rhc 'add-member', user2, '--role', 'view', '-n', domain.id + r.status.should == 0 + r.stdout.should match "Adding 1 viewer to domain" + r.stdout.should match "done" + client.find_domain(domain.id).members.any?{ |m| m.id == other_users[user2].id && m.viewer? }.should be_true + + r = rhc 'show-domain', domain.id + r.status.should == 0 + r.stdout.should match "Members:" + r.stdout.should match "#{user1} \\(admin\\)" + r.stdout.should match "#{user2} \\(view\\)" + + r = rhc 'leave-domain', domain.id, :as => other_users[user2] + r.status.should == 0 + r.stdout.should match "Leaving domain.*done" + end + + it "should remove all non owners" do + user1, user2 = other_users.keys.take(2) + r = rhc 'add-member', user1, user2, '-n', domain.id + r.status.should == 0 + r.stdout.should match "Adding 2 editors to domain" + r.stdout.should match "done" + members = client.find_domain(domain.id).members + members.any?{ |m| m.id == other_users[user1].id && m.editor? }.should be_true + members.any?{ |m| m.id == other_users[user2].id && m.editor? }.should be_true + + r = rhc 'remove-member', domain.id, '--all' + r.status.should == 0 + r.stdout.should match "Removing all members from domain.*done" + members = client.find_domain(domain.id).members + members.select(&:owner).should == members + end + + it "should reject a non-existent user" do + r = rhc 'add-member', 'not-a-user', '-n', domain.id + r.status.should_not == 1 + r.stdout.should match "There is no account with login not-a-user." + client.find_domain(domain.id).members.length.should == 1 + end + + it "should add a user by id" do + user = other_users.values.take(1).first + r = rhc 'add-member', user.id, '--ids', '-n', domain.id + r.status.should == 0 + r.stdout.should match "Adding 1 editor to domain" + r.stdout.should match "done" + client.find_domain(domain.id).members.any?{ |m| m.id == user.id && m.editor? }.should be_true + end + end + end +end \ No newline at end of file diff --git a/lib/rhc/auth/token_store.rb b/lib/rhc/auth/token_store.rb index 00b17d0f7..69ee7a67c 100644 --- a/lib/rhc/auth/token_store.rb +++ b/lib/rhc/auth/token_store.rb @@ -1,3 +1,5 @@ +require 'base64' + module RHC::Auth class TokenStore def initialize(dir) diff --git a/lib/rhc/commands.rb b/lib/rhc/commands.rb index 975a2d89f..47d94f486 100644 --- a/lib/rhc/commands.rb +++ b/lib/rhc/commands.rb @@ -13,6 +13,14 @@ def root? root.present? end + alias_method :option_old, :option + def option(*args, &block) + opts = args.pop if Hash === args.last + option_old(*args, &block).tap do |options| + options.last.merge!(opts) if opts + end + end + # # Force proxy_option_struct to default to nil for values, # backported for Commander 4.0.3 @@ -23,7 +31,7 @@ def proxy_option_struct value = true if value.nil? # if multiple values were specified for this option, collect it as an # array. on 'fill_arguments' we will decide between stick with the array - # (if :option_type => :list) or just take the last value from array. + # (if :type => :list) or just take the last value from array. # not part of the backported method. if proxy_options.select{ |item| item[0] == option }.length > 1 if options[option] @@ -217,15 +225,17 @@ def self.to_commander(instance=Commander::Runner.instance) c.info = opts (options_metadata = Array(opts[:options])).each do |o| - option_data = [o[:switches], o[:description]].flatten(1) + option_data = [o[:switches], o[:type], o[:description], o.slice(:optional, :default, :hide)].compact.flatten(1) c.option *option_data o[:arg] = Commander::Runner.switch_to_sym(Array(o[:switches]).last) end (args_metadata = Array(opts[:args])).each do |meta| switches = meta[:switches] - unless switches.nil? or switches.empty? + unless switches.blank? + switches = switches.dup switches << meta[:description] + switches << meta.slice(:optional, :default, :hide, :covered_by, :allow_nil) c.option *switches end end @@ -278,26 +288,25 @@ def self.fill_arguments(cmd, options, args_metadata, options_metadata, args) end # process options - options_metadata.each do |option_meta| - arg = option_meta[:arg] - - if arg && option_meta[:option_type] != :list && options[arg].is_a?(Array) + defaults = {} + (options_metadata + args_metadata).each do |option_meta| + arg = option_meta[:arg] || option_meta[:name] or next + if arg && option_meta[:type] != :list && options[arg].is_a?(Array) options[arg] = options[arg].last end - - # Check to see if we've provided a value for an option tagged as deprecated - if (!(val = options.__hash__[arg]).nil? && dep_info = option_meta[:deprecated]) - # Get the arg for the correct option and what the value should be - (correct_arg, default) = dep_info.values_at(:key, :value) - # Set the default value for the correct option to the passed value - ## Note: If this isn't triggered, then the original default will be honored - ## If the user specifies any value for the correct option, it will be used - options.default correct_arg => default - # Alert the users if they're using a deprecated option - (correct, incorrect) = [options_metadata.find{|x| x[:arg] == correct_arg },option_meta].flatten.map{|x| x[:switches].join(", ") } - RHC::Helpers.deprecated_option(incorrect, correct) + case v = option_meta[:default] + when Symbol + cmd.send(v, defaults, arg) + #when Proc + # v.call(defaults, arg) + when nil + else + defaults[arg] = v end - + end + options.default(defaults) + options_metadata.each do |option_meta| + arg = option_meta[:arg] if context_helper = option_meta[:context_helper] options[arg] = lambda{ cmd.send(context_helper) } if options.__hash__[arg].nil? end @@ -307,6 +316,10 @@ def self.fill_arguments(cmd, options, args_metadata, options_metadata, args) available = args.dup slots = Array.new(args_metadata.length) args_metadata.each_with_index do |arg, i| + if Array(arg[:covered_by]).any?{ |k| !options.__hash__[k].nil? } + slots[i] = nil + next + end option = arg[:option_symbol] context_helper = arg[:context_helper] @@ -314,7 +327,7 @@ def self.fill_arguments(cmd, options, args_metadata, options_metadata, args) if value.nil? value = - if arg[:arg_type] == :list + if arg[:type] == :list all = [] while available.first && available.first != '--' all << available.shift @@ -328,11 +341,11 @@ def self.fill_arguments(cmd, options, args_metadata, options_metadata, args) value = cmd.send(context_helper) if value.nil? and context_helper - if value.nil? + if value.nil? && arg[:allow_nil] != true raise ArgumentError, "Missing required argument '#{arg[:name]}'." unless arg[:optional] break if available.empty? else - value = Array(value) if arg[:arg_type] == :list + value = Array(value) if arg[:type] == :list slots[i] = value options.__hash__[option] = value if option end diff --git a/lib/rhc/commands/account.rb b/lib/rhc/commands/account.rb index 0e92fd1da..308fb22e1 100644 --- a/lib/rhc/commands/account.rb +++ b/lib/rhc/commands/account.rb @@ -13,11 +13,10 @@ def run user = rest_client.user say format_table \ - nil, - get_properties(user, :login, :plan_id, :consumed_gears, :max_gears). + ["Login #{user.login} on #{openshift_server}"], + get_properties(user, :id, :plan_id, :consumed_gears, :max_gears, :max_domains). concat(get_properties(user.capabilities, :gear_sizes)). - unshift(['Server:', openshift_server]). - push(['SSL Certificates Supported:', user.capabilities.private_ssl_certificates ? 'yes' : 'no']), + push(['SSL Certificates:', user.capabilities.private_ssl_certificates ? 'yes' : 'no']), :delete => true 0 diff --git a/lib/rhc/commands/alias.rb b/lib/rhc/commands/alias.rb index bd4a7f9b2..df5120e3c 100644 --- a/lib/rhc/commands/alias.rb +++ b/lib/rhc/commands/alias.rb @@ -18,12 +18,11 @@ class Alias < Base summary "Add a custom domain name for the application" syntax " [--namespace NAME]" - argument :app, "Application name (required)", ["-a", "--app name"], :context => :app_context, :required => true + takes_application :argument => true argument :app_alias, "Custom domain name for the application", [] - option ["-n", "--namespace NAME"], "Namespace of your application", :context => :namespace_context, :required => true alias_action :"app add-alias", :root_command => true, :deprecated => true def add(app, app_alias) - rest_app = rest_client.find_application(options.namespace, app) + rest_app = find_app rest_app.add_alias(app_alias) success "Alias '#{app_alias}' has been added." 0 @@ -31,12 +30,11 @@ def add(app, app_alias) summary "Remove a custom domain name for the application" syntax " [--namespace NAME]" - argument :app, "Application name (required)", ["-a", "--app name"], :context => :app_context, :required => true + takes_application :argument => true argument :app_alias, "Custom domain name for the application", [] - option ["-n", "--namespace NAME"], "Namespace of your application", :context => :namespace_context, :required => true alias_action :"app remove-alias", :root_command => true, :deprecated => true def remove(app, app_alias) - rest_app = rest_client.find_application(options.namespace, app) + rest_app = find_app rest_app.remove_alias(app_alias) success "Alias '#{app_alias}' has been removed." 0 @@ -56,12 +54,11 @@ def remove(app, app_alias) provided private key is encrypted. DESC syntax " --certificate FILE --private-key FILE [--passphrase PASSPHRASE]" - argument :app, "Application name (required)", ["-a", "--app name"], :context => :app_context, :required => true + takes_application :argument => true argument :app_alias, "Custom domain name for the application (required)", [] option ["--certificate FILE"], "SSL certificate filepath (file in .crt or .pem format)", :required => true option ["--private-key FILE"], "Private key filepath for the given SSL certificate", :required => true option ["--passphrase PASSPHRASE"], "Private key pass phrase, required if the private key is encrypted", :required => false - option ["-n", "--namespace NAME"], "Namespace of your application", :context => :namespace_context, :required => true def update_cert(app, app_alias) certificate_file_path = options.certificate raise ArgumentError, "Certificate file not found: #{certificate_file_path}" if !File.exist?(certificate_file_path) || !File.file?(certificate_file_path) @@ -75,7 +72,7 @@ def update_cert(app, app_alias) private_key_content = File.read(private_key_file_path) raise ArgumentError, "Invalid private key file: #{private_key_file_path} is empty" if private_key_content.to_s.strip.length == 0 - rest_app = rest_client.find_application(options.namespace, app) + rest_app = find_app rest_alias = rest_app.find_alias(app_alias) if rest_client.api_version_negotiated >= 1.4 rest_alias.add_certificate(certificate_content, private_key_content, options.passphrase) @@ -88,12 +85,11 @@ def update_cert(app, app_alias) summary "Delete the SSL certificate from an existing alias" syntax " " - argument :app, "Application name (required)", ["-a", "--app name"], :context => :app_context, :required => true + takes_application :argument => true argument :app_alias, "Custom domain name for the application (required)", [] option ["--confirm"], "Pass to confirm deleting the application" - option ["-n", "--namespace NAME"], "Namespace of your application", :context => :namespace_context, :required => true def delete_cert(app, app_alias) - rest_app = rest_client.find_application(options.namespace, app) + rest_app = find_app rest_alias = rest_app.find_alias(app_alias) if rest_client.api_version_negotiated >= 1.4 confirm_action "#{color("This is a non-reversible action! Your SSL certificate will be permanently deleted from application '#{app}'.", :yellow)}\n\nAre you sure you want to delete the SSL certificate?" @@ -107,11 +103,10 @@ def delete_cert(app, app_alias) summary "List the aliases on an application" syntax "" - argument :app, "Application name (required)", ["-a", "--app name"], :context => :app_context, :required => true - option ["-n", "--namespace NAME"], "Namespace of your application", :context => :namespace_context, :required => true + takes_application :argument => true alias_action "aliases", :root_command => true def list(app) - rest_app = rest_client.find_application(options.namespace, app) + rest_app = find_app items = rest_app.aliases.map do |a| a.is_a?(String) ? [a, 'no', '-'] : diff --git a/lib/rhc/commands/app.rb b/lib/rhc/commands/app.rb index 974233449..c74f855e7 100644 --- a/lib/rhc/commands/app.rb +++ b/lib/rhc/commands/app.rb @@ -52,15 +52,14 @@ class App < Base option ["-g", "--gear-size SIZE"], "Gear size controls how much memory and CPU your cartridges can use." option ["-s", "--scaling"], "Enable scaling for the web cartridge." option ["-r", "--repo DIR"], "Path to the Git repository (defaults to ./$app_name)" - option ["-e", "--env VARIABLE=VALUE"], "Environment variable(s) to be set on this app, or path to a file containing environment variables", :option_type => :list + option ["-e", "--env VARIABLE=VALUE"], "Environment variable(s) to be set on this app, or path to a file containing environment variables", :type => :list option ["--from-code URL"], "URL to a Git repository that will become the initial contents of the application" option ["--[no-]git"], "Skip creating the local Git repository." - option ["--nogit"], "DEPRECATED: Skip creating the local Git repository.", :deprecated => {:key => :git, :value => false} option ["--[no-]dns"], "Skip waiting for the application DNS name to resolve. Must be used in combination with --no-git" option ['--no-keys'], "Skip checking SSH keys during app creation", :hide => true option ["--enable-jenkins [NAME]"], "Enable Jenkins builds for this application (will create a Jenkins application if not already available). The default name will be 'jenkins' if not specified." argument :name, "Name for your application", ["-a", "--app NAME"], :optional => true - argument :cartridges, "The web framework this application should use", ["-t", "--type CARTRIDGE"], :optional => true, :arg_type => :list + argument :cartridges, "The web framework this application should use", ["-t", "--type CARTRIDGE"], :optional => true, :type => :list def create(name, cartridges) check_config! @@ -84,20 +83,20 @@ def create(name, cartridges) c.usage_rate? ? "#{c.short_name} (addtl. costs may apply)" : c.short_name end.join(', ') - if rest_domain.supports_add_application_with_env_vars? - environment_variables = collect_env_vars(arg_envs.concat(Array(options.env))) - else + env = collect_env_vars(arg_envs.concat(Array(options.env))) + if env.present? && !rest_domain.supports_add_application_with_env_vars? + env = [] warn "Server does not support environment variables." end paragraph do header "Application Options" - table([["Namespace:", options.namespace], + table([["Domain:", options.namespace], ["Cartridges:", cart_names], (["Source Code:", options.from_code] if options.from_code), ["Gear Size:", options.gear_size || "default"], ["Scaling:", options.scaling ? "yes" : "no"], - (["Environment Variables:", environment_variables.map{|item| "#{item.name}=#{item.value}"}.join(', ')] if environment_variables.present?), + (["Environment Variables:", env.map{|item| "#{item.name}=#{item.value}"}.join(', ')] if env.present?), ].compact ).each { |s| say " #{s}" } end @@ -106,7 +105,7 @@ def create(name, cartridges) say "Creating application '#{name}' ... " # create the main app - rest_app = create_app(name, cartridges, rest_domain, options.gear_size, options.scaling, options.from_code, environment_variables) + rest_app = create_app(name, cartridges, rest_domain, options.gear_size, options.scaling, options.from_code, env) success "done" paragraph{ indent{ success rest_app.messages.map(&:strip) } } @@ -203,13 +202,11 @@ def create(name, cartridges) description "Deletes your application and all of its data from the server.", "Use with caution as this operation is permanent." syntax " [--namespace NAME]" - option ["-n", "--namespace NAME"], "Namespace your application belongs to", :context => :namespace_context, :required => true - option ["-b", "--bypass"], "DEPRECATED Please use '--confirm'", :deprecated => {:key => :confirm, :value => true} + takes_application :argument => true option ["--confirm"], "Pass to confirm deleting the application" - argument :app, "The application you wish to delete", ["-a", "--app name"], :context => :app_context alias_action :destroy, :deprecated => true def delete(app) - rest_app = rest_client.find_application(options.namespace, app) + rest_app = find_app confirm_action "#{color("This is a non-reversible action! Your application code and data will be permanently deleted if you continue!", :yellow)}\n\nAre you sure you want to delete the application '#{app}'?" @@ -223,66 +220,60 @@ def delete(app) end summary "Start the application" - syntax " [--namespace NAME] [--app NAME]" - argument :app, "The name of the application you are starting", ["-a", "--app NAME"], :context => :app_context - option ["-n", "--namespace NAME"], "Application's domain name", :context => :namespace_context, :required => true + syntax " [--namespace NAME]" + takes_application :argument => true def start(app) - app_action app, :start + app_action :start results { say "#{app} started" } 0 end summary "Stop the application" - syntax " [--namespace NAME] [--app NAME]" - argument :app, "The name of the application you are stopping", ["-a", "--app NAME"], :context => :app_context - option ["-n", "--namespace NAME"], "Application's domain name", :context => :namespace_context, :required => true + syntax " [--namespace NAME]" + takes_application :argument => true def stop(app) - app_action app, :stop + app_action :stop results { say "#{app} stopped" } 0 end summary "Stops all application processes" - syntax " [--namespace NAME] [--app NAME]" - argument :app, "The name of the application you are stopping", ["-a", "--app NAME"], :context => :app_context - option ["-n", "--namespace NAME"], "Application's domain name", :context => :namespace_context, :required => true + syntax " [--namespace NAME]" + takes_application :argument => true def force_stop(app) - app_action app, :stop, true + app_action :stop, true results { say "#{app} force stopped" } 0 end summary "Restart the application" - syntax " [--namespace NAME] [--app NAME]" - argument :app, "The name of the application you are restarting", ["-a", "--app NAME"], :context => :app_context - option ["-n", "--namespace NAME"], "Application's domain name", :context => :namespace_context, :required => true + syntax " [--namespace NAME]" + takes_application :argument => true def restart(app) - app_action app, :restart + app_action :restart results { say "#{app} restarted" } 0 end summary "Reload the application's configuration" - syntax " [--namespace NAME] [--app NAME]" - argument :app, "The name of the application you are reloading", ["-a", "--app NAME"], :context => :app_context - option ["-n", "--namespace NAME"], "Application's domain name", :context => :namespace_context, :required => true + syntax " [--namespace NAME]" + takes_application :argument => true def reload(app) - app_action app, :reload + app_action :reload results { say "#{app} config reloaded" } 0 end summary "Clean out the application's logs and tmp directories and tidy up the git repo on the server" - syntax " [--namespace NAME] [--app NAME]" - argument :app, "The name of the application you are tidying", ["-a", "--app NAME"], :context => :app_context - option ["-n", "--namespace NAME"], "Namespace of the application belongs to", :context => :namespace_context, :required => true + syntax " [--namespace NAME]" + takes_application :argument => true def tidy(app) - app_action app, :tidy + app_action :tidy results { say "#{app} cleaned up" } 0 @@ -309,19 +300,18 @@ def tidy(app) to run and display the output from each gear. DESC syntax " [--namespace NAME]" - argument :app, "The name of the application you are getting information on", ["-a", "--app NAME"], :context => :app_context - option ["-n", "--namespace NAME"], "Application's domain name", :context => :namespace_context, :required => true + takes_application :argument => true option ["--state"], "Get the current state of the cartridges in this application" option ["--gears [quota|ssh]"], "Show information about the cartridges on each gear in this application. Pass 'quota' to see per gear disk usage and limits. Pass 'ssh' to print only the SSH connection strings of each gear." def show(app_name) if options.state - gear_groups_for_app(app_name).each do |gg| + find_app(:with_gear_groups => true).each do |gg| say "Cartridge #{gg.cartridges.collect { |c| c['name'] }.join(', ')} is #{gear_group_state(gg.gears.map{ |g| g['state'] })}" end elsif options.gears && options.gears != true - groups = rest_client.find_application_gear_groups(options.namespace, app_name) + groups = find_app(:with_gear_groups => true) case options.gears when 'quota' @@ -336,7 +326,7 @@ def show(app_name) end elsif options.gears - gear_info = gear_groups_for_app(app_name).map do |group| + gear_info = find_app(:with_gear_groups => true).map do |group| group.gears.map do |gear| [ gear['id'], @@ -350,24 +340,13 @@ def show(app_name) say table(gear_info, :header => ['ID', 'State', 'Cartridges', 'Size', 'SSH URL']) else - app = rest_client.find_application(options.namespace, app_name, :include => :cartridges) + app = find_app(:include => :cartridges) display_app(app, app.cartridges) end 0 end - summary "DEPRECATED use 'show --state' instead" - syntax " [--namespace NAME] [--app NAME]" - argument :app, "The name of the application you are getting information on", ["-a", "--app NAME"], :context => :app_context - option ["-n", "--namespace NAME"], "Namespace of the application belongs to", :context => :namespace_context, :required => true - deprecated "rhc app show --state" - def status(app) - # TODO: add a way to deprecate this and alias to show --apache - options.state = true - show(app) - end - private include RHC::GitHelpers include RHC::CartridgeHelpers @@ -427,18 +406,13 @@ def check_domain! end end - - def gear_groups_for_app(app_name) - rest_client.find_application_gear_groups(options.namespace, app_name) - end - def gear_group_state(states) return states[0] if states.length == 1 || states.uniq.length == 1 "#{states.select{ |s| s == 'started' }.count}/#{states.length} started" end - def app_action(app, action, *args) - rest_app = rest_client.find_application(options.namespace, app) + def app_action(action, *args) + rest_app = find_app result = rest_app.send action, *args result end @@ -450,9 +424,7 @@ def create_app(name, cartridges, rest_domain, gear_size=nil, scale=nil, from_cod app_options[:initial_git_url] = from_code if from_code app_options[:debug] = true if @debug app_options[:environment_variables] = environment_variables.map{ |item| item.to_hash } if environment_variables.present? - debug "Creating application '#{name}' with these options - #{app_options.inspect}" rest_app = rest_domain.add_application(name, app_options) - debug "'#{rest_app.name}' created" rest_app rescue RHC::Rest::Exception => e diff --git a/lib/rhc/commands/authorization.rb b/lib/rhc/commands/authorization.rb index 81bb5d683..5dbc727db 100644 --- a/lib/rhc/commands/authorization.rb +++ b/lib/rhc/commands/authorization.rb @@ -64,7 +64,7 @@ def add your account. After deletion, any clients using the token will no longer have access to OpenShift and will need to reauthenticate. DESC - argument :auth_token, "The token you wish to delete", ['--auth-token TOKEN'], :arg_type => :list + argument :auth_token, "The token you wish to delete", ['--auth-token TOKEN'], :type => :list def delete(tokens) raise ArgumentError, "You must specify one or more tokens to delete" if tokens.blank? say "Deleting authorization ... " diff --git a/lib/rhc/commands/base.rb b/lib/rhc/commands/base.rb index c901dffce..5811a9b87 100644 --- a/lib/rhc/commands/base.rb +++ b/lib/rhc/commands/base.rb @@ -93,9 +93,9 @@ def self.summary(value) def self.syntax(value) options[:syntax] = value end - def self.deprecated(msg) - options[:deprecated] = msg - end + #def self.deprecated(msg) + # options[:deprecated] = msg + #end def self.suppress_wizard @suppress_wizard = true end @@ -124,12 +124,14 @@ def self.option(switches, description, options={}) :context_helper => options[:context], :required => options[:required], :deprecated => options[:deprecated], - :option_type => options[:option_type] + :type => options[:type], + :hide => options[:hide], + :default => options[:default], } end def self.argument(name, description, switches=[], options={}) - arg_type = options[:arg_type] + arg_type = options[:type] option_symbol = Commander::Runner.switch_to_sym(switches.last) args_metadata << {:name => name, @@ -137,8 +139,11 @@ def self.argument(name, description, switches=[], options={}) :switches => switches, :context_helper => options[:context], :option_symbol => option_symbol, + :covered_by => options[:covered_by], :optional => options[:optional], - :arg_type => arg_type} + :default => options[:default], + :hide => options[:hide], + :type => arg_type} end def self.default_action(action) diff --git a/lib/rhc/commands/cartridge.rb b/lib/rhc/commands/cartridge.rb index 3d2585d0f..f7319b1b8 100644 --- a/lib/rhc/commands/cartridge.rb +++ b/lib/rhc/commands/cartridge.rb @@ -29,6 +29,7 @@ class Cartridge < Base Commands that affect a cartridge within an application will affect all gears the cartridge is installed to. DESC + default_action :help summary "List available cartridges" syntax '' @@ -69,9 +70,8 @@ def list summary "Add a cartridge to your application" syntax " [--namespace NAME] [--app NAME]" - option ["-n", "--namespace NAME"], "Application's domain name", :context => :namespace_context, :required => true - option ["-a", "--app NAME"], "Application you are adding the cartridge to", :context => :app_context, :required => true - option ["-e", "--env VARIABLE=VALUE"], "Environment variable(s) to be set on this cartridge, or path to a file containing environment variables", :option_type => :list + takes_application + option ["-e", "--env VARIABLE=VALUE"], "Environment variable(s) to be set on this cartridge, or path to a file containing environment variables", :type => :list argument :cart_type, "The type of the cartridge you are adding (run 'rhc cartridge list' to obtain a list of available cartridges)", ["-c", "--cartridge cart_type"] alias_action :"app cartridge add", :root_command => true, :deprecated => true def add(cart_type) @@ -81,7 +81,7 @@ def add(cart_type) say format_usage_message(cart) if cart.usage_rate? - rest_app = rest_client.find_application(options.namespace, options.app, :include => :cartridges) + rest_app = find_app(:include => :cartridges) supports_env_vars = rest_app.supports_add_cartridge_with_env_vars? cart.environment_variables = collect_env_vars(options.env).map { |item| item.to_hash } if options.env && supports_env_vars @@ -102,11 +102,10 @@ def add(cart_type) summary "Show useful information about a cartridge" syntax " [--namespace NAME] [--app NAME]" - option ["-n", "--namespace NAME"], "Application's domain name", :context => :namespace_context, :required => true - option ["-a", "--app NAME"], "Application name", :context => :app_context, :required => true + takes_application argument :cartridge, "The name of the cartridge", ["-c", "--cartridge cart_type"] def show(cartridge) - rest_app = rest_client.find_application(options.namespace, options.app, :include => :cartridges) + rest_app = find_app(:include => :cartridges) rest_cartridge = check_cartridges(cartridge, :from => rest_app.cartridges).first display_cart(rest_cartridge) @@ -117,12 +116,11 @@ def show(cartridge) summary "Remove a cartridge from your application" syntax " [--namespace NAME] [--app NAME]" argument :cartridge, "The name of the cartridge you are removing", ["-c", "--cartridge cartridge"] - option ["-n", "--namespace NAME"], "Application's domain name", :context => :namespace_context, :required => true - option ["-a", "--app NAME"], "Application name", :context => :app_context, :required => true + takes_application option ["--confirm"], "Pass to confirm removing the cartridge" alias_action :"app cartridge remove", :root_command => true, :deprecated => true def remove(cartridge) - rest_app = rest_client.find_application(options.namespace, options.app, :include => :cartridges) + rest_app = find_app(:include => :cartridges) rest_cartridge = check_cartridges(cartridge, :from => rest_app.cartridges).first confirm_action "Removing a cartridge is a destructive operation that may result in loss of data associated with the cartridge.\n\nAre you sure you wish to remove #{rest_cartridge.name} from '#{rest_app.name}'?" @@ -139,8 +137,7 @@ def remove(cartridge) summary "Start a cartridge" syntax " [--namespace NAME] [--app NAME]" argument :cart_type, "The name of the cartridge you are stopping", ["-c", "--cartridge cartridge"] - option ["-n", "--namespace NAME"], "Application's domain name", :context => :namespace_context, :required => true - option ["-a", "--app NAME"], "Application name", :context => :app_context, :required => true + takes_application alias_action :"app cartridge start", :root_command => true, :deprecated => true def start(cartridge) cartridge_action(cartridge, :start, 'Starting %s ... ') @@ -150,8 +147,7 @@ def start(cartridge) summary "Stop a cartridge" syntax " [--namespace NAME] [--app NAME]" argument :cart_type, "The name of the cartridge you are stopping", ["-c", "--cartridge cartridge"] - option ["-n", "--namespace NAME"], "Application's domain name", :context => :namespace_context, :required => true - option ["-a", "--app NAME"], "Application name", :context => :app_context, :required => true + takes_application alias_action :"app cartridge stop", :root_command => true, :deprecated => true def stop(cartridge) cartridge_action(cartridge, :stop, 'Stopping %s ... ') @@ -161,8 +157,7 @@ def stop(cartridge) summary "Restart a cartridge" syntax " [--namespace NAME] [--app NAME]" argument :cart_type, "The name of the cartridge you are restarting", ["-c", "--cartridge cartridge"] - option ["-n", "--namespace NAME"], "Application's domain name", :context => :namespace_context, :required => true - option ["-a", "--app NAME"], "Application name", :context => :app_context, :required => true + takes_application alias_action :"app cartridge restart", :root_command => true, :deprecated => true def restart(cartridge) cartridge_action(cartridge, :restart, 'Restarting %s ... ') @@ -172,11 +167,10 @@ def restart(cartridge) summary "Get current the status of a cartridge" syntax " [--namespace NAME] [--app NAME]" argument :cart_type, "The name of the cartridge you are getting the status of", ["-c", "--cartridge cartridge"] - option ["-n", "--namespace NAME"], "Application's domain name", :context => :namespace_context, :required => true - option ["-a", "--app NAME"], "Application name", :context => :app_context, :required => true + takes_application alias_action :"app cartridge status", :root_command => true, :deprecated => true def status(cartridge) - rest_app = rest_client.find_application(options.namespace, options.app, :include => :cartridges) + rest_app = find_app(:include => :cartridges) rest_cartridge = check_cartridges(cartridge, :from => rest_app.cartridges).first results { rest_cartridge.status.each{ |msg| say msg['message'] } } 0 @@ -185,8 +179,7 @@ def status(cartridge) summary "Reload the cartridge's configuration" syntax " [--namespace NAME] [--app NAME]" argument :cart_type, "The name of the cartridge you are reloading", ["-c", "--cartridge cartridge"] - option ["-n", "--namespace NAME"], "Application's domain name", :context => :namespace_context, :required => true - option ["-a", "--app NAME"], "Application name", :context => :app_context, :required => true + takes_application alias_action :"app cartridge reload", :root_command => true, :deprecated => true def reload(cartridge) cartridge_action(cartridge, :reload, 'Reloading %s ... ') @@ -210,8 +203,7 @@ def reload(cartridge) syntax " [multiplier] [--namespace NAME] [--app NAME] [--min min] [--max max]" argument :cartridge, "The name of the cartridge you are scaling", ["-c", "--cartridge cartridge"] argument :multiplier, "The number of instances of this cartridge you need", [], :optional => true, :hide => true - option ["-n", "--namespace NAME"], "Application's domain name", :context => :namespace_context, :required => true - option ["-a", "--app NAME"], "Application name", :context => :app_context, :required => true + takes_application option ["--min min", Integer], "Minimum scaling value" option ["--max max", Integer], "Maximum scaling value" def scale(cartridge, multiplier) @@ -219,7 +211,7 @@ def scale(cartridge, multiplier) raise RHC::MissingScalingValueException unless options.min || options.max - rest_app = rest_client.find_application(options.namespace, options.app, :include => :cartridges) + rest_app = find_app(:include => :cartridges) rest_cartridge = check_cartridges(cartridge, :from => rest_app.cartridges).first raise RHC::CartridgeNotScalableException unless rest_cartridge.scalable? @@ -244,9 +236,8 @@ def scale(cartridge, multiplier) summary 'View/manipulate storage on a cartridge' syntax ' -a app [--show] [--add|--remove|--set amount] [--namespace NAME]' - argument :cart_type, "The name of the cartridge", ["-c", "--cartridge cart_type"], :arg_type => :list - option ["-n", "--namespace NAME"], "Application's domain name", :context => :namespace_context, :required => true - option ["-a", "--app NAME"], "Application name", :context => :app_context, :required => true + argument :cart_type, "The name of the cartridge", ["-c", "--cartridge cart_type"], :type => :list + takes_application option ["--show"], "Show the current base and additional storage capacity" option ["--add amount"], "Add the indicated amount to the additional storage capacity" option ["--remove amount"], "Remove the indicated amount from the additional storage capacity" @@ -254,7 +245,8 @@ def scale(cartridge, multiplier) option ["-f", "--force"], "Force the action" def storage(cartridge) cartridges = Array(cartridge) - rest_app = rest_client(:min_api => 1.3).find_application(options.namespace, options.app, :include => :cartridges) + rest_client(:min_api => 1.3).api + rest_app = find_app(:include => :cartridges) # Pull the desired action # @@ -314,7 +306,7 @@ def storage(cartridge) include RHC::CartridgeHelpers def cartridge_action(cartridge, action, message=nil) - rest_app = rest_client.find_application(options.namespace, options.app, :include => :cartridges) + rest_app = find_app(:include => :cartridges) rest_cartridge = check_cartridges(cartridge, :from => rest_app.cartridges).first say message % [rest_cartridge.name] if message result = rest_cartridge.send(action) diff --git a/lib/rhc/commands/domain.rb b/lib/rhc/commands/domain.rb index 87248045d..d01a7fa1b 100644 --- a/lib/rhc/commands/domain.rb +++ b/lib/rhc/commands/domain.rb @@ -6,28 +6,43 @@ class Domain < Base syntax "" description <<-DESC OpenShift groups applications within a domain. The name of the domain - will be used as part of the public URL for an application. + will be used as part of the public URL for an application. - For example, when creating a domain with the name "test", any applications + For example, when creating a domain with the name "test", any applications created in that domain will have the public URL: http://-test.rhcloud.com - Each account may have access to one or more domains shared by others. + Each account may have access to one or more domains shared by others. Depending + on your plan or configuration, you may be able to create more than one domain. DESC default_action :list - summary "Create a new application grouping." + summary "Create a new container for applications." syntax "" - argument :namespace, "Name for your application(s) (alphanumeric)", ["-n", "--namespace NAME"] + description <<-DESC + A domain is a container for your applications. Each account may have one + or more domains (depending on plan), and you may collaborate on applications + by adding members to your domain. + + The name of the domain is called its "namespace", and becomes part of the + application public URLs. For example, when creating a domain with the name "test", + all applications in that domain will have the public URL: + + http://-test.rhcloud.com + + The domain owner may limit the gear sizes available to applications by using the + '--allowed-gear-sizes' option. If '--no-allowed-gear-sizes' is set, no applications + can be created in the domain. Older servers may not support this option. + DESC + option ['--[no-]allowed-gear-sizes [SIZES]'], 'A comma-delimited list of the gear sizes that will be allowed in this domain.', :optional => true + argument :namespace, "New domain name (letters and numbers, max 16 chars)", ["-n", "--namespace NAME"] def create(namespace) - paragraph { say "Creating domain '#{namespace}'" } - rest_client.add_domain(namespace) + say "Creating domain '#{namespace}' ... " + rest_client.add_domain(namespace, :allowed_gear_sizes => check_allowed_gear_sizes) + success "done" - results do - say "Success!" - say "You may now create an application using the 'rhc create-app' command" - end + info "You may now create an application using the 'rhc create-app' command" 0 end @@ -35,21 +50,42 @@ def create(namespace) summary "Rename a domain (will change application urls)" syntax " " argument :old_namespace, "Existing domain name", [] - argument :new_namespace, "New domain name", ["-n", "--namespace NAME"] - alias_action :alter, :deprecated => true - def update(old_namespace, new_namespace) + argument :new_namespace, "New domain name (letters and numbers, max 16 chars)", ["-n", "--namespace NAME"] + alias_action :update, :deprecated => true + def rename(old_namespace, new_namespace) domain = rest_client.find_domain(old_namespace) say "Renaming domain '#{domain.id}' to '#{new_namespace}' ... " - - domain.update(new_namespace) - + domain.rename(new_namespace) success "done" + info "Applications in this domain will use the new name in their URL." 0 end + summary "Change one or more configuration settings on the domain" + syntax "" + option ['--[no-]allowed-gear-sizes [SIZES]'], 'A comma-delimited list of the gear sizes that will be allowed in this domain.', :optional => true + argument :namespace, "Name of the domain", ["-n", "--namespace NAME"], :context => :namespace_context + def configure(namespace) + domain = rest_client.find_domain(namespace) + payload = {} + payload[:allowed_gear_sizes] = check_allowed_gear_sizes unless options.allowed_gear_sizes.nil? + + if payload.present? + say "Updating domain configuration ... " + domain.configure(payload) + success "done" + end + + paragraph do + say format_table("Domain #{namespace} configuration", get_properties(domain, :allowed_gear_sizes), :delete => true) + end + + 0 + end + summary "Display a domain and its applications" argument :namespace, "Name of the domain", ["-n", "--namespace NAME"], :optional => true def show(namespace) @@ -84,35 +120,45 @@ def list success "You have access to #{pluralize(domains.length, 'domain')}." 0 - end - - summary "DEPRECATED use 'setup' instead" - deprecated 'rhc setup' - # :nocov: - def status - 1 # return error status end - # :nocov: - summary "Deletes your domain." + summary "Delete a domain" syntax "" - argument :namespace, "Namespace you wish to destroy", ["-n", "--namespace NAME"] - alias_action :destroy, :deprecated => true + argument :namespace, "Name of the domain", ["-n", "--namespace NAME"] def delete(namespace) domain = rest_client.find_domain namespace say "Deleting domain '#{namespace}' ... " + domain.destroy + success "deleted" - begin - domain.destroy - rescue RHC::Rest::ClientErrorException #FIXME: I am insufficiently specific - raise RHC::Exception.new("Your domain contains applications. Delete applications first.", 128) - end + 0 + end + + summary "Leave a domain (remove your membership)" + syntax "" + argument :namespace, "Name of the domain", ["-n", "--namespace NAME"] + def leave(namespace) + domain = rest_client.find_domain(namespace) + + say "Leaving domain ... " + domain.leave + success "done" - success "deleted" - 0 end - end + protected + def check_allowed_gear_sizes + sizes = options.allowed_gear_sizes + raise OptionParser::InvalidOption, "The server does not support --allowed-gear-sizes" unless sizes.nil? || rest_client.api.has_param?(:add_domain, 'allowed_gear_sizes') + if sizes.is_a? String + sizes.split(',').map(&:strip).map(&:presence) + elsif sizes == false + [] + elsif sizes + raise OptionParser::InvalidOption, "Provide a comma delimited list of valid gear sizes to --allowed-gear-sizes" + end + end + end end diff --git a/lib/rhc/commands/env.rb b/lib/rhc/commands/env.rb index a0b4f237f..c7809f58f 100644 --- a/lib/rhc/commands/env.rb +++ b/lib/rhc/commands/env.rb @@ -38,13 +38,12 @@ class Env < Base DESC syntax " [... ] [--namespace NAME] [--app NAME]" - argument :env, "Environment variable name and value pair separated by an equal (=) sign, e.g. VARIABLE=VALUE", ["-e", "--env VARIABLE=VALUE"], :optional => false, :arg_type => :list - option ["-a", "--app NAME"], "Application name (required)", :context => :app_context, :required => true - option ["-n", "--namespace NAME"], "Namespace of your application", :context => :namespace_context, :required => true + argument :env, "Environment variable name and value pair separated by an equal (=) sign, e.g. VARIABLE=VALUE", ["-e", "--env VARIABLE=VALUE"], :optional => false, :type => :list + takes_application option ["--confirm"], "Pass to confirm setting the environment variable(s)" alias_action :add def set(env) - rest_app = rest_client.find_application(options.namespace, options.app) + rest_app = find_app with_file = env.index {|item| File.file? item} @@ -78,13 +77,12 @@ def set(env) DESC syntax " [... ] [--namespace NAME] [--app NAME]" - argument :env, "Name of the environment variable(s), e.g. VARIABLE", ["-e", "--env VARIABLE"], :optional => false, :arg_type => :list - option ["-a", "--app NAME"], "Application name (required)", :context => :app_context, :required => true - option ["-n", "--namespace NAME"], "Namespace of your application", :context => :namespace_context, :required => true + argument :env, "Name of the environment variable(s), e.g. VARIABLE", ["-e", "--env VARIABLE"], :optional => false, :type => :list + takes_application option ["--confirm"], "Pass to confirm removing the environment variable" alias_action :remove def unset(env) - rest_app = rest_client.find_application(options.namespace, options.app) + rest_app = find_app warn 'Removing environment variables is a destructive operation that may result in loss of data.' @@ -108,12 +106,11 @@ def unset(env) DESC syntax " [--namespace NAME]" - argument :app, "Application name (required)", ["-a", "--app name"], :context => :app_context, :required => true - option ["-n", "--namespace NAME"], "Namespace of your application", :context => :namespace_context, :required => true + takes_application :argument => true option ["--table"], "Format the output list as a table" option ["--quotes"], "Format the output list with double quotes for env var values" def list(app) - rest_app = rest_client.find_application(options.namespace, app) + rest_app = find_app rest_env_vars = rest_app.environment_variables pager @@ -125,13 +122,12 @@ def list(app) summary "Show the value of one or more environment variable(s) currently set to your application" syntax " [... ] [--namespace NAME] [--app NAME]" - argument :env, "Name of the environment variable(s), e.g. VARIABLE", ["-e", "--env VARIABLE"], :optional => false, :arg_type => :list - option ["-a", "--app NAME"], "Application name (required)", :context => :app_context, :required => true - option ["-n", "--namespace NAME"], "Namespace of your application", :context => :namespace_context, :required => true + argument :env, "Name of the environment variable(s), e.g. VARIABLE", ["-e", "--env VARIABLE"], :optional => false, :type => :list + takes_application option ["--table"], "Format the output list as a table" option ["--quotes"], "Format the output list with double quotes for env var values" def show(env) - rest_app = rest_client.find_application(options.namespace, options.app) + rest_app = find_app rest_env_vars = rest_app.find_environment_variables(env) pager diff --git a/lib/rhc/commands/git_clone.rb b/lib/rhc/commands/git_clone.rb index b17882a3d..6052e3326 100644 --- a/lib/rhc/commands/git_clone.rb +++ b/lib/rhc/commands/git_clone.rb @@ -10,14 +10,13 @@ class GitClone < Base "figures out the Git url from the application name so you don't", "have to look it up." syntax " [--namespace NAME]" - option ["-n", "--namespace NAME"], "Namespace of the application", :context => :namespace_context, :required => true + takes_application :argument => true option ["-r", "--repo dir"], "Path to the Git repository (defaults to ./$app_name)" - argument :app, "The application you wish to clone", ["-a", "--app name"] alias_action 'app git-clone', :deprecated => true, :root_command => true # TODO: Implement default values for arguments once ffranz has added context arguments # argument :directory, "The name of a new directory to clone into", [], :default => nil def run(app_name) - rest_app = rest_client.find_application(options.namespace, app_name) + rest_app = find_app dir = git_clone_application(rest_app) success "Your application Git repository has been cloned to '#{system_path(dir)}'" diff --git a/lib/rhc/commands/member.rb b/lib/rhc/commands/member.rb new file mode 100644 index 000000000..5e8bc1e8e --- /dev/null +++ b/lib/rhc/commands/member.rb @@ -0,0 +1,148 @@ +require 'rhc/commands/base' + +module RHC::Commands + class Member < Base + summary "Manage membership on domains" + syntax "" + description <<-DESC + Teams of developers can collaborate on applications by adding people to + domains as members: each member has a role (admin, editor, or viewer), + and those roles determine what the user can do with the domain and the + applications contained within. + + Roles: + + view - able to see information about the domain and its apps, but not make any changes + edit - create, update, and delete applications, and has Git and SSH access + admin - can update membership of a domain + + The default role granted to members when added is 'edit' - use the '--role' + argument to use another. When adding and removing members, you can use their + 'login' value (typically their email or a short unique name for them) or their + 'id'. Both login and ID are visible via the 'rhc account' command. + + To see existing members of a domain or application, use: + + rhc members -n [-a ] + + To change the role for a user, simply call the add-member command with the new role. You + cannot change the role of the owner. + DESC + syntax "" + default_action :help + + summary "List members of a domain or application" + syntax " [-n DOMAIN_NAME] [-a APP_NAME]" + description <<-DESC + Show the existing members of a domain or application - you can pass the name + of your domain with '-n', the name of your application with '-a', or combine + them in the first argument to the command like: + + rhc members /[] + + The owner is always listed first. To see the unique ID of members, pass + '--ids'. + DESC + option ['--ids'], "Display the IDs of each member", :optional => true + takes_application_or_domain :argument => true + alias_action :members, :root_command => true + def list(path) + target = find_app_or_domain(path) + members = target.members.sort_by{ |m| [m.owner? ? 0 : 1, m.role_weight, m.name] } + show_name = members.any?{ |m| m.name && m.name != m.login } + members.map! do |m| + [ + ((m.name || "") if show_name), + m.login || "", + m.owner? ? "#{m.role} (owner)" : m.role, + (m.id if options.ids) + ].compact + end + say table(members, :header => [('Name' if show_name), 'Login', 'Role', ("ID" if options.ids)].compact) + + 0 + end + + summary "Add or update a member on a domain" + syntax " [...] [-n DOMAIN_NAME] [--role view|edit|admin] [--ids]" + description <<-DESC + Adds or updates members on a domain by passing one or more login + or ids for other people on OpenShift. The login and ID values for each + account are displayed in 'rhc account'. To change the role for a user, simply + call the add-member command with the new role. You cannot change the role of + the owner. + + Roles + view - able to see information about the domain and its apps, + but not make any changes + edit - create, update, and delete applications, and has Git + and SSH access + admin - can update membership of a domain + + The default role granted to members when added is 'edit' - use the '--role' + argument for 'view' or 'admin'. + + Examples + rhc add-member sally joe -n mydomain + Gives the accounts with logins 'sally' and 'joe' edit access on mydomain + + rhc add-member bob@example.com --role admin -n mydomain + Gives the account with login 'bob@example.com' admin access on mydomain + + DESC + takes_domain + option ['--ids'], "Treat the arguments as a list of IDs", :optional => true + option ['-r', '--role ROLE'], "The role to give to each member - view, edit, or admin (default 'edit')", :type => Role, :optional => true + argument :members, "A list of members logins to add. Pass --ids to treat this as a list of IDs.", [], :type => :list + def add(members) + target = find_domain + role = options.role || 'edit' + raise ArgumentError, 'You must pass one or more logins or ids to this command' unless members.present? + say "Adding #{pluralize(members.length, role_name(role))} to #{target.class.model_name.downcase} ... " + target.update_members(changes_for(members, role)) + success "done" + + 0 + end + + summary "Remove a member from a domain" + syntax " [...] [-n DOMAIN_NAME] [--ids]" + description <<-DESC + Remove members on a domain by passing one or more login or ids for each + member you wish to remove. View the list of existing members with + 'rhc members '. + + Pass '--all' to remove all but the owner from the domain. + DESC + takes_domain + option ['--ids'], "Treat the arguments as a list of IDs" + option ['--all'], "Remove all members from this domain." + argument :members, "Member logins to remove from the domain. Pass --ids to treat this as a list of IDs.", [], :type => :list + def remove(members) + target = find_domain + + if options.all + say "Removing all members from #{target.class.model_name.downcase} ... " + target.delete_members + success "done" + + else + raise ArgumentError, 'You must pass one or more logins or ids to this command' unless members.present? + say "Removing #{pluralize(members.length, 'member')} from #{target.class.model_name.downcase} ... " + target.update_members(changes_for(members, 'none')) + success "done" + end + + 0 + end + + protected + def changes_for(members, role) + members.map do |m| + h = {:role => role} + h[options.ids ? :id : :login] = m + h + end + end + end +end \ No newline at end of file diff --git a/lib/rhc/commands/port_forward.rb b/lib/rhc/commands/port_forward.rb index 7be468f50..ea89ac8f3 100644 --- a/lib/rhc/commands/port_forward.rb +++ b/lib/rhc/commands/port_forward.rb @@ -69,11 +69,10 @@ class PortForward < Base summary "Forward remote ports to the workstation" syntax "" - option ["-n", "--namespace NAME"], "Namespace of the application you are port forwarding to", :context => :namespace_context, :required => true - argument :app, "Application you are port forwarding to (required)", ["-a", "--app NAME"] + takes_application :argument => true option ["-g", "--gear ID"], "Gear ID you are port forwarding to (optional)" def run(app) - rest_app = rest_client.find_application(options.namespace, app) + rest_app = find_app ssh_uri = URI.parse(options.gear ? rest_app.gear_ssh_url(options.gear) : rest_app.ssh_url) say "Using #{ssh_uri}..." if options.debug diff --git a/lib/rhc/commands/snapshot.rb b/lib/rhc/commands/snapshot.rb index 05bf95265..c12d78b72 100644 --- a/lib/rhc/commands/snapshot.rb +++ b/lib/rhc/commands/snapshot.rb @@ -19,16 +19,15 @@ class Snapshot < Base summary "Save a snapshot of your app to disk" syntax " [--filepath FILE] [--ssh path_to_ssh_executable]" - option ["-n", "--namespace NAME"], "Namespace of the application you are saving a snapshot", :context => :namespace_context, :required => true + takes_application :argument => true option ["-f", "--filepath FILE"], "Local path to save tarball (default: ./$APPNAME.tar.gz)" option ["--ssh PATH"], "Full path to your SSH executable with additional options" - argument :app, "Application you are saving a snapshot", ["-a", "--app NAME"] alias_action :"app snapshot save", :root_command => true, :deprecated => true def save(app) ssh = check_ssh_executable! options.ssh - rest_app = rest_client.find_application(options.namespace, app) + rest_app = find_app ssh_uri = URI.parse(rest_app.ssh_url) - filename = options.filepath ? options.filepath : "#{app}.tar.gz" + filename = options.filepath ? options.filepath : "#{rest_app.name}.tar.gz" ssh_cmd = "#{ssh} #{ssh_uri.user}@#{ssh_uri.host} 'snapshot' > #{filename}" debug ssh_cmd @@ -36,10 +35,9 @@ def save(app) say "Pulling down a snapshot to #{filename}..." begin - - if ! RHC::Helpers.windows? - output = Kernel.send(:`, ssh_cmd) - if $?.exitstatus != 0 + if !RHC::Helpers.windows? + status, output = exec(ssh_cmd) + if status != 0 debug output raise RHC::SnapshotSaveException.new "Error in trying to save snapshot. You can try to save manually by running:\n#{ssh_cmd}" end @@ -66,19 +64,18 @@ def save(app) summary "Restores a previously saved snapshot" syntax " [--filepath FILE] [--ssh path_to_ssh_executable]" - option ["-n", "--namespace NAME"], "Namespace of the application you are restoring a snapshot", :context => :namespace_context, :required => true + takes_application :argument => true option ["-f", "--filepath FILE"], "Local path to restore tarball" option ["--ssh PATH"], "Full path to your SSH executable with additional options" - argument :app, "Application of which you are restoring a snapshot", ["-a", "--app NAME"] alias_action :"app snapshot restore", :root_command => true, :deprecated => true def restore(app) ssh = check_ssh_executable! options.ssh - filename = options.filepath ? options.filepath : "#{app}.tar.gz" + rest_app = find_app + filename = options.filepath ? options.filepath : "#{rest_app.name}.tar.gz" if File.exists? filename include_git = RHC::Helpers.windows? ? true : RHC::TarGz.contains(filename, './*/git') - rest_app = rest_client.find_application(options.namespace, app) ssh_uri = URI.parse(rest_app.ssh_url) ssh_cmd = "cat '#{filename}' | #{ssh} #{ssh_uri.user}@#{ssh_uri.host} 'restore#{include_git ? ' INCLUDE_GIT' : ''}'" @@ -87,9 +84,9 @@ def restore(app) debug ssh_cmd begin - if ! RHC::Helpers.windows? - output = Kernel.` ssh_cmd - if $?.exitstatus != 0 + if !RHC::Helpers.windows? + status, output = exec(ssh_cmd) + if status != 0 debug output raise RHC::SnapshotRestoreException.new "Error in trying to restore snapshot. You can try to restore manually by running:\n#{ssh_cmd}" end diff --git a/lib/rhc/commands/ssh.rb b/lib/rhc/commands/ssh.rb index 7445cbf38..6520bd499 100644 --- a/lib/rhc/commands/ssh.rb +++ b/lib/rhc/commands/ssh.rb @@ -9,35 +9,32 @@ class Ssh < Base summary "SSH into the specified application" description <<-DESC - Connect to your application using SSH. This will connect to your primary gear + Connect to your application using SSH. This will connect to your primary gear (the one with your Git repository and web cartridge) by default. To SSH to other gears run 'rhc show-app --gears' to get a list of their SSH hosts. - You may run a specific SSH command by passing one or more arguments, or use a + You may run a specific SSH command by passing one or more arguments, or use a different SSH executable or pass options to SSH with the '--ssh' option. DESC syntax " [--ssh path_to_ssh_executable]" - argument :app, "The name of the application you want to SSH into", ["-a", "--app NAME"], :context => :app_context - argument :command, "Command to run in the application's SSH session", [], :arg_type => :list, :required => false + takes_application :argument => true + argument :command, "Command to run in the application's SSH session", [], :type => :list, :optional => true option ["--ssh PATH"], "Path to your SSH executable or additional options" - option ["-n", "--namespace NAME"], "Namespace of the application", :context => :namespace_context, :required => false option ["--gears"], "Execute this command on all gears in the app. Requires a command." - option ["--limit INTEGER", Integer], "Limit the number of simultaneous SSH connections opened with --gears (default: 5)." + option ["--limit INTEGER"], "Limit the number of simultaneous SSH connections opened with --gears (default: 5).", :type => Integer, :default => 5 option ["--raw"], "Output only the data returned by each host, no hostname prefix." alias_action 'app ssh', :root_command => true - def run(app_name, command) - raise ArgumentError, "No application specified" unless app_name.present? + def run(_, command) raise ArgumentError, "--gears requires a command" if options.gears && command.blank? raise ArgumentError, "--limit must be an integer greater than zero" if options.limit && options.limit < 1 ssh = check_ssh_executable! options.ssh if options.gears - groups = rest_client.find_application_gear_groups(options.namespace, app_name) - run_on_gears(command.join(' '), groups) + run_on_gears(command.join(' '), find_app(:with_gear_groups => true)) 0 else - rest_app = rest_client.find_application(options.namespace, app_name) + rest_app = find_app $stderr.puts "Connecting to #{rest_app.ssh_string.to_s} ..." unless command.present? debug "Using user specified SSH: #{options.ssh}" if options.ssh diff --git a/lib/rhc/commands/tail.rb b/lib/rhc/commands/tail.rb index 518149c11..f194c2ba1 100644 --- a/lib/rhc/commands/tail.rb +++ b/lib/rhc/commands/tail.rb @@ -8,15 +8,14 @@ class Tail < Base summary "Tail the logs of an application" syntax "" - argument :app, "Name of application you wish to view the logs of", ["-a", "--app NAME"] - option ["-n", "--namespace NAME"], "Namespace of your application", :context => :namespace_context, :required => true + takes_application :argument => true option ["-o", "--opts options"], "Options to pass to the server-side (linux based) tail command (applicable to tail command only) (-f is implicit. See the linux tail man page full list of options.) (Ex: --opts '-n 100')" option ["-f", "--files files"], "File glob relative to app (default /logs/*) (optional)" option ["-g", "--gear ID"], "Tail only a specific gear" #option ["-c", "--cartridge name"], "Tail only a specific cartridge" alias_action :"app tail", :root_command => true, :deprecated => true def run(app_name) - rest_app = rest_client.find_application(options.namespace, app_name, :include => :cartridges) + rest_app = find_app(:include => :cartridges) ssh_url = options.gear ? rest_app.gear_ssh_url(options.gear) : rest_app.ssh_url tail('*', URI(ssh_url), options) diff --git a/lib/rhc/commands/threaddump.rb b/lib/rhc/commands/threaddump.rb index 200b216df..836b80dc6 100644 --- a/lib/rhc/commands/threaddump.rb +++ b/lib/rhc/commands/threaddump.rb @@ -3,10 +3,9 @@ module RHC::Commands class Threaddump < Base summary "Trigger a thread dump for JBoss and Ruby apps" syntax "" - option ["-n", "--namespace NAME"], "Namespace of your application", :context => :namespace_context, :required => true - argument :app, "Name of the application on which to execute the thread dump", ["-a", "--app name"] + takes_application :argument => true def run(app) - rest_app = rest_client.find_application(options.namespace, app) + rest_app = find_app rest_app.threaddump.messages.each { |m| say m } 0 diff --git a/lib/rhc/config.rb b/lib/rhc/config.rb index e2041d7a1..2639cb5e2 100644 --- a/lib/rhc/config.rb +++ b/lib/rhc/config.rb @@ -126,6 +126,7 @@ def set_defaults @defaults.add('libra_server', 'openshift.redhat.com') @env_config.add('libra_server', ENV['LIBRA_SERVER']) if ENV['LIBRA_SERVER'] + @env_config.add('libra_server', ENV['RHC_SERVER']) if ENV['RHC_SERVER'] @opts_config_path = nil end diff --git a/lib/rhc/context_helper.rb b/lib/rhc/context_helper.rb index 06a66e551..784b58e88 100644 --- a/lib/rhc/context_helper.rb +++ b/lib/rhc/context_helper.rb @@ -8,30 +8,102 @@ module RHC module ContextHelpers include RHC::GitHelpers - def server_context - ENV['LIBRA_SERVER'] || (!options.clean && config['libra_server']) || "openshift.redhat.com" + def self.included(other) + other.module_eval do + def self.takes_domain(opts={}) + option ["-n", "--namespace NAME"], "Name of a domain" + #if opts[:argument] + # argument :target, "The name of a domain", ["-t", "--target NAME_OR_PATH"], :allow_nil => true, :covered_by => [:namespace] + #end + end + # Does not take defaults to avoid conflicts + def self.takes_application_or_domain(opts={}) + option ["-n", "--namespace NAME"], "Name of a domain" + option ["-a", "--app NAME"], "Name of an application" + if opts[:argument] + argument :target, "The name of a domain, or an application name with domain (domain or domain/application)", ["-t", "--target NAME_OR_PATH"], :allow_nil => true, :covered_by => [:application_id, :namespace, :app] + end + end + def self.takes_application(opts={}) + if opts[:argument] + argument :app, "Name of an application", ["-a", "--app NAME"], :allow_nil => true, :default => :from_local_git, :covered_by => :application_id + else + option ["-a", "--app NAME"], "Name of an application", :default => :from_local_git + end + option ["-n", "--namespace NAME"], "Name of a domain", :default => :from_local_git + option ["--application-id ID"], "ID of an application", :hide => true, :default => :from_local_git + end + end end - def app_context - debug "Getting app context" - - name = git_config_get "rhc.app-name" - return name if name.present? - - uuid = git_config_get "rhc.app-uuid" + def find_domain(opts={}) + domain = options.namespace || options.target || namespace_context + if domain + rest_client.find_domain(domain) + else + raise ArgumentError, "You must specify a domain with -n." + end + end - if uuid.present? - # proof of concept - we shouldn't be traversing - # the broker should expose apis for getting the application via a uuid - rest_client.domains.each do |rest_domain| - rest_domain.applications.each do |rest_app| - return rest_app.name if rest_app.uuid == uuid + def find_app_or_domain(opts={}) + domain, app = + if options.target.present? + options.target.split(/\//) + elsif options.namespace || options.app + if options.app =~ /\// + options.app.split(/\//) + else + [options.namespace || namespace_context, options.app] end end + if app && domain + rest_client.find_application(domain, app) + elsif domain + rest_client.find_domain(domain) + else + raise ArgumentError, "You must specify a domain with -n, or an application with -a." + end + end - debug "Couldn't find app with UUID == #{uuid}" + def find_app(opts={}) + if id = options.application_id + if opts.delete(:with_gear_groups) + return rest_client.find_application_by_id_gear_groups(id, opts) + else + return rest_client.find_application_by_id(id, opts) + end + end + domain, app = + if options.app + if options.app =~ /\// + options.app.split(/\//) + else + [options.namespace || namespace_context, options.app] + end + end + if app && domain + if opts.delete(:with_gear_groups) + rest_client.find_application_gear_groups(domain, app, opts) + else + rest_client.find_application(domain, app, opts) + end + else + raise ArgumentError, "You must specify an application with -a, or run this command from within Git directory cloned from OpenShift." end - nil + end + + def server_context + ENV['LIBRA_SERVER'] || (!options.clean && config['libra_server']) || "openshift.redhat.com" + end + + def from_local_git(defaults, arg) + @local_git_config ||= { + :application_id => git_config_get('rhc.app-id').presence, + :app => git_config_get('rhc.app-name').presence, + :namespace => git_config_get('rhc.domain-name').presence, + } + defaults[arg] ||= @local_git_config[arg] unless @local_git_config[arg].nil? + @local_git_config end def namespace_context diff --git a/lib/rhc/core_ext.rb b/lib/rhc/core_ext.rb index 0c93c2d2b..35d448859 100644 --- a/lib/rhc/core_ext.rb +++ b/lib/rhc/core_ext.rb @@ -189,6 +189,12 @@ def slice!(*args) end s end + def slice(*args) + args.inject({}) do |h, k| + h[k] = self[k] if has_key?(k) + h + end + end def reverse_merge!(other_hash) # right wins if there is no left merge!( other_hash ){|key,left,right| left } diff --git a/lib/rhc/coverage_helper.rb b/lib/rhc/coverage_helper.rb index 14414756c..903a09ba2 100644 --- a/lib/rhc/coverage_helper.rb +++ b/lib/rhc/coverage_helper.rb @@ -6,7 +6,7 @@ require 'simplecov' SimpleCov.start do coverage_dir 'coverage/features/' - command_name 'Cucumber Features' + command_name 'Integration Tests' # Filters - these files will be ignored. add_filter 'lib/rhc/vendor/' # vendored files should be taken directly and only diff --git a/lib/rhc/exceptions.rb b/lib/rhc/exceptions.rb index e2eeddc79..83c00fda7 100644 --- a/lib/rhc/exceptions.rb +++ b/lib/rhc/exceptions.rb @@ -159,6 +159,18 @@ def initialize(message="The amount of additional storage to be removed exceeds t end end + class ChangeMembersOnResourceNotSupported < Exception + def initialize(message="You can only add or remove members on a domain.") + super message, 1 + end + end + + class MembersNotSupported < Exception + def initialize(message="The server does not support adding or removing members.") + super message, 1 + end + end + class UnsupportedError < Exception def initialize(message="This operation is not supported by the server.") super message, 1 diff --git a/lib/rhc/git_helpers.rb b/lib/rhc/git_helpers.rb index 4776bc274..ce2702a9d 100644 --- a/lib/rhc/git_helpers.rb +++ b/lib/rhc/git_helpers.rb @@ -34,7 +34,7 @@ def git_clone_application(app) debug "Configuring git repo" Dir.chdir(repo_dir) do - git_config_set "rhc.app-uuid", app.uuid + git_config_set "rhc.app-id", app.uuid git_config_set "rhc.app-name", app.name git_config_set "rhc.domain-name", app.domain_id end @@ -47,12 +47,11 @@ def git_clone_application(app) # :nocov: These all call external binaries so test them in cucumber def git_config_get(key) config_get_cmd = "git config --get #{key}" - debug "Running #{config_get_cmd}" - uuid = %x[#{config_get_cmd}].strip - debug "UUID = '#{uuid}'" - uuid = nil if $?.exitstatus != 0 or uuid.empty? + value = %x[#{config_get_cmd}].strip + debug "Git config '#{config_get_cmd}' returned '#{value}'" + value = nil if $?.exitstatus != 0 or value.empty? - uuid + value end def git_config_set(key, value) diff --git a/lib/rhc/helpers.rb b/lib/rhc/helpers.rb index bed1e8ce7..0ce946651 100644 --- a/lib/rhc/helpers.rb +++ b/lib/rhc/helpers.rb @@ -141,6 +141,17 @@ def user_agent #:nocov: end + ROLES = {'view' => 'viewer', 'edit' => 'editor', 'admin' => 'administrator'} + OptionParser.accept(Role = Class.new) do |s| + s.downcase! + (ROLES.has_key?(s) && s) or + raise OptionParser::InvalidOption.new(nil, "The provided role '#{s}' is not valid. Supported values: #{ROLES.keys.join(', ')}") + end + + def role_name(s) + ROLES[s.downcase] + end + def openshift_server to_host((options.server rescue nil) || ENV['LIBRA_SERVER'] || "openshift.redhat.com") end @@ -191,7 +202,7 @@ def client_from_options(opts) :url => openshift_rest_endpoint.to_s, :debug => options.debug, :timeout => options.timeout, - :warn => self.class.instance_method(:warn).bind(self), + :warn => BOUND_WARNING, }.merge!(ssl_options).merge!(opts)) end @@ -211,6 +222,7 @@ def certificate_file(file) raise OptionParser::InvalidOption.new(nil, "The certificate '#{file}' cannot be loaded: #{e.message} (#{e.class})") end + # # Output helpers # @@ -229,6 +241,11 @@ def debug? $terminal.debug? end + def exec(cmd) + output = Kernel.send(:`, cmd) + [$?.exitstatus, output] + end + def disable_deprecated? ENV['DISABLE_DEPRECATED'] == '1' end @@ -237,10 +254,6 @@ def deprecated_command(correct, short=false) deprecated("This command is deprecated. Please use '#{correct}' instead.", short) end - def deprecated_option(deprecated, other) - deprecated("The option '#{deprecated}' is deprecated. Please use '#{other}' instead") - end - def deprecated(msg,short = false) raise DeprecatedError.new(msg % ['an error','a warning',0]) if disable_deprecated? warn "Warning: #{msg}\n" % ['a warning','an error',1] @@ -317,13 +330,16 @@ def table_heading(value) headings.merge!({ :creation_time => "Created", :expires_in_seconds => "Expires In", - :uuid => "UUID", + :uuid => "ID", + :id => 'ID', :current_scale => "Current", :scales_from => "Minimum", :scales_to => "Maximum", :gear_sizes => "Allowed Gear Sizes", :consumed_gears => "Gears Used", :max_gears => "Gears Allowed", + :max_domains => "Domains Allowed", + :compact_members => "Members", :gear_info => "Gears", :plan_id => "Plan", :url => "URL", @@ -453,5 +469,6 @@ def collect_env_vars(items) env_vars end + BOUND_WARNING = self.method(:warn).to_proc end end diff --git a/lib/rhc/output_helpers.rb b/lib/rhc/output_helpers.rb index 8772219a8..13e74d48d 100644 --- a/lib/rhc/output_helpers.rb +++ b/lib/rhc/output_helpers.rb @@ -10,7 +10,8 @@ def display_domain(domain, applications=nil) get_properties( domain, :creation_time, - :allowed_gear_sizes + :allowed_gear_sizes, + :compact_members ), :delete => true end @@ -156,7 +157,7 @@ def format_table(heading,values,opts = {}) values = values.to_a if values.is_a? Hash values.delete_if do |arr| arr[0] = "#{table_heading(arr.first)}:" if arr[0].is_a? Symbol - opts[:delete] and arr.last.blank? + opts[:delete] and arr.last.nil? || arr.last == "" end table(values, :heading => heading, :indent => heading ? ' ' : nil, :color => opts[:color]) @@ -206,7 +207,7 @@ def format_value(prop,value) distance_of_time_in_words(value) else case value - when Array then value.join(', ') + when Array then value.empty? ? '' : value.join(', ') else value end end diff --git a/lib/rhc/rest.rb b/lib/rhc/rest.rb index 2233d5e05..1d6878587 100644 --- a/lib/rhc/rest.rb +++ b/lib/rhc/rest.rb @@ -16,6 +16,7 @@ module Rest autoload :EnvironmentVariable, 'rhc/rest/environment_variable' autoload :GearGroup, 'rhc/rest/gear_group' autoload :Key, 'rhc/rest/key' + autoload :Membership, 'rhc/rest/membership' autoload :User, 'rhc/rest/user' class Exception < RuntimeError diff --git a/lib/rhc/rest/application.rb b/lib/rhc/rest/application.rb index 6ea6d3765..ab0782433 100644 --- a/lib/rhc/rest/application.rb +++ b/lib/rhc/rest/application.rb @@ -3,6 +3,8 @@ module RHC module Rest class Application < Base + include Membership + define_attr :domain_id, :name, :creation_time, :uuid, :git_url, :app_url, :gear_profile, :framework, :scalable, :health_check_path, :embedded, :gear_count, @@ -14,6 +16,14 @@ def scalable? scalable end + def id + attributes['id'] || uuid + end + + def domain + domain_id + end + def scalable_carts return [] unless scalable? carts = cartridges.select(&:scalable?) @@ -58,10 +68,6 @@ def cartridges end end - def domain - domain_id - end - def gear_info { :gear_count => gear_count, :gear_profile => gear_profile } unless gear_count.nil? end diff --git a/lib/rhc/rest/attributes.rb b/lib/rhc/rest/attributes.rb index f18c97f9a..271782831 100644 --- a/lib/rhc/rest/attributes.rb +++ b/lib/rhc/rest/attributes.rb @@ -29,4 +29,8 @@ def define_attr(*names) end end end + + def model_name + name.split("::").last + end end diff --git a/lib/rhc/rest/base.rb b/lib/rhc/rest/base.rb index 3290c7777..39a5caa61 100644 --- a/lib/rhc/rest/base.rb +++ b/lib/rhc/rest/base.rb @@ -51,9 +51,10 @@ def has_param?(sym, name) end end - def link_href(sym, params=nil, &block) + def link_href(sym, params=nil, resource=nil, &block) if (l = link(sym)) && (h = l['href']) h = h.gsub(/:\w+/){ |s| params[s].nil? ? s : CGI.escape(params[s]) } if params + h = "#{h}/#{resource}" if resource return h end yield if block_given? diff --git a/lib/rhc/rest/cartridge.rb b/lib/rhc/rest/cartridge.rb index 6d4b45ece..1dc75639c 100644 --- a/lib/rhc/rest/cartridge.rb +++ b/lib/rhc/rest/cartridge.rb @@ -34,6 +34,10 @@ def tags Array(attribute(:tags)) end + def gear_storage + (base_gear_storage + additional_gear_storage) * 1024 * 1024 * 1024 + end + def additional_gear_storage attribute(:additional_gear_storage).to_i rescue 0 end diff --git a/lib/rhc/rest/client.rb b/lib/rhc/rest/client.rb index 37f614a66..24e657ad3 100644 --- a/lib/rhc/rest/client.rb +++ b/lib/rhc/rest/client.rb @@ -14,10 +14,11 @@ module Rest # callable from the client for convenience. # module ApiMethods - def add_domain(id) - debug "Adding domain #{id}" + def add_domain(id, payload={}) + debug "Adding domain #{id} with options #{payload.inspect}" @domains = nil - api.rest_method "ADD_DOMAIN", :id => id + payload.delete_if{ |k,v| k.nil? or v.nil? } + api.rest_method "ADD_DOMAIN", {:id => id}.merge(payload) end def domains @@ -56,37 +57,38 @@ def user def find_domain(id) debug "Finding domain #{id}" if link = api.link_href(:SHOW_DOMAIN, ':name' => id) - request({ - :url => link, - :method => "GET", - }) + request(:url => link, :method => "GET") else domains.find{ |d| d.id.downcase == id.downcase } end or raise DomainNotFoundException.new("Domain #{id} not found") end def find_application(domain, application, options={}) - response = request({ - :url => link_show_application_by_domain_name(domain, application), - :method => "GET", - :payload => options - }) + request(:url => link_show_application_by_domain_name(domain, application), :method => "GET", :payload => options) end def find_application_gear_groups(domain, application, options={}) - response = request({ - :url => link_show_application_by_domain_name(domain, application, "gear_groups"), - :method => "GET", - :payload => options - }) + request(:url => link_show_application_by_domain_name(domain, application, "gear_groups"), :method => "GET", :payload => options) end def find_application_aliases(domain, application, options={}) - response = request({ - :url => link_show_application_by_domain_name(domain, application, "aliases"), - :method => "GET", - :payload => options - }) + request(:url => link_show_application_by_domain_name(domain, application, "aliases"), :method => "GET", :payload => options) + end + + def find_application_by_id(id, options={}) + if api.supports? :show_application + request(:url => link_show_application_by_id(id), :method => "GET", :payload => options) + else + applications.find{ |a| a.id == id } + end or raise ApplicationNotFoundException.new("Application with id #{id} not found") + end + + def find_application_by_id_gear_groups(id, options={}) + if api.supports? :show_application + request(:url => link_show_application_by_id(id, 'gear_groups'), :method => "GET", :payload => options) + else + applications.find{ |a| return a.gear_groups if a.id == id } + end or raise ApplicationNotFoundException.new("Application with id #{id} not found") end def link_show_application_by_domain_name(domain, application, *args) @@ -98,6 +100,10 @@ def link_show_application_by_domain_name(domain, application, *args) ].concat(args).map{ |s| URI.escape(s) }.join("/") end + def link_show_application_by_id(id, *args) + api.link_href(:SHOW_APPLICATION, {':id' => id}, *args) + end + def link_show_domain_by_name(domain, *args) api.link_href(:SHOW_DOMAIN, ':id' => domain) end @@ -191,6 +197,14 @@ def authorization_scope_list h end end + + def reset + (instance_variables - [ + :@end_point, :@debug, :@preferred_api_versions, :@auth, :@options, :@headers, + :@last_options, :@httpclient, :@self_signed, :@current_api_version, :@api + ]).each{ |sym| instance_variable_set(sym, nil) } + self + end end class Client < Base @@ -368,13 +382,14 @@ def httpclient_for(options, auth=nil) if !@httpclient || @last_options != options @httpclient = RHC::Rest::HTTPClient.new(:agent_name => user_agent).tap do |http| + debug "Created new httpclient" http.cookie_manager = nil http.debug_dev = $stderr if ENV['HTTP_DEBUG'] - options.select{ |sym, value| http.respond_to?("#{sym}=") }.map{ |sym, value| http.send("#{sym}=", value) } + options.select{ |sym, value| http.respond_to?("#{sym}=") }.each{ |sym, value| http.send("#{sym}=", value) } ssl = http.ssl_config - options.select{ |sym, value| ssl.respond_to?("#{sym}=") }.map{ |sym, value| ssl.send("#{sym}=", value) } + options.select{ |sym, value| ssl.respond_to?("#{sym}=") }.each{ |sym, value| ssl.send("#{sym}=", value) } ssl.add_trust_ca(options[:ca_file]) if options[:ca_file] ssl.verify_callback = default_verify_callback @@ -451,6 +466,7 @@ def new_request(options) # remove all unnecessary options options.delete(:lazy_auth) + options.delete(:accept) args = [options.delete(:method), options.delete(:url), query, payload, headers, true] [httpclient_for(options, auth), args] diff --git a/lib/rhc/rest/domain.rb b/lib/rhc/rest/domain.rb index 918841c18..f70d5f386 100644 --- a/lib/rhc/rest/domain.rb +++ b/lib/rhc/rest/domain.rb @@ -1,16 +1,18 @@ module RHC module Rest class Domain < Base - define_attr :id, :allowed_gear_sizes, :creation_time + include Membership - def allowed_gear_sizes - Array(attribute(:allowed_gear_sizes)) + define_attr :id, :name, :allowed_gear_sizes, :creation_time + + alias_method :id_attr, :id + alias_method :name_attr, :name + def id + name_attr || id_attr end - def owner - if o = Array(attribute(:members)).find{ |m| m['owner'] == true } - o['name'] - end + def allowed_gear_sizes + Array(attribute(:allowed_gear_sizes)) end #Add Application to this domain @@ -57,12 +59,17 @@ def applications(options = {}) rest_method "LIST_APPLICATIONS", options end - def update(new_id) + def rename(new_id) debug "Updating domain #{id} to #{new_id}" # 5 minute timeout as this may take time if there are a lot of apps rest_method "UPDATE", {:id => new_id}, {:timeout => 0} end - alias :save :update + alias :update :rename + + def configure(payload, options={}) + self.attributes = rest_method("UPDATE", payload, options).attributes + self + end def destroy(force=false) debug "Deleting domain #{id}" diff --git a/lib/rhc/rest/membership.rb b/lib/rhc/rest/membership.rb new file mode 100644 index 000000000..2fc6fc000 --- /dev/null +++ b/lib/rhc/rest/membership.rb @@ -0,0 +1,105 @@ +module RHC::Rest + module Membership + class Member < Base + define_attr :name, :login, :id, :type, :from, :role, :owner, :explicit_role + def owner? + !!owner + end + def admin? + role == 'admin' + end + def editor? + role == 'edit' + end + def viewer? + role == 'view' + end + def name + attributes['name'] || login + end + def to_s + if name == login + "#{login} (#{role})" + elsif login + "#{name} <#{login}> (#{role})" + else + "#{name} (#{role})" + end + end + def role_weight + case role + when 'admin' then 0 + when 'edit' then 1 + when 'view' then 2 + else 3 + end + end + end + + def self.included(other) + end + + def supports_members? + supports? 'LIST_MEMBERS' + end + + def supports_update_members? + supports? 'UPDATE_MEMBERS' + end + + def members + @members ||= + if (members = attributes['members']).nil? + debug "Getting all members for #{id}" + raise RHC::MembersNotSupported unless supports_members? + rest_method 'LIST_MEMBERS' + else + members.map{ |m| Member.new(m, client) } + end + end + + def compact_members + arr = members.reject(&:owner?) + if arr.length > 5 + arr.sort_by!(&:name) + admin, arr = arr.partition(&:admin?) + edit, arr = arr.partition(&:editor?) + view, arr = arr.partition(&:viewer?) + admin << "Admins" if admin.present? + edit << "Editors" if edit.present? + view << "Viewers" if view.present? + arr.map!(&:to_s) + admin.concat(edit).concat(view).concat(arr) + elsif arr.present? + arr.sort_by{ |m| [m.role_weight, m.name] }.join(', ') + end + end + + def update_members(members, options={}) + raise "Members must be an array" unless members.is_a?(Array) + raise RHC::MembersNotSupported unless supports_members? + raise RHC::ChangeMembersOnResourceNotSupported unless supports_update_members? + @members = (attributes['members'] = rest_method('UPDATE_MEMBERS', {:members => members}, options)) + end + + def delete_members(options={}) + raise RHC::MembersNotSupported unless supports_members? + rest_method "LIST_MEMBERS", nil, {:method => :delete}.merge(options) + ensure + @members = attributes['members'] = nil + end + + def leave(options={}) + raise RHC::MembersNotSupported unless supports? 'LEAVE' + rest_method "LEAVE", nil, options + ensure + @members = attributes['members'] = nil + end + + def owner + if o = Array(attribute(:members)).find{ |m| m['owner'] == true } + o['name'] || o['login'] + end + end + end +end \ No newline at end of file diff --git a/lib/rhc/rest/mock.rb b/lib/rhc/rest/mock.rb index 55ab6024c..cd6bedae9 100644 --- a/lib/rhc/rest/mock.rb +++ b/lib/rhc/rest/mock.rb @@ -33,6 +33,13 @@ def credentials_for(with_auth) end end + def example_allows_members? + respond_to?(:supports_members?) && supports_members? + end + def example_allows_gear_sizes? + respond_to?(:supports_allowed_gear_sizes?) && supports_allowed_gear_sizes? + end + def expect_authorization(with_auth) username, password = credentials_for(with_auth) lambda{ |r| @@ -47,7 +54,6 @@ def stub_api_request(method, uri, with_auth=true) api.with(&user_agent_header) if @challenge stub_request(method, mock_href(uri, false)).to_return(:status => 401, :headers => {'www-authenticate' => 'basic realm="openshift broker"'}) - @challenge = false end api end @@ -178,10 +184,12 @@ def stub_one_domain(name, optional_params=nil, with_auth=mock_user_auth) to_return({ :body => { :type => 'domains', - :data => [{:id => name, :links => mock_response_links([ + :data => [{:id => name, :links => mock_response_links(mock_domain_links(name).concat([ ['LIST_APPLICATIONS', "broker/rest/domains/#{name}/applications", 'get'], ['ADD_APPLICATION', "broker/rest/domains/#{name}/applications", 'post', ({:optional_params => optional_params} if optional_params)], - ])}], + (['LIST_MEMBERS', "broker/rest/domains/#{name}/members", 'get'] if example_allows_members?), + (['UPDATE_MEMBERS', "broker/rest/domains/#{name}/members", 'patch'] if example_allows_members?), + ].compact))}], }.to_json }) end @@ -370,7 +378,9 @@ def mock_app_links(domain_id='test_domain',app_id='test_app') ['LIST_ALIASES', "domains/#{domain_id}/apps/#{app_id}/aliases", 'get'], ['LIST_ENVIRONMENT_VARIABLES', "domains/#{domain_id}/apps/#{app_id}/event", 'post'], ['SET_UNSET_ENVIRONMENT_VARIABLES', "domains/#{domain_id}/apps/#{app_id}/event", 'post'], - ['DELETE', "broker/rest/domains/#{domain_id}/applications/#{app_id}", 'DELETE']] + ['DELETE', "broker/rest/domains/#{domain_id}/applications/#{app_id}", 'delete'], + (['LIST_MEMBERS', "domains/#{domain_id}/apps/#{app_id}/members", 'get'] if example_allows_members?), + ].compact end def mock_cart_links(domain_id='test_domain',app_id='test_app',cart_id='test_cart') @@ -390,7 +400,7 @@ def mock_client_links def mock_real_client_links [['GET_USER', "broker/rest/user", 'GET'], ['LIST_DOMAINS', "broker/rest/domains", 'GET'], - ['ADD_DOMAIN', "broker/rest/domains", 'POST'], + ['ADD_DOMAIN', "broker/rest/domains", 'POST', ({'optional_params' => [{'name' => 'allowed_gear_sizes'}]} if example_allows_gear_sizes?)].compact, ['LIST_CARTRIDGES', "broker/rest/cartridges", 'GET'], ] end @@ -406,7 +416,7 @@ def mock_api_with_authorizations def mock_domain_links(domain_id='test_domain') [['ADD_APPLICATION', "domains/#{domain_id}/apps/add", 'post', {'optional_params' => [{'name' => 'environment_variables'}]} ], ['LIST_APPLICATIONS', "domains/#{domain_id}/apps/", 'get' ], - ['UPDATE', "domains/#{domain_id}/update", 'post'], + ['UPDATE', "domains/#{domain_id}/update", 'put'], ['DELETE', "domains/#{domain_id}/delete", 'post']] end @@ -571,6 +581,16 @@ def find_application_gear_groups(domain, name, options = {}) raise RHC::Rest::ApplicationNotFoundException.new("Application #{name} does not exist") end + + def find_application_by_id(id, options={}) + @domains.each{ |d| d.applications.each{ |a| return a if a.id == id } } + raise RHC::Rest::ApplicationNotFoundException.new("Application with id #{id} does not exist") + end + + def find_application_by_id_gear_groups(id, options={}) + @domains.each{ |d| d.applications.each{ |a| return a.gear_groups if a.id == id } } + raise RHC::Rest::ApplicationNotFoundException.new("Application with id #{id} does not exist") + end end class MockRestApi < RHC::Rest::Api @@ -613,7 +633,7 @@ def initialize(client, id) self.attributes = {:links => mock_response_links(mock_domain_links('mock_domain_0'))} end - def update(id) + def rename(id) @id = id self end @@ -643,6 +663,11 @@ def add_application(name, type=nil, scale=nil, gear_profile='default', git_url=n def applications(*args) @applications end + + def add_member(member) + (@members ||= []) << member + self + end end class MockRestGearGroup < RHC::Rest::GearGroup @@ -759,6 +784,10 @@ def add_cartridge(cart, embedded=true, environment_variables=nil) c end + def id + @uuid || attributes['uuid'] || attributes['id'] + end + def gear_groups # we don't have heavy interaction with gear groups yet so keep this simple @gear_groups ||= begin @@ -830,6 +859,10 @@ def unset_environment_variables(env_vars=[]) end end + def add_member(member) + (@members ||= []) << member + self + end end class MockRestCartridge < RHC::Rest::Cartridge diff --git a/lib/rhc/rest/user.rb b/lib/rhc/rest/user.rb index f506f2b7e..3b4f097a4 100644 --- a/lib/rhc/rest/user.rb +++ b/lib/rhc/rest/user.rb @@ -3,7 +3,7 @@ module RHC module Rest class User < Base - define_attr :login, :plan_id, :max_gears, :consumed_gears + define_attr :id, :login, :plan_id, :max_gears, :consumed_gears, :max_domains def add_key(name, content, type) debug "Add key #{name} of type #{type} for user #{login}" @@ -20,6 +20,10 @@ def find_key(name) keys.detect { |key| key.name == name } end + def max_domains + attributes['max_domains'] || 1 + end + def capabilities @capabilities ||= OpenStruct.new attribute('capabilities') end diff --git a/lib/rhc/usage_templates/command_syntax_help.erb b/lib/rhc/usage_templates/command_syntax_help.erb index a2220b9a4..8b223e7ba 100644 --- a/lib/rhc/usage_templates/command_syntax_help.erb +++ b/lib/rhc/usage_templates/command_syntax_help.erb @@ -4,6 +4,8 @@ Pass '--help' to see the full list of options <% end -%> <% unless @actions.nil? or @actions.empty? -%> +<%= @command.description || @command.summary %> + List of Actions <%= table(@actions.map{ |a| [a[:name], a[:summary]] }, table_args(' ', 18)).join("\n") %> <% end -%> diff --git a/spec/coverage_helper.rb b/spec/coverage_helper.rb index b63080742..e8b701663 100644 --- a/spec/coverage_helper.rb +++ b/spec/coverage_helper.rb @@ -22,19 +22,41 @@ def print_missed_lines end end - SimpleCov.at_exit do - begin - SimpleCov.result.format! - if SimpleCov.result.covered_percent < 100.0 - SimpleCov.result.print_missed_lines if SimpleCov.result.covered_percent > 98.0 - STDERR.puts "Coverage not 100%, build failed." - exit 1 + unless SimpleCov.respond_to? :minimum_coverage= + module SimpleCov + def self.minimum_coverage=(value) + @minimum_coverage = value + end + def self.minimum_coverage + @minimum_coverage || -1 + end + end + + SimpleCov.at_exit do + begin + SimpleCov.result.format! + SimpleCov.result.print_missed_lines if SimpleCov.result.covered_percent.between?(98.0,99.9999999) + if SimpleCov.result.covered_percent < SimpleCov.minimum_coverage + STDERR.puts "Coverage not above #{SimpleCov.minimum_coverage}, build failed." + exit 1 + end + rescue Exception => e + STDERR.puts "Exception at exit, #{e.message}" + end + end + else + SimpleCov.at_exit do + begin + SimpleCov.result.format! + SimpleCov.result.print_missed_lines if SimpleCov.result.covered_percent.between?(98.0,99.9999999) + rescue Exception => e + STDERR.puts "Exception at exit, #{e.message}" end - rescue Exception => e - STDERR.puts "Exception at exit, #{e.message}" end end + SimpleCov.minimum_coverage = 100 + SimpleCov.start do coverage_dir 'coverage/spec/' diff --git a/spec/direct_execution_helper.rb b/spec/direct_execution_helper.rb new file mode 100644 index 000000000..7b0dcbb62 --- /dev/null +++ b/spec/direct_execution_helper.rb @@ -0,0 +1,242 @@ +require 'open4' +require 'rhc/helpers' + +$source_bin_rhc = File.expand_path('bin/rhc') + +SimpleCov.minimum_coverage = 0 # No coverage testing for features + +# +# RHC_DEBUG=true TEST_INSECURE=1 TEST_USERNAME=test1 TEST_PASSWORD=password \ +# RHC_SERVER=hostname \ +# bundle exec rspec features/*_feature.rb +# + +module RhcExecutionHelper + class Result < Struct.new(:args, :status, :stdout, :stderr) + def to_s + "Ran #{args.inspect} and got #{status}\n#{'-'*50}\n#{stdout}#{'-'*50}\n#{stderr}" + end + def successful? + status == 0 + end + end + + def when_running(*args) + subject{ rhc *args } + let(:output){ subject.stdout } + let(:status){ subject.status } + before{ standard_config } + end + + def a_web_cartridge + 'php-5.3' + end + + def rhc(*args) + opts = args.pop if args.last.is_a? Hash + opts ||= {} + if user = opts[:as] + args << '--rhlogin' + args << user.login + if user.respond_to? :token + args << '--token' + args << user.token + elsif user.respond_to? :password + args << '--password' + args << user.password + end + elsif !server_supports_sessions? + args << '--password' + args << ENV['TEST_PASSWORD'] + end + execute_command(args.unshift(rhc_executable), opts[:with]) + end + + def execute_command(args, stdin="", tty=true) + stdin = stdin.join("\n") if stdin.is_a? Array + stdout, stderr = + if debug? + [STDOUT, STDERR].map{ |t| RHC::Helpers::StringTee.new(t) } + else + [StringIO.new, StringIO.new] + end + + args.map!(&:to_s) + status = Open4.spawn(args, 'stdout' => stdout, 'stderr' => stderr, 'stdin' => stdin, 'quiet' => true) + stdout, stderr = [stdout, stderr].map(&:string) + Result.new(args, status, stdout, stderr).tap do |r| + STDERR.puts "\n[#{example_description}] #{r}" if debug? + end + end + + def rhc_executable + ENV['RHC_TEST_SYSTEM'] ? 'rhc' : $source_bin_rhc + end + + def client + @client ||= (@environment && @environment[:client]) || begin + WebMock.allow_net_connect! + opts = {:server => ENV['RHC_SERVER']} + if token = RHC::Auth::TokenStore.new(File.expand_path("~/.openshift")).get(ENV['TEST_USERNAME'], ENV['RHC_SERVER']) + opts[:token] = token + else + opts[:user] = ENV['TEST_USERNAME'] + opts[:password] = ENV['TEST_PASSWORD'] + end + opts[:verify_mode] = OpenSSL::SSL::VERIFY_NONE if ENV['TEST_INSECURE'] == '1' + env = RHC::Rest::Client.new(opts) + @environment[:client] = env if @environment + env + end + end + + def base_client(user, password) + opts = {:server => ENV['RHC_SERVER']} + opts[:user] = user + opts[:password] = password + opts[:verify_mode] = OpenSSL::SSL::VERIFY_NONE if ENV['TEST_INSECURE'] == '1' + RHC::Rest::Client.new(opts) + end + + def no_applications(constraint=nil) + STDERR.puts "Removing applications that match #{constraint}" if debug? + apps = client.reset.applications + apps.each do |app| + next if constraint && !(app.name =~ constraint) + STDERR.puts " removing #{app.name}" if debug? + app.destroy + end + end + + def other_users + $other_users ||= begin + (ENV['TEST_OTHER_USERS'] || "other1:a,other2:b,other3:c,other4:d").split(',').map{ |s| s.split(':') }.inject({}) do |h, (u, p)| + h[u] = base_client(u, p).user + h[u].attributes[:password] = p + h + end + end + end + + def no_members(object) + object.delete_members + object.members.length.should == 1 + end + + def has_an_application + STDERR.puts "Creating or reusing an app" if debug? + apps = client.applications + apps.first or begin + domain = has_a_domain + STDERR.puts " creating a new application" if debug? + client.domains.first.add_application("test#{random}", :cartridges => [a_web_cartridge]) + end + end + + def has_a_domain + STDERR.puts "Creating or reusing a domain" if debug? + domain = client.domains.first or begin + STDERR.puts " creating a new domain" if debug? + client.add_domain("test#{random}") + end + end + + def setup_args(opts={}) + args = [] + args << 'yes' if (ENV['TEST_INSECURE'] == '1' || false) + args << ENV['TEST_USERNAME'] + args << ENV['TEST_PASSWORD'] + args << 'yes' if server_supports_sessions? + args << 'yes' # generate a key, temp dir will never have one + args << ENV['TEST_USERNAME'] if (client.find_key('default').present? rescue false) # same key name as username + args << "d#{random}" if (client.domains.empty? rescue true) + args + end + + def use_clean_config + environment + FileUtils.rm_rf(File.join(@environment[:dir], ".openshift")) + client.reset + end + + def standard_config + environment(:standard) do + r = rhc :setup, :with => setup_args + raise "Unable to configure standard config" if r.status != 0 + end + client.reset + end + + def debug? + @debug ||= !!ENV['RHC_DEBUG'] + end + + def random + @environment[:id] + end + + def server_supports_sessions? + @environment && client.supports_sessions? + end + + private + def example_description + if respond_to?(:example) && example + example.metadata[:full_description] + else + self.class.example.metadata[:full_description] + end + end + + def environment(id=nil) + unless @environment + is_new = false + e = Environments.get(id){ is_new = true} + update_env(e) + + dir = Dir.mktmpdir('rhc_features_test') + at_exit{ FileUtils.rm_rf(dir) } + Dir.chdir(dir) + + @client = e[:client] + @environment = e + yield if block_given? && is_new + end + @environment + end + + def update_env(config) + ENV['HOME'] = config[:dir] + ENV['RHC_SERVER'] ||= 'openshift.redhat.com' + if ENV['TEST_RANDOM_USER'] + { + 'TEST_USERNAME' => "test_user_#{config[:id]}", + 'TEST_PASSWORD' => "password", + }.each_pair{ |k,v| ENV[k] = v } + else + ENV['TEST_USERNAME'] or raise "No TEST_USERNAME set" + ENV['TEST_PASSWORD'] or raise "No TEST_PASSWORD set" + end + ENV['GIT_SSH'] = config[:ssh_exec] + end +end + +module Environments + def self.get(id, &block) + (@environments ||= {})[id] ||= begin + dir = Dir.mktmpdir('rhc_features') + at_exit{ FileUtils.rm_rf(dir) } + id = Random.rand(1000000) + ssh_exec = File.join(dir, "ssh_exec") + IO.write(ssh_exec, "#!/bin/sh\nssh -o StrictHostKeyChecking=no -i #{dir}/.ssh/id_rsa \"$@\"") + FileUtils.chmod("u+x", ssh_exec) + yield if block_given? + {:dir => dir, :id => id, :ssh_exec => ssh_exec} + end + end +end + +RSpec.configure do |config| + config.include(RhcExecutionHelper) + config.extend(RhcExecutionHelper) +end \ No newline at end of file diff --git a/spec/rhc/command_spec.rb b/spec/rhc/command_spec.rb index 62707532a..d7412c797 100644 --- a/spec/rhc/command_spec.rb +++ b/spec/rhc/command_spec.rb @@ -118,13 +118,13 @@ def test; 1; end alias_action :exe, :deprecated => true def execute(testarg); 1; end - argument :args, "Test arg list", ['--tests ARG'], :arg_type => :list + argument :args, "Test arg list", ['--tests ARG'], :type => :list summary "Test command execute-list" def execute_list(args); 1; end argument :arg1, "Test arg", ['--test'], :optional => true - argument :arg2, "Test arg list", ['--test2'], :arg_type => :list, :optional => true - argument :arg3, "Test arg list", ['--test3'], :arg_type => :list, :optional => true + argument :arg2, "Test arg list", ['--test2'], :type => :list, :optional => true + argument :arg3, "Test arg list", ['--test3'], :type => :list, :optional => true summary "Test command execute-vararg" def execute_vararg(arg1, arg2, arg3); 1; end @@ -233,6 +233,64 @@ def context_var end end + describe "find_domain" do + let(:instance){ subject } + let(:rest_client){ subject.send(:rest_client) } + let(:options){ subject.send(:options) } + def expects_method(*args) + expect{ subject.send(:find_domain, *args) } + end + before{ subject.stub(:namespace_context).and_return(nil) } + + it("should raise without params"){ expects_method(nil).to raise_error(ArgumentError, /You must specify a domain with -n/) } + it("should handle namespace param"){ options[:namespace] = 'domain_o'; expects_method.to call(:find_domain).on(rest_client).with('domain_o') } + + context "with a context" do + before{ subject.stub(:namespace_context).and_return('domain_s') } + it("should handle namespace param"){ expects_method.to call(:find_domain).on(rest_client).with('domain_s') } + end + end + + describe "find_app" do + let(:instance){ subject } + let(:rest_client){ subject.send(:rest_client) } + let(:options){ subject.send(:options) } + def expects_method(*args) + expect{ subject.send(:find_app, *args) } + end + before{ subject.stub(:namespace_context).and_return('domain_s') } + + it("should raise without params"){ expects_method(nil).to raise_error(ArgumentError, /You must specify an application with -a/) } + + context "when looking for an app" do + it("should raise without app") { expects_method.to raise_error(ArgumentError, /You must specify an application with -a, or run this command/) } + it("should handle namespace param"){ options[:namespace] = 'domain_o'; expects_method.to raise_error(ArgumentError, /You must specify an application with -a, or run this command/) } + it("should accept app param") { options[:app] = 'app_o'; expects_method.to call(:find_application).on(rest_client).with('domain_s', 'app_o', {}) } + it("should split app param") { options[:app] = 'domain_o/app_o'; expects_method.to call(:find_application).on(rest_client).with('domain_o', 'app_o', {}) } + it("should find gear groups") { options[:app] = 'domain_o/app_o'; expects_method(:with_gear_groups => true, :include => :cartridges).to call(:find_application_gear_groups).on(rest_client).with('domain_o', 'app_o', {:include => :cartridges}) } + end + end + + describe "find_domain_or_app" do + let(:instance){ subject } + let(:rest_client){ subject.send(:rest_client) } + let(:options){ subject.send(:options) } + before{ subject.stub(:namespace_context).and_return('domain_s') } + def expects_method(*args) + expect{ subject.send(:find_app_or_domain, *args) } + end + + it("should not infer domain") { expects_method.to raise_error(ArgumentError, /You must specify a domain with -n, or an application with -a/) } + it("should assume domain with -n") { options[:namespace] = 'domain_o'; expects_method.to call(:find_domain).on(rest_client).with('domain_o') } + it("should infer -n when -a is available"){ options[:app] = 'app_o'; expects_method.to call(:find_application).on(rest_client).with('domain_s', 'app_o') } + it("should split -a param") { options[:app] = 'domain_o/app_o'; expects_method.to call(:find_application).on(rest_client).with('domain_o', 'app_o') } + + context "when an app context is available" do + before{ subject.instance_variable_set(:@local_git_config, {:app => 'app_s'}) } + it("should ignore the app context"){ options[:namespace] = 'domain_o'; expects_method(nil).to call(:find_domain).on(rest_client).with('domain_o') } + end + end + describe "rest_client" do let(:instance){ subject } before{ RHC::Rest::Client.any_instance.stub(:api_version_negotiated).and_return(1.4) } diff --git a/spec/rhc/commands/account_spec.rb b/spec/rhc/commands/account_spec.rb index d97138b49..9bb81aa4e 100644 --- a/spec/rhc/commands/account_spec.rb +++ b/spec/rhc/commands/account_spec.rb @@ -16,7 +16,8 @@ challenge{ stub_user } end - it('should display the correct user') { run_output.should =~ /Login:\s*#{username}/ } + it('should display the correct user') { run_output.should =~ /Login #{username}/ } + it('should display the correct server') { run_output.should =~ /on #{server}/ } it('should not show') { run_output.should_not =~ /Plan:/ } it('should show the gear capabilities') { run_output.should =~ /Allowed Gear Sizes:\s*small/ } it('should show the consumed gears') { run_output.should =~ /Gears Used:\s*0/ } diff --git a/spec/rhc/commands/alias_spec.rb b/spec/rhc/commands/alias_spec.rb index cbf797a32..405a66a96 100644 --- a/spec/rhc/commands/alias_spec.rb +++ b/spec/rhc/commands/alias_spec.rb @@ -166,6 +166,13 @@ it { run_output.should =~ /Alias 'www.foo.bar' has been added/m } end + describe 'add alias with implicit context' do + before{ subject.class.any_instance.stub(:git_config_get){ |key| case key; when 'rhc.app-name' then 'mock_app_0'; when 'rhc.domain-name' then 'mock_domain_0'; end } } + let(:arguments) { ['alias', 'add', 'www.foo.bar' ] } + it { expect { run }.to exit_with_code(0) } + it { run_output.should =~ /Alias 'www.foo.bar' has been added/m } + end + describe 'remove alias' do before do rest_client.stub(:api_version_negotiated).and_return(1.4) diff --git a/spec/rhc/commands/app_spec.rb b/spec/rhc/commands/app_spec.rb index 77eee6d8c..57a65b410 100644 --- a/spec/rhc/commands/app_spec.rb +++ b/spec/rhc/commands/app_spec.rb @@ -36,7 +36,7 @@ end describe 'app default' do - before(:each) do + before do FakeFS.deactivate! end @@ -141,6 +141,7 @@ end context "when dealing with ssh keys" do + before(:all){ mock_terminal } subject{ described_class.new(options) } let(:wizard){ s = double('Wizard'); RHC::SSHWizard.should_receive(:new).and_return(s); s } let(:options){ Commander::Command::Options.new(:server => 'foo.com', :rhlogin => 'test') } @@ -168,12 +169,12 @@ context 'when run without a cart' do before{ FakeFS.deactivate! } - let(:arguments) { ['app', 'create', 'app1', '--noprompt', '--timeout', '10', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['app', 'create', 'app1'] } it { run_output.should match(/mock_standalone_cart-1.*Every application needs a web cartridge/m) } end context 'when run with a valid cart' do - let(:arguments) { ['app', 'create', 'app1', 'mock_standalone_cart-1', '--noprompt', '--timeout', '10', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['app', 'create', 'app1', 'mock_standalone_cart-1'] } it { expect { run }.to exit_with_code(0) } it { run_output.should match("Success") } it { run_output.should match("Cartridges: mock_standalone_cart-1\n") } @@ -181,7 +182,7 @@ end context 'when Hosts resolver raises an Exception' do - let(:arguments) { ['app', 'create', 'app1', 'mock_standalone_cart-1', '--noprompt', '--timeout', '10', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['app', 'create', 'app1', 'mock_standalone_cart-1'] } before :each do resolver = Object.new Resolv::Hosts.should_receive(:new).and_return(resolver) @@ -193,7 +194,7 @@ end context 'when run with multiple carts' do - let(:arguments) { ['app', 'create', 'app1', 'mock_standalone_cart-1', 'mock_cart-1', '--noprompt', '-p', 'password'] } + let(:arguments) { ['app', 'create', 'app1', 'mock_standalone_cart-1', 'mock_cart-1'] } it { expect { run }.to exit_with_code(0) } it { run_output.should match("Success") } it { run_output.should match("Cartridges: mock_standalone_cart-1, mock_cart-1\n") } @@ -202,7 +203,7 @@ end context 'when run with a cart URL' do - let(:arguments) { ['app', 'create', 'app1', 'http://foo.com', 'mock_cart-1', '--noprompt', '-p', 'password'] } + let(:arguments) { ['app', 'create', 'app1', 'http://foo.com', 'mock_cart-1'] } it { expect { run }.to exit_with_code(0) } it { run_output.should match("Success") } it { run_output.should match("Cartridges: http://foo.com, mock_cart-1\n") } @@ -211,7 +212,7 @@ end context 'when run with a git url' do - let(:arguments) { ['app', 'create', 'app1', 'mock_standalone_cart-1', '--from', 'git://url', '--noprompt', '-p', 'password'] } + let(:arguments) { ['app', 'create', 'app1', 'mock_standalone_cart-1', '--from', 'git://url'] } it { expect { run }.to exit_with_code(0) } it { run_output.should match("Success") } it { run_output.should match("Git remote: git:fake.foo/git/app1.git\n") } @@ -220,15 +221,15 @@ end context 'when no cartridges are returned' do - before(:each) do + before do domain = rest_client.domains.first end context 'without trace' do - let(:arguments) { ['app', 'create', 'app1', 'nomatch_cart', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['app', 'create', 'app1', 'nomatch_cart'] } it("should display the list of cartridges") { run_output.should match(/Short Name.*mock_standalone_cart-2/m) } end context 'with trace' do - let(:arguments) { ['app', 'create', 'app1', 'nomatch_cart', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password', '--trace'] } + let(:arguments) { ['app', 'create', 'app1', 'nomatch_cart', '--trace'] } it { expect { run }.to raise_error(RHC::CartridgeNotFoundException, "There are no cartridges that match 'nomatch_cart'.") } end end @@ -279,10 +280,10 @@ end describe 'app create enable-jenkins' do - let(:arguments) { ['app', 'create', 'app1', '--trace', 'mock_unique_standalone_cart', '--enable-jenkins', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['app', 'create', 'app1', '--trace', 'mock_unique_standalone_cart', '--enable-jenkins'] } context 'when run' do - before(:each) do + before do @domain = rest_client.add_domain("mockdomain") end it "should create a jenkins app and a regular app with an embedded jenkins client" do @@ -297,10 +298,10 @@ end describe 'app create enable-jenkins with --no-dns' do - let(:arguments) { ['app', 'create', 'app1', 'mock_unique_standalone_cart', '--trace', '--enable-jenkins', '--no-dns', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['app', 'create', 'app1', 'mock_unique_standalone_cart', '--trace', '--enable-jenkins', '--no-dns'] } context 'when run' do - before(:each) do + before do domain = rest_client.add_domain("mockdomain") end it { expect { run }.to_not raise_error } @@ -308,10 +309,10 @@ end describe 'app create enable-jenkins with same name as app' do - let(:arguments) { ['app', 'create', 'app1', 'mock_unique_standalone_cart', '--trace', '--enable-jenkins', 'app1', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['app', 'create', 'app1', 'mock_unique_standalone_cart', '--trace', '--enable-jenkins', 'app1'] } context 'when run' do - before(:each) do + before do domain = rest_client.add_domain("mockdomain") end it { expect { run }.to raise_error(ArgumentError, /You have named both your main application and your Jenkins application/) } @@ -319,10 +320,10 @@ end describe 'app create enable-jenkins with existing jenkins' do - let(:arguments) { ['app', 'create', 'app1', 'mock_unique_standalone_cart', '--trace', '--enable-jenkins', 'jenkins2', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['app', 'create', 'app1', 'mock_unique_standalone_cart', '--trace', '--enable-jenkins', 'jenkins2'] } context 'when run' do - before(:each) do + before do @domain = rest_client.add_domain("mockdomain") @domain.add_application("jenkins", "jenkins-1") end @@ -335,14 +336,14 @@ end describe 'app create jenkins fails to install warnings' do - let(:arguments) { ['app', 'create', 'app1', 'mock_unique_standalone_cart', '--enable-jenkins', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['app', 'create', 'app1', 'mock_unique_standalone_cart', '--enable-jenkins'] } - before(:each) do + before do @domain = rest_client.add_domain("mockdomain") end context 'when run with error in jenkins setup' do - before(:each) do + before do @instance.stub(:add_jenkins_app) { raise Exception } end it "should print out jenkins warning" do @@ -351,7 +352,7 @@ end context 'when run with error in jenkins-client setup' do - before(:each) do + before do @instance.stub(:add_jenkins_cartridge) { raise Exception } end it "should print out jenkins warning" do @@ -360,7 +361,7 @@ end context 'when run without jenkins cartridge available on server' do - before(:each) do + before do @instance.stub(:all_cartridges) { rest_client.cartridges.delete_if { |item| item.name =~ /\Ajenkins/i } } end it "should exit with jenkins error" do @@ -370,10 +371,10 @@ end describe 'app create jenkins install with retries' do - let(:arguments) { ['app', 'create', 'app1', 'mock_unique_standalone_cart', '--enable-jenkins', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['app', 'create', 'app1', 'mock_unique_standalone_cart', '--enable-jenkins'] } context 'when run with server error in jenkins-client setup' do - before(:each) do + before do @domain = rest_client.add_domain("mockdomain") @instance.stub(:add_jenkins_cartridge) { raise RHC::Rest::ServerErrorException.new("Server error", 157) } end @@ -385,10 +386,10 @@ end describe 'dns app create warnings' do - let(:arguments) { ['app', 'create', 'app1', 'mock_unique_standalone_cart', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['app', 'create', 'app1', 'mock_unique_standalone_cart'] } context 'when run' do - before(:each) do + before do @domain = rest_client.add_domain("dnserror") end it { run_output.should match("unable to lookup your hostname") } @@ -396,9 +397,9 @@ end describe 'app create git warnings' do - let(:arguments) { ['app', 'create', 'app1', 'mock_unique_standalone_cart', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['app', 'create', 'app1', 'mock_unique_standalone_cart'] } - before(:each) do + before do @domain = rest_client.add_domain("mockdomain") @instance.stub(:git_clone_application) { raise RHC::GitException } @instance.stub(:check_sshkeys!) @@ -411,7 +412,7 @@ end context 'when run with windows and no nslookup bug' do - before(:each) do + before do RHC::Helpers.stub(:windows?) { true } @instance.stub(:run_nslookup) { true } @instance.stub(:run_ping) { true } @@ -422,7 +423,7 @@ end context 'when run with windows nslookup bug' do - before(:each) do + before do RHC::Helpers.stub(:windows?) { true } @instance.stub(:run_nslookup) { true } @instance.stub(:run_ping) { false } @@ -433,18 +434,6 @@ end end - describe 'app create --nogit deprecated' do - let(:arguments) { ['app', 'create', 'app1', 'mock_unique_standalone_cart', '--noprompt', '--nogit', '--config', '/tmp/test.conf', '-l', 'test@test.foo', '-p', 'password'] } - - before (:each) do - @domain = rest_client.add_domain("mockdomain") - end - - context 'when run' do - it { run_output.should match("The option '--nogit' is deprecated. Please use '--\\[no-\\]git' instead") } - end - end - describe 'app create prompt for sshkeys' do let(:arguments) { ['app', 'create', 'app1', 'mock_unique_standalone_cart', '--config', '/tmp/test.conf', '-l', 'test@test.foo', '-p', 'password'] } @@ -542,10 +531,10 @@ end describe 'app show' do - let(:arguments) { ['app', 'show', 'app1', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['app', 'show', 'app1'] } context 'when run with the same case as created' do - before(:each) do + before do FakeFS.deactivate! @domain = rest_client.add_domain("mockdomain") @domain.add_application("app1", "mock_type") @@ -555,7 +544,7 @@ end context 'when run with scaled app' do - before(:each) do + before do @domain = rest_client.add_domain("mockdomain") app = @domain.add_application("app1", "mock_type", true) cart1 = app.add_cartridge('mock_cart-1') @@ -569,7 +558,7 @@ end context 'when run with custom app' do - before(:each) do + before do @domain = rest_client.add_domain("mockdomain") app = @domain.add_application("app1", "mock_type", true) cart1 = app.add_cartridge('mock_cart-1') @@ -584,10 +573,10 @@ end describe 'app show' do - let(:arguments) { ['app', 'show', 'APP1', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['app', 'show', 'APP1'] } context 'when run with the different case from created' do - before(:each) do + before do @rc = MockRestClient.new @domain = @rc.add_domain("mockdomain") @domain.add_application("app1", "mock_type") @@ -597,10 +586,10 @@ end describe 'app show --state' do - let(:arguments) { ['app', 'show', 'app1', '--state', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['app', 'show', 'app1', '--state'] } context 'when run' do - before(:each) do + before do @domain = rest_client.add_domain("mockdomain") @domain.add_application("app1", "mock_type") end @@ -612,7 +601,7 @@ let(:arguments) { ['app', 'show', 'app1', '--gears'] } context 'when run' do - before(:each) do + before do @domain = rest_client.add_domain("mockdomain") @domain.add_application("app1", "mock_type") end @@ -658,57 +647,49 @@ end end - describe 'app status' do - let(:arguments) { ['app', 'status', 'app1', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } - - context 'when run' do - before(:each) do - @domain = rest_client.add_domain("mockdomain") - @domain.add_application("app1", "mock_type") - end - it { run_output.should match("started") } - it("should warn about deprecation") { run_output.should match("deprecated") } - end - end - describe 'app actions' do - - before(:each) do + before do domain = rest_client.add_domain("mockdomain") app = domain.add_application("app1", "mock_type") app.add_cartridge('mock_cart-1') end context 'app start' do - let(:arguments) { ['app', 'start', '-a', 'app1','--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['app', 'start', '-a', 'app1'] } it { run_output.should match('start') } + it { expect{ run }.to exit_with_code(0) } end context 'app stop' do - let(:arguments) { ['app', 'stop', 'app1','--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['app', 'stop', 'app1'] } it { run_output.should match('stop') } + it { expect{ run }.to exit_with_code(0) } end context 'app force stop' do - let(:arguments) { ['app', 'force-stop', 'app1','--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['app', 'force-stop', 'app1'] } it { run_output.should match('force') } + it { expect{ run }.to exit_with_code(0) } end context 'app restart' do - let(:arguments) { ['app', 'restart', 'app1','--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['app', 'restart', 'app1'] } it { run_output.should match('restart') } + it { expect{ run }.to exit_with_code(0) } end context 'app reload' do - let(:arguments) { ['app', 'reload', 'app1','--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['app', 'reload', 'app1'] } it { run_output.should match('reload') } + it { expect{ run }.to exit_with_code(0) } end context 'app tidy' do - let(:arguments) { ['app', 'tidy', 'app1','--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['app', 'tidy', 'app1'] } it { run_output.should match('cleaned') } + it { expect{ run }.to exit_with_code(0) } end end @@ -724,12 +705,13 @@ describe 'create app with env vars' do before{ @domain = rest_client.add_domain("mockdomain") } - [['app', 'create', 'app1', 'mock_standalone_cart-1', '-e', 'FOO=BAR', '--noprompt', '--timeout', '10', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'], - ['app', 'create', 'app1', 'mock_standalone_cart-1', '--env', 'FOO=BAR', '--noprompt', '--timeout', '10', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'], - ['app', 'create', 'app1', 'mock_standalone_cart-1', 'FOO=BAR', '--noprompt', '--timeout', '10', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] + [['app', 'create', 'app1', 'mock_standalone_cart-1', '-e', 'FOO=BAR'], + ['app', 'create', 'app1', 'mock_standalone_cart-1', '--env', 'FOO=BAR'], + ['app', 'create', 'app1', 'mock_standalone_cart-1', 'FOO=BAR'] ].each_with_index do |args, i| context "when run with single env var #{i}" do let(:arguments) { args } + before { @domain.should_receive(:has_param?).with('ADD_APPLICATION','environment_variables').and_return(true) } it { expect { run }.to exit_with_code(0) } it { run_output.should match("Success") } it { run_output.should match(/Cartridges:\s+mock_standalone_cart-1\n/) } @@ -737,14 +719,15 @@ end end - [['app', 'create', 'app1', 'mock_standalone_cart-1', '-e', 'VAR1=VAL1', '-e', 'VAR2=VAL2', '-e', 'VAR3=VAL3', '--noprompt', '--timeout', '10', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'], - ['app', 'create', 'app1', 'mock_standalone_cart-1', '--env', 'VAR1=VAL1', '--env', 'VAR2=VAL2', '--env', 'VAR3=VAL3', '--noprompt', '--timeout', '10', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'], - ['app', 'create', 'app1', 'mock_standalone_cart-1', 'VAR1=VAL1', 'VAR2=VAL2', 'VAR3=VAL3', '--noprompt', '--timeout', '10', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'], - ['app', 'create', 'app1', 'mock_standalone_cart-1', 'VAR1=VAL1', 'VAR2=VAL2', '-e', 'VAR3=VAL3', '--noprompt', '--timeout', '10', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'], - ['app', 'create', 'app1', 'mock_standalone_cart-1', 'VAR1=VAL1', '--env', 'VAR2=VAL2', '-e', 'VAR3=VAL3', '--noprompt', '--timeout', '10', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] + [['app', 'create', 'app1', 'mock_standalone_cart-1', '-e', 'VAR1=VAL1', '-e', 'VAR2=VAL2', '-e', 'VAR3=VAL3'], + ['app', 'create', 'app1', 'mock_standalone_cart-1', '--env', 'VAR1=VAL1', '--env', 'VAR2=VAL2', '--env', 'VAR3=VAL3'], + ['app', 'create', 'app1', 'mock_standalone_cart-1', 'VAR1=VAL1', 'VAR2=VAL2', 'VAR3=VAL3'], + ['app', 'create', 'app1', 'mock_standalone_cart-1', 'VAR1=VAL1', 'VAR2=VAL2', '-e', 'VAR3=VAL3'], + ['app', 'create', 'app1', 'mock_standalone_cart-1', 'VAR1=VAL1', '--env', 'VAR2=VAL2', '-e', 'VAR3=VAL3'] ].each_with_index do |args, i| context "when run with multiple env vars #{i}" do let(:arguments) { args } + before { @domain.should_receive(:has_param?).with('ADD_APPLICATION','environment_variables').and_return(true) } it { expect { run }.to exit_with_code(0) } it { run_output.should match("Success") } it { run_output.should match(/Cartridges:\s+mock_standalone_cart-1\n/) } @@ -752,9 +735,9 @@ end end - [['app', 'create', 'app1', 'mock_standalone_cart-1', '-e', 'FOO=BAR', '--noprompt', '--timeout', '10', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'], - ['app', 'create', 'app1', 'mock_standalone_cart-1', '--env', 'FOO=BAR', '--noprompt', '--timeout', '10', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'], - ['app', 'create', 'app1', 'mock_standalone_cart-1', 'FOO=BAR', '--noprompt', '--timeout', '10', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] + [['app', 'create', 'app1', 'mock_standalone_cart-1', '-e', 'FOO=BAR'], + ['app', 'create', 'app1', 'mock_standalone_cart-1', '--env', 'FOO=BAR'], + ['app', 'create', 'app1', 'mock_standalone_cart-1', 'FOO=BAR'] ].each_with_index do |args, i| context "when run against a server without env vars support #{i}" do let(:arguments) { args } diff --git a/spec/rhc/commands/cartridge_spec.rb b/spec/rhc/commands/cartridge_spec.rb index 9f1910ccf..a996d3618 100644 --- a/spec/rhc/commands/cartridge_spec.rb +++ b/spec/rhc/commands/cartridge_spec.rb @@ -6,8 +6,8 @@ describe RHC::Commands::Cartridge do def exit_with_code_and_message(code, message = nil) - expect{ run }.to exit_with_code(code) run_output.should match(message) if message + expect{ run }.to exit_with_code(code) end def succeed_with_message(message = "done") @@ -27,11 +27,11 @@ def fail_with_code(code = 1) describe 'run' do let!(:rest_client){ MockRestClient.new } context "with all arguments" do - let(:arguments) { ['cartridges', '--trace', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['cartridges', '--trace'] } it { succeed_with_message /mock_cart-1.*mock_cart-2.*unique_mock_cart-1/m } end context "without password" do - let(:arguments) { ['cartridges', '--trace', '--noprompt', '--config', 'test.conf'] } + let(:arguments) { ['cartridges', '--trace'] } it { succeed_with_message /mock_cart-1.*mock_cart-2.*unique_mock_cart-1/m } end end @@ -66,7 +66,7 @@ def fail_with_code(code = 1) describe 'alias app cartridge' do let!(:rest_client){ MockRestClient.new } - let(:arguments) { ['app', 'cartridge', 'list', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['app', 'cartridge', 'list'] } context 'when run' do it { succeed_with_message /mock_cart-1.*mock_cart-2.*unique_mock_cart-1/m } @@ -75,10 +75,10 @@ def fail_with_code(code = 1) describe 'cartridge add' do let!(:rest_client){ MockRestClient.new } - let(:arguments) { ['cartridge', 'add', 'mock_cart-1', '--app', 'app1', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['cartridge', 'add', 'mock_cart-1', '--app', 'app1'] } context 'when run' do - before(:each) do + before do domain = rest_client.add_domain("mock_domain") app = domain.add_application("app1", "mock_type") end @@ -103,36 +103,38 @@ def fail_with_code(code = 1) end context 'with app context' do - let(:arguments) { ['cartridge', 'add', 'mock_cart-1', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } - before(:each) do - instance.stub(:git_config_get) { |key| @app.uuid if key == "rhc.app-uuid" } + let(:arguments) { ['cartridge', 'add', 'mock_cart-1'] } + before do + instance.stub(:git_config_get) { |key| @app.id if key == "rhc.app-id" } end it{ succeed_with_message } end context 'with named app context' do - let(:arguments) { ['cartridge', 'add', 'mock_cart-1', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } - before(:each) do + let(:arguments) { ['cartridge', 'add', 'mock_cart-1'] } + before do instance.stub(:git_config_get) { |key| @app.name if key == "rhc.app-name" } end it{ succeed_with_message } end context 'without app context' do - let(:arguments) { ['cartridge', 'add', 'mock_cart-1', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } - before(:each) do + let(:arguments) { ['cartridge', 'add', 'mock_cart-1'] } + before do + instance.should_receive(:git_config_get).with('rhc.domain-name').and_return(nil) instance.should_receive(:git_config_get).with('rhc.app-name').and_return(nil) - instance.should_receive(:git_config_get).with('rhc.app-uuid').and_return('') + instance.should_receive(:git_config_get).with('rhc.app-id').and_return('') end it{ fail_with_code } end - context 'without missing app context' do - let(:arguments) { ['cartridge', 'add', 'mock_cart-1', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } - before(:each) do + context 'with unrecognized app context' do + let(:arguments) { ['cartridge', 'add', 'mock_cart-1'] } + before do + instance.should_receive(:git_config_get).with('rhc.domain-name').and_return(nil) instance.should_receive(:git_config_get).with('rhc.app-name').and_return(nil) - instance.should_receive(:git_config_get).with('rhc.app-uuid').and_return('foo') + instance.should_receive(:git_config_get).with('rhc.app-id').and_return('find') end - it{ fail_with_code } + it{ fail_with_code(101) } end end @@ -140,8 +142,8 @@ def fail_with_code(code = 1) let!(:rest_client){ MockRestClient.new } context 'when invoked through an alias' do - let(:arguments) { ['app', 'cartridge', 'add', 'unique_mock_cart', '--app', 'app1', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } - before(:each) do + let(:arguments) { ['app', 'cartridge', 'add', 'unique_mock_cart', '--app', 'app1'] } + before do domain = rest_client.add_domain("mock_domain") app = domain.add_application("app1", "mock_type") end @@ -151,8 +153,8 @@ def fail_with_code(code = 1) end context 'when cartridge does not exist' do - let(:arguments) { ['cartridge', 'add', 'nomatch_cart', '--app', 'app1', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } - before(:each) do + let(:arguments) { ['cartridge', 'add', 'nomatch_cart', '--app', 'app1'] } + before do domain = rest_client.add_domain("mock_domain") app = domain.add_application("app1", "mock_type") end @@ -160,8 +162,8 @@ def fail_with_code(code = 1) end context 'when multiple carts match' do - let(:arguments) { ['cartridge', 'add', 'mock_cart', '-a', 'app1','--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } - before(:each) do + let(:arguments) { ['cartridge', 'add', 'mock_cart', '-a', 'app1'] } + before do domain = rest_client.add_domain("mock_domain") app = domain.add_application("app1", "mock_type") end @@ -171,8 +173,8 @@ def fail_with_code(code = 1) end context 'when cart is premium' do - let(:arguments) { ['cartridge', 'add', 'premium_cart', '-a', 'app1','--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } - before(:each) do + let(:arguments) { ['cartridge', 'add', 'premium_cart', '-a', 'app1'] } + before do domain = rest_client.add_domain("mock_domain") app = domain.add_application("app1", "mock_type") end @@ -186,8 +188,8 @@ def fail_with_code(code = 1) let!(:rest_client){ MockRestClient.new } context 'when run with --noprompt and without --confirm' do - let(:arguments) { ['cartridge', 'remove', 'mock_cart-1', '-a', 'app1', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } - before(:each) do + let(:arguments) { ['cartridge', 'remove', 'mock_cart-1', '-a', 'app1', '--noprompt'] } + before do domain = rest_client.add_domain("mock_domain") app = domain.add_application("app1", "mock_type") app.add_cartridge('mock_cart-1') @@ -197,8 +199,8 @@ def fail_with_code(code = 1) end context 'when run with confirmation' do - let(:arguments) { ['cartridge', 'remove', 'mock_cart-1', '--confirm', '--trace', '-a', 'app1', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } - before(:each) do + let(:arguments) { ['cartridge', 'remove', 'mock_cart-1', '--confirm', '--trace', '-a', 'app1'] } + before do domain = rest_client.add_domain("mock_domain") @app = domain.add_application("app1", "mock_type") end @@ -249,9 +251,9 @@ def fail_with_code(code = 1) describe 'cartridge status' do let!(:rest_client){ MockRestClient.new } - let(:arguments) { ['cartridge', 'status', 'mock_cart-1', '-a', 'app1','--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['cartridge', 'status', 'mock_cart-1', '-a', 'app1'] } - before(:each) do + before do @domain = rest_client.add_domain("mock_domain") @app = @domain.add_application("app1", "mock_type") @app.add_cartridge('mock_cart-1') @@ -262,17 +264,17 @@ def fail_with_code(code = 1) end context 'when run with cart stopped' do - before(:each) { @app.find_cartridge('mock_cart-1').stop } + before { @app.find_cartridge('mock_cart-1').stop } it { run_output.should match('stopped') } end end describe 'cartridge start' do let!(:rest_client){ MockRestClient.new } - let(:arguments) { ['cartridge', 'start', 'mock_cart-1', '-a', 'app1','--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['cartridge', 'start', 'mock_cart-1', '-a', 'app1'] } context 'when run' do - before(:each) do + before do domain = rest_client.add_domain("mock_domain") app = domain.add_application("app1", "mock_type") app.add_cartridge('mock_cart-1') @@ -283,10 +285,10 @@ def fail_with_code(code = 1) describe 'cartridge stop' do let!(:rest_client){ MockRestClient.new } - let(:arguments) { ['cartridge', 'stop', 'mock_cart-1', '-a', 'app1','--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['cartridge', 'stop', 'mock_cart-1', '-a', 'app1'] } context 'when run' do - before(:each) do + before do domain = rest_client.add_domain("mock_domain") app = domain.add_application("app1", "mock_type") app.add_cartridge('mock_cart-1') @@ -297,10 +299,10 @@ def fail_with_code(code = 1) describe 'cartridge restart' do let!(:rest_client){ MockRestClient.new } - let(:arguments) { ['cartridge', 'restart', 'mock_cart-1', '-a', 'app1','--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['cartridge', 'restart', 'mock_cart-1', '-a', 'app1'] } context 'when run' do - before(:each) do + before do domain = rest_client.add_domain("mock_domain") app = domain.add_application("app1", "mock_type") app.add_cartridge('mock_cart-1') @@ -311,10 +313,10 @@ def fail_with_code(code = 1) describe 'cartridge reload' do let!(:rest_client){ MockRestClient.new } - let(:arguments) { ['cartridge', 'reload', 'mock_cart-1', '-a', 'app1','--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['cartridge', 'reload', 'mock_cart-1', '-a', 'app1'] } context 'when run' do - before(:each) do + before do domain = rest_client.add_domain("mock_domain") app = domain.add_application("app1", "mock_type") app.add_cartridge('mock_cart-1') @@ -325,9 +327,9 @@ def fail_with_code(code = 1) describe 'cartridge show' do let!(:rest_client){ MockRestClient.new } - let(:arguments) { ['cartridge', 'show', 'mock_cart-1', '-a', 'app1', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['cartridge', 'show', 'mock_cart-1', '-a', 'app1'] } - before(:each) do + before do domain = rest_client.add_domain("mock_domain") app = domain.add_application("app1", "mock_type") app.add_cartridge('mock_cart-1') @@ -340,9 +342,9 @@ def fail_with_code(code = 1) end describe 'cartridge show' do - let(:arguments) { ['cartridge', 'show', 'Mock_Cart-1', '-a', 'app1', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['cartridge', 'show', 'Mock_Cart-1', '-a', 'app1'] } - before(:each) do + before do @rc = MockRestClient.new domain = @rc.add_domain("mock_domain") app = domain.add_application("app1", "mock_type") @@ -356,9 +358,9 @@ def fail_with_code(code = 1) end describe 'cartridge show' do - let(:arguments) { ['cartridge', 'show', 'premium_cart', '-a', 'app1', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['cartridge', 'show', 'premium_cart', '-a', 'app1'] } - before(:each) do + before do @rc = MockRestClient.new domain = @rc.add_domain("mock_domain") app = domain.add_application("app1", "mock_type") @@ -372,10 +374,10 @@ def fail_with_code(code = 1) describe 'cartridge show scaled' do let!(:rest_client){ MockRestClient.new } - let(:arguments) { ['cartridge', 'show', 'mock_type', '-a', 'app1', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['cartridge', 'show', 'mock_type', '-a', 'app1'] } context 'when run' do - before(:each) do + before do domain = rest_client.add_domain("mock_domain") app = domain.add_application("app1", "mock_type", true) end @@ -387,7 +389,7 @@ def fail_with_code(code = 1) describe 'cartridge scale' do let!(:rest_client){ MockRestClient.new } - let(:arguments) { ['cartridge', 'scale', @cart_type || 'mock_type', '-a', 'app1', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] | (@extra_args || []) } + let(:arguments) { ['cartridge', 'scale', @cart_type || 'mock_type', '-a', 'app1'] | (@extra_args || []) } let(:current_scale) { 1 } before do @@ -460,10 +462,10 @@ def fail_with_code(code = 1) describe 'cartridge storage' do let!(:rest_client){ MockRestClient.new(RHC::Config, 1.3) } let(:cmd_base) { ['cartridge', 'storage'] } - let(:std_args) { ['-a', 'app1', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] | (@extra_args || []) } + let(:std_args) { ['-a', 'app1'] | (@extra_args || []) } let(:cart_type) { ['mock_cart-1'] } - before(:each) do + before do domain = rest_client.add_domain("mock_domain") app = domain.add_application("app1", "mock_type", false) app.add_cartridge('mock_cart-1', true) @@ -561,8 +563,8 @@ def fail_with_code(code = 1) @app = domain.add_application("app1", "mock_type") end - [['app', 'cartridge', 'add', 'unique_mock_cart', '-e', 'FOO=BAR', '--app', 'app1', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'], - ['app', 'cartridge', 'add', 'unique_mock_cart', '--env', 'FOO=BAR', '--app', 'app1', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] + [['app', 'cartridge', 'add', 'unique_mock_cart', '-e', 'FOO=BAR', '--app', 'app1'], + ['app', 'cartridge', 'add', 'unique_mock_cart', '--env', 'FOO=BAR', '--app', 'app1'] ].each_with_index do |args, i| context "when run with single env var #{i}" do let(:arguments) { args } @@ -570,8 +572,8 @@ def fail_with_code(code = 1) end end - [['app', 'cartridge', 'add', 'unique_mock_cart', '-e', 'FOO1=BAR1', '-e', 'FOO2=BAR2', '--app', 'app1', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'], - ['app', 'cartridge', 'add', 'unique_mock_cart', '--env', 'FOO1=BAR1', '--env', 'FOO2=BAR2', '--app', 'app1', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] + [['app', 'cartridge', 'add', 'unique_mock_cart', '-e', 'FOO1=BAR1', '-e', 'FOO2=BAR2', '--app', 'app1'], + ['app', 'cartridge', 'add', 'unique_mock_cart', '--env', 'FOO1=BAR1', '--env', 'FOO2=BAR2', '--app', 'app1'] ].each_with_index do |args, i| context "when run with multiple env vars #{i}" do let(:arguments) { args } @@ -579,8 +581,8 @@ def fail_with_code(code = 1) end end - [['app', 'cartridge', 'add', 'unique_mock_cart', '-e', File.expand_path('../../assets/env_vars.txt', __FILE__), '--app', 'app1', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'], - ['app', 'cartridge', 'add', 'unique_mock_cart', '--env', File.expand_path('../../assets/env_vars.txt', __FILE__), '--app', 'app1', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] + [['app', 'cartridge', 'add', 'unique_mock_cart', '-e', File.expand_path('../../assets/env_vars.txt', __FILE__), '--app', 'app1'], + ['app', 'cartridge', 'add', 'unique_mock_cart', '--env', File.expand_path('../../assets/env_vars.txt', __FILE__), '--app', 'app1'] ].each_with_index do |args, i| context "when run with env vars from files #{i}" do let(:arguments) { args } diff --git a/spec/rhc/commands/domain_spec.rb b/spec/rhc/commands/domain_spec.rb index b512cbd2f..559ee65fb 100644 --- a/spec/rhc/commands/domain_spec.rb +++ b/spec/rhc/commands/domain_spec.rb @@ -3,12 +3,13 @@ require 'rhc/commands/domain' describe RHC::Commands::Domain do - let!(:rest_client){ MockRestClient.new } + let(:rest_client){ MockRestClient.new } before{ user_config } describe 'default action' do + before{ rest_client } context 'when run with no domains' do - let(:arguments) { ['domain', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + let(:arguments) { ['domain'] } it { expect { run }.to exit_with_code(1) } it { run_output.should match(/In order to deploy applications.*rhc create-domain/) } @@ -22,7 +23,8 @@ end describe 'show' do - let(:arguments) { ['domain', 'show', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] } + before{ rest_client } + let(:arguments) { ['domain', 'show'] } context 'when run with no domains' do it { expect { run }.to exit_with_code(1) } @@ -40,7 +42,7 @@ end context 'when run with multiple domain no apps' do - before(:each) do + before do rest_client.add_domain("firstdomain") rest_client.add_domain("seconddomain") end @@ -53,7 +55,7 @@ end context 'when run with one domain multiple apps' do - before(:each) do + before do d = rest_client.add_domain("appdomain") a = d.add_application("app_no_carts", "testframework-1.0") a = d.add_application("app_multi_carts", "testframework-1.0") @@ -74,7 +76,7 @@ end context 'when run with an app without cartridges' do - before(:each) do + before do d = rest_client.add_domain("appdomain") a = d.add_application("app_no_carts") end @@ -89,6 +91,7 @@ describe 'list' do + before{ rest_client } let(:arguments) { ['domain', 'list'] } context 'when run with no domains' do @@ -122,7 +125,7 @@ end context 'when run with multiple domains and extra domain info' do - before(:each) do + before do rest_client.add_domain("firstdomain") rest_client.add_domain("seconddomain", true) end @@ -138,7 +141,8 @@ end describe 'create' do - let(:arguments) { ['domain', 'create', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password', 'testnamespace'] } + before{ rest_client } + let(:arguments) { ['domain', 'create', 'testnamespace'] } context 'when no issues with ' do @@ -146,12 +150,13 @@ expect { run }.to exit_with_code(0) rest_client.domains[0].id.should == 'testnamespace' end - it { run_output.should match(/'testnamespace'.*?RESULT:.*?Success/m) } + it { run_output.should match(/Creating.*'testnamespace'.*done/m) } end end - describe 'update' do - let(:arguments) { ['domain', 'update', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password', 'olddomain', 'alterednamespace'] } + describe 'rename' do + before{ rest_client } + let(:arguments) { ['domain', 'rename', 'olddomain', 'alterednamespace'] } context 'when no issues with ' do before{ rest_client.add_domain("olddomain") } @@ -172,22 +177,115 @@ end end - describe 'alter alias' do - let(:arguments) { ['domain', 'alter', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password', 'olddomain', 'alterednamespace'] } + describe 'update' do + before{ rest_client } + let(:arguments) { ['domain', 'update', 'olddomain', 'alterednamespace'] } - context 'when no issues with ' do - before{ rest_client.add_domain("olddomain") } + before{ rest_client.add_domain("olddomain") } + it "should update a domain" do + expect { run }.to exit_with_code(0) + rest_client.domains[0].id.should == 'alterednamespace' + end + it { run_output.should match(/This command is deprecated.*Renaming domain 'olddomain' to 'alterednamespace'.*done.*?Applications in this domain will use the new name in their URL./m) } + end - it "should update a domain" do - expect { run }.to exit_with_code(0) - rest_client.domains[0].id.should == 'alterednamespace' + describe 'alter alias has been removed' do + let(:arguments) { ['domain', 'alter', 'olddomain', 'alterednamespace'] } + it{ expect { run }.to exit_with_code(1) } + end + + describe 'configure' do + context "no settings" do + before{ rest_client.add_domain("domain1") } + let(:arguments) { ['domain', 'configure', '-n', 'domain1'] } + it("should succeed"){ expect { run }.to exit_with_code(0) } + it("should display the domain config"){ run_output.should match(/Domain domain1 configuration.*Allowed Gear Sizes:\s+/m) } + end + + context "when server does not support allowed-gear-sizes" do + before do + rest_client.add_domain("domain1") + rest_client.api.should_receive(:has_param?).with(:add_domain, 'allowed_gear_sizes').and_return(false) + end + let(:arguments) { ['domain', 'configure', '--allowed-gear-sizes', 'small'] } + it("display a message"){ run_output.should match 'The server does not support --allowed-gear-sizes' } + end + + context "against a server that supports gear sizes" do + let(:username){ 'test_user' } + let(:password){ 'password' } + let(:server){ 'test.domain.com' } + let(:supports_allowed_gear_sizes?){ true } + before{ subject.class.any_instance.stub(:namespace_context).and_return('domain1') } + before do + stub_api + challenge{ stub_one_domain('domain1', nil, mock_user_auth) } + end + + context "with --allowed-gear-sizes singular" do + before do + stub_api_request(:put, "domains/domain1/update", nil). + with(:body => {:allowed_gear_sizes => ['valid']}). + to_return({:body => {:type => 'domain', :data => {:allowed_gear_sizes => ['valid']}, :messages => [{:severity => 'info', :text => 'Updated allowed gear sizes'},]}.to_json, :status => 200}) + end + let(:arguments) { ['domain', 'configure', '--trace', '--allowed-gear-sizes', 'valid'] } + it("should succeed"){ expect { run }.to exit_with_code(0) } + it("should display the domain config"){ run_output.should match(/Domain domain1 configuration.*Allowed Gear Sizes:\s+valid/m) } + end + + context "with --allowed-gear-sizes multiple" do + before do + stub_api_request(:put, "domains/domain1/update", nil). + with(:body => {:allowed_gear_sizes => ['one', 'two']}). + to_return({:body => {:type => 'domain', :data => {:allowed_gear_sizes => ['one', 'two']}, :messages => [{:severity => 'info', :text => 'Updated allowed gear sizes'},]}.to_json, :status => 200}) + end + let(:arguments) { ['domain', 'configure', '--trace', '--allowed-gear-sizes', 'one,two'] } + it("should succeed"){ expect { run }.to exit_with_code(0) } + it("should display the domain config"){ run_output.should match(/Domain domain1 configuration.*Allowed Gear Sizes:\s+one, two/m) } + end + + context "with --allowed-gear-sizes" do + let(:arguments) { ['domain', 'configure', 'domain1', '--trace', '--allowed-gear-sizes'] } + it("raise an invalid option"){ expect{ run }.to raise_error(OptionParser::InvalidOption, /Provide a comma delimited list of valid gear/) } + end + + context "with --allowed-gear-sizes=false" do + before do + stub_api_request(:put, "domains/domain1/update", nil). + with(:body => {:allowed_gear_sizes => ['false']}). + to_return({:body => {:type => 'domain', :messages => [{:field => 'allowed_gear_sizes', :exit_code => 10, :severity => 'error', :text => 'The specified gear size is invalid: false'},]}.to_json, :status => 422}) + end + let(:arguments) { ['domain', 'configure', '--allowed-gear-sizes=false'] } + it("should succeed"){ expect { run }.to exit_with_code(1) } + it("should display the domain config"){ run_output.should match(/Updating domain configuration.*The specified gear size is invalid/m) } + end + + context "with --no-allowed-gear-sizes" do + before do + stub_api_request(:put, "domains/domain1/update", nil). + with(:body => {:allowed_gear_sizes => []}). + to_return({:body => {:type => 'domain', :data => {:allowed_gear_sizes => []}, :messages => [{:severity => 'info', :text => 'Updated allowed gear sizes'},]}.to_json, :status => 200}) + end + let(:arguments) { ['domain', 'configure', '--no-allowed-gear-sizes'] } + it("should succeed"){ expect { run }.to exit_with_code(0) } + it("should display the domain config"){ run_output.should match(/Domain domain1 configuration.*Allowed Gear Sizes:\s+/m) } end - it { run_output.should match(/Renaming domain 'olddomain' to 'alterednamespace'.*done.*?Applications in this domain will use the new name in their URL./m) } + end + end + + describe 'leave' do + before{ rest_client.add_domain("deleteme") } + let(:arguments) { ['domain', 'leave', '-n', 'deleteme'] } + + it "should leave the domain" do + rest_client.domains.first.should_receive(:leave) + expect { run }.to exit_with_code(0) end end describe 'delete' do - let(:arguments) { ['domain', 'delete', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password', 'deleteme'] } + before{ rest_client } + let(:arguments) { ['domain', 'delete', 'deleteme'] } context 'when no issues with ' do before{ rest_client.add_domain("deleteme") } @@ -209,28 +307,15 @@ end context 'when there are applications on the domain' do - before(:each) do + before do domain = rest_client.add_domain("deleteme") domain.add_application 'testapp1', 'mock-1.0' end it "should error out" do - expect { run }.to exit_with_code(128) + expect { run }.to exit_with_code(1) rest_client.domains[0].id.should == 'deleteme' end - it { run_output.should match("Your domain contains applications.*?Delete applications first.") } - end - end - - describe 'alias destroy' do - let(:arguments) { ['domain', 'destroy', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password', 'deleteme'] } - - context 'when no issues with ' do - before{ rest_client.add_domain("deleteme") } - - it "should delete a domain" do - expect { run }.to exit_with_code(0) - rest_client.domains.empty?.should be_true - end + it { run_output.should match("Applications must be empty") } end end diff --git a/spec/rhc/commands/member_spec.rb b/spec/rhc/commands/member_spec.rb new file mode 100644 index 000000000..0a813c31f --- /dev/null +++ b/spec/rhc/commands/member_spec.rb @@ -0,0 +1,228 @@ +require 'spec_helper' +require 'rest_spec_helper' +require 'rhc/commands/member' + +describe RHC::Commands::Member do + + before{ user_config } + + describe 'help' do + let(:arguments) { ['member', '--help'] } + + it "should display help" do + expect { run }.to exit_with_code(0) + end + it('should output usage') { run_output.should match "Usage: rhc member" } + it('should output info about roles') { run_output.should match "Teams of developers can collaborate" } + end + + let(:username){ 'test_user' } + let(:password){ 'test_password' } + let(:server){ 'test.domain.com' } + + def with_mock_rest_client + @rest_client ||= MockRestClient.new + end + def with_mock_domain + @domain ||= with_mock_rest_client.add_domain("mock-domain-0") + end + def with_mock_app + @app ||= begin + app = with_mock_domain.add_application("mock-app-0", "ruby-1.8.7") + app.stub(:ssh_url).and_return("ssh://user@test.domain.com") + app.stub(:supports_members?).and_return(supports_members) + app + end + end + + let(:owner){ RHC::Rest::Membership::Member.new(:id => '1', :role => 'admin', :owner => true, :login => 'alice') } + let(:other_admin){ RHC::Rest::Membership::Member.new(:id => '2', :role => 'admin', :login => 'Bob') } + let(:other_editor){ RHC::Rest::Membership::Member.new(:id => '3', :role => 'editor', :name => 'Carol', :login => 'carol') } + let(:other_viewer){ RHC::Rest::Membership::Member.new(:id => '4', :role => 'viewer', :name => 'Doug', :login => 'doug@doug.com') } + + describe 'list-member' do + context 'on a domain' do + let(:arguments) { ['members', '-n', 'mock-domain-0'] } + let(:supports_members){ true } + before{ with_mock_domain.add_member(owner) } + it { expect { run }.to exit_with_code(0) } + it { run_output.should =~ /alice\s+admin \(owner\)/ } + it("should not show the name column") { run_output.should =~ /^Login\s+Role$/ } + end + + context 'on an application' do + let(:arguments) { ['members', 'mock-domain-0/mock-app-0'] } + let(:supports_members){ false } + before{ with_mock_app } + + it { expect { run }.to exit_with_code(1) } + it { run_output.should =~ /The server does not support adding or removing members/ } + + context "with only owner" do + let(:supports_members){ true } + before{ with_mock_app.add_member(owner) } + it { expect { run }.to exit_with_code(0) } + it { run_output.should =~ /alice\s+admin \(owner\)/ } + it("should not show the name column") { run_output.should =~ /^Login\s+Role$/ } + + context "with ids" do + let(:arguments) { ['members', 'mock-domain-0/mock-app-0', '--ids'] } + it { expect { run }.to exit_with_code(0) } + it { run_output.should =~ /alice\s+admin \(owner\) 1/ } + it("should not show the name column") { run_output.should =~ /^Login\s+Role\s+ID$/ } + end + end + + context "with several members" do + let(:supports_members){ true } + before{ with_mock_app.add_member(owner).add_member(other_editor).add_member(other_admin).add_member(other_viewer) } + it { expect { run }.to exit_with_code(0) } + it { run_output.should =~ /alice\s+admin \(owner\)/ } + it { run_output.should =~ /Bob\s+admin/ } + it { run_output.should =~ /carol\s+editor/ } + it { run_output.should =~ /doug\.com\s+viewer/ } + it("should order the members by role") { run_output.should =~ /admin.*owner.*admin.*edit.*view/m } + it("should include the login value") { run_output.should =~ /alice.*Bob.*carol.*doug@doug\.com/m } + it("should show the name column") { run_output.should =~ /^Name\s+Login\s+Role$/ } + end + end + end + + describe 'add-member' do + before do + stub_api + challenge{ stub_one_domain('test', nil, mock_user_auth) } + end + + context "when the resource doesn't support membership changes" do + let(:arguments) { ['add-member', 'testuser', '-n', 'test'] } + it { expect { run }.to exit_with_code(1) } + it { run_output.should =~ /Adding 1 editor to domain .*The server does not support adding or removing members/ } + end + + context "with supported membership" do + let(:supports_members?){ true } + + context 'with a valid user' do + let(:arguments) { ['add-member', 'testuser', '-n', 'test'] } + before do + stub_api_request(:patch, "broker/rest/domains/test/members"). + with(:body => {:members => [{'login' => 'testuser', 'role' => 'edit'}]}). + to_return({:body => {:type => 'members', :data => [], :messages => [{:exit_code => 0, :field => 'login', :index => 0, :severity => 'info', :text => 'Added 1 member'},]}.to_json, :status => 200}) + end + it { expect { run }.to exit_with_code(0) } + it { run_output.should =~ /Adding 1 editor to domain .*done/ } + end + + context 'with an invalid role' do + let(:arguments) { ['add-member', 'testuser', '-n', 'test', '--role', 'missing'] } + it { expect { run }.to exit_with_code(1) } + it { run_output.should =~ /The provided role 'missing' is not valid\. Supported values: .*admin/ } + end + + context 'with a missing user' do + let(:arguments) { ['add-member', 'testuser', '-n', 'test'] } + before do + stub_api_request(:patch, "broker/rest/domains/test/members"). + with(:body => {:members => [{'login' => 'testuser', 'role' => 'edit'}]}). + to_return({:body => {:messages => [{:exit_code => 132, :field => 'login', :index => 0, :severity => 'error', :text => 'There is no user with a login testuser'},]}.to_json, :status => 422}) + end + it { expect { run }.to exit_with_code(1) } + it { run_output.should =~ /Adding 1 editor to domain.*There is no user with a login testuser/ } + end + + context 'with a missing user id and role' do + let(:arguments) { ['add-member', '123', '-n', 'test', '--ids', '--role', 'admin'] } + before do + stub_api_request(:patch, "broker/rest/domains/test/members"). + with(:body => {:members => [{'id' => '123', 'role' => 'admin'}]}). + to_return({:body => {:messages => [{:exit_code => 132, :field => 'id', :index => 0, :severity => 'error', :text => 'There is no user with the id 123'},]}.to_json, :status => 422}) + end + it { expect { run }.to exit_with_code(1) } + it { run_output.should =~ /Adding 1 administrator to domain.*There is no user with the id 123/ } + end + end + end + + describe 'remove-member' do + context "when the resource doesn't support membership changes" do + before{ stub_api } + + context "when adjusting a domain" do + let(:arguments) { ['remove-member', 'testuser', '-n', 'test'] } + before{ challenge{ stub_one_domain('test', nil, mock_user_auth) } } + it { expect { run }.to exit_with_code(1) } + it { run_output.should =~ /Removing 1 member from domain .*The server does not support adding or removing members/ } + end + end + + context "with supported membership" do + let(:supports_members?){ true } + before do + stub_api + challenge{ stub_one_domain('test', nil, mock_user_auth) } + end +=begin Scenario removed + context "when adjusting an app" do + let(:arguments) { ['remove-member', 'testuser', '-n', 'test', '-a', 'app'] } + before{ challenge{ stub_one_application('test', 'app') } } + it { expect { run }.to exit_with_code(1) } + it { run_output.should =~ /You can only add or remove members on a domain/ } + end +=end + context 'with a valid member' do + let(:arguments) { ['remove-member', 'testuser', '-n', 'test'] } + before do + stub_api_request(:patch, "broker/rest/domains/test/members"). + with(:body => {:members => [{'login' => 'testuser', 'role' => 'none'}]}). + to_return({:body => {:type => 'members', :data => [], :messages => [{:exit_code => 0, :field => 'login', :index => 0, :severity => 'info', :text => 'Removed 1 member'},]}.to_json, :status => 200}) + end + it { expect { run }.to exit_with_code(0) } + it { run_output.should =~ /Removing 1 member from domain .*done/ } + end + + context 'with --all' do + let(:arguments) { ['remove-member', '--all', '-n', 'test'] } + before do + stub_api_request(:delete, "broker/rest/domains/test/members"). + to_return({:body => {:type => 'members', :data => [], :messages => [{:exit_code => 0, :field => 'login', :index => 0, :severity => 'info', :text => 'Removed everyone except owner.'},]}.to_json, :status => 200}) + end + it { expect { run }.to exit_with_code(0) } + it { run_output.should =~ /Removing all members from domain .*done/ } + end + + context 'with a missing user' do + let(:arguments) { ['remove-member', 'testuser', '-n', 'test'] } + before do + stub_api_request(:patch, "broker/rest/domains/test/members"). + with(:body => {:members => [{'login' => 'testuser', 'role' => 'none'}]}). + to_return({:body => {:messages => [{:exit_code => 132, :field => 'login', :index => 0, :severity => 'error', :text => 'There is no user with a login testuser'},]}.to_json, :status => 422}) + end + it { expect { run }.to exit_with_code(1) } + it { run_output.should =~ /Removing 1 member from domain.*There is no user with a login testuser/ } + end + + context 'with a missing user id and role' do + let(:arguments) { ['remove-member', '123', '-n', 'test', '--ids'] } + before do + stub_api_request(:patch, "broker/rest/domains/test/members"). + with(:body => {:members => [{'id' => '123', 'role' => 'none'}]}). + to_return({:body => {:messages => [{:exit_code => 132, :field => 'id', :index => 0, :severity => 'error', :text => 'There is no user with the id 123'},]}.to_json, :status => 422}) + end + it { expect { run }.to exit_with_code(1) } + it { run_output.should =~ /Removing 1 member from domain.*There is no user with the id 123/ } + end + + context 'when the user isn''t a member' do + let(:arguments) { ['remove-member', 'testuser', '-n', 'test'] } + before do + stub_api_request(:patch, "broker/rest/domains/test/members"). + with(:body => {:members => [{'login' => 'testuser', 'role' => 'none'}]}). + to_return({:body => {:type => 'members', :data => [], :messages => [{:exit_code => 0, :field => 'login', :index => 0, :severity => 'warning', :text => 'testuser is not a member of this domain.'},]}.to_json, :status => 200}) + end + it { expect { run }.to exit_with_code(0) } + it { run_output.should =~ /Removing 1 member from domain.*testuser is not a member of this domain.*done/m } + end + end + end +end \ No newline at end of file diff --git a/spec/rhc/commands/port_forward_spec.rb b/spec/rhc/commands/port_forward_spec.rb index e4ebbc0a5..55fb802f4 100644 --- a/spec/rhc/commands/port_forward_spec.rb +++ b/spec/rhc/commands/port_forward_spec.rb @@ -42,7 +42,7 @@ rest_client.domains[0].applications.size.should == 1 rest_client.domains[0].applications[0].name.should == 'mockapp' end - it { run_output.should match("no available ports to forward.") } + it("should report no ports") { run_output.should match("no available ports to forward.") } end context 'when port forwarding an app with permission denied ports' do diff --git a/spec/rhc/commands/snapshot_spec.rb b/spec/rhc/commands/snapshot_spec.rb index ac43af7c3..22469d999 100644 --- a/spec/rhc/commands/snapshot_spec.rb +++ b/spec/rhc/commands/snapshot_spec.rb @@ -28,27 +28,27 @@ end describe 'snapshot save' do - let(:arguments) {['snapshot', 'save', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password', '--app', 'mockapp']} + let(:arguments) {['snapshot', 'save', '--app', 'mockapp', '-d']} context 'when saving a snapshot' do - before(:each) do - `(exit 0)` - Kernel.should_receive(:`).with("ssh #{@ssh_uri.user}@#{@ssh_uri.host} 'snapshot' > #{@app.name}.tar.gz") + before do + subject.class.any_instance.should_receive(:exec).with("ssh #{@ssh_uri.user}@#{@ssh_uri.host} 'snapshot' > #{@app.name}.tar.gz").and_return([0, 'some save output']) end it { expect { run }.to exit_with_code(0) } + it { run_output.should_not match 'some save output' } end context 'when failing to save a snapshot' do - before(:each) do - `(exit 1)` + before do subject.class.any_instance.should_receive(:has_ssh?).and_return(true) - Kernel.should_receive(:`).with("ssh #{@ssh_uri.user}@#{@ssh_uri.host} 'snapshot' > #{@app.name}.tar.gz") + subject.class.any_instance.should_receive(:exec).with("ssh #{@ssh_uri.user}@#{@ssh_uri.host} 'snapshot' > #{@app.name}.tar.gz").and_return([1, 'some save failures']) end it { expect { run }.to exit_with_code(130) } + it { run_output.should match 'some save failures' } end context 'when saving a snapshot on windows' do - before(:each) do + before do RHC::Helpers.stub(:windows?) do ; true; end RHC::Helpers.stub(:jruby?) do ; false ; end RHC::Helpers.stub(:linux?) do ; false ; end @@ -61,7 +61,7 @@ end context 'when timing out on windows' do - before(:each) do + before do RHC::Helpers.stub(:windows?) do ; true; end RHC::Helpers.stub(:jruby?) do ; false ; end RHC::Helpers.stub(:linux?) do ; false ; end @@ -84,31 +84,31 @@ end describe 'snapshot restore' do - let(:arguments) {['snapshot', 'restore', '--noprompt', '-l', 'test@test.foo', '-p', 'password', '--app', 'mockapp']} + let(:arguments) {['snapshot', 'restore', '--app', 'mockapp', '-d']} context 'when restoring a snapshot' do - before(:each) do + before do File.stub(:exists?).and_return(true) RHC::TarGz.stub(:contains).and_return(true) - `(exit 0)` - Kernel.should_receive(:`).with("cat '#{@app.name}.tar.gz' | ssh #{@ssh_uri.user}@#{@ssh_uri.host} 'restore INCLUDE_GIT'") + subject.class.any_instance.should_receive(:exec).with("cat '#{@app.name}.tar.gz' | ssh #{@ssh_uri.user}@#{@ssh_uri.host} 'restore INCLUDE_GIT'").and_return([0, 'some restore output']) end - it { expect { run }.to exit_with_code(0) } + it('should succeed') { expect { run }.to exit_with_code(0) } + it { run_output.should_not match 'some restore output' } end context 'when restoring a snapshot and failing to ssh' do - before(:each) do + before do File.stub(:exists?).and_return(true) RHC::TarGz.stub(:contains).and_return(true) subject.class.any_instance.should_receive(:has_ssh?).and_return(true) - Kernel.should_receive(:`).with("cat '#{@app.name}.tar.gz' | ssh #{@ssh_uri.user}@#{@ssh_uri.host} 'restore INCLUDE_GIT'") - $?.stub(:exitstatus) { 1 } + subject.class.any_instance.should_receive(:exec).with("cat '#{@app.name}.tar.gz' | ssh #{@ssh_uri.user}@#{@ssh_uri.host} 'restore INCLUDE_GIT'").and_return([1, 'some restore failures']) end it { expect { run }.to exit_with_code(130) } + it { run_output.should match 'some restore failures' } end context 'when restoring a snapshot on windows' do - before(:each) do + before do RHC::Helpers.stub(:windows?) do ; true; end RHC::Helpers.stub(:jruby?) do ; false ; end RHC::Helpers.stub(:linux?) do ; false ; end @@ -135,7 +135,7 @@ end context 'when timing out on windows' do - before(:each) do + before do RHC::Helpers.stub(:windows?) do ; true; end RHC::Helpers.stub(:jruby?) do ; false ; end RHC::Helpers.stub(:linux?) do ; false ; end @@ -148,7 +148,7 @@ end describe 'snapshot restore file not found' do - let(:arguments) {['snapshot', 'restore', '--noprompt', '-l', 'test@test.foo', '-p', 'password', '--app', 'mockapp', '-f', 'foo.tar.gz']} + let(:arguments) {['snapshot', 'restore', 'mockapp', '-f', 'foo.tar.gz']} context 'when restoring a snapshot' do it { expect { run }.to exit_with_code(130) } end diff --git a/spec/rhc/commands/ssh_spec.rb b/spec/rhc/commands/ssh_spec.rb index d06e062af..4028f3823 100644 --- a/spec/rhc/commands/ssh_spec.rb +++ b/spec/rhc/commands/ssh_spec.rb @@ -57,6 +57,19 @@ it('should print the ssh output') { run_output.should == "[fakegearid0 ] foo\n[fakegearid1 ] bar\n\n" } it('should return successfully') { expect{ run }.to exit_with_code(0) } end + context 'with an implicit app name' do + before{ subject.class.any_instance.stub(:git_config_get){ |key| 'app1' if key == "rhc.app-name" } } + let(:arguments) { ['app', 'ssh', '--gears', 'command', '--trace'] } + before{ expect_multi_ssh('command', 'fakegearid0@fakesshurl.com' => 'foo', 'fakegearid1@fakesshurl.com' => 'bar') } + it('should print the ssh output') { run_output.should == "[fakegearid0 ] foo\n[fakegearid1 ] bar\n\n" } + it('should return successfully') { expect{ run }.to exit_with_code(0) } + end + context 'with an application id' do + let(:arguments) { ['app', 'ssh', '--application-id', rest_client.domains.first.applications.first.id, '--gears', 'command', '--trace'] } + before{ expect_multi_ssh('command', 'fakegearid0@fakesshurl.com' => 'foo', 'fakegearid1@fakesshurl.com' => 'bar') } + it('should print the ssh output') { run_output.should == "[fakegearid0 ] foo\n[fakegearid1 ] bar\n\n" } + it('should return successfully') { expect{ run }.to exit_with_code(0) } + end context 'with --raw' do let(:arguments) { ['app', 'ssh', 'app1', '--gears', 'command', '--raw'] } before{ expect_multi_ssh('command', 'fakegearid0@fakesshurl.com' => 'foo', 'fakegearid1@fakesshurl.com' => 'bar') } diff --git a/spec/rhc/commands/tail_spec.rb b/spec/rhc/commands/tail_spec.rb index cc02dcafb..cfba0385a 100644 --- a/spec/rhc/commands/tail_spec.rb +++ b/spec/rhc/commands/tail_spec.rb @@ -78,4 +78,4 @@ end end -end +end \ No newline at end of file diff --git a/spec/rhc/helpers_spec.rb b/spec/rhc/helpers_spec.rb index ebce6ef87..9aee61338 100644 --- a/spec/rhc/helpers_spec.rb +++ b/spec/rhc/helpers_spec.rb @@ -181,6 +181,11 @@ def options it{ expect{ run }.to exit_with_code(1) } it{ run_output.should match("invalid argument: --timeout=string") } end + context "that is a negative integer" do + let(:arguments){ ['help', '--timeout=0'] } + it{ expect{ run }.to exit_with_code(1) } + it{ run_output.should match("must be a positive integer") } + end context "via the config" do before{ base_config{ |c, d| d.add 'timeout', 'string' } } let(:arguments){ ['help'] } @@ -255,6 +260,10 @@ def options end end + describe "#exec" do + it{ subject.send(:exec, 'echo foo').should == [0, "foo\n"] } + end + context "Git Helpers" do subject{ Class.new(Object){ include RHC::Helpers; include RHC::GitHelpers; def debug?; false; end }.new } before{ subject.stub(:git_version){ raise "Fake Exception" } } diff --git a/spec/rhc/rest_client_spec.rb b/spec/rhc/rest_client_spec.rb index 5b14b8afa..38831b64e 100644 --- a/spec/rhc/rest_client_spec.rb +++ b/spec/rhc/rest_client_spec.rb @@ -160,6 +160,14 @@ module Rest end end + context "#update_members" do + subject{ RHC::Rest::Application.new } + it "raises when the update link is disabled" do + subject.should_receive(:supports_members?).and_return(true) + expect{ subject.update_members([]) }.to raise_error(RHC::ChangeMembersOnResourceNotSupported) + end + end + context "#domains" do before(:each) do stub_api_request(:any, api_links['LIST_DOMAINS']['relative']). @@ -349,6 +357,66 @@ module Rest end end + context "#find_application_by_id" do + context "when server does not support SHOW_APPLICATION" do + let(:server){ mock_uri } + let(:endpoint){ "https://#{server}/broker/rest/api"} + before do + stub_api + stub_one_domain('test') + stub_one_application('test', 'app1') + end + it "returns an app object for matching IDs" do + match = nil + expect { match = client.find_application_by_id(1) }.to_not raise_error + match.id.should == 1 + match.class.should == RHC::Rest::Application + end + it "raise an error when no matching app IDs can be found" do + expect { client.find_application_by_id('2') }.to raise_error(RHC::Rest::ApplicationNotFoundException) + end + end + + context "when server supports SHOW_APPLICATION" do + let(:api_links){ mock_response_links([['SHOW_APPLICATION', 'application/:id', 'get']]) } + before do + stub_api_request(:any, api_links['SHOW_APPLICATION']['relative'].gsub(/:id/, 'app_0')). + to_return({ :body => { + :type => 'application', + :data => + { :id => 'app_0', + :links => mock_response_links(mock_app_links('app_0')), + } + }.to_json, + :status => 200 + }) + stub_api_request(:any, api_links['SHOW_APPLICATION']['relative'].gsub(/:id/, 'app_1')). + to_return({ :body => {:messages => [{:exit_code => 101}]}.to_json, + :status => 404 + }) + end + it "returns an app object for matching IDs" do + match = nil + expect { match = client.find_application_by_id('app_0') }.to_not raise_error + match.id.should == 'app_0' + match.class.should == RHC::Rest::Application + end + it "raise an error when no matching IDs can be found" do + expect { client.find_application_by_id('app_1') }.to raise_error(RHC::Rest::ApplicationNotFoundException) + end + it "should fetch application ids" do + client.api + client.should_receive(:request).with(:url => "#{api_links['SHOW_APPLICATION']['href'].gsub(/:id/, 'app_2')}", :method => "GET", :payload => {}).and_return(1) + client.find_application_by_id('app_2').should == 1 + end + it "should fetch application gear groups" do + client.api + client.should_receive(:request).with(:url => "#{api_links['SHOW_APPLICATION']['href'].gsub(/:id/, 'app_2')}/gear_groups", :method => "GET", :payload => {}).and_return(1) + client.find_application_by_id_gear_groups('app_2').should == 1 + end + end + end + describe RHC::Rest::Cartridge do subject do RHC::Rest::Cartridge.new({ diff --git a/spec/rhc/rest_spec.rb b/spec/rhc/rest_spec.rb index f331152d5..85d306ed9 100644 --- a/spec/rhc/rest_spec.rb +++ b/spec/rhc/rest_spec.rb @@ -511,7 +511,7 @@ def invoked_with(is_ok, ctx) it("raises not authenticated"){ response.should raise_error(RHC::Rest::UnAuthorizedException, 'Not authenticated') } context "when auth will retry forever" do - let(:auth){ stub('auth', :to_request => nil) } + let(:auth){ double('auth', :to_request => nil) } before{ subject.stub(:auth).and_return(auth); auth.should_receive(:retry_auth?).exactly(4).times.and_return(true) } it("raises not authenticated"){ response.should raise_error(RHC::Rest::UnAuthorizedException, 'Not authenticated') } after{ WebMock.should have_requested(:get, mock_href).times(RHC::Rest::Client::MAX_RETRIES) } diff --git a/tasks/cucumber.rake b/tasks/cucumber.rake index 7975d1804..dae2af30a 100644 --- a/tasks/cucumber.rake +++ b/tasks/cucumber.rake @@ -2,27 +2,24 @@ require 'cucumber' require 'cucumber/rake/task' require 'fileutils' -task :check_features do +task :check_cucumber do end -task :check_features_local do +task :check_cucumber_local do ENV['RHC_FEATURE_COVERAGE'] = '1' ENV['RHC_LOCAL_PATH'] = "#{File.join(Dir.pwd, 'bin')}" # clean coverage results so we do not not merged with stale data - coverage_file = './coverage/features/.resultset.json' + coverage_file = './coverage/cucumber/.resultset.json' FileUtils.rm(coverage_file) if File.exists?(coverage_file) end desc "Run integration suite" -Cucumber::Rake::Task.new(:features => :check_features) do |t| - t.cucumber_opts = "features" +Cucumber::Rake::Task.new(:cucumber => :check_cucumber) do |t| + t.cucumber_opts = "cucumber" end desc "Run integration suite on local bundle" -Cucumber::Rake::Task.new(:features_local => :check_features_local) do |t| - t.cucumber_opts = "features" +Cucumber::Rake::Task.new(:cucumber_local => :check_cucumber_local) do |t| + t.cucumber_opts = "cucumber" end - -task :cucumber => [:features] -task :cucumber_local => [:features_local]