First Impressions With dep

Dependency management in Go has never been fun. When I was starting this post, I was making updates to the Terraform ACME provider, and in dependency hell with govendor due to this issue, which still has yet to be fixed, or so it would seem.

You would think that fixing this would be as easy as just ripping out the vendor directory and installing every dependency again, version-locking the things you are concerned about (Terraform core in this case), but nope. Ultimately, I think one of the non-TF dependencies overwrote some UUID generation stuff that we depend on in Terraform, or perhaps one of the TF providers that we use for testing overwrote go-uuid, but in any case, TF core is now blocking the build and I can’t proceed.

So, what’s the only answer – burn it all down of course 🔥🔥🔥🔥🔥🔥

Since this is my personal project, I can pretty much use whatever dependency management tool I want, so I’m trying (and documenting for your pleasure) a run of using the new dep tool that will ultimately become the one dependency management system to rule them all in Go, finally.

With that said, let’s give it a spin. Since I like to live life on the edge, I’m using the bleeding edge copy fetched by go get, so if things are a bit off from the last binary release (I don’t know if they are or not, by the way), then you might want to check out master. There hasn’t been a new release since October.

(The specific install step is go get -u github.com/golang/dep/cmd/dep).

Initializing

With all of my import paths in place and raring to go, I deleted my vendor directory. You don’t have to do this, by the way, dep will back up your old vendor directory and also import the dependencies based off your existing tool’s metadata file – you can see the list of supported tools here. I just didn’t really trust my vendor.json given the amount of problems I was having trying to get it to actually converge a correct dependency set.

Here is the output of dep init on the ACME provider:

