dm-chef-adapter 0.1.0 → 0.2.0

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.
@@ -2,6 +2,7 @@ require 'json'
2
2
  require 'chef'
3
3
  require 'dm-core'
4
4
  require 'uuidtools'
5
+
5
6
  module DataMapper
6
7
  class Property
7
8
  class DocId < String
@@ -47,8 +48,10 @@ module DataMapper
47
48
  data = {"id" => resource.key.join('_')}
48
49
  data.merge! Chef::JSONCompat.from_json(resource.to_json)
49
50
  databag_item.raw_data = data
50
- #binding.pry
51
51
  databag_item.create
52
+ if !@cache.nil?
53
+ @cache.delete(resource.class.storage_name)
54
+ end
52
55
  else
53
56
  raise "DataBagItem #{resource.class.storage_name}/#{resource.key.join('_')} already exists."
54
57
  end
@@ -81,6 +84,9 @@ module DataMapper
81
84
  databag_item = Chef::DataBagItem.load collection.storage_name, doc["id"]
82
85
  databag_item.raw_data.merge! Chef::JSONCompat.from_json(fields.to_json)
83
86
  databag_item.save
87
+ if !@cache.nil?
88
+ @cache.delete(collection.storage_name)
89
+ end
84
90
  end
85
91
  end
86
92
 
@@ -89,6 +95,9 @@ module DataMapper
89
95
  read(collection.query).each do |doc|
90
96
  databag_item = Chef::DataBagItem.load collection.storage_name, doc["id"]
91
97
  databag_item.destroy collection.storage_name, doc["id"]
98
+ if !@cache.nil?
99
+ @cache.delete(collection.storage_name)
100
+ end
92
101
  end
93
102
  end
94
103
 
@@ -107,9 +116,14 @@ module DataMapper
107
116
  def initialize(name, opts = {})
108
117
  super
109
118
  Chef::Config.configuration[:node_name] = opts["node_name"]
110
- Chef::Config.configuration[:client_key] = opts["client_key"]
111
- Chef::Config.configuration[:chef_server_url] = opts["chef_server_url"] if !opts["chef_server_url"].nil?
112
- @chef = Chef::REST.new(Chef::Config[:chef_server_url])
119
+ Chef::Config.configuration[:client_key] = opts["client_key"]
120
+ Chef::Config.configuration[:chef_server_url] = opts["chef_server_url"] if !opts["chef_server_url"].nil?
121
+ if opts["memcache"]
122
+ require 'dalli'
123
+ @cache = Dalli::Client.new(opts["memcache_url"])
124
+ else
125
+ @cache = nil
126
+ end
113
127
  @uuid = UUIDTools::UUID
114
128
  end
115
129
 
@@ -139,15 +153,19 @@ module DataMapper
139
153
  #
140
154
  # @api private
141
155
  def records_for(model)
142
- records = []
143
- databag = chef_databag(model)
144
- if !databag.nil?
156
+ if !@cache.nil?
157
+ return @cache.get(model) if !@cache.get(model).nil?
158
+ end
159
+ records = []
160
+ databag = chef_databag(model)
161
+ if !databag.nil?
145
162
  chef_databag(model).keys.each do |key|
146
163
  records << Chef::DataBagItem.load(model, key).raw_data
147
164
  end
148
165
  end
149
- records
150
- end
166
+ @cache.set(model, records, 60) if !@cache.nil?
167
+ return records
168
+ end
151
169
 
152
170
  # Writes all records to a databag
153
171
  #
