Update helm plugin source code 11/55511/2
authorjh245g <jh245g@att.com>
Wed, 27 Jun 2018 18:50:33 +0000 (14:50 -0400)
committerJun (Nicolas) Hu <jh245g@att.com>
Wed, 27 Jun 2018 19:01:20 +0000 (19:01 +0000)
Change-Id: I1689d8d915c8f18a3e8230dcabb33413a2b9043e
Issue-ID: CCSDK-322
Signed-off-by: jh245g <jh245g@att.com>
15 files changed:
helm/.travis.yml [new file with mode: 0644]
helm/LICENSE [new file with mode: 0644]
helm/README.md [new file with mode: 0644]
helm/circle.yml [new file with mode: 0644]
helm/dev-requirements.txt [new file with mode: 0644]
helm/helm-type.yaml [new file with mode: 0644]
helm/plugin/__init__.py [new file with mode: 0644]
helm/plugin/tasks.py [new file with mode: 0644]
helm/plugin/tests/__init__.py [new file with mode: 0644]
helm/plugin/tests/blueprint/blueprint.yaml [new file with mode: 0644]
helm/plugin/tests/blueprint/plugin/test_plugin.yaml [new file with mode: 0644]
helm/plugin/tests/test_plugin.py [new file with mode: 0644]
helm/plugin/workflows.py [new file with mode: 0644]
helm/setup.py [new file with mode: 0644]
helm/tox.ini [new file with mode: 0644]

