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

Bump dependencies #44

Merged
merged 3 commits into from
Jan 18, 2024
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
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0

Expand Down Expand Up @@ -72,7 +72,7 @@ jobs:
run: ls -Rla ${{steps.download.outputs.download-path}}

- name: 🚀 Publish code coverage to Code Climate
uses: paambaati/codeclimate-action@v4.0.0
uses: paambaati/codeclimate-action@v5.0.0
env:
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
with:
Expand Down
12 changes: 6 additions & 6 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
repos:
- repo: https://github.com/Lucas-C/pre-commit-hooks
rev: v1.5.1
rev: v1.5.4
hooks:
- id: forbid-tabs
- id: remove-tabs
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
rev: v4.5.0
hooks:
- id: check-added-large-files
- id: check-builtin-literals
Expand All @@ -24,23 +24,23 @@ repos:
- id: isort
files: \.py$
- repo: https://github.com/psf/black
rev: 23.3.0
rev: 23.12.1
hooks:
- id: black
- repo: https://github.com/PyCQA/flake8
rev: 6.0.0
rev: 6.1.0
hooks:
- id: flake8
additional_dependencies: [flake8-typing-imports]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.950
rev: v1.8.0
hooks:
- id: mypy
files: octodns_netbox
args: [--ignore-missing-imports, --pretty]
additional_dependencies: [types-requests]
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
rev: v2.9.0
rev: v2.12.0
hooks:
- id: pretty-format-yaml
args: [--autofix]
Expand Down
63 changes: 45 additions & 18 deletions octodns_netbox/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,56 +11,83 @@
import sys
import typing
from ipaddress import ip_interface
from typing import Literal

if sys.version_info >= (3, 9):
from typing import Annotated, Literal
elif sys.version_info >= (3, 8):
from typing import Literal

from typing_extensions import Annotated
else:
from typing_extensions import Annotated, Literal

import pynetbox
import requests
from octodns.record import Record, Rr
from octodns.source.base import BaseSource
from octodns.zone import DuplicateRecordException, SubzoneRecordException, Zone
from pydantic import AnyHttpUrl, BaseModel, Extra, validator
from pydantic import (
AnyHttpUrl,
BaseModel,
BeforeValidator,
ConfigDict,
Field,
TypeAdapter,
ValidationInfo,
field_validator,
)

import octodns_netbox.reversename

Url = Annotated[
str,
BeforeValidator(lambda value: str(TypeAdapter(AnyHttpUrl).validate_python(value))),
]


class NetboxSourceConfig(BaseModel):
model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True)

multivalue_ptr: bool = False
SUPPORTS_MULTIVALUE_PTR: bool = multivalue_ptr
SUPPORTS_MULTIVALUE_PTR_: bool = Field(
multivalue_ptr, alias="SUPPORTS_MULTIVALUE_PTR"
)
SUPPORTS_DYNAMIC_: bool = Field(False, alias="SUPPORTS_DYNAMIC")
SUPPORTS_GEO: bool = False
SUPPORTS_DYNAMIC: bool = False
SUPPORTS: typing.Set[str] = set(("A", "AAAA", "PTR"))

id: str
url: AnyHttpUrl
url: Url
token: str
field_name: str = "description"
populate_tags: typing.List[str] = []
populate_vrf_id: typing.Optional[typing.Union[int, Literal["null"]]] = None
populate_vrf_name: typing.Optional[str] = None
populate_vrf_id: Annotated[
typing.Union[int, Literal["null"], None], Field(validate_default=True)
] = None
populate_vrf_name: Annotated[
typing.Optional[str], Field(validate_default=True)
] = None
populate_subdomains: bool = True
ttl: int = 60
ssl_verify: bool = True
log: logging.Logger

@validator("url")
def check_url(cls, v):
@field_validator("url")
def check_url(cls, v) -> str:
if re.search("/api/?$", v):
v = re.sub("/api/?$", "", v)
return v

