knife-joyent 0.3.6 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 97840b8a3ec364a32ca0735ec200d7f0a0823351
4
- data.tar.gz: 298e8434baa3a3c1d469fa0a39b748c3f04af02f
3
+ metadata.gz: 45293468ec017cf7ebb0f58f4a0d3a95531dad56
4
+ data.tar.gz: bfe1d3c0b2da13606dc83038fb815ca2e7dd0532
5
5
  SHA512:
6
- metadata.gz: b24792faf5a1d8b15c6e1ba29630a5f1213fe95872d863b94664dc52f0de3017204b117916584b4d2df4044378ac2d1e6670987c5f4212fd09088eb17079f0b0
7
- data.tar.gz: 16b9f5f771f78863116eb109397c467e459b348ca0e3b1d27da71c3bd711570575f617c9a930ab503319566fd859ab707f4c6ff361d4d494ebf9144ccb2a7da2
6
+ metadata.gz: 97084d24162cf1f927515cd3b64a097fed154c04b9c4937a1df969aa2f329e5fbdfd69c78f8e07320409a0ced22d26585ab2a41f44c896e55ac199d5216e9784
7
+ data.tar.gz: 58c05b85366ac511235eb3bd10847694333f0e17ca3bb595254505bcf743c578b1dd515dea310181aa92afe0b6fc46ee247b6ee11a43380354996f8d782d1ef7
data/README.md CHANGED
@@ -43,17 +43,26 @@ Currently available commands:
43
43
  knife joyent tag delete <server_id> -A
44
44
  knife joyent tag list <server_id>
45
45
 
46
+ # requires joyent_version 7.1+
47
+ knife joyent server fw enable <server_id>
48
+ knife joyent server fw disable <server_id>
49
+ knife joyent fw get <fwrule_id>
50
+ knife joyent fw create (options)
51
+ knife joyent fw list <server_id>
52
+ knife joyent fw update <rule_id> (options)
53
+ knife joyent fw delete <rule_id> (options)
54
+
46
55
  ## Example Usage
47
56
 
48
57
  The following command will provision an Ubuntu 12.04 with 1GB of memory and bootstrap it with chef
49
58
 
50
59
  # knife joyent server create \
51
- > --joyent-api-version '~7.0' \
52
- > --flavor "Small 1GB" \
53
- > --networks 42325ea0-eb62-44c1-8eb6-0af3e2f83abc,c8cde927-6277-49ca-82a3-741e8b23b02f \
54
- > --image d2ba0f30-bbe8-11e2-a9a2-6bc116856d85 \
55
- > --node-name 'cookbuntu0' \
56
- > --server-name 'cookbuntu0'
60
+ --joyent-api-version '~7.0' \
61
+ --flavor "Small 1GB" \
62
+ --networks 42325ea0-eb62-44c1-8eb6-0af3e2f83abc,c8cde927-6277-49ca-82a3-741e8b23b02f \
63
+ --image d2ba0f30-bbe8-11e2-a9a2-6bc116856d85 \
64
+ --node-name 'cookbuntu0' \
65
+ --server-name 'cookbuntu0'
57
66
 
58
67
  Creating machine cookbuntu0
59
68
  Waiting for Server to be Provisioned
@@ -111,7 +120,7 @@ to automatically unlock the key for authentication.
111
120
  **``joyent_api_url``**
112
121
 
113
122
  Specifies a custom CloudAPI endpoint, this is required if you want to manage
