vendor updates

This commit is contained in:
Serguei Bezverkhi
2018-03-06 17:33:18 -05:00
parent 4b3ebc171b
commit e9033989a0
5854 changed files with 248382 additions and 119809 deletions

View File

@ -117,6 +117,13 @@
"k8s.io/kubernetes/pkg/printers/internalversion",
"k8s.io/kubernetes/pkg/registry/rbac/reconciliation",
"k8s.io/kubernetes/pkg/registry/rbac/validation",
"k8s.io/kubernetes/pkg/scheduler/algorithm",
"k8s.io/kubernetes/pkg/scheduler/algorithm/predicates",
"k8s.io/kubernetes/pkg/scheduler/algorithm/priorities/util",
"k8s.io/kubernetes/pkg/scheduler/api",
"k8s.io/kubernetes/pkg/scheduler/schedulercache",
"k8s.io/kubernetes/pkg/scheduler/util",
"k8s.io/kubernetes/pkg/scheduler/volumebinder",
"k8s.io/kubernetes/pkg/security/apparmor",
"k8s.io/kubernetes/pkg/serviceaccount",
"k8s.io/kubernetes/pkg/util/file",
@ -137,13 +144,7 @@
"k8s.io/kubernetes/pkg/version",
"k8s.io/kubernetes/pkg/version/prometheus",
"k8s.io/kubernetes/pkg/volume",
"k8s.io/kubernetes/pkg/volume/util",
"k8s.io/kubernetes/plugin/pkg/scheduler/algorithm",
"k8s.io/kubernetes/plugin/pkg/scheduler/algorithm/predicates",
"k8s.io/kubernetes/plugin/pkg/scheduler/algorithm/priorities/util",
"k8s.io/kubernetes/plugin/pkg/scheduler/api",
"k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache",
"k8s.io/kubernetes/plugin/pkg/scheduler/util"
"k8s.io/kubernetes/pkg/volume/util"
],
"ForbiddenPrefixes": []
}]

View File

