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.
- checksums.yaml +7 -0
- data/lib/chef/data_region.rb +96 -0
- 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: []
|