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 emitter 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 |
else: |
|
82 |
service, created = service_klass.objects.get_or_create( |
|
83 |
base_url=service_dict['base_url'], |
|
84 |
secondary=True, |
|
85 |
defaults={ |
|
86 |
'title': service_dict['title'], |
|
87 |
'slug': service_slug, |
|
88 |
'secret_key': service_dict.get('secret_key'), |
|
89 |
}, |
|
90 |
) |
|
91 | ||
75 | 92 |
service.title = service_dict['title'] |
76 | 93 |
service.secret_key = service_dict.get('secret_key') |
77 | 94 |
service.template_name = service_dict.get('template_name') or '' |
hobo/environment/migrations/0023_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', '0022_emitter'), |
|
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 | ||
---|---|---|
564 | 564 |
export_ref = sort_and_remove_uuid(export_site()) |
565 | 565 |
file_ref = sort_and_remove_uuid(json.loads(content)) |
566 | 566 |
assert export_ref == file_ref |
567 | ||
568 | ||
569 |
def test_hobo_deploy_with_legacy_urls(monkeypatch, tenant_base, mocker, skeleton_dir, tmp_path): |
|
570 |
from django.core.management import call_command |
|
571 | ||
572 |
from hobo.agent.authentic2.management.commands.hobo_deploy import Command as HoboDeployCommand |
|
573 | ||
574 |
requests_get = mocker.patch('requests.get') |
|
575 |
meta1 = '''<?xml version="1.0"?> |
|
576 |
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" |
|
577 |
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" |
|
578 |
xmlns:ds="http://www.w3.org/2000/09/xmldsig#" |
|
579 |
entityID="http://passerelle.example.net/saml/metadata"> |
|
580 |
<SPSSODescriptor |
|
581 |
AuthnRequestsSigned="true" WantAssertionsSigned="true" |
|
582 |
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> |
|
583 |
<KeyDescriptor use="signing"> |
|
584 |
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> |
|
585 |
<KeyValue xmlns="http://www.w3.org/2000/09/xmldsig#"> |
|
586 |
<RSAKeyValue> |
|
587 |
<Modulus>nJpkBznHNbvE+RAC6mU+NPQnIWs8gFNCm6I3FPcUKYpaJbXaurJ4cJgvnaEiqIXPQDcbHxuLeCbYbId9yascWZirvQbh8d/r+Vv+24bPG++9gW+i3Nnz1VW8V+z0b+puHWvM/FjJjBNJgWkI38gaupz47U6/02CtWx00stitiwk=</Modulus> |
|
588 |
<Exponent>AQAB</Exponent> |
|
589 |
</RSAKeyValue> |
|
590 |
</KeyValue> |
|
591 |
</ds:KeyInfo> |
|
592 |
</KeyDescriptor> |
|
593 |
<KeyDescriptor use="encryption"> |
|
594 |
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> |
|
595 |
<KeyValue xmlns="http://www.w3.org/2000/09/xmldsig#"> |
|
596 |
<RSAKeyValue> |
|
597 |
<Modulus>3BxSiAzGvY1Yuqa31L7Zr2WHM/8cn5oX+Q6A2SYgzjuvAgnWyizN8YgW/fHR4G7MtkmZ5RFJLXfcSLwbUfpFHV6KO1ikbgViYuFempM+SWtjqEI7ribm9GaI5kUzHJZBrH3/Q9XAd9/GLLALxurGjbKDeLfc0D+7el26g4sYmA8=</Modulus> |
|
598 |
<Exponent>AQAB</Exponent> |
|
599 |
</RSAKeyValue> |
|
600 |
</KeyValue> |
|
601 |
</ds:KeyInfo> |
|
602 |
</KeyDescriptor> |
|
603 | ||
604 |
<SingleLogoutService |
|
605 |
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" |
|
606 |
Location="http://passerelle.example.net/saml/singleLogout" |
|
607 |
ResponseLocation="http://passerelle.example.net/saml/singleLogoutReturn" /> |
|
608 |
<SingleLogoutService |
|
609 |
Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" |
|
610 |
Location="http://passerelle.example.net/saml/singleLogoutSOAP" /> |
|
611 |
<ManageNameIDService |
|
612 |
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" |
|
613 |
Location="http://passerelle.example.net/saml/manageNameId" |
|
614 |
ResponseLocation="http://passerelle.example.net/saml/manageNameIdReturn" /> |
|
615 |
<ManageNameIDService |
|
616 |
Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" |
|
617 |
Location="http://passerelle.example.net/saml/manageNameIdSOAP" /> |
|
618 |
<AssertionConsumerService isDefault="true" index="0" |
|
619 |
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" |
|
620 |
Location="http://passerelle.example.net/saml/assertionConsumerArtifact" /> |
|
621 |
<AssertionConsumerService index="1" |
|
622 |
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" |
|
623 |
Location="http://passerelle.example.net/saml/assertionConsumerPost" /> |
|
624 |
<AssertionConsumerService index="2" |
|
625 |
Binding="urn:oasis:names:tc:SAML:2.0:bindings:PAOS" |
|
626 |
Location="http://passerelle.example.net/saml/assertionConsumerSOAP" /> |
|
627 |
<AssertionConsumerService index="3" |
|
628 |
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" |
|
629 |
Location="http://passerelle.example.net/saml/assertionConsumerRedirect" /> |
|
630 |
</SPSSODescriptor> |
|
631 |
</EntityDescriptor>''' |
|
632 |
meta2 = meta1.replace('passerelle.example.net', 'new-passerelle.example.net') |
|
633 |
monkeypatch.setattr(HoboDeployCommand, 'backoff_factor', 0.0001) |
|
634 | ||
635 |
side_effect_iter = iter([meta1, meta2]) |
|
636 | ||
637 |
def side_effect(*args, **kwargs): |
|
638 |
for v in side_effect_iter: |
|
639 |
m = mock.Mock() |
|
640 |
m.text = v |
|
641 |
return m |
|
642 | ||
643 |
requests_get.side_effect = side_effect |
|
644 | ||
645 |
def hobo_json(env_dict): |
|
646 |
with tempfile.NamedTemporaryFile(mode='w', dir=str(tmp_path), delete=False) as hobo_json: |
|
647 |
hobo_json_content = json.dumps(env_dict) |
|
648 |
hobo_json.write(hobo_json_content) |
|
649 |
return hobo_json.name |
|
650 | ||
651 |
env = { |
|
652 |
'services': [ |
|
653 |
{ |
|
654 |
'service-id': 'authentic', |
|
655 |
'slug': 'test', |
|
656 |
'title': 'Test', |
|
657 |
'this': True, |
|
658 |
'secret_key': '12345', |
|
659 |
'base_url': 'http://sso.example.net', |
|
660 |
'variables': { |
|
661 |
'other_variable': 'bar', |
|
662 |
}, |
|
663 |
}, |
|
664 |
{ |
|
665 |
'service-id': 'passerelle', |
|
666 |
'slug': 'passerelle', |
|
667 |
'title': u'Passerelle', |
|
668 |
'base_url': 'http://passerelle.example.net', |
|
669 |
'saml-sp-metadata-url': 'http://passerelle.example.net/saml/metadata', |
|
670 |
}, |
|
671 |
], |
|
672 |
'users': [], |
|
673 |
'profile': {'fields': []}, |
|
674 |
} |
|
675 | ||
676 |
with mock.patch('hobo.agent.authentic2.provisionning.notify_agents'): |
|
677 |
call_command('hobo_deploy', 'http://sso.example.net', hobo_json(env)) |
|
678 | ||
679 |
from hobo.multitenant.middleware import TenantMiddleware |
|
680 | ||
681 |
tenants = list(TenantMiddleware.get_tenants()) |
|
682 |
assert len(tenants) == 1 |
|
683 |
tenant = tenants[0] |
|
684 |
assert tenant.domain_url == 'sso.example.net' |
|
685 |
assert tenant.schema_name == 'sso_example_net' |
|
686 |
tenant_directory = tenant.get_directory() |
|
687 |
assert tenant_directory == os.path.join(tenant_base, tenant.domain_url) |
|
688 |
assert os.path.exists(os.path.join(tenant_directory, 'saml.crt')) |
|
689 |
assert os.path.exists(os.path.join(tenant_directory, 'saml.key')) |
|
690 | ||
691 |
from tenant_schemas.utils import tenant_context |
|
692 | ||
693 |
with tenant_context(tenant): |
|
694 |
# SAML checks |
|
695 |
from authentic2.saml.models import LibertyProvider |
|
696 | ||
697 |
assert LibertyProvider.objects.count() == 1 |
|
698 |
provider = LibertyProvider.objects.first() |
|
699 |
provider_id = provider.pk |
|
700 |
assert provider.entity_id == 'http://passerelle.example.net/saml/metadata' |
|
701 |
assert provider.metadata == meta1 |
|
702 | ||
703 |
new_env = { |
|
704 |
'services': [ |
|
705 |
{ |
|
706 |
'service-id': 'authentic', |
|
707 |
'slug': 'test', |
|
708 |
'title': 'Test', |
|
709 |
'this': True, |
|
710 |
'secret_key': '12345', |
|
711 |
'base_url': 'http://sso.example.net', |
|
712 |
'variables': { |
|
713 |
'other_variable': 'bar', |
|
714 |
}, |
|
715 |
}, |
|
716 |
{ |
|
717 |
'service-id': 'passerelle', |
|
718 |
'slug': 'passerelle', |
|
719 |
'title': u'Passerelle', |
|
720 |
'base_url': 'http://new-passerelle.example.net', |
|
721 |
'saml-sp-metadata-url': 'http://new-passerelle.example.net/saml/metadata', |
|
722 |
'legacy_urls': [ |
|
723 |
{ |
|
724 |
'base_url': 'http://passerelle.example.net', |
|
725 |
'saml-sp-metadata-url': 'http://passerelle.example.net/saml/metadata', |
|
726 |
} |
|
727 |
], |
|
728 |
}, |
|
729 |
], |
|
730 |
'users': [], |
|
731 |
'profile': {'fields': []}, |
|
732 |
} |
|
733 | ||
734 |
with mock.patch('hobo.agent.authentic2.provisionning.notify_agents'): |
|
735 |
call_command('hobo_deploy', '--ignore-timestamp', 'http://sso.example.net', hobo_json(new_env)) |
|
736 |
# check that liberty provider is updated |
|
737 |
with tenant_context(tenant): |
|
738 |
from authentic2.saml.models import LibertyProvider |
|
739 | ||
740 |
assert LibertyProvider.objects.count() == 1 |
|
741 |
provider = LibertyProvider.objects.first() |
|
742 |
assert provider.metadata == meta2 |
|
743 |
assert provider.entity_id == 'http://new-passerelle.example.net/saml/metadata' |
|
744 |
assert provider.pk == provider_id |
tests_multipublik/conftest.py | ||
---|---|---|
23 | 23 |
t.create_schema() |
24 | 24 |
return t |
25 | 25 | |
26 |
tenants = [make_tenant('tenant1.example.net')] |
|
26 |
tenants = [ |
|
27 |
make_tenant('tenant1.example.net'), |
|
28 |
make_tenant('hobo2.example.net'), |
|
29 |
make_tenant('hobo3.example.net'), |
|
30 |
] |
|
27 | 31 | |
28 | 32 |
def fin(): |
29 | 33 |
from django.db import connection |
30 | 34 | |
31 | 35 |
connection.set_schema_to_public() |
32 | 36 |
for t in tenants: |
33 |
t.delete(True) |
|
37 |
if os.path.exists(t.get_directory()): |
|
38 |
t.delete(True) |
|
34 | 39 |
shutil.rmtree(base) |
35 | 40 | |
36 | 41 |
request.addfinalizer(fin) |
tests_multipublik/test_multipublik.py | ||
---|---|---|
47 | 47 |
assert combo.base_url == 'http://combo1.example.net/' |
48 | 48 |
assert combo.secondary is True |
49 | 49 | |
50 |
assert Hobo.objects.count() == 2
|
|
50 |
assert Hobo.objects.count() == 3
|
|
51 | 51 |
# interco hobo |
52 | 52 |
hobo = Hobo.objects.get(slug='_interco_hobo') |
53 | 53 |
assert hobo.title == 'Hobo' |
... | ... | |
112 | 112 |
assert combo.base_url == 'http://combo1.example.net/' |
113 | 113 |
assert combo.secondary is True |
114 | 114 | |
115 |
assert Hobo.objects.count() == 2
|
|
115 |
assert Hobo.objects.count() == 3
|
|
116 | 116 |
# interco hobo |
117 | 117 |
hobo = Hobo.objects.get(slug='_interco_hobo') |
118 | 118 |
assert hobo.title == 'Hobo' |
... | ... | |
155 | 155 |
with tenant_context(hobo2): |
156 | 156 |
assert Combo.objects.filter(secondary=True).count() == 1 |
157 | 157 |
assert Combo.objects.filter(secondary=False).count() == 1 |
158 | ||
159 |
# URL change in interco portal |
|
160 |
with tenant_context(hobo1): |
|
161 |
combo = Combo.objects.get(slug='portal') |
|
162 |
assert combo.base_url == 'http://combo1.example.net/' |
|
163 |
combo.change_base_url('http://new-combo1.example.net') |
|
164 |
combo.save() |
|
165 | ||
166 |
# check the interco hobo json |
|
167 |
hobo_json = get_hobo_json() |
|
168 |
for service in hobo_json['services']: |
|
169 |
if service['slug'] == 'portal': |
|
170 |
assert service['base_url'] == 'http://new-combo1.example.net/' |
|
171 |
assert len(service['legacy_urls']) == 1 |
|
172 |
assert service['legacy_urls'][0]['base_url'] == 'http://combo1.example.net/' |
|
173 |
break |
|
174 |
else: |
|
175 |
assert False, "no portal found" |
|
176 | ||
177 |
# inform coll2 about interco environment |
|
178 |
HoboDeployCommand().handle(hobo2.base_url, get_hobo_json_filename(hobo1)) |
|
179 |
with tenant_context(hobo2): |
|
180 |
# no extra combo created in coll 2 |
|
181 |
assert Combo.objects.filter().count() == 2 |
|
182 |
# interco portal url changed |
|
183 |
combo = Combo.objects.get(slug='_interco_portal') |
|
184 |
assert combo.base_url == 'http://new-combo1.example.net/' |
|
185 |
assert len(combo.legacy_urls) == 1 |
|
186 |
assert combo.legacy_urls[0]['base_url'] == 'http://combo1.example.net/' |
|
187 | ||
188 |
# inform coll2 about interco environment a second time, check that nothing changes |
|
189 |
HoboDeployCommand().handle(hobo2.base_url, get_hobo_json_filename(hobo1)) |
|
190 |
with tenant_context(hobo2): |
|
191 |
assert Combo.objects.filter().count() == 2 |
|
192 |
combo = Combo.objects.get(slug='_interco_portal') |
|
193 |
assert combo.base_url == 'http://new-combo1.example.net/' |
|
194 |
assert len(combo.legacy_urls) == 1 |
|
195 |
assert combo.legacy_urls[0]['base_url'] == 'http://combo1.example.net/' |
|
196 | ||
197 |
# URL change in coll2 portal |
|
198 |
with tenant_context(hobo2): |
|
199 |
combo = Combo.objects.get(slug='portal') |
|
200 |
assert combo.base_url == 'http://combo2.example.net/' |
|
201 |
combo.change_base_url('http://new-combo2.example.net') |
|
202 |
combo.save() |
|
203 | ||
204 |
# check the coll2 hobo json |
|
205 |
hobo_json = get_hobo_json() |
|
206 |
for service in hobo_json['services']: |
|
207 |
if service['slug'] == 'portal': |
|
208 |
assert service['base_url'] == 'http://new-combo2.example.net/' |
|
209 |
assert len(service['legacy_urls']) == 1 |
|
210 |
assert service['legacy_urls'][0]['base_url'] == 'http://combo2.example.net/' |
|
211 |
break |
|
212 |
else: |
|
213 |
assert False, "no portal found" |
|
214 | ||
215 |
# inform interco about coll2 environment |
|
216 |
HoboDeployCommand().handle(hobo1.base_url, get_hobo_json_filename(hobo2)) |
|
217 |
with tenant_context(hobo1): |
|
218 |
# no extra combo created in interco |
|
219 |
assert Combo.objects.filter().count() == 3 |
|
220 |
# coll2 portal url changed |
|
221 |
combo = Combo.objects.get(slug='_hobo-coll2_portal') |
|
222 |
assert combo.base_url == 'http://new-combo2.example.net/' |
|
223 |
assert len(combo.legacy_urls) == 1 |
|
224 |
assert combo.legacy_urls[0]['base_url'] == 'http://combo2.example.net/' |
|
225 | ||
226 |
# URL change on the primary hobo |
|
227 |
with tenant_context(hobo1): |
|
228 |
hobo = Hobo.objects.get(slug='hobo') |
|
229 |
assert hobo.base_url == 'http://tenant1.example.net/' |
|
230 |
hobo.change_base_url('http://new-tenant1.example.net') |
|
231 |
hobo.save() |
|
232 | ||
233 |
# check the interco hobo json |
|
234 |
hobo_json = get_hobo_json() |
|
235 |
for service in hobo_json['services']: |
|
236 |
if service['slug'] == 'hobo': |
|
237 |
assert service['base_url'] == 'http://new-tenant1.example.net/' |
|
238 |
assert len(service['legacy_urls']) == 1 |
|
239 |
assert service['legacy_urls'][0]['base_url'] == 'http://tenant1.example.net/' |
|
240 |
break |
|
241 |
else: |
|
242 |
assert False, 'no hobo found' |
|
243 | ||
244 |
# inform coll2 about interco environment |
|
245 |
HoboDeployCommand().handle(hobo2.base_url, get_hobo_json_filename(hobo1)) |
|
246 |
with tenant_context(hobo2): |
|
247 |
# no extra hobo created in coll 2 |
|
248 |
assert Hobo.objects.count() == 3 |
|
249 |
# interco hobo url changed |
|
250 |
hobo = Hobo.objects.get(slug='_interco_hobo') |
|
251 |
assert hobo.base_url == 'http://new-tenant1.example.net/' |
|
252 |
assert len(hobo.legacy_urls) == 1 |
|
253 |
assert hobo.legacy_urls[0]['base_url'] == 'http://tenant1.example.net/' |
|
254 | ||
255 |
# URL change on coll2 hobo (initiated by the interco tenant) |
|
256 |
with tenant_context(hobo1): |
|
257 |
hobo2 = Hobo.objects.get(slug='hobo-coll2') |
|
258 |
assert hobo2.base_url == 'http://hobo2.example.net/' |
|
259 |
hobo2.change_base_url('http://new-hobo2.example.net') |
|
260 |
hobo2.save() |
|
261 | ||
262 |
# inform coll2 about interco environment |
|
263 |
HoboDeployCommand().handle(hobo2.base_url, get_hobo_json_filename(hobo1)) |
|
264 | ||
265 |
hobo2 = TenantMiddleware.get_tenant_by_hostname('new-hobo2.example.net') |
|
266 |
with tenant_context(hobo2): |
|
267 |
# no extra hobo created in coll 2 |
|
268 |
assert Hobo.objects.count() == 3 |
|
269 |
# coll2 hobo url changed |
|
270 |
hobo = Hobo.objects.get(slug='hobo') |
|
271 |
assert hobo.base_url == 'http://new-hobo2.example.net/' |
|
272 |
assert len(hobo.legacy_urls) == 1 |
|
273 |
assert hobo.legacy_urls[0]['base_url'] == 'http://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 |
- |