record_store 2.0.0 → 2.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e96a230fae8c5a5a73e2c51a2097c5b14094f82d
4
- data.tar.gz: 55c913c54e64b78454624b0844ebf1e3ca74fa36
3
+ metadata.gz: b939adb80cff762bc3463596e80a684a8a4accc6
4
+ data.tar.gz: a97fdd41e9ef24859af390d2004ff3468911b637
5
5
  SHA512:
6
- metadata.gz: 512431b9f45815ee10a9b8b10228660b944b862a99be5e1ecb42705cab33fd069f8787971881e0a905722ddf20b127c121c091da61af93b93d1ee568b48f66f9
7
- data.tar.gz: d0f22e77f0a914a0c3ee11cef077cc8bd1e31eb4ce04acc8e351e01c43cff7218831e9621350ab631bcd9e62f9c943e2b690c1f275242be05479567ca36c5b75
6
+ metadata.gz: 187c99f674558e6032f722d9bc352acca2385380c981dacae81d1ae1e24b7f4893d6bc4e61e91d1eb1e4ffe028d42fe128c4ec8b7cd15cd2f9c56160085c6c02
7
+ data.tar.gz: 33e19e66a3d68da1f1af1eb0147dca02e430e769bee89617f199c3684509a2137f9eca5ff52fbf20e007a27c2399524e92ec9b3abf70af3ac05df22998afb34c
data/Gemfile CHANGED
@@ -8,7 +8,7 @@ gem 'ejson'
8
8
  gem 'fog'
9
9
  gem 'fog-json'
10
10
  gem 'fog-xml'
11
- gem 'fog-dynect'
11
+ gem 'fog-dynect', '~> 0.1.0'
12
12
 
13
13
  group :test do
14
14
  gem 'mocha'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  GEM
2
2
  remote: https://rubygems.org/
3
3
  specs:
4
- CFPropertyList (2.3.2)
4
+ CFPropertyList (2.3.3)
5
5
  activemodel (4.2.2)
6
6
  activesupport (= 4.2.2)
7
7
  builder (~> 3.1)
@@ -14,18 +14,17 @@ GEM
14
14
  builder (3.2.2)
15
15
  coderay (1.1.0)
16
16
  ejson (1.0.0)
17
- excon (0.45.4)
17
+ excon (0.52.0)
18
18
  fission (0.5.0)
19
19
  CFPropertyList (~> 2.2)
20
- fog (1.36.0)
21
- fog-aliyun (>= 0.1.0)
20
+ fog (1.33.0)
22
21
  fog-atmos
23
22
  fog-aws (>= 0.6.0)
24
23
  fog-brightbox (~> 0.4)
25
24
  fog-core (~> 1.32)
26
- fog-dynect (~> 0.0.2)
27
- fog-ecloud (~> 0.1)
28
- fog-google (<= 0.1.0)
25
+ fog-dynect
26
+ fog-ecloud (= 0.1.1)
27
+ fog-google (>= 0.0.2)
29
28
  fog-json
30
29
  fog-local
31
30
  fog-powerdns (>= 0.1.1)
@@ -39,59 +38,49 @@ GEM
39
38
  fog-terremark
40
39
  fog-vmfusion
41
40
  fog-voxel
42
- fog-xenserver
43
41
  fog-xml (~> 0.1.1)
44
42
  ipaddress (~> 0.5)
45
43
  nokogiri (~> 1.5, >= 1.5.11)
46
- fog-aliyun (0.1.0)
47
- fog-core (~> 1.27)
48
- fog-json (~> 1.0)
49
- ipaddress (~> 0.8)
50
- xml-simple (~> 1.1)
51
44
  fog-atmos (0.1.0)
52
45
  fog-core
53
46
  fog-xml
54
- fog-aws (0.7.6)
55
- fog-core (~> 1.27)
47
+ fog-aws (0.11.0)
48
+ fog-core (~> 1.38)
56
49
  fog-json (~> 1.0)
