razor-client 0.15.0 → 0.15.1

Sign up to get free protection for your applications and to get access to all the features.
data/NEWS.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Razor Client Release Notes
2
2
 
3
+ ## 0.15.1 - 2014-06-12
4
+
5
+ Server version compatibility
6
+
7
+ * It is highly recommended that razor-client version 0.15.x be used with
8
+ razor-server version 0.15.x or higher.
9
+
3
10
  ## 0.15.0 - 2014-05-22
4
11
 
5
12
  Usability of the client has been greatly enhanced:
@@ -19,20 +19,20 @@ module Razor::CLI
19
19
  end
20
20
 
21
21
  def format_document(doc, parse = nil)
22
- format = parse.format
23
- arguments = parse.args
22
+ format = parse && parse.format
23
+ arguments = parse && parse.args
24
24
  doc = Razor::CLI::Document.new(doc, format)
25
25
 
26
26
  return "There are no items for this query." if doc.items.empty?
27
- return format_objects(doc.items).chomp if parse.show_command_help?
27
+ return format_objects(doc.items).chomp if parse && parse.show_command_help?
28
28
 
29
29
  case (doc.format_view['+layout'] or 'list')
30
30
  when 'list'
31
- format_objects(doc.items) + String(additional_details(doc, arguments)).chomp
31
+ format_objects(doc.items) + String(additional_details(doc, parse, arguments)).chomp
32
32
  when 'table'
33
33
  case doc.items
34
34
  when Array then
35
- get_table(doc.items, doc.format_view) + String(additional_details(doc, arguments))
35
+ get_table(doc.items, doc.format_view) + String(additional_details(doc, parse, arguments))
36
36
  else doc.to_s
37
37
  end
38
38
  else
@@ -43,18 +43,27 @@ module Razor::CLI
43
43
  private
44
44
  def get_table(doc, formatting)
45
45
  # Use the formatting if it exists, otherwise build from the data.
46
- headings = (formatting['+show'] and formatting['+show'].keys or [])
47
- Terminal::Table.new do |t|
48
- t.rows = doc.map do |page|
49
- page.map do |item|
50
- headings << item[0] unless headings.include? item[0]
51
- item[1]
46
+ headings = (formatting['+show'] and formatting['+show'].keys or get_headers(doc))
47
+ Terminal::Table.new do |table|
48
+ table.rows = doc.map do |page|
49
+ headings.map do |heading|
50
+ page[heading]
52
51
  end
53
52
  end
54
- t.headings = headings
53
+ table.headings = headings
55
54
  end.to_s
56
55
  end
57
56
 
57
+ def get_headers(doc)
58
+ [].tap do |headers|
59
+ doc.map do |page|
60
+ page.map do |item|
61
+ headers << item[0] unless headers.include?(item[0])
62
+ end
63
+ end
64
+ end
65
+ end
66
+
58
67
  # We assume that all collections are homogenous
59
68
  def format_objects(objects, indent = 0)
60
69
  objects.map do |obj|
@@ -106,10 +115,12 @@ module Razor::CLI
106
115
  (PriorityKeys & keys) + (keys - PriorityKeys) - ['+spec']
107
116
  end
108
117
 
109
- def additional_details(doc, arguments)
118
+ def additional_details(doc, parse, arguments)
110
119
  objects = doc.original_items
111
- # If every element has the 'name' key, it has nested elements.
112
- if doc.is_list? and objects.all? { |it| it.is_a?(Hash) && it.has_key?('name')}
120
+ if objects.empty? or (parse and not parse.query?)
121
+ ""
122
+ elsif doc.is_list? and objects.all? { |it| it.is_a?(Hash) && it.has_key?('name')}
123
+ # If every element has the 'name' key, it has nested elements.
113
124
  "\n\nQuery an entry by including its name, e.g. `razor #{arguments.join(' ')} #{objects.first['name']}`"
114
125
  elsif objects.any?
115
126
  object = objects.first
@@ -35,11 +35,11 @@ module Razor::CLI
35
35
  end
36
36
 
37
37
  def query?
38
- collections.any? { |coll| coll["name"] == @segments.first }
38
+ @query ||= collections.any? { |coll| coll["name"] == @segments.first }
39
39
  end
40
40
 
41
41
  def command(name)
42
- commands.find { |coll| coll["name"] == name }
42
+ @command ||= commands.find { |coll| coll["name"] == name }
43
43
  end
44
44
 
45
45
  def command?
@@ -102,7 +102,7 @@ module Razor::CLI
102
102
  # `--arg=value` or `--arg value`
103
103
  arg, value = [$1, $3]
104
104
  value = @segments.shift if value.nil? && @segments[0] !~ /^--/
105
- if value =~ /\A([a-zA-Z._-]+)=(\S+)?\z/
105
+ if value =~ /\A(.+?)=(\S+)?\z/
106
106
  # `--arg name=value`
107
107
  unless body[arg].nil? or body[arg].is_a?(Hash)
108
108
  # Error: `--arg value --arg name=value`
@@ -214,11 +214,16 @@ module Razor::CLI
214
214
 
215
215
  private
216
216
  def cmd_schema(cmd_name)
217
- cmd = json_get(@cmd_url)
218
- cmd['schema'] or raise VersionCompatibilityError, 'Server must supply the expected datatypes for command arguments'
217
+ begin
218
+ json_get(@cmd_url)['schema']
219
+ rescue RestClient::ResourceNotFound => _
220
+ raise VersionCompatibilityError, 'Server must supply the expected datatypes for command arguments; use `--json` or upgrade razor-server'
221
+ end
219
222
  end
220
223
 
221
224
  def arg_type(cmd_name, arg_name)
