0001-hobo_deploy-handle-url-change-on-a-service-58908.patch
hobo/agent/authentic2/management/commands/hobo_deploy.py | ||
---|---|---|
155 | 155 |
continue |
156 | 156 |
metadata_text = metadata_response.text |
157 | 157 | |
158 |
provider, service_created = LibertyProvider.objects.get_or_create( |
|
159 |
entity_id=sp_url, protocol_conformance=lasso.PROTOCOL_SAML_2_0 |
|
160 |
) |
|
158 |
provider, service_created = None, False |
|
159 |
for legacy_urls in service.get('legacy_urls', []): |
|
160 |
try: |
|
161 |
provider = LibertyProvider.objects.get( |
|
162 |
entity_id=legacy_urls['saml-sp-metadata-url'], |
|
163 |
protocol_conformance=lasso.PROTOCOL_SAML_2_0, |
|
164 |
) |
|
165 |
provider.entity_id = sp_url |
|
166 |
break |
|
167 |
except LibertyProvider.DoesNotExist: |
|
168 |
pass |
|
169 |
if not provider: |
|
170 |
provider, service_created = LibertyProvider.objects.get_or_create( |
|
171 |
entity_id=sp_url, protocol_conformance=lasso.PROTOCOL_SAML_2_0 |
|
172 |
) |
|
161 | 173 |
provider.name = service['title'] |
162 | 174 |
provider.slug = service['slug'] |
163 | 175 |
provider.federation_source = 'hobo' |
hobo/agent/common/management/commands/hobo_deploy.py | ||
---|---|---|
74 | 74 |
# early exit, we don't redeploy secondary services |
75 | 75 |
return |
76 | 76 |
domain = urlparse.urlparse(self.me.get('base_url')).netloc.split(':')[0] |
77 |
legacy_domain = None |
|
77 | 78 | |
78 | 79 |
try: |
79 | 80 |
tenant = TenantMiddleware.get_tenant_by_hostname(domain) |
80 | 81 |
except TenantNotFound: |
81 |
# create tenant for domain |
|
82 |
call_command('create_tenant', domain) |
|
82 |
# might be a domain change request |
|
83 |
for legacy_urls in self.me.get('legacy_urls', []): |
|
84 |
old_domain = urlparse.urlparse(legacy_urls['base_url']).netloc.split(':')[0] |
|
85 |
try: |
|
86 |
tenant = TenantMiddleware.get_tenant_by_hostname(old_domain) |
|
87 |
legacy_domain = old_domain |
|
88 |
break |
|
89 |
except TenantNotFound: |
|
90 |
pass |
|
91 |
call_command('create_tenant', domain, legacy_hostname=legacy_domain) |
|
83 | 92 |
tenant = TenantMiddleware.get_tenant_by_hostname(domain) |
84 | 93 | |
85 | 94 |
timestamp = hobo_environment.get('timestamp') |
hobo/agent/hobo/management/commands/hobo_deploy.py | ||
---|---|---|
5 | 5 |
from hobo.agent.common.management.commands import hobo_deploy |
6 | 6 |
from hobo.deploy.signals import notify_agents |
7 | 7 |
from hobo.environment.models import AVAILABLE_SERVICES, Hobo, Variable |
8 |
from hobo.environment.utils import get_or_create_local_hobo |
|
8 | 9 |
from hobo.multitenant.middleware import TenantMiddleware, TenantNotFound |
9 | 10 |
from hobo.profile.models import AttributeDefinition |
10 | 11 | |
... | ... | |
38 | 39 |
if service_dict.get('secondary'): |
39 | 40 |
continue |
40 | 41 |
if service_dict.get('base_url') == me['base_url']: |
42 |
# URL might have changed, need to update local hobo |
|
43 |
local_hobo = get_or_create_local_hobo() |
|
44 |
if local_hobo and local_hobo.get_base_url_path() != service_dict.get('base_url'): |
|
45 |
local_hobo.change_base_url(service_dict.get('base_url')) |
|
46 |
local_hobo.save() |
|
41 | 47 |
continue |
42 | 48 |
for service_klass in AVAILABLE_SERVICES: |
43 | 49 |
if service_klass.Extra.service_id == service_dict.get('service-id'): |
... | ... | |
63 | 69 |
slug_prefix = '_%s_' % hobo_environment['variables']['ou-slug'] |
64 | 70 | |
65 | 71 |
service_slug = '%s%s' % (slug_prefix, service_dict['slug']) |
66 |
service, created = service_klass.objects.get_or_create( |
|
67 |
base_url=service_dict['base_url'], |
|
68 |
secondary=True, |
|
69 |
defaults={ |
|
70 |
'title': service_dict['title'], |
|
71 |
'slug': service_slug, |
|
72 |
'secret_key': service_dict.get('secret_key'), |
|
73 |
}, |
|
74 |
) |
|
72 | ||
73 |
service, created = None, False |
|
74 |
for legacy_urls in service_dict.get('legacy_urls', []): |
|
75 |
try: |
|
76 |
service = service_klass.objects.get(base_url=legacy_urls['base_url'], secondary=True) |
|
77 |
service.change_base_url(service_dict['base_url']) |
|
78 |
break |
|
79 |
except service_klass.DoesNotExist: |
|
80 |
pass |
|
81 | ||
82 |
if not service: |
|
83 |
service, created = service_klass.objects.get_or_create( |
|
84 |
base_url=service_dict['base_url'], |
|
85 |
secondary=True, |
|
86 |
defaults={ |
|
87 |
'title': service_dict['title'], |
|
88 |
'slug': service_slug, |
|
89 |
'secret_key': service_dict.get('secret_key'), |
|
90 |
}, |
|
91 |
) |
|
92 | ||
75 | 93 |
service.title = service_dict['title'] |
76 | 94 |
service.secret_key = service_dict.get('secret_key') |
77 | 95 |
service.template_name = service_dict.get('template_name') or '' |
hobo/environment/migrations/0025_legacy_urls.py | ||
---|---|---|
1 |
# Generated by Django 2.2.24 on 2021-12-02 13:55 |
|
2 | ||
3 |
import django.contrib.postgres.fields.jsonb |
|
4 |
from django.db import migrations |
|
5 | ||
6 | ||
7 |
class Migration(migrations.Migration): |
|
8 | ||
9 |
dependencies = [ |
|
10 |
('environment', '0024_remove_local_hobo'), |
|
11 |
] |
|
12 | ||
13 |
operations = [ |
|
14 |
migrations.AddField( |
|
15 |
model_name='authentic', |
|
16 |
name='legacy_urls', |
|
17 |
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=list, null=True), |
|
18 |
), |
|
19 |
migrations.AddField( |
|
20 |
model_name='bijoe', |
|
21 |
name='legacy_urls', |
|
22 |
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=list, null=True), |
|
23 |
), |
|
24 |
migrations.AddField( |
|
25 |
model_name='chrono', |
|
26 |
name='legacy_urls', |
|
27 |
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=list, null=True), |
|
28 |
), |
|
29 |
migrations.AddField( |
|
30 |
model_name='combo', |
|
31 |
name='legacy_urls', |
|
32 |
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=list, null=True), |
|
33 |
), |
|
34 |
migrations.AddField( |
|
35 |
model_name='fargo', |
|
36 |
name='legacy_urls', |
|
37 |
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=list, null=True), |
|
38 |
), |
|
39 |
migrations.AddField( |
|
40 |
model_name='hobo', |
|
41 |
name='legacy_urls', |
|
42 |
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=list, null=True), |
|
43 |
), |
|
44 |
migrations.AddField( |
|
45 |
model_name='passerelle', |
|
46 |
name='legacy_urls', |
|
47 |
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=list, null=True), |
|
48 |
), |
|
49 |
migrations.AddField( |
|
50 |
model_name='wcs', |
|
51 |
name='legacy_urls', |
|
52 |
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=list, null=True), |
|
53 |
), |
|
54 |
migrations.AddField( |
|
55 |
model_name='welco', |
|
56 |
name='legacy_urls', |
|
57 |
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=list, null=True), |
|
58 |
), |
|
59 |
] |
hobo/environment/models.py | ||
---|---|---|
19 | 19 |
import random |
20 | 20 |
import re |
21 | 21 |
import socket |
22 |
import time |
|
22 | 23 | |
23 | 24 |
import requests |
24 | 25 |
from django.conf import settings |
25 | 26 |
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation |
26 | 27 |
from django.contrib.contenttypes.models import ContentType |
28 |
from django.contrib.postgres.fields import JSONField |
|
27 | 29 |
from django.core.cache import cache |
28 | 30 |
from django.core.exceptions import ValidationError |
29 | 31 |
from django.core.validators import URLValidator |
... | ... | |
104 | 106 |
title = models.CharField(_('Title'), max_length=50) |
105 | 107 |
slug = models.SlugField(_('Slug')) |
106 | 108 |
base_url = models.CharField(_('Base URL'), max_length=200, validators=[URLValidator()]) |
109 |
legacy_urls = JSONField(null=True, default=list, blank=True) |
|
107 | 110 |
secret_key = models.CharField(_('Secret Key'), max_length=60) |
108 | 111 |
template_name = models.CharField(_('Template'), max_length=60, blank=True) |
109 | 112 |
secondary = models.BooleanField(_('Secondary Service'), default=False) |
... | ... | |
159 | 162 |
] |
160 | 163 |
) |
161 | 164 |
as_dict['base_url'] = self.get_base_url_path() |
165 |
if self.legacy_urls: |
|
166 |
as_dict['legacy_urls'] = self.legacy_urls |
|
162 | 167 |
as_dict['service-id'] = self.Extra.service_id |
163 | 168 |
as_dict['service-label'] = force_text(self.Extra.service_label) |
164 | 169 |
as_dict['variables'] = dict(((v.name, v.json) for v in self.variables.all())) |
... | ... | |
294 | 299 |
result[name] = value |
295 | 300 |
return result |
296 | 301 | |
302 |
def change_base_url(self, base_url): |
|
303 |
service_dict = self.as_dict() |
|
304 |
timestamp = datetime.datetime.now() |
|
305 |
legacy_urls = { |
|
306 |
'base_url': service_dict['base_url'], |
|
307 |
'timestamp': str(time.mktime(timestamp.timetuple()) + timestamp.microsecond / 1e6), |
|
308 |
} |
|
309 |
for url_key in ( |
|
310 |
'saml-sp-metadata-url', |
|
311 |
'saml-idp-metadata-url', |
|
312 |
'backoffice-menu-url', |
|
313 |
'provisionning-url', |
|
314 |
): |
|
315 |
if url_key in service_dict: |
|
316 |
legacy_urls[url_key] = service_dict[url_key] |
|
317 |
if not self.legacy_urls: |
|
318 |
self.legacy_urls = [] |
|
319 |
self.legacy_urls.insert(0, legacy_urls) |
|
320 |
self.base_url = base_url |
|
321 | ||
297 | 322 | |
298 | 323 |
class Authentic(ServiceBase): |
299 | 324 |
use_as_idp_for_self = models.BooleanField(verbose_name=_('Use as IdP'), default=False) |
tests/test_hobo_deploy.py | ||
---|---|---|
152 | 152 |
mocked_get_tenant_by_hostname.side_effect = [TenantNotFound, tenant] |
153 | 153 |
with patch('hobo.agent.common.management.commands.hobo_deploy.call_command') as mocked_call_command: |
154 | 154 |
command.deploy(base_url, ENVIRONMENT, None) |
155 |
assert mocked_call_command.mock_calls == [call('create_tenant', 'combo.dev.publik.love')] |
|
155 |
assert mocked_call_command.mock_calls == [ |
|
156 |
call('create_tenant', 'combo.dev.publik.love', legacy_hostname=None) |
|
157 |
] |
|
156 | 158 |
assert_deployed() |
157 | 159 | |
158 | 160 |
# already there (timestamp do not change) |
tests_authentic/test_hobo_deploy.py | ||
---|---|---|
579 | 579 |
export_ref = sort_and_remove_uuid(export_site()) |
580 | 580 |
file_ref = sort_and_remove_uuid(json.loads(content)) |
581 | 581 |
assert export_ref == file_ref |
582 | ||
583 | ||
584 |
def test_hobo_deploy_with_legacy_urls(monkeypatch, tenant_base, mocker, skeleton_dir, tmp_path): |
|
585 |
from django.core.management import call_command |
|
586 | ||
587 |
from hobo.agent.authentic2.management.commands.hobo_deploy import Command as HoboDeployCommand |
|
588 | ||
589 |
requests_get = mocker.patch('requests.get') |
|
590 |
meta1 = '''<?xml version="1.0"?> |
|
591 |
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" |
|
592 |
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" |
|
593 |
xmlns:ds="http://www.w3.org/2000/09/xmldsig#" |
|
594 |
entityID="http://passerelle.example.net/saml/metadata"> |
|
595 |
<SPSSODescriptor |
|
596 |
AuthnRequestsSigned="true" WantAssertionsSigned="true" |
|
597 |
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> |
|
598 |
<KeyDescriptor use="signing"> |
|
599 |
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> |
|
600 |
<KeyValue xmlns="http://www.w3.org/2000/09/xmldsig#"> |
|
601 |
<RSAKeyValue> |
|
602 |
<Modulus>nJpkBznHNbvE+RAC6mU+NPQnIWs8gFNCm6I3FPcUKYpaJbXaurJ4cJgvnaEiqIXPQDcbHxuLeCbYbId9yascWZirvQbh8d/r+Vv+24bPG++9gW+i3Nnz1VW8V+z0b+puHWvM/FjJjBNJgWkI38gaupz47U6/02CtWx00stitiwk=</Modulus> |
|
603 |
<Exponent>AQAB</Exponent> |
|
604 |
</RSAKeyValue> |
|
605 |
</KeyValue> |
|
606 |
</ds:KeyInfo> |
|
607 |
</KeyDescriptor> |
|
608 |
<KeyDescriptor use="encryption"> |
|
609 |
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> |
|
610 |
<KeyValue xmlns="http://www.w3.org/2000/09/xmldsig#"> |
|
611 |
<RSAKeyValue> |
|
612 |
<Modulus>3BxSiAzGvY1Yuqa31L7Zr2WHM/8cn5oX+Q6A2SYgzjuvAgnWyizN8YgW/fHR4G7MtkmZ5RFJLXfcSLwbUfpFHV6KO1ikbgViYuFempM+SWtjqEI7ribm9GaI5kUzHJZBrH3/Q9XAd9/GLLALxurGjbKDeLfc0D+7el26g4sYmA8=</Modulus> |
|
613 |
<Exponent>AQAB</Exponent> |
|
614 |
</RSAKeyValue> |
|
615 |
</KeyValue> |
|
616 |
</ds:KeyInfo> |
|
617 |
</KeyDescriptor> |
|
618 | ||
619 |
<SingleLogoutService |
|
620 |
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" |
|
621 |
Location="http://passerelle.example.net/saml/singleLogout" |
|
622 |
ResponseLocation="http://passerelle.example.net/saml/singleLogoutReturn" /> |
|
623 |
<SingleLogoutService |
|
624 |
Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" |
|
625 |
Location="http://passerelle.example.net/saml/singleLogoutSOAP" /> |
|
626 |
<ManageNameIDService |
|
627 |
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" |
|
628 |
Location="http://passerelle.example.net/saml/manageNameId" |
|
629 |
ResponseLocation="http://passerelle.example.net/saml/manageNameIdReturn" /> |
|
630 |
<ManageNameIDService |
|
631 |
Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" |
|
632 |
Location="http://passerelle.example.net/saml/manageNameIdSOAP" /> |
|
633 |
<AssertionConsumerService isDefault="true" index="0" |
|
634 |
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" |
|
635 |
Location="http://passerelle.example.net/saml/assertionConsumerArtifact" /> |
|
636 |
<AssertionConsumerService index="1" |
|
637 |
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" |
|
638 |
Location="http://passerelle.example.net/saml/assertionConsumerPost" /> |
|
639 |
<AssertionConsumerService index="2" |
|
640 |
Binding="urn:oasis:names:tc:SAML:2.0:bindings:PAOS" |
|
641 |
Location="http://passerelle.example.net/saml/assertionConsumerSOAP" /> |
|
642 |
<AssertionConsumerService index="3" |
|
643 |
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" |
|
644 |
Location="http://passerelle.example.net/saml/assertionConsumerRedirect" /> |
|
645 |
</SPSSODescriptor> |
|
646 |
</EntityDescriptor>''' |
|
647 |
meta2 = meta1.replace('passerelle.example.net', 'new-passerelle.example.net') |
|
648 |
monkeypatch.setattr(HoboDeployCommand, 'backoff_factor', 0.0001) |
|
649 | ||
650 |
side_effect_iter = iter([meta1, meta2]) |
|
651 | ||
652 |
def side_effect(*args, **kwargs): |
|
653 |
for v in side_effect_iter: |
|
654 |
m = mock.Mock() |
|
655 |
m.text = v |
|
656 |
return m |
|
657 | ||
658 |
requests_get.side_effect = side_effect |
|
659 | ||
660 |
def hobo_json(env_dict): |
|
661 |
with tempfile.NamedTemporaryFile(mode='w', dir=str(tmp_path), delete=False) as hobo_json: |
|
662 |
hobo_json_content = json.dumps(env_dict) |
|
663 |
hobo_json.write(hobo_json_content) |
|
664 |
return hobo_json.name |
|
665 | ||
666 |
env = { |
|
667 |
'services': [ |
|
668 |
{ |
|
669 |
'service-id': 'authentic', |
|
670 |
'slug': 'test', |
|
671 |
'title': 'Test', |
|
672 |
'this': True, |
|
673 |
'secret_key': '12345', |
|
674 |
'base_url': 'http://sso.example.net', |
|
675 |
'variables': { |
|
676 |
'other_variable': 'bar', |
|
677 |
}, |
|
678 |
}, |
|
679 |
{ |
|
680 |
'service-id': 'passerelle', |
|
681 |
'slug': 'passerelle', |
|
682 |
'title': u'Passerelle', |
|
683 |
'base_url': 'http://passerelle.example.net', |
|
684 |
'saml-sp-metadata-url': 'http://passerelle.example.net/saml/metadata', |
|
685 |
}, |
|
686 |
], |
|
687 |
'users': [], |
|
688 |
'profile': {'fields': []}, |
|
689 |
} |
|
690 | ||
691 |
with mock.patch('hobo.agent.authentic2.provisionning.notify_agents'): |
|
692 |
call_command('hobo_deploy', 'http://sso.example.net', hobo_json(env)) |
|
693 | ||
694 |
from hobo.multitenant.middleware import TenantMiddleware |
|
695 | ||
696 |
tenants = list(TenantMiddleware.get_tenants()) |
|
697 |
assert len(tenants) == 1 |
|
698 |
tenant = tenants[0] |
|
699 |
assert tenant.domain_url == 'sso.example.net' |
|
700 |
assert tenant.schema_name == 'sso_example_net' |
|
701 |
tenant_directory = tenant.get_directory() |
|
702 |
assert tenant_directory == os.path.join(tenant_base, tenant.domain_url) |
|
703 |
assert os.path.exists(os.path.join(tenant_directory, 'saml.crt')) |
|
704 |
assert os.path.exists(os.path.join(tenant_directory, 'saml.key')) |
|
705 | ||
706 |
from tenant_schemas.utils import tenant_context |
|
707 | ||
708 |
with tenant_context(tenant): |
|
709 |
# SAML checks |
|
710 |
from authentic2.saml.models import LibertyProvider |
|
711 | ||
712 |
assert LibertyProvider.objects.count() == 1 |
|
713 |
provider = LibertyProvider.objects.first() |
|
714 |
provider_id = provider.pk |
|
715 |
assert provider.entity_id == 'http://passerelle.example.net/saml/metadata' |
|
716 |
assert provider.metadata == meta1 |
|
717 | ||
718 |
new_env = { |
|
719 |
'services': [ |
|
720 |
{ |
|
721 |
'service-id': 'authentic', |
|
722 |
'slug': 'test', |
|
723 |
'title': 'Test', |
|
724 |
'this': True, |
|
725 |
'secret_key': '12345', |
|
726 |
'base_url': 'http://sso.example.net', |
|
727 |
'variables': { |
|
728 |
'other_variable': 'bar', |
|
729 |
}, |
|
730 |
}, |
|
731 |
{ |
|
732 |
'service-id': 'passerelle', |
|
733 |
'slug': 'passerelle', |
|
734 |
'title': u'Passerelle', |
|
735 |
'base_url': 'http://new-passerelle.example.net', |
|
736 |
'saml-sp-metadata-url': 'http://new-passerelle.example.net/saml/metadata', |
|
737 |
'legacy_urls': [ |
|
738 |
{ |
|
739 |
'base_url': 'http://passerelle.example.net', |
|
740 |
'saml-sp-metadata-url': 'http://passerelle.example.net/saml/metadata', |
|
741 |
} |
|
742 |
], |
|
743 |
}, |
|
744 |
], |
|
745 |
'users': [], |
|
746 |
'profile': {'fields': []}, |
|
747 |
} |
|
748 | ||
749 |
with mock.patch('hobo.agent.authentic2.provisionning.notify_agents'): |
|
750 |
call_command('hobo_deploy', '--ignore-timestamp', 'http://sso.example.net', hobo_json(new_env)) |
|
751 |
# check that liberty provider is updated |
|
752 |
with tenant_context(tenant): |
|
753 |
from authentic2.saml.models import LibertyProvider |
|
754 | ||
755 |
assert LibertyProvider.objects.count() == 1 |
|
756 |
provider = LibertyProvider.objects.first() |
|
757 |
assert provider.metadata == meta2 |
|
758 |
assert provider.entity_id == 'http://new-passerelle.example.net/saml/metadata' |
|
759 |
assert provider.pk == provider_id |
tests_multipublik/conftest.py | ||
---|---|---|
21 | 21 |
t.create_schema() |
22 | 22 |
return t |
23 | 23 | |
24 |
tenants = [make_tenant('tenant1.example.net')] |
|
24 |
tenants = [ |
|
25 |
make_tenant('tenant1.example.net'), |
|
26 |
make_tenant('hobo2.example.net'), |
|
27 |
make_tenant('hobo3.example.net'), |
|
28 |
] |
|
25 | 29 | |
26 | 30 |
def fin(): |
27 | 31 |
from django.db import connection |
28 | 32 | |
29 | 33 |
connection.set_schema_to_public() |
30 | 34 |
for t in tenants: |
31 |
t.delete(True) |
|
35 |
if os.path.exists(t.get_directory()): |
|
36 |
t.delete(True) |
|
32 | 37 |
shutil.rmtree(base) |
33 | 38 | |
34 | 39 |
request.addfinalizer(fin) |
tests_multipublik/test_multipublik.py | ||
---|---|---|
345 | 345 |
with tenant_context(hobo2): |
346 | 346 |
assert Combo.objects.filter(secondary=True).count() == 1 |
347 | 347 |
assert Combo.objects.filter(secondary=False).count() == 1 |
348 | ||
349 |
# URL change in interco portal |
|
350 |
with tenant_context(hobo1): |
|
351 |
combo = Combo.objects.get(slug='portal') |
|
352 |
assert combo.base_url == 'https://combo1.example.net/' |
|
353 |
combo.change_base_url('https://new-combo1.example.net') |
|
354 |
combo.save() |
|
355 | ||
356 |
# check the interco hobo json |
|
357 |
hobo_json = get_hobo_json() |
|
358 |
for service in hobo_json['services']: |
|
359 |
if service['slug'] == 'portal': |
|
360 |
assert service['base_url'] == 'https://new-combo1.example.net/' |
|
361 |
assert len(service['legacy_urls']) == 1 |
|
362 |
assert service['legacy_urls'][0]['base_url'] == 'https://combo1.example.net/' |
|
363 |
break |
|
364 |
else: |
|
365 |
assert False, "no portal found" |
|
366 | ||
367 |
# inform coll2 about interco environment |
|
368 |
HoboDeployCommand().handle(hobo2.base_url, get_hobo_json_filename(hobo1)) |
|
369 |
with tenant_context(hobo2): |
|
370 |
# no extra combo created in coll 2 |
|
371 |
assert Combo.objects.filter().count() == 2 |
|
372 |
# interco portal url changed |
|
373 |
combo = Combo.objects.get(slug='_interco_portal') |
|
374 |
assert combo.base_url == 'https://new-combo1.example.net/' |
|
375 |
assert len(combo.legacy_urls) == 1 |
|
376 |
assert combo.legacy_urls[0]['base_url'] == 'https://combo1.example.net/' |
|
377 | ||
378 |
# inform coll2 about interco environment a second time, check that nothing changes |
|
379 |
HoboDeployCommand().handle(hobo2.base_url, get_hobo_json_filename(hobo1)) |
|
380 |
with tenant_context(hobo2): |
|
381 |
assert Combo.objects.filter().count() == 2 |
|
382 |
combo = Combo.objects.get(slug='_interco_portal') |
|
383 |
assert combo.base_url == 'https://new-combo1.example.net/' |
|
384 |
assert len(combo.legacy_urls) == 1 |
|
385 |
assert combo.legacy_urls[0]['base_url'] == 'https://combo1.example.net/' |
|
386 | ||
387 |
# URL change in coll2 portal |
|
388 |
with tenant_context(hobo2): |
|
389 |
combo = Combo.objects.get(slug='portal') |
|
390 |
assert combo.base_url == 'https://combo2.example.net/' |
|
391 |
combo.change_base_url('https://new-combo2.example.net') |
|
392 |
combo.save() |
|
393 | ||
394 |
# check the coll2 hobo json |
|
395 |
hobo_json = get_hobo_json() |
|
396 |
for service in hobo_json['services']: |
|
397 |
if service['slug'] == 'portal': |
|
398 |
assert service['base_url'] == 'https://new-combo2.example.net/' |
|
399 |
assert len(service['legacy_urls']) == 1 |
|
400 |
assert service['legacy_urls'][0]['base_url'] == 'https://combo2.example.net/' |
|
401 |
break |
|
402 |
else: |
|
403 |
assert False, "no portal found" |
|
404 | ||
405 |
# inform interco about coll2 environment |
|
406 |
HoboDeployCommand().handle(hobo1.base_url, get_hobo_json_filename(hobo2)) |
|
407 |
with tenant_context(hobo1): |
|
408 |
# no extra combo created in interco |
|
409 |
assert Combo.objects.filter().count() == 3 |
|
410 |
# coll2 portal url changed |
|
411 |
combo = Combo.objects.get(slug='_hobo-coll2_portal') |
|
412 |
assert combo.base_url == 'https://new-combo2.example.net/' |
|
413 |
assert len(combo.legacy_urls) == 1 |
|
414 |
assert combo.legacy_urls[0]['base_url'] == 'https://combo2.example.net/' |
|
415 | ||
416 |
# URL change on the primary hobo |
|
417 |
with tenant_context(hobo1): |
|
418 |
hobo = Hobo.objects.get(slug='hobo') |
|
419 |
assert hobo.base_url == 'https://tenant1.example.net/' |
|
420 |
hobo.change_base_url('https://new-tenant1.example.net') |
|
421 |
hobo.save() |
|
422 | ||
423 |
# check the interco hobo json |
|
424 |
hobo_json = get_hobo_json() |
|
425 |
for service in hobo_json['services']: |
|
426 |
if service['slug'] == 'hobo': |
|
427 |
assert service['base_url'] == 'https://new-tenant1.example.net/' |
|
428 |
assert len(service['legacy_urls']) == 1 |
|
429 |
assert service['legacy_urls'][0]['base_url'] == 'https://tenant1.example.net/' |
|
430 |
break |
|
431 |
else: |
|
432 |
assert False, 'no hobo found' |
|
433 | ||
434 |
# inform coll2 about interco environment |
|
435 |
HoboDeployCommand().handle(hobo2.base_url, get_hobo_json_filename(hobo1)) |
|
436 |
with tenant_context(hobo2): |
|
437 |
# no extra hobo created in coll 2 |
|
438 |
assert Hobo.objects.count() == 3 |
|
439 |
# interco hobo url changed |
|
440 |
hobo = Hobo.objects.get(slug='_interco_hobo') |
|
441 |
assert hobo.base_url == 'https://new-tenant1.example.net/' |
|
442 |
assert len(hobo.legacy_urls) == 1 |
|
443 |
assert hobo.legacy_urls[0]['base_url'] == 'https://tenant1.example.net/' |
|
444 | ||
445 |
# URL change on coll2 hobo (initiated by the interco tenant) |
|
446 |
with tenant_context(hobo1): |
|
447 |
hobo2 = Hobo.objects.get(slug='hobo-coll2') |
|
448 |
assert hobo2.base_url == 'https://hobo2.example.net/' |
|
449 |
hobo2.change_base_url('https://new-hobo2.example.net') |
|
450 |
hobo2.save() |
|
451 | ||
452 |
# inform coll2 about interco environment |
|
453 |
HoboDeployCommand().handle(hobo2.base_url, get_hobo_json_filename(hobo1)) |
|
454 | ||
455 |
hobo2 = TenantMiddleware.get_tenant_by_hostname('new-hobo2.example.net') |
|
456 |
with tenant_context(hobo2): |
|
457 |
# no extra hobo created in coll 2 |
|
458 |
assert Hobo.objects.count() == 3 |
|
459 |
# coll2 hobo url changed |
|
460 |
hobo = Hobo.objects.get(slug='hobo') |
|
461 |
assert hobo.base_url == 'https://new-hobo2.example.net/' |
|
462 |
assert len(hobo.legacy_urls) == 1 |
|
463 |
assert hobo.legacy_urls[0]['base_url'] == 'https://hobo2.example.net/' |
tests_schemas/legacy_urls_chrono_env.json | ||
---|---|---|
1 |
{ |
|
2 |
"services": [ |
|
3 |
{ |
|
4 |
"service-id": "chrono", |
|
5 |
"base_url": "https://new-chrono.dev.publik.love/", |
|
6 |
"slug": "agendas", |
|
7 |
"title": "CHRONO", |
|
8 |
"secret_key": "123", |
|
9 |
"template_name": "import_me", |
|
10 |
"legacy_urls": [ |
|
11 |
{ |
|
12 |
"base_url": "https://chrono.dev.publik.love/" |
|
13 |
} |
|
14 |
] |
|
15 |
}, |
|
16 |
{ |
|
17 |
"service-id": "wcs", |
|
18 |
"base_url": "https://wcs.dev.publik.love/", |
|
19 |
"slug": "eservices", |
|
20 |
"title": "WCS" |
|
21 |
}, |
|
22 |
{ |
|
23 |
"service-id": "hobo", |
|
24 |
"base_url": "https://hobo.dev.publik.love/", |
|
25 |
"slug": "hobo", |
|
26 |
"title": "HOBO", |
|
27 |
"secret_key": "123" |
|
28 |
}, |
|
29 |
{ |
|
30 |
"service-id": "combo", |
|
31 |
"base_url": "https://combo.dev.publik.love/", |
|
32 |
"slug": "portal", |
|
33 |
"title": "COMBO", |
|
34 |
"secret_key": "123", |
|
35 |
"template_name": "import_me" |
|
36 |
}, |
|
37 |
{ |
|
38 |
"service-id": "authentic", |
|
39 |
"base_url": "https://authentic.dev.publik.love/", |
|
40 |
"slug": "idp", |
|
41 |
"title": "A2", |
|
42 |
"secret_key": "123" |
|
43 |
} |
|
44 |
] |
|
45 |
} |
tests_schemas/test_hobo_deploy.py | ||
---|---|---|
1 | 1 |
import os |
2 | 2 | |
3 | 3 |
import mock |
4 |
import pytest |
|
4 | 5 |
from django.core.management import call_command, get_commands, load_command_class |
5 | 6 |
from tenant_schemas.utils import tenant_context |
6 | 7 | |
7 | 8 |
from hobo.environment.models import Variable |
8 |
from hobo.multitenant.middleware import TenantMiddleware |
|
9 |
from hobo.multitenant.middleware import TenantMiddleware, TenantNotFound
|
|
9 | 10 | |
10 | 11 | |
11 | 12 |
def assert_deployed(domain): |
... | ... | |
26 | 27 |
command = load_command_class('hobo.agent.common', 'hobo_deploy') |
27 | 28 |
domain = 'chrono.dev.publik.love' |
28 | 29 | |
29 |
def my_call_command(command, parameter): |
|
30 |
def my_call_command(command, parameter, **kwargs):
|
|
30 | 31 |
if command == 'import_template': |
31 | 32 |
my_call_command.import_template_was_called = True |
32 | 33 |
return |
33 |
call_command(command, parameter) |
|
34 |
call_command(command, parameter, **kwargs)
|
|
34 | 35 | |
35 | 36 |
mocked_get_commands.return_value = ['import_template'] |
36 | 37 |
mocked_call_command.side_effect = my_call_command |
... | ... | |
77 | 78 |
# fails to simulate call from bijoe agent, that overload deploy_specifics() |
78 | 79 |
# $ bijoe-mange hobo-deploy |
79 | 80 |
# here, because this code is not implemented here |
81 | ||
82 | ||
83 |
def test_deploy_with_legacy_urls(db): |
|
84 |
command = load_command_class('hobo.agent.common', 'hobo_deploy') |
|
85 |
domain = 'chrono.dev.publik.love' |
|
86 |
command.handle('https://%s/' % domain, 'tests_schemas/env.json') |
|
87 |
assert_deployed(domain) |
|
88 |
tenant_directory = TenantMiddleware.get_tenant_by_hostname(domain).get_directory() |
|
89 | ||
90 |
# change domain |
|
91 |
new_domain = 'new-chrono.dev.publik.love' |
|
92 |
command.handle('https://%s/' % new_domain, 'tests_schemas/legacy_urls_chrono_env.json') |
|
93 |
assert_deployed(new_domain) |
|
94 | ||
95 |
# check old tenant is gone |
|
96 |
with pytest.raises(TenantNotFound): |
|
97 |
TenantMiddleware.get_tenant_by_hostname(domain) |
|
98 |
assert not os.path.exists(tenant_directory) |
|
80 |
- |