orats 0.5.1 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +11 -4
- data/lib/orats/cli.rb +34 -23
- data/lib/orats/commands/common.rb +82 -0
- data/lib/orats/commands/new/ansible.rb +105 -0
- data/lib/orats/commands/new/exec.rb +54 -0
- data/lib/orats/commands/new/foreman.rb +55 -0
- data/lib/orats/commands/new/rails.rb +107 -0
- data/lib/orats/commands/nuke.rb +72 -0
- data/lib/orats/commands/outdated/compare.rb +100 -0
- data/lib/orats/commands/outdated/exec.rb +46 -0
- data/lib/orats/commands/outdated/parse.rb +62 -0
- data/lib/orats/commands/play.rb +34 -0
- data/lib/orats/commands/ui.rb +52 -0
- data/lib/orats/templates/auth.rb +1 -3
- data/lib/orats/templates/base.rb +243 -11
- data/lib/orats/templates/includes/Galaxyfile +1 -1
- data/lib/orats/templates/includes/Gemfile +5 -1
- data/lib/orats/templates/includes/inventory/group_vars/all.yml +14 -9
- data/lib/orats/templates/play.rb +0 -3
- data/lib/orats/version.rb +1 -1
- metadata +13 -6
- data/lib/orats/command.rb +0 -107
- data/lib/orats/foreman.rb +0 -54
- data/lib/orats/shell.rb +0 -503
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed2be672732a97ee0adf810bdef6be8e40df6415
|
4
|
+
data.tar.gz: caa247df80d369533b424a261ba57b254501b579
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4f26035196b05988cd3eda4a894cbd651aedb8a33a6da75eae97fb5e0d43b1df49e066945f928b2b8e7b1b21a9df1d005dbf926688ae40ecce4b70e19f81f86f
|
7
|
+
data.tar.gz: 67cbd89f2b3eb6a93e1a099e7b5b910f05c288a1d8f811fdbf8f8297af40727ed738ad6b18e2acf7f7509609c8f2a8a5415e33326b9297004d973385969bf2ae
|
data/README.md
CHANGED
@@ -6,6 +6,8 @@ It stands for opinionated rails application templates. The templates include sol
|
|
6
6
|
projects. It handles creating a rails application with a bunch of opinions and optionally an ansible playbook so you can
|
7
7
|
deploy your apps quickly.
|
8
8
|
|
9
|
+
You can also optionally include custom rails templates to append to any template you create with orats.
|
10
|
+
|
9
11
|
Everything is accessed through the [orats gem](#installation).
|
10
12
|
|
11
13
|
## What version of Rails and Ruby are you targeting?
|
@@ -62,7 +64,7 @@ Here is an overview of the available commands. You can find out more information
|
|
62
64
|
running `orats <command name> help` from your terminal. You can also type `orats` on its own to see a list of all commands.
|
63
65
|
|
64
66
|
- Create a new orats project
|
65
|
-
- `orats new <
|
67
|
+
- `orats new <TARGET_PATH> --pg-password <development postgres db password>`
|
66
68
|
- Configuration:
|
67
69
|
- Optionally takes: `--pg-location [localhost]`
|
68
70
|
- Optionally takes: `--pg-username [postgres]`
|
@@ -70,6 +72,7 @@ running `orats <command name> help` from your terminal. You can also type `orats
|
|
70
72
|
- Optionally takes: `--redis-password []`
|
71
73
|
- Template features:
|
72
74
|
- Optionally takes: `--auth [false]`
|
75
|
+
- Optionally takes: `--template []`
|
73
76
|
- Project features:
|
74
77
|
- Optionally takes: `--skip-extras [false]`
|
75
78
|
- Optionally takes: `--skip-foreman-start [false]`
|
@@ -78,10 +81,12 @@ running `orats <command name> help` from your terminal. You can also type `orats
|
|
78
81
|
- Optionally takes: `--skip-galaxy [false]`
|
79
82
|
|
80
83
|
- Create an ansible playbook
|
81
|
-
- `orats play <
|
84
|
+
- `orats play <TARGET_PATH>`
|
85
|
+
- Template features:
|
86
|
+
- Optionally takes: `--template []`
|
82
87
|
|
83
88
|
- Delete the directory and optionally all data associated to it
|
84
|
-
- `orats nuke <
|
89
|
+
- `orats nuke <TARGET_PATH>`
|
85
90
|
- Optionally takes: `--skip-data [false]`
|
86
91
|
|
87
92
|
- Detect whether or not orats, the playbook or inventory is outdated
|
@@ -124,10 +129,12 @@ it includes these features and when I do not want a specific thing it is much qu
|
|
124
129
|
- Create development, staging and production environments.
|
125
130
|
- Use environment variables for things that are likely to change per environment.
|
126
131
|
- Use environment variables for anything that is sensitive and should not be included into version control.
|
132
|
+
- Add environment variables for google analytics UI, disqus short name and S3 in addition to a bunch of typical rails values.
|
127
133
|
- Use redis as the cache backend.
|
128
134
|
- Use sidekiq as a background worker.
|
129
135
|
- Use puma as the server with settings capable of doing phased restarts.
|
130
136
|
- Use foreman in development mode to manage starting both the rails server using puma and sidekiq.
|
137
|
+
- Add a rake task to handle upgrades using the `backup` gem.
|
131
138
|
- Set the production asset precompiler to include fonts and png files.
|
132
139
|
- Set the production logger to rotate the logs daily.
|
133
140
|
- Set the timezone to EST.
|
@@ -140,7 +147,7 @@ it includes these features and when I do not want a specific thing it is much qu
|
|
140
147
|
- Add 3 view helpers to easily set a page's title, meta description and page heading. All of which are optional.
|
141
148
|
- Bootstrap ~3 layout file with conditionally loaded `html5shiv`, `json3` and `respondjs` libs for IE < 9 support.
|
142
149
|
- Separate the navigation, navigation links, flash messages and footer partials.
|
143
|
-
- Add partials
|
150
|
+
- Add partials for both google analytics and disqus.
|
144
151
|
- Public 404, 422, 500 and 502 pages so they can be served directly from your web server.
|
145
152
|
- Use sass and coffeescript.
|
146
153
|
- jquery 1.10.x loaded through a CDN.
|
data/lib/orats/cli.rb
CHANGED
@@ -1,21 +1,25 @@
|
|
1
1
|
require 'thor'
|
2
|
-
require 'orats/
|
2
|
+
require 'orats/commands/new/exec'
|
3
|
+
require 'orats/commands/outdated/exec'
|
4
|
+
require 'orats/commands/play'
|
5
|
+
require 'orats/commands/nuke'
|
3
6
|
|
4
7
|
module Orats
|
5
8
|
class CLI < Thor
|
6
|
-
option :pg_location, default: 'localhost'
|
7
|
-
option :pg_username, default: 'postgres'
|
8
|
-
option :pg_password, required: true
|
9
|
-
option :redis_location, default: 'localhost'
|
10
|
-
option :redis_password, default: ''
|
9
|
+
option :pg_location, default: 'localhost', aliases: '-l'
|
10
|
+
option :pg_username, default: 'postgres', aliases: '-u'
|
11
|
+
option :pg_password, required: true, aliases: '-p'
|
12
|
+
option :redis_location, default: 'localhost', aliases: '-n'
|
13
|
+
option :redis_password, default: '', aliases: '-d'
|
11
14
|
option :auth, type: :boolean, default: false, aliases: '-a'
|
15
|
+
option :template, default: '', aliases: '-m'
|
12
16
|
option :skip_extras, type: :boolean, default: false, aliases: '-E'
|
13
17
|
option :skip_foreman_start, type: :boolean, default: false, aliases: '-F'
|
14
|
-
option :sudo_password, default: ''
|
18
|
+
option :sudo_password, default: '', aliases: '-s'
|
15
19
|
option :skip_galaxy, type: :boolean, default: false, aliases: '-G'
|
16
|
-
desc 'new
|
20
|
+
desc 'new TARGET_PATH [options]', ''
|
17
21
|
long_desc <<-D
|
18
|
-
`orats new
|
22
|
+
`orats new target_path --pg-password supersecret` will create a new rails project and it will also create an ansible inventory to go with it by default.
|
19
23
|
|
20
24
|
You must supply at least this flag:
|
21
25
|
|
@@ -35,6 +39,8 @@ module Orats
|
|
35
39
|
|
36
40
|
`--auth` will include authentication and authorization [false]
|
37
41
|
|
42
|
+
`--template` will let you supply a custom template, a url or file is ok but urls must start with http or https []
|
43
|
+
|
38
44
|
Project features:
|
39
45
|
|
40
46
|
`--skip-extras` skip creating the services directory and ansible inventory/secrets [false]
|
@@ -47,33 +53,38 @@ module Orats
|
|
47
53
|
|
48
54
|
`--skip-galaxy` skip automatically installing roles from the galaxy [false]
|
49
55
|
D
|
50
|
-
def new(
|
51
|
-
|
56
|
+
def new(target_path)
|
57
|
+
Commands::New::Exec.new(target_path, options).init
|
52
58
|
end
|
53
59
|
|
54
|
-
|
60
|
+
option :template, default: '', aliases: '-m'
|
61
|
+
desc 'play PATH [options]', ''
|
55
62
|
long_desc <<-D
|
56
|
-
`orats play
|
63
|
+
`orats play target_path` will create an ansible playbook.
|
64
|
+
|
65
|
+
Template features:
|
66
|
+
|
67
|
+
`--template` will let you supply a custom template, a url or file is ok but urls must start with http or https []
|
57
68
|
D
|
58
|
-
def play(
|
59
|
-
|
69
|
+
def play(target_path)
|
70
|
+
Commands::Play.new(target_path, options).init
|
60
71
|
end
|
61
72
|
|
62
73
|
option :skip_data, type: :boolean, default: false, aliases: '-D'
|
63
|
-
desc 'nuke
|
74
|
+
desc 'nuke TARGET_PATH [options]', ''
|
64
75
|
long_desc <<-D
|
65
|
-
`orats nuke
|
76
|
+
`orats nuke target_path` will delete the directory and optionally all data associated to it.
|
66
77
|
|
67
78
|
Options:
|
68
79
|
|
69
80
|
`--skip-data` will skip deleting app specific postgres databases and redis namespaces [false]
|
70
81
|
D
|
71
|
-
def nuke(
|
72
|
-
|
82
|
+
def nuke(target_path)
|
83
|
+
Commands::Nuke.new(target_path, options).init
|
73
84
|
end
|
74
85
|
|
75
|
-
option :playbook, default: ''
|
76
|
-
option :inventory, default: ''
|
86
|
+
option :playbook, default: '', aliases: '-p'
|
87
|
+
option :inventory, default: '', aliases: '-i'
|
77
88
|
desc 'outdated [options]', ''
|
78
89
|
long_desc <<-D
|
79
90
|
`orats outdated` will run various comparisons on orats and your ansible files.
|
@@ -91,7 +102,7 @@ module Orats
|
|
91
102
|
`--inventory` to supply an inventory file for comparison []
|
92
103
|
D
|
93
104
|
def outdated
|
94
|
-
|
105
|
+
Commands::Outdated::Exec.new(nil, options).init
|
95
106
|
end
|
96
107
|
|
97
108
|
desc 'version', ''
|
@@ -99,7 +110,7 @@ module Orats
|
|
99
110
|
`orats version` will print the current version.
|
100
111
|
D
|
101
112
|
def version
|
102
|
-
|
113
|
+
puts "Orats version #{VERSION}"
|
103
114
|
end
|
104
115
|
map %w(-v --version) => :version
|
105
116
|
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'orats/commands/ui'
|
2
|
+
require 'orats/commands/outdated/parse'
|
3
|
+
|
4
|
+
module Orats
|
5
|
+
module Commands
|
6
|
+
class Common
|
7
|
+
include Thor::Base
|
8
|
+
include Thor::Shell
|
9
|
+
include Thor::Actions
|
10
|
+
include UI
|
11
|
+
include Outdated::Parse
|
12
|
+
|
13
|
+
RELATIVE_PATHS = {
|
14
|
+
galaxyfile: 'templates/includes/Galaxyfile',
|
15
|
+
hosts: 'templates/includes/inventory/hosts',
|
16
|
+
inventory: 'templates/includes/inventory/group_vars/all.yml',
|
17
|
+
playbook: 'templates/play.rb',
|
18
|
+
version: 'version.rb'
|
19
|
+
}
|
20
|
+
|
21
|
+
attr_accessor :remote_gem_version, :remote_paths, :local_paths
|
22
|
+
|
23
|
+
def initialize(target_path = '', options = {})
|
24
|
+
@target_path = target_path
|
25
|
+
@options = options
|
26
|
+
@active_path = @target_path
|
27
|
+
|
28
|
+
@local_paths = {}
|
29
|
+
@remote_paths = {}
|
30
|
+
|
31
|
+
build_common_paths
|
32
|
+
|
33
|
+
self.destination_root = Dir.pwd
|
34
|
+
@behavior = :invoke
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def base_path
|
40
|
+
File.join(File.expand_path(File.dirname(__FILE__)), '..')
|
41
|
+
end
|
42
|
+
|
43
|
+
def repo_path
|
44
|
+
%w(https://raw.githubusercontent.com/nickjj/orats lib/orats)
|
45
|
+
end
|
46
|
+
|
47
|
+
def select_branch(branch, value)
|
48
|
+
"#{repo_path[0]}/#{branch}/#{repo_path[1]}/#{value}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def build_common_paths
|
52
|
+
@remote_paths[:version] = select_branch 'master', RELATIVE_PATHS[:version]
|
53
|
+
@remote_gem_version = gem_version
|
54
|
+
|
55
|
+
RELATIVE_PATHS.each_pair do |key, value|
|
56
|
+
@local_paths[key] = "#{base_path}/#{value}"
|
57
|
+
@remote_paths[key] = select_branch @remote_gem_version, value
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def url_to_string(url)
|
62
|
+
begin
|
63
|
+
open(url).read
|
64
|
+
rescue *[OpenURI::HTTPError, SocketError] => ex
|
65
|
+
log_error 'error', "Error accessing URL #{url}",
|
66
|
+
'message', ex
|
67
|
+
exit 1
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def file_to_string(path)
|
72
|
+
if File.exist?(path) && File.file?(path)
|
73
|
+
IO.read(path)
|
74
|
+
else
|
75
|
+
log_error 'error', 'Error finding file',
|
76
|
+
'message', path
|
77
|
+
exit 1
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module Orats
|
4
|
+
module Commands
|
5
|
+
module New
|
6
|
+
module Ansible
|
7
|
+
def ansible_extras
|
8
|
+
create_inventory
|
9
|
+
|
10
|
+
secrets_path = "#{@target_path}/secrets"
|
11
|
+
create_secrets secrets_path
|
12
|
+
|
13
|
+
log_thor_task 'shell', 'Modifying secrets path in group_vars/all.yml'
|
14
|
+
gsub_file "#{@target_path}/#{fix_path_for_user(Commands::Common::RELATIVE_PATHS[:inventory])}",
|
15
|
+
'~/tmp/testproj/secrets/', File.expand_path(secrets_path)
|
16
|
+
|
17
|
+
log_thor_task 'shell', 'Modifying the place holder app name in group_vars/all.yml'
|
18
|
+
gsub_file "#{@target_path}/#{fix_path_for_user(Commands::Common::RELATIVE_PATHS[:inventory])}",
|
19
|
+
'testproj', File.basename(@target_path)
|
20
|
+
|
21
|
+
log_thor_task 'shell', 'Creating ssh keypair'
|
22
|
+
run "ssh-keygen -t rsa -P '' -f #{secrets_path}/id_rsa"
|
23
|
+
|
24
|
+
log_thor_task 'shell', 'Creating self signed ssl certificates'
|
25
|
+
run create_rsa_certificate(secrets_path, 'sslkey.key', 'sslcert.crt')
|
26
|
+
|
27
|
+
log_thor_task 'shell', 'Creating monit pem file'
|
28
|
+
run "#{create_rsa_certificate(secrets_path,
|
29
|
+
'monit.pem', 'monit.pem')} && openssl gendh 512 >> #{secrets_path}/monit.pem"
|
30
|
+
|
31
|
+
install_role_dependencies unless @options[:skip_galaxy]
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def create_inventory
|
37
|
+
log_thor_task 'shell', 'Creating ansible inventory'
|
38
|
+
run "mkdir -p #{@target_path}/inventory/group_vars"
|
39
|
+
|
40
|
+
local_to_user Commands::Common::RELATIVE_PATHS[:hosts]
|
41
|
+
local_to_user Commands::Common::RELATIVE_PATHS[:inventory]
|
42
|
+
end
|
43
|
+
|
44
|
+
def local_to_user(file)
|
45
|
+
fixed_file = fix_path_for_user(file)
|
46
|
+
|
47
|
+
log_thor_task 'shell', "Creating #{fixed_file}"
|
48
|
+
run "cp #{base_path}/#{file} #{@target_path}/#{fixed_file}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def create_secrets(secrets_path)
|
52
|
+
log_thor_task 'shell', 'Creating ansible secrets'
|
53
|
+
run "mkdir #{secrets_path}"
|
54
|
+
|
55
|
+
if @options[:redis_password].empty?
|
56
|
+
run "touch #{secrets_path}/redis_password"
|
57
|
+
else
|
58
|
+
save_secret_string "#{secrets_path}/redis_password"
|
59
|
+
gsub_file "#{@target_path}/#{fix_path_for_user(Commands::Common::RELATIVE_PATHS[:inventory])}",
|
60
|
+
'redis_password: false', 'redis_password: true'
|
61
|
+
end
|
62
|
+
|
63
|
+
save_secret_string "#{secrets_path}/postgres_password"
|
64
|
+
save_secret_string "#{secrets_path}/mail_password"
|
65
|
+
save_secret_string "#{secrets_path}/rails_token"
|
66
|
+
save_secret_string "#{secrets_path}/devise_token"
|
67
|
+
save_secret_string "#{secrets_path}/devise_pepper_token"
|
68
|
+
end
|
69
|
+
|
70
|
+
def save_secret_string(file)
|
71
|
+
File.open(file, 'w+') { |f| f.write(SecureRandom.hex(64)) }
|
72
|
+
end
|
73
|
+
|
74
|
+
def create_rsa_certificate(secrets_path, keyout, out)
|
75
|
+
"openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 -subj '/C=US/ST=Foo/L=Bar/O=Baz/CN=qux.com' -keyout #{secrets_path}/#{keyout} -out #{secrets_path}/#{out}"
|
76
|
+
end
|
77
|
+
|
78
|
+
def install_role_dependencies
|
79
|
+
log_thor_task 'shell', 'Updating ansible roles from the galaxy'
|
80
|
+
|
81
|
+
galaxy_install =
|
82
|
+
"ansible-galaxy install -r #{base_path}/#{Commands::Common::RELATIVE_PATHS[:galaxyfile]} --force"
|
83
|
+
|
84
|
+
galaxy_out = run(galaxy_install, capture: true)
|
85
|
+
|
86
|
+
if galaxy_out.include?('you do not have permission')
|
87
|
+
if @options[:sudo_password].empty?
|
88
|
+
sudo_galaxy_command = 'sudo'
|
89
|
+
else
|
90
|
+
sudo_galaxy_command = "echo #{@options[:sudo_password]} | sudo -S"
|
91
|
+
end
|
92
|
+
|
93
|
+
run("#{sudo_galaxy_command} #{galaxy_install}")
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def fix_path_for_user(file)
|
100
|
+
file.sub('templates/includes/', '')
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'orats/commands/common'
|
2
|
+
require 'orats/commands/new/ansible'
|
3
|
+
require 'orats/commands/new/rails'
|
4
|
+
require 'orats/commands/new/foreman'
|
5
|
+
|
6
|
+
module Orats
|
7
|
+
module Commands
|
8
|
+
module New
|
9
|
+
class Exec < Commands::Common
|
10
|
+
include Ansible
|
11
|
+
include Rails
|
12
|
+
include Foreman
|
13
|
+
|
14
|
+
def initialize(target_path = '', options = {})
|
15
|
+
super
|
16
|
+
|
17
|
+
@active_path = services_path
|
18
|
+
end
|
19
|
+
|
20
|
+
def init
|
21
|
+
rails_template 'base' do
|
22
|
+
gsub_postgres_info
|
23
|
+
gsub_redis_info unless @options[:redis_password].empty?
|
24
|
+
gsub_project_path
|
25
|
+
|
26
|
+
bundle_install
|
27
|
+
bundle_binstubs
|
28
|
+
spring_binstub
|
29
|
+
|
30
|
+
create_and_migrate_database
|
31
|
+
end
|
32
|
+
|
33
|
+
if @options[:auth]
|
34
|
+
rails_template 'auth', '--skip ' do
|
35
|
+
run_rake 'db:migrate db:seed'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
ansible_extras unless @options[:skip_extras]
|
40
|
+
|
41
|
+
custom_rails_template unless @options[:template].empty?
|
42
|
+
|
43
|
+
foreman_start unless @options[:skip_foreman_start]
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def services_path
|
49
|
+
@options[:skip_extras] ? @target_path : "#{@target_path}/services/#{File.basename @target_path}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'timeout'
|
3
|
+
|
4
|
+
module Orats
|
5
|
+
module Commands
|
6
|
+
module New
|
7
|
+
module Foreman
|
8
|
+
def foreman_start
|
9
|
+
@options[:skip_foreman_start] ? message = 'Start your' : message = 'Starting'
|
10
|
+
|
11
|
+
puts '', '='*80
|
12
|
+
log_status_top 'action', "#{message} server with the following commands", :cyan
|
13
|
+
log_status_bottom 'command', "cd #{@active_path}", :magenta, true
|
14
|
+
log_status_bottom 'command', 'bundle exec foreman start', :magenta
|
15
|
+
puts '='*80, ''
|
16
|
+
|
17
|
+
attempt_to_start
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def attempt_to_start
|
23
|
+
while port_taken? do
|
24
|
+
log_status_top 'error', "Another application is using port 3000\n", :red
|
25
|
+
|
26
|
+
exit 1 if no?('Would you like to try running the server again? (y/N)', :cyan)
|
27
|
+
end
|
28
|
+
|
29
|
+
puts
|
30
|
+
|
31
|
+
run_from @active_path, 'bundle exec foreman start'
|
32
|
+
end
|
33
|
+
|
34
|
+
def port_taken?
|
35
|
+
begin
|
36
|
+
Timeout::timeout(5) do
|
37
|
+
begin
|
38
|
+
s = TCPSocket.new('localhost', 3000)
|
39
|
+
s.close
|
40
|
+
|
41
|
+
return true
|
42
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
43
|
+
return false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
rescue Timeout::Error
|
47
|
+
false
|
48
|
+
end
|
49
|
+
|
50
|
+
false
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|