Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: supported series computation needed for juju 3.6 #1170

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 5 additions & 11 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ jobs:
timeout-minutes: 150
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python:
# We will reduce the workload to 3.10 to
Expand All @@ -108,13 +109,7 @@ jobs:
- "3.3/stable"
- "3.4/stable"
- "3.5/stable"
# A bunch of tests fail with juju.errors.JujuError: base: [email protected]/stable
# * test_subordinate_units
# * test_destroy_unit
# * test_ssh
# * ...
# - "3.6/beta"
continue-on-error: false # ultimately fail a run if one of the matrix combinations fails
- "3.6/beta"
steps:
- name: Check out code
uses: actions/checkout@v4
Expand Down Expand Up @@ -158,14 +153,14 @@ jobs:
- name: Run integration
# Force one single concurrent test
run: tox -e integration
continue-on-error: true # don't fail early, let other matrix combinations get tested

integration-quarantine:
name: Quarantined Integration Tests
needs: [lint, unit-tests]
timeout-minutes: 150
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python:
- "3.10"
Expand All @@ -174,10 +169,10 @@ jobs:
- "3.3/stable"
- "3.4/stable"
- "3.5/stable"
continue-on-error: false # ultimately fail the run if one of the matrix combinations fails
- "3.6/beta"
steps:
- name: Check out code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
Expand All @@ -189,4 +184,3 @@ jobs:
juju-channel: ${{ matrix.juju }}
- name: Run integration
run: tox -e integration-quarantine
continue-on-error: true # don't fail early, let other matrix combinations get tested
28 changes: 16 additions & 12 deletions juju/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from collections import defaultdict
from functools import partial
from pathlib import Path
from typing import Sequence
import base64
from pyasn1.type import univ, char
from pyasn1.codec.der.encoder import encode
Expand Down Expand Up @@ -457,27 +458,30 @@ def get_base_from_origin_or_channel(origin_or_channel, series=None):
return client.Base(channel=channel, name=os_name)


def series_for_charm(requested_series, supported_series):
def series_for_charm(requested_series: str, supported_series: Sequence[str]):
"""series_for_charm takes a requested series and a list of series supported by a
charm and returns the series which is relevant.
If the requested series is empty, then the first supported series is used,
otherwise the requested series is validated against the supported series.

supported_series follows Juju API convention (best first):
- LTS in reverse chronological order
- then regular releases in reverse chronological order
"""
if len(supported_series) == 1 and supported_series[0] == '':
raise JujuError("invalid supported series reported by charm : ['']")
if len(supported_series) == 0:
if requested_series == '':
raise JujuError("missing series")
if not supported_series and requested_series:
return requested_series
elif not requested_series:
raise JujuError("missing series")
elif not requested_series:
# use the charm default
return supported_series[0]

# use the charm default
if requested_series == '':
return supported_series[-1]

for s in supported_series:
if requested_series == s:
return requested_series
raise JujuError(f'requested series {requested_series} is not among the supported series {supported_series}')
if requested_series in supported_series:
return requested_series
else:
raise JujuError(f'requested series {requested_series} is not among the supported series {supported_series}')


def user_requested(series_arg, supported_series, force):
Expand Down
4 changes: 4 additions & 0 deletions tests/integration/test_crossmodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ async def test_remove_saas():
async def test_relate_with_offer():
# pytest.skip('Revise: intermittent problem with the remove_saas call')
async with base.CleanModel() as model_1:
assert model_1._info
if str(model_1._info.agent_version) < "3.4.3":
pytest.skip("postgresql charm requires Juju 3.4.3 or later")

application = await model_1.deploy(
'postgresql',
application_name='postgresql',
Expand Down
24 changes: 24 additions & 0 deletions tests/integration/test_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,10 @@ async def test_deploy_bundle_with_storage_constraint():
bundle_path = INTEGRATION_TEST_DIR / 'bundle' / 'bundle-with-storage-constraint.yaml'

async with base.CleanModel() as model:
assert model._info
if str(model._info.agent_version) < "3.4.3":
pytest.skip("bundle/postgresql charm requires Juju 3.4.3 or later")

await model.deploy(bundle_path)
await wait_for_bundle(model, bundle_path)
storage = await model.list_storage()
Expand All @@ -237,6 +241,10 @@ async def test_deploy_local_charm():
@base.bootstrapped
async def test_deploy_charm_assumes():
async with base.CleanModel() as model:
assert model._info
if str(model._info.agent_version) < "3.4.3":
pytest.skip("postgresql charm requires Juju 3.4.3 or later")

await model.deploy('postgresql', channel='14/edge')


Expand Down Expand Up @@ -298,6 +306,10 @@ async def test_deploy_bundle():
@pytest.mark.bundle
async def test_deploy_local_bundle_with_overlay_multi():
async with base.CleanModel() as model:
assert model._info
if str(model._info.agent_version) < "3.4.3":
pytest.skip("bundle/postgresql charm requires Juju 3.4.3 or later")

bundle_with_overlay_path = OVERLAYS_DIR / 'bundle-with-overlay-multi.yaml'
await model.deploy(bundle_with_overlay_path)

Expand All @@ -312,6 +324,10 @@ async def test_deploy_local_bundle_with_overlay_multi():
@pytest.mark.skip("Always fails -- investigate bundle charms")
async def test_deploy_bundle_with_overlay_as_argument():
async with base.CleanModel() as model:
assert model._info
if str(model._info.agent_version) < "3.4.3":
pytest.skip("bundle/postgresql charm requires Juju 3.4.3 or later")

overlay_path = OVERLAYS_DIR / 'test-overlay.yaml'

await model.deploy('juju-qa-bundle-test', overlays=[overlay_path])
Expand All @@ -333,6 +349,10 @@ async def test_deploy_bundle_with_overlay_as_argument():
@pytest.mark.bundle
async def test_deploy_bundle_with_multi_overlay_as_argument():
async with base.CleanModel() as model:
assert model._info
if str(model._info.agent_version) < "3.4.3":
pytest.skip("bundle/postgresql charm requires Juju 3.4.3 or later")

overlay_path = OVERLAYS_DIR / 'test-multi-overlay.yaml'

await model.deploy('juju-qa-bundle-test', overlays=[overlay_path])
Expand Down Expand Up @@ -381,6 +401,10 @@ async def test_deploy_local_charm_folder_symlink():
@base.bootstrapped
async def test_deploy_from_ch_channel_revision_success():
async with base.CleanModel() as model:
assert model._info
if str(model._info.agent_version) < "3.4.3":
pytest.skip("postgresql charm requires Juju 3.4.3 or later")

# Ensure we're able to resolve charm these with channel and revision,
# or channel without revision (note that revision requires channel,
# but not vice versa)
Expand Down
24 changes: 24 additions & 0 deletions tests/integration/test_secrets.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
@pytest.mark.bundle
async def test_add_secret():
async with base.CleanModel() as model:
assert model._info
if str(model._info.agent_version) < "3.3.0":
pytest.skip("Juju too old, need Secrets API v2")

secret = await model.add_secret(name='my-apitoken', data_args=['token=34ae35facd4'])
assert secret.startswith('secret:')

Expand All @@ -28,6 +32,10 @@ async def test_list_secrets():
charm_path = TESTS_DIR / 'charm-secret/charm-secret_ubuntu-22.04-amd64.charm'

async with base.CleanModel() as model:
assert model._info
if str(model._info.agent_version) < "3.3.0":
pytest.skip("Juju too old, need Secrets API v2")

await model.deploy(str(charm_path))
assert 'charm-secret' in model.applications
await model.wait_for_idle(status="active")
Expand All @@ -42,6 +50,10 @@ async def test_list_secrets():
@pytest.mark.bundle
async def test_update_secret():
async with base.CleanModel() as model:
assert model._info
if str(model._info.agent_version) < "3.3.0":
pytest.skip("Juju too old, need Secrets API v2")

secret = await model.add_secret(name='my-apitoken', data_args=['token=34ae35facd4'])
assert secret.startswith('secret:')

Expand All @@ -56,6 +68,10 @@ async def test_update_secret():
@pytest.mark.bundle
async def test_remove_secret():
async with base.CleanModel() as model:
assert model._info
if str(model._info.agent_version) < "3.3.0":
pytest.skip("Juju too old, need Secrets API v2")

secret = await model.add_secret(name='my-apitoken', data_args=['token=34ae35facd4'])
assert secret.startswith('secret:')

Expand All @@ -69,6 +85,10 @@ async def test_remove_secret():
@pytest.mark.bundle
async def test_grant_secret():
async with base.CleanModel() as model:
assert model._info
if str(model._info.agent_version) < "3.3.0":
pytest.skip("Juju too old, need Secrets API v2")

secret = await model.add_secret(name='my-apitoken', data_args=['token=34ae35facd4'])
assert secret.startswith('secret:')

Expand All @@ -81,6 +101,10 @@ async def test_grant_secret():
@pytest.mark.bundle
async def test_revoke_secret():
async with base.CleanModel() as model:
assert model._info
if str(model._info.agent_version) < "3.3.0":
pytest.skip("Juju too old, need Secrets API v2")

secret = await model.add_secret(name='my-apitoken', data_args=['token=34ae35facd4'])
assert secret.startswith('secret:')
await model.revoke_secret('my-apitoken', 'ubuntu')
2 changes: 1 addition & 1 deletion tests/unit/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def test_model_config(self):
assert series_selector(model_config=mconf, supported_series=['jammy']) == 'jammy'

def test_charm_list_series(self):
assert series_selector(supported_series=['focal', 'jammy']) == 'jammy'
assert series_selector(supported_series=["jammy", "focal"]) == "jammy"

def test_return_lts(self):
assert series_selector() == DEFAULT_SUPPORTED_LTS
Expand Down
Loading