Skip to content

Commit 387cfba

Browse files
committed
infra migrate-from-legacy: Utilize migration checkpoints
1 parent aa1c70c commit 387cfba

File tree

6 files changed

+229
-17
lines changed

6 files changed

+229
-17
lines changed

docs/getting-started/migrating-from-legacy-template.md

Lines changed: 102 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,35 @@ Platform CLI you'll need to convert the old file into the new format.
88
The Platform CLI provides commands for doing this migration, though the exact
99
steps you need to take will vary depending on what templates you have installed.
1010

11-
> [!IMPORTANT]
12-
>
13-
> If you running a very old (pre-summer 2024) version of a template
14-
> (particularly `template-infra`), reach out to the platform team for some
15-
> guidance.
16-
1711
## template-infra
1812

13+
The switch to Platform CLI happened with `v0.15.0`. If you are running a version
14+
earlier than this, you'll need to migrate things.
15+
16+
One way to figure out what version of `template-infra` your project is using is
17+
to run, at the root of your project:
18+
19+
```sh
20+
nava-platform infra info --template-uri gh:navapbc/template-infra .
21+
```
22+
23+
Look for the "Closest upstream version" value. If it is "Unknown", reach out to
24+
the Platform team for guidance.
25+
26+
If the value is pre-`v0.12.0`, you may want to approach the update in smaller
27+
steps than jumping directly to `v0.15.0`. You can use the Platform CLI to do
28+
these updates as well, see [Migrate in smaller
29+
steps](#migrate-in-smaller-steps).
30+
31+
As always, read the [release
32+
notes](https://github.com/navapbc/template-infra/releases) for each version
33+
between your current one and your ultimate target. This process does not
34+
eliminate the need to apply the state changes/manual migration steps, it just
35+
updates the code. See [Version callouts](#version-callouts) below for some
36+
particular things to consider.
37+
38+
### Migrate to latest
39+
1940
To transform the old `.template-version` file into the new format, run:
2041

2142
```sh
@@ -26,7 +47,9 @@ This will result in a `.template-infra/` directory with a number of files inside
2647
of it. Check that the `app-<APP_NAME>.yml` files all correspond to proper
2748
applications. Remove any that don't and update the commit.
2849

29-
Now perform the update, with:
50+
This gets your project into a state that Platform CLI can understand.
51+
52+
Now perform the actual template update, with:
3053

3154
```sh
3255
nava-platform infra update .
@@ -46,6 +69,78 @@ nava-platform infra update-app --all .
4669
Likely you'll hit merge conflicts for each app as well, resolve those, commit,
4770
and move on to the next app, until you've done them all.
4871

72+
See [the docs on updating in general](../updating.md) for more details on running
73+
updates.
74+
75+
### Migrate in smaller steps
76+
77+
This is similar to the previous section, so read that first.
78+
79+
1. Run the `migrate-from-legacy` command as stated in previous section. This
80+
gets you into the Platform CLI ecosystem.
81+
2. Then decide which version of `template-infra` you want to update to,
82+
represented by `v0.x.x` in the following example:
83+
```sh
84+
nava-platform infra update --version platform-cli-migration/v0.x.x .
85+
```
86+
3. Follow update guidance as discussed in previous section.
87+
4. Do steps 2-3 over and over, jumping versions as you see fit until you hit
88+
`v0.15.0`.
89+
5. Once on `v0.15.0`, run a final update to get to the latest release (or to
90+
whatever post-`v0.15.0` version you want):
91+
```sh
92+
nava-platform infra update [--version vA.B.C] .
93+
```
94+
95+
### Version callouts
96+
97+
No substitute for reading the [release
98+
notes](https://github.com/navapbc/template-infra/releases), but here are a few
99+
points to consider when deciding what version to update to if you are
100+
significantly behind the latest:
101+
102+
- A Feature Flags module, backed by AWS Evidently, was added in
103+
[v0.5.0](https://github.com/navapbc/template-infra/releases/tag/v0.5.0) and
104+
removed in
105+
[v0.13.0](https://github.com/navapbc/template-infra/releases/tag/v0.13.0).
106+
- If you are coming from pre-v0.5.0, you can delete the feature flag module
107+
as you move past v0.5.0, or just ignore/don't change anything about it and
108+
it will get cleaned up once you are post-v0.13.0.
109+
- [v0.9.0](https://github.com/navapbc/template-infra/releases/tag/v0.9.0) moved
110+
account mapping to each environment config file, then
111+
[v0.11.0](https://github.com/navapbc/template-infra/releases/tag/v0.11.0)
112+
removed it from each environment config file and moved it to the network config.
113+
- If you are pre-v0.9.0, you may want to consider jumping to v0.11.x+ to
114+
avoid dealing with moving things multiple times.
115+
116+
Misc. others:
117+
118+
- [v0.11.0](https://github.com/navapbc/template-infra/releases/tag/v0.11.0)
119+
- Starts pinning specific Terraform version in CI/CD
120+
- [v0.10.0](https://github.com/navapbc/template-infra/releases/tag/v0.10.0)
121+
- DB changes: PostgreSQL version update to 16.2 and DB schema name hardcoded
122+
to `app`
123+
- [v0.9.0](https://github.com/navapbc/template-infra/releases/tag/v0.9.0)
124+
- Requires Terraform 1.8.x (previous requirement was just >=1.4, more or less)
125+
- Changes the way secrets are defined
126+
- [v0.7.0](https://github.com/navapbc/template-infra/releases/tag/v0.7.0)
127+
- Minor state migration needed
128+
- [v0.6.0](https://github.com/navapbc/template-infra/releases/tag/v0.6.0)
129+
- Networking changes likely requiring hours of downtime to apply
130+
131+
### Post-migration
132+
133+
After completing the migration, you may want to see what results from
134+
re-applying, more holistically, the latest (or your ultimate target) version of
135+
the template to the project:
136+
137+
```sh
138+
nava-platform infra update --force [--version vA.B.C] .
139+
```
140+
141+
This discards some of the "smart" logic of a regular update and might catch some
142+
things that were missed while trying to be smarter.
143+
49144
## Application templates
50145

51146
These are historically less standard, so you'll have to provide a little more

docs/updating.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@ Conflicts on Update](./avoiding-conflicts-on-update.md).
2424
nava-platform infra update .
2525
```
2626

27-
This can often run into merge conflicts that need resolved manually though. The
28-
tool will provide some guidance if this happens. But you can also approach the
29-
update in the separate pieces yourself, first updating the infrastructure base
30-
with:
27+
This will attempt to update the "base" template then each "app" instance in
28+
sequence. This can often run into merge conflicts that need resolved manually.
29+
The tool will provide some guidance if this happens.
30+
31+
But you can also approach the update in the separate pieces yourself, first
32+
updating the infrastructure base with:
3133

3234
```sh
3335
nava-platform infra update-base .

nava/platform/cli/commands/infra/migrate_from_legacy_command.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ def _migrate_from_legacy(
3535
new_version_answers_file_name="base.yml",
3636
extra_answers=lambda _: (base_project_config_answers | {"template": "base"}),
3737
)
38-
base_migrate.migrate_from_legacy(preserve_legacy_file=True, commit=commit)
38+
base_migrate.migrate_from_legacy(
39+
preserve_legacy_file=True, commit=commit, use_migration_tags=True
40+
)
3941

4042
for app_name in infra_project.app_names_possible:
4143
app_answers = {"app_name": app_name, "template": "app"}
@@ -48,7 +50,9 @@ def _migrate_from_legacy(
4850
new_version_answers_file_name=f"app-{app_name}.yml",
4951
extra_answers=lambda _: app_answers, # noqa: B023
5052
)
51-
app_migrate.migrate_from_legacy(preserve_legacy_file=True, commit=commit)
53+
app_migrate.migrate_from_legacy(
54+
preserve_legacy_file=True, commit=commit, use_migration_tags=True
55+
)
5256

5357
# remove the old file once we are done with it
5458
ctx.console.print(f"Deleting legacy file ({base_migrate.legacy_version_file_path()})")

nava/platform/projects/migrate_from_legacy_template.py

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from nava.platform.get_template_name_from_uri import get_template_name_from_uri
1010
from nava.platform.projects.project import Project
1111
from nava.platform.types import RelativePath
12+
from nava.platform.util.git import GitProject
1213

1314

1415
@dataclass
@@ -61,7 +62,12 @@ def answers_file_rel(self) -> RelativePath:
6162
def answers_file(self) -> Path:
6263
return self.project.dir / self.answers_file_rel()
6364

64-
def migrate_from_legacy(self, preserve_legacy_file: bool = False, commit: bool = False) -> None:
65+
def migrate_from_legacy(
66+
self,
67+
preserve_legacy_file: bool = False,
68+
commit: bool = False,
69+
use_migration_tags: bool = False,
70+
) -> None:
6571
if not self.has_legacy_version_file:
6672
raise ValueError(
6773
f"No legacy version file found (looking for {self.legacy_version_file_path()})."
@@ -74,10 +80,23 @@ def migrate_from_legacy(self, preserve_legacy_file: bool = False, commit: bool =
7480
if not self.project_state_dir().exists():
7581
self.project_state_dir().mkdir()
7682

77-
template_version = self.legacy_version_file_path().read_text()
78-
short_version = template_version[:7]
83+
template_version = self.legacy_version_file_path().read_text().strip()
84+
85+
ref = template_version
86+
if use_migration_tags:
87+
with GitProject.clone_if_necessary(self.origin_template_uri) as template_git:
88+
ref, perfect_match = get_closest_migration_tag(template_git, template_version)
89+
90+
if not ref:
91+
raise ValueError("Issue finding suitable migration point")
92+
93+
if not perfect_match:
94+
self.ctx.console.warning.print(
95+
f"Couldn't find a perfect match to the current commit, using closest tagged version '{ref}' which is slightly older."
96+
)
97+
7998
common_answers = {
80-
"_commit": short_version,
99+
"_commit": ref,
81100
# Copier requires this to be set to a valid template path, and that template git project
82101
# needs to have _commit as a valid commit hash
83102
# If _src_path is not set, run_update will raise
@@ -111,3 +130,57 @@ def _extra_answers(self: Self) -> dict[str, str]:
111130
return self.extra_answers(self)
112131

113132
return {}
133+
134+
135+
def get_closest_tag_before_commit(git: GitProject, commit: str) -> str:
136+
"""Find nearest tag before given commit."""
137+
from nava.platform.cli.commands.infra.info_command import get_version
138+
139+
closest_tag = git.get_closest_tag(commit)
140+
if not closest_tag:
141+
raise Exception(f"Can't find closest tag for {commit}")
142+
143+
closest_version = get_version(closest_tag)
144+
if not closest_version:
145+
raise Exception(f"Can't determine version from tag {closest_tag}")
146+
147+
return "v" + closest_version.base_version
148+
149+
150+
MIGRATION_TAG_PREFIX = "platform-cli-migration/"
151+
152+
153+
def get_closest_migration_tag(git: GitProject, commit: str) -> tuple[str, bool]:
154+
"""Find nearest migration tag before given commit."""
155+
from nava.platform.cli.commands.infra.info_command import get_version
156+
157+
closest_tag = get_closest_tag_before_commit(git, commit)
158+
migration_tags = git.get_tags("--list", f"{MIGRATION_TAG_PREFIX}*")
159+
if not migration_tags:
160+
raise Exception("Can't find migration tags")
161+
162+
closest_version = get_version(closest_tag)
163+
if not closest_version:
164+
raise Exception(f"Can't determine version from {closest_tag}")
165+
166+
candidates = []
167+
168+
for migration_tag in migration_tags:
169+
migration_version = get_version(
170+
migration_tag.removeprefix(MIGRATION_TAG_PREFIX).removeprefix("v")
171+
)
172+
173+
if not migration_version:
174+
raise Exception(f"Can't determine migration version from {migration_tag}")
175+
176+
if closest_version == migration_version:
177+
return migration_tag, True
178+
179+
if migration_version < closest_version:
180+
candidates.append(migration_version)
181+
182+
if candidates:
183+
closest_migration_version = sorted(candidates, reverse=True)[0]
184+
return MIGRATION_TAG_PREFIX + "v" + str(closest_migration_version), False
185+
186+
raise Exception(f"Can't find matching migration version for {closest_tag}")
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/usr/bin/env bash
2+
3+
set -xeuo pipefail
4+
5+
#######################
6+
# Setup
7+
#######################
8+
9+
source "$(realpath "$(dirname "${BASH_SOURCE[0]}")")/common.sh"
10+
11+
init_project_dir
12+
13+
# checkout a project before it was migrated to test against
14+
pushd "${PROJECT_DIR}"
15+
git remote add origin https://github.com/navapbc/platform-test
16+
# just before the Copier files were added in
17+
#
18+
# https://github.com/navapbc/platform-test/commit/7807c81d67328b9c23eebf73d2c6d0801795e2d0
19+
git fetch origin --depth=1 bb2e4a7b064c4f78eb8008e02d8f984a9b588209
20+
git checkout FETCH_HEAD
21+
popd
22+
23+
expected_migration_point=platform-cli-migration/v0.12.4
24+
25+
#######################
26+
# Test
27+
#######################
28+
29+
$CMD infra migrate-from-legacy --commit "${PROJECT_DIR}"
30+
31+
grep "_commit: ${expected_migration_point}" "${PROJECT_DIR}"/.template-infra/*.yml

tests/conftest.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,13 @@ def infra_template_no_tags(
108108
def infra_template(infra_template_no_tags: InfraTemplateWritable) -> InfraTemplateWritable:
109109
template = infra_template_no_tags
110110
template.git_project.tag("v0.0.0")
111+
112+
template.git_project.checkout("-b", "migration-tag")
113+
(template.template_dir / "migration-test.txt").write_text("foo")
114+
template.git_project.commit_all("Migration checkpoint")
115+
template.git_project.tag("platform-cli-migration/v0.0.0")
116+
template.git_project.checkout("main")
117+
111118
return template
112119

113120

0 commit comments

Comments
 (0)