57
50
  fog-xml (~> 0.1)
58
51
  ipaddress (~> 0.8)
59
- fog-brightbox (0.9.0)
52
+ fog-brightbox (0.11.0)
60
53
  fog-core (~> 1.22)
61
54
  fog-json
62
55
  inflecto (~> 0.0.2)
63
- fog-core (1.32.1)
56
+ fog-core (1.42.0)
64
57
  builder
65
- excon (~> 0.45)
58
+ excon (~> 0.49)
66
59
  formatador (~> 0.2)
67
- mime-types
68
- net-scp (~> 1.1)
69
- net-ssh (>= 2.1.3)
70
- fog-dynect (0.0.2)
60
+ fog-dynect (0.1.0)
71
61
  fog-core
72
62
  fog-json
73
63
  fog-xml
74
- fog-ecloud (0.3.0)
64
+ fog-ecloud (0.1.1)
75
65
  fog-core
76
66
  fog-xml
77
- fog-google (0.1.0)
67
+ fog-google (0.4.0)
78
68
  fog-core
79
69
  fog-json
80
70
  fog-xml
81
71
  fog-json (1.0.2)
82
72
  fog-core (~> 1.0)
83
73
  multi_json (~> 1.10)
84
- fog-local (0.2.1)
74
+ fog-local (0.3.0)
85
75
  fog-core (~> 1.27)
86
76
  fog-powerdns (0.1.1)
87
77
  fog-core (~> 1.27)
88
78
  fog-json (~> 1.0)
89
79
  fog-xml (~> 0.1)
90
- fog-profitbricks (0.0.5)
91
- fog-core
92
- fog-xml
93
- nokogiri
94
- fog-radosgw (0.0.4)
80
+ fog-profitbricks (2.0.1)
81
+ fog-core (~> 1.42)
82
+ fog-json (~> 1.0)
83
+ fog-radosgw (0.0.5)
95
84
  fog-core (>= 1.21.0)
96
85
  fog-json
97
86
  fog-xml (>= 0.0.1)
@@ -99,13 +88,13 @@ GEM
99
88
  fog-core
100
89
  fog-json
101
90
  fog-xml
102
- fog-sakuracloud (1.4.0)
91
+ fog-sakuracloud (1.7.5)
103
92
  fog-core
104
93
  fog-json
105
94
  fog-serverlove (0.1.2)
106
95
  fog-core
107
96
  fog-json
108
- fog-softlayer (1.0.2)
97
+ fog-softlayer (1.1.4)
109
98
  fog-core
110
99
  fog-json
111
100
  fog-storm_on_demand (0.1.1)
@@ -120,30 +109,25 @@ GEM
120
109
  fog-voxel (0.1.0)
121
110
  fog-core
122
111
  fog-xml
123
- fog-xenserver (0.2.2)
124
- fog-core
125
- fog-xml
126
112
  fog-xml (0.1.2)
127
113
  fog-core
128
114
  nokogiri (~> 1.5, >= 1.5.11)
129
115
  formatador (0.2.5)
130
116
  i18n (0.7.0)
131
117
  inflecto (0.0.2)
132
- ipaddress (0.8.0)
118
+ ipaddress (0.8.3)
133
119
  json (1.8.3)
134
120
  metaclass (0.0.4)
135
121
  method_source (0.8.2)
136
- mime-types (2.6.1)
137
- mini_portile (0.6.2)
122
+ mini_portile2 (2.1.0)
138
123
  minitest (5.9.0)
139
124
  mocha (1.1.0)
140
125
  metaclass (~> 0.0.1)
141
- multi_json (1.11.2)
142
- net-scp (1.2.1)
143
- net-ssh (>= 2.6.5)
144
- net-ssh (2.9.2)
145
- nokogiri (1.6.6.2)
146
- mini_portile (~> 0.6.0)
126
+ multi_json (1.12.1)
127
+ nokogiri (1.6.8)
128
+ mini_portile2 (~> 2.1.0)
129
+ pkg-config (~> 1.1.7)
130
+ pkg-config (1.1.7)
147
131
  pry (0.10.1)
148
132
  coderay (~> 1.1.0)
149
133
  method_source (~> 0.8.1)
@@ -155,7 +139,6 @@ GEM
155
139
  tzinfo (1.2.2)
156
140
  thread_safe (~> 0.1)
157
141
  vcr (2.9.3)
158
- xml-simple (1.1.5)
159
142
 
160
143
  PLATFORMS
161
144
  ruby
@@ -165,7 +148,7 @@ DEPENDENCIES
165
148
  activesupport (~> 4.2)
166
149
  ejson
167
150
  fog
168
- fog-dynect
151
+ fog-dynect (~> 0.1.0)
169
152
  fog-json
170
153
  fog-xml
171
154
  mocha
@@ -175,4 +158,4 @@ DEPENDENCIES
175
158
  vcr
176
159
 
177
160
  BUNDLED WITH
178
- 1.11.2
161
+ 1.12.5
data/README.md CHANGED
@@ -48,9 +48,11 @@ The permissions required are broken into 2 groups:
48
48
  * WRITE: `RecordAdd`, `RecordDelete`, `ZonePublish`, `ZoneDiscardChangeset`, `ZoneFreeze`, `ZoneThaw`, `ZoneAddNode`,
49
49
  `ZoneRemoveNode`
50
50
 
51
- All CI validations only require READ permissions; deplyoing requires a user with READ and WRITE permissions.
51
+ All CI validations only require READ permissions; deploying requires a user with READ and WRITE permissions.
52
52
 
