knife-joyent 0.0.10 → 0.1.0

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.
@@ -1,168 +1,255 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/base')
1
+ require 'chef/knife/joyent_base'
2
2
 
3
- module KnifeJoyent
4
- class JoyentServerCreate < Chef::Knife
3
+ class Chef
4
+ class Knife
5
+ class JoyentServerCreate < Knife
5
6
 
6
- include KnifeJoyent::Base
7
+ include Knife::JoyentBase
7
8
 
8
- deps do
9
- require 'fog'
10
- require 'readline'
11
- require 'chef/json_compat'
12
- require 'chef/knife/bootstrap'
13
- Chef::Knife::Bootstrap.load_deps
14
- end
15
-
16
- banner 'knife joyent server create (options)'
17
-
18
- # mixlib option parsing
19
- option :name,
20
- :long => '--name <name>',
21
- :description => 'name for this machine'
22
-
23
- option :package,
24
- :short => '-f FLAVOR_NAME',
25
- :long => '--flavor FLAVOR_NAME',
26
- :description => 'specify flavor/package for the server'
27
-
28
- option :dataset,
29
- :short => '-I IMAGE_ID',
30
- :long => '--image IMAGE_ID',
31
- :description => 'specify image for the server'
32
-
33
- option :run_list,
34
- :short => "-r RUN_LIST",
35
- :long => "--run-list RUN_LIST",
36
- :description => "Comma separated list of roles/recipes to apply",
37
- :proc => lambda { |o| o.split(/[\s,]+/) },
38
- :default => []
39
-
40
- option :ssh_user,
41
- :short => "-x USERNAME",
42
- :long => "--ssh-user USERNAME",
43
- :description => "The ssh username",
44
- :default => "root"
45
-
46
- option :identity_file,
47
- :short => "-i IDENTITY_FILE",
48
- :long => "--identity-file IDENTITY_FILE",
49
- :description => "The SSH identity file used for authentication"
50
-
51
- option :chef_node_name,
52
- :short => "-N NAME",
53
- :long => "--node-name NAME",
54
- :description => "The Chef node name for your new node"
55
-
56
- option :prerelease,
57
- :long => "--prerelease",
58
- :description => "Install the pre-release chef gems"
59
-
60
- option :distro,
61
- :short => "-d DISTRO",
62
- :long => "--distro DISTRO",
63
- :description => "Bootstrap a distro using a template",
64
- :proc => Proc.new { |d| Chef::Config[:knife][:distro] = d },
65
- :default => "chef-full"
9
+ deps do
10
+ require 'fog'
11
+ require 'readline'
12
+ require 'chef/json_compat'
13
+ require 'chef/knife/bootstrap'
14
+ require 'ipaddr'
15
+ Chef::Knife::Bootstrap.load_deps
16
+ end
17
+
18
+ banner 'knife joyent server create (options)'
19
+
20
+
21
+ def is_linklocal(ip)
22
+ linklocal = IPAddr.new "169.254.0.0/16"
23
+ return linklocal.include?(ip)
24
+ end
25
+
26
+ def is_loopback(ip)
27
+ loopback = IPAddr.new "127.0.0.0/8"
28
+ return loopback.include?(ip)
29
+ end
30
+
31
+ def is_private(ip)
32
+ block_a = IPAddr.new "10.0.0.0/8"
33
+ block_b = IPAddr.new "172.16.0.0/12"
34
+ block_c = IPAddr.new "192.168.0.0/16"
35
+ return (block_a.include?(ip) or block_b.include?(ip) or block_c.include?(ip))
36
+ end
37
+
38
+ option :package,
39
+ :short => '-f FLAVOR_NAME',
40
+ :long => '--flavor FLAVOR_NAME',
41
+ :description => 'specify flavor/package for the server'
42
+
43
+ option :dataset,
44
+ :short => '-I IMAGE_ID',
45
+ :long => '--image IMAGE_ID',
46
+ :description => 'specify image for the server'
47
+
48
+ option :run_list,
49
+ :short => "-r RUN_LIST",
50
+ :long => "--run-list RUN_LIST",
51
+ :description => "Comma separated list of roles/recipes to apply",
52
+ :proc => lambda { |o| o.split(/[\s,]+/) },
53
+ :default => []
54
+
55
+ option :private_network,
56
+ :long => "--private-network",
57
+ :description => "Use the private IP for bootstrapping rather than the public IP",
58
+ :boolean => true,
59
+ :default => false
60
+
61
+ option :ssh_user,
62
+ :short => "-x USERNAME",
63
+ :long => "--ssh-user USERNAME",
64
+ :description => "The ssh username",
65
+ :default => "root"
66
+
67
+ option :identity_file,
68
+ :short => "-i IDENTITY_FILE",
69
+ :long => "--identity-file IDENTITY_FILE",
70
+ :description => "The SSH identity file used for authentication"
71
+
72
+ option :chef_node_name,
73
+ :short => "-N NAME",
74
+ :long => "--node-name NAME",
75
+ :description => "The Chef node name for your new node"
76
+
77
+ option :prerelease,
78
+ :long => "--prerelease",
79
+ :description => "Install the pre-release chef gems"
80
+
81
+ option :distro,
82
+ :short => "-d DISTRO",
83
+ :long => "--distro DISTRO",
84
+ :description => "Bootstrap a distro using a template",
85
+ :proc => Proc.new { |d| Chef::Config[:knife][:distro] = d },
86
+ :default => "chef-full"
87
+
88
+ option :no_host_key_verify,
89
+ :long => "--no-host-key-verify",
90
+ :description => "Disable host key verification",
91
+ :boolean => true,
92
+ :default => false
93
+
94
+ def is_linklocal(ip)
95
+ linklocal = IPAddr.new "169.254.0.0/16"
96
+ return linklocal.include?(ip)
97
+ end
98
+
99
+ def is_loopback(ip)
100
+ loopback = IPAddr.new "127.0.0.0/8"
101
+ return loopback.include?(ip)
102
+ end
66
103
 