114
- machines located in another datacenter or if you want to interface with any CloudAPI
123
+ machines located in another datacenter or if you want to interface with any CloudAPI
115
124
  instance powered by [SmartDataCenter](http://www.joyent.com/products/smartdatacenter/).
116
125
 
117
126
  Defaults to us-west-1
@@ -141,7 +150,7 @@ the form of a hash with a single level of nesting. See the
141
150
  By default, knife-joyent will use the version of the Joyent Cloud API that fog prefers. This
142
151
  can be overridden in knife.rb as follows:
143
152
 
144
- knife[:joyent_version] = "~7.0"
153
+ knife[:joyent_version] = "~7.1"
145
154
 
146
155
  Some command line options to knife-joyent subcommands may depend on the Joyent API version set.
147
156
 
@@ -82,6 +82,19 @@ class Chef
82
82
  config[key] || Chef::Config[:knife][key]
83
83
  end
84
84
 
85
+ def output_error_response(res)
86
+ if (res.body['message'])
87
+ ui.error ui.color(res.body["message"], :white)
88
+ end
89
+
90
+ if (res.body["errors"])
91
+ errors = res.body["errors"]
92
+ errors.each do |e|
93
+ ui.error("[#{e["field"]}] #{e["message"]}")
94
+ end
95
+ end
96
+ end
97
+
85
98
  def msg_pair(label, value = nil)
86
99
  if value && !value.empty?
87
100
  puts "#{ui.color(label, :cyan)}: #{value}"
@@ -9,13 +9,12 @@ class Chef
9
9
  banner "knife joyent flavor list <options>"
10
10
 
11
11
  def run
12
-
13
12
  flavor_list = [
14
13
  ui.color('Name', :bold),
15
14
  ui.color(' RAM', :bold),
16
15
  ui.color(' Disk', :bold),
17
16
  ui.color(' Swap', :bold),
18
- ui.color('Price $/Hr', :bold),
17
+ ui.color('$ Per Month', :bold),
19
18
  ]
20
19
 
21
20
  self.connection.flavors.sort_by(&:memory).each do |flavor|
@@ -23,7 +22,7 @@ class Chef
23
22
  flavor_list << "#{sprintf "%6.2f", flavor.memory/1024.0} GB"
24
23
  flavor_list << "#{sprintf "%6.0f", flavor.disk/1024.0} GB"
25
24
  flavor_list << "#{sprintf "%5.0f", flavor.swap/1024.0} GB"
26
- flavor_list << (pricing[flavor.name.to_s] ? sprintf("$%.3f", pricing[flavor.name.to_s]) : "")
25
+ flavor_list << pricing.monthly_formatted_price_for_flavor(flavor.name.to_s, 10)
27
26
  end
28
27
 
29
28
  puts ui.list(flavor_list, :uneven_columns_across, 5)
@@ -31,3 +30,4 @@ class Chef
31
30
  end
32
31
  end
33
32
  end
33
+
@@ -0,0 +1,48 @@
1
+ require 'chef/knife/joyent_base'
2
+ require 'pp'
3
+
4
+ class Chef
5
+ class Knife
6
+ class JoyentFwCreate < Knife
7
+
8
+ include Knife::JoyentBase
9
+
10
+ banner "knife joyent fw create (options)"
11
+
12
+ option :rule,
13
+ :long => "--rule RULE",
14
+ :description => "Firewall Rule Content"
15
+
16
+ option :enabled,
17
+ :long => "--enabled",
18
+ :boolean => true,
19
+ :description => "Enable/Disable Rule"
20
+
21
+ def run
22
+ unless config[:rule]
23
+ show_usage
24
+ exit 1
25
+ end
26
+
27
+ res = self.connection.request(
28
+ :method => "POST",
29
+ :path => "/my/fwrules",
30
+ :body => {
31
+ "enabled" => config[:enabled],
32
+ "rule" => config[:rule],
33
+ }
34
+ )
35
+
36
+ unless res.status == 201
37
+ output_error_response(res)
38
+ else
39
+ r = res.body
40
+
41
+ ui.info "Created Firewall Rule: #{r["id"]}"
42
+ msg_pair "RULE", r["rule"]
43
+ msg_pair "ENABLED", (r["enabled"] ? ui.color("✓ YES", :cyan) : "✗ NO")
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,39 @@
1
+ require 'chef/knife/joyent_base'
2
+
3
+ class Chef
4
+ class Knife
5
+ class JoyentFwDelete < Knife
6
+
7
+ include Knife::JoyentBase
8
+
9
+ banner "knife joyent fw delete <fw_id>"
10
+
11
+ def run
12
+ unless name_args.size === 1
13
+ show_usage
14
+ exit 1
15
+ end
16
+
17
+ id = name_args.first
18
+ # puts id
19
+ res = self.connection.request(
20
+ :method => "DELETE",
21
+ :path => "/my/fwrules/#{id}"
22
+ )
23
+
24
+ rules = [
25
+ ui.color('ID', :bold),
26
+ ui.color('Enabled', :bold),
27
+ ui.color('Rule', :bold),
28
+ ]
29
+
30
+ if res.status == 204
31
+ ui.info "Rule #{id} Deleted."
32
+ else
33
+ self.output_error_response(res)
34
+ end
35
+
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,43 @@
1
+ require 'chef/knife/joyent_base'
2
+
3
+ class Chef
4
+ class Knife
5
+ class JoyentFwGet < Knife
6
+
7
+ include Knife::JoyentBase
8
+
9
+ banner "knife joyent fw get <fw_id>"
10
+
11
+ def run
12
+ unless name_args.size === 1
13
+ show_usage
14
+ exit 1
15
+ end
16
+
17
+ id = name_args.first
18
+ # puts id
19
+ res = self.connection.request(
20
+ :method => "GET",
21
+ :path => "/my/fwrules/#{id}"
22
+ )
23
+
24
+ rules = [
25
+ ui.color('ID', :bold),
26
+ ui.color('Enabled', :bold),
27
+ ui.color('Rule', :bold),
28
+ ]
29
+
30
+ if (res.status == 422)
31
+ output_error_response(res)
32
+ else
33
+ r = res.body
34
+ rules << r["id"]
35
+ rules << (r["enabled"] ? ui.color("✓", :cyan) : "✗")
36
+ rules << r["rule"]
37
+ ui.list(rules, :uneven_columns_across, 3)
38
+ end
39
+
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,43 @@
1
+ require 'chef/knife/joyent_base'
2
+
3
+ class Chef
4
+ class Knife
5
+ class JoyentFwList < Knife
6
+
7
+ include Knife::JoyentBase
8
+
9
+ banner "knife joyent fw list"
10
+
11
+ def run
12
+ if name_args.size > 0
13
+ id = name_args.first
14
+ res = self.connection.request(
15
+ :method => "GET",
16
+ :path => "/my/machines/#{id}/fwrules"
17
+ )
18
+ else
19
+ res = self.connection.request(
20
+ :method => "GET",
21
+ :path => "/my/fwrules"
22
+ )
23
+ end
24
+
25
+ if res.status == 200
26
+ rules = [
27
+ ui.color('ID', :bold),
28
+ ui.color('Enabled', :bold),
29
+ ui.color('Rule', :bold),
30
+ ]
31
+
32
+ res[:body].each do |r|
33
+ rules << r["id"]
34
+ rules << (r["enabled"] ? ui.color("✓", :cyan) : "✗")
35
+ rules << r["rule"]
36
+ end
37
+ else
38
+ output_error_response(res)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,49 @@
1
+ require 'chef/knife/joyent_base'
2
+ require 'pp'
3
+
4
+ class Chef
5
+ class Knife
6
+ class JoyentFwUpdate < Knife
7
+
8
+ include Knife::JoyentBase
9
+
10
+ banner "knife joyent fw update <rule_id> (options)"
11
+
12
+ option :rule,
13
+ :long => "--rule RULE",
14
+ :description => "Firewall Rule Content"
15
+
16
+ option :enabled,
17
+ :long => "--enabled",
18
+ :boolean => true,
19
+ :description => "Enable/Disable Rule"
20
+
21
+ def run
22
+ id = name_args.first
23
+ unless id || (!config.key?(:rule) || !config.key?(:enabled))
24
+ show_usage
25
+ exit 1
26
+ end
27
+
28
+ res = self.connection.request(
29
+ :method => "POST",
30
+ :path => "/my/fwrules/#{id}",
31
+ :body => {
32
+ "enabled" => config[:enabled],
33
+ "rule" => config[:rule],
34
+ }
35
+ )
36
+
37
+ unless res.status == 200
38
+ output_error_response(res)
39
+ else
40
+ r = res.body
41
+
42
+ ui.info "Created Firewall Rule: #{r["id"]}"
43
+ msg_pair "RULE", r["rule"]
44
+ msg_pair "ENABLED", (r["enabled"] ? ui.color("✓ YES", :cyan) : "✗ NO")
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,34 @@
1
+ require 'chef/knife/joyent_base'
2
+
3
+ class Chef
4
+ class Knife
5
+ class JoyentServerFwDisable < Knife
6
+
7
+ include Knife::JoyentBase
8
+
9
+ banner "knife joyent server fw disable <server_id>"
10
+
11
+ def run
12
+ unless name_args.size === 1
13
+ show_usage
14
+ exit 1
15
+ end
16
+
17
+ id = name_args.first
18
+
19
+ path = "/my/machines/#{id}"
20
+ res = self.connection.request(
21
+ :method => "POST",
22
+ :path => path,
23
+ :query => {"action" => "disable_firewall"}
24
+ )
25
+
26
+ if (res.status == 202)
27
+ puts ui.color("Firewall Disabled for server #{id}", :cyan)
28
+ else
29
+ output_error_response(res)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,33 @@
1
+ require 'chef/knife/joyent_base'
2
+
3
+ class Chef
4
+ class Knife
5
+ class JoyentServerFwEnable < Knife
6
+
7
+ include Knife::JoyentBase
8
+
9
+ banner "knife joyent server fw enable <server_id>"
10
+
11
+ def run
12
+ unless name_args.size === 1
13
+ show_usage
14
+ exit 1
15
+ end
16
+
17
+ id = name_args.first
18
+ # puts id
19
+ res = self.connection.request(
20
+ :method => "POST",
21
+ :path => "/my/machines/#{id}",
22
+ :query => {"action" => "enable_firewall"}
23
+ )
24
+
25
+ if (res.status === 202)
26
+ ui.info ui.color("Firewall Enabled for server #{id}", :cyan)
27
+ else
28
+ output_error_response(res)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -9,6 +9,9 @@ class Chef
9
9
  banner "knife joyent server list <options>"
10
10
 
11
11
  def run
12
+ columns = 11
13
+ price_column_width = 11
14
+
12
15
  servers = [
13
16
  ui.color('ID', :bold),
14
17
  ui.color('Name', :bold),
@@ -19,9 +22,12 @@ class Chef
19
22
  ui.color('IPs', :bold),
20
23
  ui.color(' RAM', :bold),
21
24
  ui.color(' Disk', :bold),
25
+ ui.color('Price $/Month'),
22
26
  ui.color('Tags', :bold)
23
27
  ]
24
28
 
29
+ total_cost = 0
30
+
25
31
  self.connection.servers.sort do |a, b|
26
32
  (a.name || '') <=> (b.name || '')
27
33
  end.each do |s|
@@ -29,23 +35,27 @@ class Chef
29
35
  servers << s.name
30
36
 
31
37
  servers << case s.state
32
- when 'running'
33
- ui.color(s.state, :green)
34
- when 'stopping', 'provisioning'
35
- ui.color(s.state, :yellow)
36
- when 'stopped'
37
- ui.color(s.state, :red)
38
- else
39
- ui.color('unknown', :red)
40
- end
38
+ when 'running'
39
+ ui.color(s.state, :green)
40
+ when 'stopping', 'provisioning'
41
+ ui.color(s.state, :yellow)
42
+ when 'stopped'
43
+ ui.color(s.state, :red)
44
+ else
45
+ ui.color('unknown', :red)
46
+ end
47
+
48
+ flavor = s.respond_to?(:attributes) ? s.attributes["package"] : 'unknown'
41
49
 
42
50
  servers << s.type
43
51
  servers << s.dataset
44
- servers << (s.respond_to?(:attributes) ? s.attributes["package"] : 'unknown')
52
+ servers << flavor
45
53
  servers << s.ips.join(",")
46
54
  servers << "#{sprintf "%6.2f", s.memory/1024.0} GB"
47
55
  servers << "#{sprintf "%5.0f", s.disk/1024} GB"
56
+ servers << pricing.monthly_formatted_price_for_flavor(flavor, price_column_width)
48
57
 
58
+ total_cost += (pricing[flavor] || 0)
49
59
 
50
60
  if (s.tags rescue nil)
51
61
  servers << (show_tags? ? s.tags.map { |k, v| "#{k}:#{v}" }.join(' ') : "")
@@ -54,7 +64,12 @@ class Chef
54
64
  end
55
65
  end
56
66
 
57
- puts ui.list(servers, :uneven_columns_across, 10)
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)
71
+
72
+ puts ui.list(servers, :uneven_columns_across, columns)
58
73
  end
