Skip to content
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

Generate composite types #729

Merged
merged 1 commit into from
Apr 29, 2019

Conversation

spencerhance
Copy link
Contributor

@spencerhance spencerhance commented Apr 16, 2019

This PR adds generated composite types for many of the services from k8s-cloud-provider using the API spec json. This auto-generates the existing BackendService composite type as well.

The next step will to be to remove the first cloud-provider layer so that all types can be easily generated. That will also depend on: GoogleCloudPlatform/k8s-cloud-provider#9

@k8s-ci-robot k8s-ci-robot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Apr 16, 2019
@k8s-ci-robot
Copy link
Contributor

Hi @spencerhance. Thanks for your PR.

I'm waiting for a kubernetes member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@k8s-ci-robot k8s-ci-robot added the size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. label Apr 16, 2019
@k8s-ci-robot k8s-ci-robot requested review from freehan and thockin April 16, 2019 21:16
@rramkumar1
Copy link
Contributor

/ok-to-test

@k8s-ci-robot k8s-ci-robot added ok-to-test Indicates a non-member PR verified by an org member that is safe to test. and removed needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Apr 17, 2019
@spencerhance spencerhance changed the title [WIP] Generate composite types Generate composite types Apr 17, 2019
@k8s-ci-robot k8s-ci-robot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Apr 17, 2019
@rramkumar1 rramkumar1 self-assigned this Apr 17, 2019
@spencerhance spencerhance mentioned this pull request Apr 17, 2019
10 tasks
Copy link
Contributor

@rramkumar1 rramkumar1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First round of comments: Nice usage of the JSON spec! I never thought of that as an approach but it seems a bit nicer than using reflection.

One general comment: Do you mind doing a diff of (HEAD, PR) of the composite.go and composite_test.go for just the BackendService? I want to make sure there is no difference so the possibility we introduce a subtle bug is as close to zero as possible.

@@ -74,7 +74,7 @@ func (b *Backends) Create(sp utils.ServicePort, hcLink string) (*composite.Backe
HealthChecks: []string{hcLink},
}
ensureDescription(be, &sp)
if err := composite.CreateBackendService(be, b.cloud); err != nil {
if err := composite.CreateBackendService(be, b.cloud, meta.GlobalKey(be.Name)); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we wan't to silo your changes for L7 ILB as much as possible to keep the code base stable, I would suggest not introducing the key parameter in this PR. That way these function calls do not change and there is no real chance for a bug to be introduced.

I would leave a TODO in the composite generator code that states this should be added in a follow up.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Also good call, the only major change this PR is introducing is bypassing the first cloud-provider layer and calling the CRUD functions directly
(e.g cloud.Compute().AlphaBackendServices().Insert(meta.GlobalKey()) instead of cloud.CreateAlphaGlobalBackendService().)

With this change, we would be losing metrics until some replacement is implemented.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason for that was to try to make it simpler to use global and regional resources with less switches (and easier code generation logic), but I'm definitely open to suggestions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only other change I see in the diff is making the TestToX() functions simpler since I don't use known Alpha/Beta fields anymore. It's possible to add those in by doing a diff in the generate code but it would definitely add a bunch more complexity.

gofmt = "gofmt"

// This assumes that alpha contains a superset of all struct fields
apiFilePath = "../../vendor/google.golang.org/api/compute/v0.alpha/compute-api.json"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be okay for now I think. Once we migrate to go modules, its possible we may have to do something different. My understanding of go modules is that they remove the need for vendor/ but you can still use it if you want. Anyways, we can cross that bridge when we get there.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm good point. Will look into modules.

return nil, fmt.Errorf("error unmarshalling {{$type.Name}} JSON to compute {{$lower}} type: %v", err)
}