225
+ # Short-circuit to allow this as a work-around for backwards compatibility.
226
+ return nil if arg_name == 'json'
222
227
  cmd = cmd_schema(cmd_name)
223
228
  cmd && cmd[arg_name] && cmd[arg_name]['type'] or nil
224
229
  end
@@ -1,11 +1,15 @@
1
1
  require 'uri'
2
2
  require 'optparse'
3
+ require 'forwardable'
3
4
 
4
5
  module Razor::CLI
5
6
 
6
7
  class Parse
8
+ extend Forwardable
7
9
  DEFAULT_RAZOR_API = "http://localhost:8080/api"
8
10
 
11
+ def_delegator 'navigate', 'query?'
12
+
9
13
  def get_optparse
10
14
  @optparse ||= OptionParser.new do |opts|
11
15
  opts.banner = "Usage: razor [FLAGS] NAVIGATION\n"
@@ -35,5 +35,8 @@ module Razor::CLI
35
35
  def count(arr)
36
36
  arr.size
37
37
  end
38
+ def count_hash(hash)
39
+ hash.is_a?(Hash) ? hash.keys.size : 0
40
+ end
38
41
  end
39
42
  end
@@ -18,7 +18,7 @@ module Razor
18
18
  #
19
19
  # The next line is the one that our packaging tools modify, so please make
20
20
  # sure that any change to it is discussed and agreed first.
21
- version = '0.15.0'
21
+ version = '0.15.1'
22
22
 
23
23
  if version == "DEVELOPMENT"
24
24
  root = File.expand_path("../../..", File.dirname(__FILE__))
@@ -40,6 +40,9 @@ collections:
40
40
  +format: join_names
41
41
  policy:
42
42
  +format: select_name
43
+ metadata count:
44
+ +column: metadata
45
+ +format: count_hash
43
46
  member:
44
47
  +short:
45
48
  +layout: list
@@ -49,6 +52,9 @@ collections:
49
52
  +format: mac
50
53
  state:
51
54
  last_checkin:
55
+ +format: if_present
56
+ metadata:
57
+ +format: if_present
52
58
  tags:
53
59
  +format: join_names
54
60
  tags:
@@ -174,6 +180,7 @@ collections:
174
180
  command:
175
181
  name parameter:
176
182
  +format: name
183
+ +column: params
177
184
  errors:
178
185
  +format: count
179
186
  status:
@@ -14,7 +14,7 @@ describe Razor::CLI::Format do
14
14
  include described_class
15
15
 
16
16
  def format(doc, args = {})
17
- args = {:format => 'short', :args => ['something', 'else'], :show_command_help? => false}.merge(args)
17
+ args = {:format => 'short', :args => ['something', 'else'], :query? => true, :show_command_help? => false}.merge(args)
18
18
  parse = double(args)
19
19
  format_document doc, parse
20
20
  end
@@ -60,6 +60,11 @@ describe Razor::CLI::Format do
60
60
  result = format doc
61
61
  result.should =~ /Query an entry by including its name, e.g. `razor something else entirely`\z/
62
62
  end
63
+ it "hides for commands" do
64
+ doc = {'items' => [{'name' => 'entirely'}, {'name' => 'bar'} ]}
65
+ result = format doc, query?: false
66
+ result.should_not =~ /Query an entry by including its name/
67
+ end
63
68
  end
64
69
 
65
70
  context 'empty display' do
@@ -74,4 +79,21 @@ describe Razor::CLI::Format do
74
79
  result.should == "There are no items for this query."
75
80
  end
76
81
  end
82
+
83
+ context 'tabular display' do
84
+ it "works right when columns do not match up" do
85
+ doc = {"spec"=>"http://api.puppetlabs.com/razor/v1/collections/nodes/log",
86
+ "items"=>[{'a' => 'b', 'c' => 'd'},
87
+ {'b' => 'c', 'e' => 'f'}]}
88
+ result = format doc
89
+ result.should == <<-OUTPUT.rstrip
90
+ +---+---+---+---+
91
+ | a | c | b | e |
92
+ +---+---+---+---+
93
+ | b | d | | |
94
+ | | | c | f |
95
+ +---+---+---+---+
96
+ OUTPUT
97
+ end
98
+ end
77
99
  end
@@ -1,3 +1,4 @@
1
+ # -*- encoding: utf-8 -*-
1
2
  # Needed to make the client work on Ruby 1.8.7
2
3
  unless Kernel.respond_to?(:require_relative)
3
4
  module Kernel
@@ -92,7 +93,15 @@ describe Razor::CLI::Navigate do
92
93
  nav = Razor::CLI::Parse.new(['create-broker', '--name', 'broker1', '--broker-type', 'puppet',
93
94
  '--configuration', 'server=puppet.example.org', '--configuration',
94
95
  'environment=production']).navigate
95
- nav.get_document
96
+ keys = nav.get_document['configuration'].keys
97
+ keys.should include 'server'
98
+ keys.should include 'environment'
99
+ end
100
+ it "should construct a json object with unicode", :preserve_exact_body_bytes do
101
+ doc = Razor::CLI::Parse.new(['register-node', '--installed', 'true', '--hw-info', '{"net0": "abcdef"}']).navigate.get_document
102
+ name = doc['name']
103
+ nav = Razor::CLI::Parse.new(['modify-node-metadata', '--node', name, '--update', 'keyᓱ123=valueᓱ1']).navigate
104
+ nav.get_document['metadata'].should == {'keyᓱ123' => 'valueᓱ1'}
96
105
  end
97
106
  it "should fail with mixed types (array then hash)" do
