record_store 0.3.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d6555a4d5c8d65ea81e0341babdac09eba1c2df7
4
- data.tar.gz: d1a73ddb5660a5b4e7a0dd157b632b5c6325dfb7
3
+ metadata.gz: 09372c0d086fbae7cc74ca55aee2a955e1a2f761
4
+ data.tar.gz: 5b7bfedc45cb04ac8170286a608df7c83adcc71f
5
5
  SHA512:
6
- metadata.gz: eeb5548e87bd8d592d39254d18a6b19a2e56e69a8680320f240881dac8645503ed1561c39c7df4ae0ed97bb72d32e7348bfc81e3f796f26f0ae8b07014286c8e
7
- data.tar.gz: 62f9e83f0a8cbc56b1923d0c8a4047712cff41eaf1d6bb8103f490f8c1307e9deb642f566100672a38bad87626bcc15d9a5038ef28caa550b08cf4d1dc8b810f
6
+ metadata.gz: 89ee26625e1b23999f383762e9ca785f52b9f89572f0b45b72b68e522139dea82dd52cf01f90a7bc62178aeda67d1e0538b07140f43ed139c13226284eead2f0
7
+ data.tar.gz: f3f01c6a5b40cb4714bc0f147070c6d2590731c688a60fe7ae1ca83366fdaedc205ed7f24f2025a9f26239dbafb4328a3923db021101c1ad17348e4ffc5693c5
data/.gitignore CHANGED
@@ -1,9 +1,3 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
1
+ ._*
2
+ test/vcr_debug.log
3
+ dev/
data/Gemfile CHANGED
@@ -1,4 +1,21 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in record_store.gemspec
4
- gemspec
3
+ gem 'thor'
4
+ gem 'activesupport', '~> 4.2'
5
+ gem 'activemodel', '~> 4.2'
6
+ gem 'ejson'
7
+
8
+ gem 'fog'
9
+ gem 'fog-json'
10
+ gem 'fog-xml'
11
+ gem 'fog-dynect'
12
+
13
+ group :test do
14
+ gem 'mocha'
15
+ gem 'vcr'
16
+ end
17
+
18
+ group :development do
19
+ gem 'pry'
20
+ gem 'rake'
21
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,178 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ CFPropertyList (2.3.2)
5
+ activemodel (4.2.2)
6
+ activesupport (= 4.2.2)
7
+ builder (~> 3.1)
8
+ activesupport (4.2.2)
9
+ i18n (~> 0.7)
10
+ json (~> 1.7, >= 1.7.7)
11
+ minitest (~> 5.1)
12
+ thread_safe (~> 0.3, >= 0.3.4)
13
+ tzinfo (~> 1.1)
14
+ builder (3.2.2)
15
+ coderay (1.1.0)
16
+ ejson (1.0.0)
17
+ excon (0.45.4)
18
+ fission (0.5.0)
19
+ CFPropertyList (~> 2.2)
20
+ fog (1.36.0)
21
+ fog-aliyun (>= 0.1.0)
22
+ fog-atmos
23
+ fog-aws (>= 0.6.0)
24
+ fog-brightbox (~> 0.4)
25
+ fog-core (~> 1.32)
26
+ fog-dynect (~> 0.0.2)
27
+ fog-ecloud (~> 0.1)
28
+ fog-google (<= 0.1.0)
29
+ fog-json
30
+ fog-local
31
+ fog-powerdns (>= 0.1.1)
32
+ fog-profitbricks
33
+ fog-radosgw (>= 0.0.2)
34
+ fog-riakcs
35
+ fog-sakuracloud (>= 0.0.4)
36
+ fog-serverlove
37
+ fog-softlayer
38
+ fog-storm_on_demand
39
+ fog-terremark
40
+ fog-vmfusion
41
+ fog-voxel
42
+ fog-xenserver
43
+ fog-xml (~> 0.1.1)
44
+ ipaddress (~> 0.5)
45
+ 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
+ fog-atmos (0.1.0)
52
+ fog-core
53
+ fog-xml
54
+ fog-aws (0.7.6)
55
+ fog-core (~> 1.27)
56
+ fog-json (~> 1.0)
57
+ fog-xml (~> 0.1)
58
+ ipaddress (~> 0.8)
59
+ fog-brightbox (0.9.0)
60
+ fog-core (~> 1.22)
61
+ fog-json
62
+ inflecto (~> 0.0.2)
63
+ fog-core (1.32.1)
64
+ builder
65
+ excon (~> 0.45)
66
+ formatador (~> 0.2)
67
+ mime-types
68
+ net-scp (~> 1.1)
69
+ net-ssh (>= 2.1.3)
70
+ fog-dynect (0.0.2)
71
+ fog-core
72
+ fog-json
73
+ fog-xml
74
+ fog-ecloud (0.3.0)
75
+ fog-core
76
+ fog-xml
77
+ fog-google (0.1.0)
78
+ fog-core
79
+ fog-json
80
+ fog-xml
81
+ fog-json (1.0.2)
82
+ fog-core (~> 1.0)
83
+ multi_json (~> 1.10)
84
+ fog-local (0.2.1)
85
+ fog-core (~> 1.27)
86
+ fog-powerdns (0.1.1)
87
+ fog-core (~> 1.27)
88
+ fog-json (~> 1.0)
89
+ fog-xml (~> 0.1)
90
+ fog-profitbricks (0.0.5)
91
+ fog-core
92
+ fog-xml
93
+ nokogiri
94
+ fog-radosgw (0.0.4)
95
+ fog-core (>= 1.21.0)
96
+ fog-json
97
+ fog-xml (>= 0.0.1)
98
+ fog-riakcs (0.1.0)
99
+ fog-core
100
+ fog-json
101
+ fog-xml
102
+ fog-sakuracloud (1.4.0)
103
+ fog-core
104
+ fog-json
105
+ fog-serverlove (0.1.2)
106
+ fog-core
107
+ fog-json
108
+ fog-softlayer (1.0.2)
109
+ fog-core
110
+ fog-json
111
+ fog-storm_on_demand (0.1.1)
112
+ fog-core
113
+ fog-json
114
+ fog-terremark (0.1.0)
115
+ fog-core
116
+ fog-xml
117
+ fog-vmfusion (0.1.0)
118
+ fission
119
+ fog-core
120
+ fog-voxel (0.1.0)
121
+ fog-core
122
+ fog-xml
123
+ fog-xenserver (0.2.2)
124
+ fog-core
125
+ fog-xml
126
+ fog-xml (0.1.2)
127
+ fog-core
128
+ nokogiri (~> 1.5, >= 1.5.11)
129
+ formatador (0.2.5)
130
+ i18n (0.7.0)
131
+ inflecto (0.0.2)
132
+ ipaddress (0.8.0)
133
+ json (1.8.3)
134
+ metaclass (0.0.4)
135
+ method_source (0.8.2)
136
+ mime-types (2.6.1)
137
+ mini_portile (0.6.2)
138
+ minitest (5.9.0)
139
+ mocha (1.1.0)
140
+ 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)
147
+ pry (0.10.1)
148
+ coderay (~> 1.1.0)
149
+ method_source (~> 0.8.1)
150
+ slop (~> 3.4)
151
+ rake (10.4.2)
152
+ slop (3.6.0)
153
+ thor (0.19.1)
154
+ thread_safe (0.3.5)
155
+ tzinfo (1.2.2)
156
+ thread_safe (~> 0.1)
157
+ vcr (2.9.3)
158
+ xml-simple (1.1.5)
159
+
160
+ PLATFORMS
161
+ ruby
162
+
163
+ DEPENDENCIES
164
+ activemodel (~> 4.2)
165
+ activesupport (~> 4.2)
166
+ ejson
167
+ fog
168
+ fog-dynect
169
+ fog-json
170
+ fog-xml
171
+ mocha
172
+ pry
173
+ rake
174
+ thor
175
+ vcr
176
+
177
+ BUNDLED WITH
178
+ 1.11.2
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2015 Paul Barry
3
+ Copyright (c) 2016 Shopify
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
9
  copies of the Software, and to permit persons to whom the Software is
