You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pulsar.apache.org by si...@apache.org on 2020/04/21 05:57:10 UTC

[pulsar-helm-chart] 31/34: Improve Helm chart (#6673)

This is an automated email from the ASF dual-hosted git repository.

sijie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-helm-chart.git

commit f64c396906e9f99999ec14bd3ac7336e6609a86a
Author: Sijie Guo <si...@apache.org>
AuthorDate: Wed Apr 8 11:20:01 2020 -0700

    Improve Helm chart (#6673)
    
    * Improve Helm chart
    
    - Support TLS for all components
    - Support Authentication & Authorization (TLS)
    - Add CI for different cluster settings
---
 pulsar/Chart.yaml => examples/values-cs.yaml       |  31 +-
 .../values-jwt-asymmetric.yaml                     |  23 +-
 .../values-jwt-symmetric.yaml                      |  23 +-
 .../values-local-cluster.yaml                      |  23 +-
 pulsar/Chart.yaml => examples/values-local-pv.yaml |   7 +-
 pulsar/Chart.yaml => examples/values-minikube.yaml |  36 +-
 .../values-no-persistence.yaml                     |  14 +-
 pulsar/Chart.yaml => examples/values-one-node.yaml |  40 +-
 pulsar/Chart.yaml => examples/values-pulsar.yaml   |  36 +-
 pulsar/Chart.yaml => examples/values-tls.yaml      |  20 +-
 hack/common.sh                                     | 135 ++++
 hack/kind-cluster-build.sh                         | 251 +++++++
 pulsar/Chart.yaml                                  |   7 +
 pulsar/templates/_autorecovery.tpl                 |  80 +++
 pulsar/templates/_bookkeeper.tpl                   | 121 ++++
 pulsar/templates/_broker.tpl                       |  76 +++
 pulsar/templates/_helpers.tpl                      |  36 ++
 pulsar/templates/_toolset.tpl                      |  69 ++
 pulsar/templates/_zookeeper.tpl                    |  34 +
 pulsar/templates/autorecovery-configmap.yaml       |  19 +-
 pulsar/templates/autorecovery-deployment.yaml      | 105 ---
 ...on-configmap.yaml => autorecovery-service.yaml} |  22 +-
 pulsar/templates/autorecovery-statefulset.yaml     | 124 ++++
 pulsar/templates/bastion-deployment.yaml           |  80 ---
 .../templates/bookkeeper-cluster-initialize.yaml   |  71 ++
 pulsar/templates/bookkeeper-configmap.yaml         |  22 +-
 pulsar/templates/bookkeeper-pdb.yaml               |  11 +-
 pulsar/templates/bookkeeper-service.yaml           |  16 +-
 pulsar/templates/bookkeeper-statefulset.yaml       | 114 ++--
 pulsar/templates/bookkeeper-storageclass.yaml      |  21 +-
 ...-rbac.yaml => broker-cluster-role-binding.yaml} |  69 +-
 pulsar/templates/broker-configmap.yaml             | 125 +++-
 pulsar/templates/broker-deployment.yaml            | 131 ----
 pulsar/templates/broker-pdb.yaml                   |  11 +-
 .../broker-service-account.yaml}                   |  13 +-
 pulsar/templates/broker-service.yaml               |  22 +-
 pulsar/templates/broker-statefulset.yaml           | 236 +++++++
 ...nfigmap.yaml => function-worker-configmap.yaml} |  15 +-
 pulsar/templates/grafana-deployment.yaml           |  42 +-
 pulsar/templates/grafana-service.yaml              |  21 +-
 pulsar/templates/keytool.yaml                      |  98 +++
 pulsar/templates/prometheus-configmap.yaml         |   8 +-
 pulsar/templates/prometheus-deployment.yaml        |  28 +-
 pulsar/templates/prometheus-pvc.yaml               |   8 +-
 pulsar/templates/prometheus-rbac.yaml              |   4 +-
 pulsar/templates/prometheus-service.yaml           |  13 +-
 pulsar/templates/prometheus-storageclass.yaml      |  10 +-
 pulsar/templates/proxy-configmap.yaml              |  65 +-
 pulsar/templates/proxy-deployment.yaml             | 124 ----
 pulsar/templates/proxy-pdb.yaml                    |  11 +-
 pulsar/templates/proxy-service.yaml                |  29 +-
 pulsar/templates/proxy-statefulset.yaml            | 234 +++++++
 pulsar/templates/pulsar-cluster-initialize.yaml    | 102 +++
 pulsar/templates/pulsar-manager-admin-secret.yaml  |   2 +-
 pulsar/templates/pulsar-manager-configmap.yaml     |   8 +-
 pulsar/templates/pulsar-manager-deployment.yaml    |  21 +-
 pulsar/templates/pulsar-manager-service.yaml       |  12 +-
 pulsar/templates/tls-cert-internal-issuer.yaml     |  62 ++
 pulsar/templates/tls-certs-internal.yaml           | 247 +++++++
 pulsar/templates/toolset-configmap.yaml            |  70 ++
 ...okeeper-configmap.yaml => toolset-service.yaml} |  21 +-
 pulsar/templates/toolset-statefulset.yaml          | 108 ++++
 pulsar/templates/zookeeper-configmap.yaml          |  17 +-
 pulsar/templates/zookeeper-metadata.yaml           |  62 --
 pulsar/templates/zookeeper-pdb.yaml                |  12 +-
 pulsar/templates/zookeeper-service.yaml            |  23 +-
 pulsar/templates/zookeeper-statefulset.yaml        | 113 +++-
 pulsar/templates/zookeeper-storageclass.yaml       |  15 +-
 pulsar/values-mini.yaml                            | 528 ---------------
 pulsar/values.yaml                                 | 718 ++++++++++++++++-----
 scripts/cert-manager/install-cert-manager.sh       |  55 ++
 scripts/pulsar/clean_tls.sh                        | 115 ++++
 scripts/pulsar/cleanup_helm_release.sh             |  87 +++
 scripts/pulsar/common.sh                           |  73 +++
 scripts/pulsar/common_auth.sh                      |  66 ++
 scripts/pulsar/generate_token.sh                   | 121 ++++
 scripts/pulsar/generate_token_secret_key.sh        | 109 ++++
 scripts/pulsar/get_token.sh                        |  95 +++
 scripts/pulsar/prepare_helm_release.sh             | 155 +++++
 scripts/pulsar/upload_tls.sh                       | 135 ++++
 80 files changed, 4525 insertions(+), 1611 deletions(-)

diff --git a/pulsar/Chart.yaml b/examples/values-cs.yaml
similarity index 63%
copy from pulsar/Chart.yaml
copy to examples/values-cs.yaml
index f4cefc0..1611c36 100644
--- a/pulsar/Chart.yaml
+++ b/examples/values-cs.yaml
@@ -17,8 +17,29 @@
 # under the License.
 #
 
-apiVersion: v1
-appVersion: "1.0"
-description: Apache Pulsar Helm chart for Kubernetes
-name: pulsar
-version: 1.0.0
+metadataPrefix: "/configuration-store"
+
+## start
+components:
+  # zookeeper
+  zookeeper: true
+  # bookkeeper
+  bookkeeper: false
+  # bookkeeper - autorecovery
+  autorecovery: false
+  # broker
+  broker: false
+  # proxy
+  proxy: false
+  # toolset
+  toolset: false
+  # pulsar manager
+  pulsar_manager: false
+
+monitoring:
+  # monitoring - prometheus
+  prometheus: false
+  # monitoring - grafana
+  grafana: false
+  # monitoring - node_exporter
+  node_exporter: false
\ No newline at end of file
diff --git a/pulsar/Chart.yaml b/examples/values-jwt-asymmetric.yaml
similarity index 58%
copy from pulsar/Chart.yaml
copy to examples/values-jwt-asymmetric.yaml
index f4cefc0..1948894 100644
--- a/pulsar/Chart.yaml
+++ b/examples/values-jwt-asymmetric.yaml
@@ -17,8 +17,21 @@
 # under the License.
 #
 
-apiVersion: v1
-appVersion: "1.0"
-description: Apache Pulsar Helm chart for Kubernetes
-name: pulsar
-version: 1.0.0
+auth:
+  authentication:
+    enabled: true
+    provider: "jwt"
+    jwt:
+      # Enable JWT authentication
+      # If the token is generated by a secret key, set the usingSecretKey as true.
+      # If the token is generated by a private key, set the usingSecretKey as false.
+      usingSecretKey: false
+  authorization:
+    enabled: true
+  superUsers:
+    # broker to broker communication
+    broker: "broker-admin"
+    # proxy to broker communication
+    proxy: "proxy-admin"
+    # pulsar-admin client to broker/proxy communication
+    client: "admin"
\ No newline at end of file
diff --git a/pulsar/Chart.yaml b/examples/values-jwt-symmetric.yaml
similarity index 58%
copy from pulsar/Chart.yaml
copy to examples/values-jwt-symmetric.yaml
index f4cefc0..22b05ce 100644
--- a/pulsar/Chart.yaml
+++ b/examples/values-jwt-symmetric.yaml
@@ -17,8 +17,21 @@
 # under the License.
 #
 
-apiVersion: v1
-appVersion: "1.0"
-description: Apache Pulsar Helm chart for Kubernetes
-name: pulsar
-version: 1.0.0
+auth:
+  authentication:
+    enabled: true
+    provider: "jwt"
+    jwt:
+      # Enable JWT authentication
+      # If the token is generated by a secret key, set the usingSecretKey as true.
+      # If the token is generated by a private key, set the usingSecretKey as false.
+      usingSecretKey: true
+  authorization:
+    enabled: true
+  superUsers:
+    # broker to broker communication
+    broker: "broker-admin"
+    # proxy to broker communication
+    proxy: "proxy-admin"
+    # pulsar-admin client to broker/proxy communication
+    client: "admin"
\ No newline at end of file
diff --git a/pulsar/Chart.yaml b/examples/values-local-cluster.yaml
similarity index 66%
copy from pulsar/Chart.yaml
copy to examples/values-local-cluster.yaml
index f4cefc0..19eac4a 100644
--- a/pulsar/Chart.yaml
+++ b/examples/values-local-cluster.yaml
@@ -17,8 +17,21 @@
 # under the License.
 #
 
-apiVersion: v1
-appVersion: "1.0"
-description: Apache Pulsar Helm chart for Kubernetes
-name: pulsar
-version: 1.0.0
+metadataPrefix: "/cluster1"
+
+pulsar_metadata:
+  configurationStore: pulsar-cs-zookeeper
+  configurationStoreMetadataPrefix: "/configuration-store"
+
+## disable pulsar-manager
+components:
+  pulsar_manager: true
+
+## disable monitoring stack
+monitoring:
+  # monitoring - prometheus
+  prometheus: false
+  # monitoring - grafana
+  grafana: false
+  # monitoring - node_exporter
+  node_exporter: false
\ No newline at end of file
diff --git a/pulsar/Chart.yaml b/examples/values-local-pv.yaml
similarity index 87%
copy from pulsar/Chart.yaml
copy to examples/values-local-pv.yaml
index f4cefc0..c393763 100644
--- a/pulsar/Chart.yaml
+++ b/examples/values-local-pv.yaml
@@ -17,8 +17,5 @@
 # under the License.
 #
 
-apiVersion: v1
-appVersion: "1.0"
-description: Apache Pulsar Helm chart for Kubernetes
-name: pulsar
-version: 1.0.0
+volumes:
+  local_storage: true
\ No newline at end of file
diff --git a/pulsar/Chart.yaml b/examples/values-minikube.yaml
similarity index 57%
copy from pulsar/Chart.yaml
copy to examples/values-minikube.yaml
index f4cefc0..2cd2a22 100644
--- a/pulsar/Chart.yaml
+++ b/examples/values-minikube.yaml
@@ -17,8 +17,34 @@
 # under the License.
 #
 
-apiVersion: v1
-appVersion: "1.0"
-description: Apache Pulsar Helm chart for Kubernetes
-name: pulsar
-version: 1.0.0
+## deployed withh emptyDir
+volumes:
+  persistence: false
+
+# disabled AntiAffinity
+affinity:
+  anti_affinity: false
+
+# disable auto recovery
+components:
+  autorecovery: false
+
+zookeeper:
+  replicaCount: 1
+
+bookkeeper:
+  replicaCount: 1
+
+broker:
+  replicaCount: 1
+  configData:
+    ## Enable `autoSkipNonRecoverableData` since bookkeeper is running
+    ## without persistence
+    autoSkipNonRecoverableData: "true"
+    # storage settings
+    managedLedgerDefaultEnsembleSize: "1"
+    managedLedgerDefaultWriteQuorum: "1"
+    managedLedgerDefaultAckQuorum: "1"
+
+proxy:
+  replicaCount: 1
\ No newline at end of file
diff --git a/pulsar/Chart.yaml b/examples/values-no-persistence.yaml
similarity index 79%
copy from pulsar/Chart.yaml
copy to examples/values-no-persistence.yaml
index f4cefc0..a613366 100644
--- a/pulsar/Chart.yaml
+++ b/examples/values-no-persistence.yaml
@@ -17,8 +17,12 @@
 # under the License.
 #
 
-apiVersion: v1
-appVersion: "1.0"
-description: Apache Pulsar Helm chart for Kubernetes
-name: pulsar
-version: 1.0.0
+## deployed withh emptyDir
+volumes:
+  persistence: false
+
+## Enable `autoSkipNonRecoverableData` since bookkeeper is running
+## without persistence
+broker:
+  configData:
+    autoSkipNonRecoverableData: "true"
\ No newline at end of file
diff --git a/pulsar/Chart.yaml b/examples/values-one-node.yaml
similarity index 54%
copy from pulsar/Chart.yaml
copy to examples/values-one-node.yaml
index f4cefc0..80a31dd 100644
--- a/pulsar/Chart.yaml
+++ b/examples/values-one-node.yaml
@@ -17,8 +17,38 @@
 # under the License.
 #
 
-apiVersion: v1
-appVersion: "1.0"
-description: Apache Pulsar Helm chart for Kubernetes
-name: pulsar
-version: 1.0.0
+# disabled AntiAffinity
+affinity:
+  anti_affinity: false
+
+images:
+  broker:
+    repository: apachepulsar/pulsar-all
+    tag: 2.5.0
+  functions:
+    repository: apachepulsar/pulsar-all
+    tag: 2.5.0
+
+# disable auto recovery
+components:
+  autorecovery: false
+
+zookeeper:
+  replicaCount: 1
+
+bookkeeper:
+  replicaCount: 1
+
+broker:
+  replicaCount: 1
+  configData:
+    ## Enable `autoSkipNonRecoverableData` since bookkeeper is running
+    ## without persistence
+    autoSkipNonRecoverableData: "true"
+    # storage settings
+    managedLedgerDefaultEnsembleSize: "1"
+    managedLedgerDefaultWriteQuorum: "1"
+    managedLedgerDefaultAckQuorum: "1"
+
+proxy:
+  replicaCount: 1
\ No newline at end of file
diff --git a/pulsar/Chart.yaml b/examples/values-pulsar.yaml
similarity index 57%
copy from pulsar/Chart.yaml
copy to examples/values-pulsar.yaml
index f4cefc0..b1cfb0c 100644
--- a/pulsar/Chart.yaml
+++ b/examples/values-pulsar.yaml
@@ -17,8 +17,34 @@
 # under the License.
 #
 
-apiVersion: v1
-appVersion: "1.0"
-description: Apache Pulsar Helm chart for Kubernetes
-name: pulsar
-version: 1.0.0
+images:
+  zookeeper:
+    repository: apachepulsar/pulsar-all
+    tag: 2.5.0
+  bookie:
+    repository: apachepulsar/pulsar-all
+    tag: 2.5.0
+  autorecovery:
+    repository: apachepulsar/pulsar-all
+    tag: 2.5.0
+  broker:
+    repository: apachepulsar/pulsar-all
+    tag: 2.5.0
+  functions:
+    repository: apachepulsar/pulsar-all
+    tag: 2.5.0
+  proxy:
+    repository: apachepulsar/pulsar-all
+    tag: 2.5.0
+
+bookkeeper:
+  metadata:
+    image:
+      repository: apachepulsar/pulsar-all
+      tag: 2.5.0
+
+
+pulsar_metadata:
+  image:
+    repository: apachepulsar/pulsar-all
+    tag: 2.5.0
diff --git a/pulsar/Chart.yaml b/examples/values-tls.yaml
similarity index 78%
copy from pulsar/Chart.yaml
copy to examples/values-tls.yaml
index f4cefc0..fadab07 100644
--- a/pulsar/Chart.yaml
+++ b/examples/values-tls.yaml
@@ -17,8 +17,18 @@
 # under the License.
 #
 
-apiVersion: v1
-appVersion: "1.0"
-description: Apache Pulsar Helm chart for Kubernetes
-name: pulsar
-version: 1.0.0
+# enable TLS
+tls:
+  enabled: true
+  proxy:
+    enabled: true
+  broker:
+    enabled: true
+  zookeeper:
+    enabled: true
+
+# issue selfsigning certs
+certs:
+  internal_issuer:
+    enabled: true
+    type: selfsigning
\ No newline at end of file
diff --git a/hack/common.sh b/hack/common.sh
new file mode 100755
index 0000000..3d13b06
--- /dev/null
+++ b/hack/common.sh
@@ -0,0 +1,135 @@
+#!/usr/bin/env bash
+#
+# 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 [ -z "$PULSAR_CHART_HOME" ]; then
+    echo "error: PULSAR_CHART_HOME should be initialized"
+    exit 1
+fi
+
+OUTPUT=${PULSAR_CHART_HOME}/output
+OUTPUT_BIN=${OUTPUT}/bin
+KUBECTL_VERSION=1.14.3
+KUBECTL_BIN=$OUTPUT_BIN/kubectl
+HELM_BIN=$OUTPUT_BIN/helm
+HELM_VERSION=3.0.1
+KIND_VERSION=0.6.1
+KIND_BIN=$OUTPUT_BIN/kind
+CR_BIN=$OUTPUT_BIN/cr
+CR_VERSION=1.0.0-beta.1
+
+test -d "$OUTPUT_BIN" || mkdir -p "$OUTPUT_BIN"
+
+ARCH=""
+hack::discoverArch() {
+  ARCH=$(uname -m)
+  case $ARCH in
+    x86) ARCH="386";;
+    x86_64) ARCH="amd64";;
+    i686) ARCH="386";;
+    i386) ARCH="386";;
+  esac
+}
+
+hack::discoverArch
+OS=$(echo `uname`|tr '[:upper:]' '[:lower:]')
+
+function hack::verify_kubectl() {
+    if test -x "$KUBECTL_BIN"; then
+        [[ "$($KUBECTL_BIN version --client --short | grep -o -E '[0-9]+\.[0-9]+\.[0-9]+')" == "$KUBECTL_VERSION" ]]
+        return
+    fi
+    return 1
+}
+
+function hack::ensure_kubectl() {
+    if hack::verify_kubectl; then
+        return 0
+    fi
+    echo "Installing kubectl v$KUBECTL_VERSION..."
+    tmpfile=$(mktemp)
+    trap "test -f $tmpfile && rm $tmpfile" RETURN
+    curl --retry 10 -L -o $tmpfile https://storage.googleapis.com/kubernetes-release/release/v${KUBECTL_VERSION}/bin/${OS}/${ARCH}/kubectl
+    mv $tmpfile $KUBECTL_BIN
+    chmod +x $KUBECTL_BIN
+}
+
+function hack::verify_helm() {
+    if test -x "$HELM_BIN"; then
+        local v=$($HELM_BIN version --short --client | grep -o -E '[0-9]+\.[0-9]+\.[0-9]+')
+        [[ "$v" == "$HELM_VERSION" ]]
+        return
+    fi
+    return 1
+}
+
+function hack::ensure_helm() {
+    if hack::verify_helm; then
+        return 0
+    fi
+    local HELM_URL=https://get.helm.sh/helm-v${HELM_VERSION}-${OS}-${ARCH}.tar.gz
+    curl --retry 10 -L -s "$HELM_URL" | tar --strip-components 1 -C $OUTPUT_BIN -zxf - ${OS}-${ARCH}/helm
+}
+
+function hack::verify_kind() {
+    if test -x "$KIND_BIN"; then
+        [[ "$($KIND_BIN --version 2>&1 | cut -d ' ' -f 3)" == "$KIND_VERSION" ]]
+        return
+    fi
+    return 1
+}
+
+function hack::ensure_kind() {
+    if hack::verify_kind; then
+        return 0
+    fi
+    echo "Installing kind v$KIND_VERSION..."
+    tmpfile=$(mktemp)
+    trap "test -f $tmpfile && rm $tmpfile" RETURN
+    curl --retry 10 -L -o $tmpfile https://github.com/kubernetes-sigs/kind/releases/download/v${KIND_VERSION}/kind-$(uname)-amd64
+    mv $tmpfile $KIND_BIN
+    chmod +x $KIND_BIN
+}
+
+# hack::version_ge "$v1" "$v2" checks whether "v1" is greater or equal to "v2"
+function hack::version_ge() {
+    [ "$(printf '%s\n' "$1" "$2" | sort -V | head -n1)" = "$2" ]
+}
+
+function hack::verify_cr() {
+    if test -x "$CR_BIN"; then
+        return
+    fi
+    return 1
+}
+
+function hack::ensure_cr() {
+    if hack::verify_cr; then
+        $CR_BIN version
+        return 0
+    fi
+    echo "Installing chart-releaser ${CR_VERSION} ..."
+    tmpfile=$(mktemp)
+    trap "test -f $tmpfile && rm $tmpfile" RETURN
+    echo curl --retry 10 -L -o $tmpfile https://github.com/helm/chart-releaser/releases/download/v${CR_VERSION}/chart-releaser_${CR_VERSION}_${OS}_${ARCH}.tar.gz
+    curl --retry 10 -L -o $tmpfile https://github.com/helm/chart-releaser/releases/download/v${CR_VERSION}/chart-releaser_${CR_VERSION}_${OS}_${ARCH}.tar.gz
+    mv $tmpfile $CR_BIN
+    chmod +x $CR_BIN
+    $CR_BIN version
+}
diff --git a/hack/kind-cluster-build.sh b/hack/kind-cluster-build.sh
new file mode 100755
index 0000000..0ae3468
--- /dev/null
+++ b/hack/kind-cluster-build.sh
@@ -0,0 +1,251 @@
+#!/usr/bin/env bash
+#
+# 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.
+#
+
+PULSAR_CHART_HOME=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/.. && pwd)
+cd ${PULSAR_CHART_HOME}
+
+source ${PULSAR_CHART_HOME}/hack/common.sh
+
+hack::ensure_kubectl
+hack::ensure_helm
+
+usage() {
+    cat <<EOF
+This script use kind to create Kubernetes cluster, about kind please refer: https://kind.sigs.k8s.io/
+Before run this script, please ensure that:
+* have installed docker
+* have installed kind and kind's version == ${KIND_VERSION}
+Options:
+       -h,--help               prints the usage message
+       -n,--name               name of the Kubernetes cluster,default value: kind
+       -c,--nodeNum            the count of the cluster nodes,default value: 6
+       -k,--k8sVersion         version of the Kubernetes cluster,default value: v1.12.8
+       -v,--volumeNum          the volumes number of each kubernetes node,default value: 9
+Usage:
+    $0 --name testCluster --nodeNum 4 --k8sVersion v1.12.9
+EOF
+}
+
+while [[ $# -gt 0 ]]
+do
+key="$1"
+
+case $key in
+    -n|--name)
+    clusterName="$2"
+    shift
+    shift
+    ;;
+    -c|--nodeNum)
+    nodeNum="$2"
+    shift
+    shift
+    ;;
+    -k|--k8sVersion)
+    k8sVersion="$2"
+    shift
+    shift
+    ;;
+    -v|--volumeNum)
+    volumeNum="$2"
+    shift
+    shift
+    ;;
+    -h|--help)
+    usage
+    exit 0
+    ;;
+    *)
+    echo "unknown option: $key"
+    usage
+    exit 1
+    ;;
+esac
+done
+
+clusterName=${clusterName:-pulsar-dev}
+nodeNum=${nodeNum:-6}
+k8sVersion=${k8sVersion:-v1.14.10}
+volumeNum=${volumeNum:-9}
+
+echo "clusterName: ${clusterName}"
+echo "nodeNum: ${nodeNum}"
+echo "k8sVersion: ${k8sVersion}"
+echo "volumeNum: ${volumeNum}"
+
+# check requirements
+for requirement in kind docker
+do
+    echo "############ check ${requirement} ##############"
+    if hash ${requirement} 2>/dev/null;then
+        echo "${requirement} have installed"
+    else
+        echo "this script needs ${requirement}, please install ${requirement} first."
+        exit 1
+    fi
+done
+
+echo "############# start create cluster:[${clusterName}] #############"
+workDir=${HOME}/kind/${clusterName}
+mkdir -p ${workDir}
+
+data_dir=${workDir}/data
+
+echo "clean data dir: ${data_dir}"
+if [ -d ${data_dir} ]; then
+    rm -rf ${data_dir}
+fi
+
+configFile=${workDir}/kind-config.yaml
+
+cat <<EOF > ${configFile}
+kind: Cluster
+apiVersion: kind.sigs.k8s.io/v1alpha3
+nodes:
+- role: control-plane
+  extraPortMappings:
+  - containerPort: 5000
+    hostPort: 5000
+    listenAddress: 127.0.0.1
+    protocol: TCP
+EOF
+
+for ((i=0;i<${nodeNum};i++))
+do
+    mkdir -p ${data_dir}/worker${i}
+    cat <<EOF >>  ${configFile}
+- role: worker
+  extraMounts:
+EOF
+    for ((k=1;k<=${volumeNum};k++))
+    do
+        mkdir -p ${data_dir}/worker${i}/vol${k}
+        cat <<EOF >> ${configFile}
+  - containerPath: /mnt/disks/vol${k}
+    hostPath: ${data_dir}/worker${i}/vol${k}
+EOF
+    done
+done
+
+matchedCluster=$(kind get clusters | grep ${clusterName})
+if [[ "${matchedCluster}" == "${clusterName}" ]]; then
+    echo "Kind cluster ${clusterName} already exists"
+    kind delete cluster --name=${clusterName}
+fi
+echo "start to create k8s cluster"
+kind create cluster --config ${configFile} --image kindest/node:${k8sVersion} --name=${clusterName}
+export KUBECONFIG=${workDir}/kubeconfig.yaml
+kind get kubeconfig --name=${clusterName} > ${KUBECONFIG}
+
+echo "deploy docker registry in kind"
+registryNode=${clusterName}-control-plane
+registryNodeIP=$($KUBECTL_BIN get nodes ${registryNode} -o template --template='{{range.status.addresses}}{{if eq .type "InternalIP"}}{{.address}}{{end}}{{end}}')
+registryFile=${workDir}/registry.yaml
+
+cat <<EOF >${registryFile}
+apiVersion: apps/v1
+kind: DaemonSet
+metadata:
+  name: registry
+spec:
+  selector:
+    matchLabels:
+      app: registry
+  template:
+    metadata:
+      labels:
+        app: registry
+    spec:
+      hostNetwork: true
+      nodeSelector:
+        kubernetes.io/hostname: ${registryNode}
+      tolerations:
+      - key: node-role.kubernetes.io/master
+        operator: "Equal"
+        effect: "NoSchedule"
+      containers:
+      - name: registry
+        image: registry:2
+        volumeMounts:
+        - name: data
+          mountPath: /data
+      volumes:
+      - name: data
+        hostPath:
+          path: /data
+---
+apiVersion: apps/v1
+kind: DaemonSet
+metadata:
+  name: registry-proxy
+  labels:
+    app: registry-proxy
+spec:
+  selector:
+    matchLabels:
+      app: registry-proxy
+  template:
+    metadata:
+      labels:
+        app: registry-proxy
+    spec:
+      hostNetwork: true
+      affinity:
+        nodeAffinity:
+          requiredDuringSchedulingIgnoredDuringExecution:
+            nodeSelectorTerms:
+            - matchExpressions:
+              - key: kubernetes.io/hostname
+                operator: NotIn
+                values:
+                  - ${registryNode}
+      tolerations:
+      - key: node-role.kubernetes.io/master
+        operator: "Equal"
+        effect: "NoSchedule"
+      containers:
+        - name: socat
+          image: alpine/socat:1.0.5
+          args:
+          - tcp-listen:5000,fork,reuseaddr
+          - tcp-connect:${registryNodeIP}:5000
+EOF
+$KUBECTL_BIN apply -f ${registryFile}
+
+echo "init pulsar  env"
+$KUBECTL_BIN apply -f ${PULSAR_CHART_HOME}/manifests/local-dind/local-volume-provisioner.yaml
+
+docker pull gcr.io/google-containers/kube-scheduler:${k8sVersion}
+docker tag gcr.io/google-containers/kube-scheduler:${k8sVersion} mirantis/hypokube:final
+kind load docker-image --name=${clusterName} mirantis/hypokube:final
+
+echo "############# success create cluster:[${clusterName}] #############"
+
+echo "To start using your cluster, run:"
+echo "    export KUBECONFIG=${KUBECONFIG}"
+echo ""
+echo <<EOF
+NOTE: In kind, nodes run docker network and cannot access host network.
+If you configured local HTTP proxy in your docker, images may cannot be pulled
+because http proxy is inaccessible.
+If you cannot remove http proxy settings, you can either whitelist image
+domains in NO_PROXY environment or use 'docker pull <image> && kind load
+docker-image <image>' command to load images into nodes.
+EOF
\ No newline at end of file
diff --git a/pulsar/Chart.yaml b/pulsar/Chart.yaml
index f4cefc0..f2c535e 100644
--- a/pulsar/Chart.yaml
+++ b/pulsar/Chart.yaml
@@ -22,3 +22,10 @@ appVersion: "1.0"
 description: Apache Pulsar Helm chart for Kubernetes
 name: pulsar
 version: 1.0.0