@validator("populate_vrf_name")
def check_vrf_name(cls, v, values):
if "populate_vrf_id" in values and (
v is not None and values["populate_vrf_id"] is not None
@field_validator("populate_vrf_name")
def check_vrf_name(
cls, v: typing.Optional[str], info: ValidationInfo
) -> typing.Optional[str]:
if "populate_vrf_id" in info.data and (
v is not None and info.data["populate_vrf_id"] is not None
):
raise ValueError("Do not set both populate_vrf_id and populate_vrf")
return v

class Config:
extra = Extra.allow
underscore_attrs_are_private = True
arbitrary_types_allowed = True


class NetboxSource(BaseSource, NetboxSourceConfig):
def __init__(self, id: str, **kwargs):
Expand Down
16 changes: 9 additions & 7 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[build-system]
build-backend = "poetry_dynamic_versioning.backend"
requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning"]
requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning>=1.0.0,<2.0.0"]

[tool]

Expand Down Expand Up @@ -47,17 +47,19 @@ readme = "README.md"
version = "0.0.0"

[tool.poetry.dependencies]
octodns = {version = "^0.9.20"}
pydantic = "^1.10.2"
octodns = {version = "^1.0.0"}
poetry = "^1.7.1"
pydantic = "^2.0.0"
pynetbox = {version = "^7.0.0"}
python = ">=3.8,<4.0"
requests = {version = "^2.25.1"}
requests = {version = "^2.31.0"}
typing-extensions = {version = "^4.9.0", python = "<3.9"}

[tool.poetry.dev-dependencies]
pre-commit = "^3.0.0"
pytest = "^7.1.2"
pytest-cov = "^4.0.0"
requests-mock = "^1.9.3"
pytest = "^7.4.0"
pytest-cov = "^4.1.0"
requests-mock = "^1.11.0"
tox = "^4.0.0"

[tool.poetry-dynamic-versioning]
Expand Down
109 changes: 34 additions & 75 deletions tests/test_octodns_netbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import requests_mock
from octodns.record import Record
from octodns.zone import Zone
from pydantic.error_wrappers import ValidationError
from pydantic import ValidationError

from octodns_netbox import NetboxSource

Expand Down Expand Up @@ -99,17 +99,16 @@ class TestNetboxSourceFailSenarios:
def test_init_failed_due_to_missing_url(self):
with pytest.raises(ValidationError) as excinfo:
NetboxSource("test")
assert excinfo.value.errors() == [
{"loc": ("url",), "msg": "field required", "type": "value_error.missing"},
{"loc": ("token",), "msg": "field required", "type": "value_error.missing"},
]
assert excinfo.value.errors()[0]["loc"] == ("url",)
assert excinfo.value.errors()[0]["type"] == "missing"
assert excinfo.value.errors()[1]["loc"] == ("token",)
assert excinfo.value.errors()[1]["type"] == "missing"

def test_init_failed_due_to_missing_token(self):
with pytest.raises(ValidationError) as excinfo:
NetboxSource("test", url="http://netbox.example.com/")
assert excinfo.value.errors() == [
{"loc": ("token",), "msg": "field required", "type": "value_error.missing"}
]
assert excinfo.value.errors()[0]["loc"] == ("token",)
assert excinfo.value.errors()[0]["type"] == "missing"

def test_init_maintain_backword_compatibility_for_url(self):
source = NetboxSource(
Expand All @@ -127,39 +126,25 @@ def test_init_failed_due_to_invalid_field_name_type(self):
token="testtoken",
field_name=["dns_name", "description"],
)
assert excinfo.value.errors() == [
{
"loc": ("field_name",),
"msg": "str type expected",
"type": "type_error.str",
}
]

assert excinfo.value.errors()[0]["loc"] == ("field_name",)
assert excinfo.value.errors()[0]["type"] == "string_type"

def test_init_failed_due_to_invalid_ttl_type(self):
with pytest.raises(ValidationError) as excinfo:
NetboxSource(
"test", url="http://netbox.example.com/", token="testtoken", ttl=[10]
)
assert excinfo.value.errors() == [
{
"loc": ("ttl",),
"msg": "value is not a valid integer",
"type": "type_error.integer",
}
]
assert excinfo.value.errors()[0]["loc"] == ("ttl",)
assert excinfo.value.errors()[0]["type"] == "int_type"

def test_init_failed_due_to_invalid_ttl_value(self):
with pytest.raises(ValidationError) as excinfo:
NetboxSource(
"test", url="http://netbox.example.com/", token="testtoken", ttl="ten"
)
assert excinfo.value.errors() == [
{
"loc": ("ttl",),
"msg": "value is not a valid integer",
"type": "type_error.integer",
}
]
assert excinfo.value.errors()[0]["loc"] == ("ttl",)
assert excinfo.value.errors()[0]["type"] == "int_parsing"

def test_init_failed_due_to_invalid_populate_tags_type(self):
with pytest.raises(ValidationError) as excinfo:
Expand All @@ -169,13 +154,8 @@ def test_init_failed_due_to_invalid_populate_tags_type(self):
token="testtoken",
populate_tags="tag",
)
assert excinfo.value.errors() == [
{
"loc": ("populate_tags",),
"msg": "value is not a valid list",
"type": "type_error.list",
}
]
assert excinfo.value.errors()[0]["loc"] == ("populate_tags",)
assert excinfo.value.errors()[0]["type"] == "list_type"