Locking in master (06020f8) for transitive dep github.com/mitchellh/mapstructure
Using master as constraint for direct dep github.com/xenolf/lego
Locking in master (b929aa5) for direct dep github.com/xenolf/lego
Locking in master (23c074d) for transitive dep github.com/hashicorp/hcl
Locking in v1.32.0 (32e4c1e) for transitive dep gopkg.in/ini.v1
Locking in v1.1.0 (879c588) for transitive dep github.com/satori/go.uuid
Locking in v0.1.0 (4aabc24) for transitive dep github.com/bgentry/speakeasy
Locking in v1.32.0 (32e4c1e) for transitive dep github.com/go-ini/ini
Locking in v9.7.0 (8c58b47) for transitive dep github.com/Azure/go-autorest
Locking in master (7554cd9) for transitive dep github.com/hashicorp/errwrap
Locking in master (d23ffcb) for transitive dep github.com/mitchellh/copystructure
Locking in master (44bad6d) for transitive dep github.com/hashicorp/hcl2
Locking in v1.2.1 (5f10fee) for transitive dep github.com/agext/levenshtein
Locking in master (30785a2) for transitive dep golang.org/x/oauth2
Locking in v1.9.0 (f3955b8) for transitive dep google.golang.org/grpc
Locking in master (3d48364) for transitive dep github.com/jen20/awspolicyequivalence
Locking in master (709e403) for transitive dep github.com/zclconf/go-cty
Locking in master (42fe2e1) for transitive dep golang.org/x/net
Locking in master (dd9ec17) for transitive dep golang.org/x/sys
Locking in v1.1.0 (346938d) for transitive dep github.com/davecgh/go-spew
Locking in master (1e59b77) for transitive dep github.com/golang/protobuf
Locking in v0.9.1 (87b6919) for transitive dep github.com/hashicorp/vault
Locking in master (683f491) for transitive dep github.com/hashicorp/yamux
Locking in v12.1.1-beta (57da600) for transitive dep github.com/Azure/azure-sdk-for-go
Using ^1.6.0 as constraint for direct dep github.com/terraform-providers/terraform-provider-aws
Locking in v1.6.0 (ddd2205) for direct dep github.com/terraform-providers/terraform-provider-aws
Locking in master (64130c7) for transitive dep github.com/hashicorp/go-uuid
Locking in master (1fca145) for transitive dep github.com/armon/go-radix
Locking in v1.1 (dc2bc5a) for transitive dep github.com/posener/complete
Locking in master (fa9f258) for transitive dep github.com/hashicorp/hil
Locking in master (d5fe4b5) for transitive dep github.com/hashicorp/go-cleanhttp
Locking in master (f33a2c6) for transitive dep github.com/decker502/dnspod-go
Locking in v1.0.1 (787fb05) for transitive dep github.com/miekg/dns
Locking in master (59fac50) for transitive dep github.com/juju/ratelimit
Locking in v0.17.0 (050b16d) for transitive dep cloud.google.com/go
Locking in master (53e6ce1) for transitive dep github.com/google/go-querystring
Locking in v0.0.3 (0360b2a) for transitive dep github.com/mattn/go-isatty
Locking in master (2d22b6e) for transitive dep github.com/keybase/go-crypto
Locking in master (2bd8b58) for transitive dep github.com/apparentlymart/go-cidr
Locking in master (2bca23e) for transitive dep github.com/mitchellh/hashstructure
Locking in master (b1c0f9b) for transitive dep google.golang.org/api
Locking in master (37e8452) for transitive dep github.com/timewasted/linode
Locking in 1.15.0 (fa1c036) for transitive dep github.com/JamesClonk/vultr
Locking in (0b12d6b5) for transitive dep github.com/jmespath/go-jmespath
Locking in master (4fe82ae) for transitive dep github.com/hashicorp/go-version
Locking in v1.0.4 (d682213) for transitive dep github.com/sirupsen/logrus
Locking in master (994f50a) for transitive dep github.com/hashicorp/go-getter
Using ^0.11.1 as constraint for direct dep github.com/hashicorp/terraform
Locking in v0.11.1 (a42fdb0) for direct dep github.com/hashicorp/terraform
Locking in master (0dc08b1) for transitive dep github.com/hashicorp/logutils
Locking in master (02f7e94) for transitive dep github.com/ovh/go-ovh
Locking in v0.1.0 (5e23d5d) for transitive dep github.com/exoscale/egoscale
Locking in v0.15.0 (9641549) for transitive dep github.com/dnsimple/dnsimple-go
Locking in v3.1.0 (dbeaa93) for transitive dep github.com/dgrijalva/jwt-go
Locking in master (b836f5c) for transitive dep github.com/apparentlymart/go-textseg
Locking in master (ca137eb) for transitive dep github.com/hashicorp/go-hclog
Locking in master (e2fbc68) for transitive dep github.com/hashicorp/go-plugin
Locking in master (a61a995) for transitive dep github.com/mitchellh/go-testing-interface
Locking in v1.0.0 (150dc57) for transitive dep google.golang.org/appengine
Locking in master (63d60e9) for transitive dep github.com/mitchellh/reflectwalk
Locking in v3.5.1 (2ee8785) for transitive dep github.com/blang/semver
Locking in v2 (0e4404d) for transitive dep gopkg.in/yaml.v2
Locking in v1.1.0 (aa2e30f) for transitive dep gopkg.in/square/go-jose.v1
Locking in v1.0.3 (1563e62) for transitive dep github.com/edeckers/auroradnsclient
Locking in v1.2.0 (b91bfb9) for transitive dep github.com/stretchr/testify
Locking in master (a8101f2) for transitive dep google.golang.org/genproto
Using ^1.0.1 as constraint for direct dep github.com/terraform-providers/terraform-provider-tls
Locking in v1.0.1 (1be2f9e) for direct dep github.com/terraform-providers/terraform-provider-tls
Locking in master (b7773ae) for transitive dep github.com/hashicorp/go-multierror
Locking in v1.0.0 (15a30b4) for transitive dep github.com/beevik/etree
Locking in master (b8bc1bf) for transitive dep github.com/mitchellh/go-homedir
Locking in v1.12.56 (ce8d7a1) for transitive dep github.com/aws/aws-sdk-go
Locking in master (33edc47) for transitive dep github.com/mitchellh/cli
Locking in v2 (3c1c487) for transitive dep gopkg.in/ns1/ns1-go.v2
Locking in v0.5.4 (0c6b41e) for transitive dep github.com/ulikunitz/xz
Locking in master (ad45545) for transitive dep github.com/mitchellh/go-wordwrap
Locking in master (e19ae14) for transitive dep golang.org/x/text
Locking in master (553a641) for transitive dep github.com/golang/snappy
Locking in master (9fd32a8) for transitive dep github.com/bgentry/go-netrc
Using master as constraint for direct dep golang.org/x/crypto
Locking in master (0fcca48) for direct dep golang.org/x/crypto
Locking in v1.0.0 (792786c) for transitive dep github.com/pmezard/go-difflib

