knife-joyent 0.4.5 → 0.4.8

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.
data/README.md CHANGED
@@ -33,6 +33,7 @@ Currently available commands:
33
33
  knife joyent server resize <server_id> -f <flavor>
34
34
  knife joyent server start <server_id>
35
35
  knife joyent server stop <server_id>
36
+ knife joyent server pricing
36
37
  knife joyent server metadata update <server_id> -m <json>
37
38
  knife joyent server metadata delete <server_id> <options>
38
39
  knife joyent snapshot create <server_id> <snapshot_name>
data/knife-joyent.gemspec CHANGED
@@ -16,9 +16,10 @@ Gem::Specification.new do |s|
16
16
  s.files = `git ls-files`.split("\n")
17
17
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
18
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
- s.add_dependency "fog", "~> 1.15.0"
19
+ s.add_dependency "fog", "~> 1.21.0"
20
20
  s.add_dependency "multi_json", "~> 1.7"
21
- s.add_dependency "chef", ">= 0.10.10"
21
+ s.add_dependency "chef", "~> 11.6"
22
+ s.add_dependency "joyent-cloud-pricing", ">= 1.0.3"
22
23
 
23
24
  s.add_development_dependency 'rspec'
24
25
  s.add_development_dependency 'guard-rspec'
@@ -1,5 +1,5 @@
1
1
  require 'chef/knife'
2
- require_relative '../../knife-joyent/pricing'
2
+ require 'pricing'
3
3
 
4
4
  class Chef
5
5
  class Knife
@@ -73,10 +73,6 @@ class Chef
73
73
  end
74
74
  end
75
75
 
76
- def pricing
77
- @pricing ||= KnifeJoyent::Pricing::Config.new
78
- end
79
-
80
76
  def locate_config_value(key)
81
77
  key = key.to_sym
82
78
  config[key] || Chef::Config[:knife][key]
@@ -239,24 +239,28 @@ class Chef
239
239
  Chef::Log.debug("Bootstrap distro = #{config[:distro]}")
240
240
  bootstrap.config[:distro] = config[:distro]
241
241
 
242
- Chef::Log.debug("Bootstrap use_sudo = #{config[:use_sudo]}")
243
- bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root'
242
+ if config[:ssh_user] == 'root'
243
+ bootstrap.config[:use_sudo] = false
244
+ else
245
+ bootstrap.config[:use_sudo] = true
246
+ end
247
+
248
+ Chef::Log.debug("Bootstrap use_sudo = #{bootstrap.config[:use_sudo]}")
244
249
 
245
- Chef::Log.debug("Bootstrap environment = #{config[:environment]}")
246
250
  bootstrap.config[:environment] = config[:environment]
251
+ Chef::Log.debug("Bootstrap environment = #{bootstrap.config[:environment]}")
247
252
 
248
- Chef::Log.debug("Bootstrap no_host_key_verify = #{config[:no_host_key_verify]}")
249
253
  bootstrap.config[:no_host_key_verify] = config[:no_host_key_verify]
254
+ Chef::Log.debug("Bootstrap no_host_key_verify = #{bootstrap.config[:no_host_key_verify]}")
250
255
 
251
- Chef::Log.debug("Bootstrap identity_file = #{config[:identity_file]}")
252
256
  bootstrap.config[:identity_file] = config[:identity_file]
257
+ Chef::Log.debug("Bootstrap identity_file = #{bootstrap.config[:identity_file]}")
253
258
 
254
- Chef::Log.debug("Bootstrap ssh_gateway= #{config[:ssh_gateway]}")
255
259
  bootstrap.config[:ssh_gateway] = config[:ssh_gateway]
260
+ Chef::Log.debug("Bootstrap ssh_gateway= #{bootstrap.config[:ssh_gateway]}")
256
261
 
257
-
258
- Chef::Log.debug("Bootstrap json_attributes = #{config[:json_attributes]}")
259
262
  bootstrap.config[:first_boot_attributes] = config[:json_attributes]
263
+ Chef::Log.debug("Bootstrap json_attributes = #{bootstrap.config[:json_attributes]}")
260
264
 
261
265
  bootstrap
262
266
  end
@@ -6,11 +6,42 @@ class Chef
6
6
 
7
7
  include Knife::JoyentBase
8
8
 
9
+ option :show,
10
+ :long => '--show field1,field1,.',
11
+ :description => 'Show additional fields. Supported: compute_node, tags',
12
+ :proc => Proc.new {|key| Chef::Config[:knife][:show] = key.to_s.split(/,/).map(&:to_sym)}
13
+
14
+ option :sort,
15
+ :long => '--sort field',
16
+ :description => 'Sort by field, default is name, supported also: compute_node, price',
17
+ :proc => Proc.new {|key| Chef::Config[:knife][:sort] = key.to_sym}
18
+
9
19
  banner "knife joyent server list <options>"
10
20
 
21
+ ADDITIONAL_FIELDS = [:compute_node, :tags]
22
+
23
+ PRICE_COLUMN_WIDTH = 11
24
+
11
25
  def run
12
- columns = 11
13
- price_column_width = 11
26
+
27
+ sort_matrix = {
28
+ :name => lambda do |a, b|
29
+ (a.name || '') <=> (b.name || '')
30
+ end,
31
+ :compute_node => lambda do |a, b|
32
+ if a.attributes && b.attributes
33
+ (a.attributes["compute_node"]) <=> (b.attributes["compute_node"])
34
+ end
35
+ end,
36
+ :price => lambda do |a, b|
37
+ if a.package && b.package
38
+ pricing.monthly_price(a.package) <=> pricing.monthly_price(b.package)
39
+ end
40
+ end
41
+ }
42
+
43
+ columns = 10 + num_of_extra_keys
44
+
14
45
 
15
46
  servers = [
16
47
  ui.color('ID', :bold),
@@ -22,15 +53,22 @@ class Chef
22
53
  ui.color('IPs', :bold),
23
54
  ui.color(' RAM', :bold),
24
55
  ui.color(' Disk', :bold),
25
- ui.color('Price $/Month'),
26
- ui.color('Tags', :bold)
56
+ ui.color('Price $/Month')
27
57
  ]
28
58
 
29
- total_cost = 0
59
+ servers << ui.color('Compute Node', :bold) if show?(:compute_node)
60
+ servers << ui.color('Tags', :bold) if show?(:tags)
61
+
62
+ total_monthly_price = 0
63
+
64
+ prev_compute_node = nil # only needed if sorting by compute_node
65
+
66
+ self.connection.servers.sort(&sort_by(sort_matrix, sort_field)).each do |s|
67
+
68
+ compute_node = s.respond_to?(:attributes) ? s.attributes["compute_node"] : 'unknown'
69
+ columns.times { servers << "" } if sort_field == :compute_node && prev_compute_node && prev_compute_node != compute_node
70
+ prev_compute_node = compute_node
30
71
 
31
- self.connection.servers.sort do |a, b|
32
- (a.name || '') <=> (b.name || '')
33
- end.each do |s|
34
72
  servers << s.id.to_s
35
73
  servers << s.name
36
74
 
@@ -45,7 +83,7 @@ class Chef
45
83
  ui.color('unknown', :red)
46
84
  end
47
85
 
48
- flavor = s.respond_to?(:attributes) ? s.attributes["package"] : 'unknown'
86
+ flavor = s.package || 'unknown'
49
87
 
50
88
  servers << s.type
51
89
  servers << s.dataset
@@ -53,30 +91,47 @@ class Chef
53
91
  servers << s.ips.join(",")
54
92
  servers << "#{sprintf "%6.2f", s.memory/1024.0} GB"
55
93
  servers << "#{sprintf "%5.0f", s.disk/1024} GB"
56
- servers << pricing.monthly_formatted_price_for_flavor(flavor, price_column_width)
94
+ servers << pricing.format_monthly_price(flavor, PRICE_COLUMN_WIDTH)
57
95
 
58
- total_cost += (pricing[flavor] || 0)
96
+ servers << compute_node if show?(:compute_node)
97
+ servers << s.tags.map { |k, v| "#{k}:#{v}" }.join(' ') if (show?(:tags) && (s.tags rescue nil))
59
98
 
60
- if (s.tags rescue nil)
61
- servers << (show_tags? ? s.tags.map { |k, v| "#{k}:#{v}" }.join(' ') : "")
62
- else
63
- servers << "No Tags"
64
- end
99
+ total_monthly_price += pricing.monthly_price(flavor)
65
100
  end
66
101
 
67
- (columns - 3).times{servers << ""}
68
- servers << " Total"
69
- servers << pricing.formatted_price_for_value(total_cost * pricing.class::HOURS_PER_MONTH,
70
- price_column_width)
102
+ add_total_price(servers, total_monthly_price)
71
103
 
