fix: upgrade from Lilac on k8s

When upgrading from Lilac, all services break with the following error:

    Service "***" is invalid: spec.ports[0].nodePort: Forbidden: may not be used when `type` is 'ClusterIP'

Upgrading deployments fails as well:

    Deployment.apps "***" is invalid: spec.selector: Invalid value: v1.LabelSelector{MatchLabels:map[string]string{"app.kubernetes.io/instance":"openedx-********", "app.kubernetes.io/managed-by":"tutor", "app.kubernetes.io/name":"***", "app.kubernetes.io/part-of":"openedx"}, MatchExpressions:[]v1.LabelSelectorRequirement(nil)}: field is immutable

That's because deployments and services need to be deleted as part of the Maple
upgrade. So that's what we do as part of `tutor k8s upgrade --from=lilac`. And
we take the opportunity to:

1. Run upgrade as part of quickstart, when necessary.
2. Default to lilac during `tutor k8s upgrade`.

Close #551.
This commit is contained in:
Régis Behmo 2022-01-03 08:17:42 +01:00 committed by Régis Behmo
parent 43d5da83e4
commit 70dbfcb6fb
2 changed files with 42 additions and 23 deletions

View File

@ -4,6 +4,10 @@ Note: Breaking changes between versions are indicated by "💥".
## Unreleased
- [Bugfix] Ensure that ``tutor k8s upgrade`` is run during ``tutor k8s quickstart``, when necessary.
- [Bugfix] By default, upgrade from Lilac and not Koa during ``tutor k8s upgrade``.
- [Bugfix] Fix upgrading from Lilac to Maple on Kubernetes by deleting deployments and services.
## v13.0.3 (2022-01-04)
- [Security] Upgrade Django to 3.2.11 in edx-platform.
@ -15,7 +19,7 @@ Note: Breaking changes between versions are indicated by "💥".
## v13.0.1 (2021-12-20)
- [Fix] Missing requirements file in `pip install tutor[full]`.
- [Bugfix] Missing requirements file in `pip install tutor[full]`.
## v13.0.0 (2021-12-20)

View File

@ -157,12 +157,17 @@ def k8s() -> None:
@click.option("-I", "--non-interactive", is_flag=True, help="Run non-interactively")
@click.pass_context
def quickstart(context: click.Context, non_interactive: bool) -> None:
if tutor_env.needs_major_upgrade(context.obj.root):
click.echo(fmt.title("Upgrading from an older release"))
context.invoke(
upgrade,
from_version=tutor_env.current_release(context.obj.root),
)
click.echo(fmt.title("Interactive platform configuration"))
context.invoke(
config_save_command,
interactive=(not non_interactive),
set_vars=[],
unset_vars=[],
)
config = tutor_config.load(context.obj.root)
if not config["ENABLE_WEB_PROXY"]:
@ -175,8 +180,6 @@ def quickstart(context: click.Context, non_interactive: bool) -> None:
" traffic to the caddy service. See the Kubernetes section in the Tutor documentation for more"
" information."
)
click.echo(fmt.title("Updating the current environment"))
tutor_env.save(context.obj.root, config)
click.echo(fmt.title("Starting the platform"))
context.invoke(start)
click.echo(fmt.title("Database creation and migrations"))
@ -264,25 +267,29 @@ def start(context: Context, names: List[str]) -> None:
def stop(context: Context, names: List[str]) -> None:
config = tutor_config.load(context.root)
names = names or ["all"]
resource_types = "deployments,services,configmaps,jobs"
not_lb_selector = "app.kubernetes.io/component!=loadbalancer"
for name in names:
if name == "all":
utils.kubectl(
"delete",
*resource_selector(config, not_lb_selector),
resource_types,
)
delete_resources(config)
else:
utils.kubectl(
"delete",
*resource_selector(
config,
not_lb_selector,
"app.kubernetes.io/name={}".format(name),
),
resource_types,
)
delete_resources(config, name=name)
def delete_resources(
config: Config, resources: Optional[List[str]] = None, name: Optional[str] = None
) -> None:
"""
Delete resources by type and name.
The load balancer is never deleted.
"""
resources = resources or ["deployments", "services", "configmaps", "jobs"]
not_lb_selector = "app.kubernetes.io/component!=loadbalancer"
name_selector = [f"app.kubernetes.io/name={name}"] if name else []
utils.kubectl(
"delete",
*resource_selector(config, not_lb_selector, *name_selector),
",".join(resources),
)
@click.command(help="Reboot an existing platform")
@ -438,7 +445,7 @@ def wait(context: Context, name: str) -> None:
@click.option(
"--from",
"from_version",
default="koa",
default="lilac",
type=click.Choice(["ironwood", "juniper", "koa", "lilac"]),
)
@click.pass_obj
@ -459,7 +466,7 @@ def upgrade(context: Context, from_version: str) -> None:
running_version = "lilac"
if running_version == "lilac":
# Nothing to do here
upgrade_from_lilac(config)
running_version = "maple"
@ -530,6 +537,14 @@ your MongoDb cluster from v3.6 to v4.0. You should run something similar to:
fmt.echo_info(message)
def upgrade_from_lilac(config: Config) -> None:
fmt.echo_info(
"All Kubernetes services and deployments need to be deleted during "
"upgrade from Lilac to Maple"
)
delete_resources(config, resources=["deployments", "services"])
def kubectl_exec(
config: Config, service: str, command: str, attach: bool = False
) -> int: