diff --git a/Readme.md b/Readme.md index bce30e2..553231e 100644 --- a/Readme.md +++ b/Readme.md @@ -21,10 +21,11 @@ For only sync in this namespaces: Sync all namespaces excludes this namespaces: `synator/exclude-namespaces='kube-system,kube-node-lease'` +# Reload deployment when config upgraded +======= ![secret.yaml](https://miro.medium.com/max/2400/1*UH4iTu3Gg6DkofHyX2KDHg.png) -## Reload pod when config upgraded -Add annotation `synator/reload: "secret:example"` to pod or deployment template +Add annotation `synator/reload: "secret:example"` to deployment template When secret example updated busybox pod will reload Note: For multiple secrte or configmap: diff --git a/deploy.yml b/deploy.yml index dbec697..2342864 100644 --- a/deploy.yml +++ b/deploy.yml @@ -11,7 +11,7 @@ metadata: name: synator rules: - apiGroups: [""] - resources: ["secrets", "configmaps"] + resources: ["secrets", "configmaps", "deployments"] verbs: ["*"] - apiGroups: [events.k8s.io] resources: [events] @@ -20,7 +20,11 @@ rules: resources: [events] verbs: [create] - apiGroups: [""] - resources: ["namespaces", "pods", "replicasets", "namespaces/status"] + resources: + ["namespaces", "pods", "replicasets", "deployments", "namespaces/status"] + verbs: ["*"] + - apiGroups: ["extensions", "apps"] + resources: ["deployments"] verbs: ["*"] --- kind: ClusterRoleBinding diff --git a/handlers.py b/handlers.py index 4eca6e7..177f088 100644 --- a/handlers.py +++ b/handlers.py @@ -19,6 +19,7 @@ def update_secret(body, meta, spec, status, old, new, diff, **kwargs): secret = api.read_namespaced_secret(meta.name, meta.namespace) secret.metadata.annotations.pop('synator/sync') + secret.metadata.annotations.pop('field.cattle.io/projectId') secret.metadata.resource_version = None secret.metadata.uid = None for ns in parse_target_namespaces(meta, namespaces): @@ -42,6 +43,7 @@ def updateConfigMap(body, meta, spec, status, old, new, diff, **kwargs): cfg = api.read_namespaced_config_map(meta.name, meta.namespace) cfg.metadata.annotations.pop('synator/sync') + cfg.metadata.annotations.pop('field.cattle.io/projectId') cfg.metadata.resource_version = None cfg.metadata.uid = None for ns in parse_target_namespaces(meta, namespaces): @@ -98,6 +100,7 @@ def newNamespace(spec, name, meta, logger, **kwargs): # Check secret have annotation if secret.metadata.annotations and secret.metadata.annotations.get("synator/sync") == "yes": secret.metadata.annotations.pop('synator/sync') + secret.metadata.annotations.pop('field.cattle.io/projectId') secret.metadata.resource_version = None secret.metadata.uid = None for ns in parse_target_namespaces(secret.metadata, [name]): @@ -114,35 +117,54 @@ def newNamespace(spec, name, meta, logger, **kwargs): print("Exception when calling CoreV1Api->list_secret_for_all_namespaces: %s\n" % e) -# Reload Pod when update configmap or secret +# Reload deployment when update configmap or secret @kopf.on.update('', 'v1', 'configmaps', when=watch_namespace) -def reload_pod_config(body, meta, spec, status, old, new, diff, **kwargs): - # Get namespace - ns = meta.namespace - api = kubernetes.client.CoreV1Api() - pods = api.list_namespaced_pod(ns) - print(ns, meta.name) - for pod in pods.items: - # Find which pods use this secrets - if pod.metadata.annotations and pod.metadata.annotations.get('synator/reload'): - if any('configmap:' + meta.name in s for s in pod.metadata.annotations.get('synator/reload').split(',')): - # Reload pod - api.delete_namespaced_pod( - pod.metadata.name, pod.metadata.namespace) - +def reload_deployment_config(body, meta, spec, status, old, new, diff, logger, **kwargs): + reload_deployments_sync(meta, 'configmap', logger) @kopf.on.update('', 'v1', 'secrets', when=watch_namespace) -def reload_pod_secret(body, meta, spec, status, old, new, diff, **kwargs): - # Get namespace - ns = meta.namespace - api = kubernetes.client.CoreV1Api() - pods = api.list_namespaced_pod(ns) - print(ns, meta.name) - for pod in pods.items: - # Find which pods use this secrets - if pod.metadata.annotations and pod.metadata.annotations.get('synator/reload'): - if any('secret:' + meta.name in s for s in pod.metadata.annotations.get('synator/reload').split(',')): - # Reload pod - api.delete_namespaced_pod( - pod.metadata.name, pod.metadata.namespace) +def reload_deployment_secret(body, meta, spec, status, old, new, diff, logger, **kwargs): + reload_deployments_sync(meta, 'secret', logger) + +def reload_deployments_sync(meta, secretOrConfigmap, logger): + try: + # Get namespace + ns = meta.namespace + api = kubernetes.client.AppsV1Api() + deployments = api.list_namespaced_deployment(ns) + configSearch = secretOrConfigmap + ':' + meta.name + + logger.info(f"NS: %s Name: %s Deployments %s", ns, configSearch, str(len(deployments.items))) + + for deployment in deployments.items: + annotations = deployment.spec.template.metadata.annotations + syncReloads = [] + if annotations and annotations.get('synator/reload'): + syncReloads = annotations.get('synator/reload').split(',') + + if any(configSearch in s for s in syncReloads): + # Reload deployment + update_deployment(api, deployment, logger) + except kubernetes.client.rest.ApiException as e: + print("Exception when calling AppsV1Api: %s\n" % e) + +def update_deployment(api, deployment, logger): + # Update revision + revision = deployment.spec.template.metadata.annotations.get('synator/revision') + if revision is None: + revision = 1 + else: + revision = int(revision) + 1 + + deployment.spec.template.metadata.annotations['synator/revision'] = str(revision) + + # Update the deployment + api_response = api.patch_namespaced_deployment( + name=deployment.metadata.name, + namespace=deployment.metadata.namespace, + body=deployment) + + # print("Deployment updated. status='%s'" % str(api_response.status)) + + logger.info(f"Deployment %s updated revision %s", deployment.metadata.name, str(revision))