record_store 2.1.0 → 3.0.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 +4 -4
- data/bin/console +3 -2
- data/lib/record_store.rb +2 -0
- data/lib/record_store/changeset.rb +22 -3
- data/lib/record_store/cli.rb +59 -66
- data/lib/record_store/provider.rb +92 -82
- data/lib/record_store/provider/dnsimple.rb +123 -133
- data/lib/record_store/provider/dynect.rb +73 -71
- data/lib/record_store/record.rb +1 -0
- data/lib/record_store/version.rb +1 -1
- data/lib/record_store/zone.rb +62 -135
- data/lib/record_store/zone/config.rb +16 -9
- data/lib/record_store/zone/yaml_definitions.rb +105 -0
- metadata +3 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 6533d74657e0ba545a411f88adeab33272cb728b
         | 
| 4 | 
            +
              data.tar.gz: 79cdc4fd12512478cad79039f0c69eb38b7cd504
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 14019ec878ecc6593d7034c17c0cbd83eefeefc9b52b30ec8bb03c3178861ccd1c89c53c294654defec078bef29a3ca1eedd0ea72c838f0bd83cc25c70f9a7e2
         | 
| 7 | 
            +
              data.tar.gz: 418e35bc70829c278102d6f4442ef4c0a93612055ccf48eed85812b7bda57eac81475f1d1df643349d15ea4ed5342a5c93e9f2edd504083285c7d81409707850
         | 
    
        data/bin/console
    CHANGED
    
    | @@ -5,6 +5,7 @@ require 'bundler/setup' | |
| 5 5 | 
             
            require 'record_store'
         | 
| 6 6 |  | 
| 7 7 | 
             
            RecordStore.zones_path = File.expand_path('../../dev/zones', __FILE__)
         | 
| 8 | 
            +
            RecordStore.config_path = File.expand_path('../../dev/config.yml', __FILE__)
         | 
| 8 9 |  | 
| 9 | 
            -
            require ' | 
| 10 | 
            -
             | 
| 10 | 
            +
            require 'pry'
         | 
| 11 | 
            +
            binding.pry(RecordStore)
         | 
    
        data/lib/record_store.rb
    CHANGED
    
    | @@ -2,6 +2,7 @@ require 'json' | |
| 2 2 | 
             
            require 'yaml'
         | 
| 3 3 | 
             
            require 'active_support'
         | 
| 4 4 | 
             
            require 'active_support/core_ext/array/wrap'
         | 
| 5 | 
            +
            require 'active_support/core_ext/enumerable'
         | 
| 5 6 | 
             
            require 'active_support/core_ext/hash/keys'
         | 
| 6 7 | 
             
            require 'active_support/core_ext/string'
         | 
| 7 8 | 
             
            require 'active_model'
         | 
| @@ -20,6 +21,7 @@ require 'record_store/record/ns' | |
| 20 21 | 
             
            require 'record_store/record/txt'
         | 
| 21 22 | 
             
            require 'record_store/record/spf'
         | 
| 22 23 | 
             
            require 'record_store/record/srv'
         | 
| 24 | 
            +
            require 'record_store/zone/yaml_definitions'
         | 
| 23 25 | 
             
            require 'record_store/zone'
         | 
| 24 26 | 
             
            require 'record_store/zone/config'
         | 
| 25 27 | 
             
            require 'record_store/changeset'
         | 
| @@ -33,15 +33,34 @@ module RecordStore | |
| 33 33 | 
             
                  end
         | 
| 34 34 | 
             
                end
         | 
| 35 35 |  | 
| 36 | 
            -
                attr_reader :current_records, :desired_records, :removals, :additions, :updates
         | 
| 36 | 
            +
                attr_reader :current_records, :desired_records, :removals, :additions, :updates, :provider, :zone
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                def self.build_from(provider:, zone:)
         | 