67
- option :no_host_key_verify,
68
- :long => "--no-host-key-verify",
69
- :description => "Disable host key verification",
70
- :boolean => true,
71
- :default => false
72
-
73
- # wait for ssh to come up
74
- def tcp_test_ssh(hostname)
75
- tcp_socket = TCPSocket.new(hostname, 22)
76
- readable = IO.select([tcp_socket], nil, nil, 5)
77
- if readable
78
- Chef::Log.debug("sshd accepting connections on #{hostname}, banner is #{tcp_socket.gets}")
79
- yield
80
- true
81
- else
104
+ def is_private(ip)
105
+ block_a = IPAddr.new "10.0.0.0/8"
106
+ block_b = IPAddr.new "172.16.0.0/12"
107
+ block_c = IPAddr.new "192.168.0.0/16"
108
+ return (block_a.include?(ip) or block_b.include?(ip) or block_c.include?(ip))
109
+ end
110
+
111
+ # wait for ssh to come up
112
+ def tcp_test_ssh(hostname)
113
+ tcp_socket = TCPSocket.new(hostname, 22)
114
+ readable = IO.select([tcp_socket], nil, nil, 5)
115
+ if readable
116
+ Chef::Log.debug("sshd accepting connections on #{hostname}, banner is #{tcp_socket.gets}")
117
+ yield
118
+ true
119
+ else
120
+ false
121
+ end
122
+ rescue Errno::ETIMEDOUT
123
+ false
124
+ rescue Errno::EPERM
125
+ false
126
+ rescue Errno::ECONNREFUSED
127
+ sleep 2
82
128
  false
129
+ rescue Errno::EHOSTUNREACH
130
+ sleep 2
131
+ false
132
+ ensure
133
+ tcp_socket && tcp_socket.close
83
134
  end
84
- rescue Errno::ETIMEDOUT
85
- false
86
- rescue Errno::EPERM
87
- false
88
- rescue Errno::ECONNREFUSED
89
- sleep 2
90
- false
91
- rescue Errno::EHOSTUNREACH
92
- sleep 2
93
- false
94
- ensure
95
- tcp_socket && tcp_socket.close
96
- end
97
135
 
