Unable to lock versions when converting to go.mod

509 views
Skip to first unread message

Brian Candler

unread,
Nov 27, 2019, 4:08:17 PM11/27/19
to golang-nuts
I am having a problem with go.mod.  I'm using go 1.13.4 and you can easily replicate what I'm seeing.

I am trying to convert https://github.com/RobustPerception/nrpe_exporter from vendor/vendor.json to go.mod.

Here's how I start:

$ cd nrpe_exporter
$ go mod init github.com/RobustPerception/nrpe_exporter
go: creating new go.mod: module github.com/RobustPerception/nrpe_exporter
go: copying requirements from vendor/vendor.json
go: converting vendor/vendor.json: stat github.com/go-kit/log@: unknown revision
$ cat go.mod

go 1.13

require (
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf
github.com/go-kit/kit v0.5.1-0.20170917202734-0d313fb5fb3a
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515
github.com/pkg/errors v0.8.1-0.20170910134614-2b3a18b5f0fb
github.com/prometheus/common v0.0.0-20170908161822-2f17f4a9d485
)

That looks fine up to this point.  Note in particular how a specific old version of prometheus/common from 2017 has been selected - highlighted.

However, the next step is to type "go build ./...", and as soon as I do that, the dependencies are changed to point to newer versions of those libraries.

$ go build ./...
go: finding github.com/aperum/nrpe latest
./nrpe_exporter.go:124:37: cannot use &allowedLevel (type *promlog.AllowedLevel) as type *promlog.Config in argument to flag.AddFlags
./nrpe_exporter.go:128:23: cannot use allowedLevel (type promlog.AllowedLevel) as type *promlog.Config in argument to promlog.New
$ cat go.mod

go 1.13

require (
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4
github.com/aperum/nrpe v0.0.0-20170524093721-53d9ca02dfca
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515
)

You can see that go.mod has been updated to point to the newest version of prometheus/common - and that is why the build is failing (since the promlog API in prometheus/common has changed).

I could update the code to make use of the newer dependencies.  But really I'm confused: surely the point of go.mod / go.sum is to be able to refer to specific versions of libraries you depend on, so that you *don't* get caught out by this sort of problem?

Obviously I'm missing something here, so I'd be very grateful if someone could enlighten me.  What would I need to do to have "go build" use the dependencies listed in go.mod, rather than updating them?  (I tried -mod=readonly, but then it just fails saying it needs to update them)

Many thanks,

Brian.

Brian Candler

unread,
Nov 27, 2019, 6:06:01 PM11/27/19
to golang-nuts
I tried blowing away my entire ~/go tree to be sure.  Starting from a fresh checkout (outside the ~/go tree), and a fresh checkout and go mod init ..., here's what the build does:

$ go build ./...
go: downloading github.com/go-kit/kit v0.5.1-0.20170917202734-0d313fb5fb3a
go: downloading github.com/prometheus/common v0.0.0-20170908161822-2f17f4a9d485
go: downloading gopkg.in/alecthomas/kingpin.v2 v2.2.5
go: finding github.com/aperum/nrpe latest
go: extracting github.com/prometheus/common v0.0.0-20170908161822-2f17f4a9d485
go: downloading github.com/pkg/errors v0.8.1-0.20170910134614-2b3a18b5f0fb
go: extracting gopkg.in/alecthomas/kingpin.v2 v2.2.5
go: downloading github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf
go: downloading github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc
go: downloading github.com/aperum/nrpe v0.0.0-20170524093721-53d9ca02dfca
go: extracting github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf
go: extracting github.com/pkg/errors v0.8.1-0.20170910134614-2b3a18b5f0fb
go: extracting github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc
go: extracting github.com/go-kit/kit v0.5.1-0.20170917202734-0d313fb5fb3a
go: extracting github.com/aperum/nrpe v0.0.0-20170524093721-53d9ca02dfca
go: downloading github.com/go-stack/stack v1.6.0
go: downloading github.com/go-logfmt/logfmt v0.3.0
go: extracting github.com/go-logfmt/logfmt v0.3.0
go: extracting github.com/go-stack/stack v1.6.0
go: downloading gopkg.in/alecthomas/kingpin.v2 v2.2.6
go: downloading github.com/go-kit/kit v0.9.0
go: downloading github.com/prometheus/procfs v0.0.5
go: downloading github.com/golang/protobuf v1.3.2
go: downloading github.com/cespare/xxhash/v2 v2.1.0
go: downloading github.com/prometheus/common v0.7.0
go: downloading github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4
go: extracting gopkg.in/alecthomas/kingpin.v2 v2.2.6
go: extracting github.com/prometheus/procfs v0.0.5
go: extracting github.com/cespare/xxhash/v2 v2.1.0
go: extracting github.com/go-kit/kit v0.9.0
go: extracting github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4
go: downloading github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4
go: downloading github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
go: extracting github.com/prometheus/common v0.7.0
go: downloading github.com/beorn7/perks v1.0.1
go: extracting github.com/golang/protobuf v1.3.2
go: downloading github.com/pkg/errors v0.8.1
go: downloading github.com/go-logfmt/logfmt v0.4.0
go: extracting github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4
go: extracting github.com/go-logfmt/logfmt v0.4.0
go: extracting github.com/pkg/errors v0.8.1
go: extracting github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
go: extracting github.com/beorn7/perks v1.0.1
go: finding github.com/go-kit/kit v0.9.0
go: finding github.com/prometheus/common v0.7.0
go: finding github.com/go-logfmt/logfmt v0.4.0
go: finding github.com/pkg/errors v0.8.1
go: finding github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4
go: finding github.com/beorn7/perks v1.0.1
go: finding github.com/cespare/xxhash/v2 v2.1.0
go: finding github.com/golang/protobuf v1.3.2
go: finding github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
go: finding github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4
go: finding github.com/prometheus/procfs v0.0.5
./nrpe_exporter.go:124:37: cannot use &allowedLevel (type *promlog.AllowedLevel) as type *promlog.Config in argument to flag.AddFlags
./nrpe_exporter.go:128:23: cannot use allowedLevel (type promlog.AllowedLevel) as type *promlog.Config in argument to promlog.New

