blob: 696c5c8a825db2c29fd0336da1e9be05aa88408e [file] [log] [blame]
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import os
from io import BytesIO
import mock
import pytest
from twitter.common.contextutil import temporary_dir
from apache.aurora.client import config
from apache.aurora.client.config import (
PRODUCTION_DEPRECATED_WARNING,
get_config as get_aurora_config
)
from apache.aurora.config import AuroraConfig
from apache.aurora.config.loader import AuroraConfigLoader
from apache.aurora.config.schema.base import (
MB,
Announcer,
HealthCheckConfig,
Job,
Resources,
Task,
UpdateConfig
)
from apache.thermos.config.schema_base import Process
MESOS_CONFIG_BASE = """
HELLO_WORLD = Job(
name = 'hello_world',
role = 'john_doe',
cluster = 'test-cluster',
environment = 'test',
%(announce)s
task = Task(
name = 'main',
processes = [Process(name = 'hello_world', cmdline = '%(cmdline)s')],
resources = Resources(cpu = 0.1, ram = 64 * MB, disk = 64 * MB),
)
)
jobs = [HELLO_WORLD]
"""
MESOS_CONFIG_WITH_INCLUDE = """
%s
include(%s)
"""
MESOS_CONFIG_WITH_ANNOUNCE_1 = MESOS_CONFIG_BASE % {
'cmdline': 'echo {{thermos.ports[http]}}',
'announce': 'announce = Announcer(primary_port="http"),'}
MESOS_CONFIG_WITH_ANNOUNCE_2 = MESOS_CONFIG_BASE % {
'cmdline': 'echo {{thermos.ports[http]}}',
'announce': '''announce = Announcer(
primary_port = "http",
portmap = {"aurora": "http"}),
'''}
MESOS_CONFIG_WITH_INVALID_STATS = MESOS_CONFIG_BASE % {
'cmdline': 'echo {{thermos.ports[http]}}',
'announce': 'announce = Announcer(primary_port="http", stats_port="blah"),'}
MESOS_CONFIG_WITHOUT_ANNOUNCE = MESOS_CONFIG_BASE % {
'cmdline': 'echo {{thermos.ports[http]}}',
'announce': ''
}
def test_get_config_announces():
for good_config in (
MESOS_CONFIG_WITH_ANNOUNCE_1,
MESOS_CONFIG_WITH_ANNOUNCE_2,
MESOS_CONFIG_WITHOUT_ANNOUNCE):
bio = BytesIO(good_config)
get_aurora_config('hello_world', bio).job()
def test_get_config_with_broken_subscopes():
bad_config = MESOS_CONFIG_BASE % {
'cmdline': 'echo {{hello[{{thermos.ports[http]}}]}}',
'announce': '',
}
bio = BytesIO(bad_config)
with pytest.raises(AuroraConfig.InvalidConfig) as cm:
get_aurora_config('hello_world', bio).job()
assert 'Unexpected unbound refs' in str(cm.value.message)
def test_get_config_primary_port_warning(capsys):
config_unbound_primary_port = MESOS_CONFIG_BASE % {
'cmdline': 'echo {{thermos.ports[http]}}',
'announce': 'announce = Announcer(primary_port = "custom", portmap = {}),'
}
get_aurora_config('hello_world', BytesIO(config_unbound_primary_port)).job()
_, err = capsys.readouterr()
msg = "Announcer specified primary port as 'custom' but no processes have bound that port"
assert msg in err
def test_get_config_mapped_primary_port(capsys):
config_mapped_primary_port = MESOS_CONFIG_BASE % {
'cmdline': 'echo {{thermos.ports[custom]}}',
'announce': 'announce = Announcer(primary_port = "http", portmap = {"http": "custom"}),'
}
get_aurora_config('hello_world', BytesIO(config_mapped_primary_port)).job()
out, err = capsys.readouterr()
assert err == "" # No warning about unbound primary port
assert out == ""
def test_get_config_select():
bio = BytesIO(MESOS_CONFIG_WITHOUT_ANNOUNCE)
get_aurora_config(
'hello_world',
bio,
select_env='test',
select_role='john_doe',
select_cluster='test-cluster').job()
bio.seek(0)
with pytest.raises(ValueError) as cm:
get_aurora_config(
'hello_world',
bio,
select_env='staging42',
select_role='moua',
select_cluster='test-cluster').job()
assert 'test-cluster/john_doe/test/hello_world' in str(cm.value.message)
def test_include():
with temporary_dir() as dir:
hello_mesos_fname = "hello_world.mesos"
hello_mesos_path = os.path.join(dir, hello_mesos_fname)
with open(os.path.join(dir, hello_mesos_path), "wb") as hello_world_mesos:
hello_world_mesos.write(MESOS_CONFIG_WITHOUT_ANNOUNCE)
hello_world_mesos.flush()
hello_include_fname_path = os.path.join(dir, "hello_include_fname.mesos")
with open(hello_include_fname_path, "wb+") as hello_include_fname_fp:
hello_include_fname_fp.write(MESOS_CONFIG_WITH_INCLUDE %
("", """'%s'""" % hello_mesos_fname))
hello_include_fname_fp.flush()
get_aurora_config('hello_world', hello_include_fname_path)
hello_include_fname_fp.seek(0)
with pytest.raises(AuroraConfigLoader.InvalidConfigError):
get_aurora_config('hello_world', hello_include_fname_fp)
def test_dedicated_portmap():
base_job = Job(
name='hello_world', role='john_doe', cluster='test-cluster',
task=Task(name='main', processes=[],
resources=Resources(cpu=0.1, ram=64 * MB, disk=64 * MB)))
config._validate_announce_configuration(AuroraConfig(base_job))
config._validate_announce_configuration(
AuroraConfig(base_job(constraints={'dedicated': 'mesos-team'})))
config._validate_announce_configuration(
AuroraConfig(base_job(constraints={'dedicated': 'mesos-team'},
announce=Announcer(portmap={'http': 80}))))
with pytest.raises(ValueError):
config._validate_announce_configuration(
AuroraConfig(base_job(announce=Announcer(portmap={'http': 80}))))
with pytest.raises(ValueError):
config._validate_announce_configuration(
AuroraConfig(base_job(announce=Announcer(portmap={'http': 80}),
constraints={'foo': 'bar'})))
def test_update_config_passes_with_default_values():
base_job = Job(
name='hello_world', role='john_doe', cluster='test-cluster',
task=Task(name='main', processes=[],
resources=Resources(cpu=0.1, ram=64 * MB, disk=64 * MB)))
config._validate_update_config(AuroraConfig(base_job))
def test_update_config_passes_with_max_consecutive_failures_zero():
base_job = Job(
name='hello_world', role='john_doe', cluster='test-cluster',
health_check_config=HealthCheckConfig(max_consecutive_failures=0),
task=Task(name='main', processes=[],
resources=Resources(cpu=0.1, ram=64 * MB, disk=64 * MB)))
config._validate_update_config(AuroraConfig(base_job))
def test_update_config_fails_with_max_consecutive_failures_negative():
base_job = Job(
name='hello_world', role='john_doe', cluster='test-cluster',
update_config=UpdateConfig(watch_secs=26),
health_check_config=HealthCheckConfig(max_consecutive_failures=-1),
task=Task(name='main', processes=[],
resources=Resources(cpu=0.1, ram=64 * MB, disk=64 * MB)))
with pytest.raises(SystemExit):
config._validate_update_config(AuroraConfig(base_job))
def test_update_config_passes_with_min_consecutive_successes_zero():
base_job = Job(
name='hello_world', role='john_doe', cluster='test-cluster',
health_check_config=HealthCheckConfig(min_consecutive_successes=0),
task=Task(name='main', processes=[],
resources=Resources(cpu=0.1, ram=64 * MB, disk=64 * MB)))
config._validate_update_config(AuroraConfig(base_job))
def test_update_config_fails_with_min_consecutive_successes_negative():
base_job = Job(
name='hello_world', role='john_doe', cluster='test-cluster',
health_check_config=HealthCheckConfig(min_consecutive_successes=-1),
task=Task(name='main', processes=[],
resources=Resources(cpu=0.1, ram=64 * MB, disk=64 * MB)))
with pytest.raises(SystemExit):
config._validate_update_config(AuroraConfig(base_job))
def test_update_config_passes_with_watch_secs_zero():
base_job = Job(
name='hello_world', role='john_doe', cluster='test-cluster',
update_config=UpdateConfig(watch_secs=0),
task=Task(name='main', processes=[],
resources=Resources(cpu=0.1, ram=64 * MB, disk=64 * MB)))
config._validate_update_config(AuroraConfig(base_job))
def test_update_config_fails_watch_secs_negative():
base_job = Job(
name='hello_world', role='john_doe', cluster='test-cluster',
update_config=UpdateConfig(watch_secs=-1),
task=Task(name='main', processes=[],
resources=Resources(cpu=0.1, ram=64 * MB, disk=64 * MB)))
with pytest.raises(SystemExit):
config._validate_update_config(AuroraConfig(base_job))
def test_validate_deprecated_config_adds_warning_for_production():
job = Job(name='hello_world', role='john_doe', cluster='test-cluster', environment='test',
task=Task(name='main', processes=[Process(cmdline='echo {{_unbound_}}', name='eco')],
resources=Resources(cpu=0.1, ram=64 * MB, disk=64 * MB)), production='true')
with mock.patch('apache.aurora.client.config.deprecation_warning') as mock_warning:
config._validate_deprecated_config(AuroraConfig(job))
mock_warning.assert_called_once_with(PRODUCTION_DEPRECATED_WARNING)
def test_validate_deprecated_config_adds_no_warning_when_tier_is_set():
job = Job(name='hello_world', role='john_doe', cluster='test-cluster', environment='test',
task=Task(name='main', processes=[Process(cmdline='echo {{_unbound_}}', name='eco')],
resources=Resources(cpu=0.1, ram=64 * MB, disk=64 * MB)),
production='true', tier='preferred')
with mock.patch('apache.aurora.client.config.deprecation_warning') as mock_warning:
config._validate_deprecated_config(AuroraConfig(job))
assert mock_warning.call_count == 0