Update helm v2 k8s resources for helm v3
During upgrade activation, migration of helm v2 releases to helm v3 occur. This change adds support for searching for and updating existing kubernetes cluster resources deployed by helm v2 so that they are manageable by helm v3. This will allow proper application upgrade from Armada (helm v2) to FluxCD (helm v3) for applications that do not required any additional data manipulation. Test Plan: PASS CentOS upgrade-activate with successful upgrade of nginx-ingress-controller Change-Id: Ibf24a936edb29bf8e9d065f5fdc44d62329144b8 Story: 2009138 Task: 45608 Signed-off-by: Robert Church <robert.church@windriver.com>
This commit is contained in:
parent
2813d01db4
commit
d5297a8065
@ -1,5 +1,13 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2022 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# This script will perform helm v2 to helm v3 resource migration. This includes
|
||||
# using the helm 2to3 plugin along with labeling and annotating cluster
|
||||
# resources associated with the helm release so that it is managable by helm v3
|
||||
#
|
||||
|
||||
import keyring
|
||||
import psycopg2
|
||||
@ -7,14 +15,19 @@ import subprocess
|
||||
import sys
|
||||
import os
|
||||
|
||||
from controllerconfig.common import log
|
||||
from psycopg2.extras import RealDictCursor
|
||||
LOG = log.get_logger(__name__)
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2:
|
||||
raise Exception("Release name should be specified")
|
||||
|
||||
log.configure()
|
||||
|
||||
release = sys.argv[1]
|
||||
print("Starting to migrate release {}".format(release))
|
||||
LOG.info("Starting to migrate release {}".format(release))
|
||||
conn = init_connection()
|
||||
migrate_release(conn, release)
|
||||
|
||||
@ -35,6 +48,7 @@ def migrate_release(conn, release):
|
||||
release_name = release_info["name"]
|
||||
create_configmap(release_info)
|
||||
helm2to3_migrate(release_name)
|
||||
update_release_resources(release_name)
|
||||
cleanup_release(conn, release_name)
|
||||
|
||||
|
||||
@ -78,29 +92,144 @@ data:
|
||||
cmd = "kubectl --kubeconfig=/etc/kubernetes/admin.conf apply -f {}" \
|
||||
.format(configmap_path)
|
||||
execute_command(cmd)
|
||||
print("Configmap {} created".format(configmap_label_name))
|
||||
LOG.info("Configmap {} created".format(configmap_label_name))
|
||||
os.remove(configmap_path)
|
||||
|
||||
|
||||
def helm2to3_migrate(release_name):
|
||||
cmd = "helm 2to3 convert --kubeconfig=/etc/kubernetes/admin.conf \
|
||||
--tiller-out-cluster -s configmaps {}" \
|
||||
.format(release_name)
|
||||
cmd = ("helm 2to3 convert --kubeconfig=/etc/kubernetes/admin.conf "
|
||||
"--tiller-out-cluster -s configmaps {}".format(release_name))
|
||||
execute_command(cmd)
|
||||
print("Migrated {} to helm3".format(release_name))
|
||||
LOG.info("Migrated {} helm2 release to helm3".format(release_name))
|
||||
|
||||
|
||||
def get_api_resources(namespaced=True):
|
||||
if namespaced:
|
||||
namespace_arg = ' --namespaced=true'
|
||||
else:
|
||||
namespace_arg = ' --namespaced=false'
|
||||
|
||||
# Get all API resources
|
||||
try:
|
||||
api_resources = []
|
||||
api_resources_query = subprocess.check_output(
|
||||
("kubectl --kubeconfig=/etc/kubernetes/admin.conf api-resources "
|
||||
" --verbs=list {} -o name".format(namespace_arg)),
|
||||
shell=True, stderr=subprocess.STDOUT).decode('utf-8')
|
||||
if api_resources_query:
|
||||
api_resources = [a for a in api_resources_query.split("\n") if a]
|
||||
except Exception as e:
|
||||
LOG.info("Exception {} occured when trying to get kubernetes API "
|
||||
"resources".format(e))
|
||||
raise
|
||||
return api_resources
|
||||
|
||||
|
||||
def update_release_resources(release_name):
|
||||
""" Properly label resources to support Helm v3
|
||||
|
||||
Per https://github.com/helm/helm-2to3/issues/147, existing cluster
|
||||
resources deployed by helm v2 are not labeled properly for helm v3.
|
||||
Search for deployed resources based on release name and adjust the
|
||||
labeling.
|
||||
"""
|
||||
|
||||
LOG.info("Gathering namespaced kubernetes API resources...")
|
||||
namespaced_api_resources = get_api_resources(namespaced=True)
|
||||
|
||||
LOG.info("Gathering non-namespaced kubernetes API resources...")
|
||||
nonamespaced_api_resources = get_api_resources(namespaced=False)
|
||||
|
||||
# Get all helm release resources
|
||||
release_resources = []
|
||||
for r in namespaced_api_resources + nonamespaced_api_resources:
|
||||
if r in nonamespaced_api_resources:
|
||||
awk_print = '{print $1}'
|
||||
else:
|
||||
awk_print = '{print "-n "$1" "$2}'
|
||||
|
||||
LOG.info("Searching for {} resource related to {}...".format(
|
||||
r, release_name))
|
||||
try:
|
||||
cmd = ("kubectl --kubeconfig=/etc/kubernetes/admin.conf get -A "
|
||||
"-l app.kubernetes.io/instance={} --show-kind "
|
||||
"--ignore-not-found --no-headers {}".format(
|
||||
release_name, r))
|
||||
release_query = subprocess.Popen(cmd, shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
|
||||
output_filter = subprocess.Popen(['awk', awk_print],
|
||||
stdin=release_query.stdout,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
universal_newlines=True)
|
||||
release_query.stdout.close()
|
||||
resource_list, err = output_filter.communicate()
|
||||
if output_filter.returncode != 0:
|
||||
LOG.info("Command failed:\n {}\n{}\n{}".format(
|
||||
cmd, resource_list, err))
|
||||
raise Exception("Failed to execute command: %s" % cmd)
|
||||
|
||||
if resource_list:
|
||||
resources = [r for r in resource_list.split("\n") if r]
|
||||
release_resources += resources
|
||||
|
||||
except Exception as e:
|
||||
LOG.info("Exception {} occured when trying to check for {} use of "
|
||||
"API resource {}".format(e, release_name, r))
|
||||
continue
|
||||
|
||||
# Dump the resources need to be labeled/annotated
|
||||
for r in release_resources:
|
||||
LOG.info("Found {} resource: {}".format(release_name, r))
|
||||
|
||||
# Label the resources appropriately to support the release upgrade
|
||||
for tiller_managed_resource in release_resources:
|
||||
try:
|
||||
labeling_out = subprocess.check_output(
|
||||
('kubectl --kubeconfig=/etc/kubernetes/admin.conf label '
|
||||
'--overwrite {} '
|
||||
'"app.kubernetes.io/managed-by=Helm"'.format(
|
||||
tiller_managed_resource)),
|
||||
shell=True, stderr=subprocess.STDOUT).decode('utf-8')
|
||||
LOG.info(labeling_out)
|
||||
except Exception as e:
|
||||
LOG.info("Exception {} occured when trying to label '{}'".format(
|
||||
e, tiller_managed_resource))
|
||||
continue
|
||||
|
||||
if "-n " in tiller_managed_resource:
|
||||
# Extract and annotate the namespaced resource
|
||||
# Ex: '-n metrics-server deployment.apps/ms-metrics-server'
|
||||
components = [c for c in tiller_managed_resource.split(" ") if c]
|
||||
namespace = components[1]
|
||||
|
||||
try:
|
||||
annotate_out = subprocess.check_output(
|
||||
('kubectl --kubeconfig=/etc/kubernetes/admin.conf annotate'
|
||||
' --overwrite {} "meta.helm.sh/release-name={}" '
|
||||
'"meta.helm.sh/release-namespace={}"'.format(
|
||||
tiller_managed_resource, release_name, namespace)),
|
||||
shell=True, stderr=subprocess.STDOUT).decode('utf-8')
|
||||
LOG.info(annotate_out)
|
||||
except Exception as e:
|
||||
LOG.info("Exception {} occured when trying to annotate "
|
||||
"'{}'".format(e, tiller_managed_resource))
|
||||
continue
|
||||
|
||||
|
||||
def cleanup_release(conn, release_name):
|
||||
cmd = "helm 2to3 cleanup --kubeconfig=/etc/kubernetes/admin.conf --release-cleanup \
|
||||
--tiller-out-cluster -s configmaps --skip-confirmation --name {}" \
|
||||
.format(release_name)
|
||||
cmd = ("helm 2to3 cleanup --kubeconfig=/etc/kubernetes/admin.conf "
|
||||
"--release-cleanup --tiller-out-cluster -s configmaps "
|
||||
"--skip-confirmation --name {}".format(release_name))
|
||||
execute_command(cmd)
|
||||
with conn:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("delete from releases where name = %s",
|
||||
(release_name,))
|
||||
|
||||
print("Cleaned up helm2 data for {}".format(release_name))
|
||||
LOG.info("Cleaned up helm2 data for {}".format(release_name))
|
||||
|
||||
|
||||
def execute_command(cmd):
|
||||
@ -109,7 +238,7 @@ def execute_command(cmd):
|
||||
|
||||
stdout, stderr = sub.communicate()
|
||||
if sub.returncode != 0:
|
||||
print("Command failed:\n %s\n%s\n%s" % (cmd, stdout, stderr))
|
||||
LOG.info("Command failed:\n %s\n%s\n%s" % (cmd, stdout, stderr))
|
||||
raise Exception("Failed to execute command: %s" % cmd)
|
||||
return stdout
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user