-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use Gengo to generate DeepCopy methods #54
Conversation
Notes for Reviewers1. OverviewThe solution proposed here is based on a tool named Gengo. Gengo is a code generator framework widely used in Kubernetes. It works like a charm for generating DeepCopy methods that work exactly as we need them to. The main painpoint of my work was just to figure out how to use the tool in standalone mode (outisde of Kubernetes). The tool work with annotations placed on the types we want to generate DeepCopy methods for. To generate a simple DeepCopy method: // +k8s:deepcopy-gen=true
type Inet struct {
Addr net.IP
Port int32
} This will trigger the generation of a file named When the DeepCopy method is part of an interface, we need to first declare a method in the interface called type Message interface {
DeepCopyMessage() Message
} Then we can annotate each implementing type with: // +k8s:deepcopy-gen=true
// +k8s:deepcopy-gen:interfaces=github.com/datastax/go-cassandra-native-protocol/message.Message
type Startup struct {
Options map[string]string
} Note: while very flexible, the tool does NOT allow to change the tag name or the generated method names. (We would need to create our own tool based on the Gengo engine). |
2. Running the toolThis PR introduces a Makefile to facilitate running the tool. The Makefile has a
3. TestsThe tests for Clone methods could now be removed but instead I chose to keep them as a proof that the new DeepCopy methods are behaving as expected. |
@@ -23,6 +23,8 @@ import ( | |||
|
|||
// [inet] (net.IP + port) | |||
|
|||
// Inet is the [inet] protocol type. It is the combination of a net.IP + port number. | |||
// +k8s:deepcopy-gen=true | |||
type Inet struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This type wasn't being cloned before, but it needs deep copy.
@@ -14,6 +14,7 @@ | |||
|
|||
package primitive | |||
|
|||
// +k8s:deepcopy-gen=true | |||
type NillableInt32 struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI this will go away with #52.
@@ -26,6 +26,9 @@ import ( | |||
// Note: a reasonmap is inherently a map, but it is not modeled as a map in Go because [inetaddr] | |||
// is not a valid map key type. | |||
|
|||
// FailureReason is a map entry for a <reasonmap>; it contains the endpoint that failed and the corresponding failure | |||
// code. | |||
// +k8s:deepcopy-gen=true | |||
type FailureReason struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again this type wasn't being cloned.
@@ -26,11 +26,10 @@ const LengthOfUuid = 16 | |||
|
|||
type UUID [16]byte | |||
|
|||
func (u *UUID) Clone() *UUID { | |||
func (u *UUID) DeepCopy() *UUID { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the only DeepCopy method that had to be kept manual because the generator does not handle array types.
@@ -0,0 +1,144 @@ | |||
# From https://raw.githubusercontent.com/vincentbernat/hellogopher/1cf9ebeeaea41d2e159e196f7d176d6b12ff0eb2/Makefile |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This Makefile is inspired by a popular template for Go projects. I didn't invent all of this. The only modifications I brought are:
- Modification of the
all
target since this project does not have a main package. - Changed default output dir for all targets to
./build
. - Installation of additional tools:
- Gengo
- Addlicense
- Mockery
- New targets:
deep-copy
mocks
check-license
andupdate-license
pseudo-version
export GO111MODULE=on | ||
|
||
.PHONY: all | ||
all: check-license mocks deep-copy fmt lint test-coverage | $(BIN) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: running the all
target right now will bring up hundreds of lint problems, most of them related to exported types without comments. I did not fix those in this PR. I plan to do it later on.
.PHONY: all | ||
all: check-license mocks deep-copy fmt lint test-coverage | $(BIN) | ||
|
||
# Tools |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tool targets all work on the same principle: use go install
to install build tools under bin
. I added 3 tools: addlicense, mockery and deeepcopy-gen.
A few notes:
- deepcopy-gen needs a different installation procedure because it contains replace directives.
- mockery is being installed correctly so far but the author does not recommend installing this way. If we ever have problems in the future we'll need to install mockery differently but so far it's working.
Makefile
Outdated
# See https://stackoverflow.com/questions/52242077/go-modules-finding-out-right-pseudo-version-vx-y-z-timestamp-commit-of-re | ||
PSEUDO_VERSION ?= $(shell TZ=UTC git --no-pager show --quiet --abbrev=12 --date='format-local:%Y%m%d%H%M%S' --format="%cd-%h" 2> /dev/null || echo v0) | ||
.PHONY: pseudo-version | ||
pseudo-version: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Useful target to print the Go pseudo-version of a given commit.
// the statement to execute. | ||
QueryOrId interface{} | ||
// The CQL statement to execute. Exactly one of Query or Id must be present, never both. | ||
Query string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change was induced by the generator because it does not support interface{} types. But I think this is a good change anyways, I don't even know why I used a single field for both Query and Id while it's so much simpler to use 2 distinct fields.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree 👍
@@ -24,12 +24,14 @@ import ( | |||
// larger frame spanning multiple segments (multi-segment part). It can also be optionally compressed with Lz4. | |||
// Segments were introduced in protocol v5 to improve compression efficiency (especially for small messages) and to | |||
// introduce error detection in the native protocol. | |||
// +k8s:deepcopy-gen=true | |||
type Segment struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Types in this package did not have Clone methods so far, but for the sake of completeness, I'm generating methods for them as well.
// CustomType is a data type that represents a CQL custom type. | ||
// +k8s:deepcopy-gen=true | ||
// +k8s:deepcopy-gen:interfaces=github.com/datastax/go-cassandra-native-protocol/datatype.DataType | ||
type CustomType struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this package, some interfaces are now structs. This is because the tool cannot generate deepcopy methods for unexported structs.
But in fact, I don't even know why I created these interfaces in the first place. The only impl is the associated struct, so they seem pretty overkill.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also note: the names are going to change with #53.
Makefile
Outdated
test-bench: ARGS=-run=__absolutelynothing__ -bench=. ## Run benchmarks | ||
test-short: ARGS=-short ## Run only short tests | ||
test-verbose: ARGS=-v ## Run tests in verbose mode with coverage reporting | ||
test-race: ARGS=-race ## Run tests with race detector |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting to note: test-race reports about a dozen race conditions in the client
package.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
729c316
to
fcb42bc
Compare
@@ -1,12 +1,19 @@ | |||
module github.com/datastax/go-cassandra-native-protocol | |||
|
|||
go 1.16 | |||
go 1.17 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm changing to 1.17 just because the Makefile requires 1.17 (to use go install
). Note that this has little to no impact on users of this library as it remains backwards-compatible as long as we don't use any language feature specific to 1.17 (which we don't).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! Keeping the Clone()
tests was a great decision 👍
Fixes #31.