Replace godep with dep
This commit is contained in:
parent
1e7489927c
commit
bf5616c65b
14883 changed files with 3937406 additions and 361781 deletions
202
vendor/k8s.io/kubernetes/pkg/kubectl/BUILD
generated
vendored
Normal file
202
vendor/k8s.io/kubernetes/pkg/kubectl/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"cluster_test.go",
|
||||
"configmap_test.go",
|
||||
"delete_test.go",
|
||||
"env_file_test.go",
|
||||
"generate_test.go",
|
||||
"kubectl_test.go",
|
||||
"namespace_test.go",
|
||||
"quota_test.go",
|
||||
"resource_filter_test.go",
|
||||
"rolebinding_test.go",
|
||||
"rolling_updater_test.go",
|
||||
"rollout_status_test.go",
|
||||
"run_test.go",
|
||||
"scale_test.go",
|
||||
"secret_for_docker_registry_test.go",
|
||||
"secret_for_tls_test.go",
|
||||
"secret_test.go",
|
||||
"service_basic_test.go",
|
||||
"service_test.go",
|
||||
"serviceaccount_test.go",
|
||||
"sorting_printer_test.go",
|
||||
],
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//federation/apis/federation/v1beta1:go_default_library",
|
||||
"//pkg/api: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/extensions:go_default_library",
|
||||
"//pkg/apis/rbac: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/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/kubectl/util:go_default_library",
|
||||
"//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/batch/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/batch/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/batch/v2alpha1:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors: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/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/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/util/testing:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"apply.go",
|
||||
"autoscale.go",
|
||||
"bash_comp_utils.go",
|
||||
"cluster.go",
|
||||
"clusterrolebinding.go",
|
||||
"configmap.go",
|
||||
"delete.go",
|
||||
"deployment.go",
|
||||
"doc.go",
|
||||
"env_file.go",
|
||||
"explain.go",
|
||||
"generate.go",
|
||||
"history.go",
|
||||
"interfaces.go",
|
||||
"kubectl.go",
|
||||
"namespace.go",
|
||||
"pdb.go",
|
||||
"quota.go",
|
||||
"resource_filter.go",
|
||||
"rolebinding.go",
|
||||
"rollback.go",
|
||||
"rolling_updater.go",
|
||||
"rollout_status.go",
|
||||
"run.go",
|
||||
"scale.go",
|
||||
"secret.go",
|
||||
"secret_for_docker_registry.go",
|
||||
"secret_for_tls.go",
|
||||
"service.go",
|
||||
"service_basic.go",
|
||||
"serviceaccount.go",
|
||||
"sorting_printer.go",
|
||||
"versioned_client.go",
|
||||
],
|
||||
deps = [
|
||||
"//federation/apis/federation/v1beta1:go_default_library",
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/util:go_default_library",
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/api/v1/pod:go_default_library",
|
||||
"//pkg/apis/apps:go_default_library",
|
||||
"//pkg/apis/autoscaling:go_default_library",
|
||||
"//pkg/apis/batch:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//pkg/apis/policy:go_default_library",
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset: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",
|
||||
"//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",
|
||||
"//pkg/credentialprovider:go_default_library",
|
||||
"//pkg/kubectl/resource:go_default_library",
|
||||
"//pkg/kubectl/util:go_default_library",
|
||||
"//pkg/kubectl/util/hash:go_default_library",
|
||||
"//pkg/kubectl/util/slice:go_default_library",
|
||||
"//pkg/printers:go_default_library",
|
||||
"//pkg/printers/internalversion:go_default_library",
|
||||
"//vendor/github.com/emicklei/go-restful-swagger12:go_default_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/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/batch/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/batch/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/batch/v2alpha1:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/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/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",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/json:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/validation:go_default_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/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/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",
|
||||
"//vendor/vbom.ml/util/sortorder:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/kubectl/apps:all-srcs",
|
||||
"//pkg/kubectl/cmd:all-srcs",
|
||||
"//pkg/kubectl/metricsutil:all-srcs",
|
||||
"//pkg/kubectl/plugins:all-srcs",
|
||||
"//pkg/kubectl/proxy:all-srcs",
|
||||
"//pkg/kubectl/resource:all-srcs",
|
||||
"//pkg/kubectl/testing:all-srcs",
|
||||
"//pkg/kubectl/util:all-srcs",
|
||||
"//pkg/kubectl/validation:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
4
vendor/k8s.io/kubernetes/pkg/kubectl/OWNERS
generated
vendored
Normal file
4
vendor/k8s.io/kubernetes/pkg/kubectl/OWNERS
generated
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
approvers:
|
||||
- sig-cli-maintainers
|
||||
reviewers:
|
||||
- sig-cli
|
||||
184
vendor/k8s.io/kubernetes/pkg/kubectl/apply.go
generated
vendored
Normal file
184
vendor/k8s.io/kubernetes/pkg/kubectl/apply.go
generated
vendored
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
Copyright 2014 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 kubectl
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/kubectl/resource"
|
||||
)
|
||||
|
||||
// GetOriginalConfiguration retrieves the original configuration of the object
|
||||
// from the annotation, or nil if no annotation was found.
|
||||
func GetOriginalConfiguration(mapping *meta.RESTMapping, obj runtime.Object) ([]byte, error) {
|
||||
annots, err := mapping.MetadataAccessor.Annotations(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if annots == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
original, ok := annots[api.LastAppliedConfigAnnotation]
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return []byte(original), nil
|
||||
}
|
||||
|
||||
// SetOriginalConfiguration sets the original configuration of the object
|
||||
// as the annotation on the object for later use in computing a three way patch.
|
||||
func SetOriginalConfiguration(info *resource.Info, original []byte) error {
|
||||
if len(original) < 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
accessor := info.Mapping.MetadataAccessor
|
||||
annots, err := accessor.Annotations(info.Object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if annots == nil {
|
||||
annots = map[string]string{}
|
||||
}
|
||||
|
||||
annots[api.LastAppliedConfigAnnotation] = string(original)
|
||||
return info.Mapping.MetadataAccessor.SetAnnotations(info.Object, annots)
|
||||
}
|
||||
|
||||
// GetModifiedConfiguration retrieves the modified configuration of the object.
|
||||
// If annotate is true, it embeds the result as an annotation in the modified
|
||||
// configuration. If an object was read from the command input, it will use that
|
||||
// version of the object. Otherwise, it will use the version from the server.
|
||||
func GetModifiedConfiguration(info *resource.Info, annotate bool, codec runtime.Encoder) ([]byte, error) {
|
||||
// First serialize the object without the annotation to prevent recursion,
|
||||
// then add that serialization to it as the annotation and serialize it again.
|
||||
var modified []byte
|
||||
if info.VersionedObject != nil {
|
||||
// If an object was read from input, use that version.
|
||||
accessor, err := meta.Accessor(info.VersionedObject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get the current annotations from the object.
|
||||
annots := accessor.GetAnnotations()
|
||||
if annots == nil {
|
||||
annots = map[string]string{}
|
||||
}
|
||||
|
||||
original := annots[api.LastAppliedConfigAnnotation]
|
||||
delete(annots, api.LastAppliedConfigAnnotation)
|
||||
accessor.SetAnnotations(annots)
|
||||
// TODO: this needs to be abstracted - there should be no assumption that versioned object
|
||||
// can be marshalled to JSON.
|
||||
modified, err = runtime.Encode(codec, info.VersionedObject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if annotate {
|
||||
annots[api.LastAppliedConfigAnnotation] = string(modified)
|
||||
accessor.SetAnnotations(annots)
|
||||
// TODO: this needs to be abstracted - there should be no assumption that versioned object
|
||||
// can be marshalled to JSON.
|
||||
modified, err = runtime.Encode(codec, info.VersionedObject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the object to its original condition.
|
||||
annots[api.LastAppliedConfigAnnotation] = original
|
||||
accessor.SetAnnotations(annots)
|
||||
} else {
|
||||
// Otherwise, use the server side version of the object.
|
||||
accessor := info.Mapping.MetadataAccessor
|
||||
// Get the current annotations from the object.
|
||||
annots, err := accessor.Annotations(info.Object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if annots == nil {
|
||||
annots = map[string]string{}
|
||||
}
|
||||
|
||||
original := annots[api.LastAppliedConfigAnnotation]
|
||||
delete(annots, api.LastAppliedConfigAnnotation)
|
||||
if err := accessor.SetAnnotations(info.Object, annots); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
modified, err = runtime.Encode(codec, info.Object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if annotate {
|
||||
annots[api.LastAppliedConfigAnnotation] = string(modified)
|
||||
if err := info.Mapping.MetadataAccessor.SetAnnotations(info.Object, annots); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
modified, err = runtime.Encode(codec, info.Object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the object to its original condition.
|
||||
annots[api.LastAppliedConfigAnnotation] = original
|
||||
if err := info.Mapping.MetadataAccessor.SetAnnotations(info.Object, annots); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return modified, nil
|
||||
}
|
||||
|
||||
// UpdateApplyAnnotation calls CreateApplyAnnotation if the last applied
|
||||
// configuration annotation is already present. Otherwise, it does nothing.
|
||||
func UpdateApplyAnnotation(info *resource.Info, codec runtime.Encoder) error {
|
||||
if original, err := GetOriginalConfiguration(info.Mapping, info.Object); err != nil || len(original) <= 0 {
|
||||
return err
|
||||
}
|
||||
return CreateApplyAnnotation(info, codec)
|
||||
}
|
||||
|
||||
// CreateApplyAnnotation gets the modified configuration of the object,
|
||||
// without embedding it again, and then sets it on the object as the annotation.
|
||||
func CreateApplyAnnotation(info *resource.Info, codec runtime.Encoder) error {
|
||||
modified, err := GetModifiedConfiguration(info, false, codec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return SetOriginalConfiguration(info, modified)
|
||||
}
|
||||
|
||||
// Create the annotation used by kubectl apply only when createAnnotation is true
|
||||
// Otherwise, only update the annotation when it already exists
|
||||
func CreateOrUpdateAnnotation(createAnnotation bool, info *resource.Info, codec runtime.Encoder) error {
|
||||
if createAnnotation {
|
||||
return CreateApplyAnnotation(info, codec)
|
||||
}
|
||||
return UpdateApplyAnnotation(info, codec)
|
||||
}
|
||||
44
vendor/k8s.io/kubernetes/pkg/kubectl/apps/BUILD
generated
vendored
Normal file
44
vendor/k8s.io/kubernetes/pkg/kubectl/apps/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["kind_visitor.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = ["//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_xtest",
|
||||
srcs = [
|
||||
"apps_suite_test.go",
|
||||
"kind_visitor_test.go",
|
||||
],
|
||||
deps = [
|
||||
":go_default_library",
|
||||
"//vendor/github.com/onsi/ginkgo:go_default_library",
|
||||
"//vendor/github.com/onsi/ginkgo/config:go_default_library",
|
||||
"//vendor/github.com/onsi/ginkgo/types:go_default_library",
|
||||
"//vendor/github.com/onsi/gomega:go_default_library",
|
||||
],
|
||||
)
|
||||
49
vendor/k8s.io/kubernetes/pkg/kubectl/apps/apps_suite_test.go
generated
vendored
Normal file
49
vendor/k8s.io/kubernetes/pkg/kubectl/apps/apps_suite_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apps_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/config"
|
||||
. "github.com/onsi/ginkgo/types"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestApps(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecsWithDefaultAndCustomReporters(t, "Apps Suite", []Reporter{newlineReporter{}})
|
||||
}
|
||||
|
||||
// Print a newline after the default newlineReporter due to issue
|
||||
// https://github.com/jstemmer/go-junit-report/issues/31
|
||||
type newlineReporter struct{}
|
||||
|
||||
func (newlineReporter) SpecSuiteWillBegin(config GinkgoConfigType, summary *SuiteSummary) {}
|
||||
|
||||
func (newlineReporter) BeforeSuiteDidRun(setupSummary *SetupSummary) {}
|
||||
|
||||
func (newlineReporter) AfterSuiteDidRun(setupSummary *SetupSummary) {}
|
||||
|
||||
func (newlineReporter) SpecWillRun(specSummary *SpecSummary) {}
|
||||
|
||||
func (newlineReporter) SpecDidComplete(specSummary *SpecSummary) {}
|
||||
|
||||
// SpecSuiteDidEnd Prints a newline between "35 Passed | 0 Failed | 0 Pending | 0 Skipped" and "--- PASS:"
|
||||
func (newlineReporter) SpecSuiteDidEnd(summary *SuiteSummary) { fmt.Printf("\n") }
|
||||
95
vendor/k8s.io/kubernetes/pkg/kubectl/apps/kind_visitor.go
generated
vendored
Normal file
95
vendor/k8s.io/kubernetes/pkg/kubectl/apps/kind_visitor.go
generated
vendored
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apps
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// KindVisitor is used with GroupKindElement to call a particular function depending on the
|
||||
// Kind of a schema.GroupKind
|
||||
type KindVisitor interface {
|
||||
VisitDaemonSet(kind GroupKindElement)
|
||||
VisitDeployment(kind GroupKindElement)
|
||||
VisitJob(kind GroupKindElement)
|
||||
VisitPod(kind GroupKindElement)
|
||||
VisitReplicaSet(kind GroupKindElement)
|
||||
VisitReplicationController(kind GroupKindElement)
|
||||
VisitStatefulSet(kind GroupKindElement)
|
||||
}
|
||||
|
||||
// GroupKindElement defines a Kubernetes API group elem
|
||||
type GroupKindElement schema.GroupKind
|
||||
|
||||
// Accept calls the Visit method on visitor that corresponds to elem's Kind
|
||||
func (elem GroupKindElement) Accept(visitor KindVisitor) error {
|
||||
if elem.GroupMatch("apps", "extensions") && elem.Kind == "DaemonSet" {
|
||||
visitor.VisitDaemonSet(elem)
|
||||
return nil
|
||||
}
|
||||
if elem.GroupMatch("apps", "extensions") && elem.Kind == "Deployment" {
|
||||
visitor.VisitDeployment(elem)
|
||||
return nil
|
||||
}
|
||||
if elem.GroupMatch("batch") && elem.Kind == "Job" {
|
||||
visitor.VisitJob(elem)
|
||||
return nil
|
||||
}
|
||||
if elem.GroupMatch("", "core") && elem.Kind == "Pod" {
|
||||
visitor.VisitPod(elem)
|
||||
return nil
|
||||
}
|
||||
if elem.GroupMatch("extensions") && elem.Kind == "ReplicaSet" {
|
||||
visitor.VisitReplicaSet(elem)
|
||||
return nil
|
||||
}
|
||||
if elem.GroupMatch("", "core") && elem.Kind == "ReplicationController" {
|
||||
visitor.VisitReplicationController(elem)
|
||||
return nil
|
||||
}
|
||||
if elem.GroupMatch("apps") && elem.Kind == "StatefulSet" {
|
||||
visitor.VisitStatefulSet(elem)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("no visitor method exists for %v", elem)
|
||||
}
|
||||
|
||||
// GroupMatch returns true if and only if elem's group matches one
|
||||
// of the group arguments
|
||||
func (elem GroupKindElement) GroupMatch(groups ...string) bool {
|
||||
for _, g := range groups {
|
||||
if elem.Group == g {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NoOpKindVisitor implements KindVisitor with no-op functions.
|
||||
type NoOpKindVisitor struct{}
|
||||
|
||||
var _ KindVisitor = &NoOpKindVisitor{}
|
||||
|
||||
func (*NoOpKindVisitor) VisitDaemonSet(kind GroupKindElement) {}
|
||||
func (*NoOpKindVisitor) VisitDeployment(kind GroupKindElement) {}
|
||||
func (*NoOpKindVisitor) VisitJob(kind GroupKindElement) {}
|
||||
func (*NoOpKindVisitor) VisitPod(kind GroupKindElement) {}
|
||||
func (*NoOpKindVisitor) VisitReplicaSet(kind GroupKindElement) {}
|
||||
func (*NoOpKindVisitor) VisitReplicationController(kind GroupKindElement) {}
|
||||
func (*NoOpKindVisitor) VisitStatefulSet(kind GroupKindElement) {}
|
||||
173
vendor/k8s.io/kubernetes/pkg/kubectl/apps/kind_visitor_test.go
generated
vendored
Normal file
173
vendor/k8s.io/kubernetes/pkg/kubectl/apps/kind_visitor_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apps_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/apps"
|
||||
)
|
||||
|
||||
var _ = Describe("When KindVisitor accepts a GroupKind", func() {
|
||||
|
||||
var visitor *TestKindVisitor
|
||||
|
||||
BeforeEach(func() {
|
||||
visitor = &TestKindVisitor{map[string]int{}}
|
||||
})
|
||||
|
||||
It("should Visit DaemonSet iff the Kind is a DaemonSet", func() {
|
||||
kind := apps.GroupKindElement{
|
||||
Kind: "DaemonSet",
|
||||
Group: "apps",
|
||||
}
|
||||
Expect(kind.Accept(visitor)).ShouldNot(HaveOccurred())
|
||||
Expect(visitor.visits).To(Equal(map[string]int{
|
||||
"DaemonSet": 1,
|
||||
}))
|
||||
|
||||
kind = apps.GroupKindElement{
|
||||
Kind: "DaemonSet",
|
||||
Group: "extensions",
|
||||
}
|
||||
Expect(kind.Accept(visitor)).ShouldNot(HaveOccurred())
|
||||
Expect(visitor.visits).To(Equal(map[string]int{
|
||||
"DaemonSet": 2,
|
||||
}))
|
||||
})
|
||||
|
||||
It("should Visit Deployment iff the Kind is a Deployment", func() {
|
||||
kind := apps.GroupKindElement{
|
||||
Kind: "Deployment",
|
||||
Group: "apps",
|
||||
}
|
||||
Expect(kind.Accept(visitor)).ShouldNot(HaveOccurred())
|
||||
Expect(visitor.visits).To(Equal(map[string]int{
|
||||
"Deployment": 1,
|
||||
}))
|
||||
|
||||
kind = apps.GroupKindElement{
|
||||
Kind: "Deployment",
|
||||
Group: "extensions",
|
||||
}
|
||||
Expect(kind.Accept(visitor)).ShouldNot(HaveOccurred())
|
||||
Expect(visitor.visits).To(Equal(map[string]int{
|
||||
"Deployment": 2,
|
||||
}))
|
||||
})
|
||||
|
||||
It("should Visit Job iff the Kind is a Job", func() {
|
||||
kind := apps.GroupKindElement{
|
||||
Kind: "Job",
|
||||
Group: "batch",
|
||||
}
|
||||
Expect(kind.Accept(visitor)).ShouldNot(HaveOccurred())
|
||||
Expect(visitor.visits).To(Equal(map[string]int{
|
||||
"Job": 1,
|
||||
}))
|
||||
|
||||
})
|
||||
|
||||
It("should Visit Pod iff the Kind is a Pod", func() {
|
||||
kind := apps.GroupKindElement{
|
||||
Kind: "Pod",
|
||||
Group: "",
|
||||
}
|
||||
Expect(kind.Accept(visitor)).ShouldNot(HaveOccurred())
|
||||
Expect(visitor.visits).To(Equal(map[string]int{
|
||||
"Pod": 1,
|
||||
}))
|
||||
|
||||
kind = apps.GroupKindElement{
|
||||
Kind: "Pod",
|
||||
Group: "core",
|
||||
}
|
||||
Expect(kind.Accept(visitor)).ShouldNot(HaveOccurred())
|
||||
Expect(visitor.visits).To(Equal(map[string]int{
|
||||
"Pod": 2,
|
||||
}))
|
||||
})
|
||||
|
||||
It("should Visit ReplicationController iff the Kind is a ReplicationController", func() {
|
||||
kind := apps.GroupKindElement{
|
||||
Kind: "ReplicationController",
|
||||
Group: "",
|
||||
}
|
||||
Expect(kind.Accept(visitor)).ShouldNot(HaveOccurred())
|
||||
Expect(visitor.visits).To(Equal(map[string]int{
|
||||
"ReplicationController": 1,
|
||||
}))
|
||||
|
||||
kind = apps.GroupKindElement{
|
||||
Kind: "ReplicationController",
|
||||
Group: "core",
|
||||
}
|
||||
Expect(kind.Accept(visitor)).ShouldNot(HaveOccurred())
|
||||
Expect(visitor.visits).To(Equal(map[string]int{
|
||||
"ReplicationController": 2,
|
||||
}))
|
||||
})
|
||||
|
||||
It("should Visit ReplicaSet iff the Kind is a ReplicaSet", func() {
|
||||
kind := apps.GroupKindElement{
|
||||
Kind: "ReplicaSet",
|
||||
Group: "extensions",
|
||||
}
|
||||
Expect(kind.Accept(visitor)).ShouldNot(HaveOccurred())
|
||||
Expect(visitor.visits).To(Equal(map[string]int{
|
||||
"ReplicaSet": 1,
|
||||
}))
|
||||
})
|
||||
|
||||
It("should Visit StatefulSet iff the Kind is a StatefulSet", func() {
|
||||
kind := apps.GroupKindElement{
|
||||
Kind: "StatefulSet",
|
||||
Group: "apps",
|
||||
}
|
||||
Expect(kind.Accept(visitor)).ShouldNot(HaveOccurred())
|
||||
Expect(visitor.visits).To(Equal(map[string]int{
|
||||
"StatefulSet": 1,
|
||||
}))
|
||||
})
|
||||
|
||||
It("should give an error if the Kind is unknown", func() {
|
||||
kind := apps.GroupKindElement{
|
||||
Kind: "Unknown",
|
||||
Group: "apps",
|
||||
}
|
||||
Expect(kind.Accept(visitor)).Should(HaveOccurred())
|
||||
Expect(visitor.visits).To(Equal(map[string]int{}))
|
||||
})
|
||||
})
|
||||
|
||||
// TestKindVisitor increments a value each time a Visit method was called
|
||||
type TestKindVisitor struct {
|
||||
visits map[string]int
|
||||
}
|
||||
|
||||
var _ apps.KindVisitor = &TestKindVisitor{}
|
||||
|
||||
func (t *TestKindVisitor) Visit(kind apps.GroupKindElement) { t.visits[kind.Kind] += 1 }
|
||||
|
||||
func (t *TestKindVisitor) VisitDaemonSet(kind apps.GroupKindElement) { t.Visit(kind) }
|
||||
func (t *TestKindVisitor) VisitDeployment(kind apps.GroupKindElement) { t.Visit(kind) }
|
||||
func (t *TestKindVisitor) VisitJob(kind apps.GroupKindElement) { t.Visit(kind) }
|
||||
func (t *TestKindVisitor) VisitPod(kind apps.GroupKindElement) { t.Visit(kind) }
|
||||
func (t *TestKindVisitor) VisitReplicaSet(kind apps.GroupKindElement) { t.Visit(kind) }
|
||||
func (t *TestKindVisitor) VisitReplicationController(kind apps.GroupKindElement) { t.Visit(kind) }
|
||||
func (t *TestKindVisitor) VisitStatefulSet(kind apps.GroupKindElement) { t.Visit(kind) }
|
||||
124
vendor/k8s.io/kubernetes/pkg/kubectl/autoscale.go
generated
vendored
Normal file
124
vendor/k8s.io/kubernetes/pkg/kubectl/autoscale.go
generated
vendored
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
Copyright 2015 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 kubectl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/autoscaling"
|
||||
)
|
||||
|
||||
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},
|
||||
}
|
||||
}
|
||||
|
||||
func (HorizontalPodAutoscalerV1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
return generateHPA(genericParams)
|
||||
}
|
||||
|
||||
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 {
|
||||
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 := autoscaling.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: autoscaling.HorizontalPodAutoscalerSpec{
|
||||
ScaleTargetRef: autoscaling.CrossVersionObjectReference{
|
||||
Kind: params["scaleRef-kind"],
|
||||
Name: params["scaleRef-name"],
|
||||
APIVersion: params["scaleRef-apiVersion"],
|
||||
},
|
||||
MaxReplicas: int32(max),
|
||||
},
|
||||
}
|
||||
if min > 0 {
|
||||
v := int32(min)
|
||||
scaler.Spec.MinReplicas = &v
|
||||
}
|
||||
if cpu >= 0 {
|
||||
c := int32(cpu)
|
||||
scaler.Spec.Metrics = []autoscaling.MetricSpec{
|
||||
{
|
||||
Type: autoscaling.ResourceMetricSourceType,
|
||||
Resource: &autoscaling.ResourceMetricSource{
|
||||
Name: api.ResourceCPU,
|
||||
TargetAverageUtilization: &c,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
return &scaler, nil
|
||||
}
|
||||
36
vendor/k8s.io/kubernetes/pkg/kubectl/bash_comp_utils.go
generated
vendored
Normal file
36
vendor/k8s.io/kubernetes/pkg/kubectl/bash_comp_utils.go
generated
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
Copyright 2015 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.
|
||||
*/
|
||||
|
||||
// A set of common functions needed by cmd/kubectl and pkg/kubectl packages.
|
||||
|
||||
package kubectl
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/resource"
|
||||
)
|
||||
|
||||
func AddJsonFilenameFlag(cmd *cobra.Command, value *[]string, usage string) {
|
||||
cmd.Flags().StringSliceVarP(value, "filename", "f", *value, usage)
|
||||
annotations := make([]string, 0, len(resource.FileExtensions))
|
||||
for _, ext := range resource.FileExtensions {
|
||||
annotations = append(annotations, strings.TrimLeft(ext, "."))
|
||||
}
|
||||
cmd.Flags().SetAnnotation("filename", cobra.BashCompFilenameExt, annotations)
|
||||
}
|
||||
159
vendor/k8s.io/kubernetes/pkg/kubectl/cluster.go
generated
vendored
Normal file
159
vendor/k8s.io/kubernetes/pkg/kubectl/cluster.go
generated
vendored
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
Copyright 2016 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 kubectl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1"
|
||||
)
|
||||
|
||||
const (
|
||||
ServiceAccountNameAnnotation = "federation.kubernetes.io/servive-account-name"
|
||||
ClusterRoleNameAnnotation = "federation.kubernetes.io/cluster-role-name"
|
||||
)
|
||||
|
||||
// ClusterGeneratorV1Beta1 supports stable generation of a
|
||||
// federation/cluster resource.
|
||||
type ClusterGeneratorV1Beta1 struct {
|
||||
// Name of the cluster context (required)
|
||||
Name string
|
||||
// ClientCIDR is the CIDR range in which the Kubernetes APIServer
|
||||
// is available for the client (optional)
|
||||
ClientCIDR string
|
||||
// ServerAddress is the APIServer address of the Kubernetes cluster
|
||||
// that is being registered (required)
|
||||
ServerAddress string
|
||||
// SecretName is the name of the secret that stores the credentials
|
||||
// for the Kubernetes cluster that is being registered (optional)
|
||||
SecretName string
|
||||
// ServiceAccountName is the name of the service account that is
|
||||
// created in the cluster being registered. If this is provided,
|
||||
// then ClusterRoleName must also be provided (optional)
|
||||
ServiceAccountName string
|
||||
// ClusterRoleName is the name of the cluster role and cluster role
|
||||
// binding that are created in the cluster being registered. If this
|
||||
// is provided, then ServiceAccountName must also be provided
|
||||
// (optional)
|
||||
ClusterRoleName string
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameter
|
||||
// injection.
|
||||
var _ Generator = &ClusterGeneratorV1Beta1{}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters
|
||||
// specified during construction.
|
||||
var _ StructuredGenerator = &ClusterGeneratorV1Beta1{}
|
||||
|
||||
// Generate returns a cluster resource using the specified parameters.
|
||||
func (s ClusterGeneratorV1Beta1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
err := ValidateParams(s.ParamNames(), genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clustergen := &ClusterGeneratorV1Beta1{}
|
||||
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
|
||||
}
|
||||
clustergen.Name = params["name"]
|
||||
clustergen.ClientCIDR = params["client-cidr"]
|
||||
clustergen.ServerAddress = params["server-address"]
|
||||
clustergen.SecretName = params["secret"]
|
||||
clustergen.ServiceAccountName = params["service-account-name"]
|
||||
clustergen.ClusterRoleName = params["cluster-role-name"]
|
||||
return clustergen.StructuredGenerate()
|
||||
}
|
||||
|
||||
// ParamNames returns the set of supported input parameters when using
|
||||
// the parameter injection generator pattern.
|
||||
func (s ClusterGeneratorV1Beta1) ParamNames() []GeneratorParam {
|
||||
return []GeneratorParam{
|
||||
{"name", true},
|
||||
{"client-cidr", false},
|
||||
{"server-address", true},
|
||||
{"secret", false},
|
||||
{"service-account-name", false},
|
||||
{"cluster-role-name", false},
|
||||
}
|
||||
}
|
||||
|
||||
// StructuredGenerate outputs a federation cluster resource object
|
||||
// using the configured fields.
|
||||
func (s ClusterGeneratorV1Beta1) StructuredGenerate() (runtime.Object, error) {
|
||||
if err := s.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if s.ClientCIDR == "" {
|
||||
s.ClientCIDR = "0.0.0.0/0"
|
||||
}
|
||||
if s.SecretName == "" {
|
||||
s.SecretName = s.Name
|
||||
}
|
||||
cluster := &federationapi.Cluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: s.Name,
|
||||
},
|
||||
Spec: federationapi.ClusterSpec{
|
||||
ServerAddressByClientCIDRs: []federationapi.ServerAddressByClientCIDR{
|
||||
{
|
||||
ClientCIDR: s.ClientCIDR,
|
||||
ServerAddress: s.ServerAddress,
|
||||
},
|
||||
},
|
||||
SecretRef: &v1.LocalObjectReference{
|
||||
Name: s.SecretName,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
annotations := make(map[string]string)
|
||||
if s.ServiceAccountName != "" {
|
||||
annotations[ServiceAccountNameAnnotation] = s.ServiceAccountName
|
||||
}
|
||||
if s.ClusterRoleName != "" {
|
||||
annotations[ClusterRoleNameAnnotation] = s.ClusterRoleName
|
||||
}
|
||||
if len(annotations) == 1 {
|
||||
return nil, fmt.Errorf("Either both or neither of ServiceAccountName and ClusterRoleName must be provided.")
|
||||
}
|
||||
if len(annotations) > 0 {
|
||||
cluster.SetAnnotations(annotations)
|
||||
}
|
||||
|
||||
return cluster, nil
|
||||
}
|
||||
|
||||
// validate validates required fields are set to support structured
|
||||
// generation.
|
||||
func (s ClusterGeneratorV1Beta1) validate() error {
|
||||
if len(s.Name) == 0 {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
if len(s.ServerAddress) == 0 {
|
||||
return fmt.Errorf("server address must be specified")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
239
vendor/k8s.io/kubernetes/pkg/kubectl/cluster_test.go
generated
vendored
Normal file
239
vendor/k8s.io/kubernetes/pkg/kubectl/cluster_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
/*
|
||||
Copyright 2016 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 kubectl
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1"
|
||||
)
|
||||
|
||||
func TestClusterGenerate(t *testing.T) {
|
||||
tests := []struct {
|
||||
params map[string]interface{}
|
||||
expected *federationapi.Cluster
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"client-cidr": "0.0.0.0/0",
|
||||
"server-address": "10.20.30.40",
|
||||
"secret": "foo-credentials",
|
||||
},
|
||||
expected: &federationapi.Cluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: federationapi.ClusterSpec{
|
||||
ServerAddressByClientCIDRs: []federationapi.ServerAddressByClientCIDR{
|
||||
{
|
||||
ClientCIDR: "0.0.0.0/0",
|
||||
ServerAddress: "10.20.30.40",
|
||||
},
|
||||
},
|
||||
SecretRef: &v1.LocalObjectReference{
|
||||
Name: "foo-credentials",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"client-cidr": "10.20.30.40/16",
|
||||
"server-address": "https://foo.example.com",
|
||||
"secret": "foo-credentials",
|
||||
},
|
||||
expected: &federationapi.Cluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: federationapi.ClusterSpec{
|
||||
ServerAddressByClientCIDRs: []federationapi.ServerAddressByClientCIDR{
|
||||
{
|
||||
ClientCIDR: "10.20.30.40/16",
|
||||
ServerAddress: "https://foo.example.com",
|
||||
},
|
||||
},
|
||||
SecretRef: &v1.LocalObjectReference{
|
||||
Name: "foo-credentials",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
params: map[string]interface{}{
|
||||
"name": "bar-cluster",
|
||||
"client-cidr": "10.20.30.40/16",
|
||||
"server-address": "http://10.20.30.40",
|
||||
"secret": "credentials",
|
||||
},
|
||||
expected: &federationapi.Cluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "bar-cluster",
|
||||
},
|
||||
Spec: federationapi.ClusterSpec{
|
||||
ServerAddressByClientCIDRs: []federationapi.ServerAddressByClientCIDR{
|
||||
{
|
||||
ClientCIDR: "10.20.30.40/16",
|
||||
ServerAddress: "http://10.20.30.40",
|
||||
},
|
||||
},
|
||||
SecretRef: &v1.LocalObjectReference{
|
||||
Name: "credentials",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
params: map[string]interface{}{
|
||||
"name": "bar-cluster",
|
||||
"client-cidr": "10.20.30.40/16",
|
||||
"server-address": "http://10.20.30.40",
|
||||
"secret": "credentials",
|
||||
},
|
||||
expected: &federationapi.Cluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "bar-cluster",
|
||||
},
|
||||
Spec: federationapi.ClusterSpec{
|
||||
ServerAddressByClientCIDRs: []federationapi.ServerAddressByClientCIDR{
|
||||
{
|
||||
ClientCIDR: "10.20.30.40/16",
|
||||
ServerAddress: "http://10.20.30.40",
|
||||
},
|
||||
},
|
||||
SecretRef: &v1.LocalObjectReference{
|
||||
Name: "credentials",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
params: map[string]interface{}{
|
||||
"name": "bar-cluster",
|
||||
"client-cidr": "10.20.30.40/16",
|
||||
"server-address": "http://10.20.30.40",
|
||||
"secret": "credentials",
|
||||
"service-account-name": "service-account",
|
||||
"cluster-role-name": "cluster-role",
|
||||
},
|
||||
expected: &federationapi.Cluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "bar-cluster",
|
||||
Annotations: map[string]string{
|
||||
ServiceAccountNameAnnotation: "service-account",
|
||||
ClusterRoleNameAnnotation: "cluster-role",
|
||||
},
|
||||
},
|
||||
Spec: federationapi.ClusterSpec{
|
||||
ServerAddressByClientCIDRs: []federationapi.ServerAddressByClientCIDR{
|
||||
{
|
||||
ClientCIDR: "10.20.30.40/16",
|
||||
ServerAddress: "http://10.20.30.40",
|
||||
},
|
||||
},
|
||||
SecretRef: &v1.LocalObjectReference{
|
||||
Name: "credentials",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
params: map[string]interface{}{
|
||||
"server-address": "https://10.20.30.40",
|
||||
},
|
||||
expected: nil,
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
params: map[string]interface{}{
|
||||
"secret": "baz-credentials",
|
||||
},
|
||||
expected: nil,
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
params: map[string]interface{}{
|
||||
"server-address": "http://foo.example.com",
|
||||
"secret": "foo-credentials",
|
||||
},
|
||||
expected: nil,
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"secret": "foo-credentials",
|
||||
},
|
||||
expected: nil,
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"client-cidr": "10.20.30.40/16",
|
||||
},
|
||||
expected: nil,
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
params: map[string]interface{}{
|
||||
"name": "bar-cluster",
|
||||
"client-cidr": "10.20.30.40/16",
|
||||
"server-address": "http://10.20.30.40",
|
||||
"secret": "credentials",
|
||||
"cluster-role-name": "cluster-role",
|
||||
},
|
||||
expected: nil,
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
params: map[string]interface{}{
|
||||
"name": "bar-cluster",
|
||||
"client-cidr": "10.20.30.40/16",
|
||||
"server-address": "http://10.20.30.40",
|
||||
"secret": "credentials",
|
||||
"service-account-name": "service-account",
|
||||
},
|
||||
expected: nil,
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
generator := ClusterGeneratorV1Beta1{}
|
||||
for i, test := range tests {
|
||||
obj, err := generator.Generate(test.params)
|
||||
if !test.expectErr && err != nil {
|
||||
t.Errorf("[%d] unexpected error: %v", i, err)
|
||||
}
|
||||
if test.expectErr && err != nil {
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(obj.(*federationapi.Cluster), test.expected) {
|
||||
t.Errorf("\n[%d] want:\n%#v\n[%d] got:\n%#v", i, test.expected, i, obj.(*federationapi.Cluster))
|
||||
}
|
||||
}
|
||||
}
|
||||
158
vendor/k8s.io/kubernetes/pkg/kubectl/clusterrolebinding.go
generated
vendored
Normal file
158
vendor/k8s.io/kubernetes/pkg/kubectl/clusterrolebinding.go
generated
vendored
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
Copyright 2016 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 kubectl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
)
|
||||
|
||||
// ClusterRoleBindingGeneratorV1 supports stable generation of a clusterRoleBinding.
|
||||
type ClusterRoleBindingGeneratorV1 struct {
|
||||
// Name of clusterRoleBinding (required)
|
||||
Name string
|
||||
// ClusterRole for the clusterRoleBinding (required)
|
||||
ClusterRole string
|
||||
// Users to derive the clusterRoleBinding from (optional)
|
||||
Users []string
|
||||
// Groups to derive the clusterRoleBinding from (optional)
|
||||
Groups []string
|
||||
// ServiceAccounts to derive the clusterRoleBinding from in namespace:name format(optional)
|
||||
ServiceAccounts []string
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameter injection.
|
||||
var _ Generator = &ClusterRoleBindingGeneratorV1{}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters specified during construction.
|
||||
var _ StructuredGenerator = &ClusterRoleBindingGeneratorV1{}
|
||||
|
||||
// Generate returns a clusterRoleBinding using the specified parameters.
|
||||
func (s ClusterRoleBindingGeneratorV1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
err := ValidateParams(s.ParamNames(), genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
delegate := &ClusterRoleBindingGeneratorV1{}
|
||||
userStrings, found := genericParams["user"]
|
||||
if found {
|
||||
fromFileArray, isArray := userStrings.([]string)
|
||||
if !isArray {
|
||||
return nil, fmt.Errorf("expected []string, found :%v", userStrings)
|
||||
}
|
||||
delegate.Users = fromFileArray
|
||||
delete(genericParams, "user")
|
||||
}
|
||||
groupStrings, found := genericParams["group"]
|
||||
if found {
|
||||
fromLiteralArray, isArray := groupStrings.([]string)
|
||||
if !isArray {
|
||||
return nil, fmt.Errorf("expected []string, found :%v", groupStrings)
|
||||
}
|
||||
delegate.Groups = fromLiteralArray
|
||||
delete(genericParams, "group")
|
||||
}
|
||||
saStrings, found := genericParams["serviceaccount"]
|
||||
if found {
|
||||
fromLiteralArray, isArray := saStrings.([]string)
|
||||
if !isArray {
|
||||
return nil, fmt.Errorf("expected []string, found :%v", saStrings)
|
||||
}
|
||||
delegate.ServiceAccounts = fromLiteralArray
|
||||
delete(genericParams, "serviceaccount")
|
||||
}
|
||||
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
|
||||
}
|
||||
delegate.Name = params["name"]
|
||||
delegate.ClusterRole = params["clusterrole"]
|
||||
return delegate.StructuredGenerate()
|
||||
}
|
||||
|
||||
// ParamNames returns the set of supported input parameters when using the parameter injection generator pattern.
|
||||
func (s ClusterRoleBindingGeneratorV1) ParamNames() []GeneratorParam {
|
||||
return []GeneratorParam{
|
||||
{"name", true},
|
||||
{"clusterrole", false},
|
||||
{"user", false},
|
||||
{"group", false},
|
||||
{"serviceaccount", false},
|
||||
}
|
||||
}
|
||||
|
||||
// StructuredGenerate outputs a clusterRoleBinding object using the configured fields.
|
||||
func (s ClusterRoleBindingGeneratorV1) StructuredGenerate() (runtime.Object, error) {
|
||||
if err := s.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clusterRoleBinding := &rbac.ClusterRoleBinding{}
|
||||
clusterRoleBinding.Name = s.Name
|
||||
clusterRoleBinding.RoleRef = rbac.RoleRef{
|
||||
APIGroup: rbac.GroupName,
|
||||
Kind: "ClusterRole",
|
||||
Name: s.ClusterRole,
|
||||
}
|
||||
for _, user := range sets.NewString(s.Users...).List() {
|
||||
clusterRoleBinding.Subjects = append(clusterRoleBinding.Subjects, rbac.Subject{
|
||||
Kind: rbac.UserKind,
|
||||
APIGroup: rbac.GroupName,
|
||||
Name: user,
|
||||
})
|
||||
}
|
||||
for _, group := range sets.NewString(s.Groups...).List() {
|
||||
clusterRoleBinding.Subjects = append(clusterRoleBinding.Subjects, rbac.Subject{
|
||||
Kind: rbac.GroupKind,
|
||||
APIGroup: rbac.GroupName,
|
||||
Name: group,
|
||||
})
|
||||
}
|
||||
for _, sa := range sets.NewString(s.ServiceAccounts...).List() {
|
||||
tokens := strings.Split(sa, ":")
|
||||
if len(tokens) != 2 || tokens[0] == "" || tokens[1] == "" {
|
||||
return nil, fmt.Errorf("serviceaccount must be <namespace>:<name>")
|
||||
}
|
||||
clusterRoleBinding.Subjects = append(clusterRoleBinding.Subjects, rbac.Subject{
|
||||
Kind: rbac.ServiceAccountKind,
|
||||
APIGroup: "",
|
||||
Namespace: tokens[0],
|
||||
Name: tokens[1],
|
||||
})
|
||||
}
|
||||
|
||||
return clusterRoleBinding, nil
|
||||
}
|
||||
|
||||
// validate validates required fields are set to support structured generation.
|
||||
func (s ClusterRoleBindingGeneratorV1) validate() error {
|
||||
if len(s.Name) == 0 {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
if len(s.ClusterRole) == 0 {
|
||||
return fmt.Errorf("clusterrole must be specified")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
267
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/BUILD
generated
vendored
Normal file
267
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"alpha.go",
|
||||
"annotate.go",
|
||||
"apiversions.go",
|
||||
"apply.go",
|
||||
"apply_edit_last_applied.go",
|
||||
"apply_set_last_applied.go",
|
||||
"apply_view_last_applied.go",
|
||||
"attach.go",
|
||||
"autoscale.go",
|
||||
"certificates.go",
|
||||
"clusterinfo.go",
|
||||
"clusterinfo_dump.go",
|
||||
"cmd.go",
|
||||
"completion.go",
|
||||
"convert.go",
|
||||
"cp.go",
|
||||
"create.go",
|
||||
"create_clusterrole.go",
|
||||
"create_clusterrolebinding.go",
|
||||
"create_configmap.go",
|
||||
"create_deployment.go",
|
||||
"create_namespace.go",
|
||||
"create_pdb.go",
|
||||
"create_quota.go",
|
||||
"create_role.go",
|
||||
"create_rolebinding.go",
|
||||
"create_secret.go",
|
||||
"create_service.go",
|
||||
"create_serviceaccount.go",
|
||||
"delete.go",
|
||||
"describe.go",
|
||||
"drain.go",
|
||||
"edit.go",
|
||||
"exec.go",
|
||||
"explain.go",
|
||||
"expose.go",
|
||||
"get.go",
|
||||
"help.go",
|
||||
"label.go",
|
||||
"logs.go",
|
||||
"options.go",
|
||||
"patch.go",
|
||||
"plugin.go",
|
||||
"portforward.go",
|
||||
"proxy.go",
|
||||
"replace.go",
|
||||
"rollingupdate.go",
|
||||
"run.go",
|
||||
"scale.go",
|
||||
"taint.go",
|
||||
"top.go",
|
||||
"top_node.go",
|
||||
"top_pod.go",
|
||||
"version.go",
|
||||
],
|
||||
visibility = [
|
||||
"//build/visible_to:pkg_kubectl_cmd_CONSUMERS",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/validation:go_default_library",
|
||||
"//pkg/apis/certificates:go_default_library",
|
||||
"//pkg/apis/policy:go_default_library",
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion:go_default_library",
|
||||
"//pkg/client/unversioned:go_default_library",
|
||||
"//pkg/kubectl:go_default_library",
|
||||
"//pkg/kubectl/cmd/auth:go_default_library",
|
||||
"//pkg/kubectl/cmd/config:go_default_library",
|
||||
"//pkg/kubectl/cmd/rollout:go_default_library",
|
||||
"//pkg/kubectl/cmd/set:go_default_library",
|
||||
"//pkg/kubectl/cmd/templates:go_default_library",
|
||||
"//pkg/kubectl/cmd/util:go_default_library",
|
||||
"//pkg/kubectl/cmd/util/editor:go_default_library",
|
||||
"//pkg/kubectl/cmd/util/openapi:go_default_library",
|
||||
"//pkg/kubectl/metricsutil:go_default_library",
|
||||
"//pkg/kubectl/plugins:go_default_library",
|
||||
"//pkg/kubectl/proxy:go_default_library",
|
||||
"//pkg/kubectl/resource:go_default_library",
|
||||
"//pkg/kubectl/util:go_default_library",
|
||||
"//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/interrupt:go_default_library",
|
||||
"//pkg/util/taints:go_default_library",
|
||||
"//pkg/version:go_default_library",
|
||||
"//vendor/github.com/daviddengcn/go-colortext:go_default_library",
|
||||
"//vendor/github.com/docker/distribution/reference:go_default_library",
|
||||
"//vendor/github.com/docker/docker/pkg/term:go_default_library",
|
||||
"//vendor/github.com/evanphx/json-patch:go_default_library",
|
||||
"//vendor/github.com/ghodss/yaml:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/jonboulle/clockwork:go_default_library",
|
||||
"//vendor/github.com/renstrom/dedent:go_default_library",
|
||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||
"//vendor/github.com/spf13/pflag:go_default_library",
|
||||
"//vendor/k8s.io/api/apps/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/batch/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/batch/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/batch/v2alpha1:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/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",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/json:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/jsonmergepatch:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/mergepatch:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/validation:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/yaml:go_default_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/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/portforward:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/remotecommand:go_default_library",
|
||||
"//vendor/k8s.io/client-go/transport/spdy:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"annotate_test.go",
|
||||
"apply_test.go",
|
||||
"attach_test.go",
|
||||
"clusterinfo_dump_test.go",
|
||||
"cmd_test.go",
|
||||
"cp_test.go",
|
||||
"create_clusterrole_test.go",
|
||||
"create_configmap_test.go",
|
||||
"create_deployment_test.go",
|
||||
"create_namespace_test.go",
|
||||
"create_pdb_test.go",
|
||||
"create_quota_test.go",
|
||||
"create_role_test.go",
|
||||
"create_rolebinding_test.go",
|
||||
"create_secret_test.go",
|
||||
"create_service_test.go",
|
||||
"create_serviceaccount_test.go",
|
||||
"create_test.go",
|
||||
"delete_test.go",
|
||||
"describe_test.go",
|
||||
"drain_test.go",
|
||||
"edit_test.go",
|
||||
"exec_test.go",
|
||||
"expose_test.go",
|
||||
"get_test.go",
|
||||
"label_test.go",
|
||||
"logs_test.go",
|
||||
"patch_test.go",
|
||||
"plugin_test.go",
|
||||
"portforward_test.go",
|
||||
"replace_test.go",
|
||||
"rollingupdate_test.go",
|
||||
"run_test.go",
|
||||
"taint_test.go",
|
||||
"top_node_test.go",
|
||||
"top_pod_test.go",
|
||||
"top_test.go",
|
||||
],
|
||||
data = [
|
||||
"testdata",
|
||||
"//examples:config",
|
||||
"//test/fixtures",
|
||||
],
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/api: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/extensions:go_default_library",
|
||||
"//pkg/apis/policy:go_default_library",
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/kubectl: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",
|
||||
"//pkg/kubectl/plugins:go_default_library",
|
||||
"//pkg/kubectl/resource:go_default_library",
|
||||
"//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/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/core/v1: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/json:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/streaming:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/diff: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/strategicpatch:go_default_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/dynamic: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/rest/watch:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/remotecommand:go_default_library",
|
||||
"//vendor/k8s.io/metrics/pkg/apis/metrics/v1alpha1:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/kubectl/cmd/auth:all-srcs",
|
||||
"//pkg/kubectl/cmd/config:all-srcs",
|
||||
"//pkg/kubectl/cmd/rollout:all-srcs",
|
||||
"//pkg/kubectl/cmd/set:all-srcs",
|
||||
"//pkg/kubectl/cmd/templates:all-srcs",
|
||||
"//pkg/kubectl/cmd/testdata/edit:all-srcs",
|
||||
"//pkg/kubectl/cmd/testing:all-srcs",
|
||||
"//pkg/kubectl/cmd/util:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
visibility = [
|
||||
"//build/visible_to:pkg_kubectl_cmd_CONSUMERS",
|
||||
],
|
||||
)
|
||||
50
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/alpha.go
generated
vendored
Normal file
50
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/alpha.go
generated
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
// NewCmdAlpha creates a command that acts as an alternate root command for features in alpha
|
||||
func NewCmdAlpha(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "alpha",
|
||||
Short: i18n.T("Commands for features in alpha"),
|
||||
Long: templates.LongDesc(i18n.T("These commands correspond to alpha features that are not enabled in Kubernetes clusters by default.")),
|
||||
}
|
||||
|
||||
// Alpha commands should be added here. As features graduate from alpha they should move
|
||||
// from here to the CommandGroups defined by NewKubeletCommand() in cmd.go.
|
||||
//cmd.AddCommand(NewCmdDebug(f, in, out, err))
|
||||
|
||||
// NewKubeletCommand() will hide the alpha command if it has no subcommands. Overriding
|
||||
// the help function ensures a reasonable message if someone types the hidden command anyway.
|
||||
if !cmd.HasSubCommands() {
|
||||
cmd.SetHelpFunc(func(*cobra.Command, []string) {
|
||||
cmd.Println(i18n.T("No alpha commands are available in this version of kubectl"))
|
||||
})
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
371
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/annotate.go
generated
vendored
Normal file
371
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/annotate.go
generated
vendored
Normal file
|
|
@ -0,0 +1,371 @@
|
|||
/*
|
||||
Copyright 2014 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"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/json"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"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"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
)
|
||||
|
||||
// AnnotateOptions have the data required to perform the annotate operation
|
||||
type AnnotateOptions struct {
|
||||
// Filename options
|
||||
resource.FilenameOptions
|
||||
|
||||
// Common user flags
|
||||
overwrite bool
|
||||
local bool
|
||||
dryrun bool
|
||||
all bool
|
||||
resourceVersion string
|
||||
selector string
|
||||
outputFormat string
|
||||
recordChangeCause bool
|
||||
|
||||
// results of arg parsing
|
||||
resources []string
|
||||
newAnnotations map[string]string
|
||||
removeAnnotations []string
|
||||
|
||||
// Common share fields
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
var (
|
||||
annotateLong = templates.LongDesc(`
|
||||
Update the annotations on one or more resources.
|
||||
|
||||
* An annotation is a key/value pair that can hold larger (compared to a label), and possibly not human-readable, data.
|
||||
* It is intended to store non-identifying auxiliary data, especially data manipulated by tools and system extensions.
|
||||
* If --overwrite is true, then existing annotations can be overwritten, otherwise attempting to overwrite an annotation will result in an error.
|
||||
* If --resource-version is specified, then updates will use this resource version, otherwise the existing resource-version will be used.
|
||||
|
||||
` + validResources)
|
||||
|
||||
annotateExample = templates.Examples(i18n.T(`
|
||||
# Update pod 'foo' with the annotation 'description' and the value 'my frontend'.
|
||||
# If the same annotation is set multiple times, only the last value will be applied
|
||||
kubectl annotate pods foo description='my frontend'
|
||||
|
||||
# Update a pod identified by type and name in "pod.json"
|
||||
kubectl annotate -f pod.json description='my frontend'
|
||||
|
||||
# Update pod 'foo' with the annotation 'description' and the value 'my frontend running nginx', overwriting any existing value.
|
||||
kubectl annotate --overwrite pods foo description='my frontend running nginx'
|
||||
|
||||
# Update all pods in the namespace
|
||||
kubectl annotate pods --all description='my frontend running nginx'
|
||||
|
||||
# Update pod 'foo' only if the resource is unchanged from version 1.
|
||||
kubectl annotate pods foo description='my frontend running nginx' --resource-version=1
|
||||
|
||||
# Update pod 'foo' by removing an annotation named 'description' if it exists.
|
||||
# Does not require the --overwrite flag.
|
||||
kubectl annotate pods foo description-`))
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "annotate [--overwrite] (-f FILENAME | TYPE NAME) KEY_1=VAL_1 ... KEY_N=VAL_N [--resource-version=version]",
|
||||
Short: i18n.T("Update the annotations on a resource"),
|
||||
Long: annotateLong,
|
||||
Example: annotateExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if err := options.Complete(out, cmd, args); err != nil {
|
||||
cmdutil.CheckErr(cmdutil.UsageErrorf(cmd, "%v", err))
|
||||
}
|
||||
if err := options.Validate(); err != nil {
|
||||
cmdutil.CheckErr(cmdutil.UsageErrorf(cmd, "%v", err))
|
||||
}
|
||||
cmdutil.CheckErr(options.RunAnnotate(f, cmd))
|
||||
},
|
||||
ValidArgs: validArgs,
|
||||
ArgAliases: argAliases,
|
||||
}
|
||||
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."))
|
||||
usage := "identifying the resource to update the annotation"
|
||||
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
|
||||
cmdutil.AddDryRunFlag(cmd)
|
||||
cmdutil.AddRecordFlag(cmd)
|
||||
cmdutil.AddInclude3rdPartyFlags(cmd)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
// retrieves resource and annotation args from args
|
||||
// also checks args to verify that all resources are specified before annotations
|
||||
resources, annotationArgs, err := cmdutil.GetResourcesAndPairs(args, "annotation")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.resources = resources
|
||||
o.newAnnotations, o.removeAnnotations, err = parseAnnotations(annotationArgs)
|
||||
return err
|
||||
}
|
||||
|
||||
// Validate checks to the AnnotateOptions to see if there is sufficient information run the command.
|
||||
func (o AnnotateOptions) Validate() error {
|
||||
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>")
|
||||
}
|
||||
if len(o.newAnnotations) < 1 && len(o.removeAnnotations) < 1 {
|
||||
return fmt.Errorf("at least one annotation update is required")
|
||||
}
|
||||
return validateAnnotations(o.removeAnnotations, o.newAnnotations)
|
||||
}
|
||||
|
||||
// RunAnnotate does the work
|
||||
func (o AnnotateOptions) RunAnnotate(f cmdutil.Factory, cmd *cobra.Command) error {
|
||||
namespace, enforceNamespace, err := f.DefaultNamespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
changeCause := f.Command(cmd, false)
|
||||
|
||||
builder, err := f.NewUnstructuredBuilder(!o.local)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
|
||||
b := builder.
|
||||
ContinueOnError().
|
||||
NamespaceParam(namespace).DefaultNamespace().
|
||||
FilenameParam(enforceNamespace, &o.FilenameOptions).
|
||||
IncludeUninitialized(includeUninitialized).
|
||||
Flatten()
|
||||
|
||||
if !o.local {
|
||||
b = b.SelectorParam(o.selector).
|
||||
ResourceTypeOrNameArgs(o.all, o.resources...).
|
||||
Latest()
|
||||
}
|
||||
r := b.Do()
|
||||
if err := r.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var singleItemImpliedResource bool
|
||||
r.IntoSingleItemImplied(&singleItemImpliedResource)
|
||||
|
||||
// only apply resource version locking on a single resource.
|
||||
// we must perform this check after o.builder.Do() as
|
||||
// []o.resources can not not accurately return the proper number
|
||||
// of resources when they are not passed in "resource/name" format.
|
||||
if !singleItemImpliedResource && len(o.resourceVersion) > 0 {
|
||||
return fmt.Errorf("--resource-version may only be used with a single resource")
|
||||
}
|
||||
|
||||
return r.Visit(func(info *resource.Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var outputObj runtime.Object
|
||||
var obj runtime.Object
|
||||
|
||||
obj, err = info.Mapping.ConvertToVersion(info.Object, info.Mapping.GroupVersionKind.GroupVersion())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if o.dryrun || o.local {
|
||||
if err := o.updateAnnotations(obj); err != nil {
|
||||
return err
|
||||
}
|
||||
outputObj = obj
|
||||
} else {
|
||||
name, namespace := info.Name, info.Namespace
|
||||
oldData, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// If we should record change-cause, add it to new annotations
|
||||
if cmdutil.ContainsChangeCause(info) || o.recordChangeCause {
|
||||
o.newAnnotations[kubectl.ChangeCauseAnnotation] = changeCause
|
||||
}
|
||||
if err := o.updateAnnotations(obj); err != nil {
|
||||
return err
|
||||
}
|
||||
newData, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
patchBytes, err := jsonpatch.CreateMergePatch(oldData, newData)
|
||||
createdPatch := err == nil
|
||||
if err != nil {
|
||||
glog.V(2).Infof("couldn't compute patch: %v", err)
|
||||
}
|
||||
|
||||
mapping := info.ResourceMapping()
|
||||
client, err := f.UnstructuredClientForMapping(mapping)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
helper := resource.NewHelper(client, mapping)
|
||||
|
||||
if createdPatch {
|
||||
outputObj, err = helper.Patch(namespace, name, types.MergePatchType, patchBytes)
|
||||
} else {
|
||||
outputObj, err = helper.Replace(namespace, name, false, obj)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var mapper meta.RESTMapper
|
||||
if o.local {
|
||||
mapper, _ = f.Object()
|
||||
} else {
|
||||
mapper, _, err = f.UnstructuredObject()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(o.outputFormat) > 0 {
|
||||
return f.PrintObject(cmd, o.local, mapper, outputObj, o.out)
|
||||
}
|
||||
cmdutil.PrintSuccess(mapper, false, o.out, info.Mapping.Resource, info.Name, o.dryrun, "annotated")
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// parseAnnotations retrieves new and remove annotations from annotation args
|
||||
func parseAnnotations(annotationArgs []string) (map[string]string, []string, error) {
|
||||
return cmdutil.ParsePairs(annotationArgs, "annotation", true)
|
||||
}
|
||||
|
||||
// validateAnnotations checks the format of annotation args and checks removed annotations aren't in the new annotations map
|
||||
func validateAnnotations(removeAnnotations []string, newAnnotations map[string]string) error {
|
||||
var modifyRemoveBuf bytes.Buffer
|
||||
for _, removeAnnotation := range removeAnnotations {
|
||||
if _, found := newAnnotations[removeAnnotation]; found {
|
||||
if modifyRemoveBuf.Len() > 0 {
|
||||
modifyRemoveBuf.WriteString(", ")
|
||||
}
|
||||
modifyRemoveBuf.WriteString(fmt.Sprintf(removeAnnotation))
|
||||
}
|
||||
}
|
||||
if modifyRemoveBuf.Len() > 0 {
|
||||
return fmt.Errorf("can not both modify and remove the following annotation(s) in the same command: %s", modifyRemoveBuf.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateNoAnnotationOverwrites validates that when overwrite is false, to-be-updated annotations don't exist in the object annotation map (yet)
|
||||
func validateNoAnnotationOverwrites(accessor metav1.Object, annotations map[string]string) error {
|
||||
var buf bytes.Buffer
|
||||
for key := range annotations {
|
||||
// change-cause annotation can always be overwritten
|
||||
if key == kubectl.ChangeCauseAnnotation {
|
||||
continue
|
||||
}
|
||||
if value, found := accessor.GetAnnotations()[key]; found {
|
||||
if buf.Len() > 0 {
|
||||
buf.WriteString("; ")
|
||||
}
|
||||
buf.WriteString(fmt.Sprintf("'%s' already has a value (%s)", key, value))
|
||||
}
|
||||
}
|
||||
if buf.Len() > 0 {
|
||||
return fmt.Errorf("--overwrite is false but found the following declared annotation(s): %s", buf.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateAnnotations updates annotations of obj
|
||||
func (o AnnotateOptions) updateAnnotations(obj runtime.Object) error {
|
||||
accessor, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !o.overwrite {
|
||||
if err := validateNoAnnotationOverwrites(accessor, o.newAnnotations); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
annotations := accessor.GetAnnotations()
|
||||
if annotations == nil {
|
||||
annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
for key, value := range o.newAnnotations {
|
||||
annotations[key] = value
|
||||
}
|
||||
for _, annotation := range o.removeAnnotations {
|
||||
delete(annotations, annotation)
|
||||
}
|
||||
accessor.SetAnnotations(annotations)
|
||||
|
||||
if len(o.resourceVersion) != 0 {
|
||||
accessor.SetResourceVersion(o.resourceVersion)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
633
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/annotate_test.go
generated
vendored
Normal file
633
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/annotate_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,633 @@
|
|||
/*
|
||||
Copyright 2014 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"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/rest/fake"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||
)
|
||||
|
||||
func TestValidateAnnotationOverwrites(t *testing.T) {
|
||||
tests := []struct {
|
||||
meta *metav1.ObjectMeta
|
||||
annotations map[string]string
|
||||
expectErr bool
|
||||
scenario string
|
||||
}{
|
||||
{
|
||||
meta: &metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
"a": "A",
|
||||
"b": "B",
|
||||
},
|
||||
},
|
||||
annotations: map[string]string{
|
||||
"a": "a",
|
||||
"c": "C",
|
||||
},
|
||||
scenario: "share first annotation",
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
meta: &metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
"a": "A",
|
||||
"c": "C",
|
||||
},
|
||||
},
|
||||
annotations: map[string]string{
|
||||
"b": "B",
|
||||
"c": "c",
|
||||
},
|
||||
scenario: "share second annotation",
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
meta: &metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
"a": "A",
|
||||
"c": "C",
|
||||
},
|
||||
},
|
||||
annotations: map[string]string{
|
||||
"b": "B",
|
||||
"d": "D",
|
||||
},
|
||||
scenario: "no overlap",
|
||||
},
|
||||
{
|
||||
meta: &metav1.ObjectMeta{},
|
||||
annotations: map[string]string{
|
||||
"a": "A",
|
||||
"b": "B",
|
||||
},
|
||||
scenario: "no annotations",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
err := validateNoAnnotationOverwrites(test.meta, test.annotations)
|
||||
if test.expectErr && err == nil {
|
||||
t.Errorf("%s: unexpected non-error", test.scenario)
|
||||
} else if !test.expectErr && err != nil {
|
||||
t.Errorf("%s: unexpected error: %v", test.scenario, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAnnotations(t *testing.T) {
|
||||
testURL := "https://test.com/index.htm?id=123#u=user-name"
|
||||
testJSON := `'{"kind":"SerializedReference","apiVersion":"v1","reference":{"kind":"ReplicationController","namespace":"default","name":"my-nginx","uid":"c544ee78-2665-11e5-8051-42010af0c213","apiVersion":"v1","resourceVersion":"61368"}}'`
|
||||
tests := []struct {
|
||||
annotations []string
|
||||
expected map[string]string
|
||||
expectedRemove []string
|
||||
scenario string
|
||||
expectedErr string
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
annotations: []string{"a=b", "c=d"},
|
||||
expected: map[string]string{"a": "b", "c": "d"},
|
||||
expectedRemove: []string{},
|
||||
scenario: "add two annotations",
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
annotations: []string{"url=" + testURL, api.CreatedByAnnotation + "=" + testJSON},
|
||||
expected: map[string]string{"url": testURL, api.CreatedByAnnotation: testJSON},
|
||||
expectedRemove: []string{},
|
||||
scenario: "add annotations with special characters",
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
annotations: []string{},
|
||||
expected: map[string]string{},
|
||||
expectedRemove: []string{},
|
||||
scenario: "add no annotations",
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
annotations: []string{"a=b", "c=d", "e-"},
|
||||
expected: map[string]string{"a": "b", "c": "d"},
|
||||
expectedRemove: []string{"e"},
|
||||
scenario: "add two annotations, remove one",
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
annotations: []string{"ab", "c=d"},
|
||||
expectedErr: "invalid annotation format: ab",
|
||||
scenario: "incorrect annotation input (missing =value)",
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
annotations: []string{"a="},
|
||||
expected: map[string]string{"a": ""},
|
||||
expectedRemove: []string{},
|
||||
scenario: "add valid annotation with empty value",
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
annotations: []string{"ab", "a="},
|
||||
expectedErr: "invalid annotation format: ab",
|
||||
scenario: "incorrect annotation input (missing =value)",
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
annotations: []string{"-"},
|
||||
expectedErr: "invalid annotation format: -",
|
||||
scenario: "incorrect annotation input (missing key)",
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
annotations: []string{"=bar"},
|
||||
expectedErr: "invalid annotation format: =bar",
|
||||
scenario: "incorrect annotation input (missing key)",
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
annotations, remove, err := parseAnnotations(test.annotations)
|
||||
switch {
|
||||
case test.expectErr && err == nil:
|
||||
t.Errorf("%s: unexpected non-error, should return %v", test.scenario, test.expectedErr)
|
||||
case test.expectErr && err.Error() != test.expectedErr:
|
||||
t.Errorf("%s: unexpected error %v, expected %v", test.scenario, err, test.expectedErr)
|
||||
case !test.expectErr && err != nil:
|
||||
t.Errorf("%s: unexpected error %v", test.scenario, err)
|
||||
case !test.expectErr && !reflect.DeepEqual(annotations, test.expected):
|
||||
t.Errorf("%s: expected %v, got %v", test.scenario, test.expected, annotations)
|
||||
case !test.expectErr && !reflect.DeepEqual(remove, test.expectedRemove):
|
||||
t.Errorf("%s: expected %v, got %v", test.scenario, test.expectedRemove, remove)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateAnnotations(t *testing.T) {
|
||||
tests := []struct {
|
||||
removeAnnotations []string
|
||||
newAnnotations map[string]string
|
||||
expectedErr string
|
||||
scenario string
|
||||
}{
|
||||
{
|
||||
expectedErr: "can not both modify and remove the following annotation(s) in the same command: a",
|
||||
removeAnnotations: []string{"a"},
|
||||
newAnnotations: map[string]string{"a": "b", "c": "d"},
|
||||
scenario: "remove an added annotation",
|
||||
},
|
||||
{
|
||||
expectedErr: "can not both modify and remove the following annotation(s) in the same command: a, c",
|
||||
removeAnnotations: []string{"a", "c"},
|
||||
newAnnotations: map[string]string{"a": "b", "c": "d"},
|
||||
scenario: "remove added annotations",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
if err := validateAnnotations(test.removeAnnotations, test.newAnnotations); err == nil {
|
||||
t.Errorf("%s: unexpected non-error", test.scenario)
|
||||
} else if err.Error() != test.expectedErr {
|
||||
t.Errorf("%s: expected error %s, got %s", test.scenario, test.expectedErr, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateAnnotations(t *testing.T) {
|
||||
tests := []struct {
|
||||
obj runtime.Object
|
||||
overwrite bool
|
||||
version string
|
||||
annotations map[string]string
|
||||
remove []string
|
||||
expected runtime.Object
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
obj: &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"a": "b"},
|
||||
},
|
||||
},
|
||||
annotations: map[string]string{"a": "b"},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
obj: &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"a": "b"},
|
||||
},
|
||||
},
|
||||
annotations: map[string]string{"a": "c"},
|
||||
overwrite: true,
|
||||
expected: &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"a": "c"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
obj: &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"a": "b"},
|
||||
},
|
||||
},
|
||||
annotations: map[string]string{"c": "d"},
|
||||
expected: &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"a": "b", "c": "d"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
obj: &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"a": "b"},
|
||||
},
|
||||
},
|
||||
annotations: map[string]string{"c": "d"},
|
||||
version: "2",
|
||||
expected: &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"a": "b", "c": "d"},
|
||||
ResourceVersion: "2",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
obj: &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"a": "b"},
|
||||
},
|
||||
},
|
||||
annotations: map[string]string{},
|
||||
remove: []string{"a"},
|
||||
expected: &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
obj: &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"a": "b", "c": "d"},
|
||||
},
|
||||
},
|
||||
annotations: map[string]string{"e": "f"},
|
||||
remove: []string{"a"},
|
||||
expected: &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
"c": "d",
|
||||
"e": "f",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
obj: &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"a": "b", "c": "d"},
|
||||
},
|
||||
},
|
||||
annotations: map[string]string{"e": "f"},
|
||||
remove: []string{"g"},
|
||||
expected: &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
"a": "b",
|
||||
"c": "d",
|
||||
"e": "f",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
obj: &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"a": "b", "c": "d"},
|
||||
},
|
||||
},
|
||||
remove: []string{"e"},
|
||||
expected: &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
"a": "b",
|
||||
"c": "d",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
obj: &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{},
|
||||
},
|
||||
annotations: map[string]string{"a": "b"},
|
||||
expected: &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"a": "b"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
options := &AnnotateOptions{
|
||||
overwrite: test.overwrite,
|
||||
newAnnotations: test.annotations,
|
||||
removeAnnotations: test.remove,
|
||||
resourceVersion: test.version,
|
||||
}
|
||||
err := options.updateAnnotations(test.obj)
|
||||
if test.expectErr {
|
||||
if err == nil {
|
||||
t.Errorf("unexpected non-error: %v", test)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if !test.expectErr && err != nil {
|
||||
t.Errorf("unexpected error: %v %v", err, test)
|
||||
}
|
||||
if !reflect.DeepEqual(test.obj, test.expected) {
|
||||
t.Errorf("expected: %v, got %v", test.expected, test.obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnnotateErrors(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
args []string
|
||||
flags map[string]string
|
||||
errFn func(error) bool
|
||||
}{
|
||||
"no args": {
|
||||
args: []string{},
|
||||
errFn: func(err error) bool { return strings.Contains(err.Error(), "one or more resources must be specified") },
|
||||
},
|
||||
"not enough annotations": {
|
||||
args: []string{"pods"},
|
||||
errFn: func(err error) bool {
|
||||
return strings.Contains(err.Error(), "at least one annotation update is required")
|
||||
},
|
||||
},
|
||||
"wrong annotations": {
|
||||
args: []string{"pods", "-"},
|
||||
errFn: func(err error) bool {
|
||||
return strings.Contains(err.Error(), "at least one annotation update is required")
|
||||
},
|
||||
},
|
||||
"wrong annotations 2": {
|
||||
args: []string{"pods", "=bar"},
|
||||
errFn: func(err error) bool {
|
||||
return strings.Contains(err.Error(), "at least one annotation update is required")
|
||||
},
|
||||
},
|
||||
"no resources remove annotations": {
|
||||
args: []string{"pods-"},
|
||||
errFn: func(err error) bool { return strings.Contains(err.Error(), "one or more resources must be specified") },
|
||||
},
|
||||
"no resources add annotations": {
|
||||
args: []string{"pods=bar"},
|
||||
errFn: func(err error) bool { return strings.Contains(err.Error(), "one or more resources must be specified") },
|
||||
},
|
||||
}
|
||||
|
||||
for k, testCase := range testCases {
|
||||
f, tf, _, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Namespace = "test"
|
||||
tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &api.Registry.GroupOrDie(api.GroupName).GroupVersion}}
|
||||
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdAnnotate(f, buf)
|
||||
cmd.SetOutput(buf)
|
||||
|
||||
for k, v := range testCase.flags {
|
||||
cmd.Flags().Set(k, v)
|
||||
}
|
||||
options := &AnnotateOptions{}
|
||||
err := options.Complete(buf, cmd, testCase.args)
|
||||
if err == nil {
|
||||
err = options.Validate()
|
||||
}
|
||||
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()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnnotateObject(t *testing.T) {
|
||||
pods, _, _ := testData()
|
||||
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch req.Method {
|
||||
case "GET":
|
||||
switch req.URL.Path {
|
||||
case "/namespaces/test/pods/foo":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
case "PATCH":
|
||||
switch req.URL.Path {
|
||||
case "/namespaces/test/pods/foo":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
default:
|
||||
t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &api.Registry.GroupOrDie(api.GroupName).GroupVersion}}
|
||||
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdAnnotate(f, buf)
|
||||
cmd.SetOutput(buf)
|
||||
options := &AnnotateOptions{}
|
||||
args := []string{"pods/foo", "a=b", "c-"}
|
||||
if err := options.Complete(buf, cmd, args); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if err := options.Validate(); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if err := options.RunAnnotate(f, cmd); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnnotateObjectFromFile(t *testing.T) {
|
||||
pods, _, _ := testData()
|
||||
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch req.Method {
|
||||
case "GET":
|
||||
switch req.URL.Path {
|
||||
case "/namespaces/test/replicationcontrollers/cassandra":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
case "PATCH":
|
||||
switch req.URL.Path {
|
||||
case "/namespaces/test/replicationcontrollers/cassandra":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
default:
|
||||
t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &api.Registry.GroupOrDie(api.GroupName).GroupVersion}}
|
||||
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdAnnotate(f, buf)
|
||||
cmd.SetOutput(buf)
|
||||
options := &AnnotateOptions{}
|
||||
options.Filenames = []string{"../../../examples/storage/cassandra/cassandra-controller.yaml"}
|
||||
args := []string{"a=b", "c-"}
|
||||
if err := options.Complete(buf, cmd, args); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if err := options.Validate(); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if err := options.RunAnnotate(f, cmd); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnnotateLocal(t *testing.T) {
|
||||
f, tf, _, _ := cmdtesting.NewAPIFactory()
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
|
||||
return nil, nil
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &api.Registry.GroupOrDie(api.GroupName).GroupVersion}}
|
||||
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdAnnotate(f, buf)
|
||||
cmd.Flags().Set("local", "true")
|
||||
options := &AnnotateOptions{}
|
||||
options.Filenames = []string{"../../../examples/storage/cassandra/cassandra-controller.yaml"}
|
||||
args := []string{"a=b"}
|
||||
if err := options.Complete(buf, cmd, args); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if err := options.Validate(); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if err := options.RunAnnotate(f, cmd); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnnotateMultipleObjects(t *testing.T) {
|
||||
pods, _, _ := testData()
|
||||
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch req.Method {
|
||||
case "GET":
|
||||
switch req.URL.Path {
|
||||
case "/namespaces/test/pods":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
case "PATCH":
|
||||
switch req.URL.Path {
|
||||
case "/namespaces/test/pods/foo":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])}, nil
|
||||
case "/namespaces/test/pods/bar":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[1])}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
default:
|
||||
t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &api.Registry.GroupOrDie(api.GroupName).GroupVersion}}
|
||||
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdAnnotate(f, buf)
|
||||
cmd.SetOutput(buf)
|
||||
cmd.Flags().Set("all", "true")
|
||||
options := &AnnotateOptions{}
|
||||
args := []string{"pods", "a=b", "c-"}
|
||||
if err := options.Complete(buf, cmd, args); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if err := options.Validate(); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if err := options.RunAnnotate(f, cmd); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
71
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/apiversions.go
generated
vendored
Normal file
71
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/apiversions.go
generated
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
Copyright 2014 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"
|
||||
"sort"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
var (
|
||||
apiversionsExample = templates.Examples(i18n.T(`
|
||||
# Print the supported API versions
|
||||
kubectl api-versions`))
|
||||
)
|
||||
|
||||
func NewCmdApiVersions(f cmdutil.Factory, out io.Writer) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "api-versions",
|
||||
Short: "Print the supported API versions on the server, in the form of \"group/version\"",
|
||||
Long: "Print the supported API versions on the server, in the form of \"group/version\"",
|
||||
Example: apiversionsExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := RunApiVersions(f, out)
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func RunApiVersions(f cmdutil.Factory, w io.Writer) error {
|
||||
discoveryclient, err := f.DiscoveryClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Always request fresh data from the server
|
||||
discoveryclient.Invalidate()
|
||||
|
||||
groupList, err := discoveryclient.ServerGroups()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Couldn't get available api versions from server: %v\n", err)
|
||||
}
|
||||
apiVersions := metav1.ExtractGroupVersions(groupList)
|
||||
sort.Strings(apiVersions)
|
||||
for _, v := range apiVersions {
|
||||
fmt.Fprintln(w, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
664
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/apply.go
generated
vendored
Normal file
664
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/apply.go
generated
vendored
Normal file
|
|
@ -0,0 +1,664 @@
|
|||
/*
|
||||
Copyright 2014 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"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/jonboulle/clockwork"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/jsonmergepatch"
|
||||
"k8s.io/apimachinery/pkg/util/mergepatch"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"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"
|
||||
)
|
||||
|
||||
type ApplyOptions struct {
|
||||
FilenameOptions resource.FilenameOptions
|
||||
Selector string
|
||||
Force bool
|
||||
Prune bool
|
||||
Cascade bool
|
||||
GracePeriod int
|
||||
PruneResources []pruneResource
|
||||
Timeout time.Duration
|
||||
cmdBaseName string
|
||||
}
|
||||
|
||||
const (
|
||||
// maxPatchRetry is the maximum number of conflicts retry for during a patch operation before returning failure
|
||||
maxPatchRetry = 5
|
||||
// backOffPeriod is the period to back off when apply patch resutls in error.
|
||||
backOffPeriod = 1 * time.Second
|
||||
// how many times we can retry before back off
|
||||
triesBeforeBackOff = 1
|
||||
)
|
||||
|
||||
var (
|
||||
applyLong = templates.LongDesc(i18n.T(`
|
||||
Apply a configuration to a resource by filename or stdin.
|
||||
The resource name must be specified. This resource will be created if it doesn't exist yet.
|
||||
To use 'apply', always create the resource initially with either 'apply' or 'create --save-config'.
|
||||
|
||||
JSON and YAML formats are accepted.
|
||||
|
||||
Alpha Disclaimer: the --prune functionality is not yet complete. Do not use unless you are aware of what the current state is. See https://issues.k8s.io/34274.`))
|
||||
|
||||
applyExample = templates.Examples(i18n.T(`
|
||||
# Apply the configuration in pod.json to a pod.
|
||||
kubectl apply -f ./pod.json
|
||||
|
||||
# Apply the JSON passed into stdin to a pod.
|
||||
cat pod.json | kubectl apply -f -
|
||||
|
||||
# Note: --prune is still in Alpha
|
||||
# Apply the configuration in manifest.yaml that matches label app=nginx and delete all the other resources that are not in the file and match label app=nginx.
|
||||
kubectl apply --prune -f manifest.yaml -l app=nginx
|
||||
|
||||
# Apply the configuration in manifest.yaml and delete all the other configmaps that are not in the file.
|
||||
kubectl apply --prune -f manifest.yaml --all --prune-whitelist=core/v1/ConfigMap`))
|
||||
|
||||
warningNoLastAppliedConfigAnnotation = "Warning: %[1]s apply should be used on resource created by either %[1]s create --save-config or %[1]s apply\n"
|
||||
)
|
||||
|
||||
func NewCmdApply(baseName string, f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
|
||||
var options ApplyOptions
|
||||
|
||||
// Store baseName for use in printing warnings / messages involving the base command name.
|
||||
// This is useful for downstream command that wrap this one.
|
||||
options.cmdBaseName = baseName
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "apply -f FILENAME",
|
||||
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(RunApply(f, cmd, out, errOut, &options))
|
||||
},
|
||||
}
|
||||
|
||||
usage := "that contains the configuration to apply"
|
||||
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
|
||||
cmd.MarkFlagRequired("filename")
|
||||
cmd.Flags().Bool("overwrite", true, "Automatically resolve conflicts between the modified and live configuration by using values from the modified configuration")
|
||||
cmd.Flags().BoolVar(&options.Prune, "prune", false, "Automatically delete resource objects, including the uninitialized ones, that do not appear in the configs and are created by either apply or create --save-config. Should be used with either -l or --all.")
|
||||
cmd.Flags().BoolVar(&options.Cascade, "cascade", true, "Only relevant during a prune or a force apply. If true, cascade the deletion of the resources managed by pruned or deleted resources (e.g. Pods created by a ReplicationController).")
|
||||
cmd.Flags().IntVar(&options.GracePeriod, "grace-period", -1, "Only relevant during a prune or a force apply. Period of time in seconds given to pruned or deleted resources to terminate gracefully. Ignored if negative.")
|
||||
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().StringArray("prune-whitelist", []string{}, "Overwrite the default whitelist with <group/version/kind> for --prune")
|
||||
cmdutil.AddDryRunFlag(cmd)
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
cmdutil.AddRecordFlag(cmd)
|
||||
cmdutil.AddInclude3rdPartyFlags(cmd)
|
||||
cmdutil.AddIncludeUninitializedFlag(cmd)
|
||||
|
||||
// apply subcommands
|
||||
cmd.AddCommand(NewCmdApplyViewLastApplied(f, out, errOut))
|
||||
cmd.AddCommand(NewCmdApplySetLastApplied(f, out, errOut))
|
||||
cmd.AddCommand(NewCmdApplyEditLastApplied(f, out, errOut))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
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 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.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parsePruneResources(mapper meta.RESTMapper, gvks []string) ([]pruneResource, error) {
|
||||
pruneResources := []pruneResource{}
|
||||
for _, groupVersionKind := range gvks {
|
||||
gvk := strings.Split(groupVersionKind, "/")
|
||||
if len(gvk) != 3 {
|
||||
return nil, fmt.Errorf("invalid GroupVersionKind format: %v, please follow <group/version/kind>", groupVersionKind)
|
||||
}
|
||||
|
||||
if gvk[0] == "core" {
|
||||
gvk[0] = ""
|
||||
}
|
||||
mapping, err := mapper.RESTMapping(schema.GroupKind{Group: gvk[0], Kind: gvk[2]}, gvk[1])
|
||||
if err != nil {
|
||||
return pruneResources, err
|
||||
}
|
||||
var namespaced bool
|
||||
namespaceScope := mapping.Scope.Name()
|
||||
switch namespaceScope {
|
||||
case meta.RESTScopeNameNamespace:
|
||||
namespaced = true
|
||||
case meta.RESTScopeNameRoot:
|
||||
namespaced = false
|
||||
default:
|
||||
return pruneResources, fmt.Errorf("Unknown namespace scope: %q", namespaceScope)
|
||||
}
|
||||
|
||||
pruneResources = append(pruneResources, pruneResource{gvk[0], gvk[1], gvk[2], namespaced})
|
||||
}
|
||||
return pruneResources, nil
|
||||
}
|
||||
|
||||
func RunApply(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, options *ApplyOptions) error {
|
||||
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagBool(cmd, "openapi-validation"), cmdutil.GetFlagString(cmd, "schema-cache-dir"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mapper, _, err := f.UnstructuredObject()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if options.Prune {
|
||||
options.PruneResources, err = parsePruneResources(mapper, cmdutil.GetFlagStringArray(cmd, "prune-whitelist"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
builder, err := f.NewUnstructuredBuilder(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// include the uninitialized objects by default if --prune is true
|
||||
// unless explicitly set --include-uninitialized=false
|
||||
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, options.Prune)
|
||||
|
||||
r := builder.
|
||||
Schema(schema).
|
||||
ContinueOnError().
|
||||
NamespaceParam(cmdNamespace).DefaultNamespace().
|
||||
FilenameParam(enforceNamespace, &options.FilenameOptions).
|
||||
SelectorParam(options.Selector).
|
||||
IncludeUninitialized(includeUninitialized).
|
||||
Flatten().
|
||||
Do()
|
||||
err = r.Err()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dryRun := cmdutil.GetFlagBool(cmd, "dry-run")
|
||||
output := cmdutil.GetFlagString(cmd, "output")
|
||||
shortOutput := output == "name"
|
||||
|
||||
encoder := f.JSONEncoder()
|
||||
decoder := f.Decoder(false)
|
||||
|
||||
visitedUids := sets.NewString()
|
||||
visitedNamespaces := sets.NewString()
|
||||
|
||||
count := 0
|
||||
err = r.Visit(func(info *resource.Info, err error) error {
|
||||
// In this method, info.Object contains the object retrieved from the server
|
||||
// and info.VersionedObject contains the object decoded from the input source.
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if info.Namespaced() {
|
||||
visitedNamespaces.Insert(info.Namespace)
|
||||
}
|
||||
|
||||
// Add change-cause annotation to resource info if it should be recorded
|
||||
if cmdutil.ShouldRecord(cmd, info) {
|
||||
recordInObj := info.VersionedObject
|
||||
if info.VersionedObject == nil {
|
||||
recordInObj = info.Object
|
||||
}
|
||||
if err := cmdutil.RecordChangeCause(recordInObj, f.Command(cmd, false)); err != nil {
|
||||
glog.V(4).Infof("error recording current command: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Get the modified configuration of the object. Embed the result
|
||||
// as an annotation in the modified configuration, so that it will appear
|
||||
// in the patch sent to the server.
|
||||
modified, err := kubectl.GetModifiedConfiguration(info, true, encoder)
|
||||
if err != nil {
|
||||
return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving modified configuration from:\n%v\nfor:", info), info.Source, err)
|
||||
}
|
||||
|
||||
if err := info.Get(); err != nil {
|
||||
if !errors.IsNotFound(err) {
|
||||
return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving current configuration of:\n%v\nfrom server for:", info), info.Source, err)
|
||||
}
|
||||
// Create the resource if it doesn't exist
|
||||
// First, update the annotation used by kubectl apply
|
||||
if err := kubectl.CreateApplyAnnotation(info, encoder); err != nil {
|
||||
return cmdutil.AddSourceToErr("creating", info.Source, err)
|
||||
}
|
||||
|
||||
if !dryRun {
|
||||
// Then create the resource and skip the three-way merge
|
||||
if err := createAndRefresh(info); err != nil {
|
||||
return cmdutil.AddSourceToErr("creating", info.Source, err)
|
||||
}
|
||||
if uid, err := info.Mapping.UID(info.Object); err != nil {
|
||||
return err
|
||||
} else {
|
||||
visitedUids.Insert(string(uid))
|
||||
}
|
||||
}
|
||||
|
||||
count++
|
||||
if len(output) > 0 && !shortOutput {
|
||||
return cmdutil.PrintResourceInfoForCommand(cmd, info, f, out)
|
||||
}
|
||||
cmdutil.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, dryRun, "created")
|
||||
return nil
|
||||
}
|
||||
|
||||
if !dryRun {
|
||||
annotationMap, err := info.Mapping.MetadataAccessor.Annotations(info.Object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, ok := annotationMap[api.LastAppliedConfigAnnotation]; !ok {
|
||||
fmt.Fprintf(errOut, warningNoLastAppliedConfigAnnotation, options.cmdBaseName)
|
||||
}
|
||||
overwrite := cmdutil.GetFlagBool(cmd, "overwrite")
|
||||
helper := resource.NewHelper(info.Client, info.Mapping)
|
||||
patcher := &patcher{
|
||||
encoder: encoder,
|
||||
decoder: decoder,
|
||||
mapping: info.Mapping,
|
||||
helper: helper,
|
||||
clientFunc: f.UnstructuredClientForMapping,
|
||||
clientsetFunc: f.ClientSet,
|
||||
overwrite: overwrite,
|
||||
backOff: clockwork.NewRealClock(),
|
||||
force: options.Force,
|
||||
cascade: options.Cascade,
|
||||
timeout: options.Timeout,
|
||||
gracePeriod: options.GracePeriod,
|
||||
}
|
||||
|
||||
patchBytes, patchedObject, err := patcher.patch(info.Object, modified, info.Source, info.Namespace, info.Name)
|
||||
if err != nil {
|
||||
return cmdutil.AddSourceToErr(fmt.Sprintf("applying patch:\n%s\nto:\n%v\nfor:", patchBytes, info), info.Source, err)
|
||||
}
|
||||
|
||||
info.Refresh(patchedObject, true)
|
||||
|
||||
if uid, err := info.Mapping.UID(info.Object); err != nil {
|
||||
return err
|
||||
} else {
|
||||
visitedUids.Insert(string(uid))
|
||||
}
|
||||
}
|
||||
count++
|
||||
if len(output) > 0 && !shortOutput {
|
||||
return cmdutil.PrintResourceInfoForCommand(cmd, info, f, out)
|
||||
}
|
||||
cmdutil.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, dryRun, "configured")
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count == 0 {
|
||||
return fmt.Errorf("no objects passed to apply")
|
||||
}
|
||||
|
||||
if !options.Prune {
|
||||
return nil
|
||||
}
|
||||
|
||||
selector, err := labels.Parse(options.Selector)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p := pruner{
|
||||
mapper: mapper,
|
||||
clientFunc: f.UnstructuredClientForMapping,
|
||||
clientsetFunc: f.ClientSet,
|
||||
|
||||
selector: selector,
|
||||
visitedUids: visitedUids,
|
||||
|
||||
cascade: options.Cascade,
|
||||
dryRun: dryRun,
|
||||
gracePeriod: options.GracePeriod,
|
||||
|
||||
out: out,
|
||||
}
|
||||
|
||||
namespacedRESTMappings, nonNamespacedRESTMappings, err := getRESTMappings(mapper, &(options.PruneResources))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error retrieving RESTMappings to prune: %v", err)
|
||||
}
|
||||
|
||||
for n := range visitedNamespaces {
|
||||
for _, m := range namespacedRESTMappings {
|
||||
if err := p.prune(n, m, shortOutput, includeUninitialized); err != nil {
|
||||
return fmt.Errorf("error pruning namespaced object %v: %v", m.GroupVersionKind, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, m := range nonNamespacedRESTMappings {
|
||||
if err := p.prune(metav1.NamespaceNone, m, shortOutput, includeUninitialized); err != nil {
|
||||
return fmt.Errorf("error pruning nonNamespaced object %v: %v", m.GroupVersionKind, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type pruneResource struct {
|
||||
group string
|
||||
version string
|
||||
kind string
|
||||
namespaced bool
|
||||
}
|
||||
|
||||
func (pr pruneResource) String() string {
|
||||
return fmt.Sprintf("%v/%v, Kind=%v, Namespaced=%v", pr.group, pr.version, pr.kind, pr.namespaced)
|
||||
}
|
||||
|
||||
func getRESTMappings(mapper meta.RESTMapper, pruneResources *[]pruneResource) (namespaced, nonNamespaced []*meta.RESTMapping, err error) {
|
||||
if len(*pruneResources) == 0 {
|
||||
// default whitelist
|
||||
// TODO: need to handle the older api versions - e.g. v1beta1 jobs. Github issue: #35991
|
||||
*pruneResources = []pruneResource{
|
||||
{"", "v1", "ConfigMap", true},
|
||||
{"", "v1", "Endpoints", true},
|
||||
{"", "v1", "Namespace", false},
|
||||
{"", "v1", "PersistentVolumeClaim", true},
|
||||
{"", "v1", "PersistentVolume", false},
|
||||
{"", "v1", "Pod", true},
|
||||
{"", "v1", "ReplicationController", true},
|
||||
{"", "v1", "Secret", true},
|
||||
{"", "v1", "Service", true},
|
||||
{"batch", "v1", "Job", true},
|
||||
{"extensions", "v1beta1", "DaemonSet", true},
|
||||
{"extensions", "v1beta1", "Deployment", true},
|
||||
{"extensions", "v1beta1", "Ingress", true},
|
||||
{"extensions", "v1beta1", "ReplicaSet", true},
|
||||
{"apps", "v1beta1", "StatefulSet", true},
|
||||
{"apps", "v1beta1", "Deployment", true},
|
||||
}
|
||||
}
|
||||
|
||||
for _, resource := range *pruneResources {
|
||||
addedMapping, err := mapper.RESTMapping(schema.GroupKind{Group: resource.group, Kind: resource.kind}, resource.version)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("invalid resource %v: %v", resource, err)
|
||||
}
|
||||
if resource.namespaced {
|
||||
namespaced = append(namespaced, addedMapping)
|
||||
} else {
|
||||
nonNamespaced = append(nonNamespaced, addedMapping)
|
||||
}
|
||||
}
|
||||
|
||||
return namespaced, nonNamespaced, nil
|
||||
}
|
||||
|
||||
type pruner struct {
|
||||
mapper meta.RESTMapper
|
||||
clientFunc resource.ClientMapperFunc
|
||||
clientsetFunc func() (internalclientset.Interface, error)
|
||||
|
||||
visitedUids sets.String
|
||||
selector labels.Selector
|
||||
|
||||
cascade bool
|
||||
dryRun bool
|
||||
gracePeriod int
|
||||
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
func (p *pruner) prune(namespace string, mapping *meta.RESTMapping, shortOutput, includeUninitialized bool) error {
|
||||
c, err := p.clientFunc(mapping)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
objList, err := resource.NewHelper(c, mapping).List(namespace, mapping.GroupVersionKind.Version, p.selector, false, includeUninitialized)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
objs, err := meta.ExtractList(objList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, obj := range objs {
|
||||
annots, err := mapping.MetadataAccessor.Annotations(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, ok := annots[api.LastAppliedConfigAnnotation]; !ok {
|
||||
// don't prune resources not created with apply
|
||||
continue
|
||||
}
|
||||
uid, err := mapping.UID(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if p.visitedUids.Has(string(uid)) {
|
||||
continue
|
||||
}
|
||||
|
||||
name, err := mapping.Name(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !p.dryRun {
|
||||
if err := p.delete(namespace, name, mapping); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
cmdutil.PrintSuccess(p.mapper, shortOutput, p.out, mapping.Resource, name, p.dryRun, "pruned")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *pruner) delete(namespace, name string, mapping *meta.RESTMapping) error {
|
||||
c, err := p.clientFunc(mapping)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return runDelete(namespace, name, mapping, c, nil, p.cascade, p.gracePeriod, p.clientsetFunc)
|
||||
}
|
||||
|
||||
func runDelete(namespace, name string, mapping *meta.RESTMapping, c resource.RESTClient, helper *resource.Helper, cascade bool, gracePeriod int, clientsetFunc func() (internalclientset.Interface, error)) error {
|
||||
if !cascade {
|
||||
if helper == nil {
|
||||
helper = resource.NewHelper(c, mapping)
|
||||
}
|
||||
return helper.Delete(namespace, name)
|
||||
}
|
||||
cs, err := clientsetFunc()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r, err := kubectl.ReaperFor(mapping.GroupVersionKind.GroupKind(), cs)
|
||||
if err != nil {
|
||||
if _, ok := err.(*kubectl.NoSuchReaperError); !ok {
|
||||
return err
|
||||
}
|
||||
return resource.NewHelper(c, mapping).Delete(namespace, name)
|
||||
}
|
||||
var options *metav1.DeleteOptions
|
||||
if gracePeriod >= 0 {
|
||||
options = metav1.NewDeleteOptions(int64(gracePeriod))
|
||||
}
|
||||
if err := r.Stop(namespace, name, 2*time.Minute, options); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *patcher) delete(namespace, name string) error {
|
||||
c, err := p.clientFunc(p.mapping)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return runDelete(namespace, name, p.mapping, c, p.helper, p.cascade, p.gracePeriod, p.clientsetFunc)
|
||||
}
|
||||
|
||||
type patcher struct {
|
||||
encoder runtime.Encoder
|
||||
decoder runtime.Decoder
|
||||
|
||||
mapping *meta.RESTMapping
|
||||
helper *resource.Helper
|
||||
clientFunc resource.ClientMapperFunc
|
||||
clientsetFunc func() (internalclientset.Interface, error)
|
||||
|
||||
overwrite bool
|
||||
backOff clockwork.Clock
|
||||
|
||||
force bool
|
||||
cascade bool
|
||||
timeout time.Duration
|
||||
gracePeriod int
|
||||
}
|
||||
|
||||
func (p *patcher) patchSimple(obj runtime.Object, modified []byte, source, namespace, name string) ([]byte, runtime.Object, error) {
|
||||
// Serialize the current configuration of the object from the server.
|
||||
current, err := runtime.Encode(p.encoder, obj)
|
||||
if err != nil {
|
||||
return nil, nil, cmdutil.AddSourceToErr(fmt.Sprintf("serializing current configuration from:\n%v\nfor:", obj), source, err)
|
||||
}
|
||||
|
||||
// Retrieve the original configuration of the object from the annotation.
|
||||
original, err := kubectl.GetOriginalConfiguration(p.mapping, obj)
|
||||
if err != nil {
|
||||
return nil, nil, cmdutil.AddSourceToErr(fmt.Sprintf("retrieving original configuration from:\n%v\nfor:", obj), source, err)
|
||||
}
|
||||
|
||||
// Create the versioned struct from the type defined in the restmapping
|
||||
// (which is the API version we'll be submitting the patch to)
|
||||
versionedObject, err := api.Scheme.New(p.mapping.GroupVersionKind)
|
||||
var patchType types.PatchType
|
||||
var patch []byte
|
||||
createPatchErrFormat := "creating patch with:\noriginal:\n%s\nmodified:\n%s\ncurrent:\n%s\nfor:"
|
||||
switch {
|
||||
case runtime.IsNotRegisteredError(err):
|
||||
// fall back to generic JSON merge patch
|
||||
patchType = types.MergePatchType
|
||||
preconditions := []mergepatch.PreconditionFunc{mergepatch.RequireKeyUnchanged("apiVersion"),
|
||||
mergepatch.RequireKeyUnchanged("kind"), mergepatch.RequireMetadataKeyUnchanged("name")}
|
||||
patch, err = jsonmergepatch.CreateThreeWayJSONMergePatch(original, modified, current, preconditions...)
|
||||
if err != nil {
|
||||
if mergepatch.IsPreconditionFailed(err) {
|
||||
return nil, nil, fmt.Errorf("%s", "At least one of apiVersion, kind and name was changed")
|
||||
}
|
||||
return nil, nil, cmdutil.AddSourceToErr(fmt.Sprintf(createPatchErrFormat, original, modified, current), source, err)
|
||||
}
|
||||
case err != nil:
|
||||
return nil, nil, cmdutil.AddSourceToErr(fmt.Sprintf("getting instance of versioned object for %v:", p.mapping.GroupVersionKind), source, err)
|
||||
case err == nil:
|
||||
// Compute a three way strategic merge patch to send to server.
|
||||
patchType = types.StrategicMergePatchType
|
||||
patch, err = strategicpatch.CreateThreeWayMergePatch(original, modified, current, versionedObject, p.overwrite)
|
||||
if err != nil {
|
||||
return nil, nil, cmdutil.AddSourceToErr(fmt.Sprintf(createPatchErrFormat, original, modified, current), source, err)
|
||||
}
|
||||
}
|
||||
|
||||
patchedObj, err := p.helper.Patch(namespace, name, patchType, patch)
|
||||
return patch, patchedObj, err
|
||||
}
|
||||
|
||||
func (p *patcher) patch(current runtime.Object, modified []byte, source, namespace, name string) ([]byte, runtime.Object, error) {
|
||||
var getErr error
|
||||
patchBytes, patchObject, err := p.patchSimple(current, modified, source, namespace, name)
|
||||
for i := 1; i <= maxPatchRetry && errors.IsConflict(err); i++ {
|
||||
if i > triesBeforeBackOff {
|
||||
p.backOff.Sleep(backOffPeriod)
|
||||
}
|
||||
current, getErr = p.helper.Get(namespace, name, false)
|
||||
if getErr != nil {
|
||||
return nil, nil, getErr
|
||||
}
|
||||
patchBytes, patchObject, err = p.patchSimple(current, modified, source, namespace, name)
|
||||
}
|
||||
if err != nil && p.force {
|
||||
patchBytes, patchObject, err = p.deleteAndCreate(modified, namespace, name)
|
||||
}
|
||||
return patchBytes, patchObject, err
|
||||
}
|
||||
|
||||
func (p *patcher) deleteAndCreate(modified []byte, namespace, name string) ([]byte, runtime.Object, error) {
|
||||
err := p.delete(namespace, name)
|
||||
if err != nil {
|
||||
return modified, nil, err
|
||||
}
|
||||
err = wait.PollImmediate(kubectl.Interval, p.timeout, func() (bool, error) {
|
||||
if _, err := p.helper.Get(namespace, name, false); !errors.IsNotFound(err) {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
return modified, nil, err
|
||||
}
|
||||
versionedObject, _, err := p.decoder.Decode(modified, nil, nil)
|
||||
if err != nil {
|
||||
return modified, nil, err
|
||||
}
|
||||
createdObject, err := p.helper.Create(namespace, true, versionedObject)
|
||||
return modified, createdObject, err
|
||||
}
|
||||
105
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/apply_edit_last_applied.go
generated
vendored
Normal file
105
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/apply_edit_last_applied.go
generated
vendored
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"io"
|
||||
"runtime"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"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 (
|
||||
applyEditLastAppliedLong = templates.LongDesc(`
|
||||
Edit the latest last-applied-configuration annotations of resources from the default editor.
|
||||
|
||||
The edit-last-applied command allows you to directly edit any API resource you can retrieve via the
|
||||
command line tools. It will open the editor defined by your KUBE_EDITOR, or EDITOR
|
||||
environment variables, or fall back to 'vi' for Linux or 'notepad' for Windows.
|
||||
You can edit multiple objects, although changes are applied one at a time. The command
|
||||
accepts filenames as well as command line arguments, although the files you point to must
|
||||
be previously saved versions of resources.
|
||||
|
||||
The default format is YAML. To edit in JSON, specify "-o json".
|
||||
|
||||
The flag --windows-line-endings can be used to force Windows line endings,
|
||||
otherwise the default for your operating system will be used.
|
||||
|
||||
In the event an error occurs while updating, a temporary file will be created on disk
|
||||
that contains your unapplied changes. The most common error when updating a resource
|
||||
is another editor changing the resource on the server. When this occurs, you will have
|
||||
to apply your changes to the newer version of the resource, or update your temporary
|
||||
saved copy to include the latest resource version.`)
|
||||
|
||||
applyEditLastAppliedExample = templates.Examples(`
|
||||
# Edit the last-applied-configuration annotations by type/name in YAML.
|
||||
kubectl apply edit-last-applied deployment/nginx
|
||||
|
||||
# Edit the last-applied-configuration annotations by file in JSON.
|
||||
kubectl apply edit-last-applied -f deploy.yaml -o json`)
|
||||
)
|
||||
|
||||
func NewCmdApplyEditLastApplied(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
|
||||
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)
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "edit-last-applied (RESOURCE/NAME | -f FILENAME)",
|
||||
Short: "Edit latest last-applied-configuration annotations of a resource/object",
|
||||
Long: applyEditLastAppliedLong,
|
||||
Example: applyEditLastAppliedExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
options.ChangeCause = f.Command(cmd, false)
|
||||
if err := options.Complete(f, out, errOut, args, cmd); err != nil {
|
||||
cmdutil.CheckErr(err)
|
||||
}
|
||||
if err := options.Run(); err != nil {
|
||||
cmdutil.CheckErr(err)
|
||||
}
|
||||
},
|
||||
ValidArgs: validArgs,
|
||||
ArgAliases: argAliases,
|
||||
}
|
||||
|
||||
usage := "to use to edit the resource"
|
||||
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
|
||||
cmd.Flags().StringVarP(&options.Output, "output", "o", "yaml", "Output format. One of: yaml|json.")
|
||||
cmd.Flags().BoolVar(&options.WindowsLineEndings, "windows-line-endings", runtime.GOOS == "windows",
|
||||
"Defaults to the line ending native to your platform.")
|
||||
cmdutil.AddRecordVarFlag(cmd, &options.Record)
|
||||
cmdutil.AddIncludeUninitializedFlag(cmd)
|
||||
|
||||
return cmd
|
||||
}
|
||||
245
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/apply_set_last_applied.go
generated
vendored
Normal file
245
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/apply_set_last_applied.go
generated
vendored
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
apijson "k8s.io/apimachinery/pkg/util/json"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"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/kubectl/resource"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
type SetLastAppliedOptions struct {
|
||||
FilenameOptions resource.FilenameOptions
|
||||
Selector string
|
||||
InfoList []*resource.Info
|
||||
Mapper meta.RESTMapper
|
||||
Typer runtime.ObjectTyper
|
||||
Namespace string
|
||||
EnforceNamespace bool
|
||||
DryRun bool
|
||||
ShortOutput bool
|
||||
CreateAnnotation bool
|
||||
Output string
|
||||
Codec runtime.Encoder
|
||||
PatchBufferList []PatchBuffer
|
||||
Factory cmdutil.Factory
|
||||
Out io.Writer
|
||||
ErrOut io.Writer
|
||||
}
|
||||
|
||||
type PatchBuffer struct {
|
||||
Patch []byte
|
||||
PatchType types.PatchType
|
||||
}
|
||||
|
||||
var (
|
||||
applySetLastAppliedLong = templates.LongDesc(i18n.T(`
|
||||
Set the latest last-applied-configuration annotations by setting it to match the contents of a file.
|
||||
This results in the last-applied-configuration being updated as though 'kubectl apply -f <file>' was run,
|
||||
without updating any other parts of the object.`))
|
||||
|
||||
applySetLastAppliedExample = templates.Examples(i18n.T(`
|
||||
# Set the last-applied-configuration of a resource to match the contents of a file.
|
||||
kubectl apply set-last-applied -f deploy.yaml
|
||||
|
||||
# Execute set-last-applied against each configuration file in a directory.
|
||||
kubectl apply set-last-applied -f path/
|
||||
|
||||
# Set the last-applied-configuration of a resource to match the contents of a file, will create the annotation if it does not already exist.
|
||||
kubectl apply set-last-applied -f deploy.yaml --create-annotation=true
|
||||
`))
|
||||
)
|
||||
|
||||
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",
|
||||
Short: i18n.T("Set the last-applied-configuration annotation on a live object to match the contents of a file."),
|
||||
Long: applySetLastAppliedLong,
|
||||
Example: applySetLastAppliedExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(options.Complete(f, cmd))
|
||||
cmdutil.CheckErr(options.Validate(f, cmd))
|
||||
cmdutil.CheckErr(options.RunSetLastApplied(f, cmd))
|
||||
},
|
||||
}
|
||||
|
||||
cmdutil.AddDryRunFlag(cmd)
|
||||
cmdutil.AddRecordFlag(cmd)
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
cmd.Flags().BoolVar(&options.CreateAnnotation, "create-annotation", false, "Will create 'last-applied-configuration' annotations if current objects doesn't have one")
|
||||
usage := "that contains the last-applied-configuration annotations"
|
||||
kubectl.AddJsonFilenameFlag(cmd, &options.FilenameOptions.Filenames, "Filename, directory, or URL to files "+usage)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *SetLastAppliedOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
|
||||
o.DryRun = cmdutil.GetFlagBool(cmd, "dry-run")
|
||||
o.Output = cmdutil.GetFlagString(cmd, "output")
|
||||
o.ShortOutput = o.Output == "name"
|
||||
o.Codec = f.JSONEncoder()
|
||||
|
||||
var err error
|
||||
o.Mapper, o.Typer, err = f.UnstructuredObject()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.Namespace, o.EnforceNamespace, err = f.DefaultNamespace()
|
||||
return err
|
||||
}
|
||||
|
||||
func (o *SetLastAppliedOptions) Validate(f cmdutil.Factory, cmd *cobra.Command) error {
|
||||
builder, err := f.NewUnstructuredBuilder(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r := builder.
|
||||
NamespaceParam(o.Namespace).DefaultNamespace().
|
||||
FilenameParam(o.EnforceNamespace, &o.FilenameOptions).
|
||||
Latest().
|
||||
Flatten().
|
||||
Do()
|
||||
err = r.Err()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = r.Visit(func(info *resource.Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
patchBuf, diffBuf, patchType, err := editor.GetApplyPatch(info.VersionedObject, o.Codec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Verify the object exists in the cluster before trying to patch it.
|
||||
if err := info.Get(); err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
return err
|
||||
} else {
|
||||
return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving current configuration of:\n%v\nfrom server for:", info), info.Source, err)
|
||||
}
|
||||
}
|
||||
oringalBuf, err := kubectl.GetOriginalConfiguration(info.Mapping, info.Object)
|
||||
if err != nil {
|
||||
return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving current configuration of:\n%v\nfrom server for:", info), info.Source, err)
|
||||
}
|
||||
if oringalBuf == nil && !o.CreateAnnotation {
|
||||
return cmdutil.UsageErrorf(cmd, "no last-applied-configuration annotation found on resource: %s, to create the annotation, run the command with --create-annotation", info.Name)
|
||||
}
|
||||
|
||||
//only add to PatchBufferList when changed
|
||||
if !bytes.Equal(cmdutil.StripComments(oringalBuf), cmdutil.StripComments(diffBuf)) {
|
||||
p := PatchBuffer{Patch: patchBuf, PatchType: patchType}
|
||||
o.PatchBufferList = append(o.PatchBufferList, p)
|
||||
o.InfoList = append(o.InfoList, info)
|
||||
} else {
|
||||
fmt.Fprintf(o.Out, "set-last-applied %s: no changes required.\n", info.Name)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (o *SetLastAppliedOptions) RunSetLastApplied(f cmdutil.Factory, cmd *cobra.Command) error {
|
||||
for i, patch := range o.PatchBufferList {
|
||||
info := o.InfoList[i]
|
||||
if !o.DryRun {
|
||||
mapping := info.ResourceMapping()
|
||||
client, err := f.UnstructuredClientForMapping(mapping)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
helper := resource.NewHelper(client, mapping)
|
||||
patchedObj, err := helper.Patch(o.Namespace, info.Name, patch.PatchType, patch.Patch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(o.Output) > 0 && !o.ShortOutput {
|
||||
info.Refresh(patchedObj, false)
|
||||
return cmdutil.PrintResourceInfoForCommand(cmd, info, f, o.Out)
|
||||
}
|
||||
cmdutil.PrintSuccess(o.Mapper, o.ShortOutput, o.Out, info.Mapping.Resource, info.Name, o.DryRun, "configured")
|
||||
|
||||
} else {
|
||||
err := o.formatPrinter(o.Output, patch.Patch, o.Out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmdutil.PrintSuccess(o.Mapper, o.ShortOutput, o.Out, info.Mapping.Resource, info.Name, o.DryRun, "configured")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *SetLastAppliedOptions) formatPrinter(output string, buf []byte, w io.Writer) error {
|
||||
yamlOutput, err := yaml.JSONToYAML(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch output {
|
||||
case "json":
|
||||
jsonBuffer := &bytes.Buffer{}
|
||||
err = json.Indent(jsonBuffer, buf, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(w, "%s\n", jsonBuffer.String())
|
||||
case "yaml":
|
||||
fmt.Fprintf(w, "%s\n", string(yamlOutput))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *SetLastAppliedOptions) getPatch(info *resource.Info) ([]byte, []byte, error) {
|
||||
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.VersionedObject)
|
||||
if err != nil {
|
||||
return nil, localFile, err
|
||||
}
|
||||
annotationsMap[api.LastAppliedConfigAnnotation] = string(localFile)
|
||||
metadataMap["annotations"] = annotationsMap
|
||||
objMap["metadata"] = metadataMap
|
||||
jsonString, err := apijson.Marshal(objMap)
|
||||
return jsonString, localFile, err
|
||||
}
|
||||
1115
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/apply_test.go
generated
vendored
Normal file
1115
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/apply_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
171
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/apply_view_last_applied.go
generated
vendored
Normal file
171
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/apply_view_last_applied.go
generated
vendored
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"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"
|
||||
)
|
||||
|
||||
type ViewLastAppliedOptions struct {
|
||||
FilenameOptions resource.FilenameOptions
|
||||
Selector string
|
||||
LastAppliedConfigurationList []string
|
||||
OutputFormat string
|
||||
All bool
|
||||
Factory cmdutil.Factory
|
||||
Out io.Writer
|
||||
ErrOut io.Writer
|
||||
}
|
||||
|
||||
var (
|
||||
applyViewLastAppliedLong = templates.LongDesc(i18n.T(`
|
||||
View the latest last-applied-configuration annotations by type/name or file.
|
||||
|
||||
The default output will be printed to stdout in YAML format. One can use -o option
|
||||
to change output format.`))
|
||||
|
||||
applyViewLastAppliedExample = templates.Examples(i18n.T(`
|
||||
# View the last-applied-configuration annotations by type/name in YAML.
|
||||
kubectl apply view-last-applied deployment/nginx
|
||||
|
||||
# View the last-applied-configuration annotations by file in JSON
|
||||
kubectl apply view-last-applied -f deploy.yaml -o json`))
|
||||
)
|
||||
|
||||
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)",
|
||||
Short: i18n.T("View latest last-applied-configuration annotations of a resource/object"),
|
||||
Long: applyViewLastAppliedLong,
|
||||
Example: applyViewLastAppliedExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(options.ValidateOutputArgs(cmd))
|
||||
cmdutil.CheckErr(options.Complete(f, args))
|
||||
cmdutil.CheckErr(options.Validate(cmd))
|
||||
cmdutil.CheckErr(options.RunApplyViewLastApplied())
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringP("output", "o", "", "Output format. Must be one of yaml|json")
|
||||
cmd.Flags().StringVarP(&options.Selector, "selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
|
||||
cmd.Flags().BoolVar(&options.All, "all", false, "Select all resources in the namespace of the specified resource types")
|
||||
usage := "that contains the last-applied-configuration annotations"
|
||||
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *ViewLastAppliedOptions) Complete(f cmdutil.Factory, args []string) error {
|
||||
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
builder, err := f.NewUnstructuredBuilder(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r := builder.
|
||||
NamespaceParam(cmdNamespace).DefaultNamespace().
|
||||
FilenameParam(enforceNamespace, &o.FilenameOptions).
|
||||
ResourceTypeOrNameArgs(enforceNamespace, args...).
|
||||
SelectAllParam(o.All).
|
||||
SelectorParam(o.Selector).
|
||||
Latest().
|
||||
Flatten().
|
||||
Do()
|
||||
err = r.Err()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = r.Visit(func(info *resource.Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configString, err := kubectl.GetOriginalConfiguration(info.Mapping, info.Object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if configString == nil {
|
||||
return cmdutil.AddSourceToErr(fmt.Sprintf("no last-applied-configuration annotation found on resource: %s\n", info.Name), info.Source, err)
|
||||
}
|
||||
o.LastAppliedConfigurationList = append(o.LastAppliedConfigurationList, string(configString))
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *ViewLastAppliedOptions) Validate(cmd *cobra.Command) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *ViewLastAppliedOptions) RunApplyViewLastApplied() error {
|
||||
for _, str := range o.LastAppliedConfigurationList {
|
||||
switch o.OutputFormat {
|
||||
case "json":
|
||||
jsonBuffer := &bytes.Buffer{}
|
||||
err := json.Indent(jsonBuffer, []byte(str), "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprint(o.Out, string(jsonBuffer.Bytes()))
|
||||
case "yaml":
|
||||
yamlOutput, err := yaml.JSONToYAML([]byte(str))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprint(o.Out, string(yamlOutput))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *ViewLastAppliedOptions) ValidateOutputArgs(cmd *cobra.Command) error {
|
||||
format := cmdutil.GetFlagString(cmd, "output")
|
||||
switch format {
|
||||
case "json":
|
||||
o.OutputFormat = "json"
|
||||
return nil
|
||||
// If flag -o is not specified, use yaml as default
|
||||
case "yaml", "":
|
||||
o.OutputFormat = "yaml"
|
||||
return nil
|
||||
default:
|
||||
return cmdutil.UsageErrorf(cmd, "Unexpected -o output mode: %s, the flag 'output' must be one of yaml|json", format)
|
||||
}
|
||||
}
|
||||
324
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/attach.go
generated
vendored
Normal file
324
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/attach.go
generated
vendored
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
/*
|
||||
Copyright 2014 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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/remotecommand"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
var (
|
||||
attachExample = templates.Examples(i18n.T(`
|
||||
# Get output from running pod 123456-7890, using the first container by default
|
||||
kubectl attach 123456-7890
|
||||
|
||||
# Get output from ruby-container from pod 123456-7890
|
||||
kubectl attach 123456-7890 -c ruby-container
|
||||
|
||||
# Switch to raw terminal mode, sends stdin to 'bash' in ruby-container from pod 123456-7890
|
||||
# and sends stdout/stderr from 'bash' back to the client
|
||||
kubectl attach 123456-7890 -c ruby-container -i -t
|
||||
|
||||
# Get output from the first pod of a ReplicaSet named nginx
|
||||
kubectl attach rs/nginx
|
||||
`))
|
||||
)
|
||||
|
||||
const (
|
||||
defaultPodAttachTimeout = 60 * time.Second
|
||||
defaultPodLogsTimeout = 20 * time.Second
|
||||
)
|
||||
|
||||
func NewCmdAttach(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *cobra.Command {
|
||||
options := &AttachOptions{
|
||||
StreamOptions: StreamOptions{
|
||||
In: cmdIn,
|
||||
Out: cmdOut,
|
||||
Err: cmdErr,
|
||||
},
|
||||
|
||||
Attach: &DefaultRemoteAttach{},
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "attach (POD | TYPE/NAME) -c CONTAINER",
|
||||
Short: i18n.T("Attach to a running container"),
|
||||
Long: "Attach to a process that is already running inside an existing container.",
|
||||
Example: attachExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(options.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(options.Validate())
|
||||
cmdutil.CheckErr(options.Run())
|
||||
},
|
||||
}
|
||||
cmdutil.AddPodRunningTimeoutFlag(cmd, defaultPodAttachTimeout)
|
||||
cmd.Flags().StringVarP(&options.ContainerName, "container", "c", "", "Container name. If omitted, the first container in the pod will be chosen")
|
||||
cmd.Flags().BoolVarP(&options.Stdin, "stdin", "i", false, "Pass stdin to the container")
|
||||
cmd.Flags().BoolVarP(&options.TTY, "tty", "t", false, "Stdin is a TTY")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// RemoteAttach defines the interface accepted by the Attach command - provided for test stubbing
|
||||
type RemoteAttach interface {
|
||||
Attach(method string, url *url.URL, config *restclient.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool, terminalSizeQueue remotecommand.TerminalSizeQueue) error
|
||||
}
|
||||
|
||||
// DefaultRemoteAttach is the standard implementation of attaching
|
||||
type DefaultRemoteAttach struct{}
|
||||
|
||||
func (*DefaultRemoteAttach) Attach(method string, url *url.URL, config *restclient.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool, terminalSizeQueue remotecommand.TerminalSizeQueue) error {
|
||||
exec, err := remotecommand.NewSPDYExecutor(config, method, url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return exec.Stream(remotecommand.StreamOptions{
|
||||
Stdin: stdin,
|
||||
Stdout: stdout,
|
||||
Stderr: stderr,
|
||||
Tty: tty,
|
||||
TerminalSizeQueue: terminalSizeQueue,
|
||||
})
|
||||
}
|
||||
|
||||
// AttachOptions declare the arguments accepted by the Exec command
|
||||
type AttachOptions struct {
|
||||
StreamOptions
|
||||
|
||||
CommandName string
|
||||
|
||||
Pod *api.Pod
|
||||
|
||||
Attach RemoteAttach
|
||||
PodClient coreclient.PodsGetter
|
||||
GetPodTimeout time.Duration
|
||||
Config *restclient.Config
|
||||
}
|
||||
|
||||
// Complete verifies command line arguments and loads data from the command environment
|
||||
func (p *AttachOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, argsIn []string) error {
|
||||
if len(argsIn) == 0 {
|
||||
return cmdutil.UsageErrorf(cmd, "at least 1 argument is required for attach")
|
||||
}
|
||||
if len(argsIn) > 2 {
|
||||
return cmdutil.UsageErrorf(cmd, "expected POD, TYPE/NAME, or TYPE NAME, (at most 2 arguments) saw %d: %v", len(argsIn), argsIn)
|
||||
}
|
||||
|
||||
namespace, _, err := f.DefaultNamespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.GetPodTimeout, err = cmdutil.GetPodRunningTimeoutFlag(cmd)
|
||||
if err != nil {
|
||||
return cmdutil.UsageErrorf(cmd, err.Error())
|
||||
}
|
||||
|
||||
builder := f.NewBuilder(true).
|
||||
NamespaceParam(namespace).DefaultNamespace()
|
||||
|
||||
switch len(argsIn) {
|
||||
case 1:
|
||||
builder.ResourceNames("pods", argsIn[0])
|
||||
case 2:
|
||||
builder.ResourceNames(argsIn[0], argsIn[1])
|
||||
}
|
||||
|
||||
obj, err := builder.Do().Object()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
attachablePod, err := f.AttachablePodForObject(obj, p.GetPodTimeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.PodName = attachablePod.Name
|
||||
p.Namespace = namespace
|
||||
|
||||
config, err := f.ClientConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.Config = config
|
||||
|
||||
clientset, err := f.ClientSet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.PodClient = clientset.Core()
|
||||
|
||||
if p.CommandName == "" {
|
||||
p.CommandName = cmd.CommandPath()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate checks that the provided attach options are specified.
|
||||
func (p *AttachOptions) Validate() error {
|
||||
allErrs := []error{}
|
||||
if len(p.PodName) == 0 {
|
||||
allErrs = append(allErrs, errors.New("pod name must be specified"))
|
||||
}
|
||||
if p.Out == nil || p.Err == nil {
|
||||
allErrs = append(allErrs, errors.New("both output and error output must be provided"))
|
||||
}
|
||||
if p.Attach == nil || p.PodClient == nil || p.Config == nil {
|
||||
allErrs = append(allErrs, errors.New("client, client config, and attach must be provided"))
|
||||
}
|
||||
return utilerrors.NewAggregate(allErrs)
|
||||
}
|
||||
|
||||
// Run executes a validated remote execution against a pod.
|
||||
func (p *AttachOptions) Run() error {
|
||||
if p.Pod == nil {
|
||||
pod, err := p.PodClient.Pods(p.Namespace).Get(p.PodName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if pod.Status.Phase == api.PodSucceeded || pod.Status.Phase == api.PodFailed {
|
||||
return fmt.Errorf("cannot attach a container in a completed pod; current phase is %s", pod.Status.Phase)
|
||||
}
|
||||
|
||||
p.Pod = pod
|
||||
// TODO: convert this to a clean "wait" behavior
|
||||
}
|
||||
pod := p.Pod
|
||||
|
||||
// check for TTY
|
||||
containerToAttach, err := p.containerToAttachTo(pod)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot attach to the container: %v", err)
|
||||
}
|
||||
if p.TTY && !containerToAttach.TTY {
|
||||
p.TTY = false
|
||||
if p.Err != nil {
|
||||
fmt.Fprintf(p.Err, "Unable to use a TTY - container %s did not allocate one\n", containerToAttach.Name)
|
||||
}
|
||||
} else if !p.TTY && containerToAttach.TTY {
|
||||
// the container was launched with a TTY, so we have to force a TTY here, otherwise you'll get
|
||||
// an error "Unrecognized input header"
|
||||
p.TTY = true
|
||||
}
|
||||
|
||||
// ensure we can recover the terminal while attached
|
||||
t := p.setupTTY()
|
||||
|
||||
// save p.Err so we can print the command prompt message below
|
||||
stderr := p.Err
|
||||
|
||||
var sizeQueue remotecommand.TerminalSizeQueue
|
||||
if t.Raw {
|
||||
if size := t.GetSize(); size != nil {
|
||||
// fake resizing +1 and then back to normal so that attach-detach-reattach will result in the
|
||||
// screen being redrawn
|
||||
sizePlusOne := *size
|
||||
sizePlusOne.Width++
|
||||
sizePlusOne.Height++
|
||||
|
||||
// this call spawns a goroutine to monitor/update the terminal size
|
||||
sizeQueue = t.MonitorSize(&sizePlusOne, size)
|
||||
}
|
||||
|
||||
// unset p.Err if it was previously set because both stdout and stderr go over p.Out when tty is
|
||||
// true
|
||||
p.Err = nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
// TODO: consider abstracting into a client invocation or client helper
|
||||
req := restClient.Post().
|
||||
Resource("pods").
|
||||
Name(pod.Name).
|
||||
Namespace(pod.Namespace).
|
||||
SubResource("attach")
|
||||
req.VersionedParams(&api.PodAttachOptions{
|
||||
Container: containerToAttach.Name,
|
||||
Stdin: p.Stdin,
|
||||
Stdout: p.Out != nil,
|
||||
Stderr: p.Err != nil,
|
||||
TTY: t.Raw,
|
||||
}, api.ParameterCodec)
|
||||
|
||||
return p.Attach.Attach("POST", req.URL(), p.Config, p.In, p.Out, p.Err, t.Raw, sizeQueue)
|
||||
}
|
||||
|
||||
if err := t.Safe(fn); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.Stdin && t.Raw && pod.Spec.RestartPolicy == api.RestartPolicyAlways {
|
||||
fmt.Fprintf(p.Out, "Session ended, resume using '%s %s -c %s -i -t' command when the pod is running\n", p.CommandName, pod.Name, containerToAttach.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// containerToAttach returns a reference to the container to attach to, given
|
||||
// by name or the first container if name is empty.
|
||||
func (p *AttachOptions) containerToAttachTo(pod *api.Pod) (*api.Container, error) {
|
||||
if len(p.ContainerName) > 0 {
|
||||
for i := range pod.Spec.Containers {
|
||||
if pod.Spec.Containers[i].Name == p.ContainerName {
|
||||
return &pod.Spec.Containers[i], nil
|
||||
}
|
||||
}
|
||||
for i := range pod.Spec.InitContainers {
|
||||
if pod.Spec.InitContainers[i].Name == p.ContainerName {
|
||||
return &pod.Spec.InitContainers[i], nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("container not found (%s)", p.ContainerName)
|
||||
}
|
||||
|
||||
glog.V(4).Infof("defaulting container name to %s", pod.Spec.Containers[0].Name)
|
||||
return &pod.Spec.Containers[0], nil
|
||||
}
|
||||
|
||||
// GetContainerName returns the name of the container to attach to, with a fallback.
|
||||
func (p *AttachOptions) GetContainerName(pod *api.Pod) (string, error) {
|
||||
c, err := p.containerToAttachTo(pod)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return c.Name, nil
|
||||
}
|
||||
381
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/attach_test.go
generated
vendored
Normal file
381
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/attach_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,381 @@
|
|||
/*
|
||||
Copyright 2014 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"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/rest/fake"
|
||||
"k8s.io/client-go/tools/remotecommand"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
)
|
||||
|
||||
type fakeRemoteAttach struct {
|
||||
method string
|
||||
url *url.URL
|
||||
err error
|
||||
}
|
||||
|
||||
func (f *fakeRemoteAttach) Attach(method string, url *url.URL, config *restclient.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool, terminalSizeQueue remotecommand.TerminalSizeQueue) error {
|
||||
f.method = method
|
||||
f.url = url
|
||||
return f.err
|
||||
}
|
||||
|
||||
func TestPodAndContainerAttach(t *testing.T) {
|
||||
tests := []struct {
|
||||
args []string
|
||||
p *AttachOptions
|
||||
name string
|
||||
expectError bool
|
||||
expectedPod string
|
||||
expectedContainer string
|
||||
timeout time.Duration
|
||||
obj runtime.Object
|
||||
}{
|
||||
{
|
||||
p: &AttachOptions{},
|
||||
expectError: true,
|
||||
name: "empty",
|
||||
timeout: 1,
|
||||
},
|
||||
{
|
||||
p: &AttachOptions{},
|
||||
args: []string{"one", "two", "three"},
|
||||
expectError: true,
|
||||
name: "too many args",
|
||||
timeout: 2,
|
||||
},
|
||||
{
|
||||
p: &AttachOptions{},
|
||||
args: []string{"foo"},
|
||||
expectedPod: "foo",
|
||||
name: "no container, no flags",
|
||||
obj: attachPod(),
|
||||
timeout: defaultPodLogsTimeout,
|
||||
},
|
||||
{
|
||||
p: &AttachOptions{StreamOptions: StreamOptions{ContainerName: "bar"}},
|
||||
args: []string{"foo"},
|
||||
expectedPod: "foo",
|
||||
expectedContainer: "bar",
|
||||
name: "container in flag",
|
||||
obj: attachPod(),
|
||||
timeout: 10000000,
|
||||
},
|
||||
{
|
||||
p: &AttachOptions{StreamOptions: StreamOptions{ContainerName: "initfoo"}},
|
||||
args: []string{"foo"},
|
||||
expectedPod: "foo",
|
||||
expectedContainer: "initfoo",
|
||||
name: "init container in flag",
|
||||
obj: attachPod(),
|
||||
timeout: 30,
|
||||
},
|
||||
{
|
||||
p: &AttachOptions{StreamOptions: StreamOptions{ContainerName: "bar"}},
|
||||
args: []string{"foo", "-c", "wrong"},
|
||||
expectError: true,
|
||||
name: "non-existing container in flag",
|
||||
obj: attachPod(),
|
||||
timeout: 10,
|
||||
},
|
||||
{
|
||||
p: &AttachOptions{},
|
||||
args: []string{"pods", "foo"},
|
||||
expectedPod: "foo",
|
||||
name: "no container, no flags, pods and name",
|
||||
obj: attachPod(),
|
||||
timeout: 10000,
|
||||
},
|
||||
{
|
||||
p: &AttachOptions{},
|
||||
args: []string{"pod/foo"},
|
||||
expectedPod: "foo",
|
||||
name: "no container, no flags, pod/name",
|
||||
obj: attachPod(),
|
||||
timeout: 1,
|
||||
},
|
||||
{
|
||||
p: &AttachOptions{},
|
||||
args: []string{"pod/foo"},
|
||||
expectedPod: "foo",
|
||||
name: "invalid get pod timeout value",
|
||||
obj: attachPod(),
|
||||
expectError: true,
|
||||
timeout: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
f, tf, codec, ns := cmdtesting.NewAPIFactory()
|
||||
tf.Client = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: ns,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
if test.obj != nil {
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, test.obj)}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
tf.ClientConfig = defaultClientConfig()
|
||||
|
||||
cmd := &cobra.Command{}
|
||||
options := test.p
|
||||
cmdutil.AddPodRunningTimeoutFlag(cmd, test.timeout)
|
||||
|
||||
err := options.Complete(f, cmd, test.args)
|
||||
if test.expectError && err == nil {
|
||||
t.Errorf("%s: unexpected non-error", test.name)
|
||||
}
|
||||
if !test.expectError && err != nil {
|
||||
t.Errorf("%s: unexpected error: %v", test.name, err)
|
||||
}
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if options.PodName != test.expectedPod {
|
||||
t.Errorf("%s: expected: %s, got: %s", test.name, test.expectedPod, options.PodName)
|
||||
}
|
||||
if options.ContainerName != test.expectedContainer {
|
||||
t.Errorf("%s: expected: %s, got: %s", test.name, test.expectedContainer, options.ContainerName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttach(t *testing.T) {
|
||||
version := api.Registry.GroupOrDie(api.GroupName).GroupVersion.Version
|
||||
tests := []struct {
|
||||
name, version, podPath, fetchPodPath, attachPath, container string
|
||||
pod *api.Pod
|
||||
remoteAttachErr bool
|
||||
exepctedErr string
|
||||
}{
|
||||
{
|
||||
name: "pod attach",
|
||||
version: version,
|
||||
podPath: "/api/" + version + "/namespaces/test/pods/foo",
|
||||
fetchPodPath: "/namespaces/test/pods/foo",
|
||||
attachPath: "/api/" + version + "/namespaces/test/pods/foo/attach",
|
||||
pod: attachPod(),
|
||||
container: "bar",
|
||||
},
|
||||
{
|
||||
name: "pod attach error",
|
||||
version: version,
|
||||
podPath: "/api/" + version + "/namespaces/test/pods/foo",
|
||||
fetchPodPath: "/namespaces/test/pods/foo",
|
||||
attachPath: "/api/" + version + "/namespaces/test/pods/foo/attach",
|
||||
pod: attachPod(),
|
||||
remoteAttachErr: true,
|
||||
container: "bar",
|
||||
exepctedErr: "attach error",
|
||||
},
|
||||
{
|
||||
name: "container not found error",
|
||||
version: version,
|
||||
podPath: "/api/" + version + "/namespaces/test/pods/foo",
|
||||
fetchPodPath: "/namespaces/test/pods/foo",
|
||||
attachPath: "/api/" + version + "/namespaces/test/pods/foo/attach",
|
||||
pod: attachPod(),
|
||||
container: "foo",
|
||||
exepctedErr: "cannot attach to the container: container not found (foo)",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
f, tf, codec, ns := cmdtesting.NewAPIFactory()
|
||||
tf.Client = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: ns,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == test.podPath && m == "GET":
|
||||
body := objBody(codec, test.pod)
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: body}, nil
|
||||
case p == test.fetchPodPath && m == "GET":
|
||||
body := objBody(codec, test.pod)
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: body}, nil
|
||||
default:
|
||||
// Ensures no GET is performed when deleting by name
|
||||
t.Errorf("%s: unexpected request: %s %#v\n%#v", p, req.Method, req.URL, req)
|
||||
return nil, fmt.Errorf("unexpected request")
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
tf.ClientConfig = &restclient.Config{APIPath: "/api", ContentConfig: restclient.ContentConfig{NegotiatedSerializer: api.Codecs, GroupVersion: &schema.GroupVersion{Version: test.version}}}
|
||||
bufOut := bytes.NewBuffer([]byte{})
|
||||
bufErr := bytes.NewBuffer([]byte{})
|
||||
bufIn := bytes.NewBuffer([]byte{})
|
||||
remoteAttach := &fakeRemoteAttach{}
|
||||
if test.remoteAttachErr {
|
||||
remoteAttach.err = fmt.Errorf("attach error")
|
||||
}
|
||||
params := &AttachOptions{
|
||||
StreamOptions: StreamOptions{
|
||||
ContainerName: test.container,
|
||||
In: bufIn,
|
||||
Out: bufOut,
|
||||
Err: bufErr,
|
||||
},
|
||||
Attach: remoteAttach,
|
||||
GetPodTimeout: 1000,
|
||||
}
|
||||
cmd := &cobra.Command{}
|
||||
cmdutil.AddPodRunningTimeoutFlag(cmd, 1000)
|
||||
if err := params.Complete(f, cmd, []string{"foo"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err := params.Run()
|
||||
if test.exepctedErr != "" && err.Error() != test.exepctedErr {
|
||||
t.Errorf("%s: Unexpected exec error: %v", test.name, err)
|
||||
continue
|
||||
}
|
||||
if test.exepctedErr == "" && err != nil {
|
||||
t.Errorf("%s: Unexpected error: %v", test.name, err)
|
||||
continue
|
||||
}
|
||||
if test.exepctedErr != "" {
|
||||
continue
|
||||
}
|
||||
if remoteAttach.url.Path != test.attachPath {
|
||||
t.Errorf("%s: Did not get expected path for exec request: %q %q", test.name, test.attachPath, remoteAttach.url.Path)
|
||||
continue
|
||||
}
|
||||
if remoteAttach.method != "POST" {
|
||||
t.Errorf("%s: Did not get method for attach request: %s", test.name, remoteAttach.method)
|
||||
}
|
||||
if remoteAttach.url.Query().Get("container") != "bar" {
|
||||
t.Errorf("%s: Did not have query parameters: %s", test.name, remoteAttach.url.Query())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttachWarnings(t *testing.T) {
|
||||
version := api.Registry.GroupOrDie(api.GroupName).GroupVersion.Version
|
||||
tests := []struct {
|
||||
name, container, version, podPath, fetchPodPath, expectedErr, expectedOut string
|
||||
pod *api.Pod
|
||||
stdin, tty bool
|
||||
}{
|
||||
{
|
||||
name: "fallback tty if not supported",
|
||||
version: version,
|
||||
podPath: "/api/" + version + "/namespaces/test/pods/foo",
|
||||
fetchPodPath: "/namespaces/test/pods/foo",
|
||||
pod: attachPod(),
|
||||
stdin: true,
|
||||
tty: true,
|
||||
expectedErr: "Unable to use a TTY - container bar did not allocate one",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
f, tf, codec, ns := cmdtesting.NewAPIFactory()
|
||||
tf.Client = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: ns,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == test.podPath && m == "GET":
|
||||
body := objBody(codec, test.pod)
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: body}, nil
|
||||
case p == test.fetchPodPath && m == "GET":
|
||||
body := objBody(codec, test.pod)
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: body}, nil
|
||||
default:
|
||||
t.Errorf("%s: unexpected request: %s %#v\n%#v", test.name, req.Method, req.URL, req)
|
||||
return nil, fmt.Errorf("unexpected request")
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
tf.ClientConfig = &restclient.Config{APIPath: "/api", ContentConfig: restclient.ContentConfig{NegotiatedSerializer: api.Codecs, GroupVersion: &schema.GroupVersion{Version: test.version}}}
|
||||
bufOut := bytes.NewBuffer([]byte{})
|
||||
bufErr := bytes.NewBuffer([]byte{})
|
||||
bufIn := bytes.NewBuffer([]byte{})
|
||||
ex := &fakeRemoteAttach{}
|
||||
params := &AttachOptions{
|
||||
StreamOptions: StreamOptions{
|
||||
ContainerName: test.container,
|
||||
In: bufIn,
|
||||
Out: bufOut,
|
||||
Err: bufErr,
|
||||
Stdin: test.stdin,
|
||||
TTY: test.tty,
|
||||
},
|
||||
Attach: ex,
|
||||
GetPodTimeout: 1000,
|
||||
}
|
||||
cmd := &cobra.Command{}
|
||||
cmdutil.AddPodRunningTimeoutFlag(cmd, 1000)
|
||||
if err := params.Complete(f, cmd, []string{"foo"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := params.Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if test.stdin && test.tty {
|
||||
if !test.pod.Spec.Containers[0].TTY {
|
||||
if !strings.Contains(bufErr.String(), test.expectedErr) {
|
||||
t.Errorf("%s: Expected TTY fallback warning for attach request: %s", test.name, bufErr.String())
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func attachPod() *api.Pod {
|
||||
return &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{
|
||||
{
|
||||
Name: "bar",
|
||||
},
|
||||
},
|
||||
InitContainers: []api.Container{
|
||||
{
|
||||
Name: "initfoo",
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: api.PodStatus{
|
||||
Phase: api.PodRunning,
|
||||
},
|
||||
}
|
||||
}
|
||||
59
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/auth/BUILD
generated
vendored
Normal file
59
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/auth/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"auth.go",
|
||||
"cani.go",
|
||||
"reconcile.go",
|
||||
],
|
||||
visibility = [
|
||||
"//build/visible_to:pkg_kubectl_cmd_auth_CONSUMERS",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/apis/authorization:go_default_library",
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion:go_default_library",
|
||||
"//pkg/kubectl/cmd/templates:go_default_library",
|
||||
"//pkg/kubectl/cmd/util:go_default_library",
|
||||
"//pkg/kubectl/resource:go_default_library",
|
||||
"//pkg/registry/rbac/reconciliation:go_default_library",
|
||||
"//vendor/github.com/golang/glog: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/schema:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = [
|
||||
"//build/visible_to:pkg_kubectl_cmd_auth_CONSUMERS",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["cani_test.go"],
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/kubectl/cmd/testing:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest/fake:go_default_library",
|
||||
],
|
||||
)
|
||||
40
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/auth/auth.go
generated
vendored
Normal file
40
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/auth/auth.go
generated
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
Copyright 2014 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 auth
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
)
|
||||
|
||||
func NewCmdAuth(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
|
||||
// Parent command to which all subcommands are added.
|
||||
cmds := &cobra.Command{
|
||||
Use: "auth",
|
||||
Short: "Inspect authorization",
|
||||
Long: `Inspect authorization`,
|
||||
Run: cmdutil.DefaultSubCommandRun(errOut),
|
||||
}
|
||||
|
||||
cmds.AddCommand(NewCmdCanI(f, out, errOut))
|
||||
cmds.AddCommand(NewCmdReconcile(f, out, errOut))
|
||||
|
||||
return cmds
|
||||
}
|
||||
240
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/auth/cani.go
generated
vendored
Normal file
240
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/auth/cani.go
generated
vendored
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
|
||||
internalauthorizationclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
)
|
||||
|
||||
// CanIOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of
|
||||
// referencing the cmd.Flags()
|
||||
type CanIOptions struct {
|
||||
AllNamespaces bool
|
||||
Quiet bool
|
||||
Namespace string
|
||||
SelfSARClient internalauthorizationclient.SelfSubjectAccessReviewsGetter
|
||||
|
||||
Verb string
|
||||
Resource schema.GroupVersionResource
|
||||
NonResourceURL string
|
||||
Subresource string
|
||||
ResourceName string
|
||||
|
||||
Out io.Writer
|
||||
Err io.Writer
|
||||
}
|
||||
|
||||
var (
|
||||
canILong = templates.LongDesc(`
|
||||
Check whether an action is allowed.
|
||||
|
||||
VERB is a logical Kubernetes API verb like 'get', 'list', 'watch', 'delete', etc.
|
||||
TYPE is a Kubernetes resource. Shortcuts and groups will be resolved.
|
||||
NONRESOURCEURL is a partial URL starts with "/".
|
||||
NAME is the name of a particular Kubernetes resource.`)
|
||||
|
||||
canIExample = templates.Examples(`
|
||||
# Check to see if I can create pods in any namespace
|
||||
kubectl auth can-i create pods --all-namespaces
|
||||
|
||||
# Check to see if I can list deployments in my current namespace
|
||||
kubectl auth can-i list deployments.extensions
|
||||
|
||||
# Check to see if I can do everything in my current namespace ("*" means all)
|
||||
kubectl auth can-i '*' '*'
|
||||
|
||||
# Check to see if I can get the job named "bar" in namespace "foo"
|
||||
kubectl auth can-i list jobs.batch/bar -n foo
|
||||
|
||||
# Check to see if I can read pod logs
|
||||
kubectl auth can-i get pods --subresource=log
|
||||
|
||||
# Check to see if I can access the URL /logs/
|
||||
kubectl auth can-i get /logs/`)
|
||||
)
|
||||
|
||||
func NewCmdCanI(f cmdutil.Factory, out, err io.Writer) *cobra.Command {
|
||||
o := &CanIOptions{
|
||||
Out: out,
|
||||
Err: err,
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "can-i VERB [TYPE | TYPE/NAME | NONRESOURCEURL]",
|
||||
Short: "Check whether an action is allowed",
|
||||
Long: canILong,
|
||||
Example: canIExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, args))
|
||||
cmdutil.CheckErr(o.Validate())
|
||||
|
||||
allowed, err := o.RunAccessCheck()
|
||||
if err == nil {
|
||||
if o.Quiet && !allowed {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVar(&o.AllNamespaces, "all-namespaces", o.AllNamespaces, "If true, check the specified action in all namespaces.")
|
||||
cmd.Flags().BoolVarP(&o.Quiet, "quiet", "q", o.Quiet, "If true, suppress output and just return the exit code.")
|
||||
cmd.Flags().StringVar(&o.Subresource, "subresource", "", "SubResource such as pod/log or deployment/scale")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *CanIOptions) Complete(f cmdutil.Factory, args []string) error {
|
||||
if o.Quiet {
|
||||
o.Out = ioutil.Discard
|
||||
}
|
||||
|
||||
switch len(args) {
|
||||
case 2:
|
||||
o.Verb = args[0]
|
||||
if strings.HasPrefix(args[1], "/") {
|
||||
o.NonResourceURL = args[1]
|
||||
break
|
||||
}
|
||||
resourceTokens := strings.SplitN(args[1], "/", 2)
|
||||
restMapper, _ := f.Object()
|
||||
o.Resource = o.resourceFor(restMapper, resourceTokens[0])
|
||||
if len(resourceTokens) > 1 {
|
||||
o.ResourceName = resourceTokens[1]
|
||||
}
|
||||
default:
|
||||
return errors.New("you must specify two or three arguments: verb, resource, and optional resourceName")
|
||||
}
|
||||
|
||||
var err error
|
||||
client, err := f.ClientSet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.SelfSARClient = client.Authorization()
|
||||
|
||||
o.Namespace = ""
|
||||
if !o.AllNamespaces {
|
||||
o.Namespace, _, err = f.DefaultNamespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *CanIOptions) Validate() error {
|
||||
if o.NonResourceURL != "" {
|
||||
if o.Subresource != "" {
|
||||
return fmt.Errorf("--subresource can not be used with nonResourceURL")
|
||||
}
|
||||
if o.Resource != (schema.GroupVersionResource{}) || o.ResourceName != "" {
|
||||
return fmt.Errorf("nonResourceURL and Resource can not specified together")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *CanIOptions) RunAccessCheck() (bool, error) {
|
||||
var sar *authorizationapi.SelfSubjectAccessReview
|
||||
if o.NonResourceURL == "" {
|
||||
sar = &authorizationapi.SelfSubjectAccessReview{
|
||||
Spec: authorizationapi.SelfSubjectAccessReviewSpec{
|
||||
ResourceAttributes: &authorizationapi.ResourceAttributes{
|
||||
Namespace: o.Namespace,
|
||||
Verb: o.Verb,
|
||||
Group: o.Resource.Group,
|
||||
Resource: o.Resource.Resource,
|
||||
Subresource: o.Subresource,
|
||||
Name: o.ResourceName,
|
||||
},
|
||||
},
|
||||
}
|
||||
} else {
|
||||
sar = &authorizationapi.SelfSubjectAccessReview{
|
||||
Spec: authorizationapi.SelfSubjectAccessReviewSpec{
|
||||
NonResourceAttributes: &authorizationapi.NonResourceAttributes{
|
||||
Verb: o.Verb,
|
||||
Path: o.NonResourceURL,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
response, err := o.SelfSARClient.SelfSubjectAccessReviews().Create(sar)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if response.Status.Allowed {
|
||||
fmt.Fprintln(o.Out, "yes")
|
||||
} else {
|
||||
fmt.Fprint(o.Out, "no")
|
||||
if len(response.Status.Reason) > 0 {
|
||||
fmt.Fprintf(o.Out, " - %v", response.Status.Reason)
|
||||
}
|
||||
if len(response.Status.EvaluationError) > 0 {
|
||||
fmt.Fprintf(o.Out, " - %v", response.Status.EvaluationError)
|
||||
}
|
||||
fmt.Fprintln(o.Out)
|
||||
}
|
||||
|
||||
return response.Status.Allowed, nil
|
||||
}
|
||||
|
||||
func (o *CanIOptions) resourceFor(mapper meta.RESTMapper, resourceArg string) schema.GroupVersionResource {
|
||||
if resourceArg == "*" {
|
||||
return schema.GroupVersionResource{Resource: resourceArg}
|
||||
}
|
||||
|
||||
fullySpecifiedGVR, groupResource := schema.ParseResourceArg(strings.ToLower(resourceArg))
|
||||
gvr := schema.GroupVersionResource{}
|
||||
if fullySpecifiedGVR != nil {
|
||||
gvr, _ = mapper.ResourceFor(*fullySpecifiedGVR)
|
||||
}
|
||||
if gvr.Empty() {
|
||||
var err error
|
||||
gvr, err = mapper.ResourceFor(groupResource.WithVersion(""))
|
||||
if err != nil {
|
||||
if len(groupResource.Group) == 0 {
|
||||
fmt.Fprintf(o.Err, "Warning: the server doesn't have a resource type '%s'\n", groupResource.Resource)
|
||||
} else {
|
||||
fmt.Fprintf(o.Err, "Warning: the server doesn't have a resource type '%s' in group '%s'\n", groupResource.Resource, groupResource.Group)
|
||||
}
|
||||
return schema.GroupVersionResource{Resource: resourceArg}
|
||||
}
|
||||
}
|
||||
|
||||
return gvr
|
||||
}
|
||||
176
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/auth/cani_test.go
generated
vendored
Normal file
176
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/auth/cani_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/rest/fake"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||
)
|
||||
|
||||
func TestRunAccessCheck(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
o *CanIOptions
|
||||
args []string
|
||||
allowed bool
|
||||
serverErr error
|
||||
|
||||
expectedBodyStrings []string
|
||||
}{
|
||||
{
|
||||
name: "restmapping for args",
|
||||
o: &CanIOptions{},
|
||||
args: []string{"get", "replicaset"},
|
||||
allowed: true,
|
||||
expectedBodyStrings: []string{
|
||||
`{"resourceAttributes":{"namespace":"test","verb":"get","group":"extensions","resource":"replicasets"}}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "simple success",
|
||||
o: &CanIOptions{},
|
||||
args: []string{"get", "deployments.extensions/foo"},
|
||||
allowed: true,
|
||||
expectedBodyStrings: []string{
|
||||
`{"resourceAttributes":{"namespace":"test","verb":"get","group":"extensions","resource":"deployments","name":"foo"}}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "all namespaces",
|
||||
o: &CanIOptions{
|
||||
AllNamespaces: true,
|
||||
},
|
||||
args: []string{"get", "deployments.extensions/foo"},
|
||||
allowed: true,
|
||||
expectedBodyStrings: []string{
|
||||
`{"resourceAttributes":{"verb":"get","group":"extensions","resource":"deployments","name":"foo"}}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "disallowed",
|
||||
o: &CanIOptions{
|
||||
AllNamespaces: true,
|
||||
},
|
||||
args: []string{"get", "deployments.extensions/foo"},
|
||||
allowed: false,
|
||||
expectedBodyStrings: []string{
|
||||
`{"resourceAttributes":{"verb":"get","group":"extensions","resource":"deployments","name":"foo"}}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "forcedError",
|
||||
o: &CanIOptions{
|
||||
AllNamespaces: true,
|
||||
},
|
||||
args: []string{"get", "deployments.extensions/foo"},
|
||||
allowed: false,
|
||||
serverErr: fmt.Errorf("forcedError"),
|
||||
expectedBodyStrings: []string{
|
||||
`{"resourceAttributes":{"verb":"get","group":"extensions","resource":"deployments","name":"foo"}}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sub resource",
|
||||
o: &CanIOptions{
|
||||
AllNamespaces: true,
|
||||
Subresource: "log",
|
||||
},
|
||||
args: []string{"get", "pods"},
|
||||
allowed: true,
|
||||
expectedBodyStrings: []string{
|
||||
`{"resourceAttributes":{"verb":"get","resource":"pods","subresource":"log"}}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nonResourceURL",
|
||||
o: &CanIOptions{},
|
||||
args: []string{"get", "/logs"},
|
||||
allowed: true,
|
||||
expectedBodyStrings: []string{
|
||||
`{"nonResourceAttributes":{"path":"/logs","verb":"get"}}`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test.o.Out = ioutil.Discard
|
||||
test.o.Err = ioutil.Discard
|
||||
|
||||
f, tf, _, ns := cmdtesting.NewAPIFactory()
|
||||
tf.Client = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: ns,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
if req.URL.Path != "/apis/authorization.k8s.io/v1/selfsubjectaccessreviews" {
|
||||
t.Errorf("%s: %v", test.name, req.URL.Path)
|
||||
return nil, nil
|
||||
}
|
||||
bodyBits, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
t.Errorf("%s: %v", test.name, err)
|
||||
return nil, nil
|
||||
}
|
||||
body := string(bodyBits)
|
||||
|
||||
for _, expectedBody := range test.expectedBodyStrings {
|
||||
if !strings.Contains(body, expectedBody) {
|
||||
t.Errorf("%s expecting %s in %s", test.name, expectedBody, body)
|
||||
}
|
||||
}
|
||||
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: ioutil.NopCloser(bytes.NewBufferString(
|
||||
fmt.Sprintf(`{"kind":"SelfSubjectAccessReview","apiVersion":"authorization.k8s.io/v1","status":{"allowed":%v}}`, test.allowed),
|
||||
)),
|
||||
},
|
||||
test.serverErr
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &api.Registry.GroupOrDie(api.GroupName).GroupVersion}}
|
||||
|
||||
if err := test.o.Complete(f, test.args); err != nil {
|
||||
t.Errorf("%s: %v", test.name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
actualAllowed, err := test.o.RunAccessCheck()
|
||||
switch {
|
||||
case test.serverErr == nil && err == nil:
|
||||
// pass
|
||||
case err != nil && test.serverErr != nil && strings.Contains(err.Error(), test.serverErr.Error()):
|
||||
// pass
|
||||
default:
|
||||
t.Errorf("%s: expected %v, got %v", test.name, test.serverErr, err)
|
||||
continue
|
||||
}
|
||||
if actualAllowed != test.allowed {
|
||||
t.Errorf("%s: expected %v, got %v", test.name, test.allowed, actualAllowed)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
223
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/auth/reconcile.go
generated
vendored
Normal file
223
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/auth/reconcile.go
generated
vendored
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
internalcoreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
|
||||
internalrbacclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion"
|
||||
"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/registry/rbac/reconciliation"
|
||||
)
|
||||
|
||||
// ReconcileOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of
|
||||
// referencing the cmd.Flags()
|
||||
type ReconcileOptions struct {
|
||||
ResourceBuilder *resource.Builder
|
||||
RBACClient internalrbacclient.RbacInterface
|
||||
CoreClient internalcoreclient.CoreInterface
|
||||
|
||||
Print func(*resource.Info) error
|
||||
|
||||
Out io.Writer
|
||||
Err io.Writer
|
||||
}
|
||||
|
||||
var (
|
||||
reconcileLong = templates.LongDesc(`
|
||||
Reconciles rules for RBAC Role, RoleBinding, ClusterRole, and ClusterRole binding objects.
|
||||
|
||||
This is preferred to 'apply' for RBAC resources so that proper rule coverage checks are done.`)
|
||||
|
||||
reconcileExample = templates.Examples(`
|
||||
# Reconcile rbac resources from a file
|
||||
kubectl auth reconcile -f my-rbac-rules.yaml`)
|
||||
)
|
||||
|
||||
func NewCmdReconcile(f cmdutil.Factory, out, err io.Writer) *cobra.Command {
|
||||
fileOptions := &resource.FilenameOptions{}
|
||||
o := &ReconcileOptions{
|
||||
Out: out,
|
||||
Err: err,
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "reconcile -f FILENAME",
|
||||
Short: "Reconciles rules for RBAC Role, RoleBinding, ClusterRole, and ClusterRole binding objects",
|
||||
Long: reconcileLong,
|
||||
Example: reconcileExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(cmd, f, args, fileOptions))
|
||||
cmdutil.CheckErr(o.Validate())
|
||||
cmdutil.CheckErr(o.RunReconcile())
|
||||
},
|
||||
}
|
||||
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
usage := "identifying the resource to reconcile."
|
||||
cmdutil.AddFilenameOptionFlags(cmd, fileOptions, usage)
|
||||
cmd.MarkFlagRequired("filename")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *ReconcileOptions) Complete(cmd *cobra.Command, f cmdutil.Factory, args []string, options *resource.FilenameOptions) error {
|
||||
if len(args) > 0 {
|
||||
return errors.New("no arguments are allowed")
|
||||
}
|
||||
|
||||
namespace, enforceNamespace, err := f.DefaultNamespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.ResourceBuilder = f.NewBuilder(true).
|
||||
ContinueOnError().
|
||||
NamespaceParam(namespace).DefaultNamespace().
|
||||
FilenameParam(enforceNamespace, options).
|
||||
Flatten()
|
||||
|
||||
client, err := f.ClientSet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.RBACClient = client.Rbac()
|
||||
o.CoreClient = client.Core()
|
||||
|
||||
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 cmdutil.PrintResourceInfoForCommand(cmd, info, f, o.Out)
|
||||
}
|
||||
cmdutil.PrintSuccess(mapper, shortOutput, o.Out, info.Mapping.Resource, info.Name, dryRun, "reconciled")
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *ReconcileOptions) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *ReconcileOptions) RunReconcile() error {
|
||||
r := o.ResourceBuilder.Do()
|
||||
err := r.Err()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = r.Visit(func(info *resource.Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// shallowInfoCopy this is used to later twiddle the Object for printing
|
||||
// we really need more straightforward printing options
|
||||
shallowInfoCopy := *info
|
||||
|
||||
switch t := info.Object.(type) {
|
||||
case *rbac.Role:
|
||||
reconcileOptions := reconciliation.ReconcileRoleOptions{
|
||||
Confirm: true,
|
||||
RemoveExtraPermissions: false,
|
||||
Role: reconciliation.RoleRuleOwner{Role: t},
|
||||
Client: reconciliation.RoleModifier{
|
||||
NamespaceClient: o.CoreClient.Namespaces(),
|
||||
Client: o.RBACClient,
|
||||
},
|
||||
}
|
||||
result, err := reconcileOptions.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
shallowInfoCopy.Object = result.Role.GetObject()
|
||||
o.Print(&shallowInfoCopy)
|
||||
return nil
|
||||
|
||||
case *rbac.ClusterRole:
|
||||
reconcileOptions := reconciliation.ReconcileRoleOptions{
|
||||
Confirm: true,
|
||||
RemoveExtraPermissions: false,
|
||||
Role: reconciliation.ClusterRoleRuleOwner{ClusterRole: t},
|
||||
Client: reconciliation.ClusterRoleModifier{
|
||||
Client: o.RBACClient.ClusterRoles(),
|
||||
},
|
||||
}
|
||||
result, err := reconcileOptions.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
shallowInfoCopy.Object = result.Role.GetObject()
|
||||
o.Print(&shallowInfoCopy)
|
||||
return nil
|
||||
|
||||
case *rbac.RoleBinding:
|
||||
reconcileOptions := reconciliation.ReconcileRoleBindingOptions{
|
||||
Confirm: true,
|
||||
RemoveExtraSubjects: false,
|
||||
RoleBinding: reconciliation.RoleBindingAdapter{RoleBinding: t},
|
||||
Client: reconciliation.RoleBindingClientAdapter{
|
||||
Client: o.RBACClient,
|
||||
NamespaceClient: o.CoreClient.Namespaces(),
|
||||
},
|
||||
}
|
||||
result, err := reconcileOptions.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
shallowInfoCopy.Object = result.RoleBinding.GetObject()
|
||||
o.Print(&shallowInfoCopy)
|
||||
return nil
|
||||
|
||||
case *rbac.ClusterRoleBinding:
|
||||
reconcileOptions := reconciliation.ReconcileRoleBindingOptions{
|
||||
Confirm: true,
|
||||
RemoveExtraSubjects: false,
|
||||
RoleBinding: reconciliation.ClusterRoleBindingAdapter{ClusterRoleBinding: t},
|
||||
Client: reconciliation.ClusterRoleBindingClientAdapter{
|
||||
Client: o.RBACClient.ClusterRoleBindings(),
|
||||
},
|
||||
}
|
||||
result, err := reconcileOptions.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
shallowInfoCopy.Object = result.RoleBinding.GetObject()
|
||||
o.Print(&shallowInfoCopy)
|
||||
return nil
|
||||
|
||||
default:
|
||||
glog.V(1).Infof("skipping %#v", info.Object.GetObjectKind())
|
||||
// skip ignored resources
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
204
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/autoscale.go
generated
vendored
Normal file
204
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/autoscale.go
generated
vendored
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
Copyright 2015 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"
|
||||
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"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"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
autoscaleLong = templates.LongDesc(i18n.T(`
|
||||
Creates an autoscaler that automatically chooses and sets the number of pods that run in a kubernetes cluster.
|
||||
|
||||
Looks up a Deployment, ReplicaSet, or ReplicationController by name and creates an autoscaler that uses the given resource as a reference.
|
||||
An autoscaler can automatically increase or decrease number of pods deployed within the system as needed.`))
|
||||
|
||||
autoscaleExample = templates.Examples(i18n.T(`
|
||||
# Auto scale a deployment "foo", with the number of pods between 2 and 10, target CPU utilization specified so a default autoscaling policy will be used:
|
||||
kubectl autoscale deployment foo --min=2 --max=10
|
||||
|
||||
# Auto scale a replication controller "foo", with the number of pods between 1 and 5, target CPU utilization at 80%:
|
||||
kubectl autoscale rc foo --max=5 --cpu-percent=80`))
|
||||
)
|
||||
|
||||
func NewCmdAutoscale(f cmdutil.Factory, out io.Writer) *cobra.Command {
|
||||
options := &resource.FilenameOptions{}
|
||||
|
||||
validArgs := []string{"deployment", "replicaset", "replicationcontroller"}
|
||||
argAliases := kubectl.ResourceAliases(validArgs)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "autoscale (-f FILENAME | TYPE NAME | TYPE/NAME) [--min=MINPODS] --max=MAXPODS [--cpu-percent=CPU] [flags]",
|
||||
Short: i18n.T("Auto-scale a Deployment, ReplicaSet, or ReplicationController"),
|
||||
Long: autoscaleLong,
|
||||
Example: autoscaleExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := RunAutoscale(f, out, cmd, args, options)
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
ValidArgs: validArgs,
|
||||
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.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().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."
|
||||
cmdutil.AddFilenameOptionFlags(cmd, options, usage)
|
||||
cmdutil.AddApplyAnnotationFlags(cmd)
|
||||
cmdutil.AddRecordFlag(cmd)
|
||||
cmdutil.AddInclude3rdPartyFlags(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func RunAutoscale(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *resource.FilenameOptions) error {
|
||||
namespace, enforceNamespace, err := f.DefaultNamespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// validate flags
|
||||
if err := validateFlags(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mapper, typer := f.Object()
|
||||
r := f.NewBuilder(true).
|
||||
ContinueOnError().
|
||||
NamespaceParam(namespace).DefaultNamespace().
|
||||
FilenameParam(enforceNamespace, options).
|
||||
ResourceTypeOrNameArgs(false, args...).
|
||||
Flatten().
|
||||
Do()
|
||||
err = r.Err()
|
||||
if err != nil {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
mapping := info.ResourceMapping()
|
||||
if err := f.CanBeAutoscaled(mapping.GroupVersionKind.GroupKind()); err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
// Generate new object
|
||||
object, err := generator.Generate(params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resourceMapper := &resource.Mapper{
|
||||
ObjectTyper: typer,
|
||||
RESTMapper: mapper,
|
||||
ClientMapper: resource.ClientMapperFunc(f.ClientForMapping),
|
||||
Decoder: f.Decoder(true),
|
||||
}
|
||||
hpa, err := resourceMapper.InfoForObject(object, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cmdutil.ShouldRecord(cmd, hpa) {
|
||||
if err := cmdutil.RecordChangeCause(hpa.Object, f.Command(cmd, false)); err != nil {
|
||||
return err
|
||||
}
|
||||
object = hpa.Object
|
||||
}
|
||||
if cmdutil.GetDryRunFlag(cmd) {
|
||||
return f.PrintObject(cmd, false, mapper, object, out)
|
||||
}
|
||||
|
||||
if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), hpa, f.JSONEncoder()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
object, err = resource.NewHelper(hpa.Client, hpa.Mapping).Create(namespace, false, object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
count++
|
||||
if len(cmdutil.GetFlagString(cmd, "output")) > 0 {
|
||||
return f.PrintObject(cmd, false, mapper, object, out)
|
||||
}
|
||||
|
||||
cmdutil.PrintSuccess(mapper, false, out, info.Mapping.Resource, info.Name, cmdutil.GetDryRunFlag(cmd), "autoscaled")
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count == 0 {
|
||||
return fmt.Errorf("no objects passed to autoscale")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateFlags(cmd *cobra.Command) error {
|
||||
errs := []error{}
|
||||
max, min := cmdutil.GetFlagInt(cmd, "max"), cmdutil.GetFlagInt(cmd, "min")
|
||||
if max < 1 {
|
||||
errs = append(errs, fmt.Errorf("--max=MAXPODS is required and must be at least 1, max: %d", max))
|
||||
}
|
||||
if max < min {
|
||||
errs = append(errs, fmt.Errorf("--max=MAXPODS must be larger or equal to --min=MINPODS, max: %d, min: %d", max, min))
|
||||
}
|
||||
return utilerrors.NewAggregate(errs)
|
||||
}
|
||||
198
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/certificates.go
generated
vendored
Normal file
198
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/certificates.go
generated
vendored
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
Copyright 2016 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"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/apis/certificates"
|
||||
"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"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func NewCmdCertificate(f cmdutil.Factory, out io.Writer) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "certificate SUBCOMMAND",
|
||||
Short: i18n.T("Modify certificate resources."),
|
||||
Long: "Modify certificate resources.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmd.Help()
|
||||
},
|
||||
}
|
||||
|
||||
cmd.AddCommand(NewCmdCertificateApprove(f, out))
|
||||
cmd.AddCommand(NewCmdCertificateDeny(f, out))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
type CertificateOptions struct {
|
||||
resource.FilenameOptions
|
||||
csrNames []string
|
||||
outputStyle string
|
||||
}
|
||||
|
||||
func (options *CertificateOptions) Complete(cmd *cobra.Command, args []string) error {
|
||||
options.csrNames = args
|
||||
options.outputStyle = cmdutil.GetFlagString(cmd, "output")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (options *CertificateOptions) Validate() error {
|
||||
if len(options.csrNames) < 1 && cmdutil.IsFilenameSliceEmpty(options.Filenames) {
|
||||
return fmt.Errorf("one or more CSRs must be specified as <name> or -f <filename>")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewCmdCertificateApprove(f cmdutil.Factory, out io.Writer) *cobra.Command {
|
||||
options := CertificateOptions{}
|
||||
cmd := &cobra.Command{
|
||||
Use: "approve (-f FILENAME | NAME)",
|
||||
Short: i18n.T("Approve a certificate signing request"),
|
||||
Long: templates.LongDesc(`
|
||||
Approve a certificate signing request.
|
||||
|
||||
kubectl certificate approve allows a cluster admin to approve a certificate
|
||||
signing request (CSR). This action tells a certificate signing controller to
|
||||
issue a certificate to the requestor with the attributes requested in the CSR.
|
||||
|
||||
SECURITY NOTICE: Depending on the requested attributes, the issued certificate
|
||||
can potentially grant a requester access to cluster resources or to authenticate
|
||||
as a requested identity. Before approving a CSR, ensure you understand what the
|
||||
signed certificate can do.
|
||||
`),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(options.Complete(cmd, args))
|
||||
cmdutil.CheckErr(options.Validate())
|
||||
cmdutil.CheckErr(options.RunCertificateApprove(f, out))
|
||||
},
|
||||
}
|
||||
cmdutil.AddOutputFlagsForMutation(cmd)
|
||||
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, "identifying the resource to update")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (options *CertificateOptions) RunCertificateApprove(f cmdutil.Factory, out io.Writer) error {
|
||||
return options.modifyCertificateCondition(f, out, func(csr *certificates.CertificateSigningRequest) (*certificates.CertificateSigningRequest, string) {
|
||||
var alreadyApproved bool
|
||||
for _, c := range csr.Status.Conditions {
|
||||
if c.Type == certificates.CertificateApproved {
|
||||
alreadyApproved = true
|
||||
}
|
||||
}
|
||||
if alreadyApproved {
|
||||
return csr, "approved"
|
||||
}
|
||||
csr.Status.Conditions = append(csr.Status.Conditions, certificates.CertificateSigningRequestCondition{
|
||||
Type: certificates.CertificateApproved,
|
||||
Reason: "KubectlApprove",
|
||||
Message: "This CSR was approved by kubectl certificate approve.",
|
||||
LastUpdateTime: metav1.Now(),
|
||||
})
|
||||
return csr, "approved"
|
||||
})
|
||||
}
|
||||
|
||||
func NewCmdCertificateDeny(f cmdutil.Factory, out io.Writer) *cobra.Command {
|
||||
options := CertificateOptions{}
|
||||
cmd := &cobra.Command{
|
||||
Use: "deny (-f FILENAME | NAME)",
|
||||
Short: i18n.T("Deny a certificate signing request"),
|
||||
Long: templates.LongDesc(`
|
||||
Deny a certificate signing request.
|
||||
|
||||
kubectl certificate deny allows a cluster admin to deny a certificate
|
||||
signing request (CSR). This action tells a certificate signing controller to
|
||||
not to issue a certificate to the requestor.
|
||||
`),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(options.Complete(cmd, args))
|
||||
cmdutil.CheckErr(options.Validate())
|
||||
cmdutil.CheckErr(options.RunCertificateDeny(f, out))
|
||||
},
|
||||
}
|
||||
cmdutil.AddOutputFlagsForMutation(cmd)
|
||||
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, "identifying the resource to update")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (options *CertificateOptions) RunCertificateDeny(f cmdutil.Factory, out io.Writer) error {
|
||||
return options.modifyCertificateCondition(f, out, func(csr *certificates.CertificateSigningRequest) (*certificates.CertificateSigningRequest, string) {
|
||||
var alreadyDenied bool
|
||||
for _, c := range csr.Status.Conditions {
|
||||
if c.Type == certificates.CertificateDenied {
|
||||
alreadyDenied = true
|
||||
}
|
||||
}
|
||||
if alreadyDenied {
|
||||
return csr, "denied"
|
||||
}
|
||||
csr.Status.Conditions = append(csr.Status.Conditions, certificates.CertificateSigningRequestCondition{
|
||||
Type: certificates.CertificateDenied,
|
||||
Reason: "KubectlDeny",
|
||||
Message: "This CSR was approved by kubectl certificate deny.",
|
||||
LastUpdateTime: metav1.Now(),
|
||||
})
|
||||
return csr, "denied"
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
r := f.NewBuilder(true).
|
||||
ContinueOnError().
|
||||
FilenameParam(false, &options.FilenameOptions).
|
||||
ResourceNames("certificatesigningrequest", options.csrNames...).
|
||||
RequireObject(true).
|
||||
Flatten().
|
||||
Latest().
|
||||
Do()
|
||||
err = r.Visit(func(info *resource.Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
csr := info.Object.(*certificates.CertificateSigningRequest)
|
||||
csr, verb := modify(csr)
|
||||
csr, err = c.Certificates().
|
||||
CertificateSigningRequests().
|
||||
UpdateApproval(csr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
found++
|
||||
cmdutil.PrintSuccess(mapper, options.outputStyle == "name", out, info.Mapping.Resource, info.Name, false, verb)
|
||||
return nil
|
||||
})
|
||||
if found == 0 {
|
||||
fmt.Fprintf(out, "No resources found\n")
|
||||
}
|
||||
return err
|
||||
}
|
||||
126
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/clusterinfo.go
generated
vendored
Normal file
126
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/clusterinfo.go
generated
vendored
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
Copyright 2015 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"
|
||||
"strconv"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"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"
|
||||
|
||||
ct "github.com/daviddengcn/go-colortext"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
longDescr = templates.LongDesc(i18n.T(`
|
||||
Display addresses of the master and services with label kubernetes.io/cluster-service=true
|
||||
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.`))
|
||||
|
||||
clusterinfoExample = templates.Examples(i18n.T(`
|
||||
# Print the address of the master and cluster services
|
||||
kubectl cluster-info`))
|
||||
)
|
||||
|
||||
func NewCmdClusterInfo(f cmdutil.Factory, out io.Writer) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "cluster-info",
|
||||
Short: i18n.T("Display cluster info"),
|
||||
Long: longDescr,
|
||||
Example: clusterinfoExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := RunClusterInfo(f, out, cmd)
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
cmdutil.AddInclude3rdPartyFlags(cmd)
|
||||
cmd.AddCommand(NewCmdClusterInfoDump(f, out))
|
||||
return cmd
|
||||
}
|
||||
|
||||
func RunClusterInfo(f cmdutil.Factory, out io.Writer, cmd *cobra.Command) error {
|
||||
client, err := f.ClientConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printService(out, "Kubernetes master", client.Host)
|
||||
|
||||
cmdNamespace := cmdutil.GetFlagString(cmd, "namespace")
|
||||
if cmdNamespace == "" {
|
||||
cmdNamespace = metav1.NamespaceSystem
|
||||
}
|
||||
|
||||
// TODO use generalized labels once they are implemented (#341)
|
||||
b := f.NewBuilder(true).
|
||||
NamespaceParam(cmdNamespace).DefaultNamespace().
|
||||
SelectorParam("kubernetes.io/cluster-service=true").
|
||||
ResourceTypeOrNameArgs(false, []string{"services"}...).
|
||||
Latest()
|
||||
b.Do().Visit(func(r *resource.Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
services := r.Object.(*api.ServiceList).Items
|
||||
for _, service := range services {
|
||||
var link string
|
||||
if len(service.Status.LoadBalancer.Ingress) > 0 {
|
||||
ingress := service.Status.LoadBalancer.Ingress[0]
|
||||
ip := ingress.IP
|
||||
if ip == "" {
|
||||
ip = ingress.Hostname
|
||||
}
|
||||
for _, port := range service.Spec.Ports {
|
||||
link += "http://" + ip + ":" + strconv.Itoa(int(port.Port)) + " "
|
||||
}
|
||||
} else {
|
||||
if len(client.GroupVersion.Group) == 0 {
|
||||
link = client.Host + "/api/" + client.GroupVersion.Version + "/namespaces/" + service.ObjectMeta.Namespace + "/services/" + service.ObjectMeta.Name + "/proxy"
|
||||
} else {
|
||||
link = client.Host + "/api/" + client.GroupVersion.Group + "/" + client.GroupVersion.Version + "/namespaces/" + service.ObjectMeta.Namespace + "/services/" + service.ObjectMeta.Name + "/proxy"
|
||||
|
||||
}
|
||||
}
|
||||
name := service.ObjectMeta.Labels["kubernetes.io/name"]
|
||||
if len(name) == 0 {
|
||||
name = service.ObjectMeta.Name
|
||||
}
|
||||
printService(out, name, link)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
out.Write([]byte("\nTo further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.\n"))
|
||||
return nil
|
||||
|
||||
// TODO consider printing more information about cluster
|
||||
}
|
||||
|
||||
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 ")
|
||||
ct.ChangeColor(ct.Yellow, false, ct.None, false)
|
||||
fmt.Fprint(out, link)
|
||||
ct.ResetColor()
|
||||
fmt.Fprintln(out, "")
|
||||
}
|
||||
233
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/clusterinfo_dump.go
generated
vendored
Normal file
233
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/clusterinfo_dump.go
generated
vendored
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
/*
|
||||
Copyright 2016 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"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
)
|
||||
|
||||
// NewCmdCreateSecret groups subcommands to create various types of secrets
|
||||
func NewCmdClusterInfoDump(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "dump",
|
||||
Short: i18n.T("Dump lots of relevant info for debugging and diagnosis"),
|
||||
Long: dumpLong,
|
||||
Example: dumpExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(dumpClusterInfo(f, cmd, cmdOut))
|
||||
},
|
||||
}
|
||||
cmd.Flags().String("output-directory", "", i18n.T("Where to output the files. If empty or '-' uses stdout, otherwise creates a directory hierarchy in that directory"))
|
||||
cmd.Flags().StringSlice("namespaces", []string{}, "A comma separated list of namespaces to dump.")
|
||||
cmd.Flags().Bool("all-namespaces", false, "If true, dump all namespaces. If true, --namespaces is ignored.")
|
||||
cmdutil.AddPodRunningTimeoutFlag(cmd, defaultPodLogsTimeout)
|
||||
return cmd
|
||||
}
|
||||
|
||||
var (
|
||||
dumpLong = templates.LongDesc(i18n.T(`
|
||||
Dumps cluster info out suitable for debugging and diagnosing cluster problems. By default, dumps everything to
|
||||
stdout. You can optionally specify a directory with --output-directory. If you specify a directory, kubernetes will
|
||||
build a set of files in that directory. By default only dumps things in the 'kube-system' namespace, but you can
|
||||
switch to a different namespace with the --namespaces flag, or specify --all-namespaces to dump all namespaces.
|
||||
|
||||
The command also dumps the logs of all of the pods in the cluster, these logs are dumped into different directories
|
||||
based on namespace and pod name.`))
|
||||
|
||||
dumpExample = templates.Examples(i18n.T(`
|
||||
# Dump current cluster state to stdout
|
||||
kubectl cluster-info dump
|
||||
|
||||
# Dump current cluster state to /path/to/cluster-state
|
||||
kubectl cluster-info dump --output-directory=/path/to/cluster-state
|
||||
|
||||
# Dump all namespaces to stdout
|
||||
kubectl cluster-info dump --all-namespaces
|
||||
|
||||
# Dump a set of namespaces to /path/to/cluster-state
|
||||
kubectl cluster-info dump --namespaces default,kube-system --output-directory=/path/to/cluster-state`))
|
||||
)
|
||||
|
||||
func setupOutputWriter(cmd *cobra.Command, defaultWriter io.Writer, filename string) io.Writer {
|
||||
dir := cmdutil.GetFlagString(cmd, "output-directory")
|
||||
if len(dir) == 0 || dir == "-" {
|
||||
return defaultWriter
|
||||
}
|
||||
fullFile := path.Join(dir, filename)
|
||||
parent := path.Dir(fullFile)
|
||||
cmdutil.CheckErr(os.MkdirAll(parent, 0755))
|
||||
|
||||
file, err := os.Create(path.Join(dir, filename))
|
||||
cmdutil.CheckErr(err)
|
||||
return file
|
||||
}
|
||||
|
||||
func dumpClusterInfo(f cmdutil.Factory, cmd *cobra.Command, out io.Writer) error {
|
||||
timeout, err := cmdutil.GetPodRunningTimeoutFlag(cmd)
|
||||
if err != nil {
|
||||
return cmdutil.UsageErrorf(cmd, err.Error())
|
||||
}
|
||||
|
||||
clientset, err := f.ClientSet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printer := &printers.JSONPrinter{}
|
||||
|
||||
nodes, err := clientset.Core().Nodes().List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := printer.PrintObj(nodes, setupOutputWriter(cmd, out, "nodes.json")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var namespaces []string
|
||||
if cmdutil.GetFlagBool(cmd, "all-namespaces") {
|
||||
namespaceList, err := clientset.Core().Namespaces().List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for ix := range namespaceList.Items {
|
||||
namespaces = append(namespaces, namespaceList.Items[ix].Name)
|
||||
}
|
||||
} else {
|
||||
namespaces = cmdutil.GetFlagStringSlice(cmd, "namespaces")
|
||||
if len(namespaces) == 0 {
|
||||
cmdNamespace, _, err := f.DefaultNamespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
namespaces = []string{
|
||||
metav1.NamespaceSystem,
|
||||
cmdNamespace,
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, namespace := range namespaces {
|
||||
// TODO: this is repetitive in the extreme. Use reflection or
|
||||
// something to make this a for loop.
|
||||
events, err := clientset.Core().Events(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := printer.PrintObj(events, setupOutputWriter(cmd, out, path.Join(namespace, "events.json"))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rcs, err := clientset.Core().ReplicationControllers(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := printer.PrintObj(rcs, setupOutputWriter(cmd, out, path.Join(namespace, "replication-controllers.json"))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
svcs, err := clientset.Core().Services(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := printer.PrintObj(svcs, setupOutputWriter(cmd, out, path.Join(namespace, "services.json"))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sets, err := clientset.Extensions().DaemonSets(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := printer.PrintObj(sets, setupOutputWriter(cmd, out, path.Join(namespace, "daemonsets.json"))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deps, err := clientset.Extensions().Deployments(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := printer.PrintObj(deps, setupOutputWriter(cmd, out, path.Join(namespace, "deployments.json"))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rps, err := clientset.Extensions().ReplicaSets(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := printer.PrintObj(rps, setupOutputWriter(cmd, out, path.Join(namespace, "replicasets.json"))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pods, err := clientset.Core().Pods(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := printer.PrintObj(pods, setupOutputWriter(cmd, out, path.Join(namespace, "pods.json"))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printContainer := func(writer io.Writer, container api.Container, pod *api.Pod) {
|
||||
writer.Write([]byte(fmt.Sprintf("==== START logs for container %s of pod %s/%s ====\n", container.Name, pod.Namespace, pod.Name)))
|
||||
defer writer.Write([]byte(fmt.Sprintf("==== END logs for container %s of pod %s/%s ====\n", container.Name, pod.Namespace, pod.Name)))
|
||||
|
||||
request, err := f.LogsForObject(pod, &api.PodLogOptions{Container: container.Name}, timeout)
|
||||
if err != nil {
|
||||
// Print error and return.
|
||||
writer.Write([]byte(fmt.Sprintf("Create log request error: %s\n", err.Error())))
|
||||
return
|
||||
}
|
||||
|
||||
data, err := request.DoRaw()
|
||||
if err != nil {
|
||||
// Print error and return.
|
||||
writer.Write([]byte(fmt.Sprintf("Request log error: %s\n", err.Error())))
|
||||
return
|
||||
}
|
||||
writer.Write(data)
|
||||
}
|
||||
|
||||
for ix := range pods.Items {
|
||||
pod := &pods.Items[ix]
|
||||
containers := pod.Spec.Containers
|
||||
writer := setupOutputWriter(cmd, out, path.Join(namespace, pod.Name, "logs.txt"))
|
||||
|
||||
for i := range containers {
|
||||
printContainer(writer, containers[i], pod)
|
||||
}
|
||||
}
|
||||
}
|
||||
dir := cmdutil.GetFlagString(cmd, "output-directory")
|
||||
if len(dir) == 0 {
|
||||
dir = "standard output"
|
||||
}
|
||||
if dir != "-" {
|
||||
fmt.Fprintf(out, "Cluster info dumped to %s\n", dir)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
70
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/clusterinfo_dump_test.go
generated
vendored
Normal file
70
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/clusterinfo_dump_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
Copyright 2016 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"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||
)
|
||||
|
||||
func TestSetupOutputWriterNoOp(t *testing.T) {
|
||||
tests := []string{"", "-"}
|
||||
for _, test := range tests {
|
||||
out := &bytes.Buffer{}
|
||||
f, _, _, _ := cmdtesting.NewAPIFactory()
|
||||
cmd := NewCmdClusterInfoDump(f, os.Stdout)
|
||||
cmd.Flag("output-directory").Value.Set(test)
|
||||
writer := setupOutputWriter(cmd, out, "/some/file/that/should/be/ignored")
|
||||
if writer != out {
|
||||
t.Errorf("expected: %v, saw: %v", out, writer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupOutputWriterFile(t *testing.T) {
|
||||
file := "output.json"
|
||||
dir, err := ioutil.TempDir(os.TempDir(), "out")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
fullPath := path.Join(dir, file)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
out := &bytes.Buffer{}
|
||||
f, _, _, _ := cmdtesting.NewAPIFactory()
|
||||
cmd := NewCmdClusterInfoDump(f, os.Stdout)
|
||||
cmd.Flag("output-directory").Value.Set(dir)
|
||||
writer := setupOutputWriter(cmd, out, file)
|
||||
if writer == out {
|
||||
t.Errorf("expected: %v, saw: %v", out, writer)
|
||||
}
|
||||
output := "some data here"
|
||||
writer.Write([]byte(output))
|
||||
|
||||
data, err := ioutil.ReadFile(fullPath)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if string(data) != output {
|
||||
t.Errorf("expected: %v, saw: %v", output, data)
|
||||
}
|
||||
}
|
||||
418
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/cmd.go
generated
vendored
Normal file
418
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/cmd.go
generated
vendored
Normal file
|
|
@ -0,0 +1,418 @@
|
|||
/*
|
||||
Copyright 2014 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"
|
||||
|
||||
"k8s.io/apiserver/pkg/util/flag"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/auth"
|
||||
cmdconfig "k8s.io/kubernetes/pkg/kubectl/cmd/config"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/rollout"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/set"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const (
|
||||
bashCompletionFunc = `# call kubectl get $1,
|
||||
__kubectl_override_flag_list=(kubeconfig cluster user context namespace server)
|
||||
__kubectl_override_flags()
|
||||
{
|
||||
local ${__kubectl_override_flag_list[*]} two_word_of of
|
||||
for w in "${words[@]}"; do
|
||||
if [ -n "${two_word_of}" ]; then
|
||||
eval "${two_word_of}=\"--${two_word_of}=\${w}\""
|
||||
two_word_of=
|
||||
continue
|
||||
fi
|
||||
for of in "${__kubectl_override_flag_list[@]}"; do
|
||||
case "${w}" in
|
||||
--${of}=*)
|
||||
eval "${of}=\"${w}\""
|
||||
;;
|
||||
--${of})
|
||||
two_word_of="${of}"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
if [ "${w}" == "--all-namespaces" ]; then
|
||||
namespace="--all-namespaces"
|
||||
fi
|
||||
done
|
||||
for of in "${__kubectl_override_flag_list[@]}"; do
|
||||
if eval "test -n \"\$${of}\""; then
|
||||
eval "echo \${${of}}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
__kubectl_config_get_contexts()
|
||||
{
|
||||
__kubectl_parse_config "contexts"
|
||||
}
|
||||
|
||||
__kubectl_config_get_clusters()
|
||||
{
|
||||
__kubectl_parse_config "clusters"
|
||||
}
|
||||
|
||||
__kubectl_config_get_users()
|
||||
{
|
||||
__kubectl_parse_config "users"
|
||||
}
|
||||
|
||||
# $1 has to be "contexts", "clusters" or "users"
|
||||
__kubectl_parse_config()
|
||||
{
|
||||
local template kubectl_out
|
||||
template="{{ range .$1 }}{{ .name }} {{ end }}"
|
||||
if kubectl_out=$(kubectl config $(__kubectl_override_flags) -o template --template="${template}" view 2>/dev/null); then
|
||||
COMPREPLY=( $( compgen -W "${kubectl_out[*]}" -- "$cur" ) )
|
||||
fi
|
||||
}
|
||||
|
||||
__kubectl_parse_get()
|
||||
{
|
||||
local template
|
||||
template="{{ range .items }}{{ .metadata.name }} {{ end }}"
|
||||
local kubectl_out
|
||||
if kubectl_out=$(kubectl get $(__kubectl_override_flags) -o template --template="${template}" "$1" 2>/dev/null); then
|
||||
COMPREPLY=( $( compgen -W "${kubectl_out[*]}" -- "$cur" ) )
|
||||
fi
|
||||
}
|
||||
|
||||
__kubectl_get_resource()
|
||||
{
|
||||
if [[ ${#nouns[@]} -eq 0 ]]; then
|
||||
return 1
|
||||
fi
|
||||
__kubectl_parse_get "${nouns[${#nouns[@]} -1]}"
|
||||
}
|
||||
|
||||
__kubectl_get_resource_namespace()
|
||||
{
|
||||
__kubectl_parse_get "namespace"
|
||||
}
|
||||
|
||||
__kubectl_get_resource_pod()
|
||||
{
|
||||
__kubectl_parse_get "pod"
|
||||
}
|
||||
|
||||
__kubectl_get_resource_rc()
|
||||
{
|
||||
__kubectl_parse_get "rc"
|
||||
}
|
||||
|
||||
__kubectl_get_resource_node()
|
||||
{
|
||||
__kubectl_parse_get "node"
|
||||
}
|
||||
|
||||
# $1 is the name of the pod we want to get the list of containers inside
|
||||
__kubectl_get_containers()
|
||||
{
|
||||
local template
|
||||
template="{{ range .spec.containers }}{{ .name }} {{ end }}"
|
||||
__debug "${FUNCNAME} nouns are ${nouns[*]}"
|
||||
|
||||
local len="${#nouns[@]}"
|
||||
if [[ ${len} -ne 1 ]]; then
|
||||
return
|
||||
fi
|
||||
local last=${nouns[${len} -1]}
|
||||
local kubectl_out
|
||||
if kubectl_out=$(kubectl get $(__kubectl_override_flags) -o template --template="${template}" pods "${last}" 2>/dev/null); then
|
||||
COMPREPLY=( $( compgen -W "${kubectl_out[*]}" -- "$cur" ) )
|
||||
fi
|
||||
}
|
||||
|
||||
# Require both a pod and a container to be specified
|
||||
__kubectl_require_pod_and_container()
|
||||
{
|
||||
if [[ ${#nouns[@]} -eq 0 ]]; then
|
||||
__kubectl_parse_get pods
|
||||
return 0
|
||||
fi;
|
||||
__kubectl_get_containers
|
||||
return 0
|
||||
}
|
||||
|
||||
__custom_func() {
|
||||
case ${last_command} in
|
||||
kubectl_get | kubectl_describe | kubectl_delete | kubectl_label | kubectl_edit | kubectl_patch |\
|
||||
kubectl_annotate | kubectl_expose | kubectl_scale | kubectl_autoscale | kubectl_taint | kubectl_rollout_*)
|
||||
__kubectl_get_resource
|
||||
return
|
||||
;;
|
||||
kubectl_logs | kubectl_attach)
|
||||
__kubectl_require_pod_and_container
|
||||
return
|
||||
;;
|
||||
kubectl_exec | kubectl_port-forward | kubectl_top_pod)
|
||||
__kubectl_get_resource_pod
|
||||
return
|
||||
;;
|
||||
kubectl_rolling-update)
|
||||
__kubectl_get_resource_rc
|
||||
return
|
||||
;;
|
||||
kubectl_cordon | kubectl_uncordon | kubectl_drain | kubectl_top_node)
|
||||
__kubectl_get_resource_node
|
||||
return
|
||||
;;
|
||||
kubectl_config_use-context)
|
||||
__kubectl_config_get_contexts
|
||||
return
|
||||
;;
|
||||
kubectl_config_delete-cluster)
|
||||
__kubectl_config_get_clusters
|
||||
return
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
}
|
||||
`
|
||||
|
||||
// If you add a resource to this list, please also take a look at pkg/kubectl/kubectl.go
|
||||
// and add a short forms entry in expandResourceShortcut() when appropriate.
|
||||
// TODO: This should be populated using the discovery information from apiserver.
|
||||
validResources = `Valid resource types include:
|
||||
|
||||
* all
|
||||
* certificatesigningrequests (aka 'csr')
|
||||
* clusterrolebindings
|
||||
* clusterroles
|
||||
* clusters (valid only for federation apiservers)
|
||||
* componentstatuses (aka 'cs')
|
||||
* configmaps (aka 'cm')
|
||||
* controllerrevisions
|
||||
* cronjobs
|
||||
* customresourcedefinition (aka 'crd')
|
||||
* daemonsets (aka 'ds')
|
||||
* deployments (aka 'deploy')
|
||||
* endpoints (aka 'ep')
|
||||
* events (aka 'ev')
|
||||
* horizontalpodautoscalers (aka 'hpa')
|
||||
* ingresses (aka 'ing')
|
||||
* jobs
|
||||
* limitranges (aka 'limits')
|
||||
* namespaces (aka 'ns')
|
||||
* networkpolicies (aka 'netpol')
|
||||
* nodes (aka 'no')
|
||||
* persistentvolumeclaims (aka 'pvc')
|
||||
* persistentvolumes (aka 'pv')
|
||||
* poddisruptionbudgets (aka 'pdb')
|
||||
* podpreset
|
||||
* pods (aka 'po')
|
||||
* podsecuritypolicies (aka 'psp')
|
||||
* podtemplates
|
||||
* replicasets (aka 'rs')
|
||||
* replicationcontrollers (aka 'rc')
|
||||
* resourcequotas (aka 'quota')
|
||||
* rolebindings
|
||||
* roles
|
||||
* secrets
|
||||
* serviceaccounts (aka 'sa')
|
||||
* services (aka 'svc')
|
||||
* statefulsets
|
||||
* storageclasses
|
||||
`
|
||||
)
|
||||
|
||||
var (
|
||||
bash_completion_flags = map[string]string{
|
||||
"namespace": "__kubectl_get_resource_namespace",
|
||||
"context": "__kubectl_config_get_contexts",
|
||||
"cluster": "__kubectl_config_get_clusters",
|
||||
"user": "__kubectl_config_get_users",
|
||||
}
|
||||
)
|
||||
|
||||
// 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.
|
||||
cmds := &cobra.Command{
|
||||
Use: "kubectl",
|
||||
Short: i18n.T("kubectl controls the Kubernetes cluster manager"),
|
||||
Long: templates.LongDesc(`
|
||||
kubectl controls the Kubernetes cluster manager.
|
||||
|
||||
Find more information at https://github.com/kubernetes/kubernetes.`),
|
||||
Run: runHelp,
|
||||
BashCompletionFunction: bashCompletionFunc,
|
||||
}
|
||||
|
||||
f.BindFlags(cmds.PersistentFlags())
|
||||
f.BindExternalFlags(cmds.PersistentFlags())
|
||||
|
||||
// Sending in 'nil' for the getLanguageFn() results in using
|
||||
// the LANG environment variable.
|
||||
//
|
||||
// TODO: Consider adding a flag or file preference for setting
|
||||
// the language, instead of just loading from the LANG env. variable.
|
||||
i18n.LoadTranslations("kubectl", nil)
|
||||
|
||||
// From this point and forward we get warnings on flags that contain "_" separators
|
||||
cmds.SetGlobalNormalizationFunc(flag.WarnWordSepNormalizeFunc)
|
||||
|
||||
groups := templates.CommandGroups{
|
||||
{
|
||||
Message: "Basic Commands (Beginner):",
|
||||
Commands: []*cobra.Command{
|
||||
NewCmdCreate(f, out, err),
|
||||
NewCmdExposeService(f, out),
|
||||
NewCmdRun(f, in, out, err),
|
||||
set.NewCmdSet(f, in, out, err),
|
||||
deprecatedAlias("run-container", NewCmdRun(f, in, out, err)),
|
||||
},
|
||||
},
|
||||
{
|
||||
Message: "Basic Commands (Intermediate):",
|
||||
Commands: []*cobra.Command{
|
||||
NewCmdGet(f, out, err),
|
||||
NewCmdExplain(f, out, err),
|
||||
NewCmdEdit(f, out, err),
|
||||
NewCmdDelete(f, out, err),
|
||||
},
|
||||
},
|
||||
{
|
||||
Message: "Deploy Commands:",
|
||||
Commands: []*cobra.Command{
|
||||
rollout.NewCmdRollout(f, out, err),
|
||||
NewCmdRollingUpdate(f, out),
|
||||
NewCmdScale(f, out),
|
||||
NewCmdAutoscale(f, out),
|
||||
},
|
||||
},
|
||||
{
|
||||
Message: "Cluster Management Commands:",
|
||||
Commands: []*cobra.Command{
|
||||
NewCmdCertificate(f, out),
|
||||
NewCmdClusterInfo(f, out),
|
||||
NewCmdTop(f, out, err),
|
||||
NewCmdCordon(f, out),
|
||||
NewCmdUncordon(f, out),
|
||||
NewCmdDrain(f, out, err),
|
||||
NewCmdTaint(f, out),
|
||||
},
|
||||
},
|
||||
{
|
||||
Message: "Troubleshooting and Debugging Commands:",
|
||||
Commands: []*cobra.Command{
|
||||
NewCmdDescribe(f, out, err),
|
||||
NewCmdLogs(f, out),
|
||||
NewCmdAttach(f, in, out, err),
|
||||
NewCmdExec(f, in, out, err),
|
||||
NewCmdPortForward(f, out, err),
|
||||
NewCmdProxy(f, out),
|
||||
NewCmdCp(f, out, err),
|
||||
auth.NewCmdAuth(f, out, err),
|
||||
},
|
||||
},
|
||||
{
|
||||
Message: "Advanced Commands:",
|
||||
Commands: []*cobra.Command{
|
||||
NewCmdApply("kubectl", f, out, err),
|
||||
NewCmdPatch(f, out),
|
||||
NewCmdReplace(f, out),
|
||||
NewCmdConvert(f, out),
|
||||
},
|
||||
},
|
||||
{
|
||||
Message: "Settings Commands:",
|
||||
Commands: []*cobra.Command{
|
||||
NewCmdLabel(f, out),
|
||||
NewCmdAnnotate(f, out),
|
||||
NewCmdCompletion(out, ""),
|
||||
},
|
||||
},
|
||||
}
|
||||
groups.Add(cmds)
|
||||
|
||||
filters := []string{"options"}
|
||||
|
||||
// Hide the "alpha" subcommand if there are no alpha commands in this build.
|
||||
alpha := NewCmdAlpha(f, in, out, err)
|
||||
if !alpha.HasSubCommands() {
|
||||
filters = append(filters, alpha.Name())
|
||||
}
|
||||
|
||||
templates.ActsAsRootCommand(cmds, filters, groups...)
|
||||
|
||||
for name, completion := range bash_completion_flags {
|
||||
if cmds.Flag(name) != nil {
|
||||
if cmds.Flag(name).Annotations == nil {
|
||||
cmds.Flag(name).Annotations = map[string][]string{}
|
||||
}
|
||||
cmds.Flag(name).Annotations[cobra.BashCompCustom] = append(
|
||||
cmds.Flag(name).Annotations[cobra.BashCompCustom],
|
||||
completion,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
cmds.AddCommand(alpha)
|
||||
cmds.AddCommand(cmdconfig.NewCmdConfig(clientcmd.NewDefaultPathOptions(), out, err))
|
||||
cmds.AddCommand(NewCmdPlugin(f, in, out, err))
|
||||
cmds.AddCommand(NewCmdVersion(f, out))
|
||||
cmds.AddCommand(NewCmdApiVersions(f, out))
|
||||
cmds.AddCommand(NewCmdOptions(out))
|
||||
|
||||
return cmds
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// deprecatedAlias is intended to be used to create a "wrapper" command around
|
||||
// an existing command. The wrapper works the same but prints a deprecation
|
||||
// message before running. This command is identical functionality.
|
||||
func deprecatedAlias(deprecatedVersion string, cmd *cobra.Command) *cobra.Command {
|
||||
// Have to be careful here because Cobra automatically extracts the name
|
||||
// of the command from the .Use field.
|
||||
originalName := cmd.Name()
|
||||
|
||||
cmd.Use = deprecatedVersion
|
||||
cmd.Deprecated = fmt.Sprintf("use %q instead", originalName)
|
||||
cmd.Short = fmt.Sprintf("%s. This command is deprecated, use %q instead", cmd.Short, originalName)
|
||||
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()
|
||||
}
|
||||
720
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/cmd_test.go
generated
vendored
Normal file
720
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/cmd_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,720 @@
|
|||
/*
|
||||
Copyright 2014 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"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
stdstrings "strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/rest/fake"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
"k8s.io/kubernetes/pkg/util/strings"
|
||||
)
|
||||
|
||||
func initTestErrorHandler(t *testing.T) {
|
||||
cmdutil.BehaviorOnFatal(func(str string, code int) {
|
||||
t.Errorf("Error running command (exit code %d): %s", code, str)
|
||||
})
|
||||
}
|
||||
|
||||
func defaultHeader() http.Header {
|
||||
header := http.Header{}
|
||||
header.Set("Content-Type", runtime.ContentTypeJSON)
|
||||
return header
|
||||
}
|
||||
|
||||
func defaultClientConfig() *restclient.Config {
|
||||
return &restclient.Config{
|
||||
APIPath: "/api",
|
||||
ContentConfig: restclient.ContentConfig{
|
||||
NegotiatedSerializer: api.Codecs,
|
||||
ContentType: runtime.ContentTypeJSON,
|
||||
GroupVersion: &api.Registry.GroupOrDie(api.GroupName).GroupVersion,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func defaultClientConfigForVersion(version *schema.GroupVersion) *restclient.Config {
|
||||
return &restclient.Config{
|
||||
APIPath: "/api",
|
||||
ContentConfig: restclient.ContentConfig{
|
||||
NegotiatedSerializer: api.Codecs,
|
||||
ContentType: runtime.ContentTypeJSON,
|
||||
GroupVersion: version,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type testPrinter struct {
|
||||
Objects []runtime.Object
|
||||
Err error
|
||||
GenericPrinter bool
|
||||
}
|
||||
|
||||
func (t *testPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
|
||||
t.Objects = append(t.Objects, obj)
|
||||
fmt.Fprintf(out, "%#v", obj)
|
||||
return t.Err
|
||||
}
|
||||
|
||||
// TODO: implement HandledResources()
|
||||
func (t *testPrinter) HandledResources() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (t *testPrinter) AfterPrint(output io.Writer, res string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *testPrinter) IsGeneric() bool {
|
||||
return t.GenericPrinter
|
||||
}
|
||||
|
||||
type testDescriber struct {
|
||||
Name, Namespace string
|
||||
Settings printers.DescriberSettings
|
||||
Output string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (t *testDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (output string, err error) {
|
||||
t.Namespace, t.Name = namespace, name
|
||||
t.Settings = describerSettings
|
||||
return t.Output, t.Err
|
||||
}
|
||||
|
||||
func objBody(codec runtime.Codec, obj runtime.Object) io.ReadCloser {
|
||||
return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, obj))))
|
||||
}
|
||||
|
||||
func policyObjBody(obj runtime.Object) io.ReadCloser {
|
||||
return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(testapi.Policy.Codec(), obj))))
|
||||
}
|
||||
|
||||
func bytesBody(bodyBytes []byte) io.ReadCloser {
|
||||
return ioutil.NopCloser(bytes.NewReader(bodyBytes))
|
||||
}
|
||||
|
||||
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{
|
||||
APIRegistry: api.Registry,
|
||||
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.Client = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: ns,
|
||||
Client: nil,
|
||||
}
|
||||
cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr)
|
||||
ctrl := &api.ReplicationController{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
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",
|
||||
},
|
||||
{
|
||||
Name: "foo2",
|
||||
Image: "someimage2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: api.ReplicationControllerStatus{
|
||||
Replicas: 1,
|
||||
},
|
||||
}
|
||||
mapper, _ := f.Object()
|
||||
err := f.PrintObject(cmd, false, mapper, ctrl, os.Stdout)
|
||||
if err != nil {
|
||||
fmt.Printf("Unexpected error: %v", err)
|
||||
}
|
||||
// Output:
|
||||
// NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
|
||||
// foo 1 1 0 10y foo,foo2 someimage,someimage2 foo=bar
|
||||
}
|
||||
|
||||
func Example_printReplicationController() {
|
||||
f, tf, _, ns := cmdtesting.NewAPIFactory()
|
||||
p := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{
|
||||
ColumnLabels: []string{},
|
||||
})
|
||||
printersinternal.AddHandlers(p)
|
||||
tf.Printer = p
|
||||
tf.Client = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: ns,
|
||||
Client: nil,
|
||||
}
|
||||
cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr)
|
||||
ctrl := &api.ReplicationController{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
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",
|
||||
},
|
||||
{
|
||||
Name: "foo2",
|
||||
Image: "someimage",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: api.ReplicationControllerStatus{
|
||||
Replicas: 1,
|
||||
},
|
||||
}
|
||||
mapper, _ := f.Object()
|
||||
err := f.PrintObject(cmd, false, mapper, ctrl, os.Stdout)
|
||||
if err != nil {
|
||||
fmt.Printf("Unexpected error: %v", err)
|
||||
}
|
||||
// Output:
|
||||
// NAME DESIRED CURRENT READY AGE
|
||||
// foo 1 1 0 10y
|
||||
}
|
||||
|
||||
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.Client = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: ns,
|
||||
Client: nil,
|
||||
}
|
||||
nodeName := "kubernetes-node-abcd"
|
||||
cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr)
|
||||
pod := &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test1",
|
||||
CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: make([]api.Container, 2),
|
||||
NodeName: nodeName,
|
||||
},
|
||||
Status: api.PodStatus{
|
||||
Phase: "podPhase",
|
||||
ContainerStatuses: []api.ContainerStatus{
|
||||
{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
|
||||
{RestartCount: 3},
|
||||
},
|
||||
PodIP: "10.1.1.3",
|
||||
},
|
||||
}
|
||||
mapper, _ := f.Object()
|
||||
err := f.PrintObject(cmd, false, mapper, pod, os.Stdout)
|
||||
if err != nil {
|
||||
fmt.Printf("Unexpected error: %v", err)
|
||||
}
|
||||
// Output:
|
||||
// NAME READY STATUS RESTARTS AGE IP NODE
|
||||
// test1 1/2 podPhase 6 10y 10.1.1.3 kubernetes-node-abcd
|
||||
}
|
||||
|
||||
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.Client = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: ns,
|
||||
Client: nil,
|
||||
}
|
||||
nodeName := "kubernetes-node-abcd"
|
||||
cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr)
|
||||
pod := &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test1",
|
||||
CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
|
||||
Labels: map[string]string{
|
||||
"l1": "key",
|
||||
"l2": "value",
|
||||
},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: make([]api.Container, 2),
|
||||
NodeName: nodeName,
|
||||
},
|
||||
Status: api.PodStatus{
|
||||
Phase: "podPhase",
|
||||
ContainerStatuses: []api.ContainerStatus{
|
||||
{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
|
||||
{RestartCount: 3},
|
||||
},
|
||||
},
|
||||
}
|
||||
mapper, _ := f.Object()
|
||||
err := f.PrintObject(cmd, false, mapper, pod, os.Stdout)
|
||||
if err != nil {
|
||||
fmt.Printf("Unexpected error: %v", err)
|
||||
}
|
||||
// Output:
|
||||
// NAME READY STATUS RESTARTS AGE LABELS
|
||||
// test1 1/2 podPhase 6 10y l1=key,l2=value
|
||||
}
|
||||
|
||||
func newAllPhasePodList() *api.PodList {
|
||||
nodeName := "kubernetes-node-abcd"
|
||||
return &api.PodList{
|
||||
Items: []api.Pod{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test1",
|
||||
CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: make([]api.Container, 2),
|
||||
NodeName: nodeName,
|
||||
},
|
||||
Status: api.PodStatus{
|
||||
Phase: api.PodPending,
|
||||
ContainerStatuses: []api.ContainerStatus{
|
||||
{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
|
||||
{RestartCount: 3},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test2",
|
||||
CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: make([]api.Container, 2),
|
||||
NodeName: nodeName,
|
||||
},
|
||||
Status: api.PodStatus{
|
||||
Phase: api.PodRunning,
|
||||
ContainerStatuses: []api.ContainerStatus{
|
||||
{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
|
||||
{RestartCount: 3},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test3",
|
||||
CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: make([]api.Container, 2),
|
||||
NodeName: nodeName,
|
||||
},
|
||||
Status: api.PodStatus{
|
||||
Phase: api.PodSucceeded,
|
||||
ContainerStatuses: []api.ContainerStatus{
|
||||
{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
|
||||
{RestartCount: 3},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test4",
|
||||
CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: make([]api.Container, 2),
|
||||
NodeName: nodeName,
|
||||
},
|
||||
Status: api.PodStatus{
|
||||
Phase: api.PodFailed,
|
||||
ContainerStatuses: []api.ContainerStatus{
|
||||
{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
|
||||
{RestartCount: 3},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test5",
|
||||
CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: make([]api.Container, 2),
|
||||
NodeName: nodeName,
|
||||
},
|
||||
Status: api.PodStatus{
|
||||
Phase: api.PodUnknown,
|
||||
ContainerStatuses: []api.ContainerStatus{
|
||||
{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
|
||||
{RestartCount: 3},
|
||||
},
|
||||
},
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
func Example_printPodHideTerminated() {
|
||||
f, tf, _, ns := cmdtesting.NewAPIFactory()
|
||||
p := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{
|
||||
ColumnLabels: []string{},
|
||||
})
|
||||
printersinternal.AddHandlers(p)
|
||||
tf.Printer = p
|
||||
tf.Client = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: ns,
|
||||
Client: nil,
|
||||
}
|
||||
cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr)
|
||||
podList := newAllPhasePodList()
|
||||
// filter pods
|
||||
filterFuncs := f.DefaultResourceFilterFunc()
|
||||
filterOpts := f.DefaultResourceFilterOptions(cmd, false)
|
||||
_, filteredPodList, errs := cmdutil.FilterResourceList(podList, filterFuncs, filterOpts)
|
||||
if errs != nil {
|
||||
fmt.Printf("Unexpected filter error: %v\n", errs)
|
||||
}
|
||||
for _, pod := range filteredPodList {
|
||||
mapper, _ := f.Object()
|
||||
err := f.PrintObject(cmd, false, mapper, pod, os.Stdout)
|
||||
if err != nil {
|
||||
fmt.Printf("Unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
// Output:
|
||||
// NAME READY STATUS RESTARTS AGE
|
||||
// test1 1/2 Pending 6 10y
|
||||
// test2 1/2 Running 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.Client = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: ns,
|
||||
Client: nil,
|
||||
}
|
||||
cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr)
|
||||
podList := newAllPhasePodList()
|
||||
mapper, _ := f.Object()
|
||||
err := f.PrintObject(cmd, false, mapper, podList, os.Stdout)
|
||||
if err != nil {
|
||||
fmt.Printf("Unexpected error: %v", err)
|
||||
}
|
||||
// Output:
|
||||
// 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_printServiceWithNamespacesAndLabels() {
|
||||
f, tf, _, ns := cmdtesting.NewAPIFactory()
|
||||
p := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{
|
||||
WithNamespace: true,
|
||||
ColumnLabels: []string{"l1"},
|
||||
})
|
||||
printersinternal.AddHandlers(p)
|
||||
tf.Printer = p
|
||||
tf.Client = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: ns,
|
||||
Client: nil,
|
||||
}
|
||||
cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr)
|
||||
svc := &api.ServiceList{
|
||||
Items: []api.Service{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "svc1",
|
||||
Namespace: "ns1",
|
||||
CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
|
||||
Labels: map[string]string{
|
||||
"l1": "value",
|
||||
},
|
||||
},
|
||||
Spec: api.ServiceSpec{
|
||||
Ports: []api.ServicePort{
|
||||
{Protocol: "UDP", Port: 53},
|
||||
{Protocol: "TCP", Port: 53},
|
||||
},
|
||||
Selector: map[string]string{
|
||||
"s": "magic",
|
||||
},
|
||||
ClusterIP: "10.1.1.1",
|
||||
Type: api.ServiceTypeClusterIP,
|
||||
},
|
||||
Status: api.ServiceStatus{},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "svc2",
|
||||
Namespace: "ns2",
|
||||
CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
|
||||
Labels: map[string]string{
|
||||
"l1": "dolla-bill-yall",
|
||||
},
|
||||
},
|
||||
Spec: api.ServiceSpec{
|
||||
Ports: []api.ServicePort{
|
||||
{Protocol: "TCP", Port: 80},
|
||||
{Protocol: "TCP", Port: 8080},
|
||||
},
|
||||
Selector: map[string]string{
|
||||
"s": "kazam",
|
||||
},
|
||||
ClusterIP: "10.1.1.2",
|
||||
Type: api.ServiceTypeClusterIP,
|
||||
},
|
||||
Status: api.ServiceStatus{},
|
||||
}},
|
||||
}
|
||||
ld := strings.NewLineDelimiter(os.Stdout, "|")
|
||||
defer ld.Flush()
|
||||
mapper, _ := f.Object()
|
||||
err := f.PrintObject(cmd, false, mapper, 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|
|
||||
// ||
|
||||
}
|
||||
|
||||
func TestNormalizationFuncGlobalExistence(t *testing.T) {
|
||||
// This test can be safely deleted when we will not support multiple flag formats
|
||||
root := NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, os.Stdout, os.Stderr)
|
||||
|
||||
if root.Parent() != nil {
|
||||
t.Fatal("We expect the root command to be returned")
|
||||
}
|
||||
if root.GlobalNormalizationFunc() == nil {
|
||||
t.Fatal("We expect that root command has a global normalization function")
|
||||
}
|
||||
|
||||
if reflect.ValueOf(root.GlobalNormalizationFunc()).Pointer() != reflect.ValueOf(root.Flags().GetNormalizeFunc()).Pointer() {
|
||||
t.Fatal("root command seems to have a wrong normalization function")
|
||||
}
|
||||
|
||||
sub := root
|
||||
for sub.HasSubCommands() {
|
||||
sub = sub.Commands()[0]
|
||||
}
|
||||
|
||||
// In case of failure of this test check this PR: spf13/cobra#110
|
||||
if reflect.ValueOf(sub.Flags().GetNormalizeFunc()).Pointer() != reflect.ValueOf(root.Flags().GetNormalizeFunc()).Pointer() {
|
||||
t.Fatal("child and root commands should have the same normalization functions")
|
||||
}
|
||||
}
|
||||
|
||||
func genResponseWithJsonEncodedBody(bodyStruct interface{}) (*http.Response, error) {
|
||||
jsonBytes, err := json.Marshal(bodyStruct)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: bytesBody(jsonBytes)}, nil
|
||||
}
|
||||
|
||||
func Test_deprecatedAlias(t *testing.T) {
|
||||
var correctCommandCalled bool
|
||||
makeCobraCommand := func() *cobra.Command {
|
||||
cobraCmd := new(cobra.Command)
|
||||
cobraCmd.Use = "print five lines"
|
||||
cobraCmd.Run = func(*cobra.Command, []string) {
|
||||
correctCommandCalled = true
|
||||
}
|
||||
return cobraCmd
|
||||
}
|
||||
|
||||
original := makeCobraCommand()
|
||||
alias := deprecatedAlias("echo", makeCobraCommand())
|
||||
|
||||
if len(alias.Deprecated) == 0 {
|
||||
t.Error("deprecatedAlias should always have a non-empty .Deprecated")
|
||||
}
|
||||
if !stdstrings.Contains(alias.Deprecated, "print") {
|
||||
t.Error("deprecatedAlias should give the name of the new function in its .Deprecated field")
|
||||
}
|
||||
if !alias.Hidden {
|
||||
t.Error("deprecatedAlias should never have .Hidden == false (deprecated aliases should be hidden)")
|
||||
}
|
||||
|
||||
if alias.Name() != "echo" {
|
||||
t.Errorf("deprecatedAlias has name %q, expected %q",
|
||||
alias.Name(), "echo")
|
||||
}
|
||||
if original.Name() != "print" {
|
||||
t.Errorf("original command has name %q, expected %q",
|
||||
original.Name(), "print")
|
||||
}
|
||||
|
||||
buffer := new(bytes.Buffer)
|
||||
alias.SetOutput(buffer)
|
||||
alias.Execute()
|
||||
str := buffer.String()
|
||||
if !stdstrings.Contains(str, "deprecated") || !stdstrings.Contains(str, "print") {
|
||||
t.Errorf("deprecation warning %q does not include enough information", str)
|
||||
}
|
||||
|
||||
// It would be nice to test to see that original.Run == alias.Run
|
||||
// Unfortunately Golang does not allow comparing functions. I could do
|
||||
// this with reflect, but that's technically invoking undefined
|
||||
// behavior. Best we can do is make sure that the function is called.
|
||||
if !correctCommandCalled {
|
||||
t.Errorf("original function doesn't appear to have been called by alias")
|
||||
}
|
||||
}
|
||||
309
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/completion.go
generated
vendored
Normal file
309
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/completion.go
generated
vendored
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
/*
|
||||
Copyright 2016 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"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
const defaultBoilerPlate = `
|
||||
# Copyright 2016 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.
|
||||
`
|
||||
|
||||
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
|
||||
completion of kubectl commands. This can be done by sourcing it from
|
||||
the .bash_profile.
|
||||
|
||||
Detailed instructions on how to do this are available here:
|
||||
https://kubernetes.io/docs/tasks/tools/install-kubectl/#enabling-shell-autocompletion
|
||||
|
||||
Note for zsh users: [1] zsh completions are only supported in versions of zsh >= 5.2`))
|
||||
|
||||
completion_example = templates.Examples(i18n.T(`
|
||||
# Installing bash completion on macOS using homebrew
|
||||
## If running Bash 3.2 included with macOS
|
||||
brew install bash-completion
|
||||
## or, if running Bash 4.1+
|
||||
brew install bash-completion@2
|
||||
## If kubectl is installed via homebrew, this should start working immediately.
|
||||
## If you've installed via other means, you may need add the completion to your completion directory
|
||||
kubectl completion bash > $(brew --prefix)/etc/bash_completion.d/kubectl
|
||||
|
||||
|
||||
# Installing bash completion on Linux
|
||||
## Load the kubectl completion code for bash into the current shell
|
||||
source <(kubectl completion bash)
|
||||
## Write bash completion code to a file and source if from .bash_profile
|
||||
kubectl completion bash > ~/.kube/completion.bash.inc
|
||||
printf "
|
||||
# Kubectl shell completion
|
||||
source '$HOME/.kube/completion.bash.inc'
|
||||
" >> $HOME/.bash_profile
|
||||
source $HOME/.bash_profile
|
||||
|
||||
# Load the kubectl completion code for zsh[1] into the current shell
|
||||
source <(kubectl completion zsh)
|
||||
# Set the kubectl completion code for zsh[1] to autoload on startup
|
||||
kubectl completion zsh > "${fpath[1]}/_kubectl"`))
|
||||
)
|
||||
|
||||
var (
|
||||
completion_shells = map[string]func(out io.Writer, boilerPlate string, cmd *cobra.Command) error{
|
||||
"bash": runCompletionBash,
|
||||
"zsh": runCompletionZsh,
|
||||
}
|
||||
)
|
||||
|
||||
func NewCmdCompletion(out io.Writer, boilerPlate string) *cobra.Command {
|
||||
shells := []string{}
|
||||
for s := range completion_shells {
|
||||
shells = append(shells, s)
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "completion SHELL",
|
||||
Short: i18n.T("Output shell completion code for the specified shell (bash or zsh)"),
|
||||
Long: completion_long,
|
||||
Example: completion_example,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := RunCompletion(out, boilerPlate, cmd, args)
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
ValidArgs: shells,
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func RunCompletion(out io.Writer, boilerPlate string, cmd *cobra.Command, args []string) error {
|
||||
if len(args) == 0 {
|
||||
return cmdutil.UsageErrorf(cmd, "Shell not specified.")
|
||||
}
|
||||
if len(args) > 1 {
|
||||
return cmdutil.UsageErrorf(cmd, "Too many arguments. Expected only the shell type.")
|
||||
}
|
||||
run, found := completion_shells[args[0]]
|
||||
if !found {
|
||||
return cmdutil.UsageErrorf(cmd, "Unsupported shell type %q.", args[0])
|
||||
}
|
||||
|
||||
return run(out, boilerPlate, cmd.Parent())
|
||||
}
|
||||
|
||||
func runCompletionBash(out io.Writer, boilerPlate string, kubectl *cobra.Command) error {
|
||||
if len(boilerPlate) == 0 {
|
||||
boilerPlate = defaultBoilerPlate
|
||||
}
|
||||
if _, err := out.Write([]byte(boilerPlate)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return kubectl.GenBashCompletion(out)
|
||||
}
|
||||
|
||||
func runCompletionZsh(out io.Writer, boilerPlate string, kubectl *cobra.Command) error {
|
||||
zsh_head := "#compdef kubectl\n"
|
||||
|
||||
out.Write([]byte(zsh_head))
|
||||
|
||||
if len(boilerPlate) == 0 {
|
||||
boilerPlate = defaultBoilerPlate
|
||||
}
|
||||
if _, err := out.Write([]byte(boilerPlate)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
zsh_initialization := `
|
||||
__kubectl_bash_source() {
|
||||
alias shopt=':'
|
||||
alias _expand=_bash_expand
|
||||
alias _complete=_bash_comp
|
||||
emulate -L sh
|
||||
setopt kshglob noshglob braceexpand
|
||||
|
||||
source "$@"
|
||||
}
|
||||
|
||||
__kubectl_type() {
|
||||
# -t is not supported by zsh
|
||||
if [ "$1" == "-t" ]; then
|
||||
shift
|
||||
|
||||
# fake Bash 4 to disable "complete -o nospace". Instead
|
||||
# "compopt +-o nospace" is used in the code to toggle trailing
|
||||
# spaces. We don't support that, but leave trailing spaces on
|
||||
# all the time
|
||||
if [ "$1" = "__kubectl_compopt" ]; then
|
||||
echo builtin
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
type "$@"
|
||||
}
|
||||
|
||||
__kubectl_compgen() {
|
||||
local completions w
|
||||
completions=( $(compgen "$@") ) || return $?
|
||||
|
||||
# filter by given word as prefix
|
||||
while [[ "$1" = -* && "$1" != -- ]]; do
|
||||
shift
|
||||
shift
|
||||
done
|
||||
if [[ "$1" == -- ]]; then
|
||||
shift
|
||||
fi
|
||||
for w in "${completions[@]}"; do
|
||||
if [[ "${w}" = "$1"* ]]; then
|
||||
echo "${w}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
__kubectl_compopt() {
|
||||
true # don't do anything. Not supported by bashcompinit in zsh
|
||||
}
|
||||
|
||||
__kubectl_ltrim_colon_completions()
|
||||
{
|
||||
if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then
|
||||
# Remove colon-word prefix from COMPREPLY items
|
||||
local colon_word=${1%${1##*:}}
|
||||
local i=${#COMPREPLY[*]}
|
||||
while [[ $((--i)) -ge 0 ]]; do
|
||||
COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"}
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
__kubectl_get_comp_words_by_ref() {
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
prev="${COMP_WORDS[${COMP_CWORD}-1]}"
|
||||
words=("${COMP_WORDS[@]}")
|
||||
cword=("${COMP_CWORD[@]}")
|
||||
}
|
||||
|
||||
__kubectl_filedir() {
|
||||
local RET OLD_IFS w qw
|
||||
|
||||
__debug "_filedir $@ cur=$cur"
|
||||
if [[ "$1" = \~* ]]; then
|
||||
# somehow does not work. Maybe, zsh does not call this at all
|
||||
eval echo "$1"
|
||||
return 0
|
||||
fi
|
||||
|
||||
OLD_IFS="$IFS"
|
||||
IFS=$'\n'
|
||||
if [ "$1" = "-d" ]; then
|
||||
shift
|
||||
RET=( $(compgen -d) )
|
||||
else
|
||||
RET=( $(compgen -f) )
|
||||
fi
|
||||
IFS="$OLD_IFS"
|
||||
|
||||
IFS="," __debug "RET=${RET[@]} len=${#RET[@]}"
|
||||
|
||||
for w in ${RET[@]}; do
|
||||
if [[ ! "${w}" = "${cur}"* ]]; then
|
||||
continue
|
||||
fi
|
||||
if eval "[[ \"\${w}\" = *.$1 || -d \"\${w}\" ]]"; then
|
||||
qw="$(__kubectl_quote "${w}")"
|
||||
if [ -d "${w}" ]; then
|
||||
COMPREPLY+=("${qw}/")
|
||||
else
|
||||
COMPREPLY+=("${qw}")
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
__kubectl_quote() {
|
||||
if [[ $1 == \'* || $1 == \"* ]]; then
|
||||
# Leave out first character
|
||||
printf %q "${1:1}"
|
||||
else
|
||||
printf %q "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
autoload -U +X bashcompinit && bashcompinit
|
||||
|
||||
# use word boundary patterns for BSD or GNU sed
|
||||
LWORD='[[:<:]]'
|
||||
RWORD='[[:>:]]'
|
||||
if sed --help 2>&1 | grep -q GNU; then
|
||||
LWORD='\<'
|
||||
RWORD='\>'
|
||||
fi
|
||||
|
||||
__kubectl_convert_bash_to_zsh() {
|
||||
sed \
|
||||
-e 's/declare -F/whence -w/' \
|
||||
-e 's/_get_comp_words_by_ref "\$@"/_get_comp_words_by_ref "\$*"/' \
|
||||
-e 's/local \([a-zA-Z0-9_]*\)=/local \1; \1=/' \
|
||||
-e 's/flags+=("\(--.*\)=")/flags+=("\1"); two_word_flags+=("\1")/' \
|
||||
-e 's/must_have_one_flag+=("\(--.*\)=")/must_have_one_flag+=("\1")/' \
|
||||
-e "s/${LWORD}_filedir${RWORD}/__kubectl_filedir/g" \
|
||||
-e "s/${LWORD}_get_comp_words_by_ref${RWORD}/__kubectl_get_comp_words_by_ref/g" \
|
||||
-e "s/${LWORD}__ltrim_colon_completions${RWORD}/__kubectl_ltrim_colon_completions/g" \
|
||||
-e "s/${LWORD}compgen${RWORD}/__kubectl_compgen/g" \
|
||||
-e "s/${LWORD}compopt${RWORD}/__kubectl_compopt/g" \
|
||||
-e "s/${LWORD}declare${RWORD}/builtin declare/g" \
|
||||
-e "s/\\\$(type${RWORD}/\$(__kubectl_type/g" \
|
||||
<<'BASH_COMPLETION_EOF'
|
||||
`
|
||||
out.Write([]byte(zsh_initialization))
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
kubectl.GenBashCompletion(buf)
|
||||
out.Write(buf.Bytes())
|
||||
|
||||
zsh_tail := `
|
||||
BASH_COMPLETION_EOF
|
||||
}
|
||||
|
||||
__kubectl_bash_source <(__kubectl_convert_bash_to_zsh)
|
||||
_complete kubectl 2>/dev/null
|
||||
`
|
||||
out.Write([]byte(zsh_tail))
|
||||
return nil
|
||||
}
|
||||
89
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/BUILD
generated
vendored
Normal file
89
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"config.go",
|
||||
"create_authinfo.go",
|
||||
"create_cluster.go",
|
||||
"create_context.go",
|
||||
"current_context.go",
|
||||
"delete_cluster.go",
|
||||
"delete_context.go",
|
||||
"get_clusters.go",
|
||||
"get_contexts.go",
|
||||
"navigation_step_parser.go",
|
||||
"rename_context.go",
|
||||
"set.go",
|
||||
"unset.go",
|
||||
"use_context.go",
|
||||
"view.go",
|
||||
],
|
||||
visibility = [
|
||||
"//build/visible_to:pkg_kubectl_cmd_config_CONSUMERS",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/kubectl/cmd/templates:go_default_library",
|
||||
"//pkg/kubectl/cmd/util:go_default_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",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd/api:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd/api/latest:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"config_test.go",
|
||||
"create_authinfo_test.go",
|
||||
"create_cluster_test.go",
|
||||
"create_context_test.go",
|
||||
"current_context_test.go",
|
||||
"delete_cluster_test.go",
|
||||
"delete_context_test.go",
|
||||
"get_clusters_test.go",
|
||||
"get_contexts_test.go",
|
||||
"navigation_step_parser_test.go",
|
||||
"rename_context_test.go",
|
||||
"set_test.go",
|
||||
"unset_test.go",
|
||||
"use_context_test.go",
|
||||
"view_test.go",
|
||||
],
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/kubectl/cmd/util:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/flag:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd/api:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = [
|
||||
"//build/visible_to:pkg_kubectl_cmd_config_CONSUMERS",
|
||||
],
|
||||
)
|
||||
83
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/config.go
generated
vendored
Normal file
83
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/config.go
generated
vendored
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
Copyright 2014 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 config
|
||||
|
||||
import (
|
||||
"io"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
if len(pathOptions.ExplicitFileFlag) == 0 {
|
||||
pathOptions.ExplicitFileFlag = clientcmd.RecommendedConfigPathFlag
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "config SUBCOMMAND",
|
||||
Short: i18n.T("Modify kubeconfig files"),
|
||||
Long: templates.LongDesc(`
|
||||
Modify kubeconfig files using subcommands like "kubectl config set current-context my-context"
|
||||
|
||||
The loading order follows these rules:
|
||||
|
||||
1. If the --` + pathOptions.ExplicitFileFlag + ` flag is set, then only that file is loaded. The flag may only be set once and no merging takes place.
|
||||
2. If $` + pathOptions.EnvVar + ` environment variable is set, then it is used a list of paths (normal path delimitting rules for your system). These paths are merged. When a value is modified, it is modified in the file that defines the stanza. When a value is created, it is created in the first file that exists. If no files in the chain exist, then it creates the last file in the list.
|
||||
3. Otherwise, ` + path.Join("${HOME}", pathOptions.GlobalFileSubpath) + ` is used and no merging takes place.`),
|
||||
Run: cmdutil.DefaultSubCommandRun(errOut),
|
||||
}
|
||||
|
||||
// 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(NewCmdConfigSetCluster(out, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigSetAuthInfo(out, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigSetContext(out, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigSet(out, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigUnset(out, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigCurrentContext(out, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigUseContext(out, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigGetContexts(out, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigGetClusters(out, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigDeleteCluster(out, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigDeleteContext(out, errOut, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigRenameContext(out, pathOptions))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func toBool(propertyValue string) (bool, error) {
|
||||
boolValue := false
|
||||
if len(propertyValue) != 0 {
|
||||
var err error
|
||||
boolValue, err = strconv.ParseBool(propertyValue)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
return boolValue, nil
|
||||
}
|
||||
950
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/config_test.go
generated
vendored
Normal file
950
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/config_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,950 @@
|
|||
/*
|
||||
Copyright 2014 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 config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
)
|
||||
|
||||
func newRedFederalCowHammerConfig() clientcmdapi.Config {
|
||||
return clientcmdapi.Config{
|
||||
AuthInfos: map[string]*clientcmdapi.AuthInfo{
|
||||
"red-user": {Token: "red-token"}},
|
||||
Clusters: map[string]*clientcmdapi.Cluster{
|
||||
"cow-cluster": {Server: "http://cow.org:8080"}},
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
"federal-context": {AuthInfo: "red-user", Cluster: "cow-cluster"}},
|
||||
CurrentContext: "federal-context",
|
||||
}
|
||||
}
|
||||
|
||||
func Example_view() {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
test := configCommandTest{
|
||||
args: []string{"view"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
output := test.run(nil)
|
||||
fmt.Printf("%v", output)
|
||||
// Output:
|
||||
// apiVersion: v1
|
||||
// clusters:
|
||||
// - cluster:
|
||||
// server: http://cow.org:8080
|
||||
// name: cow-cluster
|
||||
// contexts:
|
||||
// - context:
|
||||
// cluster: cow-cluster
|
||||
// user: red-user
|
||||
// name: federal-context
|
||||
// current-context: federal-context
|
||||
// kind: Config
|
||||
// preferences: {}
|
||||
// users:
|
||||
// - name: red-user
|
||||
// user:
|
||||
// token: red-token
|
||||
}
|
||||
|
||||
func TestCurrentContext(t *testing.T) {
|
||||
startingConfig := newRedFederalCowHammerConfig()
|
||||
test := configCommandTest{
|
||||
args: []string{"current-context"},
|
||||
startingConfig: startingConfig,
|
||||
expectedConfig: startingConfig,
|
||||
expectedOutputs: []string{startingConfig.CurrentContext},
|
||||
}
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestSetCurrentContext(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
startingConfig := newRedFederalCowHammerConfig()
|
||||
|
||||
newContextName := "the-new-context"
|
||||
|
||||
startingConfig.Contexts[newContextName] = clientcmdapi.NewContext()
|
||||
expectedConfig.Contexts[newContextName] = clientcmdapi.NewContext()
|
||||
|
||||
expectedConfig.CurrentContext = newContextName
|
||||
|
||||
test := configCommandTest{
|
||||
args: []string{"use-context", "the-new-context"},
|
||||
startingConfig: startingConfig,
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestSetNonExistentContext(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
|
||||
test := configCommandTest{
|
||||
args: []string{"use-context", "non-existent-config"},
|
||||
startingConfig: expectedConfig,
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
func() {
|
||||
defer func() {
|
||||
// Restore cmdutil behavior.
|
||||
cmdutil.DefaultBehaviorOnFatal()
|
||||
}()
|
||||
|
||||
// Check exit code.
|
||||
cmdutil.BehaviorOnFatal(func(e string, code int) {
|
||||
if code != 1 {
|
||||
t.Errorf("The exit code is %d, expected 1", code)
|
||||
}
|
||||
expectedOutputs := []string{`no context exists with the name: "non-existent-config"`}
|
||||
test.checkOutput(e, expectedOutputs, t)
|
||||
})
|
||||
|
||||
test.run(t)
|
||||
}()
|
||||
}
|
||||
|
||||
func TestSetIntoExistingStruct(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
expectedConfig.AuthInfos["red-user"].Password = "new-path-value"
|
||||
test := configCommandTest{
|
||||
args: []string{"set", "users.red-user.password", "new-path-value"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestSetWithPathPrefixIntoExistingStruct(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
expectedConfig.Clusters["cow-cluster"].Server = "http://cow.org:8080/foo/baz"
|
||||
test := configCommandTest{
|
||||
args: []string{"set", "clusters.cow-cluster.server", "http://cow.org:8080/foo/baz"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
|
||||
dc := clientcmd.NewDefaultClientConfig(expectedConfig, &clientcmd.ConfigOverrides{})
|
||||
dcc, err := dc.ClientConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
expectedHost := "http://cow.org:8080/foo/baz"
|
||||
if expectedHost != dcc.Host {
|
||||
t.Fatalf("expected client.Config.Host = %q instead of %q", expectedHost, dcc.Host)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnsetStruct(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
delete(expectedConfig.AuthInfos, "red-user")
|
||||
test := configCommandTest{
|
||||
args: []string{"unset", "users.red-user"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestUnsetField(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
expectedConfig.AuthInfos["red-user"] = clientcmdapi.NewAuthInfo()
|
||||
test := configCommandTest{
|
||||
args: []string{"unset", "users.red-user.token"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestSetIntoNewStruct(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
cluster := clientcmdapi.NewCluster()
|
||||
cluster.Server = "new-server-value"
|
||||
expectedConfig.Clusters["big-cluster"] = cluster
|
||||
test := configCommandTest{
|
||||
args: []string{"set", "clusters.big-cluster.server", "new-server-value"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestSetBoolean(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
cluster := clientcmdapi.NewCluster()
|
||||
cluster.InsecureSkipTLSVerify = true
|
||||
expectedConfig.Clusters["big-cluster"] = cluster
|
||||
test := configCommandTest{
|
||||
args: []string{"set", "clusters.big-cluster.insecure-skip-tls-verify", "true"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestSetIntoNewConfig(t *testing.T) {
|
||||
expectedConfig := *clientcmdapi.NewConfig()
|
||||
context := clientcmdapi.NewContext()
|
||||
context.AuthInfo = "fake-user"
|
||||
expectedConfig.Contexts["new-context"] = context
|
||||
test := configCommandTest{
|
||||
args: []string{"set", "contexts.new-context.user", "fake-user"},
|
||||
startingConfig: *clientcmdapi.NewConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestNewEmptyAuth(t *testing.T) {
|
||||
expectedConfig := *clientcmdapi.NewConfig()
|
||||
expectedConfig.AuthInfos["the-user-name"] = clientcmdapi.NewAuthInfo()
|
||||
test := configCommandTest{
|
||||
args: []string{"set-credentials", "the-user-name"},
|
||||
startingConfig: *clientcmdapi.NewConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestAdditionalAuth(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
authInfo := clientcmdapi.NewAuthInfo()
|
||||
authInfo.Token = "token"
|
||||
expectedConfig.AuthInfos["another-user"] = authInfo
|
||||
test := configCommandTest{
|
||||
args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagBearerToken + "=token"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestEmbedClientCert(t *testing.T) {
|
||||
fakeCertFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(fakeCertFile.Name())
|
||||
fakeData := []byte("fake-data")
|
||||
ioutil.WriteFile(fakeCertFile.Name(), fakeData, 0600)
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
authInfo := clientcmdapi.NewAuthInfo()
|
||||
authInfo.ClientCertificateData = fakeData
|
||||
expectedConfig.AuthInfos["another-user"] = authInfo
|
||||
|
||||
test := configCommandTest{
|
||||
args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagCertFile + "=" + fakeCertFile.Name(), "--" + clientcmd.FlagEmbedCerts + "=true"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestEmbedClientKey(t *testing.T) {
|
||||
fakeKeyFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(fakeKeyFile.Name())
|
||||
fakeData := []byte("fake-data")
|
||||
ioutil.WriteFile(fakeKeyFile.Name(), fakeData, 0600)
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
authInfo := clientcmdapi.NewAuthInfo()
|
||||
authInfo.ClientKeyData = fakeData
|
||||
expectedConfig.AuthInfos["another-user"] = authInfo
|
||||
|
||||
test := configCommandTest{
|
||||
args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagKeyFile + "=" + fakeKeyFile.Name(), "--" + clientcmd.FlagEmbedCerts + "=true"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestEmbedNoKeyOrCertDisallowed(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
test := configCommandTest{
|
||||
args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagEmbedCerts + "=true"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
func() {
|
||||
defer func() {
|
||||
// Restore cmdutil behavior.
|
||||
cmdutil.DefaultBehaviorOnFatal()
|
||||
}()
|
||||
|
||||
// Check exit code.
|
||||
cmdutil.BehaviorOnFatal(func(e string, code int) {
|
||||
if code != 1 {
|
||||
t.Errorf("The exit code is %d, expected 1", code)
|
||||
}
|
||||
expectedOutputs := []string{"--client-certificate", "--client-key", "embed"}
|
||||
test.checkOutput(e, expectedOutputs, t)
|
||||
})
|
||||
|
||||
test.run(t)
|
||||
}()
|
||||
}
|
||||
|
||||
func TestEmptyTokenAndCertAllowed(t *testing.T) {
|
||||
fakeCertFile, _ := ioutil.TempFile("", "cert-file")
|
||||
defer os.Remove(fakeCertFile.Name())
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
authInfo := clientcmdapi.NewAuthInfo()
|
||||
authInfo.ClientCertificate = path.Base(fakeCertFile.Name())
|
||||
expectedConfig.AuthInfos["another-user"] = authInfo
|
||||
|
||||
test := configCommandTest{
|
||||
args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagCertFile + "=" + fakeCertFile.Name(), "--" + clientcmd.FlagBearerToken + "="},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestTokenAndCertAllowed(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
authInfo := clientcmdapi.NewAuthInfo()
|
||||
authInfo.Token = "token"
|
||||
authInfo.ClientCertificate = "/cert-file"
|
||||
expectedConfig.AuthInfos["another-user"] = authInfo
|
||||
test := configCommandTest{
|
||||
args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagCertFile + "=/cert-file", "--" + clientcmd.FlagBearerToken + "=token"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestTokenAndBasicDisallowed(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
test := configCommandTest{
|
||||
args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagUsername + "=myuser", "--" + clientcmd.FlagBearerToken + "=token"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
func() {
|
||||
defer func() {
|
||||
// Restore cmdutil behavior.
|
||||
cmdutil.DefaultBehaviorOnFatal()
|
||||
}()
|
||||
|
||||
// Check exit code.
|
||||
cmdutil.BehaviorOnFatal(func(e string, code int) {
|
||||
if code != 1 {
|
||||
t.Errorf("The exit code is %d, expected 1", code)
|
||||
}
|
||||
|
||||
expectedOutputs := []string{"--token", "--username"}
|
||||
test.checkOutput(e, expectedOutputs, t)
|
||||
})
|
||||
|
||||
test.run(t)
|
||||
}()
|
||||
}
|
||||
|
||||
func TestBasicClearsToken(t *testing.T) {
|
||||
authInfoWithToken := clientcmdapi.NewAuthInfo()
|
||||
authInfoWithToken.Token = "token"
|
||||
|
||||
authInfoWithBasic := clientcmdapi.NewAuthInfo()
|
||||
authInfoWithBasic.Username = "myuser"
|
||||
authInfoWithBasic.Password = "mypass"
|
||||
|
||||
startingConfig := newRedFederalCowHammerConfig()
|
||||
startingConfig.AuthInfos["another-user"] = authInfoWithToken
|
||||
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
expectedConfig.AuthInfos["another-user"] = authInfoWithBasic
|
||||
|
||||
test := configCommandTest{
|
||||
args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagUsername + "=myuser", "--" + clientcmd.FlagPassword + "=mypass"},
|
||||
startingConfig: startingConfig,
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestTokenClearsBasic(t *testing.T) {
|
||||
authInfoWithBasic := clientcmdapi.NewAuthInfo()
|
||||
authInfoWithBasic.Username = "myuser"
|
||||
authInfoWithBasic.Password = "mypass"
|
||||
|
||||
authInfoWithToken := clientcmdapi.NewAuthInfo()
|
||||
authInfoWithToken.Token = "token"
|
||||
|
||||
startingConfig := newRedFederalCowHammerConfig()
|
||||
startingConfig.AuthInfos["another-user"] = authInfoWithBasic
|
||||
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
expectedConfig.AuthInfos["another-user"] = authInfoWithToken
|
||||
|
||||
test := configCommandTest{
|
||||
args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagBearerToken + "=token"},
|
||||
startingConfig: startingConfig,
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestTokenLeavesCert(t *testing.T) {
|
||||
authInfoWithCerts := clientcmdapi.NewAuthInfo()
|
||||
authInfoWithCerts.ClientCertificate = "cert"
|
||||
authInfoWithCerts.ClientCertificateData = []byte("certdata")
|
||||
authInfoWithCerts.ClientKey = "key"
|
||||
authInfoWithCerts.ClientKeyData = []byte("keydata")
|
||||
|
||||
authInfoWithTokenAndCerts := clientcmdapi.NewAuthInfo()
|
||||
authInfoWithTokenAndCerts.Token = "token"
|
||||
authInfoWithTokenAndCerts.ClientCertificate = "cert"
|
||||
authInfoWithTokenAndCerts.ClientCertificateData = []byte("certdata")
|
||||
authInfoWithTokenAndCerts.ClientKey = "key"
|
||||
authInfoWithTokenAndCerts.ClientKeyData = []byte("keydata")
|
||||
|
||||
startingConfig := newRedFederalCowHammerConfig()
|
||||
startingConfig.AuthInfos["another-user"] = authInfoWithCerts
|
||||
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
expectedConfig.AuthInfos["another-user"] = authInfoWithTokenAndCerts
|
||||
|
||||
test := configCommandTest{
|
||||
args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagBearerToken + "=token"},
|
||||
startingConfig: startingConfig,
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestCertLeavesToken(t *testing.T) {
|
||||
authInfoWithToken := clientcmdapi.NewAuthInfo()
|
||||
authInfoWithToken.Token = "token"
|
||||
|
||||
authInfoWithTokenAndCerts := clientcmdapi.NewAuthInfo()
|
||||
authInfoWithTokenAndCerts.Token = "token"
|
||||
authInfoWithTokenAndCerts.ClientCertificate = "/cert"
|
||||
authInfoWithTokenAndCerts.ClientKey = "/key"
|
||||
|
||||
startingConfig := newRedFederalCowHammerConfig()
|
||||
startingConfig.AuthInfos["another-user"] = authInfoWithToken
|
||||
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
expectedConfig.AuthInfos["another-user"] = authInfoWithTokenAndCerts
|
||||
|
||||
test := configCommandTest{
|
||||
args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagCertFile + "=/cert", "--" + clientcmd.FlagKeyFile + "=/key"},
|
||||
startingConfig: startingConfig,
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestSetBytesBad(t *testing.T) {
|
||||
startingConfig := newRedFederalCowHammerConfig()
|
||||
startingConfig.Clusters["another-cluster"] = clientcmdapi.NewCluster()
|
||||
|
||||
test := configCommandTest{
|
||||
args: []string{"set", "clusters.another-cluster.certificate-authority-data", "cadata"},
|
||||
startingConfig: startingConfig,
|
||||
expectedConfig: startingConfig,
|
||||
}
|
||||
|
||||
func() {
|
||||
defer func() {
|
||||
// Restore cmdutil behavior.
|
||||
cmdutil.DefaultBehaviorOnFatal()
|
||||
}()
|
||||
|
||||
// Check exit code.
|
||||
cmdutil.BehaviorOnFatal(func(e string, code int) {
|
||||
if code != 1 {
|
||||
t.Errorf("The exit code is %d, expected 1", code)
|
||||
}
|
||||
})
|
||||
|
||||
test.run(t)
|
||||
}()
|
||||
}
|
||||
|
||||
func TestSetBytes(t *testing.T) {
|
||||
clusterInfoWithCAData := clientcmdapi.NewCluster()
|
||||
clusterInfoWithCAData.CertificateAuthorityData = []byte("cadata")
|
||||
|
||||
startingConfig := newRedFederalCowHammerConfig()
|
||||
startingConfig.Clusters["another-cluster"] = clientcmdapi.NewCluster()
|
||||
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
expectedConfig.Clusters["another-cluster"] = clusterInfoWithCAData
|
||||
|
||||
test := configCommandTest{
|
||||
args: []string{"set", "clusters.another-cluster.certificate-authority-data", "cadata", "--set-raw-bytes"},
|
||||
startingConfig: startingConfig,
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestSetBase64Bytes(t *testing.T) {
|
||||
clusterInfoWithCAData := clientcmdapi.NewCluster()
|
||||
clusterInfoWithCAData.CertificateAuthorityData = []byte("cadata")
|
||||
|
||||
startingConfig := newRedFederalCowHammerConfig()
|
||||
startingConfig.Clusters["another-cluster"] = clientcmdapi.NewCluster()
|
||||
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
expectedConfig.Clusters["another-cluster"] = clusterInfoWithCAData
|
||||
|
||||
test := configCommandTest{
|
||||
args: []string{"set", "clusters.another-cluster.certificate-authority-data", "Y2FkYXRh"},
|
||||
startingConfig: startingConfig,
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestUnsetBytes(t *testing.T) {
|
||||
clusterInfoWithCAData := clientcmdapi.NewCluster()
|
||||
clusterInfoWithCAData.CertificateAuthorityData = []byte("cadata")
|
||||
|
||||
startingConfig := newRedFederalCowHammerConfig()
|
||||
startingConfig.Clusters["another-cluster"] = clusterInfoWithCAData
|
||||
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
expectedConfig.Clusters["another-cluster"] = clientcmdapi.NewCluster()
|
||||
|
||||
test := configCommandTest{
|
||||
args: []string{"unset", "clusters.another-cluster.certificate-authority-data"},
|
||||
startingConfig: startingConfig,
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestCAClearsInsecure(t *testing.T) {
|
||||
fakeCAFile, _ := ioutil.TempFile("", "ca-file")
|
||||
defer os.Remove(fakeCAFile.Name())
|
||||
clusterInfoWithInsecure := clientcmdapi.NewCluster()
|
||||
clusterInfoWithInsecure.InsecureSkipTLSVerify = true
|
||||
|
||||
clusterInfoWithCA := clientcmdapi.NewCluster()
|
||||
clusterInfoWithCA.CertificateAuthority = path.Base(fakeCAFile.Name())
|
||||
|
||||
startingConfig := newRedFederalCowHammerConfig()
|
||||
startingConfig.Clusters["another-cluster"] = clusterInfoWithInsecure
|
||||
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
expectedConfig.Clusters["another-cluster"] = clusterInfoWithCA
|
||||
|
||||
test := configCommandTest{
|
||||
args: []string{"set-cluster", "another-cluster", "--" + clientcmd.FlagCAFile + "=" + fakeCAFile.Name()},
|
||||
startingConfig: startingConfig,
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestCAClearsCAData(t *testing.T) {
|
||||
clusterInfoWithCAData := clientcmdapi.NewCluster()
|
||||
clusterInfoWithCAData.CertificateAuthorityData = []byte("cadata")
|
||||
|
||||
clusterInfoWithCA := clientcmdapi.NewCluster()
|
||||
clusterInfoWithCA.CertificateAuthority = "/cafile"
|
||||
|
||||
startingConfig := newRedFederalCowHammerConfig()
|
||||
startingConfig.Clusters["another-cluster"] = clusterInfoWithCAData
|
||||
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
expectedConfig.Clusters["another-cluster"] = clusterInfoWithCA
|
||||
|
||||
test := configCommandTest{
|
||||
args: []string{"set-cluster", "another-cluster", "--" + clientcmd.FlagCAFile + "=/cafile", "--" + clientcmd.FlagInsecure + "=false"},
|
||||
startingConfig: startingConfig,
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestInsecureClearsCA(t *testing.T) {
|
||||
clusterInfoWithInsecure := clientcmdapi.NewCluster()
|
||||
clusterInfoWithInsecure.InsecureSkipTLSVerify = true
|
||||
|
||||
clusterInfoWithCA := clientcmdapi.NewCluster()
|
||||
clusterInfoWithCA.CertificateAuthority = "cafile"
|
||||
clusterInfoWithCA.CertificateAuthorityData = []byte("cadata")
|
||||
|
||||
startingConfig := newRedFederalCowHammerConfig()
|
||||
startingConfig.Clusters["another-cluster"] = clusterInfoWithCA
|
||||
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
expectedConfig.Clusters["another-cluster"] = clusterInfoWithInsecure
|
||||
|
||||
test := configCommandTest{
|
||||
args: []string{"set-cluster", "another-cluster", "--" + clientcmd.FlagInsecure + "=true"},
|
||||
startingConfig: startingConfig,
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestCADataClearsCA(t *testing.T) {
|
||||
fakeCAFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(fakeCAFile.Name())
|
||||
fakeData := []byte("cadata")
|
||||
ioutil.WriteFile(fakeCAFile.Name(), fakeData, 0600)
|
||||
|
||||
clusterInfoWithCAData := clientcmdapi.NewCluster()
|
||||
clusterInfoWithCAData.CertificateAuthorityData = fakeData
|
||||
|
||||
clusterInfoWithCA := clientcmdapi.NewCluster()
|
||||
clusterInfoWithCA.CertificateAuthority = "cafile"
|
||||
|
||||
startingConfig := newRedFederalCowHammerConfig()
|
||||
startingConfig.Clusters["another-cluster"] = clusterInfoWithCA
|
||||
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
expectedConfig.Clusters["another-cluster"] = clusterInfoWithCAData
|
||||
|
||||
test := configCommandTest{
|
||||
args: []string{"set-cluster", "another-cluster", "--" + clientcmd.FlagCAFile + "=" + fakeCAFile.Name(), "--" + clientcmd.FlagEmbedCerts + "=true"},
|
||||
startingConfig: startingConfig,
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestEmbedNoCADisallowed(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
test := configCommandTest{
|
||||
args: []string{"set-cluster", "another-cluster", "--" + clientcmd.FlagEmbedCerts + "=true"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
func() {
|
||||
defer func() {
|
||||
// Restore cmdutil behavior.
|
||||
cmdutil.DefaultBehaviorOnFatal()
|
||||
}()
|
||||
|
||||
// Check exit code.
|
||||
cmdutil.BehaviorOnFatal(func(e string, code int) {
|
||||
if code != 1 {
|
||||
t.Errorf("The exit code is %d, expected 1", code)
|
||||
}
|
||||
|
||||
expectedOutputs := []string{"--certificate-authority", "embed"}
|
||||
test.checkOutput(e, expectedOutputs, t)
|
||||
})
|
||||
|
||||
test.run(t)
|
||||
}()
|
||||
}
|
||||
|
||||
func TestCAAndInsecureDisallowed(t *testing.T) {
|
||||
test := configCommandTest{
|
||||
args: []string{"set-cluster", "another-cluster", "--" + clientcmd.FlagCAFile + "=cafile", "--" + clientcmd.FlagInsecure + "=true"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: newRedFederalCowHammerConfig(),
|
||||
}
|
||||
|
||||
func() {
|
||||
defer func() {
|
||||
// Restore cmdutil behavior.
|
||||
cmdutil.DefaultBehaviorOnFatal()
|
||||
}()
|
||||
|
||||
// Check exit code.
|
||||
cmdutil.BehaviorOnFatal(func(e string, code int) {
|
||||
if code != 1 {
|
||||
t.Errorf("The exit code is %d, expected 1", code)
|
||||
}
|
||||
|
||||
expectedOutputs := []string{"certificate", "insecure"}
|
||||
test.checkOutput(e, expectedOutputs, t)
|
||||
})
|
||||
|
||||
test.run(t)
|
||||
}()
|
||||
}
|
||||
|
||||
func TestMergeExistingAuth(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
authInfo := expectedConfig.AuthInfos["red-user"]
|
||||
authInfo.ClientKey = "/key"
|
||||
expectedConfig.AuthInfos["red-user"] = authInfo
|
||||
test := configCommandTest{
|
||||
args: []string{"set-credentials", "red-user", "--" + clientcmd.FlagKeyFile + "=/key"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestNewEmptyCluster(t *testing.T) {
|
||||
expectedConfig := *clientcmdapi.NewConfig()
|
||||
expectedConfig.Clusters["new-cluster"] = clientcmdapi.NewCluster()
|
||||
test := configCommandTest{
|
||||
args: []string{"set-cluster", "new-cluster"},
|
||||
startingConfig: *clientcmdapi.NewConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestAdditionalCluster(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
cluster := clientcmdapi.NewCluster()
|
||||
cluster.CertificateAuthority = "/ca-location"
|
||||
cluster.InsecureSkipTLSVerify = false
|
||||
cluster.Server = "serverlocation"
|
||||
expectedConfig.Clusters["different-cluster"] = cluster
|
||||
test := configCommandTest{
|
||||
args: []string{"set-cluster", "different-cluster", "--" + clientcmd.FlagAPIServer + "=serverlocation", "--" + clientcmd.FlagInsecure + "=false", "--" + clientcmd.FlagCAFile + "=/ca-location"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestOverwriteExistingCluster(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
cluster := clientcmdapi.NewCluster()
|
||||
cluster.Server = "serverlocation"
|
||||
expectedConfig.Clusters["cow-cluster"] = cluster
|
||||
|
||||
test := configCommandTest{
|
||||
args: []string{"set-cluster", "cow-cluster", "--" + clientcmd.FlagAPIServer + "=serverlocation"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestNewEmptyContext(t *testing.T) {
|
||||
expectedConfig := *clientcmdapi.NewConfig()
|
||||
expectedConfig.Contexts["new-context"] = clientcmdapi.NewContext()
|
||||
test := configCommandTest{
|
||||
args: []string{"set-context", "new-context"},
|
||||
startingConfig: *clientcmdapi.NewConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestAdditionalContext(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
context := clientcmdapi.NewContext()
|
||||
context.Cluster = "some-cluster"
|
||||
context.AuthInfo = "some-user"
|
||||
context.Namespace = "different-namespace"
|
||||
expectedConfig.Contexts["different-context"] = context
|
||||
test := configCommandTest{
|
||||
args: []string{"set-context", "different-context", "--" + clientcmd.FlagClusterName + "=some-cluster", "--" + clientcmd.FlagAuthInfoName + "=some-user", "--" + clientcmd.FlagNamespace + "=different-namespace"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestMergeExistingContext(t *testing.T) {
|
||||
expectedConfig := newRedFederalCowHammerConfig()
|
||||
context := expectedConfig.Contexts["federal-context"]
|
||||
context.Namespace = "hammer"
|
||||
expectedConfig.Contexts["federal-context"] = context
|
||||
|
||||
test := configCommandTest{
|
||||
args: []string{"set-context", "federal-context", "--" + clientcmd.FlagNamespace + "=hammer"},
|
||||
startingConfig: newRedFederalCowHammerConfig(),
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestToBool(t *testing.T) {
|
||||
type test struct {
|
||||
in string
|
||||
out bool
|
||||
err string
|
||||
}
|
||||
|
||||
tests := []test{
|
||||
{"", false, ""},
|
||||
{"true", true, ""},
|
||||
{"on", false, `strconv.ParseBool: parsing "on": invalid syntax`},
|
||||
}
|
||||
|
||||
for _, curr := range tests {
|
||||
b, err := toBool(curr.in)
|
||||
if (len(curr.err) != 0) && err == nil {
|
||||
t.Errorf("Expected error: %v, but got nil", curr.err)
|
||||
}
|
||||
if (len(curr.err) == 0) && err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if (err != nil) && (err.Error() != curr.err) {
|
||||
t.Errorf("Expected %v, got %v", curr.err, err)
|
||||
|
||||
}
|
||||
if b != curr.out {
|
||||
t.Errorf("Expected %v, got %v", curr.out, b)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func testConfigCommand(args []string, startingConfig clientcmdapi.Config, t *testing.T) (string, clientcmdapi.Config) {
|
||||
fakeKubeFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(fakeKubeFile.Name())
|
||||
err := clientcmd.WriteToFile(startingConfig, fakeKubeFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
argsToUse := make([]string, 0, 2+len(args))
|
||||
argsToUse = append(argsToUse, "--kubeconfig="+fakeKubeFile.Name())
|
||||
argsToUse = append(argsToUse, args...)
|
||||
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := NewCmdConfig(clientcmd.NewDefaultPathOptions(), buf, buf)
|
||||
cmd.SetArgs(argsToUse)
|
||||
cmd.Execute()
|
||||
|
||||
// outBytes, _ := ioutil.ReadFile(fakeKubeFile.Name())
|
||||
config := clientcmd.GetConfigFromFileOrDie(fakeKubeFile.Name())
|
||||
|
||||
return buf.String(), *config
|
||||
}
|
||||
|
||||
type configCommandTest struct {
|
||||
args []string
|
||||
startingConfig clientcmdapi.Config
|
||||
expectedConfig clientcmdapi.Config
|
||||
expectedOutputs []string
|
||||
}
|
||||
|
||||
func (test configCommandTest) checkOutput(out string, expectedOutputs []string, t *testing.T) {
|
||||
for _, expectedOutput := range expectedOutputs {
|
||||
if !strings.Contains(out, expectedOutput) {
|
||||
t.Errorf("expected '%s' in output, got '%s'", expectedOutput, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (test configCommandTest) run(t *testing.T) string {
|
||||
out, actualConfig := testConfigCommand(test.args, test.startingConfig, t)
|
||||
|
||||
testSetNilMapsToEmpties(reflect.ValueOf(&test.expectedConfig))
|
||||
testSetNilMapsToEmpties(reflect.ValueOf(&actualConfig))
|
||||
testClearLocationOfOrigin(&actualConfig)
|
||||
|
||||
if !apiequality.Semantic.DeepEqual(test.expectedConfig, actualConfig) {
|
||||
t.Errorf("diff: %v", diff.ObjectDiff(test.expectedConfig, actualConfig))
|
||||
t.Errorf("expected: %#v\n actual: %#v", test.expectedConfig, actualConfig)
|
||||
}
|
||||
|
||||
test.checkOutput(out, test.expectedOutputs, t)
|
||||
|
||||
return out
|
||||
}
|
||||
func testClearLocationOfOrigin(config *clientcmdapi.Config) {
|
||||
for key, obj := range config.AuthInfos {
|
||||
obj.LocationOfOrigin = ""
|
||||
config.AuthInfos[key] = obj
|
||||
}
|
||||
for key, obj := range config.Clusters {
|
||||
obj.LocationOfOrigin = ""
|
||||
config.Clusters[key] = obj
|
||||
}
|
||||
for key, obj := range config.Contexts {
|
||||
obj.LocationOfOrigin = ""
|
||||
config.Contexts[key] = obj
|
||||
}
|
||||
}
|
||||
func testSetNilMapsToEmpties(curr reflect.Value) {
|
||||
actualCurrValue := curr
|
||||
if curr.Kind() == reflect.Ptr {
|
||||
actualCurrValue = curr.Elem()
|
||||
}
|
||||
|
||||
switch actualCurrValue.Kind() {
|
||||
case reflect.Map:
|
||||
for _, mapKey := range actualCurrValue.MapKeys() {
|
||||
currMapValue := actualCurrValue.MapIndex(mapKey)
|
||||
testSetNilMapsToEmpties(currMapValue)
|
||||
}
|
||||
|
||||
case reflect.Struct:
|
||||
for fieldIndex := 0; fieldIndex < actualCurrValue.NumField(); fieldIndex++ {
|
||||
currFieldValue := actualCurrValue.Field(fieldIndex)
|
||||
|
||||
if currFieldValue.Kind() == reflect.Map && currFieldValue.IsNil() {
|
||||
newValue := reflect.MakeMap(currFieldValue.Type())
|
||||
currFieldValue.Set(newValue)
|
||||
} else {
|
||||
testSetNilMapsToEmpties(currFieldValue.Addr())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
298
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_authinfo.go
generated
vendored
Normal file
298
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_authinfo.go
generated
vendored
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
/*
|
||||
Copyright 2014 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 config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/apiserver/pkg/util/flag"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
type createAuthInfoOptions struct {
|
||||
configAccess clientcmd.ConfigAccess
|
||||
name string
|
||||
authPath flag.StringFlag
|
||||
clientCertificate flag.StringFlag
|
||||
clientKey flag.StringFlag
|
||||
token flag.StringFlag
|
||||
username flag.StringFlag
|
||||
password flag.StringFlag
|
||||
embedCertData flag.Tristate
|
||||
authProvider flag.StringFlag
|
||||
|
||||
authProviderArgs map[string]string
|
||||
authProviderArgsToRemove []string
|
||||
}
|
||||
|
||||
const (
|
||||
flagAuthProvider = "auth-provider"
|
||||
flagAuthProviderArg = "auth-provider-arg"
|
||||
)
|
||||
|
||||
var (
|
||||
create_authinfo_long = fmt.Sprintf(templates.LongDesc(`
|
||||
Sets a user entry in kubeconfig
|
||||
|
||||
Specifying a name that already exists will merge new fields on top of existing values.
|
||||
|
||||
Client-certificate flags:
|
||||
--%v=certfile --%v=keyfile
|
||||
|
||||
Bearer token flags:
|
||||
--%v=bearer_token
|
||||
|
||||
Basic auth flags:
|
||||
--%v=basic_user --%v=basic_password
|
||||
|
||||
Bearer token and basic auth are mutually exclusive.`), clientcmd.FlagCertFile, clientcmd.FlagKeyFile, clientcmd.FlagBearerToken, clientcmd.FlagUsername, clientcmd.FlagPassword)
|
||||
|
||||
create_authinfo_example = templates.Examples(`
|
||||
# Set only the "client-key" field on the "cluster-admin"
|
||||
# entry, without touching other values:
|
||||
kubectl config set-credentials cluster-admin --client-key=~/.kube/admin.key
|
||||
|
||||
# Set basic auth for the "cluster-admin" entry
|
||||
kubectl config set-credentials cluster-admin --username=admin --password=uXFGweU9l35qcif
|
||||
|
||||
# Embed client certificate data in the "cluster-admin" entry
|
||||
kubectl config set-credentials cluster-admin --client-certificate=~/.kube/admin.crt --embed-certs=true
|
||||
|
||||
# Enable the Google Compute Platform auth provider for the "cluster-admin" entry
|
||||
kubectl config set-credentials cluster-admin --auth-provider=gcp
|
||||
|
||||
# Enable the OpenID Connect auth provider for the "cluster-admin" entry with additional args
|
||||
kubectl config set-credentials cluster-admin --auth-provider=oidc --auth-provider-arg=client-id=foo --auth-provider-arg=client-secret=bar
|
||||
|
||||
# Remove the "client-secret" config value for the OpenID Connect auth provider for the "cluster-admin" entry
|
||||
kubectl config set-credentials cluster-admin --auth-provider=oidc --auth-provider-arg=client-secret-`)
|
||||
)
|
||||
|
||||
func NewCmdConfigSetAuthInfo(out io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command {
|
||||
options := &createAuthInfoOptions{configAccess: configAccess}
|
||||
return newCmdConfigSetAuthInfo(out, options)
|
||||
}
|
||||
|
||||
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),
|
||||
Short: i18n.T("Sets a user entry in kubeconfig"),
|
||||
Long: create_authinfo_long,
|
||||
Example: create_authinfo_example,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := options.complete(cmd, out)
|
||||
if err != nil {
|
||||
cmd.Help()
|
||||
cmdutil.CheckErr(err)
|
||||
}
|
||||
cmdutil.CheckErr(options.run())
|
||||
fmt.Fprintf(out, "User %q set.\n", options.name)
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().Var(&options.clientCertificate, clientcmd.FlagCertFile, "Path to "+clientcmd.FlagCertFile+" file for the user entry in kubeconfig")
|
||||
cmd.MarkFlagFilename(clientcmd.FlagCertFile)
|
||||
cmd.Flags().Var(&options.clientKey, clientcmd.FlagKeyFile, "Path to "+clientcmd.FlagKeyFile+" file for the user entry in kubeconfig")
|
||||
cmd.MarkFlagFilename(clientcmd.FlagKeyFile)
|
||||
cmd.Flags().Var(&options.token, clientcmd.FlagBearerToken, clientcmd.FlagBearerToken+" for the user entry in kubeconfig")
|
||||
cmd.Flags().Var(&options.username, clientcmd.FlagUsername, clientcmd.FlagUsername+" for the user entry in kubeconfig")
|
||||
cmd.Flags().Var(&options.password, clientcmd.FlagPassword, clientcmd.FlagPassword+" for the user entry in kubeconfig")
|
||||
cmd.Flags().Var(&options.authProvider, flagAuthProvider, "Auth provider for the user entry in kubeconfig")
|
||||
cmd.Flags().StringSlice(flagAuthProviderArg, nil, "'key=value' arguments for the auth provider")
|
||||
f := cmd.Flags().VarPF(&options.embedCertData, clientcmd.FlagEmbedCerts, "", "Embed client cert/key for the user entry in kubeconfig")
|
||||
f.NoOptDefVal = "true"
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o createAuthInfoOptions) run() error {
|
||||
err := o.validate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config, err := o.configAccess.GetStartingConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
startingStanza, exists := config.AuthInfos[o.name]
|
||||
if !exists {
|
||||
startingStanza = clientcmdapi.NewAuthInfo()
|
||||
}
|
||||
authInfo := o.modifyAuthInfo(*startingStanza)
|
||||
config.AuthInfos[o.name] = &authInfo
|
||||
|
||||
if err := clientcmd.ModifyConfig(o.configAccess, *config, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// authInfo builds an AuthInfo object from the options
|
||||
func (o *createAuthInfoOptions) modifyAuthInfo(existingAuthInfo clientcmdapi.AuthInfo) clientcmdapi.AuthInfo {
|
||||
modifiedAuthInfo := existingAuthInfo
|
||||
|
||||
var setToken, setBasic bool
|
||||
|
||||
if o.clientCertificate.Provided() {
|
||||
certPath := o.clientCertificate.Value()
|
||||
if o.embedCertData.Value() {
|
||||
modifiedAuthInfo.ClientCertificateData, _ = ioutil.ReadFile(certPath)
|
||||
modifiedAuthInfo.ClientCertificate = ""
|
||||
} else {
|
||||
certPath, _ = filepath.Abs(certPath)
|
||||
modifiedAuthInfo.ClientCertificate = certPath
|
||||
if len(modifiedAuthInfo.ClientCertificate) > 0 {
|
||||
modifiedAuthInfo.ClientCertificateData = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if o.clientKey.Provided() {
|
||||
keyPath := o.clientKey.Value()
|
||||
if o.embedCertData.Value() {
|
||||
modifiedAuthInfo.ClientKeyData, _ = ioutil.ReadFile(keyPath)
|
||||
modifiedAuthInfo.ClientKey = ""
|
||||
} else {
|
||||
keyPath, _ = filepath.Abs(keyPath)
|
||||
modifiedAuthInfo.ClientKey = keyPath
|
||||
if len(modifiedAuthInfo.ClientKey) > 0 {
|
||||
modifiedAuthInfo.ClientKeyData = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if o.token.Provided() {
|
||||
modifiedAuthInfo.Token = o.token.Value()
|
||||
setToken = len(modifiedAuthInfo.Token) > 0
|
||||
}
|
||||
|
||||
if o.username.Provided() {
|
||||
modifiedAuthInfo.Username = o.username.Value()
|
||||
setBasic = setBasic || len(modifiedAuthInfo.Username) > 0
|
||||
}
|
||||
if o.password.Provided() {
|
||||
modifiedAuthInfo.Password = o.password.Value()
|
||||
setBasic = setBasic || len(modifiedAuthInfo.Password) > 0
|
||||
}
|
||||
if o.authProvider.Provided() {
|
||||
newName := o.authProvider.Value()
|
||||
|
||||
// Only overwrite if the existing auth-provider is nil, or different than the newly specified one.
|
||||
if modifiedAuthInfo.AuthProvider == nil || modifiedAuthInfo.AuthProvider.Name != newName {
|
||||
modifiedAuthInfo.AuthProvider = &clientcmdapi.AuthProviderConfig{
|
||||
Name: newName,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if modifiedAuthInfo.AuthProvider != nil {
|
||||
if modifiedAuthInfo.AuthProvider.Config == nil {
|
||||
modifiedAuthInfo.AuthProvider.Config = make(map[string]string)
|
||||
}
|
||||
for _, toRemove := range o.authProviderArgsToRemove {
|
||||
delete(modifiedAuthInfo.AuthProvider.Config, toRemove)
|
||||
}
|
||||
for key, value := range o.authProviderArgs {
|
||||
modifiedAuthInfo.AuthProvider.Config[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
// If any auth info was set, make sure any other existing auth types are cleared
|
||||
if setToken || setBasic {
|
||||
if !setToken {
|
||||
modifiedAuthInfo.Token = ""
|
||||
}
|
||||
if !setBasic {
|
||||
modifiedAuthInfo.Username = ""
|
||||
modifiedAuthInfo.Password = ""
|
||||
}
|
||||
}
|
||||
|
||||
return modifiedAuthInfo
|
||||
}
|
||||
|
||||
func (o *createAuthInfoOptions) complete(cmd *cobra.Command, out io.Writer) error {
|
||||
args := cmd.Flags().Args()
|
||||
if len(args) != 1 {
|
||||
return fmt.Errorf("Unexpected args: %v", args)
|
||||
}
|
||||
|
||||
authProviderArgs, err := cmd.Flags().GetStringSlice(flagAuthProviderArg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error: %s\n", err)
|
||||
}
|
||||
|
||||
if len(authProviderArgs) > 0 {
|
||||
newPairs, removePairs, err := cmdutil.ParsePairs(authProviderArgs, flagAuthProviderArg, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error: %s\n", err)
|
||||
}
|
||||
o.authProviderArgs = newPairs
|
||||
o.authProviderArgsToRemove = removePairs
|
||||
}
|
||||
|
||||
o.name = args[0]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o createAuthInfoOptions) validate() error {
|
||||
if len(o.name) == 0 {
|
||||
return errors.New("you must specify a non-empty user name")
|
||||
}
|
||||
methods := []string{}
|
||||
if len(o.token.Value()) > 0 {
|
||||
methods = append(methods, fmt.Sprintf("--%v", clientcmd.FlagBearerToken))
|
||||
}
|
||||
if len(o.username.Value()) > 0 || len(o.password.Value()) > 0 {
|
||||
methods = append(methods, fmt.Sprintf("--%v/--%v", clientcmd.FlagUsername, clientcmd.FlagPassword))
|
||||
}
|
||||
if len(methods) > 1 {
|
||||
return fmt.Errorf("you cannot specify more than one authentication method at the same time: %v", strings.Join(methods, ", "))
|
||||
}
|
||||
if o.embedCertData.Value() {
|
||||
certPath := o.clientCertificate.Value()
|
||||
keyPath := o.clientKey.Value()
|
||||
if certPath == "" && keyPath == "" {
|
||||
return fmt.Errorf("you must specify a --%s or --%s to embed", clientcmd.FlagCertFile, clientcmd.FlagKeyFile)
|
||||
}
|
||||
if certPath != "" {
|
||||
if _, err := ioutil.ReadFile(certPath); err != nil {
|
||||
return fmt.Errorf("error reading %s data from %s: %v", clientcmd.FlagCertFile, certPath, err)
|
||||
}
|
||||
}
|
||||
if keyPath != "" {
|
||||
if _, err := ioutil.ReadFile(keyPath); err != nil {
|
||||
return fmt.Errorf("error reading %s data from %s: %v", clientcmd.FlagKeyFile, keyPath, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
260
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_authinfo_test.go
generated
vendored
Normal file
260
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_authinfo_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
/*
|
||||
Copyright 2016 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 config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apiserver/pkg/util/flag"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
func stringFlagFor(s string) flag.StringFlag {
|
||||
var f flag.StringFlag
|
||||
f.Set(s)
|
||||
return f
|
||||
}
|
||||
|
||||
func TestCreateAuthInfoOptions(t *testing.T) {
|
||||
tests := []struct {
|
||||
flags []string
|
||||
wantParseErr bool
|
||||
wantCompleteErr bool
|
||||
wantValidateErr bool
|
||||
|
||||
wantOptions *createAuthInfoOptions
|
||||
}{
|
||||
{
|
||||
flags: []string{
|
||||
"me",
|
||||
},
|
||||
wantOptions: &createAuthInfoOptions{
|
||||
name: "me",
|
||||
},
|
||||
},
|
||||
{
|
||||
flags: []string{
|
||||
"me",
|
||||
"--token=foo",
|
||||
},
|
||||
wantOptions: &createAuthInfoOptions{
|
||||
name: "me",
|
||||
token: stringFlagFor("foo"),
|
||||
},
|
||||
},
|
||||
{
|
||||
flags: []string{
|
||||
"me",
|
||||
"--username=jane",
|
||||
"--password=bar",
|
||||
},
|
||||
wantOptions: &createAuthInfoOptions{
|
||||
name: "me",
|
||||
username: stringFlagFor("jane"),
|
||||
password: stringFlagFor("bar"),
|
||||
},
|
||||
},
|
||||
{
|
||||
// Cannot provide both token and basic auth.
|
||||
flags: []string{
|
||||
"me",
|
||||
"--token=foo",
|
||||
"--username=jane",
|
||||
"--password=bar",
|
||||
},
|
||||
wantValidateErr: true,
|
||||
},
|
||||
{
|
||||
flags: []string{
|
||||
"--auth-provider=oidc",
|
||||
"--auth-provider-arg=client-id=foo",
|
||||
"--auth-provider-arg=client-secret=bar",
|
||||
"me",
|
||||
},
|
||||
wantOptions: &createAuthInfoOptions{
|
||||
name: "me",
|
||||
authProvider: stringFlagFor("oidc"),
|
||||
authProviderArgs: map[string]string{
|
||||
"client-id": "foo",
|
||||
"client-secret": "bar",
|
||||
},
|
||||
authProviderArgsToRemove: []string{},
|
||||
},
|
||||
},
|
||||
{
|
||||
flags: []string{
|
||||
"--auth-provider=oidc",
|
||||
"--auth-provider-arg=client-id-",
|
||||
"--auth-provider-arg=client-secret-",
|
||||
"me",
|
||||
},
|
||||
wantOptions: &createAuthInfoOptions{
|
||||
name: "me",
|
||||
authProvider: stringFlagFor("oidc"),
|
||||
authProviderArgs: map[string]string{},
|
||||
authProviderArgsToRemove: []string{
|
||||
"client-id",
|
||||
"client-secret",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
flags: []string{
|
||||
"--auth-provider-arg=client-id-", // auth provider name not required
|
||||
"--auth-provider-arg=client-secret-",
|
||||
"me",
|
||||
},
|
||||
wantOptions: &createAuthInfoOptions{
|
||||
name: "me",
|
||||
authProviderArgs: map[string]string{},
|
||||
authProviderArgsToRemove: []string{
|
||||
"client-id",
|
||||
"client-secret",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
flags: []string{
|
||||
"--auth-provider=oidc",
|
||||
"--auth-provider-arg=client-id", // values must be of form 'key=value' or 'key-'
|
||||
"me",
|
||||
},
|
||||
wantCompleteErr: true,
|
||||
},
|
||||
{
|
||||
flags: []string{
|
||||
// No name for authinfo provided.
|
||||
},
|
||||
wantCompleteErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
buff := new(bytes.Buffer)
|
||||
|
||||
opts := new(createAuthInfoOptions)
|
||||
cmd := newCmdConfigSetAuthInfo(buff, opts)
|
||||
if err := cmd.ParseFlags(test.flags); err != nil {
|
||||
if !test.wantParseErr {
|
||||
t.Errorf("case %d: parsing error for flags %q: %v: %s", i, test.flags, err, buff)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if test.wantParseErr {
|
||||
t.Errorf("case %d: expected parsing error for flags %q: %s", i, test.flags, buff)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := opts.complete(cmd, buff); err != nil {
|
||||
if !test.wantCompleteErr {
|
||||
t.Errorf("case %d: complete() error for flags %q: %s", i, test.flags, buff)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if test.wantCompleteErr {
|
||||
t.Errorf("case %d: complete() expected errors for flags %q: %s", i, test.flags, buff)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := opts.validate(); err != nil {
|
||||
if !test.wantValidateErr {
|
||||
t.Errorf("case %d: flags %q: validate failed: %v", i, test.flags, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if test.wantValidateErr {
|
||||
t.Errorf("case %d: flags %q: expected validate to fail", i, test.flags)
|
||||
continue
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(opts, test.wantOptions) {
|
||||
t.Errorf("case %d: flags %q: mis-matched options,\nwanted=%#v\ngot= %#v", i, test.flags, test.wantOptions, opts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type createAuthInfoTest struct {
|
||||
description string
|
||||
config clientcmdapi.Config
|
||||
args []string
|
||||
flags []string
|
||||
expected string
|
||||
expectedConfig clientcmdapi.Config
|
||||
}
|
||||
|
||||
func TestCreateAuthInfo(t *testing.T) {
|
||||
conf := clientcmdapi.Config{}
|
||||
test := createAuthInfoTest{
|
||||
description: "Testing for create aythinfo",
|
||||
config: conf,
|
||||
args: []string{"cluster-admin"},
|
||||
flags: []string{
|
||||
"--username=admin",
|
||||
"--password=uXFGweU9l35qcif",
|
||||
},
|
||||
expected: `User "cluster-admin" set.` + "\n",
|
||||
expectedConfig: clientcmdapi.Config{
|
||||
AuthInfos: map[string]*clientcmdapi.AuthInfo{
|
||||
"cluster-admin": {Username: "admin", Password: "uXFGweU9l35qcif"}},
|
||||
},
|
||||
}
|
||||
test.run(t)
|
||||
}
|
||||
func (test createAuthInfoTest) run(t *testing.T) {
|
||||
fakeKubeFile, err := ioutil.TempFile(os.TempDir(), "")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer os.Remove(fakeKubeFile.Name())
|
||||
err = clientcmd.WriteToFile(test.config, fakeKubeFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
pathOptions := clientcmd.NewDefaultPathOptions()
|
||||
pathOptions.GlobalFile = fakeKubeFile.Name()
|
||||
pathOptions.EnvVar = ""
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdConfigSetAuthInfo(buf, pathOptions)
|
||||
cmd.SetArgs(test.args)
|
||||
cmd.Flags().Parse(test.flags)
|
||||
if err := cmd.Execute(); err != nil {
|
||||
t.Fatalf("unexpected error executing command: %v,kubectl config set-credentials args: %v,flags: %v", err, test.args, test.flags)
|
||||
}
|
||||
config, err := clientcmd.LoadFromFile(fakeKubeFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error loading kubeconfig file: %v", err)
|
||||
}
|
||||
if len(test.expected) != 0 {
|
||||
if buf.String() != test.expected {
|
||||
t.Errorf("Fail in %q:\n expected %v\n but got %v\n", test.description, test.expected, buf.String())
|
||||
}
|
||||
}
|
||||
if test.expectedConfig.AuthInfos != nil {
|
||||
expectAuthInfo := test.expectedConfig.AuthInfos[test.args[0]]
|
||||
actualAuthInfo := config.AuthInfos[test.args[0]]
|
||||
if expectAuthInfo.Username != actualAuthInfo.Username || expectAuthInfo.Password != actualAuthInfo.Password {
|
||||
t.Errorf("Fail in %q:\n expected AuthInfo%v\n but found %v in kubeconfig\n", test.description, expectAuthInfo, actualAuthInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
179
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_cluster.go
generated
vendored
Normal file
179
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_cluster.go
generated
vendored
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
Copyright 2014 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 config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/apiserver/pkg/util/flag"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
type createClusterOptions struct {
|
||||
configAccess clientcmd.ConfigAccess
|
||||
name string
|
||||
server flag.StringFlag
|
||||
insecureSkipTLSVerify flag.Tristate
|
||||
certificateAuthority flag.StringFlag
|
||||
embedCAData flag.Tristate
|
||||
}
|
||||
|
||||
var (
|
||||
create_cluster_long = templates.LongDesc(`
|
||||
Sets a cluster entry in kubeconfig.
|
||||
|
||||
Specifying a name that already exists will merge new fields on top of existing values for those fields.`)
|
||||
|
||||
create_cluster_example = templates.Examples(`
|
||||
# Set only the server field on the e2e cluster entry without touching other values.
|
||||
kubectl config set-cluster e2e --server=https://1.2.3.4
|
||||
|
||||
# Embed certificate authority data for the e2e cluster entry
|
||||
kubectl config set-cluster e2e --certificate-authority=~/.kube/e2e/kubernetes.ca.crt
|
||||
|
||||
# Disable cert checking for the dev cluster entry
|
||||
kubectl config set-cluster e2e --insecure-skip-tls-verify=true`)
|
||||
)
|
||||
|
||||
func NewCmdConfigSetCluster(out io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command {
|
||||
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),
|
||||
Short: i18n.T("Sets a cluster entry in kubeconfig"),
|
||||
Long: create_cluster_long,
|
||||
Example: create_cluster_example,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(options.complete(cmd))
|
||||
cmdutil.CheckErr(options.run())
|
||||
fmt.Fprintf(out, "Cluster %q set.\n", options.name)
|
||||
},
|
||||
}
|
||||
|
||||
options.insecureSkipTLSVerify.Default(false)
|
||||
|
||||
cmd.Flags().Var(&options.server, clientcmd.FlagAPIServer, clientcmd.FlagAPIServer+" for the cluster entry in kubeconfig")
|
||||
f := cmd.Flags().VarPF(&options.insecureSkipTLSVerify, clientcmd.FlagInsecure, "", clientcmd.FlagInsecure+" for the cluster entry in kubeconfig")
|
||||
f.NoOptDefVal = "true"
|
||||
cmd.Flags().Var(&options.certificateAuthority, clientcmd.FlagCAFile, "Path to "+clientcmd.FlagCAFile+" file for the cluster entry in kubeconfig")
|
||||
cmd.MarkFlagFilename(clientcmd.FlagCAFile)
|
||||
f = cmd.Flags().VarPF(&options.embedCAData, clientcmd.FlagEmbedCerts, "", clientcmd.FlagEmbedCerts+" for the cluster entry in kubeconfig")
|
||||
f.NoOptDefVal = "true"
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o createClusterOptions) run() error {
|
||||
err := o.validate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config, err := o.configAccess.GetStartingConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
startingStanza, exists := config.Clusters[o.name]
|
||||
if !exists {
|
||||
startingStanza = clientcmdapi.NewCluster()
|
||||
}
|
||||
cluster := o.modifyCluster(*startingStanza)
|
||||
config.Clusters[o.name] = &cluster
|
||||
|
||||
if err := clientcmd.ModifyConfig(o.configAccess, *config, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// cluster builds a Cluster object from the options
|
||||
func (o *createClusterOptions) modifyCluster(existingCluster clientcmdapi.Cluster) clientcmdapi.Cluster {
|
||||
modifiedCluster := existingCluster
|
||||
|
||||
if o.server.Provided() {
|
||||
modifiedCluster.Server = o.server.Value()
|
||||
}
|
||||
if o.insecureSkipTLSVerify.Provided() {
|
||||
modifiedCluster.InsecureSkipTLSVerify = o.insecureSkipTLSVerify.Value()
|
||||
// Specifying insecure mode clears any certificate authority
|
||||
if modifiedCluster.InsecureSkipTLSVerify {
|
||||
modifiedCluster.CertificateAuthority = ""
|
||||
modifiedCluster.CertificateAuthorityData = nil
|
||||
}
|
||||
}
|
||||
if o.certificateAuthority.Provided() {
|
||||
caPath := o.certificateAuthority.Value()
|
||||
if o.embedCAData.Value() {
|
||||
modifiedCluster.CertificateAuthorityData, _ = ioutil.ReadFile(caPath)
|
||||
modifiedCluster.InsecureSkipTLSVerify = false
|
||||
modifiedCluster.CertificateAuthority = ""
|
||||
} else {
|
||||
caPath, _ = filepath.Abs(caPath)
|
||||
modifiedCluster.CertificateAuthority = caPath
|
||||
// Specifying a certificate authority file clears certificate authority data and insecure mode
|
||||
if caPath != "" {
|
||||
modifiedCluster.InsecureSkipTLSVerify = false
|
||||
modifiedCluster.CertificateAuthorityData = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modifiedCluster
|
||||
}
|
||||
|
||||
func (o *createClusterOptions) complete(cmd *cobra.Command) error {
|
||||
args := cmd.Flags().Args()
|
||||
if len(args) != 1 {
|
||||
cmd.Help()
|
||||
return fmt.Errorf("Unexpected args: %v", args)
|
||||
}
|
||||
|
||||
o.name = args[0]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o createClusterOptions) validate() error {
|
||||
if len(o.name) == 0 {
|
||||
return errors.New("you must specify a non-empty cluster name")
|
||||
}
|
||||
if o.insecureSkipTLSVerify.Value() && o.certificateAuthority.Value() != "" {
|
||||
return errors.New("you cannot specify a certificate authority and insecure mode at the same time")
|
||||
}
|
||||
if o.embedCAData.Value() {
|
||||
caPath := o.certificateAuthority.Value()
|
||||
if caPath == "" {
|
||||
return fmt.Errorf("you must specify a --%s to embed", clientcmd.FlagCAFile)
|
||||
}
|
||||
if _, err := ioutil.ReadFile(caPath); err != nil {
|
||||
return fmt.Errorf("could not read %s data from %s: %v", clientcmd.FlagCAFile, caPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
119
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_cluster_test.go
generated
vendored
Normal file
119
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_cluster_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
type createClusterTest struct {
|
||||
description string
|
||||
config clientcmdapi.Config
|
||||
args []string
|
||||
flags []string
|
||||
expected string
|
||||
expectedConfig clientcmdapi.Config
|
||||
}
|
||||
|
||||
func TestCreateCluster(t *testing.T) {
|
||||
conf := clientcmdapi.Config{}
|
||||
test := createClusterTest{
|
||||
description: "Testing 'kubectl config set-cluster' with a new cluster",
|
||||
config: conf,
|
||||
args: []string{"my-cluster"},
|
||||
flags: []string{
|
||||
"--server=http://192.168.0.1",
|
||||
},
|
||||
expected: `Cluster "my-cluster" set.` + "\n",
|
||||
expectedConfig: clientcmdapi.Config{
|
||||
Clusters: map[string]*clientcmdapi.Cluster{
|
||||
"my-cluster": {Server: "http://192.168.0.1"},
|
||||
},
|
||||
},
|
||||
}
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestModifyCluster(t *testing.T) {
|
||||
conf := clientcmdapi.Config{
|
||||
Clusters: map[string]*clientcmdapi.Cluster{
|
||||
"my-cluster": {Server: "https://192.168.0.1"},
|
||||
},
|
||||
}
|
||||
test := createClusterTest{
|
||||
description: "Testing 'kubectl config set-cluster' with an existing cluster",
|
||||
config: conf,
|
||||
args: []string{"my-cluster"},
|
||||
flags: []string{
|
||||
"--server=https://192.168.0.99",
|
||||
},
|
||||
expected: `Cluster "my-cluster" set.` + "\n",
|
||||
expectedConfig: clientcmdapi.Config{
|
||||
Clusters: map[string]*clientcmdapi.Cluster{
|
||||
"my-cluster": {Server: "https://192.168.0.99"},
|
||||
},
|
||||
},
|
||||
}
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func (test createClusterTest) run(t *testing.T) {
|
||||
fakeKubeFile, err := ioutil.TempFile(os.TempDir(), "")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer os.Remove(fakeKubeFile.Name())
|
||||
err = clientcmd.WriteToFile(test.config, fakeKubeFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
pathOptions := clientcmd.NewDefaultPathOptions()
|
||||
pathOptions.GlobalFile = fakeKubeFile.Name()
|
||||
pathOptions.EnvVar = ""
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdConfigSetCluster(buf, pathOptions)
|
||||
cmd.SetArgs(test.args)
|
||||
cmd.Flags().Parse(test.flags)
|
||||
if err := cmd.Execute(); err != nil {
|
||||
t.Fatalf("unexpected error executing command: %v, args: %v, flags: %v", err, test.args, test.flags)
|
||||
}
|
||||
config, err := clientcmd.LoadFromFile(fakeKubeFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error loading kubeconfig file: %v", err)
|
||||
}
|
||||
if len(test.expected) != 0 {
|
||||
if buf.String() != test.expected {
|
||||
t.Errorf("Failed in %q\n expected %v\n but got %v", test.description, test.expected, buf.String())
|
||||
}
|
||||
}
|
||||
if len(test.args) > 0 {
|
||||
cluster, ok := config.Clusters[test.args[0]]
|
||||
if !ok {
|
||||
t.Errorf("expected cluster %v, but got nil", test.args[0])
|
||||
return
|
||||
}
|
||||
if cluster.Server != test.expectedConfig.Clusters[test.args[0]].Server {
|
||||
t.Errorf("Fail in %q\n expected cluster server %v\n but got %v\n ", test.description, test.expectedConfig.Clusters[test.args[0]].Server, cluster.Server)
|
||||
}
|
||||
}
|
||||
}
|
||||
138
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_context.go
generated
vendored
Normal file
138
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_context.go
generated
vendored
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
Copyright 2014 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 config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/apiserver/pkg/util/flag"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
type createContextOptions struct {
|
||||
configAccess clientcmd.ConfigAccess
|
||||
name string
|
||||
cluster flag.StringFlag
|
||||
authInfo flag.StringFlag
|
||||
namespace flag.StringFlag
|
||||
}
|
||||
|
||||
var (
|
||||
create_context_long = templates.LongDesc(`
|
||||
Sets a context entry in kubeconfig
|
||||
|
||||
Specifying a name that already exists will merge new fields on top of existing values for those fields.`)
|
||||
|
||||
create_context_example = templates.Examples(`
|
||||
# Set the user field on the gce context entry without touching other values
|
||||
kubectl config set-context gce --user=cluster-admin`)
|
||||
)
|
||||
|
||||
func NewCmdConfigSetContext(out io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command {
|
||||
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),
|
||||
Short: i18n.T("Sets a context entry in kubeconfig"),
|
||||
Long: create_context_long,
|
||||
Example: create_context_example,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(options.complete(cmd))
|
||||
exists, err := options.run()
|
||||
cmdutil.CheckErr(err)
|
||||
if exists {
|
||||
fmt.Fprintf(out, "Context %q modified.\n", options.name)
|
||||
} else {
|
||||
fmt.Fprintf(out, "Context %q created.\n", options.name)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().Var(&options.cluster, clientcmd.FlagClusterName, clientcmd.FlagClusterName+" for the context entry in kubeconfig")
|
||||
cmd.Flags().Var(&options.authInfo, clientcmd.FlagAuthInfoName, clientcmd.FlagAuthInfoName+" for the context entry in kubeconfig")
|
||||
cmd.Flags().Var(&options.namespace, clientcmd.FlagNamespace, clientcmd.FlagNamespace+" for the context entry in kubeconfig")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o createContextOptions) run() (bool, error) {
|
||||
err := o.validate()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
config, err := o.configAccess.GetStartingConfig()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
startingStanza, exists := config.Contexts[o.name]
|
||||
if !exists {
|
||||
startingStanza = clientcmdapi.NewContext()
|
||||
}
|
||||
context := o.modifyContext(*startingStanza)
|
||||
config.Contexts[o.name] = &context
|
||||
|
||||
if err := clientcmd.ModifyConfig(o.configAccess, *config, true); err != nil {
|
||||
return exists, err
|
||||
}
|
||||
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
func (o *createContextOptions) modifyContext(existingContext clientcmdapi.Context) clientcmdapi.Context {
|
||||
modifiedContext := existingContext
|
||||
|
||||
if o.cluster.Provided() {
|
||||
modifiedContext.Cluster = o.cluster.Value()
|
||||
}
|
||||
if o.authInfo.Provided() {
|
||||
modifiedContext.AuthInfo = o.authInfo.Value()
|
||||
}
|
||||
if o.namespace.Provided() {
|
||||
modifiedContext.Namespace = o.namespace.Value()
|
||||
}
|
||||
|
||||
return modifiedContext
|
||||
}
|
||||
|
||||
func (o *createContextOptions) complete(cmd *cobra.Command) error {
|
||||
args := cmd.Flags().Args()
|
||||
if len(args) != 1 {
|
||||
cmd.Help()
|
||||
return fmt.Errorf("Unexpected args: %v", args)
|
||||
}
|
||||
|
||||
o.name = args[0]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o createContextOptions) validate() error {
|
||||
if len(o.name) == 0 {
|
||||
return errors.New("you must specify a non-empty context name")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
118
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_context_test.go
generated
vendored
Normal file
118
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_context_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
type createContextTest struct {
|
||||
description string
|
||||
config clientcmdapi.Config //initiate kubectl config
|
||||
args []string //kubectl set-context args
|
||||
flags []string //kubectl set-context flags
|
||||
expected string //expectd out
|
||||
expectedConfig clientcmdapi.Config //expect kubectl config
|
||||
}
|
||||
|
||||
func TestCreateContext(t *testing.T) {
|
||||
conf := clientcmdapi.Config{}
|
||||
test := createContextTest{
|
||||
description: "Testing for create a new context",
|
||||
config: conf,
|
||||
args: []string{"shaker-context"},
|
||||
flags: []string{
|
||||
"--cluster=cluster_nickname",
|
||||
"--user=user_nickname",
|
||||
"--namespace=namespace",
|
||||
},
|
||||
expected: `Context "shaker-context" created.` + "\n",
|
||||
expectedConfig: clientcmdapi.Config{
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
"shaker-context": {AuthInfo: "user_nickname", Cluster: "cluster_nickname", Namespace: "namespace"}},
|
||||
},
|
||||
}
|
||||
test.run(t)
|
||||
}
|
||||
func TestModifyContext(t *testing.T) {
|
||||
conf := clientcmdapi.Config{
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
"shaker-context": {AuthInfo: "blue-user", Cluster: "big-cluster", Namespace: "saw-ns"},
|
||||
"not-this": {AuthInfo: "blue-user", Cluster: "big-cluster", Namespace: "saw-ns"}}}
|
||||
test := createContextTest{
|
||||
description: "Testing for modify a already exist context",
|
||||
config: conf,
|
||||
args: []string{"shaker-context"},
|
||||
flags: []string{
|
||||
"--cluster=cluster_nickname",
|
||||
"--user=user_nickname",
|
||||
"--namespace=namespace",
|
||||
},
|
||||
expected: `Context "shaker-context" modified.` + "\n",
|
||||
expectedConfig: clientcmdapi.Config{
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
"shaker-context": {AuthInfo: "user_nickname", Cluster: "cluster_nickname", Namespace: "namespace"},
|
||||
"not-this": {AuthInfo: "blue-user", Cluster: "big-cluster", Namespace: "saw-ns"}}},
|
||||
}
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func (test createContextTest) run(t *testing.T) {
|
||||
fakeKubeFile, err := ioutil.TempFile(os.TempDir(), "")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer os.Remove(fakeKubeFile.Name())
|
||||
err = clientcmd.WriteToFile(test.config, fakeKubeFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
pathOptions := clientcmd.NewDefaultPathOptions()
|
||||
pathOptions.GlobalFile = fakeKubeFile.Name()
|
||||
pathOptions.EnvVar = ""
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdConfigSetContext(buf, pathOptions)
|
||||
cmd.SetArgs(test.args)
|
||||
cmd.Flags().Parse(test.flags)
|
||||
if err := cmd.Execute(); err != nil {
|
||||
t.Fatalf("unexpected error executing command: %v,kubectl set-context args: %v,flags: %v", err, test.args, test.flags)
|
||||
}
|
||||
config, err := clientcmd.LoadFromFile(fakeKubeFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error loading kubeconfig file: %v", err)
|
||||
}
|
||||
if len(test.expected) != 0 {
|
||||
if buf.String() != test.expected {
|
||||
t.Errorf("Fail in %q:\n expected %v\n but got %v\n", test.description, test.expected, buf.String())
|
||||
}
|
||||
}
|
||||
if test.expectedConfig.Contexts != nil {
|
||||
expectContext := test.expectedConfig.Contexts[test.args[0]]
|
||||
actualContext := config.Contexts[test.args[0]]
|
||||
if expectContext.AuthInfo != actualContext.AuthInfo || expectContext.Cluster != actualContext.Cluster ||
|
||||
expectContext.Namespace != actualContext.Namespace {
|
||||
t.Errorf("Fail in %q:\n expected Context %v\n but found %v in kubeconfig\n", test.description, expectContext, actualContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
74
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/current_context.go
generated
vendored
Normal file
74
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/current_context.go
generated
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
Copyright 2014 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 config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
type CurrentContextOptions struct {
|
||||
ConfigAccess clientcmd.ConfigAccess
|
||||
}
|
||||
|
||||
var (
|
||||
current_context_long = templates.LongDesc(`
|
||||
Displays the current-context`)
|
||||
|
||||
current_context_example = templates.Examples(`
|
||||
# Display the current-context
|
||||
kubectl config current-context`)
|
||||
)
|
||||
|
||||
func NewCmdConfigCurrentContext(out io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command {
|
||||
options := &CurrentContextOptions{ConfigAccess: configAccess}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "current-context",
|
||||
Short: i18n.T("Displays the current-context"),
|
||||
Long: current_context_long,
|
||||
Example: current_context_example,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := RunCurrentContext(out, options)
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func RunCurrentContext(out io.Writer, options *CurrentContextOptions) error {
|
||||
config, err := options.ConfigAccess.GetStartingConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if config.CurrentContext == "" {
|
||||
err = fmt.Errorf("current-context is not set\n")
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(out, "%s\n", config.CurrentContext)
|
||||
return nil
|
||||
}
|
||||
93
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/current_context_test.go
generated
vendored
Normal file
93
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/current_context_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
Copyright 2014 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 config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
type currentContextTest struct {
|
||||
startingConfig clientcmdapi.Config
|
||||
expectedError string
|
||||
}
|
||||
|
||||
func newFederalContextConfig() clientcmdapi.Config {
|
||||
return clientcmdapi.Config{
|
||||
CurrentContext: "federal-context",
|
||||
}
|
||||
}
|
||||
|
||||
func TestCurrentContextWithSetContext(t *testing.T) {
|
||||
test := currentContextTest{
|
||||
startingConfig: newFederalContextConfig(),
|
||||
expectedError: "",
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestCurrentContextWithUnsetContext(t *testing.T) {
|
||||
test := currentContextTest{
|
||||
startingConfig: *clientcmdapi.NewConfig(),
|
||||
expectedError: "current-context is not set",
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func (test currentContextTest) run(t *testing.T) {
|
||||
fakeKubeFile, err := ioutil.TempFile("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer os.Remove(fakeKubeFile.Name())
|
||||
err = clientcmd.WriteToFile(test.startingConfig, fakeKubeFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
pathOptions := clientcmd.NewDefaultPathOptions()
|
||||
pathOptions.GlobalFile = fakeKubeFile.Name()
|
||||
pathOptions.EnvVar = ""
|
||||
options := CurrentContextOptions{
|
||||
ConfigAccess: pathOptions,
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
err = RunCurrentContext(buf, &options)
|
||||
if len(test.expectedError) != 0 {
|
||||
if err == nil {
|
||||
t.Errorf("Did not get %v", test.expectedError)
|
||||
} else {
|
||||
if !strings.Contains(err.Error(), test.expectedError) {
|
||||
t.Errorf("Expected %v, but got %v", test.expectedError, err)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
83
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/delete_cluster.go
generated
vendored
Normal file
83
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/delete_cluster.go
generated
vendored
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
Copyright 2016 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 config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
var (
|
||||
delete_cluster_example = templates.Examples(`
|
||||
# Delete the minikube cluster
|
||||
kubectl config delete-cluster minikube`)
|
||||
)
|
||||
|
||||
func NewCmdConfigDeleteCluster(out io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "delete-cluster NAME",
|
||||
Short: i18n.T("Delete the specified cluster from the kubeconfig"),
|
||||
Long: "Delete the specified cluster from the kubeconfig",
|
||||
Example: delete_cluster_example,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := runDeleteCluster(out, configAccess, cmd)
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runDeleteCluster(out io.Writer, configAccess clientcmd.ConfigAccess, cmd *cobra.Command) error {
|
||||
config, err := configAccess.GetStartingConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args := cmd.Flags().Args()
|
||||
if len(args) != 1 {
|
||||
cmd.Help()
|
||||
return nil
|
||||
}
|
||||
|
||||
configFile := configAccess.GetDefaultFilename()
|
||||
if configAccess.IsExplicitFile() {
|
||||
configFile = configAccess.GetExplicitFile()
|
||||
}
|
||||
|
||||
name := args[0]
|
||||
_, ok := config.Clusters[name]
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot delete cluster %s, not in %s", name, configFile)
|
||||
}
|
||||
|
||||
delete(config.Clusters, name)
|
||||
|
||||
if err := clientcmd.ModifyConfig(configAccess, *config, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(out, "deleted cluster %s from %s\n", name, configFile)
|
||||
|
||||
return nil
|
||||
}
|
||||
97
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/delete_cluster_test.go
generated
vendored
Normal file
97
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/delete_cluster_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
Copyright 2016 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 config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
type deleteClusterTest struct {
|
||||
config clientcmdapi.Config
|
||||
clusterToDelete string
|
||||
expectedClusters []string
|
||||
expectedOut string
|
||||
}
|
||||
|
||||
func TestDeleteCluster(t *testing.T) {
|
||||
conf := clientcmdapi.Config{
|
||||
Clusters: map[string]*clientcmdapi.Cluster{
|
||||
"minikube": {Server: "https://192.168.0.99"},
|
||||
"otherkube": {Server: "https://192.168.0.100"},
|
||||
},
|
||||
}
|
||||
test := deleteClusterTest{
|
||||
config: conf,
|
||||
clusterToDelete: "minikube",
|
||||
expectedClusters: []string{"otherkube"},
|
||||
expectedOut: "deleted cluster minikube from %s\n",
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func (test deleteClusterTest) run(t *testing.T) {
|
||||
fakeKubeFile, err := ioutil.TempFile("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer os.Remove(fakeKubeFile.Name())
|
||||
err = clientcmd.WriteToFile(test.config, fakeKubeFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
pathOptions := clientcmd.NewDefaultPathOptions()
|
||||
pathOptions.GlobalFile = fakeKubeFile.Name()
|
||||
pathOptions.EnvVar = ""
|
||||
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdConfigDeleteCluster(buf, pathOptions)
|
||||
cmd.SetArgs([]string{test.clusterToDelete})
|
||||
if err := cmd.Execute(); err != nil {
|
||||
t.Fatalf("unexpected error executing command: %v", err)
|
||||
}
|
||||
|
||||
expectedOutWithFile := fmt.Sprintf(test.expectedOut, fakeKubeFile.Name())
|
||||
if expectedOutWithFile != buf.String() {
|
||||
t.Errorf("expected output %s, but got %s", expectedOutWithFile, buf.String())
|
||||
return
|
||||
}
|
||||
|
||||
// Verify cluster was removed from kubeconfig file
|
||||
config, err := clientcmd.LoadFromFile(fakeKubeFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error loading kubeconfig file: %v", err)
|
||||
}
|
||||
|
||||
clusters := make([]string, 0, len(config.Clusters))
|
||||
for k := range config.Clusters {
|
||||
clusters = append(clusters, k)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(test.expectedClusters, clusters) {
|
||||
t.Errorf("expected clusters %v, but found %v in kubeconfig", test.expectedClusters, clusters)
|
||||
}
|
||||
}
|
||||
87
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/delete_context.go
generated
vendored
Normal file
87
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/delete_context.go
generated
vendored
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
Copyright 2016 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 config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
var (
|
||||
delete_context_example = templates.Examples(`
|
||||
# Delete the context for the minikube cluster
|
||||
kubectl config delete-context minikube`)
|
||||
)
|
||||
|
||||
func NewCmdConfigDeleteContext(out, errOut io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "delete-context NAME",
|
||||
Short: i18n.T("Delete the specified context from the kubeconfig"),
|
||||
Long: "Delete the specified context from the kubeconfig",
|
||||
Example: delete_context_example,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := runDeleteContext(out, errOut, configAccess, cmd)
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runDeleteContext(out, errOut io.Writer, configAccess clientcmd.ConfigAccess, cmd *cobra.Command) error {
|
||||
config, err := configAccess.GetStartingConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args := cmd.Flags().Args()
|
||||
if len(args) != 1 {
|
||||
cmd.Help()
|
||||
return nil
|
||||
}
|
||||
|
||||
configFile := configAccess.GetDefaultFilename()
|
||||
if configAccess.IsExplicitFile() {
|
||||
configFile = configAccess.GetExplicitFile()
|
||||
}
|
||||
|
||||
name := args[0]
|
||||
_, ok := config.Contexts[name]
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot delete context %s, not in %s", name, configFile)
|
||||
}
|
||||
|
||||
if config.CurrentContext == name {
|
||||
fmt.Fprint(errOut, "warning: this removed your active context, use \"kubectl config use-context\" to select a different one\n")
|
||||
}
|
||||
|
||||
delete(config.Contexts, name)
|
||||
|
||||
if err := clientcmd.ModifyConfig(configAccess, *config, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(out, "deleted context %s from %s\n", name, configFile)
|
||||
|
||||
return nil
|
||||
}
|
||||
98
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/delete_context_test.go
generated
vendored
Normal file
98
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/delete_context_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
Copyright 2016 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 config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
type deleteContextTest struct {
|
||||
config clientcmdapi.Config
|
||||
contextToDelete string
|
||||
expectedContexts []string
|
||||
expectedOut string
|
||||
}
|
||||
|
||||
func TestDeleteContext(t *testing.T) {
|
||||
conf := clientcmdapi.Config{
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
"minikube": {Cluster: "minikube"},
|
||||
"otherkube": {Cluster: "otherkube"},
|
||||
},
|
||||
}
|
||||
test := deleteContextTest{
|
||||
config: conf,
|
||||
contextToDelete: "minikube",
|
||||
expectedContexts: []string{"otherkube"},
|
||||
expectedOut: "deleted context minikube from %s\n",
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func (test deleteContextTest) run(t *testing.T) {
|
||||
fakeKubeFile, err := ioutil.TempFile("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer os.Remove(fakeKubeFile.Name())
|
||||
err = clientcmd.WriteToFile(test.config, fakeKubeFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
pathOptions := clientcmd.NewDefaultPathOptions()
|
||||
pathOptions.GlobalFile = fakeKubeFile.Name()
|
||||
pathOptions.EnvVar = ""
|
||||
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
errBuf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdConfigDeleteContext(buf, errBuf, pathOptions)
|
||||
cmd.SetArgs([]string{test.contextToDelete})
|
||||
if err := cmd.Execute(); err != nil {
|
||||
t.Fatalf("unexpected error executing command: %v", err)
|
||||
}
|
||||
|
||||
expectedOutWithFile := fmt.Sprintf(test.expectedOut, fakeKubeFile.Name())
|
||||
if expectedOutWithFile != buf.String() {
|
||||
t.Errorf("expected output %s, but got %s", expectedOutWithFile, buf.String())
|
||||
return
|
||||
}
|
||||
|
||||
// Verify context was removed from kubeconfig file
|
||||
config, err := clientcmd.LoadFromFile(fakeKubeFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error loading kubeconfig file: %v", err)
|
||||
}
|
||||
|
||||
contexts := make([]string, 0, len(config.Contexts))
|
||||
for k := range config.Contexts {
|
||||
contexts = append(contexts, k)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(test.expectedContexts, contexts) {
|
||||
t.Errorf("expected contexts %v, but found %v in kubeconfig", test.expectedContexts, contexts)
|
||||
}
|
||||
}
|
||||
65
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/get_clusters.go
generated
vendored
Normal file
65
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/get_clusters.go
generated
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
Copyright 2016 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 config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
var (
|
||||
get_clusters_example = templates.Examples(`
|
||||
# List the clusters kubectl knows about
|
||||
kubectl config get-clusters`)
|
||||
)
|
||||
|
||||
// NewCmdConfigGetClusters creates a command object for the "get-clusters" action, which
|
||||
// lists all clusters defined in the kubeconfig.
|
||||
func NewCmdConfigGetClusters(out io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "get-clusters",
|
||||
Short: i18n.T("Display clusters defined in the kubeconfig"),
|
||||
Long: "Display clusters defined in the kubeconfig.",
|
||||
Example: get_clusters_example,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := runGetClusters(out, configAccess)
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runGetClusters(out io.Writer, configAccess clientcmd.ConfigAccess) error {
|
||||
config, err := configAccess.GetStartingConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(out, "NAME\n")
|
||||
for name := range config.Clusters {
|
||||
fmt.Fprintf(out, "%s\n", name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
84
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/get_clusters_test.go
generated
vendored
Normal file
84
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/get_clusters_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
Copyright 2014 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 config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
type getClustersTest struct {
|
||||
config clientcmdapi.Config
|
||||
expected string
|
||||
}
|
||||
|
||||
func TestGetClusters(t *testing.T) {
|
||||
conf := clientcmdapi.Config{
|
||||
Clusters: map[string]*clientcmdapi.Cluster{
|
||||
"minikube": {Server: "https://192.168.0.99"},
|
||||
},
|
||||
}
|
||||
test := getClustersTest{
|
||||
config: conf,
|
||||
expected: `NAME
|
||||
minikube
|
||||
`,
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestGetClustersEmpty(t *testing.T) {
|
||||
test := getClustersTest{
|
||||
config: clientcmdapi.Config{},
|
||||
expected: "NAME\n",
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func (test getClustersTest) run(t *testing.T) {
|
||||
fakeKubeFile, err := ioutil.TempFile("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer os.Remove(fakeKubeFile.Name())
|
||||
err = clientcmd.WriteToFile(test.config, fakeKubeFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
pathOptions := clientcmd.NewDefaultPathOptions()
|
||||
pathOptions.GlobalFile = fakeKubeFile.Name()
|
||||
pathOptions.EnvVar = ""
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdConfigGetClusters(buf, pathOptions)
|
||||
if err := cmd.Execute(); err != nil {
|
||||
t.Fatalf("unexpected error executing command: %v", err)
|
||||
}
|
||||
if len(test.expected) != 0 {
|
||||
if buf.String() != test.expected {
|
||||
t.Errorf("expected %v, but got %v", test.expected, buf.String())
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
171
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/get_contexts.go
generated
vendored
Normal file
171
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/get_contexts.go
generated
vendored
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
Copyright 2016 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 config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
)
|
||||
|
||||
// GetContextsOptions contains the assignable options from the args.
|
||||
type GetContextsOptions struct {
|
||||
configAccess clientcmd.ConfigAccess
|
||||
nameOnly bool
|
||||
showHeaders bool
|
||||
contextNames []string
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
var (
|
||||
getContextsLong = templates.LongDesc(`Displays one or many contexts from the kubeconfig file.`)
|
||||
|
||||
getContextsExample = templates.Examples(`
|
||||
# List all the contexts in your kubeconfig file
|
||||
kubectl config get-contexts
|
||||
|
||||
# Describe one context in your kubeconfig file.
|
||||
kubectl config get-contexts my-context`)
|
||||
)
|
||||
|
||||
// NewCmdConfigGetContexts creates a command object for the "get-contexts" action, which
|
||||
// retrieves one or more contexts from a kubeconfig.
|
||||
func NewCmdConfigGetContexts(out io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command {
|
||||
options := &GetContextsOptions{configAccess: configAccess}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "get-contexts [(-o|--output=)name)]",
|
||||
Short: i18n.T("Describe one or many contexts"),
|
||||
Long: getContextsLong,
|
||||
Example: getContextsExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
validOutputTypes := sets.NewString("", "json", "yaml", "wide", "name", "custom-columns", "custom-columns-file", "go-template", "go-template-file", "jsonpath", "jsonpath-file")
|
||||
supportedOutputTypes := sets.NewString("", "name")
|
||||
outputFormat := cmdutil.GetFlagString(cmd, "output")
|
||||
if !validOutputTypes.Has(outputFormat) {
|
||||
cmdutil.CheckErr(fmt.Errorf("output must be one of '' or 'name': %v", outputFormat))
|
||||
}
|
||||
if !supportedOutputTypes.Has(outputFormat) {
|
||||
fmt.Fprintf(out, "--output %v is not available in kubectl config get-contexts; resetting to default output format\n", outputFormat)
|
||||
cmd.Flags().Set("output", "")
|
||||
}
|
||||
cmdutil.CheckErr(options.Complete(cmd, args, out))
|
||||
cmdutil.CheckErr(options.RunGetContexts())
|
||||
},
|
||||
}
|
||||
cmdutil.AddOutputFlags(cmd)
|
||||
cmdutil.AddNoHeadersFlags(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete assigns GetContextsOptions from the args.
|
||||
func (o *GetContextsOptions) Complete(cmd *cobra.Command, args []string, out io.Writer) error {
|
||||
o.contextNames = args
|
||||
o.out = out
|
||||
o.nameOnly = false
|
||||
if cmdutil.GetFlagString(cmd, "output") == "name" {
|
||||
o.nameOnly = true
|
||||
}
|
||||
o.showHeaders = true
|
||||
if cmdutil.GetFlagBool(cmd, "no-headers") || o.nameOnly {
|
||||
o.showHeaders = false
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunGetContexts implements all the necessary functionality for context retrieval.
|
||||
func (o GetContextsOptions) RunGetContexts() error {
|
||||
config, err := o.configAccess.GetStartingConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out, found := o.out.(*tabwriter.Writer)
|
||||
if !found {
|
||||
out = printers.GetNewTabWriter(o.out)
|
||||
defer out.Flush()
|
||||
}
|
||||
|
||||
// Build a list of context names to print, and warn if any requested contexts are not found.
|
||||
// Do this before printing the headers so it doesn't look ugly.
|
||||
allErrs := []error{}
|
||||
toPrint := []string{}
|
||||
if len(o.contextNames) == 0 {
|
||||
for name := range config.Contexts {
|
||||
toPrint = append(toPrint, name)
|
||||
}
|
||||
} else {
|
||||
for _, name := range o.contextNames {
|
||||
_, ok := config.Contexts[name]
|
||||
if ok {
|
||||
toPrint = append(toPrint, name)
|
||||
} else {
|
||||
allErrs = append(allErrs, fmt.Errorf("context %v not found", name))
|
||||
}
|
||||
}
|
||||
}
|
||||
if o.showHeaders {
|
||||
err = printContextHeaders(out, o.nameOnly)
|
||||
if err != nil {
|
||||
allErrs = append(allErrs, err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range toPrint {
|
||||
err = printContext(name, config.Contexts[name], out, o.nameOnly, config.CurrentContext == name)
|
||||
if err != nil {
|
||||
allErrs = append(allErrs, err)
|
||||
}
|
||||
}
|
||||
|
||||
return utilerrors.NewAggregate(allErrs)
|
||||
}
|
||||
|
||||
func printContextHeaders(out io.Writer, nameOnly bool) error {
|
||||
columnNames := []string{"CURRENT", "NAME", "CLUSTER", "AUTHINFO", "NAMESPACE"}
|
||||
if nameOnly {
|
||||
columnNames = columnNames[:1]
|
||||
}
|
||||
_, err := fmt.Fprintf(out, "%s\n", strings.Join(columnNames, "\t"))
|
||||
return err
|
||||
}
|
||||
|
||||
func printContext(name string, context *clientcmdapi.Context, w io.Writer, nameOnly, current bool) error {
|
||||
if nameOnly {
|
||||
_, err := fmt.Fprintf(w, "%s\n", name)
|
||||
return err
|
||||
}
|
||||
prefix := " "
|
||||
if current {
|
||||
prefix = "*"
|
||||
}
|
||||
_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", prefix, name, context.Cluster, context.AuthInfo, context.Namespace)
|
||||
return err
|
||||
}
|
||||
157
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/get_contexts_test.go
generated
vendored
Normal file
157
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/get_contexts_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
Copyright 2014 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 config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
type getContextsTest struct {
|
||||
startingConfig clientcmdapi.Config
|
||||
names []string
|
||||
noHeader bool
|
||||
nameOnly bool
|
||||
expectedOut string
|
||||
}
|
||||
|
||||
func TestGetContextsAll(t *testing.T) {
|
||||
tconf := clientcmdapi.Config{
|
||||
CurrentContext: "shaker-context",
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
"shaker-context": {AuthInfo: "blue-user", Cluster: "big-cluster", Namespace: "saw-ns"}}}
|
||||
test := getContextsTest{
|
||||
startingConfig: tconf,
|
||||
names: []string{},
|
||||
noHeader: false,
|
||||
nameOnly: false,
|
||||
expectedOut: `CURRENT NAME CLUSTER AUTHINFO NAMESPACE
|
||||
* shaker-context big-cluster blue-user saw-ns
|
||||
`,
|
||||
}
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestGetContextsAllNoHeader(t *testing.T) {
|
||||
tconf := clientcmdapi.Config{
|
||||
CurrentContext: "shaker-context",
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
"shaker-context": {AuthInfo: "blue-user", Cluster: "big-cluster", Namespace: "saw-ns"}}}
|
||||
test := getContextsTest{
|
||||
startingConfig: tconf,
|
||||
names: []string{},
|
||||
noHeader: true,
|
||||
nameOnly: false,
|
||||
expectedOut: "* shaker-context big-cluster blue-user saw-ns\n",
|
||||
}
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestGetContextsAllName(t *testing.T) {
|
||||
tconf := clientcmdapi.Config{
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
"shaker-context": {AuthInfo: "blue-user", Cluster: "big-cluster", Namespace: "saw-ns"}}}
|
||||
test := getContextsTest{
|
||||
startingConfig: tconf,
|
||||
names: []string{},
|
||||
noHeader: false,
|
||||
nameOnly: true,
|
||||
expectedOut: "shaker-context\n",
|
||||
}
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestGetContextsAllNameNoHeader(t *testing.T) {
|
||||
tconf := clientcmdapi.Config{
|
||||
CurrentContext: "shaker-context",
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
"shaker-context": {AuthInfo: "blue-user", Cluster: "big-cluster", Namespace: "saw-ns"}}}
|
||||
test := getContextsTest{
|
||||
startingConfig: tconf,
|
||||
names: []string{},
|
||||
noHeader: true,
|
||||
nameOnly: true,
|
||||
expectedOut: "shaker-context\n",
|
||||
}
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestGetContextsAllNone(t *testing.T) {
|
||||
test := getContextsTest{
|
||||
startingConfig: *clientcmdapi.NewConfig(),
|
||||
names: []string{},
|
||||
noHeader: true,
|
||||
nameOnly: false,
|
||||
expectedOut: "",
|
||||
}
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestGetContextsSelectOneOfTwo(t *testing.T) {
|
||||
tconf := clientcmdapi.Config{
|
||||
CurrentContext: "shaker-context",
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
"shaker-context": {AuthInfo: "blue-user", Cluster: "big-cluster", Namespace: "saw-ns"},
|
||||
"not-this": {AuthInfo: "blue-user", Cluster: "big-cluster", Namespace: "saw-ns"}}}
|
||||
test := getContextsTest{
|
||||
startingConfig: tconf,
|
||||
names: []string{"shaker-context"},
|
||||
noHeader: true,
|
||||
nameOnly: true,
|
||||
expectedOut: "shaker-context\n",
|
||||
}
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func (test getContextsTest) run(t *testing.T) {
|
||||
fakeKubeFile, err := ioutil.TempFile("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer os.Remove(fakeKubeFile.Name())
|
||||
err = clientcmd.WriteToFile(test.startingConfig, fakeKubeFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
pathOptions := clientcmd.NewDefaultPathOptions()
|
||||
pathOptions.GlobalFile = fakeKubeFile.Name()
|
||||
pathOptions.EnvVar = ""
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
options := GetContextsOptions{
|
||||
configAccess: pathOptions,
|
||||
}
|
||||
cmd := NewCmdConfigGetContexts(buf, options.configAccess)
|
||||
if test.nameOnly {
|
||||
cmd.Flags().Set("output", "name")
|
||||
}
|
||||
if test.noHeader {
|
||||
cmd.Flags().Set("no-headers", "true")
|
||||
}
|
||||
cmd.Run(cmd, test.names)
|
||||
if len(test.expectedOut) != 0 {
|
||||
if buf.String() != test.expectedOut {
|
||||
t.Errorf("Expected %v, but got %v", test.expectedOut, buf.String())
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
152
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/navigation_step_parser.go
generated
vendored
Normal file
152
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/navigation_step_parser.go
generated
vendored
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
Copyright 2014 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 config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
type navigationSteps struct {
|
||||
steps []navigationStep
|
||||
currentStepIndex int
|
||||
}
|
||||
|
||||
type navigationStep struct {
|
||||
stepValue string
|
||||
stepType reflect.Type
|
||||
}
|
||||
|
||||
func newNavigationSteps(path string) (*navigationSteps, error) {
|
||||
steps := []navigationStep{}
|
||||
individualParts := strings.Split(path, ".")
|
||||
|
||||
currType := reflect.TypeOf(clientcmdapi.Config{})
|
||||
currPartIndex := 0
|
||||
for currPartIndex < len(individualParts) {
|
||||
switch currType.Kind() {
|
||||
case reflect.Map:
|
||||
// if we're in a map, we need to locate a name. That name may contain dots, so we need to know what tokens are legal for the map's value type
|
||||
// for example, we could have a set request like: `set clusters.10.10.12.56.insecure-skip-tls-verify true`. We enter this case with
|
||||
// steps representing 10, 10, 12, 56, insecure-skip-tls-verify. The name is "10.10.12.56", so we want to collect all those parts together and
|
||||
// store them as a single step. In order to do that, we need to determine what set of tokens is a legal step AFTER the name of the map key
|
||||
// This set of reflective code pulls the type of the map values, uses that type to look up the set of legal tags. Those legal tags are used to
|
||||
// walk the list of remaining parts until we find a match to a legal tag or the end of the string. That name is used to burn all the used parts.
|
||||
mapValueType := currType.Elem().Elem()
|
||||
mapValueOptions, err := getPotentialTypeValues(mapValueType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nextPart := findNameStep(individualParts[currPartIndex:], sets.StringKeySet(mapValueOptions))
|
||||
|
||||
steps = append(steps, navigationStep{nextPart, mapValueType})
|
||||
currPartIndex += len(strings.Split(nextPart, "."))
|
||||
currType = mapValueType
|
||||
|
||||
case reflect.Struct:
|
||||
nextPart := individualParts[currPartIndex]
|
||||
|
||||
options, err := getPotentialTypeValues(currType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fieldType, exists := options[nextPart]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("unable to parse %v after %v at %v", path, steps, currType)
|
||||
}
|
||||
|
||||
steps = append(steps, navigationStep{nextPart, fieldType})
|
||||
currPartIndex += len(strings.Split(nextPart, "."))
|
||||
currType = fieldType
|
||||
}
|
||||
}
|
||||
|
||||
return &navigationSteps{steps, 0}, nil
|
||||
}
|
||||
|
||||
func (s *navigationSteps) pop() navigationStep {
|
||||
if s.moreStepsRemaining() {
|
||||
s.currentStepIndex++
|
||||
return s.steps[s.currentStepIndex-1]
|
||||
}
|
||||
return navigationStep{}
|
||||
}
|
||||
|
||||
func (s *navigationSteps) peek() navigationStep {
|
||||
if s.moreStepsRemaining() {
|
||||
return s.steps[s.currentStepIndex]
|
||||
}
|
||||
return navigationStep{}
|
||||
}
|
||||
|
||||
func (s *navigationSteps) moreStepsRemaining() bool {
|
||||
return len(s.steps) > s.currentStepIndex
|
||||
}
|
||||
|
||||
// findNameStep takes the list of parts and a set of valid tags that can be used after the name. It then walks the list of parts
|
||||
// until it find a valid "next" tag or until it reaches the end of the parts and then builds the name back up out of the individual parts
|
||||
func findNameStep(parts []string, typeOptions sets.String) string {
|
||||
if len(parts) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
numberOfPartsInStep := findKnownValue(parts[1:], typeOptions) + 1
|
||||
// if we didn't find a known value, then the entire thing must be a name
|
||||
if numberOfPartsInStep == 0 {
|
||||
numberOfPartsInStep = len(parts)
|
||||
}
|
||||
nextParts := parts[0:numberOfPartsInStep]
|
||||
|
||||
return strings.Join(nextParts, ".")
|
||||
}
|
||||
|
||||
// getPotentialTypeValues takes a type and looks up the tags used to represent its fields when serialized.
|
||||
func getPotentialTypeValues(typeValue reflect.Type) (map[string]reflect.Type, error) {
|
||||
if typeValue.Kind() == reflect.Ptr {
|
||||
typeValue = typeValue.Elem()
|
||||
}
|
||||
|
||||
if typeValue.Kind() != reflect.Struct {
|
||||
return nil, fmt.Errorf("%v is not of type struct", typeValue)
|
||||
}
|
||||
|
||||
ret := make(map[string]reflect.Type)
|
||||
|
||||
for fieldIndex := 0; fieldIndex < typeValue.NumField(); fieldIndex++ {
|
||||
fieldType := typeValue.Field(fieldIndex)
|
||||
yamlTag := fieldType.Tag.Get("json")
|
||||
yamlTagName := strings.Split(yamlTag, ",")[0]
|
||||
|
||||
ret[yamlTagName] = fieldType.Type
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func findKnownValue(parts []string, valueOptions sets.String) int {
|
||||
for i := range parts {
|
||||
if valueOptions.Has(parts[i]) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
96
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/navigation_step_parser_test.go
generated
vendored
Normal file
96
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/navigation_step_parser_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
Copyright 2014 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 config
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
type stepParserTest struct {
|
||||
path string
|
||||
expectedNavigationSteps navigationSteps
|
||||
expectedError string
|
||||
}
|
||||
|
||||
func TestParseWithDots(t *testing.T) {
|
||||
test := stepParserTest{
|
||||
path: "clusters.my.dot.delimited.name.server",
|
||||
expectedNavigationSteps: navigationSteps{
|
||||
steps: []navigationStep{
|
||||
{"clusters", reflect.TypeOf(make(map[string]*clientcmdapi.Cluster))},
|
||||
{"my.dot.delimited.name", reflect.TypeOf(clientcmdapi.Cluster{})},
|
||||
{"server", reflect.TypeOf("")},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestParseWithDotsEndingWithName(t *testing.T) {
|
||||
test := stepParserTest{
|
||||
path: "contexts.10.12.12.12",
|
||||
expectedNavigationSteps: navigationSteps{
|
||||
steps: []navigationStep{
|
||||
{"contexts", reflect.TypeOf(make(map[string]*clientcmdapi.Context))},
|
||||
{"10.12.12.12", reflect.TypeOf(clientcmdapi.Context{})},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestParseWithBadValue(t *testing.T) {
|
||||
test := stepParserTest{
|
||||
path: "user.bad",
|
||||
expectedNavigationSteps: navigationSteps{
|
||||
steps: []navigationStep{},
|
||||
},
|
||||
expectedError: "unable to parse user.bad after [] at api.Config",
|
||||
}
|
||||
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func (test stepParserTest) run(t *testing.T) {
|
||||
actualSteps, err := newNavigationSteps(test.path)
|
||||
if len(test.expectedError) != 0 {
|
||||
if err == nil {
|
||||
t.Errorf("Did not get %v", test.expectedError)
|
||||
} else {
|
||||
if !strings.Contains(err.Error(), test.expectedError) {
|
||||
t.Errorf("Expected %v, but got %v", test.expectedError, err)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(test.expectedNavigationSteps, *actualSteps) {
|
||||
t.Errorf("diff: %v", diff.ObjectDiff(test.expectedNavigationSteps, *actualSteps))
|
||||
t.Errorf("expected: %#v\n actual: %#v", test.expectedNavigationSteps, *actualSteps)
|
||||
}
|
||||
}
|
||||
136
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/rename_context.go
generated
vendored
Normal file
136
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/rename_context.go
generated
vendored
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
)
|
||||
|
||||
// RenameContextOptions contains the options for running the rename-context cli command.
|
||||
type RenameContextOptions struct {
|
||||
configAccess clientcmd.ConfigAccess
|
||||
contextName string
|
||||
newName string
|
||||
}
|
||||
|
||||
const (
|
||||
renameContextUse = "rename-context CONTEXT_NAME NEW_NAME"
|
||||
|
||||
renameContextShort = "Renames a context from the kubeconfig file."
|
||||
)
|
||||
|
||||
var (
|
||||
renameContextLong = templates.LongDesc(`
|
||||
Renames a context from the kubeconfig file .
|
||||
|
||||
CONTEXT_NAME is the context name that you wish change.
|
||||
|
||||
NEW_NAME is the new name you wish to set.
|
||||
|
||||
Note: In case the context being renamed is the 'current-context', this field will also be updated.`)
|
||||
|
||||
renameContextExample = templates.Examples(`
|
||||
# Rename the context 'old-name' to 'new-name' in your kubeconfig file
|
||||
kubectl config rename-context old-name new-name`)
|
||||
)
|
||||
|
||||
// NewCmdConfigRenameContext creates a command object for the "rename-context" action
|
||||
func NewCmdConfigRenameContext(out io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command {
|
||||
options := &RenameContextOptions{configAccess: configAccess}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: renameContextUse,
|
||||
Short: renameContextShort,
|
||||
Long: renameContextLong,
|
||||
Example: renameContextExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if err := options.Complete(cmd, args, out); err != nil {
|
||||
cmdutil.CheckErr(err)
|
||||
}
|
||||
if err := options.Validate(); err != nil {
|
||||
cmdutil.UsageErrorf(cmd, err.Error())
|
||||
}
|
||||
if err := options.RunRenameContext(out); err != nil {
|
||||
cmdutil.CheckErr(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete assigns RenameContextOptions from the args.
|
||||
func (o *RenameContextOptions) Complete(cmd *cobra.Command, args []string, out io.Writer) error {
|
||||
if len(args) != 2 {
|
||||
cmd.Help()
|
||||
return fmt.Errorf("Unexpected args: %v", args)
|
||||
}
|
||||
|
||||
o.contextName = args[0]
|
||||
o.newName = args[1]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o RenameContextOptions) Validate() error {
|
||||
if len(o.newName) == 0 {
|
||||
return errors.New("You must specify a new non-empty context name")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o RenameContextOptions) RunRenameContext(out io.Writer) error {
|
||||
config, err := o.configAccess.GetStartingConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configFile := o.configAccess.GetDefaultFilename()
|
||||
if o.configAccess.IsExplicitFile() {
|
||||
configFile = o.configAccess.GetExplicitFile()
|
||||
}
|
||||
|
||||
context, exists := config.Contexts[o.contextName]
|
||||
if !exists {
|
||||
return fmt.Errorf("cannot rename the context %q, it's not in %s", o.contextName, configFile)
|
||||
}
|
||||
|
||||
_, newExists := config.Contexts[o.newName]
|
||||
if newExists {
|
||||
return fmt.Errorf("cannot rename the context %q, the context %q already exists in %s", o.contextName, o.newName, configFile)
|
||||
}
|
||||
|
||||
config.Contexts[o.newName] = context
|
||||
delete(config.Contexts, o.contextName)
|
||||
|
||||
if config.CurrentContext == o.contextName {
|
||||
config.CurrentContext = o.newName
|
||||
}
|
||||
|
||||
if err := clientcmd.ModifyConfig(o.configAccess, *config, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(out, "Context %q was renamed to %q.\n", o.contextName, o.newName)
|
||||
return nil
|
||||
}
|
||||
156
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/rename_context_test.go
generated
vendored
Normal file
156
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/rename_context_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
const (
|
||||
currentContext = "current-context"
|
||||
newContext = "new-context"
|
||||
nonexistentCurrentContext = "nonexistent-current-context"
|
||||
existentNewContext = "existent-new-context"
|
||||
)
|
||||
|
||||
var (
|
||||
contextData *clientcmdapi.Context = clientcmdapi.NewContext()
|
||||
)
|
||||
|
||||
type renameContextTest struct {
|
||||
description string
|
||||
initialConfig clientcmdapi.Config // initial config
|
||||
expectedConfig clientcmdapi.Config // expected config
|
||||
args []string // kubectl rename-context args
|
||||
expectedOut string // expected out message
|
||||
expectedErr string // expected error message
|
||||
}
|
||||
|
||||
func TestRenameContext(t *testing.T) {
|
||||
initialConfig := clientcmdapi.Config{
|
||||
CurrentContext: currentContext,
|
||||
Contexts: map[string]*clientcmdapi.Context{currentContext: contextData}}
|
||||
|
||||
expectedConfig := clientcmdapi.Config{
|
||||
CurrentContext: newContext,
|
||||
Contexts: map[string]*clientcmdapi.Context{newContext: contextData}}
|
||||
|
||||
test := renameContextTest{
|
||||
description: "Testing for kubectl config rename-context whose context to be renamed is the CurrentContext",
|
||||
initialConfig: initialConfig,
|
||||
expectedConfig: expectedConfig,
|
||||
args: []string{currentContext, newContext},
|
||||
expectedOut: fmt.Sprintf("Context %q was renamed to %q.\n", currentContext, newContext),
|
||||
expectedErr: "",
|
||||
}
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestRenameNonexistentContext(t *testing.T) {
|
||||
initialConfig := clientcmdapi.Config{
|
||||
CurrentContext: currentContext,
|
||||
Contexts: map[string]*clientcmdapi.Context{currentContext: contextData}}
|
||||
|
||||
test := renameContextTest{
|
||||
description: "Testing for kubectl config rename-context whose context to be renamed no exists",
|
||||
initialConfig: initialConfig,
|
||||
expectedConfig: initialConfig,
|
||||
args: []string{nonexistentCurrentContext, newContext},
|
||||
expectedOut: "",
|
||||
expectedErr: fmt.Sprintf("cannot rename the context %q, it's not in", nonexistentCurrentContext),
|
||||
}
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestRenameToAlreadyExistingContext(t *testing.T) {
|
||||
initialConfig := clientcmdapi.Config{
|
||||
CurrentContext: currentContext,
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
currentContext: contextData,
|
||||
existentNewContext: contextData}}
|
||||
|
||||
test := renameContextTest{
|
||||
description: "Testing for kubectl config rename-context whose the new name is already in another context.",
|
||||
initialConfig: initialConfig,
|
||||
expectedConfig: initialConfig,
|
||||
args: []string{currentContext, existentNewContext},
|
||||
expectedOut: "",
|
||||
expectedErr: fmt.Sprintf("cannot rename the context %q, the context %q already exists", currentContext, existentNewContext),
|
||||
}
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func (test renameContextTest) run(t *testing.T) {
|
||||
fakeKubeFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(fakeKubeFile.Name())
|
||||
err := clientcmd.WriteToFile(test.initialConfig, fakeKubeFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
pathOptions := clientcmd.NewDefaultPathOptions()
|
||||
pathOptions.GlobalFile = fakeKubeFile.Name()
|
||||
pathOptions.EnvVar = ""
|
||||
options := RenameContextOptions{
|
||||
configAccess: pathOptions,
|
||||
contextName: test.args[0],
|
||||
newName: test.args[1],
|
||||
}
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdConfigRenameContext(buf, options.configAccess)
|
||||
|
||||
options.Complete(cmd, test.args, buf)
|
||||
options.Validate()
|
||||
err = options.RunRenameContext(buf)
|
||||
|
||||
if len(test.expectedErr) != 0 {
|
||||
if err == nil {
|
||||
t.Errorf("Did not get %v", test.expectedErr)
|
||||
} else {
|
||||
if !strings.Contains(err.Error(), test.expectedErr) {
|
||||
t.Errorf("Expected error %v, but got %v", test.expectedErr, err)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
config, err := clientcmd.LoadFromFile(fakeKubeFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error loading kubeconfig file: %v", err)
|
||||
}
|
||||
|
||||
_, oldExists := config.Contexts[currentContext]
|
||||
_, newExists := config.Contexts[newContext]
|
||||
|
||||
if (!newExists) || (oldExists) || (config.CurrentContext != newContext) {
|
||||
t.Errorf("Failed in: %q\n expected %v\n but got %v", test.description, test.expectedConfig, *config)
|
||||
}
|
||||
|
||||
if len(test.expectedOut) != 0 {
|
||||
if buf.String() != test.expectedOut {
|
||||
t.Errorf("Failed in:%q\n expected out %v\n but got %v", test.description, test.expectedOut, buf.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
245
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/set.go
generated
vendored
Normal file
245
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/set.go
generated
vendored
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
Copyright 2014 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 config
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/apiserver/pkg/util/flag"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
const (
|
||||
cannotHaveStepsAfterError = "Cannot have steps after %v. %v are remaining"
|
||||
additionStepRequiredUnlessUnsettingError = "Must have additional steps after %v unless you are unsetting it"
|
||||
)
|
||||
|
||||
type setOptions struct {
|
||||
configAccess clientcmd.ConfigAccess
|
||||
propertyName string
|
||||
propertyValue string
|
||||
setRawBytes flag.Tristate
|
||||
}
|
||||
|
||||
var set_long = templates.LongDesc(`
|
||||
Sets 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.
|
||||
|
||||
PROPERTY_VALUE is the new value you wish to set. Binary fields such as 'certificate-authority-data' expect a base64 encoded string unless the --set-raw-bytes flag is used.`)
|
||||
|
||||
func NewCmdConfigSet(out io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command {
|
||||
options := &setOptions{configAccess: configAccess}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "set PROPERTY_NAME PROPERTY_VALUE",
|
||||
Short: i18n.T("Sets an individual value in a kubeconfig file"),
|
||||
Long: set_long,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(options.complete(cmd))
|
||||
cmdutil.CheckErr(options.run())
|
||||
fmt.Fprintf(out, "Property %q set.\n", options.propertyName)
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags().VarPF(&options.setRawBytes, "set-raw-bytes", "", "When writing a []byte PROPERTY_VALUE, write the given string directly without base64 decoding.")
|
||||
f.NoOptDefVal = "true"
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o setOptions) run() error {
|
||||
err := o.validate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config, err := o.configAccess.GetStartingConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
steps, err := newNavigationSteps(o.propertyName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
setRawBytes := false
|
||||
if o.setRawBytes.Provided() {
|
||||
setRawBytes = o.setRawBytes.Value()
|
||||
}
|
||||
|
||||
err = modifyConfig(reflect.ValueOf(config), steps, o.propertyValue, false, setRawBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := clientcmd.ModifyConfig(o.configAccess, *config, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *setOptions) complete(cmd *cobra.Command) error {
|
||||
endingArgs := cmd.Flags().Args()
|
||||
if len(endingArgs) != 2 {
|
||||
cmd.Help()
|
||||
return fmt.Errorf("Unexpected args: %v", endingArgs)
|
||||
}
|
||||
|
||||
o.propertyValue = endingArgs[1]
|
||||
o.propertyName = endingArgs[0]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o setOptions) validate() error {
|
||||
if len(o.propertyValue) == 0 {
|
||||
return errors.New("you cannot use set to unset a property")
|
||||
}
|
||||
|
||||
if len(o.propertyName) == 0 {
|
||||
return errors.New("you must specify a property")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func modifyConfig(curr reflect.Value, steps *navigationSteps, propertyValue string, unset bool, setRawBytes bool) error {
|
||||
currStep := steps.pop()
|
||||
|
||||
actualCurrValue := curr
|
||||
if curr.Kind() == reflect.Ptr {
|
||||
actualCurrValue = curr.Elem()
|
||||
}
|
||||
|
||||
switch actualCurrValue.Kind() {
|
||||
case reflect.Map:
|
||||
if !steps.moreStepsRemaining() && !unset {
|
||||
return fmt.Errorf("can't set a map to a value: %v", actualCurrValue)
|
||||
}
|
||||
|
||||
mapKey := reflect.ValueOf(currStep.stepValue)
|
||||
mapValueType := curr.Type().Elem().Elem()
|
||||
|
||||
if !steps.moreStepsRemaining() && unset {
|
||||
actualCurrValue.SetMapIndex(mapKey, reflect.Value{})
|
||||
return nil
|
||||
}
|
||||
|
||||
currMapValue := actualCurrValue.MapIndex(mapKey)
|
||||
|
||||
needToSetNewMapValue := currMapValue.Kind() == reflect.Invalid
|
||||
if needToSetNewMapValue {
|
||||
currMapValue = reflect.New(mapValueType.Elem()).Elem().Addr()
|
||||
actualCurrValue.SetMapIndex(mapKey, currMapValue)
|
||||
}
|
||||
|
||||
err := modifyConfig(currMapValue, steps, propertyValue, unset, setRawBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
case reflect.String:
|
||||
if steps.moreStepsRemaining() {
|
||||
return fmt.Errorf("can't have more steps after a string. %v", steps)
|
||||
}
|
||||
actualCurrValue.SetString(propertyValue)
|
||||
return nil
|
||||
|
||||
case reflect.Slice:
|
||||
if steps.moreStepsRemaining() {
|
||||
return fmt.Errorf("can't have more steps after bytes. %v", steps)
|
||||
}
|
||||
innerKind := actualCurrValue.Type().Elem().Kind()
|
||||
if innerKind != reflect.Uint8 {
|
||||
return fmt.Errorf("unrecognized slice type. %v", innerKind)
|
||||
}
|
||||
|
||||
if unset {
|
||||
actualCurrValue.Set(reflect.Zero(actualCurrValue.Type()))
|
||||
return nil
|
||||
}
|
||||
|
||||
if setRawBytes {
|
||||
actualCurrValue.SetBytes([]byte(propertyValue))
|
||||
} else {
|
||||
val, err := base64.StdEncoding.DecodeString(propertyValue)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error decoding input value: %v", err)
|
||||
}
|
||||
actualCurrValue.SetBytes(val)
|
||||
}
|
||||
return nil
|
||||
|
||||
case reflect.Bool:
|
||||
if steps.moreStepsRemaining() {
|
||||
return fmt.Errorf("can't have more steps after a bool. %v", steps)
|
||||
}
|
||||
boolValue, err := toBool(propertyValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
actualCurrValue.SetBool(boolValue)
|
||||
return nil
|
||||
|
||||
case reflect.Struct:
|
||||
for fieldIndex := 0; fieldIndex < actualCurrValue.NumField(); fieldIndex++ {
|
||||
currFieldValue := actualCurrValue.Field(fieldIndex)
|
||||
currFieldType := actualCurrValue.Type().Field(fieldIndex)
|
||||
currYamlTag := currFieldType.Tag.Get("json")
|
||||
currFieldTypeYamlName := strings.Split(currYamlTag, ",")[0]
|
||||
|
||||
if currFieldTypeYamlName == currStep.stepValue {
|
||||
thisMapHasNoValue := (currFieldValue.Kind() == reflect.Map && currFieldValue.IsNil())
|
||||
|
||||
if thisMapHasNoValue {
|
||||
newValue := reflect.MakeMap(currFieldValue.Type())
|
||||
currFieldValue.Set(newValue)
|
||||
|
||||
if !steps.moreStepsRemaining() && unset {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if !steps.moreStepsRemaining() && unset {
|
||||
// if we're supposed to unset the value or if the value is a map that doesn't exist, create a new value and overwrite
|
||||
newValue := reflect.New(currFieldValue.Type()).Elem()
|
||||
currFieldValue.Set(newValue)
|
||||
return nil
|
||||
}
|
||||
|
||||
return modifyConfig(currFieldValue.Addr(), steps, propertyValue, unset, setRawBytes)
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("unable to locate path %#v under %v", currStep, actualCurrValue)
|
||||
|
||||
}
|
||||
|
||||
panic(fmt.Errorf("unrecognized type: %v", actualCurrValue))
|
||||
}
|
||||
88
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/set_test.go
generated
vendored
Normal file
88
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/set_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"reflect"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
type setConfigTest struct {
|
||||
description string
|
||||
config clientcmdapi.Config
|
||||
args []string
|
||||
expected string
|
||||
expectedConfig clientcmdapi.Config
|
||||
}
|
||||
|
||||
func TestSetConfigCurrentContext(t *testing.T) {
|
||||
conf := clientcmdapi.Config{
|
||||
Kind: "Config",
|
||||
APIVersion: "v1",
|
||||
CurrentContext: "minikube",
|
||||
}
|
||||
expectedConfig := *clientcmdapi.NewConfig()
|
||||
expectedConfig.CurrentContext = "my-cluster"
|
||||
test := setConfigTest{
|
||||
description: "Testing for kubectl config set current-context",
|
||||
config: conf,
|
||||
args: []string{"current-context", "my-cluster"},
|
||||
expected: `Property "current-context" set.` + "\n",
|
||||
expectedConfig: expectedConfig,
|
||||
}
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func (test setConfigTest) run(t *testing.T) {
|
||||
fakeKubeFile, err := ioutil.TempFile("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer os.Remove(fakeKubeFile.Name())
|
||||
err = clientcmd.WriteToFile(test.config, fakeKubeFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
pathOptions := clientcmd.NewDefaultPathOptions()
|
||||
pathOptions.GlobalFile = fakeKubeFile.Name()
|
||||
pathOptions.EnvVar = ""
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdConfigSet(buf, pathOptions)
|
||||
cmd.SetArgs(test.args)
|
||||
if err := cmd.Execute(); err != nil {
|
||||
t.Fatalf("unexpected error executing command: %v", err)
|
||||
}
|
||||
config, err := clientcmd.LoadFromFile(fakeKubeFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error loading kubeconfig file: %v", err)
|
||||
}
|
||||
if len(test.expected) != 0 {
|
||||
if buf.String() != test.expected {
|
||||
t.Errorf("Failded in:%q\n expected %v\n but got %v", test.description, test.expected, buf.String())
|
||||
}
|
||||
}
|
||||
if !reflect.DeepEqual(*config, test.expectedConfig) {
|
||||
t.Errorf("Failed in: %q\n expected %v\n but got %v", test.description, *config, test.expectedConfig)
|
||||
}
|
||||
}
|
||||
104
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/unset.go
generated
vendored
Normal file
104
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/unset.go
generated
vendored
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
Copyright 2014 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 config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
type unsetOptions struct {
|
||||
configAccess clientcmd.ConfigAccess
|
||||
propertyName string
|
||||
}
|
||||
|
||||
var unset_long = 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.`)
|
||||
|
||||
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,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(options.complete(cmd))
|
||||
cmdutil.CheckErr(options.run())
|
||||
fmt.Fprintf(out, "Property %q unset.\n", options.propertyName)
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o unsetOptions) run() error {
|
||||
err := o.validate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config, err := o.configAccess.GetStartingConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
steps, err := newNavigationSteps(o.propertyName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = modifyConfig(reflect.ValueOf(config), steps, "", true, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := clientcmd.ModifyConfig(o.configAccess, *config, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *unsetOptions) complete(cmd *cobra.Command) error {
|
||||
endingArgs := cmd.Flags().Args()
|
||||
if len(endingArgs) != 1 {
|
||||
cmd.Help()
|
||||
return fmt.Errorf("Unexpected args: %v", endingArgs)
|
||||
}
|
||||
|
||||
o.propertyName = endingArgs[0]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o unsetOptions) validate() error {
|
||||
if len(o.propertyName) == 0 {
|
||||
return errors.New("you must specify a property")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
119
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/unset_test.go
generated
vendored
Normal file
119
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/unset_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
type unsetConfigTest struct {
|
||||
description string
|
||||
config clientcmdapi.Config //initiate kubectl config
|
||||
args []string //kubectl config unset args
|
||||
expected string //expect out
|
||||
}
|
||||
|
||||
func TestUnsetConfigString(t *testing.T) {
|
||||
conf := clientcmdapi.Config{
|
||||
Kind: "Config",
|
||||
APIVersion: "v1",
|
||||
Clusters: map[string]*clientcmdapi.Cluster{
|
||||
"minikube": {Server: "https://192.168.99.100:8443"},
|
||||
"my-cluster": {Server: "https://192.168.0.1:3434"},
|
||||
},
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
"minikube": {AuthInfo: "minikube", Cluster: "minikube"},
|
||||
"my-cluster": {AuthInfo: "mu-cluster", Cluster: "my-cluster"},
|
||||
},
|
||||
CurrentContext: "minikube",
|
||||
}
|
||||
test := unsetConfigTest{
|
||||
description: "Testing for kubectl config unset a value",
|
||||
config: conf,
|
||||
args: []string{"current-context"},
|
||||
expected: `Property "current-context" unset.` + "\n",
|
||||
}
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestUnsetConfigMap(t *testing.T) {
|
||||
conf := clientcmdapi.Config{
|
||||
Kind: "Config",
|
||||
APIVersion: "v1",
|
||||
Clusters: map[string]*clientcmdapi.Cluster{
|
||||
"minikube": {Server: "https://192.168.99.100:8443"},
|
||||
"my-cluster": {Server: "https://192.168.0.1:3434"},
|
||||
},
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
"minikube": {AuthInfo: "minikube", Cluster: "minikube"},
|
||||
"my-cluster": {AuthInfo: "mu-cluster", Cluster: "my-cluster"},
|
||||
},
|
||||
CurrentContext: "minikube",
|
||||
}
|
||||
test := unsetConfigTest{
|
||||
description: "Testing for kubectl config unset a map",
|
||||
config: conf,
|
||||
args: []string{"clusters"},
|
||||
expected: `Property "clusters" unset.` + "\n",
|
||||
}
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func (test unsetConfigTest) run(t *testing.T) {
|
||||
fakeKubeFile, err := ioutil.TempFile(os.TempDir(), "")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer os.Remove(fakeKubeFile.Name())
|
||||
err = clientcmd.WriteToFile(test.config, fakeKubeFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
pathOptions := clientcmd.NewDefaultPathOptions()
|
||||
pathOptions.GlobalFile = fakeKubeFile.Name()
|
||||
pathOptions.EnvVar = ""
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdConfigUnset(buf, pathOptions)
|
||||
cmd.SetArgs(test.args)
|
||||
if err := cmd.Execute(); err != nil {
|
||||
t.Fatalf("unexpected error executing command: %v,kubectl unset args: %v", err, test.args)
|
||||
}
|
||||
config, err := clientcmd.LoadFromFile(fakeKubeFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error loading kubeconfig file: %v", err)
|
||||
}
|
||||
if len(test.expected) != 0 {
|
||||
if buf.String() != test.expected {
|
||||
t.Errorf("Failed in :%q\n expected %v\n but got %v", test.description, test.expected, buf.String())
|
||||
}
|
||||
}
|
||||
if test.args[0] == "current-context" {
|
||||
if config.CurrentContext != "" {
|
||||
t.Errorf("Failed in :%q\n expected current-context nil,but got %v", test.description, config.CurrentContext)
|
||||
}
|
||||
} else if test.args[0] == "clusters" {
|
||||
if len(config.Clusters) != 0 {
|
||||
t.Errorf("Failed in :%q\n expected clusters nil map, but got %v", test.description, config.Clusters)
|
||||
}
|
||||
}
|
||||
}
|
||||
105
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/use_context.go
generated
vendored
Normal file
105
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/use_context.go
generated
vendored
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
Copyright 2014 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 config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
var (
|
||||
use_context_example = templates.Examples(`
|
||||
# Use the context for the minikube cluster
|
||||
kubectl config use-context minikube`)
|
||||
)
|
||||
|
||||
type useContextOptions struct {
|
||||
configAccess clientcmd.ConfigAccess
|
||||
contextName string
|
||||
}
|
||||
|
||||
func NewCmdConfigUseContext(out io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command {
|
||||
options := &useContextOptions{configAccess: configAccess}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "use-context CONTEXT_NAME",
|
||||
Short: i18n.T("Sets the current-context in a kubeconfig file"),
|
||||
Long: `Sets the current-context in a kubeconfig file`,
|
||||
Example: use_context_example,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(options.complete(cmd))
|
||||
cmdutil.CheckErr(options.run())
|
||||
fmt.Fprintf(out, "Switched to context %q.\n", options.contextName)
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o useContextOptions) run() error {
|
||||
config, err := o.configAccess.GetStartingConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = o.validate(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config.CurrentContext = o.contextName
|
||||
|
||||
if err := clientcmd.ModifyConfig(o.configAccess, *config, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *useContextOptions) complete(cmd *cobra.Command) error {
|
||||
endingArgs := cmd.Flags().Args()
|
||||
if len(endingArgs) != 1 {
|
||||
cmd.Help()
|
||||
return fmt.Errorf("Unexpected args: %v", endingArgs)
|
||||
}
|
||||
|
||||
o.contextName = endingArgs[0]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o useContextOptions) validate(config *clientcmdapi.Config) error {
|
||||
if len(o.contextName) == 0 {
|
||||
return errors.New("you must specify a current-context")
|
||||
}
|
||||
|
||||
for name := range config.Contexts {
|
||||
if name == o.contextName {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("no context exists with the name: %q.", o.contextName)
|
||||
}
|
||||
104
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/use_context_test.go
generated
vendored
Normal file
104
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/use_context_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
type useContextTest struct {
|
||||
description string
|
||||
config clientcmdapi.Config //initiate kubectl config
|
||||
args []string //kubectl config use-context args
|
||||
expected string //expect out
|
||||
expectedConfig clientcmdapi.Config //expect kubectl config
|
||||
}
|
||||
|
||||
func TestUseContext(t *testing.T) {
|
||||
conf := clientcmdapi.Config{
|
||||
Kind: "Config",
|
||||
APIVersion: "v1",
|
||||
Clusters: map[string]*clientcmdapi.Cluster{
|
||||
"minikube": {Server: "https://192.168.99.100:8443"},
|
||||
"my-cluster": {Server: "https://192.168.0.1:3434"},
|
||||
},
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
"minikube": {AuthInfo: "minikube", Cluster: "minikube"},
|
||||
"my-cluster": {AuthInfo: "mu-cluster", Cluster: "my-cluster"},
|
||||
},
|
||||
CurrentContext: "minikube",
|
||||
}
|
||||
test := useContextTest{
|
||||
description: "Testing for kubectl config use-context",
|
||||
config: conf,
|
||||
args: []string{"my-cluster"},
|
||||
expected: `Switched to context "my-cluster".` + "\n",
|
||||
expectedConfig: clientcmdapi.Config{
|
||||
Kind: "Config",
|
||||
APIVersion: "v1",
|
||||
Clusters: map[string]*clientcmdapi.Cluster{
|
||||
"minikube": {Server: "https://192.168.99.100:8443"},
|
||||
"my-cluster": {Server: "https://192.168.0.1:3434"},
|
||||
},
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
"minikube": {AuthInfo: "minikube", Cluster: "minikube"},
|
||||
"my-cluster": {AuthInfo: "mu-cluster", Cluster: "my-cluster"},
|
||||
},
|
||||
CurrentContext: "my-cluster",
|
||||
},
|
||||
}
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func (test useContextTest) run(t *testing.T) {
|
||||
fakeKubeFile, err := ioutil.TempFile(os.TempDir(), "")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer os.Remove(fakeKubeFile.Name())
|
||||
err = clientcmd.WriteToFile(test.config, fakeKubeFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
pathOptions := clientcmd.NewDefaultPathOptions()
|
||||
pathOptions.GlobalFile = fakeKubeFile.Name()
|
||||
pathOptions.EnvVar = ""
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdConfigUseContext(buf, pathOptions)
|
||||
cmd.SetArgs(test.args)
|
||||
if err := cmd.Execute(); err != nil {
|
||||
t.Fatalf("unexpected error executing command: %v,kubectl config use-context args: %v", err, test.args)
|
||||
}
|
||||
config, err := clientcmd.LoadFromFile(fakeKubeFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error loading kubeconfig file: %v", err)
|
||||
}
|
||||
if len(test.expected) != 0 {
|
||||
if buf.String() != test.expected {
|
||||
t.Errorf("Failed in :%q\n expected %v\n, but got %v\n", test.description, test.expected, buf.String())
|
||||
}
|
||||
}
|
||||
if test.expectedConfig.CurrentContext != config.CurrentContext {
|
||||
t.Errorf("Failed in :%q\n expected config %v, but found %v\n in kubeconfig\n", test.description, test.expectedConfig, config)
|
||||
}
|
||||
}
|
||||
169
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/view.go
generated
vendored
Normal file
169
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/view.go
generated
vendored
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
Copyright 2014 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 config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"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"
|
||||
"k8s.io/client-go/tools/clientcmd/api/latest"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
)
|
||||
|
||||
type ViewOptions struct {
|
||||
ConfigAccess clientcmd.ConfigAccess
|
||||
Merge flag.Tristate
|
||||
Flatten bool
|
||||
Minify bool
|
||||
RawByteData bool
|
||||
}
|
||||
|
||||
var (
|
||||
view_long = templates.LongDesc(`
|
||||
Display merged kubeconfig settings or a specified kubeconfig file.
|
||||
|
||||
You can use --output jsonpath={...} to extract specific values using a jsonpath expression.`)
|
||||
|
||||
view_example = templates.Examples(`
|
||||
# Show Merged kubeconfig settings.
|
||||
kubectl config view
|
||||
|
||||
# Get the password for the e2e user
|
||||
kubectl config view -o jsonpath='{.users[?(@.name == "e2e")].user.password}'`)
|
||||
)
|
||||
|
||||
func NewCmdConfigView(out, errOut io.Writer, ConfigAccess clientcmd.ConfigAccess) *cobra.Command {
|
||||
options := &ViewOptions{ConfigAccess: ConfigAccess}
|
||||
// Default to yaml
|
||||
defaultOutputFormat := "yaml"
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "view",
|
||||
Short: i18n.T("Display merged kubeconfig settings or a specified kubeconfig file"),
|
||||
Long: view_long,
|
||||
Example: view_example,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
options.Complete()
|
||||
outputFormat := cmdutil.GetFlagString(cmd, "output")
|
||||
if outputFormat == "wide" {
|
||||
fmt.Fprintf(errOut, "--output wide is not available in kubectl config view; reset to default output format (%s)\n\n", defaultOutputFormat)
|
||||
// TODO: once printing is abstracted, this should be handled at flag declaration time
|
||||
cmd.Flags().Set("output", defaultOutputFormat)
|
||||
}
|
||||
if outputFormat == "" {
|
||||
fmt.Fprintf(errOut, "Reset to default output format (%s) as --output is empty\n", defaultOutputFormat)
|
||||
// TODO: once printing is abstracted, this should be handled at flag declaration time
|
||||
cmd.Flags().Set("output", defaultOutputFormat)
|
||||
}
|
||||
|
||||
printer, err := cmdutil.PrinterForCommand(cmd, nil, meta.NewDefaultRESTMapper(nil, nil), latest.Scheme, nil, []runtime.Decoder{latest.Codec}, printers.PrintOptions{})
|
||||
cmdutil.CheckErr(err)
|
||||
printer = printers.NewVersionedPrinter(printer, latest.Scheme, latest.ExternalVersion)
|
||||
|
||||
cmdutil.CheckErr(options.Run(out, printer))
|
||||
},
|
||||
}
|
||||
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
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"
|
||||
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")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o ViewOptions) Run(out io.Writer, printer printers.ResourcePrinter) error {
|
||||
config, err := o.loadConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if o.Minify {
|
||||
if err := clientcmdapi.MinifyConfig(config); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if o.Flatten {
|
||||
if err := clientcmdapi.FlattenConfig(config); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if !o.RawByteData {
|
||||
clientcmdapi.ShortenConfig(config)
|
||||
}
|
||||
|
||||
err = printer.PrintObj(config, out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *ViewOptions) Complete() bool {
|
||||
if o.ConfigAccess.IsExplicitFile() {
|
||||
if !o.Merge.Provided() {
|
||||
o.Merge.Set("false")
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (o ViewOptions) loadConfig() (*clientcmdapi.Config, error) {
|
||||
err := o.Validate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config, err := o.getStartingConfig()
|
||||
return config, err
|
||||
}
|
||||
|
||||
func (o ViewOptions) Validate() error {
|
||||
if !o.Merge.Value() && !o.ConfigAccess.IsExplicitFile() {
|
||||
return errors.New("if merge==false a precise file must to specified")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getStartingConfig returns the Config object built from the sources specified by the options, the filename read (only if it was a single file), and an error if something goes wrong
|
||||
func (o *ViewOptions) getStartingConfig() (*clientcmdapi.Config, error) {
|
||||
switch {
|
||||
case !o.Merge.Value():
|
||||
return clientcmd.LoadFromFile(o.ConfigAccess.GetExplicitFile())
|
||||
|
||||
default:
|
||||
return o.ConfigAccess.GetStartingConfig()
|
||||
}
|
||||
}
|
||||
155
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/view_test.go
generated
vendored
Normal file
155
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/view_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
type viewClusterTest struct {
|
||||
description string
|
||||
config clientcmdapi.Config //initiate kubectl config
|
||||
flags []string //kubectl config viw flags
|
||||
expected string //expect out
|
||||
}
|
||||
|
||||
func TestViewCluster(t *testing.T) {
|
||||
conf := clientcmdapi.Config{
|
||||
Kind: "Config",
|
||||
APIVersion: "v1",
|
||||
Clusters: map[string]*clientcmdapi.Cluster{
|
||||
"minikube": {Server: "https://192.168.99.100:8443"},
|
||||
"my-cluster": {Server: "https://192.168.0.1:3434"},
|
||||
},
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
"minikube": {AuthInfo: "minikube", Cluster: "minikube"},
|
||||
"my-cluser": {AuthInfo: "mu-cluster", Cluster: "my-cluster"},
|
||||
},
|
||||
CurrentContext: "minikube",
|
||||
AuthInfos: map[string]*clientcmdapi.AuthInfo{
|
||||
"minikube": {Token: "minikube-token"},
|
||||
"my-cluser": {Token: "minikube-token"},
|
||||
},
|
||||
}
|
||||
test := viewClusterTest{
|
||||
description: "Testing for kubectl config view",
|
||||
config: conf,
|
||||
flags: []string{},
|
||||
expected: `apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
server: https://192.168.99.100:8443
|
||||
name: minikube
|
||||
- cluster:
|
||||
server: https://192.168.0.1:3434
|
||||
name: my-cluster
|
||||
contexts:
|
||||
- context:
|
||||
cluster: minikube
|
||||
user: minikube
|
||||
name: minikube
|
||||
- context:
|
||||
cluster: my-cluster
|
||||
user: mu-cluster
|
||||
name: my-cluser
|
||||
current-context: minikube
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: minikube
|
||||
user:
|
||||
token: minikube-token
|
||||
- name: my-cluser
|
||||
user:
|
||||
token: minikube-token` + "\n"}
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestViewClusterMinify(t *testing.T) {
|
||||
conf := clientcmdapi.Config{
|
||||
Kind: "Config",
|
||||
APIVersion: "v1",
|
||||
Clusters: map[string]*clientcmdapi.Cluster{
|
||||
"minikube": {Server: "https://192.168.99.100:8443"},
|
||||
"my-cluster": {Server: "https://192.168.0.1:3434"},
|
||||
},
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
"minikube": {AuthInfo: "minikube", Cluster: "minikube"},
|
||||
"my-cluser": {AuthInfo: "mu-cluster", Cluster: "my-cluster"},
|
||||
},
|
||||
CurrentContext: "minikube",
|
||||
AuthInfos: map[string]*clientcmdapi.AuthInfo{
|
||||
"minikube": {Token: "minikube-token"},
|
||||
"my-cluser": {Token: "minikube-token"},
|
||||
},
|
||||
}
|
||||
test := viewClusterTest{
|
||||
description: "Testing for kubectl config view --minify=true",
|
||||
config: conf,
|
||||
flags: []string{"--minify=true"},
|
||||
expected: `apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
server: https://192.168.99.100:8443
|
||||
name: minikube
|
||||
contexts:
|
||||
- context:
|
||||
cluster: minikube
|
||||
user: minikube
|
||||
name: minikube
|
||||
current-context: minikube
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: minikube
|
||||
user:
|
||||
token: minikube-token` + "\n"}
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func (test viewClusterTest) run(t *testing.T) {
|
||||
fakeKubeFile, err := ioutil.TempFile(os.TempDir(), "")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer os.Remove(fakeKubeFile.Name())
|
||||
err = clientcmd.WriteToFile(test.config, fakeKubeFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
pathOptions := clientcmd.NewDefaultPathOptions()
|
||||
pathOptions.GlobalFile = fakeKubeFile.Name()
|
||||
pathOptions.EnvVar = ""
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
errBuf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdConfigView(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)
|
||||
}
|
||||
if len(test.expected) != 0 {
|
||||
if buf.String() != test.expected {
|
||||
t.Errorf("Failed in %q\n expected %v\n but got %v\n", test.description, test.expected, buf.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
200
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/convert.go
generated
vendored
Normal file
200
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/convert.go
generated
vendored
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
Copyright 2014 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"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"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"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
convert_long = templates.LongDesc(i18n.T(`
|
||||
Convert config files between different API versions. Both YAML
|
||||
and JSON formats are accepted.
|
||||
|
||||
The command takes filename, directory, or URL as input, and convert it into format
|
||||
of version specified by --output-version flag. If target version is not specified or
|
||||
not supported, convert to latest version.
|
||||
|
||||
The default output will be printed to stdout in YAML format. One can use -o option
|
||||
to change to output destination.`))
|
||||
|
||||
convert_example = templates.Examples(i18n.T(`
|
||||
# Convert 'pod.yaml' to latest version and print to stdout.
|
||||
kubectl convert -f pod.yaml
|
||||
|
||||
# Convert the live state of the resource specified by 'pod.yaml' to the latest version
|
||||
# and print to stdout in json format.
|
||||
kubectl convert -f pod.yaml --local -o json
|
||||
|
||||
# Convert all files under current directory to latest version and create them all.
|
||||
kubectl convert -f . | kubectl create -f -`))
|
||||
)
|
||||
|
||||
// NewCmdConvert creates a command object for the generic "convert" action, which
|
||||
// translates the config file into a given version.
|
||||
func NewCmdConvert(f cmdutil.Factory, out io.Writer) *cobra.Command {
|
||||
options := &ConvertOptions{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "convert -f FILENAME",
|
||||
Short: i18n.T("Convert config files between different API versions"),
|
||||
Long: convert_long,
|
||||
Example: convert_example,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := options.Complete(f, out, cmd)
|
||||
cmdutil.CheckErr(err)
|
||||
err = options.RunConvert()
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
|
||||
usage := "to need to get converted."
|
||||
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
|
||||
cmd.MarkFlagRequired("filename")
|
||||
cmdutil.AddValidateFlags(cmd)
|
||||
cmdutil.AddNonDeprecatedPrinterFlags(cmd)
|
||||
cmd.Flags().BoolVar(&options.local, "local", true, "If true, convert will NOT try to contact api-server but run locally.")
|
||||
cmd.Flags().String("output-version", "", i18n.T("Output the formatted object with the given group version (for ex: 'extensions/v1beta1').)"))
|
||||
cmdutil.AddInclude3rdPartyFlags(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// ConvertOptions have the data required to perform the convert operation
|
||||
type ConvertOptions struct {
|
||||
resource.FilenameOptions
|
||||
|
||||
builder *resource.Builder
|
||||
local bool
|
||||
|
||||
encoder runtime.Encoder
|
||||
out io.Writer
|
||||
printer printers.ResourcePrinter
|
||||
|
||||
outputVersion 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) {
|
||||
outputVersionString := cmdutil.GetFlagString(cmd, "output-version")
|
||||
if len(outputVersionString) == 0 {
|
||||
if defaultVersion == nil {
|
||||
return schema.GroupVersion{}, nil
|
||||
}
|
||||
|
||||
return *defaultVersion, nil
|
||||
}
|
||||
|
||||
return schema.ParseGroupVersion(outputVersionString)
|
||||
}
|
||||
|
||||
// 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, &api.Registry.EnabledVersionsForGroup(api.GroupName)[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !api.Registry.IsEnabledVersion(o.outputVersion) {
|
||||
cmdutil.UsageErrorf(cmd, "'%s' is not a registered version.", o.outputVersion)
|
||||
}
|
||||
|
||||
// build the builder
|
||||
o.builder = f.NewBuilder(!o.local)
|
||||
if !o.local {
|
||||
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagBool(cmd, "openapi-validation"), cmdutil.GetFlagString(cmd, "schema-cache-dir"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.builder = o.builder.Schema(schema)
|
||||
}
|
||||
cmdNamespace, _, err := f.DefaultNamespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.builder = o.builder.NamespaceParam(cmdNamespace).
|
||||
ContinueOnError().
|
||||
FilenameParam(false, &o.FilenameOptions).
|
||||
Flatten()
|
||||
|
||||
// build the printer
|
||||
o.out = out
|
||||
outputFormat := cmdutil.GetFlagString(cmd, "output")
|
||||
templateFile := cmdutil.GetFlagString(cmd, "template")
|
||||
if len(outputFormat) == 0 {
|
||||
if len(templateFile) == 0 {
|
||||
outputFormat = "yaml"
|
||||
} else {
|
||||
outputFormat = "template"
|
||||
}
|
||||
// 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.PrinterForCommand(cmd, o.local, nil, printers.PrintOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
// RunConvert implements the generic Convert command
|
||||
func (o *ConvertOptions) RunConvert() error {
|
||||
r := o.builder.Do()
|
||||
err := r.Err()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
singleItemImplied := false
|
||||
infos, err := r.IntoSingleItemImplied(&singleItemImplied).Infos()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(infos) == 0 {
|
||||
return fmt.Errorf("no objects passed to convert")
|
||||
}
|
||||
|
||||
objects, err := resource.AsVersionedObject(infos, !singleItemImplied, o.outputVersion, o.encoder)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if meta.IsListType(objects) {
|
||||
_, items, err := cmdutil.FilterResourceList(objects, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filteredObj, err := cmdutil.ObjectListToVersionedObject(items, o.outputVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return o.printer.PrintObj(filteredObj, o.out)
|
||||
}
|
||||
|
||||
return o.printer.PrintObj(objects, o.out)
|
||||
}
|
||||
308
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/cp.go
generated
vendored
Normal file
308
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/cp.go
generated
vendored
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
/*
|
||||
Copyright 2016 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 (
|
||||
"archive/tar"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
|
||||
"github.com/renstrom/dedent"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
cpExample = templates.Examples(i18n.T(`
|
||||
# !!!Important Note!!!
|
||||
# Requires that the 'tar' binary is present in your container
|
||||
# image. If 'tar' is not present, 'kubectl cp' will fail.
|
||||
|
||||
# Copy /tmp/foo_dir local directory to /tmp/bar_dir in a remote pod in the default namespace
|
||||
kubectl cp /tmp/foo_dir <some-pod>:/tmp/bar_dir
|
||||
|
||||
# Copy /tmp/foo local file to /tmp/bar in a remote pod in a specific container
|
||||
kubectl cp /tmp/foo <some-pod>:/tmp/bar -c <specific-container>
|
||||
|
||||
# Copy /tmp/foo local file to /tmp/bar in a remote pod in namespace <some-namespace>
|
||||
kubectl cp /tmp/foo <some-namespace>/<some-pod>:/tmp/bar
|
||||
|
||||
# Copy /tmp/foo from a remote pod to /tmp/bar locally
|
||||
kubectl cp <some-namespace>/<some-pod>:/tmp/foo /tmp/bar`))
|
||||
|
||||
cpUsageStr = dedent.Dedent(`
|
||||
expected 'cp <file-spec-src> <file-spec-dest> [-c container]'.
|
||||
<file-spec> is:
|
||||
[namespace/]pod-name:/file/path for a remote file
|
||||
/file/path for a local file`)
|
||||
)
|
||||
|
||||
// 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>",
|
||||
Short: i18n.T("Copy files and directories to and from containers."),
|
||||
Long: "Copy files and directories to and from containers.",
|
||||
Example: cpExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(runCopy(f, cmd, cmdOut, cmdErr, args))
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringP("container", "c", "", "Container name. If omitted, the first container in the pod will be chosen")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
type fileSpec struct {
|
||||
PodNamespace string
|
||||
PodName string
|
||||
File string
|
||||
}
|
||||
|
||||
var errFileSpecDoesntMatchFormat = errors.New("Filespec must match the canonical format: [[namespace/]pod:]file/path")
|
||||
|
||||
func extractFileSpec(arg string) (fileSpec, error) {
|
||||
pieces := strings.Split(arg, ":")
|
||||
if len(pieces) == 1 {
|
||||
return fileSpec{File: arg}, nil
|
||||
}
|
||||
if len(pieces) != 2 {
|
||||
// FIXME Kubernetes can't copy files that contain a ':'
|
||||
// character.
|
||||
return fileSpec{}, errFileSpecDoesntMatchFormat
|
||||
}
|
||||
file := pieces[1]
|
||||
|
||||
pieces = strings.Split(pieces[0], "/")
|
||||
if len(pieces) == 1 {
|
||||
return fileSpec{
|
||||
PodName: pieces[0],
|
||||
File: file,
|
||||
}, nil
|
||||
}
|
||||
if len(pieces) == 2 {
|
||||
return fileSpec{
|
||||
PodNamespace: pieces[0],
|
||||
PodName: pieces[1],
|
||||
File: file,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return fileSpec{}, errFileSpecDoesntMatchFormat
|
||||
}
|
||||
|
||||
func runCopy(f cmdutil.Factory, cmd *cobra.Command, out, cmderr io.Writer, args []string) error {
|
||||
if len(args) != 2 {
|
||||
return cmdutil.UsageErrorf(cmd, cpUsageStr)
|
||||
}
|
||||
srcSpec, err := extractFileSpec(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
destSpec, err := extractFileSpec(args[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(srcSpec.PodName) != 0 {
|
||||
return copyFromPod(f, cmd, cmderr, srcSpec, destSpec)
|
||||
}
|
||||
if len(destSpec.PodName) != 0 {
|
||||
return copyToPod(f, cmd, out, cmderr, srcSpec, destSpec)
|
||||
}
|
||||
return cmdutil.UsageErrorf(cmd, "One of src or dest must be a remote file specification")
|
||||
}
|
||||
|
||||
func copyToPod(f cmdutil.Factory, cmd *cobra.Command, stdout, stderr io.Writer, src, dest fileSpec) error {
|
||||
reader, writer := io.Pipe()
|
||||
go func() {
|
||||
defer writer.Close()
|
||||
err := makeTar(src.File, writer)
|
||||
cmdutil.CheckErr(err)
|
||||
}()
|
||||
|
||||
// TODO: Improve error messages by first testing if 'tar' is present in the container?
|
||||
cmdArr := []string{"tar", "xf", "-"}
|
||||
destDir := path.Dir(dest.File)
|
||||
if len(destDir) > 0 {
|
||||
cmdArr = append(cmdArr, "-C", destDir)
|
||||
}
|
||||
|
||||
options := &ExecOptions{
|
||||
StreamOptions: StreamOptions{
|
||||
In: reader,
|
||||
Out: stdout,
|
||||
Err: stderr,
|
||||
Stdin: true,
|
||||
|
||||
Namespace: dest.PodNamespace,
|
||||
PodName: dest.PodName,
|
||||
},
|
||||
|
||||
Command: cmdArr,
|
||||
Executor: &DefaultRemoteExecutor{},
|
||||
}
|
||||
return execute(f, cmd, options)
|
||||
}
|
||||
|
||||
func copyFromPod(f cmdutil.Factory, cmd *cobra.Command, cmderr io.Writer, src, dest fileSpec) error {
|
||||
reader, outStream := io.Pipe()
|
||||
options := &ExecOptions{
|
||||
StreamOptions: StreamOptions{
|
||||
In: nil,
|
||||
Out: outStream,
|
||||
Err: cmderr,
|
||||
|
||||
Namespace: src.PodNamespace,
|
||||
PodName: src.PodName,
|
||||
},
|
||||
|
||||
// TODO: Improve error messages by first testing if 'tar' is present in the container?
|
||||
Command: []string{"tar", "cf", "-", src.File},
|
||||
Executor: &DefaultRemoteExecutor{},
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer outStream.Close()
|
||||
execute(f, cmd, options)
|
||||
}()
|
||||
prefix := getPrefix(src.File)
|
||||
|
||||
return untarAll(reader, dest.File, prefix)
|
||||
}
|
||||
|
||||
func makeTar(filepath string, writer io.Writer) error {
|
||||
// TODO: use compression here?
|
||||
tarWriter := tar.NewWriter(writer)
|
||||
defer tarWriter.Close()
|
||||
return recursiveTar(path.Dir(filepath), path.Base(filepath), tarWriter)
|
||||
}
|
||||
|
||||
func recursiveTar(base, file string, tw *tar.Writer) error {
|
||||
filepath := path.Join(base, file)
|
||||
stat, err := os.Stat(filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if stat.IsDir() {
|
||||
files, err := ioutil.ReadDir(filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, f := range files {
|
||||
if err := recursiveTar(base, path.Join(file, f.Name()), tw); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
hdr, err := tar.FileInfoHeader(stat, filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hdr.Name = file
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.Open(filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = io.Copy(tw, f)
|
||||
return err
|
||||
}
|
||||
|
||||
func untarAll(reader io.Reader, destFile, prefix string) error {
|
||||
// TODO: use compression here?
|
||||
tarReader := tar.NewReader(reader)
|
||||
for {
|
||||
header, err := tarReader.Next()
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
outFileName := path.Join(destFile, header.Name[len(prefix):])
|
||||
baseName := path.Dir(outFileName)
|
||||
if err := os.MkdirAll(baseName, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if header.FileInfo().IsDir() {
|
||||
os.MkdirAll(outFileName, 0755)
|
||||
continue
|
||||
}
|
||||
outFile, err := os.Create(outFileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer outFile.Close()
|
||||
io.Copy(outFile, tarReader)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func execute(f cmdutil.Factory, cmd *cobra.Command, options *ExecOptions) error {
|
||||
if len(options.Namespace) == 0 {
|
||||
namespace, _, err := f.DefaultNamespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options.Namespace = namespace
|
||||
}
|
||||
|
||||
container := cmdutil.GetFlagString(cmd, "container")
|
||||
if len(container) > 0 {
|
||||
options.ContainerName = container
|
||||
}
|
||||
|
||||
config, err := f.ClientConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options.Config = config
|
||||
|
||||
clientset, err := f.ClientSet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options.PodClient = clientset.Core()
|
||||
|
||||
if err := options.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := options.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
180
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/cp_test.go
generated
vendored
Normal file
180
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/cp_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
Copyright 2014 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"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExtractFileSpec(t *testing.T) {
|
||||
tests := []struct {
|
||||
spec string
|
||||
expectedPod string
|
||||
expectedNamespace string
|
||||
expectedFile string
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
spec: "namespace/pod:/some/file",
|
||||
expectedPod: "pod",
|
||||
expectedNamespace: "namespace",
|
||||
expectedFile: "/some/file",
|
||||
},
|
||||
{
|
||||
spec: "pod:/some/file",
|
||||
expectedPod: "pod",
|
||||
expectedFile: "/some/file",
|
||||
},
|
||||
{
|
||||
spec: "/some/file",
|
||||
expectedFile: "/some/file",
|
||||
},
|
||||
{
|
||||
spec: "some:bad:spec",
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
spec, err := extractFileSpec(test.spec)
|
||||
if test.expectErr && err == nil {
|
||||
t.Errorf("unexpected non-error")
|
||||
continue
|
||||
}
|
||||
if err != nil && !test.expectErr {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
continue
|
||||
}
|
||||
if spec.PodName != test.expectedPod {
|
||||
t.Errorf("expected: %s, saw: %s", test.expectedPod, spec.PodName)
|
||||
}
|
||||
if spec.PodNamespace != test.expectedNamespace {
|
||||
t.Errorf("expected: %s, saw: %s", test.expectedNamespace, spec.PodNamespace)
|
||||
}
|
||||
if spec.File != test.expectedFile {
|
||||
t.Errorf("expected: %s, saw: %s", test.expectedFile, spec.File)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPrefix(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
input: "/foo/bar",
|
||||
expected: "foo/bar",
|
||||
},
|
||||
{
|
||||
input: "foo/bar",
|
||||
expected: "foo/bar",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
out := getPrefix(test.input)
|
||||
if out != test.expected {
|
||||
t.Errorf("expected: %s, saw: %s", test.expected, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTarUntar(t *testing.T) {
|
||||
dir, err := ioutil.TempDir(os.TempDir(), "input")
|
||||
dir2, err2 := ioutil.TempDir(os.TempDir(), "output")
|
||||
if err != nil || err2 != nil {
|
||||
t.Errorf("unexpected error: %v | %v", err, err2)
|
||||
t.FailNow()
|
||||
}
|
||||
defer func() {
|
||||
if err := os.RemoveAll(dir); err != nil {
|
||||
t.Errorf("Unexpected error cleaning up: %v", err)
|
||||
}
|
||||
if err := os.RemoveAll(dir2); err != nil {
|
||||
t.Errorf("Unexpected error cleaning up: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
files := []struct {
|
||||
name string
|
||||
data string
|
||||
}{
|
||||
{
|
||||
name: "foo",
|
||||
data: "foobarbaz",
|
||||
},
|
||||
{
|
||||
name: "dir/blah",
|
||||
data: "bazblahfoo",
|
||||
},
|
||||
{
|
||||
name: "some/other/directory",
|
||||
data: "with more data here",
|
||||
},
|
||||
{
|
||||
name: "blah",
|
||||
data: "same file name different data",
|
||||
},
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
filepath := path.Join(dir, file.name)
|
||||
if err := os.MkdirAll(path.Dir(filepath), 0755); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
t.FailNow()
|
||||
}
|
||||
f, err := os.Create(filepath)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
t.FailNow()
|
||||
}
|
||||
defer f.Close()
|
||||
if _, err := io.Copy(f, bytes.NewBuffer([]byte(file.data))); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
writer := &bytes.Buffer{}
|
||||
if err := makeTar(dir, writer); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
reader := bytes.NewBuffer(writer.Bytes())
|
||||
if err := untarAll(reader, dir2, ""); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
filepath := path.Join(dir, file.name)
|
||||
f, err := os.Open(filepath)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
buff := &bytes.Buffer{}
|
||||
io.Copy(buff, f)
|
||||
if file.data != string(buff.Bytes()) {
|
||||
t.Errorf("expected: %s, saw: %s", file.data, string(buff.Bytes()))
|
||||
}
|
||||
}
|
||||
}
|
||||
299
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create.go
generated
vendored
Normal file
299
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create.go
generated
vendored
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
/*
|
||||
Copyright 2014 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"
|
||||
"runtime"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"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/kubectl/resource"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
type CreateOptions struct {
|
||||
FilenameOptions resource.FilenameOptions
|
||||
Selector string
|
||||
EditBeforeCreate bool
|
||||
}
|
||||
|
||||
var (
|
||||
createLong = templates.LongDesc(i18n.T(`
|
||||
Create a resource from a file or from stdin.
|
||||
|
||||
JSON and YAML formats are accepted.`))
|
||||
|
||||
createExample = templates.Examples(i18n.T(`
|
||||
# Create a pod using the data in pod.json.
|
||||
kubectl create -f ./pod.json
|
||||
|
||||
# Create a pod based on the JSON passed into stdin.
|
||||
cat pod.json | kubectl create -f -
|
||||
|
||||
# Edit the data in docker-registry.yaml in JSON using the v1 API format then create the resource using the edited data.
|
||||
kubectl create -f docker-registry.yaml --edit --output-version=v1 -o json`))
|
||||
)
|
||||
|
||||
func NewCmdCreate(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
|
||||
var options CreateOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "create -f FILENAME",
|
||||
Short: i18n.T("Create a resource from a file or from stdin."),
|
||||
Long: createLong,
|
||||
Example: createExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if cmdutil.IsFilenameSliceEmpty(options.FilenameOptions.Filenames) {
|
||||
defaultRunFunc := cmdutil.DefaultSubCommandRun(errOut)
|
||||
defaultRunFunc(cmd, args)
|
||||
return
|
||||
}
|
||||
cmdutil.CheckErr(ValidateArgs(cmd, args))
|
||||
cmdutil.CheckErr(RunCreate(f, cmd, out, errOut, &options))
|
||||
},
|
||||
}
|
||||
|
||||
usage := "to use to create the resource"
|
||||
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
|
||||
cmd.MarkFlagRequired("filename")
|
||||
cmdutil.AddValidateFlags(cmd)
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
cmd.Flags().BoolVar(&options.EditBeforeCreate, "edit", false, "Edit the API resource before creating")
|
||||
cmd.Flags().Bool("windows-line-endings", runtime.GOOS == "windows",
|
||||
"Only relevant if --edit=true. Defaults to the line ending native to your platform.")
|
||||
cmdutil.AddApplyAnnotationFlags(cmd)
|
||||
cmdutil.AddRecordFlag(cmd)
|
||||
cmdutil.AddDryRunFlag(cmd)
|
||||
cmdutil.AddInclude3rdPartyFlags(cmd)
|
||||
cmd.Flags().StringVarP(&options.Selector, "selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
|
||||
|
||||
// create subcommands
|
||||
cmd.AddCommand(NewCmdCreateNamespace(f, out))
|
||||
cmd.AddCommand(NewCmdCreateQuota(f, out))
|
||||
cmd.AddCommand(NewCmdCreateSecret(f, out, errOut))
|
||||
cmd.AddCommand(NewCmdCreateConfigMap(f, out))
|
||||
cmd.AddCommand(NewCmdCreateServiceAccount(f, out))
|
||||
cmd.AddCommand(NewCmdCreateService(f, out, errOut))
|
||||
cmd.AddCommand(NewCmdCreateDeployment(f, out, errOut))
|
||||
cmd.AddCommand(NewCmdCreateClusterRole(f, out))
|
||||
cmd.AddCommand(NewCmdCreateClusterRoleBinding(f, out))
|
||||
cmd.AddCommand(NewCmdCreateRole(f, out))
|
||||
cmd.AddCommand(NewCmdCreateRoleBinding(f, out))
|
||||
cmd.AddCommand(NewCmdCreatePodDisruptionBudget(f, out))
|
||||
return cmd
|
||||
}
|
||||
|
||||
func ValidateArgs(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 0 {
|
||||
return cmdutil.UsageErrorf(cmd, "Unexpected args: %v", args)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func RunCreate(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, options *CreateOptions) error {
|
||||
if options.EditBeforeCreate {
|
||||
return RunEditOnCreate(f, out, errOut, cmd, &options.FilenameOptions)
|
||||
}
|
||||
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagBool(cmd, "openapi-validation"), cmdutil.GetFlagString(cmd, "schema-cache-dir"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mapper, _, err := f.UnstructuredObject()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
builder, err := f.NewUnstructuredBuilder(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r := builder.
|
||||
Schema(schema).
|
||||
ContinueOnError().
|
||||
NamespaceParam(cmdNamespace).DefaultNamespace().
|
||||
FilenameParam(enforceNamespace, &options.FilenameOptions).
|
||||
SelectorParam(options.Selector).
|
||||
Flatten().
|
||||
Do()
|
||||
err = r.Err()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dryRun := cmdutil.GetFlagBool(cmd, "dry-run")
|
||||
output := cmdutil.GetFlagString(cmd, "output")
|
||||
|
||||
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 {
|
||||
return cmdutil.AddSourceToErr("creating", info.Source, err)
|
||||
}
|
||||
|
||||
if cmdutil.ShouldRecord(cmd, info) {
|
||||
if err := cmdutil.RecordChangeCause(info.Object, f.Command(cmd, false)); err != nil {
|
||||
return cmdutil.AddSourceToErr("creating", info.Source, err)
|
||||
}
|
||||
}
|
||||
|
||||
if !dryRun {
|
||||
if err := createAndRefresh(info); err != nil {
|
||||
return cmdutil.AddSourceToErr("creating", info.Source, err)
|
||||
}
|
||||
}
|
||||
|
||||
count++
|
||||
|
||||
shortOutput := output == "name"
|
||||
if len(output) > 0 && !shortOutput {
|
||||
return cmdutil.PrintResourceInfoForCommand(cmd, info, f, out)
|
||||
}
|
||||
if !shortOutput {
|
||||
f.PrintObjectSpecificMessage(info.Object, out)
|
||||
}
|
||||
|
||||
cmdutil.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, dryRun, "created")
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count == 0 {
|
||||
return fmt.Errorf("no objects passed to create")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func RunEditOnCreate(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, options *resource.FilenameOptions) error {
|
||||
editOptions := &editor.EditOptions{
|
||||
EditMode: editor.EditBeforeCreateMode,
|
||||
FilenameOptions: *options,
|
||||
ValidateOptions: cmdutil.ValidateOptions{
|
||||
EnableValidation: cmdutil.GetFlagBool(cmd, "validate"),
|
||||
SchemaCacheDir: cmdutil.GetFlagString(cmd, "schema-cache-dir"),
|
||||
},
|
||||
Output: cmdutil.GetFlagString(cmd, "output"),
|
||||
WindowsLineEndings: cmdutil.GetFlagBool(cmd, "windows-line-endings"),
|
||||
ApplyAnnotation: cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag),
|
||||
Record: cmdutil.GetFlagBool(cmd, "record"),
|
||||
ChangeCause: f.Command(cmd, false),
|
||||
Include3rdParty: cmdutil.GetFlagBool(cmd, "include-extended-apis"),
|
||||
}
|
||||
err := editOptions.Complete(f, out, errOut, []string{}, cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return editOptions.Run()
|
||||
}
|
||||
|
||||
// createAndRefresh creates an object from input info and refreshes info with that object
|
||||
func createAndRefresh(info *resource.Info) error {
|
||||
obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info.Refresh(obj, true)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
return args[0], nil
|
||||
}
|
||||
|
||||
// CreateSubcommandOptions is an options struct to support create subcommands
|
||||
type CreateSubcommandOptions struct {
|
||||
// Name of resource being created
|
||||
Name string
|
||||
// StructuredGenerator is the resource generator for the object being created
|
||||
StructuredGenerator kubectl.StructuredGenerator
|
||||
// DryRun is true if the command should be simulated but not run against the server
|
||||
DryRun bool
|
||||
OutputFormat string
|
||||
}
|
||||
|
||||
// RunCreateSubcommand executes a create subcommand using the specified options
|
||||
func RunCreateSubcommand(f cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *CreateSubcommandOptions) error {
|
||||
namespace, _, err := f.DefaultNamespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
obj, err := options.StructuredGenerator.StructuredGenerate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mapper, typer := f.Object()
|
||||
gvks, _, err := typer.ObjectKinds(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gvk := gvks[0]
|
||||
mapping, err := mapper.RESTMapping(schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind}, gvk.Version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := f.ClientForMapping(mapping)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resourceMapper := &resource.Mapper{
|
||||
ObjectTyper: typer,
|
||||
RESTMapper: mapper,
|
||||
ClientMapper: resource.ClientMapperFunc(f.ClientForMapping),
|
||||
}
|
||||
info, err := resourceMapper.InfoForObject(obj, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil {
|
||||
return err
|
||||
}
|
||||
obj = info.Object
|
||||
|
||||
if !options.DryRun {
|
||||
obj, err = resource.NewHelper(client, mapping).Create(namespace, false, info.Object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if useShortOutput := options.OutputFormat == "name"; useShortOutput || len(options.OutputFormat) == 0 {
|
||||
cmdutil.PrintSuccess(mapper, useShortOutput, out, mapping.Resource, info.Name, options.DryRun, "created")
|
||||
return nil
|
||||
}
|
||||
|
||||
return f.PrintObject(cmd, false, mapper, obj, out)
|
||||
}
|
||||
180
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_clusterrole.go
generated
vendored
Normal file
180
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_clusterrole.go
generated
vendored
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
var (
|
||||
clusterRoleLong = templates.LongDesc(i18n.T(`
|
||||
Create a ClusterRole.`))
|
||||
|
||||
clusterRoleExample = templates.Examples(i18n.T(`
|
||||
# Create a ClusterRole named "pod-reader" that allows user to perform "get", "watch" and "list" on pods
|
||||
kubectl create clusterrole pod-reader --verb=get,list,watch --resource=pods
|
||||
|
||||
# Create a ClusterRole named "pod-reader" with ResourceName specified
|
||||
kubectl create clusterrole pod-reader --verb=get,list,watch --resource=pods --resource-name=readablepod --resource-name=anotherpod
|
||||
|
||||
# Create a ClusterRole named "foo" with API Group specified
|
||||
kubectl create clusterrole foo --verb=get,list,watch --resource=rs.extensions
|
||||
|
||||
# Create a ClusterRole named "foo" with SubResource specified
|
||||
kubectl create clusterrole foo --verb=get,list,watch --resource=pods,pods/status
|
||||
|
||||
# Create a ClusterRole name "foo" with NonResourceURL specified
|
||||
kubectl create clusterrole "foo" --verb=get --non-resource-url=/logs/*`))
|
||||
|
||||
// Valid nonResource verb list for validation.
|
||||
validNonResourceVerbs = []string{"*", "get", "post", "put", "delete", "patch", "head", "options"}
|
||||
)
|
||||
|
||||
type CreateClusterRoleOptions struct {
|
||||
*CreateRoleOptions
|
||||
NonResourceURLs []string
|
||||
}
|
||||
|
||||
// ClusterRole is a command to ease creating ClusterRoles.
|
||||
func NewCmdCreateClusterRole(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
|
||||
c := &CreateClusterRoleOptions{
|
||||
CreateRoleOptions: &CreateRoleOptions{
|
||||
Out: cmdOut,
|
||||
},
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "clusterrole NAME --verb=verb --resource=resource.group [--resource-name=resourcename] [--dry-run]",
|
||||
Short: clusterRoleLong,
|
||||
Long: clusterRoleLong,
|
||||
Example: clusterRoleExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(c.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(c.Validate())
|
||||
cmdutil.CheckErr(c.RunCreateRole())
|
||||
},
|
||||
}
|
||||
cmdutil.AddApplyAnnotationFlags(cmd)
|
||||
cmdutil.AddValidateFlags(cmd)
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
cmdutil.AddDryRunFlag(cmd)
|
||||
cmd.Flags().StringSliceVar(&c.Verbs, "verb", []string{}, "Verb that applies to the resources contained in the rule")
|
||||
cmd.Flags().StringSliceVar(&c.NonResourceURLs, "non-resource-url", []string{}, "A partial url that user should have access to.")
|
||||
cmd.Flags().StringSlice("resource", []string{}, "Resource that the rule applies to")
|
||||
cmd.Flags().StringArrayVar(&c.ResourceNames, "resource-name", []string{}, "Resource in the white list that the rule applies to, repeat this flag for multiple items")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (c *CreateClusterRoleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
// Remove duplicate nonResourceURLs
|
||||
nonResourceURLs := []string{}
|
||||
for _, n := range c.NonResourceURLs {
|
||||
if !arrayContains(nonResourceURLs, n) {
|
||||
nonResourceURLs = append(nonResourceURLs, n)
|
||||
}
|
||||
}
|
||||
c.NonResourceURLs = nonResourceURLs
|
||||
|
||||
return c.CreateRoleOptions.Complete(f, cmd, args)
|
||||
}
|
||||
|
||||
func (c *CreateClusterRoleOptions) Validate() error {
|
||||
if c.Name == "" {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
|
||||
// validate verbs.
|
||||
if len(c.Verbs) == 0 {
|
||||
return fmt.Errorf("at least one verb must be specified")
|
||||
}
|
||||
|
||||
if len(c.Resources) == 0 && len(c.NonResourceURLs) == 0 {
|
||||
return fmt.Errorf("one of resource or nonResourceURL must be specified")
|
||||
}
|
||||
|
||||
// validate resources
|
||||
if len(c.Resources) > 0 {
|
||||
for _, v := range c.Verbs {
|
||||
if !arrayContains(validResourceVerbs, v) {
|
||||
return fmt.Errorf("invalid verb: '%s'", v)
|
||||
}
|
||||
}
|
||||
if err := c.validateResource(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
//validate non-resource-url
|
||||
if len(c.NonResourceURLs) > 0 {
|
||||
for _, v := range c.Verbs {
|
||||
if !arrayContains(validNonResourceVerbs, v) {
|
||||
return fmt.Errorf("invalid verb: '%s' for nonResourceURL", v)
|
||||
}
|
||||
}
|
||||
|
||||
for _, nonResourceURL := range c.NonResourceURLs {
|
||||
if nonResourceURL == "*" {
|
||||
continue
|
||||
}
|
||||
|
||||
if nonResourceURL == "" || !strings.HasPrefix(nonResourceURL, "/") {
|
||||
return fmt.Errorf("nonResourceURL should start with /")
|
||||
}
|
||||
|
||||
if strings.ContainsRune(nonResourceURL[:len(nonResourceURL)-1], '*') {
|
||||
return fmt.Errorf("nonResourceURL only supports wildcard matches when '*' is at the end")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (c *CreateClusterRoleOptions) RunCreateRole() error {
|
||||
clusterRole := &rbac.ClusterRole{}
|
||||
clusterRole.Name = c.Name
|
||||
rules, err := generateResourcePolicyRules(c.Mapper, c.Verbs, c.Resources, c.ResourceNames, c.NonResourceURLs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clusterRole.Rules = rules
|
||||
|
||||
// Create ClusterRole.
|
||||
if !c.DryRun {
|
||||
_, err = c.Client.ClusterRoles().Create(clusterRole)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if useShortOutput := c.OutputFormat == "name"; useShortOutput || len(c.OutputFormat) == 0 {
|
||||
cmdutil.PrintSuccess(c.Mapper, useShortOutput, c.Out, "clusterroles", c.Name, c.DryRun, "created")
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.PrintObject(clusterRole)
|
||||
}
|
||||
456
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_clusterrole_test.go
generated
vendored
Normal file
456
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_clusterrole_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,456 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/rest/fake"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
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.Namespace = "test"
|
||||
tf.Client = &fake.RESTClient{}
|
||||
tf.ClientConfig = defaultClientConfig()
|
||||
|
||||
tests := map[string]struct {
|
||||
verbs string
|
||||
resources string
|
||||
nonResourceURL string
|
||||
resourceNames string
|
||||
expectedClusterRole *rbac.ClusterRole
|
||||
}{
|
||||
"test-duplicate-resources": {
|
||||
verbs: "get,watch,list",
|
||||
resources: "pods,pods",
|
||||
expectedClusterRole: &rbac.ClusterRole{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: clusterRoleName,
|
||||
},
|
||||
Rules: []rbac.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get", "watch", "list"},
|
||||
Resources: []string{"pods"},
|
||||
APIGroups: []string{""},
|
||||
ResourceNames: []string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"test-valid-case-with-multiple-apigroups": {
|
||||
verbs: "get,watch,list",
|
||||
resources: "pods,deployments.extensions",
|
||||
expectedClusterRole: &rbac.ClusterRole{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: clusterRoleName,
|
||||
},
|
||||
Rules: []rbac.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get", "watch", "list"},
|
||||
Resources: []string{"pods"},
|
||||
APIGroups: []string{""},
|
||||
ResourceNames: []string{},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get", "watch", "list"},
|
||||
Resources: []string{"deployments"},
|
||||
APIGroups: []string{"extensions"},
|
||||
ResourceNames: []string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"test-non-resource-url": {
|
||||
verbs: "get",
|
||||
nonResourceURL: "/logs/,/healthz",
|
||||
expectedClusterRole: &rbac.ClusterRole{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: clusterRoleName,
|
||||
},
|
||||
Rules: []rbac.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
NonResourceURLs: []string{"/logs/", "/healthz"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"test-resource-and-non-resource-url": {
|
||||
verbs: "get",
|
||||
nonResourceURL: "/logs/,/healthz",
|
||||
resources: "pods",
|
||||
expectedClusterRole: &rbac.ClusterRole{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: clusterRoleName,
|
||||
},
|
||||
Rules: []rbac.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
Resources: []string{"pods"},
|
||||
APIGroups: []string{""},
|
||||
ResourceNames: []string{},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
NonResourceURLs: []string{"/logs/", "/healthz"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdCreateClusterRole(f, buf)
|
||||
cmd.Flags().Set("dry-run", "true")
|
||||
cmd.Flags().Set("output", "object")
|
||||
cmd.Flags().Set("verb", test.verbs)
|
||||
cmd.Flags().Set("resource", test.resources)
|
||||
cmd.Flags().Set("non-resource-url", test.nonResourceURL)
|
||||
if test.resourceNames != "" {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClusterRoleValidate(t *testing.T) {
|
||||
f, tf, _, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Namespace = "test"
|
||||
|
||||
tests := map[string]struct {
|
||||
clusterRoleOptions *CreateClusterRoleOptions
|
||||
expectErr bool
|
||||
}{
|
||||
"test-missing-name": {
|
||||
clusterRoleOptions: &CreateClusterRoleOptions{
|
||||
CreateRoleOptions: &CreateRoleOptions{},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-missing-verb": {
|
||||
clusterRoleOptions: &CreateClusterRoleOptions{
|
||||
CreateRoleOptions: &CreateRoleOptions{
|
||||
Name: "my-clusterrole",
|
||||
},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-missing-resource": {
|
||||
clusterRoleOptions: &CreateClusterRoleOptions{
|
||||
CreateRoleOptions: &CreateRoleOptions{
|
||||
Name: "my-clusterrole",
|
||||
Verbs: []string{"get"},
|
||||
},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-missing-resource-existing-apigroup": {
|
||||
clusterRoleOptions: &CreateClusterRoleOptions{
|
||||
CreateRoleOptions: &CreateRoleOptions{
|
||||
Name: "my-clusterrole",
|
||||
Verbs: []string{"get"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Group: "extensions",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-missing-resource-existing-subresource": {
|
||||
clusterRoleOptions: &CreateClusterRoleOptions{
|
||||
CreateRoleOptions: &CreateRoleOptions{
|
||||
Name: "my-clusterrole",
|
||||
Verbs: []string{"get"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
SubResource: "scale",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-invalid-verb": {
|
||||
clusterRoleOptions: &CreateClusterRoleOptions{
|
||||
CreateRoleOptions: &CreateRoleOptions{
|
||||
Name: "my-clusterrole",
|
||||
Verbs: []string{"invalid-verb"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Resource: "pods",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-nonresource-verb": {
|
||||
clusterRoleOptions: &CreateClusterRoleOptions{
|
||||
CreateRoleOptions: &CreateRoleOptions{
|
||||
Name: "my-clusterrole",
|
||||
Verbs: []string{"post"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Resource: "pods",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-special-verb": {
|
||||
clusterRoleOptions: &CreateClusterRoleOptions{
|
||||
CreateRoleOptions: &CreateRoleOptions{
|
||||
Name: "my-clusterrole",
|
||||
Verbs: []string{"use"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Resource: "pods",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-mix-verbs": {
|
||||
clusterRoleOptions: &CreateClusterRoleOptions{
|
||||
CreateRoleOptions: &CreateRoleOptions{
|
||||
Name: "my-clusterrole",
|
||||
Verbs: []string{"impersonate", "use"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Resource: "userextras",
|
||||
SubResource: "scopes",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-special-verb-with-wrong-apigroup": {
|
||||
clusterRoleOptions: &CreateClusterRoleOptions{
|
||||
CreateRoleOptions: &CreateRoleOptions{
|
||||
Name: "my-clusterrole",
|
||||
Verbs: []string{"impersonate"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Resource: "userextras",
|
||||
SubResource: "scopes",
|
||||
Group: "extensions",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-invalid-resource": {
|
||||
clusterRoleOptions: &CreateClusterRoleOptions{
|
||||
CreateRoleOptions: &CreateRoleOptions{
|
||||
Name: "my-clusterrole",
|
||||
Verbs: []string{"get"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Resource: "invalid-resource",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-resource-name-with-multiple-resources": {
|
||||
clusterRoleOptions: &CreateClusterRoleOptions{
|
||||
CreateRoleOptions: &CreateRoleOptions{
|
||||
Name: "my-clusterrole",
|
||||
Verbs: []string{"get"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Resource: "pods",
|
||||
},
|
||||
{
|
||||
Resource: "deployments",
|
||||
Group: "extensions",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
"test-valid-case": {
|
||||
clusterRoleOptions: &CreateClusterRoleOptions{
|
||||
CreateRoleOptions: &CreateRoleOptions{
|
||||
Name: "role-binder",
|
||||
Verbs: []string{"get", "list", "bind"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Resource: "roles",
|
||||
Group: "rbac.authorization.k8s.io",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
"test-valid-case-with-subresource": {
|
||||
clusterRoleOptions: &CreateClusterRoleOptions{
|
||||
CreateRoleOptions: &CreateRoleOptions{
|
||||
Name: "my-clusterrole",
|
||||
Verbs: []string{"get", "list"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Resource: "replicasets",
|
||||
SubResource: "scale",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
"test-valid-case-with-additional-resource": {
|
||||
clusterRoleOptions: &CreateClusterRoleOptions{
|
||||
CreateRoleOptions: &CreateRoleOptions{
|
||||
Name: "my-clusterrole",
|
||||
Verbs: []string{"impersonate"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Resource: "userextras",
|
||||
SubResource: "scopes",
|
||||
Group: "authentication.k8s.io",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
"test-invalid-empty-non-resource-url": {
|
||||
clusterRoleOptions: &CreateClusterRoleOptions{
|
||||
CreateRoleOptions: &CreateRoleOptions{
|
||||
Name: "my-clusterrole",
|
||||
Verbs: []string{"create"},
|
||||
},
|
||||
NonResourceURLs: []string{""},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-invalid-non-resource-url": {
|
||||
clusterRoleOptions: &CreateClusterRoleOptions{
|
||||
CreateRoleOptions: &CreateRoleOptions{
|
||||
Name: "my-clusterrole",
|
||||
Verbs: []string{"create"},
|
||||
},
|
||||
NonResourceURLs: []string{"logs"},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-invalid-non-resource-url-with-*": {
|
||||
clusterRoleOptions: &CreateClusterRoleOptions{
|
||||
CreateRoleOptions: &CreateRoleOptions{
|
||||
Name: "my-clusterrole",
|
||||
Verbs: []string{"create"},
|
||||
},
|
||||
NonResourceURLs: []string{"/logs/*/"},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-invalid-non-resource-url-with-multiple-*": {
|
||||
clusterRoleOptions: &CreateClusterRoleOptions{
|
||||
CreateRoleOptions: &CreateRoleOptions{
|
||||
Name: "my-clusterrole",
|
||||
Verbs: []string{"create"},
|
||||
},
|
||||
NonResourceURLs: []string{"/logs*/*"},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-invalid-verb-for-non-resource-url": {
|
||||
clusterRoleOptions: &CreateClusterRoleOptions{
|
||||
CreateRoleOptions: &CreateRoleOptions{
|
||||
Name: "my-clusterrole",
|
||||
Verbs: []string{"create"},
|
||||
},
|
||||
NonResourceURLs: []string{"/logs/"},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-resource-and-non-resource-url-specified-together": {
|
||||
clusterRoleOptions: &CreateClusterRoleOptions{
|
||||
CreateRoleOptions: &CreateRoleOptions{
|
||||
Name: "my-clusterrole",
|
||||
Verbs: []string{"get"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Resource: "replicasets",
|
||||
SubResource: "scale",
|
||||
},
|
||||
},
|
||||
},
|
||||
NonResourceURLs: []string{"/logs/", "/logs/*"},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
test.clusterRoleOptions.Mapper, _ = f.Object()
|
||||
err := test.clusterRoleOptions.Validate()
|
||||
if test.expectErr && err == nil {
|
||||
t.Errorf("%s: expect error happens, but validate passes.", name)
|
||||
}
|
||||
if !test.expectErr && err != nil {
|
||||
t.Errorf("%s: unexpected error: %v", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
87
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_clusterrolebinding.go
generated
vendored
Normal file
87
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_clusterrolebinding.go
generated
vendored
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
Copyright 2016 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 (
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
var (
|
||||
clusterRoleBindingLong = templates.LongDesc(i18n.T(`
|
||||
Create a ClusterRoleBinding for a particular ClusterRole.`))
|
||||
|
||||
clusterRoleBindingExample = templates.Examples(i18n.T(`
|
||||
# Create a ClusterRoleBinding for user1, user2, and group1 using the cluster-admin ClusterRole
|
||||
kubectl create clusterrolebinding cluster-admin --clusterrole=cluster-admin --user=user1 --user=user2 --group=group1`))
|
||||
)
|
||||
|
||||
// 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]",
|
||||
Short: i18n.T("Create a ClusterRoleBinding for a particular ClusterRole"),
|
||||
Long: clusterRoleBindingLong,
|
||||
Example: clusterRoleBindingExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := CreateClusterRoleBinding(f, cmdOut, cmd, args)
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
cmdutil.AddApplyAnnotationFlags(cmd)
|
||||
cmdutil.AddValidateFlags(cmd)
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
cmdutil.AddGeneratorFlags(cmd, cmdutil.ClusterRoleBindingV1GeneratorName)
|
||||
cmd.Flags().String("clusterrole", "", i18n.T("ClusterRole this ClusterRoleBinding should reference"))
|
||||
cmd.Flags().StringArray("user", []string{}, "Usernames to bind to the role")
|
||||
cmd.Flags().StringArray("group", []string{}, "Groups to bind to the role")
|
||||
cmd.Flags().StringArray("serviceaccount", []string{}, "Service accounts to bind to the role, in the format <namespace>:<name>")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// CreateClusterRoleBinding is the implementation of the create clusterrolebinding command.
|
||||
func CreateClusterRoleBinding(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error {
|
||||
name, err := NameFromCommandArgs(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var generator kubectl.StructuredGenerator
|
||||
switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName {
|
||||
case cmdutil.ClusterRoleBindingV1GeneratorName:
|
||||
generator = &kubectl.ClusterRoleBindingGeneratorV1{
|
||||
Name: name,
|
||||
ClusterRole: cmdutil.GetFlagString(cmd, "clusterrole"),
|
||||
Users: cmdutil.GetFlagStringArray(cmd, "user"),
|
||||
Groups: cmdutil.GetFlagStringArray(cmd, "group"),
|
||||
ServiceAccounts: cmdutil.GetFlagStringArray(cmd, "serviceaccount"),
|
||||
}
|
||||
default:
|
||||
return errUnsupportedGenerator(cmd, generatorName)
|
||||
}
|
||||
return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
|
||||
Name: name,
|
||||
StructuredGenerator: generator,
|
||||
DryRun: cmdutil.GetDryRunFlag(cmd),
|
||||
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
|
||||
})
|
||||
}
|
||||
109
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_configmap.go
generated
vendored
Normal file
109
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_configmap.go
generated
vendored
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
Copyright 2016 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 (
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
var (
|
||||
configMapLong = templates.LongDesc(i18n.T(`
|
||||
Create a configmap based on a file, directory, or specified literal value.
|
||||
|
||||
A single configmap may package one or more key/value pairs.
|
||||
|
||||
When creating a configmap based on a file, the key will default to the basename of the file, and the value will
|
||||
default to the file content. If the basename is an invalid key, you may specify an alternate key.
|
||||
|
||||
When creating a configmap based on a directory, each file whose basename is a valid key in the directory will be
|
||||
packaged into the configmap. Any directory entries except regular files are ignored (e.g. subdirectories,
|
||||
symlinks, devices, pipes, etc).`))
|
||||
|
||||
configMapExample = templates.Examples(i18n.T(`
|
||||
# Create a new configmap named my-config based on folder bar
|
||||
kubectl create configmap my-config --from-file=path/to/bar
|
||||
|
||||
# Create a new configmap named my-config with specified keys instead of file basenames on disk
|
||||
kubectl create configmap my-config --from-file=key1=/path/to/bar/file1.txt --from-file=key2=/path/to/bar/file2.txt
|
||||
|
||||
# Create a new configmap named my-config with key1=config1 and key2=config2
|
||||
kubectl create configmap my-config --from-literal=key1=config1 --from-literal=key2=config2
|
||||
|
||||
# Create a new configmap named my-config from the key=value pairs in the file
|
||||
kubectl create configmap my-config --from-file=path/to/bar
|
||||
|
||||
# Create a new configmap named my-config from an env file
|
||||
kubectl create configmap my-config --from-env-file=path/to/bar.env`))
|
||||
)
|
||||
|
||||
// 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,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := CreateConfigMap(f, cmdOut, cmd, args)
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
cmdutil.AddApplyAnnotationFlags(cmd)
|
||||
cmdutil.AddValidateFlags(cmd)
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
cmdutil.AddGeneratorFlags(cmd, cmdutil.ConfigMapV1GeneratorName)
|
||||
cmd.Flags().StringSlice("from-file", []string{}, "Key file can be specified using its file path, in which case file basename will be used as configmap key, or optionally with a key and file path, in which case the given key will be used. Specifying a directory will iterate each named file in the directory whose basename is a valid configmap key.")
|
||||
cmd.Flags().StringArray("from-literal", []string{}, "Specify a key and literal value to insert in configmap (i.e. mykey=somevalue)")
|
||||
cmd.Flags().String("from-env-file", "", "Specify the path to a file to read lines of key=val pairs to create a configmap (i.e. a Docker .env file).")
|
||||
cmd.Flags().Bool("append-hash", false, "Append a hash of the configmap to its name.")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// CreateConfigMap is the implementation of the create configmap command.
|
||||
func CreateConfigMap(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error {
|
||||
name, err := NameFromCommandArgs(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var generator kubectl.StructuredGenerator
|
||||
switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName {
|
||||
case cmdutil.ConfigMapV1GeneratorName:
|
||||
generator = &kubectl.ConfigMapGeneratorV1{
|
||||
Name: name,
|
||||
FileSources: cmdutil.GetFlagStringSlice(cmd, "from-file"),
|
||||
LiteralSources: cmdutil.GetFlagStringArray(cmd, "from-literal"),
|
||||
EnvFileSource: cmdutil.GetFlagString(cmd, "from-env-file"),
|
||||
AppendHash: cmdutil.GetFlagBool(cmd, "append-hash"),
|
||||
}
|
||||
default:
|
||||
return errUnsupportedGenerator(cmd, generatorName)
|
||||
}
|
||||
return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
|
||||
Name: name,
|
||||
StructuredGenerator: generator,
|
||||
DryRun: cmdutil.GetDryRunFlag(cmd),
|
||||
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
|
||||
})
|
||||
}
|
||||
56
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_configmap_test.go
generated
vendored
Normal file
56
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_configmap_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
Copyright 2016 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"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"k8s.io/client-go/rest/fake"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||
)
|
||||
|
||||
func TestCreateConfigMap(t *testing.T) {
|
||||
configMap := &api.ConfigMap{}
|
||||
configMap.Name = "my-configmap"
|
||||
f, tf, codec, ns := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Client = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: ns,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/configmaps" && m == "POST":
|
||||
return &http.Response{StatusCode: 201, Header: defaultHeader(), Body: objBody(codec, configMap)}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdCreateConfigMap(f, buf)
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{configMap.Name})
|
||||
expectedOutput := "configmap/" + configMap.Name + "\n"
|
||||
if buf.String() != expectedOutput {
|
||||
t.Errorf("expected output: %s, but got: %s", expectedOutput, buf.String())
|
||||
}
|
||||
}
|
||||
167
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_deployment.go
generated
vendored
Normal file
167
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_deployment.go
generated
vendored
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
Copyright 2016 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"
|
||||
|
||||
appsv1beta1 "k8s.io/api/apps/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
var (
|
||||
deploymentLong = templates.LongDesc(i18n.T(`
|
||||
Create a deployment with the specified name.`))
|
||||
|
||||
deploymentExample = templates.Examples(i18n.T(`
|
||||
# Create a new deployment named my-dep that runs the busybox image.
|
||||
kubectl create deployment my-dep --image=busybox`))
|
||||
)
|
||||
|
||||
// NewCmdCreateDeployment is a macro command to create a new deployment.
|
||||
// This command is better known to users as `kubectl create deployment`.
|
||||
// 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,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := createDeployment(f, cmdOut, cmdErr, cmd, args)
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
cmdutil.AddApplyAnnotationFlags(cmd)
|
||||
cmdutil.AddValidateFlags(cmd)
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
cmdutil.AddGeneratorFlags(cmd, cmdutil.DeploymentBasicV1Beta1GeneratorName)
|
||||
cmd.Flags().StringSlice("image", []string{}, "Image name to run.")
|
||||
cmd.MarkFlagRequired("image")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// fallbackGeneratorNameIfNecessary returns the name of the old generator
|
||||
// (v1beta1) if server does not support apps/v1beta1 deployments. Otherwise, the
|
||||
// generator string is returned unchanged.
|
||||
//
|
||||
// If the generator name is changed, print a warning message to let the user
|
||||
// know.
|
||||
func fallbackGeneratorNameIfNecessary(
|
||||
generatorName string,
|
||||
resourcesList []*metav1.APIResourceList,
|
||||
cmdErr io.Writer,
|
||||
) string {
|
||||
|
||||
if generatorName == cmdutil.DeploymentBasicAppsV1Beta1GeneratorName &&
|
||||
!contains(resourcesList, appsv1beta1.SchemeGroupVersion.WithResource("deployments")) {
|
||||
|
||||
fmt.Fprintf(cmdErr,
|
||||
"WARNING: New deployments generator %q specified, "+
|
||||
"but apps/v1beta1.Deployments are not available. "+
|
||||
"Falling back to %q.\n",
|
||||
cmdutil.DeploymentBasicAppsV1Beta1GeneratorName,
|
||||
cmdutil.DeploymentBasicV1Beta1GeneratorName,
|
||||
)
|
||||
|
||||
return cmdutil.DeploymentBasicV1Beta1GeneratorName
|
||||
}
|
||||
|
||||
return generatorName
|
||||
}
|
||||
|
||||
// generatorFromName returns the appropriate StructuredGenerator based on the
|
||||
// generatorName. If the generatorName is unrecognized, then return (nil,
|
||||
// false).
|
||||
func generatorFromName(
|
||||
generatorName string,
|
||||
imageNames []string,
|
||||
deploymentName string,
|
||||
) (kubectl.StructuredGenerator, bool) {
|
||||
|
||||
switch generatorName {
|
||||
case cmdutil.DeploymentBasicAppsV1Beta1GeneratorName:
|
||||
generator := &kubectl.DeploymentBasicAppsGeneratorV1{
|
||||
BaseDeploymentGenerator: kubectl.BaseDeploymentGenerator{
|
||||
Name: deploymentName,
|
||||
Images: imageNames,
|
||||
},
|
||||
}
|
||||
return generator, true
|
||||
|
||||
case cmdutil.DeploymentBasicV1Beta1GeneratorName:
|
||||
generator := &kubectl.DeploymentBasicGeneratorV1{
|
||||
BaseDeploymentGenerator: kubectl.BaseDeploymentGenerator{
|
||||
Name: deploymentName,
|
||||
Images: imageNames,
|
||||
},
|
||||
}
|
||||
return generator, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// createDeployment
|
||||
// 1. Reads user config values from Cobra.
|
||||
// 2. Sets up the correct Generator object.
|
||||
// 3. Calls RunCreateSubcommand.
|
||||
func createDeployment(f cmdutil.Factory, cmdOut, cmdErr io.Writer,
|
||||
cmd *cobra.Command, args []string) error {
|
||||
|
||||
deploymentName, err := NameFromCommandArgs(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
clientset, err := f.ClientSet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resourcesList, err := clientset.Discovery().ServerResources()
|
||||
// ServerResources ignores errors for old servers do not expose discovery
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to discover supported resources: %v", err)
|
||||
}
|
||||
|
||||
generatorName := cmdutil.GetFlagString(cmd, "generator")
|
||||
|
||||
// It is possible we have to modify the user-provided generator name if
|
||||
// the server does not have support for the requested generator.
|
||||
generatorName = fallbackGeneratorNameIfNecessary(generatorName, resourcesList, cmdErr)
|
||||
|
||||
imageNames := cmdutil.GetFlagStringSlice(cmd, "image")
|
||||
generator, ok := generatorFromName(generatorName, imageNames, deploymentName)
|
||||
if !ok {
|
||||
return errUnsupportedGenerator(cmd, generatorName)
|
||||
}
|
||||
|
||||
return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
|
||||
Name: deploymentName,
|
||||
StructuredGenerator: generator,
|
||||
DryRun: cmdutil.GetDryRunFlag(cmd),
|
||||
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
|
||||
})
|
||||
}
|
||||
127
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_deployment_test.go
generated
vendored
Normal file
127
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_deployment_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
Copyright 2016 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"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/rest/fake"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
)
|
||||
|
||||
func Test_generatorFromName(t *testing.T) {
|
||||
const (
|
||||
nonsenseName = "not-a-real-generator-name"
|
||||
basicName = cmdutil.DeploymentBasicV1Beta1GeneratorName
|
||||
basicAppsName = cmdutil.DeploymentBasicAppsV1Beta1GeneratorName
|
||||
deploymentName = "deployment-name"
|
||||
)
|
||||
imageNames := []string{"image-1", "image-2"}
|
||||
|
||||
generator, ok := generatorFromName(nonsenseName, imageNames, deploymentName)
|
||||
assert.Nil(t, generator)
|
||||
assert.False(t, ok)
|
||||
|
||||
generator, ok = generatorFromName(basicName, imageNames, deploymentName)
|
||||
assert.True(t, ok)
|
||||
|
||||
{
|
||||
expectedGenerator := &kubectl.DeploymentBasicGeneratorV1{
|
||||
BaseDeploymentGenerator: kubectl.BaseDeploymentGenerator{
|
||||
Name: deploymentName,
|
||||
Images: imageNames,
|
||||
},
|
||||
}
|
||||
assert.Equal(t, expectedGenerator, generator)
|
||||
}
|
||||
|
||||
generator, ok = generatorFromName(basicAppsName, imageNames, deploymentName)
|
||||
assert.True(t, ok)
|
||||
|
||||
{
|
||||
expectedGenerator := &kubectl.DeploymentBasicAppsGeneratorV1{
|
||||
BaseDeploymentGenerator: kubectl.BaseDeploymentGenerator{
|
||||
Name: deploymentName,
|
||||
Images: imageNames,
|
||||
},
|
||||
}
|
||||
assert.Equal(t, expectedGenerator, generator)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateDeployment(t *testing.T) {
|
||||
depName := "jonny-dep"
|
||||
f, tf, _, ns := cmdtesting.NewAPIFactory()
|
||||
tf.Client = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: ns,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: ioutil.NopCloser(bytes.NewBuffer([]byte("{}"))),
|
||||
}, nil
|
||||
}),
|
||||
}
|
||||
tf.ClientConfig = &restclient.Config{}
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := NewCmdCreateDeployment(f, 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"
|
||||
if buf.String() != expectedOutput {
|
||||
t.Errorf("expected output: %s, but got: %s", expectedOutput, buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateDeploymentNoImage(t *testing.T) {
|
||||
depName := "jonny-dep"
|
||||
f, tf, _, ns := cmdtesting.NewAPIFactory()
|
||||
tf.Client = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: ns,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: ioutil.NopCloser(&bytes.Buffer{}),
|
||||
}, nil
|
||||
}),
|
||||
}
|
||||
tf.ClientConfig = &restclient.Config{}
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Namespace = "test"
|
||||
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdCreateDeployment(f, buf, buf)
|
||||
cmd.Flags().Set("dry-run", "true")
|
||||
cmd.Flags().Set("output", "name")
|
||||
err := createDeployment(f, buf, buf, cmd, []string{depName})
|
||||
assert.Error(t, err, "at least one image must be specified")
|
||||
}
|
||||
79
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_namespace.go
generated
vendored
Normal file
79
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_namespace.go
generated
vendored
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
Copyright 2015 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 (
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
var (
|
||||
namespaceLong = templates.LongDesc(i18n.T(`
|
||||
Create a namespace with the specified name.`))
|
||||
|
||||
namespaceExample = templates.Examples(i18n.T(`
|
||||
# Create a new namespace named my-namespace
|
||||
kubectl create namespace my-namespace`))
|
||||
)
|
||||
|
||||
// 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,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := CreateNamespace(f, cmdOut, cmd, args)
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
cmdutil.AddApplyAnnotationFlags(cmd)
|
||||
cmdutil.AddValidateFlags(cmd)
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
cmdutil.AddGeneratorFlags(cmd, cmdutil.NamespaceV1GeneratorName)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// CreateNamespace implements the behavior to run the create namespace command
|
||||
func CreateNamespace(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error {
|
||||
name, err := NameFromCommandArgs(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var generator kubectl.StructuredGenerator
|
||||
switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName {
|
||||
case cmdutil.NamespaceV1GeneratorName:
|
||||
generator = &kubectl.NamespaceGeneratorV1{Name: name}
|
||||
default:
|
||||
return errUnsupportedGenerator(cmd, generatorName)
|
||||
}
|
||||
return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
|
||||
Name: name,
|
||||
StructuredGenerator: generator,
|
||||
DryRun: cmdutil.GetDryRunFlag(cmd),
|
||||
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
|
||||
})
|
||||
}
|
||||
55
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_namespace_test.go
generated
vendored
Normal file
55
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_namespace_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
Copyright 2014 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"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"k8s.io/client-go/rest/fake"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||
)
|
||||
|
||||
func TestCreateNamespace(t *testing.T) {
|
||||
namespaceObject := &api.Namespace{}
|
||||
namespaceObject.Name = "my-namespace"
|
||||
f, tf, codec, ns := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Client = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: ns,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces" && m == "POST":
|
||||
return &http.Response{StatusCode: 201, Header: defaultHeader(), Body: objBody(codec, namespaceObject)}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdCreateNamespace(f, buf)
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{namespaceObject.Name})
|
||||
expectedOutput := "namespace/" + namespaceObject.Name + "\n"
|
||||
if buf.String() != expectedOutput {
|
||||
t.Errorf("expected output: %s, but got: %s", expectedOutput, buf.String())
|
||||
}
|
||||
}
|
||||
99
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_pdb.go
generated
vendored
Normal file
99
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_pdb.go
generated
vendored
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
Copyright 2016 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 (
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
var (
|
||||
pdbLong = templates.LongDesc(i18n.T(`
|
||||
Create a pod disruption budget with the specified name, selector, and desired minimum available pods`))
|
||||
|
||||
pdbExample = templates.Examples(i18n.T(`
|
||||
# Create a pod disruption budget named my-pdb that will select all pods with the app=rails label
|
||||
# and require at least one of them being available at any point in time.
|
||||
kubectl create poddisruptionbudget my-pdb --selector=app=rails --min-available=1
|
||||
|
||||
# Create a pod disruption budget named my-pdb that will select all pods with the app=nginx label
|
||||
# and require at least half of the pods selected to be available at any point in time.
|
||||
kubectl create pdb my-pdb --selector=app=nginx --min-available=50%`))
|
||||
)
|
||||
|
||||
// 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,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := CreatePodDisruptionBudget(f, cmdOut, cmd, args)
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
|
||||
cmdutil.AddApplyAnnotationFlags(cmd)
|
||||
cmdutil.AddValidateFlags(cmd)
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
cmdutil.AddGeneratorFlags(cmd, cmdutil.PodDisruptionBudgetV2GeneratorName)
|
||||
|
||||
cmd.Flags().String("min-available", "", i18n.T("The minimum number or percentage of available pods this budget requires."))
|
||||
cmd.Flags().String("max-unavailable", "", i18n.T("The maximum number or percentage of unavailable pods this budget requires."))
|
||||
cmd.Flags().String("selector", "", i18n.T("A label selector to use for this budget. Only equality-based selector requirements are supported."))
|
||||
return cmd
|
||||
}
|
||||
|
||||
// CreatePodDisruptionBudget implements the behavior to run the create pdb command.
|
||||
func CreatePodDisruptionBudget(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error {
|
||||
name, err := NameFromCommandArgs(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var generator kubectl.StructuredGenerator
|
||||
switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName {
|
||||
case cmdutil.PodDisruptionBudgetV1GeneratorName:
|
||||
generator = &kubectl.PodDisruptionBudgetV1Generator{
|
||||
Name: name,
|
||||
MinAvailable: cmdutil.GetFlagString(cmd, "min-available"),
|
||||
Selector: cmdutil.GetFlagString(cmd, "selector"),
|
||||
}
|
||||
case cmdutil.PodDisruptionBudgetV2GeneratorName:
|
||||
generator = &kubectl.PodDisruptionBudgetV2Generator{
|
||||
Name: name,
|
||||
MinAvailable: cmdutil.GetFlagString(cmd, "min-available"),
|
||||
MaxUnavailable: cmdutil.GetFlagString(cmd, "max-unavailable"),
|
||||
Selector: cmdutil.GetFlagString(cmd, "selector"),
|
||||
}
|
||||
default:
|
||||
return errUnsupportedGenerator(cmd, generatorName)
|
||||
}
|
||||
return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
|
||||
Name: name,
|
||||
StructuredGenerator: generator,
|
||||
DryRun: cmdutil.GetFlagBool(cmd, "dry-run"),
|
||||
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
|
||||
})
|
||||
}
|
||||
59
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_pdb_test.go
generated
vendored
Normal file
59
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_pdb_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/rest/fake"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||
)
|
||||
|
||||
func TestCreatePdb(t *testing.T) {
|
||||
pdbName := "my-pdb"
|
||||
f, tf, _, ns := cmdtesting.NewAPIFactory()
|
||||
tf.Client = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: ns,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: ioutil.NopCloser(&bytes.Buffer{}),
|
||||
}, nil
|
||||
}),
|
||||
}
|
||||
tf.ClientConfig = &restclient.Config{}
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := NewCmdCreatePodDisruptionBudget(f, 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"
|
||||
if buf.String() != expectedOutput {
|
||||
t.Errorf("expected output: %s, but got: %s", expectedOutput, buf.String())
|
||||
}
|
||||
}
|
||||
88
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_quota.go
generated
vendored
Normal file
88
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_quota.go
generated
vendored
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
Copyright 2016 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 (
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
var (
|
||||
quotaLong = templates.LongDesc(i18n.T(`
|
||||
Create a resourcequota with the specified name, hard limits and optional scopes`))
|
||||
|
||||
quotaExample = templates.Examples(i18n.T(`
|
||||
# Create a new resourcequota named my-quota
|
||||
kubectl create quota my-quota --hard=cpu=1,memory=1G,pods=2,services=3,replicationcontrollers=2,resourcequotas=1,secrets=5,persistentvolumeclaims=10
|
||||
|
||||
# Create a new resourcequota named best-effort
|
||||
kubectl create quota best-effort --hard=pods=100 --scopes=BestEffort`))
|
||||
)
|
||||
|
||||
// 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,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := CreateQuota(f, cmdOut, cmd, args)
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
|
||||
cmdutil.AddApplyAnnotationFlags(cmd)
|
||||
cmdutil.AddValidateFlags(cmd)
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
cmdutil.AddGeneratorFlags(cmd, cmdutil.ResourceQuotaV1GeneratorName)
|
||||
cmd.Flags().String("hard", "", i18n.T("A comma-delimited set of resource=quantity pairs that define a hard limit."))
|
||||
cmd.Flags().String("scopes", "", i18n.T("A comma-delimited set of quota scopes that must all match each object tracked by the quota."))
|
||||
return cmd
|
||||
}
|
||||
|
||||
// CreateQuota implements the behavior to run the create quota command
|
||||
func CreateQuota(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error {
|
||||
name, err := NameFromCommandArgs(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var generator kubectl.StructuredGenerator
|
||||
switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName {
|
||||
case cmdutil.ResourceQuotaV1GeneratorName:
|
||||
generator = &kubectl.ResourceQuotaGeneratorV1{
|
||||
Name: name,
|
||||
Hard: cmdutil.GetFlagString(cmd, "hard"),
|
||||
Scopes: cmdutil.GetFlagString(cmd, "scopes"),
|
||||
}
|
||||
default:
|
||||
return errUnsupportedGenerator(cmd, generatorName)
|
||||
}
|
||||
return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
|
||||
Name: name,
|
||||
StructuredGenerator: generator,
|
||||
DryRun: cmdutil.GetFlagBool(cmd, "dry-run"),
|
||||
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
|
||||
})
|
||||
}
|
||||
81
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_quota_test.go
generated
vendored
Normal file
81
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_quota_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
Copyright 2016 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"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"k8s.io/client-go/rest/fake"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||
)
|
||||
|
||||
func TestCreateQuota(t *testing.T) {
|
||||
resourceQuotaObject := &api.ResourceQuota{}
|
||||
resourceQuotaObject.Name = "my-quota"
|
||||
f, tf, codec, ns := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Client = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: ns,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/resourcequotas" && m == "POST":
|
||||
return &http.Response{StatusCode: 201, Header: defaultHeader(), Body: objBody(codec, resourceQuotaObject)}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
|
||||
tests := map[string]struct {
|
||||
flags []string
|
||||
expectedOutput string
|
||||
}{
|
||||
"single resource": {
|
||||
flags: []string{"--hard=cpu=1"},
|
||||
expectedOutput: "resourcequota/" + resourceQuotaObject.Name + "\n",
|
||||
},
|
||||
"single resource with a scope": {
|
||||
flags: []string{"--hard=cpu=1", "--scopes=BestEffort"},
|
||||
expectedOutput: "resourcequota/" + resourceQuotaObject.Name + "\n",
|
||||
},
|
||||
"multiple resources": {
|
||||
flags: []string{"--hard=cpu=1,pods=42", "--scopes=BestEffort"},
|
||||
expectedOutput: "resourcequota/" + resourceQuotaObject.Name + "\n",
|
||||
},
|
||||
"single resource with multiple scopes": {
|
||||
flags: []string{"--hard=cpu=1", "--scopes=BestEffort,NotTerminating"},
|
||||
expectedOutput: "resourcequota/" + resourceQuotaObject.Name + "\n",
|
||||
},
|
||||
}
|
||||
for name, test := range tests {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdCreateQuota(f, buf)
|
||||
cmd.Flags().Parse(test.flags)
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{resourceQuotaObject.Name})
|
||||
|
||||
if buf.String() != test.expectedOutput {
|
||||
t.Errorf("%s: expected output: %s, but got: %s", name, test.expectedOutput, buf.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
355
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_role.go
generated
vendored
Normal file
355
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_role.go
generated
vendored
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
internalversionrbac "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
var (
|
||||
roleLong = templates.LongDesc(i18n.T(`
|
||||
Create a role with single rule.`))
|
||||
|
||||
roleExample = templates.Examples(i18n.T(`
|
||||
# Create a Role named "pod-reader" that allows user to perform "get", "watch" and "list" on pods
|
||||
kubectl create role pod-reader --verb=get --verb=list --verb=watch --resource=pods
|
||||
|
||||
# Create a Role named "pod-reader" with ResourceName specified
|
||||
kubectl create role pod-reader --verb=get,list,watch --resource=pods --resource-name=readablepod --resource-name=anotherpod
|
||||
|
||||
# Create a Role named "foo" with API Group specified
|
||||
kubectl create role foo --verb=get,list,watch --resource=rs.extensions
|
||||
|
||||
# Create a Role named "foo" with SubResource specified
|
||||
kubectl create role foo --verb=get,list,watch --resource=pods,pods/status`))
|
||||
|
||||
// Valid resource verb list for validation.
|
||||
validResourceVerbs = []string{"*", "get", "delete", "list", "create", "update", "patch", "watch", "proxy", "deletecollection", "use", "bind", "impersonate"}
|
||||
|
||||
// Specialized verbs and GroupResources
|
||||
specialVerbs = map[string][]schema.GroupResource{
|
||||
"use": {
|
||||
{
|
||||
Group: "extensions",
|
||||
Resource: "podsecuritypolicies",
|
||||
},
|
||||
},
|
||||
"bind": {
|
||||
{
|
||||
Group: "rbac.authorization.k8s.io",
|
||||
Resource: "roles",
|
||||
},
|
||||
{
|
||||
Group: "rbac.authorization.k8s.io",
|
||||
Resource: "clusterroles",
|
||||
},
|
||||
},
|
||||
"impersonate": {
|
||||
{
|
||||
Group: "",
|
||||
Resource: "users",
|
||||
},
|
||||
{
|
||||
Group: "",
|
||||
Resource: "serviceaccounts",
|
||||
},
|
||||
{
|
||||
Group: "",
|
||||
Resource: "groups",
|
||||
},
|
||||
{
|
||||
Group: "authentication.k8s.io",
|
||||
Resource: "userextras",
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type ResourceOptions struct {
|
||||
Group string
|
||||
Resource string
|
||||
SubResource string
|
||||
}
|
||||
|
||||
type CreateRoleOptions struct {
|
||||
Name string
|
||||
Verbs []string
|
||||
Resources []ResourceOptions
|
||||
ResourceNames []string
|
||||
|
||||
DryRun bool
|
||||
OutputFormat string
|
||||
Namespace string
|
||||
Client internalversionrbac.RbacInterface
|
||||
Mapper meta.RESTMapper
|
||||
Out io.Writer
|
||||
PrintObject func(obj runtime.Object) error
|
||||
}
|
||||
|
||||
// Role is a command to ease creating Roles.
|
||||
func NewCmdCreateRole(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
|
||||
c := &CreateRoleOptions{
|
||||
Out: cmdOut,
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "role NAME --verb=verb --resource=resource.group/subresource [--resource-name=resourcename] [--dry-run]",
|
||||
Short: roleLong,
|
||||
Long: roleLong,
|
||||
Example: roleExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(c.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(c.Validate())
|
||||
cmdutil.CheckErr(c.RunCreateRole())
|
||||
},
|
||||
}
|
||||
cmdutil.AddApplyAnnotationFlags(cmd)
|
||||
cmdutil.AddValidateFlags(cmd)
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
cmdutil.AddDryRunFlag(cmd)
|
||||
cmd.Flags().StringSliceVar(&c.Verbs, "verb", []string{}, "Verb that applies to the resources contained in the rule")
|
||||
cmd.Flags().StringSlice("resource", []string{}, "Resource that the rule applies to")
|
||||
cmd.Flags().StringArrayVar(&c.ResourceNames, "resource-name", []string{}, "Resource in the white list that the rule applies to, repeat this flag for multiple items")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (c *CreateRoleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
name, err := NameFromCommandArgs(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Name = name
|
||||
|
||||
// Remove duplicate verbs.
|
||||
verbs := []string{}
|
||||
for _, v := range c.Verbs {
|
||||
// VerbAll respresents all kinds of verbs.
|
||||
if v == "*" {
|
||||
verbs = []string{"*"}
|
||||
break
|
||||
}
|
||||
if !arrayContains(verbs, v) {
|
||||
verbs = append(verbs, v)
|
||||
}
|
||||
}
|
||||
c.Verbs = verbs
|
||||
|
||||
// Support resource.group pattern. If no API Group specified, use "" as core API Group.
|
||||
// e.g. --resource=pods,deployments.extensions
|
||||
resources := cmdutil.GetFlagStringSlice(cmd, "resource")
|
||||
for _, r := range resources {
|
||||
sections := strings.SplitN(r, "/", 2)
|
||||
|
||||
resource := &ResourceOptions{}
|
||||
if len(sections) == 2 {
|
||||
resource.SubResource = sections[1]
|
||||
}
|
||||
|
||||
parts := strings.SplitN(sections[0], ".", 2)
|
||||
if len(parts) == 2 {
|
||||
resource.Group = parts[1]
|
||||
}
|
||||
resource.Resource = parts[0]
|
||||
|
||||
c.Resources = append(c.Resources, *resource)
|
||||
}
|
||||
|
||||
// Remove duplicate resource names.
|
||||
resourceNames := []string{}
|
||||
for _, n := range c.ResourceNames {
|
||||
if !arrayContains(resourceNames, n) {
|
||||
resourceNames = append(resourceNames, n)
|
||||
}
|
||||
}
|
||||
c.ResourceNames = resourceNames
|
||||
|
||||
// Complete other options for Run.
|
||||
c.Mapper, _ = f.Object()
|
||||
|
||||
c.DryRun = cmdutil.GetDryRunFlag(cmd)
|
||||
c.OutputFormat = cmdutil.GetFlagString(cmd, "output")
|
||||
|
||||
c.Namespace, _, err = f.DefaultNamespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.PrintObject = func(obj runtime.Object) error {
|
||||
return f.PrintObject(cmd, false, c.Mapper, obj, c.Out)
|
||||
}
|
||||
|
||||
clientSet, err := f.ClientSet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Client = clientSet.Rbac()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CreateRoleOptions) Validate() error {
|
||||
if c.Name == "" {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
|
||||
// validate verbs.
|
||||
if len(c.Verbs) == 0 {
|
||||
return fmt.Errorf("at least one verb must be specified")
|
||||
}
|
||||
|
||||
for _, v := range c.Verbs {
|
||||
if !arrayContains(validResourceVerbs, v) {
|
||||
return fmt.Errorf("invalid verb: '%s'", v)
|
||||
}
|
||||
}
|
||||
|
||||
// validate resources.
|
||||
if len(c.Resources) == 0 {
|
||||
return fmt.Errorf("at least one resource must be specified")
|
||||
}
|
||||
|
||||
return c.validateResource()
|
||||
}
|
||||
|
||||
func (c *CreateRoleOptions) validateResource() error {
|
||||
for _, r := range c.Resources {
|
||||
if len(r.Resource) == 0 {
|
||||
return fmt.Errorf("resource must be specified if apiGroup/subresource specified")
|
||||
}
|
||||
|
||||
resource := schema.GroupVersionResource{Resource: r.Resource, Group: r.Group}
|
||||
groupVersionResource, err := c.Mapper.ResourceFor(schema.GroupVersionResource{Resource: r.Resource, Group: r.Group})
|
||||
if err == nil {
|
||||
resource = groupVersionResource
|
||||
}
|
||||
|
||||
for _, v := range c.Verbs {
|
||||
if groupResources, ok := specialVerbs[v]; ok {
|
||||
match := false
|
||||
for _, extra := range groupResources {
|
||||
if resource.Resource == extra.Resource && resource.Group == extra.Group {
|
||||
match = true
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
}
|
||||
if !match {
|
||||
return fmt.Errorf("can not perform '%s' on '%s' in group '%s'", v, resource.Resource, resource.Group)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CreateRoleOptions) RunCreateRole() error {
|
||||
role := &rbac.Role{}
|
||||
role.Name = c.Name
|
||||
rules, err := generateResourcePolicyRules(c.Mapper, c.Verbs, c.Resources, c.ResourceNames, []string{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
role.Rules = rules
|
||||
|
||||
// Create role.
|
||||
if !c.DryRun {
|
||||
_, err = c.Client.Roles(c.Namespace).Create(role)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if useShortOutput := c.OutputFormat == "name"; useShortOutput || len(c.OutputFormat) == 0 {
|
||||
cmdutil.PrintSuccess(c.Mapper, useShortOutput, c.Out, "roles", c.Name, c.DryRun, "created")
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.PrintObject(role)
|
||||
}
|
||||
|
||||
func arrayContains(s []string, e string) bool {
|
||||
for _, a := range s {
|
||||
if a == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func generateResourcePolicyRules(mapper meta.RESTMapper, verbs []string, resources []ResourceOptions, resourceNames []string, nonResourceURLs []string) ([]rbac.PolicyRule, error) {
|
||||
// groupResourceMapping is a apigroup-resource map. The key of this map is api group, while the value
|
||||
// is a string array of resources under this api group.
|
||||
// E.g. groupResourceMapping = {"extensions": ["replicasets", "deployments"], "batch":["jobs"]}
|
||||
groupResourceMapping := map[string][]string{}
|
||||
|
||||
// This loop does the following work:
|
||||
// 1. Constructs groupResourceMapping based on input resources.
|
||||
// 2. Prevents pointing to non-existent resources.
|
||||
// 3. Transfers resource short name to long name. E.g. rs.extensions is transferred to replicasets.extensions
|
||||
for _, r := range resources {
|
||||
resource := schema.GroupVersionResource{Resource: r.Resource, Group: r.Group}
|
||||
groupVersionResource, err := mapper.ResourceFor(schema.GroupVersionResource{Resource: r.Resource, Group: r.Group})
|
||||
if err == nil {
|
||||
resource = groupVersionResource
|
||||
}
|
||||
|
||||
if len(r.SubResource) > 0 {
|
||||
resource.Resource = resource.Resource + "/" + r.SubResource
|
||||
}
|
||||
if !arrayContains(groupResourceMapping[resource.Group], resource.Resource) {
|
||||
groupResourceMapping[resource.Group] = append(groupResourceMapping[resource.Group], resource.Resource)
|
||||
}
|
||||
}
|
||||
|
||||
// Create separate rule for each of the api group.
|
||||
rules := []rbac.PolicyRule{}
|
||||
for _, g := range sets.StringKeySet(groupResourceMapping).List() {
|
||||
rule := rbac.PolicyRule{}
|
||||
rule.Verbs = verbs
|
||||
rule.Resources = groupResourceMapping[g]
|
||||
rule.APIGroups = []string{g}
|
||||
rule.ResourceNames = resourceNames
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
|
||||
if len(nonResourceURLs) > 0 {
|
||||
rule := rbac.PolicyRule{}
|
||||
rule.Verbs = verbs
|
||||
rule.NonResourceURLs = nonResourceURLs
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
|
||||
return rules, nil
|
||||
}
|
||||
524
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_role_test.go
generated
vendored
Normal file
524
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_role_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,524 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/rest/fake"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
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.Namespace = "test"
|
||||
tf.Client = &fake.RESTClient{}
|
||||
tf.ClientConfig = defaultClientConfig()
|
||||
|
||||
tests := map[string]struct {
|
||||
verbs string
|
||||
resources string
|
||||
resourceNames string
|
||||
expectedRole *rbac.Role
|
||||
}{
|
||||
"test-duplicate-resources": {
|
||||
verbs: "get,watch,list",
|
||||
resources: "pods,pods",
|
||||
expectedRole: &rbac.Role{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: roleName,
|
||||
},
|
||||
Rules: []rbac.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get", "watch", "list"},
|
||||
Resources: []string{"pods"},
|
||||
APIGroups: []string{""},
|
||||
ResourceNames: []string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"test-subresources": {
|
||||
verbs: "get,watch,list",
|
||||
resources: "replicasets/scale",
|
||||
expectedRole: &rbac.Role{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: roleName,
|
||||
},
|
||||
Rules: []rbac.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get", "watch", "list"},
|
||||
Resources: []string{"replicasets/scale"},
|
||||
APIGroups: []string{"extensions"},
|
||||
ResourceNames: []string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"test-subresources-with-apigroup": {
|
||||
verbs: "get,watch,list",
|
||||
resources: "replicasets.extensions/scale",
|
||||
expectedRole: &rbac.Role{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: roleName,
|
||||
},
|
||||
Rules: []rbac.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get", "watch", "list"},
|
||||
Resources: []string{"replicasets/scale"},
|
||||
APIGroups: []string{"extensions"},
|
||||
ResourceNames: []string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"test-valid-case-with-multiple-apigroups": {
|
||||
verbs: "get,watch,list",
|
||||
resources: "pods,deployments.extensions",
|
||||
expectedRole: &rbac.Role{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: roleName,
|
||||
},
|
||||
Rules: []rbac.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get", "watch", "list"},
|
||||
Resources: []string{"pods"},
|
||||
APIGroups: []string{""},
|
||||
ResourceNames: []string{},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get", "watch", "list"},
|
||||
Resources: []string{"deployments"},
|
||||
APIGroups: []string{"extensions"},
|
||||
ResourceNames: []string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdCreateRole(f, buf)
|
||||
cmd.Flags().Set("dry-run", "true")
|
||||
cmd.Flags().Set("output", "object")
|
||||
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:\nexpected:\n%#v\nsaw:\n%#v", name, test.expectedRole, printer.CachedRole)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
f, tf, _, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Namespace = "test"
|
||||
|
||||
tests := map[string]struct {
|
||||
roleOptions *CreateRoleOptions
|
||||
expectErr bool
|
||||
}{
|
||||
"test-missing-name": {
|
||||
roleOptions: &CreateRoleOptions{},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-missing-verb": {
|
||||
roleOptions: &CreateRoleOptions{
|
||||
Name: "my-role",
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-missing-resource": {
|
||||
roleOptions: &CreateRoleOptions{
|
||||
Name: "my-role",
|
||||
Verbs: []string{"get"},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-missing-resource-existing-apigroup": {
|
||||
roleOptions: &CreateRoleOptions{
|
||||
Name: "my-role",
|
||||
Verbs: []string{"get"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Group: "extensions",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-missing-resource-existing-subresource": {
|
||||
roleOptions: &CreateRoleOptions{
|
||||
Name: "my-role",
|
||||
Verbs: []string{"get"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
SubResource: "scale",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-invalid-verb": {
|
||||
roleOptions: &CreateRoleOptions{
|
||||
Name: "my-role",
|
||||
Verbs: []string{"invalid-verb"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Resource: "pods",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-nonresource-verb": {
|
||||
roleOptions: &CreateRoleOptions{
|
||||
Name: "my-role",
|
||||
Verbs: []string{"post"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Resource: "pods",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-special-verb": {
|
||||
roleOptions: &CreateRoleOptions{
|
||||
Name: "my-role",
|
||||
Verbs: []string{"use"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Resource: "pods",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-mix-verbs": {
|
||||
roleOptions: &CreateRoleOptions{
|
||||
Name: "my-role",
|
||||
Verbs: []string{"impersonate", "use"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Resource: "userextras",
|
||||
SubResource: "scopes",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-special-verb-with-wrong-apigroup": {
|
||||
roleOptions: &CreateRoleOptions{
|
||||
Name: "my-role",
|
||||
Verbs: []string{"impersonate"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Resource: "userextras",
|
||||
SubResource: "scopes",
|
||||
Group: "extensions",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-invalid-resource": {
|
||||
roleOptions: &CreateRoleOptions{
|
||||
Name: "my-role",
|
||||
Verbs: []string{"get"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Resource: "invalid-resource",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-resource-name-with-multiple-resources": {
|
||||
roleOptions: &CreateRoleOptions{
|
||||
Name: "my-role",
|
||||
Verbs: []string{"get"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Resource: "pods",
|
||||
},
|
||||
{
|
||||
Resource: "deployments",
|
||||
Group: "extensions",
|
||||
},
|
||||
},
|
||||
ResourceNames: []string{"foo"},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
"test-valid-case": {
|
||||
roleOptions: &CreateRoleOptions{
|
||||
Name: "role-binder",
|
||||
Verbs: []string{"get", "list", "bind"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Resource: "roles",
|
||||
Group: "rbac.authorization.k8s.io",
|
||||
},
|
||||
},
|
||||
ResourceNames: []string{"foo"},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
"test-valid-case-with-subresource": {
|
||||
roleOptions: &CreateRoleOptions{
|
||||
Name: "my-role",
|
||||
Verbs: []string{"get", "list"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Resource: "replicasets",
|
||||
SubResource: "scale",
|
||||
},
|
||||
},
|
||||
ResourceNames: []string{"bar"},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
"test-valid-case-with-additional-resource": {
|
||||
roleOptions: &CreateRoleOptions{
|
||||
Name: "my-role",
|
||||
Verbs: []string{"impersonate"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Resource: "userextras",
|
||||
SubResource: "scopes",
|
||||
Group: "authentication.k8s.io",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
test.roleOptions.Mapper, _ = f.Object()
|
||||
err := test.roleOptions.Validate()
|
||||
if test.expectErr && err == nil {
|
||||
t.Errorf("%s: expect error happens but validate passes.", name)
|
||||
}
|
||||
if !test.expectErr && err != nil {
|
||||
t.Errorf("%s: unexpected error: %v", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestComplete(t *testing.T) {
|
||||
roleName := "my-role"
|
||||
|
||||
f, tf, _, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Namespace = "test"
|
||||
tf.Client = &fake.RESTClient{}
|
||||
tf.ClientConfig = defaultClientConfig()
|
||||
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdCreateRole(f, buf)
|
||||
cmd.Flags().Set("resource", "pods,deployments.extensions")
|
||||
|
||||
tests := map[string]struct {
|
||||
params []string
|
||||
roleOptions *CreateRoleOptions
|
||||
expected *CreateRoleOptions
|
||||
expectErr bool
|
||||
}{
|
||||
"test-missing-name": {
|
||||
params: []string{},
|
||||
roleOptions: &CreateRoleOptions{},
|
||||
expectErr: true,
|
||||
},
|
||||
"test-duplicate-verbs": {
|
||||
params: []string{roleName},
|
||||
roleOptions: &CreateRoleOptions{
|
||||
Name: roleName,
|
||||
Verbs: []string{
|
||||
"get",
|
||||
"watch",
|
||||
"list",
|
||||
"get",
|
||||
},
|
||||
},
|
||||
expected: &CreateRoleOptions{
|
||||
Name: roleName,
|
||||
Verbs: []string{
|
||||
"get",
|
||||
"watch",
|
||||
"list",
|
||||
},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Resource: "pods",
|
||||
Group: "",
|
||||
},
|
||||
{
|
||||
Resource: "deployments",
|
||||
Group: "extensions",
|
||||
},
|
||||
},
|
||||
ResourceNames: []string{},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
"test-verball": {
|
||||
params: []string{roleName},
|
||||
roleOptions: &CreateRoleOptions{
|
||||
Name: roleName,
|
||||
Verbs: []string{
|
||||
"get",
|
||||
"watch",
|
||||
"list",
|
||||
"*",
|
||||
},
|
||||
},
|
||||
expected: &CreateRoleOptions{
|
||||
Name: roleName,
|
||||
Verbs: []string{"*"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Resource: "pods",
|
||||
Group: "",
|
||||
},
|
||||
{
|
||||
Resource: "deployments",
|
||||
Group: "extensions",
|
||||
},
|
||||
},
|
||||
ResourceNames: []string{},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
"test-duplicate-resourcenames": {
|
||||
params: []string{roleName},
|
||||
roleOptions: &CreateRoleOptions{
|
||||
Name: roleName,
|
||||
Verbs: []string{"*"},
|
||||
ResourceNames: []string{"foo", "foo"},
|
||||
},
|
||||
expected: &CreateRoleOptions{
|
||||
Name: roleName,
|
||||
Verbs: []string{"*"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Resource: "pods",
|
||||
Group: "",
|
||||
},
|
||||
{
|
||||
Resource: "deployments",
|
||||
Group: "extensions",
|
||||
},
|
||||
},
|
||||
ResourceNames: []string{"foo"},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
"test-valid-complete-case": {
|
||||
params: []string{roleName},
|
||||
roleOptions: &CreateRoleOptions{
|
||||
Name: roleName,
|
||||
Verbs: []string{"*"},
|
||||
ResourceNames: []string{"foo"},
|
||||
},
|
||||
expected: &CreateRoleOptions{
|
||||
Name: roleName,
|
||||
Verbs: []string{"*"},
|
||||
Resources: []ResourceOptions{
|
||||
{
|
||||
Resource: "pods",
|
||||
Group: "",
|
||||
},
|
||||
{
|
||||
Resource: "deployments",
|
||||
Group: "extensions",
|
||||
},
|
||||
},
|
||||
ResourceNames: []string{"foo"},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
err := test.roleOptions.Complete(f, cmd, test.params)
|
||||
if !test.expectErr && err != nil {
|
||||
t.Errorf("%s: unexpected error: %v", name, err)
|
||||
}
|
||||
|
||||
if test.expectErr {
|
||||
if err != nil {
|
||||
continue
|
||||
} else {
|
||||
t.Errorf("%s: expect error happens but test passes.", name)
|
||||
}
|
||||
}
|
||||
|
||||
if test.roleOptions.Name != test.expected.Name {
|
||||
t.Errorf("%s:\nexpected name:\n%#v\nsaw name:\n%#v", name, test.expected.Name, test.roleOptions.Name)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(test.roleOptions.Verbs, test.expected.Verbs) {
|
||||
t.Errorf("%s:\nexpected verbs:\n%#v\nsaw verbs:\n%#v", name, test.expected.Verbs, test.roleOptions.Verbs)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(test.roleOptions.Resources, test.expected.Resources) {
|
||||
t.Errorf("%s:\nexpected resources:\n%#v\nsaw resources:\n%#v", name, test.expected.Resources, test.roleOptions.Resources)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(test.roleOptions.ResourceNames, test.expected.ResourceNames) {
|
||||
t.Errorf("%s:\nexpected resource names:\n%#v\nsaw resource names:\n%#v", name, test.expected.ResourceNames, test.roleOptions.ResourceNames)
|
||||
}
|
||||
}
|
||||
}
|
||||
88
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_rolebinding.go
generated
vendored
Normal file
88
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_rolebinding.go
generated
vendored
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
Copyright 2016 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 (
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
var (
|
||||
roleBindingLong = templates.LongDesc(i18n.T(`
|
||||
Create a RoleBinding for a particular Role or ClusterRole.`))
|
||||
|
||||
roleBindingExample = templates.Examples(i18n.T(`
|
||||
# Create a RoleBinding for user1, user2, and group1 using the admin ClusterRole
|
||||
kubectl create rolebinding admin --clusterrole=admin --user=user1 --user=user2 --group=group1`))
|
||||
)
|
||||
|
||||
// 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]",
|
||||
Short: i18n.T("Create a RoleBinding for a particular Role or ClusterRole"),
|
||||
Long: roleBindingLong,
|
||||
Example: roleBindingExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := CreateRoleBinding(f, cmdOut, cmd, args)
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
cmdutil.AddApplyAnnotationFlags(cmd)
|
||||
cmdutil.AddValidateFlags(cmd)
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
cmdutil.AddGeneratorFlags(cmd, cmdutil.RoleBindingV1GeneratorName)
|
||||
cmd.Flags().String("clusterrole", "", i18n.T("ClusterRole this RoleBinding should reference"))
|
||||
cmd.Flags().String("role", "", i18n.T("Role this RoleBinding should reference"))
|
||||
cmd.Flags().StringArray("user", []string{}, "Usernames to bind to the role")
|
||||
cmd.Flags().StringArray("group", []string{}, "Groups to bind to the role")
|
||||
cmd.Flags().StringArray("serviceaccount", []string{}, "Service accounts to bind to the role, in the format <namespace>:<name>")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func CreateRoleBinding(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error {
|
||||
name, err := NameFromCommandArgs(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var generator kubectl.StructuredGenerator
|
||||
switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName {
|
||||
case cmdutil.RoleBindingV1GeneratorName:
|
||||
generator = &kubectl.RoleBindingGeneratorV1{
|
||||
Name: name,
|
||||
ClusterRole: cmdutil.GetFlagString(cmd, "clusterrole"),
|
||||
Role: cmdutil.GetFlagString(cmd, "role"),
|
||||
Users: cmdutil.GetFlagStringArray(cmd, "user"),
|
||||
Groups: cmdutil.GetFlagStringArray(cmd, "group"),
|
||||
ServiceAccounts: cmdutil.GetFlagStringArray(cmd, "serviceaccount"),
|
||||
}
|
||||
default:
|
||||
return errUnsupportedGenerator(cmd, generatorName)
|
||||
}
|
||||
return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
|
||||
Name: name,
|
||||
StructuredGenerator: generator,
|
||||
DryRun: cmdutil.GetDryRunFlag(cmd),
|
||||
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
|
||||
})
|
||||
}
|
||||
140
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_rolebinding_test.go
generated
vendored
Normal file
140
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_rolebinding_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/rest/fake"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||
)
|
||||
|
||||
var groupVersion = schema.GroupVersion{Group: "rbac.authorization.k8s.io", Version: "v1alpha1"}
|
||||
|
||||
func TestCreateRoleBinding(t *testing.T) {
|
||||
expectBinding := &rbac.RoleBinding{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "fake-binding",
|
||||
},
|
||||
RoleRef: rbac.RoleRef{
|
||||
APIGroup: rbac.GroupName,
|
||||
Kind: "Role",
|
||||
Name: "fake-role",
|
||||
},
|
||||
Subjects: []rbac.Subject{
|
||||
{
|
||||
Kind: rbac.UserKind,
|
||||
APIGroup: "rbac.authorization.k8s.io",
|
||||
Name: "fake-user",
|
||||
},
|
||||
{
|
||||
Kind: rbac.GroupKind,
|
||||
APIGroup: "rbac.authorization.k8s.io",
|
||||
Name: "fake-group",
|
||||
},
|
||||
{
|
||||
Kind: rbac.ServiceAccountKind,
|
||||
Namespace: "fake-namespace",
|
||||
Name: "fake-account",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
f, tf, _, ns := cmdtesting.NewAPIFactory()
|
||||
|
||||
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{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: ns,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/rolebindings" && m == "POST":
|
||||
bodyBits, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("TestCreateRoleBinding error: %v", err)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if obj, _, err := decoder.Decode(bodyBits, nil, &rbac.RoleBinding{}); err == nil {
|
||||
if !reflect.DeepEqual(obj.(*rbac.RoleBinding), expectBinding) {
|
||||
t.Fatalf("TestCreateRoleBinding: expected:\n%#v\nsaw:\n%#v", expectBinding, obj.(*rbac.RoleBinding))
|
||||
return nil, nil
|
||||
}
|
||||
} else {
|
||||
t.Fatalf("TestCreateRoleBinding error, could not decode the request body into rbac.RoleBinding object: %v", err)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
responseBinding := &rbac.RoleBinding{}
|
||||
responseBinding.Name = "fake-binding"
|
||||
return &http.Response{StatusCode: 201, Header: defaultHeader(), Body: ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(encoder, responseBinding))))}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdCreateRoleBinding(f, buf)
|
||||
cmd.Flags().Set("role", "fake-role")
|
||||
cmd.Flags().Set("user", "fake-user")
|
||||
cmd.Flags().Set("group", "fake-group")
|
||||
cmd.Flags().Set("serviceaccount", "fake-namespace:fake-account")
|
||||
cmd.Run(cmd, []string{"fake-binding"})
|
||||
}
|
||||
|
||||
type RoleBindingRESTClient struct {
|
||||
*fake.RESTClient
|
||||
}
|
||||
|
||||
func (c *RoleBindingRESTClient) Post() *restclient.Request {
|
||||
config := restclient.ContentConfig{
|
||||
ContentType: runtime.ContentTypeJSON,
|
||||
GroupVersion: &groupVersion,
|
||||
NegotiatedSerializer: c.NegotiatedSerializer,
|
||||
}
|
||||
|
||||
info, _ := runtime.SerializerInfoForMediaType(c.NegotiatedSerializer.SupportedMediaTypes(), runtime.ContentTypeJSON)
|
||||
serializers := restclient.Serializers{
|
||||
Encoder: c.NegotiatedSerializer.EncoderForVersion(info.Serializer, groupVersion),
|
||||
Decoder: c.NegotiatedSerializer.DecoderToVersion(info.Serializer, groupVersion),
|
||||
}
|
||||
if info.StreamSerializer != nil {
|
||||
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)
|
||||
}
|
||||
270
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_secret.go
generated
vendored
Normal file
270
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_secret.go
generated
vendored
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
Copyright 2015 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 (
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
// NewCmdCreateSecret groups subcommands to create various types of secrets
|
||||
func NewCmdCreateSecret(f cmdutil.Factory, cmdOut, errOut io.Writer) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "secret",
|
||||
Short: i18n.T("Create a secret using specified subcommand"),
|
||||
Long: "Create a secret using specified subcommand.",
|
||||
Run: cmdutil.DefaultSubCommandRun(errOut),
|
||||
}
|
||||
cmd.AddCommand(NewCmdCreateSecretDockerRegistry(f, cmdOut))
|
||||
cmd.AddCommand(NewCmdCreateSecretTLS(f, cmdOut))
|
||||
cmd.AddCommand(NewCmdCreateSecretGeneric(f, cmdOut))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
var (
|
||||
secretLong = templates.LongDesc(i18n.T(`
|
||||
Create a secret based on a file, directory, or specified literal value.
|
||||
|
||||
A single secret may package one or more key/value pairs.
|
||||
|
||||
When creating a secret based on a file, the key will default to the basename of the file, and the value will
|
||||
default to the file content. If the basename is an invalid key, you may specify an alternate key.
|
||||
|
||||
When creating a secret based on a directory, each file whose basename is a valid key in the directory will be
|
||||
packaged into the secret. Any directory entries except regular files are ignored (e.g. subdirectories,
|
||||
symlinks, devices, pipes, etc).`))
|
||||
|
||||
secretExample = templates.Examples(i18n.T(`
|
||||
# Create a new secret named my-secret with keys for each file in folder bar
|
||||
kubectl create secret generic my-secret --from-file=path/to/bar
|
||||
|
||||
# Create a new secret named my-secret with specified keys instead of names on disk
|
||||
kubectl create secret generic my-secret --from-file=ssh-privatekey=~/.ssh/id_rsa --from-file=ssh-publickey=~/.ssh/id_rsa.pub
|
||||
|
||||
# Create a new secret named my-secret with key1=supersecret and key2=topsecret
|
||||
kubectl create secret generic my-secret --from-literal=key1=supersecret --from-literal=key2=topsecret
|
||||
|
||||
# Create a new secret named my-secret from an env file
|
||||
kubectl create secret generic my-secret --from-env-file=path/to/bar.env`))
|
||||
)
|
||||
|
||||
// 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]",
|
||||
Short: i18n.T("Create a secret from a local file, directory or literal value"),
|
||||
Long: secretLong,
|
||||
Example: secretExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := CreateSecretGeneric(f, cmdOut, cmd, args)
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
cmdutil.AddApplyAnnotationFlags(cmd)
|
||||
cmdutil.AddValidateFlags(cmd)
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
cmdutil.AddGeneratorFlags(cmd, cmdutil.SecretV1GeneratorName)
|
||||
cmd.Flags().StringSlice("from-file", []string{}, "Key files can be specified using their file path, in which case a default name will be given to them, or optionally with a name and file path, in which case the given name will be used. Specifying a directory will iterate each named file in the directory that is a valid secret key.")
|
||||
cmd.Flags().StringArray("from-literal", []string{}, "Specify a key and literal value to insert in secret (i.e. mykey=somevalue)")
|
||||
cmd.Flags().String("from-env-file", "", "Specify the path to a file to read lines of key=val pairs to create a secret (i.e. a Docker .env file).")
|
||||
cmd.Flags().String("type", "", i18n.T("The type of secret to create"))
|
||||
cmd.Flags().Bool("append-hash", false, "Append a hash of the secret to its name.")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// CreateSecretGeneric is the implementation of the create secret generic command
|
||||
func CreateSecretGeneric(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error {
|
||||
name, err := NameFromCommandArgs(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var generator kubectl.StructuredGenerator
|
||||
switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName {
|
||||
case cmdutil.SecretV1GeneratorName:
|
||||
generator = &kubectl.SecretGeneratorV1{
|
||||
Name: name,
|
||||
Type: cmdutil.GetFlagString(cmd, "type"),
|
||||
FileSources: cmdutil.GetFlagStringSlice(cmd, "from-file"),
|
||||
LiteralSources: cmdutil.GetFlagStringArray(cmd, "from-literal"),
|
||||
EnvFileSource: cmdutil.GetFlagString(cmd, "from-env-file"),
|
||||
AppendHash: cmdutil.GetFlagBool(cmd, "append-hash"),
|
||||
}
|
||||
default:
|
||||
return errUnsupportedGenerator(cmd, generatorName)
|
||||
}
|
||||
return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
|
||||
Name: name,
|
||||
StructuredGenerator: generator,
|
||||
DryRun: cmdutil.GetDryRunFlag(cmd),
|
||||
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
|
||||
})
|
||||
}
|
||||
|
||||
var (
|
||||
secretForDockerRegistryLong = templates.LongDesc(i18n.T(`
|
||||
Create a new secret for use with Docker registries.
|
||||
|
||||
Dockercfg secrets are used to authenticate against Docker registries.
|
||||
|
||||
When using the Docker command line to push images, you can authenticate to a given registry by running
|
||||
|
||||
$ docker login DOCKER_REGISTRY_SERVER --username=DOCKER_USER --password=DOCKER_PASSWORD --email=DOCKER_EMAIL'.
|
||||
|
||||
That produces a ~/.dockercfg file that is used by subsequent 'docker push' and 'docker pull' commands to
|
||||
authenticate to the registry. The email address is optional.
|
||||
|
||||
When creating applications, you may have a Docker registry that requires authentication. In order for the
|
||||
nodes to pull images on your behalf, they have to have the credentials. You can provide this information
|
||||
by creating a dockercfg secret and attaching it to your service account.`))
|
||||
|
||||
secretForDockerRegistryExample = templates.Examples(i18n.T(`
|
||||
# If you don't already have a .dockercfg file, you can create a dockercfg secret directly by using:
|
||||
kubectl create secret docker-registry my-secret --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL`))
|
||||
)
|
||||
|
||||
// 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]",
|
||||
Short: i18n.T("Create a secret for use with a Docker registry"),
|
||||
Long: secretForDockerRegistryLong,
|
||||
Example: secretForDockerRegistryExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := CreateSecretDockerRegistry(f, cmdOut, cmd, args)
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
cmdutil.AddApplyAnnotationFlags(cmd)
|
||||
cmdutil.AddValidateFlags(cmd)
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
cmdutil.AddGeneratorFlags(cmd, cmdutil.SecretForDockerRegistryV1GeneratorName)
|
||||
cmd.Flags().String("docker-username", "", i18n.T("Username for Docker registry authentication"))
|
||||
cmd.MarkFlagRequired("docker-username")
|
||||
cmd.Flags().String("docker-password", "", i18n.T("Password for Docker registry authentication"))
|
||||
cmd.MarkFlagRequired("docker-password")
|
||||
cmd.Flags().String("docker-email", "", i18n.T("Email for Docker registry"))
|
||||
cmd.Flags().String("docker-server", "https://index.docker.io/v1/", i18n.T("Server location for Docker registry"))
|
||||
cmd.Flags().Bool("append-hash", false, "Append a hash of the secret to its name.")
|
||||
cmdutil.AddInclude3rdPartyFlags(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// CreateSecretDockerRegistry is the implementation of the create secret docker-registry command
|
||||
func CreateSecretDockerRegistry(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error {
|
||||
name, err := NameFromCommandArgs(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
requiredFlags := []string{"docker-username", "docker-password", "docker-email", "docker-server"}
|
||||
for _, requiredFlag := range requiredFlags {
|
||||
if value := cmdutil.GetFlagString(cmd, requiredFlag); len(value) == 0 {
|
||||
return cmdutil.UsageErrorf(cmd, "flag %s is required", requiredFlag)
|
||||
}
|
||||
}
|
||||
var generator kubectl.StructuredGenerator
|
||||
switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName {
|
||||
case cmdutil.SecretForDockerRegistryV1GeneratorName:
|
||||
generator = &kubectl.SecretForDockerRegistryGeneratorV1{
|
||||
Name: name,
|
||||
Username: cmdutil.GetFlagString(cmd, "docker-username"),
|
||||
Email: cmdutil.GetFlagString(cmd, "docker-email"),
|
||||
Password: cmdutil.GetFlagString(cmd, "docker-password"),
|
||||
Server: cmdutil.GetFlagString(cmd, "docker-server"),
|
||||
AppendHash: cmdutil.GetFlagBool(cmd, "append-hash"),
|
||||
}
|
||||
default:
|
||||
return errUnsupportedGenerator(cmd, generatorName)
|
||||
}
|
||||
return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
|
||||
Name: name,
|
||||
StructuredGenerator: generator,
|
||||
DryRun: cmdutil.GetDryRunFlag(cmd),
|
||||
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
|
||||
})
|
||||
}
|
||||
|
||||
var (
|
||||
secretForTLSLong = templates.LongDesc(i18n.T(`
|
||||
Create a TLS secret from the given public/private key pair.
|
||||
|
||||
The public/private key pair must exist before hand. The public key certificate must be .PEM encoded and match the given private key.`))
|
||||
|
||||
secretForTLSExample = templates.Examples(i18n.T(`
|
||||
# Create a new TLS secret named tls-secret with the given key pair:
|
||||
kubectl create secret tls tls-secret --cert=path/to/tls.cert --key=path/to/tls.key`))
|
||||
)
|
||||
|
||||
// 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]",
|
||||
Short: i18n.T("Create a TLS secret"),
|
||||
Long: secretForTLSLong,
|
||||
Example: secretForTLSExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := CreateSecretTLS(f, cmdOut, cmd, args)
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
cmdutil.AddApplyAnnotationFlags(cmd)
|
||||
cmdutil.AddValidateFlags(cmd)
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
cmdutil.AddGeneratorFlags(cmd, cmdutil.SecretForTLSV1GeneratorName)
|
||||
cmd.Flags().String("cert", "", i18n.T("Path to PEM encoded public key certificate."))
|
||||
cmd.Flags().String("key", "", i18n.T("Path to private key associated with given certificate."))
|
||||
cmd.Flags().Bool("append-hash", false, "Append a hash of the secret to its name.")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// CreateSecretTLS is the implementation of the create secret tls command
|
||||
func CreateSecretTLS(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error {
|
||||
name, err := NameFromCommandArgs(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
requiredFlags := []string{"cert", "key"}
|
||||
for _, requiredFlag := range requiredFlags {
|
||||
if value := cmdutil.GetFlagString(cmd, requiredFlag); len(value) == 0 {
|
||||
return cmdutil.UsageErrorf(cmd, "flag %s is required", requiredFlag)
|
||||
}
|
||||
}
|
||||
var generator kubectl.StructuredGenerator
|
||||
switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName {
|
||||
case cmdutil.SecretForTLSV1GeneratorName:
|
||||
generator = &kubectl.SecretForTLSGeneratorV1{
|
||||
Name: name,
|
||||
Key: cmdutil.GetFlagString(cmd, "key"),
|
||||
Cert: cmdutil.GetFlagString(cmd, "cert"),
|
||||
AppendHash: cmdutil.GetFlagBool(cmd, "append-hash"),
|
||||
}
|
||||
default:
|
||||
return errUnsupportedGenerator(cmd, generatorName)
|
||||
}
|
||||
return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
|
||||
Name: name,
|
||||
StructuredGenerator: generator,
|
||||
DryRun: cmdutil.GetFlagBool(cmd, "dry-run"),
|
||||
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
|
||||
})
|
||||
}
|
||||
95
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_secret_test.go
generated
vendored
Normal file
95
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_secret_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
Copyright 2014 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"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"k8s.io/client-go/rest/fake"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||
)
|
||||
|
||||
func TestCreateSecretGeneric(t *testing.T) {
|
||||
secretObject := &api.Secret{
|
||||
Data: map[string][]byte{
|
||||
"password": []byte("includes,comma"),
|
||||
"username": []byte("test_user"),
|
||||
},
|
||||
}
|
||||
secretObject.Name = "my-secret"
|
||||
f, tf, codec, ns := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Client = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: ns,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/secrets" && m == "POST":
|
||||
return &http.Response{StatusCode: 201, Header: defaultHeader(), Body: objBody(codec, secretObject)}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdCreateSecretGeneric(f, buf)
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Flags().Set("from-literal", "password=includes,comma")
|
||||
cmd.Flags().Set("from-literal", "username=test_user")
|
||||
cmd.Run(cmd, []string{secretObject.Name})
|
||||
expectedOutput := "secret/" + secretObject.Name + "\n"
|
||||
if buf.String() != expectedOutput {
|
||||
t.Errorf("expected output: %s, but got: %s", expectedOutput, buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateSecretDockerRegistry(t *testing.T) {
|
||||
secretObject := &api.Secret{}
|
||||
secretObject.Name = "my-secret"
|
||||
f, tf, codec, ns := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Client = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: ns,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/secrets" && m == "POST":
|
||||
return &http.Response{StatusCode: 201, Header: defaultHeader(), Body: objBody(codec, secretObject)}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdCreateSecretDockerRegistry(f, buf)
|
||||
cmd.Flags().Set("docker-username", "test-user")
|
||||
cmd.Flags().Set("docker-password", "test-pass")
|
||||
cmd.Flags().Set("docker-email", "test-email")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{secretObject.Name})
|
||||
expectedOutput := "secret/" + secretObject.Name + "\n"
|
||||
if buf.String() != expectedOutput {
|
||||
t.Errorf("expected output: %s, but got: %s", buf.String(), expectedOutput)
|
||||
}
|
||||
}
|
||||
286
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_service.go
generated
vendored
Normal file
286
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_service.go
generated
vendored
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
/*
|
||||
Copyright 2016 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 (
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
// NewCmdCreateService is a macro command to create a new service
|
||||
func NewCmdCreateService(f cmdutil.Factory, cmdOut, errOut io.Writer) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "service",
|
||||
Aliases: []string{"svc"},
|
||||
Short: i18n.T("Create a service using specified subcommand."),
|
||||
Long: "Create a service using specified subcommand.",
|
||||
Run: cmdutil.DefaultSubCommandRun(errOut),
|
||||
}
|
||||
cmd.AddCommand(NewCmdCreateServiceClusterIP(f, cmdOut))
|
||||
cmd.AddCommand(NewCmdCreateServiceNodePort(f, cmdOut))
|
||||
cmd.AddCommand(NewCmdCreateServiceLoadBalancer(f, cmdOut))
|
||||
cmd.AddCommand(NewCmdCreateServiceExternalName(f, cmdOut))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
var (
|
||||
serviceClusterIPLong = templates.LongDesc(i18n.T(`
|
||||
Create a clusterIP service with the specified name.`))
|
||||
|
||||
serviceClusterIPExample = templates.Examples(i18n.T(`
|
||||
# Create a new clusterIP service named my-cs
|
||||
kubectl create service clusterip my-cs --tcp=5678:8080
|
||||
|
||||
# Create a new clusterIP service named my-cs (in headless mode)
|
||||
kubectl create service clusterip my-cs --clusterip="None"`))
|
||||
)
|
||||
|
||||
func addPortFlags(cmd *cobra.Command) {
|
||||
cmd.Flags().StringSlice("tcp", []string{}, "Port pairs can be specified as '<port>:<targetPort>'.")
|
||||
}
|
||||
|
||||
// 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]",
|
||||
Short: i18n.T("Create a clusterIP service."),
|
||||
Long: serviceClusterIPLong,
|
||||
Example: serviceClusterIPExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := CreateServiceClusterIP(f, cmdOut, cmd, args)
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
cmdutil.AddApplyAnnotationFlags(cmd)
|
||||
cmdutil.AddValidateFlags(cmd)
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
cmdutil.AddGeneratorFlags(cmd, cmdutil.ServiceClusterIPGeneratorV1Name)
|
||||
addPortFlags(cmd)
|
||||
cmd.Flags().String("clusterip", "", i18n.T("Assign your own ClusterIP or set to 'None' for a 'headless' service (no loadbalancing)."))
|
||||
return cmd
|
||||
}
|
||||
|
||||
func errUnsupportedGenerator(cmd *cobra.Command, generatorName string) error {
|
||||
return cmdutil.UsageErrorf(cmd, "Generator %s not supported. ", generatorName)
|
||||
}
|
||||
|
||||
// CreateServiceClusterIP implements the behavior to run the create service clusterIP command
|
||||
func CreateServiceClusterIP(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error {
|
||||
name, err := NameFromCommandArgs(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var generator kubectl.StructuredGenerator
|
||||
switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName {
|
||||
case cmdutil.ServiceClusterIPGeneratorV1Name:
|
||||
generator = &kubectl.ServiceCommonGeneratorV1{
|
||||
Name: name,
|
||||
TCP: cmdutil.GetFlagStringSlice(cmd, "tcp"),
|
||||
Type: api.ServiceTypeClusterIP,
|
||||
ClusterIP: cmdutil.GetFlagString(cmd, "clusterip"),
|
||||
}
|
||||
default:
|
||||
return errUnsupportedGenerator(cmd, generatorName)
|
||||
}
|
||||
return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
|
||||
Name: name,
|
||||
StructuredGenerator: generator,
|
||||
DryRun: cmdutil.GetDryRunFlag(cmd),
|
||||
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
|
||||
})
|
||||
}
|
||||
|
||||
var (
|
||||
serviceNodePortLong = templates.LongDesc(i18n.T(`
|
||||
Create a nodeport service with the specified name.`))
|
||||
|
||||
serviceNodePortExample = templates.Examples(i18n.T(`
|
||||
# Create a new nodeport service named my-ns
|
||||
kubectl create service nodeport my-ns --tcp=5678:8080`))
|
||||
)
|
||||
|
||||
// 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]",
|
||||
Short: i18n.T("Create a NodePort service."),
|
||||
Long: serviceNodePortLong,
|
||||
Example: serviceNodePortExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := CreateServiceNodePort(f, cmdOut, cmd, args)
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
cmdutil.AddApplyAnnotationFlags(cmd)
|
||||
cmdutil.AddValidateFlags(cmd)
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
cmdutil.AddGeneratorFlags(cmd, cmdutil.ServiceNodePortGeneratorV1Name)
|
||||
cmd.Flags().Int("node-port", 0, "Port used to expose the service on each node in a cluster.")
|
||||
addPortFlags(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// CreateServiceNodePort is the implementation of the create service nodeport command
|
||||
func CreateServiceNodePort(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error {
|
||||
name, err := NameFromCommandArgs(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var generator kubectl.StructuredGenerator
|
||||
switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName {
|
||||
case cmdutil.ServiceNodePortGeneratorV1Name:
|
||||
generator = &kubectl.ServiceCommonGeneratorV1{
|
||||
Name: name,
|
||||
TCP: cmdutil.GetFlagStringSlice(cmd, "tcp"),
|
||||
Type: api.ServiceTypeNodePort,
|
||||
ClusterIP: "",
|
||||
NodePort: cmdutil.GetFlagInt(cmd, "node-port"),
|
||||
}
|
||||
default:
|
||||
return errUnsupportedGenerator(cmd, generatorName)
|
||||
}
|
||||
return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
|
||||
Name: name,
|
||||
StructuredGenerator: generator,
|
||||
DryRun: cmdutil.GetDryRunFlag(cmd),
|
||||
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
|
||||
})
|
||||
}
|
||||
|
||||
var (
|
||||
serviceLoadBalancerLong = templates.LongDesc(i18n.T(`
|
||||
Create a LoadBalancer service with the specified name.`))
|
||||
|
||||
serviceLoadBalancerExample = templates.Examples(i18n.T(`
|
||||
# Create a new LoadBalancer service named my-lbs
|
||||
kubectl create service loadbalancer my-lbs --tcp=5678:8080`))
|
||||
)
|
||||
|
||||
// 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]",
|
||||
Short: i18n.T("Create a LoadBalancer service."),
|
||||
Long: serviceLoadBalancerLong,
|
||||
Example: serviceLoadBalancerExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := CreateServiceLoadBalancer(f, cmdOut, cmd, args)
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
cmdutil.AddApplyAnnotationFlags(cmd)
|
||||
cmdutil.AddValidateFlags(cmd)
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
cmdutil.AddGeneratorFlags(cmd, cmdutil.ServiceLoadBalancerGeneratorV1Name)
|
||||
addPortFlags(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// CreateServiceLoadBalancer is the implementation of the service loadbalancer command
|
||||
func CreateServiceLoadBalancer(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error {
|
||||
name, err := NameFromCommandArgs(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var generator kubectl.StructuredGenerator
|
||||
switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName {
|
||||
case cmdutil.ServiceLoadBalancerGeneratorV1Name:
|
||||
generator = &kubectl.ServiceCommonGeneratorV1{
|
||||
Name: name,
|
||||
TCP: cmdutil.GetFlagStringSlice(cmd, "tcp"),
|
||||
Type: api.ServiceTypeLoadBalancer,
|
||||
ClusterIP: "",
|
||||
}
|
||||
default:
|
||||
return errUnsupportedGenerator(cmd, generatorName)
|
||||
}
|
||||
return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
|
||||
Name: name,
|
||||
StructuredGenerator: generator,
|
||||
DryRun: cmdutil.GetFlagBool(cmd, "dry-run"),
|
||||
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
|
||||
})
|
||||
}
|
||||
|
||||
var (
|
||||
serviceExternalNameLong = templates.LongDesc(i18n.T(`
|
||||
Create an ExternalName service with the specified name.
|
||||
|
||||
ExternalName service references to an external DNS address instead of
|
||||
only pods, which will allow application authors to reference services
|
||||
that exist off platform, on other clusters, or locally.`))
|
||||
|
||||
serviceExternalNameExample = templates.Examples(i18n.T(`
|
||||
# Create a new ExternalName service named my-ns
|
||||
kubectl create service externalname my-ns --external-name bar.com`))
|
||||
)
|
||||
|
||||
// NewCmdCreateServiceExternalName is a macro command for creating a ExternalName service
|
||||
func NewCmdCreateServiceExternalName(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "externalname NAME --external-name external.name [--dry-run]",
|
||||
Short: i18n.T("Create an ExternalName service."),
|
||||
Long: serviceExternalNameLong,
|
||||
Example: serviceExternalNameExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := CreateExternalNameService(f, cmdOut, cmd, args)
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
cmdutil.AddApplyAnnotationFlags(cmd)
|
||||
cmdutil.AddValidateFlags(cmd)
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
cmdutil.AddGeneratorFlags(cmd, cmdutil.ServiceExternalNameGeneratorV1Name)
|
||||
addPortFlags(cmd)
|
||||
cmd.Flags().String("external-name", "", i18n.T("External name of service"))
|
||||
cmd.MarkFlagRequired("external-name")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// CreateExternalNameService is the implementation of the service externalname command
|
||||
func CreateExternalNameService(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error {
|
||||
name, err := NameFromCommandArgs(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var generator kubectl.StructuredGenerator
|
||||
switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName {
|
||||
case cmdutil.ServiceExternalNameGeneratorV1Name:
|
||||
generator = &kubectl.ServiceCommonGeneratorV1{
|
||||
Name: name,
|
||||
Type: api.ServiceTypeExternalName,
|
||||
ExternalName: cmdutil.GetFlagString(cmd, "external-name"),
|
||||
ClusterIP: "",
|
||||
}
|
||||
default:
|
||||
return errUnsupportedGenerator(cmd, generatorName)
|
||||
}
|
||||
return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
|
||||
Name: name,
|
||||
StructuredGenerator: generator,
|
||||
DryRun: cmdutil.GetDryRunFlag(cmd),
|
||||
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
|
||||
})
|
||||
}
|
||||
117
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_service_test.go
generated
vendored
Normal file
117
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_service_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
Copyright 2016 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"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"k8s.io/client-go/rest/fake"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||
)
|
||||
|
||||
func TestCreateService(t *testing.T) {
|
||||
service := &api.Service{}
|
||||
service.Name = "my-service"
|
||||
f, tf, codec, negSer := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Client = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: negSer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/services" && m == "POST":
|
||||
return &http.Response{StatusCode: 201, Header: defaultHeader(), Body: objBody(codec, service)}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdCreateServiceClusterIP(f, buf)
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Flags().Set("tcp", "8080:8000")
|
||||
cmd.Run(cmd, []string{service.Name})
|
||||
expectedOutput := "service/" + service.Name + "\n"
|
||||
if buf.String() != expectedOutput {
|
||||
t.Errorf("expected output: %s, but got: %s", expectedOutput, buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateServiceNodePort(t *testing.T) {
|
||||
service := &api.Service{}
|
||||
service.Name = "my-node-port-service"
|
||||
f, tf, codec, negSer := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Client = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: negSer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/services" && m == http.MethodPost:
|
||||
return &http.Response{StatusCode: http.StatusCreated, Header: defaultHeader(), Body: objBody(codec, service)}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdCreateServiceNodePort(f, buf)
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Flags().Set("tcp", "30000:8000")
|
||||
cmd.Run(cmd, []string{service.Name})
|
||||
expectedOutput := "service/" + service.Name + "\n"
|
||||
if buf.String() != expectedOutput {
|
||||
t.Errorf("expected output: %s, but got: %s", expectedOutput, buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateServiceExternalName(t *testing.T) {
|
||||
service := &api.Service{}
|
||||
service.Name = "my-external-name-service"
|
||||
f, tf, codec, negSer := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Client = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: negSer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/services" && m == http.MethodPost:
|
||||
return &http.Response{StatusCode: http.StatusCreated, Header: defaultHeader(), Body: objBody(codec, service)}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdCreateServiceExternalName(f, buf)
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Flags().Set("external-name", "name")
|
||||
cmd.Run(cmd, []string{service.Name})
|
||||
expectedOutput := "service/" + service.Name + "\n"
|
||||
if buf.String() != expectedOutput {
|
||||
t.Errorf("expected output: %s, but got: %s", expectedOutput, buf.String())
|
||||
}
|
||||
}
|
||||
79
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_serviceaccount.go
generated
vendored
Normal file
79
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_serviceaccount.go
generated
vendored
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
Copyright 2016 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 (
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
var (
|
||||
serviceAccountLong = templates.LongDesc(i18n.T(`
|
||||
Create a service account with the specified name.`))
|
||||
|
||||
serviceAccountExample = templates.Examples(i18n.T(`
|
||||
# Create a new service account named my-service-account
|
||||
kubectl create serviceaccount my-service-account`))
|
||||
)
|
||||
|
||||
// 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,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := CreateServiceAccount(f, cmdOut, cmd, args)
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
cmdutil.AddApplyAnnotationFlags(cmd)
|
||||
cmdutil.AddValidateFlags(cmd)
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
cmdutil.AddInclude3rdPartyFlags(cmd)
|
||||
cmdutil.AddGeneratorFlags(cmd, cmdutil.ServiceAccountV1GeneratorName)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// CreateServiceAccount implements the behavior to run the create service account command
|
||||
func CreateServiceAccount(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error {
|
||||
name, err := NameFromCommandArgs(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var generator kubectl.StructuredGenerator
|
||||
switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName {
|
||||
case cmdutil.ServiceAccountV1GeneratorName:
|
||||
generator = &kubectl.ServiceAccountGeneratorV1{Name: name}
|
||||
default:
|
||||
return errUnsupportedGenerator(cmd, generatorName)
|
||||
}
|
||||
return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
|
||||
Name: name,
|
||||
StructuredGenerator: generator,
|
||||
DryRun: cmdutil.GetDryRunFlag(cmd),
|
||||
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
|
||||
})
|
||||
}
|
||||
56
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_serviceaccount_test.go
generated
vendored
Normal file
56
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_serviceaccount_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
Copyright 2016 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"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"k8s.io/client-go/rest/fake"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||
)
|
||||
|
||||
func TestCreateServiceAccount(t *testing.T) {
|
||||
serviceAccountObject := &api.ServiceAccount{}
|
||||
serviceAccountObject.Name = "my-service-account"
|
||||
f, tf, codec, ns := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Client = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: ns,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/serviceaccounts" && m == "POST":
|
||||
return &http.Response{StatusCode: 201, Header: defaultHeader(), Body: objBody(codec, serviceAccountObject)}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdCreateServiceAccount(f, buf)
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{serviceAccountObject.Name})
|
||||
expectedOutput := "serviceaccount/" + serviceAccountObject.Name + "\n"
|
||||
if buf.String() != expectedOutput {
|
||||
t.Errorf("expected output: %s, but got: %s", expectedOutput, buf.String())
|
||||
}
|
||||
}
|
||||
145
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_test.go
generated
vendored
Normal file
145
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/create_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
Copyright 2014 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"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"k8s.io/client-go/rest/fake"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||
)
|
||||
|
||||
func TestExtraArgsFail(t *testing.T) {
|
||||
initTestErrorHandler(t)
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
errBuf := bytes.NewBuffer([]byte{})
|
||||
|
||||
f, _, _, _ := cmdtesting.NewAPIFactory()
|
||||
c := NewCmdCreate(f, buf, errBuf)
|
||||
if ValidateArgs(c, []string{"rc"}) == nil {
|
||||
t.Errorf("unexpected non-error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateObject(t *testing.T) {
|
||||
initTestErrorHandler(t)
|
||||
_, _, rc := testData()
|
||||
rc.Items[0].Name = "redis-master-controller"
|
||||
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/replicationcontrollers" && m == http.MethodPost:
|
||||
return &http.Response{StatusCode: http.StatusCreated, Header: defaultHeader(), Body: objBody(codec, &rc.Items[0])}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
errBuf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := NewCmdCreate(f, buf, errBuf)
|
||||
cmd.Flags().Set("filename", "../../../examples/guestbook/legacy/redis-master-controller.yaml")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{})
|
||||
|
||||
// uses the name from the file, not the response
|
||||
if buf.String() != "replicationcontroller/redis-master-controller\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateMultipleObject(t *testing.T) {
|
||||
initTestErrorHandler(t)
|
||||
_, svc, rc := testData()
|
||||
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/services" && m == http.MethodPost:
|
||||
return &http.Response{StatusCode: http.StatusCreated, Header: defaultHeader(), Body: objBody(codec, &svc.Items[0])}, nil
|
||||
case p == "/namespaces/test/replicationcontrollers" && m == http.MethodPost:
|
||||
return &http.Response{StatusCode: http.StatusCreated, Header: defaultHeader(), Body: objBody(codec, &rc.Items[0])}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
errBuf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := NewCmdCreate(f, 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")
|
||||
cmd.Run(cmd, []string{})
|
||||
|
||||
// Names should come from the REST response, NOT the files
|
||||
if buf.String() != "replicationcontroller/rc1\nservice/baz\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateDirectory(t *testing.T) {
|
||||
initTestErrorHandler(t)
|
||||
_, _, rc := testData()
|
||||
rc.Items[0].Name = "name"
|
||||
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/replicationcontrollers" && m == http.MethodPost:
|
||||
return &http.Response{StatusCode: http.StatusCreated, Header: defaultHeader(), Body: objBody(codec, &rc.Items[0])}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
errBuf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := NewCmdCreate(f, buf, errBuf)
|
||||
cmd.Flags().Set("filename", "../../../examples/guestbook/legacy")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{})
|
||||
|
||||
if buf.String() != "replicationcontroller/name\nreplicationcontroller/name\nreplicationcontroller/name\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
357
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/delete.go
generated
vendored
Normal file
357
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/delete.go
generated
vendored
Normal file
|
|
@ -0,0 +1,357 @@
|
|||
/*
|
||||
Copyright 2014 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"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"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"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
)
|
||||
|
||||
var (
|
||||
delete_long = templates.LongDesc(i18n.T(`
|
||||
Delete resources by filenames, stdin, resources and names, or by resources and label selector.
|
||||
|
||||
JSON and YAML formats are accepted. Only one type of the arguments may be specified: filenames,
|
||||
resources and names, or resources and label selector.
|
||||
|
||||
Some resources, such as pods, support graceful deletion. These resources define a default period
|
||||
before they are forcibly terminated (the grace period) but you may override that value with
|
||||
the --grace-period flag, or pass --now to set a grace-period of 1. Because these resources often
|
||||
represent entities in the cluster, deletion may not be acknowledged immediately. If the node
|
||||
hosting a pod is down or cannot reach the API server, termination may take significantly longer
|
||||
than the grace period. To force delete a resource, you must pass a grace period of 0 and specify
|
||||
the --force flag.
|
||||
|
||||
IMPORTANT: Force deleting pods does not wait for confirmation that the pod's processes have been
|
||||
terminated, which can leave those processes running until the node detects the deletion and
|
||||
completes graceful deletion. If your processes use shared storage or talk to a remote API and
|
||||
depend on the name of the pod to identify themselves, force deleting those pods may result in
|
||||
multiple processes running on different machines using the same identification which may lead
|
||||
to data corruption or inconsistency. Only force delete pods when you are sure the pod is
|
||||
terminated, or if your application can tolerate multiple copies of the same pod running at once.
|
||||
Also, if you force delete pods the scheduler may place new pods on those nodes before the node
|
||||
has released those resources and causing those pods to be evicted immediately.
|
||||
|
||||
Note that the delete command does NOT do resource version checks, so if someone submits an
|
||||
update to a resource right when you submit a delete, their update will be lost along with the
|
||||
rest of the resource.`))
|
||||
|
||||
delete_example = templates.Examples(i18n.T(`
|
||||
# Delete a pod using the type and name specified in pod.json.
|
||||
kubectl delete -f ./pod.json
|
||||
|
||||
# Delete a pod based on the type and name in the JSON passed into stdin.
|
||||
cat pod.json | kubectl delete -f -
|
||||
|
||||
# Delete pods and services with same names "baz" and "foo"
|
||||
kubectl delete pod,service baz foo
|
||||
|
||||
# Delete pods and services with label name=myLabel.
|
||||
kubectl delete pods,services -l name=myLabel
|
||||
|
||||
# Delete a pod with minimal delay
|
||||
kubectl delete pod foo --now
|
||||
|
||||
# Force delete a pod on a dead node
|
||||
kubectl delete pod foo --grace-period=0 --force
|
||||
|
||||
# Delete all pods
|
||||
kubectl delete pods --all`))
|
||||
)
|
||||
|
||||
type DeleteOptions struct {
|
||||
resource.FilenameOptions
|
||||
|
||||
Selector string
|
||||
DeleteAll bool
|
||||
IgnoreNotFound bool
|
||||
Cascade bool
|
||||
DeleteNow bool
|
||||
ForceDeletion bool
|
||||
WaitForDeletion bool
|
||||
|
||||
GracePeriod int
|
||||
Timeout time.Duration
|
||||
|
||||
Include3rdParty bool
|
||||
Output string
|
||||
|
||||
Mapper meta.RESTMapper
|
||||
Result *resource.Result
|
||||
|
||||
f cmdutil.Factory
|
||||
Out io.Writer
|
||||
ErrOut io.Writer
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "delete ([-f FILENAME] | TYPE [(NAME | -l label | --all)])",
|
||||
Short: i18n.T("Delete resources by filenames, stdin, resources and names, or by resources and label selector"),
|
||||
Long: delete_long,
|
||||
Example: delete_example,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd))
|
||||
if err := options.Complete(f, out, errOut, args, cmd); err != nil {
|
||||
cmdutil.CheckErr(err)
|
||||
}
|
||||
if err := options.Validate(cmd); err != nil {
|
||||
cmdutil.CheckErr(cmdutil.UsageErrorf(cmd, err.Error()))
|
||||
}
|
||||
if err := options.RunDelete(); err != nil {
|
||||
cmdutil.CheckErr(err)
|
||||
}
|
||||
},
|
||||
SuggestFor: []string{"rm"},
|
||||
ValidArgs: validArgs,
|
||||
ArgAliases: argAliases,
|
||||
}
|
||||
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.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.")
|
||||
cmd.Flags().BoolVar(&options.DeleteNow, "now", false, "If true, resources are signaled for immediate shutdown (same as --grace-period=1).")
|
||||
cmd.Flags().BoolVar(&options.ForceDeletion, "force", false, "Immediate deletion of some resources may result in inconsistency or data loss and requires confirmation.")
|
||||
cmd.Flags().DurationVar(&options.Timeout, "timeout", 0, "The length of time to wait before giving up on a delete, zero means determine a timeout from the size of the object")
|
||||
cmdutil.AddOutputVarFlagsForMutation(cmd, &options.Output)
|
||||
cmdutil.AddInclude3rdPartyVarFlags(cmd, &options.Include3rdParty)
|
||||
cmdutil.AddIncludeUninitializedFlag(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *DeleteOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args []string, cmd *cobra.Command) error {
|
||||
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set up client based support.
|
||||
mapper, _, err := f.UnstructuredObject()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
builder, err := f.NewUnstructuredBuilder(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.Mapper = mapper
|
||||
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
|
||||
r := builder.
|
||||
ContinueOnError().
|
||||
NamespaceParam(cmdNamespace).DefaultNamespace().
|
||||
FilenameParam(enforceNamespace, &o.FilenameOptions).
|
||||
SelectorParam(o.Selector).
|
||||
IncludeUninitialized(includeUninitialized).
|
||||
SelectAllParam(o.DeleteAll).
|
||||
ResourceTypeOrNameArgs(false, args...).RequireObject(false).
|
||||
Flatten().
|
||||
Do()
|
||||
err = r.Err()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.Result = r
|
||||
|
||||
o.f = f
|
||||
// Set up writer
|
||||
o.Out = out
|
||||
o.ErrOut = errOut
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *DeleteOptions) Validate(cmd *cobra.Command) error {
|
||||
if o.DeleteAll {
|
||||
f := cmd.Flags().Lookup("ignore-not-found")
|
||||
// The flag should never be missing
|
||||
if f == nil {
|
||||
return fmt.Errorf("missing --ignore-not-found flag")
|
||||
}
|
||||
// If the user didn't explicitly set the option, default to ignoring NotFound errors when used with --all
|
||||
if !f.Changed {
|
||||
o.IgnoreNotFound = true
|
||||
}
|
||||
}
|
||||
if o.DeleteNow {
|
||||
if o.GracePeriod != -1 {
|
||||
return fmt.Errorf("--now and --grace-period cannot be specified together")
|
||||
}
|
||||
o.GracePeriod = 1
|
||||
}
|
||||
if o.GracePeriod == 0 {
|
||||
if o.ForceDeletion {
|
||||
fmt.Fprintf(o.ErrOut, "warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.\n")
|
||||
} else {
|
||||
// To preserve backwards compatibility, but prevent accidental data loss, we convert --grace-period=0
|
||||
// into --grace-period=1 and wait until the object is successfully deleted. Users may provide --force
|
||||
// to bypass this wait.
|
||||
o.WaitForDeletion = true
|
||||
o.GracePeriod = 1
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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 DeleteResult(o.Result, o.Out, o.IgnoreNotFound, o.GracePeriod, shortOutput, o.Mapper)
|
||||
}
|
||||
|
||||
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 {
|
||||
found := 0
|
||||
if ignoreNotFound {
|
||||
r = r.IgnoreErrors(errors.IsNotFound)
|
||||
}
|
||||
err := r.Visit(func(info *resource.Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
found++
|
||||
reaper, err := f.Reaper(info.Mapping)
|
||||
if err != nil {
|
||||
// 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, out, shortOutput, mapper)
|
||||
}
|
||||
return cmdutil.AddSourceToErr("reaping", info.Source, err)
|
||||
}
|
||||
var options *metav1.DeleteOptions
|
||||
if gracePeriod >= 0 {
|
||||
options = metav1.NewDeleteOptions(int64(gracePeriod))
|
||||
}
|
||||
if err := reaper.Stop(info.Namespace, info.Name, timeout, options); err != nil {
|
||||
return cmdutil.AddSourceToErr("stopping", info.Source, err)
|
||||
}
|
||||
if waitForDeletion {
|
||||
if err := waitForObjectDeletion(info, timeout); err != nil {
|
||||
return cmdutil.AddSourceToErr("stopping", info.Source, err)
|
||||
}
|
||||
}
|
||||
if !quiet {
|
||||
cmdutil.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, false, "deleted")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if found == 0 {
|
||||
fmt.Fprintf(out, "No resources found\n")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeleteResult(r *resource.Result, out io.Writer, ignoreNotFound bool, gracePeriod int, shortOutput bool, mapper meta.RESTMapper) error {
|
||||
found := 0
|
||||
if ignoreNotFound {
|
||||
r = r.IgnoreErrors(errors.IsNotFound)
|
||||
}
|
||||
err := r.Visit(func(info *resource.Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
found++
|
||||
|
||||
// if we're here, it means that cascade=false (not the default), so we should orphan as requested
|
||||
orphan := true
|
||||
options := &metav1.DeleteOptions{}
|
||||
if gracePeriod >= 0 {
|
||||
options = metav1.NewDeleteOptions(int64(gracePeriod))
|
||||
}
|
||||
options.OrphanDependents = &orphan
|
||||
return deleteResource(info, out, shortOutput, mapper, options)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if found == 0 {
|
||||
fmt.Fprintf(out, "No resources found\n")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func cascadingDeleteResource(info *resource.Info, out io.Writer, shortOutput bool, mapper meta.RESTMapper) error {
|
||||
falseVar := false
|
||||
deleteOptions := &metav1.DeleteOptions{OrphanDependents: &falseVar}
|
||||
return deleteResource(info, out, shortOutput, mapper, deleteOptions)
|
||||
}
|
||||
|
||||
func deleteResource(info *resource.Info, out io.Writer, shortOutput bool, mapper meta.RESTMapper, 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)
|
||||
}
|
||||
cmdutil.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, false, "deleted")
|
||||
return nil
|
||||
}
|
||||
|
||||
// objectDeletionWaitInterval is the interval to wait between checks for deletion.
|
||||
var objectDeletionWaitInterval = time.Second
|
||||
|
||||
// waitForObjectDeletion refreshes the object, waiting until it is deleted, a timeout is reached, or
|
||||
// an error is encountered. It checks once a second.
|
||||
func waitForObjectDeletion(info *resource.Info, timeout time.Duration) error {
|
||||
copied := *info
|
||||
info = &copied
|
||||
// TODO: refactor Reaper so that we can pass the "wait" option into it, and then check for UID change.
|
||||
return wait.PollImmediate(objectDeletionWaitInterval, timeout, func() (bool, error) {
|
||||
switch err := info.Get(); {
|
||||
case err == nil:
|
||||
return false, nil
|
||||
case errors.IsNotFound(err):
|
||||
return true, nil
|
||||
default:
|
||||
return false, err
|
||||
}
|
||||
})
|
||||
}
|
||||
739
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/delete_test.go
generated
vendored
Normal file
739
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/delete_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,739 @@
|
|||
/*
|
||||
Copyright 2014 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"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/dynamic"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/rest/fake"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"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"
|
||||
)
|
||||
|
||||
var unstructuredSerializer = dynamic.ContentConfig().NegotiatedSerializer
|
||||
|
||||
var fakecmd = &cobra.Command{
|
||||
Use: "delete ([-f FILENAME] | TYPE [(NAME | -l label | --all)])",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd))
|
||||
},
|
||||
}
|
||||
|
||||
func TestDeleteObjectByTuple(t *testing.T) {
|
||||
initTestErrorHandler(t)
|
||||
_, _, rc := testData()
|
||||
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
|
||||
// replication controller with cascade off
|
||||
case p == "/namespaces/test/replicationcontrollers/redis-master-controller" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &rc.Items[0])}, nil
|
||||
|
||||
// secret with cascade on, but no client-side reaper
|
||||
case p == "/namespaces/test/secrets/mysecret" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &rc.Items[0])}, nil
|
||||
|
||||
default:
|
||||
// Ensures no GET is performed when deleting by name
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
|
||||
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdDelete(f, buf, errBuf)
|
||||
cmd.Flags().Set("namespace", "test")
|
||||
cmd.Flags().Set("cascade", "false")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{"replicationcontrollers/redis-master-controller"})
|
||||
if buf.String() != "replicationcontroller/redis-master-controller\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
|
||||
// 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.Flags().Set("namespace", "test")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{"secrets/mysecret"})
|
||||
if buf.String() != "secret/mysecret\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func hasExpectedOrphanDependents(body io.ReadCloser, expectedOrphanDependents *bool) bool {
|
||||
if body == nil || expectedOrphanDependents == nil {
|
||||
return body == nil && expectedOrphanDependents == nil
|
||||
}
|
||||
var parsedBody metav1.DeleteOptions
|
||||
rawBody, _ := ioutil.ReadAll(body)
|
||||
json.Unmarshal(rawBody, &parsedBody)
|
||||
if parsedBody.OrphanDependents == nil {
|
||||
return false
|
||||
}
|
||||
return *expectedOrphanDependents == *parsedBody.OrphanDependents
|
||||
}
|
||||
|
||||
// Tests that DeleteOptions.OrphanDependents is appropriately set while deleting objects.
|
||||
func TestOrphanDependentsInDeleteObject(t *testing.T) {
|
||||
initTestErrorHandler(t)
|
||||
_, _, rc := testData()
|
||||
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
var expectedOrphanDependents *bool
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m, b := req.URL.Path, req.Method, req.Body; {
|
||||
|
||||
case p == "/namespaces/test/secrets/mysecret" && m == "DELETE" && hasExpectedOrphanDependents(b, expectedOrphanDependents):
|
||||
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &rc.Items[0])}, nil
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
|
||||
// DeleteOptions.OrphanDependents should be false, when cascade is true (default).
|
||||
falseVar := false
|
||||
expectedOrphanDependents = &falseVar
|
||||
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdDelete(f, buf, errBuf)
|
||||
cmd.Flags().Set("namespace", "test")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{"secrets/mysecret"})
|
||||
if buf.String() != "secret/mysecret\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
|
||||
// Test that delete options should be set to orphan when cascade is false.
|
||||
trueVar := true
|
||||
expectedOrphanDependents = &trueVar
|
||||
buf, errBuf = bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
|
||||
cmd = NewCmdDelete(f, buf, errBuf)
|
||||
cmd.Flags().Set("namespace", "test")
|
||||
cmd.Flags().Set("cascade", "false")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{"secrets/mysecret"})
|
||||
if buf.String() != "secret/mysecret\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteNamedObject(t *testing.T) {
|
||||
initTestErrorHandler(t)
|
||||
initTestErrorHandler(t)
|
||||
_, _, rc := testData()
|
||||
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
|
||||
// replication controller with cascade off
|
||||
case p == "/namespaces/test/replicationcontrollers/redis-master-controller" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &rc.Items[0])}, nil
|
||||
|
||||
// secret with cascade on, but no client-side reaper
|
||||
case p == "/namespaces/test/secrets/mysecret" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &rc.Items[0])}, nil
|
||||
|
||||
default:
|
||||
// Ensures no GET is performed when deleting by name
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
|
||||
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdDelete(f, buf, errBuf)
|
||||
cmd.Flags().Set("namespace", "test")
|
||||
cmd.Flags().Set("cascade", "false")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{"replicationcontrollers", "redis-master-controller"})
|
||||
if buf.String() != "replicationcontroller/redis-master-controller\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
|
||||
// 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.Flags().Set("namespace", "test")
|
||||
cmd.Flags().Set("cascade", "false")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{"secrets", "mysecret"})
|
||||
if buf.String() != "secret/mysecret\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteObject(t *testing.T) {
|
||||
initTestErrorHandler(t)
|
||||
_, _, rc := testData()
|
||||
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/replicationcontrollers/redis-master" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &rc.Items[0])}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := NewCmdDelete(f, buf, errBuf)
|
||||
cmd.Flags().Set("filename", "../../../examples/guestbook/legacy/redis-master-controller.yaml")
|
||||
cmd.Flags().Set("cascade", "false")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{})
|
||||
|
||||
// uses the name from the file, not the response
|
||||
if buf.String() != "replicationcontroller/redis-master\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
type fakeReaper struct {
|
||||
namespace, name string
|
||||
timeout time.Duration
|
||||
deleteOptions *metav1.DeleteOptions
|
||||
err error
|
||||
}
|
||||
|
||||
func (r *fakeReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *metav1.DeleteOptions) error {
|
||||
r.namespace, r.name = namespace, name
|
||||
r.timeout = timeout
|
||||
r.deleteOptions = gracePeriod
|
||||
return r.err
|
||||
}
|
||||
|
||||
type fakeReaperFactory struct {
|
||||
cmdutil.Factory
|
||||
reaper kubectl.Reaper
|
||||
}
|
||||
|
||||
func (f *fakeReaperFactory) Reaper(mapping *meta.RESTMapping) (kubectl.Reaper, error) {
|
||||
return f.reaper, nil
|
||||
}
|
||||
|
||||
func TestDeleteObjectGraceZero(t *testing.T) {
|
||||
initTestErrorHandler(t)
|
||||
pods, _, _ := testData()
|
||||
|
||||
objectDeletionWaitInterval = time.Millisecond
|
||||
count := 0
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
t.Logf("got request %s %s", req.Method, req.URL.Path)
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/pods/nginx" && m == "GET":
|
||||
count++
|
||||
switch count {
|
||||
case 1, 2, 3:
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])}, nil
|
||||
default:
|
||||
return &http.Response{StatusCode: 404, Header: defaultHeader(), Body: objBody(codec, &metav1.Status{})}, nil
|
||||
}
|
||||
case p == "/api/v1/namespaces/test" && m == "GET":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &api.Namespace{})}, nil
|
||||
case p == "/namespaces/test/pods/nginx" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
|
||||
|
||||
reaper := &fakeReaper{}
|
||||
fake := &fakeReaperFactory{Factory: f, reaper: reaper}
|
||||
cmd := NewCmdDelete(fake, buf, errBuf)
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Flags().Set("grace-period", "0")
|
||||
cmd.Run(cmd, []string{"pod/nginx"})
|
||||
|
||||
// uses the name from the file, not the response
|
||||
if buf.String() != "pod/nginx\n" {
|
||||
t.Errorf("unexpected output: %s\n---\n%s", buf.String(), errBuf.String())
|
||||
}
|
||||
if reaper.deleteOptions == nil || reaper.deleteOptions.GracePeriodSeconds == nil || *reaper.deleteOptions.GracePeriodSeconds != 1 {
|
||||
t.Errorf("unexpected reaper options: %#v", reaper)
|
||||
}
|
||||
if count != 4 {
|
||||
t.Errorf("unexpected calls to GET: %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteObjectNotFound(t *testing.T) {
|
||||
initTestErrorHandler(t)
|
||||
f, tf, _, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/replicationcontrollers/redis-master" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 404, Header: defaultHeader(), Body: stringBody("")}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
|
||||
|
||||
options := &DeleteOptions{
|
||||
FilenameOptions: resource.FilenameOptions{
|
||||
Filenames: []string{"../../../examples/guestbook/legacy/redis-master-controller.yaml"},
|
||||
},
|
||||
GracePeriod: -1,
|
||||
Cascade: false,
|
||||
Output: "name",
|
||||
}
|
||||
err := options.Complete(f, buf, errBuf, []string{}, fakecmd)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
err = options.RunDelete()
|
||||
if err == nil || !errors.IsNotFound(err) {
|
||||
t.Errorf("unexpected error: expected NotFound, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteObjectIgnoreNotFound(t *testing.T) {
|
||||
initTestErrorHandler(t)
|
||||
f, tf, _, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/replicationcontrollers/redis-master" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 404, Header: defaultHeader(), Body: stringBody("")}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := NewCmdDelete(f, 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")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{})
|
||||
|
||||
if buf.String() != "" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteAllNotFound(t *testing.T) {
|
||||
initTestErrorHandler(t)
|
||||
_, svc, _ := testData()
|
||||
// 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
|
||||
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/services" && m == "GET":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, svc)}, nil
|
||||
case p == "/namespaces/test/services/foo" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 404, Header: defaultHeader(), Body: objBody(codec, notFoundError)}, nil
|
||||
case p == "/namespaces/test/services/baz" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &svc.Items[0])}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
|
||||
|
||||
// Make sure we can explicitly choose to fail on NotFound errors, even with --all
|
||||
options := &DeleteOptions{
|
||||
FilenameOptions: resource.FilenameOptions{},
|
||||
GracePeriod: -1,
|
||||
Cascade: false,
|
||||
DeleteAll: true,
|
||||
IgnoreNotFound: false,
|
||||
Output: "name",
|
||||
}
|
||||
err := options.Complete(f, buf, errBuf, []string{"services"}, fakecmd)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
err = options.RunDelete()
|
||||
if err == nil || !errors.IsNotFound(err) {
|
||||
t.Errorf("unexpected error: expected NotFound, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteAllIgnoreNotFound(t *testing.T) {
|
||||
initTestErrorHandler(t)
|
||||
_, svc, _ := testData()
|
||||
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
|
||||
// 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{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/services" && m == "GET":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, svc)}, nil
|
||||
case p == "/namespaces/test/services/foo" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 404, Header: defaultHeader(), Body: objBody(codec, notFoundError)}, nil
|
||||
case p == "/namespaces/test/services/baz" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &svc.Items[0])}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := NewCmdDelete(f, buf, errBuf)
|
||||
cmd.Flags().Set("all", "true")
|
||||
cmd.Flags().Set("cascade", "false")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{"services"})
|
||||
|
||||
if buf.String() != "service/baz\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteMultipleObject(t *testing.T) {
|
||||
initTestErrorHandler(t)
|
||||
_, svc, rc := testData()
|
||||
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/replicationcontrollers/redis-master" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &rc.Items[0])}, nil
|
||||
case p == "/namespaces/test/services/frontend" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &svc.Items[0])}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := NewCmdDelete(f, 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")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{})
|
||||
|
||||
if buf.String() != "replicationcontroller/redis-master\nservice/frontend\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteMultipleObjectContinueOnMissing(t *testing.T) {
|
||||
initTestErrorHandler(t)
|
||||
_, svc, _ := testData()
|
||||
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/replicationcontrollers/redis-master" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 404, Header: defaultHeader(), Body: stringBody("")}, nil
|
||||
case p == "/namespaces/test/services/frontend" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &svc.Items[0])}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
|
||||
|
||||
options := &DeleteOptions{
|
||||
FilenameOptions: resource.FilenameOptions{
|
||||
Filenames: []string{"../../../examples/guestbook/legacy/redis-master-controller.yaml", "../../../examples/guestbook/frontend-service.yaml"},
|
||||
},
|
||||
GracePeriod: -1,
|
||||
Cascade: false,
|
||||
Output: "name",
|
||||
}
|
||||
err := options.Complete(f, buf, errBuf, []string{}, fakecmd)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
err = options.RunDelete()
|
||||
if err == nil || !errors.IsNotFound(err) {
|
||||
t.Errorf("unexpected error: expected NotFound, got %v", err)
|
||||
}
|
||||
|
||||
if buf.String() != "service/frontend\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteMultipleResourcesWithTheSameName(t *testing.T) {
|
||||
initTestErrorHandler(t)
|
||||
_, svc, rc := testData()
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/replicationcontrollers/baz" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &rc.Items[0])}, nil
|
||||
case p == "/namespaces/test/replicationcontrollers/foo" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &rc.Items[0])}, nil
|
||||
case p == "/namespaces/test/services/baz" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &svc.Items[0])}, nil
|
||||
case p == "/namespaces/test/services/foo" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &svc.Items[0])}, nil
|
||||
default:
|
||||
// Ensures no GET is performed when deleting by name
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := NewCmdDelete(f, buf, errBuf)
|
||||
cmd.Flags().Set("namespace", "test")
|
||||
cmd.Flags().Set("cascade", "false")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{"replicationcontrollers,services", "baz", "foo"})
|
||||
if buf.String() != "replicationcontroller/baz\nreplicationcontroller/foo\nservice/baz\nservice/foo\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteDirectory(t *testing.T) {
|
||||
initTestErrorHandler(t)
|
||||
_, _, rc := testData()
|
||||
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case strings.HasPrefix(p, "/namespaces/test/replicationcontrollers/") && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &rc.Items[0])}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := NewCmdDelete(f, buf, errBuf)
|
||||
cmd.Flags().Set("filename", "../../../examples/guestbook/legacy")
|
||||
cmd.Flags().Set("cascade", "false")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{})
|
||||
|
||||
if buf.String() != "replicationcontroller/frontend\nreplicationcontroller/redis-master\nreplicationcontroller/redis-slave\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteMultipleSelector(t *testing.T) {
|
||||
initTestErrorHandler(t)
|
||||
pods, svc, _ := testData()
|
||||
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/pods" && m == "GET":
|
||||
if req.URL.Query().Get(metav1.LabelSelectorQueryParam(api.Registry.GroupOrDie(api.GroupName).GroupVersion.String())) != "a=b" {
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
}
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)}, nil
|
||||
case p == "/namespaces/test/services" && m == "GET":
|
||||
if req.URL.Query().Get(metav1.LabelSelectorQueryParam(api.Registry.GroupOrDie(api.GroupName).GroupVersion.String())) != "a=b" {
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
}
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, svc)}, nil
|
||||
case strings.HasPrefix(p, "/namespaces/test/pods/") && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])}, nil
|
||||
case strings.HasPrefix(p, "/namespaces/test/services/") && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &svc.Items[0])}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := NewCmdDelete(f, buf, errBuf)
|
||||
cmd.Flags().Set("selector", "a=b")
|
||||
cmd.Flags().Set("cascade", "false")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{"pods,services"})
|
||||
|
||||
if buf.String() != "pod/foo\npod/bar\nservice/baz\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceErrors(t *testing.T) {
|
||||
initTestErrorHandler(t)
|
||||
testCases := map[string]struct {
|
||||
args []string
|
||||
errFn func(error) bool
|
||||
}{
|
||||
"no args": {
|
||||
args: []string{},
|
||||
errFn: func(err error) bool { return strings.Contains(err.Error(), "You must provide one or more resources") },
|
||||
},
|
||||
"resources but no selectors": {
|
||||
args: []string{"pods"},
|
||||
errFn: func(err error) bool {
|
||||
return strings.Contains(err.Error(), "resource(s) were provided, but no name, label selector, or --all flag specified")
|
||||
},
|
||||
},
|
||||
"multiple resources but no selectors": {
|
||||
args: []string{"pods,deployments"},
|
||||
errFn: func(err error) bool {
|
||||
return strings.Contains(err.Error(), "resource(s) were provided, but no name, label selector, or --all flag specified")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for k, testCase := range testCases {
|
||||
f, tf, _, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Namespace = "test"
|
||||
tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &api.Registry.GroupOrDie(api.GroupName).GroupVersion}}
|
||||
|
||||
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
|
||||
|
||||
options := &DeleteOptions{
|
||||
FilenameOptions: resource.FilenameOptions{},
|
||||
GracePeriod: -1,
|
||||
Cascade: false,
|
||||
Output: "name",
|
||||
}
|
||||
err := options.Complete(f, 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()))
|
||||
}
|
||||
}
|
||||
}
|
||||
224
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/describe.go
generated
vendored
Normal file
224
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/describe.go
generated
vendored
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
/*
|
||||
Copyright 2014 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"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"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"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
)
|
||||
|
||||
var (
|
||||
describe_long = templates.LongDesc(`
|
||||
Show details of a specific resource or group of resources.
|
||||
It includes the uninitialized objects, unless --include-uninitialized=false is explicitly set.
|
||||
This command joins many API calls together to form a detailed description of a
|
||||
given resource or group of resources.
|
||||
|
||||
$ kubectl describe TYPE NAME_PREFIX
|
||||
|
||||
will first check for an exact match on TYPE and NAME_PREFIX. If no such resource
|
||||
exists, it will output details for every resource that has a name prefixed with NAME_PREFIX.
|
||||
|
||||
` + validResources)
|
||||
|
||||
describe_example = templates.Examples(i18n.T(`
|
||||
# Describe a node
|
||||
kubectl describe nodes kubernetes-node-emt8.c.myproject.internal
|
||||
|
||||
# Describe a pod
|
||||
kubectl describe pods/nginx
|
||||
|
||||
# Describe a pod identified by type and name in "pod.json"
|
||||
kubectl describe -f pod.json
|
||||
|
||||
# Describe all pods
|
||||
kubectl describe pods
|
||||
|
||||
# Describe pods by label name=myLabel
|
||||
kubectl describe po -l name=myLabel
|
||||
|
||||
# Describe all pods managed by the 'frontend' replication controller (rc-created pods
|
||||
# get the name of the rc as a prefix in the pod the name).
|
||||
kubectl describe pods frontend`))
|
||||
)
|
||||
|
||||
func NewCmdDescribe(f cmdutil.Factory, out, cmdErr io.Writer) *cobra.Command {
|
||||
options := &resource.FilenameOptions{}
|
||||
describerSettings := &printers.DescriberSettings{}
|
||||
|
||||
// TODO: this should come from the factory, and may need to be loaded from the server, and so is probably
|
||||
// going to have to be removed
|
||||
validArgs := printersinternal.DescribableResources()
|
||||
argAliases := kubectl.ResourceAliases(validArgs)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "describe (-f FILENAME | TYPE [NAME_PREFIX | -l label] | TYPE/NAME)",
|
||||
Short: i18n.T("Show details of a specific resource or group of resources"),
|
||||
Long: describe_long,
|
||||
Example: describe_example,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := RunDescribe(f, out, cmdErr, cmd, args, options, describerSettings)
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
ValidArgs: validArgs,
|
||||
ArgAliases: argAliases,
|
||||
}
|
||||
usage := "containing the resource to describe"
|
||||
cmdutil.AddFilenameOptionFlags(cmd, options, usage)
|
||||
cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
|
||||
cmd.Flags().Bool("all-namespaces", false, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.")
|
||||
cmd.Flags().BoolVar(&describerSettings.ShowEvents, "show-events", true, "If true, display events related to the described object.")
|
||||
cmdutil.AddInclude3rdPartyFlags(cmd)
|
||||
cmdutil.AddIncludeUninitializedFlag(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func RunDescribe(f cmdutil.Factory, out, cmdErr io.Writer, cmd *cobra.Command, args []string, options *resource.FilenameOptions, describerSettings *printers.DescriberSettings) error {
|
||||
selector := cmdutil.GetFlagString(cmd, "selector")
|
||||
allNamespaces := cmdutil.GetFlagBool(cmd, "all-namespaces")
|
||||
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if allNamespaces {
|
||||
enforceNamespace = false
|
||||
}
|
||||
if len(args) == 0 && cmdutil.IsFilenameSliceEmpty(options.Filenames) {
|
||||
fmt.Fprint(cmdErr, "You must specify the type of resource to describe. ", validResources)
|
||||
return cmdutil.UsageErrorf(cmd, "Required resource not specified.")
|
||||
}
|
||||
|
||||
builder, err := f.NewUnstructuredBuilder(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// include the uninitialized objects by default
|
||||
// unless user explicitly set --include-uninitialized=false
|
||||
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, true)
|
||||
|
||||
r := builder.
|
||||
ContinueOnError().
|
||||
NamespaceParam(cmdNamespace).DefaultNamespace().AllNamespaces(allNamespaces).
|
||||
FilenameParam(enforceNamespace, options).
|
||||
SelectorParam(selector).
|
||||
IncludeUninitialized(includeUninitialized).
|
||||
ResourceTypeOrNameArgs(true, args...).
|
||||
Flatten().
|
||||
Do()
|
||||
err = r.Err()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
allErrs := []error{}
|
||||
infos, err := r.Infos()
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) && len(args) == 2 {
|
||||
return DescribeMatchingResources(f, cmdNamespace, args[0], args[1], describerSettings, out, err)
|
||||
}
|
||||
allErrs = append(allErrs, err)
|
||||
}
|
||||
|
||||
errs := sets.NewString()
|
||||
first := true
|
||||
for _, info := range infos {
|
||||
mapping := info.ResourceMapping()
|
||||
describer, err := f.Describer(mapping)
|
||||
if err != nil {
|
||||
if errs.Has(err.Error()) {
|
||||
continue
|
||||
}
|
||||
allErrs = append(allErrs, err)
|
||||
errs.Insert(err.Error())
|
||||
continue
|
||||
}
|
||||
s, err := describer.Describe(info.Namespace, info.Name, *describerSettings)
|
||||
if err != nil {
|
||||
if errs.Has(err.Error()) {
|
||||
continue
|
||||
}
|
||||
allErrs = append(allErrs, err)
|
||||
errs.Insert(err.Error())
|
||||
continue
|
||||
}
|
||||
if first {
|
||||
first = false
|
||||
fmt.Fprint(out, s)
|
||||
} else {
|
||||
fmt.Fprintf(out, "\n\n%s", s)
|
||||
}
|
||||
}
|
||||
|
||||
return utilerrors.NewAggregate(allErrs)
|
||||
}
|
||||
|
||||
func DescribeMatchingResources(f cmdutil.Factory, namespace, rsrc, prefix string, describerSettings *printers.DescriberSettings, out io.Writer, originalError error) error {
|
||||
mapper, typer, err := f.UnstructuredObject()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r := resource.NewBuilder(mapper, f.CategoryExpander(), typer, resource.ClientMapperFunc(f.UnstructuredClientForMapping), unstructured.UnstructuredJSONScheme).
|
||||
NamespaceParam(namespace).DefaultNamespace().
|
||||
ResourceTypeOrNameArgs(true, rsrc).
|
||||
SingleResourceType().
|
||||
Flatten().
|
||||
Do()
|
||||
mapping, err := r.ResourceMapping()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
describer, err := f.Describer(mapping)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
infos, err := r.Infos()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
isFound := false
|
||||
for ix := range infos {
|
||||
info := infos[ix]
|
||||
if strings.HasPrefix(info.Name, prefix) {
|
||||
isFound = true
|
||||
s, err := describer.Describe(info.Namespace, info.Name, *describerSettings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(out, "%s\n", s)
|
||||
}
|
||||
}
|
||||
if !isFound {
|
||||
return originalError
|
||||
}
|
||||
return nil
|
||||
}
|
||||
177
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/describe_test.go
generated
vendored
Normal file
177
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/describe_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
Copyright 2014 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"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"k8s.io/client-go/rest/fake"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||
)
|
||||
|
||||
// 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.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, cmdtesting.NewInternalType("", "", "foo"))},
|
||||
}
|
||||
tf.Namespace = "non-default"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
buferr := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdDescribe(f, buf, buferr)
|
||||
cmd.Run(cmd, []string{"type", "foo"})
|
||||
|
||||
if d.Name != "foo" || d.Namespace != "" {
|
||||
t.Errorf("unexpected describer: %#v", d)
|
||||
}
|
||||
|
||||
if buf.String() != fmt.Sprintf("%s", d.Output) {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
// 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.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, cmdtesting.NewInternalNamespacedType("", "", "foo", "non-default"))},
|
||||
}
|
||||
tf.Namespace = "non-default"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
buferr := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdDescribe(f, buf, buferr)
|
||||
cmd.Run(cmd, []string{"namespacedtype", "foo"})
|
||||
|
||||
if d.Name != "foo" || d.Namespace != "non-default" {
|
||||
t.Errorf("unexpected describer: %#v", d)
|
||||
}
|
||||
|
||||
if buf.String() != fmt.Sprintf("%s", d.Output) {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDescribeObject(t *testing.T) {
|
||||
_, _, rc := testData()
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
d := &testDescriber{Output: "test output"}
|
||||
tf.Describer = d
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/replicationcontrollers/redis-master" && m == "GET":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &rc.Items[0])}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
buferr := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdDescribe(f, buf, buferr)
|
||||
cmd.Flags().Set("filename", "../../../examples/guestbook/legacy/redis-master-controller.yaml")
|
||||
cmd.Run(cmd, []string{})
|
||||
|
||||
if d.Name != "redis-master" || d.Namespace != "test" {
|
||||
t.Errorf("unexpected describer: %#v", d)
|
||||
}
|
||||
|
||||
if buf.String() != fmt.Sprintf("%s", d.Output) {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDescribeListObjects(t *testing.T) {
|
||||
pods, _, _ := testData()
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
d := &testDescriber{Output: "test output"}
|
||||
tf.Describer = d
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)},
|
||||
}
|
||||
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
buferr := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdDescribe(f, 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())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDescribeObjectShowEvents(t *testing.T) {
|
||||
pods, _, _ := testData()
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
d := &testDescriber{Output: "test output"}
|
||||
tf.Describer = d
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)},
|
||||
}
|
||||
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
buferr := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdDescribe(f, buf, buferr)
|
||||
cmd.Flags().Set("show-events", "true")
|
||||
cmd.Run(cmd, []string{"pods"})
|
||||
if d.Settings.ShowEvents != true {
|
||||
t.Errorf("ShowEvents = true expected, got ShowEvents = %v", d.Settings.ShowEvents)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDescribeObjectSkipEvents(t *testing.T) {
|
||||
pods, _, _ := testData()
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
d := &testDescriber{Output: "test output"}
|
||||
tf.Describer = d
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)},
|
||||
}
|
||||
|
||||
tf.Namespace = "test"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
buferr := bytes.NewBuffer([]byte{})
|
||||
cmd := NewCmdDescribe(f, 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)
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue