mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-31 15:25:57 +00:00
namespaces in addon update
This commit is contained in:
parent
4f9cfc84c4
commit
8df3a9cae8
@ -113,9 +113,11 @@ except Exception, ex:
|
||||
}
|
||||
|
||||
# $1 yaml file path
|
||||
function get-object-name-from-file() {
|
||||
# returns a string of the form <namespace>/<name> (we call it nsnames)
|
||||
function get-object-nsname-from-file() {
|
||||
# prints to stdout, so log cannot be used
|
||||
#WARNING: only yaml is supported
|
||||
#addons that do not specify a namespace are assumed to be in "default".
|
||||
cat $1 | python -c '''
|
||||
try:
|
||||
import pipes,sys,yaml
|
||||
@ -126,7 +128,10 @@ try:
|
||||
# Otherwise we are ignoring them (the update will not work anyway)
|
||||
print "ERROR"
|
||||
else:
|
||||
print y["metadata"]["name"]
|
||||
try:
|
||||
print "%s/%s" % (y["metadata"]["namespace"], y["metadata"]["name"])
|
||||
except Exception, ex:
|
||||
print "default/%s" % y["metadata"]["name"]
|
||||
except Exception, ex:
|
||||
print "ERROR"
|
||||
'''
|
||||
@ -136,7 +141,7 @@ except Exception, ex:
|
||||
# $2 addon type (e.g. ReplicationController)
|
||||
# echoes the string with paths to files containing addon for the given type
|
||||
# works only for yaml files (!) (ignores json files)
|
||||
function get-addons-from-disk() {
|
||||
function get-addon-paths-from-disk() {
|
||||
# prints to stdout, so log cannot be used
|
||||
local -r addon_dir=$1
|
||||
local -r obj_type=$2
|
||||
@ -184,9 +189,10 @@ function run-until-success() {
|
||||
}
|
||||
|
||||
# $1 object type
|
||||
function get-addons-from-server() {
|
||||
# returns a list of <namespace>/<name> pairs (nsnames)
|
||||
function get-addon-nsnames-from-server() {
|
||||
local -r obj_type=$1
|
||||
"${KUBECTL}" get "${obj_type}" -o template -t "{{range.items}}{{.metadata.name}} {{end}}" --api-version=v1beta3 -l kubernetes.io/cluster-service=true
|
||||
"${KUBECTL}" get "${obj_type}" --all-namespaces -o template -t "{{range.items}}{{.metadata.namespace}}/{{.metadata.name}} {{end}}" --api-version=v1beta3 -l kubernetes.io/cluster-service=true
|
||||
}
|
||||
|
||||
# returns the characters after the last separator (including)
|
||||
@ -228,38 +234,52 @@ function get-basename() {
|
||||
|
||||
function stop-object() {
|
||||
local -r obj_type=$1
|
||||
local -r obj_name=$2
|
||||
log INFO "Stopping ${obj_type} ${obj_name}"
|
||||
run-until-success "${KUBECTL} stop ${obj_type} ${obj_name}" ${NUM_TRIES} ${DELAY_AFTER_ERROR_SEC}
|
||||
local -r namespace=$2
|
||||
local -r obj_name=$3
|
||||
log INFO "Stopping ${obj_type} ${namespace}/${obj_name}"
|
||||
|
||||
run-until-success "${KUBECTL} stop --namespace=${namespace} ${obj_type} ${obj_name}" ${NUM_TRIES} ${DELAY_AFTER_ERROR_SEC}
|
||||
}
|
||||
|
||||
function create-object() {
|
||||
local -r obj_type=$1
|
||||
local -r file_path=$2
|
||||
log INFO "Creating new ${obj_type} from file ${file_path}"
|
||||
|
||||
local nsname_from_file
|
||||
nsname_from_file=$(get-object-nsname-from-file ${file_path})
|
||||
if [[ "${nsname_from_file}" == "ERROR" ]]; then
|
||||
log INFO "Cannot read object name from ${file_path}. Ignoring"
|
||||
return 1
|
||||
fi
|
||||
IFS='/' read namespace obj_name <<< "${nsname_from_file}"
|
||||
|
||||
log INFO "Creating new ${obj_type} from file ${file_path} in namespace ${namespace}, name: ${obj_name}"
|
||||
# this will keep on failing if the ${file_path} disappeared in the meantime.
|
||||
# Do not use too many retries.
|
||||
run-until-success "${KUBECTL} create -f ${file_path}" ${NUM_TRIES} ${DELAY_AFTER_ERROR_SEC}
|
||||
run-until-success "${KUBECTL} create --namespace=${namespace} -f ${file_path}" ${NUM_TRIES} ${DELAY_AFTER_ERROR_SEC}
|
||||
}
|
||||
|
||||
function update-object() {
|
||||
local -r obj_type=$1
|
||||
local -r obj_name=$2
|
||||
local -r file_path=$3
|
||||
log INFO "updating the ${obj_type} ${obj_name} with the new definition ${file_path}"
|
||||
stop-object ${obj_type} ${obj_name}
|
||||
local -r namespace=$2
|
||||
local -r obj_name=$3
|
||||
local -r file_path=$4
|
||||
log INFO "updating the ${obj_type} ${namespace}/${obj_name} with the new definition ${file_path}"
|
||||
stop-object ${obj_type} ${namespace} ${obj_name}
|
||||
create-object ${obj_type} ${file_path}
|
||||
}
|
||||
|
||||
# deletes the objects from the server
|
||||
# $1 object type
|
||||
# $2 a list of object names
|
||||
# $2 a list of object nsnames
|
||||
function stop-objects() {
|
||||
local -r obj_type=$1
|
||||
local -r obj_names=$2
|
||||
local -r obj_nsnames=$2
|
||||
local namespace
|
||||
local obj_name
|
||||
for obj_name in ${obj_names}; do
|
||||
stop-object ${obj_type} ${obj_names} &
|
||||
for nsname in ${obj_nsnames}; do
|
||||
IFS='/' read namespace obj_name <<< "${nsname}"
|
||||
stop-object ${obj_type} ${namespace} ${obj_name} &
|
||||
done
|
||||
}
|
||||
|
||||
@ -284,22 +304,28 @@ function create-objects() {
|
||||
# updates objects
|
||||
# $1 object type
|
||||
# $2 a list of update specifications
|
||||
# each update specification is a ';' separated pair: <object name>;<file path>
|
||||
# each update specification is a ';' separated pair: <nsname>;<file path>
|
||||
function update-objects() {
|
||||
local -r obj_type=$1 # ignored
|
||||
local -r update_spec=$2
|
||||
local objdesc
|
||||
local nsname
|
||||
local obj_name
|
||||
local namespace
|
||||
|
||||
for objdesc in ${update_spec}; do
|
||||
IFS=';' read -a array <<< ${objdesc}
|
||||
update-object ${obj_type} ${array[0]} ${array[1]} &
|
||||
IFS=';' read nsname file_path <<< "${objdesc}"
|
||||
IFS='/' read namespace obj_name <<< "${nsname}"
|
||||
|
||||
update-object ${obj_type} ${namespace} ${obj_name} ${file_path} &
|
||||
done
|
||||
}
|
||||
|
||||
# Global variables set by function match-objects.
|
||||
for_delete="" # a list of object names to be deleted
|
||||
for_update="" # a list of pairs <obj_name>;<filePath> for objects that should be updated
|
||||
for_ignore="" # a list of object nanes that can be ignored
|
||||
new_files="" # a list of file paths that weren't matched by any existing objects (these objects must be created now)
|
||||
nsnames_for_delete="" # a list of object nsnames to be deleted
|
||||
for_update="" # a list of pairs <nsname>;<filePath> for objects that should be updated
|
||||
nsnames_for_ignore="" # a list of object nsnames that will be ignored
|
||||
new_files="" # a list of file paths that weren't matched by any existing objects (these objects must be created now)
|
||||
|
||||
|
||||
# $1 path to files with objects
|
||||
@ -311,32 +337,36 @@ function match-objects() {
|
||||
local -r separator=$3
|
||||
|
||||
# output variables (globals)
|
||||
for_delete=""
|
||||
nsnames_for_delete=""
|
||||
for_update=""
|
||||
for_ignore=""
|
||||
nsnames_for_ignore=""
|
||||
new_files=""
|
||||
|
||||
addon_names_on_server=$(get-addons-from-server "${obj_type}")
|
||||
addon_paths_in_files=$(get-addons-from-disk "${addon_dir}" "${obj_type}")
|
||||
addon_nsnames_on_server=$(get-addon-nsnames-from-server "${obj_type}")
|
||||
addon_paths_in_files=$(get-addon-paths-from-disk "${addon_dir}" "${obj_type}")
|
||||
|
||||
log DB2 "addon_names_on_server=${addon_names_on_server}"
|
||||
log DB2 "addon_nsnames_on_server=${addon_nsnames_on_server}"
|
||||
log DB2 "addon_paths_in_files=${addon_paths_in_files}"
|
||||
|
||||
local matched_files=""
|
||||
|
||||
local basename_on_server=""
|
||||
local name_on_server=""
|
||||
local basensname_on_server=""
|
||||
local nsname_on_server=""
|
||||
local suffix_on_server=""
|
||||
local name_from_file=""
|
||||
local nsname_from_file=""
|
||||
local suffix_from_file=""
|
||||
local found=0
|
||||
local addon_path=""
|
||||
|
||||
for name_on_server in ${addon_names_on_server}; do
|
||||
basename_on_server=$(get-basename ${name_on_server} ${separator})
|
||||
suffix_on_server="$(get-suffix ${name_on_server} ${separator})"
|
||||
# objects that were moved between namespaces will have different nsname
|
||||
# because the namespace is included. So they will be treated
|
||||
# like different objects and not updated but deleted and created again
|
||||
# (in the current version update is also delete+create, so it does not matter)
|
||||
for nsname_on_server in ${addon_nsnames_on_server}; do
|
||||
basensname_on_server=$(get-basename ${nsname_on_server} ${separator})
|
||||
suffix_on_server="$(get-suffix ${nsname_on_server} ${separator})"
|
||||
|
||||
log DB3 "Found existing addon ${name_on_server}, basename=${basename_on_server}"
|
||||
log DB3 "Found existing addon ${nsname_on_server}, basename=${basensname_on_server}"
|
||||
|
||||
# check if the addon is present in the directory and decide
|
||||
# what to do with it
|
||||
@ -344,31 +374,31 @@ function match-objects() {
|
||||
# again. But for small number of addons it doesn't matter so much.
|
||||
found=0
|
||||
for addon_path in ${addon_paths_in_files}; do
|
||||
name_from_file=$(get-object-name-from-file ${addon_path})
|
||||
if [[ "${name_from_file}" == "ERROR" ]]; then
|
||||
nsname_from_file=$(get-object-nsname-from-file ${addon_path})
|
||||
if [[ "${nsname_from_file}" == "ERROR" ]]; then
|
||||
log INFO "Cannot read object name from ${addon_path}. Ignoring"
|
||||
continue
|
||||
else
|
||||
log DB2 "Found object name '${name_from_file}' in file ${addon_path}"
|
||||
log DB2 "Found object name '${nsname_from_file}' in file ${addon_path}"
|
||||
fi
|
||||
suffix_from_file="$(get-suffix ${name_from_file} ${separator})"
|
||||
suffix_from_file="$(get-suffix ${nsname_from_file} ${separator})"
|
||||
|
||||
log DB3 "matching: ${basename_on_server}${suffix_from_file} == ${name_from_file}"
|
||||
if [[ "${basename_on_server}${suffix_from_file}" == "${name_from_file}" ]]; then
|
||||
log DB3 "matched existing ${obj_type} ${name_on_server} to file ${addon_path}; suffix_on_server=${suffix_on_server}, suffix_from_file=${suffix_from_file}"
|
||||
log DB3 "matching: ${basensname_on_server}${suffix_from_file} == ${nsname_from_file}"
|
||||
if [[ "${basensname_on_server}${suffix_from_file}" == "${nsname_from_file}" ]]; then
|
||||
log DB3 "matched existing ${obj_type} ${nsname_on_server} to file ${addon_path}; suffix_on_server=${suffix_on_server}, suffix_from_file=${suffix_from_file}"
|
||||
found=1
|
||||
matched_files="${matched_files} ${addon_path}"
|
||||
if [[ "${suffix_on_server}" == "${suffix_from_file}" ]]; then
|
||||
for_ignore="${for_ignore} ${name_from_file}"
|
||||
nsnames_for_ignore="${nsnames_for_ignore} ${nsname_from_file}"
|
||||
else
|
||||
for_update="${for_update} ${name_on_server};${addon_path}"
|
||||
for_update="${for_update} ${nsname_on_server};${addon_path}"
|
||||
fi
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ ${found} -eq 0 ]]; then
|
||||
log DB2 "No definition file found for replication controller ${name_on_server}. Scheduling for deletion"
|
||||
for_delete="${for_delete} ${name_on_server}"
|
||||
log DB2 "No definition file found for replication controller ${nsname_on_server}. Scheduling for deletion"
|
||||
nsnames_for_delete="${nsnames_for_delete} ${nsname_on_server}"
|
||||
fi
|
||||
done
|
||||
|
||||
@ -395,12 +425,12 @@ function reconcile-objects() {
|
||||
local -r separator=$3 # name separator
|
||||
match-objects ${addon_path} ${obj_type} ${separator}
|
||||
|
||||
log DBG "${obj_type}: for_delete=${for_delete}"
|
||||
log DBG "${obj_type}: nsnames_for_delete=${nsnames_for_delete}"
|
||||
log DBG "${obj_type}: for_update=${for_update}"
|
||||
log DBG "${obj_type}: for_ignore=${for_ignore}"
|
||||
log DBG "${obj_type}: nsnames_for_ignore=${nsnames_for_ignore}"
|
||||
log DBG "${obj_type}: new_files=${new_files}"
|
||||
|
||||
stop-objects "${obj_type}" "${for_delete}"
|
||||
stop-objects "${obj_type}" "${nsnames_for_delete}"
|
||||
# wait for jobs below is a protection against changing the basename
|
||||
# of a replication controllerm without changing the selector.
|
||||
# If we don't wait, the new rc may be created before the old one is deleted
|
||||
@ -414,9 +444,9 @@ function reconcile-objects() {
|
||||
create-objects "${obj_type}" "${new_files}"
|
||||
update-objects "${obj_type}" "${for_update}"
|
||||
|
||||
local obj
|
||||
for obj in ${for_ignore}; do
|
||||
log DB2 "The ${obj_type} ${obj} is already up to date"
|
||||
local nsname
|
||||
for nsname in ${nsnames_for_ignore}; do
|
||||
log DB2 "The ${obj_type} ${nsname} is already up to date"
|
||||
done
|
||||
|
||||
wait-for-jobs
|
||||
|
@ -39,7 +39,7 @@ apiVersion: v1
|
||||
kind: ReplicationController
|
||||
metadata:
|
||||
name: addon-test-v1
|
||||
namespace: default
|
||||
namespace: %s
|
||||
labels:
|
||||
k8s-app: addon-test
|
||||
version: v1
|
||||
@ -69,7 +69,7 @@ apiVersion: v1
|
||||
kind: ReplicationController
|
||||
metadata:
|
||||
name: addon-test-v2
|
||||
namespace: default
|
||||
namespace: %s
|
||||
labels:
|
||||
k8s-app: addon-test
|
||||
version: v2
|
||||
@ -99,7 +99,7 @@ apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: addon-test
|
||||
namespace: default
|
||||
namespace: %s
|
||||
labels:
|
||||
k8s-app: addon-test
|
||||
kubernetes.io/cluster-service: "true"
|
||||
@ -118,7 +118,7 @@ apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: addon-test-updated
|
||||
namespace: default
|
||||
namespace: %s
|
||||
labels:
|
||||
k8s-app: addon-test
|
||||
kubernetes.io/cluster-service: "true"
|
||||
@ -138,7 +138,7 @@ apiVersion: v1
|
||||
kind: ReplicationController
|
||||
metadata:
|
||||
name: invalid-addon-test-v1
|
||||
namespace: default
|
||||
namespace: %s
|
||||
labels:
|
||||
k8s-app: invalid-addon-test
|
||||
version: v1
|
||||
@ -167,7 +167,7 @@ apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: ivalid-addon-test
|
||||
namespace: default
|
||||
namespace: %s
|
||||
labels:
|
||||
k8s-app: invalid-addon-test
|
||||
kubernetes.io/name: invalid-addon-test
|
||||
@ -181,8 +181,8 @@ spec:
|
||||
`
|
||||
|
||||
var addonTestPollInterval = 3 * time.Second
|
||||
var addonTestPollTimeout = 1 * time.Minute
|
||||
var addonNamespace = api.NamespaceDefault // addons are in the default namespace
|
||||
var addonTestPollTimeout = 5 * time.Minute
|
||||
var defaultNsName = api.NamespaceDefault
|
||||
|
||||
type stringPair struct {
|
||||
data, fileName string
|
||||
@ -260,12 +260,12 @@ var _ = Describe("Addon update", func() {
|
||||
svcInvalid := "invalid-addon-service-v1.yaml"
|
||||
|
||||
var remoteFiles []stringPair = []stringPair{
|
||||
{addon_controller_v1, rcv1},
|
||||
{addon_controller_v2, rcv2},
|
||||
{addon_service_v1, svcv1},
|
||||
{addon_service_v2, svcv2},
|
||||
{invalid_addon_controller_v1, rcInvalid},
|
||||
{invalid_addon_service_v1, svcInvalid},
|
||||
{fmt.Sprintf(addon_controller_v1, defaultNsName), rcv1},
|
||||
{fmt.Sprintf(addon_controller_v2, namespace.Name), rcv2},
|
||||
{fmt.Sprintf(addon_service_v1, namespace.Name), svcv1},
|
||||
{fmt.Sprintf(addon_service_v2, namespace.Name), svcv2},
|
||||
{fmt.Sprintf(invalid_addon_controller_v1, namespace.Name), rcInvalid},
|
||||
{fmt.Sprintf(invalid_addon_service_v1, defaultNsName), svcInvalid},
|
||||
}
|
||||
|
||||
for _, p := range remoteFiles {
|
||||
@ -293,8 +293,8 @@ var _ = Describe("Addon update", func() {
|
||||
sshExecAndVerify(sshClient, fmt.Sprintf("sudo cp %s/%s %s/%s", temporaryRemotePath, rcv1, destinationDir, rcv1))
|
||||
sshExecAndVerify(sshClient, fmt.Sprintf("sudo cp %s/%s %s/%s", temporaryRemotePath, svcv1, destinationDir, svcv1))
|
||||
|
||||
waitForServiceInAddonTest(c, "addon-test", true)
|
||||
waitForReplicationControllerInAddonTest(c, "addon-test-v1", true)
|
||||
waitForServiceInAddonTest(c, namespace.Name, "addon-test", true)
|
||||
waitForReplicationControllerInAddonTest(c, defaultNsName, "addon-test-v1", true)
|
||||
|
||||
By("update manifests")
|
||||
sshExecAndVerify(sshClient, fmt.Sprintf("sudo cp %s/%s %s/%s", temporaryRemotePath, rcv2, destinationDir, rcv2))
|
||||
@ -307,34 +307,38 @@ var _ = Describe("Addon update", func() {
|
||||
* But it is ok - as long as we don't have rolling update, the result will be the same
|
||||
*/
|
||||
|
||||
waitForServiceInAddonTest(c, "addon-test-updated", true)
|
||||
waitForReplicationControllerInAddonTest(c, "addon-test-v2", true)
|
||||
waitForServiceInAddonTest(c, namespace.Name, "addon-test-updated", true)
|
||||
waitForReplicationControllerInAddonTest(c, namespace.Name, "addon-test-v2", true)
|
||||
|
||||
waitForServiceInAddonTest(c, "addon-test", false)
|
||||
waitForReplicationControllerInAddonTest(c, "addon-test-v1", false)
|
||||
waitForServiceInAddonTest(c, namespace.Name, "addon-test", false)
|
||||
waitForReplicationControllerInAddonTest(c, defaultNsName, "addon-test-v1", false)
|
||||
|
||||
By("remove manifests")
|
||||
sshExecAndVerify(sshClient, fmt.Sprintf("sudo rm %s/%s", destinationDir, rcv2))
|
||||
sshExecAndVerify(sshClient, fmt.Sprintf("sudo rm %s/%s", destinationDir, svcv2))
|
||||
|
||||
waitForServiceInAddonTest(c, "addon-test-updated", false)
|
||||
waitForReplicationControllerInAddonTest(c, "invalid-addon-test-v1", false)
|
||||
waitForServiceInAddonTest(c, namespace.Name, "addon-test-updated", false)
|
||||
waitForReplicationControllerInAddonTest(c, namespace.Name, "addon-test-v2", false)
|
||||
|
||||
By("verify invalid API addons weren't created")
|
||||
_, err = c.ReplicationControllers(addonNamespace).Get("invalid-addon-test-v1")
|
||||
_, err = c.ReplicationControllers(namespace.Name).Get("invalid-addon-test-v1")
|
||||
Expect(err).To(HaveOccurred())
|
||||
_, err = c.Services(addonNamespace).Get("ivalid-addon-test")
|
||||
_, err = c.ReplicationControllers(defaultNsName).Get("invalid-addon-test-v1")
|
||||
Expect(err).To(HaveOccurred())
|
||||
_, err = c.Services(namespace.Name).Get("ivalid-addon-test")
|
||||
Expect(err).To(HaveOccurred())
|
||||
_, err = c.Services(defaultNsName).Get("ivalid-addon-test")
|
||||
Expect(err).To(HaveOccurred())
|
||||
|
||||
// invalid addons will be deleted by the deferred function
|
||||
})
|
||||
})
|
||||
|
||||
func waitForServiceInAddonTest(c *client.Client, name string, exist bool) {
|
||||
func waitForServiceInAddonTest(c *client.Client, addonNamespace, name string, exist bool) {
|
||||
expectNoError(waitForService(c, addonNamespace, name, exist, addonTestPollInterval, addonTestPollTimeout))
|
||||
}
|
||||
|
||||
func waitForReplicationControllerInAddonTest(c *client.Client, name string, exist bool) {
|
||||
func waitForReplicationControllerInAddonTest(c *client.Client, addonNamespace, name string, exist bool) {
|
||||
expectNoError(waitForReplicationController(c, addonNamespace, name, exist, addonTestPollInterval, addonTestPollTimeout))
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user