Merge pull request #50578 from pwittrock/depsutils

Automatic merge from submit-queue (batch tested with PRs 51047, 48573, 50764, 51092, 50578)

Library updates to better support removing kubectl deps on kubernetes/kubernetes

**What this PR does / why we need it**:

**Which issue this PR fixes**: kubernetes/kubectl#50

**Special notes for your reviewer**:

**Release note**:

```release-note
NONE
```
pull/6/head
Kubernetes Submit Queue 2017-08-23 17:56:20 -07:00 committed by GitHub
commit 83e9cadc02
11 changed files with 414 additions and 0 deletions

View File

@ -184,6 +184,7 @@ filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/kubectl/apps:all-srcs",
"//pkg/kubectl/cmd:all-srcs",
"//pkg/kubectl/metricsutil:all-srcs",
"//pkg/kubectl/plugins:all-srcs",

42
pkg/kubectl/apps/BUILD Normal file
View File

@ -0,0 +1,42 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = ["kind_visitor.go"],
tags = ["automanaged"],
deps = ["//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
go_test(
name = "go_default_xtest",
srcs = [
"apps_suite_test.go",
"kind_visitor_test.go",
],
deps = [
":go_default_library",
"//vendor/github.com/onsi/ginkgo:go_default_library",
"//vendor/github.com/onsi/gomega:go_default_library",
],
)

View File

@ -0,0 +1,29 @@
/*
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 apps_test
import (
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func TestApps(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Apps Suite")
}

View File

@ -0,0 +1,95 @@
/*
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 apps
import (
"fmt"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// KindVisitor is used with GroupKindElement to call a particular function depending on the
// Kind of a schema.GroupKind
type KindVisitor interface {
VisitDaemonSet(kind GroupKindElement)
VisitDeployment(kind GroupKindElement)
VisitJob(kind GroupKindElement)
VisitPod(kind GroupKindElement)
VisitReplicaSet(kind GroupKindElement)
VisitReplicationController(kind GroupKindElement)
VisitStatefulSet(kind GroupKindElement)
}
// GroupKindElement defines a Kubernetes API group elem
type GroupKindElement schema.GroupKind
// Accept calls the Visit method on visitor that corresponds to elem's Kind
func (elem GroupKindElement) Accept(visitor KindVisitor) error {
if elem.GroupMatch("apps", "extensions") && elem.Kind == "DaemonSet" {
visitor.VisitDaemonSet(elem)
return nil
}
if elem.GroupMatch("apps", "extensions") && elem.Kind == "Deployment" {
visitor.VisitDeployment(elem)
return nil
}
if elem.GroupMatch("batch") && elem.Kind == "Job" {
visitor.VisitJob(elem)
return nil
}
if elem.GroupMatch("", "core") && elem.Kind == "Pod" {
visitor.VisitPod(elem)
return nil
}
if elem.GroupMatch("extensions") && elem.Kind == "ReplicaSet" {
visitor.VisitReplicaSet(elem)
return nil
}
if elem.GroupMatch("", "core") && elem.Kind == "ReplicationController" {
visitor.VisitReplicationController(elem)
return nil
}
if elem.GroupMatch("apps") && elem.Kind == "StatefulSet" {
visitor.VisitStatefulSet(elem)
return nil
}
return fmt.Errorf("no visitor method exists for %v", elem)
}
// GroupMatch returns true if and only if elem's group matches one
// of the group arguments
func (elem GroupKindElement) GroupMatch(groups ...string) bool {
for _, g := range groups {
if elem.Group == g {
return true
}
}
return false
}
// NoOpKindVisitor implements KindVisitor with no-op functions.
type NoOpKindVisitor struct{}
var _ KindVisitor = &NoOpKindVisitor{}
func (*NoOpKindVisitor) VisitDaemonSet(kind GroupKindElement) {}
func (*NoOpKindVisitor) VisitDeployment(kind GroupKindElement) {}
func (*NoOpKindVisitor) VisitJob(kind GroupKindElement) {}
func (*NoOpKindVisitor) VisitPod(kind GroupKindElement) {}
func (*NoOpKindVisitor) VisitReplicaSet(kind GroupKindElement) {}
func (*NoOpKindVisitor) VisitReplicationController(kind GroupKindElement) {}
func (*NoOpKindVisitor) VisitStatefulSet(kind GroupKindElement) {}

View File

@ -0,0 +1,173 @@
/*
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 apps_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"k8s.io/kubernetes/pkg/kubectl/apps"
)
var _ = Describe("When KindVisitor accepts a GroupKind", func() {
var visitor *TestKindVisitor
BeforeEach(func() {
visitor = &TestKindVisitor{map[string]int{}}
})
It("should Visit DaemonSet iff the Kind is a DaemonSet", func() {
kind := apps.GroupKindElement{
Kind: "DaemonSet",
Group: "apps",
}
Expect(kind.Accept(visitor)).ShouldNot(HaveOccurred())
Expect(visitor.visits).To(Equal(map[string]int{
"DaemonSet": 1,
}))
kind = apps.GroupKindElement{
Kind: "DaemonSet",
Group: "extensions",
}
Expect(kind.Accept(visitor)).ShouldNot(HaveOccurred())
Expect(visitor.visits).To(Equal(map[string]int{
"DaemonSet": 2,
}))
})
It("should Visit Deployment iff the Kind is a Deployment", func() {
kind := apps.GroupKindElement{
Kind: "Deployment",
Group: "apps",
}
Expect(kind.Accept(visitor)).ShouldNot(HaveOccurred())
Expect(visitor.visits).To(Equal(map[string]int{
"Deployment": 1,
}))
kind = apps.GroupKindElement{
Kind: "Deployment",
Group: "extensions",
}
Expect(kind.Accept(visitor)).ShouldNot(HaveOccurred())
Expect(visitor.visits).To(Equal(map[string]int{
"Deployment": 2,
}))
})
It("should Visit Job iff the Kind is a Job", func() {
kind := apps.GroupKindElement{
Kind: "Job",
Group: "batch",
}
Expect(kind.Accept(visitor)).ShouldNot(HaveOccurred())
Expect(visitor.visits).To(Equal(map[string]int{
"Job": 1,
}))
})
It("should Visit Pod iff the Kind is a Pod", func() {
kind := apps.GroupKindElement{
Kind: "Pod",
Group: "",
}
Expect(kind.Accept(visitor)).ShouldNot(HaveOccurred())
Expect(visitor.visits).To(Equal(map[string]int{
"Pod": 1,
}))
kind = apps.GroupKindElement{
Kind: "Pod",
Group: "core",
}
Expect(kind.Accept(visitor)).ShouldNot(HaveOccurred())
Expect(visitor.visits).To(Equal(map[string]int{
"Pod": 2,
}))
})
It("should Visit ReplicationController iff the Kind is a ReplicationController", func() {
kind := apps.GroupKindElement{
Kind: "ReplicationController",
Group: "",
}
Expect(kind.Accept(visitor)).ShouldNot(HaveOccurred())
Expect(visitor.visits).To(Equal(map[string]int{
"ReplicationController": 1,
}))
kind = apps.GroupKindElement{
Kind: "ReplicationController",
Group: "core",
}
Expect(kind.Accept(visitor)).ShouldNot(HaveOccurred())
Expect(visitor.visits).To(Equal(map[string]int{
"ReplicationController": 2,
}))
})
It("should Visit ReplicaSet iff the Kind is a ReplicaSet", func() {
kind := apps.GroupKindElement{
Kind: "ReplicaSet",
Group: "extensions",
}
Expect(kind.Accept(visitor)).ShouldNot(HaveOccurred())
Expect(visitor.visits).To(Equal(map[string]int{
"ReplicaSet": 1,
}))
})
It("should Visit StatefulSet iff the Kind is a StatefulSet", func() {
kind := apps.GroupKindElement{
Kind: "StatefulSet",
Group: "apps",
}
Expect(kind.Accept(visitor)).ShouldNot(HaveOccurred())
Expect(visitor.visits).To(Equal(map[string]int{
"StatefulSet": 1,
}))
})
It("should give an error if the Kind is unknown", func() {
kind := apps.GroupKindElement{
Kind: "Unknown",
Group: "apps",
}
Expect(kind.Accept(visitor)).Should(HaveOccurred())
Expect(visitor.visits).To(Equal(map[string]int{}))
})
})
// TestKindVisitor increments a value each time a Visit method was called
type TestKindVisitor struct {
visits map[string]int
}
var _ apps.KindVisitor = &TestKindVisitor{}
func (t *TestKindVisitor) Visit(kind apps.GroupKindElement) { t.visits[kind.Kind] += 1 }
func (t *TestKindVisitor) VisitDaemonSet(kind apps.GroupKindElement) { t.Visit(kind) }
func (t *TestKindVisitor) VisitDeployment(kind apps.GroupKindElement) { t.Visit(kind) }
func (t *TestKindVisitor) VisitJob(kind apps.GroupKindElement) { t.Visit(kind) }
func (t *TestKindVisitor) VisitPod(kind apps.GroupKindElement) { t.Visit(kind) }
func (t *TestKindVisitor) VisitReplicaSet(kind apps.GroupKindElement) { t.Visit(kind) }
func (t *TestKindVisitor) VisitReplicationController(kind apps.GroupKindElement) { t.Visit(kind) }
func (t *TestKindVisitor) VisitStatefulSet(kind apps.GroupKindElement) { t.Visit(kind) }

View File

@ -35,6 +35,7 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
"//vendor/k8s.io/client-go/discovery:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library",
"//vendor/k8s.io/client-go/rest/fake:go_default_library",
],

View File

@ -34,6 +34,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/client-go/discovery"
"k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/rest/fake"
fedclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset"
@ -308,6 +309,10 @@ func (f *FakeFactory) RESTClient() (*restclient.RESTClient, error) {
return nil, nil
}
func (f *FakeFactory) KubernetesClientSet() (*kubernetes.Clientset, error) {
return nil, nil
}
func (f *FakeFactory) ClientSet() (internalclientset.Interface, error) {
return nil, nil
}
@ -583,6 +588,33 @@ func (f *fakeAPIFactory) JSONEncoder() runtime.Encoder {
return testapi.Default.Codec()
}
func (f *fakeAPIFactory) KubernetesClientSet() (*kubernetes.Clientset, error) {
fakeClient := f.tf.Client.(*fake.RESTClient)
clientset := kubernetes.NewForConfigOrDie(f.tf.ClientConfig)
clientset.CoreV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.AuthorizationV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.AuthorizationV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.AuthorizationV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.AuthorizationV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.AutoscalingV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.AutoscalingV2alpha1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.BatchV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.BatchV2alpha1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.CertificatesV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.ExtensionsV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.RbacV1alpha1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.RbacV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.StorageV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.StorageV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.AppsV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.AppsV1beta2().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.PolicyV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.DiscoveryClient.RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
return clientset, f.tf.Err
}
func (f *fakeAPIFactory) ClientSet() (internalclientset.Interface, error) {
// Swap the HTTP client out of the REST client with the fake
// version.

View File

@ -67,6 +67,7 @@ go_library(
"//vendor/k8s.io/apiserver/pkg/util/flag:go_default_library",
"//vendor/k8s.io/client-go/discovery:go_default_library",
"//vendor/k8s.io/client-go/dynamic:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library",
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
"//vendor/k8s.io/client-go/util/homedir:go_default_library",

View File

@ -21,6 +21,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/discovery"
"k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
fedclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset"
@ -58,6 +59,35 @@ type ClientCache struct {
// argument evaluation
discoveryClientFactory DiscoveryClientFactory
discoveryClient discovery.DiscoveryInterface
kubernetesClientCache kubernetesClientCache
}
// kubernetesClientCache creates a new kubernetes.Clientset one time
// and then returns the result for all future requests
type kubernetesClientCache struct {
// once makes sure the client is only initialized once
once sync.Once
// client is the cached client value
client *kubernetes.Clientset
// err is the cached error value
err error
}
// KubernetesClientSetForVersion returns a new kubernetes.Clientset. It will cache the value
// the first time it is called and return the cached value on subsequent calls.
// If an error is encountered the first time KubernetesClientSetForVersion is called,
// the error will be cached.
func (c *ClientCache) KubernetesClientSetForVersion(requiredVersion *schema.GroupVersion) (*kubernetes.Clientset, error) {
c.kubernetesClientCache.once.Do(func() {
config, err := c.ClientConfigForVersion(requiredVersion)
if err != nil {
c.kubernetesClientCache.err = err
return
}
c.kubernetesClientCache.client, c.kubernetesClientCache.err = kubernetes.NewForConfig(config)
})
return c.kubernetesClientCache.client, c.kubernetesClientCache.err
}
// also looks up the discovery client. We can't do this during init because the flags won't have been set

View File

@ -43,6 +43,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/serializer/json"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/discovery"
"k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
fedclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset"
@ -91,6 +92,10 @@ type ClientAccessFactory interface {
// ClientSet gives you back an internal, generated clientset
ClientSet() (internalclientset.Interface, error)
// KubernetesClientSet gives you back an external clientset
KubernetesClientSet() (*kubernetes.Clientset, error)
// Returns a RESTClient for accessing Kubernetes resources or an error.
RESTClient() (*restclient.RESTClient, error)
// Returns a client.Config for accessing the Kubernetes server.

View File

@ -37,6 +37,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
utilflag "k8s.io/apiserver/pkg/util/flag"
"k8s.io/client-go/discovery"
"k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
@ -167,6 +168,10 @@ func (f *ring0Factory) DiscoveryClient() (discovery.CachedDiscoveryInterface, er
return f.discoveryFactory.DiscoveryClient()
}
func (f *ring0Factory) KubernetesClientSet() (*kubernetes.Clientset, error) {
return f.clientCache.KubernetesClientSetForVersion(nil)
}
func (f *ring0Factory) ClientSet() (internalclientset.Interface, error) {
return f.clientCache.ClientSetForVersion(nil)
}