chef_data_region 1.0.3

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.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/chef/data_region.rb +96 -0
  3. metadata +163 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 54ead7a041eac34a55ac2e89f139b3b21ad602c3
4
+ data.tar.gz: a313837d1e0dc1bf7e803df7ceb697cacd3e9eab
5
+ SHA512:
6
+ metadata.gz: 38182f2d5e2ac8488e8410e1dab9e679e77b81c50f28cd6e8fa9b115bea0d6c3445da6920d778e0a5acdaf5324290ce6f3cba9d92cbd0b07721a5618f894643a
7
+ data.tar.gz: '08782a64f0fb303c0489b9f39767aedba09d1ac92cee06c4b463ac40a1a3e3dbb15e066049633d9deba3a758ce2307ce73ee9ef8e3d95990432773e170d3baed'
@@ -0,0 +1,96 @@
1
+ require 'chef/data_bag_item'
2
+ require 'chef/encrypted_data_bag_item'
3
+ require 'chef/recipe'
4
+
5
+ # Chef class
6
+ class Chef
7
+ # DataRegion class
8
+ class DataRegion
9
+ NAME = 'chef_data_region'.freeze
10
+ VERSION = '1.0.3'.freeze
11
+
12
+ # This class variable maps data bags to expansion patterns
13
+ @bags = {}
14
+
15
+ # Return the configured data bags
16
+ def self.bags
17
+ @bags
18
+ end
19
+
20
+ # Add a bag definition to the set of bags
21
+ def self.add(bag_name, bag_hash)
22
+ @bags[bag_name] = bag_hash
23
+ end
24
+
25
+ # DataQuery module
26
+ #
27
+ # This module borrows its name from {Chef::DSL::DataQuery}, which
28
+ # defines the `data_bag_item` method. It exists for mixing in to
29
+ # {Chef::Recipe}, as occurs with {Chef::DSL::DataQuery}. This is
30
+ # the means by which `data_bag_item` is available in Chef recipes
31
+ # without qualification.
32
+ module DataQuery
33
+ # Fetch the specified item from the specified bag
34
+ #
35
+ # @param bag [] data bag name
36
+ # @param item [] data bag item name
37
+ # @param secret [] encrypted data bag item secret key
38
+ # @return [Chef::DataBagItem] the fetched data bag item
39
+ def data_bag_item(bag, item, secret = nil)
40
+ loaded_item = Chef::DataBagItem.load(expand_bag_name(bag), item)
41
+ encrypted?(loaded_item) ? decrypt(loaded_item) : loaded_item
42
+ end
43
+
44
+ private
45
+
46
+ # Decrypt an encrypted item
47
+ #
48
+ # @param item [Chef::DataBagItem] a data bag item
49
+ def decrypt(item)
50
+ secret ||= Chef::EncryptedDataBagItem.load_secret
51
+ Chef::EncryptedDataBagItem.new(item.raw_data, secret)
52
+ end
53
+
54
+ # Is the given item encrypted?
55
+ #
56
+ # @param item [Chef::DataBagItem] a data bag item
57
+ def encrypted?(item)
58
+ item.raw_data.map do |_, value|
59
+ value.respond_to?(:key) && value.key?('encrypted_data')
60
+ end.reduce(false, :|)
61
+ end
62
+
63
+ # Expand a data bag name if it matches one of the configured patterns.
64
+ #
65
+ # If the name matches, consult the node attribute specified in
66
+ # the configuration to retrieve the region name, then substitute
67
+ # it into the expansion pattern.
68
+ #
69
+ # If the name does not match, return the name verbatim.
70
+ #
71
+ # @param bag_name [String] a data bag name
72
+ # @return [String] the expanded bag name
73
+ def expand_bag_name(bag_name)
74
+ if bags.include?(bag_name)
75
+ bag_definition = Chef::DataRegion.bags.fetch(bag_name)
76
+ format(
77
+ bag_definition[:pattern],
78
+ attribute: bag_definition[:attribute].reduce(node) do |h, i|
79
+ h.fetch(i)
80
+ end
81
+ )
82
+ else
83
+ bag_name
84
+ end
85
+ rescue KeyError
86
+ bag_name
87
+ rescue NoMethodError
88
+ raise("Undefined region for data bag '#{bag_name}'")
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ # Mix the module in to {Chef::Recipe}, thereby overriding
95
+ # Chef::DSL::DataQuery#data_bag_item
96
+ Chef::Recipe.include(Chef::DataRegion::DataQuery)
metadata ADDED
@@ -0,0 +1,163 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chef_data_region
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Someone
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-04-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rubocop
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.54'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.54'
41
+ - !ruby/object:Gem::Dependency
42
+ name: chef
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">"
46
+ - !ruby/object:Gem::Version
47
+ version: '11'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">"
53
+ - !ruby/object:Gem::Version
54
+ version: '11'
55
+ description: |
56
+ # Chef Data Region Gem
57
+
58
+ ## Motivation
59
+
60
+ This gem exists to address the following scenario:
61
+
62
+ An organization maintains data in Chef data bag items. The data is
63
+ deployed to several data center environments and is stored in data
64
+ bags whose names reference the environments. The organization wants to
65
+ write environment-agnostic recipes that access the data bags without
66
+ explicitly referencing the data bags by their environment names.
67
+
68
+ As a concrete example, imagine the organization maintains encrypted
69
+ data for three deployment environments: development, staging, and
70
+ production. It maintains this data in three data bags, one for each
71
+ environment, with data for services named `gadget` and `widget` in
72
+ items:
73
+
74
+ | Environment | Bag | Item |
75
+ |-------------+----------------+--------|
76
+ | Development | secure-dev | gadget |
77
+ | Development | secure-dev | widget |
78
+ | Production | secure-prod | gadget |
79
+ | Production | secure-prod | widget |
80
+ | Staging | secure-staging | gadget |
81
+ | Staging | secure-staging | widget |
82
+
83
+ The items are encrypted with a key unique to that environment to
84
+ maximize security.
85
+
86
+ Now consider how a recipe would access these bags. When then recipe is
87
+ running, it needs to know the data center environment in order to
88
+ construct the bag name. The organization would most likely assign the
89
+ enviroment name to a node attribute. In a naive implementation, each
90
+ recipe would include logic that examined the attribute's value to
91
+ determine which bag to load. This would obviously duplicate code.
92
+
93
+ Imagine instead that the organization wants to reference the bag by
94
+ the name `secure` and rely on an _abstraction_ to translate `secure`
95
+ into the environment-specific bag name.
96
+
97
+ This gem provides that abstraction.
98
+
99
+ ## Features
100
+
101
+ This gem overrides the `data_bag_item` method with configurable logic
102
+ that dynamically decides which bag to load. It retains API
103
+ compatibility with `Chef::DSL::DataQuery#data_bag_item`, so existing
104
+ recipes that call `data_bag_item` work without modification.
105
+
106
+ The gem imposes no constraints on data bag item structure.
107
+
108
+ ## Configuration
109
+
110
+ Assign the region name to a node attribute that varies by environment:
111
+
112
+ node.default['local'][region'] = 'staging'
113
+
114
+ Add the following configuration to Chef Client's `client.rb` file.
115
+
116
+ * Require the gem:
117
+
118
+ require 'chef/data_region'
119
+
120
+ * Configure the gem with a hash that maps a bag name to an expansion
121
+ pattern:
122
+
123
+ Chef::DataRegion.add(
124
+ 'secure',
125
+ { attribute: %w(local region), pattern: 'secure-%<attribute>s' }
126
+ )
127
+
128
+ ## Bag name expansion
129
+
130
+ The gem expands bag names using Ruby's `format` method.
131
+
132
+ _More pending..._
133
+ email: s@me.one
134
+ executables: []
135
+ extensions: []
136
+ extra_rdoc_files: []
137
+ files:
138
+ - lib/chef/data_region.rb
139
+ homepage: https://so.me.one
140
+ licenses:
141
+ - 0BSD
142
+ metadata: {}
143
+ post_install_message:
144
+ rdoc_options: []
145
+ require_paths:
146
+ - lib
147
+ required_ruby_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ required_rubygems_version: !ruby/object:Gem::Requirement
153
+ requirements:
154
+ - - ">="
155
+ - !ruby/object:Gem::Version
156
+ version: '0'
157
+ requirements: []
158
+ rubyforge_project:
159
+ rubygems_version: 2.6.14
160
+ signing_key:
161
+ specification_version: 4
162
+ summary: Access region-specific Chef data bags
163
+ test_files: []