136
+ # Run Chef bootstrap script
137
+ def bootstrap_for_node(server, bootstrap_ip_address)
138
+ bootstrap = Chef::Knife::Bootstrap.new
139
+ Chef::Log.debug("Bootstrap name_args = [ #{bootstrap_ip_address} ]")
140
+ bootstrap.name_args = [ bootstrap_ip_address ]
141
+ Chef::Log.debug("Bootstrap run_list = #{config[:run_list]}")
142
+ bootstrap.config[:run_list] = config[:run_list]
143
+ Chef::Log.debug("Bootstrap ssh_user = #{config[:ssh_user]}")
144
+ bootstrap.config[:ssh_user] = config[:ssh_user]
145
+ Chef::Log.debug("Bootstrap identity_file = #{config[:identity_file]}")
146
+ bootstrap.config[:identity_file] = config[:identity_file]
147
+ Chef::Log.debug("Bootstrap chef_node_name = #{config[:chef_node_name]}")
148
+ bootstrap.config[:chef_node_name] = config[:chef_node_name] || server.id
149
+ Chef::Log.debug("Bootstrap prerelease = #{config[:prerelease]}")
150
+ bootstrap.config[:prerelease] = config[:prerelease]
151
+ Chef::Log.debug("Bootstrap distro = #{config[:distro]}")
152
+ bootstrap.config[:distro] = config[:distro]
153
+ #Chef::Log.debug("Bootstrap use_sudo = #{config[:use_sudo]}")
154
+ #bootstrap.config[:use_sudo] = true
155
+ Chef::Log.debug("Bootstrap environment = #{config[:environment]}")
156
+ bootstrap.config[:environment] = config[:environment]
157
+ Chef::Log.debug("Bootstrap no_host_key_verify = #{config[:no_host_key_verify]}")
158
+ bootstrap.config[:no_host_key_verify] = config[:no_host_key_verify]
159
+
160
+ bootstrap
161
+ end
98
162
 
99
- # Run Chef bootstrap script
100
- def bootstrap_for_node(server)
101
- bootstrap = Chef::Knife::Bootstrap.new
102
- Chef::Log.debug("Bootstrap name_args = [ #{server.ips.first} ]")
103
- bootstrap.name_args = [ server.ips.first ]
104
- Chef::Log.debug("Bootstrap run_list = #{config[:run_list]}")
105
- bootstrap.config[:run_list] = config[:run_list]
106
- Chef::Log.debug("Bootstrap ssh_user = #{config[:ssh_user]}")
107
- bootstrap.config[:ssh_user] = config[:ssh_user]
108
- Chef::Log.debug("Bootstrap identity_file = #{config[:identity_file]}")
109
- bootstrap.config[:identity_file] = config[:identity_file]
110
- Chef::Log.debug("Bootstrap chef_node_name = #{config[:chef_node_name]}")
111
- bootstrap.config[:chef_node_name] = config[:chef_node_name] || server.id
112
- Chef::Log.debug("Bootstrap prerelease = #{config[:prerelease]}")
113
- bootstrap.config[:prerelease] = config[:prerelease]
114
- Chef::Log.debug("Bootstrap distro = #{config[:distro]}")
115
- bootstrap.config[:distro] = config[:distro]
116
- #Chef::Log.debug("Bootstrap use_sudo = #{config[:use_sudo]}")
117
- #bootstrap.config[:use_sudo] = true
118
- Chef::Log.debug("Bootstrap environment = #{config[:environment]}")
119
- bootstrap.config[:environment] = config[:environment]
120
- Chef::Log.debug("Bootstrap no_host_key_verify = #{config[:no_host_key_verify]}")
121
- bootstrap.config[:no_host_key_verify] = config[:no_host_key_verify]
122
-
123
- bootstrap
124
- end
163
+ def run
164
+ $stdout.sync = true
125
165
 
