HelmCLI: support SOL018 Helm CLI ssh access 13/8813/2
authorZhang Rong(Jon) <rong.zhang@windriver.com>
Thu, 28 Jul 2022 16:06:41 +0000 (00:06 +0800)
committerZhang Rong(Jon) <rong.zhang@windriver.com>
Fri, 29 Jul 2022 02:15:27 +0000 (10:15 +0800)
Issue-ID: INF-280
Signed-off-by: Zhang Rong(Jon) <rong.zhang@windriver.com>
Change-Id: Ifab09cbf2f5c5c608594c9be76260c46e98ec68f

15 files changed:
.gitignore
Dockerfile
Dockerfile.localtest
charts/resources/scripts/init/o2_helmcli_start.sh [new file with mode: 0644]
charts/templates/deployment.yaml
charts/templates/service.yaml
charts/values.yaml
docker-compose.yml
o2common/config/config.py
o2ims/views/ocloud_dto.py
o2ims/views/ocloud_view.py
requirements-test.txt
requirements.txt
tests/o2app-helmcli-entry.sh [new file with mode: 0644]
tests/unit/test_ocloud.py

index e103593..6ae62d8 100644 (file)
@@ -9,3 +9,4 @@ __pycache__
 .coverage
 temp
 docs/_build/
+configs/kubeconfig_*.config
index 84ebb52..8c33fbe 100644 (file)
@@ -1,6 +1,6 @@
 FROM python:3.10-slim-buster
 
-RUN apt-get update && apt-get install -y git gcc procps vim curl
+RUN apt-get update && apt-get install -y git gcc procps vim curl ssh
 
 # in case git repo is not accessable
 # RUN mkdir -p /cgtsclient
index 3a3a7ea..a46b178 100644 (file)
@@ -1,7 +1,7 @@
 FROM python:3.10-slim-buster
 
 RUN apt-get update && apt-get install -y git gcc \
-    vim curl procps
+    vim curl procps ssh
 
 # in case git repo is not accessable
 RUN mkdir -p /cgtsclient && mkdir -p /distcloud-client
diff --git a/charts/resources/scripts/init/o2_helmcli_start.sh b/charts/resources/scripts/init/o2_helmcli_start.sh
new file mode 100644 (file)
index 0000000..006b121
--- /dev/null
@@ -0,0 +1,15 @@
+apt-get update && apt-get install ssh -y
+
+if [ -z "${HELM_USER_PASSWD}" ];
+then
+    HELM_USER_PASSWD=St8rlingX*
+fi
+useradd helm
+passwd helm << EOF
+${HELM_USER_PASSWD}
+${HELM_USER_PASSWD}
+EOF
+
+service ssh restart
+
+tail -f /dev/null
\ No newline at end of file
index 0f0b433..9da4fba 100644 (file)
@@ -130,12 +130,33 @@ spec:
               value: "1"
             - name: REDIS_HOST
               value: redis
+            - name: HELM_USER_PASSWD
+              value: {{ .Values.ocloud.HELM_USER_PASSWD }}
           command: ["/bin/bash", "/opt/o2api_start.sh"]
           volumeMounts:
             - name: scripts
               mountPath: /opt
+            - name: configs
+              mountPath: /configs
+        - name: helmcli
+          image: "{{ .Values.o2ims.image.repository }}:{{ .Values.o2ims.image.tag }}"
+          ports:
+            - containerPort: 22
+          env:
+            - name: API_HOST_EXTERNAL_FLOATING
+              value: {{ .Values.ocloud.API_HOST_EXTERNAL_FLOATING }}
+            - name: HELM_USER_PASSWD
+              value: {{ .Values.ocloud.HELM_USER_PASSWD }}
+          command: ["/bin/bash", "/opt/o2_helmcli_start.sh"]
+          volumeMounts:
+            - name: scripts
+              mountPath: /opt
+            - name: configs
+              mountPath: /configs
       volumes:
         - name: scripts
           configMap:
             name: {{ .Chart.Name }}-scripts-configmap
+        - name: configs
+          emptyDir: {}
 ---
index 990f78b..8e59597 100644 (file)
@@ -29,3 +29,19 @@ spec:
   selector:
     app: o2api
 ---
+apiVersion: v1
+kind: Service
+metadata:
+  name: helmcli
+  namespace: {{ .Values.global.namespace }}
+spec:
+  type: NodePort
+  ports:
+  - name: helmcli
+    port: 10022
+    targetPort: 22
+    nodePort: 30022
+    protocol: TCP
+  selector:
+    app: o2api
+---
\ No newline at end of file
index d58033b..c343088 100644 (file)
@@ -43,3 +43,4 @@ ocloud:
   OS_PASSWORD: ""
   K8S_KUBECONFIG: ""
   API_HOST_EXTERNAL_FLOATING: ""
+  HELM_USER_PASSWD: "St8rlingX*"
index ad3d857..ca73018 100644 (file)
@@ -19,9 +19,6 @@ services:
       - OS_AUTH_URL=${OS_AUTH_URL}
       - OS_USERNAME=${OS_USERNAME}
       - OS_PASSWORD=${OS_PASSWORD}
