New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Manage gcp-types/dns-v1 Records #62

Closed
davidebelloni opened this Issue Nov 14, 2017 · 3 comments

Comments

Projects
None yet
4 participants
@davidebelloni

davidebelloni commented Nov 14, 2017

Hi,
I'v tried to manage the Record sets in a zone on CloudDNS with the following config:

- name: goog-resourcerecordsets
  type: gcp-types/dns-v1:changes
  properties:
    project: {{ env["project"] }}
    managedZone: $(ref.goog.name)
    additions:
    - name: prod.name.it.
      type: A
      ttl: 300
      rrdatas:
      - <IP>

But I've the following error when change additions:

The fingerprint of the deployment is JDiKhbhYE0QxhypamHP3Ng==
Waiting for update [operation-1510672393864-55df2d3688341-d0f6966a-176378ae]...failed.                                                                                                                                                                                                                                    
ERROR: (gcloud.deployment-manager.deployments.update) Error in Operation [operation-1510672393864-55df2d3688341-d0f6966a-176378ae]: errors:
- code: NO_METHOD_TO_UPDATE_FIELD
  message: No method found to update field 'additions' on resource 'goog-resourcerecordsets'
    of type 'dns-v1'. The resource may need to be recreated with the new field.

Is there a method to manage creation, updates and deletion of DNS records with DM?
Without the workaround to rename the DM resources.

Thanks

@adamharwayne

This comment has been minimized.

Contributor

adamharwayne commented Nov 14, 2017

Hi,

There is no good way to manage DNS records via DM, because, as you mentioned, each change needs a unique name within DM. In addition, it is not possible for DM to recognize when a record is to be removed.

I put together a simple, non-production proof of concept that seems to work correctly. However, it creates and removes each record on its own, so it is not production quality, as it will allow records to disappear for short periods of time before being recreated with a new value (as opposed to the correct behavior of altering the value atomically).

Yaml:

imports:
- path: dnsZone.py

resources:
- name: dm-zone
  type: dnsZone.py
  properties:
    dnsName: dm.deployment-manager.net.
    description: My description.
    resourceRecordSets:
    - name: foo.dm.deployment-manager.net.
      type: TXT
      ttl: 3600
      rrdatas:
      # Due to the way the RFC defines TXT records, you really want to surround
      # them in double quotes when sent to the DNS API, otherwise they are split
      # on whitespace into a series of strings.
      - '"Set by DM."'
    - name: bar.dm.deployment-manager.net.
      type: A
      ttl: 3600
      rrdatas:
      - 1.2.3.4
      - 1.2.3.5

dnsZone.py:

"""
Creates a managedZone and resource record sets in that zone. Note that each
resource record is applied by itself, multiple resource records are _not_
applied in a single atomic update.
"""

from zlib import crc32

def GenerateConfig(context):
  resources = []

  # We are going to use this type provider all over the place, so get it into a
  # local variable for easy use.
  dnsTypeProvider = 'gcp-types/dns-v1'

  # Create the zone.
  zoneName = context.env['name']
  resources.append({
    'name': zoneName,
    'type': '%(dnsTypeProvider)s:managedZones' % { 'dnsTypeProvider': dnsTypeProvider },
    'properties': {
      'dnsName': context.properties['dnsName'],
      'description': context.properties['description'],
    },
  })

  # For each ResourceRecordSet. Create:
  # 1. A Change to create it.
  # 2. A Change to delete it.
  for resourceRecordSet in context.properties['resourceRecordSets']:
    # Updates will match names, so create a stable name for this RRS.
    stableName = GenerateStableResourceRecordSetName(resourceRecordSet)
    # Change to create it.
    resources.append({
      'name': '%(stableName)s-create' % { 'stableName': stableName },
      'action': '%(dnsTypeProvider)s:dns.changes.create' % { 'dnsTypeProvider': dnsTypeProvider },
      'metadata': {
        'runtimePolicy': [
          'CREATE',
        ],
      },
      'properties': {
        'managedZone': '$(ref.%(zoneName)s.name)' % { 'zoneName': zoneName },
        'additions': [
          resourceRecordSet,
        ],
      },
    })
    # Change to delete it.
    resources.append({
      'name': '%(stableName)s-delete' % { 'stableName': stableName },
      'action': '%(dnsTypeProvider)s:dns.changes.create' % { 'dnsTypeProvider': dnsTypeProvider },
      'metadata': {
        'runtimePolicy': [
          'DELETE',
        ],
      },
      'properties': {
        'managedZone': '$(ref.%(zoneName)s.name)' % { 'zoneName': zoneName },
        'deletions': [
          resourceRecordSet,
        ],
      },
    })

  return { 'resources': resources }

def GenerateStableResourceRecordSetName(resourceRecordSet):
  return '%(name)s-%(type)s-%(ttl)s-%(rrdataHash)s' % {
    'name': resourceRecordSet['name'],
    'type': resourceRecordSet['type'].lower(),
    'ttl': resourceRecordSet['ttl'],
    'rrdataHash': GenerateStableHashOfRrdatas(resourceRecordSet['rrdatas']),
  }

def GenerateStableHashOfRrdatas(rrdatas):
  # \0 is a character that won't be in any of the real strings.
  # TODO: Should the order be canocalized?
  rrdatasString = '\0'.join(rrdatas)
  return crc32(rrdatasString)

dnsZone.py.schema

info:
  title: Cloud DNS
  author: harwayne@
  description: >
    Simple prototype to control DNS from inside DM. Note that this provides no
    atomicity, so probably shouldn't be used for any Production workload. Does
    seem to work well for simple cases like 'point a DNS entry at the dev stack
    I just brought up.'

required:
- dnsName
- description
- resourceRecordSets

properties:
  dnsName:
    description: >
      The dnsName parameter used in the ManagedZone. See
      https://cloud.google.com/dns/api/v1/managedZones. 'The DNS name of this
      managed zone, for instance "example.com.".'
    type: string
    # TODO: Find a good pattern. For now, just assert that it ends in a dot, as
    # that is the most common mistake.
    pattern: \.$
  description:
    description: >
      The description of the managedZone. Oddly enough, this is required by the
      Cloud DNS API.
    type: string
    pattern: ^.{0,1023}$
  resourceRecordSets:
    description: >
      The ResourceRecordSets meant to be in the zone. See
      https://cloud.google.com/dns/api/v1/resourceRecordSets.
    type: array
    object:
      description: >
        An individual ResourceRecordSet.
      type: object
      item:
        required:
        - name
        - type
        - ttl
        - rrdatas
        name:
          description: >
            Name of the DNS record. Must end in dnsName.
          type: string
          # TODO: Same as dnsName, find a better pattern. For now assert the
          # trailing dot as that is the most common mistake.
          pattern: \.$
        type:
          description: >
            Type of the record.
          type: string
          enum:
          - A
          - AAAA
          - SOA
          - MX
          - NS
          - TXT
        ttl:
          description: >
            The time-to-live cache seconds for this record.
          type: integer
          minimum: 0
        rrdatas:
          description: >
            As defined in RFC 1035 (section 5) and RFC 1034 (section 3.6.1).
          type: array
          object:
            type: string
@dud225

This comment has been minimized.

dud225 commented Jan 26, 2018

Hello @adamharwayne

More libs than what is mentioned here may be included now ?

Any news regarding the documentation of runtimePolicy ?

Regards

@aljim

This comment has been minimized.

Contributor

aljim commented Mar 19, 2018

There are no new libraries, closing the issue since a workaround has been posted

@aljim aljim closed this Mar 19, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment