Merge pull request #45184 from DirectXMan12/feature/metrics-client-gen-tweaks

Automatic merge from submit-queue (batch tested with PRs 46801, 45184, 45930, 46192, 45563)

Tweak client-gen to support metrics API

This PR adds support to client-gen for readonly APIs as well as for customizing the resource name used for a given type.

This is required to support the clients generated for `k8s.io/metrics/pkg/apis/metrics`.

cc @caesarxuchao 

Currently based on #45180

**Release note**:
```release-note
NONE
```
This commit is contained in:
Kubernetes Submit Queue 2017-06-02 18:05:50 -07:00 committed by GitHub
commit caead74171
20 changed files with 243 additions and 32 deletions

View File

@ -40,13 +40,15 @@ func NameSystems() namer.NameSystems {
pluralExceptions := map[string]string{
"Endpoints": "Endpoints",
}
lowercaseNamer := namer.NewAllLowercasePluralNamer(pluralExceptions)
return namer.NameSystems{
"public": namer.NewPublicNamer(0),
"private": namer.NewPrivateNamer(0),
"raw": namer.NewRawNamer("", nil),
"publicPlural": namer.NewPublicPluralNamer(pluralExceptions),
"privatePlural": namer.NewPrivatePluralNamer(pluralExceptions),
"allLowercasePlural": namer.NewAllLowercasePluralNamer(pluralExceptions),
"allLowercasePlural": lowercaseNamer,
"resource": NewTagOverrideNamer("resourceName", lowercaseNamer),
}
}
@ -335,3 +337,27 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
return generator.Packages(packageList)
}
// tagOverrideNamer is a namer which pulls names from a given tag, if specified,
// and otherwise falls back to a different namer.
type tagOverrideNamer struct {
tagName string
fallback namer.Namer
}
func (n *tagOverrideNamer) Name(t *types.Type) string {
if nameOverride := extractTag(n.tagName, t.SecondClosestCommentLines); nameOverride != "" {
return nameOverride
}
return n.fallback.Name(t)
}
// NewTagOverrideNamer creates a namer.Namer which uses the contents of the given tag as
// the name, or falls back to another Namer if the tag is not present.
func NewTagOverrideNamer(tagName string, fallback namer.Namer) namer.Namer {
return &tagOverrideNamer{
tagName: tagName,
fallback: fallback,
}
}

View File

