Skip to content

Instantly share code, notes, and snippets.

@troykelly
Last active January 29, 2025 00:01
Show Gist options
  • Save troykelly/94c9d1fc3011abd3f0ba0a679a38c31a to your computer and use it in GitHub Desktop.
Save troykelly/94c9d1fc3011abd3f0ba0a679a38c31a to your computer and use it in GitHub Desktop.
--- /home/vscode/.local/lib/python3.11/site-packages/octodns_cloudflare/__init__.py 2025-01-28 23:37:03.687375946 +0000
+++ /workspaces/dns/temp-cloudflare-patch/__init__.py 2025-01-28 23:40:24.543880502 +0000
@@ -1072,33 +1072,87 @@
self._try_request('DELETE', path)
def _apply_Delete(self, change):
+ """
+ Delete an existing record (DNS record or Pagerule) in Cloudflare.
+
+ Raises CloudflareError if critical data (e.g., zone_id, record id) is missing,
+ so the entire sync fails rather than silently skipping.
+ """
existing = change.existing
existing_name = existing.fqdn[:-1]
- # Make sure to map ALIAS to CNAME when looking for the target to delete
+ # Map ALIAS to CNAME for the purposes of deletion
existing_type = 'CNAME' if existing._type == 'ALIAS' else existing._type
+
for record in self.zone_records(existing.zone):
+ # Handle pagerules
if 'targets' in record and self.pagerules:
- uri = record['targets'][0]['constraint']['value']
+ constraint = record['targets'][0].get('constraint')
+ if not constraint or 'value' not in constraint:
+ raise CloudflareError({
+ 'errors': [{
+ 'message': (
+ f"Pagerule for '{existing_name}' missing "
+ f"'constraint' or 'constraint.value'."
+ )
+ }]
+ })
+
+ uri = constraint['value']
uri = '//' + uri if not uri.startswith('http') else uri
parsed_uri = urlsplit(uri)
record_name = parsed_uri.netloc
record_type = 'URLFWD'
- zone_id = self.zones.get(existing.zone.name, False)
- if (
- existing_name == record_name
- and existing_type == record_type
- ):
- path = f'/zones/{zone_id}/pagerules/{record["id"]}'
+ zone_id = self.zones.get(existing.zone.name)
+ if not zone_id:
+ raise CloudflareError({
+ 'errors': [{
+ 'message': (
+ f"No zone_id found for pagerule in zone "
+ f"{existing.zone.name}."
+ )
+ }]
+ })
+
+ rule_id = record.get('id')
+ if not rule_id:
+ raise CloudflareError({
+ 'errors': [{
+ 'message': (
+ f"No pagerule 'id' found for record {record}."
+ )
+ }]
+ })
+
+ if existing_name == record_name and existing_type == record_type:
+ path = f'/zones/{zone_id}/pagerules/{rule_id}'
self._try_request('DELETE', path)
+
+ # Handle normal DNS records
else:
if (
- existing_name == record['name']
- and existing_type == record['type']
+ existing_name == record.get('name')
+ and existing_type == record.get('type')
):
- path = (
- f'/zones/{record["zone_id"]}/dns_records/'
- f'{record["id"]}'
- )
+ zone_id = record.get('zone_id') or self.zones.get(existing.zone.name)
+ if not zone_id:
+ raise CloudflareError({
+ 'errors': [{
+ 'message': (
+ f"No zone_id found for record {record} "
+ f"in zone {existing.zone.name}."
+ )
+ }]
+ })
+
+ record_id = record.get('id')
+ if not record_id:
+ raise CloudflareError({
+ 'errors': [{
+ 'message': f"No record 'id' found for record {record}."
+ }]
+ })
+
+ path = f'/zones/{zone_id}/dns_records/{record_id}'
self._try_request('DELETE', path)
def _apply(self, plan):
name: OctoDNS Sync
on:
pull_request:
paths:
- 'config/**'
- 'providers/**'
- '.github/workflows/sync-providers.yaml'
types: [closed]
branches:
- main
concurrency:
group: sync-providers-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
env:
LOGFILE: sync.log
jobs:
sync:
name: Sync with Providers
runs-on: ubuntu-latest
if: github.event.pull_request.merged == true
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Install Dependencies
run: |
sudo apt-get update && sudo apt-get install -y jq
pip install --upgrade pip
pip install -r requirements.txt
- name: Determine octodns-cloudflare version
id: determine_cf_version
run: |
CF_VERSION=$(pip show octodns-cloudflare | grep '^Version:' | awk '{print $2}')
echo "OCTODNS_CLOUDFLARE_VERSION=$CF_VERSION" >> "$GITHUB_OUTPUT"
- name: Patch Cloudflare provider if needed
if: steps.determine_cf_version.outputs.OCTODNS_CLOUDFLARE_VERSION == '0.0.7'
continue-on-error: true
run: |
echo "octodns-cloudflare is version 0.0.7. Attempting to apply patch..."
# 1. Ensure 'patch' is installed (usually pre-installed on Ubuntu runners).
if ! command -v patch >/dev/null 2>&1; then
echo "ERROR: 'patch' command is not available. Skipping patch."
exit 0
fi
# 2. Download the patch file from Gist (fail if the request fails -f).
PATCH_URL="https://gist.githubusercontent.com/troykelly/94c9d1fc3011abd3f0ba0a679a38c31a/raw/octodns-cloudflare-keyerror.patch"
PATCH_TMP="/tmp/octodns-cloudflare-keyerror.patch"
echo "Downloading patch from $PATCH_URL ..."
if ! curl -fSL "$PATCH_URL" -o "$PATCH_TMP"; then
echo "ERROR: Failed to download patch from $PATCH_URL. Skipping patch."
exit 0
fi
# 3. Check that the downloaded file isn't empty or missing.
if [ ! -s "$PATCH_TMP" ]; then
echo "ERROR: Patch file '$PATCH_TMP' is empty or missing. Skipping patch."
exit 0
fi
# 4. Identify the location of the installed octodns-cloudflare package.
LOCATION=$(pip show octodns-cloudflare | grep '^Location:' | awk '{print $2}')
if [ -z "$LOCATION" ]; then
echo "ERROR: Could not determine octodns-cloudflare install location. Skipping patch."
exit 0
fi
TARGET_FILE="${LOCATION}/octodns_cloudflare/__init__.py"
if [ ! -f "$TARGET_FILE" ]; then
echo "ERROR: Target file $TARGET_FILE does not exist. Skipping patch."
exit 0
fi
# 5. Apply the patch (force to ignore already-applied hunks).
echo "Patching file: $TARGET_FILE"
if ! patch --force "$TARGET_FILE" "$PATCH_TMP"; then
echo "WARNING: Patch command failed. Continuing anyway."
fi
- name: Initialize OctoDNS Sync
env:
CLOUDFLARE_USERNAME: ${{ secrets.CLOUDFLARE_USERNAME }}
CLOUDFLARE_TOKEN: ${{ secrets.CLOUDFLARE_TOKEN }}
run: |
octodns-sync --doit --config-file="config/production.yaml" &>>"${{ env.LOGFILE }}" || \
{
echo "FAIL: octodns-sync exited with an error."
echo -e "\n\n## OctoDNS Failure by ${{ github.actor }}\n\n" >> "${{ env.LOGFILE }}"
exit 1
}
- name: Create Branch and Commit Log File on Failure
if: ${{ failure() }}
run: |
git config --global user.name 'action-bot'
git config --global user.email '[email protected]'
git checkout -b issue/failure-log-${{ github.run_number }}
git add -f ${{ env.LOGFILE }}
git add .
git commit -m "Add failure log"
git push origin issue/failure-log-${{ github.run_number }}
- name: Create Issue and Reference File on Failure
if: ${{ failure() }}
uses: actions/github-script@v7
with:
script: |
github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: 'Failed to sync OctoDNS',
body: `An error occurred during OctoDNS sync. Check the failure log for more details.
The log file is available in the branch [issue/failure-log-${{ github.run_number }}](/${{ github.repository }}/blob/issue/failure-log-${{ github.run_number }}/${{ env.LOGFILE }})`,
labels: ['report', 'automated issue']
})
- name: Cleanup Logfile
run: rm -f ${{ env.LOGFILE }}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment