record_store 6.3.1 → 6.7.1
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 +4 -4
- data/.github/dependabot.yml +17 -0
- data/.github/workflows/ci.yml +31 -0
- data/.github/workflows/cla.yml +23 -0
- data/.github/workflows/stale-action-handling.yml +31 -0
- data/.rubocop-https---shopify-github-io-ruby-style-guide-rubocop-yml +10 -5
- data/.rubocop.yml +26 -5
- data/.travis.yml +1 -1
- data/CHANGELOG.md +52 -0
- data/README.md +13 -0
- data/bin/console +1 -0
- data/bin/rubocop +4 -2
- data/dev.yml +1 -1
- data/lib/record_store/changeset.rb +22 -18
- data/lib/record_store/cli.rb +13 -14
- data/lib/record_store/provider/dnsimple/patch_request_error_to_include_errors.rb +20 -0
- data/lib/record_store/provider/dnsimple.rb +10 -11
- data/lib/record_store/provider/dynect.rb +5 -7
- data/lib/record_store/provider/google_cloud_dns.rb +9 -13
- data/lib/record_store/provider/ns1/client.rb +3 -1
- data/lib/record_store/provider/ns1/patch_api_header.rb +1 -1
- data/lib/record_store/provider/ns1.rb +11 -8
- data/lib/record_store/provider/oracle_cloud_dns.rb +4 -3
- data/lib/record_store/provider.rb +23 -19
- data/lib/record_store/record/alias.rb +1 -1
- data/lib/record_store/record/caa.rb +3 -1
- data/lib/record_store/record/cname.rb +1 -1
- data/lib/record_store/record.rb +6 -4
- data/lib/record_store/version.rb +1 -1
- data/lib/record_store/zone/config/implicit_record_template.rb +112 -0
- data/lib/record_store/zone/config.rb +5 -3
- data/lib/record_store/zone/yaml_definitions.rb +7 -2
- data/lib/record_store/zone.rb +80 -18
- data/lib/record_store.rb +15 -2
- data/record_store.gemspec +14 -14
- data/template/config.yml +1 -0
- data/template/templates/implicit_records/implicit_example.yml.erb +15 -0
- data/template/zones/dynect.example.com.yml +2 -0
- metadata +95 -70
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7870ecd8f6c7f112a049ad82c71993773df9cd47ebfec1ece571bac97d209572
|
|
4
|
+
data.tar.gz: 6c137abf69f115a6fc78e38e800ff3d270d54e6462c2467627f97b8fc524dd4e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 376c0a4183592b115118cabf2d89befa26209d0f02bd85f82bece5e0afc5c2c62db8fc2c1ae6668ccfe57148ccbee922aca7386fcb04e9ec5785fbabfb195744
|
|
7
|
+
data.tar.gz: 8a42c0c7ed8a121d57c0b2c58f2bbe6f568c809fe53e9908724083415f44643497d6019a1cb712e561901ee4e1258d523f9f36738221090ff321c921fb99037c
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
|
|
3
|
+
updates:
|
|
4
|
+
- package-ecosystem: bundler
|
|
5
|
+
directory: "/"
|
|
6
|
+
schedule:
|
|
7
|
+
interval: weekly
|
|
8
|
+
open-pull-requests-limit: 100
|
|
9
|
+
groups:
|
|
10
|
+
minor_versions:
|
|
11
|
+
update-types:
|
|
12
|
+
- 'minor'
|
|
13
|
+
- 'patch'
|
|
14
|
+
- package-ecosystem: github-actions
|
|
15
|
+
directory: '/'
|
|
16
|
+
schedule:
|
|
17
|
+
interval: weekly
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on: [push, pull_request]
|
|
4
|
+
|
|
5
|
+
env:
|
|
6
|
+
SRB_SKIP_GEM_RBIS: true
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
ruby: [ 2.7.1, 3.2.1 ]
|
|
15
|
+
name: Test Ruby ${{ matrix.ruby }}
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- name: Install dependencies
|
|
19
|
+
run: |
|
|
20
|
+
sudo apt-get update && sudo apt-get install build-essential libcurl4-openssl-dev
|
|
21
|
+
- name: Set up Ruby
|
|
22
|
+
uses: ruby/setup-ruby@v1
|
|
23
|
+
with:
|
|
24
|
+
ruby-version: ${{ matrix.ruby }}
|
|
25
|
+
bundler-cache: true
|
|
26
|
+
- name: rubocop
|
|
27
|
+
run: bin/rubocop --version && bin/rubocop
|
|
28
|
+
- name: setup
|
|
29
|
+
run: bin/setup
|
|
30
|
+
- name: test
|
|
31
|
+
run: bundle exec rake test
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# .github/workflows/cla.yml
|
|
2
|
+
name: Contributor License Agreement (CLA)
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
pull_request_target:
|
|
6
|
+
types: [opened, synchronize]
|
|
7
|
+
issue_comment:
|
|
8
|
+
types: [created]
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
cla:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
if: |
|
|
14
|
+
(github.event.issue.pull_request
|
|
15
|
+
&& !github.event.issue.pull_request.merged_at
|
|
16
|
+
&& contains(github.event.comment.body, 'signed')
|
|
17
|
+
)
|
|
18
|
+
|| (github.event.pull_request && !github.event.pull_request.merged)
|
|
19
|
+
steps:
|
|
20
|
+
- uses: Shopify/shopify-cla-action@v1
|
|
21
|
+
with:
|
|
22
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
23
|
+
cla-token: ${{ secrets.CLA_TOKEN }}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Configuration for stale action https://github.com/actions/stale
|
|
2
|
+
name: 'Close stale issues and PRs'
|
|
3
|
+
on:
|
|
4
|
+
schedule:
|
|
5
|
+
- cron: '0 14 * * *'
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
stale:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
issues: write
|
|
13
|
+
pull-requests: write
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/stale@v9
|
|
17
|
+
with:
|
|
18
|
+
ascending: true
|
|
19
|
+
operations-per-run: 100
|
|
20
|
+
stale-pr-message: 'As of today this PR is stale. If you want to keep it apply an update otherwise it will be closed in 7 days.'
|
|
21
|
+
close-pr-message: 'PR was closed because of missing activity.'
|
|
22
|
+
days-before-pr-stale: 90
|
|
23
|
+
days-before-pr-close: 7
|
|
24
|
+
stale-issue-message: >
|
|
25
|
+
This issue is stale because it has been open for 90 days with no activity. It will be closed if no further action occurs in 7 days.
|
|
26
|
+
close-issue-message: |
|
|
27
|
+
We are closing this issue because it has been inactive for a few months.
|
|
28
|
+
This probably means that it is not reproducible or it has been fixed in a newer version.
|
|
29
|
+
If it's an enhancement and hasn't been taken on since it was submitted, then it seems other issues have taken priority.
|
|
30
|
+
days-before-issue-stale: 90
|
|
31
|
+
days-before-issue-close: 7
|
|
@@ -195,6 +195,7 @@ Style/FrozenStringLiteralComment:
|
|
|
195
195
|
SupportedStyles:
|
|
196
196
|
- always
|
|
197
197
|
- never
|
|
198
|
+
SafeAutoCorrect: true
|
|
198
199
|
|
|
199
200
|
Style/GlobalVars:
|
|
200
201
|
AllowedVariables: []
|
|
@@ -264,7 +265,7 @@ Style/MethodCallWithArgsParentheses:
|
|
|
264
265
|
- raise
|
|
265
266
|
- puts
|
|
266
267
|
Exclude:
|
|
267
|
-
- Gemfile
|
|
268
|
+
- '**/Gemfile'
|
|
268
269
|
|
|
269
270
|
Style/MethodDefParentheses:
|
|
270
271
|
EnforcedStyle: require_parentheses
|
|
@@ -577,6 +578,7 @@ Layout/BlockEndNewline:
|
|
|
577
578
|
|
|
578
579
|
Style/CaseEquality:
|
|
579
580
|
Enabled: true
|
|
581
|
+
AllowOnConstant: true
|
|
580
582
|
|
|
581
583
|
Style/CharacterLiteral:
|
|
582
584
|
Enabled: true
|
|
@@ -659,6 +661,9 @@ Style/IfWithSemicolon:
|
|
|
659
661
|
Style/IdenticalConditionalBranches:
|
|
660
662
|
Enabled: true
|
|
661
663
|
|
|
664
|
+
Layout/IndentationStyle:
|
|
665
|
+
Enabled: true
|
|
666
|
+
|
|
662
667
|
Style/InfiniteLoop:
|
|
663
668
|
Enabled: true
|
|
664
669
|
|
|
@@ -803,9 +808,6 @@ Layout/SpaceInsideRangeLiteral:
|
|
|
803
808
|
Style/SymbolLiteral:
|
|
804
809
|
Enabled: true
|
|
805
810
|
|
|
806
|
-
Layout/IndentationStyle:
|
|
807
|
-
Enabled: true
|
|
808
|
-
|
|
809
811
|
Layout/TrailingWhitespace:
|
|
810
812
|
Enabled: true
|
|
811
813
|
|
|
@@ -834,7 +836,7 @@ Style/ZeroLengthPredicate:
|
|
|
834
836
|
Enabled: true
|
|
835
837
|
|
|
836
838
|
Layout/HeredocIndentation:
|
|
837
|
-
|
|
839
|
+
Enabled: true
|
|
838
840
|
|
|
839
841
|
Lint/AmbiguousOperator:
|
|
840
842
|
Enabled: true
|
|
@@ -1015,3 +1017,6 @@ Style/ModuleFunction:
|
|
|
1015
1017
|
|
|
1016
1018
|
Lint/OrderedMagicComments:
|
|
1017
1019
|
Enabled: true
|
|
1020
|
+
|
|
1021
|
+
Lint/DeprecatedOpenSSLConstant:
|
|
1022
|
+
Enabled: true
|
data/.rubocop.yml
CHANGED
|
@@ -1,19 +1,40 @@
|
|
|
1
|
-
|
|
2
|
-
-
|
|
1
|
+
inherit_gem:
|
|
2
|
+
rubocop-shopify: rubocop.yml
|
|
3
3
|
|
|
4
4
|
AllCops:
|
|
5
|
-
TargetRubyVersion: 2.4
|
|
5
|
+
TargetRubyVersion: 2.6.4
|
|
6
6
|
Exclude:
|
|
7
7
|
- vendor/**/*
|
|
8
8
|
UseCache: true
|
|
9
9
|
CacheRootDirectory: tmp/rubocop
|
|
10
10
|
|
|
11
|
+
Naming/InclusiveLanguage:
|
|
12
|
+
Enabled: false
|
|
13
|
+
|
|
14
|
+
Style/ClassAndModuleChildren:
|
|
15
|
+
Enabled: false
|
|
16
|
+
|
|
11
17
|
Style/FrozenStringLiteralComment:
|
|
12
18
|
Enabled: false
|
|
13
19
|
|
|
20
|
+
Style/QuotedSymbols:
|
|
21
|
+
Enabled: false
|
|
22
|
+
|
|
14
23
|
Style/MethodCallWithArgsParentheses:
|
|
15
24
|
Include:
|
|
16
25
|
- '**/*.rb'
|
|
17
26
|
|
|
18
|
-
Style/
|
|
19
|
-
Enabled: false
|
|
27
|
+
Style/StringLiterals:
|
|
28
|
+
Enabled: false
|
|
29
|
+
|
|
30
|
+
Style/StringLiteralsInInterpolation:
|
|
31
|
+
Enabled: false
|
|
32
|
+
|
|
33
|
+
Style/TrailingCommaInArrayLiteral:
|
|
34
|
+
Enabled: false
|
|
35
|
+
|
|
36
|
+
Style/TrailingCommaInHashLiteral:
|
|
37
|
+
Enabled: false
|
|
38
|
+
|
|
39
|
+
Style/WordArray:
|
|
40
|
+
Enabled: false
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,57 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
# 6.7.1
|
|
4
|
+
- Change update_record to update_zone_record for DNSimple
|
|
5
|
+
|
|
6
|
+
## 6.7.0
|
|
7
|
+
- Update dependencies like dnsimple, thor, ruby-limiter
|
|
8
|
+
|
|
9
|
+
## 6.6.0
|
|
10
|
+
- Update ruby version
|
|
11
|
+
|
|
12
|
+
## 6.5.11
|
|
13
|
+
- Fix updates of SRV records in using the NS1 provider
|
|
14
|
+
|
|
15
|
+
## 6.5.10
|
|
16
|
+
- print the full error for ActiveModel::Error errors
|
|
17
|
+
|
|
18
|
+
## 6.5.9
|
|
19
|
+
- Relax version requirements for the google-cloud-dns gem
|
|
20
|
+
|
|
21
|
+
## 6.5.5
|
|
22
|
+
- Include DNSimple validation error details in exceptions
|
|
23
|
+
|
|
24
|
+
## 6.5.4
|
|
25
|
+
- Updates config path structure for build pipeline
|
|
26
|
+
|
|
27
|
+
## 6.5.3
|
|
28
|
+
- Adds check for detecting shadowed records, used when a record being added to a zone in record-store will have no effect because it is shadowed by another record.
|
|
29
|
+
|
|
30
|
+
## 6.5.2
|
|
31
|
+
- Ensure filters for implicit_records, `except_record` and `conflict_with`, are truly optional [BUGFIX]
|
|
32
|
+
|
|
33
|
+
## 6.5.1
|
|
34
|
+
|
|
35
|
+
Add support for a new parameter in the implicit records templates:
|
|
36
|
+
|
|
37
|
+
- `except_record`: the template will *NOT* generate the `injected_records` for records matching `except_record`, even if they matched `each_record`.
|
|
38
|
+
|
|
39
|
+
Also added support for regular expressions in the matching, using the already available `!ruby/regexp` text in a YAML value. The object loaded from the YAML
|
|
40
|
+
will be of `Regexp` type, and will thus be used to _match_ the value, instead of being identical only. This is supported in both `except_record` and
|
|
41
|
+
`each_record` fields.
|
|
42
|
+
|
|
43
|
+
## 6.5.0
|
|
44
|
+
|
|
45
|
+
...
|
|
46
|
+
|
|
47
|
+
## 6.4.0
|
|
48
|
+
|
|
49
|
+
Add support for injecting implicit records into a zone based on a pre-configured template. Brief overview of template keys:
|
|
50
|
+
|
|
51
|
+
- `each_record`: the template will generate all `injected_records` in the template for each Record in the Zone that matches criteria listed here.
|
|
52
|
+
- `conflict_with`: if any Record in the Zone matches the criteria listed here, the template will avoid injecting any of its generated implicit records into the zone
|
|
53
|
+
- `injected_records`: the implicit records that will be generated by the template for every Zone Record that matches the `each_record` criteria
|
|
54
|
+
|
|
3
55
|
## 6.3.1
|
|
4
56
|
- Improve resiliency in the face of temporary provider outages [BUGFIX]
|
|
5
57
|
|
data/README.md
CHANGED
|
@@ -20,6 +20,12 @@ record-store validate_initial_state # Validates state hasn't diverged since t
|
|
|
20
20
|
record-store validate_records # Validates that all DNS records have valid definitions
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
## Releasing a new version
|
|
24
|
+
* Bump the version [here](https://github.com/Shopify/record_store/blob/main/lib/record_store/version.rb).
|
|
25
|
+
* Add to CHANGELOG [here](https://github.com/Shopify/record_store/blob/main/CHANGELOG.md).
|
|
26
|
+
* PR, merge and shipit [here](https://shipit.shopify.io/shopify/record_store/production).
|
|
27
|
+
|
|
28
|
+
|
|
23
29
|
## Providers
|
|
24
30
|
|
|
25
31
|
Below is the list of DNS providers supported by Record Store. PRs [adding more](#adding-new-providers) are welcome.
|
|
@@ -116,6 +122,13 @@ When running `bin/record-store apply`, a `Changeset` is generated by comparing t
|
|
|
116
122
|
|
|
117
123
|
Record store attempts to parallelize some of the bulk zone fetching operations. It does so by spawning multiple threads (default: 10). This value can be configured by setting the RECORD_STORE_MAX_THREADS environment variable to a positive integer value.
|
|
118
124
|
|
|
125
|
+
|
|
126
|
+
### Templates
|
|
127
|
+
|
|
128
|
+
Record Store supports injecting implicit records into a zone based on templates. Each `Zone` can have a list of `Zone::Config::ImplicitRecordTemplate` in its `Zone::Config` that can define their own criteria for which records they use for generating implicit template records, which records conflict with the template-generated records, and what the template-generated records look like. These records are injected at the time of `Zone` initialization within `Zone#build_records`.
|
|
129
|
+
|
|
130
|
+
Templates can help reduce zone file bloat where instead of defining many generic literals within the zone file for a given criteria zone record, these generic records can be implicitly injected into the `Zone` at the time of initialization based on information provided within the template.
|
|
131
|
+
|
|
119
132
|
----
|
|
120
133
|
|
|
121
134
|
# Development
|
data/bin/console
CHANGED
|
@@ -6,6 +6,7 @@ require 'record_store'
|
|
|
6
6
|
|
|
7
7
|
RecordStore.zones_path = File.expand_path('../../dev/zones', __FILE__)
|
|
8
8
|
RecordStore.config_path = File.expand_path('../../dev/config.yml', __FILE__)
|
|
9
|
+
RecordStore.implicit_records_templates_path = File.expand_path('../../dev/templates/implicit_records', __FILE__)
|
|
9
10
|
|
|
10
11
|
require 'pry'
|
|
11
12
|
binding.pry(RecordStore) # rubocop:disable Lint/Debugger
|
data/bin/rubocop
CHANGED
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
#
|
|
10
10
|
|
|
11
11
|
require "pathname"
|
|
12
|
-
ENV["BUNDLE_GEMFILE"] ||= File.expand_path(
|
|
13
|
-
|
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path(
|
|
13
|
+
"../../Gemfile",
|
|
14
|
+
Pathname.new(__FILE__).realpath,
|
|
15
|
+
)
|
|
14
16
|
|
|
15
17
|
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
|
16
18
|
|
data/dev.yml
CHANGED
|
@@ -9,16 +9,18 @@ module RecordStore
|
|
|
9
9
|
@id = id
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
class << self
|
|
13
|
+
def addition(record)
|
|
14
|
+
new(type: :addition, record: record)
|
|
15
|
+
end
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
def removal(record)
|
|
18
|
+
new(type: :removal, record: record)
|
|
19
|
+
end
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
def update(id, record)
|
|
22
|
+
new(type: :update, record: record, id: id)
|
|
23
|
+
end
|
|
22
24
|
end
|
|
23
25
|
|
|
24
26
|
def removal?
|
|
@@ -36,18 +38,20 @@ module RecordStore
|
|
|
36
38
|
|
|
37
39
|
attr_reader :current_records, :desired_records, :removals, :additions, :updates, :provider, :zone
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
class << self
|
|
42
|
+
def build_from(provider:, zone:, all: false)
|
|
43
|
+
current_zone = provider.build_zone(zone_name: zone.unrooted_name, config: zone.config)
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
current_records = all ? current_zone.all : current_zone.records
|
|
46
|
+
desired_records = all ? zone.all : zone.records
|
|
44
47
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
new(
|
|
49
|
+
current_records: current_records,
|
|
50
|
+
desired_records: desired_records,
|
|
51
|
+
provider: provider,
|
|
52
|
+
zone: zone.unrooted_name,
|
|
53
|
+
)
|
|
54
|
+
end
|
|
51
55
|
end
|
|
52
56
|
|
|
53
57
|
def initialize(current_records: [], desired_records: [], provider:, zone:)
|
data/lib/record_store/cli.rb
CHANGED
|
@@ -10,8 +10,10 @@ module RecordStore
|
|
|
10
10
|
RecordStore.config_path = options.fetch('config', "#{Dir.pwd}/config.yml")
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
class << self
|
|
14
|
+
def exit_on_failure?
|
|
15
|
+
true
|
|
16
|
+
end
|
|
15
17
|
end
|
|
16
18
|
|
|
17
19
|
desc 'thaw', 'Thaws all zones under management to allow manual edits'
|
|
@@ -108,6 +110,7 @@ module RecordStore
|
|
|
108
110
|
end
|
|
109
111
|
|
|
110
112
|
next unless options.fetch('verbose')
|
|
113
|
+
|
|
111
114
|
puts "Unchanged:"
|
|
112
115
|
changeset.unchanged.each do |record|
|
|
113
116
|
puts " - #{record}"
|
|
@@ -147,7 +150,7 @@ module RecordStore
|
|
|
147
150
|
option :name, desc: 'Zone to download', aliases: '-n', type: :string, required: true
|
|
148
151
|
option :provider, desc: 'Provider in which this zone exists', aliases: '-p', type: :string
|
|
149
152
|
desc 'download', 'Downloads all records from zone and creates YAML zone definition in zones/ '\
|
|
150
|
-
|
|
153
|
+
'e.g. record-store download --name=shopify.io'
|
|
151
154
|
def download
|
|
152
155
|
name = options.fetch('name')
|
|
153
156
|
abort('Please omit the period at the end of the zone') if name.ends_with?('.')
|
|
@@ -193,14 +196,12 @@ module RecordStore
|
|
|
193
196
|
|
|
194
197
|
desc 'secrets', 'Decrypts DynECT credentials'
|
|
195
198
|
def secrets
|
|
196
|
-
environment =
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
'dev'
|
|
203
|
-
end
|
|
199
|
+
environment = if ENV['PRODUCTION']
|
|
200
|
+
'production'
|
|
201
|
+
elsif ENV['CI']
|
|
202
|
+
'ci'
|
|
203
|
+
else
|
|
204
|
+
'dev'
|
|
204
205
|
end
|
|
205
206
|
|
|
206
207
|
secrets = %x(ejson decrypt #{RecordStore.secrets_path.sub(/\.json\z/, ".#{environment}.ejson")})
|
|
@@ -281,9 +282,7 @@ module RecordStore
|
|
|
281
282
|
invalid_zones << zone.unrooted_name
|
|
282
283
|
|
|
283
284
|
puts "#{zone.unrooted_name} definition is not valid:"
|
|
284
|
-
zone.errors.
|
|
285
|
-
puts " - #{field}: #{msg}"
|
|
286
|
-
end
|
|
285
|
+
puts zone.errors.full_messages.map { |msg| " - #{msg}" }
|
|
287
286
|
|
|
288
287
|
invalid_records = zone.records.reject(&:valid?)
|
|
289
288
|
puts ' Invalid records' unless invalid_records.empty?
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Dnsimple
|
|
2
|
+
class RequestError < Error
|
|
3
|
+
private
|
|
4
|
+
|
|
5
|
+
alias_method :original_message_from, :message_from
|
|
6
|
+
|
|
7
|
+
def message_from(http_response)
|
|
8
|
+
message = original_message_from(http_response)
|
|
9
|
+
return unless json_response?(http_response)
|
|
10
|
+
|
|
11
|
+
base_error = http_response.parsed_response.dig("errors", "base")&.join(", ")
|
|
12
|
+
message += ": #{base_error}" unless base_error.nil?
|
|
13
|
+
message
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def json_response?(http_response)
|
|
17
|
+
http_response.headers["Content-Type"]&.start_with?("application/json")
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require 'dnsimple'
|
|
2
2
|
require_relative 'dnsimple/patch_api_header'
|
|
3
|
+
require_relative 'dnsimple/patch_request_error_to_include_errors'
|
|
3
4
|
|
|
4
5
|
module RecordStore
|
|
5
6
|
class Provider::DNSimple < Provider
|
|
@@ -19,13 +20,11 @@ module RecordStore
|
|
|
19
20
|
# returns an array of Record objects that match the records which exist in the provider
|
|
20
21
|
def retrieve_current_records(zone:, stdout: $stdout)
|
|
21
22
|
retry_on_connection_errors do
|
|
22
|
-
session.zones.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
raise
|
|
28
|
-
end
|
|
23
|
+
session.zones.all_zone_records(account_id, zone).data.map do |record|
|
|
24
|
+
build_from_api(record, zone)
|
|
25
|
+
rescue StandardError
|
|
26
|
+
stdout.puts "Cannot build record: #{record}"
|
|
27
|
+
raise
|
|
29
28
|
end.compact
|
|
30
29
|
end
|
|
31
30
|
end
|
|
@@ -41,7 +40,7 @@ module RecordStore
|
|
|
41
40
|
|
|
42
41
|
def add(record, zone)
|
|
43
42
|
record_hash = api_hash(record, zone)
|
|
44
|
-
res = session.zones.
|
|
43
|
+
res = session.zones.create_zone_record(account_id, zone, record_hash)
|
|
45
44
|
|
|
46
45
|
if record.type == 'ALIAS'
|
|
47
46
|
txt_alias = retrieve_current_records(zone: zone).detect do |rr|
|
|
@@ -54,17 +53,17 @@ module RecordStore
|
|
|
54
53
|
end
|
|
55
54
|
|
|
56
55
|
def remove(record, zone)
|
|
57
|
-
session.zones.
|
|
56
|
+
session.zones.delete_zone_record(account_id, zone, record.id)
|
|
58
57
|
end
|
|
59
58
|
|
|
60
59
|
def update(id, record, zone)
|
|
61
|
-
session.zones.
|
|
60
|
+
session.zones.update_zone_record(account_id, zone, id, api_hash(record, zone))
|
|
62
61
|
end
|
|
63
62
|
|
|
64
63
|
def session
|
|
65
64
|
@dns ||= Dnsimple::Client.new(
|
|
66
65
|
base_url: secrets.fetch('base_url'),
|
|
67
|
-
access_token: secrets.fetch('api_token')
|
|
66
|
+
access_token: secrets.fetch('api_token'),
|
|
68
67
|
)
|
|
69
68
|
end
|
|
70
69
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
require 'fog/dynect'
|
|
2
2
|
require 'limiter'
|
|
3
3
|
|
|
4
|
-
Fog::DNS::
|
|
5
|
-
Fog::DNS::
|
|
4
|
+
Fog::Dynect::DNS::Real.extend(Limiter::Mixin)
|
|
5
|
+
Fog::Dynect::DNS::Real.limit_method(:request, rate: 5, interval: 1) # 5 RPS == 300 RPM
|
|
6
6
|
|
|
7
7
|
module RecordStore
|
|
8
8
|
class Provider::DynECT < Provider
|
|
@@ -36,11 +36,9 @@ module RecordStore
|
|
|
36
36
|
def retrieve_current_records(zone:, stdout: $stdout)
|
|
37
37
|
session.get_all_records(zone).body.fetch('data').flat_map do |_type, records|
|
|
38
38
|
records.map do |record_body|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
stdout.puts "Cannot build record: #{record_body}"
|
|
43
|
-
end
|
|
39
|
+
build_from_api(record_body)
|
|
40
|
+
rescue StandardError
|
|
41
|
+
stdout.puts "Cannot build record: #{record_body}"
|
|
44
42
|
end
|
|
45
43
|
end.compact
|
|
46
44
|
end
|
|
@@ -22,13 +22,11 @@ module RecordStore
|
|
|
22
22
|
|
|
23
23
|
# Unroll each record set into multiple records
|
|
24
24
|
record_set.data.map do |record|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
stdout.puts "Cannot build record: #{record}"
|
|
31
|
-
end
|
|
25
|
+
record_set_member = record_set.dup
|
|
26
|
+
record_set_member.data = [record]
|
|
27
|
+
build_from_api(record_set_member)
|
|
28
|
+
rescue StandardError
|
|
29
|
+
stdout.puts "Cannot build record: #{record}"
|
|
32
30
|
end
|
|
33
31
|
end
|
|
34
32
|
|
|
@@ -44,12 +42,10 @@ module RecordStore
|
|
|
44
42
|
private
|
|
45
43
|
|
|
46
44
|
def session
|
|
47
|
-
@dns ||=
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
)
|
|
52
|
-
end
|
|
45
|
+
@dns ||= Google::Cloud::Dns.new(
|
|
46
|
+
project_id: secrets.fetch('project_id'),
|
|
47
|
+
credentials: Google::Cloud::Dns::Credentials.new(secrets),
|
|
48
|
+
)
|
|
53
49
|
end
|
|
54
50
|
|
|
55
51
|
def secrets
|
|
@@ -23,7 +23,8 @@ module RecordStore
|
|
|
23
23
|
def record(zone:, fqdn:, type:, must_exist: false)
|
|
24
24
|
result = super(zone, fqdn, type)
|
|
25
25
|
raise_if_error!(result) if must_exist
|
|
26
|
-
return
|
|
26
|
+
return if result.is_a?(NS1::Response::Error)
|
|
27
|
+
|
|
27
28
|
result
|
|
28
29
|
end
|
|
29
30
|
|
|
@@ -52,6 +53,7 @@ module RecordStore
|
|
|
52
53
|
if result.is_a?(NS1::Response::UnparsableBodyError)
|
|
53
54
|
raise RecordStore::Provider::UnparseableBodyError, result.to_s
|
|
54
55
|
end
|
|
56
|
+
|
|
55
57
|
raise RecordStore::Provider::Error, result.to_s
|
|
56
58
|
end
|
|
57
59
|
end
|
|
@@ -19,7 +19,7 @@ module NS1::Transport
|
|
|
19
19
|
|
|
20
20
|
if response_hash.key?(X_RATELIMIT_PERIOD) && response_hash.key?(X_RATELIMIT_REMAINING)
|
|
21
21
|
sleep_time = response_hash[X_RATELIMIT_PERIOD].first.to_i /
|
|
22
|
-
|
|
22
|
+
[1, response_hash[X_RATELIMIT_REMAINING].first.to_i].max.to_f
|
|
23
23
|
|
|
24
24
|
rate_limit = RateLimitWaiter.new('NS1')
|
|
25
25
|
rate_limit.wait(sleep_time)
|