RIC-769: Committing individual files rather than tar archive
[ric-plt/appmgr.git] / xapp_orchestrater / dev / xapp_onboarder / xapp_onboarder / repo_manager / repo_manager.py
diff --git a/xapp_orchestrater/dev/xapp_onboarder/xapp_onboarder/repo_manager/repo_manager.py b/xapp_orchestrater/dev/xapp_onboarder/xapp_onboarder/repo_manager/repo_manager.py
new file mode 100644 (file)
index 0000000..1596b55
--- /dev/null
@@ -0,0 +1,157 @@
+################################################################################
+#   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)