-      - K8S_KUBECONFIG=${K8S_KUBECONFIG}
-      - K8S_APISERVER=${K8S_APISERVER}
-      - K8S_TOKEN=${K8S_TOKEN}
       - LOGGING_CONFIG_LEVEL=DEBUG
     volumes:
       - ./configs:/configs
@@ -35,6 +32,23 @@ services:
       - /bin/sh
       - /tests/o2app-redis-entry.sh
 
+  helm_cli:
+    image: o2imsdms
+    depends_on:
+      - watcher
+    environment:
+      - LOGGING_CONFIG_LEVEL=DEBUG
+      - HELM_USER_PASSWD=St8rlingX*
+    volumes:
+      - ./configs:/configs
+      - ./helm_sdk:/helm_sdk
+      - ./tests:/tests
+    entrypoint:
+      - /bin/sh
+      - /tests/o2app-helmcli-entry.sh
+    ports:
+      - "10022:22"
+
   api:
     image: o2imsdms
     depends_on:
@@ -52,6 +66,7 @@ services:
       - OS_USERNAME=${OS_USERNAME}
       - OS_PASSWORD=${OS_PASSWORD}
       - LOGGING_CONFIG_LEVEL=DEBUG
+      - HELM_USER_PASSWD=St8rlingX*
     volumes:
       - ./configs:/configs
       - ./o2ims:/o2ims
@@ -95,31 +110,6 @@ services:
       - /bin/sh
       - /tests/o2app-watcher-entry.sh
 
-  mock_smo:
-    build:
-      context: ./mock_smo
-      dockerfile: Dockerfile
-    image: mock-smo
-    depends_on:
-      - mock_smo_redis
-    environment:
-      - API_HOST=api
-      - REDIS_HOST=mock_smo_redis
-      - MOCK_SMO_HOST=mock_smo
-      - PYTHONDONTWRITEBYTECODE=1
-      - FLASK_APP=/mock_smo/entrypoints/mock_smo.py
-      - FLASK_DEBUG=1
-      - PYTHONUNBUFFERED=1
-      - LOGGING_CONFIG_LEVEL=DEBUG
-    volumes:
-      - ./mock_smo/etc:/tmp/etc
-      - ./mock_smo/mock_smo:/mock_smo
-    entrypoint:
-      - /bin/sh
-      - /src/o2app-mock-smo.sh
-    ports:
-      - "5001:80"
-
   postgres:
     image: postgres:9.6
     environment:
@@ -132,8 +122,3 @@ services:
     image: redis:alpine
     ports:
       - "63791:6379"
-
-  mock_smo_redis:
-    image: redis:alpine
-    ports:
-      - "63792:6379"
index 8f44fa2..64bf3eb 100644 (file)
@@ -207,3 +207,17 @@ def gen_k8s_config_dict(cluster_api_endpoint, cluster_ca_cert, admin_user,
     }
 
     return data
+
+
+def get_helmcli_access():
+    host_external = os.environ.get("API_HOST_EXTERNAL_FLOATING")
+    host = "127.0.0.1" if host_external is None or host_external == '' \
+        else host_external
+    port = "10022" if host_external is None or host_external == '' \
+        else "50022"
+
+    helm_host_with_port = host+':'+port
+    helm_user = 'helm'
+    helm_pass = os.environ.get("HELM_USER_PASSWD")
+
+    return helm_host_with_port, helm_user, helm_pass
index dacfcf1..651de97 100644 (file)
@@ -159,6 +159,11 @@ class DeploymentManagerDTO:
         'admin_client_cert': fields.String(attributes='admin_client_cert'),
         'admin_client_key': fields.String(attributes='admin_client_key'),
         # 'kube_config_file': fields.String(attributes='kube_config_file')