+home: https://pulsar.apache.org
+sources:
+- https://github.com/apache/pulsar
+icon: http://pulsar.apache.org/img/pulsar.svg
+maintainers:
+- name: The Apache Pulsar Team
+  email: dev@pulsar.apache.org
diff --git a/pulsar/templates/_autorecovery.tpl b/pulsar/templates/_autorecovery.tpl
new file mode 100644
index 0000000..3fb3f4b
--- /dev/null
+++ b/pulsar/templates/_autorecovery.tpl
@@ -0,0 +1,80 @@
+{{/*
+Define the pulsar autorecovery service
+*/}}
+{{- define "pulsar.autorecovery.service" -}}
+{{ template "pulsar.fullname" . }}-{{ .Values.autorecovery.component }}
+{{- end }}
+
+{{/*
+Define the autorecovery hostname
+*/}}
+{{- define "pulsar.autorecovery.hostname" -}}
+${HOSTNAME}.{{ template "pulsar.autorecovery.service" . }}.{{ .Values.namespace }}.svc.cluster.local
+{{- end -}}
+
+{{/*
+Define autorecovery zookeeper client tls settings
+*/}}
+{{- define "pulsar.autorecovery.zookeeper.tls.settings" -}}
+{{- if and .Values.tls.enabled .Values.tls.zookeeper.enabled }}
+/pulsar/keytool/keytool.sh autorecovery {{ template "pulsar.autorecovery.hostname" . }} true;
+{{- end }}
+{{- end }}
+
+{{/*
+Define autorecovery tls certs mounts
+*/}}
+{{- define "pulsar.autorecovery.certs.volumeMounts" -}}
+{{- if and .Values.tls.enabled .Values.tls.zookeeper.enabled }}
+- name: autorecovery-certs
+  mountPath: "/pulsar/certs/autorecovery"
+  readOnly: true
+- name: ca
+  mountPath: "/pulsar/certs/ca"
+  readOnly: true
+{{- if .Values.tls.zookeeper.enabled }}
+- name: keytool
+  mountPath: "/pulsar/keytool/keytool.sh"
+  subPath: keytool.sh
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Define autorecovery tls certs volumes
+*/}}
+{{- define "pulsar.autorecovery.certs.volumes" -}}
+{{- if and .Values.tls.enabled .Values.tls.zookeeper.enabled }}
+- name: autorecovery-certs
+  secret:
+    secretName: "{{ template "pulsar.fullname" . }}-{{ .Values.tls.autorecovery.cert_name }}"
+    items:
+    - key: tls.crt
+      path: tls.crt
+    - key: tls.key
+      path: tls.key
+- name: ca
+  secret:
+    secretName: "{{ template "pulsar.fullname" . }}-ca-tls"
+    items:
+    - key: ca.crt
+      path: ca.crt
+{{- if .Values.tls.zookeeper.enabled }}
+- name: keytool
+  configMap:
+    name: "{{ template "pulsar.fullname" . }}-keytool-configmap"
+    defaultMode: 0755
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Define autorecovery init container : verify cluster id
+*/}}
+{{- define "pulsar.autorecovery.init.verify_cluster_id" -}}
+bin/apply-config-from-env.py conf/bookkeeper.conf;
+{{- include "pulsar.autorecovery.zookeeper.tls.settings" . -}}
+until bin/bookkeeper shell whatisinstanceid; do
+  sleep 3;
+done;
+{{- end }}
\ No newline at end of file
diff --git a/pulsar/templates/_bookkeeper.tpl b/pulsar/templates/_bookkeeper.tpl
new file mode 100644
index 0000000..d828009
--- /dev/null
+++ b/pulsar/templates/_bookkeeper.tpl
@@ -0,0 +1,121 @@
+{{/*
+Define the pulsar bookkeeper service
+*/}}
+{{- define "pulsar.bookkeeper.service" -}}
+{{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }}
+{{- end }}
+
+{{/*
+Define the bookkeeper hostname
+*/}}
+{{- define "pulsar.bookkeeper.hostname" -}}
+${HOSTNAME}.{{ template "pulsar.bookkeeper.service" . }}.{{ .Values.namespace }}.svc.cluster.local
+{{- end -}}
+
+
+{{/*
+Define bookie zookeeper client tls settings
+*/}}
+{{- define "pulsar.bookkeeper.zookeeper.tls.settings" -}}
+{{- if and .Values.tls.enabled .Values.tls.zookeeper.enabled }}
+/pulsar/keytool/keytool.sh bookie {{ template "pulsar.bookkeeper.hostname" . }} true;
+{{- end }}
+{{- end }}
+
+{{/*
+Define bookie tls certs mounts
+*/}}
+{{- define "pulsar.bookkeeper.certs.volumeMounts" -}}
+{{- if and .Values.tls.enabled (or .Values.tls.bookie.enabled .Values.tls.zookeeper.enabled) }}
+- name: bookie-certs
+  mountPath: "/pulsar/certs/bookie"
+  readOnly: true
+- name: ca
+  mountPath: "/pulsar/certs/ca"
+  readOnly: true
+{{- if .Values.tls.zookeeper.enabled }}
+- name: keytool
+  mountPath: "/pulsar/keytool/keytool.sh"
+  subPath: keytool.sh
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Define bookie tls certs volumes
+*/}}
+{{- define "pulsar.bookkeeper.certs.volumes" -}}
+{{- if and .Values.tls.enabled (or .Values.tls.bookie.enabled .Values.tls.zookeeper.enabled) }}
+- name: bookie-certs
+  secret:
+    secretName: "{{ template "pulsar.fullname" . }}-{{ .Values.tls.bookie.cert_name }}"
+    items:
+    - key: tls.crt
+      path: tls.crt
+    - key: tls.key
+      path: tls.key
+- name: ca
+  secret:
+    secretName: "{{ template "pulsar.fullname" . }}-ca-tls"
+    items:
+    - key: ca.crt
+      path: ca.crt
+{{- if .Values.tls.zookeeper.enabled }}
+- name: keytool
+  configMap:
+    name: "{{ template "pulsar.fullname" . }}-keytool-configmap"
+    defaultMode: 0755
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Define bookie common config
+*/}}
+{{- define "pulsar.bookkeeper.config.common" -}}
+zkServers: "{{ template "pulsar.zookeeper.connect" . }}"
+zkLedgersRootPath: "{{ .Values.metadataPrefix }}/ledgers"
+# enable bookkeeper http server
+httpServerEnabled: "true"
+httpServerPort: "{{ .Values.bookkeeper.ports.http }}"
+# config the stats provider
+statsProviderClass: org.apache.bookkeeper.stats.prometheus.PrometheusMetricsProvider
+# use hostname as the bookie id
+useHostNameAsBookieID: "true"
+{{- end }}
+
+{{/*
+Define bookie tls config
+*/}}
+{{- define "pulsar.bookkeeper.config.tls" -}}
+{{- if and .Values.tls.enabled .Values.tls.bookie.enabled }}
+PULSAR_PREFIX_tlsProviderFactoryClass: org.apache.bookkeeper.tls.TLSContextFactory
+PULSAR_PREFIX_tlsCertificatePath: /pulsar/certs/bookie/tls.crt  
+PULSAR_PREFIX_tlsKeyStoreType: PEM
+PULSAR_PREFIX_tlsKeyStore: /pulsar/certs/bookie/tls.key
+PULSAR_PREFIX_tlsTrustStoreType: PEM
+PULSAR_PREFIX_tlsTrustStore: /pulsar/certs/ca/ca.crt 
+{{- end }}
+{{- end }}
+
+{{/*
+Define bookie init container : verify cluster id
+*/}}
+{{- define "pulsar.bookkeeper.init.verify_cluster_id" -}}
+{{- if not (and .Values.volumes.persistence .Values.bookkeeper.volumes.persistence) }}
+bin/apply-config-from-env.py conf/bookkeeper.conf;
+{{- include "pulsar.bookkeeper.zookeeper.tls.settings" . -}}
+until bin/bookkeeper shell whatisinstanceid; do
+  sleep 3;
+done;
+bin/bookkeeper shell bookieformat -nonInteractive -force -deleteCookie || true
+{{- end }}
+{{- if and .Values.volumes.persistence .Values.bookkeeper.volumes.persistence }}
+set -e;
+bin/apply-config-from-env.py conf/bookkeeper.conf;
+{{- include "pulsar.bookkeeper.zookeeper.tls.settings" . -}}
+until bin/bookkeeper shell whatisinstanceid; do
+  sleep 3;
+done;
+{{- end }}
+{{- end }}
\ No newline at end of file
diff --git a/pulsar/templates/_broker.tpl b/pulsar/templates/_broker.tpl
new file mode 100644
index 0000000..cff94f9
--- /dev/null
+++ b/pulsar/templates/_broker.tpl
@@ -0,0 +1,76 @@
+{{/*
+Define the pulsar brroker service
+*/}}
+{{- define "pulsar.broker.service" -}}
+{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}
+{{- end }}
+
+{{/*
+Define the hostname
+*/}}
+{{- define "pulsar.broker.hostname" -}}
+${HOSTNAME}.{{ template "pulsar.broker.service" . }}.{{ .Values.namespace }}.svc.cluster.local
+{{- end -}}
+
+{{/*
+Define the broker znode
+*/}}
+{{- define "pulsar.broker.znode" -}}
+{{ .Values.metadataPrefix }}/loadbalance/brokers/{{ template "pulsar.broker.hostname" . }}:{{ .Values.broker.ports.http }}
+{{- end }}
+
+{{/*
+Define broker zookeeper client tls settings
+*/}}
+{{- define "pulsar.broker.zookeeper.tls.settings" -}}
+{{- if and .Values.tls.enabled .Values.tls.zookeeper.enabled }}
+/pulsar/keytool/keytool.sh broker {{ template "pulsar.broker.hostname" . }} true;
+{{- end }}
+{{- end }}
+
+{{/*
+Define broker tls certs mounts
+*/}}
+{{- define "pulsar.broker.certs.volumeMounts" -}}
+{{- if and .Values.tls.enabled (or .Values.tls.broker.enabled (or .Values.tls.bookie.enabled .Values.tls.zookeeper.enabled)) }}
+- name: broker-certs
+  mountPath: "/pulsar/certs/broker"
+  readOnly: true
+- name: ca
+  mountPath: "/pulsar/certs/ca"
+  readOnly: true
+{{- if .Values.tls.zookeeper.enabled }}
+- name: keytool
+  mountPath: "/pulsar/keytool/keytool.sh"
+  subPath: keytool.sh
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Define broker tls certs volumes
+*/}}
+{{- define "pulsar.broker.certs.volumes" -}}
+{{- if and .Values.tls.enabled (or .Values.tls.broker.enabled (or .Values.tls.bookie.enabled .Values.tls.zookeeper.enabled)) }}
+- name: broker-certs
+  secret:
+    secretName: "{{ template "pulsar.fullname" . }}-{{ .Values.tls.broker.cert_name }}"
+    items:
+    - key: tls.crt
+      path: tls.crt
+    - key: tls.key
+      path: tls.key
+- name: ca
+  secret:
+    secretName: "{{ template "pulsar.fullname" . }}-ca-tls"
+    items:
+    - key: ca.crt
+      path: ca.crt
+{{- if .Values.tls.zookeeper.enabled }}
+- name: keytool
+  configMap:
+    name: "{{ template "pulsar.fullname" . }}-keytool-configmap"
+    defaultMode: 0755
+{{- end }}
+{{- end }}
+{{- end }}
\ No newline at end of file
diff --git a/pulsar/templates/_helpers.tpl b/pulsar/templates/_helpers.tpl
index ba25c6e..931e097 100644
--- a/pulsar/templates/_helpers.tpl
+++ b/pulsar/templates/_helpers.tpl
@@ -1,4 +1,12 @@
 {{/* vim: set filetype=mustache: */}}
+
+{{/*
+pulsar home
+*/}}
+{{- define "pulsar.home" -}}
+{{- print "/pulsar" -}}
+{{- end -}}
+
 {{/*
 Expand the name of the chart.
 */}}
@@ -30,3 +38,31 @@ Create chart name and version as used by the chart label.
 {{- define "pulsar.chart" -}}
 {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
 {{- end -}}
+
+{{/*
+Create the common labels.
+*/}}
+{{- define "pulsar.standardLabels" -}}
+app: {{ template "pulsar.name" . }}
+chart: {{ template "pulsar.chart" . }}
+release: {{ .Release.Name }}
+heritage: {{ .Release.Service }}
+cluster: {{ template "pulsar.fullname" . }}
+{{- end }}
+
+{{/*
+Create the template labels.
+*/}}
+{{- define "pulsar.template.labels" -}}
+app: {{ template "pulsar.name" . }}
+release: {{ .Release.Name }}
+cluster: {{ template "pulsar.fullname" . }}
+{{- end }}
+
+{{/*
+Create the match labels.
+*/}}
+{{- define "pulsar.matchLabels" -}}
+app: {{ template "pulsar.name" . }}
+release: {{ .Release.Name }}
+{{- end }}
diff --git a/pulsar/templates/_toolset.tpl b/pulsar/templates/_toolset.tpl
new file mode 100644
index 0000000..405fa71
--- /dev/null
+++ b/pulsar/templates/_toolset.tpl
@@ -0,0 +1,69 @@
+{{/*
+Define the pulsar toolset service
+*/}}
+{{- define "pulsar.toolset.service" -}}
+{{ template "pulsar.fullname" . }}-{{ .Values.toolset.component }}
+{{- end }}
+
+{{/*
+Define the toolset hostname
+*/}}
+{{- define "pulsar.toolset.hostname" -}}
+${HOSTNAME}.{{ template "pulsar.toolset.service" . }}.{{ .Values.namespace }}.svc.cluster.local
+{{- end -}}
+
+{{/*
+Define toolset zookeeper client tls settings
+*/}}
+{{- define "pulsar.toolset.zookeeper.tls.settings" -}}
+{{- if and .Values.tls.enabled .Values.tls.zookeeper.enabled -}}
+/pulsar/keytool/keytool.sh toolset {{ template "pulsar.toolset.hostname" . }} true;
+{{- end -}}
+{{- end }}
+
+{{/*
+Define toolset tls certs mounts
+*/}}
+{{- define "pulsar.toolset.certs.volumeMounts" -}}
+{{- if and .Values.tls.enabled .Values.tls.zookeeper.enabled }}
+- name: toolset-certs
+  mountPath: "/pulsar/certs/toolset"
+  readOnly: true
+- name: ca
+  mountPath: "/pulsar/certs/ca"
+  readOnly: true
+{{- if .Values.tls.zookeeper.enabled }}
+- name: keytool
+  mountPath: "/pulsar/keytool/keytool.sh"
+  subPath: keytool.sh
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Define toolset tls certs volumes
+*/}}
+{{- define "pulsar.toolset.certs.volumes" -}}
+{{- if and .Values.tls.enabled .Values.tls.zookeeper.enabled }}
+- name: toolset-certs
+  secret:
+    secretName: "{{ template "pulsar.fullname" . }}-{{ .Values.tls.toolset.cert_name }}"
+    items:
+    - key: tls.crt
+      path: tls.crt
+    - key: tls.key
+      path: tls.key
+- name: ca
+  secret:
+    secretName: "{{ template "pulsar.fullname" . }}-ca-tls"
+    items:
+    - key: ca.crt
+      path: ca.crt
+{{- if .Values.tls.zookeeper.enabled }}
+- name: keytool
+  configMap:
+    name: "{{ template "pulsar.fullname" . }}-keytool-configmap"
+    defaultMode: 0755
+{{- end }}
+{{- end }}
+{{- end }}
\ No newline at end of file
diff --git a/pulsar/templates/_zookeeper.tpl b/pulsar/templates/_zookeeper.tpl
new file mode 100644
index 0000000..ca5c1d1
--- /dev/null
+++ b/pulsar/templates/_zookeeper.tpl
@@ -0,0 +1,34 @@
+{{/*
+Define the pulsar zookeeper
+*/}}
+{{- define "pulsar.zookeeper.service" -}}
+{{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}
+{{- end }}
+
+{{/*
+Define the pulsar zookeeper
+*/}}
+{{- define "pulsar.zookeeper.connect" -}}
+{{- if not (and .Values.tls.enabled .Values.tls.zookeeper.enabled) -}}
+{{ template "pulsar.zookeeper.service" . }}:{{ .Values.zookeeper.ports.client }}
+{{- end -}}
+{{- if and .Values.tls.enabled .Values.tls.zookeeper.enabled -}}
+{{ template "pulsar.zookeeper.service" . }}:{{ .Values.zookeeper.ports.clientTls }}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Define the zookeeper hostname
+*/}}
+{{- define "pulsar.zookeeper.hostname" -}}
+${HOSTNAME}.{{ template "pulsar.zookeeper.service" . }}.{{ .Values.namespace }}.svc.cluster.local
+{{- end -}}
+
+{{/*
+Define zookeeper tls settings
+*/}}
+{{- define "pulsar.zookeeper.tls.settings" -}}
+{{- if and .Values.tls.enabled .Values.tls.zookeeper.enabled }}
+/pulsar/keytool/keytool.sh zookeeper {{ template "pulsar.zookeeper.hostname" . }} false;
+{{- end }}
+{{- end }}
diff --git a/pulsar/templates/autorecovery-configmap.yaml b/pulsar/templates/autorecovery-configmap.yaml
index 4200e6c..8d7fb3f 100644
--- a/pulsar/templates/autorecovery-configmap.yaml
+++ b/pulsar/templates/autorecovery-configmap.yaml
@@ -17,22 +17,17 @@
 # under the License.
 #
 
-{{- if .Values.extra.autoRecovery }}
+{{- if or .Values.components.autorecovery .Values.extra.autoRecovery  }}
 apiVersion: v1
 kind: ConfigMap
 metadata:
-  name: "{{ template "pulsar.fullname" . }}-{{ .Values.autoRecovery.component }}"
+  name: "{{ template "pulsar.fullname" . }}-{{ .Values.autorecovery.component }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
-    component: {{ .Values.autoRecovery.component }}
-    cluster: {{ template "pulsar.fullname" . }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
+    component: {{ .Values.autorecovery.component }}
 data:
-  zkServers:
-    {{- $global := . }}
-    {{ range $i, $e := until (.Values.zookeeper.replicaCount | int) }}{{ if ne $i 0 }},{{ end }}{{ template "pulsar.fullname" $global }}-{{ $global.Values.zookeeper.component }}-{{ printf "%d" $i }}.{{ template "pulsar.fullname" $global }}-{{ $global.Values.zookeeper.component }}{{ end }}
-{{ toYaml .Values.autoRecovery.configData | indent 2 }}
+  # common config
+  {{- include "pulsar.bookkeeper.config.common" . | nindent 2 }}
+{{ toYaml .Values.autorecovery.configData | indent 2 }}
 {{- end }}
diff --git a/pulsar/templates/autorecovery-deployment.yaml b/pulsar/templates/autorecovery-deployment.yaml
deleted file mode 100644
index 0872693..0000000
--- a/pulsar/templates/autorecovery-deployment.yaml
+++ /dev/null
@@ -1,105 +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 .Values.extra.autoRecovery }}
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  name: "{{ template "pulsar.fullname" . }}-{{ .Values.autoRecovery.component }}"
-  namespace: {{ .Values.namespace }}
-  labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
-    component: {{ .Values.autoRecovery.component }}
-    cluster: {{ template "pulsar.fullname" . }}
-spec:
-  replicas: {{ .Values.autoRecovery.replicaCount }}
-  selector:
-    matchLabels:
-      app: {{ template "pulsar.name" . }}
-      release: {{ .Release.Name }}
-      component: {{ .Values.autoRecovery.component }}
-  template:
-    metadata:
-      labels:
-        app: {{ template "pulsar.name" . }}
-        release: {{ .Release.Name }}
-        component: {{ .Values.autoRecovery.component }}
-        cluster: {{ template "pulsar.fullname" . }}
-      annotations:
-{{ toYaml .Values.autoRecovery.annotations | indent 8 }}
-    spec:
-    {{- if .Values.autoRecovery.nodeSelector }}
-      nodeSelector:
-{{ toYaml .Values.autoRecovery.nodeSelector | indent 8 }}
-    {{- end }}
-    {{- if .Values.autoRecovery.tolerations }}
-      tolerations:
-{{ toYaml .Values.autoRecovery.tolerations | indent 8 }}
-    {{- end }}
-      affinity:
-        podAntiAffinity:
-          requiredDuringSchedulingIgnoredDuringExecution:
-          - labelSelector:
-              matchExpressions:
-              - key: "app"
-                operator: In
-                values:
-                - "{{ template "pulsar.name" . }}"
-              - key: "release"
-                operator: In
-                values:
-                - {{ .Release.Name }}
-              - key: "component"
-                operator: In
-                values:
-                - {{ .Values.bookkeeper.component }}
-            topologyKey: "kubernetes.io/hostname"
-      terminationGracePeriodSeconds: {{ .Values.autoRecovery.gracePeriod }}
-      initContainers:
-      # This init container will wait for zookeeper to be ready before
-      # deploying the bookies
-      - name: wait-zookeeper-ready
-        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
-        imagePullPolicy: {{ .Values.image.pullPolicy }}
-        command: ["sh", "-c"]
-        args:
-          - >-
-            until bin/pulsar zookeeper-shell -server {{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }} ls /admin/clusters/{{ template "pulsar.fullname" . }}; do
-              sleep 3;
-            done;
-      containers:
-      - name: "{{ template "pulsar.fullname" . }}-{{ .Values.autoRecovery.component }}"
-        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
-        imagePullPolicy: {{ .Values.image.pullPolicy }}
-      {{- if .Values.autoRecovery.resources }}
-        resources:
-{{ toYaml .Values.autoRecovery.resources | indent 10 }}
-      {{- end }}
-        command: ["sh", "-c"]
-        args:
-        - >
-          bin/apply-config-from-env.py conf/bookkeeper.conf &&
-          bin/bookkeeper autorecovery
-        envFrom:
-        - configMapRef:
-            name: "{{ template "pulsar.fullname" . }}-{{ .Values.autoRecovery.component }}"
-{{- end }}
diff --git a/pulsar/templates/bastion-configmap.yaml b/pulsar/templates/autorecovery-service.yaml
similarity index 68%
rename from pulsar/templates/bastion-configmap.yaml
rename to pulsar/templates/autorecovery-service.yaml
index 4e42fcc..ab237c9 100644
--- a/pulsar/templates/bastion-configmap.yaml
+++ b/pulsar/templates/autorecovery-service.yaml
@@ -17,19 +17,23 @@
 # under the License.
 #
 
-{{- if .Values.extra.bastion }}
+{{- if or .Values.components.autorecovery .Values.extra.autoRecovery }}
 apiVersion: v1
-kind: ConfigMap
+kind: Service
 metadata:
-  name: "{{ template "pulsar.fullname" . }}-{{ .Values.bastion.component }}"
+  name: "{{ template "pulsar.fullname" . }}-{{ .Values.autorecovery.component }}"
   namespace: {{ .Values.namespace }}
   labels:
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
+    component: {{ .Values.autorecovery.component }}
+spec:
+  ports:
+  - name: http
+    port: {{ .Values.autorecovery.ports.http }}
+  clusterIP: None
+  selector:
     app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
     release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
-    component: {{ .Values.bastion.component }}
-    cluster: {{ template "pulsar.fullname" . }}
-data:
-{{ toYaml .Values.bastion.configData | indent 2 }}
+    component: {{ .Values.autorecovery.component }}
 {{- end }}
+
diff --git a/pulsar/templates/autorecovery-statefulset.yaml b/pulsar/templates/autorecovery-statefulset.yaml
new file mode 100644
index 0000000..c4e1f61
--- /dev/null
+++ b/pulsar/templates/autorecovery-statefulset.yaml
@@ -0,0 +1,124 @@
+#
+# 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 or .Values.components.autorecovery .Values.extra.autoRecovery }}
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+  name: "{{ template "pulsar.fullname" . }}-{{ .Values.autorecovery.component }}"
+  namespace: {{ .Values.namespace }}
+  labels:
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
+    component: {{ .Values.autorecovery.component }}
+spec:
+  serviceName: "{{ template "pulsar.fullname" . }}-{{ .Values.autorecovery.component }}"
+  replicas: {{ .Values.autorecovery.replicaCount }}
+  updateStrategy:
+    type: RollingUpdate
+  podManagementPolicy: Parallel
+  # nodeSelector:
+  selector:
+    matchLabels:
+      {{- include "pulsar.matchLabels" . | nindent 6 }}
+      component: {{ .Values.autorecovery.component }}
+  template:
+    metadata:
+      labels:
+        {{- include "pulsar.template.labels" . | nindent 8 }}
+        component: {{ .Values.autorecovery.component }}
+      annotations:
+        prometheus.io/scrape: "true"
+        prometheus.io/port: "{{ .Values.autorecovery.ports.http }}"
+{{- with .Values.autorecovery.annotations }}
+{{ toYaml . | indent 8 }}
+{{- end }}
+    spec:
+    {{- if .Values.autorecovery.nodeSelector }}
+      nodeSelector:
+{{ toYaml .Values.autorecovery.nodeSelector | indent 8 }}
+    {{- end }}
+    {{- if .Values.autorecovery.tolerations }}
+      tolerations:
+{{- with .Values.autorecovery.tolerations }}
+{{ toYaml . | indent 8 }}
+{{- end }}
+    {{- end }}
+      affinity:
+        {{- if and .Values.affinity.anti_affinity .Values.autorecovery.affinity.anti_affinity}}
+        podAntiAffinity:
+          requiredDuringSchedulingIgnoredDuringExecution:
+          - labelSelector:
+              matchExpressions:
+              - key: "app"
+                operator: In
+                values:
+                - "{{ template "pulsar.name" . }}-{{ .Values.bookkeeper.component }}"
+              - key: "release"
+                operator: In
+                values:
+                - {{ .Release.Name }}
+              - key: "component"
+                operator: In
+                values:
+                - {{ .Values.bookkeeper.component }}
+            topologyKey: "kubernetes.io/hostname"
+        {{- end }}
+      terminationGracePeriodSeconds: {{ .Values.autorecovery.gracePeriod }}
+      initContainers:
+      # This initContainer will wait for bookkeeper initnewcluster to complete
+      # before deploying the bookies
+      - name: pulsar-bookkeeper-verify-clusterid
+        image: "{{ .Values.images.autorecovery.repository }}:{{ .Values.images.autorecovery.tag }}"
+        imagePullPolicy: {{ .Values.images.autorecovery.pullPolicy }}
+        command: ["sh", "-c"]
+        args:
+        - >
+          {{- include "pulsar.autorecovery.init.verify_cluster_id" . | nindent 10 }}
+        envFrom:
+        - configMapRef:
+            name: "{{ template "pulsar.fullname" . }}-{{ .Values.autorecovery.component }}"
+        volumeMounts:
+        {{- include "pulsar.autorecovery.certs.volumeMounts" . | nindent 8 }}
+      containers:
+      - name: "{{ template "pulsar.fullname" . }}-{{ .Values.autorecovery.component }}"
+        image: "{{ .Values.images.autorecovery.repository }}:{{ .Values.images.autorecovery.tag }}"
+        imagePullPolicy: {{ .Values.images.autorecovery.pullPolicy }}
+      {{- if .Values.autorecovery.resources }}
+        resources:
+{{ toYaml .Values.autorecovery.resources | indent 10 }}
+      {{- end }}
+        command: ["sh", "-c"]
+        args:
+        - >
+          bin/apply-config-from-env.py conf/bookkeeper.conf;
+          bin/apply-config-from-env.py conf/bkenv.sh;
+          {{- include "pulsar.autorecovery.zookeeper.tls.settings" . | nindent 10 }}
+          bin/bookkeeper autorecovery
+        ports:
+        - name: http
+          containerPort: {{ .Values.autorecovery.ports.http }}
+        envFrom:
+        - configMapRef:
+            name: "{{ template "pulsar.fullname" . }}-{{ .Values.autorecovery.component }}"
+        volumeMounts:
+        {{- include "pulsar.autorecovery.certs.volumeMounts" . | nindent 8 }}
+      volumes:
+      {{- include "pulsar.autorecovery.certs.volumes" . | nindent 6 }}
+{{- end }}
+
diff --git a/pulsar/templates/bastion-deployment.yaml b/pulsar/templates/bastion-deployment.yaml
deleted file mode 100644
index afc6a73..0000000
--- a/pulsar/templates/bastion-deployment.yaml
+++ /dev/null
@@ -1,80 +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 .Values.extra.bastion }}
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  name: "{{ template "pulsar.fullname" . }}-{{ .Values.bastion.component }}"
-  namespace: {{ .Values.namespace }}
-  labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
-    component: {{ .Values.bastion.component }}
-    cluster: {{ template "pulsar.fullname" . }}
-spec:
-  replicas: {{ .Values.bastion.replicaCount }}
-  selector:
-    matchLabels:
-      app: {{ template "pulsar.name" . }}
-      release: {{ .Release.Name }}
-      component: {{ .Values.bastion.component }}
-  template:
-    metadata:
-      labels:
-        app: {{ template "pulsar.name" . }}
-        release: {{ .Release.Name }}
-        component: {{ .Values.bastion.component }}
-        cluster: {{ template "pulsar.fullname" . }}
-      annotations:
-{{ toYaml .Values.bastion.annotations | indent 8 }}
-    spec:
-    {{- if .Values.bastion.nodeSelector }}
-      nodeSelector:
-{{ toYaml .Values.bastion.nodeSelector | indent 8 }}
-    {{- end }}
-    {{- if .Values.bastion.tolerations }}
-      tolerations:
-{{ toYaml .Values.bastion.tolerations | indent 8 }}
-    {{- end }}
-      terminationGracePeriodSeconds: {{ .Values.bastion.gracePeriod }}
-      containers:
-      - name: "{{ template "pulsar.fullname" . }}-{{ .Values.bastion.component }}"
-        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
-        imagePullPolicy: {{ .Values.image.pullPolicy }}
-      {{- if .Values.bastion.resources }}
-        resources:
-{{ toYaml .Values.bastion.resources | indent 10 }}
-      {{- end }}
-        command: ["sh", "-c"]
-        args:
-        - >
-          bin/apply-config-from-env.py conf/client.conf &&
-          sleep 10000000000
-        envFrom:
-        - configMapRef:
-            name: "{{ template "pulsar.fullname" . }}-{{ .Values.bastion.component }}"
-        env:
-        - name: webServiceUrl
-          value: http://{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}:8080/
-        - name: brokerServiceUrl
-          value: pulsar://{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}:6650/
-{{- end }}
diff --git a/pulsar/templates/bookkeeper-cluster-initialize.yaml b/pulsar/templates/bookkeeper-cluster-initialize.yaml
new file mode 100644
index 0000000..a9ab647
--- /dev/null
+++ b/pulsar/templates/bookkeeper-cluster-initialize.yaml
@@ -0,0 +1,71 @@
+#
+# 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 .Values.components.bookkeeper }}
+apiVersion: batch/v1
+kind: Job
+metadata:
+  name: "{{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }}-init"
+  namespace: {{ .Values.namespace }}
+  labels:
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
+    component: "{{ .Values.bookkeeper.component }}-init"
+spec:
+  template:
+    spec:
+      initContainers:
+      - name: wait-zookeeper-ready
+        image: "{{ .Values.bookkeeper.metadata.image.repository }}:{{ .Values.bookkeeper.metadata.image.tag }}"
+        imagePullPolicy: {{ .Values.bookkeeper.metadata.image.pullPolicy }}
+        command: ["sh", "-c"]
+        args:
+          - >-
+            until nslookup {{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}-{{ add (.Values.zookeeper.replicaCount | int) -1 }}.{{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}.{{ .Values.namespace }}; do
+              sleep 3;
+            done;
+      containers:
+      - name: "{{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }}-init"
+        image: "{{ .Values.bookkeeper.metadata.image.repository }}:{{ .Values.bookkeeper.metadata.image.tag }}"
+        imagePullPolicy: {{ .Values.bookkeeper.metadata.image.pullPolicy }}
+      {{- if .Values.bookkeeper.metadata.resources }}
+        resources:
+{{ toYaml .Values.bookkeeper.metadata.resources | indent 10 }}
+      {{- end }}
+        command: ["sh", "-c"]
+        args:
+          - >
+            bin/apply-config-from-env.py conf/bookkeeper.conf;
+            {{- include "pulsar.toolset.zookeeper.tls.settings" . | nindent 12 }}
+            if bin/bookkeeper shell whatisinstanceid; then
+                echo "bookkeeper cluster already initialized";
+            else
+                {{- if not (eq .Values.metadataPrefix "") }}
+                bin/bookkeeper org.apache.zookeeper.ZooKeeperMain -server {{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }} create {{ .Values.metadataPrefix }} 'created for pulsar cluster "{{ template "pulsar.fullname" . }}"' || yes &&
+                {{- end }}
+                bin/bookkeeper shell initnewcluster;
+            fi
+        envFrom:
+        - configMapRef:
+            name: "{{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }}"
+        volumeMounts:
+        {{- include "pulsar.toolset.certs.volumeMounts" . | nindent 8 }}
+      volumes:
+      {{- include "pulsar.toolset.certs.volumes" . | nindent 6 }}
+      restartPolicy: Never
+{{- end }}
diff --git a/pulsar/templates/bookkeeper-configmap.yaml b/pulsar/templates/bookkeeper-configmap.yaml
index 45a8546..05c43ce 100644
--- a/pulsar/templates/bookkeeper-configmap.yaml
+++ b/pulsar/templates/bookkeeper-configmap.yaml
@@ -17,22 +17,28 @@
 # under the License.
 #
 
+{{- if .Values.components.bookkeeper }}
 apiVersion: v1
 kind: ConfigMap
 metadata:
   name: "{{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
     component: {{ .Values.bookkeeper.component }}
-    cluster: {{ template "pulsar.fullname" . }}
 data:
-  zkServers:
-    {{- $global := . }}
-    {{ range $i, $e := until (.Values.zookeeper.replicaCount | int) }}{{ if ne $i 0 }},{{ end }}{{ template "pulsar.fullname" $global }}-{{ $global.Values.zookeeper.component }}-{{ printf "%d" $i }}.{{ template "pulsar.fullname" $global }}-{{ $global.Values.zookeeper.component }}{{ end }}
+  # common config
+  {{- include "pulsar.bookkeeper.config.common" . | nindent 2 }}
+  {{- if .Values.components.autorecovery }}
   # disable auto recovery on bookies since we will start AutoRecovery in separated pods
   autoRecoveryDaemonEnabled: "false"
+  {{- end }}
+  # Do not retain journal files as it increase the disk utilization
+  journalMaxBackups: "0"
+  journalDirectories: "/pulsar/data/bookkeeper/journal"
+  PULSAR_PREFIX_journalDirectories: "/pulsar/data/bookkeeper/journal"
+  ledgerDirectories: "/pulsar/data/bookkeeper/ledgers"
+  # TLS config
+  {{- include "pulsar.bookkeeper.config.tls" . | nindent 2 }}
 {{ toYaml .Values.bookkeeper.configData | indent 2 }}
+{{- end }}
\ No newline at end of file
diff --git a/pulsar/templates/bookkeeper-pdb.yaml b/pulsar/templates/bookkeeper-pdb.yaml
index 8f045f7..35ac16d 100644
--- a/pulsar/templates/bookkeeper-pdb.yaml
+++ b/pulsar/templates/bookkeeper-pdb.yaml
@@ -17,6 +17,7 @@
 # under the License.
 #
 
+{{- if .Values.components.bookkeeper }}
 {{- if .Values.bookkeeper.pdb.usePolicy }}
 apiVersion: policy/v1beta1
 kind: PodDisruptionBudget
@@ -24,17 +25,13 @@ metadata:
   name: "{{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
     component: {{ .Values.bookkeeper.component }}
-    cluster: {{ template "pulsar.fullname" . }}
 spec:
   selector:
     matchLabels:
-      app: {{ template "pulsar.name" . }}
-      release: {{ .Release.Name }}
+      {{- include "pulsar.matchLabels" . | nindent 6 }}
       component: {{ .Values.bookkeeper.component }}
   maxUnavailable: {{ .Values.bookkeeper.pdb.maxUnavailable }}
 {{- end }}
+{{- end }}
diff --git a/pulsar/templates/bookkeeper-service.yaml b/pulsar/templates/bookkeeper-service.yaml
index 82658ba..388635b 100644
--- a/pulsar/templates/bookkeeper-service.yaml
+++ b/pulsar/templates/bookkeeper-service.yaml
@@ -17,25 +17,25 @@
 # under the License.
 #
 
+{{- if .Values.components.bookkeeper }}
 apiVersion: v1
 kind: Service
 metadata:
   name: "{{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
     component: {{ .Values.bookkeeper.component }}
-    cluster: {{ template "pulsar.fullname" . }}
   annotations:
 {{ toYaml .Values.bookkeeper.service.annotations | indent 4 }}
 spec:
   ports:
-{{ toYaml .Values.bookkeeper.service.ports | indent 2 }}
+  - name: bookie
+    port: {{ .Values.bookkeeper.ports.bookie }}
+  - name: http
+    port: {{ .Values.bookkeeper.ports.http }}
   clusterIP: None
   selector:
-    app: {{ template "pulsar.name" . }}
-    release: {{ .Release.Name }}
+    {{- include "pulsar.matchLabels" . | nindent 4 }}
     component: {{ .Values.bookkeeper.component }}
+{{- end }}
diff --git a/pulsar/templates/bookkeeper-statefulset.yaml b/pulsar/templates/bookkeeper-statefulset.yaml
index 012eb4e..71df9ab 100644
--- a/pulsar/templates/bookkeeper-statefulset.yaml
+++ b/pulsar/templates/bookkeeper-statefulset.yaml
@@ -17,25 +17,21 @@
 # under the License.
 #
 
+{{- if .Values.components.bookkeeper }}
 apiVersion: apps/v1
 kind: StatefulSet
 metadata:
   name: "{{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
     component: {{ .Values.bookkeeper.component }}
-    cluster: {{ template "pulsar.fullname" . }}
 spec:
   serviceName: "{{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }}"
   replicas: {{ .Values.bookkeeper.replicaCount }}
   selector:
     matchLabels:
-      app: {{ template "pulsar.name" . }}
-      release: {{ .Release.Name }}
+      {{- include "pulsar.matchLabels" . | nindent 6 }}
       component: {{ .Values.bookkeeper.component }}
   updateStrategy:
 {{ toYaml .Values.bookkeeper.updateStrategy | indent 4 }}
@@ -43,12 +39,14 @@ spec:
   template:
     metadata:
       labels:
-        app: {{ template "pulsar.name" . }}
-        release: {{ .Release.Name }}
+        {{- include "pulsar.template.labels" . | nindent 8 }}
         component: {{ .Values.bookkeeper.component }}
-        cluster: {{ template "pulsar.fullname" . }}
       annotations:
-{{ toYaml .Values.bookkeeper.annotations | indent 8 }}
+        prometheus.io/scrape: "true"
+        prometheus.io/port: "{{ .Values.bookkeeper.ports.http }}"
+{{- with .Values.bookkeeper.annotations }}
+{{ toYaml . | indent 8 }}
+{{- end }}
     spec:
     {{- if .Values.bookkeeper.nodeSelector }}
       nodeSelector:
@@ -59,6 +57,7 @@ spec:
 {{ toYaml .Values.bookkeeper.tolerations | indent 8 }}
     {{- end }}
       affinity:
+        {{- if and .Values.affinity.anti_affinity .Values.bookkeeper.affinity.anti_affinity}}
         podAntiAffinity:
           requiredDuringSchedulingIgnoredDuringExecution:
           - labelSelector:
@@ -66,7 +65,7 @@ spec:
               - key: "app"
                 operator: In
                 values:
-                - "{{ template "pulsar.name" . }}"
+                - "{{ template "pulsar.name" . }}-{{ .Values.bookkeeper.component }}"
               - key: "release"
                 operator: In
                 values:
@@ -76,36 +75,55 @@ spec:
                 values:
                 - {{ .Values.bookkeeper.component }}
             topologyKey: "kubernetes.io/hostname"
+        {{- end }}
       terminationGracePeriodSeconds: {{ .Values.bookkeeper.gracePeriod }}
       initContainers:
-      # This init container will wait for zookeeper to be ready before
-      # deploying the bookies
-      - name: wait-zookeeper-ready
-        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
-        imagePullPolicy: {{ .Values.image.pullPolicy }}
-        command: ["sh", "-c"]
-        args:
-          - >-
-            until bin/pulsar zookeeper-shell -server {{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }} ls /admin/clusters/{{ template "pulsar.fullname" . }}; do
-              sleep 3;
-            done;
-      # This initContainer will make sure that the bookeeper
-      # metadata is in zookeeper
-      - name: pulsar-bookkeeper-metaformat
-        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
-        imagePullPolicy: {{ .Values.image.pullPolicy }}
+      # This initContainer will wait for bookkeeper initnewcluster to complete
+      # before deploying the bookies
+      - name: pulsar-bookkeeper-verify-clusterid
+        image: "{{ .Values.images.bookie.repository }}:{{ .Values.images.bookie.tag }}"
+        imagePullPolicy: {{ .Values.images.bookie.pullPolicy }}
         command: ["sh", "-c"]
         args:
+        # only reformat bookie if bookkeeper is running without persistence
         - >
-          bin/apply-config-from-env.py conf/bookkeeper.conf &&
-          bin/bookkeeper shell metaformat --nonInteractive || true;
+          {{- include "pulsar.bookkeeper.init.verify_cluster_id" . | nindent 10 }}
         envFrom:
         - configMapRef:
             name: "{{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }}"
+        volumeMounts:
+        {{- include "pulsar.bookkeeper.certs.volumeMounts" . | nindent 8 }}
       containers:
       - name: "{{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }}"
-        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
-        imagePullPolicy: {{ .Values.image.pullPolicy }}
+        image: "{{ .Values.images.bookie.repository }}:{{ .Values.images.bookie.tag }}"
+        imagePullPolicy: {{ .Values.images.bookie.pullPolicy }}
+        {{- if .Values.bookkeeper.probe.liveness.enabled }}
+        livenessProbe:
+          httpGet:
+            path: /api/v1/bookie/state
+            port: {{ .Values.bookkeeper.ports.http }}
+          initialDelaySeconds: {{ .Values.bookkeeper.probe.liveness.initialDelaySeconds }}
+          periodSeconds: {{ .Values.bookkeeper.probe.liveness.periodSeconds }}
+          failureThreshold: {{ .Values.bookkeeper.probe.liveness.failureThreshold }}
+        {{- end }}
+        {{- if .Values.bookkeeper.probe.readiness.enabled }}
+        readinessProbe:
+          httpGet:
+            path: /api/v1/bookie/is_ready
+            port: {{ .Values.bookkeeper.ports.http }}
+          initialDelaySeconds: {{ .Values.bookkeeper.probe.readiness.initialDelaySeconds }}
+          periodSeconds: {{ .Values.bookkeeper.probe.readiness.periodSeconds }}
+          failureThreshold: {{ .Values.bookkeeper.probe.readiness.failureThreshold }}
+        {{- end }}
+        {{- if .Values.bookkeeper.probe.startup.enabled }}
+        startupProbe:
+          httpGet:
+            path: /api/v1/bookie/is_ready
+            port: {{ .Values.bookkeeper.ports.http }}
+          initialDelaySeconds: {{ .Values.bookkeeper.probe.startup.initialDelaySeconds }}
+          periodSeconds: {{ .Values.bookkeeper.probe.startup.periodSeconds }}
+          failureThreshold: {{ .Values.bookkeeper.probe.startup.failureThreshold }}
+        {{- end }}
       {{- if .Values.bookkeeper.resources }}
         resources:
 {{ toYaml .Values.bookkeeper.resources | indent 10 }}
@@ -113,13 +131,16 @@ spec:
         command: ["sh", "-c"]
         args:
         - >
-          bin/apply-config-from-env.py conf/bookkeeper.conf &&
-          bin/apply-config-from-env.py conf/pulsar_env.sh &&
-          bin/apply-config-from-env.py conf/bkenv.sh &&
-          bin/pulsar bookie
+          bin/apply-config-from-env.py conf/bookkeeper.conf;
+          bin/apply-config-from-env.py conf/pulsar_env.sh;
+          bin/apply-config-from-env.py conf/bkenv.sh;
+          {{- include "pulsar.bookkeeper.zookeeper.tls.settings" . | nindent 10 }}
+          bin/pulsar bookie;
         ports:
-        - name: client
-          containerPort: 3181
+        - name: bookie
+          containerPort: {{ .Values.bookkeeper.ports.bookie }}
+        - name: http
+          containerPort: {{ .Values.bookkeeper.ports.http }}
         envFrom:
         - configMapRef:
             name: "{{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }}"
@@ -128,14 +149,16 @@ spec:
           mountPath: /pulsar/data/bookkeeper/journal
         - name: "{{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }}-{{ .Values.bookkeeper.volumes.ledgers.name }}"
           mountPath: /pulsar/data/bookkeeper/ledgers
-        {{- if not .Values.persistence }}
+        {{- include "pulsar.bookkeeper.certs.volumeMounts" . | nindent 8 }}
       volumes:
+      {{- if not (and (and .Values.persistence .Values.volumes.persistence) .Values.bookkeeper.volumes.persistence) }}
       - name: "{{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }}-{{ .Values.bookkeeper.volumes.journal.name }}"
         emptyDir: {}
       - name: "{{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }}-{{ .Values.bookkeeper.volumes.ledgers.name }}"
         emptyDir: {}
-    {{- end }}
-{{- if .Values.persistence }}
+      {{- end }}
+      {{- include "pulsar.bookkeeper.certs.volumes" . | nindent 6 }}
+{{- if and (and .Values.persistence .Values.volumes.persistence) .Values.bookkeeper.volumes.persistence}}
   volumeClaimTemplates:
   - metadata:
       name: "{{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }}-{{ .Values.bookkeeper.volumes.journal.name }}"
@@ -146,8 +169,10 @@ spec:
           storage: {{ .Values.bookkeeper.volumes.journal.size }}
     {{- if .Values.bookkeeper.volumes.journal.storageClassName }}
       storageClassName: "{{ .Values.bookkeeper.volumes.journal.storageClassName }}"
-    {{- else if .Values.bookkeeper.volumes.journal.storageClass }}
+    {{- else if and (not (and .Values.volumes.local_storage .Values.bookkeeper.volumes.journal.local_storage)) .Values.bookkeeper.volumes.journal.storageClass }}
       storageClassName: "{{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }}-{{ .Values.bookkeeper.volumes.journal.name }}"
+    {{- else if and .Values.volumes.local_storage .Values.bookkeeper.volumes.journal.local_storage }}
+      storageClassName: "local-storage"
     {{- end }}
   - metadata:
       name: "{{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }}-{{ .Values.bookkeeper.volumes.ledgers.name }}"
@@ -158,7 +183,10 @@ spec:
           storage: {{ .Values.bookkeeper.volumes.ledgers.size }}
     {{- if .Values.bookkeeper.volumes.ledgers.storageClassName }}
       storageClassName: "{{ .Values.bookkeeper.volumes.ledgers.storageClassName }}"
-    {{- else if .Values.bookkeeper.volumes.ledgers.storageClass }}
+    {{- else if and (not (and .Values.volumes.local_storage .Values.bookkeeper.volumes.ledgers.local_storage)) .Values.bookkeeper.volumes.ledgers.storageClass }}
       storageClassName: "{{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }}-{{ .Values.bookkeeper.volumes.ledgers.name }}"
+    {{- else if and .Values.volumes.local_storage .Values.bookkeeper.volumes.ledgers.local_storage }}
+      storageClassName: "local-storage"
     {{- end }}
 {{- end }}
+{{- end }}
diff --git a/pulsar/templates/bookkeeper-storageclass.yaml b/pulsar/templates/bookkeeper-storageclass.yaml
index f6e7fc7..3e8ebce 100644
--- a/pulsar/templates/bookkeeper-storageclass.yaml
+++ b/pulsar/templates/bookkeeper-storageclass.yaml
@@ -17,20 +17,17 @@
 # under the License.
 #
 
-{{- if .Values.persistence }}
-{{- if .Values.bookkeeper.volumes.journal.storageClass }}
+{{- if .Values.components.bookkeeper }}
+{{- if and (and .Values.persistence .Values.volumes.persistence) .Values.bookkeeper.volumes.persistence }}
+{{- if and (not (and .Values.volumes.local_storage .Values.bookkeeper.volumes.journal.local_storage)) .Values.bookkeeper.volumes.journal.storageClass }}
 apiVersion: storage.k8s.io/v1
 kind: StorageClass
 metadata:
   name: "{{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }}-{{ .Values.bookkeeper.volumes.journal.name }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
     component: {{ .Values.bookkeeper.component }}
-    cluster: {{ template "pulsar.fullname" . }}
 provisioner: {{ .Values.bookkeeper.volumes.journal.storageClass.provisioner }}
 parameters:
   type: {{ .Values.bookkeeper.volumes.journal.storageClass.type }}
@@ -38,22 +35,20 @@ parameters:
 {{- end }}
 ---
 
-{{- if .Values.bookkeeper.volumes.ledgers.storageClass }}
+{{- if and (not (and .Values.volumes.local_storage .Values.bookkeeper.volumes.journal.local_storage)) .Values.bookkeeper.volumes.ledgers.storageClass }}
 apiVersion: storage.k8s.io/v1
 kind: StorageClass
 metadata:
   name: "{{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }}-{{ .Values.bookkeeper.volumes.ledgers.name }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
     component: {{ .Values.bookkeeper.component }}
-    cluster: {{ template "pulsar.fullname" . }}
 provisioner: {{ .Values.bookkeeper.volumes.ledgers.storageClass.provisioner }}
 parameters:
   type: {{ .Values.bookkeeper.volumes.ledgers.storageClass.type }}
   fsType: {{ .Values.bookkeeper.volumes.ledgers.storageClass.fsType }}
 {{- end }}
+
+{{- end }}
 {{- end }}
diff --git a/pulsar/templates/prometheus-rbac.yaml b/pulsar/templates/broker-cluster-role-binding.yaml
similarity index 56%
copy from pulsar/templates/prometheus-rbac.yaml
copy to pulsar/templates/broker-cluster-role-binding.yaml
index a25bbb9..803fccb 100644
--- a/pulsar/templates/prometheus-rbac.yaml
+++ b/pulsar/templates/broker-cluster-role-binding.yaml
@@ -17,43 +17,50 @@
 # under the License.
 #
 
-{{- if .Values.extra.monitoring }}
-{{- if .Values.prometheus_rbac }}
+{{- if .Values.components.broker }}
+## TODO create our own cluster role with less privledges than admin
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: "{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}-clusterrolebinding"
+  labels:
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: "{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}-clusterrole"
+subjects:
+- kind: ServiceAccount
+  name: "{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}-acct"
+  namespace: {{ .Values.namespace }}
+---
+
 apiVersion: rbac.authorization.k8s.io/v1beta1
 kind: ClusterRole
 metadata:
-  name: "{{ template "pulsar.fullname" . }}-{{ .Values.prometheus.component }}"
+  name: "{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}-clusterrole"
+  labels:
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
 rules:
 - apiGroups: [""]
   resources:
-  - nodes
-  - nodes/proxy
-  - services
-  - endpoints
-  - pods
+  - configmap
   verbs: ["get", "list", "watch"]
-- nonResourceURLs: ["/metrics"]
-  verbs: ["get"]
----
-
-apiVersion: v1
-kind: ServiceAccount
-metadata:
-  name: "{{ template "pulsar.fullname" . }}-{{ .Values.prometheus.component }}"
-  namespace: {{ .Values.namespace }}
+- apiGroups: ["", "extensions", "apps"]
+  resources:
+    - pods
+    - services
+    - deployments
+    - secrets
+    - statefulsets
+  verbs:
+    - list
+    - watch
+    - get
+    - update
+    - create
+    - delete
+    - patch
 ---
 
-apiVersion: rbac.authorization.k8s.io/v1beta1
-kind: ClusterRoleBinding
-metadata:
-  name: "{{ template "pulsar.fullname" . }}-{{ .Values.prometheus.component }}"
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: "{{ template "pulsar.fullname" . }}-{{ .Values.prometheus.component }}"
-subjects:
-- kind: ServiceAccount
-  name: "{{ template "pulsar.fullname" . }}-{{ .Values.prometheus.component }}"
-  namespace: {{ .Values.namespace }}
-{{- end }}
-{{- end }}
+{{- end }}
\ No newline at end of file
diff --git a/pulsar/templates/broker-configmap.yaml b/pulsar/templates/broker-configmap.yaml
index 6a35e39..721038a 100644
--- a/pulsar/templates/broker-configmap.yaml
+++ b/pulsar/templates/broker-configmap.yaml
@@ -17,31 +17,130 @@
 # under the License.
 #
 
+{{- if .Values.components.broker }}
 apiVersion: v1
 kind: ConfigMap
 metadata:
   name: "{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
     component: {{ .Values.broker.component }}
-    cluster: {{ template "pulsar.fullname" . }}
 data:
-  zookeeperServers:
-    {{- $global := . }}
-    {{ range $i, $e := until (.Values.zookeeper.replicaCount | int) }}{{ if ne $i 0 }},{{ end }}{{ template "pulsar.fullname" $global }}-{{ $global.Values.zookeeper.component }}-{{ printf "%d" $i }}.{{ template "pulsar.fullname" $global }}-{{ $global.Values.zookeeper.component }}{{ end }}
-  configurationStoreServers:
-    {{- $global := . }}
-    {{ range $i, $e := until (.Values.zookeeper.replicaCount | int) }}{{ if ne $i 0 }},{{ end }}{{ template "pulsar.fullname" $global }}-{{ $global.Values.zookeeper.component }}-{{ printf "%d" $i }}.{{ template "pulsar.fullname" $global }}-{{ $global.Values.zookeeper.component }}{{ end }}
+  # Metadata settings
+  zookeeperServers: "{{ template "pulsar.zookeeper.connect" . }}{{ .Values.metadataPrefix }}"
+  {{- if .Values.pulsar_metadata.configurationStore }}
+  configurationStoreServers: "{{ .Values.pulsar_metadata.configurationStore }}{{ .Values.pulsar_metadata.configurationStoreMetadataPrefix }}"
+  {{- end }}
+  {{- if not .Values.pulsar_metadata.configurationStore }}
+  configurationStoreServers: "{{ template "pulsar.zookeeper.connect" . }}{{ .Values.metadataPrefix }}"
+  {{- end }}
+
+  # Broker settings
   clusterName: {{ template "pulsar.fullname" . }}
+  exposeTopicLevelMetricsInPrometheus: "true"
+  numHttpServerThreads: "8"
+  zooKeeperSessionTimeoutMillis: "30000"
+  statusFilePath: "{{ template "pulsar.home" . }}/status"
+
+  # Function Worker Settings
+  # function worker configuration
+  {{- if not (or .Values.components.functions .Values.extra.functionsAsPods) }}
+  functionsWorkerEnabled: "false"
+  {{- end }}
+  {{- if or .Values.components.functions .Values.extra.functionsAsPods }}
   functionsWorkerEnabled: "true"
-  PF_pulsarFunctionsCluster: {{ template "pulsar.fullname" . }}
-  {{- if .Values.extra.functionsAsPods }}
   PF_functionRuntimeFactoryClassName: "org.apache.pulsar.functions.runtime.kubernetes.KubernetesRuntimeFactory"
+  PF_pulsarFunctionsCluster: {{ template "pulsar.fullname" . }}
+  PF_connectorsDirectory: ./connectors
+  PF_containerFactory: k8s
+  PF_numFunctionPackageReplicas: "{{ .Values.broker.configData.managedLedgerDefaultEnsembleSize }}"
+  # support version >= 2.5.0
+  PF_functionRuntimeFactoryConfigs_pulsarRootDir: {{ template "pulsar.home" . }}
+  PF_kubernetesContainerFactory_pulsarRootDir: {{ template "pulsar.home" . }}
+  PF_functionRuntimeFactoryConfigs_pulsarDockerImageName: "{{ .Values.images.functions.repository }}:{{ .Values.images.functions.tag }}"
   PF_functionRuntimeFactoryConfigs_submittingInsidePod: "true"
+  PF_functionRuntimeFactoryConfigs_installUserCodeDependencies: "true"
   PF_functionRuntimeFactoryConfigs_jobNamespace: {{ .Values.namespace }}
+  PF_functionRuntimeFactoryConfigs_expectedMetricsCollectionInterval: "30"
+  {{- if not (and .Values.tls.enabled .Values.tls.broker.enabled) }}
+  PF_functionRuntimeFactoryConfigs_pulsarAdminUrl: "http://{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}:{{ .Values.broker.ports.http }}/"
+  PF_functionRuntimeFactoryConfigs_pulsarServiceUrl: "pulsar://{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}:{{ .Values.broker.ports.pulsar }}/"
+  {{- end }}
+  {{- if and .Values.tls.enabled .Values.tls.broker.enabled }}
+  PF_functionRuntimeFactoryConfigs_pulsarAdminUrl: "https://{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}:{{ .Values.broker.ports.https }}/"
+  PF_functionRuntimeFactoryConfigs_pulsarServiceUrl: "pulsar+ssl://{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}:{{ .Values.broker.ports.pulsarssl }}/"
+  {{- end }}
+  PF_functionRuntimeFactoryConfigs_changeConfigMap: "{{ template "pulsar.fullname" . }}-{{ .Values.functions.component }}-config"
+  PF_functionRuntimeFactoryConfigs_changeConfigMapNamespace: {{ .Values.namespace }}
+  # support version < 2.5.0
+  PF_kubernetesContainerFactory_pulsarDockerImageName: "{{ .Values.images.functions.repository }}:{{ .Values.images.functions.tag }}"
+  PF_kubernetesContainerFactory_submittingInsidePod: "true"
+  PF_kubernetesContainerFactory_installUserCodeDependencies: "true"
+  PF_kubernetesContainerFactory_jobNamespace: {{ .Values.namespace }}
+  PF_kubernetesContainerFactory_expectedMetricsCollectionInterval: "30"
+  {{- if not (and .Values.tls.enabled .Values.tls.broker.enabled) }}
+  PF_kubernetesContainerFactory_pulsarAdminUrl: "http://{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}:{{ .Values.broker.ports.http }}/"
+  PF_kubernetesContainerFactory_pulsarServiceUrl: "pulsar://{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}:{{ .Values.broker.ports.pulsar }}/"
+  {{- end }}
+  {{- if and .Values.tls.enabled .Values.tls.broker.enabled }}
+  PF_kubernetesContainerFactory_pulsarAdminUrl: "https://{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}:{{ .Values.broker.ports.https }}/"
+  PF_kubernetesContainerFactory_pulsarServiceUrl: "pulsar+ssl://{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}:{{ .Values.broker.ports.pulsarssl }}/"
+  {{- end }}
+  PF_kubernetesContainerFactory_changeConfigMap: "{{ template "pulsar.fullname" . }}-{{ .Values.functions.component }}-config"
+  PF_kubernetesContainerFactory_changeConfigMapNamespace: {{ .Values.namespace }}
+  {{- end }}
+  
+  # prometheus needs to access /metrics endpoint
+  webServicePort: "{{ .Values.broker.ports.http }}"
+  {{- if or (not .Values.tls.enabled) (not .Values.tls.broker.enabled) }}
+  brokerServicePort: "{{ .Values.broker.ports.pulsar }}"
+  {{- end }}
+  {{- if and .Values.tls.enabled .Values.tls.broker.enabled }}
+  brokerServicePortTls: "{{ .Values.broker.ports.pulsarssl }}"
+  webServicePortTls: "{{ .Values.broker.ports.https }}"
+  # TLS Settings
+  tlsCertificateFilePath: "/pulsar/certs/broker/tls.crt"
+  tlsKeyFilePath: "/pulsar/certs/broker/tls.key"
+  tlsTrustCertsFilePath: "/pulsar/certs/ca/ca.crt"
+  {{- end }}
+
+  # Authentication Settings
+  {{- if .Values.auth.authentication.enabled }}
+  authenticationEnabled: "true"
+  {{- if .Values.auth.authorization.enabled }}
+  authorizationEnabled: "true"
+  superUserRoles: {{ .Values.auth.superUsers.broker }},{{ .Values.auth.superUsers.proxy }},{{ .Values.auth.superUsers.client }}
+  {{- end }}
+  {{- if eq .Values.auth.authentication.provider "jwt" }}
+  # token authentication configuration
+  authenticationProviders: "org.apache.pulsar.broker.authentication.AuthenticationProviderToken"
+  brokerClientAuthenticationParameters: "file:///pulsar/tokens/broker/token"
+  brokerClientAuthenticationPlugin: "org.apache.pulsar.client.impl.auth.AuthenticationToken"
+  {{- if .Values.auth.authentication.jwt.usingSecretKey }}
+  tokenSecretKey: "file:///pulsar/keys/token/secret.key"
+  {{- else }}
+  tokenPublicKey: "file:///pulsar/keys/token/public.key"
+  {{- end }}
+  {{- end }}
+  {{- end }}
+
+  {{- if and .Values.tls.enabled .Values.tls.bookie.enabled }}
+  # bookkeeper tls settings
+  bookkeeperTLSClientAuthentication: "true"
+  bookkeeperTLSKeyFileType: "PEM"
+  bookkeeperTLSKeyFilePath: "/pulsar/certs/broker/tls.key"
+  bookkeeperTLSCertificateFilePath: "/pulsar/certs/broker/tls.crt"
+  bookkeeperTLSTrustCertsFilePath: "/pulsar/certs/ca/ca.crt"
+  bookkeeperTLSTrustCertTypes: "PEM"
+  PULSAR_PREFIX_bookkeeperTLSClientAuthentication: "true"
+  PULSAR_PREFIX_bookkeeperTLSKeyFileType: "PEM"
+  PULSAR_PREFIX_bookkeeperTLSKeyFilePath: "/pulsar/certs/broker/tls.key"
+  PULSAR_PREFIX_bookkeeperTLSCertificateFilePath: "/pulsar/certs/broker/tls.crt"
+  PULSAR_PREFIX_bookkeeperTLSTrustCertsFilePath: "/pulsar/certs/ca/ca.crt"
+  PULSAR_PREFIX_bookkeeperTLSTrustCertTypes: "PEM"
+  # https://github.com/apache/bookkeeper/pull/2300
+  bookkeeperUseV2WireProtocol: "false"
   {{- end }}
 {{ toYaml .Values.broker.configData | indent 2 }}
+{{- end }}
\ No newline at end of file
diff --git a/pulsar/templates/broker-deployment.yaml b/pulsar/templates/broker-deployment.yaml
deleted file mode 100644
index 5571489..0000000
--- a/pulsar/templates/broker-deployment.yaml
+++ /dev/null
@@ -1,131 +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.
-#
-
-{{- $ensembleSize := .Values.broker.configData.managedLedgerDefaultEnsembleSize }}
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  name: "{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}"
-  namespace: {{ .Values.namespace }}
-  labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
-    component: {{ .Values.broker.component }}
-    cluster: {{ template "pulsar.fullname" . }}
-spec:
-  replicas: {{ .Values.broker.replicaCount }}
-  selector:
-    matchLabels:
-      app: {{ template "pulsar.name" . }}
-      release: {{ .Release.Name }}
-      component: {{ .Values.broker.component }}
-  template:
-    metadata:
-      labels:
-        app: {{ template "pulsar.name" . }}
-        release: {{ .Release.Name }}
-        component: {{ .Values.broker.component }}
-        cluster: {{ template "pulsar.fullname" . }}
-      annotations:
-{{ toYaml .Values.broker.annotations | indent 8 }}
-    spec:
-    {{- if .Values.broker.nodeSelector }}
-      nodeSelector:
-{{ toYaml .Values.broker.nodeSelector | indent 8 }}
-    {{- end }}
-    {{- if .Values.broker.tolerations }}
-      tolerations:
-{{ toYaml .Values.broker.tolerations | indent 8 }}
-    {{- end }}
-      {{- if .Values.extra.functionsAsPods }}
-      serviceAccount: "{{ template "pulsar.fullname" . }}-{{ .Values.broker.functions.component }}"
-      {{- end }}
-      affinity:
-        podAntiAffinity:
-          requiredDuringSchedulingIgnoredDuringExecution:
-          - labelSelector:
-              matchExpressions:
-              - key: "app"
-                operator: In
-                values:
-                - "{{ template "pulsar.name" . }}"
-              - key: "release"
-                operator: In
-                values:
-                - {{ .Release.Name }}
-              - key: "component"
-                operator: In
-                values:
-                - {{ .Values.broker.component }}
-            topologyKey: "kubernetes.io/hostname"
-      terminationGracePeriodSeconds: {{ .Values.broker.gracePeriod }}
-      initContainers:
-      # This init container will wait for zookeeper to be ready before
-      # deploying the bookies
-      - name: wait-zookeeper-ready
-        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
-        imagePullPolicy: {{ .Values.image.pullPolicy }}
-        command: ["sh", "-c"]
-        args:
-          - >-
-            until bin/pulsar zookeeper-shell -server {{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }} ls /admin/clusters/{{ template "pulsar.fullname" . }}; do
-              sleep 3;
-            done;
-      # This init container will wait for bookkeeper to be ready before
-      # deploying the broker
-      - name: wait-bookkeeper-ready
-        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
-        imagePullPolicy: {{ .Values.image.pullPolicy }}
-        command: ["sh", "-c"]
-        args:
-          - >-
-            bin/apply-config-from-env.py conf/bookkeeper.conf &&
-            until bin/bookkeeper shell simpletest -ensemble {{$ensembleSize}}; do
-              sleep 3;
-            done;
-      containers:
-      - name: "{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}"
-        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
-        imagePullPolicy: {{ .Values.image.pullPolicy }}
-      {{- if .Values.broker.resources }}
-        resources:
-{{ toYaml .Values.broker.resources | indent 10 }}
-      {{- end }}
-        command: ["sh", "-c"]
-        args:
-        - >
-          bin/apply-config-from-env.py conf/broker.conf &&
-          bin/apply-config-from-env.py conf/pulsar_env.sh &&
-          bin/gen-yml-from-env.py conf/functions_worker.yml &&
-          bin/pulsar broker
-        ports:
-        - name: http
-          containerPort: 8080
-        - name: pulsar
-          containerPort: 6650
-        envFrom:
-        - configMapRef:
-            name: "{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}"
-        env:
-        - name: advertisedAddress
-          valueFrom:
-            fieldRef:
-              fieldPath: status.podIP
diff --git a/pulsar/templates/broker-pdb.yaml b/pulsar/templates/broker-pdb.yaml
index 6f60d59..a3843a1 100644
--- a/pulsar/templates/broker-pdb.yaml
+++ b/pulsar/templates/broker-pdb.yaml
@@ -17,6 +17,7 @@
 # under the License.
 #
 
+{{- if .Values.components.broker }}
 {{- if .Values.broker.pdb.usePolicy }}
 apiVersion: policy/v1beta1
 kind: PodDisruptionBudget
@@ -24,17 +25,13 @@ metadata:
   name: "{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
     component: {{ .Values.broker.component }}
-    cluster: {{ template "pulsar.fullname" . }}
 spec:
   selector:
     matchLabels:
-      app: {{ template "pulsar.name" . }}
-      release: {{ .Release.Name }}
+      {{- include "pulsar.matchLabels" . | nindent 6 }}
       component: {{ .Values.broker.component }}
   maxUnavailable: {{ .Values.broker.pdb.maxUnavailable }}
 {{- end }}
+{{- end }}
diff --git a/pulsar/Chart.yaml b/pulsar/templates/broker-service-account.yaml
similarity index 72%
copy from pulsar/Chart.yaml
copy to pulsar/templates/broker-service-account.yaml
index f4cefc0..257f593 100644
--- a/pulsar/Chart.yaml
+++ b/pulsar/templates/broker-service-account.yaml
@@ -17,8 +17,13 @@
 # under the License.
 #
 
+{{- if .Values.components.broker }}
 apiVersion: v1
-appVersion: "1.0"
-description: Apache Pulsar Helm chart for Kubernetes
-name: pulsar
-version: 1.0.0
+kind: ServiceAccount
+metadata:
+  name: "{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}-acct"
+  namespace: {{ .Values.namespace }}
+  labels:
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
+    component: {{ .Values.broker.component }}
+{{- end }}
\ No newline at end of file
diff --git a/pulsar/templates/broker-service.yaml b/pulsar/templates/broker-service.yaml
index 8cfe718..2b30333 100644
--- a/pulsar/templates/broker-service.yaml
+++ b/pulsar/templates/broker-service.yaml
@@ -17,25 +17,35 @@
 # under the License.
 #
 
+{{- if .Values.components.broker }}
 apiVersion: v1
 kind: Service
 metadata:
   name: "{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
     component: {{ .Values.broker.component }}
-    cluster: {{ template "pulsar.fullname" . }}
   annotations:
 {{ toYaml .Values.broker.service.annotations | indent 4 }}
 spec:
   ports:
-{{ toYaml .Values.broker.service.ports | indent 2 }}
+  # prometheus needs to access /metrics endpoint
+  - name: http
+    port: {{ .Values.broker.ports.http }}
+  {{- if or (not .Values.tls.enabled) (not .Values.tls.broker.enabled) }}
+  - name: pulsar
+    port: {{ .Values.broker.ports.pulsar }}
+  {{- end }}
+  {{- if and .Values.tls.enabled .Values.tls.broker.enabled }}
+  - name: https
+    port: {{ .Values.broker.ports.https }}
+  - name: pulsarssl
+    port: {{ .Values.broker.ports.pulsarssl }}
+  {{- end }}
   clusterIP: None
   selector:
     app: {{ template "pulsar.name" . }}
     release: {{ .Release.Name }}
     component: {{ .Values.broker.component }}
+{{- end }}
diff --git a/pulsar/templates/broker-statefulset.yaml b/pulsar/templates/broker-statefulset.yaml
new file mode 100644
index 0000000..591c104
--- /dev/null
+++ b/pulsar/templates/broker-statefulset.yaml
@@ -0,0 +1,236 @@
+#
+# 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 .Values.components.broker }}
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+  name: "{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}"
+  namespace: {{ .Values.namespace }}
+  labels:
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
+    component: {{ .Values.broker.component }}
+spec:
+  serviceName: "{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}"
+  replicas: {{ .Values.broker.replicaCount }}
+  selector:
+    matchLabels:
+      {{- include "pulsar.matchLabels" . | nindent 6 }}
+      component: {{ .Values.broker.component }}
+  updateStrategy:
+    type: RollingUpdate
+  podManagementPolicy: Parallel
+  template:
+    metadata:
+      labels:
+        {{- include "pulsar.template.labels" . | nindent 8 }}
+        component: {{ .Values.broker.component }}
+      annotations:
+        prometheus.io/scrape: "true"
+        prometheus.io/port: "{{ .Values.broker.ports.http }}"
+{{- with .Values.broker.annotations }}
+{{ toYaml . | indent 8 }}
+{{- end }}
+    spec:
+      serviceAccountName: "{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}-acct"
+    {{- if .Values.broker.nodeSelector }}
+      nodeSelector:
+{{ toYaml .Values.broker.nodeSelector | indent 8 }}
+    {{- end }}
+    {{- if .Values.broker.tolerations }}
+      tolerations:
+{{ toYaml .Values.broker.tolerations | indent 8 }}
+    {{- end }}
+      affinity:
+        {{- if and .Values.affinity.anti_affinity .Values.broker.affinity.anti_affinity}}
+        podAntiAffinity:
+          requiredDuringSchedulingIgnoredDuringExecution:
+          - labelSelector:
+              matchExpressions:
+              - key: "app"
+                operator: In
+                values:
+                - "{{ template "pulsar.name" . }}-{{ .Values.broker.component }}"
+              - key: "release"
+                operator: In
+                values:
+                - {{ .Release.Name }}
+              - key: "component"
+                operator: In
+                values:
+                - {{ .Values.broker.component }}
+            topologyKey: "kubernetes.io/hostname"
+        {{- end }}
+      terminationGracePeriodSeconds: {{ .Values.broker.gracePeriod }}
+      initContainers:
+      # This init container will wait for zookeeper to be ready before
+      # deploying the bookies
+      - name: wait-zookeeper-ready
+        image: "{{ .Values.images.broker.repository }}:{{ .Values.images.broker.tag }}"
+        imagePullPolicy: {{ .Values.images.broker.pullPolicy }}
+        command: ["sh", "-c"]
+        args:
+          - >-
+            {{- include "pulsar.broker.zookeeper.tls.settings" . | nindent 12 }}
+            {{- if .Values.pulsar_metadata.configurationStore }}
+            until bin/bookkeeper org.apache.zookeeper.ZooKeeperMain -server {{ .Values.pulsar_metadata.configurationStore}} get {{ .Values.configurationStoreMetadataPrefix }}/admin/clusters/{{ template "pulsar.fullname" . }}; do
+            {{- end }}
+            {{- if not .Values.pulsar_metadata.configurationStore }}
+            until bin/bookkeeper org.apache.zookeeper.ZooKeeperMain -server {{ template "pulsar.zookeeper.connect" . }} get {{ .Values.metadataPrefix }}/admin/clusters/{{ template "pulsar.fullname" . }}; do
+            {{- end }}
+              echo "pulsar cluster {{ template "pulsar.fullname" . }} isn't initialized yet ... check in 3 seconds ..." && sleep 3;
+            done;
+        volumeMounts:
+        {{- include "pulsar.broker.certs.volumeMounts" . | nindent 8 }}
+      # This init container will wait for bookkeeper to be ready before
+      # deploying the broker
+      - name: wait-bookkeeper-ready
+        image: "{{ .Values.images.broker.repository }}:{{ .Values.images.broker.tag }}"
+        imagePullPolicy: {{ .Values.images.broker.pullPolicy }}
+        command: ["sh", "-c"]
+        args:
+          - >
+            {{- include "pulsar.broker.zookeeper.tls.settings" . | nindent 12 }}
+            bin/apply-config-from-env.py conf/bookkeeper.conf;
+            until bin/bookkeeper shell whatisinstanceid; do
+              echo "bookkeeper cluster is not initialized yet. backoff for 3 seconds ...";
+              sleep 3;
+            done;
+            echo "bookkeeper cluster is already initialized";
+            bookieServiceNumber="$(nslookup -timeout=10 {{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }} | grep Name | wc -l)";
+            until [ ${bookieServiceNumber} -ge {{ .Values.broker.configData.managedLedgerDefaultEnsembleSize }} ]; do
+              echo "bookkeeper cluster {{ template "pulsar.fullname" . }} isn't ready yet ... check in 10 seconds ...";
+              sleep 10;
+              bookieServiceNumber="$(nslookup -timeout=10 {{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }} | grep Name | wc -l)";
+            done;
+            echo "bookkeeper cluster is ready";
+        envFrom:
+          - configMapRef:
+              name: "{{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }}"
+        volumeMounts:
+        {{- include "pulsar.broker.certs.volumeMounts" . | nindent 10 }}
+      containers:
+      - name: "{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}"
+        image: "{{ .Values.images.broker.repository }}:{{ .Values.images.broker.tag }}"
+        imagePullPolicy: {{ .Values.images.broker.pullPolicy }}
+        {{- if .Values.broker.probe.liveness.enabled }}
+        livenessProbe:
+          httpGet:
+            path: /status.html
+            port: {{ .Values.broker.ports.http }}
+          initialDelaySeconds: {{ .Values.broker.probe.liveness.initialDelaySeconds }}
+          periodSeconds: {{ .Values.broker.probe.liveness.periodSeconds }}
+          failureThreshold: {{ .Values.broker.probe.liveness.failureThreshold }}
+        {{- end }}
+        {{- if .Values.broker.probe.readiness.enabled }}
+        readinessProbe:
+          httpGet:
+            path: /status.html
+            port: {{ .Values.broker.ports.http }}
+          initialDelaySeconds: {{ .Values.broker.probe.readiness.initialDelaySeconds }}
+          periodSeconds: {{ .Values.broker.probe.readiness.periodSeconds }}
+          failureThreshold: {{ .Values.broker.probe.readiness.failureThreshold }}
+        {{- end }}
+        {{- if .Values.broker.probe.startup.enabled }}
+        startupProbe:
+          httpGet:
+            path: /status.html
+            port: {{ .Values.broker.ports.http }}
+          initialDelaySeconds: {{ .Values.broker.probe.startup.initialDelaySeconds }}
+          periodSeconds: {{ .Values.broker.probe.startup.periodSeconds }}
+          failureThreshold: {{ .Values.broker.probe.startup.failureThreshold }}
+        {{- end }}
+      {{- if .Values.broker.resources }}
+        resources:
+{{ toYaml .Values.broker.resources | indent 10 }}
+      {{- end }}
+        command: ["sh", "-c"]
+        args:
+        - >
+          bin/apply-config-from-env.py conf/broker.conf;
+          bin/apply-config-from-env.py conf/pulsar_env.sh;
+          bin/gen-yml-from-env.py conf/functions_worker.yml;
+          echo "OK" > status;
+          {{- include "pulsar.broker.zookeeper.tls.settings" . | nindent 10 }}
+          bin/pulsar zookeeper-shell -server {{ template "pulsar.zookeeper.connect" . }} get {{ template "pulsar.broker.znode" . }};
+          while [ $? -eq 0 ]; do
+            echo "broker {{ template "pulsar.broker.hostname" . }} znode still exists ... check in 10 seconds ...";
+            sleep 10;
+            bin/pulsar zookeeper-shell -server {{ template "pulsar.zookeeper.connect" . }} get {{ template "pulsar.broker.znode" . }};
+          done;
+          cat conf/pulsar_env.sh;
+          bin/pulsar broker;
+        ports:
+        # prometheus needs to access /metrics endpoint
+        - name: http
+          containerPort: {{ .Values.broker.ports.http }}
+        {{- if or (not .Values.tls.enabled) (not .Values.tls.broker.enabled) }}
+        - name: pulsar
+          containerPort: {{ .Values.broker.ports.pulsar }}
+        {{- end }}
+        {{- if and .Values.tls.enabled .Values.tls.broker.enabled }}
+        - name: https
+          containerPort: {{ .Values.broker.ports.https }}
+        - name: pulsarssl
+          containerPort: {{ .Values.broker.ports.pulsarssl }}
+        {{- end }}
+        envFrom:
+        - configMapRef:
+            name: "{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}"
+        volumeMounts:
+          {{- if .Values.auth.authentication.enabled }}
+          {{- if eq .Values.auth.authentication.provider "jwt" }}
+          - mountPath: "/pulsar/keys"
+            name: token-keys
+            readOnly: true
+          - mountPath: "/pulsar/tokens"
+            name: broker-token
+            readOnly: true
+          {{- end }}
+          {{- end }}
+          {{- include "pulsar.broker.certs.volumeMounts" . | nindent 10 }}
+      volumes:
+      {{- if .Values.auth.authentication.enabled }}
+      {{- if eq .Values.auth.authentication.provider "jwt" }}
+      - name: token-keys
+        secret:
+          {{- if not .Values.auth.authentication.jwt.usingSecretKey }}
+          secretName: "{{ .Release.Name }}-token-asymmetric-key"
+          {{- end}}
+          {{- if .Values.auth.authentication.jwt.usingSecretKey }}
+          secretName: "{{ .Release.Name }}-token-symmetric-key"
+          {{- end}}
+          items:
+            {{- if .Values.auth.authentication.jwt.usingSecretKey }}
+            - key: SECRETKEY
+              path: token/secret.key
+            {{- else }}
+            - key: PUBLICKEY
+              path: token/public.key
+            {{- end}}
+      - name: broker-token
+        secret:
+          secretName: "{{ .Release.Name }}-token-{{ .Values.auth.superUsers.broker }}"
+          items:
+            - key: TOKEN
+              path: broker/token
+      {{- end}}
+      {{- end}}
+      {{- include "pulsar.broker.certs.volumes" . | nindent 6 }}
+{{- end }}
\ No newline at end of file
diff --git a/pulsar/templates/zookeeper-configmap.yaml b/pulsar/templates/function-worker-configmap.yaml
similarity index 69%
copy from pulsar/templates/zookeeper-configmap.yaml
copy to pulsar/templates/function-worker-configmap.yaml
index 754f814..98d9cfd 100644
--- a/pulsar/templates/zookeeper-configmap.yaml
+++ b/pulsar/templates/function-worker-configmap.yaml
@@ -17,17 +17,16 @@
 # under the License.
 #
 
+{{- if .Values.components.functions }}
+## function config map
 apiVersion: v1
 kind: ConfigMap
 metadata:
-  name: "{{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}"
+  name: "{{ template "pulsar.fullname" . }}-{{ .Values.functions.component }}-config"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
-    component: {{ .Values.zookeeper.component }}
-    cluster: {{ template "pulsar.fullname" . }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
+    component: {{ .Values.functions.component }}
 data:
-{{ toYaml .Values.zookeeper.configData | indent 2 }}
+  pulsarDockerImageName: "{{ .Values.images.functions.repository }}:{{ .Values.images.functions.tag }}"
+{{- end }}
\ No newline at end of file
diff --git a/pulsar/templates/grafana-deployment.yaml b/pulsar/templates/grafana-deployment.yaml
index 45f83de..675d897 100644
--- a/pulsar/templates/grafana-deployment.yaml
+++ b/pulsar/templates/grafana-deployment.yaml
@@ -17,35 +17,30 @@
 # under the License.
 #
 
-{{- if .Values.extra.monitoring }}
+{{- if or .Values.monitoring.grafana .Values.extra.monitoring }}
 apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: "{{ template "pulsar.fullname" . }}-{{ .Values.grafana.component }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
     component: {{ .Values.grafana.component }}
-    cluster: {{ template "pulsar.fullname" . }}
 spec:
   replicas: {{ .Values.grafana.replicaCount }}
   selector:
     matchLabels:
-      app: {{ template "pulsar.name" . }}
-      release: {{ .Release.Name }}
+      {{- include "pulsar.matchLabels" . | nindent 6 }}
       component: {{ .Values.grafana.component }}
   template:
     metadata:
       labels:
-        app: {{ template "pulsar.name" . }}
-        release: {{ .Release.Name }}
+        {{- include "pulsar.template.labels" . | nindent 8 }}
         component: {{ .Values.grafana.component }}
-        cluster: {{ template "pulsar.fullname" . }}
       annotations:
-{{ toYaml .Values.grafana.annotations | indent 8 }}
+{{- with .Values.grafana.annotations }}
+{{ toYaml . | indent 8 }}
+{{- end }}
     spec:
     {{- if .Values.grafana.nodeSelector }}
       nodeSelector:
@@ -58,15 +53,32 @@ spec:
       terminationGracePeriodSeconds: {{ .Values.grafana.gracePeriod }}
       containers:
       - name: "{{ template "pulsar.fullname" . }}-{{ .Values.grafana.component }}"
-        image: "{{ .Values.grafana.image.repository }}:{{ .Values.grafana.image.tag }}"
-        imagePullPolicy: {{ .Values.grafana.image.pullPolicy }}
+        image: "{{ .Values.images.grafana.repository }}:{{ .Values.images.grafana.tag }}"
+        imagePullPolicy: {{ .Values.images.grafana.pullPolicy }}
       {{- if .Values.grafana.resources }}
         resources:
 {{ toYaml .Values.grafana.resources | indent 10 }}
       {{- end }}
         ports:
-        - containerPort: 3000
+        - name: server
+          containerPort: {{ .Values.grafana.port }}
         env:
+        # for supporting apachepulsar/pulsar-grafana
         - name: PROMETHEUS_URL
           value: http://{{ template "pulsar.fullname" . }}-{{ .Values.prometheus.component }}:9090/
+        # for supporting streamnative/apache-pulsar-grafana-dashboard
+        - name: PULSAR_PROMETHEUS_URL
+          value: http://{{ template "pulsar.fullname" . }}-{{ .Values.prometheus.component }}:9090/
+        - name: PULSAR_CLUSTER
+          value: {{ template "pulsar.fullname" . }}
+        - name: GRAFANA_ADMIN_USER
+          valueFrom:
+            secretKeyRef:
+              name: "{{ template "pulsar.fullname" . }}-admin-secret"
+              key: USER
+        - name: GRAFANA_ADMIN_PASSWORD
+          valueFrom:
+            secretKeyRef:
+              name: "{{ template "pulsar.fullname" . }}-admin-secret"
+              key: PASSWORD
 {{- end }}
diff --git a/pulsar/templates/grafana-service.yaml b/pulsar/templates/grafana-service.yaml
index 56dadde..2c1ddd1 100644
--- a/pulsar/templates/grafana-service.yaml
+++ b/pulsar/templates/grafana-service.yaml
@@ -17,28 +17,27 @@
 # under the License.
 #
 
-{{- if .Values.extra.monitoring }}
+{{- if or .Values.monitoring.grafana .Values.extra.monitoring }}
 apiVersion: v1
 kind: Service
 metadata:
   name: "{{ template "pulsar.fullname" . }}-{{ .Values.grafana.component }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
     component: {{ .Values.grafana.component }}
-    cluster: {{ template "pulsar.fullname" . }}
   annotations:
-{{ toYaml .Values.grafana.service.annotations | indent 4 }}
+{{- with .Values.grafana.service.annotations }}
+{{ toYaml . | indent 4 }}
+{{- end }}
 spec:
+  type: {{ .Values.grafana.service.type }}
   ports:
-{{ toYaml .Values.grafana.service.ports | indent 2 }}
+    - name: server
+      port: {{ .Values.grafana.port }}
+      protocol: TCP
   selector:
-    app: {{ template "pulsar.name" . }}
-    release: {{ .Release.Name }}
+    {{- include "pulsar.matchLabels" . | nindent 4 }}
     component: {{ .Values.grafana.component }}
-  type: ClusterIP
   sessionAffinity: None
 {{- end }}
diff --git a/pulsar/templates/keytool.yaml b/pulsar/templates/keytool.yaml
new file mode 100644
index 0000000..b3da4a0
--- /dev/null
+++ b/pulsar/templates/keytool.yaml
@@ -0,0 +1,98 @@
+#
+# 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.
+#
+
+# script to process key/cert to keystore and truststore
+{{- if .Values.tls.zookeeper.enabled }}
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: "{{ template "pulsar.fullname" . }}-keytool-configmap"
+  namespace: {{ .Values.namespace }}
+  labels:
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
+    component: keytool 
+data:
+  keytool.sh: |
+    #!/bin/bash
+    component=$1
+    name=$2
+    isClient=$3
+    crtFile=/pulsar/certs/${component}/tls.crt
+    keyFile=/pulsar/certs/${component}/tls.key
+    caFile=/pulsar/certs/ca/ca.crt
+    p12File=/pulsar/${component}.p12
+    keyStoreFile=/pulsar/${component}.keystore.jks
+    trustStoreFile=/pulsar/${component}.truststore.jks
+    
+    function ensureFileNotEmpty() {
+        local file=$1
+        local len=$(wc -c ${file} | awk '{print $1}')
+        echo "processing ${file} : len = ${len}"
+        if [ ! -f ${file} ]; then
+            echo "${file} is not found"
+            exit -1
+        fi
+        if [ $len -le 0 ]; then
+            echo "${file} is empty"
+            exit -1
+        fi
+    }
+    
+    ensureFileNotEmpty ${crtFile}
+    ensureFileNotEmpty ${keyFile}
+    ensureFileNotEmpty ${caFile}
+    
+    PASSWORD=$(head /dev/urandom | base64 | head -c 24)
+    
+    openssl pkcs12 \
+        -export \
+        -in ${crtFile} \
+        -inkey ${keyFile} \
+        -out ${p12File} \
+        -name ${name} \
+        -passout "pass:${PASSWORD}"
+    
+    keytool -importkeystore \
+        -srckeystore ${p12File} \
+        -srcstoretype PKCS12 -srcstorepass "${PASSWORD}" \
+        -alias ${name} \
+        -destkeystore ${keyStoreFile} \
+        -deststorepass "${PASSWORD}"
+    
+    keytool -import \
+        -file ${caFile} \
+        -storetype JKS \
+        -alias ${name} \
+        -keystore ${trustStoreFile} \
+        -storepass "${PASSWORD}" \
+        -trustcacerts -noprompt
+    
+    ensureFileNotEmpty ${keyStoreFile}
+    ensureFileNotEmpty ${trustStoreFile}
+    
+    if [[ "x${isClient}" == "xtrue" ]]; then
+        echo $'\n' >> conf/pulsar_env.sh
+        echo "PULSAR_EXTRA_OPTS=\"${PULSAR_EXTRA_OPTS} -Dzookeeper.clientCnxnSocket=org.apache.zookeeper.ClientCnxnSocketNetty -Dzookeeper.client.secure=true -Dzookeeper.ssl.keyStore.location=${keyStoreFile} -Dzookeeper.ssl.keyStore.password=${PASSWORD} -Dzookeeper.ssl.trustStore.location=${trustStoreFile} -Dzookeeper.ssl.trustStore.password=${PASSWORD}\"" >> conf/pulsar_env.sh
+        echo $'\n' >> conf/bkenv.sh
+        echo "BOOKIE_EXTRA_OPTS=\"${BOOKIE_EXTRA_OPTS} -Dzookeeper.clientCnxnSocket=org.apache.zookeeper.ClientCnxnSocketNetty -Dzookeeper.client.secure=true -Dzookeeper.ssl.keyStore.location=${keyStoreFile} -Dzookeeper.ssl.keyStore.password=${PASSWORD} -Dzookeeper.ssl.trustStore.location=${trustStoreFile} -Dzookeeper.ssl.trustStore.password=${PASSWORD}\"" >> conf/bkenv.sh
+    else
+        echo $'\n' >> conf/pulsar_env.sh
+        echo "PULSAR_EXTRA_OPTS=\"${PULSAR_EXTRA_OPTS} -Dzookeeper.ssl.keyStore.location=${keyStoreFile} -Dzookeeper.ssl.keyStore.password=${PASSWORD} -Dzookeeper.ssl.trustStore.location=${trustStoreFile} -Dzookeeper.ssl.trustStore.password=${PASSWORD}\"" >> conf/pulsar_env.sh
+    fi
+{{- end }}
diff --git a/pulsar/templates/prometheus-configmap.yaml b/pulsar/templates/prometheus-configmap.yaml
index 889abf5..ab971ad 100644
--- a/pulsar/templates/prometheus-configmap.yaml
+++ b/pulsar/templates/prometheus-configmap.yaml
@@ -17,19 +17,15 @@
 # under the License.
 #
 
-{{- if .Values.extra.monitoring }}
+{{- if or .Values.monitoring.prometheus .Values.extra.monitoring }}
 apiVersion: v1
 kind: ConfigMap
 metadata:
   name: "{{ template "pulsar.fullname" . }}-{{ .Values.prometheus.component }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
     component: {{ .Values.prometheus.component }}
-    cluster: {{ template "pulsar.fullname" . }}
 data:
   # Include prometheus configuration file, setup to monitor all the
   # Kubernetes pods with the "scrape=true" annotation.
diff --git a/pulsar/templates/prometheus-deployment.yaml b/pulsar/templates/prometheus-deployment.yaml
index e722993..75bae72 100644
--- a/pulsar/templates/prometheus-deployment.yaml
+++ b/pulsar/templates/prometheus-deployment.yaml
@@ -17,33 +17,26 @@
 # under the License.
 #
 
-{{- if .Values.extra.monitoring }}
+{{- if or .Values.monitoring.prometheus .Values.extra.monitoring }}
 apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: "{{ template "pulsar.fullname" . }}-{{ .Values.prometheus.component }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
     component: {{ .Values.prometheus.component }}
-    cluster: {{ template "pulsar.fullname" . }}
 spec:
   replicas: {{ .Values.prometheus.replicaCount }}
   selector:
     matchLabels:
-      app: {{ template "pulsar.name" . }}
-      release: {{ .Release.Name }}
+      {{- include "pulsar.matchLabels" . | nindent 6 }}
       component: {{ .Values.prometheus.component }}
   template:
     metadata:
       labels:
-        app: {{ template "pulsar.name" . }}
-        release: {{ .Release.Name }}
+        {{- include "pulsar.template.labels" . | nindent 8 }}
         component: {{ .Values.prometheus.component }}
-        cluster: {{ template "pulsar.fullname" . }}
       annotations:
 {{ toYaml .Values.prometheus.annotations | indent 8 }}
     spec:
@@ -55,20 +48,21 @@ spec:
       tolerations:
 {{ toYaml .Values.prometheus.tolerations | indent 8 }}
     {{- end }}
-    {{- if .Values.prometheus_rbac }}
+    {{- if or .Values.prometheus.rbac.enabled .Values.prometheus_rbac }}
       serviceAccount: "{{ template "pulsar.fullname" . }}-{{ .Values.prometheus.component }}"
     {{- end }}
       terminationGracePeriodSeconds: {{ .Values.prometheus.gracePeriod }}
       containers:
       - name: "{{ template "pulsar.fullname" . }}-{{ .Values.prometheus.component }}"
-        image: "{{ .Values.prometheus.image.repository }}:{{ .Values.prometheus.image.tag }}"
-        imagePullPolicy: {{ .Values.prometheus.image.pullPolicy }}
+        image: "{{ .Values.images.prometheus.repository }}:{{ .Values.images.prometheus.tag }}"
+        imagePullPolicy: {{ .Values.images.prometheus.pullPolicy }}
       {{- if .Values.prometheus.resources }}
         resources:
 {{ toYaml .Values.prometheus.resources | indent 10 }}
       {{- end }}
         ports:
-        - containerPort: 9090
+        - name: server
+          containerPort: {{ .Values.prometheus.port }}
         volumeMounts:
         - name: "{{ template "pulsar.fullname" . }}-{{ .Values.prometheus.component }}-config"
           mountPath: /etc/prometheus
@@ -78,11 +72,11 @@ spec:
       - name: "{{ template "pulsar.fullname" . }}-{{ .Values.prometheus.component }}-config"
         configMap:
           name: "{{ template "pulsar.fullname" . }}-{{ .Values.prometheus.component }}"
-    {{- if not .Values.prometheus_persistence }}
+    {{- if not (and (and .Values.persistence .Values.volumes.persistence) .Values.prometheus.volumes.persistence) }}
       - name: "{{ template "pulsar.fullname" . }}-{{ .Values.prometheus.component }}-{{ .Values.prometheus.volumes.data.name }}"
         emptyDir: {}
     {{- end }}
-    {{- if .Values.prometheus_persistence }}
+    {{- if and (and .Values.persistence .Values.volumes.persistence) .Values.prometheus.volumes.persistence }}
       - name: "{{ template "pulsar.fullname" . }}-{{ .Values.prometheus.component }}-{{ .Values.prometheus.volumes.data.name }}"
         persistentVolumeClaim:
           claimName: "{{ template "pulsar.fullname" . }}-{{ .Values.prometheus.component }}-{{ .Values.prometheus.volumes.data.name }}"
diff --git a/pulsar/templates/prometheus-pvc.yaml b/pulsar/templates/prometheus-pvc.yaml
index bccb773..d647db3 100644
--- a/pulsar/templates/prometheus-pvc.yaml
+++ b/pulsar/templates/prometheus-pvc.yaml
@@ -17,8 +17,8 @@
 # under the License.
 #
 
-{{- if .Values.extra.monitoring }}
-{{- if .Values.persistence }}
+{{- if or .Values.monitoring.prometheus .Values.extra.monitoring }}
+{{- if and (and .Values.persistence .Values.volumes.persistence) .Values.prometheus.volumes.persistence }}
 apiVersion: v1
 kind: PersistentVolumeClaim
 metadata:
@@ -31,8 +31,10 @@ spec:
   accessModes: [ "ReadWriteOnce" ]
 {{- if .Values.prometheus.volumes.data.storageClassName }}
   storageClassName: "{{ .Values.prometheus.volumes.data.storageClassName }}"
-{{- else if .Values.prometheus.volumes.data.storageClass }}
+{{- else if and (not (and .Values.volumes.local_storage .Values.prometheus.volumes.data.local_storage)) .Values.prometheus.volumes.data.storageClass }}
   storageClassName: "{{ template "pulsar.fullname" . }}-{{ .Values.prometheus.component }}-{{ .Values.prometheus.volumes.data.name }}"
+{{- else if and .Values.volumes.local_storage .Values.prometheus.volumes.data.local_storage }}
+  storageClassName: "local-storage"
 {{- end }}
 {{- end }}
 {{- end }}
diff --git a/pulsar/templates/prometheus-rbac.yaml b/pulsar/templates/prometheus-rbac.yaml
index a25bbb9..d027676 100644
--- a/pulsar/templates/prometheus-rbac.yaml
+++ b/pulsar/templates/prometheus-rbac.yaml
@@ -17,8 +17,8 @@
 # under the License.
 #
 
-{{- if .Values.extra.monitoring }}
-{{- if .Values.prometheus_rbac }}
+{{- if or .Values.monitoring.prometheus .Values.extra.monitoring }}
+{{- if or .Values.prometheus.rbac.enabled .Values.prometheus_rbac }}
 apiVersion: rbac.authorization.k8s.io/v1beta1
 kind: ClusterRole
 metadata:
diff --git a/pulsar/templates/prometheus-service.yaml b/pulsar/templates/prometheus-service.yaml
index 965256d..c4a660d 100644
--- a/pulsar/templates/prometheus-service.yaml
+++ b/pulsar/templates/prometheus-service.yaml
@@ -17,25 +17,22 @@
 # under the License.
 #
 
-{{- if .Values.extra.monitoring }}
+{{- if or .Values.monitoring.prometheus .Values.extra.monitoring }}
 apiVersion: v1
 kind: Service
 metadata:
   name: "{{ template "pulsar.fullname" . }}-{{ .Values.prometheus.component }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
     component: {{ .Values.prometheus.component }}
-    cluster: {{ template "pulsar.fullname" . }}
   annotations:
 {{ toYaml .Values.prometheus.service.annotations | indent 4 }}
 spec:
-  ports:
-{{ toYaml .Values.prometheus.service.ports | indent 2 }}
   clusterIP: None
+  ports:
+    - name: server
+      port: {{ .Values.prometheus.port }}
   selector:
     app: {{ template "pulsar.name" . }}
     release: {{ .Release.Name }}
diff --git a/pulsar/templates/prometheus-storageclass.yaml b/pulsar/templates/prometheus-storageclass.yaml
index 1623a5f..7cda9e0 100644
--- a/pulsar/templates/prometheus-storageclass.yaml
+++ b/pulsar/templates/prometheus-storageclass.yaml
@@ -17,8 +17,8 @@
 # under the License.
 #
 
-{{- if .Values.extra.monitoring }}
-{{- if .Values.persistence }}
+{{- if or .Values.monitoring.prometheus .Values.extra.monitoring }}
+{{- if and (and .Values.persistence .Values.volumes.persistence) .Values.prometheus.volumes.persistence }}
 {{- if .Values.prometheus.volumes.data.storageClass }}
 apiVersion: storage.k8s.io/v1
 kind: StorageClass
@@ -26,12 +26,8 @@ metadata:
   name: "{{ template "pulsar.fullname" . }}-{{ .Values.prometheus.component }}-{{ .Values.prometheus.volumes.data.name }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
     component: {{ .Values.prometheus.component }}
-    cluster: {{ template "pulsar.fullname" . }}
 provisioner: {{ .Values.prometheus.volumes.data.storageClass.provisioner }}
 parameters:
   type: {{ .Values.prometheus.volumes.data.storageClass.type }}
diff --git a/pulsar/templates/proxy-configmap.yaml b/pulsar/templates/proxy-configmap.yaml
index 4d9d3ad..491d1bc 100644
--- a/pulsar/templates/proxy-configmap.yaml
+++ b/pulsar/templates/proxy-configmap.yaml
@@ -17,26 +17,67 @@
 # under the License.
 #
 
-{{- if .Values.extra.proxy }}
+{{- if or .Values.components.proxy .Values.extra.proxy }}
 apiVersion: v1
 kind: ConfigMap
 metadata:
   name: "{{ template "pulsar.fullname" . }}-{{ .Values.proxy.component }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
     component: {{ .Values.proxy.component }}
-    cluster: {{ template "pulsar.fullname" . }}
 data:
-  zookeeperServers:
-    {{- $global := . }}
-    {{ range $i, $e := until (.Values.zookeeper.replicaCount | int) }}{{ if ne $i 0 }},{{ end }}{{ template "pulsar.fullname" $global }}-{{ $global.Values.zookeeper.component }}-{{ printf "%d" $i }}.{{ template "pulsar.fullname" $global }}-{{ $global.Values.zookeeper.component }}{{ end }}
-  configurationStoreServers:
-    {{- $global := . }}
-    {{ range $i, $e := until (.Values.zookeeper.replicaCount | int) }}{{ if ne $i 0 }},{{ end }}{{ template "pulsar.fullname" $global }}-{{ $global.Values.zookeeper.component }}-{{ printf "%d" $i }}.{{ template "pulsar.fullname" $global }}-{{ $global.Values.zookeeper.component }}{{ end }}
   clusterName: {{ template "pulsar.fullname" . }}
+  httpNumThreads: "8"
+  statusFilePath: "{{ template "pulsar.home" . }}/status"
+  # prometheus needs to access /metrics endpoint
+  webServicePort: "{{ .Values.proxy.ports.http }}"
+  {{- if or (not .Values.tls.enabled) (not .Values.tls.proxy.enabled) }}
+  servicePort: "{{ .Values.proxy.ports.pulsar }}"
+  brokerServiceURL: pulsar://{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}:{{ .Values.broker.ports.pulsar }}
+  brokerWebServiceURL: http://{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}:{{ .Values.broker.ports.http }}
+  {{- end }}
+  {{- if and .Values.tls.enabled .Values.tls.proxy.enabled }}
+  tlsEnabledInProxy: "true"
+  servicePortTls: "{{ .Values.proxy.ports.pulsarssl }}"
+  webServicePortTls: "{{ .Values.proxy.ports.https }}"
+  tlsCertificateFilePath: "/pulsar/certs/proxy/tls.crt"
+  tlsKeyFilePath: "/pulsar/certs/proxy/tls.key"
+  tlsTrustCertsFilePath: "/pulsar/certs/ca/ca.crt"
+  {{- if and .Values.tls.enabled .Values.tls.broker.enabled }}
+  # if broker enables TLS, configure proxy to talk to broker using TLS
+  brokerServiceURLTLS: pulsar+ssl://{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}:{{ .Values.broker.ports.pulsarssl }}
+  brokerWebServiceURLTLS: https://{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}:{{ .Values.broker.ports.https }}
+  tlsEnabledWithBroker: "true"
+  tlsCertRefreshCheckDurationSec: "300"
+  brokerClientTrustCertsFilePath: "/pulsar/certs/ca/ca.crt"
+  {{- end }}
+  {{- if not (and .Values.tls.enabled .Values.tls.broker.enabled) }}
+  brokerServiceURL: pulsar://{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}:{{ .Values.broker.ports.pulsar }}
+  brokerWebServiceURL: http://{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}:{{ .Values.broker.ports.http }}
+  {{- end }}
+  {{- end }}
+
+  # Authentication Settings
+  {{- if .Values.auth.authentication.enabled }}
+  authenticationEnabled: "true"
+  {{- if .Values.auth.authorization.enabled }}
+  # disable authorization on proxy and forward authorization credentials to broker
+  authorizationEnabled: "false"
+  forwardAuthorizationCredentials: "true"
+  superUserRoles: {{ .Values.auth.superUsers.broker }},{{ .Values.auth.superUsers.proxy }},{{ .Values.auth.superUsers.client }}
+  {{- end }}
+  {{- if eq .Values.auth.authentication.provider "jwt" }}
+  # token authentication configuration
+  authenticationProviders: "org.apache.pulsar.broker.authentication.AuthenticationProviderToken"
+  brokerClientAuthenticationParameters: "file:///pulsar/tokens/proxy/token"
+  brokerClientAuthenticationPlugin: "org.apache.pulsar.client.impl.auth.AuthenticationToken"
+  {{- if .Values.auth.authentication.jwt.usingSecretKey }}
+  tokenSecretKey: "file:///pulsar/keys/token/secret.key"
+  {{- else }}
+  tokenPublicKey: "file:///pulsar/keys/token/public.key"
+  {{- end }}
+  {{- end }}
+  {{- end }}
 {{ toYaml .Values.proxy.configData | indent 2 }}
 {{- end }}
diff --git a/pulsar/templates/proxy-deployment.yaml b/pulsar/templates/proxy-deployment.yaml
deleted file mode 100644
index 476f608..0000000
--- a/pulsar/templates/proxy-deployment.yaml
+++ /dev/null
@@ -1,124 +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 .Values.extra.proxy }}
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  name: "{{ template "pulsar.fullname" . }}-{{ .Values.proxy.component }}"
-  namespace: {{ .Values.namespace }}
-  labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
-    component: {{ .Values.proxy.component }}
-    cluster: {{ template "pulsar.fullname" . }}
-spec:
-  replicas: {{ .Values.proxy.replicaCount }}
-  selector:
-    matchLabels:
-      app: {{ template "pulsar.name" . }}
-      release: {{ .Release.Name }}
-      component: {{ .Values.proxy.component }}
-  template:
-    metadata:
-      labels:
-        app: {{ template "pulsar.name" . }}
-        release: {{ .Release.Name }}
-        component: {{ .Values.proxy.component }}
-        cluster: {{ template "pulsar.fullname" . }}
-      annotations:
-{{ toYaml .Values.proxy.annotations | indent 8 }}
-    spec:
-    {{- if .Values.proxy.nodeSelector }}
-      nodeSelector:
-{{ toYaml .Values.proxy.nodeSelector | indent 8 }}
-    {{- end }}
-    {{- if .Values.proxy.tolerations }}
-      tolerations:
-{{ toYaml .Values.proxy.tolerations | indent 8 }}
-    {{- end }}
-      affinity:
-        podAntiAffinity:
-          requiredDuringSchedulingIgnoredDuringExecution:
-          - labelSelector:
-              matchExpressions:
-              - key: "app"
-                operator: In
-                values:
-                - "{{ template "pulsar.name" . }}"
-              - key: "release"
-                operator: In
-                values:
-                - {{ .Release.Name }}
-              - key: "component"
-                operator: In
-                values:
-                - {{ .Values.proxy.component }}
-            topologyKey: "kubernetes.io/hostname"
-      terminationGracePeriodSeconds: {{ .Values.proxy.gracePeriod }}
-      initContainers:
-      # This init container will wait for zookeeper to be ready before
-      # deploying the proxies
-      - name: wait-zookeeper-ready
-        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
-        imagePullPolicy: {{ .Values.image.pullPolicy }}
-        command: ["sh", "-c"]
-        args:
-          - >-
-            until bin/pulsar zookeeper-shell -server {{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }} ls /admin/clusters/{{ template "pulsar.fullname" . }}; do
-              sleep 3;
-            done;
-      # This init container will wait for at least one broker to be ready before
-      # deploying the proxy
-      - name: wait-broker-ready
-        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
-        imagePullPolicy: {{ .Values.image.pullPolicy }}
-        command: ["bash", "-c"]
-        args:
-          - >-
-            for i in {0..{{ .Values.broker.replicaCount }}}; do
-              brokerServiceNumber="$(nslookup -timeout=10 {{ template "pulsar.fullname" . }}-{{ .Values.broker.component }} | grep Name | wc -l)"
-              if [[ ${brokerServiceNumber} -ge 1 ]]; then
-                break
-              fi
-              sleep 30;
-            done;
-      containers:
-      - name: "{{ template "pulsar.fullname" . }}-{{ .Values.proxy.component }}"
-        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
-        imagePullPolicy: {{ .Values.image.pullPolicy }}
-      {{- if .Values.proxy.resources }}
-        resources:
-{{ toYaml .Values.proxy.resources | indent 10 }}
-      {{- end }}
-        command: ["sh", "-c"]
-        args:
-        - >
-          bin/apply-config-from-env.py conf/proxy.conf &&
-          bin/apply-config-from-env.py conf/pulsar_env.sh &&
-          bin/pulsar proxy
-        ports:
-        - name: http
-          containerPort: 8080
-        envFrom:
-        - configMapRef:
-            name: "{{ template "pulsar.fullname" . }}-{{ .Values.proxy.component }}"
-{{- end }}
diff --git a/pulsar/templates/proxy-pdb.yaml b/pulsar/templates/proxy-pdb.yaml
index 10188d3..50cfe7a 100644
--- a/pulsar/templates/proxy-pdb.yaml
+++ b/pulsar/templates/proxy-pdb.yaml
@@ -17,7 +17,7 @@
 # under the License.
 #
 
-{{- if .Values.extra.proxy }}
+{{- if or .Values.components.proxy .Values.extra.proxy }}
 {{- if .Values.proxy.pdb.usePolicy }}
 apiVersion: policy/v1beta1
 kind: PodDisruptionBudget
@@ -25,17 +25,12 @@ metadata:
   name: "{{ template "pulsar.fullname" . }}-{{ .Values.proxy.component }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
     component: {{ .Values.proxy.component }}
-    cluster: {{ template "pulsar.fullname" . }}
 spec:
   selector:
     matchLabels:
-      app: {{ template "pulsar.name" . }}
-      release: {{ .Release.Name }}
+      {{- include "pulsar.matchLabels" . | nindent 6 }}
       component: {{ .Values.proxy.component }}
   maxUnavailable: {{ .Values.proxy.pdb.maxUnavailable }}
 {{- end }}
diff --git a/pulsar/templates/proxy-service.yaml b/pulsar/templates/proxy-service.yaml
index 522cfbf..273d5aa 100644
--- a/pulsar/templates/proxy-service.yaml
+++ b/pulsar/templates/proxy-service.yaml
@@ -17,25 +17,38 @@
 # under the License.
 #
 
-{{- if .Values.extra.proxy }}
+{{- if or .Values.components.proxy .Values.extra.proxy }}
 apiVersion: v1
 kind: Service
 metadata:
   name: "{{ template "pulsar.fullname" . }}-{{ .Values.proxy.component }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
     component: {{ .Values.proxy.component }}
-    cluster: {{ template "pulsar.fullname" . }}
   annotations:
-{{ toYaml .Values.proxy.service.annotations | indent 4 }}
+  {{- with .Values.proxy.service.annotations }}
+    {{- toYaml . | nindent 4 }}
+  {{- end }}
 spec:
   type: {{ .Values.proxy.service.type }}
   ports:
-{{ toYaml .Values.proxy.service.ports | indent 2 }}
+    {{- if or (not .Values.tls.enabled) (not .Values.tls.proxy.enabled) }}
+    - name: http
+      port: {{ .Values.proxy.ports.http }}
+      protocol: TCP
+    - name: pulsar
+      port: {{ .Values.proxy.ports.pulsar }}
+      protocol: TCP
+    {{- end }}
+    {{- if and .Values.tls.enabled .Values.tls.proxy.enabled }}
+    - name: https
+      port: {{ .Values.proxy.ports.https }}
+      protocol: TCP
+    - name: pulsarssl
+      port: {{ .Values.proxy.ports.pulsarssl }}
+      protocol: TCP
+    {{- end }}
   selector:
     app: {{ template "pulsar.name" . }}
     release: {{ .Release.Name }}
diff --git a/pulsar/templates/proxy-statefulset.yaml b/pulsar/templates/proxy-statefulset.yaml
new file mode 100644
index 0000000..c83c2e3
--- /dev/null
+++ b/pulsar/templates/proxy-statefulset.yaml
@@ -0,0 +1,234 @@
+#
+# 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 or .Values.components.proxy .Values.extra.proxy }}
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+  name: "{{ template "pulsar.fullname" . }}-{{ .Values.proxy.component }}"
+  namespace: {{ .Values.namespace }}
+  labels:
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
+    component: {{ .Values.proxy.component }}
+spec:
+  serviceName: "{{ template "pulsar.fullname" . }}-{{ .Values.proxy.component }}"
+  replicas: {{ .Values.proxy.replicaCount }}
+  selector:
+    matchLabels:
+      {{- include "pulsar.matchLabels" . | nindent 6 }}
+      component: {{ .Values.proxy.component }}
+  updateStrategy:
+    type: RollingUpdate
+  podManagementPolicy: Parallel
+  template:
+    metadata:
+      labels:
+        {{- include "pulsar.template.labels" . | nindent 8 }}
+        component: {{ .Values.proxy.component }}
+      annotations:
+        prometheus.io/scrape: "true"
+        prometheus.io/port: "{{ .Values.proxy.ports.http }}"
+{{- with .Values.proxy.annotations }}
+{{ toYaml . | indent 8 }}
+{{- end }}
+    spec:
+    {{- if .Values.proxy.nodeSelector }}
+      nodeSelector:
+{{ toYaml .Values.proxy.nodeSelector | indent 8 }}
+    {{- end }}
+    {{- if .Values.proxy.tolerations }}
+      tolerations:
+{{ toYaml .Values.proxy.tolerations | indent 8 }}
+    {{- end }}
+      affinity:
+        {{- if and .Values.affinity.anti_affinity .Values.proxy.affinity.anti_affinity}}
+        podAntiAffinity:
+          requiredDuringSchedulingIgnoredDuringExecution:
+          - labelSelector:
+              matchExpressions:
+              - key: "app"
+                operator: In
+                values:
+                - "{{ template "pulsar.name" . }}-{{ .Values.proxy.component }}"
+              - key: "release"
+                operator: In
+                values:
+                - {{ .Release.Name }}
+              - key: "component"
+                operator: In
+                values:
+                - {{ .Values.proxy.component }}
+            topologyKey: "kubernetes.io/hostname"
+        {{- end }}
+      terminationGracePeriodSeconds: {{ .Values.proxy.gracePeriod }}
+      initContainers:
+      # This init container will wait for zookeeper to be ready before
+      # deploying the bookies
+      - name: wait-zookeeper-ready
+        image: "{{ .Values.images.proxy.repository }}:{{ .Values.images.proxy.tag }}"
+        imagePullPolicy: {{ .Values.images.proxy.pullPolicy }}
+        command: ["sh", "-c"]
+        args:
+          - >-
+            until bin/pulsar zookeeper-shell -server {{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }} get {{ .Values.metadataPrefix }}/admin/clusters/{{ template "pulsar.fullname" . }}; do
+              sleep 3;
+            done;
+      # This init container will wait for at least one broker to be ready before
+      # deploying the proxy
+      - name: wait-broker-ready
+        image: "{{ .Values.images.proxy.repository }}:{{ .Values.images.proxy.tag }}"
+        imagePullPolicy: {{ .Values.images.proxy.pullPolicy }}
+        command: ["sh", "-c"]
+        args:
+          - >-
+            set -e;
+            brokerServiceNumber="$(nslookup -timeout=10 {{ template "pulsar.fullname" . }}-{{ .Values.broker.component }} | grep Name | wc -l)";
+            until [ ${brokerServiceNumber} -ge 1 ]; do
+              echo "pulsar cluster {{ template "pulsar.fullname" . }} isn't initialized yet ... check in 10 seconds ...";
+              sleep 10;
+              brokerServiceNumber="$(nslookup -timeout=10 {{ template "pulsar.fullname" . }}-{{ .Values.broker.component }} | grep Name | wc -l)";
+            done;
+      containers:
+      - name: "{{ template "pulsar.fullname" . }}-{{ .Values.proxy.component }}"
+        image: "{{ .Values.images.proxy.repository }}:{{ .Values.images.proxy.tag }}"
+        imagePullPolicy: {{ .Values.images.proxy.pullPolicy }}
+        {{- if .Values.proxy.probe.liveness.enabled }}
+        livenessProbe:
+          httpGet:
+            path: /status.html
+            port: {{ .Values.proxy.ports.http }}
+          initialDelaySeconds: {{ .Values.proxy.probe.liveness.initialDelaySeconds }}
+          periodSeconds: {{ .Values.proxy.probe.liveness.periodSeconds }}
+          failureThreshold: {{ .Values.proxy.probe.liveness.failureThreshold }}
+        {{- end }}
+        {{- if .Values.proxy.probe.readiness.enabled }}
+        readinessProbe:
+          httpGet:
+            path: /status.html
+            port: {{ .Values.proxy.ports.http }}
+          initialDelaySeconds: {{ .Values.proxy.probe.readiness.initialDelaySeconds }}
+          periodSeconds: {{ .Values.proxy.probe.readiness.periodSeconds }}
+          failureThreshold: {{ .Values.proxy.probe.readiness.failureThreshold }}
+        {{- end }}
+        {{- if .Values.proxy.probe.startup.enabled }}
+        startupProbe:
+          httpGet:
+            path: /status.html
+            port: {{ .Values.proxy.ports.http }}
+          initialDelaySeconds: {{ .Values.proxy.probe.startup.initialDelaySeconds }}
+          periodSeconds: {{ .Values.proxy.probe.startup.periodSeconds }}
+          failureThreshold: {{ .Values.proxy.probe.startup.failureThreshold }}
+        {{- end }}
+      {{- if .Values.proxy.resources }}
+        resources:
+{{ toYaml .Values.proxy.resources | indent 10 }}
+      {{- end }}
+        command: ["sh", "-c"]
+        args:
+        - >
+          bin/apply-config-from-env.py conf/proxy.conf &&
+          bin/apply-config-from-env.py conf/pulsar_env.sh &&
+          echo "OK" > status &&
+          bin/pulsar proxy
+        ports:
+        # prometheus needs to access /metrics endpoint
+        - name: http
+          containerPort: {{ .Values.proxy.ports.http }}
+        {{- if or (not .Values.tls.enabled) (not .Values.tls.proxy.enabled) }}
+        - name: pulsar
+          containerPort: {{ .Values.proxy.ports.pulsar }}
+        {{- end }}
+        {{- if and (.Values.tls.enabled) (.Values.tls.proxy.enabled) }}
+        - name: https
+          containerPort: {{ .Values.proxy.ports.https }}
+        - name: pulsarssl
+          containerPort: {{ .Values.proxy.ports.pulsarssl }}
+        {{- end }}
+        envFrom:
+        - configMapRef:
+            name: "{{ template "pulsar.fullname" . }}-{{ .Values.proxy.component }}"
+        {{- if or .Values.auth.authentication.enabled (and .Values.tls.enabled (or .Values.tls.proxy.enabled .Values.tls.broker.enabled)) }}
+        volumeMounts:
+          {{- if .Values.auth.authentication.enabled }}
+          {{- if eq .Values.auth.authentication.provider "jwt" }}
+          - mountPath: "/pulsar/keys"
+            name: token-keys
+            readOnly: true
+          - mountPath: "/pulsar/tokens"
+            name: proxy-token
+            readOnly: true
+          {{- end }}
+          {{- end }}
+          {{- if .Values.tls.proxy.enabled }}
+          - mountPath: "/pulsar/certs/proxy"
+            name: proxy-certs
+            readOnly: true
+          {{- end}}
+          {{- if .Values.tls.enabled }}
+          - mountPath: "/pulsar/certs/ca"
+            name: ca
+            readOnly: true
+          {{- end}}
+      {{- end}}
+      {{- if or .Values.auth.authentication.enabled (and .Values.tls.enabled .Values.tls.proxy.enabled) }}
+      volumes:
+        {{- if .Values.auth.authentication.enabled }}
+        {{- if eq .Values.auth.authentication.provider "jwt" }}
+        - name: token-keys
+          secret:
+            {{- if not .Values.auth.authentication.jwt.usingSecretKey }}
+            secretName: "{{ .Release.Name }}-token-asymmetric-key"
+            {{- end}}
+            {{- if .Values.auth.authentication.jwt.usingSecretKey }}
+            secretName: "{{ .Release.Name }}-token-symmetric-key"
+            {{- end}}
+            items:
+              {{- if .Values.auth.authentication.jwt.usingSecretKey }}
+              - key: SECRETKEY
+                path: token/secret.key
+              {{- else }}
+              - key: PUBLICKEY
+                path: token/public.key
+              {{- end}}
+        - name: proxy-token
+          secret:
+            secretName: "{{ .Release.Name }}-token-{{ .Values.auth.superUsers.proxy }}"
+            items:
+              - key: TOKEN
+                path: proxy/token
+        {{- end}}
+        {{- end}}
+        {{- if .Values.tls.proxy.enabled }}
+        - name: ca
+          secret:
+            secretName: "{{ template "pulsar.fullname" . }}-ca-tls"
+            items:
+              - key: ca.crt
+                path: ca.crt
+        - name: proxy-certs
+          secret:
+            secretName: "{{ template "pulsar.fullname" . }}-{{ .Values.tls.proxy.cert_name }}"
+            items:
+              - key: tls.crt
+                path: tls.crt
+              - key: tls.key
+                path: tls.key
+        {{- end}}
+      {{- end}}
+{{- end }}
diff --git a/pulsar/templates/pulsar-cluster-initialize.yaml b/pulsar/templates/pulsar-cluster-initialize.yaml
new file mode 100644
index 0000000..a57db8e
--- /dev/null
+++ b/pulsar/templates/pulsar-cluster-initialize.yaml
@@ -0,0 +1,102 @@
+#
+# 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 .Values.components.broker }}
+apiVersion: batch/v1
+kind: Job
+metadata:
+  name: "{{ template "pulsar.fullname" . }}-{{ .Values.pulsar_metadata.component }}"
+  namespace: {{ .Values.namespace }}
+  labels:
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
+    component: {{ .Values.pulsar_metadata.component }}
+spec:
+  template:
+    spec:
+      initContainers:
+      {{- if .Values.pulsar_metadata.configurationStore }}
+      - name: wait-cs-ready
+        image: "{{ .Values.pulsar_metadata.image.repository }}:{{ .Values.pulsar_metadata.image.tag }}"
+        imagePullPolicy: {{ .Values.pulsar_metadata.image.pullPolicy }}
+        command: ["sh", "-c"]
+        args:
+          - >-
+            until nslookup {{ .Values.pulsar_metadata.configurationStore}}; do
+              sleep 3;
+            done;
+
+      {{- end }}
+      - name: wait-zookeeper-ready
+        image: "{{ .Values.pulsar_metadata.image.repository }}:{{ .Values.pulsar_metadata.image.tag }}"
+        imagePullPolicy: {{ .Values.pulsar_metadata.image.pullPolicy }}
+        command: ["sh", "-c"]
+        args:
+          - >-
+            until nslookup {{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}-{{ add (.Values.zookeeper.replicaCount | int) -1 }}.{{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}.{{ .Values.namespace }}; do
+              sleep 3;
+            done;
+      # This initContainer will wait for bookkeeper initnewcluster to complete
+      # before initializing pulsar metadata
+      - name: pulsar-bookkeeper-verify-clusterid
+        image: "{{ .Values.pulsar_metadata.image.repository }}:{{ .Values.pulsar_metadata.image.tag }}"
+        imagePullPolicy: {{ .Values.pulsar_metadata.image.pullPolicy }}
+        command: ["sh", "-c"]
+        args:
+        - >
+          bin/apply-config-from-env.py conf/bookkeeper.conf;
+          {{- include "pulsar.toolset.zookeeper.tls.settings" . | nindent 10 }}
+          until bin/bookkeeper shell whatisinstanceid; do
+            sleep 3;
+          done;
+        envFrom:
+        - configMapRef:
+            name: "{{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }}"
+        volumeMounts:
+        {{- include "pulsar.toolset.certs.volumeMounts" . | nindent 8 }}
+      containers:
+      - name: "{{ template "pulsar.fullname" . }}-{{ .Values.pulsar_metadata.component }}"
+        image: "{{ .Values.pulsar_metadata.image.repository }}:{{ .Values.pulsar_metadata.image.tag }}"
+        imagePullPolicy: {{ .Values.pulsar_metadata.image.pullPolicy }}
+      {{- if .Values.pulsar_metadata.resources }}
+        resources:
+{{ toYaml .Values.pulsar_metadata.resources | indent 10 }}
+      {{- end }}
+        command: ["sh", "-c"]
+        args:
+          - >
+            {{- include "pulsar.toolset.zookeeper.tls.settings" . | nindent 12 }}
+            bin/pulsar initialize-cluster-metadata \
+              --cluster {{ template "pulsar.fullname" . }} \
+              --zookeeper {{ template "pulsar.zookeeper.connect" . }}{{ .Values.metadataPrefix }} \
+              {{- if .Values.pulsar_metadata.configurationStore }}
+              --configuration-store {{ .Values.pulsar_metadata.configurationStore }}{{ .Values.pulsar_metadata.configurationStoreMetadataPrefix }} \
+              {{- end }}
+              {{- if not .Values.pulsar_metadata.configurationStore }}
+              --configuration-store {{ template "pulsar.zookeeper.connect" . }}{{ .Values.metadataPrefix }} \
+              {{- end }}
+              --web-service-url http://{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}.{{ .Values.namespace }}.svc.cluster.local:8080/ \
+              --web-service-url-tls https://{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}.{{ .Values.namespace }}.svc.cluster.local:8443/ \
+              --broker-service-url pulsar://{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}.{{ .Values.namespace }}.svc.cluster.local:6650/ \
+              --broker-service-url-tls pulsar+ssl://{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}.{{ .Values.namespace }}.svc.cluster.local:6651/ || true;
+        volumeMounts:
+        {{- include "pulsar.toolset.certs.volumeMounts" . | nindent 8 }}
+      volumes:
+      {{- include "pulsar.toolset.certs.volumes" . | nindent 6 }}
+      restartPolicy: Never
+{{- end }}
diff --git a/pulsar/templates/pulsar-manager-admin-secret.yaml b/pulsar/templates/pulsar-manager-admin-secret.yaml
index 427ac25..ef4ecf9 100644
--- a/pulsar/templates/pulsar-manager-admin-secret.yaml
+++ b/pulsar/templates/pulsar-manager-admin-secret.yaml
@@ -17,7 +17,7 @@
 # under the License.
 #
 
-{{- if .Values.extra.pulsar_manager }}
+{{- if or .Values.components.pulsar_manager .Values.extra.pulsar_manager }}
 apiVersion: v1
 kind: Secret
 metadata:
diff --git a/pulsar/templates/pulsar-manager-configmap.yaml b/pulsar/templates/pulsar-manager-configmap.yaml
index 5444469..acfb315 100644
--- a/pulsar/templates/pulsar-manager-configmap.yaml
+++ b/pulsar/templates/pulsar-manager-configmap.yaml
@@ -17,17 +17,15 @@
 # under the License.
 #
 
+{{- if or .Values.components.pulsar_manager .Values.extra.pulsar_manager }}
 apiVersion: v1
 kind: ConfigMap
 metadata:
   name: "{{ template "pulsar.fullname" . }}-{{ .Values.pulsar_manager.component }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
     component: {{ .Values.pulsar_manager.component }}
-    cluster: {{ template "pulsar.fullname" . }}
 data:
 {{ toYaml .Values.pulsar_manager.configData | indent 2 }}
+{{- end }}
diff --git a/pulsar/templates/pulsar-manager-deployment.yaml b/pulsar/templates/pulsar-manager-deployment.yaml
index 8a8493a..73ffc1a 100644
--- a/pulsar/templates/pulsar-manager-deployment.yaml
+++ b/pulsar/templates/pulsar-manager-deployment.yaml
@@ -17,33 +17,26 @@
 # under the License.
 #
 
-{{- if .Values.extra.pulsar_manager }}
+{{- if or .Values.components.pulsar_manager .Values.extra.pulsar_manager }}
 apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: "{{ template "pulsar.fullname" . }}-{{ .Values.pulsar_manager.component }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
     component: {{ .Values.pulsar_manager.component }}
-    cluster: {{ template "pulsar.fullname" . }}
 spec:
   replicas: 1
   selector:
     matchLabels:
-      app: {{ template "pulsar.name" . }}
-      release: {{ .Release.Name }}
+      {{- include "pulsar.matchLabels" . | nindent 6 }}
       component: {{ .Values.pulsar_manager.component }}
   template:
     metadata:
       labels:
-        app: {{ template "pulsar.name" . }}
-        release: {{ .Release.Name }}
+        {{- include "pulsar.template.labels" . | nindent 8 }}
         component: {{ .Values.pulsar_manager.component }}
-        cluster: {{ template "pulsar.fullname" . }}
       annotations:
 {{ toYaml .Values.pulsar_manager.annotations | indent 8 }}
     spec:
@@ -58,14 +51,14 @@ spec:
       terminationGracePeriodSeconds: {{ .Values.pulsar_manager.gracePeriod }}
       containers:
         - name: "{{ template "pulsar.fullname" . }}-{{ .Values.pulsar_manager.component }}"
-          image: "{{ .Values.pulsar_manager.image.repository }}:{{ .Values.pulsar_manager.image.tag }}"
-          imagePullPolicy: {{ .Values.pulsar_manager.image.pullPolicy }}
+          image: "{{ .Values.images.pulsar_manager.repository }}:{{ .Values.images.pulsar_manager.tag }}"
+          imagePullPolicy: {{ .Values.images.pulsar_manager.pullPolicy }}
         {{- if .Values.pulsar_manager.resources }}
           resources:
 {{ toYaml .Values.pulsar_manager.resources | indent 12 }}
         {{- end }}
           ports:
-          - containerPort: 9527
+          - containerPort: {{ .Values.pulsar_manager.port }}
           volumeMounts:
           - name: pulsar-manager-data
             mountPath: /data
diff --git a/pulsar/templates/pulsar-manager-service.yaml b/pulsar/templates/pulsar-manager-service.yaml
index aa61a6b..188b3a4 100644
--- a/pulsar/templates/pulsar-manager-service.yaml
+++ b/pulsar/templates/pulsar-manager-service.yaml
@@ -17,25 +17,23 @@
 # under the License.
 #
 
-{{- if .Values.extra.pulsar_manager }}
+{{- if or .Values.components.pulsar_manager .Values.extra.pulsar_manager }}
 apiVersion: v1
 kind: Service
 metadata:
   name: "{{ template "pulsar.fullname" . }}-{{ .Values.pulsar_manager.component }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
     component: {{ .Values.pulsar_manager.component }}
-    cluster: {{ template "pulsar.fullname" . }}
   annotations:
 {{ toYaml .Values.pulsar_manager.service.annotations | indent 4 }}
 spec:
   type: {{ .Values.pulsar_manager.service.type }}
   ports:
-{{ toYaml .Values.pulsar_manager.service.ports | indent 2 }}
+    - name: server
+      port: {{ .Values.pulsar_manager.port }}
+      protocol: TCP
   selector:
     app: {{ template "pulsar.name" . }}
     release: {{ .Release.Name }}
diff --git a/pulsar/templates/tls-cert-internal-issuer.yaml b/pulsar/templates/tls-cert-internal-issuer.yaml
new file mode 100644
index 0000000..5d924f4
--- /dev/null
+++ b/pulsar/templates/tls-cert-internal-issuer.yaml
@@ -0,0 +1,62 @@
+#
+# 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 .Values.certs.internal_issuer.enabled }}
+{{- if eq .Values.certs.internal_issuer.type "selfsigning" }}
+apiVersion: cert-manager.io/v1alpha2
+kind: Issuer
+metadata:
+  name: "{{ template "pulsar.fullname" . }}-{{ .Values.certs.internal_issuer.component }}"
+  namespace: {{ .Values.namespace }}
+spec:
+  selfSigned: {}
+---
+
+apiVersion: cert-manager.io/v1alpha2
+kind: Certificate
+metadata:
+  name: "{{ template "pulsar.fullname" . }}-ca"
+  namespace: {{ .Values.namespace }}
+spec:
+  secretName: "{{ template "pulsar.fullname" . }}-ca-tls"
+  commonName: "{{ .Values.namespace }}.svc.cluster.local"
+  usages:
+    - server auth
+    - client auth
+  isCA: true
+  issuerRef:
+    name: "{{ template "pulsar.fullname" . }}-{{ .Values.certs.internal_issuer.component }}"
+    # We can reference ClusterIssuers by changing the kind here.
+    # The default value is Issuer (i.e. a locally namespaced Issuer)
+    kind: Issuer
+    # This is optional since cert-manager will default to this value however
+    # if you are using an external issuer, change this to that issuer group.
+    group: cert-manager.io
+---
+
+apiVersion: cert-manager.io/v1alpha2
+kind: Issuer
+metadata:
+  name: "{{ template "pulsar.fullname" . }}-{{ .Values.certs.internal_issuer.component }}-ca-issuer"
+  namespace: {{ .Values.namespace }}
+spec:
+  ca:
+    secretName: "{{ template "pulsar.fullname" . }}-ca-tls"
+{{- end }}
+{{- end }}
\ No newline at end of file
diff --git a/pulsar/templates/tls-certs-internal.yaml b/pulsar/templates/tls-certs-internal.yaml
new file mode 100644
index 0000000..5b249c0
--- /dev/null
+++ b/pulsar/templates/tls-certs-internal.yaml
@@ -0,0 +1,247 @@
+#
+# 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 .Values.tls.enabled }}
+{{- if .Values.certs.internal_issuer.enabled }}
+
+{{- if .Values.tls.proxy.enabled }}
+apiVersion: cert-manager.io/v1alpha2
+kind: Certificate
+metadata:
+  name: "{{ template "pulsar.fullname" . }}-{{ .Values.tls.proxy.cert_name }}"
+  namespace: {{ .Values.namespace }}
+spec:
+  # Secret names are always required.
+  secretName: "{{ template "pulsar.fullname" . }}-{{ .Values.tls.proxy.cert_name }}"
+  duration: "{{ .Values.tls.common.duration }}"
+  renewBefore: "{{ .Values.tls.common.renewBefore }}"
+  organization:
+{{ toYaml .Values.tls.common.organization | indent 2 }}
+  # The use of the common name field has been deprecated since 2000 and is
+  # discouraged from being used.
+  commonName: "*.{{ template "pulsar.fullname" . }}-{{ .Values.proxy.component }}.{{ .Values.namespace }}.svc.cluster.local"
+  isCA: false
+  keySize: {{ .Values.tls.common.keySize }}
+  keyAlgorithm: {{ .Values.tls.common.keyAlgorithm }}
+  keyEncoding: {{ .Values.tls.common.keyEncoding }}
+  usages:
+    - server auth
+    - client auth
+  # At least one of a DNS Name, USI SAN, or IP address is required.
+  dnsNames:
+    -  "*.{{ template "pulsar.fullname" . }}-{{ .Values.proxy.component }}.{{ .Values.namespace }}.svc.cluster.local"
+    -  "{{ template "pulsar.fullname" . }}-{{ .Values.proxy.component }}"
+  # Issuer references are always required.
+  issuerRef:
+    name: "{{ template "pulsar.fullname" . }}-{{ .Values.certs.internal_issuer.component }}-ca-issuer"
+    # We can reference ClusterIssuers by changing the kind here.
+    # The default value is Issuer (i.e. a locally namespaced Issuer)
+    kind: Issuer
+    # This is optional since cert-manager will default to this value however
+    # if you are using an external issuer, change this to that issuer group.
+    group: cert-manager.io
+---
+{{- end }}
+
+{{- if or .Values.tls.broker.enabled (or .Values.tls.bookie.enabled .Values.tls.zookeeper.enabled) }}
+apiVersion: cert-manager.io/v1alpha2
+kind: Certificate
+metadata:
+  name: "{{ template "pulsar.fullname" . }}-{{ .Values.tls.broker.cert_name }}"
+  namespace: {{ .Values.namespace }}
+spec:
+  # Secret names are always required.
+  secretName: "{{ template "pulsar.fullname" . }}-{{ .Values.tls.broker.cert_name }}"
+  duration: "{{ .Values.tls.common.duration }}"
+  renewBefore: "{{ .Values.tls.common.renewBefore }}"
+  organization:
+{{ toYaml .Values.tls.common.organization | indent 2 }}
+  # The use of the common name field has been deprecated since 2000 and is
+  # discouraged from being used.
+  commonName: "*.{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}.{{ .Values.namespace }}.svc.cluster.local"
+  isCA: false
+  keySize: {{ .Values.tls.common.keySize }}
+  keyAlgorithm: {{ .Values.tls.common.keyAlgorithm }}
+  keyEncoding: {{ .Values.tls.common.keyEncoding }}
+  usages:
+    - server auth
+    - client auth
+  # At least one of a DNS Name, USI SAN, or IP address is required.
+  dnsNames:
+    -  "*.{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}.{{ .Values.namespace }}.svc.cluster.local"
+    -  "{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}"
+  # Issuer references are always required.
+  issuerRef:
+    name: "{{ template "pulsar.fullname" . }}-{{ .Values.certs.internal_issuer.component }}-ca-issuer"
+    # We can reference ClusterIssuers by changing the kind here.
+    # The default value is Issuer (i.e. a locally namespaced Issuer)
+    kind: Issuer
+    # This is optional since cert-manager will default to this value however
+    # if you are using an external issuer, change this to that issuer group.
+    group: cert-manager.io
+---
+{{- end }}
+
+{{- if or .Values.tls.bookie.enabled .Values.tls.zookeeper.enabled }}
+apiVersion: cert-manager.io/v1alpha2
+kind: Certificate
+metadata:
+  name: "{{ template "pulsar.fullname" . }}-{{ .Values.tls.bookie.cert_name }}"
+  namespace: {{ .Values.namespace }}
+spec:
+  # Secret names are always required.
+  secretName: "{{ template "pulsar.fullname" . }}-{{ .Values.tls.bookie.cert_name }}"
+  duration: "{{ .Values.tls.common.duration }}"
+  renewBefore: "{{ .Values.tls.common.renewBefore }}"
+  organization:
+{{ toYaml .Values.tls.common.organization | indent 2 }}
+  # The use of the common name field has been deprecated since 2000 and is
+  # discouraged from being used.
+  commonName: "*.{{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }}.{{ .Values.namespace }}.svc.cluster.local"
+  isCA: false
+  keySize: {{ .Values.tls.common.keySize }}
+  keyAlgorithm: {{ .Values.tls.common.keyAlgorithm }}
+  keyEncoding: {{ .Values.tls.common.keyEncoding }}
+  usages:
+    - server auth
+    - client auth
+  dnsNames:
+    -  "*.{{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }}.{{ .Values.namespace }}.svc.cluster.local"
+    -  "{{ template "pulsar.fullname" . }}-{{ .Values.bookkeeper.component }}"
+  # Issuer references are always required.
+  issuerRef:
+    name: "{{ template "pulsar.fullname" . }}-{{ .Values.certs.internal_issuer.component }}-ca-issuer"
+    # We can reference ClusterIssuers by changing the kind here.
+    # The default value is Issuer (i.e. a locally namespaced Issuer)
+    kind: Issuer
+    # This is optional since cert-manager will default to this value however
+    # if you are using an external issuer, change this to that issuer group.
+    group: cert-manager.io
+---
+{{- end }}
+
+{{- if .Values.tls.zookeeper.enabled }}
+apiVersion: cert-manager.io/v1alpha2
+kind: Certificate
+metadata:
+  name: "{{ template "pulsar.fullname" . }}-{{ .Values.tls.autorecovery.cert_name }}"
+  namespace: {{ .Values.namespace }}
+spec:
+  # Secret names are always required.
+  secretName: "{{ template "pulsar.fullname" . }}-{{ .Values.tls.autorecovery.cert_name }}"
+  duration: "{{ .Values.tls.common.duration }}"
+  renewBefore: "{{ .Values.tls.common.renewBefore }}"
+  organization:
+{{ toYaml .Values.tls.common.organization | indent 2 }}
+  # The use of the common name field has been deprecated since 2000 and is
+  # discouraged from being used.
+  commonName: "*.{{ template "pulsar.fullname" . }}-{{ .Values.autorecovery.component }}.{{ .Values.namespace }}.svc.cluster.local"
+  isCA: false
+  keySize: {{ .Values.tls.common.keySize }}
+  keyAlgorithm: {{ .Values.tls.common.keyAlgorithm }}
+  keyEncoding: {{ .Values.tls.common.keyEncoding }}
+  usages:
+    - server auth
+    - client auth
+  dnsNames:
+    -  "*.{{ template "pulsar.fullname" . }}-{{ .Values.autorecovery.component }}.{{ .Values.namespace }}.svc.cluster.local"
+    -  "{{ template "pulsar.fullname" . }}-{{ .Values.autorecovery.component }}"
+  # Issuer references are always required.
+  issuerRef:
+    name: "{{ template "pulsar.fullname" . }}-{{ .Values.certs.internal_issuer.component }}-ca-issuer"
+    # We can reference ClusterIssuers by changing the kind here.
+    # The default value is Issuer (i.e. a locally namespaced Issuer)
+    kind: Issuer
+    # This is optional since cert-manager will default to this value however
+    # if you are using an external issuer, change this to that issuer group.
+    group: cert-manager.io
+---
+apiVersion: cert-manager.io/v1alpha2
+kind: Certificate
+metadata:
+  name: "{{ template "pulsar.fullname" . }}-{{ .Values.tls.toolset.cert_name }}"
+  namespace: {{ .Values.namespace }}
+spec:
+  # Secret names are always required.
+  secretName: "{{ template "pulsar.fullname" . }}-{{ .Values.tls.toolset.cert_name }}"
+  duration: "{{ .Values.tls.common.duration }}"
+  renewBefore: "{{ .Values.tls.common.renewBefore }}"
+  organization:
+{{ toYaml .Values.tls.common.organization | indent 2 }}
+  # The use of the common name field has been deprecated since 2000 and is
+  # discouraged from being used.
+  commonName: "*.{{ template "pulsar.fullname" . }}-{{ .Values.toolset.component }}.{{ .Values.namespace }}.svc.cluster.local"
+  isCA: false
+  keySize: {{ .Values.tls.common.keySize }}
+  keyAlgorithm: {{ .Values.tls.common.keyAlgorithm }}
+  keyEncoding: {{ .Values.tls.common.keyEncoding }}
+  usages:
+    - server auth
+    - client auth
+  dnsNames:
+    -  "*.{{ template "pulsar.fullname" . }}-{{ .Values.toolset.component }}.{{ .Values.namespace }}.svc.cluster.local"
+    -  "{{ template "pulsar.fullname" . }}-{{ .Values.toolset.component }}"
+  # Issuer references are always required.
+  issuerRef:
+    name: "{{ template "pulsar.fullname" . }}-{{ .Values.certs.internal_issuer.component }}-ca-issuer"
+    # We can reference ClusterIssuers by changing the kind here.
+    # The default value is Issuer (i.e. a locally namespaced Issuer)
+    kind: Issuer
+    # This is optional since cert-manager will default to this value however
+    # if you are using an external issuer, change this to that issuer group.
+    group: cert-manager.io
+---
+apiVersion: cert-manager.io/v1alpha2
+kind: Certificate
+metadata:
+  name: "{{ template "pulsar.fullname" . }}-{{ .Values.tls.zookeeper.cert_name }}"
+  namespace: {{ .Values.namespace }}
+spec:
+  # Secret names are always required.
+  secretName: "{{ template "pulsar.fullname" . }}-{{ .Values.tls.zookeeper.cert_name }}"
+  duration: "{{ .Values.tls.common.duration }}"
+  renewBefore: "{{ .Values.tls.common.renewBefore }}"
+  organization:
+{{ toYaml .Values.tls.common.organization | indent 2 }}
+  # The use of the common name field has been deprecated since 2000 and is
+  # discouraged from being used.
+  commonName: "*.{{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}.{{ .Values.namespace }}.svc.cluster.local"
+  isCA: false
+  keySize: {{ .Values.tls.common.keySize }}
+  keyAlgorithm: {{ .Values.tls.common.keyAlgorithm }}
+  keyEncoding: {{ .Values.tls.common.keyEncoding }}
+  usages:
+    - server auth
+    - client auth
+  dnsNames:
+    -  "*.{{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}.{{ .Values.namespace }}.svc.cluster.local"
+    -  "{{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}"
+  # Issuer references are always required.
+  issuerRef:
+    name: "{{ template "pulsar.fullname" . }}-{{ .Values.certs.internal_issuer.component }}-ca-issuer"
+    # We can reference ClusterIssuers by changing the kind here.
+    # The default value is Issuer (i.e. a locally namespaced Issuer)
+    kind: Issuer
+    # This is optional since cert-manager will default to this value however
+    # if you are using an external issuer, change this to that issuer group.
+    group: cert-manager.io
+{{- end }}
+
+{{- end }}
+{{- end }}
\ No newline at end of file
diff --git a/pulsar/templates/toolset-configmap.yaml b/pulsar/templates/toolset-configmap.yaml
new file mode 100644
index 0000000..4e8fc16
--- /dev/null
+++ b/pulsar/templates/toolset-configmap.yaml
@@ -0,0 +1,70 @@
+#
+# 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 .Values.components.toolset }}
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: "{{ template "pulsar.fullname" . }}-{{ .Values.toolset.component }}"
+  namespace: {{ .Values.namespace }}
+  labels:
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
+    component: {{ .Values.toolset.component }}
+data:
+  BOOKIE_LOG_APPENDER: "RollingFile"
+  {{- include "pulsar.bookkeeper.config.common" . | nindent 2 }}
+  {{- if not .Values.toolset.useProxy }}
+  # talk to broker
+  {{- if and .Values.tls.enabled .Values.tls.broker.enabled }}
+  webServiceUrl: "https://{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}:{{ .Values.broker.ports.https }}/"
+  brokerServiceUrl: "pulsar+ssl://{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}:{{ .Values.broker.ports.pulsarssl }}/"
+  useTls: "true"
+  tlsAllowInsecureConnection: "false"
+  tlsTrustCertsFilePath: "/pulsar/certs/proxy-ca/ca.crt"
+  tlsEnableHostnameVerification: "false"
+  {{- end }}
+  {{- if not (and .Values.tls.enabled .Values.tls.broker.enabled) }}
+  webServiceUrl: "http://{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}:{{ .Values.broker.ports.http }}/"
+  brokerServiceUrl: "pulsar://{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}:{{ .Values.broker.ports.pulsar }}/"
+  {{- end }}
+  {{- end }}
+  {{- if .Values.toolset.useProxy }}
+  # talk to proxy
+  {{- if and .Values.tls.enabled .Values.tls.proxy.enabled }}
+  webServiceUrl: "https://{{ template "pulsar.fullname" . }}-{{ .Values.proxy.component }}:{{ .Values.proxy.ports.https }}/"
+  brokerServiceUrl: "pulsar+ssl://{{ template "pulsar.fullname" . }}-{{ .Values.proxy.component }}:{{ .Values.proxy.ports.pulsarssl }}/"
+  useTls: "true"
+  tlsAllowInsecureConnection: "false"
+  tlsTrustCertsFilePath: "/pulsar/certs/proxy-ca/ca.crt"
+  tlsEnableHostnameVerification: "false"
+  {{- end }}
+  {{- if not (and .Values.tls.enabled .Values.tls.proxy.enabled) }}
+  webServiceUrl: "http://{{ template "pulsar.fullname" . }}-{{ .Values.proxy.component }}:{{ .Values.proxy.ports.http }}/"
+  brokerServiceUrl: "pulsar://{{ template "pulsar.fullname" . }}-{{ .Values.proxy.component }}:{{ .Values.proxy.ports.pulsar }}/"
+  {{- end }}
+  {{- end }}
+  # Authentication Settings
+  {{- if .Values.auth.authentication.enabled }}
+  {{- if eq .Values.auth.authentication.provider "jwt" }}
+  authParams: "file:///pulsar/tokens/client/token"
+  authPlugin: "org.apache.pulsar.client.impl.auth.AuthenticationToken"
+  {{- end }} 
+  {{- end }} 
+{{ toYaml .Values.toolset.configData | indent 2 }}
+{{- end }}
diff --git a/pulsar/templates/zookeeper-configmap.yaml b/pulsar/templates/toolset-service.yaml
similarity index 68%
copy from pulsar/templates/zookeeper-configmap.yaml
copy to pulsar/templates/toolset-service.yaml
index 754f814..e1c0ccc 100644
--- a/pulsar/templates/zookeeper-configmap.yaml
+++ b/pulsar/templates/toolset-service.yaml
@@ -17,17 +17,18 @@
 # under the License.
 #
 
+{{- if .Values.components.toolset }}
 apiVersion: v1
-kind: ConfigMap
+kind: Service
 metadata:
-  name: "{{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}"
+  name: "{{ template "pulsar.fullname" . }}-{{ .Values.toolset.component }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
-    component: {{ .Values.zookeeper.component }}
-    cluster: {{ template "pulsar.fullname" . }}
-data:
-{{ toYaml .Values.zookeeper.configData | indent 2 }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
+    component: {{ .Values.toolset.component }}
+spec:
+  clusterIP: None
+  selector:
+    {{- include "pulsar.matchLabels" . | nindent 4 }}
+    component: {{ .Values.toolset.component }}
+{{- end }}
\ No newline at end of file
diff --git a/pulsar/templates/toolset-statefulset.yaml b/pulsar/templates/toolset-statefulset.yaml
new file mode 100644
index 0000000..b15ad8d
--- /dev/null
+++ b/pulsar/templates/toolset-statefulset.yaml
@@ -0,0 +1,108 @@
+#
+# 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 .Values.components.toolset }}
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+  name: "{{ template "pulsar.fullname" . }}-{{ .Values.toolset.component }}"
+  namespace: {{ .Values.namespace }}
+  labels:
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
+    component: {{ .Values.toolset.component }}
+spec:
+  serviceName: "{{ template "pulsar.fullname" . }}-{{ .Values.toolset.component }}"
+  replicas: {{ .Values.toolset.replicaCount }}
+  updateStrategy:
+    type: RollingUpdate
+  podManagementPolicy: Parallel
+  selector:
+    matchLabels:
+      {{- include "pulsar.matchLabels" . | nindent 6 }}
+      component: {{ .Values.toolset.component }}
+  template:
+    metadata:
+      labels:
+        {{- include "pulsar.template.labels" . | nindent 8 }}
+        component: {{ .Values.toolset.component }}
+      annotations:
+{{ toYaml .Values.toolset.annotations | indent 8 }}
+    spec:
+    {{- if .Values.toolset.nodeSelector }}
+      nodeSelector:
+{{ toYaml .Values.toolset.nodeSelector | indent 8 }}
+    {{- end }}
+    {{- if .Values.toolset.tolerations }}
+      tolerations:
+{{ toYaml .Values.toolset.tolerations | indent 8 }}
+    {{- end }}
+      terminationGracePeriodSeconds: {{ .Values.toolset.gracePeriod }}
+      containers:
+      - name: "{{ template "pulsar.fullname" . }}-{{ .Values.toolset.component }}"
+        image: "{{ .Values.images.broker.repository }}:{{ .Values.images.broker.tag }}"
+        imagePullPolicy: {{ .Values.images.broker.pullPolicy }}
+      {{- if .Values.toolset.resources }}
+        resources:
+{{ toYaml .Values.toolset.resources | indent 10 }}
+      {{- end }}
+        command: ["sh", "-c"]
+        args:
+        - >
+          bin/apply-config-from-env.py conf/client.conf;
+          bin/apply-config-from-env.py conf/bookkeeper.conf;
+          {{- include "pulsar.toolset.zookeeper.tls.settings" . | nindent 10 }}
+          sleep 10000000000
+        envFrom:
+        - configMapRef:
+            name: "{{ template "pulsar.fullname" . }}-{{ .Values.toolset.component }}"
+        volumeMounts:
+        {{- if .Values.auth.authentication.enabled }}
+        {{- if eq .Values.auth.authentication.provider "jwt" }}
+        - mountPath: "/pulsar/tokens"
+          name: client-token
+          readOnly: true
+        {{- end }}
+        {{- end }}
+        {{- if and .Values.tls.enabled (or .Values.tls.broker.enabled .Values.tls.proxy.enabled) }}
+        - mountPath: "/pulsar/certs/proxy-ca"
+          name: proxy-ca
+          readOnly: true
+        {{- end}}
+        {{- include "pulsar.toolset.certs.volumeMounts" . | nindent 8 }}
+      volumes:
+      {{- if .Values.auth.authentication.enabled }}
+      {{- if eq .Values.auth.authentication.provider "jwt" }}
+      - name: client-token
+        secret:
+          secretName: "{{ .Release.Name }}-token-{{ .Values.auth.superUsers.client }}"
+          items:
+            - key: TOKEN
+              path: client/token
+      {{- end}}
+      {{- end}}
+      {{- if and .Values.tls.enabled (or .Values.tls.broker.enabled .Values.tls.proxy.enabled) }}
+      - name: proxy-ca
+        secret:
+          secretName: "{{ template "pulsar.fullname" . }}-ca-tls"
+          items:
+            - key: ca.crt
+              path: ca.crt
+      {{- end}}
+      {{- include "pulsar.toolset.certs.volumes" . | nindent 6 }}
+{{- end }}
diff --git a/pulsar/templates/zookeeper-configmap.yaml b/pulsar/templates/zookeeper-configmap.yaml
index 754f814..3b4cabf 100644
--- a/pulsar/templates/zookeeper-configmap.yaml
+++ b/pulsar/templates/zookeeper-configmap.yaml
@@ -17,17 +17,24 @@
 # under the License.
 #
 
+# deploy zookeeper only when `components.zookeeper` is true
+{{- if .Values.components.zookeeper }}
 apiVersion: v1
 kind: ConfigMap
 metadata:
   name: "{{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
     component: {{ .Values.zookeeper.component }}
-    cluster: {{ template "pulsar.fullname" . }}
 data:
+  dataDir: /pulsar/data/zookeeper
+  PULSAR_PREFIX_serverCnxnFactory: org.apache.zookeeper.server.NettyServerCnxnFactory
+  serverCnxnFactory: org.apache.zookeeper.server.NettyServerCnxnFactory
+  # enable zookeeper tls
+  {{- if and .Values.tls.enabled .Values.tls.zookeeper.enabled }}
+  secureClientPort: "{{ .Values.zookeeper.ports.clientTls }}"
+  PULSAR_PREFIX_secureClientPort: "{{ .Values.zookeeper.ports.clientTls }}"
+  {{- end }}
 {{ toYaml .Values.zookeeper.configData | indent 2 }}
+{{- end }}
diff --git a/pulsar/templates/zookeeper-metadata.yaml b/pulsar/templates/zookeeper-metadata.yaml
deleted file mode 100644
index 5115286..0000000
--- a/pulsar/templates/zookeeper-metadata.yaml
+++ /dev/null
@@ -1,62 +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.
-#
-
-apiVersion: batch/v1
-kind: Job
-metadata:
-  name: "{{ template "pulsar.fullname" . }}-{{ .Values.zookeeperMetadata.component }}"
-  namespace: {{ .Values.namespace }}
-  labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
-    component: {{ .Values.zookeeperMetadata.component }}
-    cluster: {{ template "pulsar.fullname" . }}
-spec:
-  template:
-    spec:
-      initContainers:
-      - name: wait-zookeeper-ready
-        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
-        imagePullPolicy: {{ .Values.image.pullPolicy }}
-        command: ["sh", "-c"]
-        args:
-          - >-
-            until nslookup {{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}-{{ add (.Values.zookeeper.replicaCount | int) -1 }}.{{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}.{{ .Values.namespace }}; do
-              sleep 3;
-            done;
-      containers:
-      - name: "{{ template "pulsar.fullname" . }}-{{ .Values.zookeeperMetadata.component }}"
-        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
-        imagePullPolicy: {{ .Values.image.pullPolicy }}
-      {{- if .Values.zookeeperMetadata.resources }}
-        resources:
-{{ toYaml .Values.zookeeperMetadata.resources | indent 10 }}
-      {{- end }}
-        command: ["sh", "-c"]
-        args:
-          - >
-            bin/pulsar initialize-cluster-metadata \
-              --cluster {{ template "pulsar.fullname" . }} \
-              --zookeeper {{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }} \
-              --configuration-store {{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }} \
-              --web-service-url http://{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}.{{ .Values.namespace }}.svc.cluster.local:8080/ \
-              --broker-service-url pulsar://{{ template "pulsar.fullname" . }}-{{ .Values.broker.component }}.{{ .Values.namespace }}.svc.cluster.local:6650/ || true;
-      restartPolicy: Never
diff --git a/pulsar/templates/zookeeper-pdb.yaml b/pulsar/templates/zookeeper-pdb.yaml
index d205883..5417bed 100644
--- a/pulsar/templates/zookeeper-pdb.yaml
+++ b/pulsar/templates/zookeeper-pdb.yaml
@@ -17,6 +17,8 @@
 # under the License.
 #
 
+# deploy zookeeper only when `components.zookeeper` is true
+{{- if .Values.components.zookeeper }}
 {{- if .Values.zookeeper.pdb.usePolicy }}
 apiVersion: policy/v1beta1
 kind: PodDisruptionBudget
@@ -24,17 +26,13 @@ metadata:
   name: "{{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
     component: {{ .Values.zookeeper.component }}
-    cluster: {{ template "pulsar.fullname" . }}
 spec:
   selector:
     matchLabels:
-      app: {{ template "pulsar.name" . }}
-      release: {{ .Release.Name }}
+      {{- include "pulsar.matchLabels" . | nindent 6 }}
       component: {{ .Values.zookeeper.component }}
   maxUnavailable: {{ .Values.zookeeper.pdb.maxUnavailable }}
 {{- end }}
+{{- end }}
diff --git a/pulsar/templates/zookeeper-service.yaml b/pulsar/templates/zookeeper-service.yaml
index d7d8167..478a264 100644
--- a/pulsar/templates/zookeeper-service.yaml
+++ b/pulsar/templates/zookeeper-service.yaml
@@ -17,25 +17,32 @@
 # under the License.
 #
 
+# deploy zookeeper only when `components.zookeeper` is true
+{{- if .Values.components.zookeeper }}
 apiVersion: v1
 kind: Service
 metadata:
   name: "{{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
     component: {{ .Values.zookeeper.component }}
-    cluster: {{ template "pulsar.fullname" . }}
   annotations:
 {{ toYaml .Values.zookeeper.service.annotations | indent 4 }}
 spec:
   ports:
-{{ toYaml .Values.zookeeper.service.ports | indent 2 }}
+    - name: follower
+      port: {{ .Values.zookeeper.ports.follower }}
+    - name: leader-election
+      port: {{ .Values.zookeeper.ports.leaderElection }}
+    - name: client
+      port: {{ .Values.zookeeper.ports.client }}
+    {{- if and .Values.tls.enabled .Values.tls.zookeeper.enabled }}
+    - name: client-tls
+      port: {{ .Values.zookeeper.ports.clientTls }}
+    {{- end }}
   clusterIP: None
   selector:
-    app: {{ template "pulsar.name" . }}
-    release: {{ .Release.Name }}
+    {{- include "pulsar.matchLabels" . | nindent 4 }}
     component: {{ .Values.zookeeper.component }}
+{{- end }}
diff --git a/pulsar/templates/zookeeper-statefulset.yaml b/pulsar/templates/zookeeper-statefulset.yaml
index fda52fe..21a4553 100644
--- a/pulsar/templates/zookeeper-statefulset.yaml
+++ b/pulsar/templates/zookeeper-statefulset.yaml
@@ -17,25 +17,22 @@
 # under the License.
 #
 
+# deploy zookeeper only when `components.zookeeper` is true
+{{- if .Values.components.zookeeper }}
 apiVersion: apps/v1
 kind: StatefulSet
 metadata:
   name: "{{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
     component: {{ .Values.zookeeper.component }}
-    cluster: {{ template "pulsar.fullname" . }}
 spec:
   serviceName: "{{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}"
   replicas: {{ .Values.zookeeper.replicaCount }}
   selector:
     matchLabels:
-      app: {{ template "pulsar.name" . }}
-      release: {{ .Release.Name }}
+      {{- include "pulsar.matchLabels" . | nindent 6 }}
       component: {{ .Values.zookeeper.component }}
   updateStrategy:
 {{ toYaml .Values.zookeeper.updateStrategy | indent 4 }}
@@ -43,10 +40,8 @@ spec:
   template:
     metadata:
       labels:
-        app: {{ template "pulsar.name" . }}
-        release: {{ .Release.Name }}
+        {{- include "pulsar.template.labels" . | nindent 8 }}
         component: {{ .Values.zookeeper.component }}
-        cluster: {{ template "pulsar.fullname" . }}
       annotations:
 {{ toYaml .Values.zookeeper.annotations | indent 8 }}
     spec:
@@ -59,6 +54,7 @@ spec:
 {{ toYaml .Values.zookeeper.tolerations | indent 8 }}
     {{- end }}
       affinity:
+        {{- if and .Values.affinity.anti_affinity .Values.zookeeper.affinity.anti_affinity}}
         podAntiAffinity:
           requiredDuringSchedulingIgnoredDuringExecution:
           - labelSelector:
@@ -66,7 +62,7 @@ spec:
               - key: "app"
                 operator: In
                 values:
-                - "{{ template "pulsar.name" . }}"
+                - "{{ template "pulsar.name" . }}-{{ .Values.zookeeper.component }}"
               - key: "release"
                 operator: In
                 values:
@@ -76,11 +72,12 @@ spec:
                 values:
                 - {{ .Values.zookeeper.component }}
             topologyKey: "kubernetes.io/hostname"
+        {{- end }}
       terminationGracePeriodSeconds: {{ .Values.zookeeper.gracePeriod }}
       containers:
       - name: "{{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}"
-        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
-        imagePullPolicy: {{ .Values.image.pullPolicy }}
+        image: "{{ .Values.images.zookeeper.repository }}:{{ .Values.images.zookeeper.tag }}"
+        imagePullPolicy: {{ .Values.images.zookeeper.pullPolicy }}
       {{- if .Values.zookeeper.resources }}
         resources:
 {{ toYaml .Values.zookeeper.resources | indent 10 }}
@@ -88,17 +85,22 @@ spec:
         command: ["sh", "-c"]
         args:
         - >
-          bin/apply-config-from-env.py conf/zookeeper.conf &&
-          bin/apply-config-from-env.py conf/pulsar_env.sh &&
-          bin/generate-zookeeper-config.sh conf/zookeeper.conf &&
-          bin/pulsar zookeeper
+          bin/apply-config-from-env.py conf/zookeeper.conf;
+          bin/apply-config-from-env.py conf/pulsar_env.sh;
+          {{- include "pulsar.zookeeper.tls.settings" . | nindent 10 }}
+          bin/generate-zookeeper-config.sh conf/zookeeper.conf;
+          bin/pulsar zookeeper;
         ports:
         - name: client
-          containerPort: 2181
-        - name: server
-          containerPort: 2888
+          containerPort: {{ .Values.zookeeper.ports.client }}
+        - name: follower
+          containerPort: {{ .Values.zookeeper.ports.follower }}
         - name: leader-election
-          containerPort: 3888
+          containerPort: {{ .Values.zookeeper.ports.leaderElection }}
+        {{- if and .Values.tls.enabled .Values.tls.zookeeper.enabled }}
+        - name: client-tls
+          containerPort: {{ .Values.zookeeper.ports.clientTls }}
+        {{- end }}
         env:
         - name: ZOOKEEPER_SERVERS
           value:
@@ -107,27 +109,73 @@ spec:
         envFrom:
         - configMapRef:
             name: "{{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}"
+        {{- if .Values.zookeeper.probe.readiness.enabled }}
         readinessProbe:
           exec:
             command:
-            - "bin/pulsar-zookeeper-ruok.sh"
-          initialDelaySeconds: 5
-          timeoutSeconds: 5
+            - bin/pulsar-zookeeper-ruok.sh
+          initialDelaySeconds: {{ .Values.zookeeper.probe.readiness.initialDelaySeconds }}
+          periodSeconds: {{ .Values.zookeeper.probe.readiness.periodSeconds }}
+          failureThreshold: {{ .Values.zookeeper.probe.readiness.failureThreshold }}
+        {{- end }}
+        {{- if .Values.zookeeper.probe.liveness.enabled }}
         livenessProbe:
           exec:
             command:
-            - "bin/pulsar-zookeeper-ruok.sh"
-          initialDelaySeconds: 15
-          timeoutSeconds: 5
+            - bin/pulsar-zookeeper-ruok.sh
+          initialDelaySeconds: {{ .Values.zookeeper.probe.liveness.initialDelaySeconds }}
+          periodSeconds: {{ .Values.zookeeper.probe.liveness.periodSeconds }}
+          failureThreshold: {{ .Values.zookeeper.probe.liveness.failureThreshold }}
+        {{- end }}
+        {{- if .Values.zookeeper.probe.startup.enabled }}
+        startupProbe:
+          exec:
+            command:
+            - bin/pulsar-zookeeper-ruok.sh
+          initialDelaySeconds: {{ .Values.zookeeper.probe.startup.initialDelaySeconds }}
+          periodSeconds: {{ .Values.zookeeper.probe.startup.periodSeconds }}
+          failureThreshold: {{ .Values.zookeeper.probe.startup.failureThreshold }}
+        {{- end }}
         volumeMounts:
         - name: "{{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}-{{ .Values.zookeeper.volumes.data.name }}"
           mountPath: /pulsar/data
-    {{- if not .Values.persistence }}
+        {{- if and .Values.tls.enabled .Values.tls.zookeeper.enabled }}
+        - mountPath: "/pulsar/certs/zookeeper"
+          name: zookeeper-certs
+          readOnly: true
+        - mountPath: "/pulsar/certs/ca"
+          name: ca
+          readOnly: true
+        - name: keytool
+          mountPath: "/pulsar/keytool/keytool.sh"
+          subPath: keytool.sh
+        {{- end }}
       volumes:
+      {{- if not (and (and .Values.volumes.persistence .Values.volumes.persistence) .Values.zookeeper.volumes.persistence) }}
       - name: "{{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}-{{ .Values.zookeeper.volumes.data.name }}"
         emptyDir: {}
-    {{- end }}
-{{- if .Values.persistence }}
+      {{- end }}
+      {{- if and .Values.tls.enabled .Values.tls.zookeeper.enabled }}
+      - name: zookeeper-certs
+        secret:
+          secretName: "{{ template "pulsar.fullname" . }}-{{ .Values.tls.zookeeper.cert_name }}"
+          items:
+            - key: tls.crt
+              path: tls.crt
+            - key: tls.key
+              path: tls.key
+      - name: ca
+        secret:
+          secretName: "{{ template "pulsar.fullname" . }}-ca-tls"
+          items:
+            - key: ca.crt
+              path: ca.crt
+      - name: keytool
+        configMap:
+          name: "{{ template "pulsar.fullname" . }}-keytool-configmap"
+          defaultMode: 0755
+      {{- end}}
+{{- if and (and .Values.persistence .Values.volumes.persistence) .Values.zookeeper.volumes.persistence }}
   volumeClaimTemplates:
   - metadata:
       name: "{{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}-{{ .Values.zookeeper.volumes.data.name }}"
@@ -138,7 +186,10 @@ spec:
           storage: {{ .Values.zookeeper.volumes.data.size }}
     {{- if .Values.zookeeper.volumes.data.storageClassName }}
       storageClassName: "{{ .Values.zookeeper.volumes.data.storageClassName }}"
-    {{- else if .Values.zookeeper.volumes.data.storageClass }}
+    {{- else if and (not (and .Values.volumes.local_storage .Values.zookeeper.volumes.data.local_storage)) .Values.zookeeper.volumes.data.storageClass }}
       storageClassName: "{{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}-{{ .Values.zookeeper.volumes.data.name }}"
+    {{- else if and .Values.volumes.local_storage .Values.zookeeper.volumes.data.local_storage }}
+      storageClassName: "local-storage"
     {{- end }}
 {{- end }}
+{{- end }}
diff --git a/pulsar/templates/zookeeper-storageclass.yaml b/pulsar/templates/zookeeper-storageclass.yaml
index 7562337..08b66c8 100644
--- a/pulsar/templates/zookeeper-storageclass.yaml
+++ b/pulsar/templates/zookeeper-storageclass.yaml
@@ -17,23 +17,24 @@
 # under the License.
 #
 
-{{- if .Values.persistence }}
-{{- if .Values.zookeeper.volumes.data.storageClass }}
+# deploy zookeeper only when `components.zookeeper` is true
+{{- if .Values.components.zookeeper }}
+{{- if and (and .Values.persistence .Values.volumes.persistence) .Values.zookeeper.volumes.persistence }}
+
+# define the storage class for data directory
+{{- if and (not (and .Values.volumes.local_storage .Values.zookeeper.volumes.data.local_storage)) .Values.zookeeper.volumes.data.storageClass }}
 apiVersion: storage.k8s.io/v1
 kind: StorageClass
 metadata:
   name: "{{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}-{{ .Values.zookeeper.volumes.data.name }}"
   namespace: {{ .Values.namespace }}
   labels:
-    app: {{ template "pulsar.name" . }}
-    chart: {{ template "pulsar.chart" . }}
-    release: {{ .Release.Name }}
-    heritage: {{ .Release.Service }}
+    {{- include "pulsar.standardLabels" . | nindent 4 }}
     component: {{ .Values.zookeeper.component }}
-    cluster: {{ template "pulsar.fullname" . }}
 provisioner: {{ .Values.zookeeper.volumes.data.storageClass.provisioner }}
 parameters:
   type: {{ .Values.zookeeper.volumes.data.storageClass.type }}
   fsType: {{ .Values.zookeeper.volumes.data.storageClass.fsType }}
 {{- end }}
 {{- end }}
+{{- end }}
diff --git a/pulsar/values-mini.yaml b/pulsar/values-mini.yaml
deleted file mode 100644
index e21c3d0..0000000
--- a/pulsar/values-mini.yaml
+++ /dev/null
@@ -1,528 +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.
-#
-
-## Namespace to deploy pulsar
-namespace: pulsar
-namespaceCreate: yes
-
-## If persistence is enabled, components that have state will
-## be deployed with PersistentVolumeClaims, otherwise, for test
-## purposes, they will be deployed with emptyDir
-persistence: no
-
-## If prometheus_persistence is enabled, prometheus will be deployed
-## with PersistentVolumeClaims, otherwise, for test purposes, they
-## will be deployed with emptyDir
-prometheus_persistence: no
-
-prometheus_rbac: yes
-
-## which extra components to deploy
-extra:
-  # Pulsar proxy
-  proxy: yes
-  # Bookkeeper auto-recovery
-  autoRecovery: yes
-  # Pulsar dashboard
-  # Deprecated
-  # Replace pulsar-dashboard with pulsar-manager
-  dashboard: no
-  # pulsar manager
-  pulsar_manager: yes
-  # Bastion pod for administrative commands
-  bastion: yes
-  # Monitoring stack (prometheus and grafana)
-  monitoring: yes
-  # Configure Kubernetes runtime for Functions
-  functionsAsPods: no
-
-## Which pulsar image to use
-image:
-  repository: apachepulsar/pulsar-all
-  tag: latest
-  pullPolicy: IfNotPresent
-
-## Pulsar: Zookeeper cluster
-## templates/zookeeper-statefulset.yaml
-##
-zookeeper:
-  component: zookeeper
-  replicaCount: 3
-  updateStrategy:
-    type: OnDelete
-  podManagementPolicy: OrderedReady
-  # nodeSelector:
-    # cloud.google.com/gke-nodepool: default-pool
-  annotations:
-    prometheus.io/scrape: "true"
-    prometheus.io/port: "8000"
-  tolerations: []
-  gracePeriod: 0
-  resources:
-    requests:
-      memory: 64Mi
-      cpu: 0.1
-  volumes:
-    data:
-      name: data
-      size: 2Gi
-      ## If you already have an existent storage class and want to reuse it, you can specify its name with the option below
-      ##
-      # storageClassName: existent-storage-class
-      #
-      ## Instead if you want to create a new storage class define it below
-      ## If left undefined no storage class will be defined along with PVC
-      ##
-      # storageClass:
-        # type: pd-ssd
-        # fsType: xfs
-        # provisioner: kubernetes.io/gce-pd
-  ## Zookeeper configmap
-  ## templates/zookeeper-configmap.yaml
-  ##
-  configData:
-    PULSAR_MEM: "\"-Xms64m -Xmx128m -Dcom.sun.management.jmxremote -Djute.maxbuffer=10485760 -XX:+ParallelRefProcEnabled -XX:+UnlockExperimentalVMOptions -XX:+DoEscapeAnalysis -XX:+DisableExplicitGC -XX:+PerfDisableSharedMem -Dzookeeper.forceSync=no\""
-    PULSAR_GC: "\"-XX:+UseG1GC -XX:MaxGCPauseMillis=10\""
-  ## Zookeeper service
-  ## templates/zookeeper-service.yaml
-  ##
-  service:
-    annotations:
-      service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
-    ports:
-    - name: server
-      port: 2888
-    - name: leader-election
-      port: 3888
-    - name: stats
-      port: 2181
-  ## Zookeeper PodDisruptionBudget
-  ## templates/zookeeper-pdb.yaml
-  ##
-  pdb:
-    usePolicy: yes
-    maxUnavailable: 1
-
-## Pulsar Zookeeper metadata. The metadata will be deployed as
-## soon as the last zookeeper node is reachable. The deployment
-## of other components that depends on zookeeper, such as the
-## bookkeeper nodes, broker nodes, etc will only start to be
-## deployed when the zookeeper cluster is ready and with the
-## metadata deployed
-zookeeperMetadata:
-  component: zookeeper-metadata
-
-## Pulsar: Bookkeeper cluster
-## templates/bookkeeper-statefulset.yaml
-##
-bookkeeper:
-  component: bookkeeper
-  replicaCount: 3
-  updateStrategy:
-    type: OnDelete
-  podManagementPolicy: OrderedReady
-  # nodeSelector:
-    # cloud.google.com/gke-nodepool: default-pool
-  annotations:
-    prometheus.io/scrape: "true"
-    prometheus.io/port: "8000"
-  tolerations: []
-  gracePeriod: 0
-  resources:
-    requests:
-      memory: 128Mi
-      cpu: 0.2
-  volumes:
-    journal:
-      name: journal
-      size: 5Gi
-      ## If you already have an existent storage class and want to reuse it, you can specify its name with the option below
-      ##
-      # storageClassName: existent-storage-class
-      #
-      ## Instead if you want to create a new storage class define it below
-      ## If left undefined no storage class will be defined along with PVC
-      ##
-      # storageClass:
-        # type: pd-ssd
-        # fsType: xfs
-        # provisioner: kubernetes.io/gce-pd
-    ledgers:
-      name: ledgers
-      size: 5Gi
-      ## If you already have an existent storage class and want to reuse it, you can specify its name with the option below
-      ##
-      # storageClassName: existent-storage-class
-      #
-      ## Instead if you want to create a new storage class define it below
-      ## If left undefined no storage class will be defined along with PVC
-      ##
-      # storageClass:
-        # type: pd-ssd
-        # fsType: xfs
-        # provisioner: kubernetes.io/gce-pd
-  ## Bookkeeper configmap
-  ## templates/bookkeeper-configmap.yaml
-  ##
-  configData:
-    BOOKIE_MEM: "\"-Xms128m -Xmx256m -XX:MaxDirectMemorySize=128m -Dio.netty.leakDetectionLevel=disabled -Dio.netty.recycler.linkCapacity=1024 -XX:+ParallelRefProcEnabled -XX:+UnlockExperimentalVMOptions -XX:+DoEscapeAnalysis -XX:ParallelGCThreads=32 -XX:ConcGCThreads=32 -XX:G1NewSizePercent=50 -XX:+DisableExplicitGC -XX:-ResizePLAB -XX:+ExitOnOutOfMemoryError -XX:+PerfDisableSharedMem\""
-    BOOKIE_GC: "\"-XX:+UseG1GC -XX:MaxGCPauseMillis=10\""
-    dbStorage_writeCacheMaxSizeMb: "32"
-    dbStorage_readAheadCacheMaxSizeMb: "32"
-    journalMaxSizeMB: "2048"
-    statsProviderClass: org.apache.bookkeeper.stats.prometheus.PrometheusMetricsProvider
-    useHostNameAsBookieID: "true"
-  ## Bookkeeper configmap
-  ## templates/bookkeeper-service.yaml
-  ##
-  service:
-    annotations:
-      publishNotReadyAddresses: "true"
-    ports:
-    - name: server
-      port: 3181
-  ## Bookkeeper PodDisruptionBudget
-  ## templates/bookkeeper-pdb.yaml
-  ##
-  pdb:
-    usePolicy: yes
-    maxUnavailable: 1
-
-## Pulsar: Broker cluster
-## templates/broker-deployment.yaml
-##
-broker:
-  component: broker
-  replicaCount: 2
-  # nodeSelector:
-    # cloud.google.com/gke-nodepool: default-pool
-  annotations:
-    prometheus.io/scrape: "true"
-    prometheus.io/port: "8080"
-  tolerations: []
-  gracePeriod: 0
-  resources:
-    requests:
-      memory: 128Mi
-      cpu: 0.2
-  ## Broker configmap
-  ## templates/broker-configmap.yaml
-  ##
-  configData:
-    PULSAR_MEM: "\"-Xms128m -Xmx256m -XX:MaxDirectMemorySize=128m -Dio.netty.leakDetectionLevel=disabled -Dio.netty.recycler.linkCapacity=1024 -XX:+ParallelRefProcEnabled -XX:+UnlockExperimentalVMOptions -XX:+DoEscapeAnalysis -XX:ParallelGCThreads=32 -XX:ConcGCThreads=32 -XX:G1NewSizePercent=50 -XX:+DisableExplicitGC -XX:-ResizePLAB -XX:+ExitOnOutOfMemoryError -XX:+PerfDisableSharedMem\""
-    PULSAR_GC: "\"-XX:+UseG1GC -XX:MaxGCPauseMillis=10\""
-    managedLedgerDefaultEnsembleSize: "2"
-    managedLedgerDefaultWriteQuorum: "2"
-    managedLedgerDefaultAckQuorum: "2"
-    deduplicationEnabled: "false"
-    exposeTopicLevelMetricsInPrometheus: "true"
-  ## Broker service
-  ## templates/broker-service.yaml
-  ##
-  service:
-    annotations: {}
-    ports:
-    - name: http
-      port: 8080
-    - name: pulsar
-      port: 6650
-  ## Broker PodDisruptionBudget
-  ## templates/broker-pdb.yaml
-  ##
-  pdb:
-    usePolicy: yes
-    maxUnavailable: 1
-  ## Broker rbac
-  ## templates/broker-rbac.yaml
-  ##
-  functions:
-    component: functions-worker
-
-## Pulsar Extra: Proxy
-## templates/proxy-deployment.yaml
-##
-proxy:
-  component: proxy
-  replicaCount: 1
-  # nodeSelector:
-    # cloud.google.com/gke-nodepool: default-pool
-  annotations:
-    prometheus.io/scrape: "true"
-    prometheus.io/port: "8080"
-  tolerations: []
-  gracePeriod: 0
-  resources:
-    requests:
-      memory: 64Mi
-      cpu: 0.1
-  ## Proxy configmap
-  ## templates/proxy-configmap.yaml
-  ##
-  configData:
-    PULSAR_MEM: "\"-Xms64m -Xmx128m -XX:MaxDirectMemorySize=64m\""
-  ## Proxy service
-  ## templates/proxy-service.yaml
-  ##
-  service:
-    annotations: {}
-    type: NodePort
-    ports:
-    - name: http
-      port: 8080
-      nodePort: 30001
-      protocol: TCP
-    - name: tcp
-      port: 6650
-      nodePort: 30002
-      protocol: TCP
-  ## Proxy PodDisruptionBudget
-  ## templates/proxy-pdb.yaml
-  ##
-  pdb:
-    usePolicy: yes
-    maxUnavailable: 1
-
-## Pulsar Extra: Bookkeeper auto-recovery
-## templates/autorecovery-deployment.yaml
-##
-autoRecovery:
-  component: autorecovery
-  replicaCount: 1
-  # nodeSelector:
-    # cloud.google.com/gke-nodepool: default-pool
-  annotations: {}
-  tolerations: []
-  gracePeriod: 0
-  resources:
-    requests:
-      memory: 64Mi
-      cpu: 0.05
-  ## Bookkeeper auto-recovery configmap
-  ## templates/autorecovery-configmap.yaml
-  ##
-  configData:
-    BOOKIE_MEM: "\" -Xms64m -Xmx128m \""
-
-## Pulsar Extra: Dashboard
-## templates/dashboard-deployment.yaml
-## Deprecated
-##
-dashboard:
-  component: dashboard
-  replicaCount: 1
-  # nodeSelector:
-    # cloud.google.com/gke-nodepool: default-pool
-  annotations: {}
-  tolerations: []
-  gracePeriod: 0
-  image:
-    repository: apachepulsar/pulsar-dashboard
-    tag: latest
-    pullPolicy: IfNotPresent
-    resources:
-      requests:
-        memory: 64Mi
-        cpu: 0.1
-  ## Dashboard service
-  ## templates/dashboard-service.yaml
-  ##
-  service:
-    annotations: {}
-    ports:
-    - name: server
-      port: 80
-  ingress:
-    enabled: false
-    annotations: {}
-    tls:
-      enabled: false
-
-      ## Optional. Leave it blank if your Ingress Controller can provide a default certificate.
-      secretName: ""
-
-    ## Required if ingress is enabled
-    hostname: ""
-    path: "/"
-    port: 80
-
-
-## Pulsar Extra: Bastion
-## templates/bastion-deployment.yaml
-##
-bastion:
-  component: bastion
-  replicaCount: 1
-  # nodeSelector:
-    # cloud.google.com/gke-nodepool: default-pool
-  annotations: {}
-  tolerations: []
-  gracePeriod: 0
-  resources:
-    requests:
-      memory: 128Mi
-      cpu: 0.1
-  ## Bastion configmap
-  ## templates/bastion-configmap.yaml
-  ##
-  configData:
-    PULSAR_MEM: "\"-Xms128m -Xmx256m -XX:MaxDirectMemorySize=128m\""
-
-## Monitoring Stack: Prometheus
-## templates/prometheus-deployment.yaml
-##
-prometheus:
-  component: prometheus
-  replicaCount: 1
-  # nodeSelector:
-    # cloud.google.com/gke-nodepool: default-pool
-  annotations: {}
-  tolerations: []
-  gracePeriod: 0
-  image:
-    repository: prom/prometheus
-    tag: v1.6.3
-    pullPolicy: IfNotPresent
-    resources:
-      requests:
-        memory: 64Mi
-        cpu: 0.1
-  volumes:
-    data:
-      name: data
-      size: 2Gi
-      ## If you already have an existent storage class and want to reuse it, you can specify its name with the option below
-      ##
-      # storageClassName: existent-storage-class
-      #
-      ## Instead if you want to create a new storage class define it below
-      ## If left undefined no storage class will be defined along with PVC
-      ##
-      # storageClass:
-        # type: pd-standard
-        # fsType: xfs
-        # provisioner: kubernetes.io/gce-pd
-      ## Prometheus service
-  ## templates/prometheus-service.yaml
-  ##
-  service:
-    annotations: {}
-    ports:
-    - name: server
-      port: 9090
-
-## Monitoring Stack: Grafana
-## templates/grafana-deployment.yaml
-##
-grafana:
-  component: grafana
-  replicaCount: 1
-  # nodeSelector:
-    # cloud.google.com/gke-nodepool: default-pool
-  annotations: {}
-  tolerations: []
-  gracePeriod: 0
-  image:
-    repository: apachepulsar/pulsar-grafana
-    tag: latest
-    pullPolicy: IfNotPresent
-    resources:
-      requests:
-        memory: 64Mi
-        cpu: 0.1
-      ## Grafana service
-  ## templates/grafana-service.yaml
-  ##
-  service:
-    annotations: {}
-    ports:
-    - name: server
-      port: 3000
-  plugins: []
-  ## Grafana ingress
-  ## templates/grafana-ingress.yaml
-  ##
-  ingress:
-    enabled: false
-    annotations:
-      kubernetes.io/ingress.class: nginx
-      # nginx.ingress.kubernetes.io/rewrite-target: /$1
-      # ingress.kubernetes.io/force-ssl-redirect: "true"
-      ingress.kubernetes.io/rewrite-target: /
-    labels: {}
-
-    tls: []
-
-    ## Optional. Leave it blank if your Ingress Controller can provide a default certificate.
-    #- secretName: ""
-
-    ## Extra paths to prepend to every host configuration. This is useful when working with annotation based services.
-    extraPaths: []
-    ## Required if ingress is enabled
-    hostname: ""
-    protocol: http
-    path: /grafana
-    port: 80
-
-zookeeper_metadata:
-  resources:
-    requests:
-      memory: 128Mi
-      cpu: 0.1
-
-## Components Stack: pulsar_manager
-## templates/pulsar-manager.yaml
-##
-
-pulsar_manager:
-  component: pulsar-manager
-  replicaCount: 1
-  # nodeSelector:
-  # cloud.google.com/gke-nodepool: default-pool
-  annotations: {}
-  tolerations: []
-  gracePeriod: 0
-  image:
-    repository: apachepulsar/pulsar-manager
-    tag: v0.1.0
-    pullPolicy: IfNotPresent
-  resources:
-    requests:
-      memory: 250Mi
-      cpu: 0.1
-  configData:
-    REDIRECT_HOST: "http://127.0.0.1"
-    REDIRECT_PORT: "9527"
-    DRIVER_CLASS_NAME: org.postgresql.Driver
-    URL: jdbc:postgresql://127.0.0.1:5432/pulsar_manager
-    LOG_LEVEL: DEBUG
-    ## If you enabled authentication support
-    #JWT_TOKEN: <token>
-    #SECRET_KEY: data:base64,<secret key>
-  ## Pulsar manager service
-  ## templates/pulsar-manager-service.yaml
-  ##
-  service:
-    type: LoadBalancer
-    annotations: {}
-    ports:
-      - name: server
-        port: 9527
-  admin:
-    user: pulsar
-    password: pulsar
diff --git a/pulsar/values.yaml b/pulsar/values.yaml
index 312dfb7..5c1582d 100644
--- a/pulsar/values.yaml
+++ b/pulsar/values.yaml
@@ -17,71 +17,285 @@
 # under the License.
 #
 
+
+###
+### K8S Settings
+###
+
 ## Namespace to deploy pulsar
 namespace: pulsar
-namespaceCreate: yes
+namespaceCreate: false
+
+###
+### Global Settings
+###
+
+## Pulsar Metadata Prefix
+##
+## By default, pulsar stores all the metadata at root path.
+## You can configure to have a prefix (e.g. "/my-pulsar-cluster").
+## If you do so, all the pulsar and bookkeeper metadata will
+## be stored under the provided path
+metadataPrefix: ""
 
+## Persistence
+##
 ## If persistence is enabled, components that have state will
 ## be deployed with PersistentVolumeClaims, otherwise, for test
 ## purposes, they will be deployed with emptyDir
-persistence: no
+##
+## This is a global setting that is applied to all components.
+## If you need to disable persistence for a component,
+## you can set the `volume.persistence` setting to `false` for
+## that component.
+##
+## Deprecated in favor of using `volumes.persistence`
+persistence: true
+## Volume settings
+volumes:
+  persistence: true
+  # configure the components to use local persistent volume
+  # the local provisioner should be installed prior to enable local persistent volume
+  local_storage: false
 
-## If prometheus_persistence is enabled, prometheus will be deployed
-## with PersistentVolumeClaims, otherwise, for test purposes, they
-## will be deployed with emptyDir
-prometheus_persistence: yes
+## AntiAffinity
+##
+## Flag to enable and disable `AntiAffinity` for all components.
+## This is a global setting that is applied to all components.
+## If you need to disable AntiAffinity for a component, you can set
+## the `affinity.anti_affinity` settings to `false` for that component.
+affinity:
+  anti_affinity: true
 
-prometheus_rbac: yes
+## Components
+##
+## Control what components of Apache Pulsar to deploy for the cluster
+components:
+  # zookeeper
+  zookeeper: true
+  # bookkeeper
+  bookkeeper: true
+  # bookkeeper - autorecovery
+  autorecovery: true
+  # broker
+  broker: true
+  # functions
+  functions: true
+  # proxy
+  proxy: true
+  # toolset
+  toolset: true
+  # pulsar manager
+  pulsar_manager: true
+
+## Monitoring Components
+##
+## Control what components of the monitoring stack to deploy for the cluster
+monitoring:
+  # monitoring - prometheus
+  prometheus: true
+  # monitoring - grafana
+  grafana: true
+  # monitoring - node_exporter
+  node_exporter: true
+  # alerting - alert-manager
+  alert_manager: true
 
-## which extra components to deploy
+## which extra components to deploy (Deprecated)
 extra:
   # Pulsar proxy
-  proxy: yes
+  proxy: false
   # Bookkeeper auto-recovery
-  autoRecovery: yes
+  autoRecovery: false
   # Pulsar dashboard
   # Deprecated
   # Replace pulsar-dashboard with pulsar-manager
-  dashboard: no
+  dashboard: false
   # pulsar manager
-  pulsar_manager: yes
+  pulsar_manager: false
   # Bastion pod for administrative commands
-  bastion: yes
+  bastion: false
   # Monitoring stack (prometheus and grafana)
-  monitoring: yes
+  monitoring: false
   # Configure Kubernetes runtime for Functions
-  functionsAsPods: no
+  functionsAsPods: false
+
+## Images
+##
+## Control what images to use for each component
+images:
+  zookeeper:
+    repository: apachepulsar/pulsar-all
+    tag: 2.5.0
+    pullPolicy: IfNotPresent
+  bookie:
+    repository: apachepulsar/pulsar-all
+    tag: 2.5.0
+    pullPolicy: IfNotPresent
+  autorecovery:
+    repository: apachepulsar/pulsar-all
+    tag: 2.5.0
+    pullPolicy: IfNotPresent
+  broker:
+    repository: apachepulsar/pulsar-all
+    tag: 2.5.0
+    pullPolicy: IfNotPresent
+  proxy:
+    repository: apachepulsar/pulsar-all
+    tag: 2.5.0
+    pullPolicy: IfNotPresent
+  functions:
+    repository: apachepulsar/pulsar-all
+    tag: 2.5.0
+  prometheus:
+    repository: prom/prometheus
+    tag: v1.6.3
+    pullPolicy: IfNotPresent
+  grafana:
+    repository: streamnative/apache-pulsar-grafana-dashboard-k8s
+    tag: 0.0.4
+    pullPolicy: IfNotPresent
+  pulsar_manager:
+    repository: apachepulsar/pulsar-manager
+    tag: v0.1.0
+    pullPolicy: IfNotPresent
+    hasCommand: false
 
-## Which pulsar image to use
-image:
-  repository: apachepulsar/pulsar-all
-  tag: latest
-  pullPolicy: IfNotPresent
+## TLS
+## templates/tls-certs.yaml
+##
+## The chart is using cert-manager for provisioning TLS certs for
+## brokers and proxies.
+tls:
+  enabled: false
+  # common settings for generating certs
+  common:
+    # 90d
+    duration: 2160h
+    # 15d
+    renewBefore: 360h
+    organization:
+      - pulsar
+    keySize: 4096
+    keyAlgorithm: rsa
+    keyEncoding: pkcs8
+  # settings for generating certs for proxy
+  proxy:
+    enabled: false
+    cert_name: tls-proxy
+  # settings for generating certs for broker
+  broker:
+    enabled: false
+    cert_name: tls-broker
+  # settings for generating certs for bookies
+  bookie:
+    enabled: false
+    cert_name: tls-bookie
+  # settings for generating certs for zookeeper
+  zookeeper:
+    enabled: false
+    cert_name: tls-zookeeper
+  # settings for generating certs for recovery
+  autorecovery:
+    cert_name: tls-recovery
+  # settings for generating certs for toolset
+  toolset:
+    cert_name: tls-toolset
+
+# Enable or disable broker authentication and authorization.
+auth:
+  authentication:
+    enabled: false
+    provider: "jwt"
+    jwt:
+      # Enable JWT authentication
+      # If the token is generated by a secret key, set the usingSecretKey as true.
+      # If the token is generated by a private key, set the usingSecretKey as false.
+      usingSecretKey: false
+  authorization:
+    enabled: false
+  superUsers:
+    # broker to broker communication
+    broker: "broker-admin"
+    # proxy to broker communication
+    proxy: "proxy-admin"
+    # pulsar-admin client to broker/proxy communication
+    client: "admin"
+
+######################################################################
+# External dependencies
+######################################################################
+
+## cert-manager
+## templates/tls-cert-issuer.yaml
+##
+## Cert manager is used for automatically provisioning TLS certificates
+## for components within a Pulsar cluster
+certs:
+  internal_issuer:
+    enabled: false
+    component: internal-cert-issuer
+    type: selfsigning
+  issuers:
+    selfsigning:
+
+######################################################################
+# Below are settings for each component
+######################################################################
 
 ## Pulsar: Zookeeper cluster
 ## templates/zookeeper-statefulset.yaml
 ##
 zookeeper:
+  # use a component name that matches your grafana configuration
+  # so the metrics are correctly rendered in grafana dashboard
   component: zookeeper
+  # the number of zookeeper servers to run. it should be an odd number larger than or equal to 3.
   replicaCount: 3
   updateStrategy:
-    type: OnDelete
+    type: RollingUpdate
   podManagementPolicy: OrderedReady
+  ports:
+    client: 2181
+    clientTls: 2281
+    follower: 2888
+    leaderElection: 3888
   # nodeSelector:
     # cloud.google.com/gke-nodepool: default-pool
+  probe:
+    liveness:
+      enabled: true
+      failureThreshold: 10
+      initialDelaySeconds: 10
+      periodSeconds: 30
+    readiness:
+      enabled: true
+      failureThreshold: 10
+      initialDelaySeconds: 10
+      periodSeconds: 30
+    startup:
+      enabled: false
+      failureThreshold: 30
+      initialDelaySeconds: 10
+      periodSeconds: 30
+  affinity:
+    anti_affinity: true
   annotations:
     prometheus.io/scrape: "true"
     prometheus.io/port: "8000"
   tolerations: []
-  gracePeriod: 0
+  gracePeriod: 30
   resources:
     requests:
-      memory: 15Gi
-      cpu: 4
+      memory: 256Mi
+      cpu: 0.1
   volumes:
+    # use a persistent volume or emptyDir
+    persistence: true
     data:
       name: data
       size: 20Gi
+      local_storage: true
       ## If you already have an existent storage class and want to reuse it, you can specify its name with the option below
       ##
       # storageClassName: existent-storage-class
@@ -97,61 +311,98 @@ zookeeper:
   ## templates/zookeeper-configmap.yaml
   ##
   configData:
-    PULSAR_MEM: "\"-Xms15g -Xmx15g -Dcom.sun.management.jmxremote -Djute.maxbuffer=10485760 -XX:+ParallelRefProcEnabled -XX:+UnlockExperimentalVMOptions -XX:+DoEscapeAnalysis -XX:+DisableExplicitGC -XX:+PerfDisableSharedMem -Dzookeeper.forceSync=no\""
-    PULSAR_GC: "\"-XX:+UseG1GC -XX:MaxGCPauseMillis=10\""
+    PULSAR_MEM: >
+      "
+      -Xms64m -Xmx128m
+      -Dcom.sun.management.jmxremote
+      -Djute.maxbuffer=10485760
+      -XX:+ParallelRefProcEnabled
+      -XX:+UnlockExperimentalVMOptions
+      -XX:+DoEscapeAnalysis
+      -XX:+DisableExplicitGC
+      -XX:+PerfDisableSharedMem
+      -Dzookeeper.forceSync=no
+      "
+    PULSAR_GC: >
+      "
+      -XX:+UseG1GC
+      -XX:MaxGCPauseMillis=10
+      "
   ## Zookeeper service
   ## templates/zookeeper-service.yaml
   ##
   service:
     annotations:
       service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
-    ports:
-    - name: server
-      port: 2888
-    - name: leader-election
-      port: 3888
-    - name: stats
-      port: 2181
   ## Zookeeper PodDisruptionBudget
   ## templates/zookeeper-pdb.yaml
   ##
   pdb:
-    usePolicy: yes
+    usePolicy: true
     maxUnavailable: 1
 
-## Pulsar Zookeeper metadata. The metadata will be deployed as
-## soon as the last zookeeper node is reachable. The deployment
-## of other components that depends on zookeeper, such as the
-## bookkeeper nodes, broker nodes, etc will only start to be
-## deployed when the zookeeper cluster is ready and with the
-## metadata deployed
-zookeeperMetadata:
-  component: zookeeper-metadata
-
 ## Pulsar: Bookkeeper cluster
 ## templates/bookkeeper-statefulset.yaml
 ##
 bookkeeper:
-  component: bookkeeper
+  # use a component name that matches your grafana configuration
+  # so the metrics are correctly rendered in grafana dashboard
+  component: bookie
+  ## BookKeeper Cluster Initialize
+  ## templates/bookkeeper-cluster-initialize.yaml
+  metadata:
+    image:
+      # the image used for running `bookkeeper-cluster-initialize` job
+      repository: apachepulsar/pulsar-all
+      tag: 2.5.0
+      pullPolicy: IfNotPresent
+    ## Set the resources used for running `bin/bookkeeper shell initnewcluster`
+    ##
+    resources:
+      # requests:
+        # memory: 4Gi
+        # cpu: 2
   replicaCount: 4
   updateStrategy:
-    type: OnDelete
-  podManagementPolicy: OrderedReady
+    type: RollingUpdate
+  podManagementPolicy: Parallel
+  ports:
+    http: 8000
+    bookie: 3181
   # nodeSelector:
     # cloud.google.com/gke-nodepool: default-pool
-  annotations:
-    prometheus.io/scrape: "true"
-    prometheus.io/port: "8000"
+  probe:
+    liveness:
+      enabled: true
+      failureThreshold: 60
+      initialDelaySeconds: 10
+      periodSeconds: 30
+    readiness:
+      enabled: true
+      failureThreshold: 60
+      initialDelaySeconds: 10
+      periodSeconds: 30
+    startup:
+      enabled: false
+      failureThreshold: 30
+      initialDelaySeconds: 60
+      periodSeconds: 30
+  affinity:
+    anti_affinity: true
+  annotations: {}
   tolerations: []
-  gracePeriod: 0
+  gracePeriod: 30
   resources:
     requests:
-      memory: 15Gi
-      cpu: 4
+      memory: 512Mi
+      cpu: 0.2
   volumes:
+    # use a persistent volume or emptyDir
+    persistence: true
     journal:
       name: journal
-      size: 50Gi
+      size: 10Gi
+      local_storage: true
       ## If you already have an existent storage class and want to reuse it, you can specify its name with the option below
       ##
       # storageClassName: existent-storage-class
@@ -166,6 +417,7 @@ bookkeeper:
     ledgers:
       name: ledgers
       size: 50Gi
+      local_storage: true
       ## If you already have an existent storage class and want to reuse it, you can specify its name with the option below
       ##
       # storageClassName: existent-storage-class
@@ -181,145 +433,271 @@ bookkeeper:
   ## templates/bookkeeper-configmap.yaml
   ##
   configData:
-    BOOKIE_MEM: "\"-Xms15g -Xmx15g -XX:MaxDirectMemorySize=15g -Dio.netty.leakDetectionLevel=disabled -Dio.netty.recycler.linkCapacity=1024 -XX:+ParallelRefProcEnabled -XX:+UnlockExperimentalVMOptions -XX:+DoEscapeAnalysis -XX:ParallelGCThreads=32 -XX:ConcGCThreads=32 -XX:G1NewSizePercent=50 -XX:+DisableExplicitGC -XX:-ResizePLAB -XX:+ExitOnOutOfMemoryError -XX:+PerfDisableSharedMem -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintHeapAtGC -verboseg [...]
-    BOOKIE_GC: "\"-XX:+UseG1GC -XX:MaxGCPauseMillis=10\""
-    dbStorage_writeCacheMaxSizeMb: "2048"
-    dbStorage_readAheadCacheMaxSizeMb: "2048"
-    dbStorage_rocksDB_blockCacheSize: "268435456"
-    journalMaxSizeMB: "2048"
-    statsProviderClass: org.apache.bookkeeper.stats.prometheus.PrometheusMetricsProvider
-    useHostNameAsBookieID: "true"
-  ## Bookkeeper configmap
+    # `BOOKIE_MEM` is used for `bookie shell`
+    BOOKIE_MEM: >
+      "
+      -Xms128m
+      -Xmx256m
+      -XX:MaxDirectMemorySize=256m
+      "
+    # we use `bin/pulsar` for starting bookie daemons
+    PULSAR_MEM: >
+      "
+      -Xms128m
+      -Xmx256m
+      -XX:MaxDirectMemorySize=256m
+      "
+    PULSAR_GC: >
+      "
+      -XX:+UseG1GC
+      -XX:MaxGCPauseMillis=10
+      -XX:+ParallelRefProcEnabled
+      -XX:+UnlockExperimentalVMOptions
+      -XX:+DoEscapeAnalysis
+      -XX:ParallelGCThreads=4
+      -XX:ConcGCThreads=4
+      -XX:G1NewSizePercent=50
+      -XX:+DisableExplicitGC
+      -XX:-ResizePLAB
+      -XX:+ExitOnOutOfMemoryError
+      -XX:+PerfDisableSharedMem
+      -XX:+PrintGCDetails
+      -XX:+PrintGCTimeStamps
+      -XX:+PrintGCApplicationStoppedTime
+      -XX:+PrintHeapAtGC
+      -verbosegc
+      -Xloggc:/var/log/bookie-gc.log
+      -XX:G1LogLevel=finest
+      "
+    # configure the memory settings based on jvm memory settings
+    dbStorage_writeCacheMaxSizeMb: "32"
+    dbStorage_readAheadCacheMaxSizeMb: "32"
+    dbStorage_rocksDB_writeBufferSizeMB: "8"
+    dbStorage_rocksDB_blockCacheSize: "8388608"
+  ## Bookkeeper Service
   ## templates/bookkeeper-service.yaml
   ##
   service:
     annotations:
       publishNotReadyAddresses: "true"
-    ports:
-    - name: server
-      port: 3181
   ## Bookkeeper PodDisruptionBudget
   ## templates/bookkeeper-pdb.yaml
   ##
   pdb:
-    usePolicy: yes
+    usePolicy: true
     maxUnavailable: 1
 
+## Pulsar: Bookkeeper AutoRecovery
+## templates/autorecovery-statefulset.yaml
+##
+autorecovery:
+  # use a component name that matches your grafana configuration
+  # so the metrics are correctly rendered in grafana dashboard
+  component: recovery
+  replicaCount: 1
+  ports:
+    http: 8000
+  # nodeSelector:
+    # cloud.google.com/gke-nodepool: default-pool
+  affinity:
+    anti_affinity: true
+  annotations: {}
+  # tolerations: []
+  gracePeriod: 30
+  resources:
+    requests:
+      memory: 64Mi
+      cpu: 0.05
+  ## Bookkeeper auto-recovery configmap
+  ## templates/autorecovery-configmap.yaml
+  ##
+  configData:
+    BOOKIE_MEM: >
+      "
+      -Xms64m -Xmx64m
+      "
+
+## Pulsar Zookeeper metadata. The metadata will be deployed as
+## soon as the last zookeeper node is reachable. The deployment
+## of other components that depends on zookeeper, such as the
+## bookkeeper nodes, broker nodes, etc will only start to be
+## deployed when the zookeeper cluster is ready and with the
+## metadata deployed
+pulsar_metadata:
+  component: pulsar-init
+  image:
+    # the image used for running `pulsar-cluster-initialize` job
+    repository: apachepulsar/pulsar-all
+    tag: 2.5.0
+    pullPolicy: IfNotPresent
+  ## set an existing configuration store
+  # configurationStore:
+  configurationStoreMetadataPrefix: ""
+
 ## Pulsar: Broker cluster
-## templates/broker-deployment.yaml
+## templates/broker-statefulset.yaml
 ##
 broker:
+  # use a component name that matches your grafana configuration
+  # so the metrics are correctly rendered in grafana dashboard
   component: broker
   replicaCount: 3
+  ports:
+    http: 8080
+    https: 8443
+    pulsar: 6650
+    pulsarssl: 6651
   # nodeSelector:
     # cloud.google.com/gke-nodepool: default-pool
-  annotations:
-    prometheus.io/scrape: "true"
-    prometheus.io/port: "8080"
+  probe:
+    liveness:
+      enabled: true
+      failureThreshold: 10
+      initialDelaySeconds: 30
+      periodSeconds: 10
+    readiness:
+      enabled: true
+      failureThreshold: 10
+      initialDelaySeconds: 30
+      periodSeconds: 10
+    startup:
+      enabled: false
+      failureThreshold: 30
+      initialDelaySeconds: 60
+      periodSeconds: 10
+  affinity:
+    anti_affinity: true
+  annotations: {}
   tolerations: []
-  gracePeriod: 0
+  gracePeriod: 30
   resources:
     requests:
-      memory: 15Gi
-      cpu: 4
+      memory: 512Mi
+      cpu: 0.2
   ## Broker configmap
   ## templates/broker-configmap.yaml
   ##
   configData:
-    PULSAR_MEM: "\"-Xms15g -Xmx15g -XX:MaxDirectMemorySize=15g -Dio.netty.leakDetectionLevel=disabled -Dio.netty.recycler.linkCapacity=1024 -XX:+ParallelRefProcEnabled -XX:+UnlockExperimentalVMOptions -XX:+DoEscapeAnalysis -XX:ParallelGCThreads=32 -XX:ConcGCThreads=32 -XX:G1NewSizePercent=50 -XX:+DisableExplicitGC -XX:-ResizePLAB -XX:+ExitOnOutOfMemoryError -XX:+PerfDisableSharedMem\""
-    PULSAR_GC: "\"-XX:+UseG1GC -XX:MaxGCPauseMillis=10\""
+    PULSAR_MEM: >
+      "
+      -Xms128m -Xmx256m -XX:MaxDirectMemorySize=256m
+      -Dio.netty.leakDetectionLevel=disabled
+      -Dio.netty.recycler.linkCapacity=1024
+      -XX:+ParallelRefProcEnabled
+      -XX:+UnlockExperimentalVMOptions
+      -XX:+DoEscapeAnalysis
+      -XX:ParallelGCThreads=4
+      -XX:ConcGCThreads=4
+      -XX:G1NewSizePercent=50
+      -XX:+DisableExplicitGC
+      -XX:-ResizePLAB
+      -XX:+ExitOnOutOfMemoryError
+      -XX:+PerfDisableSharedMem
+      "
+    PULSAR_GC: >
+      "
+      -XX:+UseG1GC
+      -XX:MaxGCPauseMillis=10
+      "
     managedLedgerDefaultEnsembleSize: "3"
     managedLedgerDefaultWriteQuorum: "3"
     managedLedgerDefaultAckQuorum: "2"
-    deduplicationEnabled: "false"
-    exposeTopicLevelMetricsInPrometheus: "true"
   ## Broker service
   ## templates/broker-service.yaml
   ##
   service:
     annotations: {}
-    ports:
-    - name: http
-      port: 8080
-    - name: pulsar
-      port: 6650
   ## Broker PodDisruptionBudget
   ## templates/broker-pdb.yaml
   ##
   pdb:
-    usePolicy: yes
+    usePolicy: true
     maxUnavailable: 1
-  ## Broker rbac
-  ## templates/broker-rbac.yaml
-  ##
-  functions:
-    component: functions-worker
 
-## Pulsar Extra: Proxy
-## templates/proxy-deployment.yaml
+## Pulsar: Functions Worker
+## templates/function-worker-configmap.yaml
+##
+functions:
+  component: functions-worker
+
+## Pulsar: Proxy Cluster
+## templates/proxy-statefulset.yaml
 ##
 proxy:
+  # use a component name that matches your grafana configuration
+  # so the metrics are correctly rendered in grafana dashboard
   component: proxy
   replicaCount: 3
   # nodeSelector:
     # cloud.google.com/gke-nodepool: default-pool
-  annotations:
-    prometheus.io/scrape: "true"
-    prometheus.io/port: "8080"
+  probe:
+    liveness:
+      enabled: true
+      failureThreshold: 10
+      initialDelaySeconds: 30
+      periodSeconds: 10
+    readiness:
+      enabled: true
+      failureThreshold: 10
+      initialDelaySeconds: 30
+      periodSeconds: 10
+    startup:
+      enabled: false
+      failureThreshold: 30
+      initialDelaySeconds: 60
+      periodSeconds: 10
+  affinity:
+    anti_affinity: true
+  annotations: {}
   tolerations: []
-  gracePeriod: 0
+  gracePeriod: 30
   resources:
     requests:
-      memory: 4Gi
-      cpu: 1
+      memory: 128Mi
+      cpu: 0.2
   ## Proxy configmap
   ## templates/proxy-configmap.yaml
   ##
   configData:
-    PULSAR_MEM: "\"-Xms4g -Xmx4g -XX:MaxDirectMemorySize=4g\""
+    PULSAR_MEM: >
+      "
+      -Xms64m -Xmx64m -XX:MaxDirectMemorySize=64m
+      -Dio.netty.leakDetectionLevel=disabled
+      -Dio.netty.recycler.linkCapacity=1024
+      -XX:+ParallelRefProcEnabled
+      -XX:+UnlockExperimentalVMOptions
+      -XX:+DoEscapeAnalysis
+      -XX:ParallelGCThreads=4
+      -XX:ConcGCThreads=4
+      -XX:G1NewSizePercent=50
+      -XX:+DisableExplicitGC
+      -XX:-ResizePLAB
+      -XX:+ExitOnOutOfMemoryError
+      -XX:+PerfDisableSharedMem
+      "
+    PULSAR_GC: >
+      "
+      -XX:+UseG1GC
+      -XX:MaxGCPauseMillis=10
+      "
   ## Proxy service
   ## templates/proxy-service.yaml
   ##
+  ports:
+    http: 80
+    https: 443
+    pulsar: 6650
+    pulsarssl: 6651
   service:
     annotations: {}
-    type: NodePort
-    ports:
-    - name: http
-      port: 8080
-      nodePort: 30001
-      protocol: TCP
-    - name: tcp
-      port: 6650
-      nodePort: 30002
-      protocol: TCP
+    type: LoadBalancer
   ## Proxy PodDisruptionBudget
   ## templates/proxy-pdb.yaml
   ##
   pdb:
-    usePolicy: yes
+    usePolicy: true
     maxUnavailable: 1
 
-## Pulsar Extra: Bookkeeper auto-recovery
-## templates/autorecovery-deployment.yaml
-##
-autoRecovery:
-  component: autorecovery
-  replicaCount: 1
-  # nodeSelector:
-    # cloud.google.com/gke-nodepool: default-pool
-  annotations: {}
-  tolerations: []
-  gracePeriod: 0
-  resources:
-    requests:
-      memory: 1Gi
-      cpu: 250m
-  ## Bookkeeper auto-recovery configmap
-  ## templates/autorecovery-configmap.yaml
-  ##
-  configData:
-    BOOKIE_MEM: "\" -Xms1g -Xmx1g \""
-
 ## Pulsar Extra: Dashboard
 ## templates/dashboard-deployment.yaml
 ## Deprecated
@@ -363,50 +741,65 @@ dashboard:
     port: 80
 
 
-## Pulsar Extra: Bastion
-## templates/bastion-deployment.yaml
+## Pulsar ToolSet
+## templates/toolset-deployment.yaml
 ##
-bastion:
-  component: bastion
+toolset:
+  component: toolset
+  useProxy: true
   replicaCount: 1
   # nodeSelector:
     # cloud.google.com/gke-nodepool: default-pool
   annotations: {}
   tolerations: []
-  gracePeriod: 0
+  gracePeriod: 30
   resources:
     requests:
-      memory: 1Gi
-      cpu: 250m
+      memory: 256Mi
+      cpu: 0.1
   ## Bastion configmap
   ## templates/bastion-configmap.yaml
   ##
   configData:
-    PULSAR_MEM: "\"-Xms1g -Xmx1g -XX:MaxDirectMemorySize=1g\""
+    PULSAR_MEM: >
+      "
+      -Xms64M
+      -Xmx128M
+      -XX:MaxDirectMemorySize=128M
+      "
+
+#############################################################
+### Monitoring Stack : Prometheus / Grafana
+#############################################################
 
 ## Monitoring Stack: Prometheus
 ## templates/prometheus-deployment.yaml
 ##
+
+## Deprecated in favor of using `prometheus.rbac.enabled`
+prometheus_rbac: false
 prometheus:
   component: prometheus
+  rbac:
+    enabled: true
   replicaCount: 1
   # nodeSelector:
     # cloud.google.com/gke-nodepool: default-pool
   annotations: {}
   tolerations: []
-  gracePeriod: 0
-  image:
-    repository: prom/prometheus
-    tag: v1.6.3
-    pullPolicy: IfNotPresent
+  gracePeriod: 5
+  port: 9090
   resources:
     requests:
-      memory: 4Gi
-      cpu: 1
+      memory: 256Mi
+      cpu: 0.1
   volumes:
+    # use a persistent volume or emptyDir
+    persistence: true
     data:
       name: data
-      size: 50Gi
+      size: 10Gi
+      local_storage: true
       ## If you already have an existent storage class and want to reuse it, you can specify its name with the option below
       ##
       # storageClassName: existent-storage-class
@@ -423,9 +816,6 @@ prometheus:
   ##
   service:
     annotations: {}
-    ports:
-    - name: server
-      port: 9090
 
 ## Monitoring Stack: Grafana
 ## templates/grafana-deployment.yaml
@@ -437,23 +827,18 @@ grafana:
     # cloud.google.com/gke-nodepool: default-pool
   annotations: {}
   tolerations: []
-  gracePeriod: 0
-  image:
-    repository: apachepulsar/pulsar-grafana
-    tag: latest
-    pullPolicy: IfNotPresent
+  gracePeriod: 30
+  port: 3000
   resources:
     requests:
-      memory: 4Gi
-      cpu: 1
+      memory: 250Mi
+      cpu: 0.1
   ## Grafana service
   ## templates/grafana-service.yaml
   ##
   service:
+    type: LoadBalancer
     annotations: {}
-    ports:
-    - name: server
-      port: 3000
   plugins: []
   ## Grafana ingress
   ## templates/grafana-ingress.yaml
@@ -470,7 +855,7 @@ grafana:
     tls: []
 
     ## Optional. Leave it blank if your Ingress Controller can provide a default certificate.
-    #- secretName: ""
+    ## - secretName: ""
 
     ## Extra paths to prepend to every host configuration. This is useful when working with annotation based services.
     extraPaths: []
@@ -483,19 +868,15 @@ grafana:
 ## Components Stack: pulsar_manager
 ## templates/pulsar-manager.yaml
 ##
-
 pulsar_manager:
   component: pulsar-manager
+  port: 9527
   replicaCount: 1
   # nodeSelector:
   # cloud.google.com/gke-nodepool: default-pool
   annotations: {}
   tolerations: []
-  gracePeriod: 0
-  image:
-    repository: apachepulsar/pulsar-manager
-    tag: v0.1.0
-    pullPolicy: IfNotPresent
+  gracePeriod: 30
   resources:
     requests:
       memory: 250Mi
@@ -507,17 +888,14 @@ pulsar_manager:
     URL: jdbc:postgresql://127.0.0.1:5432/pulsar_manager
     LOG_LEVEL: DEBUG
     ## If you enabled authentication support
-    #JWT_TOKEN: <token>
-    #SECRET_KEY: data:base64,<secret key>
+    ## JWT_TOKEN: <token>
+    ## SECRET_KEY: data:base64,<secret key>
   ## Pulsar manager service
   ## templates/pulsar-manager-service.yaml
   ##
   service:
     type: LoadBalancer
     annotations: {}
-    ports:
-      - name: server
-        port: 9527
   admin:
     user: pulsar
     password: pulsar
diff --git a/scripts/cert-manager/install-cert-manager.sh b/scripts/cert-manager/install-cert-manager.sh
new file mode 100755
index 0000000..5670b92
--- /dev/null
+++ b/scripts/cert-manager/install-cert-manager.sh
@@ -0,0 +1,55 @@
+#
+# 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.
+#
+
+#!/usr/bin/env bash
+
+NAMESPACE=cert-manager
+NAME=cert-manager
+VERSION=v0.13.0
+
+# Install cert-manager CustomResourceDefinition resources
+echo "Installing cert-manager CRD resources ..."
+kubectl apply --validate=false -f https://raw.githubusercontent.com/jetstack/cert-manager/${VERSION}/deploy/manifests/00-crds.yaml
+
+# Create the namespace 
+kubectl get ns ${NAMESPACE}
+if [ $? == 0 ]; then
+    echo "Namespace '${NAMESPACE}' already exists."
+else
+    echo "Creating namespace '${NAMESPACE}' ..."
+    kubectl create namespace ${NAMESPACE}
+    echo "Successfully created namespace '${NAMESPACE}'."
+fi
+
+# Add the Jetstack Helm repository.
+echo "Adding Jetstack Helm repository."
+helm repo add jetstack https://charts.jetstack.io
+echo "Successfully added Jetstack Helm repository."
+
+# Update local helm chart repository cache.
+echo "Updating local helm chart repository cache ..."
+helm repo update
+
+echo "Installing cert-manager ${VERSION} to namespace ${NAMESPACE} as '${NAME}' ..."
+helm install \
+  --namespace ${NAMESPACE} \
+  --version ${VERSION} \
+  ${NAME} \
+  jetstack/cert-manager
+echo "Successfully installed cert-manager ${VERSION}."
\ No newline at end of file
diff --git a/scripts/pulsar/clean_tls.sh b/scripts/pulsar/clean_tls.sh
new file mode 100755
index 0000000..8dba53d
--- /dev/null
+++ b/scripts/pulsar/clean_tls.sh
@@ -0,0 +1,115 @@
+#!/usr/bin/env bash
+#
+# 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.
+#
+
+set -e
+
+CHART_HOME=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/../.. && pwd)
+cd ${CHART_HOME}
+
+namespace=${namespace:-pulsar}
+release=${release:-pulsar-dev}
+clientComponents=${clientComponents:-"toolset"}
+serverComponents=${serverComponents:-"bookie,broker,proxy,recovery,zookeeper"}
+
+usage() {
+    cat <<EOF
+This script is used to delete tls certs for a given pulsar helm deployment generated by "upload_tls.sh".
+Options:
+       -h,--help                        prints the usage message
+       -n,--namespace                   the k8s namespace to install the pulsar helm chart. Defaut to ${namespace}.
+       -k,--release                     the pulsar helm release name. Default to ${release}.
+       -c,--client-components           the client components of pulsar cluster. a comma separated list of components. Default to ${clientComponents}.
+       -s,--server-components           the server components of pulsar cluster. a comma separated list of components. Default to ${serverComponents}.
+Usage:
+    $0 --namespace pulsar --release pulsar-dev
+EOF
+}
+
+while [[ $# -gt 0 ]]
+do
+key="$1"
+
+case $key in
+    -n|--namespace)
+    namespace="$2"
+    shift
+    shift
+    ;;
+    -k|--release)
+    release="$2"
+    shift
+    shift
+    ;;
+    -c|--client-components)
+    clientComponents="$2"
+    shift
+    shift
+    ;;
+    -s|--server-components)
+    serverComponents="$2"
+    shift
+    shift
+    ;;
+    -h|--help)
+    usage
+    exit 0
+    ;;
+    *)
+    echo "unknown option: $key"
+    usage
+    exit 1
+    ;;
+esac
+done
+
+function delete_ca() {
+    local tls_ca_secret="${release}-ca-tls"
+    kubectl delete secret ${tls_ca_secret} -n ${namespace}
+}
+
+function delete_server_cert() {
+    local component=$1
+    local server_cert_secret="${release}-tls-${component}"
+
+    kubectl delete secret ${server_cert_secret} \
+        -n ${namespace}
+}
+
+function delete_client_cert() {
+    local component=$1
+    local client_cert_secret="${release}-tls-${component}"
+
+    kubectl delete secret ${client_cert_secret} \
+        -n ${namespace}
+}
+
+delete_ca
+
+IFS=', ' read -r -a server_components <<< "$serverComponents"
+for component in "${server_components[@]}"
+do
+    delete_server_cert ${component}
+done
+
+IFS=', ' read -r -a client_components <<< "$clientComponents"
+for component in "${client_components[@]}"
+do
+    delete_client_cert ${component}
+done
diff --git a/scripts/pulsar/cleanup_helm_release.sh b/scripts/pulsar/cleanup_helm_release.sh
new file mode 100755
index 0000000..73c0e01
--- /dev/null
+++ b/scripts/pulsar/cleanup_helm_release.sh
@@ -0,0 +1,87 @@
+#!/usr/bin/env bash
+#
+# 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.
+#
+
+CHART_HOME=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/../.. && pwd)
+cd ${CHART_HOME}
+
+usage() {
+    cat <<EOF
+This script is used to cleanup the credentials for a given pulsar helm release. 
+Options:
+       -h,--help                        prints the usage message
+       -n,--namespace                   the k8s namespace to install the pulsar helm chart
+       -k,--release                     the pulsar helm release name
+       -d,--delete-namespace            flag to delete k8s namespace.
+Usage:
+    $0 --namespace pulsar --release pulsar-release
+EOF
+}
+
+
+while [[ $# -gt 0 ]]
+do
+key="$1"
+
+delete_namespace=false
+
+case $key in
+    -n|--namespace)
+    namespace="$2"
+    shift
+    shift
+    ;;
+    -d|--delete-namespace)
+    delete_namespace=true
+    shift
+    ;;
+    -k|--release)
+    release="$2"
+    shift
+    shift
+    ;;
+    -h|--help)
+    usage
+    exit 0
+    ;;
+    *)
+    echo "unknown option: $key"
+    usage
+    exit 1
+    ;;
+esac
+done
+
+namespace=${namespace:-pulsar}
+release=${release:-pulsar-dev}
+
+function delete_namespace() {
+    if [[ "${delete_namespace}" == "true" ]]; then
+        kubectl create namespace ${namespace}
+    fi
+}
+
+# delete the cc admin secrets
+kubectl delete -n ${namespace} secret ${release}-admin-secret
+
+# delete tokens
+kubectl get secrets -n ${namespace} | grep ${release}-token- | awk '{print $1}' | xargs kubectl delete secrets -n ${namespace}
+
+# delete namespace
+delete_namespace
diff --git a/scripts/pulsar/common.sh b/scripts/pulsar/common.sh
new file mode 100755
index 0000000..099ec28
--- /dev/null
+++ b/scripts/pulsar/common.sh
@@ -0,0 +1,73 @@
+#!/bin/bash
+#
+# 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.
+#
+
+
+# Checks that appropriate gke params are set and
+# that gcloud and kubectl are properly installed and authenticated
+
+function need_tool(){
+  local tool="${1}"
+  local url="${2}"
+
+  echo >&2 "${tool} is required. Please follow ${url}"
+  exit 1
+}
+
+function need_gcloud(){
+  need_tool "gcloud" "https://cloud.google.com/sdk/downloads"
+}
+
+function need_kubectl(){
+  need_tool "kubectl" "https://kubernetes.io/docs/tasks/tools/install-kubectl"
+}
+
+function need_helm(){
+  need_tool "helm" "https://github.com/helm/helm/#install"
+}
+
+function need_eksctl(){
+  need_tool "eksctl" "https://eksctl.io"
+}
+
+function validate_gke_required_tools(){
+  if [ -z "$PROJECT" ]; then
+    echo "\$PROJECT needs to be set to your project id";
+    exit 1;
+  fi
+
+  for comm in gcloud kubectl helm
+  do
+    command  -v "${comm}" > /dev/null 2>&1 || "need_${comm}"
+  done
+
+  gcloud container clusters list --project $PROJECT >/dev/null 2>&1 || { echo >&2 "Gcloud seems to be configured incorrectly or authentication is unsuccessfull"; exit 1; }
+
+}
+
+function cluster_admin_password_gke(){
+  gcloud container clusters describe $CLUSTER_NAME --zone $ZONE --project $PROJECT --format='value(masterAuth.password)';
+}
+
+function validate_eks_required_tools(){
+  for comm in eksctl kubectl helm
+  do
+    command -v "${comm}" > /dev/null 2>&1 || "need_${comm}"
+  done
+}
diff --git a/scripts/pulsar/common_auth.sh b/scripts/pulsar/common_auth.sh
new file mode 100755
index 0000000..b9d3aba
--- /dev/null
+++ b/scripts/pulsar/common_auth.sh
@@ -0,0 +1,66 @@
+#!/usr/bin/env bash
+#
+# 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 [ -z "$CHART_HOME" ]; then
+    echo "error: CHART_HOME should be initialized"
+    exit 1
+fi
+
+OUTPUT=${CHART_HOME}/output
+OUTPUT_BIN=${OUTPUT}/bin
+PULSARCTL_VERSION=v0.4.0
+PULSARCTL_BIN=${HOME}/.pulsarctl/pulsarctl
+export PATH=${HOME}/.pulsarctl/plugins:${PATH}
+
+discoverArch() {
+  ARCH=$(uname -m)
+  case $ARCH in
+    x86) ARCH="386";;
+    x86_64) ARCH="amd64";;
+    i686) ARCH="386";;
+    i386) ARCH="386";;
+  esac
+}
+
+discoverArch
+OS=$(echo `uname`|tr '[:upper:]' '[:lower:]')
+
+test -d "$OUTPUT_BIN" || mkdir -p "$OUTPUT_BIN"
+
+function pulsar::verify_pulsarctl() {
+    if test -x "$PULSARCTL_BIN"; then
+        return
+    fi
+    return 1
+}
+
+function pulsar::ensure_pulsarctl() {
+    if pulsar::verify_pulsarctl; then
+        return 0
+    fi
+    echo "Get pulsarctl install.sh script ..."
+    install_script=$(mktemp)
+    trap "test -f $install_script && rm $install_script" RETURN
+    curl --retry 10 -L -o $install_script https://raw.githubusercontent.com/streamnative/pulsarctl/master/install.sh
+    chmod +x $install_script
+    $install_script --user --version ${PULSARCTL_VERSION}
+}
+
+
diff --git a/scripts/pulsar/generate_token.sh b/scripts/pulsar/generate_token.sh
new file mode 100755
index 0000000..b152bf1
--- /dev/null
+++ b/scripts/pulsar/generate_token.sh
@@ -0,0 +1,121 @@
+#!/usr/bin/env bash
+#
+# 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.
+#
+
+set -e
+
+CHART_HOME=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/../.. && pwd)
+cd ${CHART_HOME}
+
+usage() {
+    cat <<EOF
+This script is used to generate token for a given pulsar role.
+Options:
+       -h,--help                        prints the usage message
+       -n,--namespace                   the k8s namespace to install the pulsar helm chart
+       -k,--release                     the pulsar helm release name
+       -r,--role                        the pulsar role
+       -s,--symmetric                   use symmetric secret key for generating the token. If not provided, the private key of an asymmetric pair of keys is used.
+Usage:
+    $0 --namespace pulsar --release pulsar-dev -c <pulsar-role>
+EOF
+}
+
+symmetric=false
+
+while [[ $# -gt 0 ]]
+do
+key="$1"
+
+case $key in
+    -n|--namespace)
+    namespace="$2"
+    shift
+    shift
+    ;;
+    -k|--release)
+    release="$2"
+    shift
+    shift
+    ;;
+    -r|--role)
+    role="$2"
+    shift
+    shift
+    ;;
+    -s|--symmetric)
+    symmetric=true
+    shift
+    ;;
+    -h|--help)
+    usage
+    exit 0
+    ;;
+    *)
+    echo "unknown option: $key"
+    usage
+    exit 1
+    ;;
+esac
+done
+
+if [[ "x${role}" == "x" ]]; then
+    echo "No pulsar role is provided!"
+    usage
+    exit 1
+fi
+
+source ${CHART_HOME}/scripts/pulsar/common_auth.sh
+
+pulsar::ensure_pulsarctl
+
+namespace=${namespace:-pulsar}
+release=${release:-pulsar-dev}
+
+function pulsar::jwt::generate_symmetric_token() {
+    local token_name="${release}-token-${role}"
+    local secret_name="${release}-token-symmetric-key"
+
+    tmpfile=$(mktemp)
+    trap "test -f $tmpfile && rm $tmpfile" RETURN
+    tokentmpfile=$(mktemp)
+    trap "test -f $tokentmpfile && rm $tokentmpfile" RETURN
+    kubectl get -n ${namespace} secrets ${secret_name} -o jsonpath="{.data['SECRETKEY']}" | base64 --decode > ${tmpfile}
+    ${PULSARCTL_BIN} token create -a HS256 --secret-key-file ${tmpfile} --subject ${role} 2&> ${tokentmpfile}
+    kubectl create secret generic ${token_name} -n ${namespace} --from-file="TOKEN=${tokentmpfile}" --from-literal="TYPE=symmetric"
+}
+
+function pulsar::jwt::generate_asymmetric_token() {
+    local token_name="${release}-token-${role}"
+    local secret_name="${release}-token-asymmetric-key"
+
+    privatekeytmpfile=$(mktemp)
+    trap "test -f $privatekeytmpfile && rm $privatekeytmpfile" RETURN
+    tokentmpfile=$(mktemp)
+    trap "test -f $tokentmpfile && rm $tokentmpfile" RETURN
+    kubectl get -n ${namespace} secrets ${secret_name} -o jsonpath="{.data['PRIVATEKEY']}" | base64 --decode > ${privatekeytmpfile}
+    ${PULSARCTL_BIN} token create -a RS256 --private-key-file ${privatekeytmpfile} --subject ${role} 2&> ${tokentmpfile}
+    kubectl create secret generic ${token_name} -n ${namespace} --from-file="TOKEN=${tokentmpfile}" --from-literal="TYPE=asymmetric"
+}
+
+if [[ "${symmetric}" == "true" ]]; then
+    pulsar::jwt::generate_symmetric_token
+else
+    pulsar::jwt::generate_asymmetric_token
+fi
diff --git a/scripts/pulsar/generate_token_secret_key.sh b/scripts/pulsar/generate_token_secret_key.sh
new file mode 100755
index 0000000..be2f76e
--- /dev/null
+++ b/scripts/pulsar/generate_token_secret_key.sh
@@ -0,0 +1,109 @@
+#!/usr/bin/env bash
+#
+# 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.
+#
+
+set -e
+
+CHART_HOME=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/../.. && pwd)
+cd ${CHART_HOME}
+
+usage() {
+    cat <<EOF
+This script is used to generate token secret key for a given pulsar helm release.
+Options:
+       -h,--help                        prints the usage message
+       -n,--namespace                   the k8s namespace to install the pulsar helm chart
+       -k,--release                     the pulsar helm release name
+       -s,--symmetric                   generate symmetric secret key. If not provided, an asymmetric pair of keys are generated.
+Usage:
+    $0 --namespace pulsar --release pulsar-dev
+EOF
+}
+
+symmetric=false
+
+while [[ $# -gt 0 ]]
+do
+key="$1"
+
+case $key in
+    -n|--namespace)
+    namespace="$2"
+    shift
+    shift
+    ;;
+    -k|--release)
+    release="$2"
+    shift
+    shift
+    ;;
+    -s|--symmetric)
+    symmetric=true
+    shift
+    ;;
+    -h|--help)
+    usage
+    exit 0
+    ;;
+    *)
+    echo "unknown option: $key"
+    usage
+    exit 1
+    ;;
+esac
+done
+
+source ${CHART_HOME}/scripts/pulsar/common_auth.sh
+
+pulsar::ensure_pulsarctl
+
+namespace=${namespace:-pulsar}
+release=${release:-pulsar-dev}
+
+function pulsar::jwt::generate_symmetric_key() {
+    local secret_name="${release}-token-symmetric-key"
+
+    tmpfile=$(mktemp)
+    trap "test -f $tmpfile && rm $tmpfile" RETURN
+    ${PULSARCTL_BIN} token create-secret-key --output-file ${tmpfile}
+    mv $tmpfile SECRETKEY
+    kubectl create secret generic ${secret_name} -n ${namespace} --from-file=SECRETKEY
+    rm SECRETKEY
+}
+
+function pulsar::jwt::generate_asymmetric_key() {
+    local secret_name="${release}-token-asymmetric-key"
+
+    privatekeytmpfile=$(mktemp)
+    trap "test -f $privatekeytmpfile && rm $privatekeytmpfile" RETURN
+    publickeytmpfile=$(mktemp)
+    trap "test -f $publickeytmpfile && rm $publickeytmpfile" RETURN
+    ${PULSARCTL_BIN} token create-key-pair -a RS256 --output-private-key ${privatekeytmpfile} --output-public-key ${publickeytmpfile}
+    mv $privatekeytmpfile PRIVATEKEY
+    mv $publickeytmpfile PUBLICKEY
+    kubectl create secret generic ${secret_name} -n ${namespace} --from-file=PRIVATEKEY --from-file=PUBLICKEY
+    rm PRIVATEKEY
+    rm PUBLICKEY
+}
+
+if [[ "${symmetric}" == "true" ]]; then
+    pulsar::jwt::generate_symmetric_key
+else
+    pulsar::jwt::generate_asymmetric_key
+fi
diff --git a/scripts/pulsar/get_token.sh b/scripts/pulsar/get_token.sh
new file mode 100755
index 0000000..4f44365
--- /dev/null
+++ b/scripts/pulsar/get_token.sh
@@ -0,0 +1,95 @@
+#!/usr/bin/env bash
+#
+# 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.
+#
+
+set -e
+
+CHART_HOME=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/../.. && pwd)
+cd ${CHART_HOME}
+
+usage() {
+    cat <<EOF
+This script is used to retrieve token for a given pulsar role.
+Options:
+       -h,--help                        prints the usage message
+       -n,--namespace                   the k8s namespace to install the pulsar helm chart
+       -k,--release                     the pulsar helm release name
+       -r,--role                        the pulsar role
+Usage:
+    $0 --namespace pulsar --release pulsar-dev -r <pulsar-role>
+EOF
+}
+
+while [[ $# -gt 0 ]]
+do
+key="$1"
+
+case $key in
+    -n|--namespace)
+    namespace="$2"
+    shift
+    shift
+    ;;
+    -k|--release)
+    release="$2"
+    shift
+    shift
+    ;;
+    -r|--role)
+    role="$2"
+    shift
+    shift
+    ;;
+    -h|--help)
+    usage
+    exit 0
+    ;;
+    *)
+    echo "unknown option: $key"
+    usage
+    exit 1
+    ;;
+esac
+done
+
+if [[ "x${role}" == "x" ]]; then
+    echo "No pulsar role is provided!"
+    usage
+    exit 1
+fi
+
+source ${CHART_HOME}/scripts/pulsar/common_auth.sh
+
+pulsar::ensure_pulsarctl
+
+namespace=${namespace:-pulsar}
+release=${release:-pulsar-dev}
+
+function pulsar::jwt::get_token() {
+    local token_name="${release}-token-${role}"
+
+    local token=$(kubectl get -n ${namespace} secrets ${token_name} -o jsonpath="{.data['TOKEN']}" | base64 --decode)
+    local token_type=$(kubectl get -n ${namespace} secrets ${token_name} -o jsonpath="{.data['TYPE']}" | base64 --decode)
+
+    echo "token type: ${token_type}"
+    echo "-------------------------"
+    echo "${token}"
+}
+
+pulsar::jwt::get_token
diff --git a/scripts/pulsar/prepare_helm_release.sh b/scripts/pulsar/prepare_helm_release.sh
new file mode 100755
index 0000000..a174bc8
--- /dev/null
+++ b/scripts/pulsar/prepare_helm_release.sh
@@ -0,0 +1,155 @@
+#!/usr/bin/env bash
+#
+# 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.
+#
+
+CHART_HOME=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/../.. && pwd)
+cd ${CHART_HOME}
+
+usage() {
+    cat <<EOF
+This script is used to bootstrap the pulsar namespace before deploying a helm chart. 
+Options:
+       -h,--help                        prints the usage message
+       -n,--namespace                   the k8s namespace to install the pulsar helm chart
+       -k,--release                     the pulsar helm release name
+       -s,--symmetric                   generate symmetric secret key. If not provided, an asymmetric pair of keys are generated.
+       --control-center-admin           the user name of control center administrator
+       --control-center-password        the password of control center administrator
+       --pulsar-superusers              the superusers of pulsar cluster. a comma separated list of super users.
+       -c,--create-namespace            flag to create k8s namespace.
+Usage:
+    $0 --namespace pulsar --release pulsar-release
+EOF
+}
+
+
+while [[ $# -gt 0 ]]
+do
+key="$1"
+
+symmetric=false
+create_namespace=false
+
+case $key in
+    -n|--namespace)
+    namespace="$2"
+    shift
+    shift
+    ;;
+    -c|--create-namespace)
+    create_namespace=true
+    shift
+    ;;
+    -k|--release)
+    release="$2"
+    shift
+    shift
+    ;;
+    --control-center-admin)
+    cc_admin="$2"
+    shift
+    shift
+    ;;
+    --control-center-password)
+    cc_password="$2"
+    shift
+    shift
+    ;;
+    --pulsar-superusers)
+    pulsar_superusers="$2"
+    shift
+    shift
+    ;;
+    -s|--symmetric)
+    symmetric=true
+    shift
+    ;;
+    -h|--help)
+    usage
+    exit 0
+    ;;
+    *)
+    echo "unknown option: $key"
+    usage
+    exit 1
+    ;;
+esac
+done
+
+namespace=${namespace:-pulsar}
+release=${release:-pulsar-dev}
+cc_admin=${cc_admin:-pulsar}
+cc_password=${cc_password:-pulsar}
+pulsar_superusers=${pulsar_superusers:-"proxy-admin,broker-admin,admin"}
+
+function generate_cc_admin_credentials() {
+    local secret_name="${release}-admin-secret"
+    kubectl create secret generic ${secret_name} -n ${namespace} \
+        --from-literal="USER=${cc_admin}" --from-literal="PASSWORD=${cc_password}"
+}
+
+function do_create_namespace() {
+    if [[ "${create_namespace}" == "true" ]]; then
+        kubectl create namespace ${namespace}
+    fi
+}
+
+do_create_namespace
+
+echo "create the credentials for the admin user of control center (grafana & pulsar-manager)"
+generate_cc_admin_credentials
+
+extra_opts=""
+if [[ "${symmetric}" == "true" ]]; then
+  extra_opts="${extra_opts} -s"
+fi
+
+echo "generate the token keys for the pulsar cluster"
+${CHART_HOME}/scripts/pulsar/generate_token_secret_key.sh -n ${namespace} -k ${release} ${extra_opts}
+
+echo "generate the tokens for the super-users: ${pulsar_superusers}"
+
+IFS=', ' read -r -a superusers <<< "$pulsar_superusers"
+for user in "${superusers[@]}"
+do
+    echo "generate the token for $user"
+    ${CHART_HOME}/scripts/pulsar/generate_token.sh -n ${namespace} -k ${release} -r ${user} ${extra_opts} 
+done
+
+echo "-------------------------------------"
+echo
+echo "The jwt token secret keys are generated under:"
+if [[ "${symmetric}" == "true" ]]; then
+    echo "    - '${release}-token-symmetric-key'"
+else
+    echo "    - '${release}-token-asymmetric-key'"
+fi
+echo
+
+echo "The jwt tokens for superusers are generated and stored as below:"
+for user in "${superusers[@]}"
+do
+    echo "    - '${user}':secret('${release}-token-${user}')"
+done
+echo
+
+echo "The credentials of the administrator of Control Center (Grafana & Pulsar Manager)"
+echo "is stored at secret '${release}-admin-secret"
+echo
+
diff --git a/scripts/pulsar/upload_tls.sh b/scripts/pulsar/upload_tls.sh
new file mode 100755
index 0000000..c4e6a4e
--- /dev/null
+++ b/scripts/pulsar/upload_tls.sh
@@ -0,0 +1,135 @@
+#!/usr/bin/env bash
+#
+# 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.
+#
+
+set -e
+
+CHART_HOME=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/../.. && pwd)
+cd ${CHART_HOME}
+
+namespace=${namespace:-pulsar}
+release=${release:-pulsar-dev}
+tlsdir=${tlsdir:-"${HOME}/.config/pulsar/security_tool/gen/ca"}
+clientComponents=${clientComponents:-""}
+serverComponents=${serverComponents:-"bookie,broker,proxy,recovery,zookeeper,toolset"}
+
+usage() {
+    cat <<EOF
+This script is used to upload tls for a given pulsar helm deployment.
+The tls certs are generated by using "pulsarctl security-tool".
+Options:
+       -h,--help                        prints the usage message
+       -n,--namespace                   the k8s namespace to install the pulsar helm chart. Defaut to ${namespace}.
+       -k,--release                     the pulsar helm release name. Default to ${release}.
+       -d,--dir                         the dir for storing tls certs. Default to ${tlsdir}.
+       -c,--client-components           the client components of pulsar cluster. a comma separated list of components. Default to ${clientComponents}.
+       -s,--server-components           the server components of pulsar cluster. a comma separated list of components. Default to ${serverComponents}.
+Usage:
+    $0 --namespace pulsar --release pulsar-dev
+EOF
+}
+
+while [[ $# -gt 0 ]]
+do
+key="$1"
+
+case $key in
+    -n|--namespace)
+    namespace="$2"
+    shift
+    shift
+    ;;
+    -k|--release)
+    release="$2"
+    shift
+    shift
+    ;;
+    -d|--dir)
+    tlsdir="$2"
+    shift
+    shift
+    ;;
+    -c|--client-components)
+    clientComponents="$2"
+    shift
+    shift
+    ;;
+    -s|--server-components)
+    serverComponents="$2"
+    shift
+    shift
+    ;;
+    -h|--help)
+    usage
+    exit 0
+    ;;
+    *)
+    echo "unknown option: $key"
+    usage
+    exit 1
+    ;;
+esac
+done
+
+ca_cert_file=${tlsdir}/certs/ca.cert.pem
+
+function upload_ca() {
+    local tls_ca_secret="${release}-ca-tls"
+    kubectl create secret generic ${tls_ca_secret} -n ${namespace} --from-file="ca.crt=${ca_cert_file}"
+}
+
+function upload_server_cert() {
+    local component=$1
+    local server_cert_secret="${release}-tls-${component}"
+    local tls_cert_file="${tlsdir}/servers/${component}/${component}.cert.pem"
+    local tls_key_file="${tlsdir}/servers/${component}/${component}.key-pk8.pem"
+
+    kubectl create secret generic ${server_cert_secret} \
+        -n ${namespace} \
+        --from-file="tls.crt=${tls_cert_file}" \
+        --from-file="tls.key=${tls_key_file}" \
+        --from-file="ca.crt=${ca_cert_file}"
+}
+
+function upload_client_cert() {
+    local component=$1
+    local client_cert_secret="${release}-tls-${component}"
+    local tls_cert_file="${tlsdir}/clients/${component}/${component}.cert.pem"
+    local tls_key_file="${tlsdir}/clients/${component}/${component}.key-pk8.pem"
+
+    kubectl create secret generic ${client_cert_secret} \
+        -n ${namespace} \
+        --from-file="tls.crt=${tls_cert_file}" \
+        --from-file="tls.key=${tls_key_file}" \
+        --from-file="ca.crt=${ca_cert_file}"
+}
+
+upload_ca
+
+IFS=', ' read -r -a server_components <<< "$serverComponents"
+for component in "${server_components[@]}"
+do
+    upload_server_cert ${component}
+done
+
+IFS=', ' read -r -a client_components <<< "$clientComponents"
+for component in "${client_components[@]}"
+do
+    upload_client_cert ${component}
+done