Merge "Use kube-addon-manager service"
diff --git a/kubernetes/files/kube-addon-manager/kube-addons.config b/kubernetes/files/kube-addon-manager/kube-addons.config
new file mode 100644
index 0000000..c98d034
--- /dev/null
+++ b/kubernetes/files/kube-addon-manager/kube-addons.config
@@ -0,0 +1,3 @@
+KUBECTL_BIN=/usr/bin/kubectl
+ADDON_PATH=/etc/kubernetes/addons
+TEST_ADDON_CHECK_INTERVAL_SEC=30
diff --git a/kubernetes/files/kube-addon-manager/kube-addons.sh b/kubernetes/files/kube-addon-manager/kube-addons.sh
new file mode 100644
index 0000000..2ee28c4
--- /dev/null
+++ b/kubernetes/files/kube-addon-manager/kube-addons.sh
@@ -0,0 +1,226 @@
+#!/bin/bash
+
+# Copyright 2014 The Kubernetes Authors.
+#
+# Licensed 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.
+
+# LIMITATIONS
+# 1. Exit code is probably not always correct.
+# 2. There are no unittests.
+# 3. Will not work if the total length of paths to addons is greater than
+#    bash can handle. Probably it is not a problem: ARG_MAX=2097152 on GCE.
+
+# cosmetic improvements to be done
+# 1. Improve the log function; add timestamp, file name, etc.
+# 2. Logging doesn't work from files that print things out.
+# 3. Kubectl prints the output to stderr (the output should be captured and then
+#    logged)
+
+# The business logic for whether a given object should be created
+# was already enforced by salt, and /etc/kubernetes/addons is the
+# managed result is of that. Start everything below that directory.
+KUBECTL=${KUBECTL_BIN:-/usr/bin/kubectl}
+KUBECTL_OPTS=${KUBECTL_OPTS:-}
+
+ADDON_CHECK_INTERVAL_SEC=${TEST_ADDON_CHECK_INTERVAL_SEC:-60}
+ADDON_PATH=${ADDON_PATH:-/etc/kubernetes/addons}
+
+SYSTEM_NAMESPACE=kube-system
+
+# Addons could use this label with two modes:
+# - ADDON_MANAGER_LABEL=Reconcile
+# - ADDON_MANAGER_LABEL=EnsureExists
+ADDON_MANAGER_LABEL="addonmanager.kubernetes.io/mode"
+# This label is deprecated (only for Addon Manager). In future release
+# addon-manager may not respect it anymore. Addons with
+# CLUSTER_SERVICE_LABEL=true and without ADDON_MANAGER_LABEL=EnsureExists
+# will be reconciled for now.
+CLUSTER_SERVICE_LABEL="kubernetes.io/cluster-service"
+
+# Remember that you can't log from functions that print some output (because
+# logs are also printed on stdout).
+# $1 level
+# $2 message
+function log() {
+  # manage log levels manually here
+
+  # add the timestamp if you find it useful
+  case $1 in
+    DB3 )
+#        echo "$1: $2"
+        ;;
+    DB2 )
+#        echo "$1: $2"
+        ;;
+    DBG )
+#        echo "$1: $2"
+        ;;
+    INFO )
+        echo "$1: $2"
+        ;;
+    WRN )
+        echo "$1: $2"
+        ;;
+    ERR )
+        echo "$1: $2"
+        ;;
+    * )
+        echo "INVALID_LOG_LEVEL $1: $2"
+        ;;
+  esac
+}
+
+# $1 filename of addon to start.
+# $2 count of tries to start the addon.
+# $3 delay in seconds between two consecutive tries
+function start_addon() {
+  local -r addon_filename=$1;
+  local -r tries=$2;
+  local -r delay=$3;
+
+  create_resource_from_string "$(cat ${addon_filename})" "${tries}" "${delay}" "${addon_filename}"
+}
+
+# $1 string with json or yaml.
+# $2 count of tries to start the addon.
+# $3 delay in seconds between two consecutive tries
+# $4 name of this object to use when logging about it.
+function create_resource_from_string() {
+  local -r config_string=$1;
+  local tries=$2;
+  local -r delay=$3;
+  local -r config_name=$4;
+  while [ ${tries} -gt 0 ]; do
+    echo "${config_string}" | ${KUBECTL} ${KUBECTL_OPTS} apply -f - && \
+      log INFO "== Successfully started ${config_name} at $(date -Is)" && \
+      return 0;
+    let tries=tries-1;
+    log WRN "== Failed to start ${config_name} at $(date -Is). ${tries} tries remaining. =="
+    sleep ${delay};
+  done
+  return 1;
+}
+
+# $1 resource type.
+function annotate_addons() {
+  local -r obj_type=$1;
+
+  # Annotate to objects already have this annotation should fail.
+  # Only try once for now.
+  ${KUBECTL} ${KUBECTL_OPTS} annotate ${obj_type} -l ${CLUSTER_SERVICE_LABEL}=true \
+    kubectl.kubernetes.io/last-applied-configuration='' --overwrite=false
+
+  if [[ $? -eq 0 ]]; then
+    log INFO "== Annotate resources completed successfully at $(date -Is) =="
+  else
+    log WRN "== Annotate resources completed with errors at $(date -Is) =="
+  fi
+}
+
+# $1 enable --prune or not.
+function reconcile_addons() {
+  local -r enable_prune=$1;
+
+  # TODO: Remove the first command in future release.
+  # Adding this for backward compatibility. Old addons have CLUSTER_SERVICE_LABEL=true and don't have
+  # ADDON_MANAGER_LABEL=EnsureExists will still be reconciled.
+  # Filter out `configured` message to not noisily log.
+  # `created`, `pruned` and errors will be logged.
+  log INFO "== Reconciling with deprecated label =="
+  ${KUBECTL} ${KUBECTL_OPTS} apply -f ${ADDON_PATH} \
+    -l ${CLUSTER_SERVICE_LABEL}=true,${ADDON_MANAGER_LABEL}!=EnsureExists \
+    --prune=${enable_prune} --recursive | grep -v configured
+
+  log INFO "== Reconciling with addon-manager label =="
+  ${KUBECTL} ${KUBECTL_OPTS} apply -f ${ADDON_PATH} \
+    -l ${CLUSTER_SERVICE_LABEL}!=true,${ADDON_MANAGER_LABEL}=Reconcile \
+    --prune=${enable_prune} --recursive | grep -v configured
+
+  log INFO "== Kubernetes addon reconcile completed at $(date -Is) =="
+}
+
+function ensure_addons() {
+  # Create objects already exist should fail.
+  # Filter out `AlreadyExists` message to not noisily log.
+  ${KUBECTL} ${KUBECTL_OPTS} create -f ${ADDON_PATH} \
+    -l ${ADDON_MANAGER_LABEL}=EnsureExists --recursive 2>&1 | grep -v AlreadyExists
+
+  log INFO "== Kubernetes addon ensure completed at $(date -Is) =="
+}
+
+# The business logic for whether a given object should be created
+# was already enforced by salt, and /etc/kubernetes/addons is the
+# managed result is of that. Start everything below that directory.
+log INFO "== Kubernetes addon manager started at $(date -Is) with ADDON_CHECK_INTERVAL_SEC=${ADDON_CHECK_INTERVAL_SEC} =="
+
+## Wait for the default service account to be created in the kube-system namespace.
+#token_found=""
+#while [ -z "${token_found}" ]; do
+#  sleep .5
+#  token_found=$(${KUBECTL} ${KUBECTL_OPTS} get --namespace="${SYSTEM_NAMESPACE}" serviceaccount default -o go-template="{{with index .secrets 0}}{{.name}}{{end}}")
+#  if [[ $? -ne 0 ]]; then
+#    token_found="";
+#    log WRN "== Error getting default service account, retry in 0.5 second =="
+#  fi
+#done
+#
+#log INFO "== Default service account in the ${SYSTEM_NAMESPACE} namespace has token ${token_found} =="
+#
+# Create admission_control objects if defined before any other addon services. If the limits
+# are defined in a namespace other than default, we should still create the limits for the
+# default namespace.
+for obj in $(find /etc/kubernetes/admission-controls \( -name \*.yaml -o -name \*.json \)); do
+  start_addon "${obj}" 100 10 default &
+  log INFO "++ obj ${obj} is created ++"
+done
+
+# TODO: The annotate and spin up parts should be removed after 1.6 is released.
+
+# Fake the "kubectl.kubernetes.io/last-applied-configuration" annotation on old resources
+# in order to clean them up by `kubectl apply --prune`.
+# RCs have to be annotated for 1.4->1.5+ upgrade, because we migrated from RCs to deployments for all default addons in 1.5.
+# Other types resources will also need this fake annotation if their names are changed,
+# otherwise they would be leaked during upgrade.
+log INFO "== Annotating the old addon resources at $(date -Is) =="
+annotate_addons ReplicationController
+annotate_addons Deployment
+
+# Create new addon resources by apply (with --prune=false).
+# The old RCs will not fight for pods created by new Deployments with the same label because the `controllerRef` feature.
+# The new Deployments will not fight for pods created by old RCs with the same label because the additional `pod-template-hash` label.
+# Apply will fail if some fields are modified but not are allowed, in that case should bump up addon version and name (e.g. handle externally).
+log INFO "== Executing apply to spin up new addon resources at $(date -Is) =="
+ensure_addons
+reconcile_addons false
+
+# Wait for new addons to be spinned up before delete old resources
+log INFO "== Wait for addons to be spinned up at $(date -Is) =="
+sleep ${ADDON_CHECK_INTERVAL_SEC}
+
+# Start the apply loop.
+# Check if the configuration has changed recently - in case the user
+# created/updated/deleted the files on the master.
+log INFO "== Entering periodical apply loop at $(date -Is) =="
+while true; do
+  start_sec=$(date +"%s")
+  # Only print stderr for the readability of logging
+  ensure_addons
+  reconcile_addons true
+  end_sec=$(date +"%s")
+  len_sec=$((${end_sec}-${start_sec}))
+  # subtract the time passed from the sleep time
+  if [[ ${len_sec} -lt ${ADDON_CHECK_INTERVAL_SEC} ]]; then
+    sleep_time=$((${ADDON_CHECK_INTERVAL_SEC}-${len_sec}))
+    sleep ${sleep_time}
+  fi
+done
diff --git a/kubernetes/files/kube-addons/calico-policy/calico-policy-controller.yml b/kubernetes/files/kube-addons/calico-policy/calico-policy-controller.yml
index 670506c..c0e0249 100644
--- a/kubernetes/files/kube-addons/calico-policy/calico-policy-controller.yml
+++ b/kubernetes/files/kube-addons/calico-policy/calico-policy-controller.yml
@@ -7,18 +7,17 @@
   labels:
     k8s-app: calico-policy
     kubernetes.io/cluster-service: "true"
+    addonmanager.kubernetes.io/mode: Reconcile
 spec:
   replicas: 1
   selector:
     matchLabels:
-      kubernetes.io/cluster-service: "true"
       k8s-app: calico-policy
   template:
     metadata:
       name: calico-policy-controller
       namespace: {{ master.addons.calico_policy.namespace }}
       labels:
-        kubernetes.io/cluster-service: "true"
         k8s-app: calico-policy
     spec:
       hostNetwork: true
diff --git a/kubernetes/files/kube-addons/contrail_network_controller/contrail-network-controller-configmap.yml b/kubernetes/files/kube-addons/contrail_network_controller/contrail-network-controller-configmap.yml
index fbf12de..ce0f3ed 100644
--- a/kubernetes/files/kube-addons/contrail_network_controller/contrail-network-controller-configmap.yml
+++ b/kubernetes/files/kube-addons/contrail_network_controller/contrail-network-controller-configmap.yml
@@ -4,6 +4,8 @@
 metadata:
   name: contrail-kube-manager
   namespace: {{ master.addons.contrail_network_controller.get('namespace', 'kube-system') }}
+  labels:
+    addonmanager.kubernetes.io/mode: Reconcile
 data:
   contrail.conf: |
     [DEFAULT]
diff --git a/kubernetes/files/kube-addons/contrail_network_controller/contrail-network-controller-deploy.yml b/kubernetes/files/kube-addons/contrail_network_controller/contrail-network-controller-deploy.yml
index ebe981f..1d61e82 100644
--- a/kubernetes/files/kube-addons/contrail_network_controller/contrail-network-controller-deploy.yml
+++ b/kubernetes/files/kube-addons/contrail_network_controller/contrail-network-controller-deploy.yml
@@ -9,6 +9,7 @@
     metadata:
       labels:
         app: contrail-network-controller
