ey-flex-test 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Engine Yard Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,3 @@
1
+ == ey
2
+
3
+ A gem that provides...
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'rubygems/specification'
4
+ require 'date'
5
+
6
+ GEM = "ey-flex-test"
7
+ GEM_VERSION = "0.3.3"
8
+ AUTHOR = "Ezra Zygmuntowicz"
9
+ EMAIL = "awsmdev@engineyard.com"
10
+ HOMEPAGE = "http://engineyard.com/solo"
11
+ SUMMARY = "Command line interface to Engine Yard's cloud"
12
+
13
+ spec = Gem::Specification.new do |s|
14
+ s.name = GEM
15
+ s.version = GEM_VERSION
16
+ s.platform = Gem::Platform::RUBY
17
+ s.has_rdoc = true
18
+ s.extra_rdoc_files = ["README.rdoc", "LICENSE", 'TODO']
19
+ s.summary = SUMMARY
20
+ s.description = s.summary
21
+ s.author = AUTHOR
22
+ s.email = EMAIL
23
+ s.homepage = HOMEPAGE
24
+ s.bindir = "bin"
25
+ s.executables = %w( ey-slave ey-recipes eybackup ey-snapshots ey-monitor ey-agent)
26
+ # Uncomment this to add a dependency
27
+ # s.add_dependency "foo"
28
+
29
+ s.require_path = 'lib'
30
+ s.autorequire = GEM
31
+ s.files = %w(LICENSE README.rdoc Rakefile TODO) + Dir.glob("{lib,spec}/**/*")
32
+ end
33
+
34
+ Rake::GemPackageTask.new(spec) do |pkg|
35
+ pkg.gem_spec = spec
36
+ end
37
+
38
+ desc "install the gem locally"
39
+ task :install => [:package] do
40
+ sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
41
+ end
42
+
43
+ desc "create a gemspec file"
44
+ task :make_spec do
45
+ File.open("#{GEM}.gemspec", "w") do |file|
46
+ file.puts spec.to_ruby
47
+ end
48
+ end
data/TODO ADDED
@@ -0,0 +1,4 @@
1
+ TODO:
2
+ Fix LICENSE with your name
3
+ Fix Rakefile with your name and contact info
4
+ Add your code to lib/<%= name %>.rb
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'json'
3
+ require 'big-brother'
4
+
5
+ json = JSON.parse(IO.read('/etc/chef/dna.json'))
6
+
7
+
8
+ # {'skip':[
9
+ # 'mysqld'
10
+ # ],
11
+ # 'check':[
12
+ # 'ttsrv'
13
+ # ]}
14
+
15
+ skips = JSON.parse(IO.read('/etc/ey-alerts.json')) rescue {}
16
+
17
+ puts EY::BigBrother.new(json.merge(skips)).check
@@ -0,0 +1,10 @@
1
+
2
+ require 'rubygems'
3
+ require 'yaml'
4
+ require 'stonith'
5
+
6
+ EventMachine.run {
7
+ opts = YAML::load(IO.read("/etc/.mysql.backups.yml"))
8
+ opts.merge!(YAML::load(IO.read("/etc/.ey-cloud.yml")))
9
+ EY::Stonith.new(opts.merge(:heartbeat => 10))
10
+ }
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'yaml'
4
+ require "optparse"
5
+ require "json"
6
+ require 'ey'
7
+
8
+ defaults = {:config => '~/.ey-cloud.yml',
9
+ :command => :get_envs,
10
+ :type => 'recipes',
11
+ :keep => 5}
12
+ options = {}
13
+ # Build a parser for the command line arguments
14
+ opts = OptionParser.new do |opts|
15
+ opts.version = "0.0.1"
16
+
17
+ opts.banner = "Usage: ey-recipes [-flag] [argument]"
18
+ opts.define_head "ey-recipes: managing your recipes..."
19
+ opts.separator '*'*80
20
+
21
+ opts.on("-l", "--list-recipes ENV", "List recipes for ENV") do |env|
22
+ options[:env] = env
23
+ options[:command] = :list
24
+ end
25
+
26
+ opts.on("-j", "--json ENV", "Get The DNA JSON for ENV") do |env|
27
+ options[:env] = env
28
+ options[:command] = :get_json
29
+ end
30
+
31
+ opts.on("-q", "--quick", "Run only the recipes required for a quick redeploy") do
32
+ options[:quick] = true
33
+ end
34
+
35
+ opts.on("-c", "--config CONFIG", "Use config file.") do |config|
36
+ options[:config] = config
37
+ end
38
+
39
+ opts.on("-r", "--rollback ENV", "Roll back to the previous recipe set for ENV and run them on your instances.") do |env|
40
+ options[:env] = env
41
+ options[:command] = :rollback
42
+ end
43
+
44
+ opts.on("-d", "--deploy ENV", "Upload a new recipe set and run them on your instances. Be sure to commit your changes to your git repo before deploying as we create a git archive of HEAD.") do |env|
45
+ options[:command] = :deploy
46
+ options[:identifier] = 'recipes'
47
+ options[:env] = env
48
+ end
49
+
50
+ opts.on("-u", "--upload ENV", "Upload a new recipe set so s3 but don't run it. Be sure to commit your changes to your git repo before deploying as we create a git archive of HEAD.") do |env|
51
+ options[:command] = :upload
52
+ options[:identifier] = 'recipes'
53
+ options[:env] = env
54
+ end
55
+
56
+ opts.on("--clear ENV", "Clear out any customer recipes attached to ENV") do |env|
57
+ options[:command] = :clear
58
+ options[:identifier] = 'recipes'
59
+ options[:env] = env
60
+ end
61
+
62
+ opts.on("--deploy-main ENV", "Redeploy the main EY recipe set and run it on your environment ENV") do |env|
63
+ options[:command] = :deploy_main
64
+ options[:env] = env
65
+ end
66
+
67
+ opts.on("-n", "--newest ENV", "download, install and run the current custom recipe set for ENV (THIS COMMAND CAN ONLY BE RUN ON YOUR EC2 INSTANCE)") do |env|
68
+ options[:command] = :converge
69
+ options[:identifier] = 'recipes'
70
+ options[:env] = env
71
+ end
72
+
73
+ opts.on("--view-log ENV", "view the last custom chef recipe run log file for ENV") do |env|
74
+ options[:command] = :view_logs
75
+ options[:env] = env
76
+ end
77
+
78
+ opts.on("--view-main-log ENV", "view the last main ey recipe run log file for ENV") do |env|
79
+ options[:command] = :view_logs
80
+ options[:env] = env
81
+ options[:main] = true
82
+ end
83
+
84
+ opts.on("--main ENV", "download, install and run the main ey recipe set for ENV (THIS COMMAND CAN ONLY BE RUN ON YOUR EC2 INSTANCE)") do |env|
85
+ options[:command] = :converge
86
+ options[:env] = env
87
+ options[:main] = true
88
+ options[:recipeloc] = "/etc/chef/recipes"
89
+ end
90
+
91
+ opts.on("--logs", "set the type to logs") do
92
+ options[:type] = 'logs'
93
+ options[:extension] = 'gz'
94
+ end
95
+
96
+ end
97
+
98
+ opts.parse!
99
+
100
+ ey = nil
101
+ if File.exist?(config = File.expand_path(options[:config] || defaults[:config]))
102
+ ey = EY::ChefRecipes.new(options = defaults.merge(YAML::load(IO.read(config))).merge(options))
103
+ elsif File.exist?(config = "/etc/.ey-cloud.yml")
104
+ ey = EY::ChefRecipes.new(options = defaults.merge(YAML::load(IO.read(config))).merge(options))
105
+ else
106
+ puts"You need to have an ~/.ey-cloud.yml file with your credentials in it to use this tool.\nOr point it at a yaml file with -c path/to/ey-cloud.yml"
107
+ exit 1
108
+ end
109
+
110
+ case options[:command]
111
+ when :list
112
+ ey.list true
113
+ when :rollback
114
+ ey.rollback
115
+ when :deploy
116
+ ey.deploy
117
+ when :get_json
118
+ jj ey.get_json
119
+ when :deploy_main
120
+ ey.deploy_main
121
+ when :view_logs
122
+ ey.view_logs
123
+ when :clear
124
+ ey.clear_bucket
125
+ when :upload
126
+ ey.upload_recipes
127
+ when :get_envs
128
+ envs = ey.get_envs
129
+ puts "Current Environments:"
130
+ envs.each {|k,v| puts "env: #{k} running instances: #{v['instances']}\n instance_ids: #{v['instance_ids'].inspect}" }
131
+ when :converge
132
+ ey.converge
133
+ end
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'json'
3
+ require 'mysql_slave'
4
+
5
+ puts "Adding this node as a mysql slave/replicant read only database"
6
+
7
+ EY::MysqlSlave.new.bootstrap
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'yaml'
4
+ require "optparse"
5
+ require "json"
6
+ require 'snapshot_minder'
7
+
8
+ defaults = {:config => '/etc/.mysql.backups.yml',
9
+ :command => :list_snapshots,
10
+ :keep => 5}
11
+
12
+ options = {}
13
+ # Build a parser for the command line arguments
14
+ opts = OptionParser.new do |opts|
15
+ opts.version = "0.0.1"
16
+
17
+ opts.banner = "Usage: ey-snapshots [-flag] [argument]"
18
+ opts.define_head "ey-snapshots: managing your snapshots..."
19
+ opts.separator '*'*80
20
+
21
+ opts.on("-l", "--list-snapshots", "list snapshots") do
22
+ options[:command] = :list_snapshots
23
+ end
24
+
25
+ opts.on("-c", "--config CONFIG", "Use config file.") do |config|
26
+ options[:config] = config
27
+ end
28
+
29
+ opts.on("-i", "--instance-id ID", "specify the instance id to work with(only needed if you are running this from ourside of ec2)") do |iid|
30
+ options[:instance_id] = iid
31
+ end
32
+
33
+
34
+ opts.on("--snapshot", "take snapshots of both of your volumes(only runs on your ec2 instance)") do
35
+ options[:command] = :snapshot_volumes
36
+ end
37
+
38
+
39
+ end
40
+
41
+ opts.parse!
42
+
43
+ ey = nil
44
+ if File.exist?(config = File.expand_path(defaults[:config]))
45
+ ey = EY::SnapshotMinder.new(options = defaults.merge(YAML::load(IO.read(config))).merge(options))
46
+ else
47
+ puts"You need to have an /etc/.mysql.backups.yml file with your credentials in it to use this tool.\nOr point it at a yaml file with -c .mysql.backups.yml"
48
+ exit 1
49
+ end
50
+
51
+ ey.send(options[:command])
52
+ ey.clean_snapshots(options[:keep])
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'yaml'
4
+ require "optparse"
5
+ require 'mysql_backup'
6
+ require 'postgresql_backup'
7
+
8
+ options = {:config => '/etc/.mysql.backups.yml',
9
+ :command => :new_backup}
10
+
11
+ # Build a parser for the command line arguments
12
+ opts = OptionParser.new do |opts|
13
+ opts.version = "0.0.1"
14
+
15
+ opts.banner = "Usage: eybackup [-flag] [argument]"
16
+ opts.define_head "eybackup: backing up your shit since way back when..."
17
+ opts.separator '*'*80
18
+
19
+ opts.on("-l", "--list-backup DATABASE", "List mysql backups for DATABASE") do |db|
20
+ options[:db] = (db || 'all')
21
+ options[:command] = :list
22
+ end
23
+
24
+ opts.on("-n", "--new-backup", "Create new mysql backup") do
25
+ options[:command] = :new_backup
26
+ end
27
+
28
+ opts.on("-c", "--config CONFIG", "Use config file.") do |config|
29
+ options[:config] = config
30
+ end
31
+
32
+ opts.on("-d", "--download BACKUP_INDEX", "download the backup specified by index. Run eybackup -l to get the index.") do |index|
33
+ options[:command] = :download
34
+ options[:index] = index
35
+ end
36
+
37
+ opts.on("-e", "--engine DATABASE_ENGINE", "The database engine. ex: mysql, postgres.") do |engine|
38
+ options[:engine] = engine || 'mysql'
39
+ options[:config] ||= '/etc/.#{options[:engine]}.backups.yml'
40
+ end
41
+
42
+ opts.on("-r", "--restore BACKUP_INDEX", "Download and apply the backup specified by index WARNING! will overwrite the current db with the backup. Run eybackup -l to get the index.") do |index|
43
+ options[:command] = :restore
44
+ options[:index] = index
45
+ end
46
+
47
+ end
48
+
49
+ opts.parse!
50
+
51
+ eyb = nil
52
+ if File.exist?(options[:config])
53
+ eyb = case options[:engine]
54
+ when 'postgres' then EyBackup::PostgresqlBackup.new(YAML::load(IO.read(options[:config])))
55
+ when 'mysql', NilClass then EyBackup::MysqlBackup.new(YAML::load(IO.read(options[:config])))
56
+ else raise "Invalid engine: #{options[:engine]}"
57
+ end
58
+ end
59
+
60
+ case options[:command]
61
+ when :list
62
+ eyb.list options[:db], true
63
+ when :new_backup
64
+ eyb.new_backup
65
+ when :download
66
+ eyb.download(options[:index])
67
+ when :restore
68
+ eyb.restore(options[:index])
69
+ end
70
+ eyb.cleanup
@@ -0,0 +1,66 @@
1
+ require 'dbi'
2
+ module EY
3
+ class BigBrother
4
+ def initialize(dna)
5
+ @dna = dna
6
+ @result = {}
7
+ end
8
+
9
+ def nginx_or_apache
10
+ server = ''
11
+ @dna['applications'].each do |name, app_data|
12
+ if app_data['recipes'].detect { |r| r == 'nginx' }
13
+ server = 'nginx'
14
+ end
15
+
16
+ if app_data['recipes'].detect { |r| r == 'passenger' }
17
+ server = 'apache2'
18
+ end
19
+ end
20
+ server
21
+ end
22
+
23
+ def skip?(name)
24
+ (@dna['skip']||[]).include?(name)
25
+ end
26
+
27
+ def check
28
+ case @dna['instance_role']
29
+ when 'solo'
30
+ check_process(nginx_or_apache) unless skip?(nginx_or_apache)
31
+ check_mysql unless skip?('mysqld')
32
+ when 'app', 'app_master'
33
+ check_process(nginx_or_apache) unless skip?(nginx_or_apache)
34
+ check_process('haproxy') unless skip?('haproxy')
35
+ when 'db_master', 'db_slave'
36
+ check_mysql unless skip?('mysqld')
37
+ when 'util'
38
+ end
39
+ (@dna['check']||[]).each do |check|
40
+ check_process(check)
41
+ end
42
+ @result.to_json
43
+ end
44
+
45
+ def check_mysql
46
+ check_process('mysqld')
47
+ DBI.connect("DBI:Mysql:mysql:#{@dna['db_host']}", 'root', @dna['users'].first['password'])
48
+ rescue DBI::DatabaseError => e
49
+ @result['mysqld'] = 'down'
50
+ end
51
+
52
+ def check_process(name)
53
+ return if name == ''
54
+ pids = `pgrep #{name}`.split("\n")
55
+ if pids.empty?
56
+ @result[name] = 'down'
57
+ else
58
+ if pids.detect {|p| `kill -0 #{p}; echo $?`.chomp.to_i != 0}
59
+ @result[name] = 'down'
60
+ else
61
+ @result[name] = 'up'
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,110 @@
1
+ module AWS::S3
2
+ class S3Object
3
+ def <=>(other)
4
+ DateTime.parse(self.about['last-modified']) <=> DateTime.parse(other.about['last-modified'])
5
+ end
6
+ end
7
+ end
8
+
9
+ require 'open-uri'
10
+
11
+ module EY
12
+
13
+ class BucketMinder
14
+
15
+ def initialize(opts={})
16
+ AWS::S3::Base.establish_connection!(
17
+ :access_key_id => opts[:aws_secret_id],
18
+ :secret_access_key => opts[:aws_secret_key]
19
+ )
20
+ @instance_id = opts[:instance_id]
21
+ @type = opts[:type]
22
+ @env = opts[:env]
23
+ @opts = opts
24
+ opts[:extension] ||= "tgz"
25
+ @keep = opts[:keep]
26
+ @name = "#{Time.now.strftime("%Y-%m-%dT%H:%M:%S").gsub(/:/, '-')}.#{@type}.#{opts[:extension]}"
27
+ end
28
+
29
+ def bucket
30
+ @bucket ||= begin
31
+ buck = "#{@env}-#{@type}-#{instance_id}-#{Digest::SHA1.hexdigest(@opts[:aws_secret_id])[0..6]}"
32
+ begin
33
+ AWS::S3::Bucket.create buck
34
+ rescue AWS::S3::ResponseError
35
+ end
36
+ buck
37
+ end
38
+ end
39
+
40
+ def instance_id
41
+ @instance_id ||= open("http://169.254.169.254/latest/meta-data/instance-id").read
42
+ end
43
+
44
+ def upload_object(file)
45
+ AWS::S3::S3Object.store(
46
+ @name,
47
+ open(file),
48
+ bucket,
49
+ :access => :private
50
+ )
51
+ FileUtils.rm file
52
+ puts "successful upload: #{@name}"
53
+ true
54
+ end
55
+
56
+ def download(index, printer = false)
57
+ obj = list[index.to_i]
58
+ puts "downloading: #{obj}" if printer
59
+ File.open(obj.key, 'wb') do |f|
60
+ print "." if printer
61
+ obj.value {|chunk| f.write chunk }
62
+ end
63
+ puts if printer
64
+ puts "finished" if printer
65
+ obj.key
66
+ end
67
+
68
+ def cleanup
69
+ list[0...-(@keep)].each{|o|
70
+ puts "deleting: #{o.key}"
71
+ o.delete
72
+ }
73
+ end
74
+
75
+ def get_current
76
+ name = download(list.size - 1)
77
+ File.expand_path(name)
78
+ end
79
+
80
+ def clear_bucket
81
+ list.each do |o|
82
+ puts "deleting: #{o.key}"
83
+ o.delete
84
+ end
85
+ end
86
+
87
+ def rollback
88
+ o = list.last
89
+ puts "rolling back: #{o.key}"
90
+ o.delete
91
+ end
92
+
93
+ def empty?
94
+ list.empty?
95
+ end
96
+
97
+ def list(printer = false)
98
+ objects = AWS::S3::Bucket.objects(bucket).sort
99
+ puts "listing bucket #{bucket}" if printer && !objects.empty?
100
+ if printer
101
+ objects.each_with_index do |b,i|
102
+ puts "#{i}:#{@env} #{b.key}"
103
+ end
104
+ end
105
+ objects
106
+ end
107
+
108
+ end
109
+
110
+ end