ey_info 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,72 @@
1
+ Ey Info
2
+ =======
3
+
4
+ Summary
5
+ -------
6
+ Quickly set up your ~/.ssh/config shortcuts for your ey cloud servers.
7
+
8
+ Install
9
+ -------
10
+
11
+ <pre>
12
+ gem install --no-ri --no-rdoc ey_info # sudo if you need to
13
+ </pre>
14
+
15
+ Usage Command Line
16
+ -------
17
+
18
+ <pre>
19
+ # will set up your ~/ssh/config shortcuts.
20
+ $ ey_info -i "id_rsa" -u "root" # same as defaults
21
+ $ ey_info # same as above
22
+ </pre>
23
+
24
+ If you're environment in the ey cloud dashboard is called 'production' with an app_master, and 2 app instances.
25
+
26
+ <pre>
27
+ $ ssh production_app_master
28
+ vs
29
+ $ ssh -i ~/.ssh/id-rsa root@ec2-xxx-xxx-xxx-xxx.compute-1.amazonaws.com
30
+
31
+ $ ssh production_app1
32
+ $ ssh production_app2
33
+ $ ssh production_db_master
34
+ $ ssh production_db_slave1
35
+ </pre>
36
+
37
+ Usage With Capistrano
38
+ -------
39
+
40
+ I still find capistrano a very useful utility for debugging and still use wanted to use it with EY's cloud.
41
+
42
+ <pre>
43
+ $ cap invoke COMMAND="..." is extremely useful. The engineyard gem provides a
44
+ "ey ssh 'uptime' --all -e production" command but it loops through the servers one by one instead of running
45
+ the commands in parallel, which can be slow if you have lots of servers.
46
+ </pre>
47
+
48
+ You can use this gem to dynamically grab the ec2 hosts information from your ey environment and set up your
49
+ capistrano roles.
50
+
51
+ Here are a few examples of how you can use it in your config/deploy.rb:
52
+
53
+ <pre>
54
+ require 'ey_info'
55
+ task :production do
56
+ @info = EyInfo::Hosts.new
57
+ hosts = @info.hosts("production") # parameter is the environment name in EY's gui interface
58
+
59
+ role :db, hosts.find {|x| x[:role] == "app_master" }[:ssh_key], :primary => true
60
+ # app instances
61
+ hosts.select {|x| x[:role] =~ /app/ }.each do |h|
62
+ role :web, h[:ssh_key]
63
+ role :app, h[:ssh_key], :sphinx => true
64
+ end
65
+ # utility instances
66
+ hosts.select {|x| x[:role] =~ /util/ }.each do |h|
67
+ role :web, h[:ssh_key]
68
+ role :app, h[:ssh_key], :sphinx => true
69
+ end
70
+ end
71
+ </pre>
72
+
@@ -0,0 +1,30 @@
1
+ require File.expand_path('../lib/ey_info', __FILE__)
2
+ begin
3
+ require 'jeweler'
4
+ Jeweler::Tasks.new do |gem|
5
+ gem.version = EyInfo::Version
6
+ gem.name = "ey_info"
7
+ gem.executables = %W(ey_info)
8
+ gem.summary = %Q{Ey Info - Easy way to setup ssh keys for ey cloud servers}
9
+ gem.description = %Q{Ey Info - Easy way to setup ssh keys for ey cloud servers and also to dynamically pull server information from your ey servers and just it within capistrano.}
10
+ gem.homepage = "http://github.com/tongueroo/ey_info"
11
+ gem.email = [ "tongueroo@gmail.com" ]
12
+ gem.authors = [ "Tung Nguyen" ]
13
+ gem.add_dependency "engineyard", ">=1.3.11"
14
+ # gem.add_dependency "net-sftp", ">=2.0.0"
15
+ # gem.add_development_dependency "mocha", ">= 0"
16
+ end
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
19
+ end
20
+
21
+ require 'rake/testtask'
22
+ Rake::TestTask.new(:test) do |test|
23
+ test.libs << 'lib' << 'test'
24
+ test.pattern = 'test/**/*_test.rb'
25
+ test.verbose = true
26
+ end
27
+
28
+ task :test => :check_dependencies
29
+ task :default => :test
30
+
data/TODO ADDED
@@ -0,0 +1,23 @@
1
+ CloudInfo::Instances outline
2
+ 1. connect to ec2
3
+
4
+ 2. find instances that are in the ey-env
5
+ find ey instances from ey-recipes for specific environment
6
+ grab aws_group from one of the instances in the ey-env
7
+ instances in group
8
+ I can simply also just grab the instances from ey-recipes instead of working back from the aws_group..
9
+
10
+ 3. connect to instances (ssh) and build info
11
+ connect to servers
12
+ download json and store in dictionary lookup
13
+ at this point we have enough data about the servers to build up the info we want
14
+
15
+ 4. hosts
16
+ {:prod_app0 => 'xx.xx.xx.xx', :prod_app1 => 'xx.xx.xx.xx'}
17
+
18
+ ============
19
+ good stopping point:
20
+ * figure out a way to test exception from ssh.connect
21
+ * bad password
22
+ * timeout
23
+
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path('../../lib/ey_info', __FILE__)
4
+
5
+ EyInfo::CLI.run(ARGV)
@@ -0,0 +1,54 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{ey_info}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Tung Nguyen"]
12
+ s.date = %q{2011-01-03}
13
+ s.default_executable = %q{ey_info}
14
+ s.description = %q{Ey Info - Easy way to setup ssh keys for ey cloud servers and also to dynamically pull server information from your ey servers and just it within capistrano.}
15
+ s.email = ["tongueroo@gmail.com"]
16
+ s.executables = ["ey_info"]
17
+ s.extra_rdoc_files = [
18
+ "README.markdown",
19
+ "TODO"
20
+ ]
21
+ s.files = [
22
+ "README.markdown",
23
+ "Rakefile",
24
+ "TODO",
25
+ "bin/ey_info",
26
+ "lib/ey_info.rb",
27
+ "lib/templates/default_ssh_config.erb",
28
+ "lib/text_injector.rb",
29
+ "test/ey_info_test.rb",
30
+ "test/test_helper.rb"
31
+ ]
32
+ s.homepage = %q{http://github.com/tongueroo/ey_info}
33
+ s.require_paths = ["lib"]
34
+ s.rubygems_version = %q{1.3.7}
35
+ s.summary = %q{Ey Info - Easy way to setup ssh keys for ey cloud servers}
36
+ s.test_files = [
37
+ "test/ey_info_test.rb",
38
+ "test/test_helper.rb"
39
+ ]
40
+
41
+ if s.respond_to? :specification_version then
42
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
43
+ s.specification_version = 3
44
+
45
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
46
+ s.add_runtime_dependency(%q<engineyard>, [">= 1.3.11"])
47
+ else
48
+ s.add_dependency(%q<engineyard>, [">= 1.3.11"])
49
+ end
50
+ else
51
+ s.add_dependency(%q<engineyard>, [">= 1.3.11"])
52
+ end
53
+ end
54
+
@@ -0,0 +1,190 @@
1
+ require 'yaml'
2
+ require 'rubygems'
3
+ require 'engineyard'
4
+ require 'optparse'
5
+ require 'erb'
6
+ require 'pp'
7
+ require File.expand_path("../text_injector", __FILE__)
8
+
9
+ module EyInfo
10
+ Version = "0.1.0"
11
+
12
+ class CLI
13
+ def self.run(args)
14
+ cli = new(args)
15
+ cli.parse_options!
16
+ cli.run
17
+ end
18
+
19
+ # The array of (unparsed) command-line options
20
+ attr_reader :args
21
+ # The hash of (parsed) command-line options
22
+ attr_reader :options
23
+
24
+ def initialize(args)
25
+ @args = args.dup
26
+ end
27
+
28
+ def option_parser
29
+ # @logger = Logger.new
30
+ @option_parser ||= OptionParser.new do |opts|
31
+ opts.banner = "Usage: #{File.basename($0)} ssh_config"
32
+
33
+ opts.on("-i", "--identity-file [IDENTITY_FILE]", "ssh key default id_rsa.") do |value|
34
+ options[:ssh_key] = value
35
+ end
36
+
37
+ opts.on("-u", "--user [USER]", "ssh key default root.") do |value|
38
+ options[:user] = value
39
+ end
40
+
41
+ opts.on("-h", "--help", "Display this help message.") do
42
+ puts opts
43
+ exit
44
+ end
45
+
46
+ opts.on("-V", "--version",
47
+ "Display the ey_info version, and exit."
48
+ ) do
49
+ puts "EyInfo Version #{EyInfo::Version}"
50
+ exit
51
+ end
52
+
53
+ end
54
+ end
55
+
56
+ def parse_options!
57
+ # defaults
58
+ @options = {:actions => [], :user => 'root', :ssh_key => 'id_rsa'}
59
+
60
+ if args.empty?
61
+ warn "Please specifiy an action to execute."
62
+ warn option_parser
63
+ exit 1
64
+ end
65
+
66
+ option_parser.parse!(args)
67
+ extract_environment_variables!
68
+
69
+ options[:actions].concat(args)
70
+ end
71
+
72
+ # Extracts name=value pairs from the remaining command-line arguments
73
+ # and assigns them as environment variables.
74
+ def extract_environment_variables! #:nodoc:
75
+ args.delete_if do |arg|
76
+ next unless arg.match(/^(\w+)=(.*)$/)
77
+ ENV[$1] = $2
78
+ end
79
+ end
80
+
81
+ def run
82
+ write_ssh_config
83
+ puts "Your #{ENV['HOME']}/.ssh/config now has shortcuts your ey cloud servers."
84
+ end
85
+
86
+ def write_ssh_config
87
+ @ssh_config = "#{ENV['HOME']}/.ssh/config"
88
+ @info = EyInfo::Hosts.new
89
+
90
+ # clear out old known_hosts
91
+ `grep -v 'compute-1.amazonaws.com' ~/.ssh/known_hosts > ~/.ssh/known_hosts.tmp`
92
+ `mv ~/.ssh/known_hosts.tmp ~/.ssh/known_hosts`
93
+
94
+ # for erb template
95
+ @identify_file = "~/.ssh/#{options[:ssh_key]}"
96
+ @user = options[:user]
97
+
98
+ if File.exist?(@ssh_config)
99
+ injector = TextInjector.new(
100
+ :file => @ssh_config,
101
+ :update => true,
102
+ :content => ssh_servers
103
+ )
104
+ else
105
+ injector = TextInjector.new(
106
+ :file => @ssh_config,
107
+ :write => true,
108
+ :content => ssh_servers
109
+ )
110
+ end
111
+ injector.run
112
+ end
113
+
114
+ def ssh_servers
115
+ @hosts = {}
116
+ @info = EyInfo::Hosts.new
117
+ all_hosts = @info.all_hosts
118
+ all_hosts.keys.each do |env_name|
119
+ all_hosts[env_name].each do |server|
120
+ @hosts["#{server[:ssh_key]}"] = server[:hostname]
121
+ end
122
+ end
123
+ # content = IO.readlines(RAILS_ROOT+"/dev/ssh_config.erb").join("")
124
+ content = IO.readlines(File.expand_path("../templates/default_ssh_config.erb", __FILE__)).join("")
125
+ template = ERB.new(content)
126
+ template.result(binding)
127
+ end
128
+
129
+ end
130
+
131
+ class Hosts
132
+ def initialize(options = {})
133
+ @api_token = YAML.load_file(File.expand_path("~/.eyrc"))["api_token"]
134
+ @api = EY::API.new(@api_token)
135
+ @envs = @api.apps.map{ |a| a.environments }
136
+ end
137
+
138
+ def all_hosts
139
+ hosts = {}
140
+ @envs.flatten.map{|x| x.name}.sort.uniq.each do |env_name, index|
141
+ env_name = env_name.to_sym
142
+ arr = []
143
+ app_count = 0
144
+ db_count = 0
145
+
146
+ @api.environments.match_one!(env_name.to_s).instances.sort_by {|x| x.hostname}.each do |i, idx|
147
+ # if i.role == 'app_master'
148
+ # key = "app0"
149
+ # elsif i.role == 'app'
150
+ # app_count += 1
151
+ # key = "app#{app_count}"
152
+ # elsif i.role == 'db_master'
153
+ # key = "db0"
154
+ # elsif i.role == 'db_slave'
155
+ # db_count += 1
156
+ # key = "db#{db_count}"
157
+ # elsif i.role == 'util'
158
+ # key = "#{i.name}"
159
+ # elsif i.role == 'solo'
160
+ # key = "solo"
161
+ # end
162
+
163
+ key = i.role == 'util' ? i.name : i.role
164
+ if key == 'app'
165
+ app_count += 1
166
+ key = "app#{app_count}"
167
+ elsif key == 'db_slave'
168
+ db_count += 1
169
+ key = "db_slave#{db_count}"
170
+ end
171
+
172
+ arr << {
173
+ :ssh_key => "#{env_name}_#{key}",
174
+ :hostname => i.hostname,
175
+ :role => i.role,
176
+ :name => i.name
177
+ }
178
+ end
179
+
180
+ hosts[env_name] = arr
181
+ end
182
+ hosts
183
+ end
184
+
185
+ def hosts(env_name)
186
+ all_hosts[env_name.to_sym]
187
+ end
188
+
189
+ end
190
+ end
@@ -0,0 +1,12 @@
1
+ ##########################################
2
+ # ey cloud environments
3
+ StrictHostKeyChecking no
4
+
5
+ <% @hosts.keys.sort.each do |name| %>
6
+ Host <%= name %>
7
+ Hostname <%= @hosts[name] %>
8
+ Port 22
9
+ User <%= @user %>
10
+ IdentityFile <%= @identify_file %>
11
+
12
+ <% end %>
@@ -0,0 +1,92 @@
1
+ class TextInjector
2
+ def initialize(options={})
3
+ @options = options
4
+
5
+ @options[:file] ||= raise "required :file option missing"
6
+ @identifier ||= (@options[:identifier] || default_identifier)
7
+ @content ||= @options[:content]
8
+
9
+ if @options[:update] && !File.exists?(@options[:file])
10
+ warn("[fail] Can't find file: #{@options[:file]}")
11
+ exit(1)
12
+ end
13
+
14
+ if @options[:update] && @options[:write]
15
+ warn("[fail] Can't update AND write. choose one.")
16
+ exit(1)
17
+ end
18
+ end
19
+
20
+ # both setter and getter for the dsl
21
+ def content(text = nil)
22
+ text.nil? ? @content : @content = text
23
+ end
24
+ def identifier(id = nil)
25
+ id.nil? ? @identifier : @identifier = id
26
+ end
27
+
28
+ def run
29
+ if @options[:update]
30
+ write_file(updated_file)
31
+ elsif @options[:write]
32
+ write_file(marked_content)
33
+ else
34
+ puts marked_content
35
+ exit
36
+ end
37
+ end
38
+
39
+ protected
40
+
41
+ def default_identifier
42
+ File.expand_path(@options[:file])
43
+ end
44
+
45
+ def marked_content
46
+ @marked_content ||= [comment_open, @content, comment_close].join("\n")
47
+ end
48
+
49
+ def read_file
50
+ return @current_file if @current_file
51
+ results = ''
52
+ File.open(@options[:file], 'r') {|f| results = f.readlines.join("") }
53
+ @current_file = results
54
+ end
55
+
56
+ def write_file(contents)
57
+ File.open(@options[:file], 'w') do |file|
58
+ file.write(contents)
59
+ end
60
+ end
61
+
62
+ def updated_file
63
+ # Check for unopened or unclosed identifier blocks
64
+ if read_file.index(comment_open) && !read_file.index(comment_close)
65
+ warn "[fail] Unclosed indentifier; Your file contains '#{comment_open}', but no '#{comment_close}'"
66
+ exit(1)
67
+ elsif !read_file.index(comment_open) && read_file.index(comment_close)
68
+ warn "[fail] Unopened indentifier; Your file contains '#{comment_close}', but no '#{comment_open}'"
69
+ exit(1)
70
+ end
71
+
72
+ # If an existing identifier block is found, replace it with the content text
73
+ if read_file.index(comment_open) && read_file.index(comment_close)
74
+ read_file.gsub(Regexp.new("#{comment_open}.+#{comment_close}", Regexp::MULTILINE), marked_content)
75
+ else # Otherwise, append the new content after
76
+ [read_file, marked_content].join("\n\n")
77
+ end
78
+ end
79
+
80
+ def comment_base
81
+ "TextInjector marker for: #{@identifier}"
82
+ end
83
+
84
+ def comment_open
85
+ "# Begin #{comment_base}"
86
+ end
87
+
88
+ def comment_close
89
+ "# End #{comment_base}"
90
+ end
91
+
92
+ end
@@ -0,0 +1,19 @@
1
+ require File.expand_path("../test_helper", __FILE__)
2
+
3
+ class EyInfoTest < Test::Unit::TestCase
4
+ def setup
5
+ @info = EyInfo::Hosts.new
6
+ end
7
+
8
+ def test_all_hosts_hash
9
+ # pp @info.all_hosts
10
+ assert_equal @info.all_hosts.class, Hash
11
+ assert @info.all_hosts.keys.include?(:beta)
12
+ end
13
+
14
+ def test_hosts_hash
15
+ pp @info.hosts(:alpha)
16
+ assert @info.hosts(:alpha).class, Array
17
+ end
18
+
19
+ end
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ ENV['RAILS_ENV'] = 'test'
4
+
5
+ require 'rubygems'
6
+ require 'test/unit'
7
+ require 'mocha'
8
+ require 'pp'
9
+ require File.expand_path("../../lib/ey_info", __FILE__)
10
+
11
+ module TestExtensions
12
+ end
13
+
14
+ class Test::Unit::TestCase
15
+ include TestExtensions
16
+ end
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ey_info
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Tung Nguyen
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-01-03 00:00:00 -08:00
19
+ default_executable: ey_info
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: engineyard
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 13
30
+ segments:
31
+ - 1
32
+ - 3
33
+ - 11
34
+ version: 1.3.11
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ description: Ey Info - Easy way to setup ssh keys for ey cloud servers and also to dynamically pull server information from your ey servers and just it within capistrano.
38
+ email:
39
+ - tongueroo@gmail.com
40
+ executables:
41
+ - ey_info
42
+ extensions: []
43
+
44
+ extra_rdoc_files:
45
+ - README.markdown
46
+ - TODO
47
+ files:
48
+ - README.markdown
49
+ - Rakefile
50
+ - TODO
51
+ - bin/ey_info
52
+ - ey_info.gemspec
53
+ - lib/ey_info.rb
54
+ - lib/templates/default_ssh_config.erb
55
+ - lib/text_injector.rb
56
+ - test/ey_info_test.rb
57
+ - test/test_helper.rb
58
+ has_rdoc: true
59
+ homepage: http://github.com/tongueroo/ey_info
60
+ licenses: []
61
+
62
+ post_install_message:
63
+ rdoc_options: []
64
+
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ hash: 3
73
+ segments:
74
+ - 0
75
+ version: "0"
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ hash: 3
82
+ segments:
83
+ - 0
84
+ version: "0"
85
+ requirements: []
86
+
87
+ rubyforge_project:
88
+ rubygems_version: 1.3.7
89
+ signing_key:
90
+ specification_version: 3
91
+ summary: Ey Info - Easy way to setup ssh keys for ey cloud servers
92
+ test_files:
93
+ - test/ey_info_test.rb
94
+ - test/test_helper.rb