72
104
  puts ui.list(servers, :uneven_columns_across, columns)
73
105
  rescue => e
74
106
  output_error(e)
75
107
  end
76
108
 
77
- def show_tags?
78
- !ENV['SHOW_TAGS'].nil?
109
+ def add_total_price(servers, total_monthly_price)
110
+ 8.times { servers << "" }
111
+ servers << " Total"
112
+ servers << pricing.format_price(total_monthly_price, PRICE_COLUMN_WIDTH)
113
+ end
114
+
115
+ def show?(key)
116
+ (Chef::Config[:knife][:show] || []).include?(key)
79
117
  end
118
+
119
+ def num_of_extra_keys
120
+ ADDITIONAL_FIELDS.select{|k| show?(k)}.size
121
+ end
122
+
123
+ def sort_field
124
+ Chef::Config[:knife][:sort] || :name
125
+ end
126
+
127
+ def sort_by(matrix, field)
128
+ matrix[field.to_sym] || matrix[:name]
129
+ end
130
+
131
+ def pricing
132
+ @pricing ||= Joyent::Cloud::Pricing::Formatter.new(Joyent::Cloud::Pricing::Configuration.instance)
133
+ end
134
+
80
135
  end
81
136
  end
82
137
  end
@@ -0,0 +1,30 @@
1
+ require 'chef/knife/joyent_base'
2
+
3
+ class Chef
4
+ class Knife
5
+ class JoyentServerPricing < Knife
6
+
7
+ include Knife::JoyentBase
8
+
9
+ option :reserve_pricing,
10
+ :short => '-r <file>',
11
+ :long => '--reserve-pricing <file>',
12
+ :description => 'Apply reserve pricing to instances that qualify from a YAML file (see joyent-cloud-pricing gem)',
13
+ :proc => Proc.new { |key| Chef::Config[:knife][:reserve_pricing] = key }
14
+
15
+ banner "knife joyent server pricing [-r <reserve-pricing-configuration.yml> ]"
16
+
17
+ def run
18
+ flavors = []
19
+ self.connection.servers.each do |s|
20
+ flavor = s.package || 'unknown'
21
+ flavors << flavor
22
+ end
23
+ reporter = Joyent::Cloud::Pricing::Reporter.new(Chef::Config[:knife][:reserve_pricing], flavors)
24
+ puts reporter.render
25
+ rescue => e
26
+ output_error(e)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,3 +1,3 @@
1
1
  module KnifeJoyent
2
- VERSION = "0.4.5"
2
+ VERSION = "0.4.8"
3
3
  end
data/lib/knife-joyent.rb CHANGED
@@ -1,2 +1 @@
1
1
  require 'knife-joyent/version'
2
- require 'knife-joyent/pricing'
data/spec/spec_helper.rb CHANGED
@@ -8,7 +8,6 @@
8
8
  require 'knife-joyent'
9
9
 
10
10
  RSpec.configure do |config|
11
- config.treat_symbols_as_metadata_keys_with_true_values = true
12
11
  config.run_all_when_everything_filtered = true
13
12
  config.filter_run :focus
14
13
 
metadata CHANGED
@@ -1,32 +1,36 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: knife-joyent
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.5
4
+ version: 0.4.8
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Kevin Chan
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2014-03-04 00:00:00.000000000 Z
12
+ date: 2014-03-30 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: fog
15
16
  requirement: !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
19
  - - ~>
18
20
  - !ruby/object:Gem::Version
19
- version: 1.15.0
21
+ version: 1.21.0
20
22
  type: :runtime
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
23
26
  requirements:
24
27
  - - ~>
25
28
  - !ruby/object:Gem::Version
26
- version: 1.15.0
29
+ version: 1.21.0
27
30
  - !ruby/object:Gem::Dependency
28
31
  name: multi_json
29
32
  requirement: !ruby/object:Gem::Requirement
33
+ none: false
30
34
  requirements:
31
35
  - - ~>
32
36
  - !ruby/object:Gem::Version
@@ -34,6 +38,7 @@ dependencies:
34
38
  type: :runtime
35
39
  prerelease: false
36
40
  version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
37
42
  requirements:
38
43
  - - ~>
39
44
  - !ruby/object:Gem::Version
@@ -41,43 +46,65 @@ dependencies:
41
46
  - !ruby/object:Gem::Dependency
42
47
  name: chef