So it seems to be downloading both the old versions of packages as they were in the initial go.mod, *and* the newer ones.  Here is how go.mod changes:

--- go.mod.orig 2019-11-27 22:43:06.884601059 +0000
+++ go.mod 2019-11-27 22:43:20.733075672 +0000
@@ -3,13 +3,15 @@
 go 1.13

 require (
- github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc
- github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf
- github.com/go-kit/kit v0.5.1-0.20170917202734-0d313fb5fb3a
+ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
+ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4
+ github.com/aperum/nrpe v0.0.0-20170524093721-53d9ca02dfca
  github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515
- github.com/pkg/errors v0.8.1-0.20170910134614-2b3a18b5f0fb
- github.com/prometheus/common v0.0.0-20170908161822-2f17f4a9d485
 )

Almost every package gets a new version.

"go mod tidy" changes it again, but still brings everything up to latest:

--- go.mod.orig 2019-11-27 22:43:06.884601059 +0000
+++ go.mod 2019-11-27 22:49:49.218179240 +0000
@@ -3,13 +3,10 @@
 go 1.13

 require (
- github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc
- github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf
- github.com/go-kit/kit v0.5.1-0.20170917202734-0d313fb5fb3a
- github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515
- github.com/pkg/errors v0.8.1-0.20170910134614-2b3a18b5f0fb
- github.com/prometheus/common v0.0.0-20170908161822-2f17f4a9d485
+ github.com/aperum/nrpe v0.0.0-20170524093721-53d9ca02dfca
+ github.com/stretchr/testify v1.4.0 // indirect
 )



I've re-read the blog posting here on changing to modules.  It says:

go mod init creates a new go.mod file and automatically imports dependencies from Godeps.jsonGopkg.lock, or a number of other supported formats.

...

This is a good time to pause and run go build ./... and go test ./... before continuing. Later steps may modify your go.mod file


But what I'm finding is that the "go build ./..." step itself is modifying go.mod, and that's what's confusing me.

A separate wiki page talks about "minimal version selection" algorithm.  Starting with the initial go.mod, "go list -m all" doesn't mention the newer versions:

$ go list -m all
go: finding github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc
go: finding github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf
go: finding github.com/go-kit/kit v0.5.1-0.20170917202734-0d313fb5fb3a
go: finding github.com/go-logfmt/logfmt v0.3.0
go: finding github.com/go-stack/stack v1.6.0
go: finding github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515
go: finding github.com/pkg/errors v0.8.1-0.20170910134614-2b3a18b5f0fb
go: finding github.com/prometheus/common v0.0.0-20170908161822-2f17f4a9d485
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf
github.com/go-kit/kit v0.5.1-0.20170917202734-0d313fb5fb3a
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515
github.com/pkg/errors v0.8.1-0.20170910134614-2b3a18b5f0fb
github.com/prometheus/common v0.0.0-20170908161822-2f17f4a9d485

Therefore, if there is some nested dependency which is forcing newer versions of packages to be picked up, I can't work out what it is.

Thanks for any clues!

Brian Candler

unread,
Nov 28, 2019, 3:08:22 AM11/28/19
to golang-nuts
I got a private reply saying to try "go mod why -m github.com/prometheus/common"

Starting with the original go.mod, I get different answers the first and second time I run it - by which time, go.mod contents have been updated.


Note that the main module (nrpe_exporter.go) *does* require the following:


vendor/vendor.json pins specific versions of prometheus/common/promlog and prometheus/common/promlog/flag, but not prometheus/common/version - and oddly, prometheus/common/version hasn't been imported into the vendor/ directory either.  So now I'm starting to suspect that the latest prometheus/common/version is being picked up, which in turn is forcing refresh of the other dependencies; but this is just an idea, I don't have any output from any of the go commands telling me this is what is happening.

I also tried adding -v to go build:

$ go build -n -v -x ./...
go: finding github.com/aperum/nrpe latest

#
#

mkdir -p $WORK/b001/
cat >$WORK/b001/_gomod_.go << 'EOF' # internal
package main
import _ "unsafe"
//go:linkname __debug_modinfo__ runtime.modinfo
var __debug_modinfo__ = "0w\xaf\f\x92t\b\x02A\xe1\xc1\a\xe6\xd6\x18\xe6path\tgithub.com/RobustPerception/nrpe_exporter\nmod\tgithub.com/RobustPerception/nrpe_exporter\t(devel)\t\ndep\tgithub.com/alecthomas/template\tv0.0.0-20190718012654-fb15b899a751\th1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=\ndep\tgithub.com/alecthomas/units\tv0.0.0-20190717042225-c3de453c63f4\th1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E=\ndep\tgithub.com/aperum/nrpe\tv0.0.0-20170524093721-53d9ca02dfca\th1:2d9vZl6mzbSG+3cr0ghfKvM0nOa15OGkbW1vx+R5+Ww=\ndep\tgithub.com/beorn7/perks\tv1.0.1\th1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=\ndep\tgithub.com/cespare/xxhash/v2\tv2.1.0\th1:yTUvW7Vhb89inJ+8irsUqiWjh8iT6sQPZiQzI6ReGkA=\ndep\tgithub.com/go-kit/kit\tv0.9.0\th1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk=\ndep\tgithub.com/go-logfmt/logfmt\tv0.4.0\th1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=\ndep\tgithub.com/golang/protobuf\tv1.3.2\th1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=\ndep\tgithub.com/matttproud/golang_protobuf_extensions\tv1.0.1\th1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=\ndep\tgithub.com/pkg/errors\tv0.8.1\th1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=\ndep\tgithub.com/prometheus/client_golang\tv1.2.1\th1:JnMpQc6ppsNgw9QPAGF6Dod479itz7lvlsMzzNayLOI=\ndep\tgithub.com/prometheus/client_model\tv0.0.0-20190812154241-14fe0d1b01d4\th1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=\ndep\tgithub.com/prometheus/common\tv0.7.0\th1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY=\ndep\tgithub.com/prometheus/procfs\tv0.0.5\th1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=\ndep\tgopkg.in/alecthomas/kingpin.v2\tv2.2.6\th1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=\n\xf92C1\x86\x18 r\x00\x82B\x10A\x16\xd8\xf2"
EOF
cat >$WORK/b001/importcfg << 'EOF' # internal
# import config
packagefile net=/usr/local/go/pkg/linux_amd64/net.a
packagefile net/http=/usr/local/go/pkg/linux_amd64/net/http.a
packagefile os=/usr/local/go/pkg/linux_amd64/os.a
packagefile time=/usr/local/go/pkg/linux_amd64/time.a
packagefile runtime=/usr/local/go/pkg/linux_amd64/runtime.a
EOF
cd /home/ubuntu/nrpe_exporter
/usr/local/go/pkg/tool/linux_amd64/compile -o $WORK/b001/_pkg_.a -trimpath "$WORK/b001=>" -p main -lang=go1.13 -complete -buildid 0fKvaL6612In9ont9l8M/0fKvaL6612In9ont9l8M -goversion go1.13.4 -D "" -importcfg $WORK/b001/importcfg -pack -c=2 ./nrpe_exporter.go $WORK/b001/_gomod_.go
... snip rest


Brian Candler

unread,
Nov 28, 2019, 4:24:47 AM11/28/19
to golang-nuts
I *think* I got to the bottom of this.  It turns out the nrpe_exporter.go main module imports some non-vendored dependencies:


That is: they are not in the vendor/ directory and they are not listed in vendor/vendor.json, and hence weren't imported into go.mod by go mod init.  As a result, I believe that "go build" is fetching the latest versions of these packages, which in turn bump up dependencies of some of the other packages we also use.

If that's true, then I think it would be really helpful if go build (-v) could flag when it decides to bump versions and show the reason, i.e. the chain of dependencies which resulted in this particular version being required.  I showed the "go mod why" output before: it shows the dependency chain, but not where the version requirements came from.  I think this has partially been raised already as https://github.com/golang/go/issues/27900

I can't see any versioning info from "go mod why".  Again, starting with fresh go.mod:

ubuntu@builder:~/nrpe_exporter$ egrep 'common|kingpin' go.mod
github.com/prometheus/common v0.0.0-20170908161822-2f17f4a9d485

ubuntu@builder:~/nrpe_exporter$ go mod why
go: downloading gopkg.in/alecthomas/kingpin.v2 v2.2.5
go: downloading github.com/prometheus/common v0.0.0-20170908161822-2f17f4a9d485
go: downloading github.com/go-kit/kit v0.5.1-0.20170917202734-0d313fb5fb3a
go: finding github.com/aperum/nrpe latest
go: extracting gopkg.in/alecthomas/kingpin.v2 v2.2.5
go: downloading github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc
go: downloading github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf
go: extracting github.com/prometheus/common v0.0.0-20170908161822-2f17f4a9d485
go: downloading github.com/pkg/errors v0.8.1-0.20170910134614-2b3a18b5f0fb
go: extracting github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf
go: extracting github.com/go-kit/kit v0.5.1-0.20170917202734-0d313fb5fb3a
go: extracting github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc
go: extracting github.com/pkg/errors v0.8.1-0.20170910134614-2b3a18b5f0fb
go: downloading github.com/go-logfmt/logfmt v0.3.0
go: downloading github.com/go-stack/stack v1.6.0
go: extracting github.com/go-stack/stack v1.6.0
go: extracting github.com/go-logfmt/logfmt v0.3.0

ubuntu@builder:~/nrpe_exporter$ egrep 'common|kingpin' go.mod

ubuntu@builder:~/nrpe_exporter$ go mod why github.com/prometheus/common
(main module does not need package github.com/prometheus/common)
ubuntu@builder:~/nrpe_exporter$ go mod why gopkg.in/alecthomas/kingpin.v2

The first "go mod why" says "downloading gopkg.in/alecthomas/kingpin.v2 v2.2.5".  But after it has finished, it has silently updated go.mod to depend on v2.2.6 instead.

Axel Wagner

unread,
Nov 28, 2019, 4:26:59 AM11/28/19
to Brian Candler, golang-nuts
Hi,

you are importing github.com/aperum/nrpe, but you don't vendor it or mention it in vendor.json.
go mod init synthesizes a go.mod from vendor.json, using all versions you use there. Note that it doesn't mention nrpe.
You then do a `go build`, which sees an import from a module not mentioned in `go.mod`, assumes you added a new dependency, and tries to add that to `go.mod`. This then causes it to pull in the newest versions of *its* dependencies (as it doesn't have a g `go.mod` itself).
You should update your vendor.json to start with. And/or add nrpe manually to go.mod with a version that suits you.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/4beafd04-012d-43c7-b5fe-3089bdab51ef%40googlegroups.com.

Axel Wagner

unread,
Nov 28, 2019, 4:28:38 AM11/28/19
to Brian Candler, golang-nuts
FWIW (I'm not saying that the output couldn't be more helpful): I got to the conclusion of what the problem was by looking at the output of `go build` you posted:

go: finding github.com/aperum/nrpe latest

It *does* tell you that it's trying to fetch a new module here and which.


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Brian Candler

unread,
Nov 28, 2019, 5:38:49 AM11/28/19
to golang-nuts
Many thanks Axel.

The problem isn't github.com/aperum/nrpe - that doesn't have any dependencies of its own outside of the standard library.  However, the following three weren't vendored either:


and I believe one or more of them is requiring newer versions of the libraries which were vendored in.  It would be helpful if go said "I'm pulling in the latest version of github.com/prometheus/common/version - and by the way, to meet its requirements, I've had to increase the go.mod dependency of Foo from 2.2.5 to 2.2.6".  But I think I know enough now to proceed.

This is someone else's project I'm contributing to.  They want to keep the vendor/ directory.  So rather than trying to find the correct old versions of those libraries and vendor them, I've gone with:

go mod init ...
go mod tidy
go mod vendor

which has refreshed all the dependencies (including the missing entries in the vendor/ directory) - and then fixed the code to work with the new dependencies.  This means it should be good going forward from this point.

Thanks again,

Brian.
Reply all
Reply to author
Forward
0 new messages