Add tripleo_deploy_artifacts
This module takes a list of urls of rpms or tar.gz files and applies them to a system. Change-Id: Ia1b998069a5c973813c187ea1efa9b5715a55f28
This commit is contained in:
parent
5702b7ba3d
commit
f55f218535
|
@ -0,0 +1,14 @@
|
|||
=================================
|
||||
Module - tripleo_deploy_artifacts
|
||||
=================================
|
||||
|
||||
|
||||
This module provides for the following ansible plugin:
|
||||
|
||||
* tripleo_deploy_artifacts
|
||||
|
||||
|
||||
.. ansibleautoplugin::
|
||||
:module: tripleo_ansible/ansible_plugins/modules/tripleo_deploy_artifacts.py
|
||||
:documentation: true
|
||||
:examples: true
|
|
@ -0,0 +1,149 @@
|
|||
#!/usr/bin/python
|
||||
# Copyright 2020 Red Hat, Inc.
|
||||
# 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.
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import traceback
|
||||
import urllib.request
|
||||
import yaml
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'
|
||||
}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: tripleo_deploy_artifacts
|
||||
short_description: Deploy RPM/tar.gz artifact from a URL on a system
|
||||
version_added: "2.9"
|
||||
author: "Alex Schultz (@mwhahaha)"
|
||||
description:
|
||||
- Takes a set of urls as inputs, fetches their contents and deploys them
|
||||
on the system.
|
||||
options:
|
||||
artifact_urls:
|
||||
description:
|
||||
- List of artifact urls to deploy
|
||||
required: true
|
||||
type: list
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Deploy artifacts
|
||||
tripleo_deploy_artifacts:
|
||||
artifact_urls:
|
||||
- http://example.com/foo.rpm
|
||||
- http://example.com/foo.tar.gz
|
||||
'''
|
||||
|
||||
|
||||
def _get_filetype(filename):
|
||||
cmd = "file -b " + filename
|
||||
try:
|
||||
r = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, universal_newlines=True)
|
||||
except Exception as e:
|
||||
raise Exception('Unable to determine file type: %s' & e)
|
||||
if 'RPM' in r.stdout:
|
||||
return 'rpm'
|
||||
elif 'gzip compressed data' in r.stdout:
|
||||
return 'targz'
|
||||
return 'UNKNOWN'
|
||||
|
||||
|
||||
def deploy_rpm(filename):
|
||||
rpm_filename = filename + '.rpm'
|
||||
cmd = "dnf install -y " + rpm_filename
|
||||
try:
|
||||
os.rename(filename, rpm_filename)
|
||||
_ = subprocess.run(cmd, shell=True, check=True, stderr=subprocess.PIPE,
|
||||
universal_newlines=True)
|
||||
except Exception as e:
|
||||
raise Exception('Unable to install rpm: %s' % e)
|
||||
finally:
|
||||
if os.path.exists(rpm_filename):
|
||||
os.unlink(rpm_filename)
|
||||
|
||||
|
||||
def deploy_targz(filename):
|
||||
cmd = "tar xvzf -C / " + filename
|
||||
try:
|
||||
_ = subprocess.run(cmd, shell=True, check=True, stderr=subprocess.PIPE,
|
||||
universal_newlines=True)
|
||||
except Exception as e:
|
||||
raise Exception('Unable to install tar.gz: %s' % e)
|
||||
finally:
|
||||
if os.path.exists(filename):
|
||||
os.unlink(filename)
|
||||
|
||||
|
||||
def run(module):
|
||||
results = dict(
|
||||
changed=False
|
||||
)
|
||||
|
||||
args = module.params
|
||||
urls = args.get('artifact_urls')
|
||||
tmpfile = None
|
||||
|
||||
# run command
|
||||
for url in urls:
|
||||
try:
|
||||
(tmpfile, _) = urllib.request.urlretrieve(url)
|
||||
filetype = _get_filetype(tmpfile)
|
||||
if filetype == 'rpm':
|
||||
deploy_rpm(tmpfile)
|
||||
elif filetype == 'targz':
|
||||
deploy_targz(tmpfile)
|
||||
else:
|
||||
results['failed'] = True
|
||||
results['error'] = 'Invalid file format'
|
||||
results['msg'] = ('Unable to determine file format for %s' %
|
||||
url)
|
||||
break
|
||||
results['changed'] = True
|
||||
except Exception as e:
|
||||
results['failed'] = True
|
||||
results['error'] = traceback.format_exc()
|
||||
results['msg'] = "Unhandled exception: %s" % e
|
||||
break
|
||||
finally:
|
||||
if tmpfile and os.path.exists(tmpfile):
|
||||
os.unlink(tmpfile)
|
||||
|
||||
module.exit_json(**results)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=yaml.safe_load(DOCUMENTATION)['options'],
|
||||
supports_check_mode=False,
|
||||
)
|
||||
run(module)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,148 @@
|
|||
# Copyright 2019 Red Hat, Inc.
|
||||
# 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.
|
||||
|
||||
from tripleo_ansible.ansible_plugins.modules import tripleo_deploy_artifacts
|
||||
from tripleo_ansible.tests import base as tests_base
|
||||
|
||||
import mock
|
||||
|
||||
|
||||
class TestTripleoDeployArtifacts(tests_base.TestCase):
|
||||
@mock.patch('tripleo_ansible.ansible_plugins.modules.'
|
||||
'tripleo_deploy_artifacts.deploy_targz')
|
||||
@mock.patch('tripleo_ansible.ansible_plugins.modules.'
|
||||
'tripleo_deploy_artifacts.deploy_rpm')
|
||||
@mock.patch('tripleo_ansible.ansible_plugins.modules.'
|
||||
'tripleo_deploy_artifacts._get_filetype')
|
||||
@mock.patch('urllib.request.urlretrieve')
|
||||
def test_run(self, mock_urlretrieve, mock_filetype, mock_rpm, mock_tgz):
|
||||
module = mock.MagicMock()
|
||||
module.params = {'artifact_urls': ['myrpm', 'mytgz']}
|
||||
mock_exit = mock.MagicMock()
|
||||
module.exit_json = mock_exit
|
||||
mock_filetype.side_effect = ['rpm', 'targz']
|
||||
mock_urlretrieve.side_effect = [('foo', None), ('bar', None)]
|
||||
tripleo_deploy_artifacts.run(module)
|
||||
self.assertEqual(mock_filetype.call_count, 2)
|
||||
mock_filetype.has_calls([mock.call('myrpm'), mock.call('mytgz')])
|
||||
mock_rpm.assert_called_once_with('foo')
|
||||
mock_tgz.assert_called_once_with('bar')
|
||||
mock_exit.assert_called_once_with(changed=True)
|
||||
|
||||
@mock.patch('urllib.request.urlretrieve')
|
||||
def test_run_fail(self, mock_urlretrieve):
|
||||
module = mock.MagicMock()
|
||||
module.params = {'artifact_urls': ['myrpm', 'mytgz']}
|
||||
mock_exit = mock.MagicMock()
|
||||
module.exit_json = mock_exit
|
||||
mock_urlretrieve.side_effect = Exception('meh')
|
||||
tripleo_deploy_artifacts.run(module)
|
||||
mock_exit.assert_called_once_with(changed=False, error=mock.ANY,
|
||||
failed=True,
|
||||
msg='Unhandled exception: meh')
|
||||
|
||||
@mock.patch('tripleo_ansible.ansible_plugins.modules.'
|
||||
'tripleo_deploy_artifacts._get_filetype')
|
||||
@mock.patch('urllib.request.urlretrieve')
|
||||
def test_run_unknown(self, mock_urlretrieve, mock_filetype):
|
||||
module = mock.MagicMock()
|
||||
module.params = {'artifact_urls': ['bad']}
|
||||
mock_filetype.return_value = 'UNKNOWN'
|
||||
mock_exit = mock.MagicMock()
|
||||
module.exit_json = mock_exit
|
||||
mock_urlretrieve.return_value = ('foo', None)
|
||||
tripleo_deploy_artifacts.run(module)
|
||||
mock_exit.assert_called_once_with(changed=False,
|
||||
error='Invalid file format',
|
||||
failed=True,
|
||||
msg=('Unable to determine file '
|
||||
'format for bad'))
|
||||
|
||||
@mock.patch('subprocess.run')
|
||||
def test_get_filetype_rpm(self, mock_run):
|
||||
mock_rc = mock.MagicMock()
|
||||
mock_rc.stdout = 'RPM v3.0 bin i386/x86_64 foo-0.0.1'
|
||||
mock_run.return_value = mock_rc
|
||||
self.assertEqual('rpm', tripleo_deploy_artifacts._get_filetype('foo'))
|
||||
mock_run.assert_called_once_with('file -b foo', shell=True, stderr=-1,
|
||||
stdout=-1, universal_newlines=True)
|
||||
|
||||
@mock.patch('subprocess.run')
|
||||
def test_get_filetype_targz(self, mock_run):
|
||||
mock_rc = mock.MagicMock()
|
||||
mock_rc.stdout = ('gzip compressed data, last modified: Fri Mar 13 '
|
||||
'22:10:46 2020, from Unix, original size modulo '
|
||||
'2^32 4280320')
|
||||
mock_run.return_value = mock_rc
|
||||
self.assertEqual('targz',
|
||||
tripleo_deploy_artifacts._get_filetype('foo'))
|
||||
mock_run.assert_called_once_with('file -b foo', shell=True, stderr=-1,
|
||||
stdout=-1, universal_newlines=True)
|
||||
|
||||
@mock.patch('subprocess.run')
|
||||
def test_get_filetype_unknown(self, mock_run):
|
||||
mock_rc = mock.MagicMock()
|
||||
mock_rc.stdout = 'ASCII File'
|
||||
mock_run.return_value = mock_rc
|
||||
self.assertEqual('UNKNOWN',
|
||||
tripleo_deploy_artifacts._get_filetype('foo'))
|
||||
mock_run.assert_called_once_with('file -b foo', shell=True, stderr=-1,
|
||||
stdout=-1, universal_newlines=True)
|
||||
|
||||
@mock.patch('subprocess.run')
|
||||
def test_get_filetype_fail(self, mock_run):
|
||||
mock_run.side_effect = Exception('meh')
|
||||
self.assertRaises(Exception,
|
||||
tripleo_deploy_artifacts._get_filetype,
|
||||
'foo')
|
||||
|
||||
@mock.patch('os.rename')
|
||||
@mock.patch('subprocess.run')
|
||||
def test_deploy_rpm(self, mock_run, mock_rename):
|
||||
tripleo_deploy_artifacts.deploy_rpm('foo')
|
||||
mock_run.assert_called_once_with('dnf install -y foo.rpm', check=True,
|
||||
shell=True, stderr=-1,
|
||||
universal_newlines=True)
|
||||
|
||||
@mock.patch('os.unlink')
|
||||
@mock.patch('os.path.exists')
|
||||
@mock.patch('os.rename')
|
||||
@mock.patch('subprocess.run')
|
||||
def test_deploy_rpm_fail(self, mock_run, mock_rename, mock_exists,
|
||||
mock_unlink):
|
||||
mock_run.side_effect = Exception('meh')
|
||||
mock_exists.return_value = True
|
||||
self.assertRaises(Exception,
|
||||
tripleo_deploy_artifacts.deploy_rpm,
|
||||
'foo')
|
||||
mock_unlink.assert_called_once_with('foo.rpm')
|
||||
|
||||
@mock.patch('subprocess.run')
|
||||
def test_deploy_targz(self, mock_run):
|
||||
tripleo_deploy_artifacts.deploy_targz('foo')
|
||||
mock_run.assert_called_once_with('tar xvzf -C / foo', check=True,
|
||||
shell=True, stderr=-1,
|
||||
universal_newlines=True)
|
||||
|
||||
@mock.patch('os.unlink')
|
||||
@mock.patch('os.path.exists')
|
||||
@mock.patch('subprocess.run')
|
||||
def test_deploy_targz_fail(self, mock_run, mock_exists, mock_unlink):
|
||||
mock_run.side_effect = Exception('meh')
|
||||
mock_exists.return_value = True
|
||||
self.assertRaises(Exception,
|
||||
tripleo_deploy_artifacts.deploy_targz,
|
||||
'foo')
|
||||
mock_unlink.assert_called_once_with('foo')
|
Loading…
Reference in New Issue