chef-api 0.2.0 → 0.2.1
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.
- checksums.yaml +4 -4
- data/.travis.yml +2 -2
- data/CHANGELOG.md +17 -0
- data/README.md +42 -0
- data/chef-api.gemspec +1 -1
- data/lib/chef-api/configurable.rb +3 -2
- data/lib/chef-api/connection.rb +77 -47
- data/lib/chef-api/defaults.rb +28 -8
- data/lib/chef-api/errors.rb +32 -3
- data/lib/chef-api/resource.rb +1 -0
- data/lib/chef-api/resources/base.rb +30 -21
- data/lib/chef-api/resources/client.rb +6 -8
- data/lib/chef-api/resources/collection_proxy.rb +19 -2
- data/lib/chef-api/resources/data_bag.rb +1 -1
- data/lib/chef-api/resources/organization.rb +22 -0
- data/lib/chef-api/resources/user.rb +72 -1
- data/lib/chef-api/schema.rb +59 -21
- data/lib/chef-api/version.rb +1 -1
- data/lib/chef-api.rb +26 -5
- data/spec/integration/resources/client_spec.rb +54 -0
- data/spec/integration/resources/user_spec.rb +8 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/support/chef_server.rb +1 -0
- data/spec/unit/errors_spec.rb +294 -0
- data/spec/unit/resources/client_spec.rb +0 -16
- data/spec/unit/resources/connection_spec.rb +51 -0
- data/templates/errors/abstract_method.erb +5 -0
- data/templates/errors/cannot_regenerate_key.erb +1 -0
- data/templates/errors/chef_api_error.erb +1 -0
- data/templates/errors/file_not_found.erb +1 -0
- data/templates/errors/http_bad_request.erb +3 -0
- data/templates/errors/http_forbidden_request.erb +3 -0
- data/templates/errors/http_gateway_timeout.erb +3 -0
- data/templates/errors/http_method_not_allowed.erb +3 -0
- data/templates/errors/http_not_acceptable.erb +3 -0
- data/templates/errors/http_not_found.erb +3 -0
- data/templates/errors/http_server_unavailable.erb +1 -0
- data/templates/errors/http_unauthorized_request.erb +3 -0
- data/templates/errors/insufficient_file_permissions.erb +1 -0
- data/templates/errors/invalid_resource.erb +1 -0
- data/templates/errors/invalid_validator.erb +1 -0
- data/templates/errors/missing_url_parameter.erb +1 -0
- data/templates/errors/not_a_directory.erb +1 -0
- data/templates/errors/resource_already_exists.erb +1 -0
- data/templates/errors/resource_not_found.erb +1 -0
- data/templates/errors/resource_not_mutable.erb +1 -0
- data/templates/errors/unknown_attribute.erb +1 -0
- metadata +43 -17
- data/lib/chef-api/logger.rb +0 -160
- data/lib/chef-api/proxy.rb +0 -72
- data/locales/en.yml +0 -89
@@ -20,7 +20,7 @@ module ChefAPI
|
|
20
20
|
# @todo doc
|
21
21
|
#
|
22
22
|
def from_url(url, prefix = {})
|
23
|
-
from_json(
|
23
|
+
from_json(connection.get(url), prefix)
|
24
24
|
end
|
25
25
|
|
26
26
|
#
|
@@ -78,13 +78,13 @@ module ChefAPI
|
|
78
78
|
# has_many :environments, class_name: 'Environment'
|
79
79
|
#
|
80
80
|
def has_many(method, options = {})
|
81
|
-
class_name = options[:class_name] || Util.camelize(method).sub(/s$/, '')
|
81
|
+
class_name = options[:class_name] || "Resource::#{Util.camelize(method).sub(/s$/, '')}"
|
82
82
|
rest_endpoint = options[:rest_endpoint] || method
|
83
83
|
|
84
84
|
class_eval <<-EOH, __FILE__, __LINE__ + 1
|
85
85
|
def #{method}
|
86
86
|
associations[:#{method}] ||=
|
87
|
-
CollectionProxy.new(self, #{class_name}, '#{rest_endpoint}')
|
87
|
+
Resource::CollectionProxy.new(self, #{class_name}, '#{rest_endpoint}')
|
88
88
|
end
|
89
89
|
EOH
|
90
90
|
end
|
@@ -138,7 +138,7 @@ module ChefAPI
|
|
138
138
|
#
|
139
139
|
def post(body, prefix = {})
|
140
140
|
path = expanded_collection_path(prefix)
|
141
|
-
|
141
|
+
connection.post(path, body)
|
142
142
|
end
|
143
143
|
|
144
144
|
#
|
@@ -159,7 +159,7 @@ module ChefAPI
|
|
159
159
|
#
|
160
160
|
def put(id, body, prefix = {})
|
161
161
|
path = resource_path(id, prefix)
|
162
|
-
|
162
|
+
connection.put(path, body)
|
163
163
|
end
|
164
164
|
|
165
165
|
#
|
@@ -173,7 +173,7 @@ module ChefAPI
|
|
173
173
|
#
|
174
174
|
def delete(id, prefix = {})
|
175
175
|
path = resource_path(id, prefix)
|
176
|
-
|
176
|
+
connection.delete(path)
|
177
177
|
true
|
178
178
|
rescue Error::HTTPNotFound
|
179
179
|
true
|
@@ -195,7 +195,7 @@ module ChefAPI
|
|
195
195
|
#
|
196
196
|
def list(prefix = {})
|
197
197
|
path = expanded_collection_path(prefix)
|
198
|
-
|
198
|
+
connection.get(path).keys.sort
|
199
199
|
end
|
200
200
|
|
201
201
|
#
|
@@ -249,7 +249,7 @@ module ChefAPI
|
|
249
249
|
return nil if id.nil?
|
250
250
|
|
251
251
|
path = resource_path(id, prefix)
|
252
|
-
response =
|
252
|
+
response = connection.get(path)
|
253
253
|
from_json(response, prefix)
|
254
254
|
rescue Error::HTTPNotFound
|
255
255
|
nil
|
@@ -363,7 +363,7 @@ module ChefAPI
|
|
363
363
|
#
|
364
364
|
def each(prefix = {}, &block)
|
365
365
|
collection(prefix).each do |resource, path|
|
366
|
-
response =
|
366
|
+
response = connection.get(path)
|
367
367
|
result = from_json(response, prefix)
|
368
368
|
|
369
369
|
block.call(result) if block
|
@@ -383,9 +383,8 @@ module ChefAPI
|
|
383
383
|
#
|
384
384
|
# Return an array of all resources in the collection.
|
385
385
|
#
|
386
|
-
# @
|
387
|
-
#
|
388
|
-
# {size} and {each} methods instead as they are much more perforant.
|
386
|
+
# @note Unless you need the _entire_ collection, please consider using the
|
387
|
+
# {size} and {each} methods instead as they are much more perforant.
|
389
388
|
#
|
390
389
|
# @return [Array<Resource::Base>]
|
391
390
|
#
|
@@ -470,7 +469,7 @@ module ChefAPI
|
|
470
469
|
# a list of resources in the collection
|
471
470
|
#
|
472
471
|
def collection(prefix = {})
|
473
|
-
|
472
|
+
connection.get(expanded_collection_path(prefix))
|
474
473
|
end
|
475
474
|
|
476
475
|
#
|
@@ -516,6 +515,15 @@ module ChefAPI
|
|
516
515
|
URI.escape(value)
|
517
516
|
end.sub(/^\//, '') # Remove leading slash
|
518
517
|
end
|
518
|
+
|
519
|
+
#
|
520
|
+
# The current connection object.
|
521
|
+
#
|
522
|
+
# @return [ChefAPI::Connection]
|
523
|
+
#
|
524
|
+
def connection
|
525
|
+
Thread.current['chefapi.connection'] || ChefAPI.connection
|
526
|
+
end
|
519
527
|
end
|
520
528
|
|
521
529
|
#
|
@@ -546,6 +554,9 @@ module ChefAPI
|
|
546
554
|
# the list of prefix options (for nested resources)
|
547
555
|
#
|
548
556
|
def initialize(attributes = {}, prefix = {})
|
557
|
+
@schema = self.class.schema.dup
|
558
|
+
@schema.load_flavor(self.class.connection.flavor)
|
559
|
+
|
549
560
|
@associations = {}
|
550
561
|
@_prefix = prefix
|
551
562
|
|
@@ -571,7 +582,7 @@ module ChefAPI
|
|
571
582
|
# the primary key for this resource
|
572
583
|
#
|
573
584
|
def primary_key
|
574
|
-
|
585
|
+
@schema.primary_key
|
575
586
|
end
|
576
587
|
|
577
588
|
#
|
@@ -596,7 +607,7 @@ module ChefAPI
|
|
596
607
|
# @return [Hash<Symbol, Object>]
|
597
608
|
#
|
598
609
|
def _attributes
|
599
|
-
@_attributes ||= {}.merge(
|
610
|
+
@_attributes ||= {}.merge(@schema.attributes)
|
600
611
|
end
|
601
612
|
|
602
613
|
#
|
@@ -636,8 +647,7 @@ module ChefAPI
|
|
636
647
|
# so they will be reloaded the next time they are requested. If the remote
|
637
648
|
# record does not exist, no attributes are modified.
|
638
649
|
#
|
639
|
-
# @
|
640
|
-
# This will remove any custom values you have set on the resource!
|
650
|
+
# @note This will remove any custom values you have set on the resource!
|
641
651
|
#
|
642
652
|
# @return [self]
|
643
653
|
# the instance of the reloaded record
|
@@ -748,7 +758,7 @@ module ChefAPI
|
|
748
758
|
# the list of validators for this resource
|
749
759
|
#
|
750
760
|
def validators
|
751
|
-
@validators ||=
|
761
|
+
@validators ||= @schema.validators
|
752
762
|
end
|
753
763
|
|
754
764
|
#
|
@@ -845,8 +855,7 @@ module ChefAPI
|
|
845
855
|
# bacon.description = "My new description"
|
846
856
|
# bacon.diff #=> { :description => { :local => "My new description", :remote => "Old description" } }
|
847
857
|
#
|
848
|
-
# @
|
849
|
-
# This is a VERY expensive operation - use it sparringly!
|
858
|
+
# @note This is a VERY expensive operation - use it sparringly!
|
850
859
|
#
|
851
860
|
# @return [Hash]
|
852
861
|
#
|
@@ -887,7 +896,7 @@ module ChefAPI
|
|
887
896
|
# @return [Boolean]
|
888
897
|
#
|
889
898
|
def ignore_attribute?(key)
|
890
|
-
|
899
|
+
@schema.ignored_attributes.has_key?(key.to_sym)
|
891
900
|
end
|
892
901
|
|
893
902
|
#
|
@@ -26,7 +26,7 @@ module ChefAPI
|
|
26
26
|
# @return [Resource::Client]
|
27
27
|
#
|
28
28
|
def from_file(path)
|
29
|
-
name,
|
29
|
+
name, key = Util.safe_read(path)
|
30
30
|
|
31
31
|
if client = fetch(name)
|
32
32
|
client.private_key = key
|
@@ -63,15 +63,13 @@ module ChefAPI
|
|
63
63
|
# key = client.regenerate_key
|
64
64
|
# key #=> "-----BEGIN PRIVATE KEY-----\nMIGfMA0GCS..."
|
65
65
|
#
|
66
|
-
# @
|
67
|
-
#
|
68
|
-
#
|
69
|
-
# to
|
70
|
-
# +nil+ after you have committed it to disk and perform a manual GC to
|
66
|
+
# @note For security reasons, you should perform this operation sparingly!
|
67
|
+
# The resulting private key is committed to this object, meaning it is
|
68
|
+
# saved to memory somewhere. You should set this resource's +private_key+
|
69
|
+
# to +nil+ after you have committed it to disk and perform a manual GC to
|
71
70
|
# be ultra-secure.
|
72
71
|
#
|
73
|
-
# @
|
74
|
-
# Regenerating the private key also regenerates the public key!
|
72
|
+
# @note Regenerating the private key also regenerates the public key!
|
75
73
|
#
|
76
74
|
# @return [self]
|
77
75
|
# the current resource with the new public and private key attributes
|
@@ -157,6 +157,11 @@ module ChefAPI
|
|
157
157
|
#
|
158
158
|
# ["item_1", "item_2"]
|
159
159
|
#
|
160
|
+
# Or if the Chef Server is feeling especially magical, it might return the
|
161
|
+
# actual objects, but prefixed with the JSON id:
|
162
|
+
#
|
163
|
+
# [{"organization" => {"_id" => "..."}}, {"organization" => {...}}]
|
164
|
+
#
|
160
165
|
# So, this method attempts to intelligent handle these use cases. That being
|
161
166
|
# said, I can almost guarantee that someone is going to do some crazy
|
162
167
|
# strange edge case with this library and hit a bug here, so it will likely
|
@@ -165,9 +170,21 @@ module ChefAPI
|
|
165
170
|
# @return [Hash]
|
166
171
|
#
|
167
172
|
def load_collection
|
168
|
-
case response =
|
173
|
+
case response = Resource::Base.connection.get(endpoint)
|
169
174
|
when Array
|
170
|
-
|
175
|
+
if response.first.is_a?(Hash)
|
176
|
+
key = klass.schema.primary_key.to_s
|
177
|
+
|
178
|
+
{}.tap do |hash|
|
179
|
+
response.each do |results|
|
180
|
+
results.each do |_, info|
|
181
|
+
hash[key] = klass.resource_path(info[key])
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
else
|
186
|
+
Hash[*response.map { |item| [item, klass.resource_path(item)] }.flatten]
|
187
|
+
end
|
171
188
|
when Hash
|
172
189
|
response
|
173
190
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module ChefAPI
|
2
|
+
class Resource::Organization < Resource::Base
|
3
|
+
collection_path '/organizations'
|
4
|
+
|
5
|
+
schema do
|
6
|
+
attribute :name, type: String, primary: true, required: true
|
7
|
+
attribute :org_type, type: String
|
8
|
+
attribute :full_name, type: String
|
9
|
+
attribute :clientname, type: String
|
10
|
+
attribute :guid, type: String
|
11
|
+
|
12
|
+
ignore :_id
|
13
|
+
ignore :_rev
|
14
|
+
ignore :chargify_subscription_id
|
15
|
+
ignore :chargify_customer_id
|
16
|
+
ignore :billing_plan
|
17
|
+
ignore :requester_id
|
18
|
+
ignore :assigned_at
|
19
|
+
ignore 'couchrest-type'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -3,9 +3,80 @@ module ChefAPI
|
|
3
3
|
collection_path '/users'
|
4
4
|
|
5
5
|
schema do
|
6
|
-
|
6
|
+
flavor :enterprise do
|
7
|
+
attribute :username, type: String, primary: true, required: true
|
8
|
+
|
9
|
+
# "Vanity" attributes
|
10
|
+
attribute :first_name, type: String
|
11
|
+
attribute :middle_name, type: String
|
12
|
+
attribute :last_name, type: String
|
13
|
+
attribute :display_name, type: String
|
14
|
+
attribute :email, type: String
|
15
|
+
attribute :city, type: String
|
16
|
+
attribute :country, type: String
|
17
|
+
attribute :twitter_account, type: String
|
18
|
+
end
|
19
|
+
|
20
|
+
flavor :open_source do
|
21
|
+
attribute :name, type: String, primary: true, required: true
|
22
|
+
end
|
23
|
+
|
7
24
|
attribute :admin, type: Boolean, default: false
|
8
25
|
attribute :public_key, type: String
|
26
|
+
attribute :private_key, type: [String, Boolean], default: false
|
27
|
+
end
|
28
|
+
|
29
|
+
has_many :organizations
|
30
|
+
|
31
|
+
class << self
|
32
|
+
#
|
33
|
+
# @see Base.each
|
34
|
+
#
|
35
|
+
def each(prefix = {}, &block)
|
36
|
+
users = collection(prefix)
|
37
|
+
|
38
|
+
# HEC/EC returns a slightly different response than OSC/CZ
|
39
|
+
if users.is_a?(Array)
|
40
|
+
users.each do |info|
|
41
|
+
name = URI.escape(info['user']['username'])
|
42
|
+
response = connection.get("/users/#{name}")
|
43
|
+
result = from_json(response, prefix)
|
44
|
+
|
45
|
+
block.call(result) if block
|
46
|
+
end
|
47
|
+
else
|
48
|
+
users.each do |_, path|
|
49
|
+
response = connection.get(path)
|
50
|
+
result = from_json(response, prefix)
|
51
|
+
|
52
|
+
block.call(result) if block
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
# Authenticate a user with the given +username+ and +password+.
|
59
|
+
#
|
60
|
+
# @note Requires Enterprise Chef
|
61
|
+
#
|
62
|
+
# @example Authenticate a user
|
63
|
+
# User.authenticate(username: 'user', password: 'pass')
|
64
|
+
# #=> { "status" => "linked", "user" => { ... } }
|
65
|
+
#
|
66
|
+
# @param [Hash] options
|
67
|
+
# the list of options to authenticate with
|
68
|
+
#
|
69
|
+
# @option options [String] username
|
70
|
+
# the username to authenticate with
|
71
|
+
# @option options [String] password
|
72
|
+
# the plain-text password to authenticate with
|
73
|
+
#
|
74
|
+
# @return [Hash]
|
75
|
+
# the parsed JSON response from the server
|
76
|
+
#
|
77
|
+
def authenticate(options = {})
|
78
|
+
connection.post('/authenticate_user', options)
|
79
|
+
end
|
9
80
|
end
|
10
81
|
end
|
11
82
|
end
|
data/lib/chef-api/schema.rb
CHANGED
@@ -13,7 +13,6 @@ module ChefAPI
|
|
13
13
|
attr_reader :attributes
|
14
14
|
|
15
15
|
attr_reader :ignored_attributes
|
16
|
-
attr_reader :transformations
|
17
16
|
|
18
17
|
#
|
19
18
|
# The list of defined validators for this schema.
|
@@ -28,15 +27,10 @@ module ChefAPI
|
|
28
27
|
def initialize(&block)
|
29
28
|
@attributes = {}
|
30
29
|
@ignored_attributes = {}
|
31
|
-
@
|
30
|
+
@flavor_attributes = {}
|
32
31
|
@validators = []
|
33
32
|
|
34
|
-
instance_eval(&block) if block
|
35
|
-
|
36
|
-
@attributes.freeze
|
37
|
-
@ignored_attributes.freeze
|
38
|
-
@transformations.freeze
|
39
|
-
@validators.freeze
|
33
|
+
unlock { instance_eval(&block) } if block
|
40
34
|
end
|
41
35
|
|
42
36
|
#
|
@@ -49,6 +43,45 @@ module ChefAPI
|
|
49
43
|
@primary_key ||= @attributes.first[0]
|
50
44
|
end
|
51
45
|
|
46
|
+
#
|
47
|
+
# Create a lazy-loaded block for a given flavor.
|
48
|
+
#
|
49
|
+
# @example Create a block for Enterprise Chef
|
50
|
+
# flavor :enterprise do
|
51
|
+
# attribute :custom_value
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# @param [Symbol] id
|
55
|
+
# the id of the flavor to target
|
56
|
+
# @param [Proc] block
|
57
|
+
# the block to capture
|
58
|
+
#
|
59
|
+
# @return [Proc]
|
60
|
+
# the given block
|
61
|
+
#
|
62
|
+
def flavor(id, &block)
|
63
|
+
@flavor_attributes[id] = block
|
64
|
+
block
|
65
|
+
end
|
66
|
+
|
67
|
+
#
|
68
|
+
# Load the flavor block for the given id.
|
69
|
+
#
|
70
|
+
# @param [Symbol] id
|
71
|
+
# the id of the flavor to target
|
72
|
+
#
|
73
|
+
# @return [true, false]
|
74
|
+
# true if the flavor existed and was evaluted, false otherwise
|
75
|
+
#
|
76
|
+
def load_flavor(id)
|
77
|
+
if block = @flavor_attributes[id]
|
78
|
+
unlock { instance_eval(&block) }
|
79
|
+
true
|
80
|
+
else
|
81
|
+
false
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
52
85
|
#
|
53
86
|
# DSL method for defining an attribute.
|
54
87
|
#
|
@@ -91,22 +124,27 @@ module ChefAPI
|
|
91
124
|
end
|
92
125
|
end
|
93
126
|
|
127
|
+
private
|
128
|
+
|
94
129
|
#
|
95
|
-
#
|
96
|
-
#
|
97
|
-
# @example Transform the +:bacon+ attribute onto the +:ham+ attribute
|
98
|
-
# transform :bacon, ham: true
|
99
|
-
#
|
100
|
-
# @example Transform an attribute with a complex transformation
|
101
|
-
# transform :bacon, ham: ->(value) { value.split('__', 2).last }
|
130
|
+
# @private
|
102
131
|
#
|
103
|
-
#
|
104
|
-
#
|
105
|
-
#
|
106
|
-
# the key-value pair of the transformations to make
|
132
|
+
# Helper method to duplicate and unfreeze all the attributes in the schema,
|
133
|
+
# yield control to the user for modification in the current context, and
|
134
|
+
# then re-freeze the variables for modification.
|
107
135
|
#
|
108
|
-
def
|
109
|
-
@
|
136
|
+
def unlock
|
137
|
+
@attributes = @attributes.dup
|
138
|
+
@ignored_attributes = @ignored_attributes.dup
|
139
|
+
@flavor_attributes = @flavor_attributes.dup
|
140
|
+
@validators = @validators.dup
|
141
|
+
|
142
|
+
yield
|
143
|
+
|
144
|
+
@attributes.freeze
|
145
|
+
@ignored_attributes.freeze
|
146
|
+
@flavor_attributes.freeze
|
147
|
+
@validators.freeze
|
110
148
|
end
|
111
149
|
end
|
112
150
|
end
|
data/lib/chef-api/version.rb
CHANGED
data/lib/chef-api.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
require 'json'
|
2
|
+
require 'logify'
|
2
3
|
require 'pathname'
|
3
4
|
require 'chef-api/version'
|
4
5
|
|
6
|
+
# Do not inflate JSON objects
|
7
|
+
JSON.create_id = nil
|
8
|
+
|
5
9
|
module ChefAPI
|
6
10
|
autoload :Boolean, 'chef-api/boolean'
|
7
11
|
autoload :Configurable, 'chef-api/configurable'
|
@@ -9,7 +13,6 @@ module ChefAPI
|
|
9
13
|
autoload :Defaults, 'chef-api/defaults'
|
10
14
|
autoload :Error, 'chef-api/errors'
|
11
15
|
autoload :ErrorCollection, 'chef-api/error_collection'
|
12
|
-
autoload :Logger, 'chef-api/logger'
|
13
16
|
autoload :Resource, 'chef-api/resource'
|
14
17
|
autoload :Schema, 'chef-api/schema'
|
15
18
|
autoload :Util, 'chef-api/util'
|
@@ -23,6 +26,28 @@ module ChefAPI
|
|
23
26
|
class << self
|
24
27
|
include ChefAPI::Configurable
|
25
28
|
|
29
|
+
#
|
30
|
+
# Set the log level.
|
31
|
+
#
|
32
|
+
# @example Set the log level to :info
|
33
|
+
# ChefAPI.log_level = :info
|
34
|
+
#
|
35
|
+
# @param [Symbol] level
|
36
|
+
# the log level to set
|
37
|
+
#
|
38
|
+
def log_level=(level)
|
39
|
+
Logify.level = level
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# Get the current log level.
|
44
|
+
#
|
45
|
+
# @return [Symbol]
|
46
|
+
#
|
47
|
+
def log_level
|
48
|
+
Logify.level
|
49
|
+
end
|
50
|
+
|
26
51
|
#
|
27
52
|
# The source root of the ChefAPI gem. This is useful when requiring files
|
28
53
|
# that are relative to the root of the project.
|
@@ -67,10 +92,6 @@ module ChefAPI
|
|
67
92
|
end
|
68
93
|
end
|
69
94
|
|
70
|
-
require 'i18n'
|
71
|
-
I18n.enforce_available_locales = false
|
72
|
-
I18n.load_path << Dir[ChefAPI.root.join('locales', '*.yml').to_s]
|
73
|
-
|
74
95
|
# Load the initial default values
|
75
96
|
ChefAPI.setup
|
76
97
|
|
@@ -4,5 +4,59 @@ module ChefAPI
|
|
4
4
|
describe Resource::Client do
|
5
5
|
it_behaves_like 'a Chef API resource', :client,
|
6
6
|
update: { validator: true }
|
7
|
+
|
8
|
+
describe '.from_file' do
|
9
|
+
let(:private_key) do
|
10
|
+
<<-EOH.strip.gsub(/^ {10}/, '')
|
11
|
+
-----BEGIN RSA PRIVATE KEY-----
|
12
|
+
MIIEogIBAAKCAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzI
|
13
|
+
w+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoP
|
14
|
+
kcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2
|
15
|
+
hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NO
|
16
|
+
Td0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcW
|
17
|
+
yLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQIBIwKCAQEA4iqWPJXtzZA68mKd
|
18
|
+
ELs4jJsdyky+ewdZeNds5tjcnHU5zUYE25K+ffJED9qUWICcLZDc81TGWjHyAqD1
|
19
|
+
Bw7XpgUwFgeUJwUlzQurAv+/ySnxiwuaGJfhFM1CaQHzfXphgVml+fZUvnJUTvzf
|
20
|
+
TK2Lg6EdbUE9TarUlBf/xPfuEhMSlIE5keb/Zz3/LUlRg8yDqz5w+QWVJ4utnKnK
|
21
|
+
iqwZN0mwpwU7YSyJhlT4YV1F3n4YjLswM5wJs2oqm0jssQu/BT0tyEXNDYBLEF4A
|
22
|
+
sClaWuSJ2kjq7KhrrYXzagqhnSei9ODYFShJu8UWVec3Ihb5ZXlzO6vdNQ1J9Xsf
|
23
|
+
4m+2ywKBgQD6qFxx/Rv9CNN96l/4rb14HKirC2o/orApiHmHDsURs5rUKDx0f9iP
|
24
|
+
cXN7S1uePXuJRK/5hsubaOCx3Owd2u9gD6Oq0CsMkE4CUSiJcYrMANtx54cGH7Rk
|
25
|
+
EjFZxK8xAv1ldELEyxrFqkbE4BKd8QOt414qjvTGyAK+OLD3M2QdCQKBgQDtx8pN
|
26
|
+
CAxR7yhHbIWT1AH66+XWN8bXq7l3RO/ukeaci98JfkbkxURZhtxV/HHuvUhnPLdX
|
27
|
+
3TwygPBYZFNo4pzVEhzWoTtnEtrFueKxyc3+LjZpuo+mBlQ6ORtfgkr9gBVphXZG
|
28
|
+
YEzkCD3lVdl8L4cw9BVpKrJCs1c5taGjDgdInQKBgHm/fVvv96bJxc9x1tffXAcj
|
29
|
+
3OVdUN0UgXNCSaf/3A/phbeBQe9xS+3mpc4r6qvx+iy69mNBeNZ0xOitIjpjBo2+
|
30
|
+
dBEjSBwLk5q5tJqHmy/jKMJL4n9ROlx93XS+njxgibTvU6Fp9w+NOFD/HvxB3Tcz
|
31
|
+
6+jJF85D5BNAG3DBMKBjAoGBAOAxZvgsKN+JuENXsST7F89Tck2iTcQIT8g5rwWC
|
32
|
+
P9Vt74yboe2kDT531w8+egz7nAmRBKNM751U/95P9t88EDacDI/Z2OwnuFQHCPDF
|
33
|
+
llYOUI+SpLJ6/vURRbHSnnn8a/XG+nzedGH5JGqEJNQsz+xT2axM0/W/CRknmGaJ
|
34
|
+
kda/AoGANWrLCz708y7VYgAtW2Uf1DPOIYMdvo6fxIB5i9ZfISgcJ/bbCUkFrhoH
|
35
|
+
+vq/5CIWxCPp0f85R4qxxQ5ihxJ0YDQT9Jpx4TMss4PSavPaBH3RXow5Ohe+bYoQ
|
36
|
+
NE5OgEXk2wVfZczCZpigBKbKZHNYcelXtTt/nP3rsCuGcM4h53s=
|
37
|
+
-----END RSA PRIVATE KEY-----
|
38
|
+
EOH
|
39
|
+
end
|
40
|
+
|
41
|
+
let(:client) { described_class.from_file('/path/to/bacon.pem') }
|
42
|
+
|
43
|
+
before do
|
44
|
+
File.stub(:read).and_return(private_key)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'loads the client from the server' do
|
48
|
+
chef_server.create_client('bacon', validator: true)
|
49
|
+
|
50
|
+
expect(client.name).to eq('bacon')
|
51
|
+
expect(client.private_key).to eq(private_key)
|
52
|
+
expect(client.validator).to be_true
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'creates a new instance when the client does not exist' do
|
56
|
+
expect(client.name).to eq('bacon')
|
57
|
+
expect(client.validator).to be_false
|
58
|
+
expect(client.new_resource?).to be_true
|
59
|
+
end
|
60
|
+
end
|
7
61
|
end
|
8
62
|
end
|
data/spec/spec_helper.rb
CHANGED