43
48
  requirement: !ruby/object:Gem::Requirement
49
+ none: false
44
50
  requirements:
45
- - - '>='
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '11.6'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '11.6'
62
+ - !ruby/object:Gem::Dependency
63
+ name: joyent-cloud-pricing
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
46
68
  - !ruby/object:Gem::Version
47
- version: 0.10.10
69
+ version: 1.0.3
48
70
  type: :runtime
49
71
  prerelease: false
50
72
  version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
51
74
  requirements:
52
- - - '>='
75
+ - - ! '>='
53
76
  - !ruby/object:Gem::Version
54
- version: 0.10.10
77
+ version: 1.0.3
55
78
  - !ruby/object:Gem::Dependency
56
79
  name: rspec
57
80
  requirement: !ruby/object:Gem::Requirement
81
+ none: false
58
82
  requirements:
59
- - - '>='
83
+ - - ! '>='
60
84
  - !ruby/object:Gem::Version
61
85
  version: '0'
62
86
  type: :development
63
87
  prerelease: false
64
88
  version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
65
90
  requirements:
66
- - - '>='
91
+ - - ! '>='
67
92
  - !ruby/object:Gem::Version
68
93
  version: '0'
69
94
  - !ruby/object:Gem::Dependency
70
95
  name: guard-rspec
71
96
  requirement: !ruby/object:Gem::Requirement
97
+ none: false
72
98
  requirements:
73
- - - '>='
99
+ - - ! '>='
74
100
  - !ruby/object:Gem::Version
75
101
  version: '0'
76
102
  type: :development
77
103
  prerelease: false
78
104
  version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
79
106
  requirements:
80
- - - '>='
107
+ - - ! '>='
81
108
  - !ruby/object:Gem::Version
82
109
  version: '0'
83
110
  description: Joyent CloudAPI Support for Chef's Knife Command
@@ -119,6 +146,7 @@ files:
119
146
  - lib/chef/knife/joyent_server_list.rb
120
147
  - lib/chef/knife/joyent_server_metadata_delete.rb
121
148
  - lib/chef/knife/joyent_server_metadata_update.rb
149
+ - lib/chef/knife/joyent_server_pricing.rb
122
150
  - lib/chef/knife/joyent_server_reboot.rb
123
151
  - lib/chef/knife/joyent_server_resize.rb
124
152
  - lib/chef/knife/joyent_server_start.rb
@@ -131,35 +159,31 @@ files:
131
159
  - lib/chef/knife/joyent_tag_list.rb
132
160
  - lib/knife-joyent.rb
133
161
  - lib/knife-joyent/fw.rb
134
- - lib/knife-joyent/pricing.rb
135
- - lib/knife-joyent/pricing/config.rb
136
162
  - lib/knife-joyent/version.rb
137
- - spec/pricing/config_spec.rb
138
163
  - spec/spec_helper.rb
139
164
  homepage: https://github.com/kevinykchan/knife-joyent
140
165
  licenses: []
141
- metadata: {}
142
166
  post_install_message:
143
167
  rdoc_options: []
144
168
  require_paths:
145
169
  - lib
146
170
  required_ruby_version: !ruby/object:Gem::Requirement
171
+ none: false
147
172
  requirements:
148
- - - '>='
173
+ - - ! '>='
149
174
  - !ruby/object:Gem::Version
150
175
  version: '0'
151
176
  required_rubygems_version: !ruby/object:Gem::Requirement
177
+ none: false
152
178
  requirements:
153
- - - '>='
179
+ - - ! '>='
154
180
  - !ruby/object:Gem::Version
155
181
  version: '0'
156
182
  requirements: []
157
183
  rubyforge_project:
158
- rubygems_version: 2.0.5
184
+ rubygems_version: 1.8.23
159
185
  signing_key:
160
- specification_version: 4
186
+ specification_version: 3
161
187
  summary: Joyent CloudAPI Support for Chef's Knife Command
162
188
  test_files:
163
- - spec/pricing/config_spec.rb
164
189
  - spec/spec_helper.rb