+        'helmcli_host_with_port': fields.String(
+            attributes='helmcli_host_with_port'),
+        'helmcli_username': fields.String(attributes='helmcli_username'),
+        'helmcli_password': fields.String(attributes='helmcli_password'),
+        'helmcli_kubeconfig': fields.String(attributes='helmcli_kubeconfig'),
     })
 
     deployment_manager_get = api_ims_inventory_v1.model(
index 94d8601..83be178 100644 (file)
 #  See the License for the specific language governing permissions and
 #  limitations under the License.
 
+import filecmp
+import os.path
 import uuid
 import yaml
-import random
-import string
 from datetime import datetime
+import shutil
 
 from o2common.service import unit_of_work
 from o2ims.views.ocloud_dto import SubscriptionDTO
@@ -116,16 +117,27 @@ def deployment_manager_one(deploymentManagerId: str,
             return None
 
     profile_data = result.pop("profile", None)
-    result['profileName'] = 'default'
+    result['profileName'] = profile
 
-    if "sol018" == profile:
-        result['profileName'] = profile
+    if "default" == profile:
+        pass
+    elif "sol018" == profile:
         result['deploymentManagementServiceEndpoint'] = \
             profile_data['cluster_api_endpoint']
         result['profileData'] = profile_data
-    # elif "file" == profile and result.hasattr("profile"):
-        # p = result.pop("profile", None)
-        # result["profile"] = _gen_kube_config(deploymentManagerId, p)
+    elif "sol018_helmcli" == profile:
+        result['deploymentManagementServiceEndpoint'] = \
+            profile_data['cluster_api_endpoint']
+
+        helmcli_profile = dict()
+        helmcli_profile["helmcli_host_with_port"], helmcli_profile[
+            "helmcli_username"], helmcli_profile["helmcli_password"] = \
+            config.get_helmcli_access()
+        helmcli_profile["helmcli_kubeconfig"] = _gen_kube_config(
+            deploymentManagerId, profile_data)
+        result['profileData'] = helmcli_profile
+    else:
+        return None
 
     return result
 
@@ -141,21 +153,27 @@ def _gen_kube_config(dmId: str, kubeconfig: dict) -> dict:
     )
 
     # Generate a random key for tmp kube config file
-    letters = string.ascii_uppercase
-    random_key = ''.join(random.choice(letters) for i in range(10))
+    # letters = string.ascii_uppercase
+    # random_key = ''.join(random.choice(letters) for i in range(10))
+    name_key = dmId[:8]
 
     # Get datetime of now as tag of the tmp file
     current_time = datetime.now().strftime("%Y%m%d%H%M%S")
-    tmp_file_name = random_key + "_" + current_time
+    tmp_file_name = 'kubeconfig_' + name_key + "_" + current_time
+    kube_config_name = 'kubeconfig_' + name_key + '.config'
 
     # write down the yaml file of kubectl into tmp folder
-    with open('/tmp/kubeconfig_' + tmp_file_name, 'w') as file:
+    with open('/tmp/' + tmp_file_name, 'w') as file:
         yaml.dump(data, file)
 
-    kubeconfig["kube_config_file"] = config.get_api_url() + \
-        config.get_o2dms_api_base() + "/" + dmId + "/download/" + tmp_file_name
+    # generate the kube config file if not exist or update the file if it
+    # changes
+    if not os.path.exists('/configs/' + kube_config_name) or not \
+            filecmp.cmp('/tmp/'+tmp_file_name, '/configs/'+kube_config_name):
+        shutil.move(os.path.join('/tmp', tmp_file_name),
+                    os.path.join('/configs', kube_config_name))
 
-    return kubeconfig
+    return '/configs/'+kube_config_name
 
 
 def subscriptions(uow: unit_of_work.AbstractUnitOfWork):
index da66309..c5ae7f0 100644 (file)
@@ -12,4 +12,4 @@ mock
 tenacity
 
 # -e git+https://opendev.org/starlingx/distcloud-client.git@master#egg=distributedcloud-client&subdirectory=distributedcloud-client
-# -e git+https://opendev.org/starlingx/config.git@master#egg=cgtsclient&subdirectory=sysinv/cgts-client/cgts-client
+# -e git+https://opendev.org/starlingx/config.git@master#egg=cgtsclient&subdirectory=sysinv/cgts-client/cgts-client
\ No newline at end of file
index f1fc633..3d03fdd 100644 (file)
@@ -8,10 +8,13 @@ PyYAML>=5.4.1
 
 Cython>=3.0a1
 
-
 httplib2
 babel
 PrettyTable<0.8,>=0.7.2
 
 retry
-ruamel.yaml==0.17.17
\ No newline at end of file
+ruamel.yaml==0.17.17
+
+# https://github.com/python-restx/flask-restx/issues/460
+# Workaround for this issue
+werkzeug<=2.1.2
\ No newline at end of file
diff --git a/tests/o2app-helmcli-entry.sh b/tests/o2app-helmcli-entry.sh
new file mode 100644 (file)
index 0000000..e00a00b
--- /dev/null
@@ -0,0 +1,15 @@
+apt-get update && apt-get install ssh -y
+
+if [ -z "${HELM_USER_PASSWD}" ];
+then
+    HELM_USER_PASSWD=St8rlingX
+fi
+useradd helm
+passwd helm << EOF
+${HELM_USER_PASSWD}
+${HELM_USER_PASSWD}
+EOF
+
+service ssh restart
+
+tail -f /dev/null
\ No newline at end of file
index dbfa65f..461fa41 100644 (file)
@@ -303,6 +303,16 @@ def test_view_deployment_manager_one(mock_uow):
     assert str(deployment_manager_res.get(
         "profileName")) == profileName
 
+    # profile wrong name
+    profileName = 'wrong_profile'
+    session.return_value.query.return_value.filter_by.return_value.first.\
+        return_value.serialize.return_value['profile'] = {
+            "cluster_api_endpoint": cluster_endpoint
+        }
+    deployment_manager_res = ocloud_view.deployment_manager_one(
+        deployment_manager_id1, uow, profile=profileName)
+    assert deployment_manager_res is None
+
 
 def test_view_subscriptions(mock_uow):
     session, uow = mock_uow