dm-chef-adapter 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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