| 39 | 
            +
                  current_zone = provider.build_zone(zone_name: zone.unrooted_name, config: zone.config)
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  self.new(
         | 
| 42 | 
            +
                    current_records: current_zone.records,
         | 
| 43 | 
            +
                    desired_records: zone.records,
         | 
| 44 | 
            +
                    provider: provider,
         | 
| 45 | 
            +
                    zone: zone.unrooted_name
         | 
| 46 | 
            +
                  )
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                def initialize(current_records: [], desired_records: [], provider:, zone:)
         | 
| 50 | 
            +
                  @current_records = Set.new(current_records)
         | 
| 51 | 
            +
                  @desired_records = Set.new(desired_records)
         | 
| 52 | 
            +
                  @provider = provider
         | 
| 53 | 
            +
                  @zone = zone
         | 
| 37 54 |  | 
| 38 | 
            -
                def initialize(current_records: [], desired_records: [])
         | 
| 39 | 
            -
                  @current_records, @desired_records = Set.new(current_records), Set.new(desired_records)
         | 
| 40 55 | 
             
                  @additions, @removals, @updates = [], [], []
         | 
| 41 56 |  | 
| 42 57 | 
             
                  build_changeset
         | 
| 43 58 | 
             
                end
         | 
| 44 59 |  | 
| 60 | 
            +
                def apply
         | 
| 61 | 
            +
                  provider.apply_changeset(self)
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
             | 
| 45 64 | 
             
                def unchanged
         | 
| 46 65 | 
             
                  current_records & desired_records
         | 
| 47 66 | 
             
                end
         | 
    
        data/lib/record_store/cli.rb
    CHANGED
    
    | @@ -11,14 +11,18 @@ module RecordStore | |
| 11 11 | 
             
                desc 'thaw', 'Thaws all zones under management to allow manual edits'
         | 
| 12 12 | 
             
                def thaw
         | 
| 13 13 | 
             
                  Zone.each do |_, zone|
         | 
| 14 | 
            -
                    zone. | 
| 14 | 
            +
                    zone.providers.each do |provider|
         | 
| 15 | 
            +
                      provider.thaw_zone(zone.unrooted_name) if provider.thawable?
         | 
| 16 | 
            +
                    end
         | 
| 15 17 | 
             
                  end
         | 
| 16 18 | 
             
                end
         | 
| 17 19 |  | 
| 18 20 | 
             
                desc 'freeze', 'Freezes all zones under management to prevent manual edits'
         | 
| 19 21 | 
             
                def freeze
         | 
| 20 22 | 
             
                  Zone.each do |_, zone|
         | 
| 21 | 
            -
                    zone. | 
| 23 | 
            +
                    zone.providers.each do |provider|
         | 
| 24 | 
            +
                      provider.freeze_zone(zone.unrooted_name) if provider.freezable?
         | 
| 25 | 
            +
                    end
         | 
| 22 26 | 
             
                  end
         | 
| 23 27 | 
             
                end
         | 
| 24 28 |  | 
| @@ -35,47 +39,55 @@ module RecordStore | |
| 35 39 | 
             
                option :verbose, desc: 'Print records that haven\'t diverged', aliases: '-v', type: :boolean, default: false
         | 
| 36 40 | 
             
                desc 'diff', 'Displays the DNS differences between the zone files in this repo and production'
         | 
| 37 41 | 
             
                def diff
         | 
| 42 | 
            +
                  puts "Diffing #{Zone.defined.count} zones"
         | 
| 43 | 
            +
             | 
| 38 44 | 
             
                  Zone.each do |name, zone|
         | 
| 45 | 
            +
                    changesets = zone.build_changesets
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                    next if !options.fetch('verbose') && changesets.empty?
         | 
| 39 48 | 
             
                    puts "Zone: #{name}"
         | 
| 40 49 |  | 
| 41 | 
            -
                     | 
| 50 | 
            +
                    changesets.each do |changeset|
         | 
| 51 | 
            +
                      next if !options.fetch('verbose') && changeset.changes.empty?
         | 
| 42 52 |  | 
| 43 | 
            -
             | 