53
- For a breakdown of what each permssion allows read through [DynECT's permissions guide](https://help.dyn.com/user-and-group-permissions/).
53
+ In addition, the `AliasService` permission is required to be able to read or write ALIAS records on DynECT.
54
+
55
+ For a breakdown of what each permission allows read through [DynECT's permissions guide](https://help.dyn.com/user-and-group-permissions/).
54
56
 
55
57
  ----
56
58
 
@@ -145,7 +147,7 @@ end
145
147
 
146
148
  #### Provider-Specific Records
147
149
 
148
- For provider-specific records (e.g. `ALIAS`), create the record model in `lib/record_store/record` as any other record. In the provider, extend `self.record_types` and append the custom record types to the `Set` returned by `Provider.record_types` (e.g. [`DNSimple.record_types`](https://github.com/Shopify/record_store/blob/1ec0d1410cf8bedf79bc63e8e4cdc7cdb0f1019b/lib/record_store/provider/dnsimple.rb#L5-L7)).
150
+ For provider-specific records (e.g. `ALIAS`), create the record model in `lib/record_store/record` as any other record. In the provider, extend `self.record_types` and append the custom record types to the `Set` returned by `Provider.record_types` (e.g. [`DNSimple.record_types`](https://github.com/Shopify/record_store/blob/368153106068800325b4e579483faa427afe7add/lib/record_store/provider/dnsimple.rb#L6)).
149
151
 
150
152
  #### Secrets
151
153
 
data/dev.yml CHANGED
@@ -5,6 +5,18 @@ up:
5
5
  - ruby: 2.3.1
6
6
  - bundler
7
7
 
8
+ commands:
9
+ test:
10
+ syntax:
11
+ argument: file
12
+ optional: args...
13
+ desc: 'run tests'
14
+ run: |
15
+ if [[ $# -eq 0 ]]; then
16
+ bundle exec rake test
17
+ else
18
+ bundle exec ruby -Itest "$@"
19
+ fi
20
+
8
21
  packages:
9
22
  - git@github.com:Shopify/dev-shopify.git
10
-
@@ -23,6 +23,7 @@ module RecordStore
23
23
  Set.new([
24
24
  'A',
25
25
  'AAAA',
26
+ 'ALIAS',
26
27
  'CNAME',
27
28
  'MX',
28
29
  'NS',
@@ -32,6 +33,10 @@ module RecordStore
32
33
  ])
33
34
  end
34
35
 
36
+ def self.supports_alias?
37
+ false
38
+ end
39
+
35
40
  def initialize(zone:)
36
41
  @zone_name = zone
37
42
  end
@@ -2,8 +2,8 @@ require 'fog/dnsimple'
2
2
 
3
3
  module RecordStore
4
4
  class Provider::DNSimple < Provider
5
- def self.record_types
6
- super.add('ALIAS')
5
+ def self.supports_alias?
6
+ true
7
7
  end
8
8
 
9
9
  def add(record)
@@ -19,7 +19,7 @@ module RecordStore
19
19
 
20
20
  if record.type == 'ALIAS'
21
21
  txt_alias = retrieve_current_records.detect do |rr|
22
- rr.type == 'TXT' && rr.fqdn == record.fqdn && rr.txtdata == "ALIAS for #{record.cname.gsub(/.\z/, '')}"
22
+ rr.type == 'TXT' && rr.fqdn == record.fqdn && rr.txtdata == "ALIAS for #{record.alias.chomp('.')}"
23
23
  end
24
24
  remove(txt_alias)
25
25
  end
@@ -93,7 +93,7 @@ module RecordStore
93
93
  when 'AAAA'
94
94
  record.merge!(address: api_record.fetch('content'))
95
95
  when 'ALIAS'
96
- record.merge!(cname: api_record.fetch('content'))
96
+ record.merge!(alias: api_record.fetch('content'))
97
97
  when 'CNAME'
98
98
  record.merge!(cname: api_record.fetch('content'))
99
99
  when 'MX'
@@ -124,7 +124,7 @@ module RecordStore
124
124
 
125
125
  def api_hash(record)
126
126
  record_hash = {
127
- name: record.fqdn.gsub("#{Record.ensure_ends_with_dot(@zone_name)}", '').gsub(/.\z/, ''),
127
+ name: record.fqdn.gsub("#{Record.ensure_ends_with_dot(@zone_name)}", '').chomp('.'),
128
128
  ttl: record.ttl,
129
129
  type: record.type,
130
130
  }
@@ -135,18 +135,18 @@ module RecordStore
135
135
  when 'AAAA'
136
136
  record_hash[:content] = record.address
137
137
  when 'ALIAS'
138
- record_hash[:content] = record.cname.gsub(/.\z/, '')
138
+ record_hash[:content] = record.alias.chomp('.')
139
139
  when 'CNAME'
140
- record_hash[:content] = record.cname.gsub(/.\z/, '')
140
+ record_hash[:content] = record.cname.chomp('.')
141
141
  when 'MX'
142
142
  record_hash[:prio] = record.preference
143
- record_hash[:content] = record.exchange.gsub(/.\z/, '')
143
+ record_hash[:content] = record.exchange.chomp('.')
144
144
  when 'NS'
145
- record_hash[:content] = record.nsdname.gsub(/.\z/, '')
145
+ record_hash[:content] = record.nsdname.chomp('.')
146
146
  when 'SPF'
147
147
  record_hash[:content] = record.txtdata
148
148
  when 'SRV'
149
- record_hash[:content] = "#{record.weight} #{record.port} #{record.target.gsub(/.\z/, '')}"
149
+ record_hash[:content] = "#{record.weight} #{record.port} #{record.target.chomp('.')}"
150
150
  record_hash[:prio] = record.priority
151
151
  when 'TXT'
152
152
  record_hash[:content] = record.txtdata
@@ -18,7 +18,16 @@ module RecordStore
18
18
  end
19
19
 
20
20
  def self.build_from_yaml_definition(yaml_definition)
21
- Record.const_get(yaml_definition.fetch(:type)).new(yaml_definition)
21
+ record_type = yaml_definition.fetch(:type)
22
+ # TODO: remove backward compatibility support for ALIAS records using cname attribute instead of alias
23
+ # REMOVE after merging https://github.com/Shopify/record-store/pull/781
24
+ case record_type
25
+ when 'ALIAS'
26
+ if yaml_definition.key?(:cname)
27
+ yaml_definition[:alias] = yaml_definition.delete(:cname)
28
+ end
29
+ end
30
+ Record.const_get(record_type).new(yaml_definition)
22
31
  end
23
32
 
24
33
  def log!(logger=STDOUT)
@@ -1,20 +1,20 @@
1
1
  module RecordStore
2
2
  class Record::ALIAS < Record
3
- attr_accessor :cname
3
+ attr_accessor :alias
4
4
 
5
- validates :cname, presence: true, format: { with: Record::CNAME_REGEX, message: 'is not a fully qualified domain name' }
5
+ validates :alias, presence: true, format: { with: Record::CNAME_REGEX, message: 'is not a fully qualified domain name' }
6
6
 
7
7
  def initialize(record)
8
8
  super
9
- @cname = Record.ensure_ends_with_dot(record.fetch(:cname))
9
+ @alias = Record.ensure_ends_with_dot(record.fetch(:alias))
10
10
  end
11
11
 
12
12
  def rdata
13
- { cname: cname }
13
+ { alias: self.alias }
14
14
  end
15
15
 
16
16
  def to_s
17
- "[ALIASRecord] #{fqdn} #{ttl} IN ALIAS #{cname}"
17
+ "[ALIASRecord] #{fqdn} #{ttl} IN ALIAS #{self.alias}"
18
18
  end
19
19
  end
20
20
  end
@@ -1,3 +1,3 @@
1
1
  module RecordStore
2
- VERSION = '2.0.0'
2
+ VERSION = '2.1.0'
3
3
  end
@@ -108,10 +108,12 @@ module RecordStore
108
108
  validate :validate_records
109
109
  validate :validate_config
110
110
  validate :validate_all_records_are_unique
111
- validate :validate_a_records_for_same_fqdn
112
111
  validate :validate_cname_records_for_same_fqdn
113
112
  validate :validate_cname_records_dont_point_to_root
113
+ validate :validate_same_ttl_for_records_sharing_fqdn_and_type
114
114
  validate :validate_provider_can_handle_zone_records
115
+ validate :validate_can_handle_alias_records
116
+ validate :validate_alias_points_to_root
115
117
 
116
118
  def self.from_yaml_definition(name, definition)
117
119
  new(name, definition.deep_symbolize_keys)
@@ -123,6 +125,7 @@ module RecordStore
123
125
  write(name, records: current_records, config: {
124
126
  provider: provider_name,
125
127
  ignore_patterns: [{type: "NS", fqdn: "#{name}."}],
128
+ supports_alias: current_records.map(&:type).include?('ALIAS') || nil
126
129
  }, **write_options)
127
130
  end
128
131
 
@@ -155,7 +158,7 @@ module RecordStore
155
158
  end
156
159
 
157
160
  def provider
158
- Provider.const_get(config.provider).new(zone: name.gsub(/\.\z/, ''))
161
+ Provider.const_get(config.provider).new(zone: name.chomp('.'))
159
162
  end
160
163
 
161
164
  def write(**write_options)
@@ -193,11 +196,12 @@ module RecordStore
193
196
  end
194
197
  end
195
198
 
196
- def validate_a_records_for_same_fqdn
197
- a_records = records.select { |record| record.is_a?(Record::A) }.group_by(&:fqdn)
198
- a_records.each do |fqdn, records|
199
- if records.map(&:ttl).uniq.size > 1
200
- errors.add(:records, "All A records for #{fqdn} should have the same TTL")
199
+ def validate_same_ttl_for_records_sharing_fqdn_and_type
200
+ records_sharing_fqdn_and_type = records.group_by { |record| [record.type, record.fqdn] }
201
+ records_sharing_fqdn_and_type.each do |(type, fqdn), matching_records|
202
+ all_ttls = matching_records.map(&:ttl).uniq
203
+ if all_ttls.length > 1
204
+ errors.add(:records, "All #{type} records for #{fqdn} should have the same TTL")
201
205
  end
202
206
  end
203
207
  end
@@ -245,5 +249,19 @@ module RecordStore
245
249
  errors.add(:records, "#{record_type} is a not a supported record type in #{config.provider}")
246
250
  end
247
251
  end
252
+
253
+ def validate_can_handle_alias_records
254
+ return unless records.any? { |record| record.is_a?(Record::ALIAS) }
255
+ return if config.supports_alias?
256
+ errors.add(:records, "#{config.provider} does not support ALIAS records for #{name.chomp('.')} zone")
257
+ end
258
+
259
+ def validate_alias_points_to_root
260
+ alias_record = records.find { |record| record.is_a?(Record::ALIAS) && record.fqdn != @name }
261
+
262
+ return unless alias_record
263
+
264
+ errors.add(:records, "ALIAS record should be defined on the root of the zone: #{alias_record}")
265
+ end
248
266
  end
249
267
  end
@@ -3,13 +3,18 @@ module RecordStore
3
3
  class Config
4
4
  include ActiveModel::Validations
5
5
 
6
- attr_reader :ignore_patterns, :provider
6
+ attr_reader :ignore_patterns, :provider, :supports_alias
7
7
 
8
8
  validate :validate_zone_config
9
9
 
10
- def initialize(ignore_patterns: [], provider: nil)
10
+ def initialize(ignore_patterns: [], provider: nil, supports_alias: nil)
11
11
  @ignore_patterns = ignore_patterns
12
12
  @provider = provider
13
+ @supports_alias = supports_alias
14
+ end
15
+
16
+ def supports_alias?
17
+ @supports_alias.nil? && valid_provider? ? Provider.const_get(provider).supports_alias? : @supports_alias
13
18
  end
14
19
 
15
20
  def to_hash
@@ -22,9 +27,11 @@ module RecordStore
22
27
  private
23
28
 
24
29
  def validate_zone_config
25
- unless Provider.constants.include?(provider.to_s.to_sym)
26
- errors.add(:provider, 'provider specified does not exist')
27
- end
30
+ errors.add(:provider, 'provider specified does not exist') unless valid_provider?
31
+ end
32
+
33
+ def valid_provider?
34
+ Provider.constants.include?(provider.to_s.to_sym)
28
35
  end
29
36
  end
30
37
  end
data/record_store.gemspec CHANGED
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
25
25
  spec.add_runtime_dependency 'fog'
26
26
  spec.add_runtime_dependency 'fog-json'
27
27
  spec.add_runtime_dependency 'fog-xml'
28
- spec.add_runtime_dependency 'fog-dynect'
28
+ spec.add_runtime_dependency 'fog-dynect', '~> 0.1.0'
29
29
  spec.add_runtime_dependency 'ejson'
30
30
 
31
31
  spec.add_development_dependency 'rake'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: record_store
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Willem van Bergen
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-08-18 00:00:00.000000000 Z
12
+ date: 2016-09-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: thor
@@ -99,16 +99,16 @@ dependencies:
99
99
  name: fog-dynect
100
100
  requirement: !ruby/object:Gem::Requirement
101
101
  requirements:
102
- - - ">="
102
+ - - "~>"
103
103
  - !ruby/object:Gem::Version
104
- version: '0'
104
+ version: 0.1.0
105
105
  type: :runtime
106
106
  prerelease: false
107
107
  version_requirements: !ruby/object:Gem::Requirement
108
108
  requirements:
109
- - - ">="
109
+ - - "~>"
110
110
  - !ruby/object:Gem::Version
111
- version: '0'
111
+ version: 0.1.0
112
112
  - !ruby/object:Gem::Dependency
113
113
  name: ejson
114
114
  requirement: !ruby/object:Gem::Requirement