github.com/teknogeek/dnscontrol/v2@v2.10.1-0.20200227202244-ae299b55ba42/docs/adding-new-rtypes.md (about)

     1  ---
     2  layout: default
     3  title: Creating new DNS Resource Types (rtypes)
     4  ---
     5  
     6  # Creating new DNS Resource Types (rtypes)
     7  
     8  Everyone is familiar with A, AAAA, CNAME, NS and other Rtypes.
     9  However there are new record types being added all the time (possibly
    10  too many).  Each new record type requires special handling by
    11  DNSControl.
    12  
    13  If a record simply has a single "target", then there is little to
    14  do because it is handled similarly to A, CNAME, and so on.  However
    15  if there are multiple fields within the record you have more work
    16  to do.
    17  
    18  Our general philosophy is:
    19  
    20  * Internally the individual fields of a record are kept separate. If a particular provider combines them into one big string, that kind of thing is done in the provider code at the end of the food chain.  For example, an MX record has a Target (`aspmx.l.google.com.`) and a preference (`10`).  Some systems combine this into one string (`10 aspmx.l.google.com.`).  We keep the two values separate in `RecordConfig` and leave it up to the individual providers to merge them when required. An earlier implementation kept everything combined and we found ourselves constantly parsing and re-parsing the target. It was inefficient and lead to many bugs.
    21  * Anywhere we have a special case for a particular Rtype, we use a `switch` statement and have a `case` for every single record type, usually with a `default:` case that calls `panic()`. This way developers adding a new record type will quickly find where they need to add code (the panic will tell them where).  Before we did this, missing implementation code would go unnoticed for months.
    22  * Keep things alphabetical. If you are adding your record type to a case statement, function library, or whatever, please list it alphabetically along with the others when possible.
    23  
    24  ## Step 1: Update `RecordConfig` in `models/dns.go`
    25  
    26  If the record has any unique fields, add them to `RecordConfig`.
    27  The field name should be the record type, then the field name as
    28  used in `github.com/miekg/dns/types.go`. For example, the `CAA`
    29  record has a field called `Flag`, therefore the field name in
    30  `RecordConfig` is CaaFlag (not `CaaFlags` or `CAAFlags`).
    31  
    32  Here are some examples:
    33  
    34  ```
    35  type RecordConfig struct {
    36    ...
    37    MxPreference uint16            `json:"mxpreference,omitempty"` // FIXME(tlim): Rename to MxPreference
    38    SrvPriority  uint16            `json:"srvpriority,omitempty"`
    39    SrvWeight    uint16            `json:"srvweight,omitempty"`
    40    SrvPort      uint16            `json:"srvport,omitempty"`
    41    CaaTag       string            `json:"caatag,omitempty"`
    42    CaaFlag      uint8             `json:"caaflag,omitempty"`
    43    ...
    44  }
    45  ```
    46  
    47  ## Step 2: Add a capability for the record
    48  
    49  You'll need to mark which providers support this record type.  The
    50  initial PR should implement this record for the `bind` provider at
    51  a minimum, unless this is a fake or pseudo-type that only a particular
    52  provider supports.
    53  
    54  * Add the capability to the file `dnscontrol/providers/capabilities.go` (look for `CanUseAlias` and add
    55  it to the end of the list.)
    56  * Add this feature to the feature matrix in `dnscontrol/build/generate/featureMatrix.go` (Add it to the variable `matrix` then add it later in the file with a `setCap()` statement.
    57  * Add the capability to the list of features that zones are validated
    58    against (i.e. if you want dnscontrol to report an error if this
    59    feature is used with a DNS provider that doesn't support it). That's
    60    in the `checkProviderCapabilities` function in
    61    `pkg/normalize/validate.go`.
    62  * Mark the `bind` provider as supporting this record type by updating `dnscontrol/providers/bind/bindProvider.go` (look for `providers.CanUse` and you'll see what to do).
    63  
    64  DNSControl will warn/error if this new record is used with a
    65  provider that does not support the capability.
    66  
    67  * Add the capability to the validations in `pkg/normalize/validate.go`
    68    by adding it to `providerCapabilityChecks`
    69  * Some capabilities can't be tested for, such as `CanUseTXTMulti`.  If
    70    such testing can't be done, add it to the whitelist in function
    71    `TestCapabilitiesAreFiltered` in
    72    `pkg/normalize/capabilities_test.go`
    73  
    74  If the capabilities testing is not configured correctly, `go test ./...`
    75  will report something like the `MISSING` message below. In this
    76  example we removed `providers.CanUseCAA` from the
    77  `providerCapabilityChecks` list.
    78  
    79  ```
    80  --- FAIL: TestCapabilitiesAreFiltered (0.00s)
    81      capabilities_test.go:66: ok: providers.CanUseAlias (0) is checked for with "ALIAS"
    82      capabilities_test.go:68: MISSING: providers.CanUseCAA (1) is not checked by checkProviderCapabilities
    83      capabilities_test.go:66: ok: providers.CanUseNAPTR (3) is checked for with "NAPTR"
    84  ```
    85  
    86  ## Step 3: Add a helper function
    87  
    88  Add a function to `pkg/js/helpers.js` for the new record type.  This
    89  is the JavaScript file that defines `dnsconfig.js`'s functions like
    90  `A()` and `MX()`.  Look at the definition of A, MX and CAA for good
    91  examples to use as a base.
    92  
    93  Please add the function alphabetically with the others. Also, please run
    94  [prettier](https://github.com/prettier/prettier) on the file to ensure
    95  your code conforms to our coding standard:
    96  
    97      npm install prettier
    98      node_modules/.bin/prettier --write pkg/js/helpers.js
    99  
   100  FYI: If you change `pkg/js/helpers.js`, run `go generate` to update `pkg/js/static.go`.
   101  
   102  ## Step 4: Search for `#rtype_variations`
   103  
   104  Anywhere a rtype requires special handling has been marked with a
   105  comment that includes the string `#rtype_variations`.  Search for
   106  this string and add your new type to this code.
   107  
   108  ## Step 5: Add a `parse_tests` test case.
   109  
   110  Add at least one test case to the `pkg/js/parse_tests` directory.
   111  Test `013-mx.js` is a very simple one and is good for cloning.
   112  
   113  Run these tests via:
   114  
   115      cd dnscontrol/pkg/js
   116      go test ./...
   117  
   118  If this works, then you know the `dnsconfig.js` and `helpers.js`
   119  code is working correctly.
   120  
   121  As you debug, if there are places that haven't been marked
   122  `#rtype_variations` that should be, add such a comment.
   123  Every time you do this, an angel gets its wings.
   124  
   125  The tests also verify that for every "capability" there is a
   126  validation. This is explained in Step 2 (search for
   127  `TestCapabilitiesAreFiltered` or `MISSING`)
   128  
   129  ## Step 6: Add an `integrationTest` test case.
   130  
   131  Add at least one test case to the `integrationTest/integration_test.go`
   132  file. Look for `var tests =` and add the test to the end of this
   133  list.
   134  
   135  Each entry in the list is a new state.  For example:
   136  
   137  ```
   138    // MX
   139    tc("Empty"),                                    <<< 1
   140    tc("MX record", mx("@", 5, "foo.com.")),        <<< 2
   141    tc("Change MX pref", mx("@", 10, "foo.com.")),  <<< 3
   142  ```
   143  
   144  Line 1: An `tc()` entry with no records (just a comment). The test
   145  system will delete all records from the domain to make the domain
   146  match this empty configuration. This creates a "clean slate"
   147  situation.
   148  
   149  Line 2: A `tc()` entry with 1 record.  To get to this state, the
   150  provider will have to add the record. If this works, basic functionality
   151  for the MX record type has been achieved.
   152  
   153  Line 3: A `tc()` entry with 1 record, with a different priority.
   154  To get to this state, the provider will have to either change the
   155  priority on an existing record, or delete the old record and insert
   156  a new one. Either way, this test case assures us that the diff'ing
   157  functionality is working properly.
   158  
   159  If you look at the tests for `CAA`, it inserts a few records then
   160  attempts to modify each field of a record one at a time.  This test
   161  was useful because it turns out we hadn't written the code to
   162  properly see a change in priority. We fixed this bug before the
   163  code made it into production.
   164  
   165  Also notice that some tests include `.IfHasCapability()`. This
   166  limits the test to providers with certain capabilities.  You'll
   167  want to use this feature so that the tests only run on providers
   168  that support your new record type.
   169  
   170  To run the integration test with the BIND provider:
   171  
   172      cd dnscontrol/integrationTest
   173      go test -v -verbose -provider BIND
   174  
   175  Once the code works for BIND, consider submitting a PR at this point.
   176  
   177  As you debug, if there are places that haven't been marked
   178  `#rtype_variations` that should be, add such a comment.
   179  If you fail to do this, God kills a cute little kitten.
   180  
   181  ## Step 7: Support more providers
   182  
   183  Now add support other providers.  Add the `providers.CanUse...`
   184  flag to the provider and re-run the integration tests:
   185  
   186  For example, this will run the tests on Amazon AWS Route53:
   187  
   188      export R53_DOMAIN=dnscontroltest-r53.com  # Use a test domain.
   189      export R53_KEY_ID=CHANGE_TO_THE_ID
   190      export R53_KEY='CHANGE_TO_THE_KEY'
   191      go test -v -verbose -provider ROUTE53
   192  
   193  The test should reveal any bugs. Keep iterating between fixing the
   194  code and running the tests. When the tests all work, you are done.
   195  (Well, you might want to clean up some code a bit, but at least you
   196  know that everything is working.)
   197  
   198  If you find bugs that aren't covered by the tests, please please
   199  please add a test that demonstrates the bug THEN fix the bug. This
   200  will help all future contributors. If you need help with adding
   201  tests, please ask!