Ingress refresh (#687)

* rework of ingress configuration
* using networking.k8s.io/v1/Ingress makes 1.19 our minimal k83 version
diff --git a/.gitignore b/.gitignore
index 097f99e..6491baa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,4 @@
 kubernetes/nginx/certs
-helm/mycluster.yaml
-mycluster.yaml
 myruntimes.json
 .DS_Store
 *~
diff --git a/.travis.yml b/.travis.yml
index 0081600..0d0442b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -23,10 +23,9 @@
   - secure: d7CuMXbhT83W2x78qiLwgogX1+3aPicd1PlTwwNNDN6QSkImbxareyKThnsqlHIiNj3o5l5DBuiYjy7wrF/xD1g8BQMmTwm99DRx5q3CI3Im3VCi/ZK8SaNjuOy24d7cf5k2tB/87Gk7zmKsMDYm+fpCl+GpgUmIEeIwthiAxuXSDWZ8eQPIptmxj56DeFRNouvXG+dEUtBfWiwN27UPxNKExCixFnegmdtffLbz6hhst7BHr5Ry9acbycre98PCwWZcu9lxFs+SJ1kvnzX2iue4otmDkF1WkJjxaOFPJVs/D3YItg+neLCSxjwBskPed+Fct8bOjcM/uVROJPNIq5icBmaPX2isH0lvtxOeVw/dmioWYXYPN9ygBOe4eO/vtPllN0bcAUo5xl9jXev8ciAozYrYpHVh9Fplfd81rcYTeYzALmRJBdoiWoc3KQGzwGc9sB1ffmy+KWgG9T0zbnS4fALSR4PSzyNlKSLXw9vuvdNP0OBYtO+6yTJXavIxqmDoj64Lb93n+uGPatnaIGPTKEEBMJTSjsgYVEfxzzZuxUT9Ldkf2lzqvN2PCllGoMqxkgsdb8i4v4QgRaMWBDbKa5Va4k0O4dnhRmtdbJavOSKN6fECJbyfoJlV1VvJGxk5znVLRznBmUPBKbNccyPZJULugKD3QIh4q8Q5jBU=
   - secure: CJtnU94HTDqd4A6uvhFl8IpnmU+wTdlzb8bPBFUl/lI/VKXiRrYpgJdKUro5xEoxFKuqMprLhbyf66niyWLTIeogjUAEu/h/o2dBVeGgSGKoqC0hQgqvnxKFeGlzFJ0XuEs3vbStJGRnQszGsfnnDrscJtR0x9X+1w4aBKI7iPyyuFtVkDD1UsmBbSi+M8FTeq7G7A0reMDaey7uog3CFCpIMl4geshcohQEcKEGbnXQZoLPFpb7cBOE83VXBJ7Y7Dgf/U4keiLovvnuJThGKZm/SVV2KlELmBmtmbx3rMT6Vb5k9ChSdRWapromNnnzmJBIQ5Scc2mwV3A93/SMha1F3IlYpDKs5djfTw8jZfVnuiou7HhTaRjHkmmcwP12/k30gLe2kw0Vezg1TCY4zgtOpcmCxc8RHEy0ceA74rKvRi8LbexTCwX+iAMQFn/pSrh/OqAq/50JbLyczcoO1zXWS38txUQNLW8i+XllhCg9pjkjyfBeGjOOcWiVIz9rWJd2XufjSXDcj6xoZHtkh1XDt1CnVkpsYKtyyZucQnhUM9ebmaWqbSW2+bpqC/2hI+G+kOyyCesGdB1q+VmN1augMMs6RgWjk4yw5dyLAshATSoUlE8KH2cDcJL19r4ECaQ99PSLwxoB89yfPoJiNc42vwxRdsLmB1BMNyPa81Y=
   matrix:
-    - TRAVIS_KUBE_VERSION=v1.17 OW_INCLUDE_SYSTEM_TESTS=true OW_CONTAINER_FACTORY=kubernetes
-    - TRAVIS_KUBE_VERSION=v1.18 OW_INCLUDE_SYSTEM_TESTS=true OW_CONTAINER_FACTORY=kubernetes
-    - TRAVIS_KUBE_VERSION=v1.18 OW_INCLUDE_SYSTEM_TESTS=false OW_CONTAINER_FACTORY=kubernetes OW_LEAN_MODE=true
-    - TRAVIS_KUBE_VERSION=v1.19 OW_INCLUDE_SYSTEM_TESTS=false OW_CONTAINER_FACTORY=kubernetes
+    - TRAVIS_KUBE_VERSION=v1.19 OW_INCLUDE_SYSTEM_TESTS=true OW_CONTAINER_FACTORY=kubernetes
+    - TRAVIS_KUBE_VERSION=v1.19 OW_INCLUDE_SYSTEM_TESTS=false OW_CONTAINER_FACTORY=kubernetes OW_LEAN_MODE=true
+    - TRAVIS_KUBE_VERSION=v1.20 OW_INCLUDE_SYSTEM_TESTS=false OW_CONTAINER_FACTORY=kubernetes
 
 services:
   - docker
@@ -42,7 +41,7 @@
   - ./tools/travis/setupscan.sh
   - ./tools/travis/scancode.sh
   - ./tools/travis/setup.sh
-  - ./tools/travis/start-kind.sh
+  - ./deploy/kind/start-kind.sh
 
 script:
   - ./tools/travis/deploy-chart.sh
diff --git a/README.md b/README.md
index faae337..a477145 100644
--- a/README.md
+++ b/README.md
@@ -31,17 +31,10 @@
 associated events (via Triggers) from external sources (Feeds) or from
 HTTP requests.
 
-This repository supports deploying OpenWhisk to Kubernetes.
+This repository supports deploying OpenWhisk to Kubernetes and OpenShift.
 It contains a Helm chart that can be used to deploy the core
 OpenWhisk platform and optionally some of its Event Providers
-to both single-node and multi-node Kubernetes clusters.
-
-The same Helm chart can also be used to deploy OpenWhisk to
-OKD/OpenShift via a strategy of using `helm template` to
-generate yaml that is then fed to the `oc` cli.  There are some
-rough edges still in this process, we would welcome community
-contributions to help improve the targeting of OKD/OpenShift and
-document the necessary steps.
+to both single-node and multi-node Kubernetes and OpenShift clusters.
 
 # Table of Contents
 
@@ -101,29 +94,28 @@
 nodes as invoker nodes. We have
 detailed documentation on using Kubernetes clusters from the following
 major cloud providers:
-* [IBM (IKS)](docs/k8s-ibm-public.md) and [IBM (ICP)](docs/k8s-ibm-private.md)
+* [IBM (IKS)](docs/k8s-ibm-public.md)
 * [Google (GKE)](docs/k8s-google.md)
 * [Amazon (EKS)](docs/k8s-aws.md)
 
 We would welcome contributions of documentation for Azure (AKS) and any other public cloud providers.
 
-### Using OKD/OpenShift
+### Using OpenShift
 
 You will need at least 1 worker node with 4GB of memory and 2 virtual
 CPUs to deploy the default configuration of OpenWhisk.  You can deploy
 to significantly larger clusters by scaling up the replica count of
 the various components and labeling multiple nodes as invoker nodes.
 For more detailed documentation, see:
-* [OKD/OpenShift 4.5](docs/okd-45.md)
+* [OpenShift 4](docs/openshift-4.md)
 
 ### Using a Kubernetes cluster you built yourself
 
 If you are comfortable with building your own Kubernetes clusters and
 deploying services with ingresses to them, you should also
 be able to deploy OpenWhisk to a do-it-yourself cluster. Make sure
-your cluster meets the [technical
-requirements](docs/k8s-technical-requirements.md).  You will need at
-least 1 worker node with 4GB of memory and 2 virtual CPUs to deploy
+your cluster meets the [technical requirements](docs/k8s-technical-requirements.md).
+You will need at least 1 worker node with 4GB of memory and 2 virtual CPUs to deploy
 the default configuration of OpenWhisk.  You can deploy to
 significantly larger clusters by scaling up the replica count of the
 various components and labeling multiple nodes as invoker nodes.
@@ -136,7 +128,7 @@
 
 [Helm](https://github.com/kubernetes/helm) is a tool to simplify the
 deployment and management of applications on Kubernetes clusters.
-The OpenWhisk Helm chart requires the Helm 3.
+The OpenWhisk Helm chart requires Helm 3.
 
 Our automated testing currently uses Helm v3.2.4
 
@@ -153,12 +145,15 @@
 You will use Helm to deploy OpenWhisk to your Kubernetes cluster.
 There are four deployment steps that are described in more
 detail below in the rest of this section.
-1. [Initial cluster setup](#initial-setup). You will label your
-Kubernetes worker nodes to indicate their intended usage by OpenWhisk.
+1. [Initial cluster setup](#initial-setup). If you have provisioned a
+multi-node cluster, you should label the worker nodes
+to indicate their intended usage by OpenWhisk.
 2. [Customize the deployment](#customize-the-deployment). You will
 create a `mycluster.yaml` that specifies key facts about your
 Kubernetes cluster and the OpenWhisk configuration you wish to
-deploy.
+deploy. Predefined `mycluster.yaml` files for common flavors
+of Kubernetes clusters are provided in the [deploy](./deploy)
+directory.
 3. [Deploy OpenWhisk with Helm](#deploy-with-helm). You will use Helm and
 `mycluster.yaml` to deploy OpenWhisk to your Kubernetes cluster.
 4. [Configure the `wsk` CLI](#configure-the-wsk-cli). You need to
@@ -166,26 +161,42 @@
 
 ## Initial setup
 
-Indicate the Kubernetes worker nodes that should be used to execute
-user containers by OpenWhisk's invokers.  Do this by labeling each node with
+### Single Worker Node Clusters
+
+If your cluster has a single worker node, then you should
+configure OpenWhisk without node affinity. This is done by adding
+the following lines to your `mycluster.yaml`
+```
+affinity:
+  enabled: false
+
+toleration:
+  enabled: false
+
+invoker:
+  options: "-Dwhisk.kubernetes.user-pod-node-affinity.enabled=false"
+```
+
+### Multi Worker Node Clusters
+
+If you are deploying OpenWhisk to a cluster with multiple worker
+nodes, we recommend using node affinity to segregate the compute nodes
+used for the OpenWhisk control plane from those used to execute user
+functions. Do this by labeling each node with
 `openwhisk-role=invoker`. In the default configuration, which uses the
 KubernetesContainerFactory, the node labels are used in conjunction
 with Pod affinities to inform the Kubernetes scheduler how to place
 work so that user actions will not interfere with the OpenWhisk
 control plane.  When using the non-default DockerContainerFactory,
-OpenWhisk assumes it has exclusive use of these invoker nodes and
-will schedule work on them directly, completely bypassing the Kubernetes
-scheduler. For a single node cluster, simply do
+OpenWhisk assumes it has exclusive use of these invoker nodes and will
+schedule work on them directly, completely bypassing the Kubernetes
+scheduler. For each node
+<INVOKER_NODE_NAME> you want to be an invoker, execute
 ```shell
-kubectl label nodes --all openwhisk-role=invoker
-```
-If you have a multi-node cluster, then for each node <INVOKER_NODE_NAME>
-you want to be an invoker, execute
-```shell
-$ kubectl label nodes <INVOKER_NODE_NAME> openwhisk-role=invoker
+kubectl label node <INVOKER_NODE_NAME> openwhisk-role=invoker
 ```
 
-If you are targeting OKD/OpenShift, use the command
+If you are targeting OpenShift, use the command
 ```shell
 oc label node <INVOKER_NODE_NAME> openwhisk-role=invoker
 ```
@@ -200,36 +211,30 @@
 Event Providers on specific nodes, label those nodes with
 `openwhisk-role=provider`.
 
-### Disabling affinity (not recommended)
-If the Kubernetes cluster does not allow you to assign a label to a node, or you cannot use the affinity attribute, you can disable it.
-Please note that it is suitable for testing purposes only and may interfere with the OpenWhisk control plane.
-
-You can disable affinity by editing the `mycluster.yaml` file:
-
-```yaml
-affinity:
-  enabled: false
-invoker:
-  options: "-Dwhisk.kubernetes.user-pod-node-affinity.enabled=false"
-```
+If the Kubernetes cluster does not allow you to assign a label to a
+node, or you cannot use the affinity attribute, you use the yaml
+snippet shown above in the single worker node configuration to disable
+the use of affinities by OpenWhisk.
 
 ## Customize the Deployment
 
-You must create a `mycluster.yaml` file to record key aspects of your
+You will need a `mycluster.yaml` file to record key aspects of your
 Kubernetes cluster that are needed to configure the deployment of
 OpenWhisk to your cluster. For details, see the documentation
 appropriate to your Kubernetes cluster:
 * [Docker for Mac](docs/k8s-docker-for-mac.md#configuring-openwhisk)
 * [Docker for Windows](docs/k8s-docker-for-windows.md#configuring-openwhisk)
 * [kind](docs/k8s-kind.md#configuring-openwhisk)
-* [IBM Kubernetes Service (IKS)](docs/k8s-ibm-public.md#configuring-openwhisk)
-* [IBM Cloud Private (ICP)](docs/k8s-ibm-private.md#configuring-openwhisk)
+* [IBM Cloud Kubernetes Service (IKS)](docs/k8s-ibm-public.md#configuring-openwhisk)
 * [Google (GKE)](docs/k8s-google.md#configuring-openwhisk)
 * [Amazon (EKS)](docs/k8s-aws.md#configuring-openwhisk)
-* [OKD/OpenShift](docs/okd-311.md##configuring-openwhisk)
+* [OpenShift](docs/openshift-4.md##configuring-openwhisk)
 
-Beyond the Kubernetes cluster specific configuration information,
-the `mycluster.yaml` file is also used
+Default/template `mycluster.yaml` for various types of Kubernetes clusets
+can be found in subdirectories of [deploy](./deploy).
+
+Beyond the basic Kubernetes cluster specific configuration information,
+the `mycluster.yaml` file can also be used
 to customize your OpenWhisk deployment by enabling optional features
 and controlling the replication factor of the various microservices
 that make up the OpenWhisk implementation. See the [configuration
@@ -267,18 +272,6 @@
 helm install owdev ./helm/openwhisk -n openwhisk --create-namespace -f mycluster.yaml
 ```
 
-### Deploying to OKD/OpenShift
-
-Deploying to OKD/OpenShift currently requires a two command sequence:
-```shell
-helm template owdev ./helm/openwhisk -n openwhisk -f mycluster.yaml > owdev.yaml
-oc create -f owdev.yaml
-```
-The two step sequence is currently required because the `oc` command must be
-used to create the `Route` resource specified in the generated `owdev.yaml`.
-We recommend generating to a file to make it easier to undeploy OpenWhisk later
-by simply doing `oc delete -f owdev.yaml`
-
 ### Checking status
 
 You can use the command `helm status owdev -n openwhisk` to get a summary
@@ -467,13 +460,6 @@
 By default, `helm uninstall` removes the history of previous deployments.
 If you want to keep the history, add the command line flag `--keep-history`.
 
-For OpenShift deployments, you cannot use `helm uninstall` to remove the OpenWhisk
-deployment because we did not do a `helm install`.
-If you saved the output from `helm template` into a file,
-you can simply use that file as an argument to `oc delete`.  If you
-did not save the file, you can redo the `helm template` command and
-feed the generated yaml into an `oc delete` command.
-
 # Issues
 
 If your OpenWhisk deployment is not working, check our
diff --git a/deploy/README.md b/deploy/README.md
new file mode 100644
index 0000000..35c02ad
--- /dev/null
+++ b/deploy/README.md
@@ -0,0 +1,22 @@
+<!--
+#
+# 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 directory contains scripts and configuration files for configuring
+various flavors of Kubernetes cluster providers.
+
diff --git a/deploy/docker-macOS/mycluster.yaml b/deploy/docker-macOS/mycluster.yaml
new file mode 100644
index 0000000..96098a9
--- /dev/null
+++ b/deploy/docker-macOS/mycluster.yaml
@@ -0,0 +1,34 @@
+#
+# 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.
+#
+
+whisk:
+  ingress:
+    type: NodePort
+    apiHostName: localhost
+    apiHostPort: 31001
+    useInternally: false
+
+nginx:
+  httpsNodePort: 31001
+
+# A single node cluster; so disable affinity
+affinity:
+  enabled: false
+toleration:
+  enabled: false
+invoker:
+  options: "-Dwhisk.kubernetes.user-pod-node-affinity.enabled=false"
diff --git a/deploy/docker-windows/mycluster.yaml b/deploy/docker-windows/mycluster.yaml
new file mode 100644
index 0000000..96098a9
--- /dev/null
+++ b/deploy/docker-windows/mycluster.yaml
@@ -0,0 +1,34 @@
+#
+# 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.
+#
+
+whisk:
+  ingress:
+    type: NodePort
+    apiHostName: localhost
+    apiHostPort: 31001
+    useInternally: false
+
+nginx:
+  httpsNodePort: 31001
+
+# A single node cluster; so disable affinity
+affinity:
+  enabled: false
+toleration:
+  enabled: false
+invoker:
+  options: "-Dwhisk.kubernetes.user-pod-node-affinity.enabled=false"
diff --git a/deploy/ibm-public/mycluster-iks.yaml b/deploy/ibm-public/mycluster-iks.yaml
new file mode 100644
index 0000000..82bed12
--- /dev/null
+++ b/deploy/ibm-public/mycluster-iks.yaml
@@ -0,0 +1,44 @@
+#
+# 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.
+#
+
+whisk:
+  ingress:
+    # NOTE: Replace <domain> with your cluster's actual domain
+    apiHostName: openwhisk.<domain>
+    apiHostPort: 443
+    apiHostProto: https
+    type: Standard
+    useInternally: true
+    # NOTE: Replace <domain> with your cluster's actual domain
+    domain: openwhisk.<domain>
+    tls:
+      enabled: true
+      secretenabled: true
+      createsecret: false
+      # NOTE: Replace <ibmtlssecret> with your cluster's actual tlssecret
+      secretname: <ibmtlssecret>
+    annotations:
+      kubernetes.io/ingress.class: public-iks-k8s-nginx
+      nginx.ingress.kubernetes.io/use-regex: "true"
+      nginx.ingress.kubernetes.io/configuration-snippet: |
+         proxy_set_header X-Request-ID $request_id;
+      nginx.ingress.kubernetes.io/proxy-body-size: 50m
+      nginx.ingress.kubernetes.io/proxy-read-timeout: "75"
+
+invoker:
+  containerFactory:
+    impl: kubernetes
diff --git a/deploy/ibm-public/mycluster-roks.yaml b/deploy/ibm-public/mycluster-roks.yaml
new file mode 100644
index 0000000..f5b2c2b
--- /dev/null
+++ b/deploy/ibm-public/mycluster-roks.yaml
@@ -0,0 +1,47 @@
+#
+# 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.
+#
+
+whisk:
+  ingress:
+    # NOTE: Replace <domain> with your cluster's actual domain
+    apiHostName: openwhisk.<domain>
+    apiHostPort: 443
+    apiHostProto: https
+    type: Standard
+    useInternally: true
+    # NOTE: Replace <domain> with your cluster's actual domain
+    domain: openwhisk.<domain>
+    tls:
+      enabled: true
+      secretenabled: true
+      createsecret: false
+      # NOTE: Replace <ibmtlssecret> with your cluster's actual tlssecret
+      secretname: <ibmtlssecret>
+    annotations:
+      kubernetes.io/ingress.class: public-iks-k8s-nginx
+      nginx.ingress.kubernetes.io/use-regex: "true"
+      nginx.ingress.kubernetes.io/configuration-snippet: |
+         proxy_set_header X-Request-ID $request_id;
+      nginx.ingress.kubernetes.io/proxy-body-size: 50m
+      nginx.ingress.kubernetes.io/proxy-read-timeout: "75"
+
+k8s:
+  dns: dns-default.openshift-dns
+
+invoker:
+  containerFactory:
+    impl: kubernetes
diff --git a/deploy/kind/kind-cluster.yaml b/deploy/kind/kind-cluster.yaml
new file mode 100644
index 0000000..b9dc1f8
--- /dev/null
+++ b/deploy/kind/kind-cluster.yaml
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+
+kind: Cluster
+apiVersion: kind.x-k8s.io/v1alpha4
+nodes:
+- role: control-plane
+- role: worker
+  extraPortMappings:
+    - hostPort: 31001
+      containerPort: 31001
+- role: worker
diff --git a/deploy/kind/mycluster.yaml b/deploy/kind/mycluster.yaml
new file mode 100644
index 0000000..c4e5643
--- /dev/null
+++ b/deploy/kind/mycluster.yaml
@@ -0,0 +1,37 @@
+#
+# 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.
+#
+
+whisk:
+  ingress:
+    type: NodePort
+    apiHostName: localhost
+    apiHostPort: 31001
+    useInternally: false
+
+nginx:
+  httpsNodePort: 31001
+
+# disable affinity
+affinity:
+  enabled: false
+toleration:
+  enabled: false
+invoker:
+  options: "-Dwhisk.kubernetes.user-pod-node-affinity.enabled=false"
+  # must use KCF as kind uses containerd as its container runtime
+  containerFactory:
+    impl: "kubernetes"
diff --git a/tools/travis/start-kind.sh b/deploy/kind/start-kind.sh
similarity index 86%
rename from tools/travis/start-kind.sh
rename to deploy/kind/start-kind.sh
index 2a0cd7a..55e8c63 100755
--- a/tools/travis/start-kind.sh
+++ b/deploy/kind/start-kind.sh
@@ -18,15 +18,9 @@
 
 set -x
 
-# Create cluster config
-cat > mycluster.yaml <<EOF
-kind: Cluster
-apiVersion: kind.x-k8s.io/v1alpha4
-nodes:
-- role: control-plane
-- role: worker
-- role: worker
-EOF
+SCRIPTDIR=$(cd $(dirname "$0") && pwd)
+
+TRAVIS_KUBE_VERSION=${TRAVIS_KUBE_VERSION:="v1.20"}
 
 # Map from Kubernetes major versions to the kind node image tag
 case $TRAVIS_KUBE_VERSION in
@@ -41,7 +35,7 @@
 esac
 
 # Boot cluster
-kind create cluster --config mycluster.yaml --name kind --image kindest/node:${KIND_NODE_TAG} --wait 10m || exit 1
+kind create cluster --config "$SCRIPTDIR/kind-cluster.yaml" --name kind --image kindest/node:${KIND_NODE_TAG} --wait 10m || exit 1
 
 echo "Kubernetes cluster is deployed and reachable"
 kubectl describe nodes
diff --git a/docs/k8s-docker-for-mac.md b/docs/k8s-docker-for-mac.md
index 9644acf..8cd748e 100644
--- a/docs/k8s-docker-for-mac.md
+++ b/docs/k8s-docker-for-mac.md
@@ -50,18 +50,27 @@
 ### Configuring OpenWhisk
 
 You will be using a NodePort ingress to access OpenWhisk. Assuming
-`kubectl describe nodes | grep InternalIP` returns 192.168.65.3 and
 port 31001 is available to be used on your host machine, a
-`mycluster.yaml` for a standard deployment of OpenWhisk would be:
+[mycluster.yaml](../deploy/docker-macOS/mycluster.yaml]
+for a standard deployment of OpenWhisk would be:
 ```yaml
 whisk:
   ingress:
     type: NodePort
-    apiHostName: 192.168.65.3
+    apiHostName: localhost
     apiHostPort: 31001
+    useInternally: false
 
 nginx:
   httpsNodePort: 31001
+
+# A single node cluster; so disable affinity
+affinity:
+  enabled: false
+toleration:
+  enabled: false
+invoker:
+  options: "-Dwhisk.kubernetes.user-pod-node-affinity.enabled=false"
 ```
 
 ## Hints and Tips
@@ -88,16 +97,3 @@
 TLS termination will be handled by OpenWhisk's `nginx` service and
 will use self-signed certificates.  You will need to invoke `wsk` with
 the `-i` command line argument to bypass certificate checking.
-
-The docker network is not exposed to the host on MacOS. However, the
-exposed ports for NodePort services are forwarded from localhost.
-Therefore you must use different host names to connect to OpenWhisk
-from outside the cluster (with the `wsk` cli) and from inside the
-cluster (in `mycluster.yaml`).  Continuing the example from above,
-when setting the `--apihost` for the `wsk` cli, you would use
-`localhost:31001`.  This networking difference also shows up when
-listing apis via `wsk -i api list`. The listed URLs will show the
-cluster-internal apihost,
-e.g. `https://192.168.65.3:31001/api/<UID>/<PATH>`, to invoke the api
-from outside the cluster you should use `localhost:31001` instead, e.g.
-`https://localhost:31001/api/<UID>/<PATH>`.
diff --git a/docs/k8s-docker-for-windows.md b/docs/k8s-docker-for-windows.md
index aa6f4b5..c9793d9 100644
--- a/docs/k8s-docker-for-windows.md
+++ b/docs/k8s-docker-for-windows.md
@@ -53,60 +53,40 @@
 allocated to Docker**. Then switch to the Kubernetes panel, and check
 the box to enable Kubernetes.
 
-### Using Git to Clone this Repository
-
-`git clone https://github.com/apache/openwhisk-deploy-kube.git`
-
 ### Configuring OpenWhisk
 
 You will be using a NodePort ingress to access OpenWhisk. Assuming
 `kubectl describe nodes | find "InternalIP"` returns 192.168.65.3 and
 port 31001 is available to be used on your host machine, a
-`mycluster.yaml` for a standard deployment of OpenWhisk would be:
+[mycluster.yaml](../deploy/docker-windows/mycluster.yaml) for a standard deployment of OpenWhisk would be:
 
 ```yaml
 whisk:
   ingress:
     type: NodePort
-    apiHostName: 192.168.65.3
+    apiHostName: localhost
     apiHostPort: 31001
+    useInternally: false
 
 nginx:
   httpsNodePort: 31001
+
+# A single node cluster; so disable affinity
+affinity:
+  enabled: false
+toleration:
+  enabled: false
+invoker:
+  options: "-Dwhisk.kubernetes.user-pod-node-affinity.enabled=false"
 ```
 
-### Using helm to install OpenWhisk
+## Hints and Tips
 
-Indicate the Kubernetes worker nodes that should be used to execute user
-containers by OpenWhisk's invokers. For a single node development cluster,
-simply run:
-
-`kubectl label nodes --all openwhisk-role=invoker`
-
-Make sure you created your
-`mycluster.yaml` file as described above, and run:
-
-```cmd
-cd openwhisk-deploy-kube
-helm install owdev ./helm/openwhisk -n openwhisk --create-namespace -f mycluster.yaml
-```
-
-You can use the command `helm status owdev -n openwhisk` to get a summary of the various
-Kubernetes artifacts that make up your OpenWhisk deployment. Once the
-`install-packages` Pod is in the Completed state, your OpenWhisk deployment
-is ready to be used.
-
-Tip: If you notice errors or pods stuck in the pending state (`init-couchdb`
+If you notice errors or pods stuck in the pending state (`init-couchdb`
 as an example), try running `kubectl get pvc --all-namespaces`. If you notice
 that claims are stuck in the Pending state, you may need to follow the
 workaround mentioned in this [Docker for Windows Github Issue](https://github.com/docker/for-win/issues/1758#issuecomment-376054370).
 
-You are now ready to set up the wsk cli. Further instructions can be
-[found here](https://github.com/apache/openwhisk-deploy-kube#https://github.com/apache/openwhisk-deploy-kube#configure-the-wsk-cli).
-Follow the Docker for Windows instructions.
-
-## Hints and Tips
-
 One nice feature of using Kubernetes in Docker, is that the
 containers being run in Kubernetes are also directly
 visible/accessible via the usual Docker commands. Furthermore, it is
@@ -130,11 +110,3 @@
 TLS termination will be handled by OpenWhisk's `nginx` service and
 will use self-signed certificates. You will need to invoke `wsk` with
 the `-i` command line argument to bypass certificate checking.
-
-The docker network is not exposed to the host on Windows. However, the
-exposed ports for NodePort services are forwarded from localhost.
-Therefore you must use different host names to connect to OpenWhisk
-from outside the cluster (with the `wsk` cli) and from inside the
-cluster (in `mycluster.yaml`). Continuing the example from above,
-when setting the `--apihost` for the `wsk` cli, you would use
-`localhost:31001`.
diff --git a/docs/k8s-ibm-private.md b/docs/k8s-ibm-private.md
deleted file mode 100644
index 7c677fb..0000000
--- a/docs/k8s-ibm-private.md
+++ /dev/null
@@ -1,197 +0,0 @@
-<!--
-#
-# 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 initially 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.
diff --git a/docs/k8s-ibm-public.md b/docs/k8s-ibm-public.md
index 8dcd226..0df9878 100644
--- a/docs/k8s-ibm-public.md
+++ b/docs/k8s-ibm-public.md
@@ -43,21 +43,18 @@
 First, determine the values for <domain> and <ibmtlssecret> for
 your cluster by running the command:
 ```
-bx cs cluster-get <mycluster>
+ibmcloud cs cluster get -c <mycluster>
 ```
 The CLI output will look something like
 ```
-bx cs cluster-get <mycluster>
+ibmcloud cs cluster get -c <mycluster>
 Retrieving cluster <mycluster>...
 OK
 Name:    <mycluster>
-ID:    b9c6b00dc0aa487f97123440b4895f2d
-Created:  2017-04-26T19:47:08+0000
-State:    normal
-Master URL:  https://169.57.40.165:1931
-Ingress subdomain:  <domain>
-Ingress secret:  <ibmtlssecret>
-Workers:  3
+...
+Ingress Subdomain:  <domain>
+Ingress Secret:     <ibmtlssecret>
+...
 ```
 
 As described in [IBM's ingress documentation](https://cloud.ibm.com/docs/containers/cs_ingress.html#ingress),
@@ -67,65 +64,41 @@
 are deploying openwhisk into the `openwhisk` namespace, use `openwhisk`
 as your subdomain (as shown below in the example `mycluster.yaml`).
 
-Now define `mycluster.yaml` as below (substituting the real values for
-`<domain>` and `<ibmtlssecret>`).
+A template [mycluster.yaml](../deploy/ibm-public/mycluster-iks.yaml]
+for a standard deployment of OpenWhisk on IKS would be:
 ```yaml
 whisk:
   ingress:
+    # NOTE: Replace <domain> with your cluster's actual domain
     apiHostName: openwhisk.<domain>
     apiHostPort: 443
     apiHostProto: https
     type: Standard
+    useInternally: true
+    # NOTE: Replace <domain> with your cluster's actual domain
     domain: openwhisk.<domain>
     tls:
       enabled: true
       secretenabled: true
       createsecret: false
+      # NOTE: Replace <ibmtlssecret> with your cluster's actual tlssecret
       secretname: <ibmtlssecret>
     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
-      ingress.bluemix.net/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
-      ingress.bluemix.net/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
-      ingress.bluemix.net/proxy-add-headers: |
-        serviceName=owdev-controller {
-          'X-Request-ID' $request_id;
-        }
+      kubernetes.io/ingress.class: public-iks-k8s-nginx
+      nginx.ingress.kubernetes.io/use-regex: "true"
+      nginx.ingress.kubernetes.io/configuration-snippet: |
+         proxy_set_header X-Request-ID $request_id;
+      nginx.ingress.kubernetes.io/proxy-body-size: 50m
+      nginx.ingress.kubernetes.io/proxy-read-timeout: "75"
 
 invoker:
   containerFactory:
     impl: kubernetes
-
-k8s:
-  persistence:
-    hasDefaultStorageClass: false
-    explicitStorageClass: default
 ```
 
-Starting with IKS 1.11, the underlying container runtime is now
-containerd instead of docker. As a result, you cannot use the
-DockerContainerFactory on IKS and must use the
-KubernetesContainerFactory.
-
-IKS does not provide a properly configured DefaultStorageClass,
-instead you need to tell the Helm chart to use the `default`
-StorageClassName as shown above. This StorageClass does have
-a dynamic provisioner, so it is not necessary to manually create
-the PersistentVolumes. Note that it is not unusual for it to take
-several minutes for your PersistentVolumes to be created
-(dependent resources will be in `Pending` state).
+The underlying container runtime used by IKS is containerd.
+Therefore, you cannot use the DockerContainerFactory on IKS and must
+use the KubernetesContainerFactory.
 
 ####  IBM Cloud Lite cluster
 
@@ -133,7 +106,7 @@
 use a NodePort. Obtain the Public IP address of the sole worker node
 by using the command
 ```shell
-bx cs workers <my-cluster>
+ibmcloud cs workers <my-cluster>
 ```
 Then define `mycluster.yaml` as
 ```yaml
@@ -142,24 +115,23 @@
     type: NodePort
     apiHostName: YOUR_WORKERS_PUBLIC_IP_ADDR
     apiHostPort: 31001
+    useInternally: true
 
 nginx:
   httpsNodePort: 31001
 
-k8s:
-  persistence:
-    hasDefaultStorageClass: false
-    explicitStorageClass: default
+# disable affinity
+affinity:
+  enabled: false
+toleration:
+  enabled: false
+invoker:
+  options: "-Dwhisk.kubernetes.user-pod-node-affinity.enabled=false"
+  # must use KCF as IKS uses containerd as its container runtime
+  containerFactory:
+    impl: "kubernetes"
 ```
 
-IKS does not provide a properly configured DefaultStorageClass,
-instead you need to tell the Helm chart to use the `default`
-StorageClassName as shown above. This StorageClass does have
-a dynamic provisioner, so it is not necessary to manually create
-the PersistentVolumes. Note that it is not unusual for it to take
-several minutes for your PersistentVolumes to be created
-(dependent resources will be in `Pending` state).
-
 ## Hints and Tips
 
 On IBM Standard clusters, you can configure OpenWhisk to integrate
diff --git a/docs/k8s-kind.md b/docs/k8s-kind.md
index 518ef7a..072399a 100644
--- a/docs/k8s-kind.md
+++ b/docs/k8s-kind.md
@@ -44,62 +44,43 @@
 machine.  This will enable you to run `kind` without
 requiring `sudo` to gain `root` privileges.
 
-Create a kind-cluster.yaml to configure your cluster.
-```yaml
-kind: Cluster
-apiVersion: kind.x-k8s.io/v1alpha4
-nodes:
-- role: control-plane
-- role: worker
-  extraPortMappings:
-    - hostPort: 31001
-      containerPort: 31001
-- role: worker
+We've provided a [script](./deploy/kind/start-kind.sh)
+that you can use to bring up a kind cluster in a
+reasonable configuration for OpenWhisk. The script
+assumes that port 31001 is available on your machine
+and can be used by openwhisk.  To use a different port,
+edit `deploy/kind/kind-cluster.yaml`.
 ```
-The extraPortMappings stanza enables port forwarding
-from the localhost to the in-cluster network.
-This is required on MacOS, but to simplify the instructions
-we use the same setup for all platforms.
-
-Now create your cluster with the command:
-```shell
-kind create cluster --config kind-cluster.yaml
-```
-
-Then label the two worker nodes so that one is reserved for the invoker
-and the other will be used to run the rest of the OpenWhisk system.
-```shell
-kubectl label node kind-worker openwhisk-role=core
-kubectl label node kind-worker2 openwhisk-role=invoker
+./deploy/kind/start-kind.sh
 ```
 
 ### Configuring OpenWhisk
 
-To configure OpenWhisk, you first need to define a `mycluster.yaml`
-that specifies the "inside the cluster" ingress information and
-other system configuration. First, determine the internalIP of
-a worker node with the command:
-
-```
-kubectl describe node kind-worker | grep InternalIP: | awk '{print $2}'
-```
-
-A `mycluster.yaml` for a standard deployment of OpenWhisk would look
-like the below, replacing <InternalIP> with its actual value:
+Assuming you used the default port 31001 when starting kind, a
+[mycluster.yaml](../deploy/kind/mycluster.yaml]
+for a standard deployment of OpenWhisk would be:
 
 ```yaml
 whisk:
   ingress:
     type: NodePort
-    apiHostName: <INTERNAL_IP>
+    apiHostName: localhost
     apiHostPort: 31001
-
-invoker:
-  containerFactory:
-    impl: "kubernetes"
+    useInternally: false
 
 nginx:
   httpsNodePort: 31001
+
+# disable affinity
+affinity:
+  enabled: false
+toleration:
+  enabled: false
+invoker:
+  options: "-Dwhisk.kubernetes.user-pod-node-affinity.enabled=false"
+  # must use KCF as kind uses containerd as its container runtime
+  containerFactory:
+    impl: "kubernetes"
 ```
 Note that you must use the KubernetesContainerFactory when running
 OpenWhisk on `kind` because it is configured to use `containerd`
@@ -107,8 +88,8 @@
 
 External to the Kubernetes cluster, for example when using the `wsk` cli,
 we will use the port forwarding configured by the `extraPortMappings`
-in kind-cluster.yaml to allow the OpenWhisk apihost property
-to be set to localhost:31001
+in [kind-cluster.yaml](../deploy/kind/kind-cluster.yaml) to allow the
+OpenWhisk apihost property to be set to localhost:31001
 
 ## Hints and Tips
 
diff --git a/docs/k8s-technical-requirements.md b/docs/k8s-technical-requirements.md
index d62831d..042603e 100644
--- a/docs/k8s-technical-requirements.md
+++ b/docs/k8s-technical-requirements.md
@@ -21,8 +21,8 @@
 
 The Kubernetes cluster on which you are deploying OpenWhisk must meet
 the following requirements:
-* [Kubernetes](https://github.com/kubernetes/kubernetes) version 1.16+.
-  Our automated testing currently covers Kubernetes versions 1.17, 1.18 and 1.19.
+* [Kubernetes](https://github.com/kubernetes/kubernetes) version 1.19+.
+  Our automated testing currently covers Kubernetes versions 1.19 and 1.20.
 * The ability to create Ingresses to make a Kubernetes service
   available outside of the cluster so you can actually use OpenWhisk.
 * Unless you disable persistence (see
diff --git a/docs/okd-45.md b/docs/okd-45.md
deleted file mode 100644
index 65fc02d..0000000
--- a/docs/okd-45.md
+++ /dev/null
@@ -1,79 +0,0 @@
-<!--
-#
-# 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 OKD/OpenShift 4.5
-
-## Overview
-
-The 4.5 version of OKD/OpenShift is based on Kubernetes 1.18.
-
-We assume you have an operational cluster that meets the
-[technical requirements](okd-technical-requirements.md) and that you
-have sufficient privileges to perform the necessary `oc adm`
-operations detailed below.
-
-## Initial Setup
-
-Create an openwhisk project (Kubernetes namespace) using the command
-```shell
-oc new-project openwhisk
-```
-
-Because OpenShift doesn’t allow arbitrary UIDs by default, execute the following commands:
-```shell
-oc adm policy add-scc-to-user anyuid -z default
-oc adm policy add-scc-to-user privileged -z default
-oc adm policy add-scc-to-user anyuid -z openwhisk-core
-oc adm policy add-scc-to-user privileged -z openwhisk-core
-oc adm policy add-scc-to-user anyuid -z owdev-init-sa
-oc adm policy add-scc-to-user privileged -z owdev-init-sa
-```
-
-## Configuring OpenWhisk
-
-You must use the KubernetesContainerFactory on OKD/OpenShift.
-
-Here is a sample `mycluster.yaml`, where <DOMAIN_USED_IN_ROUTES_FOR_THIS_CLUSTER>
-should be replaced with the domain used for Routes in your cluster.
-```yaml
-whisk:
-  ingress:
-    type: OpenShift
-    apiHostName: openwhisk.<DOMAIN_USED_IN_ROUTES_FOR_THIS_CLUSTER>
-    apiHostPort: 443
-    apiHostProto: https
-    domain: openwhisk.<DOMAIN_USED_IN_ROUTES_FOR_THIS_CLUSTER>
-  testing:
-    includeTests: false
-
-invoker:
-  containerFactory:
-    impl: kubernetes
-``
-
-## Limitations
-
-The nginx service is currently not deployed on OpenShift (problem
-determining the appropriate value to use for `k8s.dns`, which is used to
-set the resolver in `nginx-cm.yaml`).  As a result, the namespace
-prefixed 'vanity url' rewriting routes and the download of the cli/SDK
-binaries is not currently supported when deploying on OpenShift.
-
-Smoketesting a deployment via `helm test` is not supported because
-we did not use `helm install` to deploy the chart.
diff --git a/docs/openshift-4.md b/docs/openshift-4.md
new file mode 100644
index 0000000..08681c1
--- /dev/null
+++ b/docs/openshift-4.md
@@ -0,0 +1,126 @@
+<!--
+#
+# 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 OpenShift 4.6
+
+## Overview
+
+The 4.6 version of OpenShift is based on Kubernetes 1.19.
+
+We assume you have an operational cluster that meets the
+[technical requirements](openshift-technical-requirements.md) and that you
+have sufficient privileges to perform the necessary `oc adm`
+operations detailed below.
+
+## Initial Setup
+
+Create an openwhisk project (Kubernetes namespace) using the command
+```shell
+oc new-project openwhisk
+```
+
+Because OpenShift doesn’t allow pods to run with arbitrary UIDs
+by default, you will need to add adjust some policy options
+before deploying OpenWhisk.  Execute the following commands:
+```shell
+oc adm policy add-scc-to-user anyuid -z default
+oc adm policy add-scc-to-user privileged -z default
+oc adm policy add-scc-to-user anyuid -z openwhisk-core
+oc adm policy add-scc-to-user privileged -z openwhisk-core
+oc adm policy add-scc-to-user anyuid -z owdev-init-sa
+oc adm policy add-scc-to-user privileged -z owdev-init-sa
+```
+
+## Configuring OpenWhisk
+
+You must use the KubernetesContainerFactory on OpenShift.
+
+### Red Hat OpenShift on IBM Cloud
+
+A Red Hat OpenShift on IBM Cloud cluster has full support for TLS
+including a wild-card certificate for subdomains and can be configured
+with additional annotations to fine tune ingress performance.
+
+First, determine the values for <domain> and <ibmtlssecret> for
+your cluster by running the command:
+```
+ibmcloud cs cluster get -c <mycluster>
+```
+The CLI output will look something like
+```
+ibmcloud cs cluster get -c <mycluster>
+Retrieving cluster <mycluster>...
+OK
+Name:    <mycluster>
+...
+Ingress Subdomain:  <domain>
+Ingress Secret:     <ibmtlssecret>
+...
+```
+
+The ingress secret is not automatically copied to new OpenShift
+projects. Before deploying OpenWhisk, you will need to copy the
+ingress secret (<ibmtlssecret> from the `openshift-ingress` namespace
+to the `openwhisk` namespace.
+
+As described in [IBM's ingress documentation](https://cloud.ibm.com/docs/containers/cs_ingress.html#ingress),
+to enable applications deployed in multiple namespaces to share the ingress resource,
+you should use a unique subdomain name for each namespace.  We suggest
+a convention of using the namespace name as the subdomain name.  So if you
+are deploying openwhisk into the `openwhisk` namespace, use `openwhisk`
+as your subdomain (as shown below in the example `mycluster.yaml`).
+
+A template [mycluster.yaml](../deploy/ibm-public/mycluster-roks.yaml]
+for a standard deployment of OpenWhisk on ROKS would be:
+```yaml
+whisk:
+  ingress:
+    # NOTE: Replace <domain> with your cluster's actual domain
+    apiHostName: openwhisk.<domain>
+    apiHostPort: 443
+    apiHostProto: https
+    type: Standard
+    useInternally: true
+    # NOTE: Replace <domain> with your cluster's actual domain
+    domain: openwhisk.<domain>
+    tls:
+      enabled: true
+      secretenabled: true
+      createsecret: false
+      # NOTE: Replace <ibmtlssecret> with your cluster's actual tlssecret
+      secretname: <ibmtlssecret>
+    annotations:
+      kubernetes.io/ingress.class: public-iks-k8s-nginx
+      nginx.ingress.kubernetes.io/use-regex: "true"
+      nginx.ingress.kubernetes.io/configuration-snippet: |
+         proxy_set_header X-Request-ID $request_id;
+      nginx.ingress.kubernetes.io/proxy-body-size: 50m
+      nginx.ingress.kubernetes.io/proxy-read-timeout: "75"
+
+k8s:
+  dns: dns-default.openshift-dns
+
+invoker:
+  containerFactory:
+    impl: kubernetes
+``
+
+## Limitations
+
+No known limitations.
diff --git a/docs/okd-technical-requirements.md b/docs/openshift-technical-requirements.md
similarity index 83%
rename from docs/okd-technical-requirements.md
rename to docs/openshift-technical-requirements.md
index 4ac0516..46c4b6d 100644
--- a/docs/okd-technical-requirements.md
+++ b/docs/openshift-technical-requirements.md
@@ -17,12 +17,12 @@
 #
 -->
 
-# Technical Requirements for OKD/OpenShift
+# Technical Requirements for OpenShift
 
-The OKD/OpenShift cluster on which you are deploying OpenWhisk must meet
+The OpenShift cluster on which you are deploying OpenWhisk must meet
 the following requirements:
-* OKD/OpenShift version 4.5 or newer (these instructions were tested on 4.5).
-* The ability to create routes to make a Kubernetes service
+* OpenShift version 4.5 or newer (these instructions were tested on 4.5).
+* The ability to create Ingresses to make a Kubernetes service
   available outside of the cluster so you can actually use OpenWhisk.
 * Unless you disable persistence (see
   [configurationChoices.md](configurationChoices.md)),
diff --git a/helm/openwhisk/Chart.yaml b/helm/openwhisk/Chart.yaml
index 77f45c7..4bcff78 100644
--- a/helm/openwhisk/Chart.yaml
+++ b/helm/openwhisk/Chart.yaml
@@ -28,4 +28,4 @@
 maintainers:
   - name: Apache OpenWhisk Community
     email: dev@openwhisk.apache.org
-kubeVersion: ">=v1.16.0-r0"
+kubeVersion: ">=v1.19.0-r0"
diff --git a/helm/openwhisk/README.md b/helm/openwhisk/README.md
index 80d46a5..62b47a8 100644
--- a/helm/openwhisk/README.md
+++ b/helm/openwhisk/README.md
@@ -56,7 +56,7 @@
 
 ## Prerequisites
 
-* Kubernetes 1.14 - 1.18.*
+* Kubernetes 1.19+
 
 ### Image Policy Requirements
 
diff --git a/helm/openwhisk/configMapFiles/tests/smoketest/myTask.sh b/helm/openwhisk/configMapFiles/tests/smoketest/myTask.sh
index ed3d764..0ac526d 100644
--- a/helm/openwhisk/configMapFiles/tests/smoketest/myTask.sh
+++ b/helm/openwhisk/configMapFiles/tests/smoketest/myTask.sh
@@ -62,6 +62,9 @@
 # now run it as a web action
 echo "Invoking as web action"
 HELLO_URL=$(wsk -i action get hello --url | grep "https://")
+if [ -z "$HELLO_URL" ]; then
+    HELLO_URL=$(wsk -i action get hello --url | grep "http://")
+fi
 RESULT=$(wget --no-check-certificate -qO- $HELLO_URL | grep 'Hello world')
 if [ -z "$RESULT" ]; then
   echo "FAILED! Could not invoke hello as a web action"
@@ -73,7 +76,10 @@
 wsk -i api create /demo /hello get hello || (echo "FAILED: unable to create API"; exit 1)
 echo "Invoking action via the api"
 API_URL=$(wsk -i api list | grep hello | awk '{print $4}')
-RESULT=$(wget --no-check-certificate -qO- "$API_URL" | grep 'Hello world')
+echo "External api URL: $API_URL"
+INTERNAL_URL=$(echo $API_URL | sed s#^http.*/api/#$WSK_API_HOST_URL/api/#)
+echo "Internal api URL: $INTERNAL_URL"
+RESULT=$(wget --no-check-certificate -qO- "$INTERNAL_URL" | grep 'Hello world')
 if [ -z "$RESULT" ]; then
   echo "FAILED! Could not invoke hello via apigateway"
   exit 1
diff --git a/helm/openwhisk/templates/NOTES.txt b/helm/openwhisk/templates/NOTES.txt
index b8bb560..fb19020 100644
--- a/helm/openwhisk/templates/NOTES.txt
+++ b/helm/openwhisk/templates/NOTES.txt
@@ -15,7 +15,7 @@
  limitations under the License.
 */}}
 Apache OpenWhisk
-Copyright 2016-2020 The Apache Software Foundation
+Copyright 2016-2021 The Apache Software Foundation
 
 This product includes software developed at
 The Apache Software Foundation (http://www.apache.org/).
diff --git a/helm/openwhisk/templates/_helpers.tpl b/helm/openwhisk/templates/_helpers.tpl
index f57c578..847ef8e 100644
--- a/helm/openwhisk/templates/_helpers.tpl
+++ b/helm/openwhisk/templates/_helpers.tpl
@@ -268,17 +268,17 @@
   valueFrom:
     configMapKeyRef:
       name: {{ .Release.Name }}-whisk.config
-      key: whisk_api_host_proto
+      key: whisk_internal_api_host_proto
 - name: "WHISK_API_HOST_PORT"
   valueFrom:
     configMapKeyRef:
       name: {{ .Release.Name }}-whisk.config
-      key: whisk_api_host_port
+      key: whisk_internal_api_host_port
 - name: "WHISK_API_HOST_NAME"
   valueFrom:
     configMapKeyRef:
       name: {{ .Release.Name }}-whisk.config
-      key: whisk_api_host_name
+      key: whisk_internal_api_host_name
 {{- end -}}
 
 {{/* Environment variables required for invoker containerpool/containerfactory configuration */}}
diff --git a/helm/openwhisk/templates/apigateway-pod.yaml b/helm/openwhisk/templates/apigateway-pod.yaml
index 13b3ac3..8db4b31 100644
--- a/helm/openwhisk/templates/apigateway-pod.yaml
+++ b/helm/openwhisk/templates/apigateway-pod.yaml
@@ -65,9 +65,9 @@
             valueFrom:
               configMapKeyRef:
                 name: {{ .Release.Name }}-whisk.config
-                key: whisk_api_host_url
+                key: whisk_external_api_host_url
           - name: "BACKEND_HOST"
             valueFrom:
               configMapKeyRef:
                 name: {{ .Release.Name }}-whisk.config
-                key: whisk_api_host_url
+                key: whisk_internal_api_host_url
diff --git a/helm/openwhisk/templates/controller-pod.yaml b/helm/openwhisk/templates/controller-pod.yaml
index 28a9756..6681370 100644
--- a/helm/openwhisk/templates/controller-pod.yaml
+++ b/helm/openwhisk/templates/controller-pod.yaml
@@ -131,7 +131,7 @@
 
         # specific controller arguments
         - name: "CONTROLLER_OPTS"
-          value: "{{ .Values.controller.options }} {{ if .Values.controller.lean }} {{ include "openwhisk.invoker.add_opts" . }} {{ end }}"
+          value: "{{ .Values.controller.options }} {{ if .Values.controller.lean }} {{ .Values.invoker.options }} {{ include "openwhisk.invoker.add_opts" . }} {{ end }}"
 
         # action runtimes
         - name: "RUNTIMES_MANIFEST"
diff --git a/helm/openwhisk/templates/frontdoor-ingress.yaml b/helm/openwhisk/templates/frontdoor-ingress.yaml
index e00a023..bbdf68c 100644
--- a/helm/openwhisk/templates/frontdoor-ingress.yaml
+++ b/helm/openwhisk/templates/frontdoor-ingress.yaml
@@ -16,7 +16,7 @@
 #
 
 {{- if eq .Values.whisk.ingress.type "Standard" }}
-apiVersion: extensions/v1beta1
+apiVersion: networking.k8s.io/v1
 kind: Ingress
 metadata:
   name: {{ .Release.Name }}-ingress
@@ -31,7 +31,7 @@
   tls:
   - hosts:
     - {{ .Values.whisk.ingress.domain }}
-    {{- if .Values.whisk.ingress.tls.secretenabled }}
+    {{- if ne .Values.whisk.ingress.tls.secretname "" }}
     secretName: {{ .Values.whisk.ingress.tls.secretname | quote }}
     {{- end }}
   {{- end }}
@@ -40,30 +40,48 @@
     http:
       paths:
       - path: /api/v1/web
+        pathType: Prefix
         backend:
-          serviceName: {{ .Release.Name }}-controller
-          servicePort: http
+          service:
+            name: {{ .Release.Name }}-controller
+            port:
+              name: http
       - path: /api/v1
+        pathType: Prefix
         backend:
-          serviceName: {{ .Release.Name }}-controller
-          servicePort: http
+          service:
+            name: {{ .Release.Name }}-controller
+            port:
+              name: http
       # API GW generated API invocation
       - path: /api
+        pathType: Prefix
         backend:
-          serviceName: {{ .Release.Name }}-apigateway
-          servicePort: mgmt
+          service:
+            name: {{ .Release.Name }}-apigateway
+            port:
+              name: mgmt
       # API GW health status
       - path: /v1/health-check
+        pathType: Prefix
         backend:
-          serviceName: {{ .Release.Name }}-apigateway
-          servicePort: api
+          service:
+            name: {{ .Release.Name }}-apigateway
+            port:
+              name: api
       # API management REST APIs
       - path: /v2
+        pathType: Prefix
         backend:
-          serviceName: {{ .Release.Name }}-apigateway
-          servicePort: api
+          service:
+            name: {{ .Release.Name }}-apigateway
+            port:
+              name: api
       - path: /
+        pathType: Prefix
         backend:
-          serviceName: {{ .Release.Name }}-nginx
-          servicePort: http
+          service:
+            name: {{ .Release.Name }}-nginx
+            port:
+              name: http
 {{- end }}
diff --git a/helm/openwhisk/templates/frontdoor-routes.yaml b/helm/openwhisk/templates/frontdoor-routes.yaml
deleted file mode 100644
index 305e23b..0000000
--- a/helm/openwhisk/templates/frontdoor-routes.yaml
+++ /dev/null
@@ -1,114 +0,0 @@
-#
-# 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.
-#
-
-{{- if eq .Values.whisk.ingress.type "OpenShift" }}
-apiVersion: route.openshift.io/v1
-kind: Route
-metadata:
-  name: {{ .Release.Name }}-route-c1
-spec:
-  host: {{ .Values.whisk.ingress.domain }}
-  path: /api/v1/web
-  port:
-    targetPort: http
-  tls:
-    termination: edge
-  to:
-    kind: Service
-    name: {{ .Release.Name }}-controller
-    weight: 100
----
-apiVersion: route.openshift.io/v1
-kind: Route
-metadata:
-  name: {{ .Release.Name }}-route-c2
-spec:
-  host: {{ .Values.whisk.ingress.domain }}
-  path: /api/v1
-  port:
-    targetPort: http
-  tls:
-    termination: edge
-  to:
-    kind: Service
-    name: {{ .Release.Name }}-controller
-    weight: 100
----
-apiVersion: route.openshift.io/v1
-kind: Route
-metadata:
-  name: {{ .Release.Name }}-route-a1
-spec:
-  host: {{ .Values.whisk.ingress.domain }}
-  path: /api
-  port:
-    targetPort: http
-  tls:
-    termination: edge
-  to:
-    kind: Service
-    name: {{ .Release.Name }}-apigateway
-    weight: 100
----
-apiVersion: route.openshift.io/v1
-kind: Route
-metadata:
-  name: {{ .Release.Name }}-route-a2
-spec:
-  host: {{ .Values.whisk.ingress.domain }}
-  path: /v1/health-check
-  port:
-    targetPort: http
-  tls:
-    termination: edge
-  to:
-    kind: Service
-    name: {{ .Release.Name }}-apigateway
-    weight: 100
----
-apiVersion: route.openshift.io/v1
-kind: Route
-metadata:
-  name: {{ .Release.Name }}-route-a3
-spec:
-  host: {{ .Values.whisk.ingress.domain }}
-  path: /v2
-  port:
-    targetPort: http
-  tls:
-    termination: edge
-  to:
-    kind: Service
-    name: {{ .Release.Name }}-apigateway
-    weight: 100
----
-apiVersion: route.openshift.io/v1
-kind: Route
-metadata:
-  name: {{ .Release.Name }}-route-r
-spec:
-  host: {{ .Values.whisk.ingress.domain }}
-  path: /
-  port:
-    targetPort: http
-  tls:
-    termination: edge
-  to:
-    kind: Service
-    name: {{ .Release.Name }}-controller
-    weight: 100
-{{- end }}
diff --git a/helm/openwhisk/templates/gen-certs-cm.yaml b/helm/openwhisk/templates/gen-certs-cm.yaml
index 3c69191..65f9038 100644
--- a/helm/openwhisk/templates/gen-certs-cm.yaml
+++ b/helm/openwhisk/templates/gen-certs-cm.yaml
@@ -15,7 +15,7 @@
 # limitations under the License.
 #
 
-{{- if not (eq .Values.whisk.ingress.type "OpenShift") }}
+{{- if or (eq .Values.whisk.ingress.type "NodePort") (eq .Values.whisk.ingress.type "LoadBalancer") }}
 apiVersion: v1
 kind: ConfigMap
 metadata:
diff --git a/helm/openwhisk/templates/gen-certs-job.yaml b/helm/openwhisk/templates/gen-certs-job.yaml
index 52023cb..e5d1483 100644
--- a/helm/openwhisk/templates/gen-certs-job.yaml
+++ b/helm/openwhisk/templates/gen-certs-job.yaml
@@ -15,7 +15,7 @@
 # limitations under the License.
 #
 
-{{- if not (eq .Values.whisk.ingress.type "OpenShift") }}
+{{- if or (eq .Values.whisk.ingress.type "NodePort") (eq .Values.whisk.ingress.type "LoadBalancer") }}
 {{- if not .Values.nginx.certificate.external }}
 apiVersion: batch/v1
 kind: Job
@@ -56,6 +56,6 @@
           valueFrom:
             configMapKeyRef:
               name: {{ .Release.Name }}-whisk.config
-              key: whisk_api_host_name
+              key: whisk_external_api_host_name
 {{- end }}
 {{- end }}
diff --git a/helm/openwhisk/templates/install-packages-job.yaml b/helm/openwhisk/templates/install-packages-job.yaml
index 71b2d37..fc7cb30 100644
--- a/helm/openwhisk/templates/install-packages-job.yaml
+++ b/helm/openwhisk/templates/install-packages-job.yaml
@@ -37,7 +37,9 @@
         configMap:
           name: {{ .Release.Name }}-install-packages-cm
       initContainers:
-{{- if not .Values.controller.lean }}
+{{- if .Values.controller.lean }}
+{{ include "openwhisk.readiness.waitForController" . | indent 6 }}
+{{- else }}
 {{ include "openwhisk.readiness.waitForHealthyInvoker" . | indent 6 }}
 {{ end }}
 {{ include "openwhisk.docker.imagePullSecrets" . | indent 6 }}
@@ -60,12 +62,12 @@
             valueFrom:
               configMapKeyRef:
                 name: {{ .Release.Name }}-whisk.config
-                key: whisk_api_host_nameAndPort
+                key: whisk_internal_api_host_nameAndPort
           - name: "WHISK_API_HOST_URL"
             valueFrom:
               configMapKeyRef:
                 name: {{ .Release.Name }}-whisk.config
-                key: whisk_api_host_url
+                key: whisk_internal_api_host_url
           # apigateway configuration (for installing routemgmt actions)
           - name: "WHISK_SYSTEM_NAMESPACE"
             valueFrom:
diff --git a/helm/openwhisk/templates/nginx-cm.yaml b/helm/openwhisk/templates/nginx-cm.yaml
index 77240d1..e25ca36 100644
--- a/helm/openwhisk/templates/nginx-cm.yaml
+++ b/helm/openwhisk/templates/nginx-cm.yaml
@@ -15,7 +15,6 @@
 # limitations under the License.
 #
 
-{{- if ne .Values.whisk.ingress.type "OpenShift" }}
 apiVersion: v1
 kind: ConfigMap
 metadata:
@@ -59,13 +58,16 @@
 
       server {
         listen 80;
+{{- if or (eq .Values.whisk.ingress.type "NodePort") (eq .Values.whisk.ingress.type "LoadBalancer") }}
         listen 443 default ssl;
+{{- end }}
 
         # match namespace, note while OpenWhisk allows a richer character set for a
         # namespace, not all those characters are permitted in the (sub)domain name;
         # if namespace does not match, no vanity URL rewriting takes place.
         server_name ~^(?<namespace>[0-9a-zA-Z-]+)\.{{ .Values.whisk.ingress.apiHostName }}$;
 
+{{- if or (eq .Values.whisk.ingress.type "NodePort") (eq .Values.whisk.ingress.type "LoadBalancer") }}
         ssl_session_cache    shared:SSL:1m;
         ssl_session_timeout  10m;
         ssl_certificate      /etc/nginx/certs/tls.crt;
@@ -81,6 +83,7 @@
         ssl_prefer_server_ciphers on;
         proxy_ssl_session_reuse on;
         proxy_ssl_verify off;
+{{- end }}
 
         # Hack to convince nginx to dynamically resolve the dns entries.
         resolver {{ .Values.k8s.dns }};
@@ -169,4 +172,3 @@
         }
       }
     }
-{{- end }}
diff --git a/helm/openwhisk/templates/nginx-pod.yaml b/helm/openwhisk/templates/nginx-pod.yaml
index ffdf0b1..5b040d6 100644
--- a/helm/openwhisk/templates/nginx-pod.yaml
+++ b/helm/openwhisk/templates/nginx-pod.yaml
@@ -15,7 +15,6 @@
 # limitations under the License.
 #
 
-{{- if ne .Values.whisk.ingress.type "OpenShift" }}
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -48,6 +47,7 @@
       {{- end }}
 
       volumes:
+      {{- if or (eq .Values.whisk.ingress.type "NodePort") (eq .Values.whisk.ingress.type "LoadBalancer") }}
       {{- if .Values.nginx.certificate.external }}
       - name: nginx-certs
         configMap:
@@ -57,6 +57,7 @@
         secret:
           secretName: {{ .Release.Name }}-nginx
       {{- end }}
+      {{- end }}
       - name: nginx-conf
         configMap:
           name: {{ .Release.Name }}-nginx
@@ -73,14 +74,15 @@
         ports:
         - name: http
           containerPort: {{ .Values.nginx.httpPort }}
-        - name: http-api
+        - name: https
           containerPort: {{ .Values.nginx.httpsPort }}
         volumeMounts:
         - name: nginx-conf
           mountPath: "/etc/nginx/nginx.conf"
           subPath: "nginx.conf"
+        {{- if or (eq .Values.whisk.ingress.type "NodePort") (eq .Values.whisk.ingress.type "LoadBalancer") }}
         - name: nginx-certs
           mountPath: "/etc/nginx/certs"
+        {{- end }}
         - name: logs
           mountPath: "/logs"
-{{- end }}
diff --git a/helm/openwhisk/templates/nginx-svc.yaml b/helm/openwhisk/templates/nginx-svc.yaml
index e67bc61..b82b274 100644
--- a/helm/openwhisk/templates/nginx-svc.yaml
+++ b/helm/openwhisk/templates/nginx-svc.yaml
@@ -15,7 +15,6 @@
 # limitations under the License.
 #
 
-{{- if ne .Values.whisk.ingress.type "OpenShift" }}
 apiVersion: v1
 kind: Service
 metadata:
@@ -40,12 +39,14 @@
   ports:
     - port: {{ .Values.nginx.httpPort }}
       name: http
+      targetPort: http
     - port: {{ .Values.nginx.httpsPort }}
       {{- if eq .Values.whisk.ingress.type "NodePort" }}
       nodePort: {{ .Values.nginx.httpsNodePort }}
       {{- end }}
-      name: https-api
+      name: https
       {{- if eq .Values.whisk.ingress.awsSSL "true"}}
       targetPort: http
+      {{- else }}
+      targetPort: https
       {{- end}}
-{{- end }}
diff --git a/helm/openwhisk/templates/ow-whisk-cm.yaml b/helm/openwhisk/templates/ow-whisk-cm.yaml
index 2e20623..27b614d 100644
--- a/helm/openwhisk/templates/ow-whisk-cm.yaml
+++ b/helm/openwhisk/templates/ow-whisk-cm.yaml
@@ -26,24 +26,21 @@
   whisk_info_buildNo: {{ .Values.whisk.versions.openwhisk.buildNo | quote }}
   whisk_cli_version_tag: {{ .Values.whisk.versions.openwhiskCli.tag | quote }}
   whisk_system_namespace: {{ .Values.whisk.systemNameSpace | quote }}
-{{- if eq .Values.whisk.ingress.type "LoadBalancer" }}
-{{- if eq .Values.whisk.ingress.awsSSL "true" }}
-  whisk_api_host_proto: "http"
-  whisk_api_host_port: {{ .Values.nginx.httpPort | quote }}
-  whisk_api_host_name: "{{ .Release.Name }}-nginx.{{ .Release.Namespace }}.svc.{{ .Values.k8s.domain }}"
-  whisk_api_host_nameAndPort: "{{ .Release.Name }}-nginx.{{ .Release.Namespace }}.svc.{{ .Values.k8s.domain }}:{{ .Values.nginx.httpPort }}"
-  whisk_api_host_url: "http://{{ .Release.Name }}-nginx.{{ .Release.Namespace }}.svc.{{ .Values.k8s.domain }}:{{ .Values.nginx.httpPort }}"
+  whisk_external_api_host_proto: {{ .Values.whisk.ingress.apiHostProto | quote }}
+  whisk_external_api_host_port: {{ .Values.whisk.ingress.apiHostPort | quote }}
+  whisk_external_api_host_name: {{ .Values.whisk.ingress.apiHostName | quote }}
+  whisk_external_api_host_nameAndPort: "{{ .Values.whisk.ingress.apiHostName }}:{{ .Values.whisk.ingress.apiHostPort }}"
+  whisk_external_api_host_url: "{{ .Values.whisk.ingress.apiHostProto }}://{{ .Values.whisk.ingress.apiHostName }}:{{ .Values.whisk.ingress.apiHostPort }}"
+{{- if .Values.whisk.ingress.useInternally }}
+  whisk_internal_api_host_proto: {{ .Values.whisk.ingress.apiHostProto | quote }}
+  whisk_internal_api_host_port: {{ .Values.whisk.ingress.apiHostPort | quote }}
+  whisk_internal_api_host_name: {{ .Values.whisk.ingress.apiHostName | quote }}
+  whisk_internal_api_host_nameAndPort: "{{ .Values.whisk.ingress.apiHostName }}:{{ .Values.whisk.ingress.apiHostPort }}"
+  whisk_internal_api_host_url: "{{ .Values.whisk.ingress.apiHostProto }}://{{ .Values.whisk.ingress.apiHostName }}:{{ .Values.whisk.ingress.apiHostPort }}"
 {{- else }}
-  whisk_api_host_proto: "https"
-  whisk_api_host_port: {{ .Values.nginx.httpsPort | quote }}
-  whisk_api_host_name: "{{ .Release.Name }}-nginx.{{ .Release.Namespace }}.svc.{{ .Values.k8s.domain }}"
-  whisk_api_host_nameAndPort: "{{ .Release.Name }}-nginx.{{ .Release.Namespace }}.svc.{{ .Values.k8s.domain }}:{{ .Values.nginx.httpsPort }}"
-  whisk_api_host_url: "https://{{ .Release.Name }}-nginx.{{ .Release.Namespace }}.svc.{{ .Values.k8s.domain }}:{{ .Values.nginx.httpsPort }}"
-{{- end }}
-{{- else }}
-  whisk_api_host_proto: {{ .Values.whisk.ingress.apiHostProto | quote }}
-  whisk_api_host_port: {{ .Values.whisk.ingress.apiHostPort | quote }}
-  whisk_api_host_name: {{ .Values.whisk.ingress.apiHostName | quote }}
-  whisk_api_host_nameAndPort: "{{ .Values.whisk.ingress.apiHostName }}:{{ .Values.whisk.ingress.apiHostPort }}"
-  whisk_api_host_url: "{{ .Values.whisk.ingress.apiHostProto }}://{{ .Values.whisk.ingress.apiHostName }}:{{ .Values.whisk.ingress.apiHostPort }}"
+  whisk_internal_api_host_proto: "http"
+  whisk_internal_api_host_port: {{ .Values.nginx.httpPort | quote }}
+  whisk_internal_api_host_name: "{{ .Release.Name }}-nginx.{{ .Release.Namespace }}.svc.{{ .Values.k8s.domain }}"
+  whisk_internal_api_host_nameAndPort: "{{ .Release.Name }}-nginx.{{ .Release.Namespace }}.svc.{{ .Values.k8s.domain }}:{{ .Values.nginx.httpPort }}"
+  whisk_internal_api_host_url: "http://{{ .Release.Name }}-nginx.{{ .Release.Namespace }}.svc.{{ .Values.k8s.domain }}:{{ .Values.nginx.httpPort }}"
 {{- end }}
diff --git a/helm/openwhisk/templates/provider-alarm-pod.yaml b/helm/openwhisk/templates/provider-alarm-pod.yaml
index 738170a..019a9ee 100644
--- a/helm/openwhisk/templates/provider-alarm-pod.yaml
+++ b/helm/openwhisk/templates/provider-alarm-pod.yaml
@@ -93,12 +93,12 @@
           valueFrom:
             configMapKeyRef:
               name: {{ .Release.Name }}-whisk.config
-              key: whisk_api_host_nameAndPort
+              key: whisk_internal_api_host_nameAndPort
         - name: "ENDPOINT_AUTH"
           valueFrom:
             configMapKeyRef:
               name: {{ .Release.Name }}-whisk.config
-              key: whisk_api_host_nameAndPort
+              key: whisk_internal_api_host_nameAndPort
         volumeMounts:
           - name: alarm-logs
             mountPath: /logs
diff --git a/helm/openwhisk/templates/provider-kafka-pod.yaml b/helm/openwhisk/templates/provider-kafka-pod.yaml
index 2688f42..cb8a82b 100644
--- a/helm/openwhisk/templates/provider-kafka-pod.yaml
+++ b/helm/openwhisk/templates/provider-kafka-pod.yaml
@@ -81,10 +81,10 @@
           valueFrom:
             configMapKeyRef:
               name: {{ .Release.Name }}-whisk.config
-              key: whisk_api_host_nameAndPort
+              key: whisk_internal_api_host_nameAndPort
         - name: "ENDPOINT_AUTH"
           valueFrom:
             configMapKeyRef:
               name: {{ .Release.Name }}-whisk.config
-              key: whisk_api_host_nameAndPort
+              key: whisk_internal_api_host_nameAndPort
 {{- end }}
diff --git a/helm/openwhisk/templates/tests/package-checker-pod.yaml b/helm/openwhisk/templates/tests/package-checker-pod.yaml
index 72432a1..f3f12ab 100644
--- a/helm/openwhisk/templates/tests/package-checker-pod.yaml
+++ b/helm/openwhisk/templates/tests/package-checker-pod.yaml
@@ -52,7 +52,7 @@
         valueFrom:
           configMapKeyRef:
             name: {{ .Release.Name }}-whisk.config
-            key: whisk_api_host_url
+            key: whisk_internal_api_host_url
 
       # Which of the providers was installed (and thus should be checked)?
       - name: "OW_INSTALL_ALARM_PROVIDER"
diff --git a/helm/openwhisk/templates/tests/smoketest-pod.yaml b/helm/openwhisk/templates/tests/smoketest-pod.yaml
index 30f6433..c1a9364 100644
--- a/helm/openwhisk/templates/tests/smoketest-pod.yaml
+++ b/helm/openwhisk/templates/tests/smoketest-pod.yaml
@@ -52,5 +52,5 @@
         valueFrom:
           configMapKeyRef:
             name: {{ .Release.Name }}-whisk.config
-            key: whisk_api_host_url
+            key: whisk_internal_api_host_url
 {{- end }}
diff --git a/helm/openwhisk/templates/tests/systemtest-pod.yaml b/helm/openwhisk/templates/tests/systemtest-pod.yaml
index 032b299..f8c8ca5 100644
--- a/helm/openwhisk/templates/tests/systemtest-pod.yaml
+++ b/helm/openwhisk/templates/tests/systemtest-pod.yaml
@@ -52,7 +52,7 @@
         valueFrom:
           configMapKeyRef:
             name: {{ .Release.Name }}-whisk.config
-            key: whisk_api_host_url
+            key: whisk_internal_api_host_url
       - name: "OW_GIT_TAG_OPENWHISK"
         value: {{ .Values.whisk.versions.openwhisk.gitTag | quote }}
 {{- end }}
diff --git a/helm/openwhisk/values.schema.json b/helm/openwhisk/values.schema.json
index fba7630..4f1d2ff 100644
--- a/helm/openwhisk/values.schema.json
+++ b/helm/openwhisk/values.schema.json
@@ -111,15 +111,16 @@
             "apiHostName": { "type": "string", "description": "The external hostname or IP address used to access the Ingress of your Kubernetes cluster" },
             "apiHostPort": { "type": "integer", "minimum": 0, "description": "The external port used to access the Ingress of your Kubernetes cluster" },
             "apiHostProto": { "type": "string", "enum": ["http", "https"], "description": "The protocol to be used to connect to the Ingress of your Kubernetes cluster" },
-            "type": { "type": "string", "enum": ["NodePort", "Standard", "LoadBalancer", "OpenShift"], "description": "The type of Ingress being deployed" },
+            "type": { "type": "string", "enum": ["NodePort", "Standard", "LoadBalancer"], "description": "The type of Ingress being deployed" },
             "annotations": { "type": "object", "description": "Annotations to add to Ingress resource. Specify as a list of key: value pairs" },
             "domain": { "type": "string", "description": "The Fully Qualified Host Name for the Ingress domain" },
+            "useInternally": { "type": "boolean", "description": "Should the external ingress be used for operations from inside the cluster" },
             "tls": {
               "type": "object",
               "properties": {
                 "enabled": { "type": "boolean" },
-                "secretenabled": { "type": "boolean" },
                 "createsecret": { "type": "boolean" },
+                "secretname": { "type": "string" },
                 "secrettype": { "type": "string" },
                 "crt": { "type": "string" },
                 "key": { "type": "string" }
diff --git a/helm/openwhisk/values.yaml b/helm/openwhisk/values.yaml
index e4ca1a3..050a008 100644
--- a/helm/openwhisk/values.yaml
+++ b/helm/openwhisk/values.yaml
@@ -26,7 +26,6 @@
 #   docs/k8s-docker-for-mac.md
 #   docs/k8s-aws.md
 #   docs/k8s-ibm-public.md
-#   docs/k8s-ibm-private.md
 #   docs/k8s-google.md
 #   docs/k8s-diy.md (for do-it-yourself clusters).
 #
@@ -48,7 +47,6 @@
   # See the "Configuring OpenWhisk section" of the docs/k8s-*.md that matches
   # your cluster type for details on what values to provide and how to get them.
   ingress:
-    awsSSL: "false"
     apiHostName: ""
     apiHostPort: 31001
     apiHostProto: "https"
@@ -56,9 +54,10 @@
     annotations:
       nginx.ingress.kubernetes.io/proxy-body-size: "50m"
     domain: "domain"
+    awsSSL: "false"
+    useInternally: false
     tls:
       enabled: false
-      secretenabled: false
       createsecret: false
       secretname: "ow-ingress-tls-secret"
       secrettype: "type"
diff --git a/tools/travis/deploy-chart.sh b/tools/travis/deploy-chart.sh
index 9778e51..e4bcc58 100755
--- a/tools/travis/deploy-chart.sh
+++ b/tools/travis/deploy-chart.sh
@@ -171,35 +171,18 @@
 # Default timeout limit to 60 steps
 TIMEOUT_STEP_LIMIT=${TIMEOUT_STEP_LIMIT:=60}
 
-# Label nodes for affinity.
-# For DockerContainerFactory, at least one must be labeled as an invoker.
-echo "Labeling nodes with openwhisk-role assignments"
-kubectl label nodes kind-worker openwhisk-role=core
-kubectl label nodes kind-worker2 openwhisk-role=invoker
-
 # Create namespace
 kubectl create namespace openwhisk
 
-# Configure a NodePort Ingress assuming kind conventions.
-# Use kind-worker as the ingress, since we labeled it as our core node above.
-# (But using kind-worker2 would also work because Kubernetes
-#  exposes the same NodePort service on all worker nodes.)
-WSK_PORT=31001
-WSK_HOST=$(kubectl describe node kind-worker | grep InternalIP: | awk '{print $2}')
-if [ -z "$WSK_HOST" ]; then
-  echo "FAILED! Could not determine value for WSK_HOST"
-  exit 1
-fi
+# Default to kind conventions of using localhost:31001
+WSK_PORT=${WSK_PORT:=31001}
+WSK_HOST=${WSK_HOST:=localhost}
 
 # Deploy OpenWhisk using Helm
 cd $ROOTDIR
 
-cat > mycluster.yaml <<EOF
+cat > ow-config.yaml <<EOF
 whisk:
-  ingress:
-    type: NodePort
-    apiHostName: $WSK_HOST
-    apiHostPort: $WSK_PORT
   runtimes: "runtimes-minimal-travis.json"
   testing:
     includeSystemTests: $OW_INCLUDE_SYSTEM_TESTS
@@ -208,9 +191,6 @@
   containerFactory:
     impl: $OW_CONTAINER_FACTORY
 
-nginx:
-  httpsNodePort: $WSK_PORT
-
 controller:
   lean: ${OW_LEAN_MODE:-false}
 
@@ -218,11 +198,11 @@
   userMetricsEnabled: true
 EOF
 
-echo "Contents of mycluster.yaml are:"
-cat mycluster.yaml
+echo "Contents of ow-config.yaml are:"
+cat ow-config.yaml
 
-helm lint helm/openwhisk -n openwhisk -f mycluster.yaml || exit 1
-helm install ow4travis helm/openwhisk -n openwhisk -f mycluster.yaml || exit 1
+helm lint helm/openwhisk -n openwhisk -f deploy/kind/mycluster.yaml -f ow-config.yaml || exit 1
+helm install ow4travis helm/openwhisk -n openwhisk -f deploy/kind/mycluster.yaml -f ow-config.yaml || exit 1
 
 # Wait for controller to be up
 statefulsetHealthCheck "ow4travis-controller"