Skip to content
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

✨ feat(): add CI & dev tools #12

Merged
merged 10 commits into from
Apr 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
version: 2
updates:
- package-ecosystem: github-actions
directory: /
pull-request-branch-name:
separator: "-"
schedule:
interval: monthly
- package-ecosystem: docker
directory: /
pull-request-branch-name:
separator: "-"
schedule:
interval: monthly
- package-ecosystem: pip
directory: /
pull-request-branch-name:
separator: "-"
schedule:
interval: monthly
14 changes: 10 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
strategy:
max-parallel: 4
matrix:
python-version: ["3.8", "3.9", "3.10"]
python-version: ["3.8", "3.9", "3.10", "3.11"]

steps:
- uses: actions/checkout@v2
Expand All @@ -44,8 +44,14 @@ jobs:
- name: Install dependencies
run: poetry install --no-interaction

- name: Lint with flake8
run: poetry run flake8 --max-line-length 119 gandi_tf/ tests/ --count --show-source --statistics

- name: Check with black
run: poetry run black --diff --check gandi_tf/ tests/

- name: Lint with flake8
run: poetry run flake8 gandi_tf/ tests/

- name: Check with pylint
run: poetry run pylint gandi_tf/ tests/

- name: Check with mypy
run: poetry run mypy gandi_tf/ tests/
15 changes: 15 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
repos:
- repo: https://github.com/psf/black
rev: 23.3.0 # Replace by any tag/version: https://github.com/psf/black/tags
hooks:
- id: black
language_version: python3 # Should be a command that runs python3.6+
- repo: https://github.com/pycqa/flake8
rev: 6.0.0
hooks:
- id: flake8
- repo: https://github.com/timothycrosley/isort
rev: 5.12.0
hooks:
- id: isort
2 changes: 1 addition & 1 deletion gandi_tf/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""CLI tool to generate terraform code based on existing Gandi DNS entries."""
import importlib.metadata


__version__ = importlib.metadata.version("gandi-2-terraform")
88 changes: 56 additions & 32 deletions gandi_tf/main.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
"""Main module for the CLI."""
import os

import click

import requests


# pylint: disable = too-few-public-methods
class Record:
"""
Class representing the TF record entries to write out.
"""

def __init__(self, name, r_type, ttl, value) -> None:
self.name = name
self.r_type = r_type
Expand All @@ -14,18 +19,26 @@ def __init__(self, name, r_type, ttl, value) -> None:


def fetch_records(domain):
r = requests.get(
"""
Fetch DNS records for a given domain name.
"""
req = requests.get(
f"https://dns.api.gandi.net/api/v5/domains/{domain}/records",
headers={
"X-Api-Key": os.getenv("GANDI_KEY"),
"Accept": "text/plain",
},
timeout=5,
)
r.raise_for_status()
return r.text
req.raise_for_status()
return req.text


def fetch_domains_list(organization_id):
"""
Fetch domains of the API key account, optionally filtered by the given
organiyation id.
"""
payload = {"per_page": 1, "sort_by": "fqdn", "nameserver": "livedns"}
if organization_id is not None:
payload["sharing_id"] = organization_id
Expand All @@ -35,6 +48,7 @@ def fetch_domains_list(organization_id):
"https://api.gandi.net/v5/domain/domains",
headers={"authorization": f'Apikey {os.getenv("GANDI_KEY")}'},
params=payload,
timeout=5,
)
fake_head.raise_for_status()

Expand All @@ -45,17 +59,20 @@ def fetch_domains_list(organization_id):
if total_count > 0:
payload["per_page"] = total_count

r = requests.get(
req = requests.get(
"https://api.gandi.net/v5/domain/domains",
headers={"authorization": f'Apikey {os.getenv("GANDI_KEY")}'},
params=payload,
timeout=5,
)
r.raise_for_status()
return r.json()
req.raise_for_status()
return req.json()


def parse_content(content):
entries = dict()
"""
Parser for the API response to return a list of Record objects."""
entries = {}
for line in content.splitlines():
words = line.strip().split(" ", maxsplit=4)
r_name = words[0]
Expand All @@ -82,11 +99,16 @@ def parse_content(content):


def generate_tf(domain, entries, subdir):
"""
Generate the Terraform files for the gandi provider with gandi_livedns_record
resources.
"""
if subdir:
try:
os.mkdir(f"./{domain}")
except OSError as error:
raise Exception(f"Error in generate_tf os.mkdir failed with error: {error}")
click.echo(f"Error in generate_tf os.mkdir failed with error: {error}")
raise error
filename = f"./{domain}/main.tf"
filename_tfimport = f"./{domain}/main.tfimport"
else:
Expand All @@ -101,43 +123,43 @@ def generate_tf(domain, entries, subdir):
pass

commands = []
with click.open_file(filename, "w") as f:
f.write("locals {\n")
f.write(f" {tf_name}_records = " + "{\n")
with click.open_file(filename, "w") as file:
file.write("locals {\n")
file.write(f" {tf_name}_records = " + "{\n")

for key, record in entries.items():
f.write(f" {key} = {{\n")
f.write(f' name = "{record.name}"\n')
f.write(f' type = "{record.r_type}"\n')
f.write(f" ttl = {record.ttl}\n")
f.write(" values = [\n")
file.write(f" {key} = {{\n")
file.write(f' name = "{record.name}"\n')
file.write(f' type = "{record.r_type}"\n')
file.write(f" ttl = {record.ttl}\n")
file.write(" values = [\n")

for value in record.values:
f.write(f' "{value}",\n')
f.write(" ]\n")
f.write(" }\n")
file.write(f' "{value}",\n')
file.write(" ]\n")
file.write(" }\n")

commands.append(
"terraform import "
f"'gandi_livedns_record.{tf_name}[\"{key}\"]' "
f'"{domain}/{record.name}/{record.r_type}"'
)

f.write(" }\n}\n\n")
file.write(" }\n}\n\n")

f.write(f'resource "gandi_livedns_record" "{tf_name}" {{\n')
f.write(f" for_each = local.{tf_name}_records\n\n")
f.write(f' zone = "{domain}"\n\n')
f.write(" name = each.value.name\n")
f.write(" ttl = each.value.ttl\n")
f.write(" type = each.value.type\n")
f.write(" values = each.value.values\n")
f.write("}\n")
file.write(f'resource "gandi_livedns_record" "{tf_name}" {{\n')
file.write(f" for_each = local.{tf_name}_records\n\n")
file.write(f' zone = "{domain}"\n\n')
file.write(" name = each.value.name\n")
file.write(" ttl = each.value.ttl\n")
file.write(" type = each.value.type\n")
file.write(" values = each.value.values\n")
file.write("}\n")

if subdir:
with click.open_file(filename_tfimport, "w") as f:
with click.open_file(filename_tfimport, "w") as file:
for cmd in commands:
f.write(f"{cmd}\n")
file.write(f"{cmd}\n")

return commands

Expand All @@ -160,14 +182,15 @@ def generate(domains, version, organization_id, subdir):
terraform provider, therefore they will not be retrieved.
"""
if version:
# pylint: disable = import-outside-toplevel
import importlib.metadata

_version = importlib.metadata.version("gandi-2-terraform")
click.echo(f"Version {_version}")
return

if len(domains) == 0:
domains = tuple([domain["fqdn_unicode"] for domain in fetch_domains_list(organization_id)])
domains = tuple(domain["fqdn_unicode"] for domain in fetch_domains_list(organization_id))
if len(domains) == 0:
click.echo("No domain found")

Expand All @@ -183,4 +206,5 @@ def generate(domains, version, organization_id, subdir):


if __name__ == "__main__":
# pylint: disable = no-value-for-parameter
generate()
Loading