System application - tarfile download support

This commit adds support for tarfile download from http/ftp server.

Tests conducted:
  - successful upload, apply and removal of stx-openstack app on
    premise.
  - successful upload and apply of stx-openstack app downloaded
    from http & ftp test servers.
  - common failed tests (both remote and local tarfile): wrong
    tarfile extension, no helm charts, corrupted manifest, wrong
    chart tarfile extension, multiple manifests.
  - download specific failed tests: timeout error, http error,
    url error.

Story: 2003908
Task: 28054

Change-Id: If02d0d84e6de4bc395da28bef914b991d24b045b
Signed-off-by: Tee Ngo <Tee.Ngo@windriver.com>
This commit is contained in:
Tee Ngo 2018-12-03 14:32:14 -05:00
parent 977112e99e
commit 16c4db1bb9
5 changed files with 370 additions and 122 deletions

View File

@ -6,6 +6,7 @@
# SPDX-License-Identifier: Apache-2.0
#
import os
import re
from cgtsclient.common import utils
from cgtsclient import exc
@ -23,6 +24,24 @@ def _print_reminder_msg(app_name):
"application-show %s' to view the current progress." % app_name)
def _is_url(url_str):
# Django url validation patterns
r = re.compile(
r'^(?:http|ftp)s?://' # http:// or https://
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)' # domain...
r'+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|'
r'localhost|' # localhost...
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
r'(?::\d+)?' # optional port
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
url = r.match(url_str)
if url:
return True
else:
return False
def do_application_list(cc, args):
"""List all containerized applications"""
apps = cc.app.list()
@ -50,15 +69,17 @@ def do_application_show(cc, args):
def do_application_upload(cc, args):
"""Upload application Helm chart(s) and manifest"""
tarfile = args.tarfile
if not os.path.isabs(tarfile):
tarfile = os.path.join(os.getcwd(), tarfile)
if not os.path.isfile(tarfile):
raise exc.CommandError("Error: Tar file %s does not exist" % tarfile)
if not tarfile.endswith('.tgz') and not tarfile.endswith('.tar.gz'):
raise exc.CommandError("Error: File %s has unrecognizable tar file "
"extension. Supported extensions are: .tgz "
"and .tar.gz" % tarfile)
if not _is_url(tarfile):
if not os.path.isabs(tarfile):
tarfile = os.path.join(os.getcwd(), tarfile)
if not os.path.isfile(tarfile):
raise exc.CommandError("Error: Tar file %s does not exist" % tarfile)
if not tarfile.endswith('.tgz') and not tarfile.endswith('.tar.gz'):
raise exc.CommandError("Error: File %s has unrecognizable tar file "
"extension. Supported extensions are: .tgz "
"and .tar.gz" % tarfile)
data = {'name': args.name,
'tarfile': tarfile}

View File

@ -8,14 +8,11 @@ import os
import pecan
from pecan import rest
import shutil
import subprocess
import tempfile
import wsme
from wsme import types as wtypes
import wsmeext.pecan as wsme_pecan
import yaml
from contextlib import contextmanager
from sysinv import objects
from sysinv.api.controllers.v1 import base
@ -141,7 +138,7 @@ class KubeAppController(rest.RestController):
"{}.".format(os.path.basename(app_tarfile))))
# If checksum file is included in the tarball, verify its contents.
if not self._verify_checksum(app_path):
if not cutils.verify_checksum(app_path):
raise wsme.exc.ClientSideError(_(
"Application-upload rejected: checksum validation failed."))
@ -171,60 +168,15 @@ class KubeAppController(rest.RestController):
"Application-upload rejected: both application name and tar "
"file must be specified."))
def _verify_checksum(self, app_path):
rc = True
for file in os.listdir(app_path):
if file.endswith('.md5'):
cwd = os.getcwd()
os.chdir(app_path)
with open(os.devnull, "w") as fnull:
try:
subprocess.check_call(['md5sum', '-c', file],
stdout=fnull, stderr=fnull)
LOG.info("Checksum file is included and validated.")
except subprocess.CalledProcessError as e:
LOG.exception(e)
rc = False
finally:
os.chdir(cwd)
return rc
# Do we need to make the inclusion of md5 file a hard requirement?
LOG.info("Checksum file is not included, skipping validation.")
return rc
def _find_manifest_file(self, app_path):
# It is expected that there is only one manifest file
# per application and the file exists at top level of
# the application path.
mfiles = cutils.find_manifest_file(app_path)
def _is_manifest(yaml_file):
with open(yaml_file, 'r') as f:
docs = yaml.load_all(f)
for doc in docs:
try:
if "armada/Manifest" in doc['schema']:
manifest_name = doc['metadata']['name']
return manifest_name, yaml_file
except KeyError:
# Could be some other yaml files
pass
return None, None
mfiles = []
for file in os.listdir(app_path):
if file.endswith('.yaml'):
yaml_file = os.path.join(app_path, file)
try:
mname, mfile = _is_manifest(yaml_file)
if mfile:
mfiles.append((mname, mfile))
except Exception as e:
# Included yaml file is corrupted
LOG.exception(e)
raise wsme.exc.ClientSideError(_(
"Application-upload rejected: failed to process "
"file {}.".format(file)))
if mfiles is None:
raise wsme.exc.ClientSideError(_(
"Application-upload rejected: manifest file is corrupted."))
if mfiles:
if len(mfiles) == 1:
@ -272,7 +224,15 @@ class KubeAppController(rest.RestController):
except exception.KubeAppNotFound:
pass
mname, mfile = self._check_tarfile(name, tarfile)
if not cutils.is_url(tarfile):
mname, mfile = self._check_tarfile(name, tarfile)
else:
# For tarfile that is downloaded remotely, defer the checksum, manifest
# and tarfile content validations to sysinv-conductor as download can
# take some time depending on network traffic, target server and file
# size.
mname = constants.APP_MANIFEST_NAME_PLACEHOLDER
mfile = constants.APP_TARFILE_NAME_PLACEHOLDER
# Create a database entry and make an rpc async request to upload
# the application

View File

@ -1230,6 +1230,7 @@ NETWORK_CONFIG_LOCK_FILE = os.path.join(
SYSINV_USERNAME = "sysinv"
SYSINV_GRPNAME = "sysinv"
SYSINV_WRS_GRPNAME = "wrs_protected"
# SSL configuration
CERT_TYPE_SSL = 'ssl'
@ -1473,7 +1474,8 @@ K8S_RBD_PROV_STOR_CLASS_NAME = 'general'
# Kubernetes application section #
##################################
# Working paths
APP_INSTALL_PATH = '/scratch/apps'
APP_INSTALL_ROOT_PATH = '/scratch'
APP_INSTALL_PATH = APP_INSTALL_ROOT_PATH + '/apps'
APP_SYNCED_DATA_PATH = os.path.join(tsc.PLATFORM_PATH, 'armada', tsc.SW_VERSION)
# State constants
@ -1500,12 +1502,17 @@ APP_PROGRESS_DELETE_MANIFEST = 'deleting application manifest'
APP_PROGRESS_DOWNLOAD_IMAGES = 'retrieving docker images'
APP_PROGRESS_EXTRACT_TARFILE = 'extracting application tar file'
APP_PROGRESS_GENERATE_OVERRIDES = 'generating application overrides'
APP_PROGRESS_TARFILE_DOWNLOAD = 'downloading tarfile'
APP_PROGRESS_VALIDATE_UPLOAD_CHARTS = 'validating and uploading charts'
# Node label operation constants
LABEL_ASSIGN_OP = 'assign'
LABEL_REMOVE_OP = 'remove'
# Placeholder constants
APP_MANIFEST_NAME_PLACEHOLDER = 'manifest-placeholder'
APP_TARFILE_NAME_PLACEHOLDER = 'tarfile-placeholder'
# Default node labels
CONTROL_PLANE_LABEL = 'openstack-control-plane=enabled'
COMPUTE_NODE_LABEL = 'openstack-compute-node=enabled'

View File

@ -48,6 +48,7 @@ import tempfile
import time
import uuid
import wsme
import yaml
from eventlet.green import subprocess
from eventlet import greenthread
@ -1795,12 +1796,20 @@ def get_files_matching(path, pattern):
for file in files if file.endswith(pattern)]
def extract_tarfile(target_dir, tarfile):
def extract_tarfile(target_dir, tarfile, demote_user=False):
with open(os.devnull, "w") as fnull:
try:
subprocess.check_call(['tar', '-xf', tarfile, '-m', '--no-same-owner',
'--no-same-permissions', '-C', target_dir],
stdout=fnull, stderr=fnull)
if demote_user:
tarcmd_str = 'tar -xf ' + tarfile + ' -m --no-same-owner ' +\
'--no-same-permissions -C ' + target_dir
cmd = ['su', '-s', '/bin/bash', constants.SYSINV_USERNAME,
'-c', tarcmd_str]
else:
cmd = ['tar', '-xf', tarfile, '-m', '--no-same-owner',
'--no-same-permissions', '-C', target_dir]
subprocess.check_call(cmd, stdout=fnull, stderr=fnull)
return True
except subprocess.CalledProcessError as e:
LOG.error("Error while extracting tarfile %s: %s" % (tarfile, e))
@ -1817,3 +1826,74 @@ def is_openstack_installed(dbapi):
return False
except exception.KubeAppNotFound:
return False
def is_url(url_str):
# Django URL validation patterns
r = re.compile(
r'^(?:http|ftp)s?://' # http:// or https://
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)' # domain...
r'+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|'
r'localhost|' # localhost...
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
r'(?::\d+)?' # optional port
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
url = r.match(url_str)
if url:
return True
else:
return False
def verify_checksum(path):
""" Find and validate the checksum file in a given directory. """
rc = True
for f in os.listdir(path):
if f.endswith('.md5'):
cwd = os.getcwd()
os.chdir(path)
with open(os.devnull, "w") as fnull:
try:
subprocess.check_call(['md5sum', '-c', f],
stdout=fnull, stderr=fnull)
LOG.info("Checksum file is included and validated.")
except Exception as e:
LOG.exception(e)
rc = False
finally:
os.chdir(cwd)
return rc
LOG.info("Checksum file is not included, skipping validation.")
return rc
def find_manifest_file(path):
""" Find all manifest files in a given directory. """
def _is_manifest(yaml_file):
with open(yaml_file, 'r') as f:
docs = yaml.load_all(f)
for doc in docs:
try:
if "armada/Manifest" in doc['schema']:
manifest_name = doc['metadata']['name']
return manifest_name, yaml_file
except KeyError:
# Could be some other yaml files
pass
return None, None
mfiles = []
for file in os.listdir(path):
if file.endswith('.yaml'):
yaml_file = os.path.join(path, file)
try:
mname, mfile = _is_manifest(yaml_file)
if mfile:
mfiles.append((mname, mfile))
except Exception as e:
# Included yaml file is corrupted
LOG.exception(e)
return None
return mfiles

View File

@ -12,9 +12,9 @@
import docker
import grp
import os
import pwd
import re
import shutil
import stat
import subprocess
import threading
import time
@ -35,6 +35,7 @@ from sysinv.helm import common
from sysinv.helm import helm
# Log and config
LOG = logging.getLogger(__name__)
kube_app_opts = [
cfg.StrOpt('armada_image_tag',
@ -44,13 +45,56 @@ kube_app_opts = [
]
CONF = cfg.CONF
CONF.register_opts(kube_app_opts)
ARMADA_CONTAINER_NAME = 'armada_service'
MAX_DOWNLOAD_THREAD = 20
INSTALLATION_TIMEOUT = 3600
# Constants
APPLY_SEARCH_PATTERN = 'Processing Chart,'
DELETE_SEARCH_PATTERN = 'Deleting release'
ARMADA_CONTAINER_NAME = 'armada_service'
ARMADA_MANIFEST_APPLY_SUCCESS_MSG = 'Done applying manifest'
CONTAINER_ABNORMAL_EXIT_CODE = 137
DELETE_SEARCH_PATTERN = 'Deleting release'
INSTALLATION_TIMEOUT = 3600
MAX_DOWNLOAD_THREAD = 20
TARFILE_DOWNLOAD_CONNECTION_TIMEOUT = 60
TARFILE_TRANSFER_CHUNK_SIZE = 1024 * 512
# Helper functions
def generate_armada_manifest_filename(app_name, manifest_filename):
return os.path.join('/manifests', app_name + '-' + manifest_filename)
def generate_armada_manifest_filename_abs(app_name, manifest_filename):
return os.path.join(constants.APP_SYNCED_DATA_PATH,
app_name + '-' + manifest_filename)
def generate_manifest_filename_abs(app_name, manifest_filename):
return os.path.join(constants.APP_INSTALL_PATH,
app_name, manifest_filename)
def generate_images_filename_abs(app_name):
return os.path.join(constants.APP_SYNCED_DATA_PATH,
app_name + '-images.yaml')
def create_app_path(path):
uid = pwd.getpwnam(constants.SYSINV_USERNAME).pw_uid
gid = os.getgid()
if not os.path.exists(constants.APP_INSTALL_PATH):
os.makedirs(constants.APP_INSTALL_PATH)
os.chown(constants.APP_INSTALL_PATH, uid, gid)
os.makedirs(path)
os.chown(path, uid, gid)
def get_app_install_root_path_ownership():
uid = os.stat(constants.APP_INSTALL_ROOT_PATH).st_uid
gid = os.stat(constants.APP_INSTALL_ROOT_PATH).st_gid
return (uid, gid)
Chart = namedtuple('Chart', 'name namespace')
@ -75,12 +119,15 @@ class AppOperator(object):
if app.system_app and app.status != constants.APP_UPLOAD_FAILURE:
self._remove_chart_overrides(app.armada_mfile_abs)
os.unlink(app.armada_mfile_abs)
os.unlink(app.imgfile_abs)
if os.path.exists(app.armada_mfile_abs):
os.unlink(app.armada_mfile_abs)
if os.path.exists(app.imgfile_abs):
os.unlink(app.imgfile_abs)
if os.path.exists(app.path):
shutil.rmtree(app.path)
except OSError as e:
LOG.exception(e)
LOG.error(e)
def _update_app_status(self, app, new_status=None, new_progress=None):
""" Persist new app status """
@ -94,46 +141,151 @@ class AppOperator(object):
with self._lock:
app.update_status(new_status, new_progress)
def _abort_operation(self, app, operation):
def _abort_operation(self, app, operation,
progress=constants.APP_PROGRESS_ABORTED):
if (app.status == constants.APP_UPLOAD_IN_PROGRESS):
self._update_app_status(app, constants.APP_UPLOAD_FAILURE,
constants.APP_PROGRESS_ABORTED)
progress)
elif (app.status == constants.APP_APPLY_IN_PROGRESS):
self._update_app_status(app, constants.APP_APPLY_FAILURE,
constants.APP_PROGRESS_ABORTED)
progress)
elif (app.status == constants.APP_REMOVE_IN_PROGRESS):
self._update_app_status(app, constants.APP_REMOVE_FAILURE,
constants.APP_PROGRESS_ABORTED)
progress)
LOG.error("Application %s aborted!." % operation)
def _extract_tarfile(self, app):
def _handle_extract_failure():
def _download_tarfile(self, app):
from six.moves.urllib.request import urlopen
from six.moves.urllib.error import HTTPError
from six.moves.urllib.error import URLError
from socket import timeout as socket_timeout
try:
import urlparse
except ImportError:
from urllib2 import urlparse
def _handle_download_failure(reason):
raise exception.KubeAppUploadFailure(
name=app.name,
reason="failed to extract tarfile content.")
try:
# One time set up of install path per controller
if not os.path.isdir(constants.APP_INSTALL_PATH):
os.makedirs(constants.APP_INSTALL_PATH)
reason=reason)
try:
remote_file = urlopen(
app.tarfile, timeout=TARFILE_DOWNLOAD_CONNECTION_TIMEOUT)
try:
remote_filename = remote_file.info()['Content-Disposition']
except KeyError:
remote_filename = os.path.basename(
urlparse.urlsplit(remote_file.url).path)
filename_avail = True if (remote_filename is None or
remote_filename == '') else False
if filename_avail:
if (not remote_filename.endswith('.tgz') and
not remote_filename.endswith('.tar.gz')):
reason = app.tarfile + ' has unrecognizable tar file ' + \
'extension. Supported extensions are: .tgz and .tar.gz.'
_handle_download_failure(reason)
return None
filename = '/tmp/' + remote_filename
else:
filename = '/tmp/' + app.name + '.tgz'
with open(filename, 'wb') as dest:
shutil.copyfileobj(remote_file, dest, TARFILE_TRANSFER_CHUNK_SIZE)
return filename
except HTTPError as err:
LOG.error(err)
reason = 'failed to download tarfile ' + app.tarfile + \
', error code = ' + str(err.code)
_handle_download_failure(reason)
except URLError as err:
LOG.error(err)
reason = app.tarfile + ' is unreachable.'
_handle_download_failure(reason)
except shutil.Error as err:
LOG.error(err)
err_file = os.path.basename(filename) if filename_avail else app.tarfile
reason = 'failed to process tarfile ' + err_file
_handle_download_failure(reason)
except socket_timeout as e:
LOG.error(e)
reason = 'failed to download tarfile ' + app.tarfile + \
', connection timed out.'
_handle_download_failure(reason)
def _extract_tarfile(self, app):
def _handle_extract_failure(
reason='failed to extract tarfile content.'):
raise exception.KubeAppUploadFailure(
name=app.name,
reason=reason)
def _find_manifest_file(app_path):
mfiles = cutils.find_manifest_file(app_path)
if mfiles is None:
_handle_extract_failure('manifest file is corrupted.')
if mfiles:
if len(mfiles) == 1:
return mfiles[0]
else:
_handle_extract_failure(
'tarfile contains more than one manifest file.')
else:
_handle_extract_failure('manifest file is missing.')
orig_uid, orig_gid = get_app_install_root_path_ownership()
try:
# One time set up of Armada manifest path for the system
if not os.path.isdir(constants.APP_SYNCED_DATA_PATH):
os.makedirs(constants.APP_SYNCED_DATA_PATH)
if not os.path.isdir(app.path):
os.makedirs(app.path)
if not cutils.extract_tarfile(app.path, app.tarfile):
create_app_path(app.path)
# Temporarily change /scratch group ownership to wrs_protected
os.chown(constants.APP_INSTALL_ROOT_PATH, orig_uid,
grp.getgrnam(constants.SYSINV_WRS_GRPNAME).gr_gid)
# Extract the tarfile as sysinv user
if not cutils.extract_tarfile(app.path, app.tarfile, demote_user=True):
_handle_extract_failure()
if app.downloaded_tarfile:
if not cutils.verify_checksum(app.path):
_handle_extract_failure('checksum validation failed.')
mname, mfile = _find_manifest_file(app.path)
# Save the official manifest file info. They will be persisted
# in the next status update
app.regenerate_manifest_filename(mname, os.path.basename(mfile))
if os.path.isdir(app.charts_dir):
if len(os.listdir(app.charts_dir)) == 0:
_handle_extract_failure('tarfile contains no Helm charts.')
tar_filelist = cutils.get_files_matching(app.charts_dir,
'.tgz')
if not tar_filelist:
reason = 'tarfile contains no Helm charts of expected ' + \
'file extension (.tgz).'
_handle_extract_failure(reason)
for p, f in tar_filelist:
if not cutils.extract_tarfile(p, os.path.join(p, f)):
if not cutils.extract_tarfile(
p, os.path.join(p, f), demote_user=True):
_handle_extract_failure()
except OSError as e:
LOG.error(e)
_handle_extract_failure()
finally:
os.chown(constants.APP_INSTALL_ROOT_PATH, orig_uid, orig_gid)
def _get_image_tags_by_path(self, path):
""" Mine the image tags from values.yaml files in the chart directory,
@ -218,6 +370,9 @@ class AppOperator(object):
images_to_download = self._get_image_tags_by_path(app.path)
if not images_to_download:
# TODO(tngo): We may want to support the deployment of apps that
# set up resources only in the future. In which case, generate
# an info log and let it advance to the next step.
raise exception.KubeAppUploadFailure(
name=app.name,
reason="charts specify no docker images.")
@ -300,15 +455,21 @@ class AppOperator(object):
charts = [os.path.join(r, f)
for r, f in cutils.get_files_matching(app.charts_dir, '.tgz')]
with open(os.devnull, "w") as fnull:
for chart in charts:
try:
orig_uid, orig_gid = get_app_install_root_path_ownership()
try:
# Temporarily change /scratch group ownership to wrs_protected
os.chown(constants.APP_INSTALL_ROOT_PATH, orig_uid,
grp.getgrnam(constants.SYSINV_WRS_GRPNAME).gr_gid)
with open(os.devnull, "w") as fnull:
for chart in charts:
subprocess.check_call(['helm-upload', chart], env=env,
stdout=fnull, stderr=fnull)
LOG.info("Helm chart %s uploaded" % os.path.basename(chart))
except Exception as e:
raise exception.KubeAppUploadFailure(
name=app.name, reason=str(e))
except Exception as e:
raise exception.KubeAppUploadFailure(
name=app.name, reason=str(e))
finally:
os.chown(constants.APP_INSTALL_ROOT_PATH, orig_uid, orig_gid)
def _validate_labels(self, labels):
expr = re.compile(r'[a-z0-9]([-a-z0-9]*[a-z0-9])')
@ -596,14 +757,28 @@ class AppOperator(object):
LOG.info("Application (%s) upload started." % app.name)
try:
app.tarfile = tarfile
if cutils.is_url(app.tarfile):
self._update_app_status(
app, new_progress=constants.APP_PROGRESS_TARFILE_DOWNLOAD)
downloaded_tarfile = self._download_tarfile(app)
if downloaded_tarfile is None:
self._abort_operation(app, constants.APP_UPLOAD_OP)
else:
app.tarfile = downloaded_tarfile
app.downloaded_tarfile = True
# Full extraction of application tarball at /scratch/apps.
# Manifest file is placed under /opt/platform/armada
# which is managed by drbd-sync and visible to Armada.
self._update_app_status(
app, new_progress=constants.APP_PROGRESS_EXTRACT_TARFILE)
orig_mode = stat.S_IMODE(os.lstat("/scratch").st_mode)
app.tarfile = tarfile
self._extract_tarfile(app)
with self._lock:
self._extract_tarfile(app)
shutil.copy(app.mfile_abs, app.armada_mfile_abs)
if not self._docker.make_armada_request('validate', app.armada_mfile):
@ -613,19 +788,18 @@ class AppOperator(object):
app, new_progress=constants.APP_PROGRESS_VALIDATE_UPLOAD_CHARTS)
if os.path.isdir(app.charts_dir):
self._validate_helm_charts(app)
# Temporarily allow read and execute access to /scratch so www
# user can upload helm charts
os.chmod('/scratch', 0o755)
self._upload_helm_charts(app)
with self._lock:
self._upload_helm_charts(app)
self._save_images_list(app)
self._update_app_status(app, constants.APP_UPLOAD_SUCCESS)
LOG.info("Application (%s) upload completed." % app.name)
except exception.KubeAppUploadFailure as e:
LOG.exception(e)
self._abort_operation(app, constants.APP_UPLOAD_OP, str(e))
except Exception as e:
LOG.exception(e)
self._abort_operation(app, constants.APP_UPLOAD_OP)
finally:
os.chmod('/scratch', orig_mode)
def perform_app_apply(self, rpc_app):
"""Process application install request
@ -779,32 +953,28 @@ class AppOperator(object):
self.charts_dir = os.path.join(self.path, 'charts')
self.images_dir = os.path.join(self.path, 'images')
self.tarfile = None
self.downloaded_tarfile = False
self.system_app =\
(self._kube_app.get('name') == constants.HELM_APP_OPENSTACK)
self.armada_mfile =\
os.path.join('/manifests', self._kube_app.get('name') + "-" +
self._kube_app.get('manifest_file'))
self.armada_mfile_abs =\
os.path.join(constants.APP_SYNCED_DATA_PATH,
self._kube_app.get('name') + "-" +
self._kube_app.get('manifest_file'))
self.mfile_abs =\
os.path.join(constants.APP_INSTALL_PATH,
self._kube_app.get('name'),
self._kube_app.get('manifest_file'))
self.imgfile_abs =\
os.path.join(constants.APP_SYNCED_DATA_PATH,
self._kube_app.get('name') + "-images.yaml")
self.armada_mfile = generate_armada_manifest_filename(
self._kube_app.get('name'),
self._kube_app.get('manifest_file'))
self.armada_mfile_abs = generate_armada_manifest_filename_abs(
self._kube_app.get('name'),
self._kube_app.get('manifest_file'))
self.mfile_abs = generate_manifest_filename_abs(
self._kube_app.get('name'),
self._kube_app.get('manifest_file'))
self.imgfile_abs = generate_images_filename_abs(
self._kube_app.get('name'))
self.charts = []
@property
def name(self):
return self._kube_app.get('name')
@property
def mfile(self):
return self._kube_app.get('manifest_file')
@property
def status(self):
return self._kube_app.get('status')
@ -819,6 +989,16 @@ class AppOperator(object):
self._kube_app.progress = new_progress
self._kube_app.save()
def regenerate_manifest_filename(self, new_mname, new_mfile):
self._kube_app.manifest_name = new_mname
self._kube_app.manifest_file = new_mfile
self.armada_mfile = generate_armada_manifest_filename(
self.name, new_mfile)
self.armada_mfile_abs = generate_armada_manifest_filename_abs(
self.name, new_mfile)
self.mfile_abs = generate_manifest_filename_abs(
self.name, new_mfile)
class DockerHelper(object):
""" Utility class to encapsulate Docker related operations """