From 6e49e5a1fbd6c95abedfb3e5e4f6b215b2f0c205 Mon Sep 17 00:00:00 2001 From: Lin Yang Date: Fri, 6 Jan 2017 15:55:50 -0800 Subject: [PATCH] Fix execute db_manager init failed Previously, `db_manager init` command failed because pbr generated script will not pass sys.argv as argument to main function. So fix it. And improve etcd_db.init_etcd_db to make it check whether each directory already exists in etcd at first. If it exist and it's not directory, delete and re-create it. Change-Id: I7d9980e0a1b38d988fe8144d0cbd618ac12f5498 Closes-Bug: #1654638 --- README.rst | 7 ++- valence/cmd/db_manager.py | 10 ++-- valence/db/etcd_db.py | 13 ++++- valence/tests/unit/db/test_etcd_db.py | 78 +++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 7 deletions(-) mode change 100644 => 100755 valence/cmd/db_manager.py create mode 100644 valence/tests/unit/db/test_etcd_db.py diff --git a/README.rst b/README.rst index 66d227a..c676a4c 100644 --- a/README.rst +++ b/README.rst @@ -55,7 +55,12 @@ Valence installation 6. Initialize etcd database - ``$ sudo db_manager init`` + ``$ db_manager init`` + + Note: The TypeError exception "TypeError: NoneType object is not callable" + is caused by known python-etcd bug, which will not impact this db init + functionality. + https://github.com/jplana/python-etcd/issues/190 7. Start valence service diff --git a/valence/cmd/db_manager.py b/valence/cmd/db_manager.py old mode 100644 new mode 100755 index e1d476a..b394d28 --- a/valence/cmd/db_manager.py +++ b/valence/cmd/db_manager.py @@ -16,24 +16,24 @@ import logging import sys -from valence.db.etcd_db import init_etcd_db +from valence.db import etcd_db LOG = logging.getLogger(__name__) def init(): - init_etcd_db() + etcd_db.init_etcd_db() def migrate(): pass -def main(argv): - if argv[1] == 'init': +def main(): + if sys.argv[1] == 'init': LOG.info("initialize database") init() if __name__ == '__main__': - main(sys.argv) + sys.exit(main()) diff --git a/valence/db/etcd_db.py b/valence/db/etcd_db.py index fed83e2..9fcbd11 100644 --- a/valence/db/etcd_db.py +++ b/valence/db/etcd_db.py @@ -12,7 +12,6 @@ # License for the specific language governing permissions and limitations # under the License. - import etcd from valence import config @@ -29,4 +28,16 @@ etcd_client = etcd.Client(config.etcd_host, config.etcd_port) def init_etcd_db(): """initialize etcd database""" for directory in etcd_directories: + try: + if not etcd_client.read(directory).dir: + # This directory name already exists, but is regular file, not + # directory, so delete it. + etcd_client.delete(directory) + else: + # This directory already exists, so skip + continue + except etcd.EtcdKeyNotFound: + # This directory name don't exist + pass + etcd_client.write(directory, None, dir=True, append=True) diff --git a/valence/tests/unit/db/test_etcd_db.py b/valence/tests/unit/db/test_etcd_db.py new file mode 100644 index 0000000..74c449c --- /dev/null +++ b/valence/tests/unit/db/test_etcd_db.py @@ -0,0 +1,78 @@ +# copyright (c) 2016 Intel, Inc. +# +# 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 unittest + +import etcd + +from valence.db import etcd_db + + +class TestDBInit(unittest.TestCase): + + def setUp(self): + self.backup_directories = etcd_db.etcd_directories + etcd_db.etcd_directories = ['/test'] + + def tearDown(self): + etcd_db.etcd_directories = self.backup_directories + + @mock.patch('etcd.Client.delete') + @mock.patch('etcd.Client.write') + @mock.patch('etcd.Client.read') + def test_init_db(self, mock_etcd_read, mock_etcd_write, mock_etcd_delete): + """Test init DB when no corresponding dir exists""" + + # Directory don't exist in etcd db + mock_etcd_read.side_effect = etcd.EtcdKeyNotFound + + etcd_db.init_etcd_db() + + mock_etcd_read.assert_called_with('/test') + mock_etcd_write.assert_called_with( + '/test', None, dir=True, append=True) + mock_etcd_delete.assert_not_called() + + @mock.patch('etcd.Client.delete') + @mock.patch('etcd.Client.write') + @mock.patch('etcd.Client.read') + def test_init_db_with_existing_key(self, mock_etcd_read, mock_etcd_write, + mock_etcd_delete): + """Test init DB when a regular key with same name already exists""" + + # A regular key with same name already exists + mock_etcd_read.return_value = mock.Mock(dir=False) + + etcd_db.init_etcd_db() + + mock_etcd_read.assert_called_with('/test') + mock_etcd_write.assert_called_with( + '/test', None, dir=True, append=True) + mock_etcd_delete.assert_called_with('/test') + + @mock.patch('etcd.Client.delete') + @mock.patch('etcd.Client.write') + @mock.patch('etcd.Client.read') + def test_init_db_with_existing_dir(self, mock_etcd_read, mock_etcd_write, + mock_etcd_delete): + """Test init DB when a dir with same name already exists""" + + # A dir with same name already exists + mock_etcd_read.return_value = mock.Mock(dir=True) + + etcd_db.init_etcd_db() + + mock_etcd_read.assert_called_with('/test') + mock_etcd_write.assert_not_called() + mock_etcd_delete.assert_not_called()