98
107
  nav = Razor::CLI::Parse.new(['create-broker', '--name', 'broker2', '--broker-type', 'puppet',
@@ -0,0 +1,412 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: http://localhost:8080/api
6
+ body:
7
+ encoding: UTF-8
8
+ string: ''
9
+ headers:
10
+ Accept:
11
+ - application/json
12
+ Accept-Encoding:
13
+ - gzip, deflate
14
+ User-Agent:
15
+ - Ruby
16
+ response:
17
+ status:
18
+ code: 200
19
+ message: OK
20
+ headers:
21
+ Server:
22
+ - Apache-Coyote/1.1
23
+ X-Content-Type-Options:
24
+ - nosniff
25
+ Content-Type:
26
+ - application/json;charset=utf-8
27
+ Content-Length:
28
+ - '4980'
29
+ Date:
30
+ - Tue, 03 Jun 2014 23:18:44 GMT
31
+ body:
32
+ encoding: UTF-8
33
+ string: '{"commands":[{"name":"add-policy-tag","rel":"http://api.puppetlabs.com/razor/v1/commands/add-policy-tag","id":"http://localhost:8080/api/commands/add-policy-tag"},{"name":"create-broker","rel":"http://api.puppetlabs.com/razor/v1/commands/create-broker","id":"http://localhost:8080/api/commands/create-broker"},{"name":"create-policy","rel":"http://api.puppetlabs.com/razor/v1/commands/create-policy","id":"http://localhost:8080/api/commands/create-policy"},{"name":"create-repo","rel":"http://api.puppetlabs.com/razor/v1/commands/create-repo","id":"http://localhost:8080/api/commands/create-repo"},{"name":"create-tag","rel":"http://api.puppetlabs.com/razor/v1/commands/create-tag","id":"http://localhost:8080/api/commands/create-tag"},{"name":"create-task","rel":"http://api.puppetlabs.com/razor/v1/commands/create-task","id":"http://localhost:8080/api/commands/create-task"},{"name":"delete-broker","rel":"http://api.puppetlabs.com/razor/v1/commands/delete-broker","id":"http://localhost:8080/api/commands/delete-broker"},{"name":"delete-node","rel":"http://api.puppetlabs.com/razor/v1/commands/delete-node","id":"http://localhost:8080/api/commands/delete-node"},{"name":"delete-policy","rel":"http://api.puppetlabs.com/razor/v1/commands/delete-policy","id":"http://localhost:8080/api/commands/delete-policy"},{"name":"delete-repo","rel":"http://api.puppetlabs.com/razor/v1/commands/delete-repo","id":"http://localhost:8080/api/commands/delete-repo"},{"name":"delete-tag","rel":"http://api.puppetlabs.com/razor/v1/commands/delete-tag","id":"http://localhost:8080/api/commands/delete-tag"},{"name":"disable-policy","rel":"http://api.puppetlabs.com/razor/v1/commands/disable-policy","id":"http://localhost:8080/api/commands/disable-policy"},{"name":"enable-policy","rel":"http://api.puppetlabs.com/razor/v1/commands/enable-policy","id":"http://localhost:8080/api/commands/enable-policy"},{"name":"modify-node-metadata","rel":"http://api.puppetlabs.com/razor/v1/commands/modify-node-metadata","id":"http://localhost:8080/api/commands/modify-node-metadata"},{"name":"modify-policy-max-count","rel":"http://api.puppetlabs.com/razor/v1/commands/modify-policy-max-count","id":"http://localhost:8080/api/commands/modify-policy-max-count"},{"name":"move-policy","rel":"http://api.puppetlabs.com/razor/v1/commands/move-policy","id":"http://localhost:8080/api/commands/move-policy"},{"name":"reboot-node","rel":"http://api.puppetlabs.com/razor/v1/commands/reboot-node","id":"http://localhost:8080/api/commands/reboot-node"},{"name":"register-node","rel":"http://api.puppetlabs.com/razor/v1/commands/register-node","id":"http://localhost:8080/api/commands/register-node"},{"name":"reinstall-node","rel":"http://api.puppetlabs.com/razor/v1/commands/reinstall-node","id":"http://localhost:8080/api/commands/reinstall-node"},{"name":"remove-node-metadata","rel":"http://api.puppetlabs.com/razor/v1/commands/remove-node-metadata","id":"http://localhost:8080/api/commands/remove-node-metadata"},{"name":"remove-policy-tag","rel":"http://api.puppetlabs.com/razor/v1/commands/remove-policy-tag","id":"http://localhost:8080/api/commands/remove-policy-tag"},{"name":"set-node-desired-power-state","rel":"http://api.puppetlabs.com/razor/v1/commands/set-node-desired-power-state","id":"http://localhost:8080/api/commands/set-node-desired-power-state"},{"name":"set-node-hw-info","rel":"http://api.puppetlabs.com/razor/v1/commands/set-node-hw-info","id":"http://localhost:8080/api/commands/set-node-hw-info"},{"name":"set-node-ipmi-credentials","rel":"http://api.puppetlabs.com/razor/v1/commands/set-node-ipmi-credentials","id":"http://localhost:8080/api/commands/set-node-ipmi-credentials"},{"name":"update-node-metadata","rel":"http://api.puppetlabs.com/razor/v1/commands/update-node-metadata","id":"http://localhost:8080/api/commands/update-node-metadata"},{"name":"update-tag-rule","rel":"http://api.puppetlabs.com/razor/v1/commands/update-tag-rule","id":"http://localhost:8080/api/commands/update-tag-rule"}],"collections":[{"name":"brokers","rel":"http://api.puppetlabs.com/razor/v1/collections/brokers","id":"http://localhost:8080/api/collections/brokers"},{"name":"repos","rel":"http://api.puppetlabs.com/razor/v1/collections/repos","id":"http://localhost:8080/api/collections/repos"},{"name":"tags","rel":"http://api.puppetlabs.com/razor/v1/collections/tags","id":"http://localhost:8080/api/collections/tags"},{"name":"policies","rel":"http://api.puppetlabs.com/razor/v1/collections/policies","id":"http://localhost:8080/api/collections/policies"},{"name":"nodes","rel":"http://api.puppetlabs.com/razor/v1/collections/nodes","id":"http://localhost:8080/api/collections/nodes"},{"name":"tasks","rel":"http://api.puppetlabs.com/razor/v1/collections/tasks","id":"http://localhost:8080/api/collections/tasks"},{"name":"commands","rel":"http://api.puppetlabs.com/razor/v1/collections/commands","id":"http://localhost:8080/api/collections/commands"}],"version":{"server":"v0.15.0-3-gb0cbb3f-dirty"}}'
34
+ http_version:
35
+ recorded_at: Tue, 03 Jun 2014 23:18:44 GMT
36
+ - request:
37
+ method: get
38
+ uri: http://localhost:8080/api/commands/register-node
39
+ body:
40
+ encoding: UTF-8
41
+ string: ''
42
+ headers:
43
+ Accept:
44
+ - application/json
45
+ Accept-Encoding:
46
+ - gzip, deflate
47
+ User-Agent:
48
+ - Ruby
49
+ response:
50
+ status:
51
+ code: 200
52
+ message: OK
53
+ headers:
54
+ Server:
55
+ - Apache-Coyote/1.1
56
+ Etag:
57
+ - '"server-version-v0.15.0-3-gb0cbb3f-dirty"'
58
+ X-Content-Type-Options:
59
+ - nosniff
60
+ Content-Type:
61
+ - application/json;charset=utf-8
62
+ Content-Length:
63
+ - '2936'
64
+ Date:
65
+ - Tue, 03 Jun 2014 23:18:44 GMT
66
+ body:
67
+ encoding: UTF-8
68
+ string: '{"name":"register-node","help":{"full":"# SYNOPSIS\nRegister a node
69
+ with Razor before it is discovered\n\n# DESCRIPTION\nIn order to make brownfield
70
+ deployments of Razor easier we allow users to\nregister nodes explicitly. This
71
+ command allows you to perform the same\nregistration that would happen when
72
+ a new node checked in, ahead of time.\n\nIn order for this to be effective
73
+ the hw_info must contain enough information\nthat the node can successfully
74
+ be matched during the iPXE boot phase.\n\nIf the node matches an existing
75
+ node, in keeping with the overall policy of\ncommands declaring desired state,
76
+ the node installed field will be updated to\nmatch the value in this command.\n\nThe
77
+ final state is that a node with the supplied hardware information, and the\ndesired
78
+ installed state, will be present in the database, regardless of it\nexisting
79
+ before hand or not.\n\n# Access Control\n\nThis command''s access control
80
+ pattern: `commands:register-node`\n\nFor more detail on how the permission
81
+ strings are structured and work, you can\nsee the [Shiro Permissions documentation][shiro]. That
82
+ pattern is expanded\nand then a permission check applied to it, before the
83
+ command is authorized.\n\nThese checks only apply if security is enabled in
84
+ the Razor configuration\nfile; on this server security is currently disabled.\n\n[shiro]:
85
+ http://shiro.apache.org/permissions.html\n\n# Attributes\n\n * installed\n -
86
+ Should the node be considered ''installed'' already? Installed nodes are\n not
87
+ eligible for policy matching, and will simply boot locally.\n - This attribute
88
+ is required.\n - It must be of type boolean.\n\n * hw-info\n - The hardware
89
+ information for the node. This is used to match the node on first\n boot
90
+ with the record in the database. The order of MAC address assignment in\n this
91
+ data is not significant, as a node with reordered MAC addresses will be\n treated
92
+ as the same node.\n - This attribute is required.\n - It must be of type
93
+ object.\n - It must be between 1 and Infinity in length.\n \n # Attributes\n \n *
94
+ serial\n - The DMI serial number of the node\n - It must be of type
95
+ string.\n \n * asset\n - The DMI asset tag of the node\n -
96
+ It must be of type string.\n \n * uuid\n - The DMI UUID of the node\n -
97
+ It must be of type string.\n\n# EXAMPLES\n\n Register a machine before you
98
+ boot it, and note that it already has an OS\n installed, so should not be
99
+ subject to policy-based reinstallation:\n \n {\n \"hw_info\":
100
+ {\n \"net0\": \"78:31:c1:be:c8:00\",\n \"net1\": \"72:00:01:f2:13:f0\",\n \"net2\": \"72:00:01:f2:13:f1\",\n \"serial\":
101
+ \"xxxxxxxxxxx\",\n \"asset\": \"Asset-1234567890\",\n \"uuid\": \"Not
102
+ Settable\"\n },\n \"installed\": true\n }\n"},"schema":{"installed":{"type":"boolean"},"hw-info":{"type":"object"}}}'
103
+ http_version:
104
+ recorded_at: Tue, 03 Jun 2014 23:18:45 GMT
105
+ - request:
106
+ method: get
107
+ uri: http://localhost:8080/api/commands/register-node
108
+ body:
109
+ encoding: UTF-8
110
+ string: ''
111
+ headers:
112
+ Accept:
113
+ - application/json
114
+ Accept-Encoding:
115
+ - gzip, deflate
116
+ User-Agent:
117
+ - Ruby
118
+ response:
119
+ status:
120
+ code: 200
121
+ message: OK
122
+ headers:
123
+ Server:
124
+ - Apache-Coyote/1.1
125
+ Etag:
126
+ - '"server-version-v0.15.0-3-gb0cbb3f-dirty"'
127
+ X-Content-Type-Options:
128
+ - nosniff
129
+ Content-Type:
130
+ - application/json;charset=utf-8
131
+ Content-Length:
132
+ - '2936'
133
+ Date:
134
+ - Tue, 03 Jun 2014 23:18:44 GMT
135
+ body:
136
+ encoding: UTF-8
137
+ string: '{"name":"register-node","help":{"full":"# SYNOPSIS\nRegister a node
138
+ with Razor before it is discovered\n\n# DESCRIPTION\nIn order to make brownfield
139
+ deployments of Razor easier we allow users to\nregister nodes explicitly. This
140
+ command allows you to perform the same\nregistration that would happen when
141
+ a new node checked in, ahead of time.\n\nIn order for this to be effective
142
+ the hw_info must contain enough information\nthat the node can successfully
143
+ be matched during the iPXE boot phase.\n\nIf the node matches an existing
144
+ node, in keeping with the overall policy of\ncommands declaring desired state,
145
+ the node installed field will be updated to\nmatch the value in this command.\n\nThe
146
+ final state is that a node with the supplied hardware information, and the\ndesired
147
+ installed state, will be present in the database, regardless of it\nexisting
148
+ before hand or not.\n\n# Access Control\n\nThis command''s access control
149
+ pattern: `commands:register-node`\n\nFor more detail on how the permission
150
+ strings are structured and work, you can\nsee the [Shiro Permissions documentation][shiro]. That
151
+ pattern is expanded\nand then a permission check applied to it, before the
152
+ command is authorized.\n\nThese checks only apply if security is enabled in
153
+ the Razor configuration\nfile; on this server security is currently disabled.\n\n[shiro]:
154
+ http://shiro.apache.org/permissions.html\n\n# Attributes\n\n * installed\n -
155
+ Should the node be considered ''installed'' already? Installed nodes are\n not
156
+ eligible for policy matching, and will simply boot locally.\n - This attribute
157
+ is required.\n - It must be of type boolean.\n\n * hw-info\n - The hardware
158
+ information for the node. This is used to match the node on first\n boot
159
+ with the record in the database. The order of MAC address assignment in\n this
160
+ data is not significant, as a node with reordered MAC addresses will be\n treated
161
+ as the same node.\n - This attribute is required.\n - It must be of type
162
+ object.\n - It must be between 1 and Infinity in length.\n \n # Attributes\n \n *
163
+ serial\n - The DMI serial number of the node\n - It must be of type
164
+ string.\n \n * asset\n - The DMI asset tag of the node\n -
165
+ It must be of type string.\n \n * uuid\n - The DMI UUID of the node\n -
166
+ It must be of type string.\n\n# EXAMPLES\n\n Register a machine before you
167
+ boot it, and note that it already has an OS\n installed, so should not be
168
+ subject to policy-based reinstallation:\n \n {\n \"hw_info\":
169
+ {\n \"net0\": \"78:31:c1:be:c8:00\",\n \"net1\": \"72:00:01:f2:13:f0\",\n \"net2\": \"72:00:01:f2:13:f1\",\n \"serial\":
170
+ \"xxxxxxxxxxx\",\n \"asset\": \"Asset-1234567890\",\n \"uuid\": \"Not
171
+ Settable\"\n },\n \"installed\": true\n }\n"},"schema":{"installed":{"type":"boolean"},"hw-info":{"type":"object"}}}'
172
+ http_version:
173
+ recorded_at: Tue, 03 Jun 2014 23:18:45 GMT
174
+ - request:
175
+ method: post
176
+ uri: http://localhost:8080/api/commands/register-node
177
+ body:
178
+ encoding: UTF-8
179
+ string: '{"installed":true,"hw-info":{"net0":"abcdef"}}'
180
+ headers:
181
+ Accept:
182
+ - application/json
183
+ Accept-Encoding:
184
+ - gzip, deflate
185
+ Content-Type:
186
+ - application/json
187
+ Content-Length:
188
+ - '46'
189
+ User-Agent:
190
+ - Ruby
191
+ response:
192
+ status:
193
+ code: 202
194
+ message: Accepted
195
+ headers:
196
+ Server:
197
+ - Apache-Coyote/1.1
198
+ X-Content-Type-Options:
199
+ - nosniff
200
+ Content-Type:
201
+ - application/json;charset=utf-8
202
+ Content-Length:
203
+ - '203'
204
+ Date:
205
+ - Tue, 03 Jun 2014 23:18:44 GMT
206
+ body:
207
+ encoding: UTF-8
208
+ string: '{"spec":"http://api.puppetlabs.com/razor/v1/collections/nodes/member","id":"http://localhost:8080/api/collections/nodes/node1","name":"node1","command":"http://localhost:8080/api/collections/commands/1"}'
209
+ http_version:
210
+ recorded_at: Tue, 03 Jun 2014 23:18:45 GMT
211
+ - request:
212
+ method: get
213
+ uri: http://localhost:8080/api/collections/nodes/node1
214
+ body:
215
+ encoding: UTF-8
216
+ string: ''
217
+ headers:
218
+ Accept:
219
+ - application/json
220
+ Accept-Encoding:
221
+ - gzip, deflate
222
+ User-Agent:
223
+ - Ruby
224
+ response:
225
+ status:
226
+ code: 200
227
+ message: OK
228
+ headers:
229
+ Server:
230
+ - Apache-Coyote/1.1
231
+ X-Content-Type-Options:
232
+ - nosniff
233
+ Content-Type:
234
+ - application/json;charset=utf-8
235
+ Content-Length:
236
+ - '335'
237
+ Date:
238
+ - Tue, 03 Jun 2014 23:18:44 GMT
239
+ body:
240
+ encoding: UTF-8
241
+ string: '{"spec":"http://api.puppetlabs.com/razor/v1/collections/nodes/member","id":"http://localhost:8080/api/collections/nodes/node1","name":"node1","hw_info":{"mac":["abcdef"]},"log":{"id":"http://localhost:8080/api/collections/nodes/node1/log","name":"log"},"tags":[],"state":{"installed":"true","installed_at":"2014-06-03T18:18:45-05:00"}}'
242
+ http_version:
243
+ recorded_at: Tue, 03 Jun 2014 23:18:45 GMT
244
+ - request:
245
+ method: get
246
+ uri: http://localhost:8080/api
247
+ body:
248
+ encoding: UTF-8
249
+ string: ''
250
+ headers:
251
+ Accept:
252
+ - application/json
253
+ Accept-Encoding:
254
+ - gzip, deflate
255
+ User-Agent:
256
+ - Ruby
257
+ response:
258
+ status:
259
+ code: 200
260
+ message: OK
261
+ headers:
262
+ Server:
263
+ - Apache-Coyote/1.1
264
+ X-Content-Type-Options:
265
+ - nosniff
266
+ Content-Type:
267
+ - application/json;charset=utf-8
268
+ Content-Length:
269
+ - '4980'
270
+ Date:
271
+ - Tue, 03 Jun 2014 23:18:44 GMT
272
+ body:
273
+ encoding: UTF-8
274
+ string: '{"commands":[{"name":"add-policy-tag","rel":"http://api.puppetlabs.com/razor/v1/commands/add-policy-tag","id":"http://localhost:8080/api/commands/add-policy-tag"},{"name":"create-broker","rel":"http://api.puppetlabs.com/razor/v1/commands/create-broker","id":"http://localhost:8080/api/commands/create-broker"},{"name":"create-policy","rel":"http://api.puppetlabs.com/razor/v1/commands/create-policy","id":"http://localhost:8080/api/commands/create-policy"},{"name":"create-repo","rel":"http://api.puppetlabs.com/razor/v1/commands/create-repo","id":"http://localhost:8080/api/commands/create-repo"},{"name":"create-tag","rel":"http://api.puppetlabs.com/razor/v1/commands/create-tag","id":"http://localhost:8080/api/commands/create-tag"},{"name":"create-task","rel":"http://api.puppetlabs.com/razor/v1/commands/create-task","id":"http://localhost:8080/api/commands/create-task"},{"name":"delete-broker","rel":"http://api.puppetlabs.com/razor/v1/commands/delete-broker","id":"http://localhost:8080/api/commands/delete-broker"},{"name":"delete-node","rel":"http://api.puppetlabs.com/razor/v1/commands/delete-node","id":"http://localhost:8080/api/commands/delete-node"},{"name":"delete-policy","rel":"http://api.puppetlabs.com/razor/v1/commands/delete-policy","id":"http://localhost:8080/api/commands/delete-policy"},{"name":"delete-repo","rel":"http://api.puppetlabs.com/razor/v1/commands/delete-repo","id":"http://localhost:8080/api/commands/delete-repo"},{"name":"delete-tag","rel":"http://api.puppetlabs.com/razor/v1/commands/delete-tag","id":"http://localhost:8080/api/commands/delete-tag"},{"name":"disable-policy","rel":"http://api.puppetlabs.com/razor/v1/commands/disable-policy","id":"http://localhost:8080/api/commands/disable-policy"},{"name":"enable-policy","rel":"http://api.puppetlabs.com/razor/v1/commands/enable-policy","id":"http://localhost:8080/api/commands/enable-policy"},{"name":"modify-node-metadata","rel":"http://api.puppetlabs.com/razor/v1/commands/modify-node-metadata","id":"http://localhost:8080/api/commands/modify-node-metadata"},{"name":"modify-policy-max-count","rel":"http://api.puppetlabs.com/razor/v1/commands/modify-policy-max-count","id":"http://localhost:8080/api/commands/modify-policy-max-count"},{"name":"move-policy","rel":"http://api.puppetlabs.com/razor/v1/commands/move-policy","id":"http://localhost:8080/api/commands/move-policy"},{"name":"reboot-node","rel":"http://api.puppetlabs.com/razor/v1/commands/reboot-node","id":"http://localhost:8080/api/commands/reboot-node"},{"name":"register-node","rel":"http://api.puppetlabs.com/razor/v1/commands/register-node","id":"http://localhost:8080/api/commands/register-node"},{"name":"reinstall-node","rel":"http://api.puppetlabs.com/razor/v1/commands/reinstall-node","id":"http://localhost:8080/api/commands/reinstall-node"},{"name":"remove-node-metadata","rel":"http://api.puppetlabs.com/razor/v1/commands/remove-node-metadata","id":"http://localhost:8080/api/commands/remove-node-metadata"},{"name":"remove-policy-tag","rel":"http://api.puppetlabs.com/razor/v1/commands/remove-policy-tag","id":"http://localhost:8080/api/commands/remove-policy-tag"},{"name":"set-node-desired-power-state","rel":"http://api.puppetlabs.com/razor/v1/commands/set-node-desired-power-state","id":"http://localhost:8080/api/commands/set-node-desired-power-state"},{"name":"set-node-hw-info","rel":"http://api.puppetlabs.com/razor/v1/commands/set-node-hw-info","id":"http://localhost:8080/api/commands/set-node-hw-info"},{"name":"set-node-ipmi-credentials","rel":"http://api.puppetlabs.com/razor/v1/commands/set-node-ipmi-credentials","id":"http://localhost:8080/api/commands/set-node-ipmi-credentials"},{"name":"update-node-metadata","rel":"http://api.puppetlabs.com/razor/v1/commands/update-node-metadata","id":"http://localhost:8080/api/commands/update-node-metadata"},{"name":"update-tag-rule","rel":"http://api.puppetlabs.com/razor/v1/commands/update-tag-rule","id":"http://localhost:8080/api/commands/update-tag-rule"}],"collections":[{"name":"brokers","rel":"http://api.puppetlabs.com/razor/v1/collections/brokers","id":"http://localhost:8080/api/collections/brokers"},{"name":"repos","rel":"http://api.puppetlabs.com/razor/v1/collections/repos","id":"http://localhost:8080/api/collections/repos"},{"name":"tags","rel":"http://api.puppetlabs.com/razor/v1/collections/tags","id":"http://localhost:8080/api/collections/tags"},{"name":"policies","rel":"http://api.puppetlabs.com/razor/v1/collections/policies","id":"http://localhost:8080/api/collections/policies"},{"name":"nodes","rel":"http://api.puppetlabs.com/razor/v1/collections/nodes","id":"http://localhost:8080/api/collections/nodes"},{"name":"tasks","rel":"http://api.puppetlabs.com/razor/v1/collections/tasks","id":"http://localhost:8080/api/collections/tasks"},{"name":"commands","rel":"http://api.puppetlabs.com/razor/v1/collections/commands","id":"http://localhost:8080/api/collections/commands"}],"version":{"server":"v0.15.0-3-gb0cbb3f-dirty"}}'
275
+ http_version:
276
+ recorded_at: Tue, 03 Jun 2014 23:18:45 GMT
277
+ - request:
278
+ method: get
279
+ uri: http://localhost:8080/api/commands/modify-node-metadata
280
+ body:
281
+ encoding: UTF-8
282
+ string: ''
283
+ headers:
284
+ Accept:
285
+ - application/json
286
+ Accept-Encoding:
287
+ - gzip, deflate
288
+ User-Agent:
289
+ - Ruby
290
+ response:
291
+ status:
292
+ code: 200
293
+ message: OK
294
+ headers:
295
+ Server:
296
+ - Apache-Coyote/1.1
297
+ Etag:
298
+ - '"server-version-v0.15.0-3-gb0cbb3f-dirty"'
299
+ X-Content-Type-Options:
300
+ - nosniff
301
+ Content-Type:
302
+ - application/json;charset=utf-8
303
+ Content-Length:
304
+ - '2610'
305
+ Date:
306
+ - Tue, 03 Jun 2014 23:18:44 GMT
307
+ body:
308
+ encoding: UTF-8
309
+ string: '{"name":"modify-node-metadata","help":{"full":"# SYNOPSIS\nPerform
310
+ various editing operations on node metadata\n\n# DESCRIPTION\nNode metadata
311
+ can be added, changed, or removed with this command; it contains\na limited
312
+ editing language to make changes to the existing metadata in an\natomic fashion.\n\nIt
313
+ can also clear all metadata from a node, although that operation is\nexclusive
314
+ to all other editing operations, and cannot be performed atomically\nwith
315
+ them.\n\n# Access Control\n\nThis command''s access control pattern: `commands:modify-node-metadata:%{node}`\n\nWords
316
+ surrounded by `%{...}` are substitutions from the input data: typically\nthe
317
+ name of the object being modified, or some other critical detail, these\nallow
318
+ roles to be granted partial access to modify the system.\n\nFor more detail
319
+ on how the permission strings are structured and work, you can\nsee the [Shiro
320
+ Permissions documentation][shiro]. That pattern is expanded\nand then a permission
321
+ check applied to it, before the command is authorized.\n\nThese checks only
322
+ apply if security is enabled in the Razor configuration\nfile; on this server
323
+ security is currently disabled.\n\n[shiro]: http://shiro.apache.org/permissions.html\n\n#
324
+ Attributes\n\n * node\n - The name of the node for which to modify metadata.\n -
325
+ This attribute is required.\n - It must be of type string.\n - It must
326
+ match the name of an existing node.\n\n * update\n - The metadata to update\n -
327
+ It must be of type object.\n\n * remove\n - The metadata to remove\n -
328
+ It must be of type array.\n\n * clear\n - Remove all metadata from the node. Cannot
329
+ be used together with\n either ''update'' or ''remove''.\n - It must
330
+ be of type boolean.\n - If present, update, remove must not be present.\n\n
331
+ * no-replace\n - If true, the `update` operation will cause this command
332
+ to fail if the\n metadata key is already present on the node. No effect
333
+ on `remove` or\n clear.\n - It must be of type boolean.\n\n# EXAMPLES\n\n Editing
334
+ node metadata, by adding and removing some keys, but refusing to\n modify
335
+ an existing value already present on a node:\n \n {\n \"node\":
336
+ \"node1\",\n \"update\": {\n \"key1\": \"value1\",\n \"key2\":
337
+ \"value2\"\n }\n \"remove\": [\"key3\", \"key4\"],\n \"no-replace\":
338
+ true\n }\n \n Removing all node metadata:\n \n {\"node\": \"node1\",
339
+ \"clear\": true}\n"},"schema":{"node":{"type":"string"},"update":{"type":"object"},"remove":{"type":"array"},"clear":{"type":"boolean"},"no-replace":{"type":"boolean"}}}'
340
+ http_version:
341
+ recorded_at: Tue, 03 Jun 2014 23:18:45 GMT
342
+ - request:
343
+ method: post
344
+ uri: http://localhost:8080/api/commands/modify-node-metadata
345
+ body:
346
+ encoding: UTF-8
347
+ string: '{"node":"node1","update":{"keyᓱ123":"valueᓱ1"}}'
348
+ headers:
349
+ Accept:
350
+ - application/json
351
+ Accept-Encoding:
352
+ - gzip, deflate
353
+ Content-Type:
354
+ - application/json
355
+ Content-Length:
356
+ - '51'
357
+ User-Agent:
358
+ - Ruby
359
+ response:
360
+ status:
361
+ code: 202
362
+ message: Accepted
363
+ headers:
364
+ Server:
365
+ - Apache-Coyote/1.1
366
+ X-Content-Type-Options:
367
+ - nosniff
368
+ Content-Type:
369
+ - application/json;charset=utf-8
370
+ Content-Length:
371
+ - '203'
372
+ Date:
373
+ - Tue, 03 Jun 2014 23:18:44 GMT
374
+ body:
375
+ encoding: UTF-8
376
+ string: '{"spec":"http://api.puppetlabs.com/razor/v1/collections/nodes/member","id":"http://localhost:8080/api/collections/nodes/node1","name":"node1","command":"http://localhost:8080/api/collections/commands/2"}'
377
+ http_version:
378
+ recorded_at: Tue, 03 Jun 2014 23:18:45 GMT
379
+ - request:
380
+ method: get
381
+ uri: http://localhost:8080/api/collections/nodes/node1
382
+ body:
383
+ encoding: UTF-8
384
+ string: ''
385
+ headers:
386
+ Accept:
387
+ - application/json
388
+ Accept-Encoding:
389
+ - gzip, deflate
390
+ User-Agent:
391
+ - Ruby
392
+ response:
393
+ status:
394
+ code: 200
395
+ message: OK
396
+ headers:
397
+ Server:
398
+ - Apache-Coyote/1.1
399
+ X-Content-Type-Options:
400
+ - nosniff
401
+ Content-Type:
402
+ - application/json;charset=utf-8
403
+ Content-Length:
404
+ - '368'
405
+ Date:
406
+ - Tue, 03 Jun 2014 23:18:44 GMT
407
+ body:
408
+ encoding: UTF-8
409
+ string: '{"spec":"http://api.puppetlabs.com/razor/v1/collections/nodes/member","id":"http://localhost:8080/api/collections/nodes/node1","name":"node1","hw_info":{"mac":["abcdef"]},"log":{"id":"http://localhost:8080/api/collections/nodes/node1/log","name":"log"},"tags":[],"metadata":{"keyᓱ123":"valueᓱ1"},"state":{"installed":"true","installed_at":"2014-06-03T18:18:45-05:00"}}'
410
+ http_version:
411
+ recorded_at: Tue, 03 Jun 2014 23:18:45 GMT
412
+ recorded_with: VCR 2.5.0
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: razor-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.0
4
+ version: 0.15.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-05-28 00:00:00.000000000 Z
12
+ date: 2014-06-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mime-types
@@ -112,6 +112,7 @@ files:
112
112
  - LICENSE
113
113
  - spec/fixtures/vcr/Razor_CLI_Navigate/with_multiple_arguments_with_same_name/combining_as_an_object/should_fail_with_mixed_types_hash_then_array_.yml
114
114
  - spec/fixtures/vcr/Razor_CLI_Navigate/with_multiple_arguments_with_same_name/combining_as_an_object/should_construct_a_json_object.yml
115
+ - spec/fixtures/vcr/Razor_CLI_Navigate/with_multiple_arguments_with_same_name/combining_as_an_object/should_construct_a_json_object_with_unicode.yml
115
116
  - spec/fixtures/vcr/Razor_CLI_Navigate/with_multiple_arguments_with_same_name/combining_as_an_object/should_fail_with_mixed_types_array_then_hash_.yml
116
117
  - spec/fixtures/vcr/Razor_CLI_Navigate/with_multiple_arguments_with_same_name/combining_as_an_array/should_merge_an_array_into_an_existing_array.yml
117
118
  - spec/fixtures/vcr/Razor_CLI_Navigate/with_multiple_arguments_with_same_name/combining_as_an_array/should_merge_the_arguments_into_an_existing_array.yml
@@ -163,6 +164,7 @@ summary: Razor is an advanced provisioning application
163
164
  test_files:
164
165
  - spec/fixtures/vcr/Razor_CLI_Navigate/with_multiple_arguments_with_same_name/combining_as_an_object/should_fail_with_mixed_types_hash_then_array_.yml
165
166
  - spec/fixtures/vcr/Razor_CLI_Navigate/with_multiple_arguments_with_same_name/combining_as_an_object/should_construct_a_json_object.yml
167
+ - spec/fixtures/vcr/Razor_CLI_Navigate/with_multiple_arguments_with_same_name/combining_as_an_object/should_construct_a_json_object_with_unicode.yml
166
168
  - spec/fixtures/vcr/Razor_CLI_Navigate/with_multiple_arguments_with_same_name/combining_as_an_object/should_fail_with_mixed_types_array_then_hash_.yml
167
169
  - spec/fixtures/vcr/Razor_CLI_Navigate/with_multiple_arguments_with_same_name/combining_as_an_array/should_merge_an_array_into_an_existing_array.yml
168
170
  - spec/fixtures/vcr/Razor_CLI_Navigate/with_multiple_arguments_with_same_name/combining_as_an_array/should_merge_the_arguments_into_an_existing_array.yml