def test_init_failed_due_to_invalid_populate_vrf_id_type(self):
with pytest.raises(ValidationError) as excinfo:
Expand All @@ -185,18 +165,13 @@ def test_init_failed_due_to_invalid_populate_vrf_id_type(self):
token="testtoken",
populate_vrf_id=[10],
)
assert excinfo.value.errors() == [
{
"loc": ("populate_vrf_id",),
"msg": "value is not a valid integer",
"type": "type_error.integer",
},
{
"loc": ("populate_vrf_id",),
"msg": "unhashable type: 'list'",
"type": "type_error",
},
]
assert excinfo.value.errors()[0]["loc"] == ("populate_vrf_id", "int")
assert excinfo.value.errors()[0]["type"] == "int_type"
assert excinfo.value.errors()[1]["loc"] == (
"populate_vrf_id",
"literal['null']",
)
assert excinfo.value.errors()[1]["type"] == "literal_error"

def test_init_failed_due_to_invalid_populate_vrf_id_value(self):
with pytest.raises(ValidationError) as excinfo:
Expand All @@ -206,19 +181,13 @@ def test_init_failed_due_to_invalid_populate_vrf_id_value(self):
token="testtoken",
populate_vrf_id="ten",
)
assert excinfo.value.errors() == [
{
"loc": ("populate_vrf_id",),
"msg": "value is not a valid integer",
"type": "type_error.integer",
},
{
"ctx": {"given": "ten", "permitted": ("null",)},
"loc": ("populate_vrf_id",),
"msg": "unexpected value; permitted: 'null'",
"type": "value_error.const",
},
]
assert excinfo.value.errors()[0]["loc"] == ("populate_vrf_id", "int")
assert excinfo.value.errors()[0]["type"] == "int_parsing"
assert excinfo.value.errors()[1]["loc"] == (
"populate_vrf_id",
"literal['null']",
)
assert excinfo.value.errors()[1]["type"] == "literal_error"

def test_init_failed_because_both_populate_vrf_id_populate_vrf_name_are_provided(
self,
Expand All @@ -241,13 +210,8 @@ def test_init_failed_due_to_invalid_populate_vrf_name_type(self):
token="testtoken",
populate_vrf_name=["TEST"],
)
assert excinfo.value.errors() == [
{
"loc": ("populate_vrf_name",),
"msg": "str type expected",
"type": "type_error.str",
}
]
assert excinfo.value.errors()[0]["loc"] == ("populate_vrf_name",)
assert excinfo.value.errors()[0]["type"] == "string_type"

def test_init_failed_because_invalid_populate_vrf_name_is_not_found_at_netbox(self):
with pytest.raises(ValueError) as excinfo:
Expand All @@ -267,13 +231,8 @@ def test_init_failed_due_to_invalid_populate_subdomains_type(self):
token="testtoken",
populate_subdomains="ok",
)
assert excinfo.value.errors() == [
{
"loc": ("populate_subdomains",),
"msg": "value could not be parsed to a boolean",
"type": "type_error.bool",
}
]
assert excinfo.value.errors()[0]["loc"] == ("populate_subdomains",)
assert excinfo.value.errors()[0]["type"] == "bool_parsing"


class TestNetboxSourcePopulateIPv4PTRNonOctecBoundary:
Expand Down
7 changes: 6 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,14 @@ python =
3.8: py38, coverage

[testenv]
setenv =
PYTHONIOENCODING=utf-8
PY_COLORS=1
passenv = CI
deps = poetry
allowlist_externals =
poetry
commands_pre =
poetry self update
poetry self add "poetry-dynamic-versioning[plugin]"
poetry run python -m pip install pip -U
commands =
Expand Down