├── .circleci ├── blueprint.yaml ├── config.yml ├── k8s-resources.yaml ├── py3fixers └── test_features.py ├── .drp └── trufflehog_config.yml ├── .gitignore ├── .gitmodules ├── CHANGELOG.txt ├── LICENSE ├── Makefile ├── README.md ├── cloudify_kubernetes ├── __init__.py ├── __version__.py ├── _compat.py ├── decorators.py ├── k8s │ ├── __init__.py │ ├── authentication.py │ ├── client.py │ ├── config.py │ ├── exceptions.py │ ├── mapping.py │ ├── operations.py │ └── status_mapping.py ├── tasks │ ├── __init__.py │ ├── api_calls.py │ ├── nested_resources │ │ ├── __init__.py │ │ └── tokens.py │ ├── operations.py │ └── shared_cluster.py ├── tests │ ├── __init__.py │ ├── resources │ │ └── blueprint.yaml │ ├── test_decorators.py │ ├── test_k8s_authentication.py │ ├── test_k8s_client.py │ ├── test_k8s_config.py │ ├── test_k8s_exceptions.py │ ├── test_k8s_mapping.py │ ├── test_k8s_operations.py │ ├── test_tasks.py │ └── test_utils.py ├── utils.py └── workflows.py ├── examples ├── basic.yaml ├── file-test-multiple-resources.yaml ├── file-test-persistent-volume.yaml ├── file-test-with-file-content.yaml ├── file-test.yaml ├── files-test-multiple-resources.yaml ├── inputs │ ├── dashboard-inputs.yaml │ └── simple-blueprint-authentication-token-inputs.yaml ├── old-examples │ ├── cassandra-blueprint.yaml │ ├── cloudify-manager.yaml │ ├── load-balancer-blueprint.yaml │ ├── multiple-resource-file-test.yaml │ ├── openstack-node-existing-cluster.yaml │ ├── persistent-volumes-blueprint.yaml │ ├── replicasets-blueprint.yaml │ ├── replication-controller-blueprint.yaml │ ├── service-account.yaml │ ├── simple-blueprint-authentication-token.yaml │ ├── simple-blueprint-defined-resource.yaml │ ├── simple-custom-blueprint-defined-resource.yaml │ ├── simple-file-defined-resource.yaml │ ├── test-deployment.yaml │ ├── test-persistent-volume.yaml │ ├── test-pod.yaml │ ├── test-service.yaml │ └── wordpress-blueprint.yaml ├── resources │ ├── cloudify-crd.yaml │ ├── cloudify_manager │ │ ├── pod.yaml │ │ └── service.yaml │ ├── cluster-role-binding.yaml │ ├── cluster-role.yaml │ ├── config-map.yaml │ ├── crd.yaml │ ├── crontabs.yaml │ ├── custom-resource.yaml │ ├── daemon-set.yaml │ ├── deployment.yaml │ ├── ingress.yaml │ ├── namespace.yaml │ ├── network-policy.yaml │ ├── node.yaml │ ├── pod-a.yaml │ ├── pod-b.yaml │ ├── pod-security-policy.yaml │ ├── pod.yaml │ ├── pods-c-d.yaml │ ├── pv.yaml │ ├── pvc.yaml │ ├── replica-set.yaml │ ├── replication-controller.yaml │ ├── role-binding.yaml │ ├── role.yaml │ ├── secret.yaml │ ├── service.yaml │ ├── stateful-set.yaml │ └── storage-class.yaml ├── scripts │ ├── configure_node.py │ ├── create.py │ ├── tasks.py │ └── token.py ├── storage-class.yaml ├── test-deployment.yaml └── test-stateful-set.yaml ├── extra-packaging-instructions.sh ├── ignore_plugin_yaml_differences ├── plugin.yaml ├── plugin_1_4.yaml ├── plugin_1_5.yaml ├── requirements-3.6.in ├── requirements-3.6.txt ├── requirements.in ├── requirements.txt ├── setup.py ├── test-requirements.txt ├── tox.ini └── v2_plugin.yaml /.circleci/blueprint.yaml: -------------------------------------------------------------------------------- 1 | tosca_definitions_version: cloudify_dsl_1_5 2 | 3 | imports: 4 | - cloudify/types/types.yaml 5 | - plugin:cloudify-kubernetes-plugin?version= >=2.7.0 6 | 7 | inputs: 8 | 9 | app_name: 10 | display_label: App Name 11 | type: string 12 | description: Prefix to use for application service / deployment names 13 | default: cloudify-hello-world 14 | 15 | container: 16 | display_label: Container 17 | description: Standard container reference 18 | type: string 19 | default: gcr.io/google-samples/node-hello:1.0 20 | 21 | container_port: 22 | display_label: Container Port 23 | description: Exposed container port to reference 24 | type: integer 25 | default: 8080 26 | 27 | node_templates: 28 | 29 | hello-world: 30 | type: cloudify.nodes.kubernetes.resources.FileDefinedResource 31 | properties: 32 | client_config: 33 | configuration: 34 | file_content: { get_secret: kubernetes_config } 35 | file: 36 | resource_path: k8s-resources.yaml 37 | template_variables: 38 | APP_NAME: { get_input: app_name } 39 | PORT: { get_input: container_port } 40 | CONTAINER_NAME: { get_input: app_name } 41 | CONTAINER_IMAGE: { get_input: container } 42 | 43 | outputs: 44 | 45 | service: 46 | description: Service endpoint 47 | value: 48 | endpoint: { concat: ["http://", { get_attribute: [hello-world, kubernetes, resources.yaml#1, status, load_balancer, ingress, 0, ip] } ] } 49 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | orbs: 4 | node: cloudify/public-unittest-orb@volatile 5 | wagonorb: cloudify/wagon-bulder-orb@volatile 6 | releaseorb: cloudify/release-orb@volatile 7 | 8 | checkout: 9 | post: 10 | - > 11 | if [ -n "$CI_PULL_REQUEST" ]; then 12 | PR_ID=${CI_PULL_REQUEST##*/} 13 | git fetch origin +refs/pull/$PR_ID/merge: 14 | git checkout -qf FETCH_HEAD 15 | fi 16 | 17 | executors: 18 | py36: 19 | docker: 20 | - image: circleci/python:3.6 21 | 22 | cloudify-machine-py3: 23 | machine: 24 | image: ubuntu-2004:202201-02 25 | 26 | commands: 27 | 28 | setup_manager: 29 | steps: 30 | - run: | 31 | if [[ -z "${CLOUDIFY_HOST}" ]]; then 32 | exit 1 33 | fi 34 | - run: | 35 | if [[ -z "${CLOUDIFY_TENANT}" ]] && [ -z "${CIRCLE_PROJECT_REPONAME}" ]; then 36 | exit 1 37 | fi 38 | - run: | 39 | if [[ -z "${CLOUDIFY_TOKEN}" ]]; then 40 | exit 1 41 | fi 42 | - run: | 43 | response=$(curl --write-out '%{http_code}' --silent --insecure --header "Tenant: ${CLOUDIFY_TENANT}" --header "Authentication-Token: ${CLOUDIFY_TOKEN}" https://$CLOUDIFY_HOST/api/v3.1/status --output /dev/null) 44 | if [[ $response != 200 ]]; then 45 | echo "Failed to get manager status"; 46 | exit 1 47 | fi 48 | - run: pip3 install urllib3==1.26.15 requests-toolbelt==0.10.1 49 | - run: pip3 install https://github.com/cloudify-incubator/cloudify-ecosystem-test/archive/refs/heads/master.zip 50 | - run: git submodule update --init --recursive --remote 51 | 52 | prepare_test_manager: 53 | steps: 54 | - run: ecosystem-test prepare-remote-test-manager -es gcp_credentials=$gcp_credentials -p $(find ~/project/workspace/build/ -name *manylinux-py311-none-linux_x86_64.wgn*) ~/project/plugin.yaml 55 | - run: curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" 56 | - run: sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl 57 | 58 | run_gke_test: 59 | steps: 60 | - run: ecosystem-test remote-blueprint-test -b .circleci/blueprint.yaml --test-id=kube-test-$CIRCLE_BUILD_NUM --on-failure=uninstall-force --timeout=5400 61 | 62 | jobs: 63 | kubernetes_integration_tests_py3: 64 | executor: cloudify-machine-py3 65 | environment: 66 | CLOUDIFY_SSL_TRUST_ALL: true 67 | steps: 68 | - checkout 69 | - attach_workspace: 70 | at: workspace 71 | - setup_manager 72 | - prepare_test_manager 73 | - run_gke_test 74 | 75 | workflows: 76 | version: 2 77 | tests: 78 | jobs: 79 | - node/check_py3_compat_job 80 | - node/unittests_job 81 | - node/validate_version_job 82 | - node/validate_documentation_job 83 | - wagonorb/wagon: 84 | filters: 85 | branches: 86 | only: /([0-9\.]*\-build|master|dev)/ 87 | - wagonorb/arch64_wagon: 88 | filters: 89 | branches: 90 | only: /([0-9\.]*\-build|master|dev)/ 91 | - wagonorb/rhel_wagon: 92 | filters: 93 | branches: 94 | only: /([0-9\.]*\-build|master|dev)/ 95 | - wagonorb/wagon_311: 96 | filters: 97 | branches: 98 | only: /([0-9\.]*\-build|master|dev)/ 99 | - kubernetes_integration_tests_py3: 100 | context: 101 | - plugins-inputs 102 | requires: 103 | - wagonorb/wagon 104 | - wagonorb/arch64_wagon 105 | - wagonorb/rhel_wagon 106 | - wagonorb/wagon_311 107 | filters: 108 | branches: 109 | only: /([0-9\.]*\-build|master|dev|RND-1248-k-8-s-plugin-nightly-failing)/ 110 | - releaseorb/release: 111 | filters: 112 | branches: 113 | only: /master/ 114 | requires: 115 | - node/unittests_job 116 | - wagonorb/wagon 117 | - wagonorb/wagon_311 118 | - wagonorb/arch64_wagon 119 | - wagonorb/rhel_wagon 120 | - kubernetes_integration_tests_py3 121 | - releaseorb/merge_docs_job: 122 | filters: 123 | branches: 124 | only: /master/ 125 | requires: 126 | - releaseorb/release 127 | - node/validate_documentation_job 128 | 129 | nightly: 130 | triggers: 131 | - schedule: 132 | cron: "0 1 * * 0,1,3,5" 133 | filters: 134 | branches: 135 | only: 136 | - master 137 | jobs: 138 | - node/check_py3_compat_job 139 | - node/unittests_job 140 | - node/validate_version_job 141 | - wagonorb/wagon: 142 | filters: 143 | branches: 144 | only: /([0-9\.]*\-build|master)/ 145 | - wagonorb/wagon_311: 146 | filters: 147 | branches: 148 | only: /([0-9\.]*\-build|master)/ 149 | - kubernetes_integration_tests_py3: 150 | context: 151 | - plugins-inputs 152 | requires: 153 | - wagonorb/wagon 154 | - wagonorb/wagon_311 155 | filters: 156 | branches: 157 | only: /([0-9\.]*\-build|master)/ 158 | -------------------------------------------------------------------------------- /.circleci/k8s-resources.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: "{{ APP_NAME }}-app" 5 | labels: 6 | app: {{ APP_NAME }} 7 | manager: cloudify 8 | tier: app 9 | spec: 10 | selector: 11 | matchLabels: 12 | app: {{ APP_NAME }} 13 | manager: cloudify 14 | tier: app 15 | replicas: 1 16 | template: 17 | metadata: 18 | labels: 19 | app: {{ APP_NAME }} 20 | manager: cloudify 21 | tier: app 22 | spec: 23 | containers: 24 | - name: {{ CONTAINER_NAME }} 25 | image: {{ CONTAINER_IMAGE }} 26 | ports: 27 | - containerPort: {{ PORT }} 28 | name: http-server 29 | resources: 30 | limits: 31 | cpu: 1 32 | memory: 128Mi 33 | requests: 34 | cpu: 1 35 | memory: 128Mi 36 | --- 37 | apiVersion: v1 38 | kind: Service 39 | metadata: 40 | name: "{{ APP_NAME }}-svc" 41 | labels: 42 | app: {{ APP_NAME }} 43 | manager: cloudify 44 | tier: app 45 | spec: 46 | type: LoadBalancer 47 | selector: 48 | app: {{ APP_NAME }} 49 | manager: cloudify 50 | tier: app 51 | ports: 52 | - port: 80 53 | targetPort: {{ PORT }} 54 | protocol: TCP 55 | -------------------------------------------------------------------------------- /.circleci/py3fixers: -------------------------------------------------------------------------------- 1 | --stage1 2 | -f lib2to3.fixes.fix_getcwdu 3 | -f lib2to3.fixes.fix_long 4 | -f lib2to3.fixes.fix_nonzero 5 | -f lib2to3.fixes.fix_input 6 | -f lib2to3.fixes.fix_raw_input 7 | -f lib2to3.fixes.fix_itertools 8 | -f lib2to3.fixes.fix_itertools_imports 9 | -f lib2to3.fixes.fix_exec 10 | -f lib2to3.fixes.fix_operator 11 | -f libfuturize.fixes.fix_execfile 12 | -f libpasteurize.fixes.fix_newstyle 13 | -f lib2to3.fixes.fix_filter 14 | # fix_dict is not idempotent 15 | # -f lib2to3.fixes.fix_dict 16 | -f lib2to3.fixes.fix_map 17 | -f lib2to3.fixes.fix_zip 18 | -f lib2to3.fixes.fix_xrange 19 | -f lib2to3.fixes.fix_basestring 20 | -f libfuturize.fixes.fix_cmp 21 | -f libfuturize.fixes.fix_division_safe 22 | -f lib2to3.fixes.fix_metaclass 23 | -f libfuturize.fixes.fix_unicode_keep_u 24 | -------------------------------------------------------------------------------- /.circleci/test_features.py: -------------------------------------------------------------------------------- 1 | ######## 2 | # Copyright (c) 2014-2019 Cloudify Platform Ltd. All rights reserved 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import os 17 | import json 18 | import yaml 19 | import base64 20 | import tempfile 21 | from os import environ 22 | from contextlib import contextmanager 23 | 24 | import pytest 25 | 26 | from ecosystem_tests.dorkl.commands import handle_process 27 | from ecosystem_tests.ecosystem_tests_cli.logger import logger 28 | from ecosystem_tests.nerdl.api import ( 29 | create_secret, 30 | with_client, 31 | get_node_instance, 32 | list_node_instances, 33 | upload_blueprint, 34 | create_deployment, 35 | wait_for_install, 36 | cleanup_on_failure) 37 | 38 | 39 | TEST_ID = environ.get('__ECOSYSTEM_TEST_ID', 'plugin-examples') 40 | environ['USE_GKE_GCLOUD_AUTH_PLUGIN'] = 'true' 41 | 42 | 43 | @pytest.mark.dependency() 44 | def test_update(*_, **__): 45 | deployment_id = TEST_ID + '-update' 46 | setup_cli() 47 | try: 48 | # Upload Cloud Watch Blueprint 49 | upload_blueprint( 50 | 'examples/file-test.yaml', 51 | deployment_id) 52 | # Create Cloud Watch Deployment with Instance ID input 53 | create_deployment( 54 | deployment_id, 55 | deployment_id, 56 | { 57 | "resource_path": "resources/pod.yaml" 58 | } 59 | ) 60 | # Install Cloud Watch Deployment 61 | wait_for_install(deployment_id, 300) 62 | after_install = get_pod_info() 63 | update_params = { 64 | "kind": "Pod", 65 | "metadata": { 66 | "name": "nginx-test-pod" 67 | }, 68 | "spec": { 69 | "containers": [ 70 | { 71 | "name": "nginx-test-pod", 72 | "image": "nginx:latest" 73 | } 74 | ] 75 | } 76 | } 77 | params = {'resource_definition_changes': update_params} 78 | tmp = tempfile.NamedTemporaryFile(delete=false, mode='w', suffix='.yaml') 79 | yaml.dump(params, tmp) 80 | tmp.close() 81 | wait_for_workflow( 82 | deployment_id, 83 | 'update_resource_definition', 84 | 300, 85 | params=tmp 86 | ) 87 | os.remove(tmp.name) 88 | after_update = get_pod_info() 89 | assert after_install['spec']['containers'][0]['image'] == 'nginx:stable' 90 | assert after_update['spec']['containers'][0]['image'] == 'nginx:latest' 91 | # Uninstall Cloud Watch Deployment 92 | wait_for_uninstall(deployment_id, 300) 93 | except: 94 | cleanup_on_failure(deployment_id) 95 | 96 | 97 | def setup_cli(): 98 | cluster_name = runtime_properties( 99 | node_instance_by_name('kubernetes-cluster')['id'])['name'] 100 | capabilities = get_capabilities('gcp-gke') 101 | logger.info('capabilities: {}'.format(capabilities)) 102 | create_secret('kubernetes_endpoint', capabilities['endpoint']) 103 | with open('gcp.json', 'wb') as outfile: 104 | creds = base64.b64decode(os.environ['gcp_credentials']) 105 | outfile.write(creds) 106 | handle_process('gcloud auth activate-service-account --key-file gcp.json') 107 | # handle_process('gcloud components install gke-gcloud-auth-plugin') 108 | handle_process( 109 | 'gcloud container clusters get-credentials {} ' 110 | '--region us-west1-a --project {}'.format( 111 | cluster_name, 'trammell-project')) 112 | 113 | 114 | @with_client 115 | def get_capabilities(dep_id, client): 116 | dep = client.deployments.capabilities.get(dep_id) 117 | return dep.capabilities 118 | 119 | 120 | def get_pod_info(): 121 | cluster_name = runtime_properties( 122 | node_instance_by_name('kubernetes-cluster')['id'])['name'] 123 | handle_process( 124 | 'gcloud container clusters get-credentials ' 125 | '{} --region us-west1-a --project trammell-project'.format( 126 | cluster_name)) 127 | return json.loads( 128 | handle_process('kubectl get pod nginx-test-pod --output="json"')) 129 | 130 | 131 | def node_instance_by_name(name): 132 | for node_instance in node_instances(): 133 | if node_instance['node_id'] == name: 134 | return node_instance 135 | raise Exception('No node instances found.') 136 | 137 | 138 | def node_instances(): 139 | return list_node_instances(TEST_ID) 140 | 141 | 142 | def runtime_properties(node_instance_id): 143 | return get_node_instance(node_instance_id)['runtime_properties'] 144 | -------------------------------------------------------------------------------- /.drp/trufflehog_config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | project_exclusion_list: 3 | exclusion_folders: 4 | - .drp 5 | - .github 6 | - examples 7 | - test 8 | - jenkins 9 | - helm_sdk/tests 10 | - cloudify_helm/tests 11 | exclusion_file_paths: 12 | - Makefile 13 | - dev-requirements.txt 14 | - .gitignore 15 | - CHANGELOG.txt 16 | - README.md 17 | - tox.ini 18 | - requirements.in 19 | - cloudify_cloudinit/README.md 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/python,pycharm 3 | 4 | ### PyCharm ### 5 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 6 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 7 | 8 | .idea 9 | .DS_Store 10 | 11 | # User-specific stuff: 12 | .idea/**/workspace.xml 13 | .idea/**/tasks.xml 14 | .idea/dictionaries 15 | 16 | # Sensitive or high-churn files: 17 | .idea/**/dataSources/ 18 | .idea/**/dataSources.ids 19 | .idea/**/dataSources.xml 20 | .idea/**/dataSources.local.xml 21 | .idea/**/sqlDataSources.xml 22 | .idea/**/dynamic.xml 23 | .idea/**/uiDesigner.xml 24 | 25 | # Gradle: 26 | .idea/**/gradle.xml 27 | .idea/**/libraries 28 | 29 | # Mongo Explorer plugin: 30 | .idea/**/mongoSettings.xml 31 | 32 | ## File-based project format: 33 | *.iws 34 | 35 | ## Plugin-specific files: 36 | 37 | # IntelliJ 38 | /out/ 39 | 40 | # mpeltonen/sbt-idea plugin 41 | .idea_modules/ 42 | 43 | # JIRA plugin 44 | atlassian-ide-plugin.xml 45 | 46 | # Cursive Clojure plugin 47 | .idea/replstate.xml 48 | 49 | # Crashlytics plugin (for Android Studio and IntelliJ) 50 | com_crashlytics_export_strings.xml 51 | crashlytics.properties 52 | crashlytics-build.properties 53 | fabric.properties 54 | 55 | ### PyCharm Patch ### 56 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 57 | 58 | # *.iml 59 | # modules.xml 60 | # .idea/misc.xml 61 | # *.ipr 62 | 63 | # Sonarlint plugin 64 | .idea/sonarlint 65 | 66 | ### Python ### 67 | # Byte-compiled / optimized / DLL files 68 | __pycache__/ 69 | *.py[cod] 70 | *$py.class 71 | 72 | # C extensions 73 | *.so 74 | 75 | # Distribution / packaging 76 | .Python 77 | env/ 78 | build/ 79 | develop-eggs/ 80 | dist/ 81 | downloads/ 82 | eggs/ 83 | .eggs/ 84 | lib/ 85 | lib64/ 86 | parts/ 87 | sdist/ 88 | var/ 89 | wheels/ 90 | *.egg-info/ 91 | .installed.cfg 92 | *.egg 93 | 94 | # PyInstaller 95 | # Usually these files are written by a python script from a template 96 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 97 | *.manifest 98 | *.spec 99 | 100 | # Installer logs 101 | pip-log.txt 102 | pip-delete-this-directory.txt 103 | 104 | # Unit test / coverage reports 105 | htmlcov/ 106 | .tox/ 107 | .coverage 108 | .coverage.* 109 | .cache 110 | nosetests.xml 111 | coverage.xml 112 | cover 113 | .hypothesis/ 114 | 115 | # Translations 116 | *.mo 117 | *.pot 118 | 119 | # Django stuff: 120 | *.log 121 | local_settings.py 122 | 123 | # Flask stuff: 124 | instance/ 125 | .webassets-cache 126 | 127 | # Scrapy stuff: 128 | .scrapy 129 | 130 | # Sphinx documentation 131 | docs/_build/ 132 | 133 | # PyBuilder 134 | target/ 135 | 136 | # Jupyter Notebook 137 | .ipynb_checkpoints 138 | 139 | # pyenv 140 | .python-version 141 | 142 | # celery beat schedule file 143 | celerybeat-schedule 144 | 145 | # SageMath parsed files 146 | *.sage.py 147 | 148 | # dotenv 149 | .env 150 | 151 | # virtualenv 152 | .venv 153 | venv/ 154 | ENV/ 155 | 156 | # Spyder project settings 157 | .spyderproject 158 | 159 | # Rope project settings 160 | .ropeproject 161 | 162 | # End of https://www.gitignore.io/api/python,pycharm 163 | 164 | # Cloudify 165 | inputs.yaml 166 | local-storage/ 167 | .cloudify/ 168 | **/*.wgn 169 | 170 | .vs/ 171 | gcp.json 172 | pydoc/ 173 | fusion-agent 174 | fusion-common 175 | fusion-manager 176 | cloudify-utilities-plugins-sdk 177 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "examples/blueprint-examples"] 2 | path = examples/blueprint-examples 3 | url = https://github.com/cloudify-community/blueprint-examples.git 4 | -------------------------------------------------------------------------------- /CHANGELOG.txt: -------------------------------------------------------------------------------- 1 | 1.0.0: 2 | - Lifecycle support for pods and services. 3 | 4 | 1.1.0: 5 | - Get master configuration from runtime properties 6 | - Add ReplicaSet support 7 | - Add PersistentVolume and StorageClass support 8 | - Support Google Authentication for GKE 9 | 10 | 1.2.0: 11 | - Delete Replica Sets by name 12 | - Deployment resource support 13 | - Replication Controller support 14 | - File resource definition support 15 | 16 | 1.2.1: 17 | - Multiple-file resource support. 18 | - Distinguished Main type to cloudify.kubernetes.resources.BlueprintDefinedResource and cloudify.kubernetes.resources.FileDefinedResource. 19 | 20 | 1.2.2: 21 | - Support ConfigMaps. 22 | 23 | 1.3.0: 24 | - Pod resource type state verification in create and delete. 25 | 26 | 1.3.1: 27 | - Alter state verification handling. 28 | 29 | 1.3.1.1: 30 | - Set dependencies 31 | 32 | 1.4.0: 33 | - Add update workflow 34 | 35 | 2.0.0: 36 | - Support propagate resource deletion policy. 37 | - Update Kubernetes Python Client to version 4.0.0 38 | - Add RBAC node type. 39 | - Support checking statuses for Kubernetes resources. 40 | 41 | 2.0.0.1: 42 | - Fix Conditional service check 43 | 44 | 2.1.0: 45 | - Add support for creating resources on Kubernetes using service account token 46 | 47 | 2.2.0: 48 | - Adding an "update_resource_definition" workflow. 49 | 50 | 2.2.1: 51 | - Use patch_ methods for update instead of replace_ in the mappings module. 52 | 53 | 2.2.2: 54 | - Improve Pod state verification. 55 | 56 | 2.3.0: 57 | - Add Node type. 58 | - Add use_external_resource property. Performs read operations on create and delete instead of performing create and delete. 59 | 60 | 2.4.0: 61 | - Support `---` separator in resource file `cloudify.kubernetes.resources.MultipleFileDefinedResources`. 62 | - Bump client library to `kubernetes==9.0.0` 63 | 64 | 2.4.1: 65 | - Fix bug in `---` separator. 66 | 67 | 2.5.0: 68 | - Add support "resumable" actions. 69 | 70 | 2.6.0: 71 | - Bump client library to `kubernetes==10.0.1` 72 | - Update node mappings for 16.1 deprecation notices: https://kubernetes.io/blog/2019/07/18/api-deprecations-in-1-16/. 73 | - Fix bug that prevented using multiple resource configurations in a single file. 74 | - Add poststart operation mapped to read resource. 75 | 76 | 2.6.1: 77 | - Fix bug in status check for persistent volume. 78 | 79 | 2.6.2: 80 | - Add namespace type. 81 | 82 | 2.6.3: 83 | - Fix bug in Custom Resource Delete when use_external_resource is true. 84 | 85 | 2.6.4: 86 | - Handle empty file resource. 87 | 88 | 2.6.5: 89 | - Fix issue with `GCPServiceAccountAuthentication`. 90 | 91 | 2.7.0: 92 | - added client_config property to all resources 93 | 94 | 2.7.1: 95 | - added cloudify.kubernetes.resources.ResourceWithValidateStatus to handle all the resources that didnt derived from ResourceBase type. 96 | 97 | 2.7.2: 98 | - Support Python 3 99 | 100 | 2.8.0: 101 | - Remove trailing slash in URL. 102 | - Support modifying a resource type and number during update workflow. 103 | 104 | 2.8.1: 105 | - Update wagon builder to py2py3 wagon. 106 | - Add 5.1.0 integration tests. 107 | 108 | 2.8.2: 109 | - Add resource_state_function to resource_create,custom_resource_create and custom_resource_delete. 110 | 111 | 2.8.3: 112 | - Add simple validation on resource definition fields. 113 | 114 | 2.9.0: 115 | - Resolve resource uniqueness on name, kind, and namespace. 116 | - Namespace resolution feature. 117 | 2.9.1: 118 | - Bump PyYAML 119 | 2.9.2: 120 | - Improved exception logging. 121 | 2.9.3: 122 | - Correct issue in build. 123 | 2.9.4: 124 | - Support file content in addition to paths for ssl_ca_cert, cert_file, key_file. 125 | 2.10.0: 126 | - Add pull operation. 127 | - Save in resource_definitions the updated result from kubernetes API. 128 | 2.11.0: 129 | - Support Custom Resource Definitions and Custom Objects API. 130 | 2.11.1: 131 | - Degrade Kubernetes 132 | 2.11.2: 133 | - Add previously used options to options dictionary. 134 | 2.12.0: 135 | - Add use_if_exists property to all node types. 136 | - Add create_if_missing property to all node types. 137 | 2.12.1: 138 | - Update client. 139 | 2.13.0: 140 | - Add token type. 141 | 2.13.1: 142 | - RD-2199 - Support AKS Discovery 143 | 2.13.2: 144 | - RD-2198 - Support GKE Discovery 145 | 2.13.3: 146 | - RD-3325-Handle-Superfluous-Annotations 147 | 2.13.4: 148 | - CYBL-1297: Enable skipping __resource_definitions runtime property. 149 | 2.13.5: Rerelease with aarch64 support. 150 | 2.13.6: RD-3555 - Future Proof Kubernetes API Mapping 151 | 2.13.7: RD-4205 Fix issue with file content 152 | 2.13.8: 153 | - RD-1899 Speed up uninstall workflow. 154 | - RD-4411 Add update resource test. 155 | 2.13.9: 156 | -RD-4494-write-workflow-eks-token-refresh. 157 | 2.13.10: V2 Plugin YAML 158 | 2.13.11: RD-4411 Improve cloudify-kubernetes-plugin to support API v1.22.5. 159 | 2.13.12: Adjust V2 Plugin YAML invalid-types. 160 | 2.13.13: rerelease v2 without dsl 4 types 161 | 2.13.14: release redhat 8 wagon and dsl 1_4 162 | 2.13.15: 163 | - updated mappings 164 | - deprecate old node types and old relationship types. 165 | 2.13.16: 166 | - re-release to fix inconsistant version in v2_plugin.yaml. 167 | 2.13.17: Check Status Workflow Support 168 | 2.13.18: release workflow 169 | 2.13.19: Update google auth library. 170 | 2.13.20: Remove constraints in 1 4 plugin YAML and improve node instance resolution. 171 | 2.13.21: RD-6518 - K8s LB Service Endpoint IP not available in Cloudify with AKS. 172 | 2.13.22: RD-6692 Update Kubernetes Client 173 | 2.13.23: 174 | - Fix SharedCluster 175 | - Update Workflow 176 | - Check Drift Workflow 177 | - Check Status Workflow 178 | 2.13.24: Support Azure Service Account Authentication. 179 | 2.13.25: add __version__.py file in cloudify_kubernetes folder. 180 | 2.13.26: check plugin and update circleci config. 181 | 2.13.27: Use DSL 1.5. 182 | 2.13.28: Update dependencies. 183 | 2.13.29: Update requirements. 184 | 2.13.30: Add back support for master type. 185 | 2.13.31: added .drp folder for trufflehog. 186 | 2.13.32: blackduck. 187 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for collecting and installing requirements for nativeedge-plugins-sdk. 2 | VENVS := $(shell pyenv virtualenvs --skip-aliases --bare | grep 'project\b') 3 | FUSION_COMMON := fusion-common 4 | FUSION_AGENT := fusion-agent 5 | FUSION_MANAGER := fusion-manager 6 | NATIVEEDGE_SDK := cloudify-utilities-plugins-sdk 7 | INCUBATOR_DOMAIN := github.com/cloudify-incubator 8 | BRANCH := master 9 | SHELL := /bin/bash 10 | DOMAIN=${GH_TOKEN}@github.com/fusion-e 11 | 12 | default: 13 | make download_from_git 14 | make setup_local_virtual_env 15 | make run_tests 16 | 17 | compile: 18 | make download_from_git 19 | make setup_local_virtual_env 20 | 21 | download_from_git: 22 | make download_fusion_common 23 | make download_fusion_agent 24 | make download_fusion_manager 25 | make download_nativeedge_sdk 26 | 27 | setup_local_virtual_env: 28 | ifneq ($(VENVS),) 29 | @echo We have $(VENVS) 30 | pyenv virtualenv-delete -f project && pyenv deactivate 31 | endif 32 | pyenv virtualenv 3.11 project 33 | 34 | download_fusion_common: 35 | ifneq ($(wildcard ${HOME}/${FUSION_COMMON}*),) 36 | @echo "Found ${HOME}/${FUSION_COMMON}." 37 | else 38 | git clone --depth 1 https://${DOMAIN}/${FUSION_COMMON}.git ${HOME}/${FUSION_COMMON} -b ${BRANCH} && cd ${HOME}/${FUSION_COMMON} && cd 39 | endif 40 | 41 | download_fusion_agent: 42 | ifneq ($(wildcard ${HOME}/${FUSION_AGENT}*),) 43 | @echo "Found ${HOME}/${FUSION_AGENT}." 44 | else 45 | git clone --depth 1 https://${DOMAIN}/${FUSION_AGENT}.git ${HOME}/${FUSION_AGENT} -b ${BRANCH} && cd ${HOME}/${FUSION_AGENT} && cd 46 | endif 47 | 48 | download_fusion_manager: 49 | ifneq ($(wildcard ${HOME}/${FUSION_MANAGER}*),) 50 | @echo "Found ${HOME}/${FUSION_MANAGER}." 51 | else 52 | git clone --depth 1 https://${DOMAIN}/${FUSION_MANAGER}.git ${HOME}/${FUSION_MANAGER} -b ${BRANCH} && cd ${HOME}/${FUSION_MANAGER}/mgmtworker && cd 53 | endif 54 | 55 | download_nativeedge_sdk: 56 | ifneq ($(wildcard ${HOME}/${NATIVEEDGE_SDK}*),) 57 | @echo "Found ${HOME}/${NATIVEEDGE_SDK}." 58 | else 59 | git clone --depth 1 https://${INCUBATOR_DOMAIN}/${NATIVEEDGE_SDK}.git ${HOME}/${NATIVEEDGE_SDK} -b master && cd ${HOME}/${NATIVEEDGE_SDK} && cd 60 | endif 61 | 62 | cleanup: 63 | pyenv virtualenv-delete -f project 64 | rm -rf ${FUSION_MANAGER} ${FUSION_AGENT} ${FUSION_COMMON} 65 | 66 | run_tests: 67 | @echo "Starting executing the tests." 68 | git submodule init 69 | git submodule update --remote --recursive | true 70 | HOME=${HOME} VIRTUAL_ENV=${HOME}/.pyenv/${VENVS} tox 71 | 72 | clrf: 73 | @find . \( -path ./.tox -prune -o -path ./.git -prune \) -o -type f -exec dos2unix {} \; 74 | 75 | wheels: 76 | @echo "Creating wheels..." 77 | @pip wheel ${HOME}/${FUSION_COMMON}/ -w /workspace/build/ --find-links /workspace/build 78 | @pip wheel ${HOME}/${FUSION_AGENT}/ -w /workspace/build/ --find-links /workspace/build 79 | @pip wheel ${HOME}/${FUSION_MANAGER}/mgmtworker -w /workspace/build/ --find-links /workspace/build 80 | @pip wheel ${HOME}/${NATIVEEDGE_SDK} -w /workspace/build/ --find-links /workspace/build -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CircleCI](https://circleci.com/gh/cloudify-incubator/cloudify-kubernetes-plugin.svg?style=svg)](https://circleci.com/gh/cloudify-incubator/cloudify-kubernetes-plugin) 2 | 3 | # Cloudify Kubernetes Plugin 4 | 5 | ### Overview 6 | 7 | Cloudify Kubernetes Plugin enables possibility of creating and deleting resources 8 | hosted by some Kubernetes cluster using Cloudify blueprints. 9 | 10 | Plugin is using Kubernetes python client 11 | (https://github.com/kubernetes-incubator/client-python) 12 | to communicate with Kubernetes Master API. 13 | 14 | All node types and relationships exposed by plugin are defined in *plugin.yaml* file. 15 | 16 | Main entrypoints to python logic are defined in *tasks.py* file. 17 | 18 | 19 | #### Blueprint concept 20 | 21 | Plugin exposes two kinds of node types: 22 | 23 | * ***cloudify.kubernetes.nodes.Master*** 24 | 25 | Node type describes Kubernetes maser configuration. 26 | It is responsible for handling all data required to use Kubernetes API from outside. 27 | Every blueprint using plugin has to define node template of this type. 28 | It defines two properties: 29 | 30 | - ***configuration*** 31 | 32 | - ***authentication*** 33 | 34 | * ***cloudify.kubernetes.resources.**** 35 | 36 | Family of node types designed to describe Kubernetes resources (e.g. Pods, Deployments, Services etc.) 37 | Plugin supports different ways of Kubernetes resources definition. 38 | Resources definition used in Cloudify blueprints are also compliant with Kubernetes YAML schema. 39 | 40 | Plugin defines also one relationship: 41 | 42 | ***cloudify.kubernetes.relationships.managed_by_master*** 43 | 44 | It is required for each ***cloudify.kubernetes.resources.**** node template 45 | to be bounded using this relationship to the ***cloudify.kubernetes.nodes.Master*** node template. 46 | 47 | During installation of deployment for all *cloudify.kubernetes.resources.** nodes 48 | plugin is looking for target of defined *managed_by_master* relationship to find related Master node. 49 | Data stored by Master node bounded using relationship to Resource node will be used to perform API call to create / delete this resource. 50 | Result of each operation is stored in *kubernetes* runtime_property for each resource node. 51 | 52 | ``` 53 | master: 54 | type: cloudify.kubernetes.nodes.Master 55 | properties: 56 | configuration: 57 | file_content: { get_input: kubernetes_configuration_file_content } 58 | 59 | resource: 60 | type: cloudify.kubernetes.resources.Pod 61 | properties: 62 | [...] 63 | - type: cloudify.kubernetes.relationships.managed_by_master 64 | target: master 65 | 66 | ``` 67 | 68 | ### Master configuration possibilities 69 | 70 | There are four possible ways of *cloudify.kubernetes.nodes.Master* (Kubernetes API python client) configuration. 71 | Each method is associated with one key (below) and required value which you should put under *configuration* property of *cloudify.kubernetes.nodes.Master* node. 72 | For each Master node you should choose one method (one dictionary entry for *configuration* property should be defined): 73 | 74 | * ***blueprint_file_name*** - value should be relative to the blueprint path to Kubernetes config file (contained by blueprint archive) 75 | 76 | * ***manager_file_path*** - value should be absolute path to Kubernetes config file previously uploaded into Cloudify Manager virtual machine 77 | 78 | * ***file_content*** - value should be (YAML) content of Kubernetes config file 79 | 80 | * ***api_options*** - value should be a dictionary contains basic Kubernetes API properties: 81 | - *host* (HTTP/HTTPS URL to Kubernetes API) 82 | - *ssl_ca_cert* 83 | - *cert_file* 84 | - *key_file* 85 | - *verify_ssl* 86 | 87 | Kubernetes config file is by default stored in: 88 | 89 | ```~/.kube/config``` 90 | 91 | on Kubernetes Master VM. You can also obtain it executing: 92 | 93 | ```kubectl config view --raw``` 94 | 95 | ### Master authentication possibilities 96 | 97 | Plugin has been designed to support different Kubernetes clusters providers. 98 | As *authentication* property of Master node you can specify dictionary with key and value: 99 | 100 | * ***gcp_service_account*** - value should be (JSON) content of Google Cloud Platform Service Accout file 101 | 102 | ### Resources definition possibilities 103 | 104 | * ***cloudify.kubernetes.resources.BlueprintDefinedResource*** 105 | 106 | Simplest way to define kubernetes resource. 107 | It uses Kubernetes YAML description to define resource. 108 | Properties of *cloudify.kubernetes.resources.BlueprintDefinedResource*: 109 | 110 | - *definition* - Kubernetes YAML resource definition 111 | - *options* - Kubernetes python client operation options 112 | 113 | Only subtypes of BlueprintDefinedResource can be used. 114 | Each subtype represents single kind of kubernetes resource. 115 | Currently supported resources: 116 | 117 | - *cloudify.kubernetes.resources.Deployment* 118 | - *cloudify.kubernetes.resources.Pod* 119 | - *cloudify.kubernetes.resources.ReplicaSet* 120 | - *cloudify.kubernetes.resources.ReplicationController* 121 | - *cloudify.kubernetes.resources.Service* 122 | - *cloudify.kubernetes.resources.PersistentVolume* 123 | - *cloudify.kubernetes.resources.StorageClass* 124 | - *cloudify.kubernetes.resources.ConfigMap* 125 | 126 | Example blueprint: 127 | 128 | ```examples/simple-blueprint_defined_resource.yaml``` 129 | 130 | * ***cloudify.kubernetes.resources.CustomBlueprintDefinedResource*** 131 | 132 | Node type extending *cloudify.kubernetes.resources.BlueprintDefinedResource*. 133 | It has been introduced to support some custom kinds of Kubernetes resources 134 | which hasn't defined their own subtype definition in *plugin.yaml*. 135 | 136 | This node type has the same properties like *BlueprintDefinedResource* 137 | and additional one: *api_mapping* - containing information about Kubernetes python client objects 138 | which should be used to create / delete this resource object on Kubernetes cluster. 139 | 140 | ``` 141 | create: 142 | api: CoreV1Api 143 | method: create_namespaced_pod 144 | payload: V1Pod 145 | read: 146 | api: CoreV1Api 147 | method: read_namespaced_pod 148 | delete: 149 | api: CoreV1Api 150 | method: delete_namespaced_pod 151 | payload: V1DeleteOptions 152 | ``` 153 | Detailed info about Kubernetes python client objects / methods you can find here: 154 | 155 | https://github.com/kubernetes-incubator/client-python/tree/master/kubernetes 156 | 157 | Example blueprint: 158 | 159 | ```examples/simple-custom_blueprint_defined_resource.yaml``` 160 | 161 | * ***cloudify.kubernetes.resources.FileDefinedResource*** 162 | 163 | It enables creation / deletion of Kubernetes resource defined in YAML file. 164 | This file may be specified using relative path to file in blueprint or external URL. 165 | It should be defined as *file/resource_path* property. 166 | 167 | Example blueprint: 168 | 169 | ```examples/simple-file_defined_resource.yaml``` 170 | 171 | * ***cloudify.kubernetes.resources.MultipleFileDefinedResources*** 172 | 173 | The same like *cloudify.kubernetes.resources.FileDefinedResource*, but it takes list of multiple kubernetes resources to be deployed. 174 | This list should be defined as *files* property. Each item in this list should be one-item dictionary contains *resource_path* key and path / URL to file as value. 175 | 176 | Example blueprint: 177 | 178 | ```examples/simple-multiple_file_defined_resources.yaml``` 179 | 180 | 181 | 182 | 183 | ### Upload Kubernetes Dashboard UI Blueprint To Manager 184 | ```shell 185 | 186 | 1. Update dashboard input file ***dashboard_input.yaml*** 187 | 2. Install the dashboard: cfy install -b kubernetes-dashboard -n kubernetes-dashboard examples/dashboard.yaml -i examples/inputs/dashboard-inputs.yaml 188 | 3. Run the following command: cfy deployment outputs kubernetes-dashboard 189 | 4. The output of deployment command should generate ***dashboard_url*** && ***bearer_token*** 190 | 5. Access the Dashboard using ***dashboard_url*** (https://MASTER_IP:DASHBOARD_PORT) 191 | 6. You may need to open ***DASHBOARD_PORT*** on your security group on which dashboard is running if it is not already open 192 | 7. Login to the Dashboard by selecting token authentication, use the token value of ***{{bearer_token}}*** 193 | ``` 194 | 195 | ## Examples 196 | For official blueprint examples using this Cloudify plugin, please see [Cloudify Community Blueprints Examples](https://github.com/cloudify-community/blueprint-examples/). 197 | -------------------------------------------------------------------------------- /cloudify_kubernetes/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017-2019 Cloudify Platform Ltd. All rights reserved 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /cloudify_kubernetes/__version__.py: -------------------------------------------------------------------------------- 1 | version = '2.13.32' 2 | -------------------------------------------------------------------------------- /cloudify_kubernetes/_compat.py: -------------------------------------------------------------------------------- 1 | ######## 2 | # Copyright (c) 2020 Cloudify Platform Ltd. All rights reserved 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # * See the License for the specific language governing permissions and 14 | # * limitations under the License. 15 | 16 | # flake8: noqa 17 | # pylint: skip-file 18 | 19 | import sys 20 | import inspect 21 | PY2 = sys.version_info[0] == 2 22 | PY311 = sys.version_info[0] == 3 and sys.version_info[1] == 11 23 | 24 | if PY2: 25 | text_type = unicode 26 | getfullargspec = inspect.getargspec 27 | else: 28 | getfullargspec = inspect.getfullargspec 29 | text_type = str 30 | 31 | __all__ = [ 32 | 'PY2', 'text_type', 'getfullargspec' 33 | ] 34 | -------------------------------------------------------------------------------- /cloudify_kubernetes/k8s/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017-2019 Cloudify Platform Ltd. All rights reserved 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from .authentication import KubernetesApiAuthenticationVariants # noqa 16 | from .client import (KubernetesResourceDefinition, # noqa 17 | CloudifyKubernetesClient) # noqa 18 | from .config import KubernetesApiConfigurationVariants # noqa 19 | from .mapping import (get_mapping, # noqa 20 | KubernetesApiMapping) # noqa 21 | from .exceptions import (KuberentesError, # noqa 22 | KuberentesApiInitializationFailedError, # noqa 23 | KuberentesApiOperationError, # noqa 24 | KuberentesAuthenticationError, # noqa 25 | KuberentesInvalidDefinitionError, # noqa 26 | KuberentesInvalidPayloadClassError, # noqa 27 | KuberentesInvalidApiClassError, # noqa 28 | KuberentesInvalidApiMethodError, # noqa 29 | KuberentesMappingNotFoundError) # noqa 30 | 31 | # Monkey Patch "V1beta1CustomResourceDefinitionStatus" 32 | # https://github.com/kubernetes-client/python/issues/415 33 | -------------------------------------------------------------------------------- /cloudify_kubernetes/k8s/authentication.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017-2019 Cloudify Platform Ltd. All rights reserved 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import json 16 | 17 | import google.auth.transport.requests 18 | from google.oauth2 import service_account 19 | 20 | from .._compat import text_type 21 | from .exceptions import KuberentesAuthenticationError 22 | 23 | 24 | class KubernetesApiAuthentication(object): 25 | 26 | def __init__(self, logger, authentication_data): 27 | self.logger = logger 28 | self.authentication_data = authentication_data 29 | 30 | def _do_authenticate(self, api): 31 | if api: 32 | return api 33 | 34 | def authenticate(self, api): 35 | authenticated = self._do_authenticate(api) 36 | if authenticated: 37 | return authenticated 38 | 39 | raise KuberentesAuthenticationError( 40 | 'Cannot use {0} authenticate option for data: {1} and API: {2}' 41 | .format( 42 | self.__class__.__name__, 43 | self.authentication_data, 44 | api 45 | ) 46 | ) 47 | 48 | 49 | class StandardBearerToken(KubernetesApiAuthentication): 50 | 51 | def _do_authenticate(self, api): 52 | 53 | if hasattr(api, 'api_key') and hasattr(api, 'api_key_prefix'): 54 | return api 55 | 56 | raise KuberentesAuthenticationError( 57 | 'Cannot use {0} authenticate option for data: {1} and API: {2}' 58 | .format( 59 | self.__class__.__name__, 60 | self.authentication_data, 61 | api 62 | ) 63 | ) 64 | 65 | 66 | class GCPServiceAccountAuthentication(KubernetesApiAuthentication): 67 | 68 | K8S_API_AUTHORIZATION = 'authorization' 69 | 70 | ENV_CREDENTIALS = 'GOOGLE_APPLICATION_CREDENTIALS' 71 | 72 | PROPERTY_GCE_SERVICE_ACCOUNT = 'gcp_service_account' 73 | 74 | SCOPES = ['https://www.googleapis.com/auth/cloud-platform'] 75 | 76 | TOKEN_PREFIX = 'Bearer' 77 | 78 | def _do_authenticate(self, configuration): 79 | service_account_file_content = self.authentication_data.get( 80 | self.PROPERTY_GCE_SERVICE_ACCOUNT 81 | ) 82 | if service_account_file_content: 83 | 84 | if isinstance(service_account_file_content, text_type): 85 | service_account_file_content = \ 86 | json.loads(service_account_file_content) 87 | 88 | storage_credentials = \ 89 | service_account.Credentials.from_service_account_info( 90 | service_account_file_content) 91 | scoped_credentials = storage_credentials.with_scopes(self.SCOPES) 92 | auth_req = google.auth.transport.requests.Request() 93 | scoped_credentials.refresh(auth_req) 94 | token = scoped_credentials.token 95 | configuration.api_key[self.K8S_API_AUTHORIZATION] = token 96 | configuration.api_key_prefix[self.K8S_API_AUTHORIZATION] = \ 97 | self.TOKEN_PREFIX 98 | return configuration 99 | 100 | raise KuberentesAuthenticationError( 101 | 'Cannot use {0} authenticate option for data: {1} and API: {2}' 102 | .format( 103 | self.__class__.__name__, 104 | self.authentication_data, 105 | configuration 106 | ) 107 | ) 108 | 109 | 110 | class KubernetesApiAuthenticationVariants(KubernetesApiAuthentication): 111 | 112 | VARIANTS = ( 113 | GCPServiceAccountAuthentication, 114 | StandardBearerToken, 115 | ) 116 | 117 | def authenticate(self, api): 118 | self.logger.debug('Checking Kubernetes authentication options') 119 | 120 | for variant in self.VARIANTS: 121 | try: 122 | candidate = variant(self.logger, self.authentication_data)\ 123 | .authenticate(api) 124 | 125 | self.logger.debug( 126 | 'Authentication option {0} will be used' 127 | .format(variant.__name__) 128 | ) 129 | return candidate 130 | except KuberentesAuthenticationError: 131 | self.logger.debug( 132 | 'Authentication option {0} cannot be used' 133 | .format(variant.__name__) 134 | ) 135 | 136 | self.logger.warn( 137 | 'Cannot initialize Kubernetes API - no suitable authentication ' 138 | 'variant found for {0} properties' 139 | .format(self.authentication_data) 140 | ) 141 | return None 142 | -------------------------------------------------------------------------------- /cloudify_kubernetes/k8s/config.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017-2019 Cloudify Platform Ltd. All rights reserved 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | import yaml 17 | import kubernetes 18 | 19 | from kubernetes.config.kube_config import KUBE_CONFIG_DEFAULT_LOCATION 20 | 21 | from .exceptions import KuberentesApiInitializationFailedError 22 | 23 | 24 | class KubernetesApiConfiguration(object): 25 | 26 | def __init__(self, logger, configuration_data, **kwargs): 27 | self.logger = logger 28 | self.configuration_data = configuration_data 29 | self.kwargs = kwargs 30 | 31 | def _do_prepare_api(self): 32 | return None 33 | 34 | def prepare_api(self): 35 | api = self._do_prepare_api() 36 | 37 | if not api: 38 | raise KuberentesApiInitializationFailedError( 39 | 'Cannot initialize Kubernetes API with {0} configuration ' 40 | 'and {1} properties' 41 | .format(self.__class__.__name__, self.configuration_data)) 42 | 43 | return api 44 | 45 | 46 | class BlueprintFileConfiguration(KubernetesApiConfiguration): 47 | BLUEPRINT_FILE_NAME_KEY = 'blueprint_file_name' 48 | 49 | def _do_prepare_api(self): 50 | if self.BLUEPRINT_FILE_NAME_KEY in self.configuration_data: 51 | blueprint_file_name = self.configuration_data[ 52 | self.BLUEPRINT_FILE_NAME_KEY 53 | ] 54 | 55 | try: 56 | download_resource = self.kwargs.get('download_resource') 57 | manager_file_path = download_resource(blueprint_file_name) 58 | 59 | if manager_file_path and os.path.isfile( 60 | os.path.expanduser(manager_file_path) 61 | ): 62 | configuration = kubernetes.client.Configuration() 63 | kubernetes.config.load_kube_config( 64 | config_file=manager_file_path, 65 | client_configuration=configuration 66 | ) 67 | return configuration 68 | except Exception as e: 69 | self.logger.error( 70 | 'Cannot download config file from blueprint: {0}' 71 | .format(str(e)) 72 | ) 73 | 74 | return None 75 | 76 | 77 | class ManagerFilePathConfiguration(KubernetesApiConfiguration): 78 | MANAGER_FILE_PATH_KEY = 'manager_file_path' 79 | 80 | def _do_prepare_api(self): 81 | if self.MANAGER_FILE_PATH_KEY in self.configuration_data: 82 | manager_file_path = self.configuration_data[ 83 | self.MANAGER_FILE_PATH_KEY 84 | ] 85 | 86 | if manager_file_path and os.path.isfile( 87 | os.path.expanduser(manager_file_path) 88 | ): 89 | configuration = kubernetes.client.Configuration() 90 | kubernetes.config.load_kube_config( 91 | config_file=manager_file_path, 92 | client_configuration=configuration 93 | ) 94 | return configuration 95 | 96 | return None 97 | 98 | 99 | class FileContentConfiguration(KubernetesApiConfiguration): 100 | FILE_CONTENT_KEY = 'file_content' 101 | 102 | def _do_prepare_api(self): 103 | 104 | if self.FILE_CONTENT_KEY in self.configuration_data: 105 | file_content = self.configuration_data[self.FILE_CONTENT_KEY] 106 | config_base_path = os.path.abspath( 107 | os.path.dirname( 108 | os.path.expanduser(KUBE_CONFIG_DEFAULT_LOCATION) 109 | ) 110 | ) 111 | if isinstance(file_content, str): 112 | file_content = yaml.safe_load(file_content) 113 | 114 | loader = kubernetes.config.kube_config.KubeConfigLoader( 115 | config_dict=file_content, 116 | config_base_path=config_base_path) 117 | 118 | config = type.__call__(kubernetes.client.Configuration) 119 | loader.load_and_set(config) 120 | kubernetes.client.Configuration.set_default(config) 121 | 122 | return kubernetes.client 123 | 124 | return None 125 | 126 | 127 | class ApiOptionsConfiguration(KubernetesApiConfiguration): 128 | API_OPTIONS_KEY = 'api_options' 129 | API_OPTIONS_HOST_KEY = 'host' 130 | API_OPTIONS_ALL_KEYS = ['host', 'ssl_ca_cert', 'cert_file', 'key_file', 131 | 'verify_ssl', 'api_key', 'debug'] 132 | 133 | def _do_prepare_api(self): 134 | if self.API_OPTIONS_KEY in self.configuration_data: 135 | api_options = self.configuration_data[self.API_OPTIONS_KEY] 136 | 137 | if self.API_OPTIONS_HOST_KEY not in api_options: 138 | return None 139 | else: 140 | api_options[self.API_OPTIONS_HOST_KEY] = \ 141 | api_options[self.API_OPTIONS_HOST_KEY].rstrip('/') 142 | 143 | configuration = kubernetes.client.Configuration() 144 | 145 | for key in self.API_OPTIONS_ALL_KEYS: 146 | if key in api_options: 147 | # Update the api_key value in order to use on the header 148 | # api request 149 | if key == 'api_key': 150 | api_options[key] =\ 151 | {"authorization": 152 | "Bearer {0}".format(api_options[key])} 153 | setattr(configuration, key, api_options[key]) 154 | return configuration 155 | return None 156 | 157 | 158 | class KubernetesApiConfigurationVariants(KubernetesApiConfiguration): 159 | 160 | VARIANTS = ( 161 | BlueprintFileConfiguration, 162 | ManagerFilePathConfiguration, 163 | FileContentConfiguration, 164 | ApiOptionsConfiguration 165 | ) 166 | 167 | def _do_prepare_api(self): 168 | self.logger.debug( 169 | 'Checking how Kubernetes API should be configured' 170 | ) 171 | 172 | for variant in self.VARIANTS: 173 | try: 174 | api_candidate = variant( 175 | self.logger, 176 | self.configuration_data, 177 | **self.kwargs 178 | ).prepare_api() 179 | 180 | self.logger.debug( 181 | 'Configuration option {0} will be used' 182 | .format(variant.__name__) 183 | ) 184 | 185 | return api_candidate 186 | except KuberentesApiInitializationFailedError: 187 | self.logger.debug( 188 | 'Configuration option {0} cannot be used' 189 | .format(variant.__name__) 190 | ) 191 | 192 | raise KuberentesApiInitializationFailedError( 193 | 'Cannot initialize Kubernetes API - no suitable configuration ' 194 | 'variant found for {0} properties' 195 | .format(self.configuration_data) 196 | ) 197 | -------------------------------------------------------------------------------- /cloudify_kubernetes/k8s/exceptions.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017-2019 Cloudify Platform Ltd. All rights reserved 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | class KuberentesError(Exception): 17 | pass 18 | 19 | 20 | class KuberentesApiInitializationFailedError(KuberentesError): 21 | pass 22 | 23 | 24 | class KuberentesApiOperationError(KuberentesError): 25 | pass 26 | 27 | 28 | class KuberentesAuthenticationError(KuberentesError): 29 | pass 30 | 31 | 32 | class KuberentesMappingNotFoundError(KuberentesError): 33 | pass 34 | 35 | 36 | class KuberentesInvalidDefinitionError(KuberentesError): 37 | pass 38 | 39 | 40 | class KuberentesInvalidPayloadClassError(KuberentesError): 41 | pass 42 | 43 | 44 | class KuberentesInvalidApiClassError(KuberentesError): 45 | pass 46 | 47 | 48 | class KuberentesInvalidApiMethodError(KuberentesError): 49 | pass 50 | -------------------------------------------------------------------------------- /cloudify_kubernetes/k8s/operations.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017-2019 Cloudify Platform Ltd. All rights reserved 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | from kubernetes.client.rest import ApiException 15 | 16 | from .exceptions import KuberentesApiOperationError 17 | 18 | 19 | class KubernetesOperartion(object): 20 | 21 | API_ACCEPTED_ARGUMENTS = [] 22 | 23 | def __init__(self, api_method, api_method_arguments_names): 24 | self.api_method = api_method 25 | self.api_method_arguments_names = api_method_arguments_names 26 | 27 | def _prepare_arguments(self, arguments): 28 | result_arguments = {} 29 | 30 | for mandatory_argument_name in self.api_method_arguments_names: 31 | if mandatory_argument_name in arguments: 32 | result_arguments[mandatory_argument_name] = arguments[ 33 | mandatory_argument_name 34 | ] 35 | else: 36 | raise KuberentesApiOperationError( 37 | 'Invalid input data for execution of Kubernetes API ' 38 | 'method: input argument {0} is not defined but is ' 39 | 'mandatory'.format(mandatory_argument_name)) 40 | 41 | for optional_argument_name in self.API_ACCEPTED_ARGUMENTS: 42 | if optional_argument_name in arguments: 43 | result_arguments[optional_argument_name] = arguments[ 44 | optional_argument_name 45 | ] 46 | 47 | return result_arguments 48 | 49 | def execute(self, arguments): 50 | try: 51 | return self.api_method(**self._prepare_arguments(arguments)) 52 | except ApiException as e: 53 | raise KuberentesApiOperationError( 54 | 'Operation execution failed. Exception during Kubernetes ' 55 | 'API call: {0}' 56 | .format(str(e))) 57 | 58 | 59 | class KubernetesCreateOperation(KubernetesOperartion): 60 | pass 61 | 62 | 63 | class KubernetesReadOperation(KubernetesOperartion): 64 | 65 | API_ACCEPTED_ARGUMENTS = ['exact', 'export'] 66 | 67 | 68 | class KubernetesUpdateOperation(KubernetesOperartion): 69 | pass 70 | 71 | 72 | class KubernetesDeleteOperation(KubernetesOperartion): 73 | 74 | API_ACCEPTED_ARGUMENTS = ['grace_period_seconds', 'propagation_policy'] 75 | -------------------------------------------------------------------------------- /cloudify_kubernetes/k8s/status_mapping.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017-2019 Cloudify Platform Ltd. All rights reserved 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Issue: Many Kubernetes API has a very confusing method for handling 16 | # Kubernetes resource status. 17 | # I want to use this file to trace the logic of a few and try to find 18 | # something that can be generalized from this. 19 | 20 | 21 | from cloudify import ctx 22 | from cloudify.exceptions import ( 23 | NonRecoverableError, OperationRetry) 24 | 25 | 26 | class KubernetesResourceStatus(object): 27 | 28 | def __init__(self, status, validate_status=False): 29 | self._status = status 30 | self.validate_status = validate_status 31 | 32 | @property 33 | def status(self): 34 | return self._status 35 | 36 | @property 37 | def status_message(self): 38 | return 'Status is {0}'.format(self._status) 39 | 40 | def is_resource_ready(self): 41 | pass 42 | 43 | def ready(self): 44 | ctx.logger.info('Checking if the resource is ready.') 45 | if not self.validate_status: 46 | ctx.logger.info('Ignoring status validation. ' 47 | 'You can toggle this with ' 48 | '"validate_resource_status" node property. ' 49 | 'Status: {0}'.format(self._status)) 50 | else: 51 | return self.is_resource_ready() 52 | 53 | 54 | class KubernetesPodStatus(KubernetesResourceStatus): 55 | 56 | @property 57 | def status(self): 58 | return self._status['phase'] 59 | 60 | def is_resource_ready(self): 61 | if self.status in ['Running', 'Succeeded']: 62 | ctx.logger.debug(self.status_message) 63 | elif self.status in ['Pending', 'Unknown']: 64 | raise OperationRetry(self.status_message) 65 | elif self.status in ['Failed']: 66 | raise NonRecoverableError(self.status_message) 67 | else: 68 | ctx.logger.error('Unexpected status. Please report: {0}'.format( 69 | self.status)) 70 | return False 71 | return True 72 | 73 | 74 | class KubernetesServiceStatus(KubernetesResourceStatus): 75 | 76 | @property 77 | def status(self): 78 | return self._status.get('load_balancer', {}).get('ingress', False) 79 | 80 | def is_resource_ready(self): 81 | if not self.status: 82 | raise OperationRetry(self.status_message) 83 | return True 84 | 85 | 86 | class KubernetesIngressStatus(KubernetesServiceStatus): 87 | 88 | pass 89 | 90 | 91 | class KubernetesDeploymentStatus(KubernetesResourceStatus): 92 | 93 | def is_resource_ready(self): 94 | if self.status['unavailable_replicas']: 95 | raise OperationRetry(self.status_message) 96 | return True 97 | 98 | 99 | class KubernetesPersistentVolumeClaimStatus(KubernetesResourceStatus): 100 | 101 | @property 102 | def status(self): 103 | return self._status['phase'] 104 | 105 | def is_resource_ready(self): 106 | if self.status in ['Pending', 'Available', 'Bound']: 107 | ctx.logger.debug(self.status_message) 108 | else: 109 | raise OperationRetry(self.status_message) 110 | return True 111 | 112 | 113 | class KubernetesPersistentVolumeStatus(KubernetesResourceStatus): 114 | 115 | def is_resource_ready(self): 116 | if self.status['phase'] in ['Bound', 'Available']: 117 | ctx.logger.debug(self.status_message) 118 | else: 119 | raise OperationRetry(self.status_message) 120 | return True 121 | 122 | 123 | class KubernetesReplicaSetStatus(KubernetesResourceStatus): 124 | 125 | def is_resource_ready(self): 126 | if self.status.get('ready_replicas') == self.status.get('replicas'): 127 | ctx.logger.debug(self.status_message) 128 | return True 129 | else: 130 | raise OperationRetry(self.status_message) 131 | 132 | 133 | class KubernetesReplicationControllerStatus(KubernetesReplicaSetStatus): 134 | pass 135 | 136 | 137 | class KubernetesDaemonSetStatus(KubernetesResourceStatus): 138 | 139 | def is_resource_ready(self): 140 | if not self.status['number_unavailable']: 141 | ctx.logger.debug(self.status_message) 142 | else: 143 | raise OperationRetry(self.status_message) 144 | return True 145 | 146 | 147 | class KubernetesStatefulSetStatus(KubernetesResourceStatus): 148 | 149 | def is_resource_ready(self): 150 | if self.status['ready_replicas']: 151 | ctx.logger.debug(self.status_message) 152 | else: 153 | raise OperationRetry(self.status_message) 154 | return True 155 | -------------------------------------------------------------------------------- /cloudify_kubernetes/tasks/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017-2019 Cloudify Platform Ltd. All rights reserved 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # The cloudify_kubernetes.tasks module has really got out of control. 16 | from .._compat import PY2 17 | if not PY2: 18 | from .shared_cluster import refresh_config # noqa 19 | from .operations import (read_token, # noqa 20 | create_token, # noqa 21 | delete_token, # noqa 22 | resource_read, # noqa 23 | resource_create, # noqa 24 | resource_update, # noqa 25 | resource_delete, # noqa 26 | get_token_status, # noqad 27 | custom_check_drift, # noqad 28 | custom_check_status, # noqad 29 | file_resource_read, # noqa 30 | file_resource_create, # noqa 31 | file_resource_update, # noqa 32 | file_resource_delete, # noqa 33 | custom_resource_read, # noqa 34 | custom_resource_create, # noqa 35 | custom_resource_update, # noqa 36 | custom_resource_delete, # noqa 37 | resource_read_check_drift, # noqa 38 | file_resource_check_drift, # noqa 39 | resource_read_check_status, # noqa 40 | file_resource_check_status, # noqa 41 | multiple_file_resource_read, # noqa 42 | multiple_file_resource_create, # noqa 43 | multiple_file_resource_delete, # noqa 44 | multiple_file_resource_check_status, # noqa 45 | multiple_file_resource_check_drift) # noqa 46 | from .api_calls import (_do_resource_read, # noqa 47 | _do_resource_create, # noqa 48 | _do_resource_update, # noqa 49 | _do_resource_status_check, # noqa 50 | _do_resource_delete) # noqa 51 | -------------------------------------------------------------------------------- /cloudify_kubernetes/tasks/api_calls.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017-2019 Cloudify Platform Ltd. All rights reserved 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # hack for import namespaced modules (google.auth) 16 | import cloudify_importer # noqa 17 | 18 | from cloudify import ctx 19 | from cloudify.exceptions import NonRecoverableError 20 | 21 | from ..k8s import status_mapping 22 | from ..k8s.exceptions import KuberentesApiOperationError 23 | from ..utils import (set_namespace, 24 | JsonCleanuper, 25 | PERMIT_REDEFINE, 26 | set_custom_resource, 27 | NODE_PROPERTY_OPTIONS, 28 | handle_delete_resource) 29 | 30 | 31 | def _do_resource_create(client, api_mapping, resource_definition, **kwargs): 32 | options = ctx.node.properties.get(NODE_PROPERTY_OPTIONS, kwargs) 33 | set_namespace(kwargs, resource_definition) 34 | set_custom_resource(options, resource_definition) 35 | perform_task = ctx.instance.runtime_properties.get('__perform_task', False) 36 | if not perform_task: 37 | return _do_resource_read( 38 | client, api_mapping, resource_definition, **kwargs) 39 | return JsonCleanuper(client.create_resource( 40 | api_mapping, 41 | resource_definition, 42 | options)).to_dict() 43 | 44 | 45 | def _do_resource_read(client, api_mapping, resource_definition, **kwargs): 46 | if not resource_definition: 47 | raise NonRecoverableError( 48 | 'No resource was found in runtime properties for reading. ' 49 | 'This can occur when the node property {0} is True, and ' 50 | 'resources were created and deleted out of order.'.format( 51 | PERMIT_REDEFINE) 52 | ) 53 | options = ctx.node.properties.get(NODE_PROPERTY_OPTIONS, kwargs) 54 | set_namespace(kwargs, resource_definition) 55 | set_custom_resource(options, resource_definition) 56 | return JsonCleanuper(client.read_resource( 57 | api_mapping, 58 | resource_definition, 59 | options 60 | )).to_dict() 61 | 62 | 63 | def _do_resource_update(client, api_mapping, resource_definition, **kwargs): 64 | options = ctx.node.properties.get(NODE_PROPERTY_OPTIONS, kwargs) 65 | set_namespace(kwargs, resource_definition) 66 | set_custom_resource(options, resource_definition) 67 | return JsonCleanuper(client.update_resource( 68 | api_mapping, 69 | resource_definition, 70 | ctx.node.properties.get(NODE_PROPERTY_OPTIONS, kwargs) 71 | )).to_dict() 72 | 73 | 74 | def _do_resource_status_check(resource_kind, response): 75 | ctx.logger.info('Checking resource status.') 76 | status_obj_name = 'Kubernetes{0}Status'.format(resource_kind) 77 | if hasattr(status_mapping, status_obj_name): 78 | return getattr(status_mapping, status_obj_name)( 79 | response['status'], 80 | ctx.node.properties['validate_resource_status']).ready() 81 | ctx.logger.debug( 82 | 'Resource status check not supported for {0}'.format( 83 | resource_kind)) 84 | return True 85 | 86 | 87 | def _do_resource_delete(client, api_mapping, resource_definition, 88 | resource_id, **kwargs): 89 | options = ctx.node.properties.get(NODE_PROPERTY_OPTIONS, kwargs) 90 | set_namespace(kwargs, resource_definition) 91 | set_custom_resource(options, resource_definition) 92 | 93 | # The required fields for all kubernetes resources are 94 | # - name 95 | # - namespace 96 | # - body 97 | 98 | # But the ``ReplicationController`` resource have only one required arg 99 | # which is namespace 100 | 101 | # Moreover all resources have also payload with type ``V1DeleteOptions`` 102 | # except ``ReplicationController`` that does not have one 103 | 104 | # The resource is not a type of ``ReplicationController`` then we must 105 | # pass all the required fields 106 | 107 | resource_exists = _check_if_resource_exists( 108 | client, api_mapping, resource_definition, **kwargs) 109 | handle_delete_resource(resource_exists) 110 | perform_task = ctx.instance.runtime_properties.get('__perform_task', False) 111 | if not perform_task: 112 | return JsonCleanuper(resource_exists).to_dict() 113 | return JsonCleanuper(client.delete_resource( 114 | api_mapping, 115 | resource_definition, 116 | resource_id, 117 | options, 118 | )).to_dict() 119 | 120 | 121 | def _check_if_resource_exists(client, 122 | api_mapping, 123 | resource_definition, 124 | **kwargs): 125 | try: 126 | return _do_resource_read( 127 | client, api_mapping, resource_definition, **kwargs) 128 | except KuberentesApiOperationError: 129 | ctx.logger.error('The resource {0} was not found.'.format( 130 | resource_definition.metadata['name'])) 131 | return 132 | -------------------------------------------------------------------------------- /cloudify_kubernetes/tasks/nested_resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudify-cosmo/cloudify-kubernetes-plugin/6b92ea403ad9ef56b93551bcbcadfcf869a79677/cloudify_kubernetes/tasks/nested_resources/__init__.py -------------------------------------------------------------------------------- /cloudify_kubernetes/tasks/nested_resources/tokens.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017-2021 Cloudify Platform Ltd. All rights reserved 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | SERVICE_ACCOUNT = """{{ 16 | 'apiVersion': 'v1', 17 | 'kind': 'ServiceAccount', 18 | 'metadata': {{ 19 | 'name': {service_account_name}, 20 | 'namespace': {service_account_namespace} 21 | }} 22 | }}""" 23 | 24 | CLUSTER_ROLE_BINDING = """{{ 25 | 'apiVersion': 'rbac.authorization.k8s.io/v1', 26 | 'kind': 'ClusterRoleBinding', 27 | 'metadata': {{ 28 | 'name': {service_account_name} 29 | }}, 30 | 'roleRef': {{ 31 | 'apiGroup': 'rbac.authorization.k8s.io', 32 | 'kind': 'ClusterRole', 33 | 'name': 'cluster-admin' 34 | }}, 35 | 'subjects': [ 36 | {{ 37 | 'kind': 'ServiceAccount', 38 | 'name': {service_account_name}, 39 | 'namespace': {service_account_namespace} 40 | }} 41 | ] 42 | }}""" 43 | 44 | SECRET = """{{ 45 | 'apiVersion': 'v1', 46 | 'kind': 'Secret', 47 | 'metadata': {{ 48 | 'name': {token_name}, 49 | 'annotations': {{ 50 | 'kubernetes.io/service-account.name': {service_account_name} 51 | }}, 52 | }}, 53 | 'type': 'kubernetes.io/service-account-token' 54 | }}""" 55 | 56 | 57 | def get_service_account_payload(name, namespace): 58 | return SERVICE_ACCOUNT.format( 59 | service_account_name=name, 60 | service_account_namespace=namespace 61 | ) 62 | 63 | 64 | def get_cluster_role_binding_payload(name, namespace): 65 | return CLUSTER_ROLE_BINDING.format( 66 | service_account_name=name, 67 | service_account_namespace=namespace 68 | ) 69 | 70 | 71 | def get_secret_payload(service_account_name): 72 | return SECRET.format( 73 | token_name=service_account_name + '-token', 74 | service_account_name=service_account_name) 75 | -------------------------------------------------------------------------------- /cloudify_kubernetes/tasks/shared_cluster.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017-2019 Cloudify Platform Ltd. All rights reserved 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # hack for import namespaced modules (google.auth) 16 | import cloudify_importer # noqa 17 | 18 | from cloudify import ctx 19 | from cloudify.exceptions import NonRecoverableError 20 | 21 | from .._compat import PY2, PY311 22 | from ..utils import (get_node, 23 | get_instance, 24 | with_rest_client, 25 | execute_workflow, 26 | get_kubernetes_cluster_node_instance_id) 27 | 28 | try: 29 | from cloudify_types.component.polling import ( 30 | poll_with_timeout, 31 | verify_execution_state, 32 | is_all_executions_finished) 33 | from cloudify_types.shared_resource.constants import \ 34 | WORKFLOW_EXECUTION_TIMEOUT 35 | except ImportError as e: 36 | if PY311: 37 | from mgmtworker.cloudify_types.polling import poll_with_timeout 38 | from mgmtworker.cloudify_types.component.polling import ( 39 | verify_execution_state, 40 | is_all_executions_finished) 41 | from mgmtworker.cloudify_types.shared_resource.constants import \ 42 | WORKFLOW_EXECUTION_TIMEOUT 43 | elif not PY2: 44 | raise e 45 | 46 | 47 | @with_rest_client 48 | def refresh_config(rest_client, **_): 49 | """cfy_extensions.cloudify_types.shared_resource.execute_workflow""" 50 | node = get_node(ctx) 51 | try: 52 | deployment_id = node.properties['resource_config']['deployment']['id'] 53 | except KeyError: 54 | raise NonRecoverableError( 55 | 'A deployment ID for the shared resource was not provided.') 56 | node_instance = get_kubernetes_cluster_node_instance_id(deployment_id) 57 | 58 | if not poll_with_timeout( 59 | lambda: is_all_executions_finished(rest_client, deployment_id), 60 | timeout=WORKFLOW_EXECUTION_TIMEOUT, 61 | expected_result=True): 62 | return ctx.operation.retry( 63 | 'The "{0}" deployment is not ready for workflow execution.'.format( 64 | deployment_id)) 65 | 66 | execution = execute_workflow( 67 | deployment_id, 68 | 'execute_operation', 69 | { 70 | 'operation': 'cloudify.interfaces.lifecycle.poststart', 71 | 'node_instance_ids': [node_instance] 72 | }) 73 | if not verify_execution_state( 74 | client=rest_client, 75 | execution_id=execution['id'], 76 | deployment_id=deployment_id, 77 | timeout=WORKFLOW_EXECUTION_TIMEOUT, 78 | redirect_log=True, 79 | workflow_state='terminated', 80 | instance_ctx=get_instance(ctx)): 81 | raise NonRecoverableError( 82 | 'Execution "{0}" failed for "{1}" deployment.'.format( 83 | execution['id'], deployment_id)) 84 | -------------------------------------------------------------------------------- /cloudify_kubernetes/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017-2019 Cloudify Platform Ltd. All rights reserved 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /cloudify_kubernetes/tests/resources/blueprint.yaml: -------------------------------------------------------------------------------- 1 | tosca_definitions_version: cloudify_dsl_1_3 2 | 3 | imports: 4 | - http://www.getcloudify.org/spec/cloudify/4.3/types.yaml 5 | - plugin.yaml 6 | 7 | node_templates: 8 | 9 | master: 10 | type: cloudify.kubernetes.nodes.Master 11 | properties: 12 | configuration: 13 | file_content: 14 | apiVersion: v1 15 | kind: Config 16 | preferences: {} 17 | current-context: kubernetes-admin@kubernetes 18 | clusters: 19 | - name: kubernetes 20 | cluster: 21 | certificate-authority-data: 22 | server: 23 | contexts: 24 | - name: kubernetes-admin@kubernetes 25 | context: 26 | cluster: kubernetes 27 | user: kubernetes-admin 28 | users: 29 | - name: kubernetes-admin 30 | user: 31 | client-certificate-data: 32 | client-key-data: 33 | 34 | nginx_pod: 35 | type: cloudify.kubernetes.resources.Pod 36 | properties: 37 | definition: 38 | apiVersion: v1 39 | kind: Pod 40 | metadata: 41 | name: nginx 42 | spec: 43 | ports: 44 | - port: 80 45 | containers: 46 | - name: nginx 47 | image: nginx 48 | relationships: 49 | - type: cloudify.kubernetes.relationships.managed_by_master 50 | target: master 51 | -------------------------------------------------------------------------------- /cloudify_kubernetes/tests/test_k8s_authentication.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017-2019 Cloudify Platform Ltd. All rights reserved 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | from mock import MagicMock, patch 17 | 18 | from ..k8s.authentication import ( 19 | KubernetesApiAuthentication, 20 | GCPServiceAccountAuthentication, 21 | KubernetesApiAuthenticationVariants 22 | ) 23 | from ..k8s.exceptions import KuberentesAuthenticationError 24 | 25 | 26 | class BaseTestK8SAuthentication(unittest.TestCase): 27 | def _mock_api(self): 28 | api_key = {} 29 | 30 | api_key_prefix = {} 31 | 32 | mock_configuration = MagicMock( 33 | api_key={}, 34 | api_key_prefix={} 35 | ) 36 | 37 | return api_key, api_key_prefix, mock_configuration 38 | 39 | 40 | class TestKubernetesApiAuthentication(BaseTestK8SAuthentication): 41 | 42 | def test_KubernetesApiAuthentication(self): 43 | mock_api = MagicMock() 44 | instance = KubernetesApiAuthentication('logger', 'conf') 45 | 46 | self.assertEqual(instance.logger, 'logger') 47 | self.assertEqual(instance.authentication_data, 'conf') 48 | self.assertEqual(instance._do_authenticate(mock_api), mock_api) 49 | self.assertEqual(instance.authenticate(mock_api), mock_api) 50 | 51 | 52 | class TestGCPServiceAccountAuthentication(BaseTestK8SAuthentication): 53 | 54 | def test_GCPServiceAccountAuthentication_ErrorNoData(self): 55 | mock_api = MagicMock() 56 | mock_logger = MagicMock() 57 | 58 | instance = GCPServiceAccountAuthentication( 59 | mock_logger, 60 | {}, 61 | ) 62 | 63 | with self.assertRaises(KuberentesAuthenticationError): 64 | instance.authenticate(mock_api) 65 | 66 | def test_GCPServiceAccountAuthentication(self): 67 | mock_logger = MagicMock() 68 | 69 | api_key, api_key_prefix, mock_api = self._mock_api() 70 | 71 | token = 'token' 72 | access_token_mock = MagicMock(access_token=token) 73 | mock_credentials = MagicMock() 74 | mock_credentials.get_access_token = MagicMock( 75 | return_value=access_token_mock 76 | ) 77 | 78 | # use dict as account 79 | instance = GCPServiceAccountAuthentication( 80 | mock_logger, 81 | {'gcp_service_account': {'blah': 'blah'}}, 82 | ) 83 | 84 | with patch( 85 | 'google.oauth2.service_account.' 86 | 'Credentials.from_service_account_info', 87 | MagicMock(return_value=mock_credentials) 88 | ): 89 | instance.authenticate(mock_api) 90 | api_key = mock_api.api_key 91 | api_key_prefix = mock_api.api_key_prefix 92 | # raise Exception(vars(api_key['authorization'])) 93 | self.assertEquals(token, api_key['authorization']._mock_name) 94 | self.assertEquals('Bearer', api_key_prefix['authorization']) 95 | 96 | # use json as account 97 | instance = GCPServiceAccountAuthentication( 98 | mock_logger, 99 | {'gcp_service_account': '{"blah": "blah"}'}, 100 | ) 101 | 102 | with patch( 103 | 'google.oauth2.service_account.' 104 | 'Credentials.from_service_account_info', 105 | MagicMock(return_value=mock_credentials) 106 | ): 107 | instance.authenticate(mock_api) 108 | 109 | 110 | class TestKubernetesAuthenticationVariants(BaseTestK8SAuthentication): 111 | 112 | def test_KubernetesApiAuthenticationVariants_Error(self): 113 | api_key, api_key_prefix, mock_api = self._mock_api() 114 | mock_logger = MagicMock() 115 | 116 | instance = KubernetesApiAuthenticationVariants( 117 | mock_logger, 118 | {}, 119 | ) 120 | 121 | instance.authenticate(mock_api) 122 | 123 | self.assertFalse('authorization' in api_key) 124 | self.assertFalse('authorization' in api_key_prefix) 125 | 126 | def test_KubernetesApiAuthenticationVariants(self): 127 | api_key, api_key_prefix, mock_api = self._mock_api() 128 | mock_logger = MagicMock() 129 | authentication_data = {'gcp_service_account': {'blah': 'blah'}} 130 | 131 | instance = KubernetesApiAuthenticationVariants( 132 | mock_logger, 133 | authentication_data 134 | ) 135 | 136 | def fake_authenticate(tested_instance, api): 137 | self.assertEquals(mock_api, api) 138 | self.assertEquals( 139 | tested_instance.authentication_data, 140 | authentication_data 141 | ) 142 | 143 | return True 144 | 145 | with patch( 146 | 'cloudify_kubernetes.k8s.authentication.' 147 | 'GCPServiceAccountAuthentication.authenticate', 148 | fake_authenticate 149 | ): 150 | self.assertTrue(instance.authenticate(mock_api)) 151 | 152 | 153 | if __name__ == '__main__': 154 | unittest.main() 155 | -------------------------------------------------------------------------------- /cloudify_kubernetes/tests/test_k8s_config.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017-2019 Cloudify Platform Ltd. All rights reserved 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import unittest 15 | import os 16 | from mock import MagicMock, patch 17 | 18 | from ..k8s.config import ( 19 | ApiOptionsConfiguration, 20 | FileContentConfiguration, 21 | KubernetesApiConfiguration, 22 | BlueprintFileConfiguration, 23 | ManagerFilePathConfiguration, 24 | KubernetesApiConfigurationVariants) 25 | from ..k8s.exceptions import ( 26 | KuberentesApiInitializationFailedError) 27 | 28 | 29 | class TestKubernetesApiConfiguration(unittest.TestCase): 30 | 31 | def test_KubernetesApiConfiguration(self): 32 | instance = KubernetesApiConfiguration('logger', 'conf') 33 | 34 | self.assertEqual(instance.logger, 'logger') 35 | self.assertEqual(instance.configuration_data, 'conf') 36 | self.assertEqual(instance._do_prepare_api(), None) 37 | 38 | with self.assertRaises(KuberentesApiInitializationFailedError): 39 | instance.prepare_api() 40 | 41 | 42 | class TestBlueprintFileConfiguration(unittest.TestCase): 43 | 44 | def test_BlueprintFileConfiguration_Error(self): 45 | mock_download_resource = MagicMock(side_effect=Exception()) 46 | mock_logger = MagicMock() 47 | 48 | instance = BlueprintFileConfiguration( 49 | mock_logger, 50 | {'blueprint_file_name': 'kubernetes.conf'}, 51 | download_resource=mock_download_resource 52 | ) 53 | 54 | with self.assertRaises(KuberentesApiInitializationFailedError): 55 | instance.prepare_api() 56 | 57 | mock_download_resource.assert_called_with('kubernetes.conf') 58 | 59 | def test_BlueprintFileConfiguration(self): 60 | mock_download_resource = MagicMock() 61 | mock_logger = MagicMock() 62 | mock_client = MagicMock() 63 | mock_loader = MagicMock() 64 | mock_kubernetes_config_load_kube_config = MagicMock( 65 | return_value=mock_loader 66 | ) 67 | mock_download_resource = MagicMock( 68 | return_value="downloaded_resource" 69 | ) 70 | 71 | instance = BlueprintFileConfiguration( 72 | mock_logger, 73 | {'blueprint_file_name': 'kubernetes.conf'}, 74 | download_resource=mock_download_resource 75 | ) 76 | 77 | mock_isfile = MagicMock(return_value=True) 78 | 79 | with patch('os.path.isfile', mock_isfile): 80 | with patch( 81 | 'cloudify_kubernetes.k8s.config.' 82 | 'kubernetes.config.load_kube_config', 83 | mock_kubernetes_config_load_kube_config 84 | ): 85 | with patch('kubernetes.client', mock_client): 86 | self.assertEqual( 87 | instance.prepare_api(), mock_client.Configuration() 88 | ) 89 | 90 | mock_download_resource.assert_called_with('kubernetes.conf') 91 | mock_kubernetes_config_load_kube_config.assert_called_with( 92 | config_file='downloaded_resource', 93 | client_configuration=mock_client.Configuration() 94 | ) 95 | 96 | 97 | class TestManagerFilePathConfiguration(unittest.TestCase): 98 | 99 | def test_ManagerFilePathConfiguration_Error(self): 100 | mock_logger = MagicMock() 101 | 102 | instance = ManagerFilePathConfiguration( 103 | mock_logger, 104 | {'manager_file_path': 'kubernetes.conf'} 105 | ) 106 | 107 | with self.assertRaises(KuberentesApiInitializationFailedError): 108 | instance.prepare_api() 109 | 110 | def test_ManagerFilePathConfiguration(self): 111 | mock_logger = MagicMock() 112 | mock_client = MagicMock() 113 | mock_loader = MagicMock() 114 | mock_kubernetes_config_load_kube_config = MagicMock( 115 | return_value=mock_loader 116 | ) 117 | 118 | instance = ManagerFilePathConfiguration(mock_logger, { 119 | 'manager_file_path': 'kubernetes.conf' 120 | }) 121 | 122 | mock_isfile = MagicMock(return_value=True) 123 | 124 | with patch('os.path.isfile', mock_isfile): 125 | with patch( 126 | 'cloudify_kubernetes.k8s.config.' 127 | 'kubernetes.config.load_kube_config', 128 | mock_kubernetes_config_load_kube_config 129 | ): 130 | with patch('kubernetes.client', mock_client): 131 | self.assertEqual( 132 | instance.prepare_api(), mock_client.Configuration() 133 | ) 134 | 135 | mock_isfile.assert_called_with('kubernetes.conf') 136 | mock_kubernetes_config_load_kube_config.assert_called_with( 137 | config_file='kubernetes.conf', 138 | client_configuration=mock_client.Configuration() 139 | ) 140 | 141 | 142 | class TestFileContentConfiguration(unittest.TestCase): 143 | 144 | def test_FileContentConfiguration_Error(self): 145 | mock_logger = MagicMock() 146 | download_resource_mock = MagicMock(side_effect=Exception()) 147 | 148 | instance = FileContentConfiguration( 149 | mock_logger, 150 | {}, 151 | download_resource=download_resource_mock 152 | ) 153 | 154 | with self.assertRaises(KuberentesApiInitializationFailedError): 155 | instance.prepare_api() 156 | 157 | def test_FileContentConfiguration(self): 158 | mock_download_resource = MagicMock() 159 | mock_logger = MagicMock() 160 | mock_config = MagicMock() 161 | 162 | class Config(object): 163 | 164 | def set_default(self, *_, **__): 165 | pass 166 | 167 | mock_client = MagicMock() 168 | mock_client.Configuration = Config 169 | 170 | instance = FileContentConfiguration( 171 | mock_logger, 172 | {'file_content': 'kubernetes.conf'}, 173 | download_resource=mock_download_resource 174 | ) 175 | 176 | with patch( 177 | 'kubernetes.config.kube_config.KubeConfigLoader', mock_config 178 | ): 179 | with patch('kubernetes.client', mock_client): 180 | self.assertEqual( 181 | instance.prepare_api(), mock_client 182 | ) 183 | 184 | mock_config.assert_called_with( 185 | config_dict='kubernetes.conf', 186 | config_base_path=os.path.expanduser('~/.kube') 187 | ) 188 | 189 | 190 | class TestApiOptionsConfiguration(unittest.TestCase): 191 | 192 | def test_ApiOptionsConfiguration_Error(self): 193 | mock_logger = MagicMock() 194 | 195 | instance = ApiOptionsConfiguration(mock_logger, {}) 196 | 197 | with self.assertRaises(KuberentesApiInitializationFailedError): 198 | instance.prepare_api() 199 | 200 | def test_ApiOptionsConfiguration_EmptyError(self): 201 | mock_logger = MagicMock() 202 | 203 | instance = ApiOptionsConfiguration(mock_logger, { 204 | 'api_options': {} 205 | }) 206 | 207 | with self.assertRaises(KuberentesApiInitializationFailedError): 208 | instance.prepare_api() 209 | 210 | def test_ApiOptionsConfiguration(self): 211 | mock_logger = MagicMock() 212 | mock_client = MagicMock() 213 | 214 | instance = ApiOptionsConfiguration(mock_logger, { 215 | 'api_options': {'host': 'some_host'} 216 | }) 217 | 218 | with patch('kubernetes.client', mock_client): 219 | self.assertEqual( 220 | instance.prepare_api(), mock_client.Configuration() 221 | ) 222 | 223 | # check keys 224 | instance = ApiOptionsConfiguration(mock_logger, { 225 | 'api_options': {'host': 'some_host', 226 | 'api_key': 'secret key'} 227 | }) 228 | 229 | with patch('kubernetes.client', mock_client): 230 | config = MagicMock() 231 | mock_client.Configuration = MagicMock(return_value=config) 232 | self.assertEqual( 233 | instance.prepare_api(), mock_client.Configuration() 234 | ) 235 | self.assertEqual(config.api_key, 236 | {'authorization': 'Bearer secret key'}) 237 | 238 | 239 | class TestKubernetesApiConfigurationVariants(unittest.TestCase): 240 | 241 | def test_KubernetesApiConfigurationVariants_Error(self): 242 | mock_download_resource = MagicMock(side_effect=Exception()) 243 | mock_logger = MagicMock() 244 | 245 | instance = KubernetesApiConfigurationVariants( 246 | mock_logger, 247 | {}, 248 | download_resource=mock_download_resource 249 | ) 250 | 251 | with self.assertRaises(KuberentesApiInitializationFailedError): 252 | instance.prepare_api() 253 | 254 | def test_KubernetesApiConfigurationVariants(self): 255 | mock_logger = MagicMock() 256 | mock_config = MagicMock() 257 | 258 | class Config(object): 259 | 260 | def set_default(self, *_, **__): 261 | pass 262 | 263 | mock_client = MagicMock() 264 | mock_client.Configuration = Config 265 | 266 | instance = KubernetesApiConfigurationVariants( 267 | mock_logger, 268 | {'file_content': 'kubernetes.conf'} 269 | ) 270 | 271 | with patch( 272 | 'kubernetes.config.kube_config.KubeConfigLoader', mock_config 273 | ): 274 | with patch('kubernetes.client', mock_client): 275 | self.assertEqual( 276 | instance.prepare_api(), mock_client 277 | ) 278 | 279 | mock_config.assert_called_with( 280 | config_dict='kubernetes.conf', 281 | config_base_path=os.path.expanduser('~/.kube') 282 | ) 283 | 284 | 285 | if __name__ == '__main__': 286 | unittest.main() 287 | -------------------------------------------------------------------------------- /cloudify_kubernetes/tests/test_k8s_exceptions.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017-2019 Cloudify Platform Ltd. All rights reserved 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import unittest 15 | from ..k8s import ( 16 | KuberentesError, 17 | KuberentesApiOperationError, 18 | KuberentesAuthenticationError, 19 | KuberentesInvalidApiClassError, 20 | KuberentesInvalidApiMethodError, 21 | KuberentesInvalidPayloadClassError, 22 | KuberentesApiInitializationFailedError) 23 | 24 | 25 | class TestException(unittest.TestCase): 26 | 27 | def test_KuberentesApiInitializationFailedError(self): 28 | instance = KuberentesApiInitializationFailedError('message') 29 | self.assertTrue(isinstance(instance, KuberentesError)) 30 | 31 | def test_KuberentesApiOperationError(self): 32 | instance = KuberentesApiOperationError('message') 33 | self.assertTrue(isinstance(instance, KuberentesError)) 34 | 35 | def test_KuberentesAuthenticationError(self): 36 | instance = KuberentesAuthenticationError('message') 37 | self.assertTrue(isinstance(instance, KuberentesError)) 38 | 39 | def test_KuberentesInvalidPayloadClassError(self): 40 | instance = KuberentesInvalidPayloadClassError('message') 41 | self.assertTrue(isinstance(instance, KuberentesError)) 42 | 43 | def test_KuberentesInvalidApiClassError(self): 44 | instance = KuberentesInvalidApiClassError('message') 45 | self.assertTrue(isinstance(instance, KuberentesError)) 46 | 47 | def test_KuberentesInvalidApiMethodError(self): 48 | instance = KuberentesInvalidApiMethodError('message') 49 | self.assertTrue(isinstance(instance, KuberentesError)) 50 | 51 | def test_KuberentesError(self): 52 | instance = KuberentesError('message') 53 | self.assertTrue(isinstance(instance, Exception)) 54 | 55 | 56 | if __name__ == '__main__': 57 | unittest.main() 58 | -------------------------------------------------------------------------------- /cloudify_kubernetes/tests/test_k8s_operations.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017-2019 Cloudify Platform Ltd. All rights reserved 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | from mock import MagicMock 17 | from kubernetes.client.rest import ApiException 18 | 19 | from ..k8s.operations import ( 20 | KubernetesReadOperation, 21 | KubernetesCreateOperation, 22 | KubernetesUpdateOperation, 23 | KubernetesDeleteOperation) 24 | 25 | from ..k8s.exceptions import KuberentesApiOperationError 26 | 27 | 28 | class TestKubernetesCreateOperation(unittest.TestCase): 29 | 30 | def test_init(self): 31 | instance = KubernetesCreateOperation("api_method", ['a', 'b']) 32 | self.assertEqual(instance.api_method, 'api_method') 33 | self.assertEqual(instance.api_method_arguments_names, ['a', 'b']) 34 | 35 | def test_prepare_arguments(self): 36 | instance = KubernetesCreateOperation("api_method", ['a', 'b']) 37 | self.assertEqual( 38 | instance._prepare_arguments({'a': 'c', 'b': 'd'}), 39 | {'a': 'c', 'b': 'd'} 40 | ) 41 | self.assertEqual( 42 | instance._prepare_arguments({'a': 'd', 'b': 'e', 'c': 'f'}), 43 | {'a': 'd', 'b': 'e'} 44 | ) 45 | 46 | with self.assertRaises(KuberentesApiOperationError) as error: 47 | instance._prepare_arguments({'a': 'd', 'c': 'f'}) 48 | 49 | self.assertEqual( 50 | str(error.exception), 51 | "Invalid input data for execution of Kubernetes API method: " 52 | "input argument b is not defined but is mandatory" 53 | ) 54 | 55 | def test_execute(self): 56 | call_mock = MagicMock(return_value="!") 57 | instance = KubernetesCreateOperation(call_mock, ['a', 'b']) 58 | 59 | self.assertEqual( 60 | instance.execute({'a': 'd', 'b': 'e', 'c': 'f'}), 61 | "!" 62 | ) 63 | call_mock.assert_called_with(a='d', b='e') 64 | 65 | def test_execute_ApiException(self): 66 | call_mock = MagicMock(side_effect=ApiException("!")) 67 | instance = KubernetesCreateOperation(call_mock, ['a', 'b']) 68 | 69 | with self.assertRaises(KuberentesApiOperationError) as error: 70 | instance.execute({'a': 'd', 'b': 'e', 'c': 'f'}) 71 | 72 | self.assertEqual( 73 | str(error.exception), 74 | "Operation execution failed. Exception during Kubernetes API " 75 | "call: (!)\nReason: None\n" 76 | ) 77 | call_mock.assert_called_with(a='d', b='e') 78 | 79 | 80 | class TestKubernetesReadOperation(unittest.TestCase): 81 | 82 | def test_init(self): 83 | instance = KubernetesReadOperation("api_method", ['a', 'b']) 84 | self.assertEqual(instance.api_method, 'api_method') 85 | self.assertEqual(instance.api_method_arguments_names, ['a', 'b']) 86 | 87 | def test_prepare_arguments(self): 88 | instance = KubernetesReadOperation("api_method", ['a', 'b']) 89 | self.assertEqual( 90 | instance._prepare_arguments({'a': 'c', 'b': 'd'}), 91 | {'a': 'c', 'b': 'd'} 92 | ) 93 | self.assertEqual( 94 | instance._prepare_arguments({ 95 | 'a': 'd', 'b': 'e', 'exact': 'f', 'export': 'g', 'h': 'i' 96 | }), 97 | {'a': 'd', 'b': 'e', 'exact': 'f', 'export': 'g'} 98 | ) 99 | 100 | with self.assertRaises(KuberentesApiOperationError) as error: 101 | instance._prepare_arguments({'a': 'd', 'c': 'f'}) 102 | 103 | self.assertEqual( 104 | str(error.exception), 105 | "Invalid input data for execution of Kubernetes API method: " 106 | "input argument b is not defined but is mandatory" 107 | ) 108 | 109 | 110 | class TestKubernetesUpdateOperation(unittest.TestCase): 111 | 112 | def test_init(self): 113 | instance = KubernetesUpdateOperation("api_method", ['a', 'b']) 114 | self.assertEqual(instance.api_method, 'api_method') 115 | self.assertEqual(instance.api_method_arguments_names, ['a', 'b']) 116 | 117 | def test_prepare_arguments(self): 118 | instance = KubernetesUpdateOperation("api_method", ['a', 'b']) 119 | self.assertEqual( 120 | instance._prepare_arguments({'a': 'c', 'b': 'd'}), 121 | {'a': 'c', 'b': 'd'} 122 | ) 123 | self.assertEqual( 124 | instance._prepare_arguments({'a': 'd', 'b': 'e', 'c': 'f'}), 125 | {'a': 'd', 'b': 'e'} 126 | ) 127 | 128 | with self.assertRaises(KuberentesApiOperationError) as error: 129 | instance._prepare_arguments({'a': 'd', 'c': 'f'}) 130 | 131 | self.assertEqual( 132 | str(error.exception), 133 | "Invalid input data for execution of Kubernetes API method: " 134 | "input argument b is not defined but is mandatory" 135 | ) 136 | 137 | def test_execute(self): 138 | call_mock = MagicMock(return_value="!") 139 | instance = KubernetesUpdateOperation(call_mock, ['a', 'b']) 140 | 141 | self.assertEqual( 142 | instance.execute({'a': 'd', 'b': 'e', 'c': 'f'}), 143 | "!" 144 | ) 145 | call_mock.assert_called_with(a='d', b='e') 146 | 147 | def test_execute_ApiException(self): 148 | call_mock = MagicMock(side_effect=ApiException("!")) 149 | instance = KubernetesUpdateOperation(call_mock, ['a', 'b']) 150 | 151 | with self.assertRaises(KuberentesApiOperationError) as error: 152 | instance.execute({'a': 'd', 'b': 'e', 'c': 'f'}) 153 | 154 | self.assertEqual( 155 | str(error.exception), 156 | "Operation execution failed. Exception during Kubernetes API " 157 | "call: (!)\nReason: None\n" 158 | ) 159 | call_mock.assert_called_with(a='d', b='e') 160 | 161 | 162 | class TestKubernetesDeleteOperation(unittest.TestCase): 163 | 164 | def test_init(self): 165 | instance = KubernetesDeleteOperation("api_method", ['a', 'b']) 166 | self.assertEqual(instance.api_method, 'api_method') 167 | self.assertEqual(instance.api_method_arguments_names, ['a', 'b']) 168 | 169 | def test_prepare_arguments(self): 170 | instance = KubernetesDeleteOperation("api_method", ['a', 'b']) 171 | self.assertEqual( 172 | instance._prepare_arguments({'a': 'c', 'b': 'd'}), 173 | {'a': 'c', 'b': 'd'} 174 | ) 175 | self.assertEqual( 176 | instance._prepare_arguments({ 177 | 'a': 'd', 'b': 'e', 'exact': 'f', 178 | 'grace_period_seconds': 'g', 'propagation_policy': 'i' 179 | }), { 180 | 'a': 'd', 'b': 'e', 'grace_period_seconds': 'g', 181 | 'propagation_policy': 'i' 182 | } 183 | ) 184 | 185 | with self.assertRaises(KuberentesApiOperationError) as error: 186 | instance._prepare_arguments({'a': 'd', 'c': 'f'}) 187 | 188 | self.assertEqual( 189 | str(error.exception), 190 | "Invalid input data for execution of Kubernetes API method: " 191 | "input argument b is not defined but is mandatory" 192 | ) 193 | 194 | 195 | if __name__ == '__main__': 196 | unittest.main() 197 | -------------------------------------------------------------------------------- /cloudify_kubernetes/workflows.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017-2019 Cloudify Platform Ltd. All rights reserved 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import ast 16 | import json 17 | 18 | from cloudify.workflows import ctx 19 | from cloudify.decorators import workflow 20 | from cloudify.manager import get_rest_client 21 | from cloudify.exceptions import NonRecoverableError 22 | from cloudify_rest_client.exceptions import CloudifyClientError 23 | 24 | from . import utils 25 | 26 | POSTSTART = 'cloudify.interfaces.lifecycle.poststart' 27 | UPDATE = 'cloudify.interfaces.lifecycle.update' 28 | CHECKDRIFT = 'cloudify.interfaces.lifecycle.check_drift' 29 | DELETE = 'cloudify.interfaces.lifecycle.delete' 30 | CREATE = 'cloudify.interfaces.lifecycle.create' 31 | 32 | 33 | def execute_node_instance_operation(_node_instance, 34 | _operation, 35 | _params=None): 36 | """ 37 | Handles sending events and executing operations. 38 | 39 | :param _node_instance: A NodeInstance object from cloudify.manager. 40 | :param _operation: A string with the name of the workflow operation. 41 | :param _params: Optional parameters to the workflow operation. 42 | """ 43 | 44 | # Prepare the kwargs to the execute_operation method. 45 | _params = _params or {} 46 | operation_args = { 47 | 'operation': _operation, 48 | } 49 | if _params: 50 | operation_args['kwargs'] = _params 51 | 52 | graph = ctx.graph_mode() 53 | sequence = graph.sequence() 54 | sequence.add( 55 | _node_instance.send_event( 56 | 'Starting to run operation: {0}'.format(operation_args)), 57 | _node_instance.execute_operation(**operation_args), 58 | _node_instance.send_event( 59 | 'Finished running operation: {0}'.format(operation_args)) 60 | ) 61 | return graph.execute() 62 | 63 | 64 | @workflow 65 | def update_resource_definition(node_instance_id, 66 | resource_definition_changes, 67 | **_): 68 | """ 69 | Updates a Kubernetes Resource's resource definition. 70 | 71 | Example Usage: 72 | ```shell 73 | $ cfy blueprints upload \ 74 | examples/wordpress-blueprint.yaml -b wordpress 75 | $ cfy deployments create --skip-plugins-validation -b wordpress 76 | $ cfy executions start install -d wordpress 77 | $ cfy node-instances list -d wordpress 78 | # At this point copy the node_instance_id of wordpress_svc node. 79 | $ cfy node-instances get [wordpress_svc node instance id] 80 | # At this point copy the cluster_ip in the resource definition. 81 | $ cfy executions start update_resource_definition -d wordpress -vv \ 82 | -p resource_definition_changes=" 83 | {'metadata': {'resourceVersion': '0'}, 84 | 'spec': {'clusterIP': '10.110.97.242', 85 | 'ports': [{'port': 80, 'nodePort': 30081}]} 86 | }" -p node_instance_id=[wordpress_svc node instance id] 87 | ``` 88 | 89 | :param node_instance_id: A string. 90 | The node instance ID of the node instance containing the resource. 91 | :param resource_definition_changes: A dictionary encoded as a unicode 92 | string representing the changes to the resoruce definition. 93 | """ 94 | 95 | try: 96 | resource_definition_changes = \ 97 | json.loads(resource_definition_changes) 98 | except json.JSONDecodeError as e: 99 | 100 | if 'Key name must be string at char' in str(e): 101 | resource_definition_changes = \ 102 | ast.literal_eval(resource_definition_changes) 103 | elif 'Unexpected' in str(e): 104 | resource_definition_changes = \ 105 | utils.resource_definitions_from_file_result( 106 | resource_definition_changes) 107 | 108 | node_instance = ctx.get_node_instance(node_instance_id) 109 | 110 | if not node_instance_id: 111 | raise NonRecoverableError( 112 | 'No such node_instance_id in deployment: {0}.'.format( 113 | node_instance_id)) 114 | 115 | # Execute start operation to update to 116 | # the latest version of the resource definition. 117 | node_instance.logger.info( 118 | 'Executing start in order to get the current state.') 119 | execute_node_instance_operation(node_instance, POSTSTART) 120 | node_instance.logger.info( 121 | 'Executed start in order to get the current state.') 122 | 123 | # Execute update operation to push the change to Kubernetes. 124 | node_instance.logger.info( 125 | 'Executing update in order to push the new changes.') 126 | execute_node_instance_operation( 127 | node_instance, 128 | UPDATE, 129 | _params={utils.DEFINITION_ADDITIONS: resource_definition_changes}) 130 | node_instance.logger.info( 131 | 'Executed update in order to push the new changes.') 132 | 133 | 134 | def refresh_and_store_token(ctx, 135 | kubernetes_cluster_node_instance_id, 136 | deployment_capability_name, 137 | service_account_node_instance_id, 138 | secret_token_node_instance_id, 139 | store_token_and_kubeconfig_id): 140 | 141 | cluster_ni = lookup_node_instance( 142 | kubernetes_cluster_node_instance_id) 143 | execute_node_instance_operation(cluster_ni, POSTSTART) 144 | execute_node_instance_operation(cluster_ni, CHECKDRIFT) 145 | 146 | create_secrets_kubernetes_config(deployment_capability_name) 147 | 148 | service_account_ni = lookup_node_instance(service_account_node_instance_id) 149 | execute_node_instance_operation(service_account_ni, UPDATE) 150 | execute_node_instance_operation(service_account_ni, POSTSTART) 151 | 152 | secret_token_ni = lookup_node_instance(secret_token_node_instance_id) 153 | execute_node_instance_operation(secret_token_ni, DELETE) 154 | execute_node_instance_operation(secret_token_ni, CREATE) 155 | 156 | store_token_and_kubeconfig_ni = lookup_node_instance( 157 | store_token_and_kubeconfig_id) 158 | execute_node_instance_operation(store_token_and_kubeconfig_ni, CREATE) 159 | 160 | 161 | def create_secrets_kubernetes_config(deployment_capability_name): 162 | client = get_rest_client() 163 | 164 | capabilities = client.deployments.capabilities. \ 165 | get(ctx.deployment.id).get('capabilities', {}) 166 | kubernetes_config = capabilities.get(deployment_capability_name, {}) \ 167 | .get('file_content', {}) 168 | ctx.logger.info('This is the capability: {}'.format(kubernetes_config)) 169 | 170 | try: 171 | client.secrets.create('kubernetes_config', str(kubernetes_config)) 172 | except CloudifyClientError as err: 173 | ctx.logger.error('{}'.format(str(err))) 174 | 175 | 176 | def lookup_node_instance(provided_node_instance_id): 177 | ctx.logger.debug('Looking for {}'.format(provided_node_instance_id)) 178 | try: 179 | desired_node_instance = ctx.get_node_instance( 180 | provided_node_instance_id) 181 | if not desired_node_instance: 182 | raise RuntimeError('Did not find desired node instance.') 183 | except RuntimeError: 184 | desired_node_instance = None 185 | for node_instance in ctx.node_instances: 186 | ctx.logger.debug('Checking node_instance {} = {}'.format( 187 | node_instance.node_id, 188 | provided_node_instance_id 189 | )) 190 | if node_instance.node_id == provided_node_instance_id: 191 | desired_node_instance = node_instance 192 | break 193 | if not desired_node_instance: 194 | raise NonRecoverableError( 195 | 'A valid node instance or node ID for ' 196 | '{} node was not found'.format(provided_node_instance_id) 197 | ) 198 | return desired_node_instance 199 | -------------------------------------------------------------------------------- /examples/basic.yaml: -------------------------------------------------------------------------------- 1 | tosca_definitions_version: cloudify_dsl_1_3 2 | 3 | imports: 4 | - http://www.getcloudify.org/spec/cloudify/5.0.5/types.yaml 5 | - plugin:cloudify-kubernetes-plugin 6 | 7 | node_templates: 8 | 9 | resource: 10 | type: cloudify.kubernetes.resources.Pod 11 | properties: 12 | client_config: 13 | authentication: 14 | gcp_service_account: { get_secret: gcp_credentials } 15 | configuration: 16 | api_options: 17 | host: { get_secret: kubernetes_endpoint } 18 | verify_ssl: false 19 | debug: false 20 | definition: 21 | apiVersion: v1 22 | kind: Pod 23 | metadata: 24 | name: nginx-test-pod 25 | spec: 26 | containers: 27 | - name: nginx-test-pod 28 | image: nginx:stable 29 | -------------------------------------------------------------------------------- /examples/file-test-multiple-resources.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: wordpress 6 | labels: 7 | app: wordpress 8 | spec: 9 | strategy: 10 | type: Recreate 11 | selector: 12 | matchLabels: 13 | app: wordpress 14 | template: 15 | metadata: 16 | labels: 17 | app: wordpress 18 | tier: frontend 19 | spec: 20 | containers: 21 | - image: wordpress:4.8.0-apache 22 | name: wordpress 23 | env: 24 | - name: WORDPRESS_DB_HOST 25 | value: wordpress-mysql 26 | - name: WORDPRESS_DB_PASSWORD 27 | value: {{ WORDPRESS_PASSWORD }} 28 | ports: 29 | - containerPort: 80 30 | name: wordpress 31 | volumeMounts: 32 | - name: wordpress-persistent-storage 33 | mountPath: /var/www/html 34 | volumes: 35 | - name: wordpress-persistent-storage 36 | persistentVolumeClaim: 37 | claimName: wp-pv-claim 38 | --- 39 | apiVersion: v1 40 | kind: Service 41 | metadata: 42 | name: wordpress 43 | labels: 44 | app: wordpress 45 | spec: 46 | ports: 47 | - port: 80 48 | nodePort: 30080 49 | selector: 50 | app: wordpress 51 | tier: frontend 52 | type: LoadBalancer 53 | --- 54 | apiVersion: v1 55 | kind: PersistentVolumeClaim 56 | metadata: 57 | name: wp-pv-claim 58 | labels: 59 | app: wordpress 60 | spec: 61 | accessModes: 62 | - ReadWriteOnce 63 | resources: 64 | requests: 65 | storage: 20Gi 66 | --- 67 | apiVersion: apps/v1 68 | kind: Deployment 69 | metadata: 70 | name: wordpress-mysql 71 | labels: 72 | app: wordpress 73 | spec: 74 | selector: 75 | matchLabels: 76 | app: wordpress 77 | strategy: 78 | type: Recreate 79 | template: 80 | metadata: 81 | labels: 82 | app: wordpress 83 | tier: mysql 84 | spec: 85 | containers: 86 | - image: mysql:5.6 87 | name: mysql 88 | env: 89 | # $ kubectl create secret generic mysql-pass --from-file=password.txt 90 | # make sure password.txt does not have a trailing newline 91 | - name: MYSQL_ROOT_PASSWORD 92 | value: {{ MYSQL_ROOT_PASSWORD }} 93 | ports: 94 | - containerPort: 3306 95 | name: mysql 96 | volumeMounts: 97 | - name: mysql-persistent-storage 98 | mountPath: /var/lib/mysql 99 | volumes: 100 | - name: mysql-persistent-storage 101 | persistentVolumeClaim: 102 | claimName: mysql-pv-claim 103 | --- 104 | apiVersion: v1 105 | kind: Service 106 | metadata: 107 | name: wordpress-mysql 108 | labels: 109 | app: wordpress 110 | spec: 111 | ports: 112 | - port: 3306 113 | selector: 114 | app: wordpress 115 | tier: mysql 116 | clusterIP: None 117 | --- 118 | apiVersion: v1 119 | kind: PersistentVolumeClaim 120 | metadata: 121 | name: mysql-pv-claim 122 | labels: 123 | app: wordpress 124 | spec: 125 | accessModes: 126 | - ReadWriteOnce 127 | resources: 128 | requests: 129 | storage: 20Gi 130 | --- 131 | apiVersion: v1 132 | kind: PersistentVolume 133 | metadata: 134 | name: local-pv-2 135 | labels: 136 | type: local 137 | spec: 138 | capacity: 139 | storage: 20Gi 140 | accessModes: 141 | - ReadWriteOnce 142 | hostPath: 143 | path: /tmp/data/pv-2 144 | --- 145 | apiVersion: v1 146 | kind: PersistentVolume 147 | metadata: 148 | name: local-pv-1 149 | labels: 150 | type: local 151 | spec: 152 | capacity: 153 | storage: 20Gi 154 | accessModes: 155 | - ReadWriteOnce 156 | hostPath: 157 | path: /tmp/data/pv-1 158 | -------------------------------------------------------------------------------- /examples/file-test-persistent-volume.yaml: -------------------------------------------------------------------------------- 1 | tosca_definitions_version: cloudify_dsl_1_3 2 | 3 | imports: 4 | - http://www.getcloudify.org/spec/cloudify/5.0.5/types.yaml 5 | - plugin:cloudify-kubernetes-plugin 6 | 7 | node_templates: 8 | 9 | master: 10 | type: cloudify.kubernetes.nodes.Master 11 | properties: 12 | configuration: 13 | api_options: 14 | host: { concat: [ 'https://', { get_secret: kubernetes_master_ip }, ':', { get_secret: kubernetes_master_port } ] } 15 | api_key: { get_secret: kubernetes_token } 16 | debug: false 17 | verify_ssl: false 18 | 19 | pvc: 20 | type: cloudify.kubernetes.resources.FileDefinedResource 21 | properties: 22 | validate_resource_status: true 23 | file: 24 | resource_path: resources/pvc.yaml 25 | relationships: 26 | - type: cloudify.kubernetes.relationships.managed_by_master 27 | target: master 28 | 29 | pv: 30 | type: cloudify.kubernetes.resources.FileDefinedResource 31 | properties: 32 | validate_resource_status: true 33 | file: 34 | resource_path: resources/pv.yaml 35 | relationships: 36 | - type: cloudify.kubernetes.relationships.managed_by_master 37 | target: master 38 | -------------------------------------------------------------------------------- /examples/file-test-with-file-content.yaml: -------------------------------------------------------------------------------- 1 | tosca_definitions_version: cloudify_dsl_1_3 2 | 3 | imports: 4 | - http://www.getcloudify.org/spec/cloudify/5.0.5/types.yaml 5 | - plugin:cloudify-kubernetes-plugin 6 | 7 | inputs: 8 | 9 | resource_path: 10 | type: string 11 | default: file-test-multiple-resources.yaml 12 | 13 | file_content: 14 | type: string 15 | default: { get_secret: kubernetes-file-content } 16 | 17 | validate_status: 18 | type: boolean 19 | default: false 20 | 21 | resource_template_variables: 22 | default: {} 23 | 24 | allow_node_redefinition: 25 | type: boolean 26 | default: true 27 | 28 | node_templates: 29 | 30 | resource: 31 | type: cloudify.kubernetes.resources.FileDefinedResource 32 | properties: 33 | client_config: 34 | configuration: 35 | file_content: { get_input: file_content } 36 | validate_resource_status: { get_input: validate_status } 37 | allow_node_redefinition: { get_input: allow_node_redefinition } 38 | file: 39 | resource_path: { get_input: resource_path } 40 | template_variables: { get_input: resource_template_variables } 41 | -------------------------------------------------------------------------------- /examples/file-test.yaml: -------------------------------------------------------------------------------- 1 | tosca_definitions_version: cloudify_dsl_1_3 2 | 3 | imports: 4 | - https://cloudify.co/spec/cloudify/6.2.0/types.yaml 5 | - plugin:cloudify-kubernetes-plugin 6 | 7 | inputs: 8 | 9 | resource_path: 10 | type: string 11 | default: resources/crd.yaml 12 | 13 | kubernetes_master: 14 | type: string 15 | default: { get_secret: kubernetes_endpoint } 16 | 17 | validate_status: 18 | type: boolean 19 | default: false 20 | 21 | resource_template_variables: 22 | default: {} 23 | 24 | allow_node_redefinition: 25 | type: boolean 26 | default: true 27 | 28 | node_templates: 29 | 30 | resource: 31 | type: cloudify.kubernetes.resources.FileDefinedResource 32 | properties: 33 | client_config: 34 | authentication: 35 | gcp_service_account: { get_secret: gcp_credentials } 36 | configuration: 37 | api_options: 38 | host: { get_input: kubernetes_master } 39 | verify_ssl: false 40 | debug: false 41 | validate_resource_status: { get_input: validate_status } 42 | allow_node_redefinition: { get_input: allow_node_redefinition } 43 | file: 44 | resource_path: { get_input: resource_path } 45 | template_variables: { get_input: resource_template_variables } 46 | -------------------------------------------------------------------------------- /examples/files-test-multiple-resources.yaml: -------------------------------------------------------------------------------- 1 | tosca_definitions_version: cloudify_dsl_1_3 2 | 3 | imports: 4 | - http://www.getcloudify.org/spec/cloudify/5.0.0/types.yaml 5 | - plugin:cloudify-kubernetes-plugin 6 | 7 | inputs: 8 | kubernetes_master: 9 | type: string 10 | default: { get_secret: kubernetes_master_endpoint } 11 | 12 | resources: 13 | default: 14 | - resource_path: resources/cloudify-crd.yaml 15 | 16 | node_templates: 17 | 18 | resources: 19 | type: cloudify.kubernetes.resources.MultipleFileDefinedResources 20 | properties: 21 | client_config: 22 | configuration: 23 | api_options: 24 | host: { get_secret: kubernetes_endpoint } 25 | api_key: { get_secret: kubernetes_token } 26 | verify_ssl: false 27 | debug: false 28 | files: { get_input: resources } 29 | -------------------------------------------------------------------------------- /examples/inputs/dashboard-inputs.yaml: -------------------------------------------------------------------------------- 1 | public_dashboard_ip: -cut- 2 | dashboard_agent_user: -cut- -------------------------------------------------------------------------------- /examples/inputs/simple-blueprint-authentication-token-inputs.yaml: -------------------------------------------------------------------------------- 1 | kubernetes_public_master_url: -cut- 2 | kubernetes_authentication_token: -cut- 3 | kubernetes_api_debug_mode: -cut- -------------------------------------------------------------------------------- /examples/old-examples/cloudify-manager.yaml: -------------------------------------------------------------------------------- 1 | tosca_definitions_version: cloudify_dsl_1_3 2 | 3 | imports: 4 | - http://www.getcloudify.org/spec/cloudify/4.3/types.yaml 5 | - plugin.yaml 6 | 7 | node_templates: 8 | 9 | kubernetes_master: 10 | type: cloudify.kubernetes.nodes.Master 11 | properties: 12 | configuration: 13 | api_options: 14 | host: { concat: [ 'https://', { get_secret: kubernetes_master_ip }, ':', { get_secret: kubernetes_master_port } ] } 15 | api_key: { get_secret: kubernetes_token } 16 | debug: false 17 | verify_ssl: false 18 | 19 | cloudify_service: 20 | type: cloudify.kubernetes.resources.Service 21 | properties: 22 | definition: 23 | apiVersion: v1 24 | kind: Service 25 | metadata: 26 | name: cfy-service 27 | labels: 28 | app: cloudify 29 | component: cfy-cluster 30 | spec: 31 | ports: 32 | - name: http 33 | port: 80 34 | selector: 35 | component: cfy-cluster 36 | type: LoadBalancer 37 | relationships: 38 | - type: cloudify.kubernetes.relationships.managed_by_master 39 | target: kubernetes_master 40 | 41 | cloudify_pod: 42 | type: cloudify.kubernetes.resources.Pod 43 | properties: 44 | definition: 45 | apiVersion: v1 46 | kind: Pod 47 | metadata: 48 | generateName: cfy-manager 49 | labels: 50 | app: cloudify 51 | component: cfy-cluster 52 | spec: 53 | restartPolicy: Never 54 | volumes: 55 | - name: cgroup 56 | hostPath: 57 | path: /sys/fs/cgroup 58 | containers: 59 | - name: cloudify-manager 60 | image: cloudifyplatform/premium 61 | env: 62 | - name: PRIVATE_IP 63 | valueFrom: 64 | fieldRef: 65 | fieldPath: status.podIP 66 | lifecycle: 67 | postStart: 68 | exec: 69 | command: 70 | - bash 71 | - "-c" 72 | - | 73 | set -ex 74 | 75 | sleep 15 76 | 77 | PUBLIC_IP=$CFY_SERVICE_PORT_80_TCP_ADDR 78 | UPDATE_CERT_MD_SCRIPT=/tmp/update_cert_metadata.py 79 | 80 | curl -o $UPDATE_CERT_MD_SCRIPT https://raw.githubusercontent.com/Cloudify-PS/cloudify-labs-env-blueprints/4068a109545f9dfdf87e42b70583b831abfa3c63/components/CFYManager4x/scripts/update_cert_metadata.py 81 | 82 | sudo chmod +x $UPDATE_CERT_MD_SCRIPT 83 | sudo cp /etc/cloudify/ssl/certificate_metadata /etc/cloudify/ssl/certificate_metadata.old 84 | sudo sh -c "cat /etc/cloudify/ssl/certificate_metadata.old | $UPDATE_CERT_MD_SCRIPT external $PUBLIC_IP > /etc/cloudify/ssl/certificate_metadata" 85 | 86 | sudo /opt/manager/env/bin/python /opt/cloudify/manager-ip-setter/update-provider-context.py --networks /etc/cloudify/ssl/certificate_metadata $PRIVATE_IP 87 | 88 | sudo rm /opt/cloudify/manager-ip-setter/touched 89 | 90 | sudo /opt/cloudify/manager-ip-setter/manager-ip-setter.sh 91 | 92 | sudo systemctl restart nginx 2>&1 >/dev/null 93 | sudo systemctl restart cloudify-rabbitmq 2>&1 >/dev/null 94 | ports: 95 | - containerPort: 1025 96 | hostPort: 22 97 | - containerPort: 80 98 | hostPort: 80 99 | - containerPort: 999 100 | hostPort: 999 101 | - containerPort: 8080 102 | hostPort: 8080 103 | - containerPort: 5432 104 | hostPort: 5432 105 | - containerPort: 5671 106 | hostPort: 5671 107 | - containerPort: 5672 108 | hostPort: 5672 109 | - containerPort: 8086 110 | hostPort: 8086 111 | - containerPort: 8300 112 | hostPort: 8300 113 | - containerPort: 8301 114 | hostPort: 8301 115 | - containerPort: 8500 116 | hostPort: 8500 117 | - containerPort: 9200 118 | hostPort: 9200 119 | - containerPort: 15432 120 | hostPort: 15432 121 | - containerPort: 15672 122 | hostPort: 15672 123 | - containerPort: 22000 124 | hostPort: 22000 125 | volumeMounts: 126 | - mountPath: /sys/fs/cgroup 127 | readOnly: True 128 | name: cgroup 129 | securityContext: 130 | capabilities: 131 | add: ["SYS_ADMIN"] 132 | volumeMounts: 133 | - mountPath: /sys/fs/cgroup 134 | readOnly: True 135 | name: cgroup 136 | securityContext: 137 | capabilities: 138 | add: ["SYS_ADMIN"] 139 | relationships: 140 | - type: cloudify.kubernetes.relationships.managed_by_master 141 | target: kubernetes_master 142 | 143 | groups: 144 | 145 | cloudify_manager: 146 | members: 147 | - cloudify_pod 148 | - cloudify_service 149 | 150 | policies: 151 | 152 | cloudify_cluster_scaling: 153 | type: cloudify.policies.scaling 154 | properties: 155 | default_instances: 1 156 | targets: [cloudify_manager] 157 | 158 | outputs: 159 | 160 | cloudify_manager_console: 161 | value: { concat: [ "https://", { get_attribute: [ cloudify_service, kubernetes, status, load_balancer, ingress, 0, ip ] }, ':80' ] } 162 | -------------------------------------------------------------------------------- /examples/old-examples/load-balancer-blueprint.yaml: -------------------------------------------------------------------------------- 1 | tosca_definitions_version: cloudify_dsl_1_3 2 | 3 | imports: 4 | - http://www.getcloudify.org/spec/cloudify/4.3/types.yaml 5 | - plugin.yaml 6 | 7 | inputs: 8 | 9 | kubernetes_master_ip: 10 | type: string 11 | default: { get_secret: kubernetes_master_ip } 12 | 13 | kubernetes_master_port: 14 | type: string 15 | default: { get_secret: kubernetes_master_port } 16 | 17 | kubernetes_certificate_authority_data: 18 | default: { get_secret: kubernetes_certificate_authority_data } 19 | 20 | kubernetes-admin_client_certificate_data: 21 | default: { get_secret: kubernetes-admin_client_certificate_data } 22 | 23 | kubernetes-admin_client_key_data: 24 | default: { get_secret: kubernetes-admin_client_key_data } 25 | 26 | kubernetes_master_configuration: 27 | default: 28 | apiVersion: v1 29 | kind: Config 30 | preferences: {} 31 | current-context: kubernetes-admin@kubernetes 32 | clusters: 33 | - name: kubernetes 34 | cluster: 35 | certificate-authority-data: { get_input: kubernetes_certificate_authority_data } 36 | server: { concat: [ 'https://', { get_input: kubernetes_master_ip}, ':', { get_input: kubernetes_master_port } ] } 37 | contexts: 38 | - name: kubernetes-admin@kubernetes 39 | context: 40 | cluster: kubernetes 41 | user: kubernetes-admin 42 | users: 43 | - name: kubernetes-admin 44 | user: 45 | client-certificate-data: { get_input: kubernetes-admin_client_certificate_data } 46 | client-key-data: { get_input: kubernetes-admin_client_key_data } 47 | 48 | node_types: 49 | 50 | cloudify.kubernetes.resources.Ingress: 51 | derived_from: cloudify.kubernetes.resources.CustomBlueprintDefinedResource 52 | properties: 53 | api_mapping: 54 | default: 55 | create: 56 | api: ExtensionsV1beta1Api 57 | method: create_namespaced_ingress 58 | payload: V1beta1Ingress 59 | read: 60 | api: ExtensionsV1beta1Api 61 | method: read_namespaced_ingress 62 | delete: 63 | api: ExtensionsV1beta1Api 64 | method: delete_namespaced_ingress 65 | payload: V1DeleteOptions 66 | update: 67 | api: ExtensionsV1beta1Api 68 | method: replace_namespaced_ingress 69 | 70 | node_templates: 71 | 72 | kubernetes_master: 73 | type: cloudify.kubernetes.nodes.Master 74 | properties: 75 | configuration: 76 | file_content: { get_input: kubernetes_master_configuration } 77 | 78 | nginx_deployment: 79 | type: cloudify.kubernetes.resources.Deployment 80 | properties: 81 | definition: 82 | apiVersion: extensions/v1beta1 83 | kind: Deployment 84 | metadata: 85 | name: nginx-deployment 86 | spec: 87 | selector: 88 | matchLabels: 89 | app: nginx 90 | replicas: 2 91 | template: 92 | metadata: 93 | labels: 94 | app: nginx 95 | spec: 96 | containers: 97 | - name: nginx 98 | image: nginx:1.7.9 99 | ports: 100 | - containerPort: 80 101 | relationships: 102 | - type: cloudify.kubernetes.relationships.managed_by_master 103 | target: kubernetes_master 104 | 105 | nginx_service: 106 | type: cloudify.kubernetes.resources.Service 107 | properties: 108 | definition: 109 | apiVersion: v1 110 | kind: Service 111 | metadata: 112 | labels: 113 | app: nginx 114 | name: nginx 115 | spec: 116 | ports: 117 | - port: 80 118 | selector: 119 | app: nginx 120 | tier: frontend 121 | type: LoadBalancer 122 | relationships: 123 | - type: cloudify.kubernetes.relationships.managed_by_master 124 | target: kubernetes_master 125 | - type: cloudify.relationships.depends_on 126 | target: nginx_deployment 127 | 128 | nginx_ingress: 129 | type: cloudify.kubernetes.resources.Ingress 130 | properties: 131 | definition: 132 | apiVersion: extensions/v1beta1 133 | kind: Ingress 134 | metadata: 135 | name: basic-ingress 136 | spec: 137 | backend: 138 | serviceName: nginx 139 | servicePort: 80 140 | relationships: 141 | - type: cloudify.kubernetes.relationships.managed_by_master 142 | target: kubernetes_master 143 | - type: cloudify.relationships.depends_on 144 | target: nginx_service 145 | 146 | outputs: 147 | lb_out: 148 | value: { get_attribute: [ nginx_service, kubernetes, status, load_balancer, ingress, 0, ip ] } 149 | -------------------------------------------------------------------------------- /examples/old-examples/multiple-resource-file-test.yaml: -------------------------------------------------------------------------------- 1 | tosca_definitions_version: cloudify_dsl_1_3 2 | 3 | imports: 4 | - http://www.getcloudify.org/spec/cloudify/5.0.5/types.yaml 5 | - plugin:cloudify-kubernetes-plugin 6 | 7 | node_templates: 8 | 9 | master: 10 | type: cloudify.kubernetes.nodes.Master 11 | properties: 12 | configuration: 13 | api_options: 14 | host: { concat: [ 'https://', { get_secret: kubernetes_master_ip }, ':', { get_secret: kubernetes_master_port } ] } 15 | api_key: { get_secret: kubernetes_token } 16 | debug: false 17 | verify_ssl: false 18 | 19 | multiple-file-resource: 20 | type: cloudify.kubernetes.resources.FileDefinedResource 21 | properties: 22 | file: 23 | resource_path: resources/multiple-resource-file-test.yaml 24 | relationships: 25 | - type: cloudify.kubernetes.relationships.managed_by_master 26 | target: master 27 | -------------------------------------------------------------------------------- /examples/old-examples/persistent-volumes-blueprint.yaml: -------------------------------------------------------------------------------- 1 | tosca_definitions_version: cloudify_dsl_1_3 2 | 3 | description: > 4 | This runs a trivial deployment in kubernetes. 5 | Expects that you have first installed https://github.com/cloudify-examples/simple-kubernetes-blueprint/tree/4.0.1. 6 | Also install the wagon with the command: "cfy plugins upload PATH_TO_WAGON". 7 | 8 | imports: 9 | - http://www.getcloudify.org/spec/cloudify/4.3.1/types.yaml 10 | - http://www.getcloudify.org/spec/openstack-plugin/2.7.4/plugin.yaml 11 | - plugin.yaml 12 | 13 | inputs: 14 | 15 | configuration_file_content: 16 | type: string 17 | 18 | spec_port: 19 | default: 8000 20 | 21 | target_port: 22 | default: 80 23 | 24 | container_port: 25 | default: 80 26 | 27 | dsl_definitions: 28 | 29 | openstack_config: &openstack_config 30 | username: { get_secret: keystone_username } 31 | password: { get_secret: keystone_password } 32 | tenant_name: { get_secret: keystone_tenant_name } 33 | auth_url: { get_secret: keystone_url } 34 | region: { get_secret: region } 35 | 36 | node_templates: 37 | 38 | persistent_volume: 39 | type: cloudify.kubernetes.resources.PersistentVolume 40 | properties: 41 | definition: 42 | apiVersion: v1 43 | metadata: 44 | name: pv0003 45 | spec: 46 | capacity: 47 | storage: 5Gi 48 | accessModes: 49 | - ReadWriteOnce 50 | persistentVolumeReclaimPolicy: Recycle 51 | storageClassName: gold 52 | cinder: 53 | volumeID: { get_attribute: [openstack_volume, external_id] } 54 | relationships: 55 | - type: cloudify.relationships.depends_on 56 | target: example_storage 57 | - type: cloudify.relationships.depends_on 58 | target: openstack_volume 59 | - type: cloudify.kubernetes.relationships.managed_by_master 60 | target: kubernetes_master 61 | 62 | openstack_volume: 63 | type: cloudify.openstack.nodes.Volume 64 | properties: 65 | openstack_config: *openstack_config 66 | interfaces: 67 | cloudify.interfaces.lifecycle: 68 | create: 69 | inputs: 70 | args: 71 | size: 10 72 | 73 | example_storage: 74 | type: cloudify.kubernetes.resources.StorageClass 75 | properties: 76 | definition: 77 | apiVersion: storage.k8s.io/v1beta1 78 | metadata: 79 | name: gold 80 | provisioner: kubernetes.io/cinder 81 | parameters: 82 | type: fast 83 | availability: nova 84 | relationships: 85 | - type: cloudify.kubernetes.relationships.managed_by_master 86 | target: kubernetes_master 87 | 88 | kubernetes_master: 89 | type: cloudify.kubernetes.nodes.Master 90 | properties: 91 | configuration: 92 | file_content: { get_input: configuration_file_content } 93 | -------------------------------------------------------------------------------- /examples/old-examples/replicasets-blueprint.yaml: -------------------------------------------------------------------------------- 1 | tosca_definitions_version: cloudify_dsl_1_3 2 | 3 | imports: 4 | - http://www.getcloudify.org/spec/cloudify/4.3/types.yaml 5 | - plugin.yaml 6 | 7 | inputs: 8 | 9 | configuration_file_content: 10 | type: string 11 | 12 | spec_port: 13 | default: 8000 14 | 15 | target_port: 16 | default: 80 17 | 18 | container_port: 19 | default: 80 20 | 21 | node_templates: 22 | 23 | nginx_replicaset: 24 | type: cloudify.kubernetes.resources.ReplicaSet 25 | properties: 26 | definition: 27 | apiVersion: extensions/v1beta1 28 | metadata: 29 | name: frontend 30 | spec: 31 | replicas: 3 32 | selector: 33 | matchLabels: 34 | tier: frontend 35 | matchExpressions: 36 | - {key: tier, operator: In, values: [frontend]} 37 | template: 38 | metadata: 39 | labels: 40 | app: nginx 41 | tier: frontend 42 | spec: 43 | containers: 44 | - name: nginx 45 | image: nginx:1.7.9 46 | ports: 47 | - containerPort: { get_input: container_port } 48 | options: 49 | grace_period_seconds: 5 50 | propagation_policy: 'Foreground' 51 | namespace: 'default' 52 | relationships: 53 | - type: cloudify.kubernetes.relationships.managed_by_master 54 | target: kubernetes_master 55 | 56 | kubernetes_master: 57 | type: cloudify.kubernetes.nodes.Master 58 | properties: 59 | configuration: 60 | file_content: { get_input: configuration_file_content } 61 | -------------------------------------------------------------------------------- /examples/old-examples/replication-controller-blueprint.yaml: -------------------------------------------------------------------------------- 1 | tosca_definitions_version: cloudify_dsl_1_3 2 | 3 | imports: 4 | - http://www.getcloudify.org/spec/cloudify/4.3/types.yaml 5 | - plugin.yaml 6 | 7 | inputs: 8 | 9 | kubernetes_master_ip: 10 | type: string 11 | default: { get_secret: kubernetes_master_ip } 12 | 13 | kubernetes_master_port: 14 | type: string 15 | default: { get_secret: kubernetes_master_port } 16 | 17 | kubernetes_certificate_authority_data: 18 | default: { get_secret: kubernetes_certificate_authority_data } 19 | 20 | kubernetes-admin_client_certificate_data: 21 | default: { get_secret: kubernetes-admin_client_certificate_data } 22 | 23 | kubernetes-admin_client_key_data: 24 | default: { get_secret: kubernetes-admin_client_key_data } 25 | 26 | kubernetes_master_configuration: 27 | default: 28 | apiVersion: v1 29 | kind: Config 30 | preferences: {} 31 | current-context: kubernetes-admin@kubernetes 32 | clusters: 33 | - name: kubernetes 34 | cluster: 35 | certificate-authority-data: { get_input: kubernetes_certificate_authority_data } 36 | server: { concat: [ 'https://', { get_input: kubernetes_master_ip}, ':', { get_input: kubernetes_master_port } ] } 37 | contexts: 38 | - name: kubernetes-admin@kubernetes 39 | context: 40 | cluster: kubernetes 41 | user: kubernetes-admin 42 | users: 43 | - name: kubernetes-admin 44 | user: 45 | client-certificate-data: { get_input: kubernetes-admin_client_certificate_data } 46 | client-key-data: { get_input: kubernetes-admin_client_key_data } 47 | 48 | container_port: 49 | default: 80 50 | 51 | node_templates: 52 | 53 | nginx_replication_controller: 54 | type: cloudify.kubernetes.resources.ReplicationController 55 | properties: 56 | definition: 57 | apiVersion: v1 58 | kind: ReplicationController 59 | metadata: 60 | name: nginx 61 | spec: 62 | replicas: 3 63 | selector: 64 | app: nginx 65 | template: 66 | metadata: 67 | name: nginx 68 | labels: 69 | app: nginx 70 | spec: 71 | containers: 72 | - name: nginx 73 | image: nginx 74 | ports: 75 | - containerPort: { get_input: container_port } 76 | options: 77 | grace_period_seconds: 5 78 | propagation_policy: 'Foreground' 79 | namespace: 'default' 80 | relationships: 81 | - type: cloudify.kubernetes.relationships.managed_by_master 82 | target: kubernetes_master 83 | 84 | kubernetes_master: 85 | type: cloudify.kubernetes.nodes.Master 86 | properties: 87 | configuration: 88 | file_content: { get_input: kubernetes_master_configuration } 89 | -------------------------------------------------------------------------------- /examples/old-examples/service-account.yaml: -------------------------------------------------------------------------------- 1 | tosca_definitions_version: cloudify_dsl_1_3 2 | 3 | imports: 4 | - http://www.getcloudify.org/spec/cloudify/4.3.1/types.yaml 5 | - plugin:cloudify-fabric-plugin 6 | - plugin:cloudify-kubernetes-plugin 7 | 8 | inputs: 9 | 10 | kubernetes_master_public_ip: 11 | type: string 12 | 13 | agent_user: 14 | type: string 15 | 16 | kubernetes_master_ip: 17 | type: string 18 | default: { get_secret: kubernetes_master_ip } 19 | 20 | kubernetes_master_port: 21 | type: string 22 | default: { get_secret: kubernetes_master_port } 23 | 24 | kubernetes_certificate_authority_data: 25 | default: { get_secret: kubernetes_certificate_authority_data } 26 | 27 | kubernetes-admin_client_certificate_data: 28 | default: { get_secret: kubernetes-admin_client_certificate_data } 29 | 30 | kubernetes-admin_client_key_data: 31 | default: { get_secret: kubernetes-admin_client_key_data } 32 | 33 | kubernetes_master_configuration: 34 | default: 35 | apiVersion: v1 36 | kind: Config 37 | preferences: {} 38 | current-context: kubernetes-admin@kubernetes 39 | clusters: 40 | - name: kubernetes 41 | cluster: 42 | certificate-authority-data: { get_input: kubernetes_certificate_authority_data } 43 | server: { concat: [ 'https://', { get_input: kubernetes_master_ip}, ':', { get_input: kubernetes_master_port } ] } 44 | contexts: 45 | - name: kubernetes-admin@kubernetes 46 | context: 47 | cluster: kubernetes 48 | user: kubernetes-admin 49 | users: 50 | - name: kubernetes-admin 51 | user: 52 | client-certificate-data: { get_input: kubernetes-admin_client_certificate_data } 53 | client-key-data: { get_input: kubernetes-admin_client_key_data } 54 | 55 | kubernetes_configuration_file_content: 56 | description: > 57 | File content of kubernetes master YAML configuration 58 | default: { get_input: kubernetes_master_configuration } 59 | 60 | service_account_name: 61 | description: A new service account name. 62 | default: examples-user 63 | 64 | service_account_namespace: 65 | description: The namespace to create the new service account in. 66 | default: default 67 | 68 | node_templates: 69 | 70 | kubernetes_master: 71 | type: cloudify.kubernetes.nodes.Master 72 | properties: 73 | configuration: 74 | file_content: { get_input: kubernetes_configuration_file_content } 75 | 76 | new_service_account: 77 | type: cloudify.kubernetes.resources.ServiceAccount 78 | properties: 79 | definition: 80 | apiVersion: v1 81 | kind: ServiceAccount 82 | metadata: 83 | name: { get_input: service_account_name } 84 | namespace: { get_input: service_account_namespace } 85 | options: 86 | namespace: { get_input: service_account_namespace } 87 | relationships: 88 | - type: cloudify.kubernetes.relationships.managed_by_master 89 | target: kubernetes_master 90 | 91 | new_role_binding: 92 | type: cloudify.kubernetes.resources.RoleBinding 93 | properties: 94 | definition: 95 | apiVersion: rbac.authorization.k8s.io/v1beta1 96 | kind: ClusterRoleBinding 97 | metadata: 98 | name: { get_input: service_account_name } 99 | roleRef: 100 | apiGroup: rbac.authorization.k8s.io 101 | kind: ClusterRole 102 | name: cluster-admin 103 | subjects: 104 | - kind: ServiceAccount 105 | name: { get_input: service_account_name } 106 | namespace: { get_input: service_account_namespace } 107 | options: 108 | namespace: { get_input: service_account_namespace } 109 | relationships: 110 | - type: cloudify.kubernetes.relationships.managed_by_master 111 | target: kubernetes_master 112 | 113 | set_token: 114 | type: cloudify.nodes.ApplicationServer 115 | relationships: 116 | - type: cloudify.relationships.depends_on 117 | target: new_role_binding 118 | - type: cloudify.relationships.depends_on 119 | target: new_service_account 120 | interfaces: 121 | cloudify.interfaces.lifecycle: 122 | start: 123 | implementation: fabric.fabric_plugin.tasks.run_task 124 | inputs: 125 | tasks_file: scripts/token.py 126 | task_name: get_token 127 | task_properties: 128 | service_account_name: { get_input: service_account_name } 129 | fabric_env: 130 | host_string: { get_input: kubernetes_master_public_ip } 131 | user: { get_input: agent_user } 132 | key: { get_secret: agent_key_private } 133 | -------------------------------------------------------------------------------- /examples/old-examples/simple-blueprint-authentication-token.yaml: -------------------------------------------------------------------------------- 1 | tosca_definitions_version: cloudify_dsl_1_3 2 | 3 | imports: 4 | - http://www.getcloudify.org/spec/cloudify/4.3/types.yaml 5 | - plugin.yaml 6 | 7 | inputs: 8 | 9 | kubernetes_public_master_url: 10 | type: string 11 | description: > 12 | This represent the host url for kubernetes public url api and it 13 | should on the following format https://MASTER_IP:PORT 14 | MASTER_IP: Refer to the public ip address of the kubernetes manager 15 | PORT: Represent the port on which api server listen on usually it is 6443 16 | 17 | kubernetes_authentication_token: 18 | type: string 19 | description: > 20 | This represent the token which is an authentication token directly to the API server 21 | More info for how to get the token can be found here https://kubernetes.io/docs/tasks/administer-cluster/access-cluster-api/#accessing-the-cluster-api 22 | The generated user token may not have the enough permission to get/create/edit/delete kubernetes resources which required to grant it enough permission 23 | 24 | kubernetes_api_debug_mode: 25 | type: boolean 26 | description: > 27 | Debug switch to enable/track the logs generated 28 | default: false 29 | 30 | 31 | kubernetes_master_configuration: 32 | default: 33 | host: { get_input: kubernetes_public_master_url } 34 | api_key: { get_input: kubernetes_authentication_token } 35 | debug: { get_input: kubernetes_api_debug_mode } 36 | verify_ssl: false # This will be set to false 37 | 38 | kubernetes_api_options: 39 | description: > 40 | kubernetes api options 41 | default: { get_input: kubernetes_master_configuration } 42 | 43 | 44 | node_templates: 45 | master: 46 | type: cloudify.kubernetes.nodes.Master 47 | properties: 48 | configuration: 49 | api_options: { get_input: kubernetes_api_options } 50 | 51 | pod_a: 52 | type: cloudify.kubernetes.resources.Pod 53 | properties: 54 | definition: 55 | apiVersion: v1 56 | kind: Pod 57 | metadata: 58 | name: nginx 59 | namespace: default 60 | spec: 61 | containers: 62 | - name: nginx 63 | image: nginx 64 | volumeMounts: 65 | - name: someunxists 66 | mountPath: /data 67 | ports: 68 | - containerPort: 80 69 | volumes: 70 | - name: someunxists 71 | flexVolume: 72 | driver: "cloudify/mount" 73 | fsType: "ext4" 74 | options: 75 | volumeID: "vol1" 76 | size: "1000m" 77 | volumegroup: "kube_vg" 78 | relationships: 79 | - type: cloudify.kubernetes.relationships.managed_by_master 80 | target: master 81 | 82 | -------------------------------------------------------------------------------- /examples/old-examples/simple-blueprint-defined-resource.yaml: -------------------------------------------------------------------------------- 1 | tosca_definitions_version: cloudify_dsl_1_3 2 | 3 | imports: 4 | - http://www.getcloudify.org/spec/cloudify/4.3/types.yaml 5 | - plugin.yaml 6 | 7 | inputs: 8 | 9 | kubernetes_master_ip: 10 | type: string 11 | default: { get_secret: kubernetes_master_ip } 12 | 13 | kubernetes_master_port: 14 | type: string 15 | default: { get_secret: kubernetes_master_port } 16 | 17 | kubernetes_certificate_authority_data: 18 | default: { get_secret: kubernetes_certificate_authority_data } 19 | 20 | kubernetes-admin_client_certificate_data: 21 | default: { get_secret: kubernetes-admin_client_certificate_data } 22 | 23 | kubernetes-admin_client_key_data: 24 | default: { get_secret: kubernetes-admin_client_key_data } 25 | 26 | kubernetes_master_configuration: 27 | default: 28 | apiVersion: v1 29 | kind: Config 30 | preferences: {} 31 | current-context: kubernetes-admin@kubernetes 32 | clusters: 33 | - name: kubernetes 34 | cluster: 35 | certificate-authority-data: { get_input: kubernetes_certificate_authority_data } 36 | server: { concat: [ 'https://', { get_input: kubernetes_master_ip}, ':', { get_input: kubernetes_master_port } ] } 37 | contexts: 38 | - name: kubernetes-admin@kubernetes 39 | context: 40 | cluster: kubernetes 41 | user: kubernetes-admin 42 | users: 43 | - name: kubernetes-admin 44 | user: 45 | client-certificate-data: { get_input: kubernetes-admin_client_certificate_data } 46 | client-key-data: { get_input: kubernetes-admin_client_key_data } 47 | 48 | kubernetes_configuration_file_content: 49 | description: > 50 | File content of kubernetes master YAML configuration 51 | default: { get_input: kubernetes_master_configuration } 52 | 53 | node_templates: 54 | master: 55 | type: cloudify.kubernetes.nodes.Master 56 | properties: 57 | configuration: 58 | file_content: { get_input: kubernetes_configuration_file_content } 59 | 60 | pod_a: 61 | type: cloudify.kubernetes.resources.Pod 62 | properties: 63 | definition: 64 | apiVersion: v1 65 | kind: Pod 66 | metadata: 67 | name: nginx 68 | namespace: default 69 | spec: 70 | containers: 71 | - name: nginx 72 | image: nginx 73 | volumeMounts: 74 | - name: someunxists 75 | mountPath: /data 76 | ports: 77 | - containerPort: 80 78 | volumes: 79 | - name: someunxists 80 | flexVolume: 81 | driver: "cloudify/mount" 82 | fsType: "ext4" 83 | options: 84 | volumeID: "vol1" 85 | size: "1000m" 86 | volumegroup: "kube_vg" 87 | relationships: 88 | - type: cloudify.kubernetes.relationships.managed_by_master 89 | target: master 90 | -------------------------------------------------------------------------------- /examples/old-examples/simple-custom-blueprint-defined-resource.yaml: -------------------------------------------------------------------------------- 1 | tosca_definitions_version: cloudify_dsl_1_3 2 | 3 | imports: 4 | - http://www.getcloudify.org/spec/cloudify/4.3/types.yaml 5 | - plugin.yaml 6 | 7 | inputs: 8 | 9 | kubernetes_master_ip: 10 | type: string 11 | default: { get_secret: kubernetes_master_ip } 12 | 13 | kubernetes_master_port: 14 | type: string 15 | default: { get_secret: kubernetes_master_port } 16 | 17 | kubernetes_certificate_authority_data: 18 | default: { get_secret: kubernetes_certificate_authority_data } 19 | 20 | kubernetes-admin_client_certificate_data: 21 | default: { get_secret: kubernetes-admin_client_certificate_data } 22 | 23 | kubernetes-admin_client_key_data: 24 | default: { get_secret: kubernetes-admin_client_key_data } 25 | 26 | kubernetes_master_configuration: 27 | default: 28 | apiVersion: v1 29 | kind: Config 30 | preferences: {} 31 | current-context: kubernetes-admin@kubernetes 32 | clusters: 33 | - name: kubernetes 34 | cluster: 35 | certificate-authority-data: { get_input: kubernetes_certificate_authority_data } 36 | server: { concat: [ 'https://', { get_input: kubernetes_master_ip}, ':', { get_input: kubernetes_master_port } ] } 37 | contexts: 38 | - name: kubernetes-admin@kubernetes 39 | context: 40 | cluster: kubernetes 41 | user: kubernetes-admin 42 | users: 43 | - name: kubernetes-admin 44 | user: 45 | client-certificate-data: { get_input: kubernetes-admin_client_certificate_data } 46 | client-key-data: { get_input: kubernetes-admin_client_key_data } 47 | 48 | kubernetes_configuration_file_content: 49 | description: > 50 | File content of kubernetes master YAML configuration 51 | default: { get_input: kubernetes_master_configuration } 52 | 53 | node_templates: 54 | master: 55 | type: cloudify.kubernetes.nodes.Master 56 | properties: 57 | configuration: 58 | file_content: { get_input: kubernetes_configuration_file_content } 59 | 60 | pod_a: 61 | type: cloudify.kubernetes.resources.CustomBlueprintDefinedResource 62 | properties: 63 | definition: 64 | apiVersion: v1 65 | kind: Pod 66 | metadata: 67 | name: pod-a 68 | spec: 69 | containers: 70 | - name: pod-a-1 71 | image: "centos:7" 72 | command: ["/bin/bash"] 73 | stdin: true 74 | tty: true 75 | securityContext: 76 | privileged: true 77 | api_mapping: 78 | create: 79 | api: CoreV1Api 80 | method: create_namespaced_pod 81 | payload: V1Pod 82 | read: 83 | api: CoreV1Api 84 | method: read_namespaced_pod 85 | delete: 86 | api: CoreV1Api 87 | method: delete_namespaced_pod 88 | payload: V1DeleteOptions 89 | relationships: 90 | - type: cloudify.kubernetes.relationships.managed_by_master 91 | target: master 92 | -------------------------------------------------------------------------------- /examples/old-examples/simple-file-defined-resource.yaml: -------------------------------------------------------------------------------- 1 | tosca_definitions_version: cloudify_dsl_1_3 2 | 3 | imports: 4 | - http://www.getcloudify.org/spec/cloudify/4.3/types.yaml 5 | - plugin.yaml 6 | 7 | inputs: 8 | 9 | kubernetes_master_ip: 10 | type: string 11 | default: { get_secret: kubernetes_master_ip } 12 | 13 | kubernetes_master_port: 14 | type: string 15 | default: { get_secret: kubernetes_master_port } 16 | 17 | kubernetes_certificate_authority_data: 18 | default: { get_secret: kubernetes_certificate_authority_data } 19 | 20 | kubernetes-admin_client_certificate_data: 21 | default: { get_secret: kubernetes-admin_client_certificate_data } 22 | 23 | kubernetes-admin_client_key_data: 24 | default: { get_secret: kubernetes-admin_client_key_data } 25 | 26 | kubernetes_master_configuration: 27 | default: 28 | apiVersion: v1 29 | kind: Config 30 | preferences: {} 31 | current-context: kubernetes-admin@kubernetes 32 | clusters: 33 | - name: kubernetes 34 | cluster: 35 | certificate-authority-data: { get_input: kubernetes_certificate_authority_data } 36 | server: { concat: [ 'https://', { get_input: kubernetes_master_ip}, ':', { get_input: kubernetes_master_port } ] } 37 | contexts: 38 | - name: kubernetes-admin@kubernetes 39 | context: 40 | cluster: kubernetes 41 | user: kubernetes-admin 42 | users: 43 | - name: kubernetes-admin 44 | user: 45 | client-certificate-data: { get_input: kubernetes-admin_client_certificate_data } 46 | client-key-data: { get_input: kubernetes-admin_client_key_data } 47 | 48 | kubernetes_configuration_file_content: 49 | description: > 50 | File content of kubernetes master YAML configuration 51 | default: { get_input: kubernetes_master_configuration } 52 | 53 | node_templates: 54 | master: 55 | type: cloudify.kubernetes.nodes.Master 56 | properties: 57 | configuration: 58 | file_content: { get_input: kubernetes_configuration_file_content } 59 | 60 | pod_a: 61 | type: cloudify.kubernetes.resources.FileDefinedResource 62 | properties: 63 | file: 64 | resource_path: resources/podA.yaml 65 | relationships: 66 | - type: cloudify.kubernetes.relationships.managed_by_master 67 | target: master 68 | -------------------------------------------------------------------------------- /examples/old-examples/test-deployment.yaml: -------------------------------------------------------------------------------- 1 | tosca_definitions_version: cloudify_dsl_1_3 2 | 3 | imports: 4 | - http://www.getcloudify.org/spec/cloudify/5.0.5/types.yaml 5 | - plugin:cloudify-kubernetes-plugin 6 | 7 | node_templates: 8 | 9 | master: 10 | type: cloudify.kubernetes.nodes.Master 11 | properties: 12 | configuration: 13 | api_options: 14 | host: { concat: [ 'https://', { get_secret: kubernetes_master_ip }, ':', { get_secret: kubernetes_master_port } ] } 15 | api_key: { get_secret: kubernetes_token } 16 | debug: false 17 | verify_ssl: false 18 | 19 | 20 | deployment: 21 | type: cloudify.kubernetes.resources.Deployment 22 | properties: 23 | definition: 24 | apiVersion: apps/v1 25 | kind: Deployment 26 | metadata: 27 | name: nginx-deployment 28 | labels: 29 | app: nginx 30 | spec: 31 | replicas: 3 32 | selector: 33 | matchLabels: 34 | app: nginx 35 | template: 36 | metadata: 37 | labels: 38 | app: nginx 39 | spec: 40 | containers: 41 | - name: nginx 42 | image: nginx:1.7.9 43 | ports: 44 | - containerPort: 80 45 | relationships: 46 | - type: cloudify.kubernetes.relationships.managed_by_master 47 | target: master -------------------------------------------------------------------------------- /examples/old-examples/test-persistent-volume.yaml: -------------------------------------------------------------------------------- 1 | tosca_definitions_version: cloudify_dsl_1_3 2 | 3 | imports: 4 | - http://www.getcloudify.org/spec/cloudify/5.0.0/types.yaml 5 | - plugin:cloudify-kubernetes-plugin 6 | 7 | node_templates: 8 | 9 | master: 10 | type: cloudify.kubernetes.nodes.Master 11 | properties: 12 | configuration: 13 | api_options: 14 | host: { concat: [ 'https://', { get_secret: kubernetes_master_ip }, ':', { get_secret: kubernetes_master_port } ] } 15 | api_key: { get_secret: kubernetes_token } 16 | debug: false 17 | verify_ssl: false 18 | 19 | pv: 20 | type: cloudify.kubernetes.resources.Pod 21 | properties: 22 | definition: 23 | apiVersion: v1 24 | kind: PersistentVolume 25 | metadata: 26 | name: local-pv-2 27 | labels: 28 | type: local 29 | spec: 30 | capacity: 31 | storage: 20Gi 32 | accessModes: 33 | - ReadWriteOnce 34 | hostPath: 35 | path: /tmp/data/pv-2 36 | relationships: 37 | - type: cloudify.kubernetes.relationships.managed_by_master 38 | target: master 39 | 40 | pvc: 41 | type: cloudify.kubernetes.resources.Pod 42 | properties: 43 | definition: 44 | apiVersion: v1 45 | kind: PersistentVolumeClaim 46 | metadata: 47 | name: wp-pv-claim 48 | labels: 49 | app: wordpress 50 | spec: 51 | accessModes: 52 | - ReadWriteOnce 53 | resources: 54 | requests: 55 | storage: 20Gi 56 | relationships: 57 | - type: cloudify.kubernetes.relationships.managed_by_master 58 | target: master 59 | -------------------------------------------------------------------------------- /examples/old-examples/test-pod.yaml: -------------------------------------------------------------------------------- 1 | tosca_definitions_version: cloudify_dsl_1_3 2 | 3 | imports: 4 | - http://www.getcloudify.org/spec/cloudify/5.0.0/types.yaml 5 | - plugin:cloudify-kubernetes-plugin 6 | 7 | node_templates: 8 | 9 | master: 10 | type: cloudify.kubernetes.nodes.Master 11 | properties: 12 | configuration: 13 | api_options: 14 | host: { concat: [ 'https://', { get_secret: kubernetes_master_ip }, ':', { get_secret: kubernetes_master_port } ] } 15 | api_key: { get_secret: kubernetes_token } 16 | debug: false 17 | verify_ssl: false 18 | 19 | nginx: 20 | type: cloudify.kubernetes.resources.Pod 21 | properties: 22 | definition: 23 | apiVersion: v1 24 | kind: Pod 25 | metadata: 26 | name: nginx 27 | spec: 28 | containers: 29 | - name: nginx 30 | image: nginx:stable 31 | relationships: 32 | - type: cloudify.kubernetes.relationships.managed_by_master 33 | target: master 34 | -------------------------------------------------------------------------------- /examples/old-examples/test-service.yaml: -------------------------------------------------------------------------------- 1 | tosca_definitions_version: cloudify_dsl_1_3 2 | 3 | imports: 4 | - http://www.getcloudify.org/spec/cloudify/5.0.0/types.yaml 5 | - plugin:cloudify-kubernetes-plugin 6 | 7 | node_templates: 8 | 9 | master: 10 | type: cloudify.kubernetes.nodes.Master 11 | properties: 12 | configuration: 13 | api_options: 14 | host: { concat: [ 'https://', { get_secret: kubernetes_master_ip }, ':', { get_secret: kubernetes_master_port } ] } 15 | api_key: { get_secret: kubernetes_token } 16 | debug: false 17 | verify_ssl: false 18 | 19 | nginx: 20 | type: cloudify.kubernetes.resources.Pod 21 | properties: 22 | definition: 23 | apiVersion: v1 24 | kind: Service 25 | metadata: 26 | name: wordpress 27 | labels: 28 | app: wordpress 29 | spec: 30 | ports: 31 | - port: 80 32 | nodePort: 30080 33 | selector: 34 | app: wordpress 35 | tier: frontend 36 | type: LoadBalancer 37 | relationships: 38 | - type: cloudify.kubernetes.relationships.managed_by_master 39 | target: master 40 | -------------------------------------------------------------------------------- /examples/resources/cloudify-crd.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | # name must match the spec fields below, and be in the form: . 5 | name: crontabs.stable.example.com 6 | spec: 7 | # group name to use for REST API: /apis// 8 | group: stable.example.com 9 | # list of versions supported by this CustomResourceDefinition 10 | versions: 11 | - name: v1 12 | # Each version can be enabled/disabled by Served flag. 13 | served: true 14 | # One and only one version must be marked as the storage version. 15 | storage: true 16 | schema: 17 | openAPIV3Schema: 18 | type: object 19 | properties: 20 | spec: 21 | type: object 22 | properties: 23 | cronSpec: 24 | type: string 25 | image: 26 | type: string 27 | replicas: 28 | type: integer 29 | # either Namespaced or Cluster 30 | scope: Namespaced 31 | names: 32 | # plural name to be used in the URL: /apis/// 33 | plural: crontabs 34 | # singular name to be used as an alias on the CLI and for display 35 | singular: crontab 36 | # kind is normally the CamelCased singular type. Your resource manifests use this. 37 | kind: CronTab 38 | # shortNames allow shorter string to match your resource on the CLI 39 | shortNames: 40 | - ct 41 | --- 42 | apiVersion: "stable.example.com/v1" 43 | kind: CronTab 44 | metadata: 45 | name: my-new-cron-object 46 | annotations: 47 | cloudify-crd-group: stable.example.com 48 | cloudify-crd-plural: crontabs 49 | cloudify-crd-version: v1 50 | spec: 51 | cronSpec: "* * * * */5" 52 | image: my-awesome-cron-image 53 | -------------------------------------------------------------------------------- /examples/resources/cloudify_manager/pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | generateName: cfy-manager 5 | labels: 6 | app: cloudify 7 | component: cfy-cluster 8 | spec: 9 | restartPolicy: Never 10 | volumes: 11 | - name: cgroup 12 | hostPath: 13 | path: /sys/fs/cgroup 14 | containers: 15 | - name: cloudify-manager 16 | image: cloudifyplatform/premium 17 | env: 18 | - name: PRIVATE_IP 19 | valueFrom: 20 | fieldRef: 21 | fieldPath: status.podIP 22 | lifecycle: 23 | postStart: 24 | exec: 25 | command: 26 | - bash 27 | - "-c" 28 | - | 29 | set -ex 30 | 31 | sleep 15 32 | 33 | PUBLIC_IP=$CFY_SERVICE_PORT_80_TCP_ADDR 34 | UPDATE_CERT_MD_SCRIPT=/tmp/update_cert_metadata.py 35 | 36 | curl -o $UPDATE_CERT_MD_SCRIPT https://raw.githubusercontent.com/Cloudify-PS/cloudify-labs-env-blueprints/4068a109545f9dfdf87e42b70583b831abfa3c63/components/CFYManager4x/scripts/update_cert_metadata.py 37 | 38 | sudo chmod +x $UPDATE_CERT_MD_SCRIPT 39 | sudo cp /etc/cloudify/ssl/certificate_metadata /etc/cloudify/ssl/certificate_metadata.old 40 | sudo sh -c "cat /etc/cloudify/ssl/certificate_metadata.old | $UPDATE_CERT_MD_SCRIPT external $PUBLIC_IP > /etc/cloudify/ssl/certificate_metadata" 41 | 42 | sudo /opt/manager/env/bin/python /opt/cloudify/manager-ip-setter/update-provider-context.py --networks /etc/cloudify/ssl/certificate_metadata $PRIVATE_IP 43 | 44 | sudo rm /opt/cloudify/manager-ip-setter/touched 45 | 46 | sudo /opt/cloudify/manager-ip-setter/manager-ip-setter.sh 47 | 48 | sudo systemctl restart nginx 2>&1 >/dev/null 49 | sudo systemctl restart cloudify-rabbitmq 2>&1 >/dev/null 50 | ports: 51 | - containerPort: 1025 52 | hostPort: 22 53 | - containerPort: 80 54 | hostPort: 80 55 | - containerPort: 999 56 | hostPort: 999 57 | - containerPort: 8080 58 | hostPort: 8080 59 | - containerPort: 5432 60 | hostPort: 5432 61 | - containerPort: 5671 62 | hostPort: 5671 63 | - containerPort: 5672 64 | hostPort: 5672 65 | - containerPort: 8086 66 | hostPort: 8086 67 | - containerPort: 8300 68 | hostPort: 8300 69 | - containerPort: 8301 70 | hostPort: 8301 71 | - containerPort: 8500 72 | hostPort: 8500 73 | - containerPort: 9200 74 | hostPort: 9200 75 | - containerPort: 15432 76 | hostPort: 15432 77 | - containerPort: 15672 78 | hostPort: 15672 79 | - containerPort: 22000 80 | hostPort: 22000 81 | volumeMounts: 82 | - mountPath: /sys/fs/cgroup 83 | readOnly: True 84 | name: cgroup 85 | securityContext: 86 | capabilities: 87 | add: ["SYS_ADMIN"] 88 | volumeMounts: 89 | - mountPath: /sys/fs/cgroup 90 | readOnly: True 91 | name: cgroup 92 | securityContext: 93 | capabilities: 94 | add: ["SYS_ADMIN"] 95 | -------------------------------------------------------------------------------- /examples/resources/cloudify_manager/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: cfy-service 5 | labels: 6 | app: cloudify 7 | component: cfy-cluster 8 | spec: 9 | ports: 10 | - name: http 11 | port: 80 12 | selector: 13 | component: cfy-cluster 14 | type: LoadBalancer 15 | -------------------------------------------------------------------------------- /examples/resources/cluster-role-binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1beta1 2 | # This cluster role binding allows anyone in the "manager" group to read secrets in any namespace. 3 | kind: ClusterRoleBinding 4 | metadata: 5 | name: read-secrets-global 6 | subjects: 7 | - kind: Group 8 | name: manager # Name is case sensitive 9 | apiGroup: rbac.authorization.k8s.io 10 | roleRef: 11 | kind: ClusterRole 12 | name: secret-reader 13 | apiGroup: rbac.authorization.k8s.io 14 | -------------------------------------------------------------------------------- /examples/resources/cluster-role.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | # "namespace" omitted since ClusterRoles are not namespaced 6 | name: secret-reader 7 | rules: 8 | - apiGroups: [""] 9 | resources: ["secrets"] 10 | verbs: ["get", "watch", "list"] 11 | -------------------------------------------------------------------------------- /examples/resources/config-map.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: simpleconfig 5 | data: 6 | foo: bar 7 | hello: world 8 | -------------------------------------------------------------------------------- /examples/resources/crd.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: crontabs.stable.example.com 5 | spec: 6 | # This value is used in the cloudify-crd-group annotation in the usage of the CRD. 7 | group: stable.example.com 8 | versions: 9 | # This value is used in the cloudify-crd-version annotation in the usage of the CRD. 10 | - name: v1 11 | served: true 12 | storage: true 13 | schema: 14 | openAPIV3Schema: 15 | type: object 16 | properties: 17 | spec: 18 | type: object 19 | properties: 20 | cronSpec: 21 | type: string 22 | image: 23 | type: string 24 | replicas: 25 | type: integer 26 | scope: Namespaced 27 | names: 28 | # This value is used in the cloudify-crd-plural annotation in the usage of the CRD. 29 | plural: crontabs 30 | singular: crontab 31 | kind: CronTab 32 | shortNames: 33 | - ct 34 | --- 35 | apiVersion: "stable.example.com/v1" 36 | kind: CronTab 37 | metadata: 38 | name: my-new-cron-object 39 | annotations: 40 | # See comments in the CRD defintion above for where to find these values. 41 | cloudify-crd-group: stable.example.com 42 | cloudify-crd-plural: crontabs 43 | cloudify-crd-version: v1 44 | spec: 45 | cronSpec: "* * * * */5" 46 | image: my-awesome-cron-image -------------------------------------------------------------------------------- /examples/resources/crontabs.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "stable.example.com/v1" 2 | kind: CronTab 3 | metadata: 4 | name: my-new-cron-object 5 | annotations: 6 | cloudify-crd-group: stable.example.com 7 | cloudify-crd-plural: crontabs 8 | cloudify-crd-version: v1 9 | spec: 10 | cronSpec: "* * * * */5" 11 | image: my-awesome-cron-image 12 | -------------------------------------------------------------------------------- /examples/resources/custom-resource.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: crontabs.stable.example.com 5 | spec: 6 | group: stable.example.com 7 | versions: 8 | - name: v1 9 | served: true 10 | storage: true 11 | schema: 12 | openAPIV3Schema: 13 | type: object 14 | properties: 15 | spec: 16 | type: object 17 | properties: 18 | cronSpec: 19 | type: string 20 | image: 21 | type: string 22 | replicas: 23 | type: integer 24 | scope: Namespaced 25 | names: 26 | plural: crontabs 27 | singular: crontab 28 | kind: CronTab 29 | shortNames: 30 | - ct 31 | -------------------------------------------------------------------------------- /examples/resources/daemon-set.yaml: -------------------------------------------------------------------------------- 1 | # Use this configuration file to create a DaemonSet that disables the root squash restriction for NFS file shares that are mounted to cluster pods in IBM Cloud Kubernetes Service. 2 | # Note that the pods run in privileged mode, which can pose a security risk. Use with caution. 3 | apiVersion: apps/v1 4 | kind: DaemonSet 5 | metadata: 6 | name: norootsquash 7 | labels: 8 | tier: management 9 | app: norootsquash 10 | spec: 11 | selector: 12 | matchLabels: 13 | name: norootsquash 14 | template: 15 | metadata: 16 | labels: 17 | name: norootsquash 18 | spec: 19 | containers: 20 | - resources: 21 | requests: 22 | cpu: 0.1 23 | securityContext: 24 | privileged: true 25 | image: ubuntu:16.04 26 | name: unrootsquash 27 | command: ["/bin/sh", "-c"] 28 | args: 29 | - > 30 | grep "^Domain = slnfsv4.com" /modifyetc/idmapd.conf; 31 | if [ "$?" -ne "0" ] ; then 32 | sed -i 's/.*Domain =.*/Domain = slnfsv4.com/g' /modifyetc/idmapd.conf; 33 | nfsidmap -c; 34 | fi; 35 | while true; do 36 | sleep 100000; 37 | done 38 | volumeMounts: 39 | - name: modifyetc 40 | mountPath: /modifyetc 41 | volumes: 42 | - name: modifyetc 43 | hostPath: 44 | path: /etc -------------------------------------------------------------------------------- /examples/resources/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: nginx-deployment 5 | labels: 6 | app: nginx 7 | spec: 8 | replicas: 3 9 | selector: 10 | matchLabels: 11 | app: nginx 12 | template: 13 | metadata: 14 | labels: 15 | app: nginx 16 | spec: 17 | containers: 18 | - name: nginx 19 | image: nginx:1.7.9 20 | ports: 21 | - containerPort: 80 22 | -------------------------------------------------------------------------------- /examples/resources/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: test-ingress 5 | annotations: 6 | nginx.ingress.kubernetes.io/rewrite-target: / 7 | spec: 8 | rules: 9 | - http: 10 | paths: 11 | - pathType: Prefix 12 | path: /testpath 13 | backend: 14 | # serviceName: test 15 | # servicePort: 80 16 | service: 17 | name: service2 18 | port: 19 | number: 80 20 | -------------------------------------------------------------------------------- /examples/resources/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: my-test-namespace 5 | -------------------------------------------------------------------------------- /examples/resources/network-policy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: test-network-policy 5 | namespace: default 6 | spec: 7 | podSelector: 8 | matchLabels: 9 | role: db 10 | policyTypes: 11 | - Ingress 12 | - Egress 13 | ingress: 14 | - from: 15 | - ipBlock: 16 | cidr: 172.17.0.0/16 17 | except: 18 | - 172.17.1.0/24 19 | - namespaceSelector: 20 | matchLabels: 21 | project: myproject 22 | - podSelector: 23 | matchLabels: 24 | role: frontend 25 | ports: 26 | - protocol: TCP 27 | port: 6379 28 | egress: 29 | - to: 30 | - ipBlock: 31 | cidr: 10.0.0.0/24 32 | ports: 33 | - protocol: TCP 34 | port: 5978 35 | -------------------------------------------------------------------------------- /examples/resources/node.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Node 3 | metadata: 4 | name: k8s-node 5 | labels: 6 | name: k8s-node 7 | -------------------------------------------------------------------------------- /examples/resources/pod-a.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: pod-a 5 | spec: 6 | containers: 7 | - name: pod-a-1 8 | image: "centos:7" 9 | command: ["/bin/bash"] 10 | stdin: true 11 | tty: true 12 | securityContext: 13 | privileged: true 14 | -------------------------------------------------------------------------------- /examples/resources/pod-b.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: pod-b 5 | spec: 6 | containers: 7 | - name: pod-b-1 8 | image: "centos:7" 9 | command: ["/bin/bash"] 10 | stdin: true 11 | tty: true 12 | securityContext: 13 | privileged: true -------------------------------------------------------------------------------- /examples/resources/pod-security-policy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: policy/v1beta1 2 | kind: PodSecurityPolicy 3 | metadata: 4 | name: example 5 | spec: 6 | privileged: false # Don't allow privileged pods! 7 | # The rest fills in some required fields. 8 | seLinux: 9 | rule: RunAsAny 10 | supplementalGroups: 11 | rule: RunAsAny 12 | runAsUser: 13 | rule: RunAsAny 14 | fsGroup: 15 | rule: RunAsAny 16 | volumes: 17 | - '*' 18 | -------------------------------------------------------------------------------- /examples/resources/pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: nginx-test-pod 5 | spec: 6 | containers: 7 | - name: nginx-test-pod 8 | image: nginx:stable 9 | -------------------------------------------------------------------------------- /examples/resources/pods-c-d.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: pod-c 5 | spec: 6 | containers: 7 | - name: pod-c-1 8 | image: "centos:7" 9 | command: ["/bin/bash"] 10 | stdin: true 11 | tty: true 12 | securityContext: 13 | privileged: true 14 | --- 15 | apiVersion: v1 16 | kind: Pod 17 | metadata: 18 | name: pod-d 19 | spec: 20 | containers: 21 | - name: pod-d-1 22 | image: "centos:7" 23 | command: ["/bin/bash"] 24 | stdin: true 25 | tty: true 26 | securityContext: 27 | privileged: true 28 | -------------------------------------------------------------------------------- /examples/resources/pv.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolume 3 | metadata: 4 | name: local-pv-2 5 | labels: 6 | type: local 7 | spec: 8 | capacity: 9 | storage: 20Gi 10 | accessModes: 11 | - ReadWriteOnce 12 | hostPath: 13 | path: /tmp/data/pv-2 14 | -------------------------------------------------------------------------------- /examples/resources/pvc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: wp-pv-claim 5 | labels: 6 | app: wordpress 7 | spec: 8 | accessModes: 9 | - ReadWriteOnce 10 | resources: 11 | requests: 12 | storage: 20Gi 13 | -------------------------------------------------------------------------------- /examples/resources/replica-set.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: ReplicaSet 3 | metadata: 4 | name: frontend 5 | labels: 6 | app: guestbook 7 | tier: frontend 8 | spec: 9 | # modify replicas according to your case 10 | replicas: 3 11 | selector: 12 | matchLabels: 13 | tier: frontend 14 | template: 15 | metadata: 16 | labels: 17 | tier: frontend 18 | spec: 19 | containers: 20 | - name: php-redis 21 | image: gcr.io/google_samples/gb-frontend:v3 22 | -------------------------------------------------------------------------------- /examples/resources/replication-controller.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ReplicationController 3 | metadata: 4 | name: nginx 5 | spec: 6 | replicas: 3 7 | selector: 8 | app: nginx 9 | template: 10 | metadata: 11 | name: nginx 12 | labels: 13 | app: nginx 14 | spec: 15 | containers: 16 | - name: nginx 17 | image: nginx 18 | ports: 19 | - containerPort: 80 20 | -------------------------------------------------------------------------------- /examples/resources/role-binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | # This role binding allows "jane" to read pods in the "default" namespace. 3 | kind: RoleBinding 4 | metadata: 5 | name: read-pods 6 | namespace: default 7 | subjects: 8 | - kind: User 9 | name: jane # Name is case sensitive 10 | apiGroup: rbac.authorization.k8s.io 11 | roleRef: 12 | kind: Role #this must be Role or ClusterRole 13 | name: pod-reader # this must match the name of the Role or ClusterRole you wish to bind to 14 | apiGroup: rbac.authorization.k8s.io 15 | -------------------------------------------------------------------------------- /examples/resources/role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: Role 3 | metadata: 4 | namespace: default 5 | name: pod-reader 6 | rules: 7 | - apiGroups: [""] # "" indicates the core API group 8 | resources: ["pods"] 9 | verbs: ["get", "watch", "list"] 10 | -------------------------------------------------------------------------------- /examples/resources/secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: build-robot 5 | --- 6 | apiVersion: v1 7 | kind: Secret 8 | metadata: 9 | name: build-robot-secret 10 | annotations: 11 | kubernetes.io/service-account.name: build-robot 12 | type: kubernetes.io/service-account-token 13 | -------------------------------------------------------------------------------- /examples/resources/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: nginx-test-service 5 | labels: 6 | app: wordpress 7 | spec: 8 | ports: 9 | - port: 80 10 | nodePort: 30080 11 | selector: 12 | app: wordpress 13 | tier: frontend 14 | type: LoadBalancer 15 | -------------------------------------------------------------------------------- /examples/resources/stateful-set.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 # for k8s versions before 1.9.0 use apps/v1beta2 and before 1.8.0 use extensions/v1beta1 2 | kind: StatefulSet 3 | metadata: 4 | name: web 5 | labels: 6 | app: nginx 7 | spec: 8 | serviceName: "nginx" 9 | selector: 10 | matchLabels: 11 | app: nginx 12 | replicas: 14 13 | template: 14 | metadata: 15 | labels: 16 | app: nginx 17 | spec: 18 | containers: 19 | - name: nginx 20 | image: k8s.gcr.io/nginx-slim:0.8 21 | -------------------------------------------------------------------------------- /examples/resources/storage-class.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: storage.k8s.io/v1beta1 2 | kind: StorageClass 3 | metadata: 4 | name: standard 5 | provisioner: kubernetes.io/local 6 | -------------------------------------------------------------------------------- /examples/scripts/configure_node.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2017-2019 Cloudify Platform Ltd. All rights reserved 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import subprocess 18 | from cloudify import ctx 19 | from cloudify.state import ctx_parameters as inputs 20 | 21 | 22 | def execute_command(_command): 23 | 24 | ctx.logger.debug('_command {0}.'.format(_command)) 25 | 26 | subprocess_args = { 27 | 'args': _command.split(), 28 | 'stdout': subprocess.PIPE, 29 | 'stderr': subprocess.PIPE 30 | } 31 | 32 | ctx.logger.debug('subprocess_args {0}.'.format(subprocess_args)) 33 | 34 | process = subprocess.Popen(**subprocess_args) 35 | output, error = process.communicate() 36 | 37 | ctx.logger.debug('command: {0} '.format(_command)) 38 | ctx.logger.debug('output: {0} '.format(output)) 39 | ctx.logger.debug('error: {0} '.format(error)) 40 | ctx.logger.debug('process.returncode: {0} '.format(process.returncode)) 41 | 42 | if process.returncode: 43 | ctx.logger.error('Running `{0}` returns error.'.format(_command)) 44 | return False 45 | 46 | return output 47 | 48 | 49 | if __name__ == '__main__': 50 | 51 | join_command = inputs['join_command'] 52 | join_command = 'sudo {0} --skip-preflight-checks'.format(join_command) 53 | execute_command(join_command) 54 | 55 | # Install weave-related utils 56 | execute_command('sudo curl -L git.io/weave -o /usr/local/bin/weave') 57 | execute_command('sudo chmod a+x /usr/local/bin/weave') 58 | execute_command('sudo curl -L git.io/scope -o /usr/local/bin/scope') 59 | execute_command('sudo chmod a+x /usr/local/bin/scope') 60 | execute_command('/usr/local/bin/scope launch') 61 | 62 | hostname = execute_command('hostname') 63 | ctx.instance.runtime_properties['hostname'] = hostname.rstrip('\n') 64 | -------------------------------------------------------------------------------- /examples/scripts/create.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2017-2019 Cloudify Platform Ltd. All rights reserved 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import subprocess 18 | from cloudify import ctx 19 | from cloudify.exceptions import RecoverableError 20 | 21 | 22 | def check_for_docker(): 23 | 24 | command = 'docker ps' 25 | 26 | try: 27 | process = subprocess.Popen( 28 | command.split() 29 | ) 30 | except OSError: 31 | return False 32 | 33 | output, error = process.communicate() 34 | 35 | ctx.logger.debug('command: {0} '.format(command)) 36 | ctx.logger.debug('output: {0} '.format(output)) 37 | ctx.logger.debug('error: {0} '.format(error)) 38 | ctx.logger.debug('process.returncode: {0} '.format(process.returncode)) 39 | 40 | if process.returncode: 41 | ctx.logger.error('Running `{0}` returns error.'.format(command)) 42 | return False 43 | 44 | return True 45 | 46 | 47 | if __name__ == '__main__': 48 | 49 | if not check_for_docker(): 50 | raise RecoverableError('Waiting for docker to be installed.') 51 | -------------------------------------------------------------------------------- /examples/scripts/tasks.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2017-2019 Cloudify Platform Ltd. All rights reserved 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from fabric.api import run 18 | 19 | 20 | def label_node(labels, hostname): 21 | if labels: 22 | label_list = [] 23 | for key, value in labels.items(): 24 | label_pair_string = '%s=%s' % (key, value) 25 | label_list.append(label_pair_string) 26 | label_string = ' '.join(label_list) 27 | command = 'kubectl label nodes %s %s' % (hostname, label_string) 28 | run(command) 29 | 30 | 31 | def stop_node(hostname): 32 | command = 'kubectl drain %s' % (hostname) 33 | run(command) 34 | 35 | 36 | def delete_node(hostname): 37 | command = 'kubectl delete no %s' % (hostname) 38 | run(command) 39 | -------------------------------------------------------------------------------- /examples/scripts/token.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2018 Cloudify Platform Ltd. All rights reserved 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | from cloudify.manager import get_rest_client 19 | from cloudify_rest_client.exceptions import CloudifyClientError 20 | from cloudify.exceptions import NonRecoverableError 21 | from fabric.api import run 22 | 23 | command = "kubectl -n default describe secret " \ 24 | "$(kubectl -n default get secret | " \ 25 | "grep {0} | awk '{{print $1}}') " \ 26 | "| grep 'token:' | awk '{{print $2}}'" 27 | 28 | 29 | def get_token(service_account_name): 30 | token = run(command.format(service_account_name)) 31 | try: 32 | client = get_rest_client() 33 | client.secrets.create(key=service_account_name, value=token) 34 | except CloudifyClientError as e: 35 | raise NonRecoverableError(str(e)) 36 | -------------------------------------------------------------------------------- /examples/storage-class.yaml: -------------------------------------------------------------------------------- 1 | tosca_definitions_version: cloudify_dsl_1_3 2 | 3 | imports: 4 | - http://www.getcloudify.org/spec/cloudify/5.0.5/types.yaml 5 | - plugin:cloudify-kubernetes-plugin 6 | 7 | inputs: 8 | 9 | kubernetes_master: 10 | type: string 11 | 12 | node_templates: 13 | 14 | storage_class: 15 | type: cloudify.kubernetes.resources.StorageClass 16 | properties: 17 | client_config: 18 | authentication: 19 | gcp_service_account: { get_secret: gcp_credentials } 20 | configuration: 21 | api_options: 22 | host: { get_input: kubernetes_master } 23 | verify_ssl: false 24 | debug: false 25 | definition: 26 | apiVersion: storage.k8s.io/v1beta1 27 | kind: StorageClass 28 | metadata: 29 | name: standard 30 | provisioner: kubernetes.io/local 31 | -------------------------------------------------------------------------------- /examples/test-deployment.yaml: -------------------------------------------------------------------------------- 1 | tosca_definitions_version: cloudify_dsl_1_3 2 | 3 | imports: 4 | - https://cloudify.co/spec/cloudify/6.4.0/types.yaml 5 | - plugin:cloudify-kubernetes-plugin 6 | 7 | inputs: 8 | 9 | kubernetes_master: 10 | type: string 11 | 12 | node_templates: 13 | 14 | deployment: 15 | type: cloudify.kubernetes.resources.Deployment 16 | properties: 17 | client_config: 18 | configuration: 19 | api_options: 20 | host: { get_input: kubernetes_master } 21 | api_key: { get_secret: kubernetes_token } 22 | debug: false 23 | verify_ssl: false 24 | definition: 25 | apiVersion: apps/v1 26 | kind: Deployment 27 | metadata: 28 | name: nginx-deployment 29 | labels: 30 | app: nginx 31 | spec: 32 | replicas: 3 33 | selector: 34 | matchLabels: 35 | app: nginx 36 | template: 37 | metadata: 38 | labels: 39 | app: nginx 40 | spec: 41 | containers: 42 | - name: nginx 43 | image: nginx:1.7.9 44 | ports: 45 | - containerPort: 80 46 | -------------------------------------------------------------------------------- /examples/test-stateful-set.yaml: -------------------------------------------------------------------------------- 1 | tosca_definitions_version: cloudify_dsl_1_3 2 | 3 | imports: 4 | - http://www.getcloudify.org/spec/cloudify/5.0.5/types.yaml 5 | - plugin:cloudify-kubernetes-plugin 6 | 7 | inputs: 8 | 9 | kubernetes_master: 10 | type: string 11 | 12 | node_templates: 13 | 14 | stateful_set: 15 | type: cloudify.kubernetes.resources.StatefulSet 16 | properties: 17 | client_config: 18 | authentication: 19 | gcp_service_account: { get_secret: gcp_credentials } 20 | configuration: 21 | api_options: 22 | host: { get_input: kubernetes_master } 23 | verify_ssl: false 24 | debug: false 25 | definition: 26 | apiVersion: apps/v1 # for k8s versions before 1.9.0 use apps/v1beta2 and before 1.8.0 use extensions/v1beta1 27 | kind: StatefulSet 28 | metadata: 29 | name: web 30 | labels: 31 | app: nginx 32 | spec: 33 | serviceName: "nginx" 34 | selector: 35 | matchLabels: 36 | app: nginx 37 | replicas: 14 38 | template: 39 | metadata: 40 | labels: 41 | app: nginx 42 | spec: 43 | containers: 44 | - name: nginx 45 | image: k8s.gcr.io/nginx-slim:0.8 46 | -------------------------------------------------------------------------------- /extra-packaging-instructions.sh: -------------------------------------------------------------------------------- 1 | if [[ $PY311 == 1 ]] 2 | then 3 | mkdir -p ./pydoc 4 | touch ./pydoc/__init__.py 5 | cat < ./pydoc/__init__.py 6 | def get_doc(*args): 7 | return '' 8 | EOF 9 | mkdir -p ./webbrowser 10 | touch ./webbrowser/__init__.py 11 | cat < ./webbrowser/__init__.py 12 | def register(*args, **kwargs): 13 | return '' 14 | EOF 15 | fi 16 | -------------------------------------------------------------------------------- /ignore_plugin_yaml_differences: -------------------------------------------------------------------------------- 1 | {'dictionary_item_added': [root['workflows']['update_resource_definition']['parameters']['resource_definition_changes']['type']], 'dictionary_item_removed': [root['workflows']['update_resource_definition']['parameters']['resource_definition_changes']['default']]} 2 | -------------------------------------------------------------------------------- /requirements-3.6.in: -------------------------------------------------------------------------------- 1 | cloudify-common==6.4.2 2 | git+https://github.com/cloudify-cosmo/cloudify-manager.git@6.4.2-build#egg=cloudify-types&subdirectory=cloudify_types 3 | cryptography==40.0.2 4 | -------------------------------------------------------------------------------- /requirements-3.6.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile with python 3.6 3 | # To update, run: 4 | # 5 | # pip-compile --no-emit-index-url --output-file=requirements-3.6.txt requirements-3.6.in setup.py 6 | # 7 | adal==1.2.7 8 | # via msrestazure 9 | aiohttp==3.7.4.post0 10 | # via cloudify-common 11 | async-timeout==3.0.1 12 | # via aiohttp 13 | attrs==22.2.0 14 | # via aiohttp 15 | azure-common==1.1.28 16 | # via azure-mgmt-containerservice 17 | azure-core==1.24.2 18 | # via 19 | # azure-identity 20 | # azure-mgmt-core 21 | # msrest 22 | azure-identity==1.10.0 23 | # via cloudify-utilities-plugins-sdk 24 | azure-mgmt-containerservice==17.0.0 25 | # via cloudify-utilities-plugins-sdk 26 | azure-mgmt-core==1.3.2 27 | # via azure-mgmt-containerservice 28 | bcrypt==4.0.1 29 | # via paramiko 30 | boto3==1.23.10 31 | # via cloudify-utilities-plugins-sdk 32 | botocore==1.26.10 33 | # via 34 | # boto3 35 | # s3transfer 36 | bottle==0.12.19 37 | # via cloudify-common 38 | cachetools==4.2.4 39 | # via google-auth 40 | certifi==2023.11.17 41 | # via 42 | # kubernetes 43 | # msrest 44 | # requests 45 | cffi==1.15.1 46 | # via 47 | # cryptography 48 | # pynacl 49 | chardet==4.0.0 50 | # via aiohttp 51 | charset-normalizer==2.0.12 52 | # via requests 53 | cloudify-common==6.4.2.0 54 | # via 55 | # -r requirements-3.6.in 56 | # cloudify-kubernetes-plugin (setup.py) 57 | # cloudify-types 58 | # cloudify-utilities-plugins-sdk 59 | cloudify-python-importer==0.2.1 60 | # via cloudify-kubernetes-plugin (setup.py) 61 | cloudify-types @ git+https://github.com/cloudify-cosmo/cloudify-manager.git@6.4.2-build#subdirectory=cloudify_types 62 | # via 63 | # -r requirements-3.6.in 64 | # cloudify-kubernetes-plugin (setup.py) 65 | cloudify-utilities-plugins-sdk==0.0.130 66 | # via cloudify-kubernetes-plugin (setup.py) 67 | cryptography==40.0.2 68 | # via 69 | # -r requirements-3.6.in 70 | # adal 71 | # azure-identity 72 | # msal 73 | # paramiko 74 | # pyjwt 75 | deepdiff==3.3.0 76 | # via cloudify-kubernetes-plugin (setup.py) 77 | fasteners==0.17.3 78 | # via cloudify-common 79 | gitdb==4.0.8 80 | # via 81 | # cloudify-utilities-plugins-sdk 82 | # gitpython 83 | gitpython==3.1.18 84 | # via cloudify-utilities-plugins-sdk 85 | google-auth==2.15.0 86 | # via 87 | # cloudify-utilities-plugins-sdk 88 | # kubernetes 89 | idna==3.6 90 | # via 91 | # idna-ssl 92 | # requests 93 | # yarl 94 | idna-ssl==1.1.0 95 | # via aiohttp 96 | importlib-metadata==4.8.3 97 | # via jsonpickle 98 | isodate==0.6.1 99 | # via msrest 100 | jinja2==2.11.3 101 | # via 102 | # cloudify-common 103 | # cloudify-utilities-plugins-sdk 104 | jmespath==0.10.0 105 | # via 106 | # boto3 107 | # botocore 108 | jsonpickle==2.2.0 109 | # via deepdiff 110 | kubernetes==v26.1.0 111 | # via cloudify-utilities-plugins-sdk 112 | markupsafe==2.0.1 113 | # via jinja2 114 | msal==1.25.0 115 | # via 116 | # azure-identity 117 | # msal-extensions 118 | msal-extensions==1.0.0 119 | # via azure-identity 120 | msrest==0.7.1 121 | # via 122 | # azure-mgmt-containerservice 123 | # msrestazure 124 | msrestazure==0.6.4 125 | # via cloudify-utilities-plugins-sdk 126 | multidict==5.2.0 127 | # via 128 | # aiohttp 129 | # yarl 130 | oauthlib==3.2.2 131 | # via requests-oauthlib 132 | packaging==21.3 133 | # via cloudify-utilities-plugins-sdk 134 | paramiko==3.3.1 135 | # via cloudify-utilities-plugins-sdk 136 | pika==1.1.0 137 | # via cloudify-common 138 | pkginfo==1.9.6 139 | # via wagon 140 | portalocker==2.7.0 141 | # via msal-extensions 142 | proxy_tools==0.1.0 143 | # via cloudify-common 144 | psutil==5.9.6 145 | # via cloudify-utilities-plugins-sdk 146 | pyasn1==0.5.1 147 | # via 148 | # pyasn1-modules 149 | # rsa 150 | pyasn1-modules==0.3.0 151 | # via google-auth 152 | pycdlib==1.14.0 153 | # via cloudify-utilities-plugins-sdk 154 | pycparser==2.21 155 | # via cffi 156 | pyjwt[crypto]==2.4.0 157 | # via 158 | # adal 159 | # msal 160 | pynacl==1.5.0 161 | # via paramiko 162 | pyparsing==3.0.7 163 | # via packaging 164 | python-dateutil==2.8.2 165 | # via 166 | # adal 167 | # botocore 168 | # kubernetes 169 | pytz==2021.3 170 | # via cloudify-common 171 | pyyaml==6.0 172 | # via 173 | # cloudify-types 174 | # cloudify-utilities-plugins-sdk 175 | # kubernetes 176 | requests==2.27.1 177 | # via 178 | # adal 179 | # azure-core 180 | # cloudify-common 181 | # cloudify-types 182 | # cloudify-utilities-plugins-sdk 183 | # kubernetes 184 | # msal 185 | # msrest 186 | # requests-oauthlib 187 | # requests-toolbelt 188 | requests-oauthlib==1.3.1 189 | # via 190 | # kubernetes 191 | # msrest 192 | requests_toolbelt==0.9.1 193 | # via cloudify-common 194 | retrying==1.3.3 195 | # via cloudify-common 196 | rsa==4.9 197 | # via google-auth 198 | s3transfer==0.5.2 199 | # via boto3 200 | six==1.16.0 201 | # via 202 | # azure-core 203 | # azure-identity 204 | # google-auth 205 | # isodate 206 | # kubernetes 207 | # msrestazure 208 | # python-dateutil 209 | # retrying 210 | smmap==5.0.0 211 | # via gitdb 212 | typing-extensions==4.1.1 213 | # via 214 | # aiohttp 215 | # azure-core 216 | # gitpython 217 | # importlib-metadata 218 | # yarl 219 | urllib3==1.26.18 220 | # via 221 | # botocore 222 | # kubernetes 223 | # requests 224 | wagon==1.0.1 225 | # via cloudify-common 226 | websocket-client==1.3.1 227 | # via kubernetes 228 | wheel==0.37.1 229 | # via wagon 230 | xmltodict==0.13.0 231 | # via cloudify-utilities-plugins-sdk 232 | yarl==1.7.2 233 | # via aiohttp 234 | zipp==3.6.0 235 | # via importlib-metadata 236 | 237 | # The following packages are considered to be unsafe in a requirements file: 238 | # setuptools 239 | -------------------------------------------------------------------------------- /requirements.in: -------------------------------------------------------------------------------- 1 | -e ${HOME}/fusion-common 2 | -e ${HOME}/fusion-manager/mgmtworker 3 | -e ${HOME}/fusion-agent 4 | -e ${HOME}/cloudify-utilities-plugins-sdk 5 | cryptography>=42.0.5 6 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile with Python 3.11 3 | # by the following command: 4 | # 5 | # pip-compile --no-emit-index-url --output-file=requirements.txt requirements.in setup.py 6 | # 7 | # -e file:///home/circleci/cloudify-utilities-plugins-sdk 8 | # # via 9 | # # -r requirements.in 10 | # # cloudify-kubernetes-plugin (setup.py) 11 | # -e file:///home/circleci/fusion-agent 12 | # # via 13 | # # -r requirements.in 14 | # # fusion-mgmtworker 15 | # -e file:///home/circleci/fusion-common 16 | # # via 17 | # # -r requirements.in 18 | # # cloudify-kubernetes-plugin (setup.py) 19 | # # fusion-agent 20 | # # fusion-mgmtworker 21 | # -e file:///home/circleci/fusion-manager/mgmtworker 22 | # # via 23 | # # -r requirements.in 24 | # # cloudify-kubernetes-plugin (setup.py) 25 | adal==1.2.7 26 | # via msrestazure 27 | aiohttp==3.9.3 28 | # via 29 | # cloudify-common 30 | # fusion-common 31 | aiosignal==1.3.1 32 | # via aiohttp 33 | amqp==5.2.0 34 | # via kombu 35 | appdirs==1.4.4 36 | # via fusion-agent 37 | attrs==23.2.0 38 | # via aiohttp 39 | azure-common==1.1.28 40 | # via azure-mgmt-containerservice 41 | azure-core==1.30.1 42 | # via 43 | # azure-identity 44 | # azure-mgmt-core 45 | # msrest 46 | azure-identity==1.15.0 47 | # via cloudify-utilities-plugins-sdk 48 | azure-mgmt-containerservice==17.0.0 49 | # via cloudify-utilities-plugins-sdk 50 | azure-mgmt-core==1.4.0 51 | # via azure-mgmt-containerservice 52 | bcrypt==4.1.2 53 | # via paramiko 54 | billiard==4.2.0 55 | # via celery 56 | boto3==1.34.67 57 | # via cloudify-utilities-plugins-sdk 58 | botocore==1.34.67 59 | # via 60 | # boto3 61 | # s3transfer 62 | bottle==0.12.25 63 | # via 64 | # cloudify-common 65 | # fusion-common 66 | cachetools==5.3.3 67 | # via google-auth 68 | celery==5.3.6 69 | # via fusion-agent 70 | certifi==2024.2.2 71 | # via 72 | # kubernetes 73 | # msrest 74 | # requests 75 | cffi==1.16.0 76 | # via 77 | # cryptography 78 | # fusion-agent 79 | # pynacl 80 | # python-gssapi 81 | charset-normalizer==3.3.2 82 | # via requests 83 | click==8.1.7 84 | # via 85 | # celery 86 | # click-didyoumean 87 | # click-plugins 88 | # click-repl 89 | # fusion-agent 90 | click-didyoumean==0.3.0 91 | # via celery 92 | click-plugins==1.1.1 93 | # via celery 94 | click-repl==0.3.0 95 | # via celery 96 | cloudify-common==7.0.3 97 | # via cloudify-utilities-plugins-sdk 98 | cloudify-python-importer==0.2.1 99 | # via cloudify-kubernetes-plugin (setup.py) 100 | cryptography==42.0.5 101 | # via 102 | # -r requirements.in 103 | # adal 104 | # azure-identity 105 | # fusion-mgmtworker 106 | # msal 107 | # paramiko 108 | # pyjwt 109 | # pyspnego 110 | # requests-ntlm 111 | deepdiff==5.7.0 112 | # via cloudify-kubernetes-plugin (setup.py) 113 | distro==1.9.0 114 | # via 115 | # cloudify-common 116 | # fusion-common 117 | fabric==2.7.1 118 | # via fusion-agent 119 | fasteners==0.19 120 | # via 121 | # cloudify-common 122 | # fusion-common 123 | frozenlist==1.4.1 124 | # via 125 | # aiohttp 126 | # aiosignal 127 | gitdb==4.0.11 128 | # via 129 | # cloudify-utilities-plugins-sdk 130 | # gitpython 131 | gitpython==3.1.42 132 | # via cloudify-utilities-plugins-sdk 133 | google-auth==2.15.0 134 | # via 135 | # cloudify-utilities-plugins-sdk 136 | # kubernetes 137 | idna==3.6 138 | # via 139 | # requests 140 | # yarl 141 | invoke==1.7.3 142 | # via fabric 143 | isodate==0.6.1 144 | # via msrest 145 | jinja2==3.1.3 146 | # via 147 | # cloudify-common 148 | # cloudify-utilities-plugins-sdk 149 | # fusion-agent 150 | # fusion-common 151 | jmespath==1.0.1 152 | # via 153 | # boto3 154 | # botocore 155 | kombu==5.3.5 156 | # via celery 157 | kubernetes==26.1.0 158 | # via cloudify-utilities-plugins-sdk 159 | markupsafe==2.1.5 160 | # via jinja2 161 | msal==1.28.0 162 | # via 163 | # azure-identity 164 | # msal-extensions 165 | msal-extensions==1.1.0 166 | # via azure-identity 167 | msrest==0.7.1 168 | # via 169 | # azure-mgmt-containerservice 170 | # msrestazure 171 | msrestazure==0.6.4 172 | # via cloudify-utilities-plugins-sdk 173 | multidict==6.0.5 174 | # via 175 | # aiohttp 176 | # yarl 177 | nats-py==2.7.2 178 | # via fusion-common 179 | networkx==3.2.1 180 | # via fusion-common 181 | oauthlib==3.2.2 182 | # via requests-oauthlib 183 | ordered-set==4.0.2 184 | # via deepdiff 185 | packaging==21.3 186 | # via 187 | # cloudify-utilities-plugins-sdk 188 | # fusion-agent 189 | # fusion-mgmtworker 190 | # msal-extensions 191 | paramiko==3.4.0 192 | # via 193 | # cloudify-utilities-plugins-sdk 194 | # fabric 195 | pathlib2==2.3.7.post1 196 | # via fabric 197 | pika==1.3.2 198 | # via 199 | # cloudify-common 200 | # fusion-common 201 | pkginfo==1.10.0 202 | # via wagon 203 | ply==3.11 204 | # via pysmi 205 | portalocker==2.8.2 206 | # via msal-extensions 207 | prompt-toolkit==3.0.43 208 | # via click-repl 209 | proxy-tools==0.1.0 210 | # via 211 | # cloudify-common 212 | # fusion-common 213 | psutil==5.9.8 214 | # via cloudify-utilities-plugins-sdk 215 | psycopg2==2.9.9 216 | # via fusion-mgmtworker 217 | pyasn1==0.5.1 218 | # via 219 | # pyasn1-modules 220 | # pysnmp 221 | # python-gssapi 222 | # rsa 223 | pyasn1-modules==0.3.0 224 | # via google-auth 225 | pycdlib==1.14.0 226 | # via cloudify-utilities-plugins-sdk 227 | pycparser==2.21 228 | # via cffi 229 | pycryptodomex==3.20.0 230 | # via pysnmp 231 | pyjwt[crypto]==2.8.0 232 | # via 233 | # adal 234 | # msal 235 | pykerberos==1.2.4 236 | # via pywinrm 237 | pynacl==1.5.0 238 | # via 239 | # fusion-agent 240 | # paramiko 241 | pyparsing==3.1.2 242 | # via packaging 243 | pysmi==0.3.4 244 | # via pysnmp 245 | pysnmp==4.4.12 246 | # via fusion-common 247 | pyspnego==0.10.2 248 | # via requests-ntlm 249 | python-dateutil==2.9.0.post0 250 | # via 251 | # adal 252 | # botocore 253 | # celery 254 | # fusion-mgmtworker 255 | # kubernetes 256 | python-gssapi==0.6.4 257 | # via fusion-agent 258 | pytz==2024.1 259 | # via 260 | # cloudify-common 261 | # fusion-common 262 | # fusion-mgmtworker 263 | pywinrm[kerberos]==0.4.3 264 | # via fusion-agent 265 | pyyaml==6.0.1 266 | # via 267 | # cloudify-utilities-plugins-sdk 268 | # fusion-common 269 | # kubernetes 270 | requests==2.31.0 271 | # via 272 | # adal 273 | # azure-core 274 | # cloudify-common 275 | # cloudify-utilities-plugins-sdk 276 | # fusion-agent 277 | # fusion-common 278 | # kubernetes 279 | # msal 280 | # msrest 281 | # pywinrm 282 | # requests-ntlm 283 | # requests-oauthlib 284 | # requests-toolbelt 285 | requests-ntlm==1.2.0 286 | # via pywinrm 287 | requests-oauthlib==1.4.0 288 | # via 289 | # kubernetes 290 | # msrest 291 | requests-toolbelt==1.0.0 292 | # via 293 | # cloudify-common 294 | # fusion-common 295 | retrying==1.3.4 296 | # via fusion-mgmtworker 297 | rsa==4.9 298 | # via google-auth 299 | s3transfer==0.10.1 300 | # via boto3 301 | six==1.16.0 302 | # via 303 | # azure-core 304 | # google-auth 305 | # isodate 306 | # kubernetes 307 | # msrestazure 308 | # pathlib2 309 | # python-dateutil 310 | # python-gssapi 311 | # pywinrm 312 | # retrying 313 | smmap==5.0.1 314 | # via gitdb 315 | typing-extensions==4.10.0 316 | # via azure-core 317 | tzdata==2024.1 318 | # via celery 319 | urllib3==2.2.1 320 | # via 321 | # botocore 322 | # fusion-common 323 | # kubernetes 324 | # requests 325 | vine==5.1.0 326 | # via 327 | # amqp 328 | # celery 329 | # kombu 330 | wagon==1.0.1 331 | # via 332 | # cloudify-common 333 | # fusion-common 334 | wcwidth==0.2.13 335 | # via prompt-toolkit 336 | websocket-client==1.7.0 337 | # via kubernetes 338 | wheel==0.43.0 339 | # via wagon 340 | xmltodict==0.13.0 341 | # via 342 | # cloudify-utilities-plugins-sdk 343 | # pywinrm 344 | yarl==1.9.4 345 | # via aiohttp 346 | 347 | # The following packages are considered to be unsafe in a requirements file: 348 | # setuptools 349 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017-2019 Cloudify Platform Ltd. All rights reserved 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | import re 17 | import sys 18 | import pathlib 19 | from setuptools import setup, find_packages 20 | 21 | PY2 = sys.version_info[0] == 2 22 | 23 | 24 | def get_version(): 25 | current_dir = pathlib.Path(__file__).parent.resolve() 26 | with open(os.path.join(current_dir,'cloudify_kubernetes/__version__.py'), 27 | 'r') as outfile: 28 | var = outfile.read() 29 | return re.search(r'\d+.\d+.\d+', var).group() 30 | 31 | 32 | install_requires = [ 33 | 'cloudify-python-importer==0.2.1', 34 | ] 35 | 36 | if sys.version_info.major == 3 and sys.version_info.minor == 6: 37 | packages=[ 38 | 'cloudify_kubernetes', 39 | 'cloudify_kubernetes.k8s', 40 | 'cloudify_kubernetes.tasks', 41 | 'cloudify_kubernetes.tasks.nested_resources' 42 | ] 43 | install_requires += [ 44 | 'cloudify-common>=4.5,<7.0', 45 | 'cloudify_types @ git+https://github.com/cloudify-cosmo/' \ 46 | 'cloudify-manager.git@6.4.2-build#egg=cloudify-types' \ 47 | '&subdirectory=cloudify_types', 48 | 'deepdiff==3.3.0', 49 | 'cloudify-utilities-plugins-sdk>=0.0.112', # Provides kubernetes and google-auth. 50 | ] 51 | else: 52 | packages=find_packages() 53 | install_requires += [ 54 | 'fusion-common', 55 | 'fusion-mgmtworker', 56 | 'deepdiff==5.7.0', 57 | 'cloudify-utilities-plugins-sdk', 58 | ] 59 | 60 | setup( 61 | name='cloudify-kubernetes-plugin', 62 | version=get_version(), 63 | author='Cloudify Platform Ltd.', 64 | author_email='hello@cloudify.co', 65 | description='Plugin provides Kubernetes management possibility', 66 | include_package_data=True, 67 | packages=packages, 68 | license='LICENSE', 69 | install_requires=install_requires 70 | ) 71 | -------------------------------------------------------------------------------- /test-requirements.txt: -------------------------------------------------------------------------------- 1 | flake8 2 | testtools 3 | mock 4 | nose-testconfig 5 | nose==1.3.7 6 | nose-cov==1.6 7 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = unittesting,linting,validate 3 | minversion = 1.6 4 | skipsdist = True 5 | 6 | [testenv] 7 | setenv = 8 | VIRTUAL_ENV={envdir} 9 | 10 | # NOTE: relative paths were used due to '-w' flag for nosetests util 11 | 12 | usedevelop = True 13 | install_command = pip install -U {opts} {packages} 14 | deps = -r{toxinidir}/requirements-3.6.txt 15 | -r{toxinidir}/test-requirements.txt 16 | -e . 17 | whitelist_externals = bash 18 | 19 | [testenv:linting] 20 | commands = 21 | flake8 cloudify_kubernetes 22 | 23 | [testenv:unittesting] 24 | commands = 25 | # nosetests --cover-html --with-coverage --cover-package=cloudify_kubernetes --with-xunit --xunit-file=nosetests.xml --cover-xml --cover-xml-file=coverage.xml --exe cloudify_kubernetes 26 | nosetests --exe cloudify_kubernetes 27 | 28 | [testenv:venv] 29 | commands = {posargs} 30 | 31 | [linting] 32 | show-source = True 33 | ignore = 34 | exclude=.venv,.tox,dist,*egg,etc,build,bin,lib,local,share 35 | filename=*.py 36 | 37 | --------------------------------------------------------------------------------