chef-infra-api 0.9.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 +7 -0
- data/LICENSE +201 -0
- data/lib/chef-api.rb +96 -0
- data/lib/chef-api/aclable.rb +35 -0
- data/lib/chef-api/authentication.rb +300 -0
- data/lib/chef-api/boolean.rb +6 -0
- data/lib/chef-api/configurable.rb +80 -0
- data/lib/chef-api/connection.rb +507 -0
- data/lib/chef-api/defaults.rb +197 -0
- data/lib/chef-api/error_collection.rb +44 -0
- data/lib/chef-api/errors.rb +64 -0
- data/lib/chef-api/multipart.rb +164 -0
- data/lib/chef-api/resource.rb +21 -0
- data/lib/chef-api/resources/base.rb +960 -0
- data/lib/chef-api/resources/client.rb +84 -0
- data/lib/chef-api/resources/collection_proxy.rb +234 -0
- data/lib/chef-api/resources/cookbook.rb +24 -0
- data/lib/chef-api/resources/cookbook_version.rb +23 -0
- data/lib/chef-api/resources/data_bag.rb +136 -0
- data/lib/chef-api/resources/data_bag_item.rb +53 -0
- data/lib/chef-api/resources/environment.rb +16 -0
- data/lib/chef-api/resources/group.rb +16 -0
- data/lib/chef-api/resources/node.rb +20 -0
- data/lib/chef-api/resources/organization.rb +22 -0
- data/lib/chef-api/resources/partial_search.rb +44 -0
- data/lib/chef-api/resources/principal.rb +11 -0
- data/lib/chef-api/resources/role.rb +18 -0
- data/lib/chef-api/resources/search.rb +47 -0
- data/lib/chef-api/resources/user.rb +82 -0
- data/lib/chef-api/schema.rb +150 -0
- data/lib/chef-api/util.rb +119 -0
- data/lib/chef-api/validator.rb +16 -0
- data/lib/chef-api/validators/base.rb +82 -0
- data/lib/chef-api/validators/required.rb +11 -0
- data/lib/chef-api/validators/type.rb +23 -0
- data/lib/chef-api/version.rb +3 -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 +130 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
module ChefAPI
|
2
|
+
class Resource::DataBagItem < Resource::Base
|
3
|
+
collection_path '/data/:bag'
|
4
|
+
|
5
|
+
schema do
|
6
|
+
attribute :id, type: String, primary: true, required: true
|
7
|
+
attribute :data, type: Hash, default: {}
|
8
|
+
end
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def from_file(path, bag = File.basename(File.dirname(path)))
|
12
|
+
id, contents = Util.safe_read(path)
|
13
|
+
data = JSON.parse(contents)
|
14
|
+
data[:id] = id
|
15
|
+
|
16
|
+
bag = bag.is_a?(Resource::DataBag) ? bag : Resource::DataBag.new(name: bag)
|
17
|
+
|
18
|
+
new(data, { bag: bag.name }, bag)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_reader :bag
|
23
|
+
|
24
|
+
#
|
25
|
+
# Override the initialize method to move any attributes into the +data+
|
26
|
+
# hash.
|
27
|
+
#
|
28
|
+
def initialize(attributes = {}, prefix = {}, bag = nil)
|
29
|
+
@bag = bag || Resource::DataBag.fetch(prefix[:bag])
|
30
|
+
|
31
|
+
id = attributes.delete(:id) || attributes.delete('id')
|
32
|
+
super({ id: id, data: attributes }, prefix)
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
#
|
37
|
+
# Override the to_hash method to move data to the upper scope.
|
38
|
+
#
|
39
|
+
# @see (Resource::Base#to_hash)
|
40
|
+
#
|
41
|
+
def to_hash
|
42
|
+
{}.tap do |hash|
|
43
|
+
_attributes.each do |key, value|
|
44
|
+
if key == :data
|
45
|
+
hash.merge!(value)
|
46
|
+
else
|
47
|
+
hash[key] = value.respond_to?(:to_hash) ? value.to_hash : value
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ChefAPI
|
2
|
+
class Resource::Environment < Resource::Base
|
3
|
+
collection_path '/environments'
|
4
|
+
|
5
|
+
schema do
|
6
|
+
attribute :name, type: String, primary: true, required: true
|
7
|
+
attribute :description, type: String
|
8
|
+
attribute :default_attributes, type: Hash, default: {}
|
9
|
+
attribute :override_attributes, type: Hash, default: {}
|
10
|
+
attribute :cookbook_versions, type: Hash, default: {}
|
11
|
+
end
|
12
|
+
|
13
|
+
has_many :cookbooks
|
14
|
+
has_many :nodes
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ChefAPI
|
2
|
+
class Resource::Group < Resource::Base
|
3
|
+
collection_path '/groups'
|
4
|
+
|
5
|
+
schema do
|
6
|
+
attribute :groupname, type: String, primary: true, required: true
|
7
|
+
attribute :name, type: String
|
8
|
+
attribute :orgname, type: String
|
9
|
+
attribute :actors, type: Array, default: []
|
10
|
+
attribute :users, type: Array, default: []
|
11
|
+
attribute :clients, type: Array, default: []
|
12
|
+
attribute :groups, type: Array, default: []
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ChefAPI
|
2
|
+
class Resource::Node < Resource::Base
|
3
|
+
include ChefAPI::AclAble
|
4
|
+
collection_path '/nodes'
|
5
|
+
|
6
|
+
schema do
|
7
|
+
attribute :name, type: String, primary: true, required: true
|
8
|
+
attribute :automatic, type: Hash, default: {}
|
9
|
+
attribute :default, type: Hash, default: {}
|
10
|
+
attribute :normal, type: Hash, default: {}
|
11
|
+
attribute :override, type: Hash, default: {}
|
12
|
+
attribute :run_list, type: Array, default: []
|
13
|
+
attribute :policy_name, type: String
|
14
|
+
attribute :policy_group, type: String
|
15
|
+
|
16
|
+
# Enterprise Chef attributes
|
17
|
+
attribute :chef_environment, type: String, default: '_default'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
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
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module ChefAPI
|
2
|
+
class Resource::PartialSearch < Resource::Base
|
3
|
+
collection_path '/search/:index'
|
4
|
+
|
5
|
+
schema do
|
6
|
+
attribute :total, type: Integer
|
7
|
+
attribute :start, type: Integer
|
8
|
+
attribute :rows, type: Array
|
9
|
+
end
|
10
|
+
|
11
|
+
class << self
|
12
|
+
#
|
13
|
+
# About search : https://docs.chef.io/chef_search.html
|
14
|
+
#
|
15
|
+
# @param [String] index
|
16
|
+
# the name of the index to search
|
17
|
+
# @param [Hash] keys
|
18
|
+
# key paths for the attributes to be returned
|
19
|
+
# @param [String] query
|
20
|
+
# the query string
|
21
|
+
# @param [Hash] options
|
22
|
+
# the query string
|
23
|
+
#
|
24
|
+
# @return [self]
|
25
|
+
# the current resource
|
26
|
+
#
|
27
|
+
def query(index, keys, query = '*:*', options = {})
|
28
|
+
return nil if index.nil?
|
29
|
+
|
30
|
+
params = {}.tap do |o|
|
31
|
+
o[:q] = query
|
32
|
+
o[:rows] = options[:rows] || 1000
|
33
|
+
o[:sort] = options[:sort] || 'X_CHEF_id_CHEF_X'
|
34
|
+
o[:start] = options[:start] || 0
|
35
|
+
end
|
36
|
+
|
37
|
+
path = expanded_collection_path(index: index.to_s)
|
38
|
+
response = connection.post(path, keys.to_json, params)
|
39
|
+
response['rows'].map! { |row| row['data'] }
|
40
|
+
from_json(response, index: index.to_s)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ChefAPI
|
2
|
+
class Resource::Role < Resource::Base
|
3
|
+
include ChefAPI::AclAble
|
4
|
+
collection_path '/roles'
|
5
|
+
|
6
|
+
schema do
|
7
|
+
attribute :name, type: String, primary: true, required: true
|
8
|
+
attribute :json_class, type: String, default: "Chef::Role"
|
9
|
+
attribute :description, type: String
|
10
|
+
attribute :default_attributes, type: Hash, default: {}
|
11
|
+
attribute :override_attributes, type: Hash, default: {}
|
12
|
+
attribute :run_list, type: Array, default: []
|
13
|
+
attribute :env_run_lists, type: Hash, default: {}
|
14
|
+
end
|
15
|
+
|
16
|
+
has_many :environments
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module ChefAPI
|
2
|
+
class Resource::Search < Resource::Base
|
3
|
+
collection_path '/search/:index'
|
4
|
+
|
5
|
+
schema do
|
6
|
+
attribute :total, type: Integer
|
7
|
+
attribute :start, type: Integer
|
8
|
+
attribute :rows, type: Array
|
9
|
+
end
|
10
|
+
|
11
|
+
class << self
|
12
|
+
#
|
13
|
+
# About search : https://docs.chef.io/chef_search.html
|
14
|
+
#
|
15
|
+
# @param [String] index
|
16
|
+
# the name of the index to search
|
17
|
+
# @param [String] query
|
18
|
+
# the query string
|
19
|
+
# @param [Hash] options
|
20
|
+
# the query string
|
21
|
+
#
|
22
|
+
# @return [self]
|
23
|
+
# the current resource
|
24
|
+
#
|
25
|
+
def query(index, query = '*:*', options = {})
|
26
|
+
return nil if index.nil?
|
27
|
+
|
28
|
+
params = {}.tap do |o|
|
29
|
+
o[:q] = query
|
30
|
+
o[:rows] = options[:rows] || 1000
|
31
|
+
o[:sort] = options[:sort] || 'X_CHEF_id_CHEF_X'
|
32
|
+
o[:start] = options[:start] || 0
|
33
|
+
end
|
34
|
+
|
35
|
+
path = expanded_collection_path(index: index.to_s)
|
36
|
+
|
37
|
+
response = if filter_result = options[:filter_result]
|
38
|
+
connection.post(path, filter_result.to_json, params)
|
39
|
+
else
|
40
|
+
connection.get(path, params)
|
41
|
+
end
|
42
|
+
|
43
|
+
from_json(response, index: index.to_s)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module ChefAPI
|
2
|
+
class Resource::User < Resource::Base
|
3
|
+
collection_path '/users'
|
4
|
+
|
5
|
+
schema do
|
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
|
+
|
24
|
+
attribute :admin, type: Boolean, default: false
|
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.to_json)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
module ChefAPI
|
2
|
+
#
|
3
|
+
# A wrapper class that describes a remote schema (such as the Chef Server
|
4
|
+
# API layer), with validation and other magic spinkled on top.
|
5
|
+
#
|
6
|
+
class Schema
|
7
|
+
|
8
|
+
#
|
9
|
+
# The full list of attributes defined on this schema.
|
10
|
+
#
|
11
|
+
# @return [Hash]
|
12
|
+
#
|
13
|
+
attr_reader :attributes
|
14
|
+
|
15
|
+
attr_reader :ignored_attributes
|
16
|
+
|
17
|
+
#
|
18
|
+
# The list of defined validators for this schema.
|
19
|
+
#
|
20
|
+
# @return [Array]
|
21
|
+
#
|
22
|
+
attr_reader :validators
|
23
|
+
|
24
|
+
#
|
25
|
+
# Create a new schema and evaulte the block contents in a clean room.
|
26
|
+
#
|
27
|
+
def initialize(&block)
|
28
|
+
@attributes = {}
|
29
|
+
@ignored_attributes = {}
|
30
|
+
@flavor_attributes = {}
|
31
|
+
@validators = []
|
32
|
+
|
33
|
+
unlock { instance_eval(&block) } if block
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# The defined primary key for this schema. If no primary key is given, it
|
38
|
+
# is assumed to be the first item in the list.
|
39
|
+
#
|
40
|
+
# @return [Symbol]
|
41
|
+
#
|
42
|
+
def primary_key
|
43
|
+
@primary_key ||= @attributes.first[0]
|
44
|
+
end
|
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
|
+
|
85
|
+
#
|
86
|
+
# DSL method for defining an attribute.
|
87
|
+
#
|
88
|
+
# @param [Symbol] key
|
89
|
+
# the key to use
|
90
|
+
# @param [Hash] options
|
91
|
+
# a list of options to create the attribute with
|
92
|
+
#
|
93
|
+
# @return [Symbol]
|
94
|
+
# the attribute
|
95
|
+
#
|
96
|
+
def attribute(key, options = {})
|
97
|
+
if primary_key = options.delete(:primary)
|
98
|
+
@primary_key = key.to_sym
|
99
|
+
end
|
100
|
+
|
101
|
+
@attributes[key] = options.delete(:default)
|
102
|
+
|
103
|
+
# All remaining options are assumed to be validations
|
104
|
+
options.each do |validation, options|
|
105
|
+
if options
|
106
|
+
@validators << Validator.find(validation).new(key, options)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
key
|
111
|
+
end
|
112
|
+
|
113
|
+
#
|
114
|
+
# Ignore an attribute. This is handy if you know there's an attribute that
|
115
|
+
# the remote server will return, but you don't want that information
|
116
|
+
# exposed to the user (or the data is sensitive).
|
117
|
+
#
|
118
|
+
# @param [Array<Symbol>] keys
|
119
|
+
# the list of attributes to ignore
|
120
|
+
#
|
121
|
+
def ignore(*keys)
|
122
|
+
keys.each do |key|
|
123
|
+
@ignored_attributes[key.to_sym] = true
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
#
|
130
|
+
# @private
|
131
|
+
#
|
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.
|
135
|
+
#
|
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
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|