@ -150,6 +150,8 @@ func (g *genFakeForType) GenerateType(c *generator.Context, t *types.Type, w io.
noMethods := extractBoolTagOrDie("noMethods", t.SecondClosestCommentLines) == true
readonly := extractBoolTagOrDie("readonly", t.SecondClosestCommentLines) == true
if namespaced {
sw.Do(structNamespaced, m)
} else {
@ -159,6 +161,9 @@ func (g *genFakeForType) GenerateType(c *generator.Context, t *types.Type, w io.
if !noMethods {
sw.Do(resource, m)
sw.Do(kind, m)
}
if !noMethods && !readonly {
sw.Do(createTemplate, m)
sw.Do(updateTemplate, m)
// Generate the UpdateStatus method if the type has a status
@ -167,6 +172,9 @@ func (g *genFakeForType) GenerateType(c *generator.Context, t *types.Type, w io.
}
sw.Do(deleteTemplate, m)
sw.Do(deleteCollectionTemplate, m)
}
if !noMethods {
sw.Do(getTemplate, m)
if hasObjectMeta(t) {
sw.Do(listUsingOptionsTemplate, m)
@ -174,6 +182,9 @@ func (g *genFakeForType) GenerateType(c *generator.Context, t *types.Type, w io.
sw.Do(listTemplate, m)
}
sw.Do(watchTemplate, m)
}
if !noMethods && !readonly {
sw.Do(patchTemplate, m)
}
@ -198,7 +209,7 @@ type Fake$.type|publicPlural$ struct {
`
var resource = `
var $.type|allLowercasePlural$Resource = $.GroupVersionResource|raw${Group: "$.groupName$", Version: "$.version$", Resource: "$.type|allLowercasePlural$"}
var $.type|allLowercasePlural$Resource = $.GroupVersionResource|raw${Group: "$.groupName$", Version: "$.version$", Resource: "$.type|resource$"}
`
var kind = `

View File

@ -102,8 +102,13 @@ func (g *genClientForType) GenerateType(c *generator.Context, t *types.Type, w i
}
noMethods := extractBoolTagOrDie("noMethods", t.SecondClosestCommentLines) == true
readonly := extractBoolTagOrDie("readonly", t.SecondClosestCommentLines) == true
sw.Do(interfaceTemplate1, m)
if !noMethods {
if readonly {
sw.Do(interfaceTemplateReadonly, m)
} else {
sw.Do(interfaceTemplate2, m)
// Include the UpdateStatus method if the type has a status
if genStatus(t) {
@ -111,6 +116,7 @@ func (g *genClientForType) GenerateType(c *generator.Context, t *types.Type, w i
}
sw.Do(interfaceTemplate3, m)
}
}
sw.Do(interfaceTemplate4, m)
if namespaced {
@ -121,7 +127,7 @@ func (g *genClientForType) GenerateType(c *generator.Context, t *types.Type, w i
sw.Do(newStructNonNamespaced, m)
}
if !noMethods {
if !noMethods && !readonly {
sw.Do(createTemplate, m)
sw.Do(updateTemplate, m)
// Generate the UpdateStatus method if the type has a status
@ -130,9 +136,15 @@ func (g *genClientForType) GenerateType(c *generator.Context, t *types.Type, w i
}
sw.Do(deleteTemplate, m)
sw.Do(deleteCollectionTemplate, m)
}
if !noMethods {
sw.Do(getTemplate, m)
sw.Do(listTemplate, m)
sw.Do(watchTemplate, m)
}
if !noMethods && !readonly {
sw.Do(patchTemplate, m)
}
@ -177,6 +189,11 @@ var interfaceTemplate3 = `
Watch(opts $.ListOptions|raw$) ($.watchInterface|raw$, error)
Patch(name string, pt $.PatchType|raw$, data []byte, subresources ...string) (result *$.type|raw$, err error)`
var interfaceTemplateReadonly = `
Get(name string, options $.GetOptions|raw$) (*$.type|raw$, error)
List(opts $.ListOptions|raw$) (*$.type|raw$List, error)
Watch(opts $.ListOptions|raw$) ($.watchInterface|raw$, error)`
var interfaceTemplate4 = `
$.type|public$Expansion
}
@ -224,7 +241,7 @@ func (c *$.type|privatePlural$) List(opts $.ListOptions|raw$) (result *$.type|ra
result = &$.type|raw$List{}
err = c.client.Get().
$if .namespaced$Namespace(c.ns).$end$
Resource("$.type|allLowercasePlural$").
Resource("$.type|resource$").
VersionedParams(&opts, $.schemeParameterCodec|raw$).
Do().
Into(result)
@ -237,7 +254,7 @@ func (c *$.type|privatePlural$) Get(name string, options $.GetOptions|raw$) (res
result = &$.type|raw${}
err = c.client.Get().
$if .namespaced$Namespace(c.ns).$end$
Resource("$.type|allLowercasePlural$").
Resource("$.type|resource$").
Name(name).
VersionedParams(&options, $.schemeParameterCodec|raw$).
Do().
@ -251,7 +268,7 @@ var deleteTemplate = `
func (c *$.type|privatePlural$) Delete(name string, options *$.DeleteOptions|raw$) error {
return c.client.Delete().
$if .namespaced$Namespace(c.ns).$end$
Resource("$.type|allLowercasePlural$").
Resource("$.type|resource$").
Name(name).
Body(options).
Do().
@ -264,7 +281,7 @@ var deleteCollectionTemplate = `
func (c *$.type|privatePlural$) DeleteCollection(options *$.DeleteOptions|raw$, listOptions $.ListOptions|raw$) error {
return c.client.Delete().
$if .namespaced$Namespace(c.ns).$end$
Resource("$.type|allLowercasePlural$").
Resource("$.type|resource$").
VersionedParams(&listOptions, $.schemeParameterCodec|raw$).
Body(options).
Do().
@ -278,7 +295,7 @@ func (c *$.type|privatePlural$) Create($.type|private$ *$.type|raw$) (result *$.
result = &$.type|raw${}
err = c.client.Post().
$if .namespaced$Namespace(c.ns).$end$
Resource("$.type|allLowercasePlural$").
Resource("$.type|resource$").
Body($.type|private$).
Do().
Into(result)
@ -292,7 +309,7 @@ func (c *$.type|privatePlural$) Update($.type|private$ *$.type|raw$) (result *$.
result = &$.type|raw${}
err = c.client.Put().
$if .namespaced$Namespace(c.ns).$end$
Resource("$.type|allLowercasePlural$").
Resource("$.type|resource$").
Name($.type|private$.Name).
Body($.type|private$).
Do().
@ -309,7 +326,7 @@ func (c *$.type|privatePlural$) UpdateStatus($.type|private$ *$.type|raw$) (resu
result = &$.type|raw${}
err = c.client.Put().
$if .namespaced$Namespace(c.ns).$end$
Resource("$.type|allLowercasePlural$").
Resource("$.type|resource$").
Name($.type|private$.Name).
SubResource("status").
Body($.type|private$).
@ -325,7 +342,7 @@ func (c *$.type|privatePlural$) Watch(opts $.ListOptions|raw$) ($.watchInterface
opts.Watch = true
return c.client.Get().
$if .namespaced$Namespace(c.ns).$end$
Resource("$.type|allLowercasePlural$").
Resource("$.type|resource$").
VersionedParams(&opts, $.schemeParameterCodec|raw$).
Watch()
}
@ -337,7 +354,7 @@ func (c *$.type|privatePlural$) Patch(name string, pt $.PatchType|raw$, data []b
result = &$.type|raw${}
err = c.client.Patch(pt).
$if .namespaced$Namespace(c.ns).$end$
Resource("$.type|allLowercasePlural$").
Resource("$.type|resource$").
SubResource(subresources...).
Name(name).
Body(data).

View File

@ -31,3 +31,14 @@ func extractBoolTagOrDie(key string, lines []string) bool {
}
return val
}
// extractTag gets the comment-tags for the key. If the tag did not exist, it
// returns the empty string.
func extractTag(key string, lines []string) string {
val, present := types.ExtractCommentTags("+", lines)[key]
if !present || len(val) < 1 {
return ""
}
return val[0]
}

View File

@ -104,3 +104,4 @@ ${informergen} \
vendor/k8s.io/kube-aggregator/hack/update-codegen.sh
vendor/k8s.io/sample-apiserver/hack/update-codegen.sh
vendor/k8s.io/kube-apiextensions-server/hack/update-codegen.sh
vendor/k8s.io/metrics/hack/update-codegen.sh

View File

@ -27,5 +27,6 @@ kube::golang::setup_env
vendor/k8s.io/kube-aggregator/hack/verify-codegen.sh
vendor/k8s.io/sample-apiserver/hack/verify-codegen.sh
vendor/k8s.io/kube-apiextensions-server/hack/verify-codegen.sh
vendor/k8s.io/metrics/hack/verify-codegen.sh
"${KUBE_ROOT}/hack/update-codegen.sh" --verify-only

View File

@ -360,8 +360,7 @@ func (tc *testCase) prepareTestClient(t *testing.T) (*fake.Clientset, *metricsfa
fakeClient.AddWatchReactor("*", core.DefaultWatchReactor(fakeWatch, nil))
fakeMetricsClient := &metricsfake.Clientset{}
// NB: we have to sound like Gollum due to gengo's inability to handle already-plural resource names
fakeMetricsClient.AddReactor("list", "podmetricses", func(action core.Action) (handled bool, ret runtime.Object, err error) {
fakeMetricsClient.AddReactor("list", "pods", func(action core.Action) (handled bool, ret runtime.Object, err error) {
tc.Lock()
defer tc.Unlock()

View File

@ -70,7 +70,7 @@ func (tc *restClientTestCase) prepareTestClient(t *testing.T) (*metricsfake.Clie
fakeCMClient := &cmfake.FakeCustomMetricsClient{}
if isResource {
fakeMetricsClient.AddReactor("list", "podmetricses", func(action core.Action) (handled bool, ret runtime.Object, err error) {
fakeMetricsClient.AddReactor("list", "pods", func(action core.Action) (handled bool, ret runtime.Object, err error) {
metrics := &metricsapi.PodMetricsList{}
for i, containers := range tc.reportedPodMetrics {
metric := metricsapi.PodMetrics{

View File

@ -135,7 +135,7 @@ func (tc *replicaCalcTestCase) prepareTestClient(t *testing.T) (*fake.Clientset,
fakeMetricsClient := &metricsfake.Clientset{}
// NB: we have to sound like Gollum due to gengo's inability to handle already-plural resource names
fakeMetricsClient.AddReactor("list", "podmetricses", func(action core.Action) (handled bool, ret runtime.Object, err error) {
fakeMetricsClient.AddReactor("list", "pods", func(action core.Action) (handled bool, ret runtime.Object, err error) {
if tc.resource != nil {
metrics := &metricsapi.PodMetricsList{}
for i, resValue := range tc.resource.levels {

View File

@ -0,0 +1,42 @@
#!/bin/bash
# Copyright 2017 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit
set -o nounset
set -o pipefail
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../../../../..
METRICS_ROOT=$(dirname "${BASH_SOURCE}")/..
source "${KUBE_ROOT}/hack/lib/init.sh"
# Register function to be called on EXIT to remove generated binary.
function cleanup {
rm -f "${CLIENTGEN:-}"
}
trap cleanup EXIT
echo "Building client-gen"
CLIENTGEN="${PWD}/client-gen-binary"
go build -o "${CLIENTGEN}" ./cmd/libs/go2idl/client-gen
PREFIX=k8s.io/metrics/pkg/apis
INPUT_BASE="--input-base ${PREFIX}"
CLIENTSET_PATH="--clientset-path k8s.io/metrics/pkg/client/clientset_generated"
${CLIENTGEN} --clientset-name="clientset" ${INPUT_BASE} --input metrics/v1alpha1 ${CLIENTSET_PATH} --output-base ${KUBE_ROOT}/vendor
# we skip informers and listers for metrics, because we don't quite support the requisite operations yet
# we skip generating the internal clientset as it's not really needed

View File

@ -0,0 +1,51 @@
#!/bin/bash
# Copyright 2017 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit
set -o nounset
set -o pipefail
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../../../../..
METRICS_ROOT=$(dirname "${BASH_SOURCE}")/..
source "${KUBE_ROOT}/hack/lib/init.sh"
DIFFROOT="${METRICS_ROOT}/pkg"
TMP_DIFFROOT="${METRICS_ROOT}/_tmp/pkg"
_tmp="${METRICS_ROOT}/_tmp"
cleanup() {
rm -rf "${_tmp}"
}
trap "cleanup" EXIT SIGINT
cleanup
mkdir -p "${TMP_DIFFROOT}"
cp -a "${DIFFROOT}"/* "${TMP_DIFFROOT}"
"${METRICS_ROOT}/hack/update-codegen.sh"
echo "diffing ${DIFFROOT} against freshly generated codegen"
ret=0
diff -Naupr "${DIFFROOT}" "${TMP_DIFFROOT}" || ret=$?
cp -a "${TMP_DIFFROOT}"/* "${DIFFROOT}"
if [[ $ret -eq 0 ]]
then
echo "${DIFFROOT} up to date."
else
echo "${DIFFROOT} is out of date. Please run hack/update-codegen.sh"
exit 1
fi

View File

@ -17,7 +17,6 @@ go_library(
deps = [
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/client-go/discovery:go_default_library",
"//vendor/k8s.io/client-go/plugin/pkg/client/auth:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library",
"//vendor/k8s.io/client-go/util/flowcontrol:go_default_library",
"//vendor/k8s.io/metrics/pkg/client/clientset_generated/clientset/typed/metrics/v1alpha1:go_default_library",

View File

@ -19,7 +19,6 @@ package clientset
import (
glog "github.com/golang/glog"
discovery "k8s.io/client-go/discovery"
_ "k8s.io/client-go/plugin/pkg/client/auth"
rest "k8s.io/client-go/rest"
flowcontrol "k8s.io/client-go/util/flowcontrol"
metricsv1alpha1 "k8s.io/metrics/pkg/client/clientset_generated/clientset/typed/metrics/v1alpha1"

View File

@ -12,15 +12,19 @@ go_library(
srcs = [
"clientset_generated.go",
"doc.go",
"register.go",
],
tags = ["automanaged"],
deps = [
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
"//vendor/k8s.io/client-go/discovery:go_default_library",
"//vendor/k8s.io/client-go/discovery/fake:go_default_library",
"//vendor/k8s.io/client-go/pkg/api:go_default_library",
"//vendor/k8s.io/client-go/testing:go_default_library",
"//vendor/k8s.io/metrics/pkg/apis/metrics/v1alpha1:go_default_library",
"//vendor/k8s.io/metrics/pkg/client/clientset_generated/clientset:go_default_library",
"//vendor/k8s.io/metrics/pkg/client/clientset_generated/clientset/typed/metrics/v1alpha1:go_default_library",
"//vendor/k8s.io/metrics/pkg/client/clientset_generated/clientset/typed/metrics/v1alpha1/fake:go_default_library",

View File

@ -21,7 +21,6 @@ import (
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/discovery"
fakediscovery "k8s.io/client-go/discovery/fake"
"k8s.io/client-go/pkg/api"
"k8s.io/client-go/testing"
clientset "k8s.io/metrics/pkg/client/clientset_generated/clientset"
metricsv1alpha1 "k8s.io/metrics/pkg/client/clientset_generated/clientset/typed/metrics/v1alpha1"
@ -33,7 +32,7 @@ import (
// without applying any validations and/or defaults. It shouldn't be considered a replacement
// for a real clientset and is mostly useful in simple unit tests.
func NewSimpleClientset(objects ...runtime.Object) *Clientset {
o := testing.NewObjectTracker(api.Scheme, api.Codecs.UniversalDecoder())
o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder())
for _, obj := range objects {
if err := o.Add(obj); err != nil {
panic(err)

View File

@ -0,0 +1,53 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package fake
import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
schema "k8s.io/apimachinery/pkg/runtime/schema"
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
metricsv1alpha1 "k8s.io/metrics/pkg/apis/metrics/v1alpha1"
)
var scheme = runtime.NewScheme()
var codecs = serializer.NewCodecFactory(scheme)
var parameterCodec = runtime.NewParameterCodec(scheme)
func init() {
v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"})
AddToScheme(scheme)
}
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
// of clientsets, like in:
//
// import (
// "k8s.io/client-go/kubernetes"
// clientsetscheme "k8s.io/client-go/kuberentes/scheme"
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
// )
//
// kclientset, _ := kubernetes.NewForConfig(c)
// aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
//
// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
// correctly.
func AddToScheme(scheme *runtime.Scheme) {
metricsv1alpha1.AddToScheme(scheme)
}

View File

@ -30,7 +30,7 @@ type FakeNodeMetricses struct {
Fake *FakeMetricsV1alpha1
}
var nodemetricsesResource = schema.GroupVersionResource{Group: "metrics", Version: "v1alpha1", Resource: "nodemetricses"}
var nodemetricsesResource = schema.GroupVersionResource{Group: "metrics", Version: "v1alpha1", Resource: "nodes"}
var nodemetricsesKind = schema.GroupVersionKind{Group: "metrics", Version: "v1alpha1", Kind: "NodeMetrics"}

View File

@ -31,7 +31,7 @@ type FakePodMetricses struct {
ns string
}
var podmetricsesResource = schema.GroupVersionResource{Group: "metrics", Version: "v1alpha1", Resource: "podmetricses"}
var podmetricsesResource = schema.GroupVersionResource{Group: "metrics", Version: "v1alpha1", Resource: "pods"}
var podmetricsesKind = schema.GroupVersionKind{Group: "metrics", Version: "v1alpha1", Kind: "PodMetrics"}

View File

@ -35,7 +35,6 @@ type NodeMetricsInterface interface {
Get(name string, options v1.GetOptions) (*v1alpha1.NodeMetrics, error)
List(opts v1.ListOptions) (*v1alpha1.NodeMetricsList, error)
Watch(opts v1.ListOptions) (watch.Interface, error)
NodeMetricsExpansion
}
@ -76,8 +75,8 @@ func (c *nodeMetricses) List(opts v1.ListOptions) (result *v1alpha1.NodeMetricsL
// Watch returns a watch.Interface that watches the requested nodeMetricses.
func (c *nodeMetricses) Watch(opts v1.ListOptions) (watch.Interface, error) {
opts.Watch = true
return c.client.Get().
Prefix("watch").
Resource("nodes").
VersionedParams(&opts, scheme.ParameterCodec).
Watch()

View File

@ -35,7 +35,6 @@ type PodMetricsInterface interface {
Get(name string, options v1.GetOptions) (*v1alpha1.PodMetrics, error)
List(opts v1.ListOptions) (*v1alpha1.PodMetricsList, error)
Watch(opts v1.ListOptions) (watch.Interface, error)
PodMetricsExpansion
}
@ -80,8 +79,8 @@ func (c *podMetricses) List(opts v1.ListOptions) (result *v1alpha1.PodMetricsLis
// Watch returns a watch.Interface that watches the requested podMetricses.
func (c *podMetricses) Watch(opts v1.ListOptions) (watch.Interface, error) {
opts.Watch = true
return c.client.Get().
Prefix("watch").
Namespace(c.ns).
Resource("pods").
VersionedParams(&opts, scheme.ParameterCodec).