Replies: 7 comments 16 replies
-
@brunoapimentel @taylormadore @ben-alkov @ejegrova @slimreaper35 please have a look and leave comments. |
Beta Was this translation helpful? Give feedback.
-
Just to clarify my understanding: what happens if the repo has a module that declares Assuming I got the above right, I believe there could be some corner cases in which the build would fail because some module can't be built with a different version of go than what's in the toolchain (e.g. some new feature was added to a newer go version, and this specific module requires it). My only concern here is that this build error could be hard to troubleshoot, it would be nice to have some guidance to the user. |
Beta Was this translation helpful? Give feedback.
This comment has been hidden.
This comment has been hidden.
-
Checkboxes for approvals are up there ⬆️ |
Beta Was this translation helpful? Give feedback.
-
I think it would make it clearer if we specified in the solutions which environment variables are meant for build time, and which ones are meant for the prefetch. |
Beta Was this translation helpful? Give feedback.
-
There was an issue that popped up recently about Golang 1.21. With Golang 1.21, the
This complicates matters for cachi2 significantly, because what happens with projects which specify their minimum required Go version as let's say 1.20, but any of it dependencies declare 1.21, go would update the However, here's a proposal that should in theory take care of the problem (although I'm not proud of it in any way, because it'd add maintenance burden, although how big?):
|
Beta Was this translation helpful? Give feedback.
-
@brunoapimentel @ejegrova @taylormadore @slimreaper35 are we all in agreement with the approach proposed in this discussion, including the necessary fix proposed in #419 (comment)? If so, please consider an ACK: #419 (reply in thread) |
Beta Was this translation helpful? Give feedback.
-
Go 1.21 toolchains
Go 1.21 introduced a new feature called toolchains which allow usage of different Go toolchains to be used for different modules simultaneously instead of the default bundled Go toolchain (i.e. the bundled
go
binary). The toolchain feature is enabled by thetoolchain
keyword which can either be in thego.mod
file or thego.work
(workspaces) file [1].Important things to keep in mind
go
line ingo.mod
denotes the minimum required Go version to use to compile a moduletoolchain
line denotes the suggested go toolchain version to be usedtoolchain
keyword if that version is less than the bundled Go's versionControlling toolchain behaviour
When it comes to toolchains Go's behaviour depends primarily on the
GOTOOLCHAIN
environment variable which can be set in a few different places. Go's lookup for environment variables goes as follows:GOTOOLCHAIN=<selector>
go )$GOENV
variable is checked for user's environment configuration file [3] (e.g. it may point to/home/user/.config/go/env
)$GOROOT/go.env
is checked; this is the global installation environment file and its location is platform-dependentGOTOOLCHAIN=local
is used as the defaultGOTOOLCHAIN
selector valuesThe selector is what determines Go's behaviour when it comes to respecting the
toolchain
setting ingo.mod
orgo.work
files.The value
GOTOOLCHAIN
accepts could be roughly described asGOTOOLCHAIN=<selector>(+<selector>)?
where<selector>
can be one of:local
- always run the default bundled toolchain; it's often paired with another selector, e.g.local+path
which means that whenever possible Go should prefer the alternative toolchainname
- always runs toolchain with that specific name; Go will look into itsPATH
and if such toolchain binary is missing it'll download it from the internet)path
- same asname
but DOES NOT download the toolchain if Go couldn't find it inPATH
; in its bare form it's a shortcut forlocal+path
auto
- shortcut forlocal+auto
, i.e download new toolchains as neededFor our purposes though we should only ever be concerned with
GOTOOLCHAIN=auto
and pre-fetch whatever toolchain version Go encounters during processing.Downloading toolchains
Luckily toolchains are nothing else than a gomod dependency which is cached like any other Go dependency. Unfortunately for us:
GOSUMDB
variable (see Other Affecting Variables)Go always performs checksum validation on toolchains
Starting with the checksums first as the downloads may be more clear later. So
GOSUMDB
was mentioned to control the checksum validation, so ifGOSUMDB=off
(Fedora default) what happens with eithergo mod download
orgo build
while suggesting atoolchain
to be used is that Go goes and fetches the toolchain from the internet, but then fails the checksum verification and so it refuses to proceed further with anything (e.g. downloading other dependencies or building anything) (see Fetching toolchain with checksum disabled). Therefore, to support Go 1.21 toolchainsGOSUMDB
needs to be always set explicitly by us and point to a valid checksum server as long asGOTOOLCHAIN
is set to anything butlocal
which forces Go to always use the bundled toolchain (see GOTOOLCHAIN selectors).Note there's also the
GONOSUMDB
variable (see Other Affecting Variables) which might give the impression that if we specify a pattern for toolchains, we should be able to either download toolchains insecurely (not that we want to!). Turns out toolchains are exempt from any checksum verification settings [5] and so we need to account for this.Go always downloads the toolchain
With setting
GOSUMDB
explicitly we only takes care of a part of the puzzle, because it only ensures that we'll pre-fetch a toolchain correctly, but will not make sure in any way that such toolchain is actually going to be used during the project's container build or even that such a container build would succeed in general, because Go will ultimately try to download it again (except it won't, because builds may be hermetic).To solve the fetch/cache/use issue, one needs to can set the
GOPROXY
variable as well so that it points to the cache on the local file system [6]. That way Go will continue using its original logic and will fetch the toolchain from the pre-fetched dependencies cache (into the same cache btw, fill in your favourite meme...) instead of pulling it from the internet. So far so good, back to checksums. So, we already know that Go does checksum verification on downloaded toolchains by pointing it to a valid checksum server, so how do we get them verified during hermetic builds? Turns out, Go only performs toolchain checksum verification when pulling from the internet, but not when using thefile://
URL scheme insideGOPROXY
\o/. In other words, Go only tries to verify remote resources, but somehow trusts local resources (see Fetching with GOMODCACHE and GOPROXY).And now the "Crème de la crème" of Go toolchain downloads and verification. Remember the section intro about Go seemingly never looking into
GOMODCACHE
for toolchains? It actually does, but guess what, it needs to verify the toolchains checksum (see Use toolchain with an offline cache.Example invocations
This section serves just as a reference to various invocations of Go forcing the toolchain usage.
Fetching toolchain with checksum disabled
Use toolchain with an offline cache
Fetching with GOMODCACHE and GOPROXY
Proposed solutions
1. Ignore toolchains alltogether
Since we only need to control Go's behaviour using environment variables, these are the necessary settings:
Dependency pre-fetch Go configuration
Set the following on top of our existing env settings:
GOTOOLCHAIN=local
User container build Go configuration
Set the following on top of our existing env settings:
GOTOOLCHAIN=local
Advantages:
Disadvantages:
2. Pre-fetch ALL affecting toolchains
Let Go download all toolchains it may need (for all modules). Since we only need to control Go's behaviour using environment variables, these are the necessary settings:
Dependency pre-fetch Go configuration
Set the following on top of our existing env settings:
GOTOOLCHAIN=auto
GOSUMDB=sum.golang.org
(we already set this one...)User container build Go configuration
Set the following on top of our existing env settings:
GOTOOLCHAIN=auto
GOSUMDB=off
GOPROXY=file://${GOMODCACHE}/cache/download
Advantages:
go 1.21
and thentoolchain 1.24.0
(provided 1.24 is out) then the 1.21 binary will be able to download a newer toolchain and use it for compiling; however, this whole idea crumbles like a house of cards when it comes to features that include new keywordsDisadvantages:
go
andtoolchain
versions set - but this is a user problem in general though, not cachi2's (mentioning it just in case)References
[1] standard Go rules apply when it comes to evaluating modules vs workspaces
[2] there are some strict rules when it comes to versioning of suggested toolchains which applies transitively too across all dependencies, but luckily that's not our problem to figure out! As noted in https://go.dev/doc/toolchain:
[3] the
go.env
file has a simpleKEY=VALUE
structure[4] Fedora repackaged go.env:
Go's default vendored go.env :
[5] Snippet from the docs:
[6] Snippet from the docs:
Resources
https://go.dev/doc/toolchain
https://go.dev/ref/mod#environment-variables
Apendix
Other affecting variables
There are a couple of other variables that further affect module checksum verification and the download process:
GOSUMDB
- Identifies the name of the checksum database to use and optionally its public key and URL, normally defaults tosum.golang.org
, on Fedora it's"off"
[5]GONOSUMDB
- Comma-separated list of glob patterns of module path prefixes for which the go should not verify checksums using the checksum databaseGOPROXY
- a (comma separated) list of URLs pointing to module proxies (instead of downloading modules directly from VCS); it supports the following URL schemes:https/http
file
GOPRIVATE
- list of glob patterns of module path prefixes that should be considered private. Acts as a default value forGONOPROXY
andGONOSUMDB
. This is useful when no proxy serves private modulesBeta Was this translation helpful? Give feedback.
All reactions