126
- # Go
127
- def run
128
- puts ui.color("Creating machine #{config[:chef_node_name]}", :cyan)
129
- begin
130
- server = self.connection.servers.create(:dataset => config[:dataset],
131
- :package => config[:package],
132
- :name => config[:name])
133
- server.wait_for { print "."; ready? }
134
- rescue => e
135
- Chef::Log.debug("e: #{e}")
136
- if e.response && e.response.body.kind_of?(String)
137
- error = MultiJson.decode(e.response.body)
138
- puts ui.error(error['message'])
139
- exit 1
166
+ # add some validation here ala knife-ec2
167
+
168
+ node_name = config[:chef_node_name] || config[:server_name]
169
+
170
+ puts ui.color("Creating machine #{node_name}", :cyan)
171
+
172
+ server = connection.servers.create(
173
+ :name => node_name,
174
+ :dataset => config[:dataset],
175
+ :package => config[:package]
176
+ )
177
+
178
+ print "\n#{ui.color("Waiting for server", :magenta)}"
179
+ server.wait_for { print "."; ready? }
180
+
181
+ #which IP address to bootstrap
182
+ bootstrap_ip_addresses = server.ips.select{|ip|
183
+ ip and not(is_loopback(ip) or is_linklocal(ip))
184
+ }
185
+
186
+ if bootstrap_ip_addresses.count == 1
187
+ bootstrap_ip_address = bootstrap_ip_addresses.first
140
188
  else
141
- raise
189
+ if config[:private_network]
190
+ bootstrap_ip_address = bootstrap_ip_addresses.find{|ip| is_private(ip)}
191
+ else
192
+ bootstrap_ip_address = bootstrap_ip_addresses.find{|ip| not is_private(ip)}
193
+ end
142
194
  end
195
+
196
+ Chef::Log.debug("Bootstrap IP Address #{bootstrap_ip_address}")
197
+ if bootstrap_ip_address.nil?
198
+ ui.error("No IP address available for bootstrapping.")
199
+ exit 1
200
+ end
201
+
202
+ puts ui.color("attempting to bootstrap on #{bootstrap_ip_address}", :cyan)
203
+
204
+ print(".") until tcp_test_ssh(bootstrap_ip_address) {
205
+ sleep @initial_sleep_delay ||= 10
206
+ puts("done")
207
+ }
208
+
209
+ bootstrap_for_node(server, bootstrap_ip_address).run
210
+
211
+ puts ui.color("Created machine:", :cyan)
212
+ msg_pair("ID", server.id.to_s)
213
+ msg_pair("Name", server.name)
214
+ msg_pair("State", server.state)
215
+ msg_pair("Type", server.type)
216
+ msg_pair("Dataset", server.dataset)
217
+ msg_pair("IP's", server.ips)
218
+ end
219
+
220
+ # Run Chef bootstrap script
221
+ def bootstrap_for_node(server, bootstrap_ip_address)
222
+ bootstrap = Chef::Knife::Bootstrap.new
223
+ Chef::Log.debug("Bootstrap name_args = [ #{bootstrap_ip_address} ]")
224
+ bootstrap.name_args = [ bootstrap_ip_address ]
225
+ Chef::Log.debug("Bootstrap run_list = #{config[:run_list]}")
226
+ bootstrap.config[:run_list] = config[:run_list]
227
+ Chef::Log.debug("Bootstrap ssh_user = #{config[:ssh_user]}")
228
+ bootstrap.config[:ssh_user] = config[:ssh_user]
229
+ Chef::Log.debug("Bootstrap identity_file = #{config[:identity_file]}")
230
+ bootstrap.config[:identity_file] = config[:identity_file]
231
+ Chef::Log.debug("Bootstrap chef_node_name = #{config[:chef_node_name]}")
232
+ bootstrap.config[:chef_node_name] = config[:chef_node_name] || server.id
233
+ Chef::Log.debug("Bootstrap prerelease = #{config[:prerelease]}")
234
+ bootstrap.config[:prerelease] = config[:prerelease]
235
+ Chef::Log.debug("Bootstrap distro = #{config[:distro]}")
236
+ bootstrap.config[:distro] = config[:distro]
237
+ #Chef::Log.debug("Bootstrap use_sudo = #{config[:use_sudo]}")
238
+ #bootstrap.config[:use_sudo] = true
239
+ Chef::Log.debug("Bootstrap environment = #{config[:environment]}")
240
+ bootstrap.config[:environment] = config[:environment]
241
+ Chef::Log.debug("Bootstrap no_host_key_verify = #{config[:no_host_key_verify]}")
242
+ bootstrap.config[:no_host_key_verify] = config[:no_host_key_verify]
243
+
244
+ bootstrap
143
245
  end
144
246
 