| 44 | 
            -
                      puts " | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 53 | 
            +
                      puts '-' * 20
         | 
| 54 | 
            +
                      puts "Provider: #{changeset.provider.to_s}"
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                      if !changeset.additions.empty? || options.fetch('verbose')
         | 
| 57 | 
            +
                        puts "Add:"
         | 
| 58 | 
            +
                        changeset.additions.map(&:record).each do |record|
         | 
| 59 | 
            +
                          puts " - #{record.to_s}"
         | 
| 60 | 
            +
                        end
         | 
| 47 61 | 
             
                      end
         | 
| 48 | 
            -
                    end
         | 
| 49 62 |  | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 63 | 
            +
                      if !changeset.removals.empty? || options.fetch('verbose')
         | 
| 64 | 
            +
                        puts "Remove:"
         | 
| 65 | 
            +
                        changeset.removals.map(&:record).each do |record|
         | 
| 66 | 
            +
                          puts " - #{record.to_s}"
         | 
| 67 | 
            +
                        end
         | 
| 54 68 | 
             
                      end
         | 
| 55 | 
            -
                    end
         | 
| 56 69 |  | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 70 | 
            +
                      if !changeset.updates.empty? || options.fetch('verbose')
         | 
| 71 | 
            +
                        puts "Update:"
         | 
| 72 | 
            +
                        changeset.updates.map(&:record).each do |record|
         | 
| 73 | 
            +
                          puts " - #{record.to_s}"
         | 
| 74 | 
            +
                        end
         | 
| 61 75 | 
             
                      end
         | 
| 62 | 
            -
                    end
         | 
| 63 76 |  | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 77 | 
            +
                      if options.fetch('verbose')
         | 
| 78 | 
            +
                        puts "Unchanged:"
         | 
| 79 | 
            +
                        changeset.unchanged.each do |record|
         | 
| 80 | 
            +
                          puts " - #{record.to_s}"
         | 
| 81 | 
            +
                        end
         | 
| 68 82 | 
             
                      end
         | 
| 69 83 | 
             
                    end
         | 
| 70 | 
            -
             | 
| 71 | 
            -
                    puts "Empty diff" if diff.changes.empty?
         | 
| 72 84 | 
             
                    puts '=' * 20
         | 
| 73 85 | 
             
                  end
         | 
| 74 86 | 
             
                end
         | 
| 75 87 |  | 
| 76 88 | 
             
                desc 'apply', 'Applies the DNS changes'
         | 
| 77 89 | 
             
                def apply
         | 
| 78 | 
            -
                  zones =  | 
| 90 | 
            +
                  zones = Zone.modified
         | 
| 79 91 |  | 
| 80 92 | 
             
                  if zones.empty?
         | 
| 81 93 | 
             
                    puts "No changes to sync"
         | 
| @@ -84,8 +96,9 @@ module RecordStore | |
| 84 96 |  | 
| 85 97 | 
             
                  zones.each do |zone|
         | 
| 86 98 | 
             
                    abort "Attempted to apply invalid zone: #{zone.name}" unless zone.valid?
         | 
| 87 | 
            -
             | 
| 88 | 
            -
                     | 
| 99 | 
            +
             | 
| 100 | 
            +
                    changesets = zone.build_changesets
         | 
| 101 | 
            +
                    changesets.each(&:apply)
         | 
| 89 102 | 
             
                  end
         | 
| 90 103 |  | 
| 91 104 | 
             
                  puts "All zone changes deployed"
         | 
| @@ -93,7 +106,6 @@ module RecordStore | |
| 93 106 |  | 
| 94 107 | 
             
                option :name, desc: 'Zone to download', aliases: '-n', type: :string, required: true
         | 
| 95 108 | 
             
                option :provider, desc: 'Provider in which this zone exists', aliases: '-p', type: :string
         | 
| 96 | 
            -
                option :format, desc: 'Format', aliases: '-f', type: :string, default: 'file', enum: FORMATS
         | 
| 97 109 | 
             
                desc 'download', 'Downloads all records from zone and creates YAML zone definition in zones/ e.g. record-store download --name=shopify.io'
         | 
| 98 110 | 
             
                def download
         | 
| 99 111 | 
             
                  name = options.fetch('name')
         | 
| @@ -109,23 +121,19 @@ module RecordStore | |
| 109 121 | 
             
                  end
         | 
| 110 122 |  | 
| 111 123 | 
             
                  puts "Downloading records for #{name}"
         | 
| 112 | 
            -
                  Zone.download(name, provider | 
| 124 | 
            +
                  Zone.download(name, provider)
         | 
| 113 125 | 
             
                  puts "Records have been downloaded & can be found in zones/#{name}.yml"
         | 
| 114 126 | 
             
                end
         | 
| 115 127 |  | 
| 116 128 | 
             
                option :name, desc: 'Zone to reformat', aliases: '-n', type: :string, required: false
         | 
| 117 | 
            -
                option :format, desc: 'Format', aliases: '-f', type: :string, default: 'file', enum: FORMATS
         | 
| 118 129 | 
             
                desc 'reformat', 'Sorts and re-outputs the zone (or all zones) as specified format (file)'
         | 
| 119 130 | 
             
                def reformat
         | 
| 120 131 | 
             
                  name = options['name']
         | 
| 121 | 
            -
                  zones =  | 
| 122 | 
            -
             | 
| 123 | 
            -
                  else
         | 
| 124 | 
            -
                    Zone.all
         | 
| 125 | 
            -
                  end
         | 
| 132 | 
            +
                  zones = name ? [Zone.find(name)] : Zone.all
         | 
| 133 | 
            +
             | 
| 126 134 | 
             
                  zones.each do |zone|
         | 
| 127 135 | 
             
                    puts "Writing #{zone.name}"
         | 
| 128 | 
            -
                    zone.write | 
| 136 | 
            +
                    zone.write
         | 
| 129 137 | 
             
                  end
         | 
| 130 138 | 
             
                end
         | 
| 131 139 |  | 
| @@ -163,7 +171,7 @@ module RecordStore | |
| 163 171 |  | 
| 164 172 | 
             
                desc 'assert_empty_diff', 'Asserts there is no divergence between DynECT & the zone files'
         | 
| 165 173 | 
             
                def assert_empty_diff
         | 
| 166 | 
            -
                  zones =  | 
| 174 | 
            +
                  zones = Zone.modified.map(&:name)
         | 
| 167 175 |  | 
| 168 176 | 
             
                  unless zones.empty?
         | 
| 169 177 | 
             
                    abort "The following zones have diverged: #{zones.join(', ')}"
         | 
| @@ -173,23 +181,21 @@ module RecordStore | |
| 173 181 | 
             
                desc 'validate_records', 'Validates that all DNS records have valid definitions'
         | 
| 174 182 | 
             
                def validate_records
         | 
| 175 183 | 
             
                  invalid_zones = []
         | 
| 176 | 
            -
                  Zone.each do | | 
| 177 | 
            -
                     | 
| 178 | 
            -
                      invalid_zones << name
         | 
| 184 | 
            +
                  Zone.all.reject(&:valid?).each do |zone|
         | 
| 185 | 
            +
                    invalid_zones << zone.unrooted_name
         | 
| 179 186 |  | 
| 180 | 
            -
             | 
| 181 | 
            -
             | 
| 182 | 
            -
             | 
| 183 | 
            -
             | 
| 187 | 
            +
                    puts "#{zone.unrooted_name} definition is not valid:"
         | 
| 188 | 
            +
                    zone.errors.each do |field, msg|
         | 
| 189 | 
            +
                      puts " - #{field}: #{msg}"
         | 
| 190 | 
            +
                    end
         | 
| 184 191 |  | 
| 185 | 
            -
             | 
| 186 | 
            -
             | 
| 192 | 
            +
                    invalid_records = zone.records.reject(&:valid?)
         | 
| 193 | 
            +
                    puts '  Invalid records' if invalid_records.size > 0
         | 
| 187 194 |  | 
| 188 | 
            -
             | 
| 189 | 
            -
             | 
| 190 | 
            -
             | 
| 191 | 
            -
             | 
| 192 | 
            -
                        end
         | 
| 195 | 
            +
                    invalid_records.each do |record|
         | 
| 196 | 
            +
                      puts "    #{record.to_s}"
         | 
| 197 | 
            +
                      record.errors.each do |field, msg|
         | 
| 198 | 
            +
                        puts "      - #{field}: #{msg}"
         | 
| 193 199 | 
             
                      end
         | 
| 194 200 | 
             
                    end
         | 
| 195 201 | 
             
                  end
         | 
| @@ -197,13 +203,13 @@ module RecordStore | |
| 197 203 | 
             
                  if invalid_zones.size > 0
         | 
| 198 204 | 
             
                    abort "The following zones were invalid: #{invalid_zones.join(', ')}"
         | 
| 199 205 | 
             
                  else
         | 
| 200 | 
            -
                    puts "All  | 
| 206 | 
            +
                    puts "All zones have valid definitions."
         | 
| 201 207 | 
             
                  end
         | 
| 202 208 | 
             
                end
         | 
| 203 209 |  | 
| 204 210 | 
             
                desc 'validate_change_size', "Validates no more then particular limit of DNS records are removed per zone at a time"
         | 
| 205 211 | 
             
                def validate_change_size
         | 
| 206 | 
            -
                  zones =  | 
| 212 | 
            +
                  zones = Zone.modified
         | 
| 207 213 |  | 
| 208 214 | 
             
                  unless zones.empty?
         | 
| 209 215 | 
             
                    removals = zones.select do |zone|
         | 
| @@ -256,18 +262,5 @@ module RecordStore | |
| 256 262 | 
             
                    end
         | 
| 257 263 | 
             
                  end
         | 
| 258 264 | 
             
                end
         | 
| 259 | 
            -
             | 
| 260 | 
            -
                private
         | 
| 261 | 
            -
             | 
| 262 | 
            -
                def zones_modified
         | 
| 263 | 
            -
                  modified_zones, mutex = [], Mutex.new
         | 
| 264 | 
            -
                  Zone.all.map do |zone|
         | 
| 265 | 
            -
                    thread = Thread.new do
         | 
| 266 | 
            -
                      mutex.synchronize {modified_zones << zone} unless zone.unchanged?
         | 
| 267 | 
            -
                    end
         | 
| 268 | 
            -
                  end.each(&:join)
         | 
| 269 | 
            -
             | 
| 270 | 
            -
                  modified_zones
         | 
| 271 | 
            -
                end
         | 
| 272 265 | 
             
              end
         | 
| 273 266 | 
             
            end
         | 
| @@ -1,107 +1,117 @@ | |
| 1 1 | 
             
            module RecordStore
         | 
| 2 2 | 
             
              class Provider
         | 
| 3 | 
            -
                 | 
| 4 | 
            -
                   | 
| 3 | 
            +
                class << self
         | 
| 4 | 
            +
                  def provider_for(zone_name)
         | 
| 5 | 
            +
                    dns = Resolv::DNS.new(nameserver: ['8.8.8.8', '8.8.4.4'])
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                    begin
         | 
| 8 | 
            +
                      ns_server = dns.getresource(zone_name, Resolv::DNS::Resource::IN::SOA).mname.to_s
         | 
| 9 | 
            +
                    rescue Resolv::ResolvError => e
         | 
| 10 | 
            +
                      abort "Domain doesn't exist"
         | 
| 11 | 
            +
                    end
         | 
| 5 12 |  | 
| 6 | 
            -
             | 
| 7 | 
            -
                     | 
| 8 | 
            -
             | 
| 9 | 
            -
                     | 
| 13 | 
            +
                    case ns_server
         | 
| 14 | 
            +
                    when /dnsimple\.com\z/
         | 
| 15 | 
            +
                      'DNSimple'
         | 
| 16 | 
            +
                    when /dynect\.net\z/
         | 
| 17 | 
            +
                      'DynECT'
         | 
| 18 | 
            +
                    else
         | 
| 19 | 
            +
                      nil
         | 
| 20 | 
            +
                    end
         | 
| 10 21 | 
             
                  end
         | 
| 11 22 |  | 
| 12 | 
            -
                   | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 23 | 
            +
                  def record_types
         | 
| 24 | 
            +
                    Set.new([
         | 
| 25 | 
            +
                      'A',
         | 
| 26 | 
            +
                      'AAAA',
         | 
| 27 | 
            +
                      'ALIAS',
         | 
| 28 | 
            +
                      'CNAME',
         | 
| 29 | 
            +
                      'MX',
         | 
| 30 | 
            +
                      'NS',
         | 
| 31 | 
            +
                      'SPF',
         | 
| 32 | 
            +
                      'SRV',
         | 
| 33 | 
            +
                      'TXT',
         | 
| 34 | 
            +
                    ])
         | 
| 19 35 | 
             
                  end
         | 
| 20 | 
            -
                end
         | 
| 21 36 |  | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
                    'AAAA',
         | 
| 26 | 
            -
                    'ALIAS',
         | 
| 27 | 
            -
                    'CNAME',
         | 
| 28 | 
            -
                    'MX',
         | 
| 29 | 
            -
                    'NS',
         | 
| 30 | 
            -
                    'SPF',
         | 
| 31 | 
            -
                    'SRV',
         | 
| 32 | 
            -
                    'TXT',
         | 
| 33 | 
            -
                  ])
         | 
| 34 | 
            -
                end
         | 
| 37 | 
            +
                  def supports_alias?
         | 
| 38 | 
            +
                    false
         | 
| 39 | 
            +
                  end
         | 
| 35 40 |  | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 41 | 
            +
                  def build_zone(zone_name:, config:)
         | 
| 42 | 
            +
                    zone = Zone.new(name: zone_name)
         | 
| 43 | 
            +
                    zone.records = retrieve_current_records(zone: zone_name)
         | 
| 44 | 
            +
                    zone.config = config
         | 
| 39 45 |  | 
| 40 | 
            -
             | 
| 41 | 
            -
                   | 
| 42 | 
            -
                end
         | 
| 46 | 
            +
                    zone
         | 
| 47 | 
            +
                  end
         | 
| 43 48 |  | 
| 44 | 
            -
             | 
| 45 | 
            -
                   | 
| 46 | 
            -
             | 
| 49 | 
            +
                  # returns an array of Record objects that match the records which exist in the provider
         | 
| 50 | 
            +
                  def retrieve_current_records(zone:, stdout: $stdout)
         | 
| 51 | 
            +
                    raise NotImplementedError
         | 
| 52 | 
            +
                  end
         | 
| 47 53 |  | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 54 | 
            +
                  def add(record)
         | 