My initial impression here is the UX is much better than govendor. It actually tells you what versions it’s pulling in? What sorcery is this? 😂

Looking at the output, it seems to have done a pretty good (initial) job at giving me what I want. The most important thing is that it’s vendoring lego at master. The tool hasn’t had a release for a while, and I need some post-release fixes I specifically need.

Going even farther, it vendored Terraform at it’s most recent release, 0.11.1. This is amazing, because it’s really the only dependency that I was concerned with vendoring at a specific version lock!

Now, editing one of the files within the provider does not give me the UUID-related build issues I was having, or any of the other missing or outdated dependency issues, for that matter. Pack it up, we’re done here.

But wait, are we done? Let’s check out a few more things.

File Count

dep will – currently anyway – bloat your vendor directory pretty hard. The ACME project’s directory probably inflated about 5 times (from about 2800 files to over 16000).

This is due to some currently ongoing work in dep to ensure that the steps undertaken by dep prune are done by the rest of the process automatically. This removes unused packages and running it got the file count down to a much more manageable ~4700 files, which, I would imagine, is much easier to get by someone during code review (thankfully I don’t have to do that here 😉).

This still did not remove everything though. It appears the team is still working on making this work on a scale that will ultimately ensure that only what is truly needed remains – this includes removing _test.go files in necessary packages. This is being tracked in #944, it appears, or at the very least questions about the pruning functionality are being answered there. Note that as mentioned, and as the subject of the issue mentions, prune will soon be consolidated into ensure, with that command assuming prune’s functionality along with its own.

Metadata and Configuration Files

Two files control dep’s behavior.

The first one, Gopkg.toml, is a configuration file that only displays your direct dependencies – the packages your project directly references. This is where you will want to make changes if you need to increment a version of a particular dependency before running dep ensure. Personally, I like this approach over something like govendor where you need to manage this on the command line.

The documentation seems to indicate that you can make changes here, run dep ensure, and dep will automatically transition the entire vendor tree to properly satisfy the new dependency chain, if that’s possible.

The other file is Gopkg.lock. This is similar to any other lock file that you would see in a dependency management system (such as Bundler), and replaces other files like vendor.json (if you were using govendor, for example). This file you would normally not touch, and serves to catalog all of the dependencies the tool needs, including your transitive dependencies – the packages your direct dependencies require.

Viewing Dependency Status

dep status gives you information about the vendored dependencies.

Running it with no options gives you stats on every project that has been vendored, including versions and used package count within each project.

Also, running dep status -dot allows you to output a GraphViz diagram, much like terraform graph does, if you are familiar with that. If you are familiar with terraform graph though, you might know how ridiculous the graphs get at scale, and obviously vendored dependencies are going to be worse, so don’t expect the graphs to be entirely simple.

Local Work

Aniother thing to note is that dep does not work out of local paths at all currently, it seems. This means that any local work you do will be superseded when you run dep ensure, which I personally think is a great addition, because it removes the ambiguity that other tools currently have surrounding this behavior.

My hope is they they keep this default behavior if/when they do actually introduce support for working with local packages.

Version Constraints for Transitive Dependencies

The last thing I want to note is how to deal with transitive dependencies. These are dependencies that your direct dependencies (the packages your project directly references) depend on.

This probably cost me about half a day of debugging and troubleshooting, but one thing that did happen was the correctly vendored dependencies for copystructure and reflectwalk were not imported, and there are currently some “correct” behaviors in Terraform that depend on bugs that were present in these two packages. This was fixed by adding some extra override blocks in Gopkg.toml that locked these two packages at the correct commits. After adding these in, it was as easy as running dep ensure to get everything corrected.

Conclusion

dep is a great tool. It’s got a bit more work to go, but the UX is very pleasant, and everyone involved should give themselves a pat on the back for finally getting the Go community to a place where we might be able to put dependency management behind us. It’s always going to be somewhat of a struggle, but at the very least we can all struggle under one banner versus the dozen or so different ones that currently comprise the vendor tool ecosystem.

If you are curious as to how it looks in action, you can check out the code for the Terraform ACME provider, which is now being managed with dep!

(If you read all the way through this and notice that the TF being used is now constrained to master, even though I mentioned that I wanted it locked at 0.11.1, it’s because not only did I do that during my troubleshooting process, but there has also been some changes to the “provider SDK” in helper/schema that I figured would be good to have on hand.)

Advertisements