+        addonmanager.kubernetes.io/mode: Reconcile
     spec:
       hostNetwork: true
       tolerations:
diff --git a/kubernetes/files/kube-addons/dashboard/dashboard-address.yaml b/kubernetes/files/kube-addons/dashboard/dashboard-address.yaml
index 92c63a7..86b74a6 100644
--- a/kubernetes/files/kube-addons/dashboard/dashboard-address.yaml
+++ b/kubernetes/files/kube-addons/dashboard/dashboard-address.yaml
@@ -7,6 +7,7 @@
   labels:
     k8s-app: kubernetes-dashboard
     kubernetes.io/cluster-service: "true"
+    addonmanager.kubernetes.io/mode: Reconcile
 spec:
   selector:
     k8s-app: kubernetes-dashboard
diff --git a/kubernetes/files/kube-addons/dashboard/dashboard-controller.yaml b/kubernetes/files/kube-addons/dashboard/dashboard-controller.yaml
index 1135f29..46517de 100644
--- a/kubernetes/files/kube-addons/dashboard/dashboard-controller.yaml
+++ b/kubernetes/files/kube-addons/dashboard/dashboard-controller.yaml
@@ -6,6 +6,7 @@
   labels:
     k8s-app: kubernetes-dashboard
     kubernetes.io/cluster-service: "true"
