################################################################################ # Copyright (c) 2020 AT&T Intellectual Property. # # # # 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 yaml import json from xapp_onboarder.server import settings import logging import requests import time from requests.adapters import HTTPAdapter from requests.packages.urllib3.util.retry import Retry log = logging.getLogger(__name__) def requests_retry_session(retries=3, backoff_factor=0.3, status_forcelist=(500, 502, 504, 400, 401, 409), session=None,): session = session or requests.Session() retry = Retry( total=retries, read=retries, connect=retries, backoff_factor=backoff_factor, status_forcelist=status_forcelist, ) adapter = HTTPAdapter(max_retries=retry) session.mount('http://', adapter) session.mount('https://', adapter) return session class RepoManagerError(Exception): def __init__(self, message, status_code): # Call the base class constructor with the parameters it needs super().__init__(message) self.status_code = status_code class repoManager(): def __init__(self, repo_url): self.repo_url = repo_url self.__is_repo_ready__ = False log.debug("Initialize connection to helm chart repo at "+self.repo_url) t0 = time.time() self.retry_session = requests_retry_session() try: response = self.retry_session.get(self.repo_url, timeout=settings.HTTP_TIME_OUT) except Exception as err: t1 = time.time() log.error('Failed to connect to helm chart repo ' + self.repo_url + ' after ' + str( settings.HTTP_RETRY) + ' retries and ' + str(t1 - t0) + ' seconds. (Caused by: ' + err.__class__.__name__ + ')') else: self.__is_repo_ready__ = True def is_repo_ready(self): return self.__is_repo_ready__ def get_index(self): try: response = self.retry_session.get(self.repo_url +'/index.yaml', timeout=settings.HTTP_TIME_OUT) except Exception as err: raise RepoManagerError("Get helm repo index failed. (Caused by: " + str(err)+")", 500) else: if response.status_code != 200: raise RepoManagerError("Get helm repo index failed. Helm repo return status code: {}, {}".format(response.status_code, response.content.decode("utf-8"))) return yaml.load(response.content, Loader=yaml.FullLoader) def upload_chart(self, xapp): xapp_chart_index = self.get_index() found_xapp = False for chart in xapp_chart_index.get('entries', {}).get(xapp.chart_name, []): if chart['version'] == xapp.chart_version: found_xapp = True if found_xapp: if settings.ALLOW_REDEPLOY: self.delete_chart(xapp) else: raise RepoManagerError("Upload helm chart failed. Redeploy xApp helm chart is not allowed.", 400) headers = {'Content-Type': 'application/json'} chart_package_path = xapp.chart_workspace_path + '/' + xapp.chart_name + '-' + xapp.chart_version + '.tgz' with open(chart_package_path, mode='rb') as filereader: fileContent = filereader.read() try: response = self.retry_session.post(self.repo_url +'/api/charts', headers=headers, data=fileContent, timeout=settings.HTTP_TIME_OUT) except Exception as err: raise RepoManagerError("Upload helm chart failed. (Caused by: " + str(err) + ")", 500) else: if response.status_code != 201: raise RepoManagerError("Upload helm chart failed. Helm repo return status code: "+ str(response.status_code) +" "+ response.content.decode("utf-8"), response.status_code) def delete_chart(self, xapp): headers = {'Content-Type': 'application/json'} try: response = self.retry_session.delete(self.repo_url +'/api/charts/' + xapp.chart_name + '/' + xapp.chart_version, headers=headers, timeout=settings.HTTP_TIME_OUT) except Exception as err: raise RepoManagerError("Delete helm chart failed." + str(err), 500) else: if response.status_code != 200: response_dict = json.loads(response.content) if xapp.chart_name+'-'+xapp.chart_version+'.tgz' not in response_dict["error"]: raise RepoManagerError("Delete helm chart failed. Helm repo return status code:" + str(response.status_code) +" "+ response.content.decode("utf-8"),response.status_code) def get_xapp_list(self, xapp_chart_name=None): request_path = self.repo_url+'/api/charts' if xapp_chart_name: request_path = request_path +'/' + xapp_chart_name try: response = self.retry_session.get(request_path, timeout=settings.HTTP_TIME_OUT) except Exception as err: raise RepoManagerError("Get xApp charts list failed. (Caused by: " + str(err)+")", 500) else: if response.status_code != 200: raise RepoManagerError("Get xApp charts list failed. Helm repo return status code: "+ str(response.status_code) +" "+ response.content.decode("utf-8"), response.status_code) return json.loads(response.content) def download_xapp_chart(self, xapp_chart_name, version): request_path = self.repo_url+'/charts/'+xapp_chart_name+'-'+version+'.tgz' try: response = self.retry_session.get(request_path, timeout=settings.HTTP_TIME_OUT) except Exception as err: raise RepoManagerError("Download helm chart failed. (Caused by: " + str(err)+")", 500) else: if response.status_code != 200: raise RepoManagerError("Download helm chart failed. Helm repo return status code: "+ str(response.status_code) +" "+ response.content.decode("utf-8"), response.status_code) return response.content repo_manager = repoManager(settings.CHART_REPO_URL)