{{- if eq $type.Name "BackendService"}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will have to think of a better solution for this. It may be fine to just add all fields to ForceSendFields but for now I think this is fine.

Copy link
Contributor Author

@spencerhance spencerhance Apr 17, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, do you remember why those ForceSendFields for BackendService were added originally?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They were needed because of some subtle behavior with how the GCE API's work. Specifically, if you there is a boolean field (like "IncludeHost") and you are trying to set the field to false, GCE will not recognize that false setting unless you add the field to the ForceSendFields list.

From GCE docs: "By default, fields with empty values are omitted from API requests." (https://godoc.org/google.golang.org/api/compute/v1).

I'm trying to remember why though I needed to add this here rather than have callers set those fields...

@spencerhance
Copy link
Contributor Author

@rramkumar1 Thanks! There's also an additional approach which uses the parsing code found in k8s-cloud-provider, but that would have to be heavily modified or copy-pasted into ingress-gce which seemed worse than not having any dependencies.

Also ready for another round of review

return nil, fmt.Errorf("error unmarshalling {{$type.Name}} JSON to compute {{$lower}} type: %v", err)
}

{{- if eq $type.Name "BackendService"}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They were needed because of some subtle behavior with how the GCE API's work. Specifically, if you there is a boolean field (like "IncludeHost") and you are trying to set the field to false, GCE will not recognize that false setting unless you add the field to the ForceSendFields list.

From GCE docs: "By default, fields with empty values are omitted from API requests." (https://godoc.org/google.golang.org/api/compute/v1).

I'm trying to remember why though I needed to add this here rather than have callers set those fields...

return err
}
klog.V(3).Infof("Creating alpha backend service %v", alpha.Name)
return cloud.CreateAlphaGlobalBackendService(alpha)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My feeling is that we should not remove the usage of the wrapper until we have a solution for keeping the metrics around.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the two main reasons for removing the wrapper are that it doesn't follow a consistent formula like the generated wrapper, and it's in k/k so it would be potentially harder for me to change it for the PR. https://github.com/kubernetes/kubernetes/tree/master/pkg/cloudprovider/providers/gce.

That being said, I could just limit this PR to doing a drop-in replacement of BackendService and leave the other composite types for another PR once I have a replacement for metrics.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't mind that idea. That way, we don't have to block this necessarily on the metrics stuff.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Contributor Author

@spencerhance spencerhance Apr 18, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

side note, do you know offhand if anything uses those metrics?

@spencerhance
Copy link
Contributor Author

@rramkumar1 Ready for another round

// The other types that are discovered as dependencies will simply be wrapped with a composite struct
// The format of the map is ServiceName -> k8s-cloud-provider wrapper name
// TODO: (shance) Add the commented services and remove dependency on first cloud-provider layer
var MainServices = map[string]string{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you extract this and all the other types and methods below into a separate package?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm starting to wonder if it would make sense for all of this stuff to be moved into k8s-cloud-provider 🙃 . Maybe not at first though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I've thought about that. Reason I hesitate right now is that its pretty specific to us. If someone else finds a need for this, then definitely it should go there.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense. It would allow us to refactor the parsing code a bit too which would be nice eventually.

}
}

func init() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This init can also be moved to a separate package when you move the other stuff.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@spencerhance
Copy link
Contributor Author

@rramkumar1 Ready for another review

Copy link
Contributor

@rramkumar1 rramkumar1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, LGTM. Would like @bowei to take a look as well. Also, please squash commits.

/assign @bowei

@@ -0,0 +1,476 @@
/*
Copyright 2019 The Kubernetes Authors.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need the proper line breaks for the copyright. You can look at some other file for reference.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

"fmt"
"io"
"io/ioutil"
compositemeta "k8s.io/ingress-gce/pkg/composite/meta"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typically standard library imports (like bytes or strings) are first (and in alphabetical order) and the stuff imported from elsewhere is next (after a empty line). Example:

"bytes"
"fmt"
"io"
...

compositemeta "k8s.io...."

Can you make sure the imports for other files is fixed as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

)

// gofmtContent runs "gofmt" on the given contents.
// Duplicate of the function in k8s-cloud-provider
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is pretty trivial so no need to mention that its a dup.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

fmt.Fprintf(wr, "\n\n")
}

// genTypes() generates all of the struct definitions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: genTypes() generates all of the composite structs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}
}

// genFuncs() generates all of the struct methods
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: genFuncs() generates helper methods attached to composite structs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}

// genTests() generates all of the tests
// TODO: (shance) figure out a better way to test the toGA(), toAlpha(), and toBeta() functions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can remove TODO since you addressed it I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

"GA": "",
}

// ApiService is a struct to hold all of the relevant data for generating a composite service
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: ApiService holds relevant data for generating a composite type + helper methods for a single API service.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

json.Unmarshal([]byte(byteValue), &result)

// Queue of ApiService names for BFS
typesQueue := []string{}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the extra spaces between here and line 136.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@rramkumar1
Copy link
Contributor

/lgtm
/approve

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Apr 29, 2019
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: rramkumar1, spencerhance

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Apr 29, 2019
@k8s-ci-robot k8s-ci-robot merged commit 4918eb2 into kubernetes:master Apr 29, 2019
@spencerhance spencerhance deleted the new-composite-types branch June 5, 2019 02:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. lgtm "Looks good to me", indicates that a PR is ready to be merged. ok-to-test Indicates a non-member PR verified by an org member that is safe to test. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants