| # |
| # Licensed to the Apache Software Foundation (ASF) under one or more |
| # contributor license agreements. See the NOTICE file distributed with |
| # this work for additional information regarding copyright ownership. |
| # The ASF licenses this file to You under the Apache License, Version 2.0 |
| # (the "License"); you may not use this file except in compliance with |
| # the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| --- |
| # This role will install Controller in group 'controllers' in the environment |
| # inventory |
| |
| - import_tasks: docker_login.yml |
| |
| - name: get controller name and index |
| set_fact: |
| controller_name: "{{ name_prefix ~ host_group.index(inventory_hostname) }}" |
| controller_index: |
| "{{ (controller_index_base|int) + host_group.index(inventory_hostname) }}" |
| |
| - name: "pull the {{ docker.image.tag }} image of controller" |
| shell: "docker pull {{docker_registry}}{{ docker.image.prefix }}/controller:{{docker.image.tag}}" |
| when: docker_registry != "" |
| register: result |
| until: (result.rc == 0) |
| retries: "{{ docker.pull.retries }}" |
| delay: "{{ docker.pull.delay }}" |
| |
| - name: ensure controller log directory is created with permissions |
| file: |
| path: "{{ whisk_logs_dir }}/{{ controller_name }}" |
| state: directory |
| mode: 0777 |
| become: "{{ logs.dir.become }}" |
| |
| # We need to create the file with proper permissions because the dir creation above |
| # does not result in a dir with full permissions in docker machine especially with macos mounts |
| - name: ensure controller log file is created with permissions |
| file: |
| path: "{{ whisk_logs_dir }}/{{ controller_name }}/{{ controller_name }}_logs.log" |
| state: touch |
| mode: 0777 |
| when: environment_type is defined and environment_type == "docker-machine" |
| |
| - name: ensure controller config directory is created with permissions |
| file: |
| path: "{{ controller.confdir }}/{{ controller_name }}" |
| state: directory |
| mode: 0777 |
| become: "{{ controller.dir.become }}" |
| |
| - name: copy jmxremote password file |
| when: jmx.enabled |
| template: |
| src: "jmxremote.password.j2" |
| dest: "{{ controller.confdir }}/{{ controller_name }}/jmxremote.password" |
| mode: 0777 |
| |
| - name: copy jmxremote access file |
| when: jmx.enabled |
| template: |
| src: "jmxremote.access.j2" |
| dest: "{{ controller.confdir }}/{{ controller_name }}/jmxremote.access" |
| mode: 0777 |
| |
| - name: "copy kafka truststore/keystore" |
| when: kafka.protocol == 'SSL' |
| copy: |
| src: |
| "{{openwhisk_home~'/ansible/roles/kafka/files/'~kafka.ssl.keystore.name}}" |
| dest: "{{ controller.confdir }}/{{ controller_name }}" |
| |
| - name: copy nginx certificate keystore |
| when: controller.protocol == 'https' |
| copy: |
| src: files/{{ controller.ssl.keystore.name }} |
| mode: 0666 |
| dest: "{{ controller.confdir }}/{{ controller_name }}" |
| become: "{{ controller.dir.become }}" |
| |
| - name: copy certificates |
| when: controller.protocol == 'https' |
| copy: |
| src: "{{ openwhisk_home }}/ansible/roles/controller/files/{{ item }}" |
| mode: 0666 |
| dest: "{{ controller.confdir }}/{{ controller_name }}" |
| with_items: |
| - "{{ controller.ssl.cert }}" |
| - "{{ controller.ssl.key }}" |
| become: "{{ controller.dir.become }}" |
| |
| - name: check, that required databases exist |
| include_tasks: "{{ openwhisk_home }}/ansible/tasks/db/checkDb.yml" |
| vars: |
| dbName: "{{ item }}" |
| dbUser: "{{ db.credentials.controller.user }}" |
| dbPass: "{{ db.credentials.controller.pass }}" |
| with_items: |
| - "{{ db.whisk.actions }}" |
| - "{{ db.whisk.auth }}" |
| - "{{ db.whisk.activations }}" |
| |
| - name: prepare controller port |
| set_fact: |
| controller_port: "{{ controller.basePort + (controller_index | int) }}" |
| ports_to_expose: |
| - "{{ controller.basePort + (controller_index | int) }}:8080" |
| |
| - name: expose additional ports if jmxremote is enabled |
| when: jmx.enabled |
| vars: |
| jmx_remote_port: "{{ jmx.basePortController + (controller_index|int) }}" |
| jmx_remote_rmi_port: |
| "{{ jmx.rmiBasePortController + (controller_index|int) }}" |
| set_fact: |
| ports_to_expose: >- |
| {{ ports_to_expose }} + |
| [ '{{ jmx_remote_port }}:{{ jmx_remote_port }}' ] + |
| [ '{{ jmx_remote_rmi_port }}:{{ jmx_remote_rmi_port }}' ] |
| controller_args: >- |
| {{ controller.arguments }} |
| {{ jmx.jvmCommonArgs }} |
| -Djava.rmi.server.hostname={{ inventory_hostname }} |
| -Dcom.sun.management.jmxremote.rmi.port={{ jmx_remote_rmi_port }} |
| -Dcom.sun.management.jmxremote.port={{ jmx_remote_port }} |
| |
| - name: Load config from template |
| set_fact: |
| openwhisk_config: "{{ lookup('template', 'config.j2') | b64encode }}" |
| |
| - name: populate environment variables for controller |
| set_fact: |
| env: |
| "JAVA_OPTS": |
| -Xmx{{ controller.heap }} |
| -XX:+CrashOnOutOfMemoryError |
| -XX:+UseGCOverheadLimit |
| -XX:ErrorFile=/logs/java_error.log |
| -XX:+HeapDumpOnOutOfMemoryError |
| -XX:HeapDumpPath=/logs |
| "CONTROLLER_OPTS": "{{ controller_args | default(controller.arguments) }}" |
| "JMX_REMOTE": "{{ jmx.enabled }}" |
| "OPENWHISK_ENCODED_CONFIG": "{{ openwhisk_config }}" |
| "PORT": "8080" |
| "TZ": "{{ docker.timezone }}" |
| |
| "CONFIG_whisk_info_date": "{{ whisk.version.date }}" |
| "CONFIG_whisk_info_buildNo": "{{ docker.image.tag }}" |
| |
| "KAFKA_HOSTS": "{{ kafka_connect_string }}" |
| "CONFIG_whisk_kafka_replicationFactor": |
| "{{ kafka.replicationFactor | default() }}" |
| "CONFIG_whisk_kafka_topics_cacheInvalidation_retentionBytes": |
| "{{ kafka_topics_cacheInvalidation_retentionBytes | default() }}" |
| "CONFIG_whisk_kafka_topics_cacheInvalidation_retentionMs": |
| "{{ kafka_topics_cacheInvalidation_retentionMS | default() }}" |
| "CONFIG_whisk_kafka_topics_cacheInvalidation_segmentBytes": |
| "{{ kafka_topics_cacheInvalidation_segmentBytes | default() }}" |
| "CONFIG_whisk_kafka_topics_completed_retentionBytes": |
| "{{ kafka_topics_completed_retentionBytes | default() }}" |
| "CONFIG_whisk_kafka_topics_completed_retentionMs": |
| "{{ kafka_topics_completed_retentionMS | default() }}" |
| "CONFIG_whisk_kafka_topics_completed_segmentBytes": |
| "{{ kafka_topics_completed_segmentBytes | default() }}" |
| "CONFIG_whisk_kafka_topics_health_retentionBytes": |
| "{{ kafka_topics_health_retentionBytes | default() }}" |
| "CONFIG_whisk_kafka_topics_health_retentionMs": |
| "{{ kafka_topics_health_retentionMS | default() }}" |
| "CONFIG_whisk_kafka_topics_health_segmentBytes": |
| "{{ kafka_topics_health_segmentBytes | default() }}" |
| "CONFIG_whisk_kafka_topics_prefix": |
| "{{ kafka.topicsPrefix }}" |
| "CONFIG_whisk_kafka_topics_userEvent_prefix": |
| "{{ kafka.topicsUserEventPrefix }}" |
| "CONFIG_whisk_kafka_common_securityProtocol": |
| "{{ kafka.protocol }}" |
| "CONFIG_whisk_kafka_common_sslTruststoreLocation": |
| "/conf/{{ kafka.ssl.keystore.name }}" |
| "CONFIG_whisk_kafka_common_sslTruststorePassword": |
| "{{ kafka.ssl.keystore.password }}" |
| "CONFIG_whisk_kafka_common_sslKeystoreLocation": |
| "/conf/{{ kafka.ssl.keystore.name }}" |
| "CONFIG_whisk_kafka_common_sslKeystorePassword": |
| "{{ kafka.ssl.keystore.password }}" |
| |
| "CONFIG_whisk_couchdb_protocol": "{{ db.protocol }}" |
| "CONFIG_whisk_couchdb_host": "{{ db.host }}" |
| "CONFIG_whisk_couchdb_port": "{{ db.port }}" |
| "CONFIG_whisk_couchdb_username": "{{ db.credentials.controller.user }}" |
| "CONFIG_whisk_couchdb_password": "{{ db.credentials.controller.pass }}" |
| "CONFIG_whisk_couchdb_provider": "{{ db.provider }}" |
| "CONFIG_whisk_couchdb_databases_WhiskAuth": "{{ db.whisk.auth }}" |
| "CONFIG_whisk_couchdb_databases_WhiskEntity": "{{ db.whisk.actions }}" |
| "CONFIG_whisk_couchdb_databases_WhiskActivation": |
| "{{ db.whisk.activations }}" |
| "CONFIG_whisk_db_subjectsDdoc": "{{ db_whisk_subjects_ddoc | default() }}" |
| "CONFIG_whisk_db_actionsDdoc": "{{ db_whisk_actions_ddoc | default() }}" |
| "CONFIG_whisk_db_activationsDdoc": "{{ db_whisk_activations_ddoc | default() }}" |
| "CONFIG_whisk_db_activationsFilterDdoc": "{{ db_whisk_activations_filter_ddoc | default() }}" |
| "CONFIG_whisk_userEvents_enabled": "{{ user_events | default(false) | lower }}" |
| |
| "LIMITS_ACTIONS_INVOKES_PERMINUTE": "{{ limits.invocationsPerMinute }}" |
| "LIMITS_ACTIONS_INVOKES_CONCURRENT": "{{ limits.concurrentInvocations }}" |
| "LIMITS_TRIGGERS_FIRES_PERMINUTE": "{{ limits.firesPerMinute }}" |
| "LIMITS_ACTIONS_SEQUENCE_MAXLENGTH": "{{ limits.sequenceMaxLength }}" |
| |
| "CONFIG_whisk_memory_min": "{{ limit_action_memory_min | default() }}" |
| "CONFIG_whisk_memory_max": "{{ limit_action_memory_max | default() }}" |
| "CONFIG_whisk_memory_std": "{{ limit_action_memory_std | default() }}" |
| |
| "CONFIG_whisk_timeLimit_min": "{{ limit_action_time_min | default() }}" |
| "CONFIG_whisk_timeLimit_max": "{{ limit_action_time_max | default() }}" |
| "CONFIG_whisk_timeLimit_std": "{{ limit_action_time_std | default() }}" |
| |
| "CONFIG_whisk_concurrencyLimit_min": "{{ limit_action_concurrency_min | default() }}" |
| "CONFIG_whisk_concurrencyLimit_max": "{{ limit_action_concurrency_max | default() }}" |
| "CONFIG_whisk_concurrencyLimit_std": "{{ limit_action_concurrency_std | default() }}" |
| |
| "CONFIG_whisk_featureFlags_requireApiKeyAnnotation": "{{ whisk.feature_flags.require_api_key_annotation | default(true) | lower }}" |
| "CONFIG_whisk_featureFlags_requireResponsePayload": "{{ whisk.feature_flags.require_response_payload | default(true) | lower }}" |
| |
| "CONFIG_whisk_activation_payload_max": |
| "{{ limit_activation_payload | default() }}" |
| |
| "RUNTIMES_MANIFEST": "{{ runtimesManifest | to_json }}" |
| "CONFIG_whisk_runtimes_defaultImagePrefix": |
| "{{ runtimes_default_image_prefix | default() }}" |
| "CONFIG_whisk_runtimes_defaultImageTag": |
| "{{ runtimes_default_image_tag | default() }}" |
| "CONFIG_whisk_runtimes_bypassPullForLocalImages": |
| "{{ runtimes_bypass_pull_for_local_images | default() | lower }}" |
| "CONFIG_whisk_runtimes_localImagePrefix": |
| "{{ runtimes_local_image_prefix | default() }}" |
| |
| "METRICS_KAMON": "{{ metrics.kamon.enabled | default(false) | lower }}" |
| "METRICS_KAMON_TAGS": "{{ metrics.kamon.tags | default() | lower }}" |
| "METRICS_LOG": "{{ metrics.log.enabled | default(false) | lower }}" |
| "CONFIG_whisk_controller_protocol": "{{ controller.protocol }}" |
| "CONFIG_whisk_controller_https_keystorePath": |
| "/conf/{{ controller.ssl.keystore.name }}" |
| "CONFIG_whisk_controller_https_keystorePassword": |
| "{{ controller.ssl.keystore.password }}" |
| "CONFIG_whisk_controller_https_keystoreFlavor": |
| "{{ controller.ssl.storeFlavor }}" |
| "CONFIG_whisk_controller_https_clientAuth": |
| "{{ controller.ssl.clientAuth }}" |
| "CONFIG_whisk_loadbalancer_managedFraction": |
| "{{ controller.managedFraction }}" |
| "CONFIG_whisk_loadbalancer_blackboxFraction": |
| "{{ controller.blackboxFraction }}" |
| "CONFIG_whisk_loadbalancer_timeoutFactor": |
| "{{ controller.timeoutFactor }}" |
| "CONFIG_whisk_loadbalancer_timeoutAddon": |
| "{{ controller.timeoutAddon }}" |
| |
| "CONFIG_kamon_statsd_hostname": "{{ metrics.kamon.host }}" |
| "CONFIG_kamon_statsd_port": "{{ metrics.kamon.port }}" |
| |
| "CONFIG_whisk_spi_LogStoreProvider": "{{ userLogs.spi }}" |
| "CONFIG_whisk_spi_LoadBalancerProvider": |
| "{{ controller.loadbalancer.spi }}" |
| "CONFIG_whisk_spi_EntitlementSpiProvider": "{{ controller.entitlement.spi }}" |
| |
| "CONFIG_whisk_spi_AuthenticationDirectiveProvider": "{{ controller.authentication.spi }}" |
| "CONFIG_logback_log_level": "{{ controller.loglevel }}" |
| |
| "CONFIG_whisk_transactions_header": "{{ transactions.header }}" |
| |
| "CONFIG_whisk_controller_activation_pollingFromDb": "{{ controller_activation_pollingFromDb | default(true) | lower }}" |
| |
| - name: merge extra env variables |
| set_fact: |
| env: "{{ env | combine(controller.extraEnv) }}" |
| |
| - name: setup elasticsearch activation store env |
| set_fact: |
| elastic_env: |
| "CONFIG_whisk_activationStore_elasticsearch_protocol": "{{ db.elasticsearch.protocol}}" |
| "CONFIG_whisk_activationStore_elasticsearch_hosts": "{{ elasticsearch_connect_string }}" |
| "CONFIG_whisk_activationStore_elasticsearch_indexPattern": "{{ db.elasticsearch.index_pattern }}" |
| "CONFIG_whisk_activationStore_elasticsearch_username": "{{ db.elasticsearch.auth.admin.username }}" |
| "CONFIG_whisk_activationStore_elasticsearch_password": "{{ db.elasticsearch.auth.admin.password }}" |
| "CONFIG_whisk_spi_ActivationStoreProvider": "org.apache.openwhisk.core.database.elasticsearch.ElasticSearchActivationStoreProvider" |
| when: db.activation_store.backend == "ElasticSearch" |
| |
| - name: merge elasticsearch activation store env |
| set_fact: |
| env: "{{ env | combine(elastic_env) }}" |
| when: db.activation_store.backend == "ElasticSearch" |
| |
| - name: populate volumes for controller |
| set_fact: |
| controller_volumes: |
| - "{{ whisk_logs_dir }}/{{ controller_name }}:/logs" |
| - "{{ controller.confdir }}/{{ controller_name }}:/conf" |
| |
| - name: check if coverage collection is enabled |
| set_fact: |
| coverage_enabled: false |
| when: coverage_enabled is undefined |
| |
| - name: ensure controller coverage directory is created with permissions |
| file: |
| path: "{{ coverage_logs_dir }}/controller/{{ item }}" |
| state: directory |
| mode: 0777 |
| with_items: |
| - controller |
| - common |
| become: "{{ logs.dir.become }}" |
| when: coverage_enabled |
| |
| - name: extend controller volume for coverage |
| set_fact: |
| controller_volumes: "{{ controller_volumes|default({}) + [coverage_logs_dir+'/controller:/coverage'] }}" |
| when: coverage_enabled |
| |
| - name: include plugins |
| include_tasks: "{{ item }}.yml" |
| with_items: "{{ controller_plugins | default([]) }}" |
| when: not lean |
| |
| - name: lean controller setup |
| include_tasks: "lean.yml" |
| when: lean |
| |
| - name: (re)start controller |
| docker_container: |
| name: "{{ controller_name }}" |
| image: |
| "{{docker_registry~docker.image.prefix}}/controller:{{ 'cov' if (coverage_enabled) else docker.image.tag }}" |
| state: started |
| recreate: true |
| restart_policy: "{{ docker.restart.policy }}" |
| hostname: "{{ controller_name }}" |
| env: "{{ env }}" |
| volumes: "{{ controller_volumes }}" |
| ports: "{{ ports_to_expose }}" |
| # userns_mode, pid_mode and privileged required when controller running in lean mode |
| userns_mode: "{{ userns_mode | default('') }}" |
| pid_mode: "{{ pid_mode | default('') }}" |
| privileged: "{{ privileged | default('no') }}" |
| command: |
| /bin/sh -c |
| "exec /init.sh {{ controller_index }} |
| >> /logs/{{ controller_name }}_logs.log 2>&1" |
| |
| - name: wait until the Controller in this host is up and running |
| uri: |
| url: |
| "{{controller.protocol}}://{{ansible_host}}:{{controller_port}}/ping" |
| validate_certs: "no" |
| client_key: |
| "{{ controller.confdir }}/{{ controller_name }}/{{ controller.ssl.key }}" |
| client_cert: |
| "{{ controller.confdir }}/{{ controller_name }}/{{ controller.ssl.cert }}" |
| register: result |
| until: result.status == 200 |
| retries: 12 |
| delay: 10 |