165
- has_rdoc: true
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: 06f8a691e8fcef6cd3a29f19b6c1e0b690b600ba
4
- data.tar.gz: 1b03a7c634502edc8a0d3b150772c71a3f342073
5
- SHA512:
6
- metadata.gz: 3e1780a45058aa5788c6076209a4e4ab4c87de2dfad622a10efd79a32208b2d8d6a3145cf1f1754a52e089eeef7015813d38877e986df743999cf7ef5c6f9bf3
7
- data.tar.gz: 71668684a9418d201ea0029f15f17879bf2a95eadcc89b7ff8986e28e9913b7ca0b1d426bc19c78e679e99916e0705827a928062fcf6c88efe9a1f041b419ece
@@ -1,62 +0,0 @@
1
- require 'open-uri'
2
- require 'nokogiri'
3
-
4
- module KnifeJoyent
5
- module Pricing
6
- class Config < ::Hash
7
- JOYENT_URL = "http://www.joyent.com/products/compute-service/pricing"
8
- HOURS_PER_MONTH = 720
9
-
10
- def initialize
11
- super
12
- from_uri
13
- end
14
-
15
- def from_uri(uri = JOYENT_URL)
16
- parse_html_document Nokogiri::HTML(open(uri))
17
- rescue
18
- end
19
-
20
- def from_html_file filename
21
- parse_html_document Nokogiri::HTML(File.read(filename))
22
- end
23
-
24
- def monthly_price_for_flavor(flavor_name)
25
- self[flavor_name] ? sprintf("$%.2f", self[flavor_name] * HOURS_PER_MONTH) : ""
26
- end
27
-
28
- def monthly_formatted_price_for_flavor(flavor, width = 10)
29
- self[flavor] ? formatted_price_for_value(self[flavor] * HOURS_PER_MONTH, width) : ""
30
- end
31
-
32
- def formatted_price_for_value(value, width = 10)
33
- sprintf("%#{width}s", currency_format(sprintf("$%.2f", value)))
34
- end
35
-
36
- # Returns string formatted with commas in the middle, such as "9,999,999"
37
- def currency_format string
38
- while string.sub!(/(\d+)(\d\d\d)/,'\1,\2'); end
39
- string
40
- end
41
-
42
- private
43
-
44
- def parse_html_document doc
45
- mappings = Hash.new
46
- specs = doc.css("ul.full-specs")
47
- specs.each do |ul|
48
- lis = ul.css("span").map(&:content)
49
- # grab last two <li> elements in each <ul class="full-spec"> block
50
- os, cost, flavor = lis[-3], lis[-2].gsub(/^\$/, ''), lis[-1]
51
- next if cost == "N/A"
52
- next if flavor =~ /kvm/ && os !~ /linux/i
53
-
54
- mappings[flavor] = cost.to_f
55
- end
56
-
57
- self.merge! mappings
58
- end
59
- end
60
- end
61
- end
62
-
@@ -1 +0,0 @@
1
- require_relative 'pricing/config'
@@ -1,42 +0,0 @@
1
- require_relative '../spec_helper'
2
-
3
-
4
- describe KnifeJoyent::Pricing::Config do
5
- let(:config) { KnifeJoyent::Pricing::Config.new() }
6
-
7
- let(:prices) { {"g3-standard-48-smartos" => 1.536,
8
- "g3-standard-0.625-smartos" => 0.02,
9
- "g3-standard-30-kvm" => 0.960} }
10
-
11
- def verify
12
- prices.keys.each do |flavor|
13
- config[flavor].should eql(prices[flavor])
14
- end
15
- end
16
-
17
- it "should load pricing configuration hash from Joyent Website" do
18
- config.from_uri
19
- verify
20
- end
21
-
22
- context "#monthly_price_for_flavor" do
23
- it "should return properly formatted monthly price" do
24
- expect(config.monthly_price_for_flavor "g3-standard-0.625-smartos").to eql("$14.40")
25
- expect(config.monthly_price_for_flavor "g3-standard-30-kvm").to eql("$691.20")
26
- end
27
- end
28
- context "#monthly_formatted_price_for_flavor" do
29
- it "should return properly formatted monthly price" do
30
- expect(config.monthly_formatted_price_for_flavor "g3-standard-48-smartos").to eql(" $1,105.92")
31
- end
32
- it "should return blank when no match was found" do
33
- expect(config.monthly_formatted_price_for_flavor "asdfkasdfasdlfkjasl;dkjf").to eql("")
34
- end
35
- end
36
- context "#formatted_price_for_value" do
37
- it "should return properly formatted price" do
38
- expect(config.formatted_price_for_value 24566.34).to eql("$24,566.34")
39
- expect(config.formatted_price_for_value 4566.34).to eql(" $4,566.34")
40
- end
41
- end
42
- end