| 55 | 
            +
                    raise NotImplementedError
         | 
| 56 | 
            +
                  end
         | 
| 51 57 |  | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 54 | 
            -
             | 
| 58 | 
            +
                  def remove(record)
         | 
| 59 | 
            +
                    raise NotImplementedError
         | 
| 60 | 
            +
                  end
         | 
| 55 61 |  | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 58 | 
            -
                   | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 66 | 
            -
                         | 
| 67 | 
            -
                           | 
| 68 | 
            -
             | 
| 69 | 
            -
             | 
| 70 | 
            -
                           | 
| 71 | 
            -
             | 
| 72 | 
            -
             | 
| 73 | 
            -
                           | 
| 62 | 
            +
                  def update(id, record)
         | 
| 63 | 
            +
                    raise NotImplementedError
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  # Applies changeset to provider
         | 
| 67 | 
            +
                  def apply_changeset(changeset, stdout = $stdout)
         | 
| 68 | 
            +
                    begin
         | 
| 69 | 
            +
                      stdout.puts "Applying #{changeset.additions.size} additions, #{changeset.removals.size} removals, & #{changeset.updates.size} updates..."
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                      changeset.changes.each do |change|
         | 
| 72 | 
            +
                        case change.type
         | 
| 73 | 
            +
                          when :removal;
         | 
