crazy-yard 3.2.2

Sign up to get free protection for your applications and to get access to all the features.
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