From 0a42af428591cc06b1a894336c244369c4927164 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 9 Sep 2024 17:00:33 -0400 Subject: [PATCH 1/3] Closes #84: Introduce the max_active_branches parameter --- docs/configuration.md | 10 +++++++++- netbox_branching/__init__.py | 3 +++ netbox_branching/choices.py | 8 +++++++- netbox_branching/models/branches.py | 17 ++++++++++++++--- netbox_branching/tests/test_branches.py | 25 +++++++++++++++++++++++++ 5 files changed, 58 insertions(+), 5 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 6e141aa..e9c305d 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,10 +1,18 @@ # Configuration Parameters +## `max_active_branches` + +Default: None + +The maximum number of _active_ branches that can exist simultaneously. This count excludes branches which have been merged or archived. + +--- + ## `max_branches` Default: None -The maximum number of branches that can exist simultaneously, including merged branches that have not been deleted. It may be desirable to limit the total number of provisioned branches to safeguard against excessive database size. +The maximum total number of branches that can exist simultaneously, including merged branches that have not been deleted. It may be desirable to limit the total number of provisioned branches to safeguard against excessive database size. --- diff --git a/netbox_branching/__init__.py b/netbox_branching/__init__.py index 5174856..25f4ce6 100644 --- a/netbox_branching/__init__.py +++ b/netbox_branching/__init__.py @@ -16,6 +16,9 @@ class AppConfig(PluginConfig): 'netbox_branching.middleware.BranchMiddleware' ] default_settings = { + # The maximum number of active branches (excludes merged & archived branches) + 'max_active_branches': None, + # The maximum number of branches which can be provisioned simultaneously 'max_branches': None, diff --git a/netbox_branching/choices.py b/netbox_branching/choices.py index db8c9c8..47adcb1 100644 --- a/netbox_branching/choices.py +++ b/netbox_branching/choices.py @@ -28,7 +28,13 @@ class BranchStatusChoices(ChoiceSet): PROVISIONING, SYNCING, MERGING, - REVERTING + REVERTING, + ) + + ACTIVE_STATUSES = ( + NEW, + READY, + *TRANSITIONAL, ) diff --git a/netbox_branching/models/branches.py b/netbox_branching/models/branches.py index b337ab1..b4de428 100644 --- a/netbox_branching/models/branches.py +++ b/netbox_branching/models/branches.py @@ -127,10 +127,10 @@ def synced_time(self): def clean(self): - # Check whether we're exceeding the maximum number of Branches + # Enforce the maximum number of total branches if not self.pk and (max_branches := get_plugin_config('netbox_branching', 'max_branches')): - branch_count = Branch.objects.count() - if branch_count >= max_branches: + total_branch_count = Branch.objects.count() + if total_branch_count >= max_branches: raise ValidationError( _( "The configured maximum number of branches ({max}) cannot be exceeded. One or more existing " @@ -138,6 +138,17 @@ def clean(self): ).format(max=max_branches) ) + # Enforce the maximum number of active branches + if not self.pk and (max_active_branches := get_plugin_config('netbox_branching', 'max_active_branches')): + active_branch_count = Branch.objects.filter(status__in=BranchStatusChoices.ACTIVE_STATUSES).count() + if active_branch_count >= max_active_branches: + raise ValidationError( + _( + "The configured maximum number of active branches ({max}) cannot be exceeded. One or more " + "active branches must be archived or deleted before a new branch may be created." + ).format(max=max_active_branches) + ) + def save(self, provision=True, *args, **kwargs): """ Args: diff --git a/netbox_branching/tests/test_branches.py b/netbox_branching/tests/test_branches.py index 9ba97b9..11aebb5 100644 --- a/netbox_branching/tests/test_branches.py +++ b/netbox_branching/tests/test_branches.py @@ -4,6 +4,7 @@ from django.db import connection from django.test import TransactionTestCase, override_settings +from netbox_branching.choices import BranchStatusChoices from netbox_branching.constants import MAIN_SCHEMA from netbox_branching.models import Branch from netbox_branching.utilities import get_tables_to_replicate @@ -77,6 +78,30 @@ def test_branch_schema_id(self): branch.refresh_from_db() self.assertEqual(branch.schema_id, schema_id, msg="Schema ID was changed during save()") + @override_settings(PLUGINS_CONFIG={ + 'netbox_branching': { + 'max_active_branches': 2, + } + }) + def test_max_active_branches(self): + """ + Verify that the max_active_branches config parameter is enforced. + """ + Branch.objects.bulk_create(( + Branch(name='Branch 1', status=BranchStatusChoices.MERGED), + Branch(name='Branch 2', status=BranchStatusChoices.READY), + )) + + # Second active branch should be permitted (merged branches don't count) + branch = Branch(name='Branch 3') + branch.full_clean() + branch.save() + + # Attempting to create a third active branch should fail + branch = Branch(name='Branch 4') + with self.assertRaises(ValidationError): + branch.full_clean() + @override_settings(PLUGINS_CONFIG={ 'netbox_branching': { 'max_branches': 2, From 1e718d15bc44307f59e060a84f0f7e2dca385483 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 10 Sep 2024 12:49:50 -0400 Subject: [PATCH 2/3] Rename max_active_branches to max_working_branches --- docs/configuration.md | 4 ++-- netbox_branching/__init__.py | 4 ++-- netbox_branching/choices.py | 2 +- netbox_branching/models/branches.py | 8 ++++---- netbox_branching/template_content.py | 4 +--- netbox_branching/tests/test_branches.py | 6 +++--- 6 files changed, 13 insertions(+), 15 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index e9c305d..f70b842 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,10 +1,10 @@ # Configuration Parameters -## `max_active_branches` +## `max_working_branches` Default: None -The maximum number of _active_ branches that can exist simultaneously. This count excludes branches which have been merged or archived. +The maximum number of operational branches that can exist simultaneously. This count excludes branches which have been merged or archived. --- diff --git a/netbox_branching/__init__.py b/netbox_branching/__init__.py index 25f4ce6..a17aa6c 100644 --- a/netbox_branching/__init__.py +++ b/netbox_branching/__init__.py @@ -16,8 +16,8 @@ class AppConfig(PluginConfig): 'netbox_branching.middleware.BranchMiddleware' ] default_settings = { - # The maximum number of active branches (excludes merged & archived branches) - 'max_active_branches': None, + # The maximum number of working branches (excludes merged & archived branches) + 'max_working_branches': None, # The maximum number of branches which can be provisioned simultaneously 'max_branches': None, diff --git a/netbox_branching/choices.py b/netbox_branching/choices.py index 70df92a..90c6f46 100644 --- a/netbox_branching/choices.py +++ b/netbox_branching/choices.py @@ -33,7 +33,7 @@ class BranchStatusChoices(ChoiceSet): REVERTING, ) - ACTIVE_STATUSES = ( + WORKING = ( NEW, READY, *TRANSITIONAL, diff --git a/netbox_branching/models/branches.py b/netbox_branching/models/branches.py index a484b41..9291c17 100644 --- a/netbox_branching/models/branches.py +++ b/netbox_branching/models/branches.py @@ -139,14 +139,14 @@ def clean(self): ) # Enforce the maximum number of active branches - if not self.pk and (max_active_branches := get_plugin_config('netbox_branching', 'max_active_branches')): - active_branch_count = Branch.objects.filter(status__in=BranchStatusChoices.ACTIVE_STATUSES).count() - if active_branch_count >= max_active_branches: + if not self.pk and (max_working_branches := get_plugin_config('netbox_branching', 'max_working_branches')): + working_branch_count = Branch.objects.filter(status__in=BranchStatusChoices.WORKING).count() + if working_branch_count >= max_working_branches: raise ValidationError( _( "The configured maximum number of active branches ({max}) cannot be exceeded. One or more " "active branches must be archived or deleted before a new branch may be created." - ).format(max=max_active_branches) + ).format(max=max_working_branches) ) def save(self, provision=True, *args, **kwargs): diff --git a/netbox_branching/template_content.py b/netbox_branching/template_content.py index 3ef7507..13bea68 100644 --- a/netbox_branching/template_content.py +++ b/netbox_branching/template_content.py @@ -17,9 +17,7 @@ class BranchSelector(PluginTemplateExtension): def navbar(self): return self.render('netbox_branching/inc/branch_selector.html', extra_context={ 'active_branch': active_branch.get(), - 'branches': Branch.objects.exclude( - status__in=[BranchStatusChoices.MERGED, BranchStatusChoices.ARCHIVED] - ), + 'branches': Branch.objects.filter(status__in=BranchStatusChoices.WORKING), }) diff --git a/netbox_branching/tests/test_branches.py b/netbox_branching/tests/test_branches.py index 11aebb5..6cbd01d 100644 --- a/netbox_branching/tests/test_branches.py +++ b/netbox_branching/tests/test_branches.py @@ -80,12 +80,12 @@ def test_branch_schema_id(self): @override_settings(PLUGINS_CONFIG={ 'netbox_branching': { - 'max_active_branches': 2, + 'max_working_branches': 2, } }) - def test_max_active_branches(self): + def test_max_working_branches(self): """ - Verify that the max_active_branches config parameter is enforced. + Verify that the max_working_branches config parameter is enforced. """ Branch.objects.bulk_create(( Branch(name='Branch 1', status=BranchStatusChoices.MERGED), From 624138a53338d8973e907c1ff863d0b3bcfab3fe Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 10 Sep 2024 13:43:43 -0400 Subject: [PATCH 3/3] Update error message --- netbox_branching/models/branches.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/netbox_branching/models/branches.py b/netbox_branching/models/branches.py index 9291c17..23591d0 100644 --- a/netbox_branching/models/branches.py +++ b/netbox_branching/models/branches.py @@ -144,8 +144,8 @@ def clean(self): if working_branch_count >= max_working_branches: raise ValidationError( _( - "The configured maximum number of active branches ({max}) cannot be exceeded. One or more " - "active branches must be archived or deleted before a new branch may be created." + "The configured maximum number of working branches ({max}) cannot be exceeded. One or more " + "working branches must be merged or archived before a new branch may be created." ).format(max=max_working_branches) )