diff --git a/helm/.travis.yml b/helm/.travis.yml
new file mode 100644 (file)
index 0000000..edb3cf0
--- /dev/null
@@ -0,0 +1,11 @@
+sudo: true
+language: python
+python:
+  - "2.7"
+env:
+    - TOX_ENV=flake8
+    - TOX_ENV=py27
+install:
+    - pip install tox
+script:
+    - tox -e $TOX_ENV
diff --git a/helm/LICENSE b/helm/LICENSE
new file mode 100644 (file)
index 0000000..ad410e1
--- /dev/null
@@ -0,0 +1,201 @@
+Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   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.
\ No newline at end of file
diff --git a/helm/README.md b/helm/README.md
new file mode 100644 (file)
index 0000000..4128fba
--- /dev/null
@@ -0,0 +1,12 @@
+cloudify-plugin-template
+========================
+
+[![Build Status](https://travis-ci.org/cloudify-cosmo/cloudify-plugin-template.svg?branch=master)](https://travis-ci.org/cloudify-cosmo/cloudify-plugin-template)
+
+ONAP Helm Plugin.
+This Plugin will utilize the ONAP helm chart to install, uninstall, upgrade, and rollback ONAP components.
+
+## Documents
+
+See https://wiki.onap.org/display/DW/Introduction+of+Helm+Plugin
+
diff --git a/helm/circle.yml b/helm/circle.yml
new file mode 100644 (file)
index 0000000..4245150
--- /dev/null
@@ -0,0 +1,25 @@
+checkout:
+  post:
+    - >
+      if [ -n "$CI_PULL_REQUEST" ]; then
+        PR_ID=${CI_PULL_REQUEST##*/}
+        git fetch origin +refs/pull/$PR_ID/merge:
+        git checkout -qf FETCH_HEAD
+      fi
+
+dependencies:
+  pre:
+    - pyenv local 2.7.9 
+  override:
+    - pip install tox
+
+test:
+  override:
+    - tox -e flake8
+    - tox -e py27
+
+deployment:
+  release:
+    tag: /.*/
+    commands:
+      - (true)
diff --git a/helm/dev-requirements.txt b/helm/dev-requirements.txt
new file mode 100644 (file)
index 0000000..48bc5ba
--- /dev/null
@@ -0,0 +1,6 @@
+pyyaml==3.12
+
+-e git+https://github.com/cloudify-cosmo/cloudify-dsl-parser@4.1.1-build#egg=cloudify-dsl-parser==4.1.1
+-e git+https://github.com/cloudify-cosmo/cloudify-rest-client@4.1.1-build#egg=cloudify-rest-client==4.1.1
+-e git+https://github.com/cloudify-cosmo/cloudify-plugins-common@4.1.1-build#egg=cloudify-plugins-common==4.1.1
+nose
diff --git a/helm/helm-type.yaml b/helm/helm-type.yaml
new file mode 100644 (file)
index 0000000..2e79849
--- /dev/null
@@ -0,0 +1,129 @@
+# ============LICENSE_START==========================================
+# ===================================================================
+# Copyright (c) 2017 AT&T
+#
+# 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.
+#============LICENSE_END============================================
+
+plugins:
+  helm-plugin:
+    executor: central_deployment_agent
+    package_name: onap-helm-plugin
+    package_version: 2.2.0
+
+node_types:
+
+  onap.nodes.component:
+    derived_from: cloudify.nodes.Root
+    properties:
+      tiller-server-ip:
+        description: IP of tiller server
+        type: string
+      tiller-server-port:
+        default: local
+        description: Port of tiller server
+        type: string
+      chart-repo-url:
+        default: local
+        description: helm chart repo url
+        type: string
+      component-name:
+        description: onap component string
+        type: string
+      chart-version:
+        description: helm chart version
+        type: string
+      config-dir:
+        description: config file dir
+        default: '/opt/manager/resources/'
+        type: string
+      namespace:
+        description: k8s namespace
+        default: onap
+      config:
+        description: String format config file
+        type: string
+        default: ''
+      config-url:
+        description: String format config file url
+        type: string
+        default: ''
+      runtime-config:
+        default: ''
+        description: String format json object. To save the runtime config generate from other nodes.
+      tls-enable:
+        description: enable helm TSL
+        type: boolean
+        default: false
+      ca:
+        description: value of ca.pem
+        type: string
+        default: ''
+      cert:
+        description:  value of cert.pem
+        type: string
+        default: ''
+      key:
+        description:  value of key.pem
+        type: string
+        default: ''
+      stable-repo-url:
+        description:   URL for stable repository
+        type: string
+        default: 'https://kubernetes-charts.storage.googleapis.com'
+
+      # This part should handel by Blueprint not plugin
+      # the default docker values points to ONAP nexus3 docker repo.
+      # If need point to other private docker repo you can overrite it in blueprint node templates .
+#      docker-server:
+#        description: Private Docker Registry FQDN.
+#        default: nexus3.onap.org:10001
+#      docker-username:
+#        description: Docker username.
+#        default: docker
+#      docker-password:
+#        description: Docker password.
+#        default: docker
+
+    interfaces:
+      cloudify.interfaces.lifecycle:
+        configure: helm-plugin.plugin.tasks.config
+        start: helm-plugin.plugin.tasks.start
+        stop: helm-plugin.plugin.tasks.stop
+        upgrade: helm-plugin.plugin.tasks.upgrade
+        rollback: helm-plugin.plugin.tasks.rollback
+
+
+workflows:
+  upgrade:
+    mapping: helm-plugin.plugin.workflows.upgrade
+    parameters:
+      node_instance_id:
+        description: The id of the node-instance that you want to modify.
+      config_json:
+        description: The changes to the new config json
+        default: ''
+      config_json_url:
+        description: The changes to the new config json url
+        default: ''
+      chartVersion:
+        description: chart version
+      chartRepo:
+        description: chart repo url
+  rollback:
+    mapping: helm-plugin.plugin.workflows.rollback
+    parameters:
+      node_instance_id:
+        description: The id of the node-instance that you want to modify.
+      revision:
+        description: Check the node runtime property history, find the revision number you want to rollback to
diff --git a/helm/plugin/__init__.py b/helm/plugin/__init__.py
new file mode 100644 (file)
index 0000000..749f68f
--- /dev/null
@@ -0,0 +1,14 @@
+########
+# Copyright (c) 2014 GigaSpaces Technologies Ltd. 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.
diff --git a/helm/plugin/tasks.py b/helm/plugin/tasks.py
new file mode 100644 (file)
index 0000000..8df29ac
--- /dev/null
@@ -0,0 +1,305 @@
+# ============LICENSE_START==========================================
+# ===================================================================
+# Copyright (c) 2018 AT&T
+#
+# 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.
+#============LICENSE_END============================================
+
+from cloudify.decorators import operation
+import shutil
+import errno
+import sys
+import pwd
+import grp
+import os
+import re
+import getpass
+import subprocess
+from cloudify import ctx
+from cloudify.exceptions import OperationRetry
+from cloudify_rest_client.exceptions import CloudifyClientError
+import pip
+import json
+import yaml
+import urllib2
+from cloudify.decorators import operation
+from cloudify import exceptions
+from cloudify.exceptions import NonRecoverableError
+
+
+
+def execute_command(_command):
+    ctx.logger.debug('_command {0}.'.format(_command))
+
+    subprocess_args = {
+        'args': _command.split(),
+        'stdout': subprocess.PIPE,
+        'stderr': subprocess.PIPE
+    }
+
+    ctx.logger.debug('subprocess_args {0}.'.format(subprocess_args))
+
+    process = subprocess.Popen(**subprocess_args)
+    output, error = process.communicate()
+
+    ctx.logger.debug('command: {0} '.format(_command))
+    ctx.logger.debug('output: {0} '.format(output))
+    ctx.logger.debug('error: {0} '.format(error))
+    ctx.logger.debug('process.returncode: {0} '.format(process.returncode))
+
+    if process.returncode:
+        ctx.logger.error('Running `{0}` returns error.'.format(_command))
+        return False
+
+    return output
+
+
+def configure_admin_conf():
+    # Add the kubeadmin config to environment
+    agent_user = getpass.getuser()
+    uid = pwd.getpwnam(agent_user).pw_uid
+    gid = grp.getgrnam('docker').gr_gid
+    admin_file_dest = os.path.join(os.path.expanduser('~'), 'admin.conf')
+
+    execute_command('sudo cp {0} {1}'.format('/etc/kubernetes/admin.conf', admin_file_dest))
+    execute_command('sudo chown {0}:{1} {2}'.format(uid, gid, admin_file_dest))
+
+    with open(os.path.join(os.path.expanduser('~'), '.bashrc'), 'a') as outfile:
+        outfile.write('export KUBECONFIG=$HOME/admin.conf')
+    os.environ['KUBECONFIG'] = admin_file_dest
+
+def get_current_helm_value(chart_name):
+    tiller_host= str(ctx.node.properties['tiller-server-ip'])+':'+str(ctx.node.properties['tiller-server-port'])
+    config_dir_root= str(ctx.node.properties['config-dir'])
+    config_dir=config_dir_root+str(ctx.deployment.id)+'/'
+    if str_to_bool(ctx.node.properties['tls-enable']):
+        getValueCommand=subprocess.Popen(["helm", "get","values","-a",chart_name,'--host',tiller_host,'--tls','--tls-ca-cert',config_dir+'ca.cert.pem','--tls-cert',config_dir+'helm.cert.pem','--tls-key',config_dir+'helm.key.pem'], stdout=subprocess.PIPE)
+    else:
+        getValueCommand=subprocess.Popen(["helm", "get","values","-a",chart_name,'--host',tiller_host], stdout=subprocess.PIPE)
+    value=getValueCommand.communicate()[0]
+    valueMap= {}
+    valueMap = yaml.safe_load(value)
+    ctx.instance.runtime_properties['current-helm-value'] = valueMap
+
+def get_helm_history(chart_name):
+    tiller_host= str(ctx.node.properties['tiller-server-ip'])+':'+str(ctx.node.properties['tiller-server-port'])
+    config_dir_root= str(ctx.node.properties['config-dir'])
+    config_dir=config_dir_root+str(ctx.deployment.id)+'/'
+    if str_to_bool(ctx.node.properties['tls-enable']):
+        getHistoryCommand=subprocess.Popen(["helm", "history",chart_name,'--host',tiller_host,'--tls','--tls-ca-cert',config_dir+'ca.cert.pem','--tls-cert',config_dir+'helm.cert.pem','--tls-key',config_dir+'helm.key.pem'], stdout=subprocess.PIPE)
+    else:
+        getHistoryCommand=subprocess.Popen(["helm", "history",chart_name,'--host',tiller_host], stdout=subprocess.PIPE)
+    history=getHistoryCommand.communicate()[0]
+    history_start_output = [line.strip() for line in history.split('\n') if line.strip()]
+    for index  in range(len(history_start_output)):
+        history_start_output[index]=history_start_output[index].replace('\t',' ')
+    ctx.instance.runtime_properties['helm-history'] = history_start_output
+
+def mergedict(dict1, dict2):
+    for key in dict2.keys():
+        if key not in dict1.keys():
+            dict1[key] = dict2[key]
+        else:
+            if type(dict1[key]) == dict and type(dict2[key]) == dict :
+                mergedict(dict1[key], dict2[key])
+            else:
+                dict1[key] = dict2[key]
+
+def tls():
+    if str_to_bool(ctx.node.properties['tls-enable']):
+        config_dir_root= str(ctx.node.properties['config-dir'])
+        config_dir=config_dir_root+str(ctx.deployment.id)+'/'
+        tls_command= ' --tls --tls-ca-cert '+config_dir+'ca.cert.pem --tls-cert '+config_dir+'helm.cert.pem --tls-key '+config_dir+'helm.key.pem '
+        ctx.logger.debug(tls_command)
+        return tls_command
+    else :
+        return ''
+
+def tiller_host():
+    tiller_host= ' --host '+str(ctx.node.properties['tiller-server-ip'])+':'+str(ctx.node.properties['tiller-server-port'])+' '
+    ctx.logger.debug(tiller_host)
+    return tiller_host
+
+
+def str_to_bool(s):
+    s=str(s)
+    if s == 'True' or s == 'true':
+        return True
+    elif s == 'False' or s== 'false':
+        return False
+    else:
+        raise False
+
+
+@operation
+def config(**kwargs):
+    # create helm value file on K8s master
+    #configPath = ctx.node.properties['config-path']
+    configJson = str(ctx.node.properties['config'])
+    configJsonUrl = str(ctx.node.properties['config-url'])
+    runtime_config = str(ctx.node.properties['runtime-config'])  #json
+    componentName = ctx.node.properties['component-name']
+    config_dir_root= str(ctx.node.properties['config-dir'])
+    stable_repo_url = str(ctx.node.properties['stable-repo-url'])
+    ctx.logger.debug("debug "+ configJson + runtime_config )
+    #load input config
+    config_dir=config_dir_root+str(ctx.deployment.id)
+    try:
+        os.makedirs(config_dir)
+    except OSError as e:
+        if e.errno != errno.EEXIST:
+            raise
+    ctx.logger.debug('tls-enable type '+str(type(str_to_bool(ctx.node.properties['tls-enable']))) )
+    #create TLS cert files
+    if str_to_bool(ctx.node.properties['tls-enable']):
+        ctx.logger.debug('tls enable' )
+        ca_value = ctx.node.properties['ca']
+        cert_value = ctx.node.properties['cert']
+        key_value = ctx.node.properties['key']
+        ca= open(config_dir+'/ca.cert.pem',"w+")
+        ca.write(ca_value)
+        ca.close()
+        cert= open(config_dir+'/helm.cert.pem',"w+")
+        cert.write(cert_value)
+        cert.close()
+        key= open(config_dir+'/helm.key.pem',"w+")
+        key.write(key_value)
+        key.close()
+    else:
+        ctx.logger.debug('tls disable' )
+
+    # create helm value.yaml file
+    configPath=config_dir_root+str(ctx.deployment.id)+'/'+componentName+'.yaml'
+    ctx.logger.debug(configPath)
+
+    configObj ={}
+    if configJson == '' and configJsonUrl == '':
+        ctx.logger.debug("Will use default HELM value")
+    elif configJson == '' and configJsonUrl != '':
+        response = urllib2.urlopen(configJsonUrl)
+        configObj = json.load(response)
+    elif configJson != '' and configJsonUrl == '':
+        configObj = json.loads(configJson)
+    else:
+        raise NonRecoverableError("Unable to get Json config input")
+
+    # load runtime config
+    ctx.logger.debug("debug check runtime config")
+    if runtime_config == '':
+        ctx.logger.debug("there is no runtime config value")
+    else:
+        runtime_config_obj= json.loads(runtime_config)
+        mergedict(configObj,runtime_config_obj)
+
+    with open(configPath, 'w') as outfile:
+        yaml.safe_dump(configObj, outfile, default_flow_style=False)
+
+    output = execute_command('helm init --client-only --stable-repo-url '+stable_repo_url)
+    if output == False :
+        raise NonRecoverableError("helm init failed")
+
+
+
+
+@operation
+def start(**kwargs):
+    # install the ONAP Helm chart
+    # get properties from node
+    chartRepo = ctx.node.properties['chart-repo-url']
+    componentName = ctx.node.properties['component-name']
+    chartVersion = ctx.node.properties['chart-version']
+    config_dir_root= str(ctx.node.properties['config-dir'])
+    configPath=config_dir_root+str(ctx.deployment.id)+'/'+componentName+'.yaml'
+    namespace = ctx.node.properties['namespace']
+    configJson = str(ctx.node.properties['config'])
+    configJsonUrl = str(ctx.node.properties['config-url'])
+    runtimeconfigJson =  str(ctx.node.properties['runtime-config'])
+
+
+    chart = chartRepo + "/" + componentName + "-" + chartVersion + ".tgz"
+    chartName = namespace + "-" + componentName
+
+    if configJson == '' and runtimeconfigJson == '' and configJsonUrl == '':
+        installCommand = 'helm install '+ chart + ' --name ' + chartName + ' --namespace ' + namespace+tiller_host()+tls()
+    else:
+        installCommand = 'helm install ' + chart + ' --name ' + chartName + ' --namespace ' + namespace + ' -f '+ configPath +tiller_host()+tls()
+
+    output =execute_command(installCommand)
+    if output == False :
+        return ctx.operation.retry(message='helm install failed, re-try after 5 second ',
+                                   retry_after=5)
+
+    get_current_helm_value(chartName)
+    get_helm_history(chartName)
+
+@operation
+def stop(**kwargs):
+    # delete the ONAP helm chart
+    #configure_admin_conf()
+    # get properties from node
+    namespace = ctx.node.properties['namespace']
+    component = ctx.node.properties['component-name']
+    chartName = namespace + "-" + component
+    config_dir_root= str(ctx.node.properties['config-dir'])
+    # Delete helm chart
+    command = 'helm delete --purge '+ chartName+tiller_host()+tls()
+    output =execute_command(command)
+    config_dir=config_dir_root+str(ctx.deployment.id)
+    shutil.rmtree(config_dir)
+    if output == False :
+        raise NonRecoverableError("helm delete failed")
+
+@operation
+def upgrade(**kwargs):
+    # upgrade the helm chart
+    componentName = ctx.node.properties['component-name']
+    config_dir_root= str(ctx.node.properties['config-dir'])
+    configPath=config_dir_root+str(ctx.deployment.id)+'/'+componentName+'.yaml'
+    componentName = ctx.node.properties['component-name']
+    namespace = ctx.node.properties['namespace']
+    configJson = kwargs['config']
+    chartRepo =  kwargs['chart_repo']
+    chartVersion = kwargs['chart_version']
+
+    ctx.logger.debug('debug ' + str(configJson))
+    chartName = namespace + "-" + componentName
+    chart=chartRepo + "/" + componentName + "-" + chartVersion + ".tgz"
+    if str(configJson) == '':
+        upgradeCommand = 'helm upgrade '+ chartName + ' '+ chart+tiller_host()+tls()
+    else:
+        with open(configPath, 'w') as outfile:
+            yaml.safe_dump(configJson, outfile, default_flow_style=False)
+        #configure_admin_conf()
+        upgradeCommand = 'helm upgrade '+ chartName + ' '+ chart + ' -f ' + configPath+tiller_host()+tls()
+    output=execute_command(upgradeCommand)
+    if output == False :
+        return ctx.operation.retry(message='helm upgrade failed, re-try after 5 second ',
+                                   retry_after=5)
+    get_current_helm_value(chartName)
+    get_helm_history(chartName)
+
+@operation
+def rollback(**kwargs):
+    # rollback to some revision
+    componentName = ctx.node.properties['component-name']
+    namespace = ctx.node.properties['namespace']
+    revision = kwargs['revision']
+    #configure_admin_conf()
+    chartName = namespace + "-" + componentName
+    rollbackCommand = 'helm rollback '+ chartName + ' '+ revision+tiller_host()+tls()
+    output=execute_command(rollbackCommand)
+    if output == False :
+        return ctx.operation.retry(message='helm rollback failed, re-try after 5 second ',
+                                   retry_after=5)
+    get_current_helm_value(chartName)
+    get_helm_history(chartName)
diff --git a/helm/plugin/tests/__init__.py b/helm/plugin/tests/__init__.py
new file mode 100644 (file)
index 0000000..749f68f
--- /dev/null
@@ -0,0 +1,14 @@
+########
+# Copyright (c) 2014 GigaSpaces Technologies Ltd. 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.
diff --git a/helm/plugin/tests/blueprint/blueprint.yaml b/helm/plugin/tests/blueprint/blueprint.yaml
new file mode 100644 (file)
index 0000000..2588e8d
--- /dev/null
@@ -0,0 +1,42 @@
+# DSL version, should appear in the main blueprint.yaml
+# and may appear in other imports. In such case, the versions must match
+tosca_definitions_version: cloudify_dsl_1_3
+
+imports:
+    # importing cloudify related types, plugins, workflow, etc...
+    # to speed things up, it is possible downloading this file,
+    # including it in the blueprint directory and importing it
+    # instead.
+    - http://www.getcloudify.org/spec/cloudify/4.1.1/types.yaml
+    # relative import of plugin.yaml that resides in the blueprint directory
+    - plugin/test_plugin.yaml
+
+inputs:
+    # example input that could be injected by test
+    test_input:
+        description: an input for the test
+        default: default_test_input
+
+node_templates:
+    # defining a single node template that will serve as our test node
+    test_node_template:
+        # using base cloudify type
+        type: cloudify.nodes.Root
+        interfaces:
+            cloudify.interfaces.lifecycle:
+                start:
+                    # here we map the single plugin task to the start operation
+                    # of the cloudify.interfaces.lifecycle interface
+                    implementation: plugin_name.plugin.tasks.my_task
+                    inputs:
+                        # my_task accepts a single property named
+                        # some property. Here we inject this property
+                        # from the input provided by the test
+                        # (or 'default_test_input' if no input was provided)
+                        some_property: { get_input: test_input }
+
+outputs:
+    # example output the could be used to simplify assertions by test
+    test_output:
+        description: an output for the test
+        value: { get_attribute: [test_node_template, some_property] }
diff --git a/helm/plugin/tests/blueprint/plugin/test_plugin.yaml b/helm/plugin/tests/blueprint/plugin/test_plugin.yaml
new file mode 100644 (file)
index 0000000..9701318
--- /dev/null
@@ -0,0 +1,20 @@
+plugins:
+    # Name could be anything, this name is what appears on the beginning of operation
+    # mappings.
+    plugin_name:
+        # Could be 'central_deployment_agent' or 'host_agent'.
+        # If 'central_deployment_agent', this plugin will be executed on the
+        # deployment dedicated agent, other wise it will be executed on the host agent.
+        # We set it the 'central_deployment_agent' here because 'host_agent' plugins should
+        # be contained in a host and this is not required for testing purposes
+        executor: central_deployment_agent
+
+        # Setting install to false in testing environment. In the non-test plugin definition
+        # this property could be omitted usually (its default is true), in which case
+        # the source property should be set
+        install: false
+
+        # source: URL to archive containing the plugin or name of directory containing
+        #         the plugin if it is included in the the blueprint directory under the
+        #         "plugins" directory. Not required in testing environments as the plugin
+        #         need not be installed on any agent
diff --git a/helm/plugin/tests/test_plugin.py b/helm/plugin/tests/test_plugin.py
new file mode 100644 (file)
index 0000000..be0882f
--- /dev/null
@@ -0,0 +1,47 @@
+########
+# Copyright (c) 2014 GigaSpaces Technologies Ltd. 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 os import path
+import unittest
+
+from cloudify.test_utils import workflow_test
+
+
+class TestPlugin(unittest.TestCase):
+
+    @workflow_test(path.join('blueprint', 'blueprint.yaml'),
+                   resources_to_copy=[(path.join('blueprint', 'plugin',
+                                                 'test_plugin.yaml'),
+                                       'plugin')],
+                   inputs={'test_input': 'new_test_input'})
+    def test_my_task(self, cfy_local):
+        # execute install workflow
+        """
+
+        :param cfy_local:
+        """
+        cfy_local.execute('install', task_retries=0)
+
+        # extract single node instance
+        instance = cfy_local.storage.get_node_instances()[0]
+
+        # assert runtime properties is properly set in node instance
+        self.assertEqual(instance.runtime_properties['some_property'],
+                         'new_test_input')
+
+        # assert deployment outputs are ok
+        self.assertDictEqual(cfy_local.outputs(),
+                             {'test_output': 'new_test_input'})
diff --git a/helm/plugin/workflows.py b/helm/plugin/workflows.py
new file mode 100644 (file)
index 0000000..d341bf7
--- /dev/null
@@ -0,0 +1,64 @@
+# ============LICENSE_START==========================================
+# ===================================================================
+# Copyright (c) 2018 AT&T
+#
+# 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.
+#============LICENSE_END============================================
+
+from cloudify.decorators import workflow
+from cloudify.workflows import ctx
+from cloudify.exceptions import NonRecoverableError
+import urllib2
+import json
+
+@workflow
+def upgrade(node_instance_id,config_json,config_json_url,chartVersion,chartRepo,**kwargs):
+    node_instance = ctx.get_node_instance(node_instance_id)
+
+    if not node_instance_id:
+        raise NonRecoverableError(
+            'No such node_instance_id in deployment: {0}.'.format(
+                node_instance_id))
+
+    kwargs = {}
+    if config_json == '' and config_json_url == '':
+       kwargs['config'] = config_json
+    elif config_json == '' and config_json_url != '':
+        response = urllib2.urlopen(config_json_url)
+        kwargs['config'] = json.load(response)
+    elif config_json != '' and config_json_url == '':
+       kwargs['config'] = config_json
+    else:
+        raise NonRecoverableError("Unable to get Json config input")
+
+    kwargs['chart_version'] = str(chartVersion)
+    kwargs['chart_repo'] = str(chartRepo)
+    operation_args = {'operation': 'upgrade',}
+    operation_args['kwargs'] = kwargs
+    node_instance.execute_operation(**operation_args)
+
+
+@workflow
+def rollback(node_instance_id,revision,**kwargs):
+    node_instance = ctx.get_node_instance(node_instance_id)
+
+    if not node_instance_id:
+        raise NonRecoverableError(
+            'No such node_instance_id in deployment: {0}.'.format(
+                node_instance_id))
+
+    kwargs = {}
+    kwargs['revision'] = str(revision)
+    operation_args = {'operation': 'rollback',}
+    operation_args['kwargs'] = kwargs
+    node_instance.execute_operation(**operation_args)
diff --git a/helm/setup.py b/helm/setup.py
new file mode 100644 (file)
index 0000000..c3bfe88
--- /dev/null
@@ -0,0 +1,46 @@
+# ============LICENSE_START==========================================
+# ===================================================================
+# Copyright (c) 2018 AT&T
+#
+# 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.
+#============LICENSE_END============================================
+
+
+from setuptools import setup
+
+# Replace the place holders with values for your project
+
+setup(
+
+    # Do not use underscores in the plugin name.
+    name='onap-helm-plugin',
+    version='2.2.0',
+    author='Nicolas Hu(AT&T)',
+    author_email='jh245g@att.com',
+    description='This plugin will install/uninstall/upgrade/rollback helm charts of ONAP components. ',
+
+    # This must correspond to the actual packages in the plugin.
+    packages=['plugin'],
+
+    license='LICENSE',
+    zip_safe=False,
+    install_requires=[
+        # Necessary dependency for developing plugins, do not remove!
+        'pyyaml>=3.12',
+        "cloudify-plugins-common>=4.1.1"
+    ],
+    test_requires=[
+        "cloudify-dsl-parser>=4.1.1"
+        "nose"
+    ]
+)
diff --git a/helm/tox.ini b/helm/tox.ini
new file mode 100644 (file)
index 0000000..14c340c
--- /dev/null
@@ -0,0 +1,18 @@
+# content of: tox.ini , put in same dir as setup.py
+[tox]
+envlist=flake8,py27
+
+[testenv:py27]
+deps =
+    # this fixes issue with tox installing coverage --pre
+    coverage==3.7.1
+    nose-cov
+    testfixtures
+    -rdev-requirements.txt
+commands=nosetests --with-cov --cov-report term-missing --cov plugin plugin/tests
+
+[testenv:flake8]
+deps =
+    flake8
+    -rdev-requirements.txt
+commands=flake8 plugin