59
74
 
60
75
  def show_tags?
File without changes
@@ -5,6 +5,7 @@ module KnifeJoyent
5
5
  module Pricing
6
6
  class Config < ::Hash
7
7
  JOYENT_URL = "http://www.joyent.com/products/compute-service/pricing"
8
+ HOURS_PER_MONTH = 720
8
9
 
9
10
  def initialize
10
11
  super
@@ -20,6 +21,24 @@ module KnifeJoyent
20
21
  parse_html_document Nokogiri::HTML(File.read(filename))
21
22
  end
22
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
+
23
42
  private
24
43
 
25
44
  def parse_html_document doc
@@ -40,3 +59,4 @@ module KnifeJoyent
40
59
  end
41
60
  end
42
61
  end
62
+
@@ -1,3 +1,3 @@
1
1
  module KnifeJoyent
2
- VERSION = "0.3.6"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -1,26 +1,42 @@
1
1
  require_relative '../spec_helper'
2
2
 
3
3
 
4
-
5
4
  describe KnifeJoyent::Pricing::Config do
6
- context "URL Scraper" do
7
-
8
- let(:config) { KnifeJoyent::Pricing::Config.new() }
5
+ let(:config) { KnifeJoyent::Pricing::Config.new() }
9
6
 
10
- let(:prices) { { "g3-standard-48-smartos" => 1.536,
11
- "g3-standard-0.625-smartos" => 0.02,
12
- "g3-standard-30-kvm" => 0.960 } }
7
+ let(:prices) { {"g3-standard-48-smartos" => 1.536,
8
+ "g3-standard-0.625-smartos" => 0.02,
9
+ "g3-standard-30-kvm" => 0.960} }
13
10
 
14
- def verify
15
- prices.keys.each do |flavor|
16
- config[flavor].should eql(prices[flavor])
17
- end
11
+ def verify
12
+ prices.keys.each do |flavor|
13
+ config[flavor].should eql(prices[flavor])
18
14
  end
15
+ end
19
16
 
20
- it "should load pricing configuration hash from Joyent Website" do
21
- config.from_uri
22
- verify
23
- end
17
+ it "should load pricing configuration hash from Joyent Website" do
18
+ config.from_uri
19
+ verify
20
+ end
24
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
25
41
  end
26
42
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: knife-joyent
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.6
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Chan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-11-19 00:00:00.000000000 Z
11
+ date: 2014-01-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fog
@@ -100,6 +100,11 @@ files:
100
100
  - knife-joyent.gemspec
101
101
  - lib/chef/knife/joyent_base.rb
102
102
  - lib/chef/knife/joyent_flavor_list.rb
103
+ - lib/chef/knife/joyent_fw_create.rb
104
+ - lib/chef/knife/joyent_fw_delete.rb
105
+ - lib/chef/knife/joyent_fw_get.rb
106
+ - lib/chef/knife/joyent_fw_list.rb
107
+ - lib/chef/knife/joyent_fw_update.rb
103
108
  - lib/chef/knife/joyent_image_list.rb
104
109
  - lib/chef/knife/joyent_key_add.rb
105
110
  - lib/chef/knife/joyent_key_delete.rb
@@ -107,6 +112,8 @@ files:
107
112
  - lib/chef/knife/joyent_network_list.rb
108
113
  - lib/chef/knife/joyent_server_create.rb
109
114
  - lib/chef/knife/joyent_server_delete.rb
115
+ - lib/chef/knife/joyent_server_fw_disable.rb
116
+ - lib/chef/knife/joyent_server_fw_enable.rb
110
117
  - lib/chef/knife/joyent_server_list.rb
111
118
  - lib/chef/knife/joyent_server_metadata_delete.rb
112
119
  - lib/chef/knife/joyent_server_metadata_update.rb
@@ -121,6 +128,7 @@ files:
121
128
  - lib/chef/knife/joyent_tag_delete.rb
122
129
  - lib/chef/knife/joyent_tag_list.rb
123
130
  - lib/knife-joyent.rb
131
+ - lib/knife-joyent/fw.rb
124
132
  - lib/knife-joyent/pricing.rb
125
133
  - lib/knife-joyent/pricing/config.rb
126
134
  - lib/knife-joyent/version.rb