shipyard/src/bin/shipyard_airflow/tests/unit/common/deployment_group/test_deployment_group_manag...

323 lines
10 KiB
Python

# Copyright 2017 AT&T Intellectual Property. All other rights reserved.
#
# 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.
"""Tests to validate behavior of the classes in the deployment_group_manager
module
"""
import pytest
import yaml
from shipyard_airflow.common.deployment_group.deployment_group import (
Stage
)
from shipyard_airflow.common.deployment_group.deployment_group_manager import (
DeploymentGroupManager
)
from shipyard_airflow.common.deployment_group.errors import (
DeploymentGroupCycleError, DeploymentGroupStageError,
UnknownDeploymentGroupError, UnknownNodeError
)
from .node_lookup_stubs import node_lookup
_GROUPS_YAML = """
- name: control-nodes
critical: true
depends_on:
- ntp-node
selectors:
- node_names:
- node1
- node2
node_labels: []
node_tags: []
rack_names:
- rack1
success_criteria:
percent_successful_nodes: 100
- name: compute-nodes-1
critical: false
depends_on:
- control-nodes
selectors:
- node_names: []
node_labels:
- compute:true
rack_names:
- rack2
node_tags: []
success_criteria:
percent_successful_nodes: 50
- name: compute-nodes-2
critical: false
depends_on:
- control-nodes
selectors:
- node_names: []
node_labels:
- compute:true
rack_names:
- rack3
node_tags: []
success_criteria:
percent_successful_nodes: 50
- name: spare-compute-nodes
critical: false
depends_on:
- compute-nodes-2
- compute-nodes-1
selectors:
- node_names: []
node_labels:
- compute:true
rack_names:
- rack4
node_tags: []
- name: all-compute-nodes
critical: false
depends_on:
- compute-nodes-2
- compute-nodes-1
- spare-compute-nodes
selectors:
- node_names: []
node_labels:
- compute:true
rack_names: []
node_tags: []
- name: monitoring-nodes
critical: false
depends_on: []
selectors:
- node_names: []
node_labels: []
node_tags:
- monitoring
rack_names: []
success_criteria:
minimum_successful_nodes: 3
- name: ntp-node
critical: true
depends_on: []
selectors:
- node_names:
- node3
node_labels: []
node_tags: []
rack_names:
- rack1
success_criteria:
minimum_successful_nodes: 1
"""
_CYCLE_GROUPS_YAML = """
- name: group-a
critical: true
depends_on:
- group-c
selectors: []
- name: group-b
critical: true
depends_on:
- group-a
selectors: []
- name: group-c
critical: true
depends_on:
- group-d
selectors: []
- name: group-d
critical: true
depends_on:
- group-a
selectors: []
"""
class TestDeploymentGroupManager:
def test_basic_class(self):
dgm = DeploymentGroupManager(yaml.safe_load(_GROUPS_YAML), node_lookup)
assert dgm is not None
# topological sort doesn't guarantee a specific order.
assert dgm.get_next_group(Stage.PREPARED).name in ['ntp-node',
'monitoring-nodes']
assert len(dgm._all_groups) == 7
assert len(dgm._all_nodes) == 12
for name, group in dgm._all_groups.items():
assert name == group.name
def test_cycle_error(self):
with pytest.raises(DeploymentGroupCycleError) as ce:
DeploymentGroupManager(yaml.safe_load(_CYCLE_GROUPS_YAML),
node_lookup)
assert 'The following are involved' in str(ce)
for g in ['group-a', 'group-c', 'group-d']:
assert g in str(ce)
assert 'group-b' not in str(ce)
def test_no_next_group(self):
dgm = DeploymentGroupManager(yaml.safe_load(_GROUPS_YAML), node_lookup)
assert dgm.get_next_group(Stage.DEPLOYED) is None
def test_ordering_stages_flow_failure(self):
dgm = DeploymentGroupManager(yaml.safe_load(_GROUPS_YAML), node_lookup)
group = dgm.get_next_group(Stage.PREPARED)
if group.name == 'monitoring-nodes':
dgm.mark_group_prepared(group.name)
dgm.mark_group_deployed(group.name)
group = dgm.get_next_group(Stage.PREPARED)
if group.name == 'ntp-node':
dgm.mark_group_failed(group.name)
group = dgm.get_next_group(Stage.PREPARED)
if group and group.name == 'monitoring-nodes':
dgm.mark_group_prepared(group.name)
dgm.mark_group_deployed(group.name)
group = dgm.get_next_group(Stage.PREPARED)
# all remaining groups should be failed, so no more to prepare
for name, grp in dgm._all_groups.items():
if (name == 'monitoring-nodes'):
assert grp.stage == Stage.DEPLOYED
else:
assert grp.stage == Stage.FAILED
assert group is None
def test_deduplication(self):
"""all-compute-nodes is a duplicate of things it's dependent on, it
should have no actionable nodes"""
dgm = DeploymentGroupManager(yaml.safe_load(_GROUPS_YAML), node_lookup)
acn = dgm._all_groups['all-compute-nodes']
assert len(acn.actionable_nodes) == 0
assert len(acn.full_nodes) == 6
def test_bad_group_name_lookup(self):
dgm = DeploymentGroupManager(yaml.safe_load(_GROUPS_YAML), node_lookup)
with pytest.raises(UnknownDeploymentGroupError) as udge:
dgm.mark_group_prepared('Limburger Cheese')
assert "Group name Limburger Cheese does not refer" in str(udge)
def test_get_group_failures_for_stage_bad_input(self):
dgm = DeploymentGroupManager(yaml.safe_load(_GROUPS_YAML), node_lookup)
with pytest.raises(DeploymentGroupStageError):
dgm.get_group_failures_for_stage('group1', Stage.FAILED)
def test_get_group_failures_for_stage(self):
dgm = DeploymentGroupManager(yaml.safe_load(_GROUPS_YAML), node_lookup)
dgm._all_nodes = {
'node1': Stage.DEPLOYED,
'node2': Stage.DEPLOYED,
'node3': Stage.DEPLOYED,
'node4': Stage.DEPLOYED,
'node5': Stage.DEPLOYED,
'node6': Stage.DEPLOYED,
'node7': Stage.DEPLOYED,
'node8': Stage.DEPLOYED,
'node9': Stage.DEPLOYED,
'node10': Stage.DEPLOYED,
'node11': Stage.DEPLOYED,
'node12': Stage.DEPLOYED,
}
for group_name in dgm._all_groups:
assert not dgm.get_group_failures_for_stage(group_name,
Stage.DEPLOYED)
assert not dgm.get_group_failures_for_stage(group_name,
Stage.PREPARED)
dgm._all_nodes = {
'node1': Stage.PREPARED,
'node2': Stage.PREPARED,
'node3': Stage.PREPARED,
'node4': Stage.PREPARED,
'node5': Stage.PREPARED,
'node6': Stage.PREPARED,
'node7': Stage.PREPARED,
'node8': Stage.PREPARED,
'node9': Stage.PREPARED,
'node10': Stage.PREPARED,
'node11': Stage.PREPARED,
'node12': Stage.PREPARED,
}
for group_name in dgm._all_groups:
assert not dgm.get_group_failures_for_stage(group_name,
Stage.PREPARED)
for group_name in ['compute-nodes-1',
'monitoring-nodes',
'compute-nodes-2',
'control-nodes',
'ntp-node']:
# assert that these have a failure
assert dgm.get_group_failures_for_stage(group_name, Stage.DEPLOYED)
dgm._all_nodes = {
'node1': Stage.FAILED,
'node2': Stage.PREPARED,
'node3': Stage.FAILED,
'node4': Stage.PREPARED,
'node5': Stage.FAILED,
'node6': Stage.PREPARED,
'node7': Stage.FAILED,
'node8': Stage.PREPARED,
'node9': Stage.FAILED,
'node10': Stage.PREPARED,
'node11': Stage.FAILED,
'node12': Stage.PREPARED,
}
for group_name in dgm._all_groups:
scf = dgm.get_group_failures_for_stage(group_name,
Stage.PREPARED)
if group_name == 'monitoring-nodes':
assert scf[0] == {'criteria': 'minimum_successful_nodes',
'needed': 3,
'actual': 2}
if group_name == 'control-nodes':
assert scf[0] == {'criteria': 'percent_successful_nodes',
'needed': 100,
'actual': 50.0}
if group_name == 'ntp-node':
assert scf[0] == {'criteria': 'minimum_successful_nodes',
'needed': 1,
'actual': 0}
def test_mark_node_deployed(self):
dgm = DeploymentGroupManager(yaml.safe_load(_GROUPS_YAML), node_lookup)
dgm.mark_node_deployed('node1')
assert dgm.get_nodes(Stage.DEPLOYED) == ['node1']
def test_mark_node_prepared(self):
dgm = DeploymentGroupManager(yaml.safe_load(_GROUPS_YAML), node_lookup)
dgm.mark_node_prepared('node1')
assert dgm.get_nodes(Stage.PREPARED) == ['node1']
def test_mark_node_failed(self):
dgm = DeploymentGroupManager(yaml.safe_load(_GROUPS_YAML), node_lookup)
dgm.mark_node_failed('node1')
assert dgm.get_nodes(Stage.FAILED) == ['node1']
def test_mark_node_failed_unknown(self):
dgm = DeploymentGroupManager(yaml.safe_load(_GROUPS_YAML), node_lookup)
with pytest.raises(UnknownNodeError):
dgm.mark_node_failed('not_node')
def test_get_nodes_all(self):
dgm = DeploymentGroupManager(yaml.safe_load(_GROUPS_YAML), node_lookup)
assert set(dgm.get_nodes()) == set(
['node1', 'node2', 'node3', 'node4', 'node5', 'node6', 'node7',
'node8', 'node9', 'node10', 'node11', 'node12']
)