Merge pull request #1 from openstack-charmers/github-workflow

Github workflow
This commit is contained in:
Liam Young
2022-02-05 07:55:55 +00:00
committed by GitHub
9 changed files with 116 additions and 126 deletions

27
.github/workflows/tox.yaml vendored Normal file
View File

@@ -0,0 +1,27 @@
name: Python package
on:
- push
- pull_request
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.8, 3.9]
steps:
- uses: actions/checkout@v1
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install tox tox-gh-actions
- name: Lint with tox
run: tox -e pep8
- name: Test with tox
run: tox -e py${{ matrix.python-version }}

View File

@@ -1,5 +1,3 @@
# Learn more about charmcraft.yaml configuration at:
# https://juju.is/docs/sdk/charmcraft-config
type: "charm"
bases:
- build-on:
@@ -12,3 +10,8 @@ parts:
charm:
build-packages:
- git
- libffi-dev
- libssl-dev
charm-python-packages:
- setuptools < 58
- cryptography < 3.4

View File

@@ -35,7 +35,6 @@ to get the relevant information from the relation data.
import json
import uuid
import logging
from ops.relation import ConsumerBase
from ops.framework import (
StoredState,
@@ -53,7 +52,7 @@ LIBAPI = 0
# Increment this PATCH version before using `charmcraft publish-lib` or reset
# to 0 if you are raising the major API version
LIBPATCH = 1
LIBPATCH = 2
logger = logging.getLogger(__name__)

View File

@@ -1,6 +1,5 @@
# ops >= 1.2.0
ops
jinja2
git+https://github.com/canonical/operator@2875e73e#egg=ops
git+https://opendev.org/openstack/charm-ops-openstack#egg=ops_openstack
git+https://github.com/openstack-charmers/advanced-sunbeam-openstack#egg=advanced_sunbeam_openstack
lightkube

View File

@@ -5,19 +5,11 @@ This charm provide Placement services as part of an OpenStack deployment
"""
import logging
from typing import List
from ops.framework import StoredState
from ops.main import main
import advanced_sunbeam_openstack.cprocess as sunbeam_cprocess
import advanced_sunbeam_openstack.charm as sunbeam_charm
import advanced_sunbeam_openstack.core as sunbeam_core
import advanced_sunbeam_openstack.relation_handlers as sunbeam_rhandlers
import advanced_sunbeam_openstack.config_contexts as sunbeam_ctxts
from charms.observability_libs.v0.kubernetes_service_patch \
import KubernetesServicePatch
logger = logging.getLogger(__name__)
@@ -30,14 +22,8 @@ class PlacementOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
wsgi_admin_script = '/usr/bin/placement-wsgi-api'
wsgi_public_script = '/usr/bin/placement-wsgi-api'
def __init__(self, framework):
super().__init__(framework)
self.service_patcher = KubernetesServicePatch(
self,
[
('public', self.default_public_ingress_port),
]
)
db_sync_cmds = [
['sudo', '-u', 'placement', 'placement-manage', 'db', 'sync']]
@property
def service_conf(self) -> str:
@@ -69,28 +55,6 @@ class PlacementOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
def default_public_ingress_port(self):
return 8778
def _do_bootstrap(self):
"""
Starts the appropriate services in the order they are needed.
If the service has not yet been bootstrapped, then this will
1. Create the database
"""
super()._do_bootstrap()
try:
container = self.unit.get_container(self.wsgi_container_name)
logger.info("Syncing database...")
out = sunbeam_cprocess.check_output(
container,
[
'sudo', '-u', 'placement',
'placement-manage', 'db', 'sync'],
service_name='placement-db-sync',
timeout=180)
logging.debug(f'Output from database sync: \n{out}')
except sunbeam_cprocess.ContainerProcessError:
logger.exception('Failed to bootstrap')
self._state.bootstrapped = False
return
class PlacementWallabyOperatorCharm(PlacementOperatorCharm):

View File

@@ -1,66 +0,0 @@
# Copyright 2022 liam
# See LICENSE file for licensing details.
#
# Learn more about testing at: https://juju.is/docs/sdk/testing
import unittest
from unittest.mock import Mock
from charm import SunbeamPlacementOperatorCharm
from ops.model import ActiveStatus
from ops.testing import Harness
class TestCharm(unittest.TestCase):
def setUp(self):
self.harness = Harness(SunbeamPlacementOperatorCharm)
self.addCleanup(self.harness.cleanup)
self.harness.begin()
def test_config_changed(self):
self.assertEqual(list(self.harness.charm._stored.things), [])
self.harness.update_config({"thing": "foo"})
self.assertEqual(list(self.harness.charm._stored.things), ["foo"])
def test_action(self):
# the harness doesn't (yet!) help much with actions themselves
action_event = Mock(params={"fail": ""})
self.harness.charm._on_fortune_action(action_event)
self.assertTrue(action_event.set_results.called)
def test_action_fail(self):
action_event = Mock(params={"fail": "fail this"})
self.harness.charm._on_fortune_action(action_event)
self.assertEqual(action_event.fail.call_args, [("fail this",)])
def test_httpbin_pebble_ready(self):
# Check the initial Pebble plan is empty
initial_plan = self.harness.get_container_pebble_plan("httpbin")
self.assertEqual(initial_plan.to_yaml(), "{}\n")
# Expected plan after Pebble ready with default config
expected_plan = {
"services": {
"httpbin": {
"override": "replace",
"summary": "httpbin",
"command": "gunicorn -b 0.0.0.0:80 httpbin:app -k gevent",
"startup": "enabled",
"environment": {"thing": "🎁"},
}
},
}
# Get the httpbin container from the model
container = self.harness.model.unit.get_container("httpbin")
# Emit the PebbleReadyEvent carrying the httpbin container
self.harness.charm.on.httpbin_pebble_ready.emit(container)
# Get the plan now we've run PebbleReady
updated_plan = self.harness.get_container_pebble_plan("httpbin").to_dict()
# Check we've got the plan we expected
self.assertEqual(expected_plan, updated_plan)
# Check the service was started
service = self.harness.model.unit.get_container("httpbin").get_service("httpbin")
self.assertTrue(service.is_running())
# Ensure we set an ActiveStatus with no message
self.assertEqual(self.harness.model.unit.status, ActiveStatus())

22
tox.ini
View File

@@ -35,26 +35,16 @@ whitelist_externals =
passenv = HOME TERM CS_* OS_* TEST_*
deps = -r{toxinidir}/test-requirements.txt
[testenv:py35]
basepython = python3.5
# python3.5 is irrelevant on a focal+ charm.
commands = /bin/true
[testenv:py36]
basepython = python3.6
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
[testenv:py37]
basepython = python3.7
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
[testenv:py38]
[testenv:py3.8]
basepython = python3.8
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
[testenv:py3.9]
basepython = python3.9
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
[testenv:py3]
basepython = python3
deps = -r{toxinidir}/requirements.txt

View File

@@ -0,0 +1,74 @@
#!/usr/bin/env python3
# Copyright 2021 Canonical Ltd.
#
# 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 mock
import sys
sys.path.append('lib') # noqa
sys.path.append('src') # noqa
import charm
import advanced_sunbeam_openstack.test_utils as test_utils
class _PlacementWallabyOperatorCharm(charm.PlacementWallabyOperatorCharm):
def __init__(self, framework):
self.seen_events = []
self.render_calls = []
super().__init__(framework)
def _log_event(self, event):
self.seen_events.append(type(event).__name__)
def renderer(self, containers, container_configs, template_dir,
openstack_release, adapters):
self.render_calls.append(
(
containers,
container_configs,
template_dir,
openstack_release,
adapters))
def configure_charm(self, event):
super().configure_charm(event)
self._log_event(event)
class TestPlacementOperatorCharm(test_utils.CharmTestCase):
PATCHES = []
@mock.patch(
'charms.observability_libs.v0.kubernetes_service_patch.'
'KubernetesServicePatch')
def setUp(self, mock_patch):
self.container_calls = {
'push': {},
'pull': [],
'remove_path': []}
super().setUp(charm, self.PATCHES)
self.harness = test_utils.get_harness(
_PlacementWallabyOperatorCharm,
container_calls=self.container_calls)
self.addCleanup(self.harness.cleanup)
self.harness.begin()
def test_pebble_ready_handler(self):
self.assertEqual(self.harness.charm.seen_events, [])
self.harness.container_pebble_ready('placement-api')
self.assertEqual(self.harness.charm.seen_events, ['PebbleReadyEvent'])