Skip to content

Commit

Permalink
Add base structure for testing of statuses from real cloud (#334)
Browse files Browse the repository at this point in the history
With these changes, we can simply add any result form real cloud to
`tests/unit/sample_plans` directory in yaml file formatted like this:
```yaml
plan: |
  Upgrade plan defined as string

applications:
  keystone:
    can_upgrade_to: ussuri/stable
    charm: keystone
    channel: ussuri/stable
    config:
      openstack-origin:
        value: distro
      action-managed-upgrade:
        value: true
    origin: ch
    series: focal
    subordinate_to: []
    workload_version: 17.0.1
    units:
      keystone/0:
        name: keystone/0
        machine: '0'
        workload_version: 17.0.1
        os_version: ussuri
    machines:
      '0':
        id: '0'
        apps: !!python/tuple ['keystone-ldap']
        az: az-0
```

My only two concerns are:
~~- usage of `pytest-subtests`, which result could be strange if test
failed, as you can see in current CI~~ After discussion I choose to got
with only fixture returning dictionary with file as key, and tuple value
with model and exp_plan. The model.get_applications returns applications
defined in file.
~~- these test should be more part of functional tests instead of unit
tests, or somehow exclude them from coverage report (e.g. 100% coverage
is achieved without these tests)~~ After discussions we agreed that new
tox env should be created, so I created `mocked-plans`. I also add new
jobs to run this environment to our GitHub workflow.

---------

Co-authored-by: TQ X <[email protected]>
  • Loading branch information
rgildein and agileshaw authored Apr 4, 2024
1 parent 5ac7427 commit 45950d0
Show file tree
Hide file tree
Showing 10 changed files with 383 additions and 38 deletions.
64 changes: 51 additions & 13 deletions .github/workflows/check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,53 @@ concurrency:

jobs:
lint-unit:
name: Lint checkers and unit tests
uses: canonical/bootstack-actions/.github/workflows/lint-unit.yaml@v2
strategy:
fail-fast: false
matrix:
python-version: ['3.10']
with:
python-version: ${{ matrix.python-version }}
tox-version: '<4'
name: Lint checkers and Unit tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install tox
- name: Run lint checkers
run: tox -e lint
- name: Run unit tests
run: tox -e unit
- name: Save PR number to file
run: echo ${{ github.event.number }} > PR_NUMBER.txt
- name: Archive PR number
uses: actions/upload-artifact@v4
with:
name: PR_NUMBER
path: PR_NUMBER.txt
- name: Archive code coverage results
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: ./tests/unit/report/coverage.xml

mocked-plans:
name: Mocked plans tests
needs: lint-unit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install tox
- name: Run mocked-plans tests
run: tox -e mocked-plans

snap-build:
name: Build snap package
Expand All @@ -37,7 +75,7 @@ jobs:
with:
submodules: true
- name: Setup Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install tox
Expand All @@ -53,7 +91,7 @@ jobs:
- name: Build snap
run: make build
- name: Upload the built snap as an artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: SNAP_FILE
path: charmed-openstack-upgrader.snap
Expand All @@ -68,7 +106,7 @@ jobs:
with:
submodules: true
- name: Setup Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Setup Juju 2.9/stable environment
Expand All @@ -83,7 +121,7 @@ jobs:
python -m pip install --upgrade pip
python -m pip install tox
- name: Download snap file artifact
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: SNAP_FILE
- name: Run func tests
Expand Down
2 changes: 1 addition & 1 deletion cou/utils/nova_compute.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
async def get_empty_hypervisors(units: list[Unit], model: Model) -> list[Machine]:
"""Get the empty hypervisors in the model.
:param units: all nova-compute units.
:param units: All nova-compute units.
:type units: list[Unit]
:param model: Model object
:type model: Model
Expand Down
4 changes: 0 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
# This is a template `pyproject.toml` file for snaps
# This file is managed by bootstack-charms-spec and should not be modified
# within individual snap repos. https://launchpad.net/bootstack-charms-spec

[tool.flake8]
ignore = ["C901", "D100", "D101", "D102", "D103", "W503", "W504"]
exclude = ['.eggs', '.git', '.tox', '.venv', '.build', 'build', 'report', 'docs']
Expand Down
37 changes: 37 additions & 0 deletions tests/mocked_plans/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Copyright 2023 Canonical Limited
#
# 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.

from pathlib import Path

import pytest

from cou.utils.juju_utils import Model
from tests.mocked_plans.utils import get_sample_plan


@pytest.fixture(scope="session")
def sample_plans() -> dict[str, tuple[Model, str]]:
"""Fixture that returns all sample plans in a directory.
This fixture returns a dictionary with the filename as the key and
a tuple consisting of a cou.utils.juju_utils.Model object and the
expected plan in string format as the value. The get_applications
function of this Model object returns the applications read from a
YAML file, from which the expected plan is also parsed.
"""
directory = Path(__file__).parent / "sample_plans"

yield {
sample_file.name: get_sample_plan(sample_file) for sample_file in directory.glob("*.yaml")
}
157 changes: 157 additions & 0 deletions tests/mocked_plans/sample_plans/base.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
plan: |
Upgrade cloud from 'ussuri' to 'victoria'
Verify that all OpenStack applications are in idle state
Back up MySQL databases
Control Plane principal(s) upgrade plan
Upgrade plan for 'keystone' to 'victoria'
Upgrade software packages of 'keystone' from the current APT repositories
Upgrade software packages on unit 'keystone/0'
Refresh 'keystone' to the latest revision of 'ussuri/stable'
Change charm config of 'keystone' 'action-managed-upgrade' to 'False'
Upgrade 'keystone' to the new channel: 'victoria/stable'
Change charm config of 'keystone' 'openstack-origin' to 'cloud:focal-victoria'
Wait for up to 1800s for model 'base' to reach the idle state
Verify that the workload of 'keystone' has been upgraded on units: keystone/0
Control Plane subordinate(s) upgrade plan
Upgrade plan for 'keystone-ldap' to 'victoria'
Refresh 'keystone-ldap' to the latest revision of 'ussuri/stable'
Upgrade 'keystone-ldap' to the new channel: 'victoria/stable'
Upgrading all applications deployed on machines with hypervisor.
Upgrade plan for 'az-0' to 'victoria'
Disable nova-compute scheduler from unit: 'nova-compute/0'
Upgrade software packages of 'nova-compute' from the current APT repositories
Upgrade software packages on unit 'nova-compute/0'
Refresh 'nova-compute' to the latest revision of 'ussuri/stable'
Change charm config of 'nova-compute' 'action-managed-upgrade' to 'True'
Upgrade 'nova-compute' to the new channel: 'victoria/stable'
Change charm config of 'nova-compute' 'source' to 'cloud:focal-victoria'
Upgrade plan for units: nova-compute/0
Upgrade plan for unit 'nova-compute/0'
Verify that unit 'nova-compute/0' has no VMs running
├── Pause the unit: 'nova-compute/0'
├── Upgrade the unit: 'nova-compute/0'
├── Resume the unit: 'nova-compute/0'
Enable nova-compute scheduler from unit: 'nova-compute/0'
Wait for up to 1800s for model 'base' to reach the idle state
Verify that the workload of 'nova-compute' has been upgraded on units: nova-compute/0
Remaining Data Plane principal(s) upgrade plan
Upgrade plan for 'ceph-osd' to 'victoria'
Verify that all 'nova-compute' units has been upgraded
Upgrade software packages of 'ceph-osd' from the current APT repositories
Upgrade software packages on unit 'ceph-osd/0'
Change charm config of 'ceph-osd' 'source' to 'cloud:focal-victoria'
Wait for up to 300s for app 'ceph-osd' to reach the idle state
Verify that the workload of 'ceph-osd' has been upgraded on units: ceph-osd/0
Data Plane subordinate(s) upgrade plan
Upgrade plan for 'ovn-chassis' to 'victoria'
Refresh 'ovn-chassis' to the latest revision of '22.03/stable'
applications:
keystone:
can_upgrade_to: ussuri/stable
charm: keystone
channel: ussuri/stable
config:
openstack-origin:
value: distro
action-managed-upgrade:
value: true
origin: ch
series: focal
subordinate_to: []
workload_version: 17.0.1
units:
keystone/0:
name: keystone/0
machine: '0'
workload_version: 17.0.1
os_version: ussuri
machines:
'0':
id: '0'
apps: !!python/tuple ['keystone', 'keystone-ldap']
az: az-0

keystone-ldap:
can_upgrade_to: ussuri/stable
charm: keystone-ldap
channel: ussuri/stable
config: {}
origin: ch
series: focal
subordinate_to:
- keystone
workload_version: 17.0.1
units: {}
machines:
'0':
id: '0'
apps: !!python/tuple ['keystone', 'keystone-ldap']
az: az-0

ceph-osd:
can_upgrade_to: octopus/stable
charm: ceph-osd
channel: octopus/stable
config:
source:
value: distro
origin: ch
series: focal
subordinate_to: []
workload_version: 17.0.1
units:
ceph-osd/0:
name: ceph-osd/0
machine: '2'
workload_version: 17.0.1
os_version: xena
machines:
'2':
id: '2'
apps: !!python/tuple ['ceph-osd']
az: az-0

nova-compute:
can_upgrade_to: ussuri/stable
charm: nova-compute
channel: ussuri/stable
config:
source:
value: distro
action-managed-upgrade:
value: false
origin: ch
series: focal
subordinate_to: []
workload_version: 21.0.0
units:
nova-compute/0:
name: nova-compute/0
machine: '1'
workload_version: 21.0.0
os_version: ussuri
machines:
'1':
id: '1'
apps: !!python/tuple ['nova-compute', 'ovn-chassis']
az: az-0

ovn-chassis:
can_upgrade_to: 22.03/stable
charm: ovn-chassis
channel: 22.03/stable
config:
enable-version-pinning:
value: false
origin: ch
series: focal
subordinate_to:
- nova-compute
workload_version: '22.3'
units: {}
machines:
'1':
id: '1'
apps: !!python/tuple ['nova-compute', 'ovn-chassis']
az: az-0
34 changes: 34 additions & 0 deletions tests/mocked_plans/test_base_plan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright 2023 Canonical Limited
#
# 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.
"""Test all sample plans."""
from unittest.mock import patch

import pytest

from cou.commands import CLIargs
from cou.steps.analyze import Analysis
from cou.steps.plan import generate_plan


@pytest.mark.asyncio
@patch("cou.utils.nova_compute.get_instance_count", return_value=0)
async def test_base_plan(_, sample_plans):
"""Testing the base plans."""
args = CLIargs("plan", auto_approve=True)
model, exp_plan = sample_plans["base.yaml"]

analysis_results = await Analysis.create(model)
plan = await generate_plan(analysis_results, args)

assert str(plan) == exp_plan
Loading

0 comments on commit 45950d0

Please sign in to comment.