diff --git a/openstack_releases/series_status.py b/openstack_releases/series_status.py new file mode 100644 index 0000000000..dad456aa28 --- /dev/null +++ b/openstack_releases/series_status.py @@ -0,0 +1,85 @@ +# All 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. +"""Class for reading series status information. +""" + +import collections.abc +import os.path + +from openstack_releases import yamlutils + + +class Phase(object): + + def __init__(self, data): + self._data = data + self.status = data['status'] + self.date = data['date'] + + +class Series(object): + + def __init__(self, data): + self._data = data + self.name = data['name'] + self.status = data['status'] + self.initial_release = data['initial-release'] + + @property + def next_phase(self): + try: + return Phase(self._data['next-phase']) + except KeyError: + return None + + @property + def eol_date(self): + return self._data.get('eol-date', None) + + +class SeriesStatus(collections.abc.Mapping): + + def __init__(self, raw_data): + self._raw_data = raw_data + self._data = self._organize_data(raw_data) + + @classmethod + def from_directory(cls, root_dir): + raw_data = cls._load_series_status_data(root_dir) + return cls(raw_data) + + @staticmethod + def _load_series_status_data(root_dir): + filename = os.path.join(root_dir, 'deliverables', 'series_status.yaml') + with open(filename, 'r', encoding='utf-8') as f: + return yamlutils.loads(f.read()) + + @staticmethod + def _organize_data(raw_data): + organized = { + s['name']: Series(s) + for s in raw_data + } + return organized + + # Mapping API + + def __len__(self): + return len(self._data) + + def __iter__(self): + return iter(self._data) + + def __getitem__(self, series): + return self._data[series] diff --git a/openstack_releases/tests/test_series_status.py b/openstack_releases/tests/test_series_status.py new file mode 100644 index 0000000000..f3ddcad650 --- /dev/null +++ b/openstack_releases/tests/test_series_status.py @@ -0,0 +1,103 @@ +# All 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. + +import datetime +import os +import os.path +import textwrap + +import fixtures +from oslotest import base + +from openstack_releases import series_status +from openstack_releases import yamlutils + + +class TestConstructSeriesStatus(base.BaseTestCase): + + _body = textwrap.dedent(''' + - name: rocky + status: development + initial-release: 2018-08-30 + next-phase: + status: maintained + date: 2018-08-30 + - name: queens + status: maintained + initial-release: 2018-02-28 + next-phase: + status: extended maintenance + date: 2019-08-25 + ''') + + def setUp(self): + super().setUp() + self.tmpdir = self.useFixture(fixtures.TempDir()).path + deliv_dir = os.path.join(self.tmpdir, 'deliverables') + os.mkdir(deliv_dir) + with open(os.path.join(deliv_dir, 'series_status.yaml'), + 'w', encoding='utf-8') as f: + f.write(self._body) + + def test_init(self): + data = yamlutils.loads(self._body) + status = series_status.SeriesStatus(data) + self.assertIn('rocky', status) + + def test_from_directory(self): + status = series_status.SeriesStatus.from_directory(self.tmpdir) + self.assertIn('rocky', status) + + +class TestSeries(base.BaseTestCase): + + def setUp(self): + super().setUp() + + def test_next_phase(self): + s = series_status.Series({ + 'name': 'rocky', + 'status': 'development', + 'initial-release': datetime.date(2018, 8, 30), + 'next-phase': { + 'status': 'maintained', + 'date': datetime.date(2018, 8, 30), + }, + }) + self.assertIsNotNone(s.next_phase) + + def test_no_next_phase(self): + s = series_status.Series({ + 'name': 'rocky', + 'status': 'development', + 'initial-release': datetime.date(2018, 8, 30), + }) + self.assertIsNone(s.next_phase) + + def test_eol_date(self): + s = series_status.Series({ + 'name': 'icehouse', + 'status': 'end of life', + 'initial-release': datetime.date(2014, 4, 17), + 'eol-date': datetime.date(2015, 7, 2), + }) + self.assertIsNotNone(s.eol_date) + + def test_no_eol_date(self): + s = series_status.Series({ + 'name': 'austin', + 'status': 'end of life', + 'initial-release': datetime.date(2010, 10, 21), + }) + self.assertIsNone(s.eol_date)