<!--
#
# 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.
#
-->

# Deploying OpenWhisk on IBM Cloud Private (ICP)

## Overview

IBM Cloud Private (ICP) provides the core infrastructure needed to provision a
production-quality OpenWhisk installation.  This document outlines
ICP-specific steps needed to provision that installation, and calls out
shortcuts that could be taken for development-grade installation.

## Initial setup

### Creating the Kubernetes Cluster

Follow IBM Cloud Private instructions to provision your cluster.  Include
GlusterFS provisioning, add
[dynamic NFS provisioning](./k8s-nfs-dynamic-storage.md),
or be prepared to provision volumes manually for OpenWhisk
(see [here](./configurationChoices.md#persistence)).

### Configuring OpenWhisk

#### Configuring Image Security

IBM Cloud Private includes a provision for filtering the images that are
allowed to be deployed into a particular namespace.  One _could_ disable this
capability for the OpenWhisk namespace, but initally it is best to define
a policy for the namespace:  (In this case we assume the namespace is
`openwhisk`)

```yaml
apiVersion: securityenforcement.admission.cloud.ibm.com/v1beta1
kind: ImagePolicy
metadata:
  name: openwhisk-image-policy
  namespace: openwhisk
spec:
  repositories:
  - name: docker.io/openwhisk/*
    policy:
      va:
        enabled: false
  - name: docker.io/apache/couchdb:*
    policy:
      va:
        enabled: false
  - name: docker.io/nginx:*
    policy:
      va:
        enabled: false
  - name: docker.io/redis:*
    policy:
      va:
        enabled: false
  - name: docker.io/zookeeper:*
    policy:
      va:
        enabled: false
  - name: docker.io/wurstmeister/kafka:*
    policy:
      va:
        enabled: false
```

#### Configuring Ingress

An IBM Cloud Private cluster has full support for TLS
and can be configured with additional annotations to
fine tune ingress performance.

A prerequisite for OpenWhisk TLS access via Ingress as currently configured
is a Fully Qualified Domain Name (FQDN) that can be resolved correctly from
within OpenWhisk and points to the SSL Ingress point, usually your load
balancer or proxy node.

You will also need to create a TLS certificate to be used by the Ingress
controller for your domain.  The YAML to create in Kubernetes is
(substituting the real values for `<your fqdn>`):

```yaml
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
  name: openwhisk-tls-secret-1
  namespace: openwhisk
spec:
  commonName: <your fqdn>
  dnsNames:
  - <your fqdn>
  issuerRef:
    kind: ClusterIssuer
    name: icp-ca-issuer
  secretName: openwhisk-tls-secret-1
```

#### Putting it all together

Now define `mycluster.yaml` as below (substituting the real values for
`<your fqdn>`).

```yaml
whisk:
  ingress:
    apiHostName: <your fqdn>
    apiHostPort: 443
    apiHostProto: https
    type: Standard
    domain: <your fqdn>
    tls:
      enabled: true
      secretenabled: true
      createsecret: false
      secretname: openwhisk-tls-secret-1
    annotations:
      # A blocking request is held open by the controller for slightly more than 60 seconds
      # before it is responded to with HTTP status code 202 (accepted) and closed.
      # Set to 75s to be on the safe side.
      # See https://console.bluemix.net/docs/containers/cs_annotations.html#proxy-connect-timeout
      # See http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_read_timeout
      nginx.ingress.kubernetes.io/proxy-read-timeout: "75s"

      # Allow up to 50 MiB body size to support creation of large actions and large
      # parameter sizes.
      # See https://console.bluemix.net/docs/containers/cs_annotations.html#client-max-body-size
      # See http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size
      nginx.ingress.kubernetes.io/client-max-body-size: "size=50m"

      # Add the request_id, generated by nginx, to the request against the controllers. This id will be used as tid there.
      # Note that the serviceName includes the argument to --name from the helm deploy command. (owdev in this example)
      # https://console.bluemix.net/docs/containers/cs_annotations.html#proxy-add-headers
      nginx.ingress.kubernetes.io/proxy-add-headers: |
        serviceName=owdev-controller {
          'X-Request-ID' $request_id;
        }

k8s:
  persistence:
    hasDefaultStorageClass: false
    explicitStorageClass: openwhisk
```

ICP does not (by default) provide a properly configured DefaultStorageClass,
instead you need to tell the Helm chart to use a storage class you've
defined (see Creating the Kubernetes Cluster
[above](#creating-the-kubernetes-cluster)).

#### Don't want to deal with Ingress (or can't create an FQDN)?

An alternative to the Ingress-based access model is to
use a NodePort. Use the IP address of any worker node in the cluster to
define `mycluster.yaml` as

```yaml
whisk:
  ingress:
    type: NodePort
    apiHostName: YOUR_WORKERS_PUBLIC_IP_ADDR
    apiHostPort: 31001

nginx:
  httpsNodePort: 31001

k8s:
  persistence:
    hasDefaultStorageClass: false
    explicitStorageClass: openwhisk
```

ICP does not (by default) provide a properly configured DefaultStorageClass,
instead you need to tell the Helm chart to use a storage class you've
defined (see Creating the Kubernetes Cluster
[above](#creating-the-kubernetes-cluster)).

## Hints and Tips

On IBM Cloud Private clusters, you can configure OpenWhisk to integrate
with platform logging and monitoring services following the general
instructions for enabling these services for pods deployed on
Kubernetes.