-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(app): case insensitive project and namespace (#858)
enhancement(app): add the ability to do simple migrations of the jupyterserver k8s resources
- Loading branch information
Showing
12 changed files
with
251 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# Resource Schema Migrations | ||
|
||
In most cases the notebooks code is compatible with old versions of the JuyterServer CRD and the values of existing | ||
JupyterServer resources. However sometimes changes are made that require all existing JupyterServer resources | ||
to be adjusted and migrated to avoid interruption of users' work. | ||
|
||
The migrations here are python scripts that utilize the python k8s SDK and modify | ||
existing resources in the cluster so that they properly function after a new version of the notebook | ||
service has been released. | ||
|
||
The migrations run in an `init` container before the notebook service starts. Since the notebook service | ||
is a `StatefulSet` the migrations will only truly run in the first pod of the deployment - i.e. the one whose | ||
name ends with `-0`. | ||
|
||
The migrations are made to be idempotent - which means that running a single migration more than once | ||
will not provide different results. I.e. if you run the same migration twice, the second time | ||
around the changes will not be applied to any pods because the label of the jupyterserver resources | ||
will prevent applying a migration to the wrong servers or to servers that have already been patched. | ||
|
||
**WARNING**: The migrations do not support downgrading the notebook service. In the case | ||
where it is required to downgrade the notebook service and migrations were applied by | ||
the upgrades that will be rolled back, then all active sessions should be deleted prior to | ||
the downgrade to avoid problems. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import os | ||
|
||
PAGINATION_LIMIT = 50 | ||
POD_NAME = os.environ.get("POD_NAME") | ||
SCHEMA_VERSION_LABEL_NAME = "schemaVersion" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
from kubernetes import client | ||
from kubernetes import config as k8s_config | ||
|
||
from run_all import parse_args | ||
import config | ||
|
||
|
||
def adjust_annotations(args): | ||
k8s_config.load_config() | ||
k8s_api = client.CustomObjectsApi(client.ApiClient()) | ||
|
||
print( | ||
"Running migration 1: Patching projecName and namespace " | ||
"in annotations to be lowercarse." | ||
) | ||
|
||
annotation_keys = [ | ||
f"{args.prefix}projectName", | ||
f"{args.prefix}namespace", | ||
] | ||
next_page = "" | ||
dry_run_prefix = "DRY RUN: " if args.dry_run else "" | ||
|
||
while (True): | ||
jss = k8s_api.list_namespaced_custom_object( | ||
version=args.api_version, | ||
namespace=args.namespace, | ||
plural=args.plural, | ||
limit=config.PAGINATION_LIMIT, | ||
group=args.group, | ||
_continue=next_page, | ||
# select only servers that do not have a schema version label | ||
label_selector=f"!{args.prefix}{config.SCHEMA_VERSION_LABEL_NAME}", | ||
) | ||
|
||
for js in jss["items"]: | ||
annotation_patches = {} | ||
js_name = js["metadata"]["name"] | ||
print(f"Checking session {js_name}") | ||
for annotation_key in annotation_keys: | ||
annotations = js["metadata"]["annotations"] | ||
try: | ||
annotation_val = annotations[annotation_key] | ||
except KeyError: | ||
print(f"Annotation {annotation_key} not found in {js_name}.") | ||
continue | ||
if annotation_val != annotation_val.lower(): | ||
print( | ||
f"{dry_run_prefix}Patching {js_name} for annotation {annotation_key}: " | ||
f"{annotation_val} --> {annotation_val.lower()}" | ||
) | ||
annotation_patches[annotation_key] = annotation_val.lower() | ||
else: | ||
print(f"No need to patch {js_name} for annotation {annotation_key}") | ||
|
||
patch = { | ||
"metadata": { | ||
"labels": {f"{args.prefix}{config.SCHEMA_VERSION_LABEL_NAME}": "1"}, | ||
} | ||
} | ||
if len(annotation_patches.keys()) > 0: | ||
patch["metadata"]["annotations"] = annotation_patches | ||
if not args.dry_run: | ||
k8s_api.patch_namespaced_custom_object( | ||
group=args.group, | ||
version=args.api_version, | ||
namespace=args.namespace, | ||
plural=args.plural, | ||
name=js_name, | ||
body=patch, | ||
) | ||
|
||
next_page = jss["metadata"].get("continue") | ||
if next_page is None or next_page == "": | ||
break | ||
|
||
|
||
if __name__ == "__main__": | ||
args = parse_args() | ||
adjust_annotations(args) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import argparse | ||
import os | ||
import re | ||
|
||
import migration_1 | ||
import config | ||
|
||
|
||
def parse_args(): | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument( | ||
"-n", | ||
"--namespace", | ||
required=False, | ||
default=os.environ.get("K8S_NAMESPACE", "default"), | ||
type=str, | ||
help="The k8s namespace where to run.", | ||
) | ||
parser.add_argument( | ||
"-d", | ||
"--dry-run", | ||
action="store_true", | ||
required=False, | ||
help="If set, no changes are made at all.", | ||
) | ||
parser.add_argument( | ||
"-g", | ||
"--group", | ||
type=str, | ||
required=False, | ||
default=os.environ.get("CRD_GROUP", "amalthea.dev"), | ||
help="The group for the jupyterserver CRD.", | ||
) | ||
parser.add_argument( | ||
"-a", | ||
"--api-version", | ||
type=str, | ||
required=False, | ||
default=os.environ.get("CRD_VERSION", "v1alpha1"), | ||
help="The api version for the jupyterserver CRD.", | ||
) | ||
parser.add_argument( | ||
"-p", | ||
"--plural", | ||
type=str, | ||
required=False, | ||
default=os.environ.get("CRD_PLURAL", "jupyterservers"), | ||
help="The plural name for the jupyterserver CRD.", | ||
) | ||
parser.add_argument( | ||
"--prefix", | ||
type=str, | ||
required=False, | ||
default="renku.io/", | ||
help="The renku k8s annotation prefix.", | ||
) | ||
args = parser.parse_args() | ||
return args | ||
|
||
|
||
def run_all(args): | ||
# Run all migrations in order | ||
print("Starting k8s resource migrations.") | ||
migration_1.adjust_annotations(args) | ||
|
||
|
||
if __name__ == "__main__": | ||
if config.POD_NAME is not None and re.match(r".*-0$", config.POD_NAME) is not None: | ||
# This is the first pod in the deployment - only that one will run the migrations | ||
args = parse_args() | ||
run_all(args) |