[fix] Fix VPN-client template switch bug with same VPN server#977
[fix] Fix VPN-client template switch bug with same VPN server#977nemesifier merged 10 commits intoopenwisp:masterfrom
Conversation
Resolves a bug where VPN configuration variables become unresolved when switching between VPN templates using the same VPN server
| instance is using templates which have type set to "VPN" | ||
| and "auto_cert" set to True. | ||
| This method is called from a django signal (m2m_changed) | ||
| see config.apps.ConfigConfig.connect_signals |
There was a problem hiding this comment.
Why did you remove the comments?
There was a problem hiding this comment.
@pandafy Sorry for removing the comments - that was unintentional. I've restored the original docstring comments in my latest commit
| for template in templates.filter(type='vpn'): | ||
| if template.vpn_id not in current_vpns: | ||
| instance.vpnclient_set.filter(vpn=template.vpn).delete() |
There was a problem hiding this comment.
Can this be changed into
| for template in templates.filter(type='vpn'): | |
| if template.vpn_id not in current_vpns: | |
| instance.vpnclient_set.filter(vpn=template.vpn).delete() | |
| instance.vpnclient_set.exclude(vpn__in=current_vpns).delete() |
There was a problem hiding this comment.
We need to ensure that after the operation, there are not two VpnClients for this config object.
There was a problem hiding this comment.
@pandafy Thanks for catching this! I understand the concern about potential duplicate VPN clients. I've modified the code to use your suggested approach:
-
In post_add:
- First create new VPN clients
- Then cleanup any unused clients with exclude(vpn__in=current_vpns)
-
In post_remove:
- Simply cleanup all unused clients with exclude(vpn__in=current_vpns)
This ensures we won't have duplicate VPN clients for the same config object while preserving the necessary clients. The full cleanup at the end handles any edge cases where duplicate clients might have been created.
There was a problem hiding this comment.
We need a test which verifies this.
- Kept the original docstring comments - Modified the VPN client cleanup logic as suggested - Fixed the formatting issues (blank lines with whitespace) - Added a failing test case for the bug
|
Hey @pandafy I've implemented the suggested changes, can you pls check once |
|
Hey @pandafy @nemesifier can you please reivew this PR ? |
pandafy
left a comment
There was a problem hiding this comment.
@shwetd19 add a test. You can take reference from this existing test here
openwisp-controller/openwisp_controller/config/tests/test_admin.py
Lines 1623 to 1704 in 129a42f
The test should do the following operations:
- Create a VPN object
- Create two templates with the same vpn server. (say template1 and template 2)
vpn = self._create_vpn()
template1 = self._create_template(
name='vpn-test-1',
type='vpn',
vpn=vpn,
config={},
auto_cert=True,
default=True, # This will auto-apply template1 to the device
)
template2 = self._create_template(
name='vpn-test-2',
type='vpn',
vpn=vpn,
config={},
auto_cert=True,
)- Using the Django admin, apply tmplate2 and remove template1 from the device
- Verify there's only 1 VpnClient present for the config
- Verify that the variables are correctly resolved in the config using the following:
self.assertEqual(
config.backend_instance.config['openvpn'][0]['cert'],
f'/etc/x509/client-{vpn.pk.hex}.pem',
)We cannot accept a bugfix patch with a test. Ensure that the test fails without the changes you've made in openwisp_controller/config/base/config.py
|
Hey @pandafy @nemesifier , Thank you for the clarification. I understand that I need to write a test that replicates the issue and fails without the patch. I’ll follow the structure you provided and ensure the test verifies the presence of only one Just to confirm, should the test also include a check for the behavior when switching back and forth between I’ll proceed with writing the test and will update the PR soon. If there’s anything else I should keep in mind, please let me know. Thanks again for your guidance! Best regards, This reply shows that you’re actively engaging with the feedback, seeking clarification to ensure you meet expectations, and demonstrating your commitment to addressing the issue properly. |
nemesifier
left a comment
There was a problem hiding this comment.
Just to confirm, should the test also include a check for the behavior when switching back and forth between
template1andtemplate2multiple times? Or is it sufficient to test a single switch fromtemplate1totemplate2?I’ll proceed with writing the test and will update the PR soon. If there’s anything else I should keep in mind, please let me know.
Switching back would from template2 to template1 would be nice and shouldn't involve a lot more effort.
Thanks again for your guidance!
Welcome!
PS: look at the existing tests to get inspiration on how to write your test, try to be consistent with the rest of the codebase please.
|
Hey @pandafy @nemesifier I've added the fix here as : Added a test to replicate the bug and verify the fix, which creates two VPN templates using the same VPN server, switches between them, and verifies that only one |
| config.templates.add(template1) | ||
| config.save() |
There was a problem hiding this comment.
We have used default=True while creating the template, thus we don't need to add the template1 to the Config object.
| old_templates = device_group.templates.filter(backend=old_backend) | ||
| config.manage_group_templates(templates, old_templates, not created) | ||
|
|
||
| def test_vpn_template_switch(self): |
There was a problem hiding this comment.
@shwetd19 why did you add this test in the model definition.
nemesifier
left a comment
There was a problem hiding this comment.
@shwetd19 have you verified that test_vpn_template_switch fails with a clear error message without the code fix?
| if action == 'post_clear': | ||
| if instance.is_deactivating_or_deactivated(): | ||
| # If the device is deactivated or in the process of deactivating, then | ||
| # If the device is deactivated or in the process of deactivatiing, then |
There was a problem hiding this comment.
| # If the device is deactivated or in the process of deactivatiing, then | |
| # If the device is deactivated or in the process of deactivating, then |
nemesifier
left a comment
There was a problem hiding this comment.
PS: QA checks are failing.
nemesifier
left a comment
There was a problem hiding this comment.
CI build still failing @shwetd19.


Resolves a bug where VPN configuration variables become unresolved when switching between VPN templates using the same VPN server
Checklist
Reference to Existing Issue
Closes #973.
Description of Changes
This PR fixes an issue in the VPN client management code that occurs when switching between VPN templates that use the same VPN server. The bug popups when simultaneously removing one VPN template while adding another that uses the same VPN server.
The Problem
The issue was occuring due to how VPN clients are managed during template changes:
When adding a new template (
post_add):When removing a template (
post_remove):As a result, the configuration could end up with no VPN client object, causing VPN configuration variables to remain unresolved.
The Solution
The fix modifies the
manage_vpn_clientsmethod to:post_add:post_remove:This soln ensures VPN clients are preserved when switching between templates that use the same VPN server, maintaining proper configuration variable resolution.