10
10
  furnished to do so, subject to the following conditions:
11
11
 
12
- The above copyright notice and this permission notice shall be included in
13
- all copies or substantial portions of the Software.
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
14
 
15
15
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
16
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
17
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- THE SOFTWARE.
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -1,73 +1,160 @@
1
1
  # Record Store
2
2
 
3
- Store records in your database using Sequel
3
+ Record Store is a tool to manage DNS through a git-based workflow.
4
4
 
5
- ## Installation
5
+ ## Getting Started
6
6
 
7
- Add this line to your application's Gemfile:
8
-
9
- ```ruby
10
- gem 'record_store'
11
7
  ```
8
+ record-store apply # Applies the DNS changes
9
+ record-store assert_empty_diff # Asserts there is no divergence between DynECT & the zone files
10
+ record-store diff # Displays the DNS differences between the zone files in this repo and production
11
+ record-store download -n, --name=NAME # Downloads all records from zone and creates YAML zone definition in zones/ e.g. record-store download --name=sho...
12
+ record-store freeze # Freezes all zones under management to prevent manual edits
13
+ record-store help [COMMAND] # Describe available commands or one specific command
14
+ record-store list # Lists out records in YAML zonefiles
15
+ record-store secrets # Decrypts DynECT credentials
16
+ record-store sort -n, --name=NAME # Sorts the zonefile alphabetically e.g. record-store sort --name=shopify.io
17
+ record-store thaw # Thaws all zones under management to allow manual edits
18
+ record-store validate_all_present # Validates that all the zones that are expected are defined
19
+ record-store validate_change_size # Validates no more then particular limit of DNS records are removed per zone at a time
20
+ record-store validate_initial_state # Validates state hasn't diverged since the last deploy
21
+ record-store validate_records # Validates that all DNS records have valid definitions
22
+ ```
23
+
24
+ ## Providers
25
+
26
+ Below is the list of DNS providers supported by Record Store. PRs [adding more](#adding-new-providers) are welcome.
27
+
28
+ ### DNSimple
29
+
30
+ Record Store uses DNSimple's [v1 API](https://developer.dnsimple.com/v1/). To use DNSimple, you'll need to add the primary user's `email` and `api_token` to `secrets.json`. There's currently no support for 2FA.
31
+
32
+ ### DynECT
33
+
34
+ In order to use DynECT, you'll need to create a user that has the [correct read and write permissions](#dynect-permissions). Add the user's `username` & `password` (i.e. API password) as well as your DynECT `customer` name to `secrets.json`.
35
+
36
+ #### Design
37
+
38
+ The DynECT provider uses [DynECT's DNS API](https://help.dyn.com/rest-resources/) to sync the YAML zone files. DynECT uses an [update/publish cycle](https://help.dyn.com/understanding-works-api/) in their API which means no changes take place until POSTing to the publish endpoint. This allows us to handle all failures by discarding the changes we attempted to create.
39
+
40
+ The DynECT zones managed by Record Store are frozen in DynECT (frozen zones cannot be changed); the deploy process will thaw them so it can make the necessary changes, and refreeze once the deploy process has completed.
41
+
42
+
43
+ #### DynECT permissions
44
+
45
+ The permissions required are broken into 2 groups:
46
+
47
+ * READ: `RecordGet`, `ZoneGet`
48
+ * WRITE: `RecordAdd`, `RecordDelete`, `ZonePublish`, `ZoneDiscardChangeset`, `ZoneFreeze`, `ZoneThaw`, `ZoneAddNode`,
49
+ `ZoneRemoveNode`
50
+
51
+ All CI validations only require READ permissions; deplyoing requires a user with READ and WRITE permissions.
52
+
53
+ For a breakdown of what each permssion allows read through [DynECT's permissions guide](https://help.dyn.com/user-and-group-permissions/).
54
+
55
+ ----
56
+
57
+ # Architecture
58
+
59
+ All CLI commands are defined in [`lib/record_store/cli.rb`](lib/record_store/cli.rb) with [Thor](https://github.com/erikhuda/thor).
60
+
61
+ ### Zones and Records
62
+
63
+ The `Zone` and `Record` models are representations of their DNS equivalents. Both have validations to ensure configurations are RFC compliant. These are specified using `ActiveModel::Validations`.
64
+
65
+ Most CLI interactions are through the `Zone` model.
66
+
67
+ ### Providers
68
+
69
+ In order to be provider agnostic, Record Store encapsulates all provider interactions in the `Provider` model and its children. A provider is initialized for each `zone`.
12
70
 
13
- And then execute:
14
71
 
15
- $ bundle
72
+ ### Changeset
16
73
 
17
- Or install it yourself as:
74
+ Changesets are how Record Store knows what updates to make. A `Changeset` is generated by comparing the current records in a zone with the desired final state. A `Changeset` is composed of one or more `Changeset::Change`. Each `Change` is either an `addition`, `removal`, or `update`. Since the ID of records aren't specified in zone files, FQDNs are used to dedup when records can be updated or when new ones need to be created.
18
75
 
19
- $ gem install record_store
76
+ When running `bin/record-store apply`, a `Changeset` is generated by comparing the current records in a zone's YAML file with the records the provider defines. A zone's YAML file is always considered the primary source of truth.
20
77
 
21
- ## Usage
78
+ ----
22
79
 
23
- If you have an application where you are working with records as hashes and you would like store them in your database using Sequel, this gem can help make that happen. To use Record Store, you should subclass the `RecordStore` class at least once in your application. If for example you already have a reference to a Sequel database stored in a constant called `DB`, then you would do something lke this:
80
+ # Development
24
81
 
82
+ To get started developing on Record Store, run `bin/setup`. This will create a development directory, `dev/`, that mimics what a production directory managing DNS records using Record Store would look like. Use it as a sandbox when developing Record Store.
83
+
84
+ ### Adding new Providers
85
+
86
+ To add a new Provider, create a class inherriting `Provider` in [`lib/record_store/provider/`](lib/record_store/provider/). The [DynECT provider](lib/record_store/provider/dnsimple.rb) is good to use as a reference implementation.
87
+
88
+ **Note**: _there's no need to wrap `Provider#apply_changeset` unless it's necessary to do something before/after making changes to a zone._
89
+
90
+ Provider API interactions are tested with [VCR](https://github.com/vcr/vcr). To generate the fixtures, update [`test/dummy/secrets.json`](test/dummy/secrets.json) with valid credentials, run the test suite, and change the values back to stub credentials.
91
+
92
+ **Important**: _be sure to [filter sensitive data](https://github.com/Shopify/record_store/blob/1ec0d1410cf8bedf79bc63e8e4cdc7cdb0f1019b/test/test_helper.rb#L23-L56) from the fixtures or you're going to have a bad time._
93
+
94
+ Outline of [`Provider`](lib/record_store/provider.rb):
25
95
  ```ruby
26
- class Store < RecordStore
27
- def self.database
28
- DB
96
+ class Provider
97
+ # Creates a new record to the zone. It is expected this call modifies external state.
98
+ #
99
+ # Arguments:
100
+ # record - a kind of `Record`
101
+ def add(record)
29
102
  end
30
- end
31
- ```
32
103
 
33
- Then for each table you want to store records in, you subclass that class:
104
+ # Deletes an existing record from the zone. It is expected this call modifies external state.
105
+ #
106
+ # Arguments:
107
+ # record - a kind of `Record`
108
+ def remove(record)
109
+ end
34
110
 
35
- ```ruby
36
- class UserStore < Store
37
- end
38
- ```
111
+ # Updates an existing record in the zone. It is expected this call modifies external state.
112
+ #
113
+ # Arguments:
114
+ # id - provider specific ID of record to update
115
+ # record - a kind of `Record` which the record with `id` should be updated to
116
+ def update(id, record)
117
+ end
39
118
 
40
- You can now store records in the `users` table like this:
119
+ # Downloads all the records from the provider.
120
+ #
121
+ # Returns: an array of `Record` for each record in the provider's zone
122
+ def retrieve_current_records
123
+ end
41
124
 
42
- ```ruby
43
- UserStore << { first_name: 'Paul', last_name: 'Barry' }
44
- ```
125
+ # Returns an array of the zones managed by provider as strings
126
+ def zones
127
+ end
45
128
 
46
- You can also add validations to your store, like this:
129
+ ######## NOTE ########
130
+ # The following methods only need to be implemented if the provider supports the ability to
131
+ # lock/unlock changes to zones.
132
+ ######################
47
133
 
48
- ```ruby
49
- class UserStore < Store
50
- required_attributes :first_name, :last_name
134
+ # Lock the ability to make any changes to the zone without unlocking it first. It is expected
135
+ # this call modifies external state.
136
+ def freeze_zone
137
+ end
138
+
139
+ # Unlocks the zone to allow making changes (see `Provider#freeze_zone`).
140
+ def thaw
141
+ end
51
142
  end
52
143
  ```
53
144
 
54
- If you try to store a record without values for those attributes, you will get an error:
145
+ #### Provider-Specific Records
55
146
 
56
- ```ruby
57
- UserStore << {}
58
- # => { errors: ["First Name is required", "Last Name is required"]}
59
- ```
147
+ 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)).
148
+
149
+ #### Secrets
150
+
151
+ When adding a new provider, be sure to update the `secrets.json` in [`template/secrets.json`](template/secrets.json) and [`test/dummy/secrets.json`](test/dummy/secrets.json) with the new provider and required fields for the API to work.
60
152
 
61
- ## Development
62
153
 
63
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
154
+ ### Test Changes on Providers
64
155
 
65
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
156
+ In order to test changes on providers, you're going to need to update `dev/secrets.json` with credentials. **Note**: make sure the credentials are for test zone(s) as the changes specified in the directory **will be applied**.
66
157
 
67
- ## Contributing
158
+ # Acknowledgements
68
159
 
69
- 1. Fork it ( https://github.com/[my-github-username]/record_store/fork )
70
- 2. Create your feature branch (`git checkout -b my-new-feature`)
71
- 3. Commit your changes (`git commit -am 'Add some feature'`)
72
- 4. Push to the branch (`git push origin my-new-feature`)
73
- 5. Create a new Pull Request
160
+ Big thanks to [@pjb3](https://github.com/pjb3) for graciously letting us use the `record_store` gem namespace.