+    addonmanager.kubernetes.io/mode: Reconcile
 spec:
   selector:
     matchLabels:
diff --git a/kubernetes/files/kube-addons/dashboard/dashboard-endpoint.yaml b/kubernetes/files/kube-addons/dashboard/dashboard-endpoint.yaml
index c35fad0..23327a5 100644
--- a/kubernetes/files/kube-addons/dashboard/dashboard-endpoint.yaml
+++ b/kubernetes/files/kube-addons/dashboard/dashboard-endpoint.yaml
@@ -7,6 +7,7 @@
   labels:
     k8s-app: kubernetes-dashboard
     kubernetes.io/cluster-service: "true"
+    addonmanager.kubernetes.io/mode: Reconcile
 subsets:
   - addresses:
     - ip: {{ master.addons.dashboard.public_ip }}
diff --git a/kubernetes/files/kube-addons/dashboard/dashboard-service.yaml b/kubernetes/files/kube-addons/dashboard/dashboard-service.yaml
index 2c2ce3f..26deb7b 100644
--- a/kubernetes/files/kube-addons/dashboard/dashboard-service.yaml
+++ b/kubernetes/files/kube-addons/dashboard/dashboard-service.yaml
@@ -7,6 +7,7 @@
   labels:
     k8s-app: kubernetes-dashboard
     kubernetes.io/cluster-service: "true"
+    addonmanager.kubernetes.io/mode: Reconcile
 spec:
 {%- if master.network.engine != 'opencontrail' %}
   selector:
diff --git a/kubernetes/files/kube-addons/dns/kubedns-autoscaler.yaml b/kubernetes/files/kube-addons/dns/kubedns-autoscaler.yaml
index 2260a7b..96161c1 100644
--- a/kubernetes/files/kube-addons/dns/kubedns-autoscaler.yaml
+++ b/kubernetes/files/kube-addons/dns/kubedns-autoscaler.yaml
@@ -6,6 +6,7 @@
   namespace: kube-system
   labels:
     k8s-app: kube-dns-autoscaler
+    addonmanager.kubernetes.io/mode: Reconcile
 spec:
   template:
     metadata:
diff --git a/kubernetes/files/kube-addons/dns/kubedns-rc.yaml b/kubernetes/files/kube-addons/dns/kubedns-rc.yaml
index 514bc26..b583220 100644
--- a/kubernetes/files/kube-addons/dns/kubedns-rc.yaml
+++ b/kubernetes/files/kube-addons/dns/kubedns-rc.yaml
@@ -21,6 +21,7 @@
   labels:
     k8s-app: kube-dns
     kubernetes.io/cluster-service: "true"
+    addonmanager.kubernetes.io/mode: Reconcile
 spec:
   # replicas: not specified here:
   # 1. In order to make Addon Manager do not reconcile this replicas parameter.
