Bumping k8s dependencies to 1.13

This commit is contained in:
Cheng Xing
2018-11-16 14:08:25 -08:00
parent 305407125c
commit b4c0b68ec7
8002 changed files with 884099 additions and 276228 deletions

View File

@@ -28,38 +28,45 @@ go_library(
"//pkg/apis/core:go_default_library",
"//pkg/apis/core/v1:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
"//pkg/controller:go_default_library",
"//pkg/kubectl:go_default_library",
"//pkg/kubectl/genericclioptions:go_default_library",
"//vendor/k8s.io/api/apps/v1:go_default_library",
"//vendor/k8s.io/api/apps/v1beta1:go_default_library",
"//vendor/k8s.io/api/apps/v1beta2:go_default_library",
"//vendor/k8s.io/api/batch/v1:go_default_library",
"//vendor/k8s.io/api/batch/v1beta1:go_default_library",
"//vendor/k8s.io/api/batch/v2alpha1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels: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/watch:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library",
"//pkg/kubectl/scheme:go_default_library",
"//staging/src/k8s.io/api/apps/v1:go_default_library",
"//staging/src/k8s.io/api/apps/v1beta1:go_default_library",
"//staging/src/k8s.io/api/apps/v1beta2:go_default_library",
"//staging/src/k8s.io/api/batch/v1:go_default_library",
"//staging/src/k8s.io/api/batch/v1beta1:go_default_library",
"//staging/src/k8s.io/api/batch/v2alpha1:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/api/extensions/v1beta1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
"//staging/src/k8s.io/cli-runtime/pkg/genericclioptions:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
"//staging/src/k8s.io/client-go/rest:go_default_library",
"//staging/src/k8s.io/client-go/tools/watch:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = [
"canbeautoscaled_test.go",
"canbeexposed_test.go",
"helpers_test.go",
"logsforobject_test.go",
"mapbasedselectorforobject_test.go",
"objectpauser_test.go",
"objectresumer_test.go",
"portsforobject_test.go",
"protocolsforobject_test.go",
"updatepodspec_test.go",
],
embed = [":go_default_library"],
deps = [
@@ -67,18 +74,24 @@ go_test(
"//pkg/apis/batch:go_default_library",
"//pkg/apis/core:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
"//pkg/controller:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels: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/util/diff:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
"//vendor/k8s.io/client-go/testing:go_default_library",
"//staging/src/k8s.io/api/apps/v1:go_default_library",
"//staging/src/k8s.io/api/apps/v1beta1:go_default_library",
"//staging/src/k8s.io/api/apps/v1beta2:go_default_library",
"//staging/src/k8s.io/api/batch/v1:go_default_library",
"//staging/src/k8s.io/api/batch/v1beta1:go_default_library",
"//staging/src/k8s.io/api/batch/v2alpha1:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/api/extensions/v1beta1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
"//staging/src/k8s.io/client-go/testing:go_default_library",
],
)

View File

@@ -24,30 +24,29 @@ import (
"k8s.io/api/core/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/cli-runtime/pkg/genericclioptions"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
api "k8s.io/kubernetes/pkg/apis/core"
apiv1 "k8s.io/kubernetes/pkg/apis/core/v1"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
)
// attachablePodForObject returns the pod to which to attach given an object.
func attachablePodForObject(restClientGetter genericclioptions.RESTClientGetter, object runtime.Object, timeout time.Duration) (*api.Pod, error) {
func attachablePodForObject(restClientGetter genericclioptions.RESTClientGetter, object runtime.Object, timeout time.Duration) (*corev1.Pod, error) {
switch t := object.(type) {
case *api.Pod:
return t, nil
externalPod := &corev1.Pod{}
err := apiv1.Convert_core_Pod_To_v1_Pod(t, externalPod, nil)
return externalPod, err
case *corev1.Pod:
internalPod := &api.Pod{}
err := apiv1.Convert_v1_Pod_To_core_Pod(t, internalPod, nil)
return internalPod, err
return t, nil
}
clientConfig, err := restClientGetter.ToRESTConfig()
if err != nil {
return nil, err
}
clientset, err := internalclientset.NewForConfig(clientConfig)
clientset, err := corev1client.NewForConfig(clientConfig)
if err != nil {
return nil, err
}
@@ -57,6 +56,6 @@ func attachablePodForObject(restClientGetter genericclioptions.RESTClientGetter,
return nil, fmt.Errorf("cannot attach to %T: %v", object, err)
}
sortBy := func(pods []*v1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) }
pod, _, err := GetFirstPod(clientset.Core(), namespace, selector.String(), timeout, sortBy)
pod, _, err := GetFirstPod(clientset, namespace, selector.String(), timeout, sortBy)
return pod, err
}

View File