145
- puts ui.color("Created machine:", :cyan)
146
- msg("ID", server.id.to_s)
147
- msg("Name", server.name)
148
- msg("State", server.state)
149
- msg("Type", server.type)
150
- msg("Dataset", server.dataset)
151
- msg("IP's", server.ips)
152
- puts ui.color("attempting to bootstrap on #{server.ips.first}", :cyan)
153
-
154
- print(".") until tcp_test_ssh(server.ips.first) {
155
- sleep 1
156
- puts("done")
157
- }
158
- bootstrap_for_node(server).run
159
- exit 0
160
- end
161
-
162
- def msg(label, value = nil)
163
- if value && !value.empty?
164
- puts "#{ui.color(label, :cyan)}: #{value}"
247
+ def msg_pair(label, value = nil)
248
+ if value && !value.empty?
249
+ puts "#{ui.color(label, :cyan)}: #{value}"
250
+ end
165
251
  end
166
252
  end
167
253
  end
168
254
  end
255
+
@@ -1,72 +1,74 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/base')
1
+ require 'chef/knife/joyent_base'
2
2
 
3
3
  require 'chef/api_client'
4
4
 
5
- module KnifeJoyent
6
- class JoyentServerDelete < Chef::Knife
5
+ class Chef
6
+ class Knife
7
+ class JoyentServerDelete < Knife
7
8
 
8
- include KnifeJoyent::Base
9
+ include Knife::JoyentBase
9
10
 
10
- banner 'knife joyent server delete <server_id>'
11
+ banner 'knife joyent server delete <server_id>'
11
12
 
12
- def run
13
- unless name_args.size === 1
14
- show_usage
15
- exit 1
16
- end
13
+ def run
14
+ unless name_args.size === 1
15
+ show_usage
16
+ exit 1
17
+ end
17
18
 
18
- id = name_args.first
19
+ id = name_args.first
19
20
 
20
- server = self.connection.servers.get(id)
21
-
22
- msg("ID", server.id.to_s)
23
- msg("Name", server.name)
24
- msg("State", server.state)
25
- msg("Type", server.type)
26
- msg("Dataset", server.dataset)
27
- msg("IP's", server.ips)
28
-
29
- unless server
30
- puts ui.error("Unable to locate server: #{id}")
31
- exit 1
32
- end
33
-
34
- puts "\n"
35
- confirm("Do you really want to delete this server")
36
-
37
- puts ui.color("Stopping server...", :cyan)
21
+ server = self.connection.servers.get(id)
22
+
23
+ msg("ID", server.id.to_s)
24
+ msg("Name", server.name)
25
+ msg("State", server.state)
26
+ msg("Type", server.type)
27
+ msg("Dataset", server.dataset)
28
+ msg("IP's", server.ips)
29
+
30
+ unless server
31
+ puts ui.error("Unable to locate server: #{id}")
32
+ exit 1
33
+ end
34
+
35
+ puts "\n"
36
+ confirm("Do you really want to delete this server")
37
+
38
+ puts ui.color("Stopping server...", :cyan)
38
39
 
39
- if server.stopped?
40
- puts ui.color("Server #{id} is already stopped", :cyan)
41
- else
42
- if server.stop
43
- puts ui.color("Server stopped", :cyan)
40
+ if server.stopped?
41
+ puts ui.color("Server #{id} is already stopped", :cyan)
44
42
  else
45
- puts ui.error("Failed to stop server")
46
- exit 1
43
+ if server.stop
44
+ puts ui.color("Server stopped", :cyan)
45
+ else
46
+ puts ui.error("Failed to stop server")
47
+ exit 1
48
+ end
47
49
  end
48
- end
49
-
50
- server.destroy
51
- puts ui.color("Deleted server: #{id}", :cyan)
52
-
53
- puts "\n"
54
- confirm("Delete client and node for #{server.name}?")
55
50
 
56
- node = Chef::Node.load(server.name)
57
- puts "deleting node #{node.name}"
58
- node.destroy
59
- ui.warn("Deleted node named #{node.name}")
51
+ server.destroy
52
+ puts ui.color("Deleted server: #{id}", :cyan)
60
53
 
