crazy-yard 3.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +19 -0
  3. data/README.md +438 -0
  4. data/bin/ey +9 -0
  5. data/lib/engineyard.rb +9 -0
  6. data/lib/engineyard/cli.rb +816 -0
  7. data/lib/engineyard/cli/api.rb +98 -0
  8. data/lib/engineyard/cli/recipes.rb +129 -0
  9. data/lib/engineyard/cli/ui.rb +275 -0
  10. data/lib/engineyard/cli/web.rb +85 -0
  11. data/lib/engineyard/config.rb +158 -0
  12. data/lib/engineyard/deploy_config.rb +65 -0
  13. data/lib/engineyard/deploy_config/ref.rb +56 -0
  14. data/lib/engineyard/error.rb +82 -0
  15. data/lib/engineyard/eyrc.rb +59 -0
  16. data/lib/engineyard/repo.rb +105 -0
  17. data/lib/engineyard/serverside_runner.rb +159 -0
  18. data/lib/engineyard/templates.rb +6 -0
  19. data/lib/engineyard/templates/ey.yml.erb +196 -0
  20. data/lib/engineyard/templates/ey_yml.rb +119 -0
  21. data/lib/engineyard/thor.rb +215 -0
  22. data/lib/engineyard/version.rb +4 -0
  23. data/lib/vendor/thor/Gemfile +15 -0
  24. data/lib/vendor/thor/LICENSE.md +20 -0
  25. data/lib/vendor/thor/README.md +35 -0
  26. data/lib/vendor/thor/lib/thor.rb +473 -0
  27. data/lib/vendor/thor/lib/thor/actions.rb +318 -0
  28. data/lib/vendor/thor/lib/thor/actions/create_file.rb +105 -0
  29. data/lib/vendor/thor/lib/thor/actions/create_link.rb +60 -0
  30. data/lib/vendor/thor/lib/thor/actions/directory.rb +119 -0
  31. data/lib/vendor/thor/lib/thor/actions/empty_directory.rb +137 -0
  32. data/lib/vendor/thor/lib/thor/actions/file_manipulation.rb +314 -0
  33. data/lib/vendor/thor/lib/thor/actions/inject_into_file.rb +109 -0
  34. data/lib/vendor/thor/lib/thor/base.rb +652 -0
  35. data/lib/vendor/thor/lib/thor/command.rb +136 -0
  36. data/lib/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +80 -0
  37. data/lib/vendor/thor/lib/thor/core_ext/io_binary_read.rb +12 -0
  38. data/lib/vendor/thor/lib/thor/core_ext/ordered_hash.rb +100 -0
  39. data/lib/vendor/thor/lib/thor/error.rb +28 -0
  40. data/lib/vendor/thor/lib/thor/group.rb +282 -0
  41. data/lib/vendor/thor/lib/thor/invocation.rb +172 -0
  42. data/lib/vendor/thor/lib/thor/parser.rb +4 -0
  43. data/lib/vendor/thor/lib/thor/parser/argument.rb +74 -0
  44. data/lib/vendor/thor/lib/thor/parser/arguments.rb +171 -0
  45. data/lib/vendor/thor/lib/thor/parser/option.rb +121 -0
  46. data/lib/vendor/thor/lib/thor/parser/options.rb +218 -0
  47. data/lib/vendor/thor/lib/thor/rake_compat.rb +72 -0
  48. data/lib/vendor/thor/lib/thor/runner.rb +322 -0
  49. data/lib/vendor/thor/lib/thor/shell.rb +88 -0
  50. data/lib/vendor/thor/lib/thor/shell/basic.rb +393 -0
  51. data/lib/vendor/thor/lib/thor/shell/color.rb +148 -0
  52. data/lib/vendor/thor/lib/thor/shell/html.rb +127 -0
  53. data/lib/vendor/thor/lib/thor/util.rb +270 -0
  54. data/lib/vendor/thor/lib/thor/version.rb +3 -0
  55. data/lib/vendor/thor/thor.gemspec +24 -0
  56. data/spec/engineyard/cli/api_spec.rb +50 -0
  57. data/spec/engineyard/cli_spec.rb +28 -0
  58. data/spec/engineyard/config_spec.rb +61 -0
  59. data/spec/engineyard/deploy_config_spec.rb +194 -0
  60. data/spec/engineyard/eyrc_spec.rb +76 -0
  61. data/spec/engineyard/repo_spec.rb +83 -0
  62. data/spec/engineyard_spec.rb +7 -0
  63. data/spec/ey/console_spec.rb +57 -0
  64. data/spec/ey/deploy_spec.rb +435 -0
  65. data/spec/ey/ey_spec.rb +23 -0
  66. data/spec/ey/init_spec.rb +123 -0
  67. data/spec/ey/list_environments_spec.rb +120 -0
  68. data/spec/ey/login_spec.rb +33 -0
  69. data/spec/ey/logout_spec.rb +24 -0
  70. data/spec/ey/logs_spec.rb +36 -0
  71. data/spec/ey/rebuild_spec.rb +18 -0
  72. data/spec/ey/recipes/apply_spec.rb +29 -0
  73. data/spec/ey/recipes/download_spec.rb +43 -0
  74. data/spec/ey/recipes/upload_spec.rb +99 -0
  75. data/spec/ey/rollback_spec.rb +73 -0
  76. data/spec/ey/scp_spec.rb +176 -0
  77. data/spec/ey/servers_spec.rb +209 -0
  78. data/spec/ey/ssh_spec.rb +273 -0
  79. data/spec/ey/status_spec.rb +45 -0
  80. data/spec/ey/timeout_deploy_spec.rb +18 -0
  81. data/spec/ey/web/disable_spec.rb +21 -0
  82. data/spec/ey/web/enable_spec.rb +26 -0
  83. data/spec/ey/web/restart_spec.rb +21 -0
  84. data/spec/ey/whoami_spec.rb +30 -0
  85. data/spec/spec_helper.rb +84 -0
  86. data/spec/support/bundled_ey +7 -0
  87. data/spec/support/fixture_recipes.tgz +0 -0
  88. data/spec/support/git_repos.rb +115 -0
  89. data/spec/support/helpers.rb +330 -0
  90. data/spec/support/matchers.rb +16 -0
  91. data/spec/support/ruby_ext.rb +13 -0
  92. data/spec/support/shared_behavior.rb +278 -0
  93. metadata +411 -0