@ -23,6 +23,7 @@ go_test(
"quota_test.go",
"resource_filter_test.go",
"rolebinding_test.go",
"rollback_test.go",
"rolling_updater_test.go",
"rollout_status_test.go",
"run_test.go",
@ -35,17 +36,18 @@ go_test(
"serviceaccount_test.go",
"sorting_printer_test.go",
],
importpath = "k8s.io/kubernetes/pkg/kubectl",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/api/testapi:go_default_library",
"//pkg/api/testing:go_default_library",
"//pkg/apis/apps:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/core:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/apps/internalversion:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/batch/internalversion:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion:go_default_library",
@ -53,6 +55,7 @@ go_test(
"//pkg/printers:go_default_library",
"//vendor/github.com/spf13/cobra: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/autoscaling/v1:go_default_library",
"//vendor/k8s.io/api/batch/v1:go_default_library",
"//vendor/k8s.io/api/batch/v1beta1:go_default_library",
@ -65,18 +68,24 @@ go_test(
"//vendor/k8s.io/api/scheduling/v1alpha1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/resource: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/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/util/intstr:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/uuid: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/dynamic:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/fake:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library",
"//vendor/k8s.io/client-go/rest/fake:go_default_library",
"//vendor/k8s.io/client-go/scale:go_default_library",
"//vendor/k8s.io/client-go/testing:go_default_library",
"//vendor/k8s.io/client-go/util/testing:go_default_library",
],
@ -89,6 +98,7 @@ go_library(
"autoscale.go",
"bash_comp_utils.go",
"clusterrolebinding.go",
"conditions.go",
"configmap.go",
"delete.go",
"deployment.go",
@ -116,11 +126,11 @@ go_library(
"service_basic.go",
"serviceaccount.go",
"sorting_printer.go",
"versioned_client.go",
],
importpath = "k8s.io/kubernetes/pkg/kubectl",
deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/api/pod:go_default_library",
"//pkg/api/v1/pod:go_default_library",
"//pkg/apis/apps:go_default_library",
"//pkg/apis/batch:go_default_library",
@ -132,7 +142,6 @@ go_library(
"//pkg/client/clientset_generated/internalclientset/typed/batch/internalversion:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion:go_default_library",
"//pkg/client/unversioned:go_default_library",
"//pkg/controller/daemon:go_default_library",
"//pkg/controller/deployment/util:go_default_library",
"//pkg/controller/statefulset:go_default_library",
@ -147,6 +156,7 @@ go_library(
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library",
"//vendor/github.com/spf13/pflag: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/autoscaling/v1:go_default_library",
"//vendor/k8s.io/api/batch/v1:go_default_library",
@ -163,7 +173,6 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/api/resource: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/fields: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",
@ -178,9 +187,11 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/util/wait: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/kubernetes/typed/apps/v1:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/typed/apps/v1beta1:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/typed/extensions/v1beta1:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library",
"//vendor/k8s.io/client-go/scale:go_default_library",
"//vendor/k8s.io/client-go/util/integer:go_default_library",
"//vendor/k8s.io/client-go/util/jsonpath:go_default_library",
"//vendor/k8s.io/client-go/util/retry:go_default_library",
@ -209,7 +220,6 @@ filegroup(
"//pkg/kubectl/proxy:all-srcs",
"//pkg/kubectl/resource:all-srcs",
"//pkg/kubectl/scheme:all-srcs",
"//pkg/kubectl/testing:all-srcs",
"//pkg/kubectl/util:all-srcs",
"//pkg/kubectl/validation:all-srcs",
],

View File

@ -17,9 +17,9 @@ limitations under the License.
package kubectl
import (
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/kubectl/resource"
)
@ -35,7 +35,7 @@ func GetOriginalConfiguration(mapping *meta.RESTMapping, obj runtime.Object) ([]
return nil, nil
}
original, ok := annots[api.LastAppliedConfigAnnotation]
original, ok := annots[v1.LastAppliedConfigAnnotation]
if !ok {
return nil, nil
}
@ -60,7 +60,7 @@ func SetOriginalConfiguration(info *resource.Info, original []byte) error {
annots = map[string]string{}
}
annots[api.LastAppliedConfigAnnotation] = string(original)
annots[v1.LastAppliedConfigAnnotation] = string(original)
return info.Mapping.MetadataAccessor.SetAnnotations(info.Object, annots)
}
@ -85,8 +85,8 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool, codec runtime.
annots = map[string]string{}
}
original := annots[api.LastAppliedConfigAnnotation]
delete(annots, api.LastAppliedConfigAnnotation)
original := annots[v1.LastAppliedConfigAnnotation]
delete(annots, v1.LastAppliedConfigAnnotation)
if err := accessor.SetAnnotations(info.Object, annots); err != nil {
return nil, err
}
@ -97,7 +97,7 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool, codec runtime.
}
if annotate {
annots[api.LastAppliedConfigAnnotation] = string(modified)
annots[v1.LastAppliedConfigAnnotation] = string(modified)
if err := info.Mapping.MetadataAccessor.SetAnnotations(info.Object, annots); err != nil {
return nil, err
}
@ -109,7 +109,7 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool, codec runtime.
}
// Restore the object to its original condition.
annots[api.LastAppliedConfigAnnotation] = original
annots[v1.LastAppliedConfigAnnotation] = original
if err := info.Mapping.MetadataAccessor.SetAnnotations(info.Object, annots); err != nil {
return nil, err
}

View File

@ -29,7 +29,6 @@ go_test(
data = [
"//api/openapi-spec:swagger-spec",
],
importpath = "k8s.io/kubernetes/pkg/kubectl/apply/parse_test",
deps = [
"//vendor/github.com/onsi/ginkgo:go_default_library",
"//vendor/github.com/onsi/ginkgo/config:go_default_library",

View File

@ -33,7 +33,6 @@ go_test(
":swagger-spec",
"//api/openapi-spec:swagger-spec",
],
importpath = "k8s.io/kubernetes/pkg/kubectl/apply/strategy_test",
deps = [
":go_default_library",
"//pkg/kubectl/apply:go_default_library",

View File

@ -32,7 +32,6 @@ go_test(
"apps_suite_test.go",
"kind_visitor_test.go",
],
importpath = "k8s.io/kubernetes/pkg/kubectl/apps_test",
deps = [
":go_default_library",
"//vendor/github.com/onsi/ginkgo:go_default_library",

View File

@ -18,98 +18,68 @@ package kubectl
import (
"fmt"
"strconv"
autoscalingv1 "k8s.io/api/autoscaling/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
type HorizontalPodAutoscalerV1 struct{}
func (HorizontalPodAutoscalerV1) ParamNames() []GeneratorParam {
return []GeneratorParam{
{"default-name", true},
{"name", false},
{"scaleRef-kind", false},
{"scaleRef-name", false},
{"scaleRef-apiVersion", false},
{"min", false},
{"max", true},
{"cpu-percent", false},
}
// HorizontalPodAutoscalerV1Generator supports stable generation of a horizontal pod autoscaler.
type HorizontalPodAutoscalerGeneratorV1 struct {
Name string
ScaleRefKind string
ScaleRefName string
ScaleRefApiVersion string
MinReplicas int32
MaxReplicas int32
CPUPercent int32
}
func (HorizontalPodAutoscalerV1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
return generateHPA(genericParams)
}
// Ensure it supports the generator pattern that uses parameters specified during construction.
var _ StructuredGenerator = &HorizontalPodAutoscalerGeneratorV1{}
func generateHPA(genericParams map[string]interface{}) (runtime.Object, error) {
params := map[string]string{}
for key, value := range genericParams {
strVal, isString := value.(string)
if !isString {
return nil, fmt.Errorf("expected string, saw %v for '%s'", value, key)
}
params[key] = strVal
}
name, found := params["name"]
if !found || len(name) == 0 {
name, found = params["default-name"]
if !found || len(name) == 0 {
return nil, fmt.Errorf("'name' is a required parameter.")
}
}
minString, found := params["min"]
min := -1
var err error
if found {
if min, err = strconv.Atoi(minString); err != nil {
return nil, err
}
}
maxString, found := params["max"]
if !found {
return nil, fmt.Errorf("'max' is a required parameter.")
}
max, err := strconv.Atoi(maxString)
if err != nil {
// StructuredGenerate outputs a horizontal pod autoscaler object using the configured fields.
func (s *HorizontalPodAutoscalerGeneratorV1) StructuredGenerate() (runtime.Object, error) {
if err := s.validate(); err != nil {
return nil, err
}
if min > max {
return nil, fmt.Errorf("'max' must be greater than or equal to 'min'.")
}
cpuString, found := params["cpu-percent"]
cpu := -1
if found {
if cpu, err = strconv.Atoi(cpuString); err != nil {
return nil, err
}
}
scaler := autoscalingv1.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Name: s.Name,
},
Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
Kind: params["scaleRef-kind"],
Name: params["scaleRef-name"],
APIVersion: params["scaleRef-apiVersion"],
Kind: s.ScaleRefKind,
Name: s.ScaleRefName,
APIVersion: s.ScaleRefApiVersion,
},
MaxReplicas: int32(max),
MaxReplicas: s.MaxReplicas,
},
}
if min > 0 {
v := int32(min)
if s.MinReplicas > 0 {
v := int32(s.MinReplicas)
scaler.Spec.MinReplicas = &v
}
if cpu >= 0 {
c := int32(cpu)
if s.CPUPercent >= 0 {
c := int32(s.CPUPercent)
scaler.Spec.TargetCPUUtilizationPercentage = &c
}
return &scaler, nil
}
// validate check if the caller has set the right fields.
func (s HorizontalPodAutoscalerGeneratorV1) validate() error {
if len(s.Name) == 0 {
return fmt.Errorf("name must be specified")
}
if s.MaxReplicas < 1 {
return fmt.Errorf("'max' is a required parameter and must be at least 1")
}
if s.MinReplicas > s.MaxReplicas {
return fmt.Errorf("'max' must be greater than or equal to 'min'")
}
return nil
}

View File

@ -26,22 +26,26 @@ import (
func TestHPAGenerate(t *testing.T) {
tests := []struct {
name string
params map[string]interface{}
expected *autoscalingv1.HorizontalPodAutoscaler
expectErr bool
name string
HPAName string
scaleRefKind string
scaleRefName string
scaleRefApiVersion string
minReplicas int32
maxReplicas int32
CPUPercent int32
expected *autoscalingv1.HorizontalPodAutoscaler
expectErr bool
}{
{
name: "valid case",
params: map[string]interface{}{
"name": "foo",
"min": "1",
"max": "10",
"cpu-percent": "80",
"scaleRef-kind": "kind",
"scaleRef-name": "name",
"scaleRef-apiVersion": "apiVersion",
},
name: "valid case",
HPAName: "foo",
minReplicas: 1,
maxReplicas: 10,
CPUPercent: 80,
scaleRefKind: "kind",
scaleRefName: "name",
scaleRefApiVersion: "apiVersion",
expected: &autoscalingv1.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@ -60,79 +64,53 @@ func TestHPAGenerate(t *testing.T) {
expectErr: false,
},
{
name: "'name' is a required parameter",
params: map[string]interface{}{
"scaleRef-kind": "kind",
"scaleRef-name": "name",
"scaleRef-apiVersion": "apiVersion",
},
expectErr: true,
name: "'name' is a required parameter",
scaleRefKind: "kind",
scaleRefName: "name",
scaleRefApiVersion: "apiVersion",
expectErr: true,
},
{
name: "'max' is a required parameter",
params: map[string]interface{}{
"default-name": "foo",
"scaleRef-kind": "kind",
"scaleRef-name": "name",
"scaleRef-apiVersion": "apiVersion",
},
expectErr: true,
name: "'max' is a required parameter",
HPAName: "foo",
scaleRefKind: "kind",
scaleRefName: "name",
scaleRefApiVersion: "apiVersion",
expectErr: true,
},
{
name: "'max' must be greater than or equal to 'min'",
params: map[string]interface{}{
"name": "foo",
"min": "10",
"max": "1",
"scaleRef-kind": "kind",
"scaleRef-name": "name",
"scaleRef-apiVersion": "apiVersion",
},
expectErr: true,
name: "'max' must be greater than or equal to 'min'",
HPAName: "foo",
minReplicas: 10,
maxReplicas: 1,
scaleRefKind: "kind",
scaleRefName: "name",
scaleRefApiVersion: "apiVersion",
expectErr: true,
},
{
name: "cpu-percent must be an integer if specified",
params: map[string]interface{}{
"name": "foo",
"min": "1",
"max": "10",
"cpu-percent": "",
"scaleRef-kind": "kind",
"scaleRef-name": "name",
"scaleRef-apiVersion": "apiVersion",
},
expectErr: true,
},
{
name: "'min' must be an integer if specified",
params: map[string]interface{}{
"name": "foo",
"min": "foo",
"max": "10",
"cpu-percent": "60",
"scaleRef-kind": "kind",
"scaleRef-name": "name",
"scaleRef-apiVersion": "apiVersion",
},
expectErr: true,
},
{
name: "'max' must be an integer if specified",
params: map[string]interface{}{
"name": "foo",
"min": "1",
"max": "bar",
"cpu-percent": "90",
"scaleRef-kind": "kind",
"scaleRef-name": "name",
"scaleRef-apiVersion": "apiVersion",
},
expectErr: true,
name: "'max' must be at least 1",
HPAName: "foo",
minReplicas: 1,
maxReplicas: -10,
scaleRefKind: "kind",
scaleRefName: "name",
scaleRefApiVersion: "apiVersion",
expectErr: true,
},
}
generator := HorizontalPodAutoscalerV1{}
for _, test := range tests {
obj, err := generator.Generate(test.params)
generator := HorizontalPodAutoscalerGeneratorV1{
Name: test.HPAName,
ScaleRefKind: test.scaleRefKind,
ScaleRefName: test.scaleRefName,
ScaleRefApiVersion: test.scaleRefApiVersion,
MinReplicas: test.minReplicas,
MaxReplicas: test.maxReplicas,
CPUPercent: test.CPUPercent,
}
obj, err := generator.StructuredGenerate()
if test.expectErr && err != nil {
continue
}

View File

@ -14,8 +14,7 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["categories_test.go"],
importpath = "k8s.io/kubernetes/pkg/kubectl/categories",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",

View File

@ -21,10 +21,14 @@ import (
"k8s.io/client-go/discovery"
)
// CategoryExpander maps category strings to GroupResouces.
// Categories are classification or 'tag' of a group of resources.
type CategoryExpander interface {
Expand(category string) ([]schema.GroupResource, bool)
}
// SimpleCategoryExpander implements CategoryExpander interface
// using a static mapping of categories to GroupResource mapping.
type SimpleCategoryExpander struct {
Expansions map[string][]schema.GroupResource
}
@ -34,6 +38,8 @@ func (e SimpleCategoryExpander) Expand(category string) ([]schema.GroupResource,
return ret, ok
}
// discoveryCategoryExpander struct lets a REST Client wrapper (discoveryClient) to retrieve list of APIResourceList,
// and then convert to fallbackExpander
type discoveryCategoryExpander struct {
fallbackExpander CategoryExpander
discoveryClient discovery.DiscoveryInterface
@ -50,6 +56,7 @@ func NewDiscoveryCategoryExpander(fallbackExpander CategoryExpander, client disc
}
func (e discoveryCategoryExpander) Expand(category string) ([]schema.GroupResource, bool) {
// Get all supported resources for groups and versions from server, if no resource found, fallback anyway.
apiResourceLists, _ := e.discoveryClient.ServerResources()
if len(apiResourceLists) == 0 {
return e.fallbackExpander.Expand(category)
@ -62,7 +69,7 @@ func (e discoveryCategoryExpander) Expand(category string) ([]schema.GroupResour
if err != nil {
return e.fallbackExpander.Expand(category)
}
// Collect GroupVersions by categories
for _, apiResource := range apiResourceList.APIResources {
if categories := apiResource.Categories; len(categories) > 0 {
for _, category := range categories {
@ -86,14 +93,15 @@ func (e discoveryCategoryExpander) Expand(category string) ([]schema.GroupResour
return ret, ok
}
// discoveryFilteredExpander expands the given CategoryExpander (delegate) to filter group and resource returned from server
type discoveryFilteredExpander struct {
delegate CategoryExpander
discoveryClient discovery.DiscoveryInterface
}
// NewDiscoveryFilteredExpander returns a category expander that filters the returned groupresources by
// what the server has available
// NewDiscoveryFilteredExpander returns a category expander that filters the returned groupresources
// by what the server has available
func NewDiscoveryFilteredExpander(delegate CategoryExpander, client discovery.DiscoveryInterface) (discoveryFilteredExpander, error) {
if client == nil {
panic("Please provide discovery client to shortcut expander")
@ -129,12 +137,15 @@ func (e discoveryFilteredExpander) Expand(category string) ([]schema.GroupResour
return available, ok
}
// UnionCategoryExpander implements CategoryExpander interface.
// It maps given category string to union of expansions returned by all the CategoryExpanders in the list.
type UnionCategoryExpander []CategoryExpander
func (u UnionCategoryExpander) Expand(category string) ([]schema.GroupResource, bool) {
ret := []schema.GroupResource{}
ok := false
// Expand the category for each CategoryExpander in the list and merge/combine the results.
for _, expansion := range u {
curr, currOk := expansion.Expand(category)

View File

@ -28,6 +28,7 @@ go_library(
"create_clusterrolebinding.go",
"create_configmap.go",
"create_deployment.go",
"create_job.go",
"create_namespace.go",
"create_pdb.go",
"create_priorityclass.go",
@ -74,7 +75,6 @@ go_library(
"//pkg/apis/core/validation:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
"//pkg/client/unversioned:go_default_library",
"//pkg/kubectl:go_default_library",
"//pkg/kubectl/apply/parse:go_default_library",
"//pkg/kubectl/apply/strategy:go_default_library",
@ -140,7 +140,9 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/version:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_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/kubernetes:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/typed/batch/v1:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/typed/rbac/v1:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library",
@ -149,6 +151,9 @@ go_library(
"//vendor/k8s.io/client-go/tools/remotecommand:go_default_library",
"//vendor/k8s.io/client-go/transport/spdy:go_default_library",
"//vendor/k8s.io/kube-openapi/pkg/util/proto:go_default_library",
"//vendor/k8s.io/metrics/pkg/apis/metrics:go_default_library",
"//vendor/k8s.io/metrics/pkg/apis/metrics/v1beta1:go_default_library",
"//vendor/k8s.io/metrics/pkg/client/clientset_generated/clientset:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library",
],
)
@ -167,6 +172,7 @@ go_test(
"create_clusterrolebinding_test.go",
"create_configmap_test.go",
"create_deployment_test.go",
"create_job_test.go",
"create_namespace_test.go",
"create_pdb_test.go",
"create_priorityclass_test.go",
@ -201,19 +207,19 @@ go_test(
"testdata",
"//api/openapi-spec:swagger-spec",
"//examples:config",
"//test/e2e/testing-manifests:all-srcs",
"//test/fixtures",
],
importpath = "k8s.io/kubernetes/pkg/kubectl/cmd",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/api/ref:go_default_library",
"//pkg/api/testapi:go_default_library",
"//pkg/api/testing:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/core:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/kubectl:go_default_library",
"//pkg/kubectl/cmd/resource:go_default_library",
"//pkg/kubectl/cmd/testing:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubectl/cmd/util/openapi:go_default_library",
@ -223,15 +229,18 @@ go_test(
"//pkg/kubectl/util/i18n:go_default_library",
"//pkg/kubectl/util/term:go_default_library",
"//pkg/printers:go_default_library",
"//pkg/printers/internalversion:go_default_library",
"//pkg/util/strings:go_default_library",
"//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/gopkg.in/yaml.v2: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/core/v1:go_default_library",
"//vendor/k8s.io/api/policy/v1beta1:go_default_library",
"//vendor/k8s.io/api/rbac/v1:go_default_library",
"//vendor/k8s.io/api/rbac/v1beta1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
@ -246,11 +255,16 @@ go_test(
"//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/strategicpatch/testing:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/version:go_default_library",
"//vendor/k8s.io/client-go/dynamic:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/fake:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library",
"//vendor/k8s.io/client-go/rest/fake:go_default_library",
"//vendor/k8s.io/client-go/testing:go_default_library",
"//vendor/k8s.io/client-go/tools/remotecommand:go_default_library",
"//vendor/k8s.io/metrics/pkg/apis/metrics/v1alpha1:go_default_library",
"//vendor/k8s.io/metrics/pkg/apis/metrics/v1beta1:go_default_library",
"//vendor/k8s.io/metrics/pkg/client/clientset_generated/clientset/fake:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library",
],
)

View File

@ -36,7 +36,6 @@ import (
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/printers"
)
// AnnotateOptions have the data required to perform the annotate operation
@ -100,20 +99,11 @@ var (
func NewCmdAnnotate(f cmdutil.Factory, out io.Writer) *cobra.Command {
options := &AnnotateOptions{}
// retrieve a list of handled resources from printer as valid args
validArgs, argAliases := []string{}, []string{}
p, err := f.Printer(nil, printers.PrintOptions{
ColumnLabels: []string{},
})
cmdutil.CheckErr(err)
if p != nil {
validArgs = p.HandledResources()
argAliases = kubectl.ResourceAliases(validArgs)
}
validArgs := cmdutil.ValidArgList(f)
cmd := &cobra.Command{
Use: "annotate [--overwrite] (-f FILENAME | TYPE NAME) KEY_1=VAL_1 ... KEY_N=VAL_N [--resource-version=version]",
Use: "annotate [--overwrite] (-f FILENAME | TYPE NAME) KEY_1=VAL_1 ... KEY_N=VAL_N [--resource-version=version]",
DisableFlagsInUseLine: true,
Short: i18n.T("Update the annotations on a resource"),
Long: annotateLong + "\n\n" + cmdutil.ValidResourceTypeList(f),
Example: annotateExample,
@ -127,15 +117,15 @@ func NewCmdAnnotate(f cmdutil.Factory, out io.Writer) *cobra.Command {
cmdutil.CheckErr(options.RunAnnotate(f, cmd))
},
ValidArgs: validArgs,
ArgAliases: argAliases,
ArgAliases: kubectl.ResourceAliases(validArgs),
}
cmdutil.AddPrinterFlags(cmd)
cmdutil.AddIncludeUninitializedFlag(cmd)
cmd.Flags().Bool("overwrite", false, "If true, allow annotations to be overwritten, otherwise reject annotation updates that overwrite existing annotations.")
cmd.Flags().Bool("local", false, "If true, annotation will NOT contact api-server but run locally.")
cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on, not including uninitialized ones, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2).")
cmd.Flags().Bool("all", false, "Select all resources, including uninitialized ones, in the namespace of the specified resource types.")
cmd.Flags().String("resource-version", "", i18n.T("If non-empty, the annotation update will only succeed if this is the current resource-version for the object. Only valid when specifying a single resource."))
cmd.Flags().BoolVar(&options.overwrite, "overwrite", options.overwrite, "If true, allow annotations to be overwritten, otherwise reject annotation updates that overwrite existing annotations.")
cmd.Flags().BoolVar(&options.local, "local", options.local, "If true, annotation will NOT contact api-server but run locally.")
cmd.Flags().StringVarP(&options.selector, "selector", "l", options.selector, "Selector (label query) to filter on, not including uninitialized ones, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2).")
cmd.Flags().BoolVar(&options.all, "all", options.all, "Select all resources, including uninitialized ones, in the namespace of the specified resource types.")
cmd.Flags().StringVar(&options.resourceVersion, "resource-version", options.resourceVersion, i18n.T("If non-empty, the annotation update will only succeed if this is the current resource-version for the object. Only valid when specifying a single resource."))
usage := "identifying the resource to update the annotation"
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
cmdutil.AddDryRunFlag(cmd)
@ -148,11 +138,6 @@ func NewCmdAnnotate(f cmdutil.Factory, out io.Writer) *cobra.Command {
// Complete adapts from the command line args and factory to the data required.
func (o *AnnotateOptions) Complete(out io.Writer, cmd *cobra.Command, args []string) (err error) {
o.out = out
o.local = cmdutil.GetFlagBool(cmd, "local")
o.overwrite = cmdutil.GetFlagBool(cmd, "overwrite")
o.all = cmdutil.GetFlagBool(cmd, "all")
o.resourceVersion = cmdutil.GetFlagString(cmd, "resource-version")
o.selector = cmdutil.GetFlagString(cmd, "selector")
o.outputFormat = cmdutil.GetFlagString(cmd, "output")
o.dryrun = cmdutil.GetDryRunFlag(cmd)
o.recordChangeCause = cmdutil.GetRecordFlag(cmd)
@ -170,6 +155,9 @@ func (o *AnnotateOptions) Complete(out io.Writer, cmd *cobra.Command, args []str
// Validate checks to the AnnotateOptions to see if there is sufficient information run the command.
func (o AnnotateOptions) Validate() error {
if o.all && len(o.selector) > 0 {
return fmt.Errorf("cannot set --all and --selector at the same time")
}
if len(o.resources) < 1 && cmdutil.IsFilenameSliceEmpty(o.Filenames) {
return fmt.Errorf("one or more resources must be specified as <resource> <name> or <resource>/<name>")
}
@ -278,11 +266,10 @@ func (o AnnotateOptions) RunAnnotate(f cmdutil.Factory, cmd *cobra.Command) erro
}
}
mapper := r.Mapper().RESTMapper
if len(o.outputFormat) > 0 {
return f.PrintObject(cmd, o.local, mapper, outputObj, o.out)
return cmdutil.PrintObject(cmd, outputObj, o.out)
}
f.PrintSuccess(mapper, false, o.out, info.Mapping.Resource, info.Name, o.dryrun, "annotated")
cmdutil.PrintSuccess(false, o.out, info.Object, o.dryrun, "annotated")
return nil
})
}

View File

@ -23,12 +23,14 @@ import (
"strings"
"testing"
"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/client-go/rest/fake"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/api/legacyscheme"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
func TestValidateAnnotationOverwrites(t *testing.T) {
@ -226,7 +228,7 @@ func TestUpdateAnnotations(t *testing.T) {
expectErr bool
}{
{
obj: &api.Pod{
obj: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{"a": "b"},
},
@ -235,41 +237,41 @@ func TestUpdateAnnotations(t *testing.T) {
expectErr: true,
},
{
obj: &api.Pod{
obj: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{"a": "b"},
},
},
annotations: map[string]string{"a": "c"},
overwrite: true,
expected: &api.Pod{
expected: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{"a": "c"},
},
},
},
{
obj: &api.Pod{
obj: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{"a": "b"},
},
},
annotations: map[string]string{"c": "d"},
expected: &api.Pod{
expected: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{"a": "b", "c": "d"},
},
},
},
{
obj: &api.Pod{
obj: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{"a": "b"},
},
},
annotations: map[string]string{"c": "d"},
version: "2",
expected: &api.Pod{
expected: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{"a": "b", "c": "d"},
ResourceVersion: "2",
@ -277,28 +279,28 @@ func TestUpdateAnnotations(t *testing.T) {
},
},
{
obj: &api.Pod{
obj: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{"a": "b"},
},
},
annotations: map[string]string{},
remove: []string{"a"},
expected: &api.Pod{
expected: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{},
},
},
},
{
obj: &api.Pod{
obj: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{"a": "b", "c": "d"},
},
},
annotations: map[string]string{"e": "f"},
remove: []string{"a"},
expected: &api.Pod{
expected: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
"c": "d",
@ -308,14 +310,14 @@ func TestUpdateAnnotations(t *testing.T) {
},
},
{
obj: &api.Pod{
obj: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{"a": "b", "c": "d"},
},
},
annotations: map[string]string{"e": "f"},
remove: []string{"g"},
expected: &api.Pod{
expected: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
"a": "b",
@ -326,13 +328,13 @@ func TestUpdateAnnotations(t *testing.T) {
},
},
{
obj: &api.Pod{
obj: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{"a": "b", "c": "d"},
},
},
remove: []string{"e"},
expected: &api.Pod{
expected: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
"a": "b",
@ -342,11 +344,11 @@ func TestUpdateAnnotations(t *testing.T) {
},
},
{
obj: &api.Pod{
obj: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{},
},
annotations: map[string]string{"a": "b"},
expected: &api.Pod{
expected: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{"a": "b"},
},
@ -415,13 +417,12 @@ func TestAnnotateErrors(t *testing.T) {
}
for k, testCase := range testCases {
f, tf, _, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
tf.Namespace = "test"
tf.ClientConfig = defaultClientConfig()
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdAnnotate(f, buf)
cmd := NewCmdAnnotate(tf, buf)
cmd.SetOutput(buf)
for k, v := range testCase.flags {
@ -436,9 +437,6 @@ func TestAnnotateErrors(t *testing.T) {
t.Errorf("%s: unexpected error: %v", k, err)
continue
}
if tf.Printer.(*testPrinter).Objects != nil {
t.Errorf("unexpected print to default printer")
}
if buf.Len() > 0 {
t.Errorf("buffer should be empty: %s", string(buf.Bytes()))
}
@ -448,8 +446,9 @@ func TestAnnotateErrors(t *testing.T) {
func TestAnnotateObject(t *testing.T) {
pods, _, _ := testData()
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
tf.UnstructuredClient = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Group: "testgroup", Version: "v1"},
NegotiatedSerializer: unstructuredSerializer,
@ -478,10 +477,10 @@ func TestAnnotateObject(t *testing.T) {
}),
}
tf.Namespace = "test"
tf.ClientConfig = defaultClientConfig()
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdAnnotate(f, buf)
cmd := NewCmdAnnotate(tf, buf)
cmd.SetOutput(buf)
options := &AnnotateOptions{}
args := []string{"pods/foo", "a=b", "c-"}
@ -491,7 +490,7 @@ func TestAnnotateObject(t *testing.T) {
if err := options.Validate(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err := options.RunAnnotate(f, cmd); err != nil {
if err := options.RunAnnotate(tf, cmd); err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
@ -499,8 +498,9 @@ func TestAnnotateObject(t *testing.T) {
func TestAnnotateObjectFromFile(t *testing.T) {
pods, _, _ := testData()
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
tf.UnstructuredClient = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Group: "testgroup", Version: "v1"},
NegotiatedSerializer: unstructuredSerializer,
@ -529,13 +529,13 @@ func TestAnnotateObjectFromFile(t *testing.T) {
}),
}
tf.Namespace = "test"
tf.ClientConfig = defaultClientConfig()
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdAnnotate(f, buf)
cmd := NewCmdAnnotate(tf, buf)
cmd.SetOutput(buf)
options := &AnnotateOptions{}
options.Filenames = []string{"../../../examples/storage/cassandra/cassandra-controller.yaml"}
options.Filenames = []string{"../../../test/e2e/testing-manifests/statefulset/cassandra/controller.yaml"}
args := []string{"a=b", "c-"}
if err := options.Complete(buf, cmd, args); err != nil {
t.Fatalf("unexpected error: %v", err)
@ -543,13 +543,13 @@ func TestAnnotateObjectFromFile(t *testing.T) {
if err := options.Validate(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err := options.RunAnnotate(f, cmd); err != nil {
if err := options.RunAnnotate(tf, cmd); err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
func TestAnnotateLocal(t *testing.T) {
f, tf, _, _ := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
tf.UnstructuredClient = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Group: "testgroup", Version: "v1"},
NegotiatedSerializer: unstructuredSerializer,
@ -559,13 +559,12 @@ func TestAnnotateLocal(t *testing.T) {
}),
}
tf.Namespace = "test"
tf.ClientConfig = defaultClientConfig()
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdAnnotate(f, buf)
cmd.Flags().Set("local", "true")
options := &AnnotateOptions{}
options.Filenames = []string{"../../../examples/storage/cassandra/cassandra-controller.yaml"}
cmd := NewCmdAnnotate(tf, buf)
options := &AnnotateOptions{local: true}
options.Filenames = []string{"../../../test/e2e/testing-manifests/statefulset/cassandra/controller.yaml"}
args := []string{"a=b"}
if err := options.Complete(buf, cmd, args); err != nil {
t.Fatalf("unexpected error: %v", err)
@ -573,7 +572,7 @@ func TestAnnotateLocal(t *testing.T) {
if err := options.Validate(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err := options.RunAnnotate(f, cmd); err != nil {
if err := options.RunAnnotate(tf, cmd); err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
@ -581,8 +580,8 @@ func TestAnnotateLocal(t *testing.T) {
func TestAnnotateMultipleObjects(t *testing.T) {
pods, _, _ := testData()
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
tf.UnstructuredClient = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Group: "testgroup", Version: "v1"},
NegotiatedSerializer: unstructuredSerializer,
@ -613,13 +612,12 @@ func TestAnnotateMultipleObjects(t *testing.T) {
}),
}
tf.Namespace = "test"
tf.ClientConfig = defaultClientConfig()
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdAnnotate(f, buf)
cmd := NewCmdAnnotate(tf, buf)
cmd.SetOutput(buf)
cmd.Flags().Set("all", "true")
options := &AnnotateOptions{}
options := &AnnotateOptions{all: true}
args := []string{"pods", "a=b", "c-"}
if err := options.Complete(buf, cmd, args); err != nil {
t.Fatalf("unexpected error: %v", err)
@ -627,7 +625,7 @@ func TestAnnotateMultipleObjects(t *testing.T) {
if err := options.Validate(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err := options.RunAnnotate(f, cmd); err != nil {
if err := options.RunAnnotate(tf, cmd); err != nil {
t.Fatalf("unexpected error: %v", err)
}
}

View File

@ -59,6 +59,7 @@ type ApplyOptions struct {
PruneResources []pruneResource
Timeout time.Duration
cmdBaseName string
all bool
}
const (
@ -105,13 +106,14 @@ func NewCmdApply(baseName string, f cmdutil.Factory, out, errOut io.Writer) *cob
options.cmdBaseName = baseName
cmd := &cobra.Command{
Use: "apply -f FILENAME",
Use: "apply -f FILENAME",
DisableFlagsInUseLine: true,
Short: i18n.T("Apply a configuration to a resource by filename or stdin"),
Long: applyLong,
Example: applyExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(validateArgs(cmd, args))
cmdutil.CheckErr(validatePruneAll(options.Prune, cmdutil.GetFlagBool(cmd, "all"), options.Selector))
cmdutil.CheckErr(validatePruneAll(options.Prune, options.all, options.Selector))
cmdutil.CheckErr(RunApply(f, cmd, out, errOut, &options))
},
}
@ -126,8 +128,8 @@ func NewCmdApply(baseName string, f cmdutil.Factory, out, errOut io.Writer) *cob
cmd.Flags().BoolVar(&options.Force, "force", false, fmt.Sprintf("Delete and re-create the specified resource, when PATCH encounters conflict and has retried for %d times.", maxPatchRetry))
cmd.Flags().DurationVar(&options.Timeout, "timeout", 0, "Only relevant during a force apply. The length of time to wait before giving up on a delete of the old resource, zero means determine a timeout from the size of the object. Any other values should contain a corresponding time unit (e.g. 1s, 2m, 3h).")
cmdutil.AddValidateFlags(cmd)
cmd.Flags().StringVarP(&options.Selector, "selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
cmd.Flags().Bool("all", false, "Select all resources in the namespace of the specified resource types.")
cmd.Flags().StringVarP(&options.Selector, "selector", "l", options.Selector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
cmd.Flags().BoolVar(&options.all, "all", options.all, "Select all resources in the namespace of the specified resource types.")
cmd.Flags().StringArray("prune-whitelist", []string{}, "Overwrite the default whitelist with <group/version/kind> for --prune")
cmd.Flags().Bool("openapi-patch", true, "If true, use openapi to calculate diff when the openapi presents and the resource can be found in the openapi spec. Otherwise, fall back to use baked-in types.")
cmdutil.AddDryRunFlag(cmd)
@ -148,11 +150,13 @@ func validateArgs(cmd *cobra.Command, args []string) error {
if len(args) != 0 {
return cmdutil.UsageErrorf(cmd, "Unexpected args: %v", args)
}
return nil
}
func validatePruneAll(prune, all bool, selector string) error {
if all && len(selector) > 0 {
return fmt.Errorf("cannot set --all and --selector at the same time")
}
if prune && !all && selector == "" {
return fmt.Errorf("all resources selected for prune without explicitly passing --all. To prune all resources, pass the --all flag. If you did not mean to prune all resources, specify a label selector.")
}
@ -233,12 +237,12 @@ func RunApply(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, opti
}
}
dryRun := cmdutil.GetFlagBool(cmd, "dry-run")
dryRun := cmdutil.GetDryRunFlag(cmd)
output := cmdutil.GetFlagString(cmd, "output")
shortOutput := output == "name"
encoder := f.JSONEncoder()
decoder := f.Decoder(false)
encoder := scheme.DefaultJSONEncoder()
deserializer := scheme.Codecs.UniversalDeserializer()
mapper := r.Mapper().RESTMapper
visitedUids := sets.NewString()
@ -294,9 +298,9 @@ func RunApply(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, opti
count++
if len(output) > 0 && !shortOutput {
return f.PrintResourceInfoForCommand(cmd, info, out)
return cmdutil.PrintObject(cmd, info.Object, out)
}
f.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, dryRun, "created")
cmdutil.PrintSuccess(shortOutput, out, info.Object, dryRun, "created")
return nil
}
@ -312,7 +316,7 @@ func RunApply(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, opti
helper := resource.NewHelper(info.Client, info.Mapping)
patcher := &patcher{
encoder: encoder,
decoder: decoder,
decoder: deserializer,
mapping: info.Mapping,
helper: helper,
clientFunc: f.UnstructuredClientForMapping,
@ -341,15 +345,15 @@ func RunApply(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, opti
if string(patchBytes) == "{}" {
count++
f.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, false, "unchanged")
cmdutil.PrintSuccess(shortOutput, out, info.Object, false, "unchanged")
return nil
}
}
count++
if len(output) > 0 && !shortOutput {
return f.PrintResourceInfoForCommand(cmd, info, out)
return cmdutil.PrintObject(cmd, info.Object, out)
}
f.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, dryRun, "configured")
cmdutil.PrintSuccess(shortOutput, out, info.Object, dryRun, "configured")
return nil
})
@ -516,7 +520,7 @@ func (p *pruner) prune(f cmdutil.Factory, namespace string, mapping *meta.RESTMa
return err
}
}
f.PrintSuccess(p.mapper, shortOutput, p.out, mapping.Resource, name, p.dryRun, "pruned")
cmdutil.PrintSuccess(shortOutput, p.out, obj, p.dryRun, "pruned")
}
return nil
}
@ -674,13 +678,13 @@ func (p *patcher) patch(current runtime.Object, modified []byte, source, namespa
}
patchBytes, patchObject, err = p.patchSimple(current, modified, source, namespace, name, errOut)
}
if err != nil && p.force {
patchBytes, patchObject, err = p.deleteAndCreate(modified, namespace, name)
if err != nil && errors.IsConflict(err) && p.force {
patchBytes, patchObject, err = p.deleteAndCreate(current, modified, namespace, name)
}
return patchBytes, patchObject, err
}
func (p *patcher) deleteAndCreate(modified []byte, namespace, name string) ([]byte, runtime.Object, error) {
func (p *patcher) deleteAndCreate(original runtime.Object, modified []byte, namespace, name string) ([]byte, runtime.Object, error) {
err := p.delete(namespace, name)
if err != nil {
return modified, nil, err
@ -699,5 +703,15 @@ func (p *patcher) deleteAndCreate(modified []byte, namespace, name string) ([]by
return modified, nil, err
}
createdObject, err := p.helper.Create(namespace, true, versionedObject)
if err != nil {
// restore the original object if we fail to create the new one
// but still propagate and advertise error to user
recreated, recreateErr := p.helper.Create(namespace, true, original)
if recreateErr != nil {
err = fmt.Errorf("An error occurred force-replacing the existing object with the newly provided one:\n\n%v.\n\nAdditionally, an error occurred attempting to restore the original object:\n\n%v\n", err, recreateErr)
} else {
createdObject = recreated
}
}
return modified, createdObject, err
}

View File

@ -26,7 +26,6 @@ import (
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/cmd/util/editor"
"k8s.io/kubernetes/pkg/printers"
)
var (
@ -63,20 +62,11 @@ func NewCmdApplyEditLastApplied(f cmdutil.Factory, out, errOut io.Writer) *cobra
options := &editor.EditOptions{
EditMode: editor.ApplyEditMode,
}
// retrieve a list of handled resources from printer as valid args
validArgs, argAliases := []string{}, []string{}
p, err := f.Printer(nil, printers.PrintOptions{
ColumnLabels: []string{},
})
cmdutil.CheckErr(err)
if p != nil {
validArgs = p.HandledResources()
argAliases = kubectl.ResourceAliases(validArgs)
}
validArgs := cmdutil.ValidArgList(f)
cmd := &cobra.Command{
Use: "edit-last-applied (RESOURCE/NAME | -f FILENAME)",
Use: "edit-last-applied (RESOURCE/NAME | -f FILENAME)",
DisableFlagsInUseLine: true,
Short: "Edit latest last-applied-configuration annotations of a resource/object",
Long: applyEditLastAppliedLong,
Example: applyEditLastAppliedExample,
@ -90,7 +80,7 @@ func NewCmdApplyEditLastApplied(f cmdutil.Factory, out, errOut io.Writer) *cobra
}
},
ValidArgs: validArgs,
ArgAliases: argAliases,
ArgAliases: kubectl.ResourceAliases(validArgs),
}
usage := "to use to edit the resource"

View File

@ -36,6 +36,7 @@ import (
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/cmd/util/editor"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/scheme"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
)
@ -51,7 +52,6 @@ type SetLastAppliedOptions struct {
ShortOutput bool
CreateAnnotation bool
Output string
Codec runtime.Encoder
PatchBufferList []PatchBuffer
Factory cmdutil.Factory
Out io.Writer
@ -84,7 +84,8 @@ var (
func NewCmdApplySetLastApplied(f cmdutil.Factory, out, err io.Writer) *cobra.Command {
options := &SetLastAppliedOptions{Out: out, ErrOut: err}
cmd := &cobra.Command{
Use: "set-last-applied -f FILENAME",
Use: "set-last-applied -f FILENAME",
DisableFlagsInUseLine: true,
Short: i18n.T("Set the last-applied-configuration annotation on a live object to match the contents of a file."),
Long: applySetLastAppliedLong,
Example: applySetLastAppliedExample,
@ -106,10 +107,9 @@ func NewCmdApplySetLastApplied(f cmdutil.Factory, out, err io.Writer) *cobra.Com
}
func (o *SetLastAppliedOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
o.DryRun = cmdutil.GetFlagBool(cmd, "dry-run")
o.DryRun = cmdutil.GetDryRunFlag(cmd)
o.Output = cmdutil.GetFlagString(cmd, "output")
o.ShortOutput = o.Output == "name"
o.Codec = f.JSONEncoder()
var err error
o.Mapper, o.Typer = f.Object()
@ -133,7 +133,7 @@ func (o *SetLastAppliedOptions) Validate(f cmdutil.Factory, cmd *cobra.Command)
if err != nil {
return err
}
patchBuf, diffBuf, patchType, err := editor.GetApplyPatch(info.Object, o.Codec)
patchBuf, diffBuf, patchType, err := editor.GetApplyPatch(info.Object, scheme.DefaultJSONEncoder())
if err != nil {
return err
}
@ -185,16 +185,16 @@ func (o *SetLastAppliedOptions) RunSetLastApplied(f cmdutil.Factory, cmd *cobra.
if len(o.Output) > 0 && !o.ShortOutput {
info.Refresh(patchedObj, false)
return f.PrintResourceInfoForCommand(cmd, info, o.Out)
return cmdutil.PrintObject(cmd, info.Object, o.Out)
}
f.PrintSuccess(o.Mapper, o.ShortOutput, o.Out, info.Mapping.Resource, info.Name, o.DryRun, "configured")
cmdutil.PrintSuccess(o.ShortOutput, o.Out, info.Object, o.DryRun, "configured")
} else {
err := o.formatPrinter(o.Output, patch.Patch, o.Out)
if err != nil {
return err
}
f.PrintSuccess(o.Mapper, o.ShortOutput, o.Out, info.Mapping.Resource, info.Name, o.DryRun, "configured")
cmdutil.PrintSuccess(o.ShortOutput, o.Out, info.Object, o.DryRun, "configured")
}
}
return nil
@ -223,7 +223,7 @@ func (o *SetLastAppliedOptions) getPatch(info *resource.Info) ([]byte, []byte, e
objMap := map[string]map[string]map[string]string{}
metadataMap := map[string]map[string]string{}
annotationsMap := map[string]string{}
localFile, err := runtime.Encode(o.Codec, info.Object)
localFile, err := runtime.Encode(scheme.DefaultJSONEncoder(), info.Object)
if err != nil {
return nil, localFile, err
}

View File

@ -21,6 +21,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
@ -38,13 +39,14 @@ import (
sptest "k8s.io/apimachinery/pkg/util/strategicpatch/testing"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/api/testapi"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/extensions"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
"k8s.io/kubernetes/pkg/printers"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
var (
@ -66,7 +68,7 @@ func TestApplyExtraArgsFail(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{})
f, _, _, _ := cmdtesting.NewAPIFactory()
f := cmdtesting.NewTestFactory()
c := NewCmdApply("kubectl", f, buf, errBuf)
if validateApplyArgs(c, []string{"rc"}) == nil {
t.Fatalf("unexpected non-error")
@ -330,8 +332,9 @@ func TestRunApplyViewLastApplied(t *testing.T) {
},
}
for _, test := range tests {
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
tf.UnstructuredClient = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: "v1"},
NegotiatedSerializer: unstructuredSerializer,
@ -354,7 +357,7 @@ func TestRunApplyViewLastApplied(t *testing.T) {
}),
}
tf.Namespace = "test"
tf.ClientConfig = defaultClientConfig()
tf.ClientConfigVal = defaultClientConfig()
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
cmdutil.BehaviorOnFatal(func(str string, code int) {
@ -363,7 +366,7 @@ func TestRunApplyViewLastApplied(t *testing.T) {
}
})
cmd := NewCmdApplyViewLastApplied(f, buf, errBuf)
cmd := NewCmdApplyViewLastApplied(tf, buf, errBuf)
if test.filePath != "" {
cmd.Flags().Set("filename", test.filePath)
}
@ -386,8 +389,7 @@ func TestApplyObjectWithoutAnnotation(t *testing.T) {
nameRC, rcBytes := readReplicationController(t, filenameRC)
pathRC := "/namespaces/test/replicationcontrollers/" + nameRC
f, tf, _, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -405,11 +407,11 @@ func TestApplyObjectWithoutAnnotation(t *testing.T) {
}),
}
tf.Namespace = "test"
tf.ClientConfig = defaultClientConfig()
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{})
cmd := NewCmdApply("kubectl", f, buf, errBuf)
cmd := NewCmdApply("kubectl", tf, buf, errBuf)
cmd.Flags().Set("filename", filenameRC)
cmd.Flags().Set("output", "name")
cmd.Run(cmd, []string{})
@ -431,8 +433,7 @@ func TestApplyObject(t *testing.T) {
pathRC := "/namespaces/test/replicationcontrollers/" + nameRC
for _, fn := range testingOpenAPISchemaFns {
f, tf, _, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -455,7 +456,7 @@ func TestApplyObject(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{})
cmd := NewCmdApply("kubectl", f, buf, errBuf)
cmd := NewCmdApply("kubectl", tf, buf, errBuf)
cmd.Flags().Set("filename", filenameRC)
cmd.Flags().Set("output", "name")
cmd.Run(cmd, []string{})
@ -493,8 +494,7 @@ func TestApplyObjectOutput(t *testing.T) {
}
for _, fn := range testingOpenAPISchemaFns {
f, tf, _, _ := cmdtesting.NewAPIFactory()
tf.Printer = &printers.YAMLPrinter{}
tf := cmdtesting.NewTestFactory()
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -517,13 +517,13 @@ func TestApplyObjectOutput(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{})
cmd := NewCmdApply("kubectl", f, buf, errBuf)
cmd := NewCmdApply("kubectl", tf, buf, errBuf)
cmd.Flags().Set("filename", filenameRC)
cmd.Flags().Set("output", "yaml")
cmd.Run(cmd, []string{})
if !strings.Contains(buf.String(), "name: test-rc") {
t.Fatalf("unexpected output: %s\nexpected to contain: %s", buf.String(), "name: test-rc")
if !strings.Contains(buf.String(), "test-rc") {
t.Fatalf("unexpected output: %s\nexpected to contain: %s", buf.String(), "test-rc")
}
if !strings.Contains(buf.String(), "post-patch: value") {
t.Fatalf("unexpected output: %s\nexpected to contain: %s", buf.String(), "post-patch: value")
@ -543,8 +543,7 @@ func TestApplyRetry(t *testing.T) {
firstPatch := true
retry := false
getCount := 0
f, tf, _, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -576,7 +575,7 @@ func TestApplyRetry(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{})
cmd := NewCmdApply("kubectl", f, buf, errBuf)
cmd := NewCmdApply("kubectl", tf, buf, errBuf)
cmd.Flags().Set("filename", filenameRC)
cmd.Flags().Set("output", "name")
cmd.Run(cmd, []string{})
@ -601,8 +600,7 @@ func TestApplyNonExistObject(t *testing.T) {
pathRC := "/namespaces/test/replicationcontrollers"
pathNameRC := pathRC + "/" + nameRC
f, tf, _, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -624,7 +622,7 @@ func TestApplyNonExistObject(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{})
cmd := NewCmdApply("kubectl", f, buf, errBuf)
cmd := NewCmdApply("kubectl", tf, buf, errBuf)
cmd.Flags().Set("filename", filenameRC)
cmd.Flags().Set("output", "name")
cmd.Run(cmd, []string{})
@ -646,8 +644,7 @@ func TestApplyEmptyPatch(t *testing.T) {
var body []byte
f, tf, _, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
tf.UnstructuredClient = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: "v1"},
NegotiatedSerializer: unstructuredSerializer,
@ -678,7 +675,7 @@ func TestApplyEmptyPatch(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{})
cmd := NewCmdApply("kubectl", f, buf, errBuf)
cmd := NewCmdApply("kubectl", tf, buf, errBuf)
cmd.Flags().Set("filename", filenameRC)
cmd.Flags().Set("output", "name")
cmd.Run(cmd, []string{})
@ -695,7 +692,7 @@ func TestApplyEmptyPatch(t *testing.T) {
buf = bytes.NewBuffer([]byte{})
errBuf = bytes.NewBuffer([]byte{})
cmd = NewCmdApply("kubectl", f, buf, errBuf)
cmd = NewCmdApply("kubectl", tf, buf, errBuf)
cmd.Flags().Set("filename", filenameRC)
cmd.Flags().Set("output", "name")
cmd.Run(cmd, []string{})
@ -721,8 +718,7 @@ func testApplyMultipleObjects(t *testing.T, asList bool) {
pathSVC := "/namespaces/test/services/" + nameSVC
for _, fn := range testingOpenAPISchemaFns {
f, tf, _, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -752,7 +748,7 @@ func testApplyMultipleObjects(t *testing.T, asList bool) {
buf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{})
cmd := NewCmdApply("kubectl", f, buf, errBuf)
cmd := NewCmdApply("kubectl", tf, buf, errBuf)
if asList {
cmd.Flags().Set("filename", filenameRCSVC)
} else {
@ -805,7 +801,7 @@ func TestApplyNULLPreservation(t *testing.T) {
deploymentBytes := readDeploymentFromFile(t, filenameDeployObjServerside)
for _, fn := range testingOpenAPISchemaFns {
f, tf, _, _ := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -849,13 +845,13 @@ func TestApplyNULLPreservation(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{})
cmd := NewCmdApply("kubectl", f, buf, errBuf)
cmd := NewCmdApply("kubectl", tf, buf, errBuf)
cmd.Flags().Set("filename", filenameDeployObjClientside)
cmd.Flags().Set("output", "name")
cmd.Run(cmd, []string{})
expected := "deployment/" + deploymentName + "\n"
expected := "deployment.extensions/" + deploymentName + "\n"
if buf.String() != expected {
t.Fatalf("unexpected output: %s\nexpected: %s", buf.String(), expected)
}
@ -877,8 +873,7 @@ func TestUnstructuredApply(t *testing.T) {
verifiedPatch := false
for _, fn := range testingOpenAPISchemaFns {
f, tf, _, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -913,12 +908,12 @@ func TestUnstructuredApply(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{})
cmd := NewCmdApply("kubectl", f, buf, errBuf)
cmd := NewCmdApply("kubectl", tf, buf, errBuf)
cmd.Flags().Set("filename", filenameWidgetClientside)
cmd.Flags().Set("output", "name")
cmd.Run(cmd, []string{})
expected := "widget/" + name + "\n"
expected := "widget.unit-test.test.com/" + name + "\n"
if buf.String() != expected {
t.Fatalf("unexpected output: %s\nexpected: %s", buf.String(), expected)
}
@ -945,8 +940,7 @@ func TestUnstructuredIdempotentApply(t *testing.T) {
verifiedPatch := false
for _, fn := range testingOpenAPISchemaFns {
f, tf, _, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -1004,12 +998,12 @@ func TestUnstructuredIdempotentApply(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{})
cmd := NewCmdApply("kubectl", f, buf, errBuf)
cmd := NewCmdApply("kubectl", tf, buf, errBuf)
cmd.Flags().Set("filename", filenameWidgetClientside)
cmd.Flags().Set("output", "name")
cmd.Run(cmd, []string{})
expected := "widget/widget\n"
expected := "widget.unit-test.test.com/widget\n"
if buf.String() != expected {
t.Fatalf("unexpected output: %s\nexpected: %s", buf.String(), expected)
}
@ -1074,8 +1068,9 @@ func TestRunApplySetLastApplied(t *testing.T) {
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
tf.UnstructuredClient = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: "v1"},
NegotiatedSerializer: unstructuredSerializer,
@ -1102,7 +1097,7 @@ func TestRunApplySetLastApplied(t *testing.T) {
}),
}
tf.Namespace = "test"
tf.ClientConfig = defaultClientConfig()
tf.ClientConfigVal = defaultClientConfig()
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
cmdutil.BehaviorOnFatal(func(str string, code int) {
@ -1111,7 +1106,7 @@ func TestRunApplySetLastApplied(t *testing.T) {
}
})
cmd := NewCmdApplySetLastApplied(f, buf, errBuf)
cmd := NewCmdApplySetLastApplied(tf, buf, errBuf)
cmd.Flags().Set("filename", test.filePath)
cmd.Flags().Set("output", test.output)
cmd.Run(cmd, []string{})
@ -1153,7 +1148,7 @@ func TestForceApply(t *testing.T) {
pathRC := "/namespaces/test/replicationcontrollers/" + nameRC
pathRCList := "/namespaces/test/replicationcontrollers"
expected := map[string]int{
"getOk": 9,
"getOk": 10,
"getNotFound": 1,
"getList": 1,
"patch": 6,
@ -1164,9 +1159,9 @@ func TestForceApply(t *testing.T) {
for _, fn := range testingOpenAPISchemaFns {
deleted := false
isScaledDownToZero := false
counts := map[string]int{}
f, tf, _, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -1177,7 +1172,18 @@ func TestForceApply(t *testing.T) {
return &http.Response{StatusCode: 404, Header: defaultHeader(), Body: ioutil.NopCloser(bytes.NewReader([]byte{}))}, nil
}
counts["getOk"]++
bodyRC := ioutil.NopCloser(bytes.NewReader(currentRC))
var bodyRC io.ReadCloser
if isScaledDownToZero {
rcObj := readReplicationControllerFromFile(t, filenameRC)
rcObj.Spec.Replicas = 0
rcBytes, err := runtime.Encode(testapi.Default.Codec(), rcObj)
if err != nil {
t.Fatal(err)
}
bodyRC = ioutil.NopCloser(bytes.NewReader(rcBytes))
} else {
bodyRC = ioutil.NopCloser(bytes.NewReader(currentRC))
}
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: bodyRC}, nil
case strings.HasSuffix(p, pathRCList) && m == "GET":
counts["getList"]++
@ -1212,10 +1218,12 @@ func TestForceApply(t *testing.T) {
case strings.HasSuffix(p, pathRC) && m == "PUT":
counts["put"]++
bodyRC := ioutil.NopCloser(bytes.NewReader(currentRC))
isScaledDownToZero = true
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: bodyRC}, nil
case strings.HasSuffix(p, pathRCList) && m == "POST":
counts["post"]++
deleted = false
isScaledDownToZero = false
bodyRC := ioutil.NopCloser(bytes.NewReader(currentRC))
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: bodyRC}, nil
default:
@ -1226,12 +1234,12 @@ func TestForceApply(t *testing.T) {
}
tf.OpenAPISchemaFunc = fn
tf.Client = tf.UnstructuredClient
tf.ClientConfig = &restclient.Config{}
tf.ClientConfigVal = &restclient.Config{}
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{})
cmd := NewCmdApply("kubectl", f, buf, errBuf)
cmd := NewCmdApply("kubectl", tf, buf, errBuf)
cmd.Flags().Set("filename", filenameRC)
cmd.Flags().Set("output", "name")
cmd.Flags().Set("force", "true")

View File

@ -60,7 +60,8 @@ var (
func NewCmdApplyViewLastApplied(f cmdutil.Factory, out, err io.Writer) *cobra.Command {
options := &ViewLastAppliedOptions{Out: out, ErrOut: err}
cmd := &cobra.Command{
Use: "view-last-applied (TYPE [NAME | -l label] | TYPE/NAME | -f FILENAME)",
Use: "view-last-applied (TYPE [NAME | -l label] | TYPE/NAME | -f FILENAME)",
DisableFlagsInUseLine: true,
Short: i18n.T("View latest last-applied-configuration annotations of a resource/object"),
Long: applyViewLastAppliedLong,
Example: applyViewLastAppliedExample,

View File

@ -71,7 +71,8 @@ func NewCmdAttach(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer)
Attach: &DefaultRemoteAttach{},
}
cmd := &cobra.Command{
Use: "attach (POD | TYPE/NAME) -c CONTAINER",
Use: "attach (POD | TYPE/NAME) -c CONTAINER",
DisableFlagsInUseLine: true,
Short: i18n.T("Attach to a running container"),
Long: "Attach to a process that is already running inside an existing container.",
Example: attachExample,
@ -114,7 +115,8 @@ func (*DefaultRemoteAttach) Attach(method string, url *url.URL, config *restclie
type AttachOptions struct {
StreamOptions
CommandName string
CommandName string
SuggestedCmdUsage string
Pod *api.Pod
@ -167,6 +169,15 @@ func (p *AttachOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, argsIn [
p.PodName = attachablePod.Name
p.Namespace = namespace
fullCmdName := ""
cmdParent := cmd.Parent()
if cmdParent != nil {
fullCmdName = cmdParent.CommandPath()
}
if len(fullCmdName) > 0 && cmdutil.IsSiblingCommandExists(cmd, "describe") {
p.SuggestedCmdUsage = fmt.Sprintf("Use '%s describe pod/%s -n %s' to see all of the containers in this pod.", fullCmdName, p.PodName, p.Namespace)
}
config, err := f.ClientConfig()
if err != nil {
return err
@ -259,11 +270,6 @@ func (p *AttachOptions) Run() error {
}
fn := func() error {
if !p.Quiet && stderr != nil {
fmt.Fprintln(stderr, "If you don't see a command prompt, try pressing enter.")
}
restClient, err := restclient.RESTClientFor(p.Config)
if err != nil {
return err
@ -285,6 +291,9 @@ func (p *AttachOptions) Run() error {
return p.Attach.Attach("POST", req.URL(), p.Config, p.In, p.Out, p.Err, t.Raw, sizeQueue)
}
if !p.Quiet && stderr != nil {
fmt.Fprintln(stderr, "If you don't see a command prompt, try pressing enter.")
}
if err := t.Safe(fn); err != nil {
return err
}
@ -312,6 +321,11 @@ func (p *AttachOptions) containerToAttachTo(pod *api.Pod) (*api.Container, error
return nil, fmt.Errorf("container not found (%s)", p.ContainerName)
}
if len(p.SuggestedCmdUsage) > 0 {
fmt.Fprintf(p.Err, "Defaulting container name to %s.\n", pod.Spec.Containers[0].Name)
fmt.Fprintf(p.Err, "%s\n", p.SuggestedCmdUsage)
}
glog.V(4).Infof("defaulting container name to %s", pod.Spec.Containers[0].Name)
return &pod.Spec.Containers[0], nil
}

View File

@ -38,6 +38,7 @@ import (
api "k8s.io/kubernetes/pkg/apis/core"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
type fakeRemoteAttach struct {
@ -138,7 +139,10 @@ func TestPodAndContainerAttach(t *testing.T) {
}
for _, test := range tests {
f, tf, codec, ns := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion,
NegotiatedSerializer: ns,
@ -150,13 +154,13 @@ func TestPodAndContainerAttach(t *testing.T) {
}),
}
tf.Namespace = "test"
tf.ClientConfig = defaultClientConfig()
tf.ClientConfigVal = defaultClientConfig()
cmd := &cobra.Command{}
options := test.p
cmdutil.AddPodRunningTimeoutFlag(cmd, test.timeout)
err := options.Complete(f, cmd, test.args)
err := options.Complete(tf, cmd, test.args)
if test.expectError && err == nil {
t.Errorf("%s: unexpected non-error", test.name)
}
@ -215,7 +219,10 @@ func TestAttach(t *testing.T) {
},
}
for _, test := range tests {
f, tf, codec, ns := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion,
NegotiatedSerializer: ns,
@ -235,7 +242,7 @@ func TestAttach(t *testing.T) {
}),
}
tf.Namespace = "test"
tf.ClientConfig = &restclient.Config{APIPath: "/api", ContentConfig: restclient.ContentConfig{NegotiatedSerializer: legacyscheme.Codecs, GroupVersion: &schema.GroupVersion{Version: test.version}}}
tf.ClientConfigVal = &restclient.Config{APIPath: "/api", ContentConfig: restclient.ContentConfig{NegotiatedSerializer: legacyscheme.Codecs, GroupVersion: &schema.GroupVersion{Version: test.version}}}
bufOut := bytes.NewBuffer([]byte{})
bufErr := bytes.NewBuffer([]byte{})
bufIn := bytes.NewBuffer([]byte{})
@ -255,7 +262,7 @@ func TestAttach(t *testing.T) {
}
cmd := &cobra.Command{}
cmdutil.AddPodRunningTimeoutFlag(cmd, 1000)
if err := params.Complete(f, cmd, []string{"foo"}); err != nil {
if err := params.Complete(tf, cmd, []string{"foo"}); err != nil {
t.Fatal(err)
}
err := params.Run()
@ -286,9 +293,9 @@ func TestAttach(t *testing.T) {
func TestAttachWarnings(t *testing.T) {
version := legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion.Version
tests := []struct {
name, container, version, podPath, fetchPodPath, expectedErr, expectedOut string
pod *api.Pod
stdin, tty bool
name, container, version, podPath, fetchPodPath, expectedErr string
pod *api.Pod
stdin, tty bool
}{
{
name: "fallback tty if not supported",
@ -302,7 +309,10 @@ func TestAttachWarnings(t *testing.T) {
},
}
for _, test := range tests {
f, tf, codec, ns := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion,
NegotiatedSerializer: ns,
@ -321,7 +331,7 @@ func TestAttachWarnings(t *testing.T) {
}),
}
tf.Namespace = "test"
tf.ClientConfig = &restclient.Config{APIPath: "/api", ContentConfig: restclient.ContentConfig{NegotiatedSerializer: legacyscheme.Codecs, GroupVersion: &schema.GroupVersion{Version: test.version}}}
tf.ClientConfigVal = &restclient.Config{APIPath: "/api", ContentConfig: restclient.ContentConfig{NegotiatedSerializer: legacyscheme.Codecs, GroupVersion: &schema.GroupVersion{Version: test.version}}}
bufOut := bytes.NewBuffer([]byte{})
bufErr := bytes.NewBuffer([]byte{})
bufIn := bytes.NewBuffer([]byte{})
@ -340,7 +350,7 @@ func TestAttachWarnings(t *testing.T) {
}
cmd := &cobra.Command{}
cmdutil.AddPodRunningTimeoutFlag(cmd, 1000)
if err := params.Complete(f, cmd, []string{"foo"}); err != nil {
if err := params.Complete(tf, cmd, []string{"foo"}); err != nil {
t.Fatal(err)
}
if err := params.Run(); err != nil {

View File

@ -50,9 +50,9 @@ filegroup(
go_test(
name = "go_default_test",
srcs = ["cani_test.go"],
importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/auth",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/kubectl/cmd/testing:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library",

View File

@ -88,7 +88,8 @@ func NewCmdCanI(f cmdutil.Factory, out, err io.Writer) *cobra.Command {
}
cmd := &cobra.Command{
Use: "can-i VERB [TYPE | TYPE/NAME | NONRESOURCEURL]",
Use: "can-i VERB [TYPE | TYPE/NAME | NONRESOURCEURL]",
DisableFlagsInUseLine: true,
Short: "Check whether an action is allowed",
Long: canILong,
Example: canIExample,

View File

@ -27,6 +27,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
)
@ -119,7 +120,9 @@ func TestRunAccessCheck(t *testing.T) {
test.o.Out = ioutil.Discard
test.o.Err = ioutil.Discard
f, tf, _, ns := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Group: "", Version: "v1"},
NegotiatedSerializer: ns,
@ -152,9 +155,9 @@ func TestRunAccessCheck(t *testing.T) {
}),
}
tf.Namespace = "test"
tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}}
tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}}
if err := test.o.Complete(f, test.args); err != nil {
if err := test.o.Complete(tf, test.args); err != nil {
t.Errorf("%s: %v", test.name, err)
continue
}

View File

@ -64,7 +64,8 @@ func NewCmdReconcile(f cmdutil.Factory, out, err io.Writer) *cobra.Command {
}
cmd := &cobra.Command{
Use: "reconcile -f FILENAME",
Use: "reconcile -f FILENAME",
DisableFlagsInUseLine: true,
Short: "Reconciles rules for RBAC Role, RoleBinding, ClusterRole, and ClusterRole binding objects",
Long: reconcileLong,
Example: reconcileExample,
@ -113,15 +114,14 @@ func (o *ReconcileOptions) Complete(cmd *cobra.Command, f cmdutil.Factory, args
o.RBACClient = client.Rbac()
o.NamespaceClient = client.Core().Namespaces()
mapper, _ := f.Object()
dryRun := false
output := cmdutil.GetFlagString(cmd, "output")
shortOutput := output == "name"
o.Print = func(info *resource.Info) error {
if len(output) > 0 && !shortOutput {
return f.PrintResourceInfoForCommand(cmd, info, o.Out)
return cmdutil.PrintObject(cmd, info.Object, o.Out)
}
f.PrintSuccess(mapper, shortOutput, o.Out, info.Mapping.Resource, info.Name, dryRun, "reconciled")
cmdutil.PrintSuccess(shortOutput, o.Out, info.Object, dryRun, "reconciled")
return nil
}

View File

@ -52,7 +52,8 @@ func NewCmdAutoscale(f cmdutil.Factory, out io.Writer) *cobra.Command {
argAliases := kubectl.ResourceAliases(validArgs)
cmd := &cobra.Command{
Use: "autoscale (-f FILENAME | TYPE NAME | TYPE/NAME) [--min=MINPODS] --max=MAXPODS [--cpu-percent=CPU] [flags]",
Use: "autoscale (-f FILENAME | TYPE NAME | TYPE/NAME) [--min=MINPODS] --max=MAXPODS [--cpu-percent=CPU]",
DisableFlagsInUseLine: true,
Short: i18n.T("Auto-scale a Deployment, ReplicaSet, or ReplicationController"),
Long: autoscaleLong,
Example: autoscaleExample,
@ -64,11 +65,11 @@ func NewCmdAutoscale(f cmdutil.Factory, out io.Writer) *cobra.Command {
ArgAliases: argAliases,
}
cmdutil.AddPrinterFlags(cmd)
cmd.Flags().String("generator", "horizontalpodautoscaler/v1", i18n.T("The name of the API generator to use. Currently there is only 1 generator."))
cmd.Flags().Int("min", -1, "The lower limit for the number of pods that can be set by the autoscaler. If it's not specified or negative, the server will apply a default value.")
cmd.Flags().Int("max", -1, "The upper limit for the number of pods that can be set by the autoscaler. Required.")
cmd.Flags().String("generator", cmdutil.HorizontalPodAutoscalerV1GeneratorName, i18n.T("The name of the API generator to use. Currently there is only 1 generator."))
cmd.Flags().Int32("min", -1, "The lower limit for the number of pods that can be set by the autoscaler. If it's not specified or negative, the server will apply a default value.")
cmd.Flags().Int32("max", -1, "The upper limit for the number of pods that can be set by the autoscaler. Required.")
cmd.MarkFlagRequired("max")
cmd.Flags().Int("cpu-percent", -1, fmt.Sprintf("The target average CPU utilization (represented as a percent of requested CPU) over all the pods. If it's not specified or negative, a default autoscaling policy will be used."))
cmd.Flags().Int32("cpu-percent", -1, fmt.Sprintf("The target average CPU utilization (represented as a percent of requested CPU) over all the pods. If it's not specified or negative, a default autoscaling policy will be used."))
cmd.Flags().String("name", "", i18n.T("The name for the newly created object. If not specified, the name of the input resource will be used."))
cmdutil.AddDryRunFlag(cmd)
usage := "identifying the resource to autoscale."
@ -103,15 +104,6 @@ func RunAutoscale(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s
return err
}
// Get the generator, setup and validate all required parameters
generatorName := cmdutil.GetFlagString(cmd, "generator")
generators := f.Generators("autoscale")
generator, found := generators[generatorName]
if !found {
return cmdutil.UsageErrorf(cmd, "generator %q not found.", generatorName)
}
names := generator.ParamNames()
count := 0
err = r.Visit(func(info *resource.Info, err error) error {
if err != nil {
@ -123,24 +115,25 @@ func RunAutoscale(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s
return err
}
name := info.Name
params := kubectl.MakeParams(cmd, names)
params["default-name"] = name
params["scaleRef-kind"] = mapping.GroupVersionKind.Kind
params["scaleRef-name"] = name
params["scaleRef-apiVersion"] = mapping.GroupVersionKind.GroupVersion().String()
if err = kubectl.ValidateParams(names, params); err != nil {
return err
}
// Check for invalid flags used against the present generator.
if err := kubectl.EnsureFlagsValid(cmd, generators, generatorName); err != nil {
return err
// get the generator
var generator kubectl.StructuredGenerator
switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName {
case cmdutil.HorizontalPodAutoscalerV1GeneratorName:
generator = &kubectl.HorizontalPodAutoscalerGeneratorV1{
Name: info.Name,
MinReplicas: cmdutil.GetFlagInt32(cmd, "min"),
MaxReplicas: cmdutil.GetFlagInt32(cmd, "max"),
CPUPercent: cmdutil.GetFlagInt32(cmd, "cpu-percent"),
ScaleRefName: info.Name,
ScaleRefKind: mapping.GroupVersionKind.Kind,
ScaleRefApiVersion: mapping.GroupVersionKind.GroupVersion().String(),
}
default:
return errUnsupportedGenerator(cmd, generatorName)
}
// Generate new object
object, err := generator.Generate(params)
object, err := generator.StructuredGenerate()
if err != nil {
return err
}
@ -150,7 +143,7 @@ func RunAutoscale(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s
ObjectTyper: typer,
RESTMapper: mapper,
ClientMapper: resource.ClientMapperFunc(f.ClientForMapping),
Decoder: f.Decoder(true),
Decoder: cmdutil.InternalVersionDecoder(),
}
hpa, err := resourceMapper.InfoForObject(object, nil)
if err != nil {
@ -163,10 +156,10 @@ func RunAutoscale(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s
object = hpa.Object
}
if cmdutil.GetDryRunFlag(cmd) {
return f.PrintObject(cmd, false, mapper, object, out)
return cmdutil.PrintObject(cmd, object, out)
}
if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), hpa, f.JSONEncoder()); err != nil {
if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), hpa, cmdutil.InternalVersionJSONEncoder()); err != nil {
return err
}
@ -177,10 +170,10 @@ func RunAutoscale(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s
count++
if len(cmdutil.GetFlagString(cmd, "output")) > 0 {
return f.PrintObject(cmd, false, mapper, object, out)
return cmdutil.PrintObject(cmd, object, out)
}
f.PrintSuccess(mapper, false, out, info.Mapping.Resource, info.Name, cmdutil.GetDryRunFlag(cmd), "autoscaled")
cmdutil.PrintSuccess(false, out, info.Object, cmdutil.GetDryRunFlag(cmd), "autoscaled")
return nil
})
if err != nil {
@ -194,7 +187,7 @@ func RunAutoscale(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s
func validateFlags(cmd *cobra.Command) error {
errs := []error{}
max, min := cmdutil.GetFlagInt(cmd, "max"), cmdutil.GetFlagInt(cmd, "min")
max, min := cmdutil.GetFlagInt32(cmd, "max"), cmdutil.GetFlagInt32(cmd, "min")
if max < 1 {
errs = append(errs, fmt.Errorf("--max=MAXPODS is required and must be at least 1, max: %d", max))
}

View File

@ -32,7 +32,8 @@ import (
func NewCmdCertificate(f cmdutil.Factory, out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "certificate SUBCOMMAND",
Use: "certificate SUBCOMMAND",
DisableFlagsInUseLine: true,
Short: i18n.T("Modify certificate resources."),
Long: "Modify certificate resources.",
Run: func(cmd *cobra.Command, args []string) {
@ -68,7 +69,8 @@ func (options *CertificateOptions) Validate() error {
func NewCmdCertificateApprove(f cmdutil.Factory, out io.Writer) *cobra.Command {
options := CertificateOptions{}
cmd := &cobra.Command{
Use: "approve (-f FILENAME | NAME)",
Use: "approve (-f FILENAME | NAME)",
DisableFlagsInUseLine: true,
Short: i18n.T("Approve a certificate signing request"),
Long: templates.LongDesc(`
Approve a certificate signing request.
@ -118,7 +120,8 @@ func (options *CertificateOptions) RunCertificateApprove(f cmdutil.Factory, out
func NewCmdCertificateDeny(f cmdutil.Factory, out io.Writer) *cobra.Command {
options := CertificateOptions{}
cmd := &cobra.Command{
Use: "deny (-f FILENAME | NAME)",
Use: "deny (-f FILENAME | NAME)",
DisableFlagsInUseLine: true,
Short: i18n.T("Deny a certificate signing request"),
Long: templates.LongDesc(`
Deny a certificate signing request.
@ -162,7 +165,6 @@ func (options *CertificateOptions) RunCertificateDeny(f cmdutil.Factory, out io.
func (options *CertificateOptions) modifyCertificateCondition(f cmdutil.Factory, out io.Writer, modify func(csr *certificates.CertificateSigningRequest) (*certificates.CertificateSigningRequest, string)) error {
var found int
mapper, _ := f.Object()
c, err := f.ClientSet()
if err != nil {
return err
@ -189,7 +191,7 @@ func (options *CertificateOptions) modifyCertificateCondition(f cmdutil.Factory,
return err
}
found++
f.PrintSuccess(mapper, options.outputStyle == "name", out, info.Mapping.Resource, info.Name, false, verb)
cmdutil.PrintSuccess(options.outputStyle == "name", out, info.Object, false, verb)
return nil
})
if found == 0 {

View File

@ -135,7 +135,7 @@ func printService(out io.Writer, name, link string) {
ct.ChangeColor(ct.Green, false, ct.None, false)
fmt.Fprint(out, name)
ct.ResetColor()
fmt.Fprintf(out, " is running at ")
fmt.Fprint(out, " is running at ")
ct.ChangeColor(ct.Yellow, false, ct.None, false)
fmt.Fprint(out, link)
ct.ResetColor()

View File

@ -30,7 +30,7 @@ func TestSetupOutputWriterNoOp(t *testing.T) {
tests := []string{"", "-"}
for _, test := range tests {
out := &bytes.Buffer{}
f, _, _, _ := cmdtesting.NewAPIFactory()
f := cmdtesting.NewTestFactory()
cmd := NewCmdClusterInfoDump(f, os.Stdout)
cmd.Flag("output-directory").Value.Set(test)
writer := setupOutputWriter(cmd, out, "/some/file/that/should/be/ignored")
@ -50,7 +50,7 @@ func TestSetupOutputWriterFile(t *testing.T) {
defer os.RemoveAll(dir)
out := &bytes.Buffer{}
f, _, _, _ := cmdtesting.NewAPIFactory()
f := cmdtesting.NewTestFactory()
cmd := NewCmdClusterInfoDump(f, os.Stdout)
cmd.Flag("output-directory").Value.Set(dir)
writer := setupOutputWriter(cmd, out, file)

View File

@ -19,6 +19,7 @@ package cmd
import (
"fmt"
"io"
"os"
"k8s.io/apiserver/pkg/util/flag"
"k8s.io/client-go/tools/clientcmd"
@ -31,7 +32,6 @@ import (
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"github.com/golang/glog"
"github.com/spf13/cobra"
)
@ -212,6 +212,10 @@ var (
}
)
func NewDefaultKubectlCommand() *cobra.Command {
return NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, os.Stdout, os.Stderr)
}
// NewKubectlCommand creates the `kubectl` command and its nested children.
func NewKubectlCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cobra.Command {
// Parent command to which all subcommands are added.
@ -221,7 +225,8 @@ func NewKubectlCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob
Long: templates.LongDesc(`
kubectl controls the Kubernetes cluster manager.
Find more information at https://github.com/kubernetes/kubernetes.`),
Find more information at:
https://kubernetes.io/docs/reference/kubectl/overview/`),
Run: runHelp,
BashCompletionFunction: bashCompletionFunc,
}
@ -264,7 +269,7 @@ func NewKubectlCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob
Commands: []*cobra.Command{
rollout.NewCmdRollout(f, out, err),
NewCmdRollingUpdate(f, out),
NewCmdScale(f, out),
NewCmdScale(f, out, err),
NewCmdAutoscale(f, out),
},
},
@ -284,7 +289,7 @@ func NewKubectlCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob
Message: "Troubleshooting and Debugging Commands:",
Commands: []*cobra.Command{
NewCmdDescribe(f, out, err),
NewCmdLogs(f, out),
NewCmdLogs(f, out, err),
NewCmdAttach(f, in, out, err),
NewCmdExec(f, in, out, err),
NewCmdPortForward(f, out, err),
@ -336,7 +341,7 @@ func NewKubectlCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob
}
cmds.AddCommand(alpha)
cmds.AddCommand(cmdconfig.NewCmdConfig(clientcmd.NewDefaultPathOptions(), out, err))
cmds.AddCommand(cmdconfig.NewCmdConfig(f, clientcmd.NewDefaultPathOptions(), out, err))
cmds.AddCommand(NewCmdPlugin(f, in, out, err))
cmds.AddCommand(NewCmdVersion(f, out))
cmds.AddCommand(NewCmdApiVersions(f, out))
@ -349,8 +354,8 @@ func runHelp(cmd *cobra.Command, args []string) {
cmd.Help()
}
func printDeprecationWarning(command, alias string) {
glog.Warningf("%s is DEPRECATED and will be removed in a future version. Use %s instead.", alias, command)
func printDeprecationWarning(errOut io.Writer, command, alias string) {
fmt.Fprintf(errOut, "%s is DEPRECATED and will be removed in a future version. Use %s instead.", alias, command)
}
// deprecatedAlias is intended to be used to create a "wrapper" command around
@ -367,13 +372,3 @@ func deprecatedAlias(deprecatedVersion string, cmd *cobra.Command) *cobra.Comman
cmd.Hidden = true
return cmd
}
// deprecated is similar to deprecatedAlias, but it is used for deprecations
// that are not simple aliases; this command is actually a different
// (deprecated) codepath.
func deprecated(baseName, to string, parent, cmd *cobra.Command) string {
cmd.Long = fmt.Sprintf("Deprecated: all functionality can be found in \"%s %s\"", baseName, to)
cmd.Short = fmt.Sprintf("Deprecated: use %s", to)
parent.AddCommand(cmd)
return cmd.Name()
}

View File

@ -36,14 +36,15 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/api/testapi"
apitesting "k8s.io/kubernetes/pkg/api/testing"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/kubectl/cmd/resource"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/scheme"
"k8s.io/kubernetes/pkg/printers"
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
"k8s.io/kubernetes/pkg/util/strings"
)
@ -175,91 +176,15 @@ func stringBody(body string) io.ReadCloser {
return ioutil.NopCloser(bytes.NewReader([]byte(body)))
}
// TODO(jlowdermilk): refactor the Factory so we can test client versions properly,
// with different client/server version skew scenarios.
// Verify that resource.RESTClients constructed from a factory respect mapping.APIVersion
//func TestClientVersions(t *testing.T) {
// f := cmdutil.NewFactory(nil)
//
// version := testapi.Default.Version()
// mapping := &meta.RESTMapping{
// APIVersion: version,
// }
// c, err := f.ClientForMapping(mapping)
// if err != nil {
// t.Errorf("unexpected error: %v", err)
// }
// client := c.(*client.RESTClient)
// if client.APIVersion() != version {
// t.Errorf("unexpected Client APIVersion: %s %v", client.APIVersion, client)
// }
//}
func Example_printReplicationControllerWithNamespace() {
f, tf, _, ns := cmdtesting.NewAPIFactory()
p := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{
WithNamespace: true,
ColumnLabels: []string{},
})
printersinternal.AddHandlers(p)
tf.Printer = p
tf.Client = &fake.RESTClient{
NegotiatedSerializer: ns,
Client: nil,
}
cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr)
ctrl := &api.ReplicationController{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: "beep",
Labels: map[string]string{"foo": "bar"},
CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
},
Spec: api.ReplicationControllerSpec{
Replicas: 1,
Selector: map[string]string{"foo": "bar"},
Template: &api.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"foo": "bar"},
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "foo",
Image: "someimage",
},
},
},
},
},
Status: api.ReplicationControllerStatus{
Replicas: 1,
ReadyReplicas: 1,
},
}
mapper, _ := f.Object()
err := f.PrintObject(cmd, false, mapper, ctrl, os.Stdout)
if err != nil {
fmt.Printf("Unexpected error: %v", err)
}
// Output:
// NAMESPACE NAME DESIRED CURRENT READY AGE
// beep foo 1 1 1 10y
}
func Example_printMultiContainersReplicationControllerWithWide() {
f, tf, _, ns := cmdtesting.NewAPIFactory()
p := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{
Wide: true,
ColumnLabels: []string{},
})
printersinternal.AddHandlers(p)
tf.Printer = p
tf := cmdtesting.NewTestFactory()
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
NegotiatedSerializer: ns,
Client: nil,
}
cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr)
cmd := NewCmdRun(tf, os.Stdin, os.Stdout, os.Stderr)
ctrl := &api.ReplicationController{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@ -291,8 +216,8 @@ func Example_printMultiContainersReplicationControllerWithWide() {
Replicas: 1,
},
}
mapper, _ := f.Object()
err := f.PrintObject(cmd, false, mapper, ctrl, os.Stdout)
cmd.Flags().Set("output", "wide")
err := cmdutil.PrintObject(cmd, ctrl, os.Stdout)
if err != nil {
fmt.Printf("Unexpected error: %v", err)
}
@ -302,17 +227,14 @@ func Example_printMultiContainersReplicationControllerWithWide() {
}
func Example_printReplicationController() {
f, tf, _, ns := cmdtesting.NewAPIFactory()
p := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{
ColumnLabels: []string{},
})
printersinternal.AddHandlers(p)
tf.Printer = p
tf := cmdtesting.NewTestFactory()
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
NegotiatedSerializer: ns,
Client: nil,
}
cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr)
cmd := NewCmdRun(tf, os.Stdin, os.Stdout, os.Stderr)
ctrl := &api.ReplicationController{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@ -344,8 +266,7 @@ func Example_printReplicationController() {
Replicas: 1,
},
}
mapper, _ := f.Object()
err := f.PrintObject(cmd, false, mapper, ctrl, os.Stdout)
err := cmdutil.PrintObject(cmd, ctrl, os.Stdout)
if err != nil {
fmt.Printf("Unexpected error: %v", err)
}
@ -355,19 +276,15 @@ func Example_printReplicationController() {
}
func Example_printPodWithWideFormat() {
f, tf, _, ns := cmdtesting.NewAPIFactory()
p := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{
Wide: true,
ColumnLabels: []string{},
})
printersinternal.AddHandlers(p)
tf.Printer = p
tf := cmdtesting.NewTestFactory()
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
NegotiatedSerializer: ns,
Client: nil,
}
nodeName := "kubernetes-node-abcd"
cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr)
cmd := NewCmdRun(tf, os.Stdin, os.Stdout, os.Stderr)
pod := &api.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
@ -386,8 +303,8 @@ func Example_printPodWithWideFormat() {
PodIP: "10.1.1.3",
},
}
mapper, _ := f.Object()
err := f.PrintObject(cmd, false, mapper, pod, os.Stdout)
cmd.Flags().Set("output", "wide")
err := cmdutil.PrintObject(cmd, pod, os.Stdout)
if err != nil {
fmt.Printf("Unexpected error: %v", err)
}
@ -397,19 +314,15 @@ func Example_printPodWithWideFormat() {
}
func Example_printPodWithShowLabels() {
f, tf, _, ns := cmdtesting.NewAPIFactory()
p := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{
ShowLabels: true,
ColumnLabels: []string{},
})
printersinternal.AddHandlers(p)
tf.Printer = p
tf := cmdtesting.NewTestFactory()
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
NegotiatedSerializer: ns,
Client: nil,
}
nodeName := "kubernetes-node-abcd"
cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr)
cmd := NewCmdRun(tf, os.Stdin, os.Stdout, os.Stderr)
pod := &api.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
@ -431,8 +344,8 @@ func Example_printPodWithShowLabels() {
},
},
}
mapper, _ := f.Object()
err := f.PrintObject(cmd, false, mapper, pod, os.Stdout)
cmd.Flags().Set("show-labels", "true")
err := cmdutil.PrintObject(cmd, pod, os.Stdout)
if err != nil {
fmt.Printf("Unexpected error: %v", err)
}
@ -533,29 +446,29 @@ func newAllPhasePodList() *api.PodList {
}
}
func Example_printPodHideTerminated() {
f, tf, _, ns := cmdtesting.NewAPIFactory()
p := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{
ColumnLabels: []string{},
})
printersinternal.AddHandlers(p)
tf.Printer = p
func Example_printPodShowTerminated() {
tf := cmdtesting.NewTestFactory()
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
NegotiatedSerializer: ns,
Client: nil,
}
cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr)
cmd := NewCmdRun(tf, os.Stdin, os.Stdout, os.Stderr)
podList := newAllPhasePodList()
// filter pods
filterFuncs := f.DefaultResourceFilterFunc()
filterFuncs := tf.DefaultResourceFilterFunc()
filterOpts := cmdutil.ExtractCmdPrintOptions(cmd, false)
_, filteredPodList, errs := cmdutil.FilterResourceList(podList, filterFuncs, filterOpts)
if errs != nil {
fmt.Printf("Unexpected filter error: %v\n", errs)
}
printer, err := cmdutil.PrinterForOptions(cmdutil.ExtractCmdPrintOptions(cmd, false))
if err != nil {
fmt.Printf("Unexpected printer get error: %v\n", errs)
}
for _, pod := range filteredPodList {
mapper, _ := f.Object()
err := f.PrintObject(cmd, false, mapper, pod, os.Stdout)
err := printer.PrintObj(pod, os.Stdout)
if err != nil {
fmt.Printf("Unexpected error: %v", err)
}
@ -564,25 +477,22 @@ func Example_printPodHideTerminated() {
// NAME READY STATUS RESTARTS AGE
// test1 1/2 Pending 6 10y
// test2 1/2 Running 6 10y
// test3 1/2 Succeeded 6 10y
// test4 1/2 Failed 6 10y
// test5 1/2 Unknown 6 10y
}
func Example_printPodShowAll() {
f, tf, _, ns := cmdtesting.NewAPIFactory()
p := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{
ShowAll: true,
ColumnLabels: []string{},
})
printersinternal.AddHandlers(p)
tf.Printer = p
tf := cmdtesting.NewTestFactory()
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
NegotiatedSerializer: ns,
Client: nil,
}
cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr)
cmd := NewCmdRun(tf, os.Stdin, os.Stdout, os.Stderr)
podList := newAllPhasePodList()
mapper, _ := f.Object()
err := f.PrintObject(cmd, false, mapper, podList, os.Stdout)
err := cmdutil.PrintObject(cmd, podList, os.Stdout)
if err != nil {
fmt.Printf("Unexpected error: %v", err)
}
@ -595,19 +505,15 @@ func Example_printPodShowAll() {
// test5 1/2 Unknown 6 10y
}
func Example_printServiceWithNamespacesAndLabels() {
f, tf, _, ns := cmdtesting.NewAPIFactory()
p := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{
WithNamespace: true,
ColumnLabels: []string{"l1"},
})
printersinternal.AddHandlers(p)
tf.Printer = p
func Example_printServiceWithLabels() {
tf := cmdtesting.NewTestFactory()
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
NegotiatedSerializer: ns,
Client: nil,
}
cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr)
cmd := resource.NewCmdGet(tf, os.Stdout, os.Stderr)
svc := &api.ServiceList{
Items: []api.Service{
{
@ -657,15 +563,15 @@ func Example_printServiceWithNamespacesAndLabels() {
}
ld := strings.NewLineDelimiter(os.Stdout, "|")
defer ld.Flush()
mapper, _ := f.Object()
err := f.PrintObject(cmd, false, mapper, svc, ld)
cmd.Flags().Set("label-columns", "l1")
err := cmdutil.PrintObject(cmd, svc, ld)
if err != nil {
fmt.Printf("Unexpected error: %v", err)
}
// Output:
// |NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE L1|
// |ns1 svc1 ClusterIP 10.1.1.1 <none> 53/UDP,53/TCP 10y value|
// |ns2 svc2 ClusterIP 10.1.1.2 <none> 80/TCP,8080/TCP 10y dolla-bill-yall|
// |NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE L1|
// |svc1 ClusterIP 10.1.1.1 <none> 53/UDP,53/TCP 10y value|
// |svc2 ClusterIP 10.1.1.2 <none> 80/TCP,8080/TCP 10y dolla-bill-yall|
// ||
}

View File

@ -46,7 +46,7 @@ const defaultBoilerPlate = `
var (
completion_long = templates.LongDesc(i18n.T(`
Output shell completion code for the specified shell (bash or zsh).
The shell code must be evalutated to provide interactive
The shell code must be evaluated to provide interactive
completion of kubectl commands. This can be done by sourcing it from
the .bash_profile.
@ -97,7 +97,8 @@ func NewCmdCompletion(out io.Writer, boilerPlate string) *cobra.Command {
}
cmd := &cobra.Command{
Use: "completion SHELL",
Use: "completion SHELL",
DisableFlagsInUseLine: true,
Short: i18n.T("Output shell completion code for the specified shell (bash or zsh)"),
Long: completion_long,
Example: completion_example,

View File

@ -33,8 +33,6 @@ go_library(
"//pkg/kubectl/util/i18n:go_default_library",
"//pkg/printers:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/flag:go_default_library",
@ -63,8 +61,7 @@ go_test(
"use_context_test.go",
"view_test.go",
],
importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/config",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/kubectl/cmd/util:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",

View File

@ -31,13 +31,14 @@ import (
)
// NewCmdConfig creates a command object for the "config" action, and adds all child commands to it.
func NewCmdConfig(pathOptions *clientcmd.PathOptions, out, errOut io.Writer) *cobra.Command {
func NewCmdConfig(f cmdutil.Factory, pathOptions *clientcmd.PathOptions, out, errOut io.Writer) *cobra.Command {
if len(pathOptions.ExplicitFileFlag) == 0 {
pathOptions.ExplicitFileFlag = clientcmd.RecommendedConfigPathFlag
}
cmd := &cobra.Command{
Use: "config SUBCOMMAND",
Use: "config SUBCOMMAND",
DisableFlagsInUseLine: true,
Short: i18n.T("Modify kubeconfig files"),
Long: templates.LongDesc(`
Modify kubeconfig files using subcommands like "kubectl config set current-context my-context"
@ -53,7 +54,7 @@ func NewCmdConfig(pathOptions *clientcmd.PathOptions, out, errOut io.Writer) *co
// file paths are common to all sub commands
cmd.PersistentFlags().StringVar(&pathOptions.LoadingRules.ExplicitPath, pathOptions.ExplicitFileFlag, pathOptions.LoadingRules.ExplicitPath, "use a particular kubeconfig file")
cmd.AddCommand(NewCmdConfigView(out, errOut, pathOptions))
cmd.AddCommand(NewCmdConfigView(f, out, errOut, pathOptions))
cmd.AddCommand(NewCmdConfigSetCluster(out, pathOptions))
cmd.AddCommand(NewCmdConfigSetAuthInfo(out, pathOptions))
cmd.AddCommand(NewCmdConfigSetContext(out, pathOptions))

View File

@ -865,7 +865,7 @@ func testConfigCommand(args []string, startingConfig clientcmdapi.Config, t *tes
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdConfig(clientcmd.NewDefaultPathOptions(), buf, buf)
cmd := NewCmdConfig(cmdutil.NewFactory(nil), clientcmd.NewDefaultPathOptions(), buf, buf)
cmd.SetArgs(argsToUse)
cmd.Execute()

View File

@ -100,7 +100,8 @@ func NewCmdConfigSetAuthInfo(out io.Writer, configAccess clientcmd.ConfigAccess)
func newCmdConfigSetAuthInfo(out io.Writer, options *createAuthInfoOptions) *cobra.Command {
cmd := &cobra.Command{
Use: fmt.Sprintf("set-credentials NAME [--%v=path/to/certfile] [--%v=path/to/keyfile] [--%v=bearer_token] [--%v=basic_user] [--%v=basic_password] [--%v=provider_name] [--%v=key=value]", clientcmd.FlagCertFile, clientcmd.FlagKeyFile, clientcmd.FlagBearerToken, clientcmd.FlagUsername, clientcmd.FlagPassword, flagAuthProvider, flagAuthProviderArg),
Use: fmt.Sprintf("set-credentials NAME [--%v=path/to/certfile] [--%v=path/to/keyfile] [--%v=bearer_token] [--%v=basic_user] [--%v=basic_password] [--%v=provider_name] [--%v=key=value]", clientcmd.FlagCertFile, clientcmd.FlagKeyFile, clientcmd.FlagBearerToken, clientcmd.FlagUsername, clientcmd.FlagPassword, flagAuthProvider, flagAuthProviderArg),
DisableFlagsInUseLine: true,
Short: i18n.T("Sets a user entry in kubeconfig"),
Long: create_authinfo_long,
Example: create_authinfo_example,

View File

@ -63,7 +63,8 @@ func NewCmdConfigSetCluster(out io.Writer, configAccess clientcmd.ConfigAccess)
options := &createClusterOptions{configAccess: configAccess}
cmd := &cobra.Command{
Use: fmt.Sprintf("set-cluster NAME [--%v=server] [--%v=path/to/certificate/authority] [--%v=true]", clientcmd.FlagAPIServer, clientcmd.FlagCAFile, clientcmd.FlagInsecure),
Use: fmt.Sprintf("set-cluster NAME [--%v=server] [--%v=path/to/certificate/authority] [--%v=true]", clientcmd.FlagAPIServer, clientcmd.FlagCAFile, clientcmd.FlagInsecure),
DisableFlagsInUseLine: true,
Short: i18n.T("Sets a cluster entry in kubeconfig"),
Long: create_cluster_long,
Example: create_cluster_example,

View File

@ -54,7 +54,8 @@ func NewCmdConfigSetContext(out io.Writer, configAccess clientcmd.ConfigAccess)
options := &createContextOptions{configAccess: configAccess}
cmd := &cobra.Command{
Use: fmt.Sprintf("set-context NAME [--%v=cluster_nickname] [--%v=user_nickname] [--%v=namespace]", clientcmd.FlagClusterName, clientcmd.FlagAuthInfoName, clientcmd.FlagNamespace),
Use: fmt.Sprintf("set-context NAME [--%v=cluster_nickname] [--%v=user_nickname] [--%v=namespace]", clientcmd.FlagClusterName, clientcmd.FlagAuthInfoName, clientcmd.FlagNamespace),
DisableFlagsInUseLine: true,
Short: i18n.T("Sets a context entry in kubeconfig"),
Long: create_context_long,
Example: create_context_example,

View File

@ -35,7 +35,8 @@ var (
func NewCmdConfigDeleteCluster(out io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command {
cmd := &cobra.Command{
Use: "delete-cluster NAME",
Use: "delete-cluster NAME",
DisableFlagsInUseLine: true,
Short: i18n.T("Delete the specified cluster from the kubeconfig"),
Long: "Delete the specified cluster from the kubeconfig",
Example: delete_cluster_example,

View File

@ -35,7 +35,8 @@ var (
func NewCmdConfigDeleteContext(out, errOut io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command {
cmd := &cobra.Command{
Use: "delete-context NAME",
Use: "delete-context NAME",
DisableFlagsInUseLine: true,
Short: i18n.T("Delete the specified context from the kubeconfig"),
Long: "Delete the specified context from the kubeconfig",
Example: delete_context_example,

View File

@ -61,7 +61,8 @@ func NewCmdConfigGetContexts(out io.Writer, configAccess clientcmd.ConfigAccess)
options := &GetContextsOptions{configAccess: configAccess}
cmd := &cobra.Command{
Use: "get-contexts [(-o|--output=)name)]",
Use: "get-contexts [(-o|--output=)name)]",
DisableFlagsInUseLine: true,
Short: i18n.T("Describe one or many contexts"),
Long: getContextsLong,
Example: getContextsExample,

View File

@ -61,7 +61,8 @@ func NewCmdConfigRenameContext(out io.Writer, configAccess clientcmd.ConfigAcces
options := &RenameContextOptions{configAccess: configAccess}
cmd := &cobra.Command{
Use: renameContextUse,
Use: renameContextUse,
DisableFlagsInUseLine: true,
Short: renameContextShort,
Long: renameContextLong,
Example: renameContextExample,

View File

@ -56,7 +56,8 @@ func NewCmdConfigSet(out io.Writer, configAccess clientcmd.ConfigAccess) *cobra.
options := &setOptions{configAccess: configAccess}
cmd := &cobra.Command{
Use: "set PROPERTY_NAME PROPERTY_VALUE",
Use: "set PROPERTY_NAME PROPERTY_VALUE",
DisableFlagsInUseLine: true,
Short: i18n.T("Sets an individual value in a kubeconfig file"),
Long: set_long,
Run: func(cmd *cobra.Command, args []string) {

View File

@ -35,18 +35,29 @@ type unsetOptions struct {
propertyName string
}
var unset_long = templates.LongDesc(`
var (
unsetLong = templates.LongDesc(`
Unsets an individual value in a kubeconfig file
PROPERTY_NAME is a dot delimited name where each token represents either an attribute name or a map key. Map keys may not contain dots.`)
unsetExample = templates.Examples(`
# Unset the current-context.
kubectl config unset current-context
# Unset namespace in foo context.
kubectl config unset contexts.foo.namespace`)
)
func NewCmdConfigUnset(out io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command {
options := &unsetOptions{configAccess: configAccess}
cmd := &cobra.Command{
Use: "unset PROPERTY_NAME",
Short: i18n.T("Unsets an individual value in a kubeconfig file"),
Long: unset_long,
Use: "unset PROPERTY_NAME",
DisableFlagsInUseLine: true,
Short: i18n.T("Unsets an individual value in a kubeconfig file"),
Long: unsetLong,
Example: unsetExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(options.complete(cmd, args))
cmdutil.CheckErr(options.run(out))

View File

@ -45,7 +45,8 @@ func NewCmdConfigUseContext(out io.Writer, configAccess clientcmd.ConfigAccess)
options := &useContextOptions{configAccess: configAccess}
cmd := &cobra.Command{
Use: "use-context CONTEXT_NAME",
Use: "use-context CONTEXT_NAME",
DisableFlagsInUseLine: true,
Short: i18n.T("Sets the current-context in a kubeconfig file"),
Aliases: []string{"use"},
Long: `Sets the current-context in a kubeconfig file`,
@ -88,7 +89,7 @@ func (o *useContextOptions) complete(cmd *cobra.Command) error {
func (o useContextOptions) validate(config *clientcmdapi.Config) error {
if len(o.contextName) == 0 {
return errors.New("you must specify a current-context")
return errors.New("empty context names are not allowed")
}
for name := range config.Contexts {

View File

@ -23,8 +23,6 @@ import (
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/util/flag"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
@ -57,7 +55,7 @@ var (
kubectl config view -o jsonpath='{.users[?(@.name == "e2e")].user.password}'`)
)
func NewCmdConfigView(out, errOut io.Writer, ConfigAccess clientcmd.ConfigAccess) *cobra.Command {
func NewCmdConfigView(f cmdutil.Factory, out, errOut io.Writer, ConfigAccess clientcmd.ConfigAccess) *cobra.Command {
options := &ViewOptions{ConfigAccess: ConfigAccess}
// Default to yaml
defaultOutputFormat := "yaml"
@ -82,9 +80,8 @@ func NewCmdConfigView(out, errOut io.Writer, ConfigAccess clientcmd.ConfigAccess
}
printOpts := cmdutil.ExtractCmdPrintOptions(cmd, false)
printer, err := cmdutil.PrinterForOptions(meta.NewDefaultRESTMapper(nil, nil), latest.Scheme, nil, []runtime.Decoder{latest.Codec}, printOpts)
printer, err := cmdutil.PrinterForOptions(printOpts)
cmdutil.CheckErr(err)
printer = printers.NewVersionedPrinter(printer, latest.Scheme, latest.ExternalVersion)
cmdutil.CheckErr(options.Run(out, printer))
},
@ -94,8 +91,8 @@ func NewCmdConfigView(out, errOut io.Writer, ConfigAccess clientcmd.ConfigAccess
cmd.Flags().Set("output", defaultOutputFormat)
options.Merge.Default(true)
f := cmd.Flags().VarPF(&options.Merge, "merge", "", "Merge the full hierarchy of kubeconfig files")
f.NoOptDefVal = "true"
mergeFlag := cmd.Flags().VarPF(&options.Merge, "merge", "", "Merge the full hierarchy of kubeconfig files")
mergeFlag.NoOptDefVal = "true"
cmd.Flags().BoolVar(&options.RawByteData, "raw", false, "Display raw byte data")
cmd.Flags().BoolVar(&options.Flatten, "flatten", false, "Flatten the resulting kubeconfig file into self-contained output (useful for creating portable kubeconfig files)")
cmd.Flags().BoolVar(&options.Minify, "minify", false, "Remove all information not used by current-context from the output")
@ -122,7 +119,12 @@ func (o ViewOptions) Run(out io.Writer, printer printers.ResourcePrinter) error
clientcmdapi.ShortenConfig(config)
}
err = printer.PrintObj(config, out)
convertedObj, err := latest.Scheme.ConvertToVersion(config, latest.ExternalVersion)
if err != nil {
return err
}
err = printer.PrintObj(convertedObj, out)
if err != nil {
return err
}

View File

@ -24,6 +24,7 @@ import (
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)
type viewClusterTest struct {
@ -142,7 +143,7 @@ func (test viewClusterTest) run(t *testing.T) {
pathOptions.EnvVar = ""
buf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{})
cmd := NewCmdConfigView(buf, errBuf, pathOptions)
cmd := NewCmdConfigView(cmdutil.NewFactory(nil), buf, errBuf, pathOptions)
cmd.Flags().Parse(test.flags)
if err := cmd.Execute(); err != nil {
t.Fatalf("unexpected error executing command: %v,kubectl config view flags: %v", err, test.flags)

View File

@ -65,7 +65,8 @@ func NewCmdConvert(f cmdutil.Factory, out io.Writer) *cobra.Command {
options := &ConvertOptions{}
cmd := &cobra.Command{
Use: "convert -f FILENAME",
Use: "convert -f FILENAME",
DisableFlagsInUseLine: true,
Short: i18n.T("Convert config files between different API versions"),
Long: convert_long,
Example: convert_example,
@ -95,23 +96,18 @@ type ConvertOptions struct {
builder *resource.Builder
local bool
encoder runtime.Encoder
out io.Writer
printer printers.ResourcePrinter
outputVersion schema.GroupVersion
specifiedOutputVersion schema.GroupVersion
}
// outputVersion returns the preferred output version for generic content (JSON, YAML, or templates)
// defaultVersion is never mutated. Nil simply allows clean passing in common usage from client.Config
func outputVersion(cmd *cobra.Command, defaultVersion *schema.GroupVersion) (schema.GroupVersion, error) {
func outputVersion(cmd *cobra.Command) (schema.GroupVersion, error) {
outputVersionString := cmdutil.GetFlagString(cmd, "output-version")
if len(outputVersionString) == 0 {
if defaultVersion == nil {
return schema.GroupVersion{}, nil
}
return *defaultVersion, nil
return schema.GroupVersion{}, nil
}
return schema.ParseGroupVersion(outputVersionString)
@ -119,13 +115,10 @@ func outputVersion(cmd *cobra.Command, defaultVersion *schema.GroupVersion) (sch
// Complete collects information required to run Convert command from command line.
func (o *ConvertOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.Command) (err error) {
o.outputVersion, err = outputVersion(cmd, &scheme.Registry.EnabledVersionsForGroup(api.GroupName)[0])
o.specifiedOutputVersion, err = outputVersion(cmd)
if err != nil {
return err
}
if !scheme.Registry.IsEnabledVersion(o.outputVersion) {
return cmdutil.UsageErrorf(cmd, "'%s' is not a registered version.", o.outputVersion)
}
// build the builder
o.builder = f.NewBuilder().
@ -161,8 +154,7 @@ func (o *ConvertOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.C
// TODO: once printing is abstracted, this should be handled at flag declaration time
cmd.Flags().Set("output", outputFormat)
}
o.encoder = f.JSONEncoder()
o.printer, err = f.PrinterForOptions(cmdutil.ExtractCmdPrintOptions(cmd, false))
o.printer, err = cmdutil.PrinterForOptions(cmdutil.ExtractCmdPrintOptions(cmd, false))
return err
}
@ -184,7 +176,7 @@ func (o *ConvertOptions) RunConvert() error {
return fmt.Errorf("no objects passed to convert")
}
objects, err := asVersionedObject(infos, !singleItemImplied, o.outputVersion, o.encoder)
objects, err := asVersionedObject(infos, !singleItemImplied, o.specifiedOutputVersion, cmdutil.InternalVersionJSONEncoder())
if err != nil {
return err
}
@ -194,7 +186,7 @@ func (o *ConvertOptions) RunConvert() error {
if err != nil {
return err
}
filteredObj, err := objectListToVersionedObject(items, o.outputVersion)
filteredObj, err := objectListToVersionedObject(items, o.specifiedOutputVersion)
if err != nil {
return err
}
@ -206,9 +198,14 @@ func (o *ConvertOptions) RunConvert() error {
// objectListToVersionedObject receives a list of api objects and a group version
// and squashes the list's items into a single versioned runtime.Object.
func objectListToVersionedObject(objects []runtime.Object, version schema.GroupVersion) (runtime.Object, error) {
func objectListToVersionedObject(objects []runtime.Object, specifiedOutputVersion schema.GroupVersion) (runtime.Object, error) {
objectList := &api.List{Items: objects}
converted, err := tryConvert(scheme.Scheme, objectList, version, scheme.Registry.GroupOrDie(api.GroupName).GroupVersion)
targetVersions := []schema.GroupVersion{}
if !specifiedOutputVersion.Empty() {
targetVersions = append(targetVersions, specifiedOutputVersion)
}
targetVersions = append(targetVersions, scheme.Registry.GroupOrDie(api.GroupName).GroupVersion)
converted, err := tryConvert(scheme.Scheme, objectList, targetVersions...)
if err != nil {
return nil, err
}
@ -219,8 +216,8 @@ func objectListToVersionedObject(objects []runtime.Object, version schema.GroupV
// the objects as children, or if only a single Object is present, as that object. The provided
// version will be preferred as the conversion target, but the Object's mapping version will be
// used if that version is not present.
func asVersionedObject(infos []*resource.Info, forceList bool, version schema.GroupVersion, encoder runtime.Encoder) (runtime.Object, error) {
objects, err := asVersionedObjects(infos, version, encoder)
func asVersionedObject(infos []*resource.Info, forceList bool, specifiedOutputVersion schema.GroupVersion, encoder runtime.Encoder) (runtime.Object, error) {
objects, err := asVersionedObjects(infos, specifiedOutputVersion, encoder)
if err != nil {
return nil, err
}
@ -230,7 +227,13 @@ func asVersionedObject(infos []*resource.Info, forceList bool, version schema.Gr
object = objects[0]
} else {
object = &api.List{Items: objects}
converted, err := tryConvert(scheme.Scheme, object, version, scheme.Registry.GroupOrDie(api.GroupName).GroupVersion)
targetVersions := []schema.GroupVersion{}
if !specifiedOutputVersion.Empty() {
targetVersions = append(targetVersions, specifiedOutputVersion)
}
targetVersions = append(targetVersions, scheme.Registry.GroupOrDie(api.GroupName).GroupVersion)
converted, err := tryConvert(scheme.Scheme, object, targetVersions...)
if err != nil {
return nil, err
}
@ -238,7 +241,7 @@ func asVersionedObject(infos []*resource.Info, forceList bool, version schema.Gr
}
actualVersion := object.GetObjectKind().GroupVersionKind()
if actualVersion.Version != version.Version {
if actualVersion.Version != specifiedOutputVersion.Version {
defaultVersionInfo := ""
if len(actualVersion.Version) > 0 {
defaultVersionInfo = fmt.Sprintf("Defaulting to %q", actualVersion.Version)
@ -251,16 +254,17 @@ func asVersionedObject(infos []*resource.Info, forceList bool, version schema.Gr
// asVersionedObjects converts a list of infos into versioned objects. The provided
// version will be preferred as the conversion target, but the Object's mapping version will be
// used if that version is not present.
func asVersionedObjects(infos []*resource.Info, version schema.GroupVersion, encoder runtime.Encoder) ([]runtime.Object, error) {
func asVersionedObjects(infos []*resource.Info, specifiedOutputVersion schema.GroupVersion, encoder runtime.Encoder) ([]runtime.Object, error) {
objects := []runtime.Object{}
for _, info := range infos {
if info.Object == nil {
continue
}
targetVersions := []schema.GroupVersion{}
// objects that are not part of api.Scheme must be converted to JSON
// TODO: convert to map[string]interface{}, attach to runtime.Unknown?
if !version.Empty() {
if !specifiedOutputVersion.Empty() {
if _, _, err := scheme.Scheme.ObjectKinds(info.Object); runtime.IsNotRegisteredError(err) {
// TODO: ideally this would encode to version, but we don't expose multiple codecs here.
data, err := runtime.Encode(encoder, info.Object)
@ -271,9 +275,19 @@ func asVersionedObjects(infos []*resource.Info, version schema.GroupVersion, enc
objects = append(objects, &runtime.Unknown{Raw: data})
continue
}
targetVersions = append(targetVersions, specifiedOutputVersion)
} else {
gvks, _, err := scheme.Scheme.ObjectKinds(info.Object)
if err == nil {
for _, gvk := range gvks {
for _, version := range scheme.Registry.EnabledVersionsForGroup(gvk.Group) {
targetVersions = append(targetVersions, version)
}
}
}
}
converted, err := tryConvert(info.Mapping.ObjectConvertor, info.Object, version, info.Mapping.GroupVersionKind.GroupVersion())
converted, err := tryConvert(info.Mapping.ObjectConvertor, info.Object, targetVersions...)
if err != nil {
return nil, err
}

View File

@ -24,7 +24,6 @@ import (
"k8s.io/client-go/rest/fake"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/printers"
)
type testcase struct {
@ -102,7 +101,7 @@ func TestConvertObject(t *testing.T) {
for _, tc := range testcases {
for _, field := range tc.fields {
t.Run(fmt.Sprintf("%s %s", tc.name, field), func(t *testing.T) {
f, tf, _, _ := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
tf.UnstructuredClient = &fake.RESTClient{
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
@ -112,12 +111,11 @@ func TestConvertObject(t *testing.T) {
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdConvert(f, buf)
cmd := NewCmdConvert(tf, buf)
cmd.Flags().Set("filename", tc.file)
cmd.Flags().Set("output-version", tc.outputVersion)
cmd.Flags().Set("local", "true")
cmd.Flags().Set("output", "go-template")
tf.Printer, _ = printers.NewTemplatePrinter([]byte(field.template))
cmd.Flags().Set("output", "go-template="+field.template)
cmd.Run(cmd, []string{})
if buf.String() != field.expected {
t.Errorf("unexpected output when converting %s to %q, expected: %q, but got %q", tc.file, tc.outputVersion, field.expected, buf.String())

View File

@ -64,7 +64,8 @@ var (
// NewCmdCp creates a new Copy command.
func NewCmdCp(f cmdutil.Factory, cmdOut, cmdErr io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "cp <file-spec-src> <file-spec-dest>",
Use: "cp <file-spec-src> <file-spec-dest>",
DisableFlagsInUseLine: true,
Short: i18n.T("Copy files and directories to and from containers."),
Long: "Copy files and directories to and from containers.",
Example: cpExample,
@ -378,11 +379,8 @@ func untarAll(reader io.Reader, destFile, prefix string) error {
}
func getPrefix(file string) string {
if file[0] == '/' {
// tar strips the leading '/' if it's there, so we will too
return file[1:]
}
return file
// tar strips the leading '/' if it's there, so we will too
return strings.TrimLeft(file, "/")
}
func execute(f cmdutil.Factory, cmd *cobra.Command, options *ExecOptions) error {

View File

@ -42,6 +42,8 @@ type CreateOptions struct {
Selector string
EditBeforeCreate bool
Raw string
Out io.Writer
ErrOut io.Writer
}
var (
@ -62,10 +64,14 @@ var (
)
func NewCmdCreate(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
var options CreateOptions
options := &CreateOptions{
Out: out,
ErrOut: errOut,
}
cmd := &cobra.Command{
Use: "create -f FILENAME",
Use: "create -f FILENAME",
DisableFlagsInUseLine: true,
Short: i18n.T("Create a resource from a file or from stdin."),
Long: createLong,
Example: createExample,
@ -76,7 +82,7 @@ func NewCmdCreate(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
return
}
cmdutil.CheckErr(options.ValidateArgs(cmd, args))
cmdutil.CheckErr(RunCreate(f, cmd, out, errOut, &options))
cmdutil.CheckErr(options.RunCreate(f, cmd))
},
}
@ -109,6 +115,7 @@ func NewCmdCreate(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
cmd.AddCommand(NewCmdCreateRoleBinding(f, out))
cmd.AddCommand(NewCmdCreatePodDisruptionBudget(f, out))
cmd.AddCommand(NewCmdCreatePriorityClass(f, out))
cmd.AddCommand(NewCmdCreateJob(f, out))
return cmd
}
@ -123,7 +130,7 @@ func (o *CreateOptions) ValidateArgs(cmd *cobra.Command, args []string) error {
if len(o.FilenameOptions.Filenames) != 1 {
return cmdutil.UsageErrorf(cmd, "--raw can only use a single local file or stdin")
}
if strings.HasPrefix(o.FilenameOptions.Filenames[0], "http") {
if strings.Index(o.FilenameOptions.Filenames[0], "http://") == 0 || strings.Index(o.FilenameOptions.Filenames[0], "https://") == 0 {
return cmdutil.UsageErrorf(cmd, "--raw cannot read from a url")
}
if o.FilenameOptions.Recursive {
@ -143,36 +150,15 @@ func (o *CreateOptions) ValidateArgs(cmd *cobra.Command, args []string) error {
return nil
}
func RunCreate(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, options *CreateOptions) error {
func (o *CreateOptions) RunCreate(f cmdutil.Factory, cmd *cobra.Command) error {
// raw only makes sense for a single file resource multiple objects aren't likely to do what you want.
// the validator enforces this, so
if len(options.Raw) > 0 {
restClient, err := f.RESTClient()
if err != nil {
return err
}
var data io.ReadCloser
if options.FilenameOptions.Filenames[0] == "-" {
data = os.Stdin
} else {
data, err = os.Open(options.FilenameOptions.Filenames[0])
if err != nil {
return err
}
}
// TODO post content with stream. Right now it ignores body content
bytes, err := restClient.Post().RequestURI(options.Raw).Body(data).DoRaw()
if err != nil {
return err
}
fmt.Fprintf(out, "%v", string(bytes))
return nil
if len(o.Raw) > 0 {
return o.raw(f)
}
if options.EditBeforeCreate {
return RunEditOnCreate(f, out, errOut, cmd, &options.FilenameOptions)
if o.EditBeforeCreate {
return RunEditOnCreate(f, o.Out, o.ErrOut, cmd, &o.FilenameOptions)
}
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"))
if err != nil {
@ -189,8 +175,8 @@ func RunCreate(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, opt
Schema(schema).
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &options.FilenameOptions).
LabelSelectorParam(options.Selector).
FilenameParam(enforceNamespace, &o.FilenameOptions).
LabelSelectorParam(o.Selector).
Flatten().
Do()
err = r.Err()
@ -198,16 +184,15 @@ func RunCreate(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, opt
return err
}
dryRun := cmdutil.GetFlagBool(cmd, "dry-run")
dryRun := cmdutil.GetDryRunFlag(cmd)
output := cmdutil.GetFlagString(cmd, "output")
mapper := r.Mapper().RESTMapper
count := 0
err = r.Visit(func(info *resource.Info, err error) error {
if err != nil {
return err
}
if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil {
if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, cmdutil.InternalVersionJSONEncoder()); err != nil {
return cmdutil.AddSourceToErr("creating", info.Source, err)
}
@ -227,13 +212,10 @@ func RunCreate(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, opt
shortOutput := output == "name"
if len(output) > 0 && !shortOutput {
return f.PrintResourceInfoForCommand(cmd, info, out)
}
if !shortOutput {
f.PrintObjectSpecificMessage(info.Object, out)
return cmdutil.PrintObject(cmd, info.Object, o.Out)
}
f.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, dryRun, "created")
cmdutil.PrintSuccess(shortOutput, o.Out, info.Object, dryRun, "created")
return nil
})
if err != nil {
@ -245,6 +227,33 @@ func RunCreate(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, opt
return nil
}
// raw makes a simple HTTP request to the provided path on the server using the default
// credentials.
func (o *CreateOptions) raw(f cmdutil.Factory) error {
restClient, err := f.RESTClient()
if err != nil {
return err
}
var data io.ReadCloser
if o.FilenameOptions.Filenames[0] == "-" {
data = os.Stdin
} else {
data, err = os.Open(o.FilenameOptions.Filenames[0])
if err != nil {
return err
}
}
// TODO post content with stream. Right now it ignores body content
bytes, err := restClient.Post().RequestURI(o.Raw).Body(data).DoRaw()
if err != nil {
return err
}
fmt.Fprintf(o.Out, "%v", string(bytes))
return nil
}
func RunEditOnCreate(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, options *resource.FilenameOptions) error {
editOptions := &editor.EditOptions{
EditMode: editor.EditBeforeCreateMode,
@ -278,8 +287,8 @@ func createAndRefresh(info *resource.Info) error {
// NameFromCommandArgs is a utility function for commands that assume the first argument is a resource name
func NameFromCommandArgs(cmd *cobra.Command, args []string) (string, error) {
if len(args) == 0 {
return "", cmdutil.UsageErrorf(cmd, "NAME is required")
if len(args) != 1 {
return "", cmdutil.UsageErrorf(cmd, "exactly one NAME is required, got %d", len(args))
}
return args[0], nil
}
@ -328,7 +337,7 @@ func RunCreateSubcommand(f cmdutil.Factory, cmd *cobra.Command, out io.Writer, o
if err != nil {
return err
}
if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil {
if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, cmdutil.InternalVersionJSONEncoder()); err != nil {
return err
}
obj = info.Object
@ -345,9 +354,9 @@ func RunCreateSubcommand(f cmdutil.Factory, cmd *cobra.Command, out io.Writer, o
}
if useShortOutput := options.OutputFormat == "name"; useShortOutput || len(options.OutputFormat) == 0 {
f.PrintSuccess(mapper, useShortOutput, out, mapping.Resource, info.Name, options.DryRun, "created")
cmdutil.PrintSuccess(useShortOutput, out, info.Object, options.DryRun, "created")
return nil
}
return f.PrintObject(cmd, false, mapper, obj, out)
return cmdutil.PrintObject(cmd, obj, out)
}

View File

@ -66,7 +66,8 @@ func NewCmdCreateClusterRole(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command
},
}
cmd := &cobra.Command{
Use: "clusterrole NAME --verb=verb --resource=resource.group [--resource-name=resourcename] [--dry-run]",
Use: "clusterrole NAME --verb=verb --resource=resource.group [--resource-name=resourcename] [--dry-run]",
DisableFlagsInUseLine: true,
Short: clusterRoleLong,
Long: clusterRoleLong,
Example: clusterRoleExample,
@ -172,7 +173,7 @@ func (c *CreateClusterRoleOptions) RunCreateRole() error {
}
if useShortOutput := c.OutputFormat == "name"; useShortOutput || len(c.OutputFormat) == 0 {
c.PrintSuccess(c.Mapper, useShortOutput, c.Out, "clusterroles", c.Name, c.DryRun, "created")
cmdutil.PrintSuccess(useShortOutput, c.Out, clusterRole, c.DryRun, "created")
return nil
}

View File

@ -18,47 +18,24 @@ package cmd
import (
"bytes"
"io"
"reflect"
"testing"
rbac "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
)
type testClusterRolePrinter struct {
CachedClusterRole *rbac.ClusterRole
}
func (t *testClusterRolePrinter) PrintObj(obj runtime.Object, out io.Writer) error {
t.CachedClusterRole = obj.(*rbac.ClusterRole)
return nil
}
func (t *testClusterRolePrinter) AfterPrint(output io.Writer, res string) error {
return nil
}
func (t *testClusterRolePrinter) HandledResources() []string {
return []string{}
}
func (t *testClusterRolePrinter) IsGeneric() bool {
return true
}
func TestCreateClusterRole(t *testing.T) {
clusterRoleName := "my-cluster-role"
f, tf, _, _ := cmdtesting.NewAPIFactory()
printer := &testClusterRolePrinter{}
tf.Printer = printer
tf := cmdtesting.NewTestFactory()
tf.Namespace = "test"
tf.Client = &fake.RESTClient{}
tf.ClientConfig = defaultClientConfig()
tf.ClientConfigVal = defaultClientConfig()
tests := map[string]struct {
verbs string
@ -148,9 +125,9 @@ func TestCreateClusterRole(t *testing.T) {
for name, test := range tests {
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdCreateClusterRole(f, buf)
cmd := NewCmdCreateClusterRole(tf, buf)
cmd.Flags().Set("dry-run", "true")
cmd.Flags().Set("output", "object")
cmd.Flags().Set("output", "yaml")
cmd.Flags().Set("verb", test.verbs)
cmd.Flags().Set("resource", test.resources)
cmd.Flags().Set("non-resource-url", test.nonResourceURL)
@ -158,15 +135,19 @@ func TestCreateClusterRole(t *testing.T) {
cmd.Flags().Set("resource-name", test.resourceNames)
}
cmd.Run(cmd, []string{clusterRoleName})
if !reflect.DeepEqual(test.expectedClusterRole, printer.CachedClusterRole) {
t.Errorf("%s:\nexpected:\n%#v\nsaw:\n%#v", name, test.expectedClusterRole, printer.CachedClusterRole)
actual := &rbac.ClusterRole{}
if err := runtime.DecodeInto(legacyscheme.Codecs.UniversalDecoder(), buf.Bytes(), actual); err != nil {
t.Log(string(buf.Bytes()))
t.Fatal(err)
}
if !equality.Semantic.DeepEqual(test.expectedClusterRole, actual) {
t.Errorf("%s:\nexpected:\n%#v\nsaw:\n%#v", name, test.expectedClusterRole, actual)
}
}
}
func TestClusterRoleValidate(t *testing.T) {
f, tf, _, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
tf.Namespace = "test"
tests := map[string]struct {
@ -445,7 +426,7 @@ func TestClusterRoleValidate(t *testing.T) {
for name, test := range tests {
t.Run(name, func(t *testing.T) {
test.clusterRoleOptions.Mapper, _ = f.Object()
test.clusterRoleOptions.Mapper, _ = tf.Object()
err := test.clusterRoleOptions.Validate()
if test.expectErr && err == nil {
t.Errorf("%s: expect error happens, but validate passes.", name)

View File

@ -39,7 +39,8 @@ var (
// ClusterRoleBinding is a command to ease creating ClusterRoleBindings.
func NewCmdCreateClusterRoleBinding(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "clusterrolebinding NAME --clusterrole=NAME [--user=username] [--group=groupname] [--serviceaccount=namespace:serviceaccountname] [--dry-run]",
Use: "clusterrolebinding NAME --clusterrole=NAME [--user=username] [--group=groupname] [--serviceaccount=namespace:serviceaccountname] [--dry-run]",
DisableFlagsInUseLine: true,
Short: i18n.T("Create a ClusterRoleBinding for a particular ClusterRole"),
Long: clusterRoleBindingLong,
Example: clusterRoleBindingExample,

View File

@ -30,6 +30,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
)
@ -66,14 +67,14 @@ func TestCreateClusterRoleBinding(t *testing.T) {
},
}
f, tf, _, ns := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
ns := legacyscheme.Codecs
info, _ := runtime.SerializerInfoForMediaType(ns.SupportedMediaTypes(), runtime.ContentTypeJSON)
encoder := ns.EncoderForVersion(info.Serializer, groupVersion)
decoder := ns.DecoderToVersion(info.Serializer, groupVersion)
tf.Namespace = "test"
tf.Printer = &testPrinter{}
tf.Client = &ClusterRoleBindingRESTClient{
RESTClient: &fake.RESTClient{
NegotiatedSerializer: ns,
@ -107,9 +108,9 @@ func TestCreateClusterRoleBinding(t *testing.T) {
},
}
expectedOutput := "clusterrolebinding/" + expectBinding.Name + "\n"
expectedOutput := "clusterrolebinding.rbac.authorization.k8s.io/" + expectBinding.Name + "\n"
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdCreateClusterRoleBinding(f, buf)
cmd := NewCmdCreateClusterRoleBinding(tf, buf)
cmd.Flags().Set("clusterrole", "fake-clusterrole")
cmd.Flags().Set("user", "fake-user")
cmd.Flags().Set("group", "fake-group")
@ -140,5 +141,5 @@ func (c *ClusterRoleBindingRESTClient) Post() *restclient.Request {
serializers.StreamingSerializer = info.StreamSerializer.Serializer
serializers.Framer = info.StreamSerializer.Framer
}
return restclient.NewRequest(c, "POST", &url.URL{Host: "localhost"}, c.VersionedAPIPath, config, serializers, nil, nil)
return restclient.NewRequest(c, "POST", &url.URL{Host: "localhost"}, c.VersionedAPIPath, config, serializers, nil, nil, 0)
}

View File

@ -60,11 +60,12 @@ var (
// ConfigMap is a command to ease creating ConfigMaps.
func NewCmdCreateConfigMap(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "configmap NAME [--from-file=[key=]source] [--from-literal=key1=value1] [--dry-run]",
Aliases: []string{"cm"},
Short: i18n.T("Create a configmap from a local file, directory or literal value"),
Long: configMapLong,
Example: configMapExample,
Use: "configmap NAME [--from-file=[key=]source] [--from-literal=key1=value1] [--dry-run]",
DisableFlagsInUseLine: true,
Aliases: []string{"cm"},
Short: i18n.T("Create a configmap from a local file, directory or literal value"),
Long: configMapLong,
Example: configMapExample,
Run: func(cmd *cobra.Command, args []string) {
err := CreateConfigMap(f, cmdOut, cmd, args)
cmdutil.CheckErr(err)

View File

@ -24,14 +24,18 @@ import (
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
func TestCreateConfigMap(t *testing.T) {
configMap := &v1.ConfigMap{}
configMap.Name = "my-configmap"
f, tf, codec, ns := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Group: "", Version: "v1"},
NegotiatedSerializer: ns,
@ -47,7 +51,7 @@ func TestCreateConfigMap(t *testing.T) {
}
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdCreateConfigMap(f, buf)
cmd := NewCmdCreateConfigMap(tf, buf)
cmd.Flags().Set("output", "name")
cmd.Run(cmd, []string{configMap.Name})
expectedOutput := "configmap/" + configMap.Name + "\n"

View File

@ -41,11 +41,12 @@ var (
// Note that this command overlaps significantly with the `kubectl run` command.
func NewCmdCreateDeployment(f cmdutil.Factory, cmdOut, cmdErr io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "deployment NAME --image=image [--dry-run]",
Aliases: []string{"deploy"},
Short: i18n.T("Create a deployment with the specified name."),
Long: deploymentLong,
Example: deploymentExample,
Use: "deployment NAME --image=image [--dry-run]",
DisableFlagsInUseLine: true,
Aliases: []string{"deploy"},
Short: i18n.T("Create a deployment with the specified name."),
Long: deploymentLong,
Example: deploymentExample,
Run: func(cmd *cobra.Command, args []string) {
err := createDeployment(f, cmdOut, cmdErr, cmd, args)
cmdutil.CheckErr(err)

View File

@ -26,6 +26,7 @@ import (
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/kubectl"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
@ -73,7 +74,9 @@ func Test_generatorFromName(t *testing.T) {
func TestCreateDeployment(t *testing.T) {
depName := "jonny-dep"
f, tf, _, ns := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
NegotiatedSerializer: ns,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -83,17 +86,16 @@ func TestCreateDeployment(t *testing.T) {
}, nil
}),
}
tf.ClientConfig = &restclient.Config{}
tf.Printer = &testPrinter{}
tf.ClientConfigVal = &restclient.Config{}
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdCreateDeployment(f, buf, buf)
cmd := NewCmdCreateDeployment(tf, buf, buf)
cmd.Flags().Set("dry-run", "true")
cmd.Flags().Set("output", "name")
cmd.Flags().Set("image", "hollywood/jonny.depp:v2")
cmd.Run(cmd, []string{depName})
expectedOutput := "deployment/" + depName + "\n"
expectedOutput := "deployment.extensions/" + depName + "\n"
if buf.String() != expectedOutput {
t.Errorf("expected output: %s, but got: %s", expectedOutput, buf.String())
}
@ -101,7 +103,9 @@ func TestCreateDeployment(t *testing.T) {
func TestCreateDeploymentNoImage(t *testing.T) {
depName := "jonny-dep"
f, tf, _, ns := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
NegotiatedSerializer: ns,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -111,14 +115,13 @@ func TestCreateDeploymentNoImage(t *testing.T) {
}, nil
}),
}
tf.ClientConfig = &restclient.Config{}
tf.Printer = &testPrinter{}
tf.ClientConfigVal = &restclient.Config{}
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdCreateDeployment(f, buf, buf)
cmd := NewCmdCreateDeployment(tf, buf, buf)
cmd.Flags().Set("dry-run", "true")
cmd.Flags().Set("output", "name")
err := createDeployment(f, buf, buf, cmd, []string{depName})
err := createDeployment(tf, buf, buf, cmd, []string{depName})
assert.Error(t, err, "at least one image must be specified")
}

146
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_job.go generated vendored Normal file
View File

@ -0,0 +1,146 @@
/*
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 cmd
import (
"fmt"
"io"
"github.com/spf13/cobra"
batchv1 "k8s.io/api/batch/v1"
batchv1beta1 "k8s.io/api/batch/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clientbatchv1 "k8s.io/client-go/kubernetes/typed/batch/v1"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
)
var (
jobLong = templates.LongDesc(i18n.T(`
Create a job with the specified name.`))
jobExample = templates.Examples(i18n.T(`
# Create a job from a CronJob named "a-cronjob"
kubectl create job --from=cronjob/a-cronjob`))
)
type CreateJobOptions struct {
Name string
From string
Namespace string
Client clientbatchv1.BatchV1Interface
Out io.Writer
DryRun bool
Builder *resource.Builder
Cmd *cobra.Command
}
// NewCmdCreateJob is a command to ease creating Jobs from CronJobs.
func NewCmdCreateJob(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
c := &CreateJobOptions{
Out: cmdOut,
}
cmd := &cobra.Command{
Use: "job NAME [--from-cronjob=CRONJOB]",
Short: jobLong,
Long: jobLong,
Example: jobExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(c.Complete(f, cmd, args))
cmdutil.CheckErr(c.RunCreateJob())
},
}
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddValidateFlags(cmd)
cmdutil.AddPrinterFlags(cmd)
cmd.Flags().String("from", "", "The name of the resource to create a Job from (only cronjob is supported).")
return cmd
}
func (c *CreateJobOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) (err error) {
if len(args) == 0 {
return cmdutil.UsageErrorf(cmd, "NAME is required")
}
c.Name = args[0]
c.From = cmdutil.GetFlagString(cmd, "from")
c.Namespace, _, err = f.DefaultNamespace()
if err != nil {
return err
}
clientset, err := f.KubernetesClientSet()
if err != nil {
return err
}
c.Client = clientset.BatchV1()
c.Builder = f.NewBuilder()
c.Cmd = cmd
return nil
}
func (c *CreateJobOptions) RunCreateJob() error {
infos, err := c.Builder.
Unstructured().
NamespaceParam(c.Namespace).DefaultNamespace().
ResourceTypeOrNameArgs(false, c.From).
Flatten().
Latest().
Do().
Infos()
if err != nil {
return err
}
if len(infos) != 1 {
return fmt.Errorf("from must be an existing cronjob")
}
cronJob, ok := infos[0].AsVersioned().(*batchv1beta1.CronJob)
if !ok {
return fmt.Errorf("from must be an existing cronjob")
}
return c.createJob(cronJob)
}
func (c *CreateJobOptions) createJob(cronJob *batchv1beta1.CronJob) error {
annotations := make(map[string]string)
annotations["cronjob.kubernetes.io/instantiate"] = "manual"
for k, v := range cronJob.Spec.JobTemplate.Annotations {
annotations[k] = v
}
jobToCreate := &batchv1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: c.Name,
Namespace: c.Namespace,
Annotations: annotations,
Labels: cronJob.Spec.JobTemplate.Labels,
},
Spec: cronJob.Spec.JobTemplate.Spec,
}
job, err := c.Client.Jobs(c.Namespace).Create(jobToCreate)
if err != nil {
return fmt.Errorf("failed to create job: %v", err)
}
return cmdutil.PrintObject(c.Cmd, job, c.Out)
}

View File

@ -0,0 +1,123 @@
/*
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 cmd
import (
"bytes"
"testing"
batchv1 "k8s.io/api/batch/v1"
batchv1beta1 "k8s.io/api/batch/v1beta1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
fake "k8s.io/client-go/kubernetes/fake"
clienttesting "k8s.io/client-go/testing"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
)
func TestCreateJobFromCronJob(t *testing.T) {
var submittedJob *batchv1.Job
testNamespaceName := "test"
testCronJobName := "test-cronjob"
testJobName := "test-job"
testImageName := "fake"
expectedLabels := make(map[string]string)
expectedAnnotations := make(map[string]string)
expectedLabels["test-label"] = "test-value"
expectJob := &batchv1.Job{
ObjectMeta: metav1.ObjectMeta{
Namespace: testNamespaceName,
Labels: expectedLabels,
Annotations: expectedAnnotations,
},
Spec: batchv1.JobSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{Image: testImageName},
},
},
},
},
}
cronJob := &batchv1beta1.CronJob{
ObjectMeta: metav1.ObjectMeta{
Name: testCronJobName,
Namespace: testNamespaceName,
},
Spec: batchv1beta1.CronJobSpec{
Schedule: "* * * * *",
JobTemplate: batchv1beta1.JobTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Namespace: testNamespaceName,
Labels: expectedLabels,
},
Spec: expectJob.Spec,
},
},
}
clientset := fake.Clientset{}
clientset.PrependReactor("create", "jobs", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
ca := action.(clienttesting.CreateAction)
submittedJob = ca.GetObject().(*batchv1.Job)
return true, expectJob, nil
})
f := cmdtesting.NewTestFactory()
buf := bytes.NewBuffer([]byte{})
cmdOptions := &CreateJobOptions{
Name: testJobName,
Namespace: testNamespaceName,
Client: clientset.BatchV1(),
Out: buf,
Cmd: NewCmdCreateJob(f, buf),
}
err := cmdOptions.createJob(cronJob)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if submittedJob.ObjectMeta.Name != testJobName {
t.Errorf("expected '%s', got '%s'", testJobName, submittedJob.ObjectMeta.Name)
}
if l := len(submittedJob.Annotations); l != 1 {
t.Errorf("expected length of annotations array to be 1, got %d", l)
}
if v, ok := submittedJob.Annotations["cronjob.kubernetes.io/instantiate"]; !ok || v != "manual" {
t.Errorf("expected annotation cronjob.kubernetes.io/instantiate=manual to exist, got '%s'", v)
}
if l := len(submittedJob.Labels); l != 1 {
t.Errorf("expected length of labels array to be 1, got %d", l)
}
if v, ok := submittedJob.Labels["test-label"]; !ok || v != "test-value" {
t.Errorf("expected label test-label=test-value to to exist, got '%s'", v)
}
if l := len(submittedJob.Spec.Template.Spec.Containers); l != 1 {
t.Errorf("expected length of container array to be 1, got %d", l)
}
if submittedJob.Spec.Template.Spec.Containers[0].Image != testImageName {
t.Errorf("expected '%s', got '%s'", testImageName, submittedJob.Spec.Template.Spec.Containers[0].Image)
}
}

View File

@ -39,11 +39,12 @@ var (
// NewCmdCreateNamespace is a macro command to create a new namespace
func NewCmdCreateNamespace(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "namespace NAME [--dry-run]",
Aliases: []string{"ns"},
Short: i18n.T("Create a namespace with the specified name"),
Long: namespaceLong,
Example: namespaceExample,
Use: "namespace NAME [--dry-run]",
DisableFlagsInUseLine: true,
Aliases: []string{"ns"},
Short: i18n.T("Create a namespace with the specified name"),
Long: namespaceLong,
Example: namespaceExample,
Run: func(cmd *cobra.Command, args []string) {
err := CreateNamespace(f, cmdOut, cmd, args)
cmdutil.CheckErr(err)

View File

@ -24,14 +24,18 @@ import (
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
func TestCreateNamespace(t *testing.T) {
namespaceObject := &v1.Namespace{}
namespaceObject.Name = "my-namespace"
f, tf, codec, ns := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: "v1"},
NegotiatedSerializer: ns,
@ -46,7 +50,7 @@ func TestCreateNamespace(t *testing.T) {
}),
}
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdCreateNamespace(f, buf)
cmd := NewCmdCreateNamespace(tf, buf)
cmd.Flags().Set("output", "name")
cmd.Run(cmd, []string{namespaceObject.Name})
expectedOutput := "namespace/" + namespaceObject.Name + "\n"

View File

@ -44,11 +44,12 @@ var (
// NewCmdCreatePodDisruptionBudget is a macro command to create a new pod disruption budget.
func NewCmdCreatePodDisruptionBudget(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "poddisruptionbudget NAME --selector=SELECTOR --min-available=N [--dry-run]",
Aliases: []string{"pdb"},
Short: i18n.T("Create a pod disruption budget with the specified name."),
Long: pdbLong,
Example: pdbExample,
Use: "poddisruptionbudget NAME --selector=SELECTOR --min-available=N [--dry-run]",
DisableFlagsInUseLine: true,
Aliases: []string{"pdb"},
Short: i18n.T("Create a pod disruption budget with the specified name."),
Long: pdbLong,
Example: pdbExample,
Run: func(cmd *cobra.Command, args []string) {
err := CreatePodDisruptionBudget(f, cmdOut, cmd, args)
cmdutil.CheckErr(err)
@ -93,7 +94,7 @@ func CreatePodDisruptionBudget(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.C
return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
Name: name,
StructuredGenerator: generator,
DryRun: cmdutil.GetFlagBool(cmd, "dry-run"),
DryRun: cmdutil.GetDryRunFlag(cmd),
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
})
}

View File

@ -25,12 +25,15 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
)
func TestCreatePdb(t *testing.T) {
pdbName := "my-pdb"
f, tf, _, ns := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Group: "policy", Version: "v1beta1"},
NegotiatedSerializer: ns,
@ -41,18 +44,17 @@ func TestCreatePdb(t *testing.T) {
}, nil
}),
}
tf.ClientConfig = &restclient.Config{}
tf.Printer = &testPrinter{}
tf.ClientConfigVal = &restclient.Config{}
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdCreatePodDisruptionBudget(f, buf)
cmd := NewCmdCreatePodDisruptionBudget(tf, buf)
cmd.Flags().Set("min-available", "1")
cmd.Flags().Set("selector", "app=rails")
cmd.Flags().Set("dry-run", "true")
cmd.Flags().Set("output", "name")
CreatePodDisruptionBudget(f, buf, cmd, []string{pdbName})
expectedOutput := "poddisruptionbudget/" + pdbName + "\n"
CreatePodDisruptionBudget(tf, buf, cmd, []string{pdbName})
expectedOutput := "poddisruptionbudget.policy/" + pdbName + "\n"
if buf.String() != expectedOutput {
t.Errorf("expected output: %s, but got: %s", expectedOutput, buf.String())
}

View File

@ -42,11 +42,12 @@ var (
// NewCmdCreatePriorityClass is a macro command to create a new priorityClass.
func NewCmdCreatePriorityClass(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "priorityclass NAME --value=VALUE --global-default=BOOL [--dry-run]",
Aliases: []string{"pc"},
Short: i18n.T("Create a priorityclass with the specified name."),
Long: pcLong,
Example: pcExample,
Use: "priorityclass NAME --value=VALUE --global-default=BOOL [--dry-run]",
DisableFlagsInUseLine: true,
Aliases: []string{"pc"},
Short: i18n.T("Create a priorityclass with the specified name."),
Long: pcLong,
Example: pcExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(CreatePriorityClass(f, cmdOut, cmd, args))
},
@ -84,7 +85,7 @@ func CreatePriorityClass(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command
return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
Name: name,
StructuredGenerator: generator,
DryRun: cmdutil.GetFlagBool(cmd, "dry-run"),
DryRun: cmdutil.GetDryRunFlag(cmd),
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
})
}

View File

@ -25,12 +25,15 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
)
func TestCreatePriorityClass(t *testing.T) {
pcName := "my-pc"
f, tf, _, ns := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Group: "scheduling.k8s.io", Version: "v1alpha1"},
NegotiatedSerializer: ns,
@ -41,18 +44,17 @@ func TestCreatePriorityClass(t *testing.T) {
}, nil
}),
}
tf.ClientConfig = &restclient.Config{}
tf.Printer = &testPrinter{}
tf.ClientConfigVal = &restclient.Config{}
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdCreatePriorityClass(f, buf)
cmd := NewCmdCreatePriorityClass(tf, buf)
cmd.Flags().Set("value", "1000")
cmd.Flags().Set("global-default", "true")
cmd.Flags().Set("description", "my priority")
cmd.Flags().Set("dry-run", "true")
cmd.Flags().Set("output", "name")
CreatePriorityClass(f, buf, cmd, []string{pcName})
expectedOutput := "priorityclass/" + pcName + "\n"
CreatePriorityClass(tf, buf, cmd, []string{pcName})
expectedOutput := "priorityclass.scheduling.k8s.io/" + pcName + "\n"
if buf.String() != expectedOutput {
t.Errorf("expected output: %s, but got: %s", expectedOutput, buf.String())
}

View File

@ -42,11 +42,12 @@ var (
// NewCmdCreateQuota is a macro command to create a new quota
func NewCmdCreateQuota(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "quota NAME [--hard=key1=value1,key2=value2] [--scopes=Scope1,Scope2] [--dry-run=bool]",
Aliases: []string{"resourcequota"},
Short: i18n.T("Create a quota with the specified name."),
Long: quotaLong,
Example: quotaExample,
Use: "quota NAME [--hard=key1=value1,key2=value2] [--scopes=Scope1,Scope2] [--dry-run=bool]",
DisableFlagsInUseLine: true,
Aliases: []string{"resourcequota"},
Short: i18n.T("Create a quota with the specified name."),
Long: quotaLong,
Example: quotaExample,
Run: func(cmd *cobra.Command, args []string) {
err := CreateQuota(f, cmdOut, cmd, args)
cmdutil.CheckErr(err)
@ -82,7 +83,7 @@ func CreateQuota(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args [
return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
Name: name,
StructuredGenerator: generator,
DryRun: cmdutil.GetFlagBool(cmd, "dry-run"),
DryRun: cmdutil.GetDryRunFlag(cmd),
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
})
}

View File

@ -24,14 +24,18 @@ import (
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
func TestCreateQuota(t *testing.T) {
resourceQuotaObject := &v1.ResourceQuota{}
resourceQuotaObject.Name = "my-quota"
f, tf, codec, ns := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: "v1"},
NegotiatedSerializer: ns,
@ -70,7 +74,7 @@ func TestCreateQuota(t *testing.T) {
}
for name, test := range tests {
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdCreateQuota(f, buf)
cmd := NewCmdCreateQuota(tf, buf)
cmd.Flags().Parse(test.flags)
cmd.Flags().Set("output", "name")
cmd.Run(cmd, []string{resourceQuotaObject.Name})

View File

@ -112,7 +112,6 @@ type CreateRoleOptions struct {
Mapper meta.RESTMapper
Out io.Writer
PrintObject func(obj runtime.Object) error
PrintSuccess func(mapper meta.RESTMapper, shortOutput bool, out io.Writer, resource, name string, dryRun bool, operation string)
}
// Role is a command to ease creating Roles.
@ -121,7 +120,8 @@ func NewCmdCreateRole(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
Out: cmdOut,
}
cmd := &cobra.Command{
Use: "role NAME --verb=verb --resource=resource.group/subresource [--resource-name=resourcename] [--dry-run]",
Use: "role NAME --verb=verb --resource=resource.group/subresource [--resource-name=resourcename] [--dry-run]",
DisableFlagsInUseLine: true,
Short: roleLong,
Long: roleLong,
Example: roleExample,
@ -162,7 +162,6 @@ func (c *CreateRoleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args
}
}
c.Verbs = verbs
c.PrintSuccess = f.PrintSuccess
// Support resource.group pattern. If no API Group specified, use "" as core API Group.
// e.g. --resource=pods,deployments.extensions
@ -205,7 +204,7 @@ func (c *CreateRoleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args
}
c.PrintObject = func(obj runtime.Object) error {
return f.PrintObject(cmd, false, c.Mapper, obj, c.Out)
return cmdutil.PrintObject(cmd, obj, c.Out)
}
clientset, err := f.KubernetesClientSet()
@ -294,7 +293,7 @@ func (c *CreateRoleOptions) RunCreateRole() error {
}
if useShortOutput := c.OutputFormat == "name"; useShortOutput || len(c.OutputFormat) == 0 {
c.PrintSuccess(c.Mapper, useShortOutput, c.Out, "roles", c.Name, c.DryRun, "created")
cmdutil.PrintSuccess(useShortOutput, c.Out, role, c.DryRun, "created")
return nil
}

View File

@ -18,48 +18,26 @@ package cmd
import (
"bytes"
"io"
"reflect"
"testing"
rbac "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
)
type testRolePrinter struct {
CachedRole *rbac.Role
}
func (t *testRolePrinter) PrintObj(obj runtime.Object, out io.Writer) error {
t.CachedRole = obj.(*rbac.Role)
return nil
}
func (t *testRolePrinter) AfterPrint(output io.Writer, res string) error {
return nil
}
func (t *testRolePrinter) HandledResources() []string {
return []string{}
}
func (t *testRolePrinter) IsGeneric() bool {
return true
}
func TestCreateRole(t *testing.T) {
roleName := "my-role"
f, tf, _, _ := cmdtesting.NewAPIFactory()
printer := &testRolePrinter{}
tf.Printer = printer
tf := cmdtesting.NewTestFactory()
tf.Namespace = "test"
tf.Client = &fake.RESTClient{}
tf.ClientConfig = defaultClientConfig()
tf.ClientConfigVal = defaultClientConfig()
tests := map[string]struct {
verbs string
@ -146,25 +124,29 @@ func TestCreateRole(t *testing.T) {
for name, test := range tests {
t.Run(name, func(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdCreateRole(f, buf)
cmd := NewCmdCreateRole(tf, buf)
cmd.Flags().Set("dry-run", "true")
cmd.Flags().Set("output", "object")
cmd.Flags().Set("output", "yaml")
cmd.Flags().Set("verb", test.verbs)
cmd.Flags().Set("resource", test.resources)
if test.resourceNames != "" {
cmd.Flags().Set("resource-name", test.resourceNames)
}
cmd.Run(cmd, []string{roleName})
if !reflect.DeepEqual(test.expectedRole, printer.CachedRole) {
t.Errorf("%s", diff.ObjectReflectDiff(test.expectedRole, printer.CachedRole))
actual := &rbac.Role{}
if err := runtime.DecodeInto(legacyscheme.Codecs.UniversalDecoder(), buf.Bytes(), actual); err != nil {
t.Log(string(buf.Bytes()))
t.Fatal(err)
}
if !equality.Semantic.DeepEqual(test.expectedRole, actual) {
t.Errorf("%s", diff.ObjectReflectDiff(test.expectedRole, actual))
}
})
}
}
func TestValidate(t *testing.T) {
f, tf, _, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
tf.Namespace = "test"
tests := map[string]struct {
@ -349,7 +331,7 @@ func TestValidate(t *testing.T) {
}
for name, test := range tests {
test.roleOptions.Mapper, _ = f.Object()
test.roleOptions.Mapper, _ = tf.Object()
err := test.roleOptions.Validate()
if test.expectErr && err == nil {
t.Errorf("%s: expect error happens but validate passes.", name)
@ -363,14 +345,13 @@ func TestValidate(t *testing.T) {
func TestComplete(t *testing.T) {
roleName := "my-role"
f, tf, _, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
tf.Namespace = "test"
tf.Client = &fake.RESTClient{}
tf.ClientConfig = defaultClientConfig()
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdCreateRole(f, buf)
cmd := NewCmdCreateRole(tf, buf)
cmd.Flags().Set("resource", "pods,deployments.extensions")
tests := map[string]struct {
@ -495,7 +476,7 @@ func TestComplete(t *testing.T) {
}
for name, test := range tests {
err := test.roleOptions.Complete(f, cmd, test.params)
err := test.roleOptions.Complete(tf, cmd, test.params)
if !test.expectErr && err != nil {
t.Errorf("%s: unexpected error: %v", name, err)
}

View File

@ -39,7 +39,8 @@ var (
// RoleBinding is a command to ease creating RoleBindings.
func NewCmdCreateRoleBinding(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "rolebinding NAME --clusterrole=NAME|--role=NAME [--user=username] [--group=groupname] [--serviceaccount=namespace:serviceaccountname] [--dry-run]",
Use: "rolebinding NAME --clusterrole=NAME|--role=NAME [--user=username] [--group=groupname] [--serviceaccount=namespace:serviceaccountname] [--dry-run]",
DisableFlagsInUseLine: true,
Short: i18n.T("Create a RoleBinding for a particular Role or ClusterRole"),
Long: roleBindingLong,
Example: roleBindingExample,

View File

@ -30,6 +30,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
)
@ -68,14 +69,14 @@ func TestCreateRoleBinding(t *testing.T) {
},
}
f, tf, _, ns := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
ns := legacyscheme.Codecs
info, _ := runtime.SerializerInfoForMediaType(ns.SupportedMediaTypes(), runtime.ContentTypeJSON)
encoder := ns.EncoderForVersion(info.Serializer, groupVersion)
decoder := ns.DecoderToVersion(info.Serializer, groupVersion)
tf.Namespace = "test"
tf.Printer = &testPrinter{}
tf.Client = &RoleBindingRESTClient{
RESTClient: &fake.RESTClient{
NegotiatedSerializer: ns,
@ -110,7 +111,7 @@ func TestCreateRoleBinding(t *testing.T) {
}
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdCreateRoleBinding(f, buf)
cmd := NewCmdCreateRoleBinding(tf, buf)
cmd.Flags().Set("role", "fake-role")
cmd.Flags().Set("user", "fake-user")
cmd.Flags().Set("group", "fake-group")
@ -138,5 +139,5 @@ func (c *RoleBindingRESTClient) Post() *restclient.Request {
serializers.StreamingSerializer = info.StreamSerializer.Serializer
serializers.Framer = info.StreamSerializer.Framer
}
return restclient.NewRequest(c, "POST", &url.URL{Host: "localhost"}, c.VersionedAPIPath, config, serializers, nil, nil)
return restclient.NewRequest(c, "POST", &url.URL{Host: "localhost"}, c.VersionedAPIPath, config, serializers, nil, nil, 0)
}

View File

@ -76,7 +76,8 @@ var (
// NewCmdCreateSecretGeneric is a command to create generic secrets from files, directories, or literal values
func NewCmdCreateSecretGeneric(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "generic NAME [--type=string] [--from-file=[key=]source] [--from-literal=key1=value1] [--dry-run]",
Use: "generic NAME [--type=string] [--from-file=[key=]source] [--from-literal=key1=value1] [--dry-run]",
DisableFlagsInUseLine: true,
Short: i18n.T("Create a secret from a local file, directory or literal value"),
Long: secretLong,
Example: secretExample,
@ -149,7 +150,8 @@ var (
// NewCmdCreateSecretDockerRegistry is a macro command for creating secrets to work with Docker registries
func NewCmdCreateSecretDockerRegistry(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "docker-registry NAME --docker-username=user --docker-password=password --docker-email=email [--docker-server=string] [--from-literal=key1=value1] [--dry-run]",
Use: "docker-registry NAME --docker-username=user --docker-password=password --docker-email=email [--docker-server=string] [--from-literal=key1=value1] [--dry-run]",
DisableFlagsInUseLine: true,
Short: i18n.T("Create a secret for use with a Docker registry"),
Long: secretForDockerRegistryLong,
Example: secretForDockerRegistryExample,
@ -223,7 +225,8 @@ var (
// NewCmdCreateSecretTLS is a macro command for creating secrets to work with Docker registries
func NewCmdCreateSecretTLS(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "tls NAME --cert=path/to/cert/file --key=path/to/key/file [--dry-run]",
Use: "tls NAME --cert=path/to/cert/file --key=path/to/key/file [--dry-run]",
DisableFlagsInUseLine: true,
Short: i18n.T("Create a TLS secret"),
Long: secretForTLSLong,
Example: secretForTLSExample,
@ -269,7 +272,7 @@ func CreateSecretTLS(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, ar
return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
Name: name,
StructuredGenerator: generator,
DryRun: cmdutil.GetFlagBool(cmd, "dry-run"),
DryRun: cmdutil.GetDryRunFlag(cmd),
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
})
}

View File

@ -24,7 +24,9 @@ import (
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
func TestCreateSecretGeneric(t *testing.T) {
@ -35,8 +37,10 @@ func TestCreateSecretGeneric(t *testing.T) {
},
}
secretObject.Name = "my-secret"
f, tf, codec, ns := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: "v1"},
NegotiatedSerializer: ns,
@ -52,7 +56,7 @@ func TestCreateSecretGeneric(t *testing.T) {
}
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdCreateSecretGeneric(f, buf)
cmd := NewCmdCreateSecretGeneric(tf, buf)
cmd.Flags().Set("output", "name")
cmd.Flags().Set("from-literal", "password=includes,comma")
cmd.Flags().Set("from-literal", "username=test_user")
@ -66,8 +70,10 @@ func TestCreateSecretGeneric(t *testing.T) {
func TestCreateSecretDockerRegistry(t *testing.T) {
secretObject := &v1.Secret{}
secretObject.Name = "my-secret"
f, tf, codec, ns := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: "v1"},
NegotiatedSerializer: ns,
@ -83,7 +89,7 @@ func TestCreateSecretDockerRegistry(t *testing.T) {
}
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdCreateSecretDockerRegistry(f, buf)
cmd := NewCmdCreateSecretDockerRegistry(tf, buf)
cmd.Flags().Set("docker-username", "test-user")
cmd.Flags().Set("docker-password", "test-pass")
cmd.Flags().Set("docker-email", "test-email")

View File

@ -64,7 +64,8 @@ func addPortFlags(cmd *cobra.Command) {
// NewCmdCreateServiceClusterIP is a command to create a ClusterIP service
func NewCmdCreateServiceClusterIP(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "clusterip NAME [--tcp=<port>:<targetPort>] [--dry-run]",
Use: "clusterip NAME [--tcp=<port>:<targetPort>] [--dry-run]",
DisableFlagsInUseLine: true,
Short: i18n.T("Create a ClusterIP service."),
Long: serviceClusterIPLong,
Example: serviceClusterIPExample,
@ -124,7 +125,8 @@ var (
// NewCmdCreateServiceNodePort is a macro command for creating a NodePort service
func NewCmdCreateServiceNodePort(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "nodeport NAME [--tcp=port:targetPort] [--dry-run]",
Use: "nodeport NAME [--tcp=port:targetPort] [--dry-run]",
DisableFlagsInUseLine: true,
Short: i18n.T("Create a NodePort service."),
Long: serviceNodePortLong,
Example: serviceNodePortExample,
@ -181,7 +183,8 @@ var (
// NewCmdCreateServiceLoadBalancer is a macro command for creating a LoadBalancer service
func NewCmdCreateServiceLoadBalancer(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "loadbalancer NAME [--tcp=port:targetPort] [--dry-run]",
Use: "loadbalancer NAME [--tcp=port:targetPort] [--dry-run]",
DisableFlagsInUseLine: true,
Short: i18n.T("Create a LoadBalancer service."),
Long: serviceLoadBalancerLong,
Example: serviceLoadBalancerExample,
@ -219,7 +222,7 @@ func CreateServiceLoadBalancer(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.C
return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
Name: name,
StructuredGenerator: generator,
DryRun: cmdutil.GetFlagBool(cmd, "dry-run"),
DryRun: cmdutil.GetDryRunFlag(cmd),
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
})
}
@ -240,7 +243,8 @@ var (
// NewCmdCreateServiceExternalName is a macro command for creating an ExternalName service
func NewCmdCreateServiceExternalName(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "externalname NAME --external-name external.name [--dry-run]",
Use: "externalname NAME --external-name external.name [--dry-run]",
DisableFlagsInUseLine: true,
Short: i18n.T("Create an ExternalName service."),
Long: serviceExternalNameLong,
Example: serviceExternalNameExample,

View File

@ -24,14 +24,18 @@ import (
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
func TestCreateService(t *testing.T) {
service := &v1.Service{}
service.Name = "my-service"
f, tf, codec, negSer := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
negSer := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: "v1"},
NegotiatedSerializer: negSer,
@ -47,7 +51,7 @@ func TestCreateService(t *testing.T) {
}
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdCreateServiceClusterIP(f, buf)
cmd := NewCmdCreateServiceClusterIP(tf, buf)
cmd.Flags().Set("output", "name")
cmd.Flags().Set("tcp", "8080:8000")
cmd.Run(cmd, []string{service.Name})
@ -60,8 +64,10 @@ func TestCreateService(t *testing.T) {
func TestCreateServiceNodePort(t *testing.T) {
service := &v1.Service{}
service.Name = "my-node-port-service"
f, tf, codec, negSer := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
negSer := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: "v1"},
NegotiatedSerializer: negSer,
@ -77,7 +83,7 @@ func TestCreateServiceNodePort(t *testing.T) {
}
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdCreateServiceNodePort(f, buf)
cmd := NewCmdCreateServiceNodePort(tf, buf)
cmd.Flags().Set("output", "name")
cmd.Flags().Set("tcp", "30000:8000")
cmd.Run(cmd, []string{service.Name})
@ -90,8 +96,10 @@ func TestCreateServiceNodePort(t *testing.T) {
func TestCreateServiceExternalName(t *testing.T) {
service := &v1.Service{}
service.Name = "my-external-name-service"
f, tf, codec, negSer := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
negSer := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: "v1"},
NegotiatedSerializer: negSer,
@ -107,7 +115,7 @@ func TestCreateServiceExternalName(t *testing.T) {
}
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdCreateServiceExternalName(f, buf)
cmd := NewCmdCreateServiceExternalName(tf, buf)
cmd.Flags().Set("output", "name")
cmd.Flags().Set("external-name", "name")
cmd.Run(cmd, []string{service.Name})

View File

@ -39,11 +39,12 @@ var (
// NewCmdCreateServiceAccount is a macro command to create a new service account
func NewCmdCreateServiceAccount(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "serviceaccount NAME [--dry-run]",
Aliases: []string{"sa"},
Short: i18n.T("Create a service account with the specified name"),
Long: serviceAccountLong,
Example: serviceAccountExample,
Use: "serviceaccount NAME [--dry-run]",
DisableFlagsInUseLine: true,
Aliases: []string{"sa"},
Short: i18n.T("Create a service account with the specified name"),
Long: serviceAccountLong,
Example: serviceAccountExample,
Run: func(cmd *cobra.Command, args []string) {
err := CreateServiceAccount(f, cmdOut, cmd, args)
cmdutil.CheckErr(err)

View File

@ -24,14 +24,18 @@ import (
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
func TestCreateServiceAccount(t *testing.T) {
serviceAccountObject := &v1.ServiceAccount{}
serviceAccountObject.Name = "my-service-account"
f, tf, codec, ns := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: "v1"},
NegotiatedSerializer: ns,
@ -47,7 +51,7 @@ func TestCreateServiceAccount(t *testing.T) {
}
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdCreateServiceAccount(f, buf)
cmd := NewCmdCreateServiceAccount(tf, buf)
cmd.Flags().Set("output", "name")
cmd.Run(cmd, []string{serviceAccountObject.Name})
expectedOutput := "serviceaccount/" + serviceAccountObject.Name + "\n"

View File

@ -23,7 +23,9 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
func TestExtraArgsFail(t *testing.T) {
@ -31,7 +33,7 @@ func TestExtraArgsFail(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{})
f, _, _, _ := cmdtesting.NewAPIFactory()
f := cmdtesting.NewTestFactory()
c := NewCmdCreate(f, buf, errBuf)
options := CreateOptions{}
if options.ValidateArgs(c, []string{"rc"}) == nil {
@ -44,8 +46,9 @@ func TestCreateObject(t *testing.T) {
_, _, rc := testData()
rc.Items[0].Name = "redis-master-controller"
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
tf.UnstructuredClient = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: "v1"},
NegotiatedSerializer: unstructuredSerializer,
@ -63,7 +66,7 @@ func TestCreateObject(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{})
cmd := NewCmdCreate(f, buf, errBuf)
cmd := NewCmdCreate(tf, buf, errBuf)
cmd.Flags().Set("filename", "../../../examples/guestbook/legacy/redis-master-controller.yaml")
cmd.Flags().Set("output", "name")
cmd.Run(cmd, []string{})
@ -78,8 +81,9 @@ func TestCreateMultipleObject(t *testing.T) {
initTestErrorHandler(t)
_, svc, rc := testData()
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
tf.UnstructuredClient = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: "v1"},
NegotiatedSerializer: unstructuredSerializer,
@ -99,7 +103,7 @@ func TestCreateMultipleObject(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{})
cmd := NewCmdCreate(f, buf, errBuf)
cmd := NewCmdCreate(tf, buf, errBuf)
cmd.Flags().Set("filename", "../../../examples/guestbook/legacy/redis-master-controller.yaml")
cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.yaml")
cmd.Flags().Set("output", "name")
@ -116,8 +120,9 @@ func TestCreateDirectory(t *testing.T) {
_, _, rc := testData()
rc.Items[0].Name = "name"
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
tf.UnstructuredClient = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: "v1"},
NegotiatedSerializer: unstructuredSerializer,
@ -135,7 +140,7 @@ func TestCreateDirectory(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{})
cmd := NewCmdCreate(f, buf, errBuf)
cmd := NewCmdCreate(tf, buf, errBuf)
cmd.Flags().Set("filename", "../../../examples/guestbook/legacy")
cmd.Flags().Set("output", "name")
cmd.Run(cmd, []string{})

View File

@ -19,6 +19,7 @@ package cmd
import (
"fmt"
"io"
"strings"
"time"
"github.com/spf13/cobra"
@ -32,7 +33,6 @@ import (
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/printers"
)
var (
@ -113,21 +113,12 @@ type DeleteOptions struct {
}
func NewCmdDelete(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
options := &DeleteOptions{}
// retrieve a list of handled resources from printer as valid args
validArgs, argAliases := []string{}, []string{}
p, err := f.Printer(nil, printers.PrintOptions{
ColumnLabels: []string{},
})
cmdutil.CheckErr(err)
if p != nil {
validArgs = p.HandledResources()
argAliases = kubectl.ResourceAliases(validArgs)
}
options := &DeleteOptions{Include3rdParty: true}
validArgs := cmdutil.ValidArgList(f)
cmd := &cobra.Command{
Use: "delete ([-f FILENAME] | TYPE [(NAME | -l label | --all)])",
Use: "delete ([-f FILENAME] | TYPE [(NAME | -l label | --all)])",
DisableFlagsInUseLine: true,
Short: i18n.T("Delete resources by filenames, stdin, resources and names, or by resources and label selector"),
Long: delete_long,
Example: delete_example,
@ -145,12 +136,12 @@ func NewCmdDelete(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
},
SuggestFor: []string{"rm"},
ValidArgs: validArgs,
ArgAliases: argAliases,
ArgAliases: kubectl.ResourceAliases(validArgs),
}
usage := "containing the resource to delete."
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
cmd.Flags().StringVarP(&options.Selector, "selector", "l", "", "Selector (label query) to filter on, not including uninitialized ones.")
cmd.Flags().BoolVar(&options.DeleteAll, "all", false, "Delete all resources, including uninitialized ones, in the namespace of the specified resource types.")
cmd.Flags().BoolVar(&options.DeleteAll, "all", options.DeleteAll, "Delete all resources, including uninitialized ones, in the namespace of the specified resource types.")
cmd.Flags().BoolVar(&options.IgnoreNotFound, "ignore-not-found", false, "Treat \"resource not found\" as a successful delete. Defaults to \"true\" when --all is specified.")
cmd.Flags().BoolVar(&options.Cascade, "cascade", true, "If true, cascade the deletion of the resources managed by this resource (e.g. Pods created by a ReplicationController). Default true.")
cmd.Flags().IntVar(&options.GracePeriod, "grace-period", -1, "Period of time in seconds given to the resource to terminate gracefully. Ignored if negative.")
@ -197,6 +188,9 @@ func (o *DeleteOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args
}
func (o *DeleteOptions) Validate(cmd *cobra.Command) error {
if o.DeleteAll && len(o.Selector) > 0 {
return fmt.Errorf("cannot set --all and --selector at the same time")
}
if o.DeleteAll {
f := cmd.Flags().Lookup("ignore-not-found")
// The flag should never be missing
@ -232,12 +226,12 @@ func (o *DeleteOptions) RunDelete() error {
shortOutput := o.Output == "name"
// By default use a reaper to delete all related resources.
if o.Cascade {
return ReapResult(o.Result, o.f, o.Out, true, o.IgnoreNotFound, o.Timeout, o.GracePeriod, o.WaitForDeletion, shortOutput, o.Mapper, false)
return ReapResult(o.Result, o.f, o.Out, true, o.IgnoreNotFound, o.Timeout, o.GracePeriod, o.WaitForDeletion, shortOutput, false)
}
return DeleteResult(o.Result, o.f, o.Out, o.IgnoreNotFound, o.GracePeriod, shortOutput, o.Mapper)
return DeleteResult(o.Result, o.Out, o.IgnoreNotFound, o.GracePeriod, shortOutput)
}
func ReapResult(r *resource.Result, f cmdutil.Factory, out io.Writer, isDefaultDelete, ignoreNotFound bool, timeout time.Duration, gracePeriod int, waitForDeletion, shortOutput bool, mapper meta.RESTMapper, quiet bool) error {
func ReapResult(r *resource.Result, f cmdutil.Factory, out io.Writer, isDefaultDelete, ignoreNotFound bool, timeout time.Duration, gracePeriod int, waitForDeletion, shortOutput bool, quiet bool) error {
found := 0
if ignoreNotFound {
r = r.IgnoreErrors(errors.IsNotFound)
@ -252,7 +246,7 @@ func ReapResult(r *resource.Result, f cmdutil.Factory, out io.Writer, isDefaultD
// If there is no reaper for this resources and the user didn't explicitly ask for stop.
if kubectl.IsNoSuchReaperError(err) && isDefaultDelete {
// No client side reaper found. Let the server do cascading deletion.
return cascadingDeleteResource(info, f, out, shortOutput, mapper)
return cascadingDeleteResource(info, out, shortOutput)
}
return cmdutil.AddSourceToErr("reaping", info.Source, err)
}
@ -269,7 +263,7 @@ func ReapResult(r *resource.Result, f cmdutil.Factory, out io.Writer, isDefaultD
}
}
if !quiet {
f.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, false, "deleted")
printDeletion(info, out, shortOutput)
}
return nil
})
@ -282,7 +276,7 @@ func ReapResult(r *resource.Result, f cmdutil.Factory, out io.Writer, isDefaultD
return nil
}
func DeleteResult(r *resource.Result, f cmdutil.Factory, out io.Writer, ignoreNotFound bool, gracePeriod int, shortOutput bool, mapper meta.RESTMapper) error {
func DeleteResult(r *resource.Result, out io.Writer, ignoreNotFound bool, gracePeriod int, shortOutput bool) error {
found := 0
if ignoreNotFound {
r = r.IgnoreErrors(errors.IsNotFound)
@ -300,7 +294,7 @@ func DeleteResult(r *resource.Result, f cmdutil.Factory, out io.Writer, ignoreNo
options = metav1.NewDeleteOptions(int64(gracePeriod))
}
options.OrphanDependents = &orphan
return deleteResource(info, f, out, shortOutput, mapper, options)
return deleteResource(info, out, shortOutput, options)
})
if err != nil {
return err
@ -311,20 +305,40 @@ func DeleteResult(r *resource.Result, f cmdutil.Factory, out io.Writer, ignoreNo
return nil
}
func cascadingDeleteResource(info *resource.Info, f cmdutil.Factory, out io.Writer, shortOutput bool, mapper meta.RESTMapper) error {
func cascadingDeleteResource(info *resource.Info, out io.Writer, shortOutput bool) error {
falseVar := false
deleteOptions := &metav1.DeleteOptions{OrphanDependents: &falseVar}
return deleteResource(info, f, out, shortOutput, mapper, deleteOptions)
return deleteResource(info, out, shortOutput, deleteOptions)
}
func deleteResource(info *resource.Info, f cmdutil.Factory, out io.Writer, shortOutput bool, mapper meta.RESTMapper, deleteOptions *metav1.DeleteOptions) error {
func deleteResource(info *resource.Info, out io.Writer, shortOutput bool, deleteOptions *metav1.DeleteOptions) error {
if err := resource.NewHelper(info.Client, info.Mapping).DeleteWithOptions(info.Namespace, info.Name, deleteOptions); err != nil {
return cmdutil.AddSourceToErr("deleting", info.Source, err)
}
f.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, false, "deleted")
printDeletion(info, out, shortOutput)
return nil
}
// deletion printing is special because they don't have an object to print. This logic mirrors PrintSuccess
func printDeletion(info *resource.Info, out io.Writer, shortOutput bool) {
operation := "deleted"
groupKind := info.Mapping.GroupVersionKind
kindString := fmt.Sprintf("%s.%s", strings.ToLower(groupKind.Kind), groupKind.Group)
if len(groupKind.Group) == 0 {
kindString = strings.ToLower(groupKind.Kind)
}
if shortOutput {
// -o name: prints resource/name
fmt.Fprintf(out, "%s/%s\n", kindString, info.Name)
return
}
// understandable output by default
fmt.Fprintf(out, "%s \"%s\" %s\n", kindString, info.Name, operation)
}
// objectDeletionWaitInterval is the interval to wait between checks for deletion.
var objectDeletionWaitInterval = time.Second

View File

@ -33,17 +33,20 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/kubectl"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
var unstructuredSerializer = dynamic.ContentConfig().NegotiatedSerializer
var fakecmd = &cobra.Command{
Use: "delete ([-f FILENAME] | TYPE [(NAME | -l label | --all)])",
DisableFlagsInUseLine: true,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd))
},
@ -53,8 +56,9 @@ func TestDeleteObjectByTuple(t *testing.T) {
initTestErrorHandler(t)
_, _, rc := testData()
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -78,7 +82,7 @@ func TestDeleteObjectByTuple(t *testing.T) {
tf.Namespace = "test"
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
cmd := NewCmdDelete(f, buf, errBuf)
cmd := NewCmdDelete(tf, buf, errBuf)
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("cascade", "false")
cmd.Flags().Set("output", "name")
@ -89,7 +93,7 @@ func TestDeleteObjectByTuple(t *testing.T) {
// Test cascading delete of object without client-side reaper doesn't make GET requests
buf, errBuf = bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
cmd = NewCmdDelete(f, buf, errBuf)
cmd = NewCmdDelete(tf, buf, errBuf)
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("output", "name")
cmd.Run(cmd, []string{"secrets/mysecret"})
@ -116,8 +120,9 @@ func TestOrphanDependentsInDeleteObject(t *testing.T) {
initTestErrorHandler(t)
_, _, rc := testData()
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
var expectedOrphanDependents *bool
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
@ -138,7 +143,7 @@ func TestOrphanDependentsInDeleteObject(t *testing.T) {
falseVar := false
expectedOrphanDependents = &falseVar
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
cmd := NewCmdDelete(f, buf, errBuf)
cmd := NewCmdDelete(tf, buf, errBuf)
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("output", "name")
cmd.Run(cmd, []string{"secrets/mysecret"})
@ -150,7 +155,7 @@ func TestOrphanDependentsInDeleteObject(t *testing.T) {
trueVar := true
expectedOrphanDependents = &trueVar
buf, errBuf = bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
cmd = NewCmdDelete(f, buf, errBuf)
cmd = NewCmdDelete(tf, buf, errBuf)
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("cascade", "false")
cmd.Flags().Set("output", "name")
@ -165,8 +170,9 @@ func TestDeleteNamedObject(t *testing.T) {
initTestErrorHandler(t)
_, _, rc := testData()
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -190,7 +196,7 @@ func TestDeleteNamedObject(t *testing.T) {
tf.Namespace = "test"
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
cmd := NewCmdDelete(f, buf, errBuf)
cmd := NewCmdDelete(tf, buf, errBuf)
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("cascade", "false")
cmd.Flags().Set("output", "name")
@ -201,7 +207,7 @@ func TestDeleteNamedObject(t *testing.T) {
// Test cascading delete of object without client-side reaper doesn't make GET requests
buf, errBuf = bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
cmd = NewCmdDelete(f, buf, errBuf)
cmd = NewCmdDelete(tf, buf, errBuf)
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("cascade", "false")
cmd.Flags().Set("output", "name")
@ -215,8 +221,9 @@ func TestDeleteObject(t *testing.T) {
initTestErrorHandler(t)
_, _, rc := testData()
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -232,7 +239,7 @@ func TestDeleteObject(t *testing.T) {
tf.Namespace = "test"
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
cmd := NewCmdDelete(f, buf, errBuf)
cmd := NewCmdDelete(tf, buf, errBuf)
cmd.Flags().Set("filename", "../../../examples/guestbook/legacy/redis-master-controller.yaml")
cmd.Flags().Set("cascade", "false")
cmd.Flags().Set("output", "name")
@ -273,8 +280,9 @@ func TestDeleteObjectGraceZero(t *testing.T) {
objectDeletionWaitInterval = time.Millisecond
count := 0
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -302,11 +310,11 @@ func TestDeleteObjectGraceZero(t *testing.T) {
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
reaper := &fakeReaper{}
fake := &fakeReaperFactory{Factory: f, reaper: reaper}
fake := &fakeReaperFactory{Factory: tf, reaper: reaper}
cmd := NewCmdDelete(fake, buf, errBuf)
cmd.Flags().Set("output", "name")
cmd.Flags().Set("grace-period", "0")
cmd.Run(cmd, []string{"pod/nginx"})
cmd.Run(cmd, []string{"pods/nginx"})
// uses the name from the file, not the response
if buf.String() != "pod/nginx\n" {
@ -322,8 +330,7 @@ func TestDeleteObjectGraceZero(t *testing.T) {
func TestDeleteObjectNotFound(t *testing.T) {
initTestErrorHandler(t)
f, tf, _, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -347,7 +354,7 @@ func TestDeleteObjectNotFound(t *testing.T) {
Cascade: false,
Output: "name",
}
err := options.Complete(f, buf, errBuf, []string{}, fakecmd)
err := options.Complete(tf, buf, errBuf, []string{}, fakecmd)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@ -359,8 +366,7 @@ func TestDeleteObjectNotFound(t *testing.T) {
func TestDeleteObjectIgnoreNotFound(t *testing.T) {
initTestErrorHandler(t)
f, tf, _, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -376,7 +382,7 @@ func TestDeleteObjectIgnoreNotFound(t *testing.T) {
tf.Namespace = "test"
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
cmd := NewCmdDelete(f, buf, errBuf)
cmd := NewCmdDelete(tf, buf, errBuf)
cmd.Flags().Set("filename", "../../../examples/guestbook/legacy/redis-master-controller.yaml")
cmd.Flags().Set("cascade", "false")
cmd.Flags().Set("ignore-not-found", "true")
@ -395,9 +401,9 @@ func TestDeleteAllNotFound(t *testing.T) {
svc.Items = append(svc.Items, api.Service{ObjectMeta: metav1.ObjectMeta{Name: "foo"}})
notFoundError := &errors.NewNotFound(api.Resource("services"), "foo").ErrStatus
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
tf.Printer = &testPrinter{}
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -426,7 +432,7 @@ func TestDeleteAllNotFound(t *testing.T) {
IgnoreNotFound: false,
Output: "name",
}
err := options.Complete(f, buf, errBuf, []string{"services"}, fakecmd)
err := options.Complete(tf, buf, errBuf, []string{"services"}, fakecmd)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@ -440,13 +446,13 @@ func TestDeleteAllIgnoreNotFound(t *testing.T) {
initTestErrorHandler(t)
_, svc, _ := testData()
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
// Add an item to the list which will result in a 404 on delete
svc.Items = append(svc.Items, api.Service{ObjectMeta: metav1.ObjectMeta{Name: "foo"}})
notFoundError := &errors.NewNotFound(api.Resource("services"), "foo").ErrStatus
tf.Printer = &testPrinter{}
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -466,7 +472,7 @@ func TestDeleteAllIgnoreNotFound(t *testing.T) {
tf.Namespace = "test"
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
cmd := NewCmdDelete(f, buf, errBuf)
cmd := NewCmdDelete(tf, buf, errBuf)
cmd.Flags().Set("all", "true")
cmd.Flags().Set("cascade", "false")
cmd.Flags().Set("output", "name")
@ -481,8 +487,9 @@ func TestDeleteMultipleObject(t *testing.T) {
initTestErrorHandler(t)
_, svc, rc := testData()
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -500,7 +507,7 @@ func TestDeleteMultipleObject(t *testing.T) {
tf.Namespace = "test"
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
cmd := NewCmdDelete(f, buf, errBuf)
cmd := NewCmdDelete(tf, buf, errBuf)
cmd.Flags().Set("filename", "../../../examples/guestbook/legacy/redis-master-controller.yaml")
cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.yaml")
cmd.Flags().Set("cascade", "false")
@ -516,8 +523,9 @@ func TestDeleteMultipleObjectContinueOnMissing(t *testing.T) {
initTestErrorHandler(t)
_, svc, _ := testData()
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -543,7 +551,7 @@ func TestDeleteMultipleObjectContinueOnMissing(t *testing.T) {
Cascade: false,
Output: "name",
}
err := options.Complete(f, buf, errBuf, []string{}, fakecmd)
err := options.Complete(tf, buf, errBuf, []string{}, fakecmd)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@ -560,8 +568,9 @@ func TestDeleteMultipleObjectContinueOnMissing(t *testing.T) {
func TestDeleteMultipleResourcesWithTheSameName(t *testing.T) {
initTestErrorHandler(t)
_, svc, rc := testData()
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -584,7 +593,7 @@ func TestDeleteMultipleResourcesWithTheSameName(t *testing.T) {
tf.Namespace = "test"
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
cmd := NewCmdDelete(f, buf, errBuf)
cmd := NewCmdDelete(tf, buf, errBuf)
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("cascade", "false")
cmd.Flags().Set("output", "name")
@ -598,8 +607,9 @@ func TestDeleteDirectory(t *testing.T) {
initTestErrorHandler(t)
_, _, rc := testData()
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -615,7 +625,7 @@ func TestDeleteDirectory(t *testing.T) {
tf.Namespace = "test"
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
cmd := NewCmdDelete(f, buf, errBuf)
cmd := NewCmdDelete(tf, buf, errBuf)
cmd.Flags().Set("filename", "../../../examples/guestbook/legacy")
cmd.Flags().Set("cascade", "false")
cmd.Flags().Set("output", "name")
@ -630,8 +640,9 @@ func TestDeleteMultipleSelector(t *testing.T) {
initTestErrorHandler(t)
pods, svc, _ := testData()
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -659,7 +670,7 @@ func TestDeleteMultipleSelector(t *testing.T) {
tf.Namespace = "test"
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
cmd := NewCmdDelete(f, buf, errBuf)
cmd := NewCmdDelete(tf, buf, errBuf)
cmd.Flags().Set("selector", "a=b")
cmd.Flags().Set("cascade", "false")
cmd.Flags().Set("output", "name")
@ -695,10 +706,9 @@ func TestResourceErrors(t *testing.T) {
}
for k, testCase := range testCases {
f, tf, _, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
tf.Namespace = "test"
tf.ClientConfig = defaultClientConfig()
tf.ClientConfigVal = defaultClientConfig()
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
@ -708,15 +718,12 @@ func TestResourceErrors(t *testing.T) {
Cascade: false,
Output: "name",
}
err := options.Complete(f, buf, errBuf, testCase.args, fakecmd)
err := options.Complete(tf, buf, errBuf, testCase.args, fakecmd)
if !testCase.errFn(err) {
t.Errorf("%s: unexpected error: %v", k, err)
continue
}
if tf.Printer.(*testPrinter).Objects != nil {
t.Errorf("unexpected print to default printer")
}
if buf.Len() > 0 {
t.Errorf("buffer should be empty: %s", string(buf.Bytes()))
}

View File

@ -79,7 +79,8 @@ func NewCmdDescribe(f cmdutil.Factory, out, cmdErr io.Writer) *cobra.Command {
argAliases := kubectl.ResourceAliases(validArgs)
cmd := &cobra.Command{
Use: "describe (-f FILENAME | TYPE [NAME_PREFIX | -l label] | TYPE/NAME)",
Use: "describe (-f FILENAME | TYPE [NAME_PREFIX | -l label] | TYPE/NAME)",
DisableFlagsInUseLine: true,
Short: i18n.T("Show details of a specific resource or group of resources"),
Long: describeLong + "\n\n" + cmdutil.ValidResourceTypeList(f),
Example: describeExample,

View File

@ -20,17 +20,21 @@ import (
"bytes"
"fmt"
"net/http"
"strings"
"testing"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
// Verifies that schemas that are not in the master tree of Kubernetes can be retrieved via Get.
func TestDescribeUnknownSchemaObject(t *testing.T) {
d := &testDescriber{Output: "test output"}
f, tf, codec, _ := cmdtesting.NewTestFactory()
tf.Describer = d
tf := cmdtesting.NewTestFactory()
_, _, codec := cmdtesting.NewExternalScheme()
tf.DescriberVal = d
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, cmdtesting.NewInternalType("", "", "foo"))},
@ -38,7 +42,7 @@ func TestDescribeUnknownSchemaObject(t *testing.T) {
tf.Namespace = "non-default"
buf := bytes.NewBuffer([]byte{})
buferr := bytes.NewBuffer([]byte{})
cmd := NewCmdDescribe(f, buf, buferr)
cmd := NewCmdDescribe(tf, buf, buferr)
cmd.Run(cmd, []string{"type", "foo"})
if d.Name != "foo" || d.Namespace != "" {
@ -53,8 +57,10 @@ func TestDescribeUnknownSchemaObject(t *testing.T) {
// Verifies that schemas that are not in the master tree of Kubernetes can be retrieved via Get.
func TestDescribeUnknownNamespacedSchemaObject(t *testing.T) {
d := &testDescriber{Output: "test output"}
f, tf, codec, _ := cmdtesting.NewTestFactory()
tf.Describer = d
tf := cmdtesting.NewTestFactory()
_, _, codec := cmdtesting.NewExternalScheme()
tf.DescriberVal = d
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, cmdtesting.NewInternalNamespacedType("", "", "foo", "non-default"))},
@ -62,7 +68,7 @@ func TestDescribeUnknownNamespacedSchemaObject(t *testing.T) {
tf.Namespace = "non-default"
buf := bytes.NewBuffer([]byte{})
buferr := bytes.NewBuffer([]byte{})
cmd := NewCmdDescribe(f, buf, buferr)
cmd := NewCmdDescribe(tf, buf, buferr)
cmd.Run(cmd, []string{"namespacedtype", "foo"})
if d.Name != "foo" || d.Namespace != "non-default" {
@ -76,9 +82,11 @@ func TestDescribeUnknownNamespacedSchemaObject(t *testing.T) {
func TestDescribeObject(t *testing.T) {
_, _, rc := testData()
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
d := &testDescriber{Output: "test output"}
tf.Describer = d
tf.DescriberVal = d
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -94,7 +102,7 @@ func TestDescribeObject(t *testing.T) {
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
buferr := bytes.NewBuffer([]byte{})
cmd := NewCmdDescribe(f, buf, buferr)
cmd := NewCmdDescribe(tf, buf, buferr)
cmd.Flags().Set("filename", "../../../examples/guestbook/legacy/redis-master-controller.yaml")
cmd.Run(cmd, []string{})
@ -109,9 +117,11 @@ func TestDescribeObject(t *testing.T) {
func TestDescribeListObjects(t *testing.T) {
pods, _, _ := testData()
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
d := &testDescriber{Output: "test output"}
tf.Describer = d
tf.DescriberVal = d
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)},
@ -120,7 +130,7 @@ func TestDescribeListObjects(t *testing.T) {
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
buferr := bytes.NewBuffer([]byte{})
cmd := NewCmdDescribe(f, buf, buferr)
cmd := NewCmdDescribe(tf, buf, buferr)
cmd.Run(cmd, []string{"pods"})
if buf.String() != fmt.Sprintf("%s\n\n%s", d.Output, d.Output) {
t.Errorf("unexpected output: %s", buf.String())
@ -129,9 +139,11 @@ func TestDescribeListObjects(t *testing.T) {
func TestDescribeObjectShowEvents(t *testing.T) {
pods, _, _ := testData()
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
d := &testDescriber{Output: "test output"}
tf.Describer = d
tf.DescriberVal = d
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)},
@ -140,7 +152,7 @@ func TestDescribeObjectShowEvents(t *testing.T) {
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
buferr := bytes.NewBuffer([]byte{})
cmd := NewCmdDescribe(f, buf, buferr)
cmd := NewCmdDescribe(tf, buf, buferr)
cmd.Flags().Set("show-events", "true")
cmd.Run(cmd, []string{"pods"})
if d.Settings.ShowEvents != true {
@ -150,9 +162,11 @@ func TestDescribeObjectShowEvents(t *testing.T) {
func TestDescribeObjectSkipEvents(t *testing.T) {
pods, _, _ := testData()
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
d := &testDescriber{Output: "test output"}
tf.Describer = d
tf.DescriberVal = d
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)},
@ -161,10 +175,37 @@ func TestDescribeObjectSkipEvents(t *testing.T) {
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
buferr := bytes.NewBuffer([]byte{})
cmd := NewCmdDescribe(f, buf, buferr)
cmd := NewCmdDescribe(tf, buf, buferr)
cmd.Flags().Set("show-events", "false")
cmd.Run(cmd, []string{"pods"})
if d.Settings.ShowEvents != false {
t.Errorf("ShowEvents = false expected, got ShowEvents = %v", d.Settings.ShowEvents)
}
}
func TestDescribeHelpMessage(t *testing.T) {
tf := cmdtesting.NewTestFactory()
buf := bytes.NewBuffer([]byte{})
buferr := bytes.NewBuffer([]byte{})
cmd := NewCmdDescribe(tf, buf, buferr)
cmd.SetArgs([]string{"-h"})
cmd.SetOutput(buf)
_, err := cmd.ExecuteC()
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
got := buf.String()
expected := `describe (-f FILENAME | TYPE [NAME_PREFIX | -l label] | TYPE/NAME)`
if !strings.Contains(got, expected) {
t.Errorf("Expected to contain: \n %v\nGot:\n %v\n", expected, got)
}
unexpected := `describe (-f FILENAME | TYPE [NAME_PREFIX | -l label] | TYPE/NAME) [flags]`
if strings.Contains(got, unexpected) {
t.Errorf("Expected not to contain: \n %v\nGot:\n %v\n", unexpected, got)
}
}

View File

@ -44,7 +44,7 @@ var (
Diff configurations specified by filename or stdin between their local,
last-applied, live and/or "merged" versions.
LOCAL and LIVE versions are diffed by default. Other availble keywords
LOCAL and LIVE versions are diffed by default. Other available keywords
are MERGED and LAST.
Output is always YAML.
@ -109,7 +109,8 @@ func NewCmdDiff(f cmdutil.Factory, stdout, stderr io.Writer) *cobra.Command {
Stderr: stderr,
}
cmd := &cobra.Command{
Use: "diff -f FILENAME",
Use: "diff -f FILENAME",
DisableFlagsInUseLine: true,
Short: i18n.T("Diff different versions of configurations"),
Long: diffLong,
Example: diffExample,
@ -315,7 +316,7 @@ func (obj InfoObject) Merged() (map[string]interface{}, error) {
}
if live == nil || last == nil {
return local, nil // We probably don't have a live verison, merged is local.
return local, nil // We probably don't have a live version, merged is local.
}
elmt, err := obj.Parser.CreateElement(last, local, live)
@ -438,7 +439,7 @@ func RunDiff(f cmdutil.Factory, diff *DiffProgram, options *DiffOptions, from, t
obj := InfoObject{
Info: info,
Parser: parser,
Encoder: f.JSONEncoder(),
Encoder: cmdutil.InternalVersionJSONEncoder(),
}
return differ.Diff(obj, printer)

View File

@ -33,6 +33,7 @@ import (
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/json"
@ -61,6 +62,7 @@ type DrainOptions struct {
backOff clockwork.Clock
DeleteLocalData bool
Selector string
PodSelector string
mapper meta.RESTMapper
nodeInfos []*resource.Info
Out io.Writer
@ -103,7 +105,8 @@ func NewCmdCordon(f cmdutil.Factory, out io.Writer) *cobra.Command {
options := &DrainOptions{Factory: f, Out: out}
cmd := &cobra.Command{
Use: "cordon NODE",
Use: "cordon NODE",
DisableFlagsInUseLine: true,
Short: i18n.T("Mark node as unschedulable"),
Long: cordon_long,
Example: cordon_example,
@ -130,7 +133,8 @@ func NewCmdUncordon(f cmdutil.Factory, out io.Writer) *cobra.Command {
options := &DrainOptions{Factory: f, Out: out}
cmd := &cobra.Command{
Use: "uncordon NODE",
Use: "uncordon NODE",
DisableFlagsInUseLine: true,
Short: i18n.T("Mark node as schedulable"),
Long: uncordon_long,
Example: uncordon_example,
@ -182,7 +186,8 @@ func NewCmdDrain(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
options := &DrainOptions{Factory: f, Out: out, ErrOut: errOut, backOff: clockwork.NewRealClock()}
cmd := &cobra.Command{
Use: "drain NODE",
Use: "drain NODE",
DisableFlagsInUseLine: true,
Short: i18n.T("Drain node in preparation for maintenance"),
Long: drain_long,
Example: drain_example,
@ -197,6 +202,8 @@ func NewCmdDrain(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
cmd.Flags().IntVar(&options.GracePeriodSeconds, "grace-period", -1, "Period of time in seconds given to each pod to terminate gracefully. If negative, the default value specified in the pod will be used.")
cmd.Flags().DurationVar(&options.Timeout, "timeout", 0, "The length of time to wait before giving up, zero means infinite")
cmd.Flags().StringVarP(&options.Selector, "selector", "l", options.Selector, "Selector (label query) to filter on")
cmd.Flags().StringVarP(&options.PodSelector, "pod-selector", "", options.PodSelector, "Label selector to filter pods on the node")
cmdutil.AddDryRunFlag(cmd)
return cmd
}
@ -217,19 +224,24 @@ func (o *DrainOptions) SetupDrain(cmd *cobra.Command, args []string) error {
return cmdutil.UsageErrorf(cmd, fmt.Sprintf("USAGE: %s [flags]", cmd.Use))
}
o.DryRun = cmdutil.GetFlagBool(cmd, "dry-run")
o.DryRun = cmdutil.GetDryRunFlag(cmd)
if o.client, err = o.Factory.KubernetesClientSet(); err != nil {
return err
}
if len(o.PodSelector) > 0 {
if _, err := labels.Parse(o.PodSelector); err != nil {
return errors.New("--pod-selector=<pod_selector> must be a valid label selector")
}
}
o.restClient, err = o.Factory.RESTClient()
if err != nil {
return err
}
o.nodeInfos = []*resource.Info{}
o.mapper, o.typer = o.Factory.Object()
cmdNamespace, _, err := o.Factory.DefaultNamespace()
if err != nil {
@ -283,7 +295,7 @@ func (o *DrainOptions) RunDrain() error {
}
if err == nil || o.DryRun {
drainedNodes.Insert(info.Name)
o.Factory.PrintSuccess(o.mapper, false, o.Out, "node", info.Name, o.DryRun, "drained")
cmdutil.PrintSuccess(false, o.Out, info.Object, o.DryRun, "drained")
} else {
fmt.Fprintf(o.ErrOut, "error: unable to drain node %q, aborting command...\n\n", info.Name)
remainingNodes := []string{}
@ -328,38 +340,8 @@ func (o *DrainOptions) deleteOrEvictPodsSimple(nodeInfo *resource.Info) error {
return err
}
func (o *DrainOptions) getController(namespace string, controllerRef *metav1.OwnerReference) (interface{}, error) {
switch controllerRef.Kind {
case "ReplicationController":
return o.client.Core().ReplicationControllers(namespace).Get(controllerRef.Name, metav1.GetOptions{})
case "DaemonSet":
return o.client.Extensions().DaemonSets(namespace).Get(controllerRef.Name, metav1.GetOptions{})
case "Job":
return o.client.Batch().Jobs(namespace).Get(controllerRef.Name, metav1.GetOptions{})
case "ReplicaSet":
return o.client.Extensions().ReplicaSets(namespace).Get(controllerRef.Name, metav1.GetOptions{})
case "StatefulSet":
return o.client.AppsV1beta1().StatefulSets(namespace).Get(controllerRef.Name, metav1.GetOptions{})
}
return nil, fmt.Errorf("Unknown controller kind %q", controllerRef.Kind)
}
func (o *DrainOptions) getPodController(pod corev1.Pod) (*metav1.OwnerReference, error) {
controllerRef := metav1.GetControllerOf(&pod)
if controllerRef == nil {
return nil, nil
}
// We assume the only reason for an error is because the controller is
// gone/missing, not for any other cause.
// TODO(mml): something more sophisticated than this
// TODO(juntee): determine if it's safe to remove getController(),
// so that drain can work for controller types that we don't know about
_, err := o.getController(pod.Namespace, controllerRef)
if err != nil {
return nil, err
}
return controllerRef, nil
func (o *DrainOptions) getPodController(pod corev1.Pod) *metav1.OwnerReference {
return metav1.GetControllerOf(&pod)
}
func (o *DrainOptions) unreplicatedFilter(pod corev1.Pod) (bool, *warning, *fatal) {
@ -368,21 +350,15 @@ func (o *DrainOptions) unreplicatedFilter(pod corev1.Pod) (bool, *warning, *fata
return true, nil, nil
}
controllerRef, err := o.getPodController(pod)
if err != nil {
// if we're forcing, remove orphaned pods with a warning
if apierrors.IsNotFound(err) && o.Force {
return true, &warning{err.Error()}, nil
}
return false, nil, &fatal{err.Error()}
}
controllerRef := o.getPodController(pod)
if controllerRef != nil {
return true, nil, nil
}
if !o.Force {
return false, nil, &fatal{kUnmanagedFatal}
if o.Force {
return true, &warning{kUnmanagedWarning}, nil
}
return true, &warning{kUnmanagedWarning}, nil
return false, nil, &fatal{kUnmanagedFatal}
}
func (o *DrainOptions) daemonsetFilter(pod corev1.Pod) (bool, *warning, *fatal) {
@ -393,23 +369,23 @@ func (o *DrainOptions) daemonsetFilter(pod corev1.Pod) (bool, *warning, *fatal)
// The exception is for pods that are orphaned (the referencing
// management resource - including DaemonSet - is not found).
// Such pods will be deleted if --force is used.
controllerRef, err := o.getPodController(pod)
if err != nil {
// if we're forcing, remove orphaned pods with a warning
controllerRef := o.getPodController(pod)
if controllerRef == nil || controllerRef.Kind != "DaemonSet" {
return true, nil, nil
}
if _, err := o.client.ExtensionsV1beta1().DaemonSets(pod.Namespace).Get(controllerRef.Name, metav1.GetOptions{}); err != nil {
// remove orphaned pods with a warning if --force is used
if apierrors.IsNotFound(err) && o.Force {
return true, &warning{err.Error()}, nil
}
return false, nil, &fatal{err.Error()}
}
if controllerRef == nil || controllerRef.Kind != "DaemonSet" {
return true, nil, nil
}
if _, err := o.client.Extensions().DaemonSets(pod.Namespace).Get(controllerRef.Name, metav1.GetOptions{}); err != nil {
return false, nil, &fatal{err.Error()}
}
if !o.IgnoreDaemonsets {
return false, nil, &fatal{kDaemonsetFatal}
}
return false, &warning{kDaemonsetWarning}, nil
}
@ -455,7 +431,13 @@ func (ps podStatuses) Message() string {
// getPodsForDeletion receives resource info for a node, and returns all the pods from the given node that we
// are planning on deleting. If there are any pods preventing us from deleting, we return that list in an error.
func (o *DrainOptions) getPodsForDeletion(nodeInfo *resource.Info) (pods []corev1.Pod, err error) {
podList, err := o.client.Core().Pods(metav1.NamespaceAll).List(metav1.ListOptions{
labelSelector, err := labels.Parse(o.PodSelector)
if err != nil {
return pods, err
}
podList, err := o.client.CoreV1().Pods(metav1.NamespaceAll).List(metav1.ListOptions{
LabelSelector: labelSelector.String(),
FieldSelector: fields.SelectorFromSet(fields.Set{"spec.nodeName": nodeInfo.Name}).String()})
if err != nil {
return pods, err
@ -466,7 +448,7 @@ func (o *DrainOptions) getPodsForDeletion(nodeInfo *resource.Info) (pods []corev
for _, pod := range podList.Items {
podOk := true
for _, filt := range []podFilter{mirrorPodFilter, o.localStorageFilter, o.unreplicatedFilter, o.daemonsetFilter} {
for _, filt := range []podFilter{o.daemonsetFilter, mirrorPodFilter, o.localStorageFilter, o.unreplicatedFilter} {
filterOk, w, f := filt(pod)
podOk = podOk && filterOk
@ -476,6 +458,13 @@ func (o *DrainOptions) getPodsForDeletion(nodeInfo *resource.Info) (pods []corev
if f != nil {
fs[f.string] = append(fs[f.string], pod.Name)
}
// short-circuit as soon as pod not ok
// at that point, there is no reason to run pod
// through any additional filters
if !podOk {
break
}
}
if podOk {
pods = append(pods, pod)
@ -497,7 +486,7 @@ func (o *DrainOptions) deletePod(pod corev1.Pod) error {
gracePeriodSeconds := int64(o.GracePeriodSeconds)
deleteOptions.GracePeriodSeconds = &gracePeriodSeconds
}
return o.client.Core().Pods(pod.Namespace).Delete(pod.Name, deleteOptions)
return o.client.CoreV1().Pods(pod.Namespace).Delete(pod.Name, deleteOptions)
}
func (o *DrainOptions) evictPod(pod corev1.Pod, policyGroupVersion string) error {
@ -518,7 +507,7 @@ func (o *DrainOptions) evictPod(pod corev1.Pod, policyGroupVersion string) error
DeleteOptions: deleteOptions,
}
// Remember to change change the URL manipulation func when Evction's version change
return o.client.Policy().Evictions(eviction.Namespace).Evict(eviction)
return o.client.PolicyV1beta1().Evictions(eviction.Namespace).Evict(eviction)
}
// deleteOrEvictPods deletes or evicts the pods on the api server
@ -533,7 +522,7 @@ func (o *DrainOptions) deleteOrEvictPods(pods []corev1.Pod) error {
}
getPodFn := func(namespace, name string) (*corev1.Pod, error) {
return o.client.Core().Pods(namespace).Get(name, metav1.GetOptions{})
return o.client.CoreV1().Pods(namespace).Get(name, metav1.GetOptions{})
}
if len(policyGroupVersion) > 0 {
@ -627,7 +616,7 @@ func (o *DrainOptions) waitForDelete(pods []corev1.Pod, interval, timeout time.D
for i, pod := range pods {
p, err := getPodFn(pod.Namespace, pod.Name)
if apierrors.IsNotFound(err) || (p != nil && p.ObjectMeta.UID != pod.ObjectMeta.UID) {
o.Factory.PrintSuccess(o.mapper, false, o.Out, "pod", pod.Name, false, verbStr)
cmdutil.PrintSuccess(false, o.Out, &pod, false, verbStr)
continue
} else if err != nil {
return false, err
@ -708,7 +697,7 @@ func (o *DrainOptions) RunCordonOrUncordon(desired bool) error {
}
unsched := node.Spec.Unschedulable
if unsched == desired {
o.Factory.PrintSuccess(o.mapper, false, o.Out, nodeInfo.Mapping.Resource, nodeInfo.Name, o.DryRun, already(desired))
cmdutil.PrintSuccess(false, o.Out, nodeInfo.Object, o.DryRun, already(desired))
} else {
if !o.DryRun {
helper := resource.NewHelper(o.restClient, nodeInfo.Mapping)
@ -729,10 +718,10 @@ func (o *DrainOptions) RunCordonOrUncordon(desired bool) error {
continue
}
}
o.Factory.PrintSuccess(o.mapper, false, o.Out, nodeInfo.Mapping.Resource, nodeInfo.Name, o.DryRun, changed(desired))
cmdutil.PrintSuccess(false, o.Out, nodeInfo.Object, o.DryRun, changed(desired))
}
} else {
o.Factory.PrintSuccess(o.mapper, false, o.Out, nodeInfo.Mapping.Resource, nodeInfo.Name, o.DryRun, "skipped")
cmdutil.PrintSuccess(false, o.Out, nodeInfo.Object, o.DryRun, "skipped")
}
}

View File

@ -44,13 +44,13 @@ import (
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/api/ref"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/apis/batch"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/extensions"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
const (
@ -150,7 +150,10 @@ func TestCordon(t *testing.T) {
}
for _, test := range tests {
f, tf, codec, ns := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
ns := legacyscheme.Codecs
new_node := &corev1.Node{}
updated := false
tf.Client = &fake.RESTClient{
@ -191,10 +194,10 @@ func TestCordon(t *testing.T) {
}
}),
}
tf.ClientConfig = defaultClientConfig()
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
cmd := test.cmd(f, buf)
cmd := test.cmd(tf, buf)
saw_fatal := false
func() {
@ -217,7 +220,7 @@ func TestCordon(t *testing.T) {
t.Fatalf("%s: unexpected non-error", test.description)
}
if updated {
t.Fatalf("%s: unexpcted update", test.description)
t.Fatalf("%s: unexpected update", test.description)
}
}
@ -304,6 +307,34 @@ func TestDrain(t *testing.T) {
},
}
ds_pod_with_emptyDir := corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "bar",
Namespace: "default",
CreationTimestamp: metav1.Time{Time: time.Now()},
Labels: labels,
SelfLink: testapi.Default.SelfLink("pods", "bar"),
OwnerReferences: []metav1.OwnerReference{
{
APIVersion: "extensions/v1beta1",
Kind: "DaemonSet",
Name: "ds",
BlockOwnerDeletion: boolptr(true),
Controller: boolptr(true),
},
},
},
Spec: corev1.PodSpec{
NodeName: "node",
Volumes: []corev1.Volume{
{
Name: "scratch",
VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{Medium: ""}},
},
},
},
}
orphaned_ds_pod := corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "bar",
@ -414,15 +445,16 @@ func TestDrain(t *testing.T) {
}
tests := []struct {
description string
node *corev1.Node
expected *corev1.Node
pods []corev1.Pod
rcs []api.ReplicationController
replicaSets []extensions.ReplicaSet
args []string
expectFatal bool
expectDelete bool
description string
node *corev1.Node
expected *corev1.Node
pods []corev1.Pod
rcs []api.ReplicationController
replicaSets []extensions.ReplicaSet
args []string
expectWarning string
expectFatal bool
expectDelete bool
}{
{
description: "RC-managed pod",
@ -474,6 +506,17 @@ func TestDrain(t *testing.T) {
expectFatal: false,
expectDelete: false,
},
{
description: "DS-managed pod with emptyDir with --ignore-daemonsets",
node: node,
expected: cordoned_node,
pods: []corev1.Pod{ds_pod_with_emptyDir},
rcs: []api.ReplicationController{rc},
args: []string{"node", "--ignore-daemonsets"},
expectWarning: "WARNING: Ignoring DaemonSet-managed pods: bar\n",
expectFatal: false,
expectDelete: false,
},
{
description: "Job-managed pod",
node: node,
@ -557,7 +600,10 @@ func TestDrain(t *testing.T) {
new_node := &corev1.Node{}
deleted := false
evicted := false
f, tf, codec, ns := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion,
NegotiatedSerializer: ns,
@ -654,13 +700,14 @@ func TestDrain(t *testing.T) {
}
}),
}
tf.ClientConfig = defaultClientConfig()
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{})
cmd := NewCmdDrain(f, buf, errBuf)
cmd := NewCmdDrain(tf, buf, errBuf)
saw_fatal := false
fatal_msg := ""
func() {
defer func() {
// Recover from the panic below.
@ -668,7 +715,7 @@ func TestDrain(t *testing.T) {
// Restore cmdutil behavior
cmdutil.DefaultBehaviorOnFatal()
}()
cmdutil.BehaviorOnFatal(func(e string, code int) { saw_fatal = true; panic(e) })
cmdutil.BehaviorOnFatal(func(e string, code int) { saw_fatal = true; fatal_msg = e; panic(e) })
cmd.SetArgs(test.args)
cmd.Execute()
}()
@ -676,6 +723,11 @@ func TestDrain(t *testing.T) {
if !saw_fatal {
t.Fatalf("%s: unexpected non-error when using %s", test.description, currMethod)
}
} else {
if saw_fatal {
t.Fatalf("%s: unexpected error when using %s: %s", test.description, currMethod, fatal_msg)
}
}
if test.expectDelete {
@ -693,6 +745,16 @@ func TestDrain(t *testing.T) {
t.Fatalf("%s: unexpected delete when using %s", test.description, currMethod)
}
}
if len(test.expectWarning) > 0 {
if len(errBuf.String()) == 0 {
t.Fatalf("%s: expected warning, but found no stderr output", test.description)
}
if errBuf.String() != test.expectWarning {
t.Fatalf("%s: actual warning message did not match expected warning message.\n Expecting: %s\n Got: %s", test.description, test.expectWarning, errBuf.String())
}
}
}
}
}
@ -762,9 +824,9 @@ func TestDeletePods(t *testing.T) {
}
for _, test := range tests {
f, _, _, _ := cmdtesting.NewAPIFactory()
o := DrainOptions{Factory: f}
o.mapper, _ = f.Object()
tf := cmdtesting.NewTestFactory()
o := DrainOptions{Factory: tf}
o.mapper, _ = tf.Object()
o.Out = os.Stdout
_, pods := createPods(false)
pendingPods, err := o.waitForDelete(pods, test.interval, test.timeout, false, test.getPodFn)
@ -826,18 +888,3 @@ func (m *MyReq) isFor(method string, path string) bool {
req.URL.Path == strings.Join([]string{"/apis/extensions/v1beta1", path}, "") ||
req.URL.Path == strings.Join([]string{"/apis/batch/v1", path}, ""))
}
func refJson(t *testing.T, o runtime.Object) string {
ref, err := ref.GetReference(legacyscheme.Scheme, o)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
_, _, codec, _ := cmdtesting.NewAPIFactory()
json, err := runtime.Encode(codec, &api.SerializedReference{Reference: *ref})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
return string(json)
}

View File

@ -28,7 +28,6 @@ import (
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/cmd/util/editor"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/printers"
)
var (
@ -72,22 +71,15 @@ var (
func NewCmdEdit(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
options := &editor.EditOptions{
EditMode: editor.NormalEditMode,
}
// retrieve a list of handled resources from printer as valid args
validArgs, argAliases := []string{}, []string{}
p, err := f.Printer(nil, printers.PrintOptions{
ColumnLabels: []string{},
})
cmdutil.CheckErr(err)
if p != nil {
validArgs = p.HandledResources()
argAliases = kubectl.ResourceAliases(validArgs)
EditMode: editor.NormalEditMode,
ValidateOptions: cmdutil.ValidateOptions{EnableValidation: true},
Include3rdParty: true,
}
validArgs := cmdutil.ValidArgList(f)
cmd := &cobra.Command{
Use: "edit (RESOURCE/NAME | -f FILENAME)",
Use: "edit (RESOURCE/NAME | -f FILENAME)",
DisableFlagsInUseLine: true,
Short: i18n.T("Edit a resource on the server"),
Long: editLong,
Example: fmt.Sprintf(editExample),
@ -101,7 +93,7 @@ func NewCmdEdit(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
}
},
ValidArgs: validArgs,
ArgAliases: argAliases,
ArgAliases: kubectl.ResourceAliases(validArgs),
}
usage := "to use to edit the resource"
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)

View File

@ -206,8 +206,7 @@ func TestEdit(t *testing.T) {
t.Fatalf("%s: %v", name, err)
}
f, tf, _, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
tf.UnstructuredClientForMappingFunc = func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
versionedAPIPath := ""
if mapping.GroupVersionKind.Group == "" {
@ -225,20 +224,20 @@ func TestEdit(t *testing.T) {
if len(testcase.Namespace) > 0 {
tf.Namespace = testcase.Namespace
}
tf.ClientConfig = defaultClientConfig()
tf.Command = "edit test cmd invocation"
tf.ClientConfigVal = defaultClientConfig()
tf.CommandVal = "edit test cmd invocation"
buf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{})
var cmd *cobra.Command
switch testcase.Mode {
case "edit":
cmd = NewCmdEdit(f, buf, errBuf)
cmd = NewCmdEdit(tf, buf, errBuf)
case "create":
cmd = NewCmdCreate(f, buf, errBuf)
cmd = NewCmdCreate(tf, buf, errBuf)
cmd.Flags().Set("edit", "true")
case "edit-last-applied":
cmd = NewCmdApplyEditLastApplied(f, buf, errBuf)
cmd = NewCmdApplyEditLastApplied(tf, buf, errBuf)
default:
t.Fatalf("%s: unexpected mode %s", name, testcase.Mode)
}

View File

@ -73,7 +73,8 @@ func NewCmdExec(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *c
Executor: &DefaultRemoteExecutor{},
}
cmd := &cobra.Command{
Use: "exec POD [-c CONTAINER] -- COMMAND [args...]",
Use: "exec POD [-c CONTAINER] -- COMMAND [args...]",
DisableFlagsInUseLine: true,
Short: i18n.T("Execute a command in a container"),
Long: "Execute a command in a container.",
Example: exec_example,
@ -154,7 +155,7 @@ func (p *ExecOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, argsIn []s
return cmdutil.UsageErrorf(cmd, execUsageStr)
}
if len(p.PodName) != 0 {
printDeprecationWarning("exec POD_NAME", "-p POD_NAME")
printDeprecationWarning(p.Err, "exec POD_NAME", "-p POD_NAME")
if len(argsIn) < 1 {
return cmdutil.UsageErrorf(cmd, execUsageStr)
}

View File

@ -33,8 +33,10 @@ import (
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/rest/fake"
"k8s.io/client-go/tools/remotecommand"
"k8s.io/kubernetes/pkg/api/legacyscheme"
api "k8s.io/kubernetes/pkg/apis/core"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/scheme"
"k8s.io/kubernetes/pkg/kubectl/util/term"
)
@ -128,17 +130,20 @@ func TestPodAndContainer(t *testing.T) {
},
}
for _, test := range tests {
f, tf, _, ns := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
NegotiatedSerializer: ns,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { return nil, nil }),
}
tf.Namespace = "test"
tf.ClientConfig = defaultClientConfig()
tf.ClientConfigVal = defaultClientConfig()
cmd := &cobra.Command{}
options := test.p
err := options.Complete(f, cmd, test.args, test.argsLenAtDash)
options.Err = bytes.NewBuffer([]byte{})
err := options.Complete(tf, cmd, test.args, test.argsLenAtDash)
if test.expectError && err == nil {
t.Errorf("%s: unexpected non-error", test.name)
}
@ -163,9 +168,9 @@ func TestPodAndContainer(t *testing.T) {
func TestExec(t *testing.T) {
version := "v1"
tests := []struct {
name, podPath, execPath, container string
pod *api.Pod
execErr bool
name, podPath, execPath string
pod *api.Pod
execErr bool
}{
{
name: "pod exec",
@ -182,7 +187,10 @@ func TestExec(t *testing.T) {
},
}
for _, test := range tests {
f, tf, codec, ns := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
NegotiatedSerializer: ns,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -198,7 +206,7 @@ func TestExec(t *testing.T) {
}),
}
tf.Namespace = "test"
tf.ClientConfig = defaultClientConfig()
tf.ClientConfigVal = defaultClientConfig()
bufOut := bytes.NewBuffer([]byte{})
bufErr := bytes.NewBuffer([]byte{})
bufIn := bytes.NewBuffer([]byte{})
@ -218,7 +226,7 @@ func TestExec(t *testing.T) {
}
cmd := &cobra.Command{}
args := []string{"test", "command"}
if err := params.Complete(f, cmd, args, -1); err != nil {
if err := params.Complete(tf, cmd, args, -1); err != nil {
t.Fatal(err)
}
err := params.Run()

View File

@ -26,19 +26,18 @@ import (
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/explain"
"k8s.io/kubernetes/pkg/kubectl/scheme"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
)
var (
explainLong = templates.LongDesc(`
List the fields for supported resources
This command describes the fields associated with each supported API resource.
Fields are identified via a simple JSONPath identifier:
Fields are identified via a simple JSONPath identifier:
<type>.<fieldName>[.<fieldName>]
Add the --recursive flag to display all of the fields at once without descriptions.
Information about each field is retrieved from the server in OpenAPI format.`)
@ -53,7 +52,8 @@ var (
// NewCmdExplain returns a cobra command for swagger docs
func NewCmdExplain(f cmdutil.Factory, out, cmdErr io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "explain RESOURCE",
Use: "explain RESOURCE",
DisableFlagsInUseLine: true,
Short: i18n.T("Documentation of resources"),
Long: explainLong + "\n\n" + cmdutil.ValidResourceTypeList(f),
Example: explainExamples,
@ -80,7 +80,6 @@ func RunExplain(f cmdutil.Factory, out, cmdErr io.Writer, cmd *cobra.Command, ar
recursive := cmdutil.GetFlagBool(cmd, "recursive")
apiVersionString := cmdutil.GetFlagString(cmd, "api-version")
apiVersion := schema.GroupVersion{}
mapper, _ := f.Object()
// TODO: After we figured out the new syntax to separate group and resource, allow
@ -104,20 +103,13 @@ func RunExplain(f cmdutil.Factory, out, cmdErr io.Writer, cmd *cobra.Command, ar
}
}
if len(apiVersionString) == 0 {
groupMeta, err := scheme.Registry.Group(gvk.Group)
if err != nil {
return err
}
apiVersion = groupMeta.GroupVersion
} else {
apiVersion, err = schema.ParseGroupVersion(apiVersionString)
if len(apiVersionString) != 0 {
apiVersion, err := schema.ParseGroupVersion(apiVersionString)
if err != nil {
return err
}
gvk = apiVersion.WithKind(gvk.Kind)
}
gvk = apiVersion.WithKind(gvk.Kind)
resources, err := f.OpenAPISchema()
if err != nil {
@ -129,5 +121,5 @@ func RunExplain(f cmdutil.Factory, out, cmdErr io.Writer, cmd *cobra.Command, ar
return fmt.Errorf("Couldn't find resource for %q", gvk)
}
return explain.PrintModelDescription(fieldsPath, out, schema, recursive)
return explain.PrintModelDescription(fieldsPath, out, schema, gvk, recursive)
}

View File

@ -75,15 +75,15 @@ var (
func NewCmdExposeService(f cmdutil.Factory, out io.Writer) *cobra.Command {
options := &resource.FilenameOptions{}
validArgs, argAliases := []string{}, []string{}
validArgs := []string{}
resources := regexp.MustCompile(`\s*,`).Split(exposeResources, -1)
for _, r := range resources {
validArgs = append(validArgs, strings.Fields(r)[0])
argAliases = kubectl.ResourceAliases(validArgs)
}
cmd := &cobra.Command{
Use: "expose (-f FILENAME | TYPE NAME) [--port=port] [--protocol=TCP|UDP] [--target-port=number-or-name] [--name=name] [--external-ip=external-ip-of-service] [--type=type]",
Use: "expose (-f FILENAME | TYPE NAME) [--port=port] [--protocol=TCP|UDP] [--target-port=number-or-name] [--name=name] [--external-ip=external-ip-of-service] [--type=type]",
DisableFlagsInUseLine: true,
Short: i18n.T("Take a replication controller, service, deployment or pod and expose it as a new Kubernetes Service"),
Long: exposeLong,
Example: exposeExample,
@ -92,14 +92,14 @@ func NewCmdExposeService(f cmdutil.Factory, out io.Writer) *cobra.Command {
cmdutil.CheckErr(err)
},
ValidArgs: validArgs,
ArgAliases: argAliases,
ArgAliases: kubectl.ResourceAliases(validArgs),
}
cmdutil.AddPrinterFlags(cmd)
cmd.Flags().String("generator", "service/v2", i18n.T("The name of the API generator to use. There are 2 generators: 'service/v1' and 'service/v2'. The only difference between them is that service port in v1 is named 'default', while it is left unnamed in v2. Default is 'service/v2'."))
cmd.Flags().String("protocol", "", i18n.T("The network protocol for the service to be created. Default is 'TCP'."))
cmd.Flags().String("port", "", i18n.T("The port that the service should serve on. Copied from the resource being exposed, if unspecified"))
cmd.Flags().String("type", "", i18n.T("Type for this service: ClusterIP, NodePort, LoadBalancer, or ExternalName. Default is 'ClusterIP'."))
cmd.Flags().String("load-balancer-ip", "", i18n.T("IP to assign to the Load Balancer. If empty, an ephemeral IP will be created and used (cloud-provider specific)."))
cmd.Flags().String("load-balancer-ip", "", i18n.T("IP to assign to the LoadBalancer. If empty, an ephemeral IP will be created and used (cloud-provider specific)."))
cmd.Flags().String("selector", "", i18n.T("A label selector to use for this service. Only equality-based selector requirements are supported. If empty (the default) infer the selector from the replication controller or replica set.)"))
cmd.Flags().StringP("labels", "l", "", "Labels to apply to the service created by this call.")
cmd.Flags().String("container-port", "", i18n.T("Synonym for --target-port"))
@ -230,7 +230,7 @@ func RunExpose(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri
}
if inline := cmdutil.GetFlagString(cmd, "overrides"); len(inline) > 0 {
codec := runtime.NewCodec(f.JSONEncoder(), f.Decoder(true))
codec := runtime.NewCodec(cmdutil.InternalVersionJSONEncoder(), cmdutil.InternalVersionDecoder())
object, err = cmdutil.Merge(codec, object, inline)
if err != nil {
return err
@ -241,7 +241,7 @@ func RunExpose(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri
ObjectTyper: typer,
RESTMapper: mapper,
ClientMapper: resource.ClientMapperFunc(f.ClientForMapping),
Decoder: f.Decoder(true),
Decoder: cmdutil.InternalVersionDecoder(),
}
info, err = resourceMapper.InfoForObject(object, nil)
if err != nil {
@ -255,12 +255,12 @@ func RunExpose(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri
info.Refresh(object, true)
if cmdutil.GetDryRunFlag(cmd) {
if len(cmdutil.GetFlagString(cmd, "output")) > 0 {
return f.PrintObject(cmd, false, mapper, object, out)
return cmdutil.PrintObject(cmd, object, out)
}
f.PrintSuccess(mapper, false, out, info.Mapping.Resource, info.Name, true, "exposed")
cmdutil.PrintSuccess(false, out, info.Object, true, "exposed")
return nil
}
if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil {
if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, cmdutil.InternalVersionJSONEncoder()); err != nil {
return err
}
@ -271,10 +271,10 @@ func RunExpose(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri
}
if len(cmdutil.GetFlagString(cmd, "output")) > 0 {
return f.PrintObject(cmd, false, mapper, object, out)
return cmdutil.PrintObject(cmd, object, out)
}
f.PrintSuccess(mapper, false, out, info.Mapping.Resource, info.Name, false, "exposed")
cmdutil.PrintSuccess(false, out, info.Object, false, "exposed")
return nil
})
if err != nil {

View File

@ -28,10 +28,10 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
api "k8s.io/kubernetes/pkg/apis/core"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/scheme"
"k8s.io/kubernetes/pkg/printers"
)
// This init should be removed after switching this command and its tests to user external types.
@ -466,8 +466,10 @@ func TestRunExposeService(t *testing.T) {
}
for _, test := range tests {
f, tf, codec, ns := cmdtesting.NewAPIFactory()
tf.Printer = &printers.JSONPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: "v1"},
NegotiatedSerializer: ns,
@ -486,7 +488,7 @@ func TestRunExposeService(t *testing.T) {
tf.Namespace = test.ns
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdExposeService(f, buf)
cmd := NewCmdExposeService(tf, buf)
cmd.SetOutput(buf)
for flag, value := range test.flags {
cmd.Flags().Set(flag, value)
@ -495,12 +497,6 @@ func TestRunExposeService(t *testing.T) {
out := buf.String()
if _, ok := test.flags["dry-run"]; ok {
buf.Reset()
if err := tf.Printer.PrintObj(test.output, buf); err != nil {
t.Errorf("%s: Unexpected error: %v", test.name, err)
continue
}
test.expected = fmt.Sprintf("service %q exposed (dry run)", test.flags["name"])
}

View File

@ -31,7 +31,8 @@ var helpLong = templates.LongDesc(i18n.T(`
func NewCmdHelp() *cobra.Command {
cmd := &cobra.Command{
Use: "help [command] | STRING_TO_SEARCH",
Use: "help [command] | STRING_TO_SEARCH",
DisableFlagsInUseLine: true,
Short: i18n.T("Help about any command"),
Long: helpLong,

View File

@ -39,7 +39,6 @@ import (
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/printers"
)
// LabelOptions have the data required to perform the label operation
@ -98,20 +97,11 @@ var (
func NewCmdLabel(f cmdutil.Factory, out io.Writer) *cobra.Command {
options := &LabelOptions{}
// retrieve a list of handled resources from printer as valid args
validArgs, argAliases := []string{}, []string{}
p, err := f.Printer(nil, printers.PrintOptions{
ColumnLabels: []string{},
})
cmdutil.CheckErr(err)
if p != nil {
validArgs = p.HandledResources()
argAliases = kubectl.ResourceAliases(validArgs)
}
validArgs := cmdutil.ValidArgList(f)
cmd := &cobra.Command{
Use: "label [--overwrite] (-f FILENAME | TYPE NAME) KEY_1=VAL_1 ... KEY_N=VAL_N [--resource-version=version]",
Use: "label [--overwrite] (-f FILENAME | TYPE NAME) KEY_1=VAL_1 ... KEY_N=VAL_N [--resource-version=version]",
DisableFlagsInUseLine: true,
Short: i18n.T("Update the labels on a resource"),
Long: fmt.Sprintf(labelLong, validation.LabelValueMaxLength),
Example: labelExample,
@ -125,15 +115,15 @@ func NewCmdLabel(f cmdutil.Factory, out io.Writer) *cobra.Command {
cmdutil.CheckErr(options.RunLabel(f, cmd))
},
ValidArgs: validArgs,
ArgAliases: argAliases,
ArgAliases: kubectl.ResourceAliases(validArgs),
}
cmdutil.AddPrinterFlags(cmd)
cmd.Flags().Bool("overwrite", false, "If true, allow labels to be overwritten, otherwise reject label updates that overwrite existing labels.")
cmd.Flags().BoolVar(&options.overwrite, "overwrite", options.overwrite, "If true, allow labels to be overwritten, otherwise reject label updates that overwrite existing labels.")
cmd.Flags().BoolVar(&options.list, "list", options.list, "If true, display the labels for a given resource.")
cmd.Flags().Bool("local", false, "If true, label will NOT contact api-server but run locally.")
cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on, not including uninitialized ones, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2).")
cmd.Flags().Bool("all", false, "Select all resources, including uninitialized ones, in the namespace of the specified resource types")
cmd.Flags().String("resource-version", "", i18n.T("If non-empty, the labels update will only succeed if this is the current resource-version for the object. Only valid when specifying a single resource."))
cmd.Flags().BoolVar(&options.local, "local", options.local, "If true, label will NOT contact api-server but run locally.")
cmd.Flags().StringVarP(&options.selector, "selector", "l", options.selector, "Selector (label query) to filter on, not including uninitialized ones, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2).")
cmd.Flags().BoolVar(&options.all, "all", options.all, "Select all resources, including uninitialized ones, in the namespace of the specified resource types")
cmd.Flags().StringVar(&options.resourceVersion, "resource-version", options.resourceVersion, i18n.T("If non-empty, the labels update will only succeed if this is the current resource-version for the object. Only valid when specifying a single resource."))
usage := "identifying the resource to update the labels"
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
cmdutil.AddDryRunFlag(cmd)
@ -147,12 +137,6 @@ func NewCmdLabel(f cmdutil.Factory, out io.Writer) *cobra.Command {
// Complete adapts from the command line args and factory to the data required.
func (o *LabelOptions) Complete(out io.Writer, cmd *cobra.Command, args []string) (err error) {
o.out = out
o.list = cmdutil.GetFlagBool(cmd, "list")
o.local = cmdutil.GetFlagBool(cmd, "local")
o.overwrite = cmdutil.GetFlagBool(cmd, "overwrite")
o.all = cmdutil.GetFlagBool(cmd, "all")
o.resourceVersion = cmdutil.GetFlagString(cmd, "resource-version")
o.selector = cmdutil.GetFlagString(cmd, "selector")
o.outputFormat = cmdutil.GetFlagString(cmd, "output")
o.dryrun = cmdutil.GetDryRunFlag(cmd)
@ -172,6 +156,9 @@ func (o *LabelOptions) Complete(out io.Writer, cmd *cobra.Command, args []string
// Validate checks to the LabelOptions to see if there is sufficient information run the command.
func (o *LabelOptions) Validate() error {
if o.all && len(o.selector) > 0 {
return fmt.Errorf("cannot set --all and --selector at the same time")
}
if len(o.resources) < 1 && cmdutil.IsFilenameSliceEmpty(o.FilenameOptions.Filenames) {
return fmt.Errorf("one or more resources must be specified as <resource> <name> or <resource>/<name>")
}
@ -300,10 +287,10 @@ func (o *LabelOptions) RunLabel(f cmdutil.Factory, cmd *cobra.Command) error {
return nil
}
if o.outputFormat != "" {
return f.PrintObject(cmd, o.local, r.Mapper().RESTMapper, outputObj, o.out)
if len(o.outputFormat) > 0 {
return cmdutil.PrintObject(cmd, outputObj, o.out)
}
f.PrintSuccess(r.Mapper().RESTMapper, false, o.out, info.Mapping.Resource, info.Name, o.dryrun, dataChangeMsg)
cmdutil.PrintSuccess(false, o.out, info.Object, o.dryrun, dataChangeMsg)
return nil
})
}

View File

@ -23,12 +23,14 @@ import (
"strings"
"testing"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest/fake"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/api/legacyscheme"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
func TestValidateLabels(t *testing.T) {
@ -164,7 +166,7 @@ func TestLabelFunc(t *testing.T) {
expectErr bool
}{
{
obj: &api.Pod{
obj: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"a": "b"},
},
@ -173,41 +175,41 @@ func TestLabelFunc(t *testing.T) {
expectErr: true,
},
{
obj: &api.Pod{
obj: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"a": "b"},
},
},
labels: map[string]string{"a": "c"},
overwrite: true,
expected: &api.Pod{
expected: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"a": "c"},
},
},
},
{
obj: &api.Pod{
obj: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"a": "b"},
},
},
labels: map[string]string{"c": "d"},
expected: &api.Pod{
expected: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"a": "b", "c": "d"},
},
},
},
{
obj: &api.Pod{
obj: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"a": "b"},
},
},
labels: map[string]string{"c": "d"},
version: "2",
expected: &api.Pod{
expected: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"a": "b", "c": "d"},
ResourceVersion: "2",
@ -215,28 +217,28 @@ func TestLabelFunc(t *testing.T) {
},
},
{
obj: &api.Pod{
obj: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"a": "b"},
},
},
labels: map[string]string{},
remove: []string{"a"},
expected: &api.Pod{
expected: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{},
},
},
},
{
obj: &api.Pod{
obj: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"a": "b", "c": "d"},
},
},
labels: map[string]string{"e": "f"},
remove: []string{"a"},
expected: &api.Pod{
expected: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"c": "d",
@ -246,11 +248,11 @@ func TestLabelFunc(t *testing.T) {
},
},
{
obj: &api.Pod{
obj: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{},
},
labels: map[string]string{"a": "b"},
expected: &api.Pod{
expected: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"a": "b"},
},
@ -319,13 +321,12 @@ func TestLabelErrors(t *testing.T) {
}
for k, testCase := range testCases {
f, tf, _, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf := cmdtesting.NewTestFactory()
tf.Namespace = "test"
tf.ClientConfig = defaultClientConfig()
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdLabel(f, buf)
cmd := NewCmdLabel(tf, buf)
cmd.SetOutput(buf)
for k, v := range testCase.flags {
@ -337,15 +338,12 @@ func TestLabelErrors(t *testing.T) {
err = opts.Validate()
}
if err == nil {
err = opts.RunLabel(f, cmd)
err = opts.RunLabel(tf, cmd)
}
if !testCase.errFn(err) {
t.Errorf("%s: unexpected error: %v", k, err)
continue
}
if tf.Printer.(*testPrinter).Objects != nil {
t.Errorf("unexpected print to default printer")
}
if buf.Len() > 0 {
t.Errorf("buffer should be empty: %s", string(buf.Bytes()))
}
@ -354,7 +352,9 @@ func TestLabelErrors(t *testing.T) {
func TestLabelForResourceFromFile(t *testing.T) {
pods, _, _ := testData()
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -382,18 +382,18 @@ func TestLabelForResourceFromFile(t *testing.T) {
}),
}
tf.Namespace = "test"
tf.ClientConfig = defaultClientConfig()
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdLabel(f, buf)
cmd := NewCmdLabel(tf, buf)
opts := LabelOptions{FilenameOptions: resource.FilenameOptions{
Filenames: []string{"../../../examples/storage/cassandra/cassandra-controller.yaml"}}}
Filenames: []string{"../../../test/e2e/testing-manifests/statefulset/cassandra/controller.yaml"}}}
err := opts.Complete(buf, cmd, []string{"a=b"})
if err == nil {
err = opts.Validate()
}
if err == nil {
err = opts.RunLabel(f, cmd)
err = opts.RunLabel(tf, cmd)
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
@ -404,7 +404,7 @@ func TestLabelForResourceFromFile(t *testing.T) {
}
func TestLabelLocal(t *testing.T) {
f, tf, _, _ := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -413,19 +413,19 @@ func TestLabelLocal(t *testing.T) {
}),
}
tf.Namespace = "test"
tf.ClientConfig = defaultClientConfig()
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdLabel(f, buf)
cmd.Flags().Set("local", "true")
cmd := NewCmdLabel(tf, buf)
opts := LabelOptions{FilenameOptions: resource.FilenameOptions{
Filenames: []string{"../../../examples/storage/cassandra/cassandra-controller.yaml"}}}
Filenames: []string{"../../../test/e2e/testing-manifests/statefulset/cassandra/controller.yaml"}},
local: true}
err := opts.Complete(buf, cmd, []string{"a=b"})
if err == nil {
err = opts.Validate()
}
if err == nil {
err = opts.RunLabel(f, cmd)
err = opts.RunLabel(tf, cmd)
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
@ -437,7 +437,9 @@ func TestLabelLocal(t *testing.T) {
func TestLabelMultipleObjects(t *testing.T) {
pods, _, _ := testData()
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -467,19 +469,17 @@ func TestLabelMultipleObjects(t *testing.T) {
}),
}
tf.Namespace = "test"
tf.ClientConfig = defaultClientConfig()
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdLabel(f, buf)
cmd.Flags().Set("all", "true")
opts := LabelOptions{}
cmd := NewCmdLabel(tf, buf)
opts := LabelOptions{all: true}
err := opts.Complete(buf, cmd, []string{"pods", "a=b"})
if err == nil {
err = opts.Validate()
}
if err == nil {
err = opts.RunLabel(f, cmd)
err = opts.RunLabel(tf, cmd)
}
if err != nil {
t.Fatalf("unexpected error: %v", err)

View File

@ -86,16 +86,17 @@ type LogsOptions struct {
}
// NewCmdLogs creates a new pod logs command
func NewCmdLogs(f cmdutil.Factory, out io.Writer) *cobra.Command {
func NewCmdLogs(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
o := &LogsOptions{}
cmd := &cobra.Command{
Use: "logs [-f] [-p] (POD | TYPE/NAME) [-c CONTAINER]",
Use: "logs [-f] [-p] (POD | TYPE/NAME) [-c CONTAINER]",
DisableFlagsInUseLine: true,
Short: i18n.T("Print the logs for a container in a pod"),
Long: "Print the logs for a container in a pod or specified resource. If the pod has only one container, the container name is optional.",
Example: logsExample,
PreRun: func(cmd *cobra.Command, args []string) {
if len(os.Args) > 1 && os.Args[1] == "log" {
printDeprecationWarning("logs", "log")
printDeprecationWarning(errOut, "logs", "log")
}
},
Run: func(cmd *cobra.Command, args []string) {

View File

@ -28,14 +28,16 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
api "k8s.io/kubernetes/pkg/apis/core"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
func TestLog(t *testing.T) {
tests := []struct {
name, version, podPath, logPath, container string
pod *api.Pod
name, version, podPath, logPath string
pod *api.Pod
}{
{
name: "v1 - pod log",
@ -47,7 +49,10 @@ func TestLog(t *testing.T) {
}
for _, test := range tests {
logContent := "test log content"
f, tf, codec, ns := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
NegotiatedSerializer: ns,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -66,10 +71,10 @@ func TestLog(t *testing.T) {
}),
}
tf.Namespace = "test"
tf.ClientConfig = defaultClientConfig()
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdLogs(f, buf)
cmd := NewCmdLogs(tf, buf, buf)
cmd.Flags().Set("namespace", "test")
cmd.Run(cmd, []string{"foo"})
@ -95,7 +100,7 @@ func testPod() *api.Pod {
}
func TestValidateLogFlags(t *testing.T) {
f, _, _, _ := cmdtesting.NewAPIFactory()
f := cmdtesting.NewTestFactory()
tests := []struct {
name string
@ -107,6 +112,11 @@ func TestValidateLogFlags(t *testing.T) {
flags: map[string]string{"since": "1h", "since-time": "2006-01-02T15:04:05Z"},
expected: "at most one of `sinceTime` or `sinceSeconds` may be specified",
},
{
name: "negative since-time",
flags: map[string]string{"since": "-1s"},
expected: "must be greater than 0",
},
{
name: "negative limit-bytes",
flags: map[string]string{"limit-bytes": "-100"},
@ -119,7 +129,8 @@ func TestValidateLogFlags(t *testing.T) {
},
}
for _, test := range tests {
cmd := NewCmdLogs(f, bytes.NewBuffer([]byte{}))
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdLogs(f, buf, buf)
out := ""
for flag, value := range test.flags {
cmd.Flags().Set(flag, value)
@ -138,3 +149,60 @@ func TestValidateLogFlags(t *testing.T) {
}
}
}
func TestLogComplete(t *testing.T) {
f := cmdtesting.NewTestFactory()
tests := []struct {
name string
args []string
flags map[string]string
expected string
}{
{
name: "No args case",
flags: map[string]string{"selector": ""},
expected: "'logs (POD | TYPE/NAME) [CONTAINER_NAME]'.\nPOD or TYPE/NAME is a required argument for the logs command",
},
{
name: "One args case",
args: []string{"foo"},
flags: map[string]string{"selector": "foo"},
expected: "only a selector (-l) or a POD name is allowed",
},
{
name: "Two args case",
args: []string{"foo", "foo1"},
flags: map[string]string{"container": "foo1"},
expected: "only one of -c or an inline [CONTAINER] arg is allowed",
},
{
name: "More than two args case",
args: []string{"foo", "foo1", "foo2"},
flags: map[string]string{"tail": "1"},
expected: "'logs (POD | TYPE/NAME) [CONTAINER_NAME]'.\nPOD or TYPE/NAME is a required argument for the logs command",
},
{
name: "follow and selecter conflict",
flags: map[string]string{"selector": "foo", "follow": "true"},
expected: "only one of follow (-f) or selector (-l) is allowed",
},
}
for _, test := range tests {
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdLogs(f, buf, buf)
var err error
out := ""
for flag, value := range test.flags {
cmd.Flags().Set(flag, value)
}
// checkErr breaks tests in case of errors, plus we just
// need to check errors returned by the command validation
o := &LogsOptions{}
err = o.Complete(f, os.Stdout, cmd, test.args)
out = err.Error()
if !strings.Contains(out, test.expected) {
t.Errorf("%s: expected to find:\n\t%s\nfound:\n\t%s\n", test.name, test.expected, out)
}
}
}

View File

@ -40,7 +40,6 @@ import (
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/scheme"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/printers"
)
var patchTypes = map[string]types.PatchType{"json": types.JSONPatchType, "merge": types.MergePatchType, "strategic": types.StrategicMergePatchType}
@ -82,20 +81,11 @@ var (
func NewCmdPatch(f cmdutil.Factory, out io.Writer) *cobra.Command {
options := &PatchOptions{}
// retrieve a list of handled resources from printer as valid args
validArgs, argAliases := []string{}, []string{}
p, err := f.Printer(nil, printers.PrintOptions{
ColumnLabels: []string{},
})
cmdutil.CheckErr(err)
if p != nil {
validArgs = p.HandledResources()
argAliases = kubectl.ResourceAliases(validArgs)
}
validArgs := cmdutil.ValidArgList(f)
cmd := &cobra.Command{
Use: "patch (-f FILENAME | TYPE NAME) -p PATCH",
Use: "patch (-f FILENAME | TYPE NAME) -p PATCH",
DisableFlagsInUseLine: true,
Short: i18n.T("Update field(s) of a resource using strategic merge patch"),
Long: patchLong,
Example: patchExample,
@ -105,7 +95,7 @@ func NewCmdPatch(f cmdutil.Factory, out io.Writer) *cobra.Command {
cmdutil.CheckErr(err)
},
ValidArgs: validArgs,
ArgAliases: argAliases,
ArgAliases: kubectl.ResourceAliases(validArgs),
}
cmd.Flags().StringP("patch", "p", "", "The patch to be applied to the resource JSON file.")
cmd.MarkFlagRequired("patch")
@ -117,7 +107,7 @@ func NewCmdPatch(f cmdutil.Factory, out io.Writer) *cobra.Command {
usage := "identifying the resource to update"
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
cmd.Flags().BoolVar(&options.Local, "local", false, "If true, patch will operate on the content of the file, not the server-side resource.")
cmd.Flags().BoolVar(&options.Local, "local", options.Local, "If true, patch will operate on the content of the file, not the server-side resource.")
return cmd
}
@ -220,9 +210,9 @@ func RunPatch(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []strin
}
if len(options.OutputFormat) > 0 && options.OutputFormat != "name" {
return f.PrintResourceInfoForCommand(cmd, info, out)
return cmdutil.PrintObject(cmd, info.Object, out)
}
f.PrintSuccess(r.Mapper().RESTMapper, options.OutputFormat == "name", out, info.Mapping.Resource, info.Name, false, dataChangedMsg)
cmdutil.PrintSuccess(options.OutputFormat == "name", out, info.Object, false, dataChangedMsg)
// if object was not successfully patched, exit with error code 1
if !didPatch {
@ -256,7 +246,7 @@ func RunPatch(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []strin
if err := info.Refresh(targetObj, true); err != nil {
return err
}
return f.PrintResourceInfoForCommand(cmd, info, out)
return cmdutil.PrintObject(cmd, info.Object, out)
})
if err != nil {
return err

View File

@ -23,14 +23,17 @@ import (
"testing"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/printers"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
func TestPatchObject(t *testing.T) {
_, svc, _ := testData()
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -53,7 +56,7 @@ func TestPatchObject(t *testing.T) {
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdPatch(f, buf)
cmd := NewCmdPatch(tf, buf)
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("patch", `{"spec":{"type":"NodePort"}}`)
cmd.Flags().Set("output", "name")
@ -68,7 +71,9 @@ func TestPatchObject(t *testing.T) {
func TestPatchObjectFromFile(t *testing.T) {
_, svc, _ := testData()
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -84,7 +89,7 @@ func TestPatchObjectFromFile(t *testing.T) {
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdPatch(f, buf)
cmd := NewCmdPatch(tf, buf)
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("patch", `{"spec":{"type":"NodePort"}}`)
cmd.Flags().Set("output", "name")
@ -102,7 +107,9 @@ func TestPatchNoop(t *testing.T) {
getObject := &svc.Items[0]
patchObject := &svc.Items[0]
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -127,7 +134,7 @@ func TestPatchNoop(t *testing.T) {
}
patchObject.Annotations["foo"] = "bar"
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdPatch(f, buf)
cmd := NewCmdPatch(tf, buf)
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("patch", `{"metadata":{"annotations":{"foo":"bar"}}}`)
cmd.Run(cmd, []string{"services", "frontend"})
@ -146,8 +153,9 @@ func TestPatchObjectFromFileOutput(t *testing.T) {
}
svcCopy.Labels["post-patch"] = "post-patch-value"
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf.Printer = &printers.YAMLPrinter{}
tf := cmdtesting.NewTestFactory()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -165,7 +173,7 @@ func TestPatchObjectFromFileOutput(t *testing.T) {
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdPatch(f, buf)
cmd := NewCmdPatch(tf, buf)
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("patch", `{"spec":{"type":"NodePort"}}`)
cmd.Flags().Set("output", "yaml")

Some files were not shown because too many files have changed in this diff Show More