@@ -0,0 +1,50 @@
/*
Copyright 2018 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 polymorphichelpers
import (
"testing"
"k8s.io/apimachinery/pkg/runtime/schema"
api "k8s.io/kubernetes/pkg/apis/core"
)
func TestCanBeAutoscaled(t *testing.T) {
tests := []struct {
kind schema.GroupKind
expectErr bool
}{
{
kind: api.Kind("ReplicationController"),
expectErr: false,
},
{
kind: api.Kind("Node"),
expectErr: true,
},
}
for _, test := range tests {
err := canBeAutoscaled(test.kind)
if test.expectErr && err == nil {
t.Error("unexpected non-error")
}
if !test.expectErr && err != nil {
t.Errorf("unexpected error: %v", err)
}
}
}

View File

@@ -17,6 +17,7 @@ limitations under the License.
package polymorphichelpers
import (
"context"
"fmt"
"sort"
"time"
@@ -32,17 +33,17 @@ import (
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
coreclient "k8s.io/client-go/kubernetes/typed/core/v1"
watchtools "k8s.io/client-go/tools/watch"
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/batch"
api "k8s.io/kubernetes/pkg/apis/core"
apiv1 "k8s.io/kubernetes/pkg/apis/core/v1"
"k8s.io/kubernetes/pkg/apis/extensions"
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
)
// GetFirstPod returns a pod matching the namespace and label selector
// and the number of all pods that match the label selector.
func GetFirstPod(client coreclient.PodsGetter, namespace string, selector string, timeout time.Duration, sortBy func([]*v1.Pod) sort.Interface) (*api.Pod, int, error) {
func GetFirstPod(client coreclient.PodsGetter, namespace string, selector string, timeout time.Duration, sortBy func([]*v1.Pod) sort.Interface) (*v1.Pod, int, error) {
options := metav1.ListOptions{LabelSelector: selector}
podList, err := client.Pods(namespace).List(options)
@@ -52,15 +53,11 @@ func GetFirstPod(client coreclient.PodsGetter, namespace string, selector string
pods := []*v1.Pod{}
for i := range podList.Items {
pod := podList.Items[i]
externalPod := &v1.Pod{}
apiv1.Convert_core_Pod_To_v1_Pod(&pod, externalPod, nil)
pods = append(pods, externalPod)
pods = append(pods, &pod)
}
if len(pods) > 0 {
sort.Sort(sortBy(pods))
internalPod := &api.Pod{}
apiv1.Convert_v1_Pod_To_core_Pod(pods[0], internalPod, nil)
return internalPod, len(podList.Items), nil
return pods[0], len(podList.Items), nil
}
// Watch until we observe a pod
@@ -74,11 +71,14 @@ func GetFirstPod(client coreclient.PodsGetter, namespace string, selector string
condition := func(event watch.Event) (bool, error) {
return event.Type == watch.Added || event.Type == watch.Modified, nil
}
event, err := watch.Until(timeout, w, condition)
ctx, cancel := watchtools.ContextWithOptionalTimeout(context.Background(), timeout)
defer cancel()
event, err := watchtools.UntilWithoutRetry(ctx, w, condition)
if err != nil {
return nil, 0, err
}
pod, ok := event.Object.(*api.Pod)
pod, ok := event.Object.(*v1.Pod)
if !ok {
return nil, 0, fmt.Errorf("%#v is not a pod event", event)
}

View File

@@ -22,15 +22,14 @@ import (
"testing"
"time"
"k8s.io/api/core/v1"
corev1 "k8s.io/api/core/v1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/watch"
fakeexternal "k8s.io/client-go/kubernetes/fake"
testcore "k8s.io/client-go/testing"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
"k8s.io/kubernetes/pkg/controller"
)
@@ -39,30 +38,30 @@ func TestGetFirstPod(t *testing.T) {
tests := []struct {
name string
podList *api.PodList
podList *corev1.PodList
watching []watch.Event
sortBy func([]*v1.Pod) sort.Interface
sortBy func([]*corev1.Pod) sort.Interface
expected *api.Pod
expected *corev1.Pod
expectedNum int
expectedErr bool
}{
{
name: "kubectl logs - two ready pods",
podList: newPodList(2, -1, -1, labelSet),
sortBy: func(pods []*v1.Pod) sort.Interface { return controller.ByLogging(pods) },
expected: &api.Pod{
sortBy: func(pods []*corev1.Pod) sort.Interface { return controller.ByLogging(pods) },
expected: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "pod-1",
Namespace: metav1.NamespaceDefault,
CreationTimestamp: metav1.Date(2016, time.April, 1, 1, 0, 0, 0, time.UTC),
Labels: map[string]string{"test": "selector"},
},
Status: api.PodStatus{
Conditions: []api.PodCondition{
Status: corev1.PodStatus{
Conditions: []corev1.PodCondition{
{
Status: api.ConditionTrue,
Type: api.PodReady,
Status: corev1.ConditionTrue,
Type: corev1.PodReady,
},
},
},
@@ -72,22 +71,22 @@ func TestGetFirstPod(t *testing.T) {
{
name: "kubectl logs - one unhealthy, one healthy",
podList: newPodList(2, -1, 1, labelSet),
sortBy: func(pods []*v1.Pod) sort.Interface { return controller.ByLogging(pods) },
expected: &api.Pod{
sortBy: func(pods []*corev1.Pod) sort.Interface { return controller.ByLogging(pods) },
expected: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "pod-2",
Namespace: metav1.NamespaceDefault,
CreationTimestamp: metav1.Date(2016, time.April, 1, 1, 0, 1, 0, time.UTC),
Labels: map[string]string{"test": "selector"},
},
Status: api.PodStatus{
Conditions: []api.PodCondition{
Status: corev1.PodStatus{
Conditions: []corev1.PodCondition{
{
Status: api.ConditionTrue,
Type: api.PodReady,
Status: corev1.ConditionTrue,
Type: corev1.PodReady,
},
},
ContainerStatuses: []api.ContainerStatus{{RestartCount: 5}},
ContainerStatuses: []corev1.ContainerStatus{{RestartCount: 5}},
},
},
expectedNum: 2,
@@ -95,19 +94,19 @@ func TestGetFirstPod(t *testing.T) {
{
name: "kubectl attach - two ready pods",
podList: newPodList(2, -1, -1, labelSet),
sortBy: func(pods []*v1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) },
expected: &api.Pod{
sortBy: func(pods []*corev1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) },
expected: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "pod-1",
Namespace: metav1.NamespaceDefault,
CreationTimestamp: metav1.Date(2016, time.April, 1, 1, 0, 0, 0, time.UTC),
Labels: map[string]string{"test": "selector"},
},
Status: api.PodStatus{
Conditions: []api.PodCondition{
Status: corev1.PodStatus{
Conditions: []corev1.PodCondition{
{
Status: api.ConditionTrue,
Type: api.PodReady,
Status: corev1.ConditionTrue,
Type: corev1.PodReady,
},
},
},
@@ -138,19 +137,19 @@ func TestGetFirstPod(t *testing.T) {
},
},
},
sortBy: func(pods []*v1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) },
expected: &api.Pod{
sortBy: func(pods []*corev1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) },
expected: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "pod-1",
Namespace: metav1.NamespaceDefault,
CreationTimestamp: metav1.Date(2016, time.April, 1, 1, 0, 0, 0, time.UTC),
Labels: map[string]string{"test": "selector"},
},
Status: api.PodStatus{
Conditions: []api.PodCondition{
Status: corev1.PodStatus{
Conditions: []corev1.PodCondition{
{
Status: api.ConditionTrue,
Type: api.PodReady,
Status: corev1.ConditionTrue,
Type: corev1.PodReady,
},
},
},
@@ -161,7 +160,7 @@ func TestGetFirstPod(t *testing.T) {
for i := range tests {
test := tests[i]
fake := fake.NewSimpleClientset(test.podList)
fake := fakeexternal.NewSimpleClientset(test.podList)
if len(test.watching) > 0 {
watcher := watch.NewFake()
for _, event := range test.watching {
@@ -196,21 +195,21 @@ func TestGetFirstPod(t *testing.T) {
}
}
func newPodList(count, isUnready, isUnhealthy int, labels map[string]string) *api.PodList {
pods := []api.Pod{}
func newPodList(count, isUnready, isUnhealthy int, labels map[string]string) *corev1.PodList {
pods := []corev1.Pod{}
for i := 0; i < count; i++ {
newPod := api.Pod{
newPod := corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("pod-%d", i+1),
Namespace: metav1.NamespaceDefault,
CreationTimestamp: metav1.Date(2016, time.April, 1, 1, 0, i, 0, time.UTC),
Labels: labels,
},
Status: api.PodStatus{
Conditions: []api.PodCondition{
Status: corev1.PodStatus{
Conditions: []corev1.PodCondition{
{
Status: api.ConditionTrue,
Type: api.PodReady,
Status: corev1.ConditionTrue,
Type: corev1.PodReady,
},
},
},
@@ -218,12 +217,12 @@ func newPodList(count, isUnready, isUnhealthy int, labels map[string]string) *ap
pods = append(pods, newPod)
}
if isUnready > -1 && isUnready < count {
pods[isUnready].Status.Conditions[0].Status = api.ConditionFalse
pods[isUnready].Status.Conditions[0].Status = corev1.ConditionFalse
}
if isUnhealthy > -1 && isUnhealthy < count {
pods[isUnhealthy].Status.ContainerStatuses = []api.ContainerStatus{{RestartCount: 5}}
pods[isUnhealthy].Status.ContainerStatuses = []corev1.ContainerStatus{{RestartCount: 5}}
}
return &api.PodList{
return &corev1.PodList{
Items: pods,
}
}

View File

@@ -18,9 +18,9 @@ package polymorphichelpers
import (
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/kubernetes"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
)
// historyViewer Returns a HistoryViewer for viewing change history

View File

@@ -23,20 +23,19 @@ import (
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/rest"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
)
// LogsForObjectFunc is a function type that can tell you how to get logs for a runtime.object
type LogsForObjectFunc func(restClientGetter genericclioptions.RESTClientGetter, object, options runtime.Object, timeout time.Duration) (*rest.Request, error)
type LogsForObjectFunc func(restClientGetter genericclioptions.RESTClientGetter, object, options runtime.Object, timeout time.Duration, allContainers bool) ([]*rest.Request, error)
// LogsForObjectFn gives a way to easily override the function for unit testing if needed.
var LogsForObjectFn LogsForObjectFunc = logsForObject
// AttachableLogsForObjectFunc is a function type that can tell you how to get the pod for which to attach a given object
type AttachableLogsForObjectFunc func(restClientGetter genericclioptions.RESTClientGetter, object runtime.Object, timeout time.Duration) (*api.Pod, error)
type AttachableLogsForObjectFunc func(restClientGetter genericclioptions.RESTClientGetter, object runtime.Object, timeout time.Duration) (*v1.Pod, error)
// AttachablePodForObjectFn gives a way to easily override the function for unit testing if needed.
var AttachablePodForObjectFn AttachableLogsForObjectFunc = attachablePodForObject

View File

@@ -26,50 +26,127 @@ import (
"k8s.io/api/core/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/cli-runtime/pkg/genericclioptions"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/rest"
coreinternal "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
)
func logsForObject(restClientGetter genericclioptions.RESTClientGetter, object, options runtime.Object, timeout time.Duration) (*rest.Request, error) {
func logsForObject(restClientGetter genericclioptions.RESTClientGetter, object, options runtime.Object, timeout time.Duration, allContainers bool) ([]*rest.Request, error) {
clientConfig, err := restClientGetter.ToRESTConfig()
if err != nil {
return nil, err
}
clientset, err := internalclientset.NewForConfig(clientConfig)
clientset, err := corev1client.NewForConfig(clientConfig)
if err != nil {
return nil, err
}
return logsForObjectWithClient(clientset, object, options, timeout)
return logsForObjectWithClient(clientset, object, options, timeout, allContainers)
}
// TODO: remove internal clientset once all callers use external versions
// this is split for easy test-ability
func logsForObjectWithClient(clientset internalclientset.Interface, object, options runtime.Object, timeout time.Duration) (*rest.Request, error) {
opts, ok := options.(*coreinternal.PodLogOptions)
func logsForObjectWithClient(clientset corev1client.CoreV1Interface, object, options runtime.Object, timeout time.Duration, allContainers bool) ([]*rest.Request, error) {
opts, ok := options.(*corev1.PodLogOptions)
if !ok {
return nil, errors.New("provided options object is not a PodLogOptions")
}
switch t := object.(type) {
case *coreinternal.PodList:
ret := []*rest.Request{}
for i := range t.Items {
currRet, err := logsForObjectWithClient(clientset, &t.Items[i], options, timeout, allContainers)
if err != nil {
return nil, err
}
ret = append(ret, currRet...)
}
return ret, nil
case *corev1.PodList:
ret := []*rest.Request{}
for i := range t.Items {
currRet, err := logsForObjectWithClient(clientset, &t.Items[i], options, timeout, allContainers)
if err != nil {
return nil, err
}
ret = append(ret, currRet...)
}
return ret, nil
case *coreinternal.Pod:
return clientset.Core().Pods(t.Namespace).GetLogs(t.Name, opts), nil
// if allContainers is true, then we're going to locate all containers and then iterate through them. At that point, "allContainers" is false
if !allContainers {
return []*rest.Request{clientset.Pods(t.Namespace).GetLogs(t.Name, opts)}, nil
}
ret := []*rest.Request{}
for _, c := range t.Spec.InitContainers {
currOpts := opts.DeepCopy()
currOpts.Container = c.Name
currRet, err := logsForObjectWithClient(clientset, t, currOpts, timeout, false)
if err != nil {
return nil, err
}
ret = append(ret, currRet...)
}
for _, c := range t.Spec.Containers {
currOpts := opts.DeepCopy()
currOpts.Container = c.Name
currRet, err := logsForObjectWithClient(clientset, t, currOpts, timeout, false)
if err != nil {
return nil, err
}
ret = append(ret, currRet...)
}
return ret, nil
case *corev1.Pod:
return clientset.Core().Pods(t.Namespace).GetLogs(t.Name, opts), nil
// if allContainers is true, then we're going to locate all containers and then iterate through them. At that point, "allContainers" is false
if !allContainers {
return []*rest.Request{clientset.Pods(t.Namespace).GetLogs(t.Name, opts)}, nil
}
ret := []*rest.Request{}
for _, c := range t.Spec.InitContainers {
currOpts := opts.DeepCopy()
currOpts.Container = c.Name
currRet, err := logsForObjectWithClient(clientset, t, currOpts, timeout, false)
if err != nil {
return nil, err
}
ret = append(ret, currRet...)
}
for _, c := range t.Spec.Containers {
currOpts := opts.DeepCopy()
currOpts.Container = c.Name
currRet, err := logsForObjectWithClient(clientset, t, currOpts, timeout, false)
if err != nil {
return nil, err
}
ret = append(ret, currRet...)
}
return ret, nil
}
namespace, selector, err := SelectorsForObject(object)
if err != nil {
return nil, fmt.Errorf("cannot get the logs from %T: %v", object, err)
}
sortBy := func(pods []*v1.Pod) sort.Interface { return controller.ByLogging(pods) }
pod, numPods, err := GetFirstPod(clientset.Core(), namespace, selector.String(), timeout, sortBy)
pod, numPods, err := GetFirstPod(clientset, namespace, selector.String(), timeout, sortBy)
if err != nil {
return nil, err
}
if numPods > 1 {
fmt.Fprintf(os.Stderr, "Found %v pods, using pod/%v\n", numPods, pod.Name)
}
return clientset.Core().Pods(pod.Namespace).GetLogs(pod.Name, opts), nil
return logsForObjectWithClient(clientset, pod, options, timeout, allContainers)
}

View File

@@ -21,34 +21,35 @@ import (
"testing"
"time"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/diff"
fakeexternal "k8s.io/client-go/kubernetes/fake"
testclient "k8s.io/client-go/testing"
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/batch"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
)
var (
podsResource = schema.GroupVersionResource{Resource: "pods"}
podsKind = schema.GroupVersionKind{Kind: "Pod"}
podsResource = schema.GroupVersionResource{Version: "v1", Resource: "pods"}
podsKind = schema.GroupVersionKind{Version: "v1", Kind: "Pod"}
)
func TestLogsForObject(t *testing.T) {
tests := []struct {
name string
obj runtime.Object
opts *api.PodLogOptions
pods []runtime.Object
actions []testclient.Action
name string
obj runtime.Object
opts *corev1.PodLogOptions
allContainers bool
pods []runtime.Object
actions []testclient.Action
}{
{
name: "pod logs",
obj: &api.Pod{
obj: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "hello", Namespace: "test"},
},
pods: []runtime.Object{testPod()},
@@ -57,10 +58,88 @@ func TestLogsForObject(t *testing.T) {
},
},
{
name: "replication controller logs",
obj: &api.ReplicationController{
name: "pod logs: all containers",
obj: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "hello", Namespace: "test"},
Spec: api.ReplicationControllerSpec{
Spec: corev1.PodSpec{
InitContainers: []corev1.Container{
{Name: "initc1"},
{Name: "initc2"},
},
Containers: []corev1.Container{
{Name: "c1"},
{Name: "c2"},
},
},
},
opts: &corev1.PodLogOptions{},
allContainers: true,
pods: []runtime.Object{testPod()},
actions: []testclient.Action{
getLogsAction("test", &corev1.PodLogOptions{Container: "initc1"}),
getLogsAction("test", &corev1.PodLogOptions{Container: "initc2"}),
getLogsAction("test", &corev1.PodLogOptions{Container: "c1"}),
getLogsAction("test", &corev1.PodLogOptions{Container: "c2"}),
},
},
{
name: "pods list logs",
obj: &corev1.PodList{
Items: []corev1.Pod{
{
ObjectMeta: metav1.ObjectMeta{Name: "hello", Namespace: "test"},
Spec: corev1.PodSpec{
InitContainers: []corev1.Container{
{Name: "initc1"},
{Name: "initc2"},
},
Containers: []corev1.Container{
{Name: "c1"},
{Name: "c2"},
},
},
},
},
},
pods: []runtime.Object{testPod()},
actions: []testclient.Action{
getLogsAction("test", nil),
},
},
{
name: "pods list logs: all containers",
obj: &corev1.PodList{
Items: []corev1.Pod{
{
ObjectMeta: metav1.ObjectMeta{Name: "hello", Namespace: "test"},
Spec: corev1.PodSpec{
InitContainers: []corev1.Container{
{Name: "initc1"},
{Name: "initc2"},
},
Containers: []corev1.Container{
{Name: "c1"},
{Name: "c2"},
},
},
},
},
},
opts: &corev1.PodLogOptions{},
allContainers: true,
pods: []runtime.Object{testPod()},
actions: []testclient.Action{
getLogsAction("test", &corev1.PodLogOptions{Container: "initc1"}),
getLogsAction("test", &corev1.PodLogOptions{Container: "initc2"}),
getLogsAction("test", &corev1.PodLogOptions{Container: "c1"}),
getLogsAction("test", &corev1.PodLogOptions{Container: "c2"}),
},
},
{
name: "replication controller logs",
obj: &corev1.ReplicationController{
ObjectMeta: metav1.ObjectMeta{Name: "hello", Namespace: "test"},
Spec: corev1.ReplicationControllerSpec{
Selector: map[string]string{"foo": "bar"},
},
},
@@ -129,12 +208,13 @@ func TestLogsForObject(t *testing.T) {
}
for _, test := range tests {
fakeClientset := fake.NewSimpleClientset(test.pods...)
_, err := logsForObjectWithClient(fakeClientset, test.obj, test.opts, 20*time.Second)
fakeClientset := fakeexternal.NewSimpleClientset(test.pods...)
_, err := logsForObjectWithClient(fakeClientset.CoreV1(), test.obj, test.opts, 20*time.Second, test.allContainers)
if err != nil {
t.Errorf("%s: unexpected error: %v", test.name, err)
continue
}
for i := range test.actions {
if len(fakeClientset.Actions()) < i {
t.Errorf("%s: action %d does not exists in actual actions: %#v",
@@ -151,21 +231,24 @@ func TestLogsForObject(t *testing.T) {
}
func testPod() runtime.Object {
return &api.Pod{
return &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: "test",
Labels: map[string]string{"foo": "bar"},
},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "c1"}},
Spec: corev1.PodSpec{
RestartPolicy: corev1.RestartPolicyAlways,
DNSPolicy: corev1.DNSClusterFirst,
Containers: []corev1.Container{
{Name: "c1"},
{Name: "c2"},
},
},
}
}
func getLogsAction(namespace string, opts *api.PodLogOptions) testclient.Action {
func getLogsAction(namespace string, opts *corev1.PodLogOptions) testclient.Action {
action := testclient.GenericActionImpl{}
action.Verb = "get"
action.Namespace = namespace

View File

@@ -19,6 +19,11 @@ package polymorphichelpers
import (
"fmt"
appsv1 "k8s.io/api/apps/v1"
appsv1beta1 "k8s.io/api/apps/v1beta1"
appsv1beta2 "k8s.io/api/apps/v1beta2"
corev1 "k8s.io/api/core/v1"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/runtime"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/extensions"
@@ -33,16 +38,31 @@ func mapBasedSelectorForObject(object runtime.Object) (string, error) {
switch t := object.(type) {
case *api.ReplicationController:
return kubectl.MakeLabels(t.Spec.Selector), nil
case *corev1.ReplicationController:
return kubectl.MakeLabels(t.Spec.Selector), nil
case *api.Pod:
if len(t.Labels) == 0 {
return "", fmt.Errorf("the pod has no labels and cannot be exposed")
}
return kubectl.MakeLabels(t.Labels), nil
case *corev1.Pod:
if len(t.Labels) == 0 {
return "", fmt.Errorf("the pod has no labels and cannot be exposed")
}
return kubectl.MakeLabels(t.Labels), nil
case *api.Service:
if t.Spec.Selector == nil {
return "", fmt.Errorf("the service has no pod selector set")
}
return kubectl.MakeLabels(t.Spec.Selector), nil
case *corev1.Service:
if t.Spec.Selector == nil {
return "", fmt.Errorf("the service has no pod selector set")
}
return kubectl.MakeLabels(t.Spec.Selector), nil
case *extensions.Deployment:
// TODO(madhusudancs): Make this smarter by admitting MatchExpressions with Equals
// operator, DoubleEquals operator and In operator with only one element in the set.
@@ -50,6 +70,35 @@ func mapBasedSelectorForObject(object runtime.Object) (string, error) {
return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format", t.Spec.Selector.MatchExpressions)
}
return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil
case *extensionsv1beta1.Deployment:
// TODO(madhusudancs): Make this smarter by admitting MatchExpressions with Equals
// operator, DoubleEquals operator and In operator with only one element in the set.
if len(t.Spec.Selector.MatchExpressions) > 0 {
return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format", t.Spec.Selector.MatchExpressions)
}
return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil
case *appsv1.Deployment:
// TODO(madhusudancs): Make this smarter by admitting MatchExpressions with Equals
// operator, DoubleEquals operator and In operator with only one element in the set.
if len(t.Spec.Selector.MatchExpressions) > 0 {
return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format", t.Spec.Selector.MatchExpressions)
}
return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil
case *appsv1beta2.Deployment:
// TODO(madhusudancs): Make this smarter by admitting MatchExpressions with Equals
// operator, DoubleEquals operator and In operator with only one element in the set.
if len(t.Spec.Selector.MatchExpressions) > 0 {
return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format", t.Spec.Selector.MatchExpressions)
}
return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil
case *appsv1beta1.Deployment:
// TODO(madhusudancs): Make this smarter by admitting MatchExpressions with Equals
// operator, DoubleEquals operator and In operator with only one element in the set.
if len(t.Spec.Selector.MatchExpressions) > 0 {
return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format", t.Spec.Selector.MatchExpressions)
}
return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil
case *extensions.ReplicaSet:
// TODO(madhusudancs): Make this smarter by admitting MatchExpressions with Equals
// operator, DoubleEquals operator and In operator with only one element in the set.
@@ -57,6 +106,28 @@ func mapBasedSelectorForObject(object runtime.Object) (string, error) {
return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format", t.Spec.Selector.MatchExpressions)
}
return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil
case *extensionsv1beta1.ReplicaSet:
// TODO(madhusudancs): Make this smarter by admitting MatchExpressions with Equals
// operator, DoubleEquals operator and In operator with only one element in the set.
if len(t.Spec.Selector.MatchExpressions) > 0 {
return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format", t.Spec.Selector.MatchExpressions)
}
return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil
case *appsv1.ReplicaSet:
// TODO(madhusudancs): Make this smarter by admitting MatchExpressions with Equals
// operator, DoubleEquals operator and In operator with only one element in the set.
if len(t.Spec.Selector.MatchExpressions) > 0 {
return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format", t.Spec.Selector.MatchExpressions)
}
return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil
case *appsv1beta2.ReplicaSet:
// TODO(madhusudancs): Make this smarter by admitting MatchExpressions with Equals
// operator, DoubleEquals operator and In operator with only one element in the set.
if len(t.Spec.Selector.MatchExpressions) > 0 {
return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format", t.Spec.Selector.MatchExpressions)
}
return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil
default:
return "", fmt.Errorf("cannot extract pod selector from %T", object)
}

View File

@@ -0,0 +1,142 @@
/*
Copyright 2018 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 polymorphichelpers
import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/extensions"
)
func TestMapBasedSelectorForObject(t *testing.T) {
tests := []struct {
object runtime.Object
expectSelector string
expectErr bool
}{
{
object: &api.ReplicationController{
Spec: api.ReplicationControllerSpec{
Selector: map[string]string{
"foo": "bar",
},
},
},
expectSelector: "foo=bar",
},
{
object: &api.Pod{},
expectErr: true,
},
{
object: &api.Pod{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"foo": "bar",
},
},
},
expectSelector: "foo=bar",
},
{
object: &api.Service{
Spec: api.ServiceSpec{
Selector: map[string]string{
"foo": "bar",
},
},
},
expectSelector: "foo=bar",
},
{
object: &api.Service{},
expectErr: true,
},
{
object: &extensions.Deployment{
Spec: extensions.DeploymentSpec{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"foo": "bar",
},
},
},
},
expectSelector: "foo=bar",
},
{
object: &extensions.Deployment{
Spec: extensions.DeploymentSpec{
Selector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "foo",
},
},
},
},
},
expectErr: true,
},
{
object: &extensions.ReplicaSet{
Spec: extensions.ReplicaSetSpec{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"foo": "bar",
},
},
},
},
expectSelector: "foo=bar",
},
{
object: &extensions.ReplicaSet{
Spec: extensions.ReplicaSetSpec{
Selector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "foo",
},
},
},
},
},
expectErr: true,
},
{
object: &api.Node{},
expectErr: true,
},
}
for _, test := range tests {
actual, err := mapBasedSelectorForObject(test.object)
if test.expectErr && err == nil {
t.Error("unexpected non-error")
}
if !test.expectErr && err != nil {
t.Errorf("unexpected error: %v", err)
}
if actual != test.expectSelector {
t.Errorf("expected selector %q, but got %q", test.expectSelector, actual)
}
}
}

View File

@@ -20,10 +20,15 @@ import (
"errors"
"fmt"
appsv1 "k8s.io/api/apps/v1"
appsv1beta1 "k8s.io/api/apps/v1beta1"
appsv1beta2 "k8s.io/api/apps/v1beta2"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
// Currently only supports Deployments.
@@ -35,6 +40,35 @@ func defaultObjectPauser(obj runtime.Object) ([]byte, error) {
}
obj.Spec.Paused = true
return runtime.Encode(internalVersionJSONEncoder(), obj)
case *extensionsv1beta1.Deployment:
if obj.Spec.Paused {
return nil, errors.New("is already paused")
}
obj.Spec.Paused = true
return runtime.Encode(scheme.Codecs.LegacyCodec(extensionsv1beta1.SchemeGroupVersion), obj)
case *appsv1.Deployment:
if obj.Spec.Paused {
return nil, errors.New("is already paused")
}
obj.Spec.Paused = true
return runtime.Encode(scheme.Codecs.LegacyCodec(appsv1.SchemeGroupVersion), obj)
case *appsv1beta2.Deployment:
if obj.Spec.Paused {
return nil, errors.New("is already paused")
}
obj.Spec.Paused = true
return runtime.Encode(scheme.Codecs.LegacyCodec(appsv1beta2.SchemeGroupVersion), obj)
case *appsv1beta1.Deployment:
if obj.Spec.Paused {
return nil, errors.New("is already paused")
}
obj.Spec.Paused = true
return runtime.Encode(scheme.Codecs.LegacyCodec(appsv1beta1.SchemeGroupVersion), obj)
default:
return nil, fmt.Errorf("pausing is not supported")
}

View File

@@ -0,0 +1,72 @@
/*
Copyright 2018 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 polymorphichelpers
import (
"bytes"
"testing"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/apis/extensions"
)
func TestDefaultObjectPauser(t *testing.T) {
tests := []struct {
object runtime.Object
expect []byte
expectErr bool
}{
{
object: &extensions.Deployment{
Spec: extensions.DeploymentSpec{
Paused: false,
},
},
expect: []byte(`paused":true`),
expectErr: false,
},
{
object: &extensions.Deployment{
Spec: extensions.DeploymentSpec{
Paused: true,
},
},
expectErr: true,
},
{
object: &extensions.ReplicaSet{},
expectErr: true,
},
}
for _, test := range tests {
actual, err := defaultObjectPauser(test.object)
if test.expectErr {
if err == nil {
t.Error("unexpected non-error")
}
continue
}
if !test.expectErr && err != nil {
t.Errorf("unexpected error: %v", err)
continue
}
if !bytes.Contains(actual, test.expect) {
t.Errorf("expected %s, but got %s", test.expect, actual)
}
}
}

View File

@@ -20,8 +20,13 @@ import (
"errors"
"fmt"
appsv1 "k8s.io/api/apps/v1"
appsv1beta1 "k8s.io/api/apps/v1beta1"
appsv1beta2 "k8s.io/api/apps/v1beta2"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
func defaultObjectResumer(obj runtime.Object) ([]byte, error) {
@@ -32,6 +37,35 @@ func defaultObjectResumer(obj runtime.Object) ([]byte, error) {
}
obj.Spec.Paused = false
return runtime.Encode(internalVersionJSONEncoder(), obj)
case *extensionsv1beta1.Deployment:
if !obj.Spec.Paused {
return nil, errors.New("is not paused")
}
obj.Spec.Paused = false
return runtime.Encode(scheme.Codecs.LegacyCodec(extensionsv1beta1.SchemeGroupVersion), obj)
case *appsv1.Deployment:
if !obj.Spec.Paused {
return nil, errors.New("is not paused")
}
obj.Spec.Paused = false
return runtime.Encode(scheme.Codecs.LegacyCodec(appsv1.SchemeGroupVersion), obj)
case *appsv1beta2.Deployment:
if !obj.Spec.Paused {
return nil, errors.New("is not paused")
}
obj.Spec.Paused = false
return runtime.Encode(scheme.Codecs.LegacyCodec(appsv1beta2.SchemeGroupVersion), obj)
case *appsv1beta1.Deployment:
if !obj.Spec.Paused {
return nil, errors.New("is not paused")
}
obj.Spec.Paused = false
return runtime.Encode(scheme.Codecs.LegacyCodec(appsv1beta1.SchemeGroupVersion), obj)
default:
return nil, fmt.Errorf("resuming is not supported")
}

View File

@@ -0,0 +1,72 @@
/*
Copyright 2018 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 polymorphichelpers
import (
"bytes"
"testing"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/apis/extensions"
)
func TestDefaultObjectResumer(t *testing.T) {
tests := []struct {
object runtime.Object
notHave []byte
expectErr bool
}{
{
object: &extensions.Deployment{
Spec: extensions.DeploymentSpec{
Paused: true,
},
},
notHave: []byte(`paused":true`),
expectErr: false,
},
{
object: &extensions.Deployment{
Spec: extensions.DeploymentSpec{
Paused: false,
},
},
expectErr: true,
},
{
object: &extensions.ReplicaSet{},
expectErr: true,
},
}
for _, test := range tests {
actual, err := defaultObjectResumer(test.object)
if test.expectErr {
if err == nil {
t.Error("unexpected non-error")
}
continue
}
if !test.expectErr && err != nil {
t.Errorf("unexpected error: %v", err)
continue
}
if bytes.Contains(actual, test.notHave) {
t.Errorf("expected to not have %s, but got %s", test.notHave, actual)
}
}
}

View File

@@ -20,6 +20,11 @@ import (
"fmt"
"strconv"
appsv1 "k8s.io/api/apps/v1"
appsv1beta1 "k8s.io/api/apps/v1beta1"
appsv1beta2 "k8s.io/api/apps/v1beta2"
corev1 "k8s.io/api/core/v1"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/runtime"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/extensions"
@@ -28,21 +33,45 @@ import (
func portsForObject(object runtime.Object) ([]string, error) {
switch t := object.(type) {
case *api.ReplicationController:
return getPortsInternal(t.Spec.Template.Spec), nil
case *corev1.ReplicationController:
return getPorts(t.Spec.Template.Spec), nil
case *api.Pod:
return getPortsInternal(t.Spec), nil
case *corev1.Pod:
return getPorts(t.Spec), nil
case *api.Service:
return getServicePortsInternal(t.Spec), nil
case *corev1.Service:
return getServicePorts(t.Spec), nil
case *extensions.Deployment:
return getPortsInternal(t.Spec.Template.Spec), nil
case *extensionsv1beta1.Deployment:
return getPorts(t.Spec.Template.Spec), nil
case *appsv1.Deployment:
return getPorts(t.Spec.Template.Spec), nil
case *appsv1beta2.Deployment:
return getPorts(t.Spec.Template.Spec), nil
case *appsv1beta1.Deployment:
return getPorts(t.Spec.Template.Spec), nil
case *extensions.ReplicaSet:
return getPortsInternal(t.Spec.Template.Spec), nil
case *extensionsv1beta1.ReplicaSet:
return getPorts(t.Spec.Template.Spec), nil
case *appsv1.ReplicaSet:
return getPorts(t.Spec.Template.Spec), nil
case *appsv1beta2.ReplicaSet:
return getPorts(t.Spec.Template.Spec), nil
default:
return nil, fmt.Errorf("cannot extract ports from %T", object)
}
}
func getPorts(spec api.PodSpec) []string {
func getPortsInternal(spec api.PodSpec) []string {
result := []string{}
for _, container := range spec.Containers {
for _, port := range container.Ports {
@@ -53,7 +82,25 @@ func getPorts(spec api.PodSpec) []string {
}
// Extracts the ports exposed by a service from the given service spec.
func getServicePorts(spec api.ServiceSpec) []string {
func getServicePortsInternal(spec api.ServiceSpec) []string {
result := []string{}
for _, servicePort := range spec.Ports {
result = append(result, strconv.Itoa(int(servicePort.Port)))
}
return result
}
func getPorts(spec corev1.PodSpec) []string {
result := []string{}
for _, container := range spec.Containers {
for _, port := range container.Ports {
result = append(result, strconv.Itoa(int(port.ContainerPort)))
}
}
return result
}
func getServicePorts(spec corev1.ServiceSpec) []string {
result := []string{}
for _, servicePort := range spec.Ports {
result = append(result, strconv.Itoa(int(servicePort.Port)))

View File

@@ -19,36 +19,122 @@ package polymorphichelpers
import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
"reflect"
"k8s.io/apimachinery/pkg/runtime"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/extensions"
)
func TestPortsForObject(t *testing.T) {
pod := &api.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
Spec: api.PodSpec{
Containers: []api.Container{
{
Ports: []api.ContainerPort{
tests := []struct {
object runtime.Object
expectErr bool
}{
{
object: &api.Pod{
Spec: api.PodSpec{
Containers: []api.Container{
{
ContainerPort: 101,
Ports: []api.ContainerPort{
{
ContainerPort: 101,
},
},
},
},
},
},
},
{
object: &api.Service{
Spec: api.ServiceSpec{
Ports: []api.ServicePort{
{
Port: 101,
},
},
},
},
},
{
object: &api.ReplicationController{
Spec: api.ReplicationControllerSpec{
Template: &api.PodTemplateSpec{
Spec: api.PodSpec{
Containers: []api.Container{
{
Ports: []api.ContainerPort{
{
ContainerPort: 101,
},
},
},
},
},
},
},
},
},
{
object: &extensions.Deployment{
Spec: extensions.DeploymentSpec{
Template: api.PodTemplateSpec{
Spec: api.PodSpec{
Containers: []api.Container{
{
Ports: []api.ContainerPort{
{
ContainerPort: 101,
},
},
},
},
},
},
},
},
},
{
object: &extensions.ReplicaSet{
Spec: extensions.ReplicaSetSpec{
Template: api.PodTemplateSpec{
Spec: api.PodSpec{
Containers: []api.Container{
{
Ports: []api.ContainerPort{
{
ContainerPort: 101,
},
},
},
},
},
},
},
},
},
{
object: &api.Node{},
expectErr: true,
},
}
expectedPorts := []string{"101"}
expected := sets.NewString("101")
ports, err := portsForObject(pod)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
got := sets.NewString(ports...)
if !expected.Equal(got) {
t.Fatalf("Ports mismatch! Expected %v, got %v", expected, got)
for _, test := range tests {
actual, err := portsForObject(test.object)
if test.expectErr {
if err == nil {
t.Error("unexpected non-error")
}
continue
}
if !test.expectErr && err != nil {
t.Errorf("unexpected error: %v", err)
continue
}
if !reflect.DeepEqual(actual, expectedPorts) {
t.Errorf("expected ports %v, but got %v", expectedPorts, actual)
}
}
}

View File

@@ -20,6 +20,11 @@ import (
"fmt"
"strconv"
appsv1 "k8s.io/api/apps/v1"
appsv1beta1 "k8s.io/api/apps/v1beta1"
appsv1beta2 "k8s.io/api/apps/v1beta2"
corev1 "k8s.io/api/core/v1"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/runtime"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/extensions"
@@ -29,21 +34,46 @@ func protocolsForObject(object runtime.Object) (map[string]string, error) {
// TODO: replace with a swagger schema based approach (identify pod selector via schema introspection)
switch t := object.(type) {
case *api.ReplicationController:
return getProtocolsInternal(t.Spec.Template.Spec), nil
case *corev1.ReplicationController:
return getProtocols(t.Spec.Template.Spec), nil
case *api.Pod:
return getProtocolsInternal(t.Spec), nil
case *corev1.Pod:
return getProtocols(t.Spec), nil
case *api.Service:
return getServiceProtocolsInternal(t.Spec), nil
case *corev1.Service:
return getServiceProtocols(t.Spec), nil
case *extensions.Deployment:
return getProtocolsInternal(t.Spec.Template.Spec), nil
case *extensionsv1beta1.Deployment:
return getProtocols(t.Spec.Template.Spec), nil
case *appsv1.Deployment:
return getProtocols(t.Spec.Template.Spec), nil
case *appsv1beta2.Deployment:
return getProtocols(t.Spec.Template.Spec), nil
case *appsv1beta1.Deployment:
return getProtocols(t.Spec.Template.Spec), nil
case *extensions.ReplicaSet:
return getProtocolsInternal(t.Spec.Template.Spec), nil
case *extensionsv1beta1.ReplicaSet:
return getProtocols(t.Spec.Template.Spec), nil
case *appsv1.ReplicaSet:
return getProtocols(t.Spec.Template.Spec), nil
case *appsv1beta2.ReplicaSet:
return getProtocols(t.Spec.Template.Spec), nil
default:
return nil, fmt.Errorf("cannot extract protocols from %T", object)
}
}
func getProtocols(spec api.PodSpec) map[string]string {
func getProtocolsInternal(spec api.PodSpec) map[string]string {
result := make(map[string]string)
for _, container := range spec.Containers {
for _, port := range container.Ports {
@@ -54,7 +84,26 @@ func getProtocols(spec api.PodSpec) map[string]string {
}
// Extracts the protocols exposed by a service from the given service spec.
func getServiceProtocols(spec api.ServiceSpec) map[string]string {
func getServiceProtocolsInternal(spec api.ServiceSpec) map[string]string {
result := make(map[string]string)
for _, servicePort := range spec.Ports {
result[strconv.Itoa(int(servicePort.Port))] = string(servicePort.Protocol)
}
return result
}
func getProtocols(spec corev1.PodSpec) map[string]string {
result := make(map[string]string)
for _, container := range spec.Containers {
for _, port := range container.Ports {
result[strconv.Itoa(int(port.ContainerPort))] = string(port.Protocol)
}
}
return result
}
// Extracts the protocols exposed by a service from the given service spec.
func getServiceProtocols(spec corev1.ServiceSpec) map[string]string {
result := make(map[string]string)
for _, servicePort := range spec.Ports {
result[strconv.Itoa(int(servicePort.Port))] = string(servicePort.Protocol)

View File

@@ -0,0 +1,145 @@
/*
Copyright 2018 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 polymorphichelpers
import (
"testing"
"reflect"
"k8s.io/apimachinery/pkg/runtime"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/extensions"
)
func TestProtocolsForObject(t *testing.T) {
tests := []struct {
object runtime.Object
expectErr bool
}{
{
object: &api.Pod{
Spec: api.PodSpec{
Containers: []api.Container{
{
Ports: []api.ContainerPort{
{
ContainerPort: 101,
Protocol: "tcp",
},
},
},
},
},
},
},
{
object: &api.Service{
Spec: api.ServiceSpec{
Ports: []api.ServicePort{
{
Port: 101,
Protocol: "tcp",
},
},
},
},
},
{
object: &api.ReplicationController{
Spec: api.ReplicationControllerSpec{
Template: &api.PodTemplateSpec{
Spec: api.PodSpec{
Containers: []api.Container{
{
Ports: []api.ContainerPort{
{
ContainerPort: 101,
Protocol: "tcp",
},
},
},
},
},
},
},
},
},
{
object: &extensions.Deployment{
Spec: extensions.DeploymentSpec{
Template: api.PodTemplateSpec{
Spec: api.PodSpec{
Containers: []api.Container{
{
Ports: []api.ContainerPort{
{
ContainerPort: 101,
Protocol: "tcp",
},
},
},
},
},
},
},
},
},
{
object: &extensions.ReplicaSet{
Spec: extensions.ReplicaSetSpec{
Template: api.PodTemplateSpec{
Spec: api.PodSpec{
Containers: []api.Container{
{
Ports: []api.ContainerPort{
{
ContainerPort: 101,
Protocol: "tcp",
},
},
},
},
},
},
},
},
},
{
object: &api.Node{},
expectErr: true,
},
}
expectedPorts := map[string]string{"101": "tcp"}
for _, test := range tests {
actual, err := protocolsForObject(test.object)
if test.expectErr {
if err == nil {
t.Error("unexpected non-error")
}
continue
}
if !test.expectErr && err != nil {
t.Errorf("unexpected error: %v", err)
continue
}
if !reflect.DeepEqual(actual, expectedPorts) {
t.Errorf("expected ports %v, but got %v", expectedPorts, actual)
}
}
}

View File

@@ -18,9 +18,9 @@ package polymorphichelpers
import (
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/kubernetes"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
)
// Returns a Rollbacker for changing the rollback version of the specified RESTMapping type or an error

View File

@@ -18,9 +18,9 @@ package polymorphichelpers
import (
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/kubernetes"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
)
// statusViewer returns a StatusViewer for printing rollout status.

View File

@@ -0,0 +1,131 @@
/*
Copyright 2018 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 polymorphichelpers
import (
"testing"
appsv1 "k8s.io/api/apps/v1"
appsv1beta1 "k8s.io/api/apps/v1beta1"
appsv1beta2 "k8s.io/api/apps/v1beta2"
batchv1 "k8s.io/api/batch/v1"
batchv1beta1 "k8s.io/api/batch/v1beta1"
batchv2alpha1 "k8s.io/api/batch/v2alpha1"
"k8s.io/api/core/v1"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/runtime"
)
func TestUpdatePodSpecForObject(t *testing.T) {
tests := []struct {
object runtime.Object
expect bool
expectErr bool
}{
{
object: &v1.Pod{},
expect: true,
},
{
object: &v1.ReplicationController{},
expect: true,
},
{
object: &extensionsv1beta1.Deployment{},
expect: true,
},
{
object: &appsv1beta1.Deployment{},
expect: true,
},
{
object: &appsv1beta2.Deployment{},
expect: true,
},
{
object: &appsv1.Deployment{},
expect: true,
},
{
object: &extensionsv1beta1.DaemonSet{},
expect: true,
}, {
object: &appsv1beta2.DaemonSet{},
expect: true,
},
{
object: &appsv1.DaemonSet{},
expect: true,
},
{
object: &extensionsv1beta1.ReplicaSet{},
expect: true,
},
{
object: &appsv1beta2.ReplicaSet{},
expect: true,
},
{
object: &appsv1.ReplicaSet{},
expect: true,
},
{
object: &appsv1beta1.StatefulSet{},
expect: true,
},
{
object: &appsv1beta2.StatefulSet{},
expect: true,
},
{
object: &appsv1.StatefulSet{},
expect: true,
},
{
object: &batchv1.Job{},
expect: true,
},
{
object: &batchv1beta1.CronJob{},
expect: true,
},
{
object: &batchv2alpha1.CronJob{},
expect: true,
},
{
object: &v1.Node{},
expect: false,
expectErr: true,
},
}
for _, test := range tests {
actual, err := updatePodSpecForObject(test.object, func(*v1.PodSpec) error {
return nil
})
if test.expectErr && err == nil {
t.Error("unexpected non-error")
}
if !test.expectErr && err != nil {
t.Errorf("unexpected error: %v", err)
}
if actual != test.expect {
t.Errorf("expected %v, but got %v", test.expect, actual)
}
}
}