diff --git a/kubernetes/files/kube-addons/dns/kubedns-svc.yaml b/kubernetes/files/kube-addons/dns/kubedns-svc.yaml
index 6585954..83a4103 100644
--- a/kubernetes/files/kube-addons/dns/kubedns-svc.yaml
+++ b/kubernetes/files/kube-addons/dns/kubedns-svc.yaml
@@ -22,6 +22,7 @@
     k8s-app: kube-dns
     kubernetes.io/cluster-service: "true"
     kubernetes.io/name: "KubeDNS"
+    addonmanager.kubernetes.io/mode: Reconcile
 spec:
   selector:
     k8s-app: kube-dns
diff --git a/kubernetes/files/kube-addons/heapster-influxdb/heapster-address.yaml b/kubernetes/files/kube-addons/heapster-influxdb/heapster-address.yaml
index 28f2c24..04462b8 100644
--- a/kubernetes/files/kube-addons/heapster-influxdb/heapster-address.yaml
+++ b/kubernetes/files/kube-addons/heapster-influxdb/heapster-address.yaml
@@ -6,6 +6,7 @@
     k8s-app: heapster
     kubernetes.io/cluster-service: 'true'
     kubernetes.io/name: 'Heapster'
+    addonmanager.kubernetes.io/mode: Reconcile
   name: heapster-address
   namespace: kube-system
 spec:
@@ -15,4 +16,4 @@
   selector:
     k8s-app: heapster
   deprecatedPublicIPs: ['{{ master.addons.heapster_influxdb.public_ip }}']
-  type: LoadBalancer
\ No newline at end of file
+  type: LoadBalancer
diff --git a/kubernetes/files/kube-addons/heapster-influxdb/heapster-controller.yaml b/kubernetes/files/kube-addons/heapster-influxdb/heapster-controller.yaml
index 8b3f251..6f26727 100644
--- a/kubernetes/files/kube-addons/heapster-influxdb/heapster-controller.yaml
+++ b/kubernetes/files/kube-addons/heapster-influxdb/heapster-controller.yaml
@@ -4,6 +4,7 @@
   labels:
     k8s-app: heapster
     version: v6
+    addonmanager.kubernetes.io/mode: Reconcile
   name: heapster
   namespace: kube-system
 spec:
@@ -27,4 +28,4 @@
         command:
         - /heapster
         - --source=kubernetes:https://kubernetes.default
-        - --sink=influxdb:http://monitoring-influxdb:8086
\ No newline at end of file
+        - --sink=influxdb:http://monitoring-influxdb:8086
diff --git a/kubernetes/files/kube-addons/heapster-influxdb/heapster-endpoint.yaml b/kubernetes/files/kube-addons/heapster-influxdb/heapster-endpoint.yaml
index 35a140c..0790a2d 100644
--- a/kubernetes/files/kube-addons/heapster-influxdb/heapster-endpoint.yaml
+++ b/kubernetes/files/kube-addons/heapster-influxdb/heapster-endpoint.yaml
@@ -8,10 +8,11 @@
     k8s-app: heapster
     kubernetes.io/cluster-service: "true"
     kubernetes.io/name: "Heapster"
+    addonmanager.kubernetes.io/mode: Reconcile
 subsets:
   - addresses:
     - ip: {{ master.addons.heapster_influxdb.public_ip }}
 
     ports:
     - port: 8082
-      protocol: TCP
\ No newline at end of file
+      protocol: TCP
diff --git a/kubernetes/files/kube-addons/heapster-influxdb/heapster-service.yaml b/kubernetes/files/kube-addons/heapster-influxdb/heapster-service.yaml
index f04cf83..d487f2e 100644
--- a/kubernetes/files/kube-addons/heapster-influxdb/heapster-service.yaml
+++ b/kubernetes/files/kube-addons/heapster-influxdb/heapster-service.yaml
@@ -5,9 +5,10 @@
     k8s-app: heapster
     kubernetes.io/cluster-service: 'true'
     kubernetes.io/name: 'Heapster'
+    addonmanager.kubernetes.io/mode: Reconcile
   name: heapster
   namespace: kube-system
 spec:
   ports:
   - port: 80