| 74 | 
            +
                            stdout.puts "Removing #{change.record}..."
         | 
| 75 | 
            +
                            remove(change.record, changeset.zone)
         | 
| 76 | 
            +
                          when :addition;
         | 
| 77 | 
            +
                            stdout.puts "Creating #{change.record}..."
         | 
| 78 | 
            +
                            add(change.record, changeset.zone)
         | 
| 79 | 
            +
                          when :update;
         | 
| 80 | 
            +
                            stdout.puts "Updating record with ID #{change.id} to #{change.record}..."
         | 
| 81 | 
            +
                            update(change.id, change.record, changeset.zone)
         | 
| 82 | 
            +
                          else
         | 
| 83 | 
            +
                            raise ArgumentError, "Unknown change type #{change.type.inspect}"
         | 
| 84 | 
            +
                        end
         | 
| 74 85 | 
             
                      end
         | 
| 75 | 
            -
                    end
         | 
| 76 86 |  | 
| 77 | 
            -
             | 
| 87 | 
            +
                      puts "\nPublished #{changeset.zone} changes to #{changeset.provider.to_s}\n"
         | 
| 88 | 
            +
                    end
         | 
| 78 89 | 
             
                  end
         | 
| 79 | 
            -
                end
         | 
| 80 90 |  | 
| 81 | 
            -
             | 
| 82 | 
            -
             | 
| 83 | 
            -
             | 
| 84 | 
            -
             | 
| 91 | 
            +
                  # Returns an array of the zones managed by provider as strings
         | 
| 92 | 
            +
                  def zones
         | 
| 93 | 
            +
                    raise NotImplementedError
         | 
| 94 | 
            +
                  end
         | 
| 85 95 |  | 
| 86 | 
            -
             | 
| 87 | 
            -
             | 
| 88 | 
            -
             | 
| 89 | 
            -
             | 
| 96 | 
            +
                  def secrets
         | 
| 97 | 
            +
                    @secrets ||= if File.exists?(RecordStore.secrets_path)
         | 
| 98 | 
            +
                      JSON.parse(File.read(RecordStore.secrets_path))
         | 
| 99 | 
            +
                    else
         | 
| 100 | 
            +
                      raise "You don't have a secrets.json file set up!"
         | 
| 101 | 
            +
                    end
         | 
| 102 | 
            +
                  end
         | 
| 90 103 |  | 
| 91 | 
            -
             | 
| 92 | 
            -
             | 
| 93 | 
            -
                    JSON.parse(File.read(RecordStore.secrets_path))
         | 
| 94 | 
            -
                  else
         | 
| 95 | 
            -
                    raise "You don't have a secrets.json file set up!"
         | 
| 104 | 
            +
                  def thawable?
         | 
| 105 | 
            +
                    self.respond_to?(:thaw_zone)
         | 
| 96 106 | 
             
                  end
         | 
| 97 | 
            -
                end
         | 
| 98 107 |  | 
| 99 | 
            -
             | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 108 | 
            +
                  def freezable?
         | 
| 109 | 
            +
                    self.respond_to?(:freeze_zone)
         | 
| 110 | 
            +
                  end
         | 
| 102 111 |  | 
| 103 | 
            -
             | 
| 104 | 
            -
             | 
| 112 | 
            +
                  def to_s
         | 
| 113 | 
            +
                    self.name.demodulize
         | 
| 114 | 
            +
                  end
         | 
| 105 115 | 
             
                end
         | 
| 106 116 | 
             
              end
         | 
| 107 117 | 
             
            end
         |