Repository navigation
testing: use $GOTMPDIR for temporary files when set #61585
Description
Outline
This started as a bug report, however this appears to be a greater change which has a far reaching impact. The use case is regarding devices under restrictive security profiles which require specific directories to be used for specific purposes.
Due to the nature of current security profiles, when generating unit test data which requires a file system usage, the TMPDIR usage is causing the file to become locked by antivirus or other security applications. We can create a security profile which allows for an exception for golang application files (based on directory). This would require an override to TMPDIR or TEMPDIR env vars with the value from GOTMPDIR.
We can bypass this restriction by using a VM on windows (WSL or otherwise) which is unrestricted within the VM. However, this is severely sub-optimal.
What version of Go are you using (go version)?
$ go version go version go1.20.4 windows/amd64
Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (go env)?
go env Output
$ go env set GO111MODULE= set GOARCH=amd64 set GOBIN= set GOCACHE=C:\Dev\GO_ENV\CACHE set GOENV=C:\Users\anonymous\AppData\Roaming\go\env set GOEXE=.exe set GOEXPERIMENT= set GOFLAGS= set GOHOSTARCH=amd64 set GOHOSTOS=windows set GOINSECURE= set GOMODCACHE=C:\Users\anonymous\go\pkg\mod set GONOPROXY=none set GOOS=windows set GOPATH=C:\Users\anonymous\go set GOROOT=C:\Dev\GO set GOSUMDB=sum.golang.org set GOTMPDIR=C:\Dev\GoTemp set GOTOOLDIR=C:\Dev\GO\pkg\tool\windows_amd64 set GOVCS= set GOVERSION=go1.20.4 set GCCGO=gccgo set GOAMD64=v1 set AR=ar set CC=gcc set CXX=g++ set CGO_ENABLED=1 set GOWORK= set CGO_CFLAGS=-O2 -g set CGO_CPPFLAGS= set CGO_CXXFLAGS=-O2 -g set CGO_FFLAGS=-O2 -g set CGO_LDFLAGS=-O2 -g set PKG_CONFIG=pkg-config set GOGCCFLAGS=-m64 -mthreads -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=C:\Dev\GoTemp\go-build3481635822=/tmp/go-build -gno-record-gcc-switches
What did you do?
Using GoLang across many different environments.
func TestUserStore_writeRead(t *testing.T) {
t.Parallel()
dir := os.TempDir()
dir := t.TempDir()
}dir = TMP/TEMP system variable in both cases.
What did you expect to see?
Allow for GOTMPDIR to override OS setting for TMPDIR if set. This allows us to use TMPDIR for all other operations while enabling golang to adhere to security posture on development machines which are windows based.
What did you see instead?
TMP / TEMP dir setting within Windows ENV VAR.
Activity
ianlancetaylor commentedon Jul 25, 2023
Some points of clarification to make sure that I understand the proposal.
cmd/go already recognizes
GOTMPDIRas the place to put build artifacts.It sounds like you are only talking about Windows.
On Windows, we currently call
GetTempPath2Wto get the temporary directory.It sounds like you are saying that on Windows we should change all Go programs to check
GOTMPDIRand use it as the temporary directory, if it is set.What is the advantage of doing that over just changing the
TMPorTEMPenvironment variable? Either way the user has to set an environment variable.[-]proposal: affected/package: os & testing [/-][+]proposal: os: change default temporary directory on Windows[/+]ianlancetaylor commentedon Jul 25, 2023
CC @golang/windows
UnaffiliatedCode commentedon Jul 26, 2023
Use Case
With corporate devices there are device level posture requirements which state that tmp/temp environment variables must be specific values/locked. Since I am only looking to override GoLangs usage of tmp/temp. GoLang, already has an artifact path and override for this purpose. There are sections within our codebase with use the following code (for testing purposes):
This problem is an extension of the problem where build artifacts were being scanned by AV, blocking GoLang from successfully deleting the artifacts. I was able to get past this with a simple GO Configuration change to an established directory which is only used for my GoLang work. I was able to update the AV profile to match.
Why GOTMPDIR
The GOTMPDIR env is already established as an override for build artifacts and other temporary files, which are intended to be cleared after usage. The pattern used to generate the files does not conflict with existing "build artifacts" pattern.
GetTempPath2
Usage of GetTempPath2 would replicate the current results.
Why not Change TMP/TEMP variables
This would redirect all applications which generate items within the TMP / TEMP directory (such as Microsoft Office applications) to a directory which natively bypasses the AV.
This would create a larger security vulnerability on the monitored device. Follow-up Impact on the device would be for security profiles to remove configured "safe" directories (causing build artifacts to fail at destruction stage). Which makes Windows based development not viable on corporate devices.
I will however note that there are some other options which could resolve this issue:
Develop within a VM
This is a viable option, however it does not resolve the issue. Currently, I'll be ignoring the tests locally since the remote pipelines are still viable test hosts.
arp242 commentedon Jul 27, 2023
I set
GOTMPDIRon my system to/extra/go, which is an second SSD rather than the default (in memory) /tmp tmpfs. My system has relatively little RAM, and after a few days of uptime and a few ^C's I found there's quite a lot of files hanging around in memory (alternatively, I suppose I could set it to /tmp/go and automatically clean files older than >1 hour or so).I can unapologetically rm -rf this directory as long as no go builds are running.
Changing the meaning of
GOTMPDIRwould be pretty surprising, and potentially breaking.Your request is essentially "I want to use a system-wide temporary directory, but only for Go programs I don't want to use the standard system-wide temporary directory". I think it should matter as little as possible in which language something in written: do we also need PYTHONTMPDIR? RUBYTMPDIR? RUSTTMPDIR? ZIGTMPDIR?
If you don't want the system default temporary directory, then maybe your application shouldn't use
os.TempDir()andt.TempDir(), but write your own replacements for them?UnaffiliatedCode commentedon Jul 27, 2023
Looking at your feedback, lets review the release documentation:
The default for a non-assigned GOTMPDIR is the TMP/TEMP directory. Which could be interpreted as "When you want to override the TMP directory, update this env". This use-case matches the secondary definition. That being said, GOTMPDIR is for compilation changes, and does not explicitly state run-time. This is the change I am proposing.
This is still the case. All TMP/TEMP directories should be treated as such. If I, as a developer, set it to a directory which cannot be rm -rf'd, then that's on the developer.
Change Impact
This change would impact machines which have a GOTMPDIR configured and use a os.Tempdir command.
There is no actionable way to address this feedback. The view that we should not make a change because other languages refuse to make a change, which I believe is in direct contrast to the founding principals of Golang (A language which prides itself in being unconventional).
I mentioned previously that there are alternative solutions available, however they work around the problem as opposed to solving them. We agree that programming languages should work regardless of developer environment. We do not agree on what that means. Personally, I believe that developers should not have to worry about native configuration management within a language, it should be abstracted away from that necessity. If I configure my language (environment) to override a TMP directory, it should do so for both compilation and runtime.
Concern about the "breaking" nature of this change
Regarding the potential for this change to be a breaking change, maybe
GOTMPDIRisn't the right spot for this then. Perhaps an alternative is required. I brought up this proposal to further discussion regarding the issue.apparentlymart commentedon Jul 28, 2023
I develop an application that is written in Go where, from an end-user perspective, it is purely an implementation detail that it is written in Go. As much as possible, my application ought to behave like a "normal application" without the user needing to think about what language I wrote the application in.
I expect
os.TempDirto honor the current operating system's conventions for selecting a temporary directory. It would be very surprising if someone who is both a Go developer and a user of my software to see my software behave differently just because they changed the configuration of their Go toolchain.If this change were implemented, I would need to replace my uses of
os.TempDirwith a different function so that my application can continue to honor the operating system's typical conventions;os.TempDirwould no longer be useful to me. That seems a very strange outcome for a function in package "os", which means "operating system".Given that this use-case seems very specialized to your particular situation, I might suggest writing a function in your own codebase to meet this need, rather than changing the Go standard library. For example:
If that turns out to be a problem that many different Go codebases share then perhaps there could be a new function in the Go standard library which behaves like the above.
The way we typically learn that something is a common enough need to appear in the standard library is to first implement it in third-party codebases and then observe that either the same code is being written repeatedly or that someone has written a very popular library offering the capability. Therefore, if you believe this is a common need then I would suggest starting by building your own library with a function like the above and then observe how commonly it is used.
(I am personally skeptical that this particular framing is a common need. I can believe that "ability to override the temporary directory for just one program" is a common need, and I understand that you are asserting that reassigning the standard environment variable is not a sufficient answer in your environment even though that is the typical answer to this problem, but it doesn't necessarily follow that the Go standard library must do something special to solve that problem for you, or that a solution to this problem ought to be programming-language-specific rather than something offered by your operating system.)
apparentlymart commentedon Jul 28, 2023
I notice that in the body of your proposal you showed an example belonging to a test case.
It might be more defensible for
t.TempDirin particular to honorGOTMPDIRbecause that result is only relevant in the context of tests for programs written in Go.I don't personally have any need for that, but it would also not impact me and so I would not be concerned if a proposal limited just to that function were to be accepted.
arp242 commentedon Jul 28, 2023
This is absolutely not the case. Deleting files in /tmp while they're in use will break any program that's using them, you cannot safely "just" delete stuff in /tmp. There's a reason I used the "as long as no go builds are running" qualifier.
UnaffiliatedCode commentedon Jul 28, 2023
I think this is a very good point.
os.TempDir()may be an overreach due to the additional issues mentioned by @arp242 .For our use case, t.TempDir() would also meet our needs to resolve the problem. This is only a problem for unit testing.
7 remaining items
rsc commentedon Aug 30, 2023
Based on the discussion above, this proposal seems like a likely accept.
— rsc for the proposal review group
rsc commentedon Sep 7, 2023
No change in consensus, so accepted. 🎉
This issue now tracks the work of implementing the proposal.
— rsc for the proposal review group
[-]proposal: testing: use $GOTMPDIR for temporary files when set[/-][+]testing: use $GOTMPDIR for temporary files when set[/+]gopherbot commentedon Aug 1, 2025
Change https://go.dev/cl/692455 mentions this issue:
testing: allow specify temp dir by GOTMPDIR environment variable