-    targetPort: 8082
\ No newline at end of file
+    targetPort: 8082
diff --git a/kubernetes/files/kube-addons/heapster-influxdb/influxdb-controller.yaml b/kubernetes/files/kube-addons/heapster-influxdb/influxdb-controller.yaml
index 6235c18..695f592 100644
--- a/kubernetes/files/kube-addons/heapster-influxdb/influxdb-controller.yaml
+++ b/kubernetes/files/kube-addons/heapster-influxdb/influxdb-controller.yaml
@@ -3,6 +3,7 @@
 metadata:
   labels:
     name: influxGrafana
+    addonmanager.kubernetes.io/mode: Reconcile
   name: influxdb-grafana
   namespace: kube-system
 spec:
@@ -22,4 +23,4 @@
           name: influxdb-storage
       volumes:
       - name: influxdb-storage
-        emptyDir: {}
\ No newline at end of file
+        emptyDir: {}
diff --git a/kubernetes/files/kube-addons/heapster-influxdb/influxdb-service.yaml b/kubernetes/files/kube-addons/heapster-influxdb/influxdb-service.yaml
index 64bed1e..f4565eb 100644
--- a/kubernetes/files/kube-addons/heapster-influxdb/influxdb-service.yaml
+++ b/kubernetes/files/kube-addons/heapster-influxdb/influxdb-service.yaml
@@ -3,6 +3,7 @@
 metadata:
   labels:
     name: monitoring-influxdb
+    addonmanager.kubernetes.io/mode: Reconcile
   name: monitoring-influxdb
   namespace: kube-system
 spec:
@@ -14,4 +15,4 @@
     port: 8086
     targetPort: 8086
   selector:
-    name: influxGrafana
\ No newline at end of file
+    name: influxGrafana
diff --git a/kubernetes/files/kube-addons/helm/helm-tiller-deploy.yml b/kubernetes/files/kube-addons/helm/helm-tiller-deploy.yml
index b1828ce..f3501ce 100644
--- a/kubernetes/files/kube-addons/helm/helm-tiller-deploy.yml
+++ b/kubernetes/files/kube-addons/helm/helm-tiller-deploy.yml
@@ -6,6 +6,7 @@
   labels:
     app: helm
     name: tiller
+    addonmanager.kubernetes.io/mode: Reconcile
   name: tiller-deploy
   namespace: kube-system
 spec:
diff --git a/kubernetes/files/kube-addons/netchecker/netchecker-agent.yml b/kubernetes/files/kube-addons/netchecker/netchecker-agent.yml
index 855884a..f937c52 100644
--- a/kubernetes/files/kube-addons/netchecker/netchecker-agent.yml
+++ b/kubernetes/files/kube-addons/netchecker/netchecker-agent.yml
@@ -4,6 +4,7 @@
 metadata:
   labels:
     app: netchecker-agent
+    addonmanager.kubernetes.io/mode: Reconcile
   name: netchecker-agent
   namespace: {{ master.addons.netchecker.namespace }}
 spec:
diff --git a/kubernetes/files/kube-addons/netchecker/netchecker-server.yml b/kubernetes/files/kube-addons/netchecker/netchecker-server.yml
index dc5195c..95e05b4 100644
--- a/kubernetes/files/kube-addons/netchecker/netchecker-server.yml
+++ b/kubernetes/files/kube-addons/netchecker/netchecker-server.yml
@@ -10,6 +10,7 @@
     metadata:
       labels:
         app: netchecker-server
+        addonmanager.kubernetes.io/mode: Reconcile
       annotations:
         prometheus.io/scrape: "true"
         prometheus.io/port: "{{ master.addons.netchecker.port }}"
diff --git a/kubernetes/files/kube-addons/netchecker/netchecker-svc.yml b/kubernetes/files/kube-addons/netchecker/netchecker-svc.yml
index 309d61f..301e69e 100644
--- a/kubernetes/files/kube-addons/netchecker/netchecker-svc.yml
+++ b/kubernetes/files/kube-addons/netchecker/netchecker-svc.yml
@@ -4,6 +4,8 @@
 metadata:
   name: netchecker
   namespace: {{ master.addons.netchecker.namespace }}
+  labels:
+    addonmanager.kubernetes.io/mode: Reconcile
 spec:
   selector:
     app: netchecker-server
diff --git a/kubernetes/files/kube-addons/registry/registry-rc.yaml b/kubernetes/files/kube-addons/registry/registry-rc.yaml
index 055e596..eb69a0c 100644
--- a/kubernetes/files/kube-addons/registry/registry-rc.yaml
+++ b/kubernetes/files/kube-addons/registry/registry-rc.yaml
@@ -8,6 +8,7 @@
     k8s-app: kube-registry
     version: v0
     kubernetes.io/cluster-service: "true"
+    addonmanager.kubernetes.io/mode: Reconcile
 spec:
   replicas: 1
   selector:
@@ -18,7 +19,6 @@
       labels:
         k8s-app: kube-registry
         version: v0
-        kubernetes.io/cluster-service: "true"
     spec:
       tolerations:
         - key: node-role.kubernetes.io/master
diff --git a/kubernetes/files/kube-addons/registry/registry.svc b/kubernetes/files/kube-addons/registry/registry.svc
index 708a1ba..0baec84 100644
--- a/kubernetes/files/kube-addons/registry/registry.svc
+++ b/kubernetes/files/kube-addons/registry/registry.svc
@@ -8,10 +8,11 @@
     k8s-app: kube-registry
     kubernetes.io/cluster-service: "true"
     kubernetes.io/name: "KubeRegistry"
+    addonmanager.kubernetes.io/mode: Reconcile
 spec:
   selector:
     k8s-app: kube-registry
   ports:
   - name: registry
     port: {{ master.addons.registry.bind.get('port', '5000') }}
-    protocol: TCP
\ No newline at end of file
+    protocol: TCP
diff --git a/kubernetes/files/systemd/kube-addon-manager.service b/kubernetes/files/systemd/kube-addon-manager.service
new file mode 100644
index 0000000..1785096
--- /dev/null
+++ b/kubernetes/files/systemd/kube-addon-manager.service
@@ -0,0 +1,15 @@
+[Unit]
+Description=Kubernetes Addon Manager
+Documentation=https://github.com/kubernetes/kubernetes
+After=kube-apiserver.service
+
+[Service]
+SyslogIdentifier=kube-addon-manager
+EnvironmentFile=-/etc/default/%p
+User=root
+ExecStart=/usr/bin/kube-addons.sh
+Restart=on-failure
+LimitNOFILE=65536
+
+[Install]
+WantedBy=multi-user.target
diff --git a/kubernetes/master/setup.sls b/kubernetes/master/setup.sls
index b7d3806..b45ef2e 100644
--- a/kubernetes/master/setup.sls
+++ b/kubernetes/master/setup.sls
@@ -16,19 +16,41 @@
     - watch:
       - file: /etc/kubernetes/kubeconfig.sh
 
-{%- for addon_name, addon in master.addons.iteritems() %}
-{%- if addon.enabled %}
+/etc/default/kube-addon-manager:
+  file.managed:
+    - source: salt://kubernetes/files/kube-addon-manager/kube-addons.config
+    - user: root
+    - group: root
+    - mode: 755
+    - makedirs: True
 
-kubernetes_addons_{{ addon_name }}:
-  cmd.run:
-    - name: "hyperkube kubectl apply -f /etc/kubernetes/addons/{{ addon_name }}"
-    - unless: "hyperkube kubectl get {{ addon.get('creates', 'service') }} kube-{{ addon.get('name', addon_name) }} --namespace={{ addon.get('namespace', 'kube-system') }}"
-    {%- if grains.get('noservices') %}
-    - onlyif: /bin/false
-    {%- endif %}
+/usr/bin/kube-addons.sh:
+  file.managed:
+    - source: salt://kubernetes/files/kube-addon-manager/kube-addons.sh
+    - user: root
+    - group: root
+    - mode: 755
+    - makedirs: True
 
-{%- endif %}
-{%- endfor %}
+/etc/systemd/system/kube-addon-manager.service:
+  file.managed:
+    - source: salt://kubernetes/files/systemd/kube-addon-manager.service
+    - user: root
+    - group: root
+    - mode: 644
+    - makedirs: True
+
+kube-addon-manager_service:
+  service.running:
+  - name: kube-addon-manager
+  - enable: True
+  - watch:
+    - file: /etc/default/kube-addon-manager
+    - file: /usr/bin/kube-addons.sh
+    - file: /etc/systemd/system/kube-addon-manager.service
+  {%- if grains.get('noservices') %}
+  - onlyif: /bin/false
+  {%- endif %}
 
 {%- if master.label is defined %}