61
- client = Chef::ApiClient.load(server.name)
62
- puts "deleting client #{client.name}"
63
- client.destroy
64
- ui.warn("Deleted client named #{client.name}")
65
- end
66
-
67
- def msg(label, value)
68
- if value && !value.empty?
69
- puts "#{ui.color(label, :cyan)}: #{value}"
54
+ puts "\n"
55
+ confirm("Delete client and node for #{server.name}?")
56
+
57
+ node = Chef::Node.load(server.name)
58
+ puts "deleting node #{node.name}"
59
+ node.destroy
60
+ ui.warn("Deleted node named #{node.name}")
61
+
62
+ client = Chef::ApiClient.load(server.name)
63
+ puts "deleting client #{client.name}"
64
+ client.destroy
65
+ ui.warn("Deleted client named #{client.name}")
66
+ end
67
+
68
+ def msg(label, value)
69
+ if value && !value.empty?
70
+ puts "#{ui.color(label, :cyan)}: #{value}"
71
+ end
70
72
  end
71
73
  end
72
74
  end
@@ -1,50 +1,53 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/base')
2
-
3
-
4
- module KnifeJoyent
5
- class JoyentServerList < Chef::Knife
6
-
7
- include KnifeJoyent::Base
8
-
9
- banner "knife joyent server list <options>"
10
-
11
- def run
12
- servers = [
13
- ui.color('ID', :bold),
14
- ui.color('Name', :bold),
15
- ui.color('State', :bold),
16
- ui.color('Type', :bold),
17
- ui.color('Image', :bold),
18
- ui.color('IPs', :bold),
19
- ui.color('RAM', :bold),
20
- ui.color('Disk', :bold),
21
- ]
22
-
23
- self.connection.servers.sort do |a, b|
24
- (a.name || '') <=> (b.name || '')
25
- end.each do |s|
26
- servers << s.id.to_s
27
- servers << s.name
28
-
29
- servers << case s.state
30
- when 'running'
31
- ui.color(s.state, :green)
32
- when 'stopping', 'provisioning'
33
- ui.color(s.state, :yellow)
34
- when 'stopped'
35
- ui.color(s.state, :red)
36
- else
37
- ui.color('unknown', :red)
1
+ require 'chef/knife/joyent_base'
2
+
3
+ class Chef
4
+ class Knife
5
+ class JoyentServerList < Knife
6
+
7
+ include Knife::JoyentBase
8
+
9
+ banner "knife joyent server list <options>"
10
+
11
+ def run
12
+ servers = [
13
+ ui.color('ID', :bold),
14
+ ui.color('Name', :bold),
15
+ ui.color('State', :bold),
16
+ ui.color('Type', :bold),
17
+ ui.color('Image', :bold),
18
+ ui.color('IPs', :bold),
19
+ ui.color('RAM', :bold),
20
+ ui.color('Disk', :bold),
21
+ ui.color('Tags', :bold)
22
+ ]
23
+
24
+ self.connection.servers.sort do |a, b|
25
+ (a.name || '') <=> (b.name || '')
26
+ end.each do |s|
27
+ servers << s.id.to_s
28
+ servers << s.name
29
+
30
+ servers << case s.state
31
+ when 'running'
32
+ ui.color(s.state, :green)
33
+ when 'stopping', 'provisioning'
34
+ ui.color(s.state, :yellow)
35
+ when 'stopped'
36
+ ui.color(s.state, :red)
37
+ else
38
+ ui.color('unknown', :red)
39
+ end
40
+
41
+ servers << s.type
42
+ servers << s.dataset
43
+ servers << s.ips.join(" ")
44
+ servers << "#{s.memory/1024} GB".to_s
45
+ servers << "#{s.disk/1024} GB".to_s
46
+ servers << s.tags.map { |k, v| "#{k}:#{v}" }.join(' ')
38
47
  end
39
48
 
40
- servers << s.type
41
- servers << s.dataset
42
- servers << s.ips.join(" ")
43
- servers << "#{s.memory/1024} GB".to_s
44
- servers << "#{s.disk/1024} GB".to_s
49
+ puts ui.list(servers, :uneven_columns_across, 9)
45
50
  end
46
-
47
- puts ui.list(servers, :uneven_columns_across, 8)
48
51
  end
49
52
  end
50
53
  end