@@ -0,0 +1,204 @@
1
+ require 'json'
2
+ require 'chef'
3
+ require 'dm-core'
4
+ require 'uuidtools'
5
+ require 'pry'
6
+
7
+ module DataMapper
8
+ class Property
9
+ class DocId < String
10
+ key true
11
+ writer false
12
+ default ''
13
+ # @api private
14
+ def to_child_key
15
+ Property::String
16
+ end
17
+
18
+ end # class DocId
19
+ end # module Property
20
+ module Is
21
+ module Chef
22
+ def is_chef(options={})
23
+ require 'dm-serializer'
24
+ property :id, DataMapper::Property::DocId
25
+ end
26
+ end
27
+ end
28
+ module Adapters
29
+ class ChefAdapter < AbstractAdapter
30
+ # @api semipublic
31
+ def create(resources)
32
+ resources.collect do |resource|
33
+ if !Chef::DataBag.list.keys.include?(resource.class.storage_name)
34
+ databag = Chef::DataBag.new
35
+ databag.name resource.class.storage_name
36
+ databag.create
37
+ end
38
+ begin
39
+ resource.id = @uuid.sha1_create(
40
+ UUIDTools::UUID_DNS_NAMESPACE,
41
+ ("%10.6f#{resource.class.storage_name}" % Time.now.to_f)
42
+ )
43
+ rescue
44
+ resource.send :instance_variable_set, :@id, resource.key
45
+ end
46
+ if !Chef::DataBag.load(resource.class.storage_name).keys.include?(resource.key.join('_'))
47
+ databag_item = Chef::DataBagItem.new
48
+ databag_item.data_bag resource.class.storage_name
49
+ data = {"id" => resource.key.join('_')}
50
+ data.merge! Chef::JSONCompat.from_json(resource.to_json)
51
+ databag_item.raw_data = data
52
+ databag_item.create
53
+ else
54
+ raise "DataBagItem #{resource.class.storage_name}/#{resource.key.join('_')} already exists."
55
+ end
56
+ end.count
57
+ end
58
+
59
+ # @api semipublic
60
+ def read(query)
61
+ records = records_for(query.model.storage_name)
62
+ ret = []
63
+ query.links.reverse.each do |link|
64
+ children = records_for(link.child_model.storage_name)
65
+ parents = records_for(link.parent_model.storage_name)
66
+ children.each do |child|
67
+ parents.each do |parent|
68
+ if child[link.child_key.first.name.to_s] == parent[link.parent_key.first.name.to_s]
69
+ records.each do |record|
70
+ ret << child.merge(record)
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ query.filter_records(ret+records)
77
+ end
78
+
79
+ def update(attributes, collection)
80
+ fields = attributes_as_fields(attributes)
81
+ read(collection.query).each do |doc|
82
+ databag_item = Chef::DataBagItem.load collection.storage_name, doc["id"]
83
+ databag_item.raw_data.merge! Chef::JSONCompat.from_json(fields.to_json)
84
+ databag_item.save
85
+ end
86
+ end
87
+
88
+ # @api semipublic
89
+ def delete(collection)
90
+ read(collection.query).each do |doc|
91
+ databag_item = Chef::DataBagItem.load collection.storage_name, doc["id"]
92
+ databag_item.destroy collection.storage_name, doc["id"]
93
+ end
94
+ end
95
+
96
+ # @api semipublic
97
+ def attributes_as_fields(attributes)
98
+ pairs = attributes.map do |property, value|
99
+ dumped = value.kind_of?(Module) ? value.name : property.dump(value)
100
+ [ property.field, dumped ]
101
+ end
102
+ Hash[pairs]
103
+ end
104
+
105
+ private
106
+
107
+ # @api semipublic
108
+ def initialize(name, opts = {})
109
+ super
110
+ Chef::Config.configuration[:node_name] = opts["node_name"]
111
+ Chef::Config.configuration[:client_key] = opts["client_key"]
112
+ Chef::Config.configuration[:chef_server_url] = opts["chef_server_url"] if !opts["chef_server_url"].nil?
113
+ if !opts["cache"].nil?
114
+ Chef::Config.configuration[:dm_cache] = opts["cache"]
115
+ else
116
+ Chef::Config.configuration[:dm_cache] = 60
117
+ end
118
+ @cache = {}
119
+ @chef = Chef::REST.new(Chef::Config[:chef_server_url])
120
+ @uuid = UUIDTools::UUID
121
+ end
122
+
123
+ # Retrieves all records for a model and yields them to a block.
124
+ #
125
+ # The block should make any changes to the records in-place. After
126
+ # the block executes all the records are dumped back to the databag.
127
+ #
128
+ # @param [Model, #to_s] model
129
+ # Used to determine which file to read/write to
130
+ #
131
+ # @yieldparam [Hash]
132
+ # A hash of record.key => record pairs retrieved from the databag
133
+ #
134
+ # @api private
135
+ def update_records(model)
136
+ records = records_for(model)
137
+ result = yield records
138
+ write_records(model, records)
139
+ result
140
+ end
141
+
142
+ # Read all records from a databag for a model
143
+ #
144
+ # @param [#storage_name] model
145
+ # The model/name to retieve records for
146
+ #
147
+ # @api private
148
+ def records_for(model)
149
+ if !@cache[model].nil? && (Time.now - @cache[model][:timestamp]) < Chef::Config.configuration[:dm_cache]
150
+ return @cache[model][:records]
151
+ else
152
+ records = []
153
+ databag = chef_databag(model)
154
+ if !databag.nil?
155
+ chef_databag(model).keys.each do |key|
156
+ records << Chef::DataBagItem.load(model, key).raw_data
157
+ end
158
+ end
159
+ @cache[model] = {}
160
+ @cache[model][:timestamp] = Time.now
161
+ @cache[model][:records] = records
162
+ return records
163
+ end
164
+ end
165
+
166
+ # Writes all records to a databag
167
+ #
168
+ # @param [#storage_name] model
169
+ # The model/name to write the records for
170
+ #
171
+ # @param [Hash] records
172
+ # A hash of record.key => record pairs to be written
173
+ #
174
+ # @api private
175
+ def write_records(model, records)
176
+ item = Chef::DataBagItem.load(model. records["id"])
177
+ item.from_hash records
178
+ item.save
179
+ end
180
+
181
+ # Given a model, gives the databag to be used for record storage
182
+ #
183
+ # @example
184
+ # chef_databag(Article) #=> "Chef::DataBag"
185
+ #
186
+ # @param [#storage_name] model
187
+ # The model to be used to determine the databag.
188
+ #
189
+ # @api private
190
+ def chef_databag(model)
191
+ begin
192
+ Chef::DataBag.load model
193
+ rescue
194
+ nil
195
+ end
196
+ end
197
+
198
+ end # class ChefAdapter
199
+
200
+ const_added(:ChefAdapter)
201
+ end # module Adapters
202
+ Model.append_extensions(Is::Chef)
203
+ end # module DataMapper
204
+
@@ -1 +1 @@
1
- DMCHEFADAPTER_VERSION = "0.1.0"
1
+ DMCHEFADAPTER_VERSION = "0.2.0"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dm-chef-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-02-01 00:00:00.000000000Z
12
+ date: 2012-03-07 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: chef
16
- requirement: &81217850 !ruby/object:Gem::Requirement
16
+ requirement: &9732900 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - =
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 0.10.8
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *81217850
24
+ version_requirements: *9732900
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: dm-core
27
- requirement: &81217580 !ruby/object:Gem::Requirement
27
+ requirement: &9732400 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - =
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 1.2.0
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *81217580
35
+ version_requirements: *9732400
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: dm-serializer
38
- requirement: &81217290 !ruby/object:Gem::Requirement
38
+ requirement: &9731940 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - =
@@ -43,7 +43,18 @@ dependencies:
43
43
  version: 1.2.1
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *81217290
46
+ version_requirements: *9731940
47
+ - !ruby/object:Gem::Dependency
48
+ name: dalli
49
+ requirement: &9731560 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *9731560
47
58
  description: uses chef databags as a backend for the datamapper ORM
48
59
  email:
49
60
  - ncethier@gmail.com
@@ -51,9 +62,10 @@ executables: []
51
62
  extensions: []
52
63
  extra_rdoc_files: []
53
64
  files:
65
+ - lib/dm-chef-adapter.rb
54
66
  - lib/dm-chef-adapter/version.rb
55
67
  - lib/dm-chef-adapter/adapter.rb
56
- - lib/dm-chef-adapter.rb
68
+ - lib/dm-chef-adapter/adapter.rb~
57
69
  - examples/blog.rb
58
70
  homepage: https://github.com/nickethier/dm-chef-adapter
59
71
  licenses:
@@ -77,7 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
77
89
  version: '0'
78
90
  requirements: []
79
91
  rubyforge_project:
80
- rubygems_version: 1.8.10
92
+ rubygems_version: 1.8.15
81
93
  signing_key:
82
94
  specification_version: 3
83
95
  summary: datamapper adapter to use a chef server as a datastore backend