@@ -0,0 +1,85 @@
1
+ module EY
2
+ class CLI
3
+ class Web < EY::Thor
4
+ desc "enable [--environment/-e ENVIRONMENT]",
5
+ "Remove the maintenance page for this application in the given environment."
6
+ method_option :environment, type: :string, aliases: %w(-e),
7
+ required: true, default: '',
8
+ desc: "Environment on which to take down the maintenance page"
9
+ method_option :app, type: :string, aliases: %w(-a),
10
+ required: true, default: '',
11
+ desc: "Name of the application whose maintenance page will be removed"
12
+ method_option :account, type: :string, aliases: %w(-c),
13
+ required: true, default: '',
14
+ desc: "Name of the account in which the environment can be found"
15
+ method_option :verbose, type: :boolean, aliases: %w(-v),
16
+ desc: "Be verbose"
17
+ def enable
18
+ app_env = fetch_app_environment(options[:app], options[:environment], options[:account])
19
+ ui.info "Taking down maintenance page for '#{app_env.app.name}' in '#{app_env.environment.name}'"
20
+ serverside_runner(app_env, options[:verbose]).take_down_maintenance_page.call(ui.out, ui.err)
21
+ end
22
+
23
+ desc "disable [--environment/-e ENVIRONMENT]",
24
+ "Put up the maintenance page for this application in the given environment."
25
+ long_desc <<-DESC
26
+ The maintenance page is taken from the app currently being deployed. This means
27
+ that you can customize maintenance pages to tell users the reason for downtime
28
+ on every particular deploy.
29
+
30
+ Maintenance pages searched for in order of decreasing priority:
31
+ * public/maintenance.html.custom
32
+ * public/maintenance.html.tmp
33
+ * public/maintenance.html
34
+ * public/system/maintenance.html.default
35
+ DESC
36
+ method_option :environment, type: :string, aliases: %w(-e),
37
+ required: true, default: '',
38
+ desc: "Environment on which to put up the maintenance page"
39
+ method_option :app, type: :string, aliases: %w(-a),
40
+ required: true, default: '',
41
+ desc: "Name of the application whose maintenance page will be put up"
42
+ method_option :account, type: :string, aliases: %w(-c),
43
+ required: true, default: '',
44
+ desc: "Name of the account in which the environment can be found"
45
+ method_option :verbose, type: :boolean, aliases: %w(-v),
46
+ desc: "Be verbose"
47
+ def disable
48
+ app_env = fetch_app_environment(options[:app], options[:environment], options[:account])
49
+ ui.info "Putting up maintenance page for '#{app_env.app.name}' in '#{app_env.environment.name}'"
50
+ serverside_runner(app_env, options[:verbose]).put_up_maintenance_page.call(ui.out, ui.err)
51
+ end
52
+
53
+ desc "restart [--environment ENVIRONMENT]",
54
+ "Restart the application servers without deploying."
55
+ long_desc <<-DESC
56
+ Restarts the application servers (e.g. Passenger, Unicorn, etc).
57
+
58
+ Respects the maintenance_on_restart settings in the application's ey.yml.
59
+
60
+ Note: Uses the version of the ey.yml currently checked out on the servers.
61
+ DESC
62
+ method_option :environment, type: :string, aliases: %w(-e),
63
+ required: true, default: false,
64
+ desc: "Environment in which to deploy this application"
65
+ method_option :app, type: :string, aliases: %w(-a),
66
+ required: true, default: '',
67
+ desc: "Name of the application to deploy"
68
+ method_option :account, type: :string, aliases: %w(-c),
69
+ required: true, default: '',
70
+ desc: "Name of the account in which the environment can be found"
71
+ method_option :verbose, type: :boolean, aliases: %w(-v),
72
+ desc: "Be verbose"
73
+ def restart
74
+ app_env = fetch_app_environment(options[:app], options[:environment], options[:account])
75
+ ui.info "Restarting servers on #{app_env.hierarchy_name}"
76
+ if serverside_runner(app_env, options[:verbose]).restart.call(ui.out, ui.err)
77
+ ui.info "Restart complete"
78
+ else
79
+ raise EY::Error, "Restart failed"
80
+ end
81
+ end
82
+
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,158 @@
1
+ require 'uri'
2
+ require 'yaml'
3
+ require 'pathname'
4
+ require 'engineyard/error'
5
+
6
+ module EY
7
+ class Config
8
+ # This order is important.
9
+ CONFIG_FILES = ["config/ey.yml", "ey.yml"].map {|path| Pathname.new(path)}.freeze
10
+ TEMPLATE_PATHNAME = Pathname.new(__FILE__).dirname.join('templates','ey.yml').freeze
11
+
12
+ def self.pathname_for_write
13
+ pathname || CONFIG_FILES.find{|pathname| pathname.dirname.exist? }
14
+ end
15
+
16
+ def self.pathname
17
+ CONFIG_FILES.find{|pathname| pathname.exist? }
18
+ end
19
+
20
+ def self.template_pathname
21
+ TEMPLATE_PATHNAME
22
+ end
23
+
24
+ def self.load_config(path = pathname)
25
+ config = YAML.load_file(path.to_s) if path && path.exist?
26
+ config ||= {} # load_file returns `false' when the file is empty
27
+
28
+ unless Hash === config
29
+ raise "ey.yml load error: Expected a Hash but a #{config.class.name} was returned."
30
+ end
31
+ config
32
+ end
33
+
34
+ attr_reader :path
35
+
36
+ def initialize(file = nil)
37
+ @path = file ? Pathname.new(file) : self.class.pathname
38
+ @config = self.class.load_config(@path)
39
+ @config["environments"] ||= {}
40
+ end
41
+
42
+ def method_missing(meth, *args, &blk)
43
+ key = meth.to_s.downcase
44
+ if @config.key?(key)
45
+ @config[key]
46
+ else
47
+ super
48
+ end
49
+ end
50
+
51
+ def respond_to?(meth)
52
+ key = meth.to_s.downcase
53
+ @config.key?(key) || super
54
+ end
55
+
56
+ def fetch(key, default = nil, &block)
57
+ block ? @config.fetch(key.to_s, &block) : @config.fetch(key.to_s, default)
58
+ end
59
+
60
+ def fetch_from_defaults(key, default=nil, &block)
61
+ block ? defaults.fetch(key.to_s, &block) : defaults.fetch(key.to_s, default)
62
+ end
63
+
64
+ def [](key)
65
+ @config[key.to_s.downcase]
66
+ end
67
+
68
+ def endpoint
69
+ env_var_endpoint || default_endpoint
70
+ end
71
+
72
+ def env_var_endpoint
73
+ ENV["CLOUD_URL"]
74
+ end
75
+
76
+ def default_endpoint
77
+ "https://cloud.engineyard.com/"
78
+ end
79
+
80
+ def default_endpoint?
81
+ default_endpoint == endpoint
82
+ end
83
+
84
+ def default_environment
85
+ d = environments.find do |name, env|
86
+ env && env["default"]
87
+ end
88
+ d && d.first
89
+ end
90
+
91
+ def defaults
92
+ @config['defaults'] ||= {}
93
+ end
94
+
95
+ def environment_config(environment_name)
96
+ environments[environment_name] ||= {}
97
+ EnvironmentConfig.new(environments[environment_name], environment_name, self)
98
+ end
99
+
100
+ class EnvironmentConfig
101
+ attr_reader :name
102
+
103
+ def initialize(config, name, parent)
104
+ @config = config || {}
105
+ @name = name
106
+ @parent = parent
107
+ end
108
+
109
+ def path
110
+ @parent.path
111
+ end
112
+
113
+ def ensure_exists
114
+ unless path && path.exist?
115
+ raise EY::Error, "Please initialize this application with the following command:\n\tey init"
116
+ end
117
+ end
118
+
119
+ def fetch(key, default = nil, &block)
120
+ @config.fetch(key.to_s) do
121
+ @parent.fetch_from_defaults(key.to_s, default, &block)
122
+ end
123
+ end
124
+
125
+ def branch
126
+ fetch('branch', nil)
127
+ end
128
+
129
+ def migrate
130
+ ensure_exists
131
+ fetch('migrate') do
132
+ raise EY::Error, "'migrate' not found in #{path}. Reinitialize with:\n\tey init"
133
+ end
134
+ end
135
+
136
+ def migration_command
137
+ ensure_exists
138
+ fetch('migration_command') do
139
+ raise EY::Error, "'migration_command' not found in #{path}. Reinitialize with:\n\tey init"
140
+ end
141
+ end
142
+
143
+ alias migrate_command migration_command
144
+
145
+ def verbose
146
+ fetch('verbose', false)
147
+ end
148
+ end
149
+
150
+ private
151
+
152
+ class ConfigurationError < EY::Error
153
+ def initialize(key, value, source, message=nil)
154
+ super %|"#{key}" from #{source} has invalid value: #{value.inspect}#{": #{message}" if message}|
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,65 @@
1
+ module EY
2
+ class DeployConfig
3
+ MIGRATE = 'rake db:migrate --trace'
4
+
5
+ def initialize(cli_opts, env_config, repo, ui)
6
+ @cli_opts = cli_opts
7
+ @env_config = env_config
8
+ @repo = repo
9
+ @ui = ui
10
+ end
11
+
12
+ def ref
13
+ @ref ||= decide_ref
14
+ end
15
+
16
+ def migrate
17
+ @migrate ||= @cli_opts.fetch('migrate') do
18
+ if in_repo?
19
+ @env_config.migrate
20
+ else
21
+ raise RefAndMigrateRequiredOutsideRepo.new(@cli_opts)
22
+ end
23
+ end
24
+ end
25
+
26
+ def migrate_command
27
+ return @command if defined? @command
28
+
29
+ if migrate
30
+ @command = migrate.respond_to?(:to_str) && migrate.to_str
31
+ @command ||= in_repo? ? @env_config.migration_command : MIGRATE
32
+ else
33
+ @command = nil
34
+ end
35
+
36
+ @command
37
+ end
38
+
39
+ def verbose
40
+ @cli_opts.fetch('verbose') { in_repo? && @env_config.verbose }
41
+ end
42
+
43
+ def extra_config
44
+ @cli_opts.fetch('config', {})
45
+ end
46
+
47
+ private
48
+
49
+ # passing an app means we assume PWD is not the app.
50
+ def in_repo?
51
+ @cli_opts['app'].nil? || @cli_opts['app'] == ''
52
+ end
53
+
54
+ def decide_ref
55
+ ref_decider = EY::DeployConfig::Ref.new(@cli_opts, @env_config, @repo, @ui)
56
+ if in_repo?
57
+ ref_decider.when_inside_repo
58
+ else
59
+ ref_decider.when_outside_repo
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ require 'engineyard/deploy_config/ref'
@@ -0,0 +1,56 @@
1
+ require 'engineyard/error'
2
+
3
+ module EY
4
+ class DeployConfig
5
+ class Ref
6
+
7
+ def initialize(cli_opts, env_config, repo, ui)
8
+ @cli_opts = cli_opts
9
+ @default = env_config.branch
10
+ @repo = repo
11
+ @force_ref = @cli_opts.fetch('force_ref', false)
12
+ @ui = ui
13
+
14
+ if @force_ref.kind_of?(String)
15
+ @ref, @force_ref = @force_ref, true
16
+ else
17
+ @ref = @cli_opts.fetch('ref', nil)
18
+ @ref = nil if @ref == ''
19
+ end
20
+ end
21
+
22
+ def when_inside_repo
23
+ if !@force_ref && @ref && @default && @ref != @default
24
+ raise BranchMismatchError.new(@default, @ref)
25
+ elsif @force_ref && @ref && @default
26
+ @ui.say "Default ref overridden with #{@ref.inspect}."
27
+ end
28
+
29
+ @ref || use_default || use_current_branch || raise(RefRequired.new(@cli_opts))
30
+ end
31
+
32
+ def use_default
33
+ if @default
34
+ @ui.say "Using default branch #{@default.inspect} from ey.yml."
35
+ @default
36
+ end
37
+ end
38
+
39
+ def use_current_branch
40
+ if current = @repo.current_branch
41
+ @ui.say "Using current HEAD branch #{current.inspect}."
42
+ current
43
+ end
44
+ end
45
+
46
+ # a.k.a. not in the correct repo
47
+ #
48
+ # returns the ref if it was passed in the cli opts.
49
+ # or raise
50
+ def when_outside_repo
51
+ @ref or raise RefAndMigrateRequiredOutsideRepo.new(@cli_opts)
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,82 @@
1
+ module EY
2
+ class Error < RuntimeError
3
+ end
4
+
5
+ class NoCommandError < EY::Error
6
+ def initialize
7
+ super "Specify a command to run via ssh or use --each to cycle through each server one at a time."
8
+ end
9
+ end
10
+
11
+ class NoInstancesError < EY::Error
12
+ def initialize(env_name)
13
+ super "The environment '#{env_name}' does not have any matching instances."
14
+ end
15
+ end
16
+
17
+ class ResolverError < Error; end
18
+ class NoMatchesError < ResolverError; end
19
+ class MultipleMatchesError < ResolverError; end
20
+
21
+ class AmbiguousEnvironmentGitUriError < ResolverError
22
+ def initialize(environments)
23
+ message = "The repository url in this directory is ambiguous.\n"
24
+ message << "Please use -e <envname> to specify one of the following environments:\n"
25
+ environments.sort do |a, b|
26
+ if a.account == b.account
27
+ a.name <=> b.name
28
+ else
29
+ a.account.name <=> b.account.name
30
+ end
31
+ end.each { |env| message << "\t#{env.name} (#{env.account.name})\n" }
32
+ super message
33
+ end
34
+ end
35
+
36
+
37
+ class DeployArgumentError < EY::Error; end
38
+ class BranchMismatchError < DeployArgumentError
39
+ def initialize(default, ref)
40
+ super <<-ERR
41
+ Your default branch is set to #{default.inspect} in ey.yml.
42
+ To deploy #{ref.inspect} you can:
43
+ * Delete the line 'branch: #{default}' in ey.yml
44
+ OR
45
+ * Use the -R [REF] or --force-ref [REF] options as follows:
46
+ Usage: ey deploy -R #{ref}
47
+ ey deploy --force-ref #{ref}
48
+ ERR
49
+ end
50
+ end
51
+
52
+ class RefAndMigrateRequiredOutsideRepo < DeployArgumentError
53
+ def initialize(options)
54
+ super <<-ERR
55
+ Because defaults are stored in a file in your application dir, when specifying
56
+ --app you must also specify the --ref and the --migrate or --no-migrate options.
57
+ Usage: ey deploy --app #{options[:app]} --ref [ref] --migrate [COMMAND]
58
+ ey deploy --app #{options[:app]} --ref [branch] --no-migrate
59
+ ERR
60
+ end
61
+ end
62
+
63
+ class RefRequired < DeployArgumentError
64
+ def initialize(options)
65
+ super <<-ERR
66
+ Unable to determine the branch or ref to deploy
67
+ Usage: ey deploy --ref [ref]
68
+ ERR
69
+ end
70
+ end
71
+
72
+ class MigrateRequired < DeployArgumentError
73
+ def initialize(options)
74
+ super <<-ERR
75
+ Unable to determine migration choice. ey deploy no longer migrates by default.
76
+ Usage: ey deploy --migrate
77
+ ey deploy --no-migrate
78
+ ERR
79
+ end
80
+ end
81
+
82
+ end