Skip to content

Disable upgrades on unsupported kernel (fixes #9290)#9635

Closed
rasa wants to merge 23 commits intosyncthing:mainfrom
rasa:feature/9290-disable-upgrades
Closed

Disable upgrades on unsupported kernel (fixes #9290)#9635
rasa wants to merge 23 commits intosyncthing:mainfrom
rasa:feature/9290-disable-upgrades

Conversation

@rasa
Copy link
Copy Markdown
Member

@rasa rasa commented Aug 7, 2024

Purpose

See #9290. This code uses go/version which was first provided in go1.22. Is that acceptable?

Testing

Tested on Linux, macOS Catalina, and Windows.

Documentation

Do we need to update the docs?

@calmh
Copy link
Copy Markdown
Member

calmh commented Aug 7, 2024

We need to be buildable with Go 1.21 until at least Go 1.23 is released, unfortunately. (But that's soon)

@rasa
Copy link
Copy Markdown
Member Author

rasa commented Aug 7, 2024

We need to be buildable with Go 1.21 until at least Go 1.23 is released, unfortunately. (But that's soon)

@calmh So, do you wanna wait until we bump go.mod to 1.22, or should I backport go/version's functionality? Probably the easiest way would be to simply copy its code locally, and tag it //go:build go1.21.

@calmh
Copy link
Copy Markdown
Member

calmh commented Aug 7, 2024

I didn't check what you're really after but there already is a CompareVersions in the upgrade package, since doing that is part of its core business. It should be mostly applicable.

@rasa
Copy link
Copy Markdown
Member Author

rasa commented Aug 7, 2024

... there already is a CompareVersions ...

@calmh Yeah, I switched to that and it actually works better, as we not only need to check for equality, but allow for a minor version mismatch in the CI env.

@tomasz1986
Copy link
Copy Markdown
Member

When it comes to Windows, I think one potential problem is that everything starting from the first release of Windows 10 is version 10.0 (including Windows 11 and all Servers), so if Go decides to stop supporting Windows 10 (or older versions of Windows 10), then this will require tweaking to accommodate that (as just 10.0 alone will not be enough).

Comment on lines +181 to +184
// If we're running inside a GitHub action, we need to compare the go
// version in compatibility.json with both the current runtime version, and
// the previous one, as build-syncthing.yaml runs our unit tests with
// both versions.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this brings to light the fact that this check is going to be a pain in the ass for anyone wanting to use a different Go version for whatever reason and not planning to distribute Syncthing or care about the compat file. We should probably have this test run only with a specific release tag or something, to make sure we run it but others don't have to.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, I'll add that.

A dev could also tweak compatibility.json to match their env, or even delete the file. Of course, they would need to be careful not to commit their tweak.

Comment on lines +7 to +8
"darwin@go1.23": "11",
"linux@go1.24": "3.10"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this for?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just documentation, so the dev who needs to update this file, won't have to do any research, but simply cut-n-paste.

Copy link
Copy Markdown
Member

@calmh calmh Aug 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mmm let's not do that, it's confusing and how do you know what will be the requirements for Go 1.24? This should be updated at release time, or at least just shortly before.

What we could do to be more flexible is to have a source file with multiple sets of data in it, for example in yaml format:

- runtime: go1.21
  requirements:
    darwin: 20
    windows: 10.0
    ...
- runtime: go1.22
  requirements:
    darwin: 21
    windows: 10.1
    ...

and then we could at build time pick the set based on runtime.Version to create compatibility.json, and then it could be a build failure (for everyone) when there aren't any requirements for the current version. Then you could in principle add things for the future as well, though I think we should be careful about that.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@calmh I implemented your suggestion, so compatibility.json is now generated dynamically from compatibility.yaml based on the current runtime.Version().

@rasa
Copy link
Copy Markdown
Member Author

rasa commented Aug 7, 2024

When it comes to Windows, I think one potential problem is that everything starting from the first release of Windows 10 is version 10.0 (including Windows 11 and all Servers), so if Go decides to stop supporting Windows 10 (or older versions of Windows 10), then this will require tweaking to accommodate that (as just 10.0 alone will not be enough).

That's true, but the patch level should allow us to disable Windows 10 or 2016, when and if that ever happens.

@calmh
Copy link
Copy Markdown
Member

calmh commented Aug 10, 2024

I think we can do a refinement here. We can include the compatibility data directly in the upgrade server response, so it's available in the list of releases that the upgrade code looks at. Then it becomes just one more filtering criteria there, and the upgrade code can simply filter out upgrades that are no longer compatible.

@bt90
Copy link
Copy Markdown
Contributor

bt90 commented Aug 10, 2024

Delivering the metadata via the server would also have the positive side effect that we can continue to intervene. Otherwise, if we make a mistake, we may already have dozens of installations that no longer update automatically.

@rasa
Copy link
Copy Markdown
Member Author

rasa commented Aug 11, 2024

We can include the compatibility data directly in the upgrade server response ...

@calmh Proposed solution:

The release bundle includes a metadata.json that includes the go runtime used to build the release.
The upgrade server includes the compatibility info for all the runtime versions found in compatibility.yaml, so it doesn't need to be release aware.

old commentSomehow the upgrade server will need to know what the `runtime.Version()` of the latest release of syncthing is. We could:
  1. The first time the upgrade server sees a new tag_name in the GitHub API's response, it downloads the release, untars it, and determines the version of go used to compile the release. It caches this result so it only does this once per tag/release.
  2. Immediately restart the upgrade server after a release, making sure it was compiled with the same version of go that was used to compile the release (yuk).
  3. Pass the version on the command line (requires an immediate restart).

Which method would you prefer?

EDIT: Alternatively, the upgrade server could include the compatibility info for all runtime versions, and the upgrading syncthing could determine the runtime of the downloaded release, per below.

The upgrade server would use debug.ReadBuildInfo to determine the version of go that was used to compile a binary.

@calmh
Copy link
Copy Markdown
Member

calmh commented Aug 12, 2024

The point is to reduce the amount of unnecessary downloads the upgrading Syncthing instance needs to do. I suggest the following:

  • The build process creates a compat.json for the runtime used.
  • The release process adds compat.json as a release artifact, similar as all the tar.gz files and the signed checksums, etc.
  • The upgrade server already reads the list of assets for the release and provides a pared-down view of them. It now just also needs to read compat.json and merge it into its response.
  • The upgrading client will get the compat.json for the release together with the list of assets, and can directly compare if this is a release it can upgrade to or not. If not, just skip it same as it would skip a pre-release it's not configured to upgrade to, etc.

rasa added 3 commits August 13, 2024 12:11
1. The build process creates a compatability.json
for the runtime used.
2. The release process adds compatability.json as
a release artifact
3. The upgrade server reads compatability.yaml
and merges it into its /meta.json response.
4. The upgrading client reads the
compatability.json file in the release bundle,
and using the go runtime setting found in it,
looks up the minimum OS versions listed in the
upgrade server's /meta.json response.
5. If the upgrading client's OS version is lower
than the minimum OS version listed, the upgrade
is aborted.
@rasa rasa requested a review from calmh August 13, 2024 20:21
Copy link
Copy Markdown
Member

@calmh calmh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel I'm not sure we're envisioning the same solution. Looking the code I'm not sure of the intention. There's stuff to extract the compat.json data from inside the archive, but I think it should be returned by the upgrade server. There's something like it in the upgrade server response, but it does not seem targeted for the release but a general blob of all the compat data?

I think we should:

  • Generate a compat.json for the current runtime when building Syncthing
  • Have that compat.json be one of the files uploaded as part of the release (this is not in scope for this PR, the release process is separate and I'll deal with it)
  • Have the upgrade server look for (the release specific) compat.json among the assets, when it gets the release metadata from Github, load it, parse it, and add it to the response for that release.
  • Have Syncthing look at this when it decides to try to upgrade or not to a given release, before downloading the archive.

@rasa
Copy link
Copy Markdown
Member Author

rasa commented Aug 14, 2024

I think we should:

  • Generate a compat.json for the current runtime when building Syncthing

@calmh Done.

  • Have that compat.json be one of the files uploaded as part of the release (this is not in scope for this PR, the release process is separate and I'll deal with it)

Gotcha.

  • Have the upgrade server look for (the release specific) compat.json among the assets, when it gets the release metadata from Github, load it, parse it, and add it to the response for that release.

Gotcha, I will implement this, but it will make the code slightly more complex, but in other ways simpler, too.

tl;dr:

I was trying to avoid the upgrade server having to download anything, as it's just another point of failure, and you and other's liked re @bt90's comment:

Delivering the metadata via the server would also have the positive side effect that we can continue to intervene. Otherwise, if we make a mistake, we may already have dozens of installations that no longer update automatically.

So the only way to "intervene" is for the upgrade server to always send the contents of the most recent compatibility.yaml, and not the compat.json contained in the release.

But I agree that the upgrade server could download each compat.json for each active release, and using the runtime value found, extract the most recent values from compatibility.yaml, and add the values found to the meta.json response. We can cache this info, as it never changes.

  • Have Syncthing look at this when it decides to try to upgrade or not to a given release, before downloading the archive.

Agreed! My apologies for not figuring this out sooner, and thank you for your patience.

@calmh
Copy link
Copy Markdown
Member

calmh commented Aug 14, 2024

I think by "intervene" above we meant that we control the upgrade server, so we can change what it does and what it returns if we want, by programming.

@rasa rasa marked this pull request as draft August 14, 2024 13:36
@rasa
Copy link
Copy Markdown
Member Author

rasa commented Aug 17, 2024

@calmh OK, I implemented the changes you requested. The upgrade server simply downloads config.json from GitHub and includes its contents in its meta.json response.

@rasa rasa marked this pull request as ready for review August 17, 2024 01:45
@calmh
Copy link
Copy Markdown
Member

calmh commented Aug 20, 2024

I think I'm failing to communicate here, unfortunately. I do not want a json file in the release archive; I don't want the upgrade server nor Syncthing itself downloading a release archive in order to look for a json file. The compatibility file should be an entirely standalone thing that can be checked before even beginning to download a release archive. I'll take a stab at showing what I mean.

@calmh
Copy link
Copy Markdown
Member

calmh commented Aug 20, 2024

This is what I mean #9656

@rasa
Copy link
Copy Markdown
Member Author

rasa commented Aug 20, 2024

@calmh Your solution is much simpler. Sorry I wasn't able to understand the solution you were envisioning, and thanks for reusing my work in your solution. I will test #9656 soon.

@rasa rasa closed this Aug 20, 2024
@rasa rasa deleted the feature/9290-disable-upgrades branch August 23, 2024 15:38
@st-review st-review added the frozen-due-to-age Issues closed and untouched for a long time, together with being locked for discussion label Aug 20, 2025
@syncthing syncthing locked and limited conversation to collaborators Aug 20, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

frozen-due-to-age Issues closed and untouched for a long time, together with being locked for discussion

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants