mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 10:33:35 +00:00
vendor cleanup: remove unused,non-go and test files
This commit is contained in:
151
vendor/k8s.io/kubernetes/pkg/kubectl/.import-restrictions
generated
vendored
151
vendor/k8s.io/kubernetes/pkg/kubectl/.import-restrictions
generated
vendored
@ -1,151 +0,0 @@
|
||||
{
|
||||
"Rules": [{
|
||||
"SelectorRegexp": "k8s[.]io/kubernetes/pkg",
|
||||
"AllowedPrefixes": [
|
||||
"k8s.io/kubernetes/pkg/api",
|
||||
"k8s.io/kubernetes/pkg/api/events",
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme",
|
||||
"k8s.io/kubernetes/pkg/api/pod",
|
||||
"k8s.io/kubernetes/pkg/api/ref",
|
||||
"k8s.io/kubernetes/pkg/api/resource",
|
||||
"k8s.io/kubernetes/pkg/api/service",
|
||||
"k8s.io/kubernetes/pkg/api/v1/pod",
|
||||
"k8s.io/kubernetes/pkg/api/v1/service",
|
||||
"k8s.io/kubernetes/pkg/apis/admissionregistration",
|
||||
"k8s.io/kubernetes/pkg/apis/admissionregistration/install",
|
||||
"k8s.io/kubernetes/pkg/apis/admissionregistration/v1alpha1",
|
||||
"k8s.io/kubernetes/pkg/apis/apps",
|
||||
"k8s.io/kubernetes/pkg/apis/apps/install",
|
||||
"k8s.io/kubernetes/pkg/apis/apps/v1",
|
||||
"k8s.io/kubernetes/pkg/apis/apps/v1beta1",
|
||||
"k8s.io/kubernetes/pkg/apis/apps/v1beta2",
|
||||
"k8s.io/kubernetes/pkg/apis/authentication",
|
||||
"k8s.io/kubernetes/pkg/apis/authentication/install",
|
||||
"k8s.io/kubernetes/pkg/apis/authentication/v1",
|
||||
"k8s.io/kubernetes/pkg/apis/authentication/v1beta1",
|
||||
"k8s.io/kubernetes/pkg/apis/authorization",
|
||||
"k8s.io/kubernetes/pkg/apis/authorization/install",
|
||||
"k8s.io/kubernetes/pkg/apis/authorization/v1",
|
||||
"k8s.io/kubernetes/pkg/apis/authorization/v1beta1",
|
||||
"k8s.io/kubernetes/pkg/apis/autoscaling",
|
||||
"k8s.io/kubernetes/pkg/apis/autoscaling/install",
|
||||
"k8s.io/kubernetes/pkg/apis/autoscaling/v1",
|
||||
"k8s.io/kubernetes/pkg/apis/autoscaling/v2beta1",
|
||||
"k8s.io/kubernetes/pkg/apis/batch",
|
||||
"k8s.io/kubernetes/pkg/apis/batch/install",
|
||||
"k8s.io/kubernetes/pkg/apis/batch/v1",
|
||||
"k8s.io/kubernetes/pkg/apis/batch/v1beta1",
|
||||
"k8s.io/kubernetes/pkg/apis/batch/v2alpha1",
|
||||
"k8s.io/kubernetes/pkg/apis/certificates",
|
||||
"k8s.io/kubernetes/pkg/apis/certificates/install",
|
||||
"k8s.io/kubernetes/pkg/apis/certificates/v1beta1",
|
||||
"k8s.io/kubernetes/pkg/apis/componentconfig",
|
||||
"k8s.io/kubernetes/pkg/apis/componentconfig/install",
|
||||
"k8s.io/kubernetes/pkg/apis/componentconfig/v1alpha1",
|
||||
"k8s.io/kubernetes/pkg/apis/core",
|
||||
"k8s.io/kubernetes/pkg/apis/core/helper",
|
||||
"k8s.io/kubernetes/pkg/apis/core/helper/qos",
|
||||
"k8s.io/kubernetes/pkg/apis/core/install",
|
||||
"k8s.io/kubernetes/pkg/apis/core/v1",
|
||||
"k8s.io/kubernetes/pkg/apis/core/v1/helper",
|
||||
"k8s.io/kubernetes/pkg/apis/core/v1/helper/qos",
|
||||
"k8s.io/kubernetes/pkg/apis/core/validation",
|
||||
"k8s.io/kubernetes/pkg/apis/extensions",
|
||||
"k8s.io/kubernetes/pkg/apis/extensions/install",
|
||||
"k8s.io/kubernetes/pkg/apis/extensions/v1beta1",
|
||||
"k8s.io/kubernetes/pkg/apis/networking",
|
||||
"k8s.io/kubernetes/pkg/apis/networking/install",
|
||||
"k8s.io/kubernetes/pkg/apis/networking/v1",
|
||||
"k8s.io/kubernetes/pkg/apis/policy",
|
||||
"k8s.io/kubernetes/pkg/apis/policy/install",
|
||||
"k8s.io/kubernetes/pkg/apis/policy/v1beta1",
|
||||
"k8s.io/kubernetes/pkg/apis/rbac",
|
||||
"k8s.io/kubernetes/pkg/apis/rbac/install",
|
||||
"k8s.io/kubernetes/pkg/apis/rbac/v1",
|
||||
"k8s.io/kubernetes/pkg/apis/rbac/v1alpha1",
|
||||
"k8s.io/kubernetes/pkg/apis/rbac/v1beta1",
|
||||
"k8s.io/kubernetes/pkg/apis/scheduling",
|
||||
"k8s.io/kubernetes/pkg/apis/scheduling/install",
|
||||
"k8s.io/kubernetes/pkg/apis/scheduling/v1alpha1",
|
||||
"k8s.io/kubernetes/pkg/apis/settings",
|
||||
"k8s.io/kubernetes/pkg/apis/settings/install",
|
||||
"k8s.io/kubernetes/pkg/apis/settings/v1alpha1",
|
||||
"k8s.io/kubernetes/pkg/apis/storage",
|
||||
"k8s.io/kubernetes/pkg/apis/storage/install",
|
||||
"k8s.io/kubernetes/pkg/apis/storage/util",
|
||||
"k8s.io/kubernetes/pkg/apis/storage/v1",
|
||||
"k8s.io/kubernetes/pkg/apis/storage/v1beta1",
|
||||
"k8s.io/kubernetes/pkg/capabilities",
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset",
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/scheme",
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/admissionregistration/internalversion",
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/apps/internalversion",
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authentication/internalversion",
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion",
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/autoscaling/internalversion",
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/batch/internalversion",
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/certificates/internalversion",
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion",
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion",
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/networking/internalversion",
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/policy/internalversion",
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion",
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/scheduling/internalversion",
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/settings/internalversion",
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/storage/internalversion",
|
||||
"k8s.io/kubernetes/pkg/client/metrics/prometheus",
|
||||
"k8s.io/kubernetes/pkg/client/unversioned",
|
||||
"k8s.io/kubernetes/pkg/cloudprovider",
|
||||
"k8s.io/kubernetes/pkg/cloudprovider/providers/aws",
|
||||
"k8s.io/kubernetes/pkg/controller",
|
||||
"k8s.io/kubernetes/pkg/controller/daemon",
|
||||
"k8s.io/kubernetes/pkg/controller/daemon/util",
|
||||
"k8s.io/kubernetes/pkg/controller/deployment/util",
|
||||
"k8s.io/kubernetes/pkg/controller/history",
|
||||
"k8s.io/kubernetes/pkg/controller/statefulset",
|
||||
"k8s.io/kubernetes/pkg/credentialprovider",
|
||||
"k8s.io/kubernetes/pkg/credentialprovider/aws",
|
||||
"k8s.io/kubernetes/pkg/features",
|
||||
"k8s.io/kubernetes/pkg/fieldpath",
|
||||
"k8s.io/kubernetes/pkg/generated",
|
||||
"k8s.io/kubernetes/pkg/kubectl",
|
||||
"k8s.io/kubernetes/pkg/kubelet/apis",
|
||||
"k8s.io/kubernetes/pkg/kubelet/qos",
|
||||
"k8s.io/kubernetes/pkg/kubelet/types",
|
||||
"k8s.io/kubernetes/pkg/master/ports",
|
||||
"k8s.io/kubernetes/pkg/printers",
|
||||
"k8s.io/kubernetes/pkg/printers/internalversion",
|
||||
"k8s.io/kubernetes/pkg/registry/rbac/reconciliation",
|
||||
"k8s.io/kubernetes/pkg/registry/rbac/validation",
|
||||
"k8s.io/kubernetes/pkg/scheduler/algorithm",
|
||||
"k8s.io/kubernetes/pkg/scheduler/algorithm/predicates",
|
||||
"k8s.io/kubernetes/pkg/scheduler/algorithm/priorities/util",
|
||||
"k8s.io/kubernetes/pkg/scheduler/api",
|
||||
"k8s.io/kubernetes/pkg/scheduler/cache",
|
||||
"k8s.io/kubernetes/pkg/scheduler/util",
|
||||
"k8s.io/kubernetes/pkg/scheduler/volumebinder",
|
||||
"k8s.io/kubernetes/pkg/security/apparmor",
|
||||
"k8s.io/kubernetes/pkg/serviceaccount",
|
||||
"k8s.io/kubernetes/pkg/util/file",
|
||||
"k8s.io/kubernetes/pkg/util/goroutinemap",
|
||||
"k8s.io/kubernetes/pkg/util/hash",
|
||||
"k8s.io/kubernetes/pkg/util/interrupt",
|
||||
"k8s.io/kubernetes/pkg/util/io",
|
||||
"k8s.io/kubernetes/pkg/util/labels",
|
||||
"k8s.io/kubernetes/pkg/util/metrics",
|
||||
"k8s.io/kubernetes/pkg/util/mount",
|
||||
"k8s.io/kubernetes/pkg/util/net/sets",
|
||||
"k8s.io/kubernetes/pkg/util/node",
|
||||
"k8s.io/kubernetes/pkg/util/nsenter",
|
||||
"k8s.io/kubernetes/pkg/util/parsers",
|
||||
"k8s.io/kubernetes/pkg/util/pointer",
|
||||
"k8s.io/kubernetes/pkg/util/slice",
|
||||
"k8s.io/kubernetes/pkg/util/taints",
|
||||
"k8s.io/kubernetes/pkg/version",
|
||||
"k8s.io/kubernetes/pkg/version/prometheus",
|
||||
"k8s.io/kubernetes/pkg/volume",
|
||||
"k8s.io/kubernetes/pkg/volume/util"
|
||||
],
|
||||
"ForbiddenPrefixes": []
|
||||
}]
|
||||
}
|
200
vendor/k8s.io/kubernetes/pkg/kubectl/BUILD
generated
vendored
200
vendor/k8s.io/kubernetes/pkg/kubectl/BUILD
generated
vendored
@ -1,200 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"autoscale_test.go",
|
||||
"clusterrolebinding_test.go",
|
||||
"configmap_test.go",
|
||||
"deployment_test.go",
|
||||
"env_file_test.go",
|
||||
"generate_test.go",
|
||||
"history_test.go",
|
||||
"namespace_test.go",
|
||||
"pdb_test.go",
|
||||
"priorityclass_test.go",
|
||||
"quota_test.go",
|
||||
"rolebinding_test.go",
|
||||
"rollback_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",
|
||||
"sorter_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/api/testapi:go_default_library",
|
||||
"//pkg/api/testing:go_default_library",
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
|
||||
"//pkg/kubectl/util:go_default_library",
|
||||
"//pkg/util/pointer:go_default_library",
|
||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||
"//vendor/k8s.io/api/apps/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/apps/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/autoscaling/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/batch/v1:go_default_library",
|
||||
"//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/api/policy/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/rbac/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/rbac/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/scheduling/v1alpha1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/fake:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest/fake:go_default_library",
|
||||
"//vendor/k8s.io/client-go/scale:go_default_library",
|
||||
"//vendor/k8s.io/client-go/scale/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",
|
||||
"clusterrolebinding.go",
|
||||
"conditions.go",
|
||||
"configmap.go",
|
||||
"deployment.go",
|
||||
"doc.go",
|
||||
"env_file.go",
|
||||
"generate.go",
|
||||
"history.go",
|
||||
"interfaces.go",
|
||||
"namespace.go",
|
||||
"pdb.go",
|
||||
"priorityclass.go",
|
||||
"quota.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",
|
||||
"sorter.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubectl",
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/api/pod:go_default_library",
|
||||
"//pkg/api/v1/pod:go_default_library",
|
||||
"//pkg/apis/apps:go_default_library",
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/core/v1:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/typed/apps/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/controller/deployment/util:go_default_library",
|
||||
"//pkg/credentialprovider:go_default_library",
|
||||
"//pkg/kubectl/apps: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/golang/glog:go_default_library",
|
||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||
"//vendor/github.com/spf13/pflag:go_default_library",
|
||||
"//vendor/k8s.io/api/apps/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/apps/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/autoscaling/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/batch/v1:go_default_library",
|
||||
"//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/api/policy/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/rbac/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/rbac/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/scheduling/v1alpha1: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/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/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:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/typed/apps/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/scale:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/integer:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/jsonpath:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/retry:go_default_library",
|
||||
"//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/apply:all-srcs",
|
||||
"//pkg/kubectl/apps:all-srcs",
|
||||
"//pkg/kubectl/cmd:all-srcs",
|
||||
"//pkg/kubectl/explain:all-srcs",
|
||||
"//pkg/kubectl/genericclioptions:all-srcs",
|
||||
"//pkg/kubectl/metricsutil:all-srcs",
|
||||
"//pkg/kubectl/plugins:all-srcs",
|
||||
"//pkg/kubectl/polymorphichelpers:all-srcs",
|
||||
"//pkg/kubectl/proxy:all-srcs",
|
||||
"//pkg/kubectl/scheme:all-srcs",
|
||||
"//pkg/kubectl/util:all-srcs",
|
||||
"//pkg/kubectl/validation:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
4
vendor/k8s.io/kubernetes/pkg/kubectl/OWNERS
generated
vendored
4
vendor/k8s.io/kubernetes/pkg/kubectl/OWNERS
generated
vendored
@ -1,4 +0,0 @@
|
||||
approvers:
|
||||
- sig-cli-maintainers
|
||||
reviewers:
|
||||
- sig-cli
|
145
vendor/k8s.io/kubernetes/pkg/kubectl/apply.go
generated
vendored
145
vendor/k8s.io/kubernetes/pkg/kubectl/apply.go
generated
vendored
@ -1,145 +0,0 @@
|
||||
/*
|
||||
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/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
var metadataAccessor = meta.NewAccessor()
|
||||
|
||||
// GetOriginalConfiguration retrieves the original configuration of the object
|
||||
// from the annotation, or nil if no annotation was found.
|
||||
func GetOriginalConfiguration(obj runtime.Object) ([]byte, error) {
|
||||
annots, err := metadataAccessor.Annotations(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if annots == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
original, ok := annots[v1.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(obj runtime.Object, original []byte) error {
|
||||
if len(original) < 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
annots, err := metadataAccessor.Annotations(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if annots == nil {
|
||||
annots = map[string]string{}
|
||||
}
|
||||
|
||||
annots[v1.LastAppliedConfigAnnotation] = string(original)
|
||||
return metadataAccessor.SetAnnotations(obj, 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(obj runtime.Object, 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
|
||||
|
||||
// Otherwise, use the server side version of the object.
|
||||
// Get the current annotations from the object.
|
||||
annots, err := metadataAccessor.Annotations(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if annots == nil {
|
||||
annots = map[string]string{}
|
||||
}
|
||||
|
||||
original := annots[v1.LastAppliedConfigAnnotation]
|
||||
delete(annots, v1.LastAppliedConfigAnnotation)
|
||||
if err := metadataAccessor.SetAnnotations(obj, annots); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
modified, err = runtime.Encode(codec, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if annotate {
|
||||
annots[v1.LastAppliedConfigAnnotation] = string(modified)
|
||||
if err := metadataAccessor.SetAnnotations(obj, annots); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
modified, err = runtime.Encode(codec, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the object to its original condition.
|
||||
annots[v1.LastAppliedConfigAnnotation] = original
|
||||
if err := metadataAccessor.SetAnnotations(obj, 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(obj runtime.Object, codec runtime.Encoder) error {
|
||||
if original, err := GetOriginalConfiguration(obj); err != nil || len(original) <= 0 {
|
||||
return err
|
||||
}
|
||||
return CreateApplyAnnotation(obj, 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(obj runtime.Object, codec runtime.Encoder) error {
|
||||
modified, err := GetModifiedConfiguration(obj, false, codec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return setOriginalConfiguration(obj, 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, obj runtime.Object, codec runtime.Encoder) error {
|
||||
if createAnnotation {
|
||||
return CreateApplyAnnotation(obj, codec)
|
||||
}
|
||||
return UpdateApplyAnnotation(obj, codec)
|
||||
}
|
36
vendor/k8s.io/kubernetes/pkg/kubectl/apply/BUILD
generated
vendored
36
vendor/k8s.io/kubernetes/pkg/kubectl/apply/BUILD
generated
vendored
@ -1,36 +0,0 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"element.go",
|
||||
"empty_element.go",
|
||||
"error.go",
|
||||
"list_element.go",
|
||||
"map_element.go",
|
||||
"primitive_element.go",
|
||||
"type_element.go",
|
||||
"visitor.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubectl/apply",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/kubectl/apply/parse:all-srcs",
|
||||
"//pkg/kubectl/apply/strategy:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
34
vendor/k8s.io/kubernetes/pkg/kubectl/apply/doc.go
generated
vendored
34
vendor/k8s.io/kubernetes/pkg/kubectl/apply/doc.go
generated
vendored
@ -1,34 +0,0 @@
|
||||
/*
|
||||
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 apply
|
||||
|
||||
// This package is used for creating and applying patches generated
|
||||
// from a last recorded config, local config, remote object.
|
||||
// Example usage for a test:
|
||||
//
|
||||
//fakeSchema := tst.Fake{Path: swaggerPath}
|
||||
//s, err := fakeSchema.OpenAPISchema()
|
||||
//Expect(err).To(BeNil())
|
||||
//resources, err := openapi.NewOpenAPIData(s)
|
||||
//Expect(err).To(BeNil())
|
||||
//elementParser := parse.Factory{resources}
|
||||
//
|
||||
//obj, err := parser.CreateElement(recorded, local, remote)
|
||||
//Expect(err).Should(Not(HaveOccurred()))
|
||||
//
|
||||
//merged, err := obj.Merge(strategy.Create(strategy.Options{}))
|
||||
//
|
423
vendor/k8s.io/kubernetes/pkg/kubectl/apply/element.go
generated
vendored
423
vendor/k8s.io/kubernetes/pkg/kubectl/apply/element.go
generated
vendored
@ -1,423 +0,0 @@
|
||||
/*
|
||||
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 apply
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Element contains the record, local, and remote value for a field in an object
|
||||
// and metadata about the field read from openapi.
|
||||
// Calling Merge on an element will apply the passed in strategy to Element -
|
||||
// e.g. either replacing the whole element with the local copy or merging each
|
||||
// of the recorded, local and remote fields of the element.
|
||||
type Element interface {
|
||||
// FieldMeta specifies which merge strategy to use for this element
|
||||
FieldMeta
|
||||
|
||||
// Merge merges the recorded, local and remote values in the element using the Strategy
|
||||
// provided as an argument. Calls the type specific method on the Strategy - following the
|
||||
// "Accept" method from the "Visitor" pattern.
|
||||
// e.g. Merge on a ListElement will call Strategy.MergeList(self)
|
||||
// Returns the Result of the merged elements
|
||||
Merge(Strategy) (Result, error)
|
||||
|
||||
// HasRecorded returns true if the field was explicitly
|
||||
// present in the recorded source. This is to differentiate between
|
||||
// undefined and set to null
|
||||
HasRecorded() bool
|
||||
|
||||
// GetRecorded returns the field value from the recorded source of the object
|
||||
GetRecorded() interface{}
|
||||
|
||||
// HasLocal returns true if the field was explicitly
|
||||
// present in the local source. This is to differentiate between
|
||||
// undefined and set to null
|
||||
HasLocal() bool
|
||||
|
||||
// GetLocal returns the field value from the local source of the object
|
||||
GetLocal() interface{}
|
||||
|
||||
// HasRemote returns true if the field was explicitly
|
||||
// present in the remote source. This is to differentiate between
|
||||
// undefined and set to null
|
||||
HasRemote() bool
|
||||
|
||||
// GetRemote returns the field value from the remote source of the object
|
||||
GetRemote() interface{}
|
||||
}
|
||||
|
||||
// FieldMeta defines the strategy used to apply a Patch for an element
|
||||
type FieldMeta interface {
|
||||
// GetFieldMergeType returns the type of merge strategy to use for this field
|
||||
// maybe "merge", "replace" or "retainkeys"
|
||||
// TODO: There maybe multiple strategies, so this may need to be a slice, map, or struct
|
||||
// Address this in a follow up in the PR to introduce retainkeys strategy
|
||||
GetFieldMergeType() string
|
||||
|
||||
// GetFieldMergeKeys returns the merge key to use when the MergeType is "merge" and underlying type is a list
|
||||
GetFieldMergeKeys() MergeKeys
|
||||
|
||||
// GetFieldType returns the openapi field type - e.g. primitive, array, map, type, reference
|
||||
GetFieldType() string
|
||||
}
|
||||
|
||||
// FieldMetaImpl implements FieldMeta
|
||||
type FieldMetaImpl struct {
|
||||
// MergeType is the type of merge strategy to use for this field
|
||||
// maybe "merge", "replace" or "retainkeys"
|
||||
MergeType string
|
||||
|
||||
// MergeKeys are the merge keys to use when the MergeType is "merge" and underlying type is a list
|
||||
MergeKeys MergeKeys
|
||||
|
||||
// Type is the openapi type of the field - "list", "primitive", "map"
|
||||
Type string
|
||||
|
||||
// Name contains name of the field
|
||||
Name string
|
||||
}
|
||||
|
||||
// GetFieldMergeType implements FieldMeta.GetFieldMergeType
|
||||
func (s FieldMetaImpl) GetFieldMergeType() string {
|
||||
return s.MergeType
|
||||
}
|
||||
|
||||
// GetFieldMergeKeys implements FieldMeta.GetFieldMergeKeys
|
||||
func (s FieldMetaImpl) GetFieldMergeKeys() MergeKeys {
|
||||
return s.MergeKeys
|
||||
}
|
||||
|
||||
// GetFieldType implements FieldMeta.GetFieldType
|
||||
func (s FieldMetaImpl) GetFieldType() string {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
// MergeKeyValue records the value of the mergekey for an item in a list
|
||||
type MergeKeyValue map[string]string
|
||||
|
||||
// Equal returns true if the MergeKeyValues share the same value,
|
||||
// representing the same item in a list
|
||||
func (v MergeKeyValue) Equal(o MergeKeyValue) bool {
|
||||
if len(v) != len(o) {
|
||||
return false
|
||||
}
|
||||
|
||||
for key, v1 := range v {
|
||||
if v2, found := o[key]; !found || v1 != v2 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// MergeKeys is the set of fields on an object that uniquely identify
|
||||
// and is used when merging lists to identify the "same" object
|
||||
// independent of the ordering of the objects
|
||||
type MergeKeys []string
|
||||
|
||||
// GetMergeKeyValue parses the MergeKeyValue from an item in a list
|
||||
func (mk MergeKeys) GetMergeKeyValue(i interface{}) (MergeKeyValue, error) {
|
||||
result := MergeKeyValue{}
|
||||
if len(mk) <= 0 {
|
||||
return result, fmt.Errorf("merge key must have at least 1 value to merge")
|
||||
}
|
||||
m, ok := i.(map[string]interface{})
|
||||
if !ok {
|
||||
return result, fmt.Errorf("cannot use mergekey %v for primitive item in list %v", mk, i)
|
||||
}
|
||||
for _, field := range mk {
|
||||
if value, found := m[field]; !found {
|
||||
result[field] = ""
|
||||
} else {
|
||||
result[field] = fmt.Sprintf("%v", value)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type source int
|
||||
|
||||
const (
|
||||
recorded source = iota
|
||||
local
|
||||
remote
|
||||
)
|
||||
|
||||
// CombinedPrimitiveSlice implements a slice of primitives
|
||||
type CombinedPrimitiveSlice struct {
|
||||
Items []*PrimitiveListItem
|
||||
}
|
||||
|
||||
// PrimitiveListItem represents a single value in a slice of primitives
|
||||
type PrimitiveListItem struct {
|
||||
// Value is the value of the primitive, should match recorded, local and remote
|
||||
Value interface{}
|
||||
|
||||
RawElementData
|
||||
}
|
||||
|
||||
// Contains returns true if the slice contains the l
|
||||
func (s *CombinedPrimitiveSlice) lookup(l interface{}) *PrimitiveListItem {
|
||||
val := fmt.Sprintf("%v", l)
|
||||
for _, i := range s.Items {
|
||||
if fmt.Sprintf("%v", i.Value) == val {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *CombinedPrimitiveSlice) upsert(l interface{}) *PrimitiveListItem {
|
||||
// Return the item if it exists
|
||||
if item := s.lookup(l); item != nil {
|
||||
return item
|
||||
}
|
||||
|
||||
// Otherwise create a new item and append to the list
|
||||
item := &PrimitiveListItem{
|
||||
Value: l,
|
||||
}
|
||||
s.Items = append(s.Items, item)
|
||||
return item
|
||||
}
|
||||
|
||||
// UpsertRecorded adds l to the slice. If there is already a value of l in the
|
||||
// slice for either the local or remote, set on that value as the recorded value
|
||||
// Otherwise append a new item to the list with the recorded value.
|
||||
func (s *CombinedPrimitiveSlice) UpsertRecorded(l interface{}) {
|
||||
v := s.upsert(l)
|
||||
v.recorded = l
|
||||
v.recordedSet = true
|
||||
}
|
||||
|
||||
// UpsertLocal adds l to the slice. If there is already a value of l in the
|
||||
// slice for either the recorded or remote, set on that value as the local value
|
||||
// Otherwise append a new item to the list with the local value.
|
||||
func (s *CombinedPrimitiveSlice) UpsertLocal(l interface{}) {
|
||||
v := s.upsert(l)
|
||||
v.local = l
|
||||
v.localSet = true
|
||||
}
|
||||
|
||||
// UpsertRemote adds l to the slice. If there is already a value of l in the
|
||||
// slice for either the local or recorded, set on that value as the remote value
|
||||
// Otherwise append a new item to the list with the remote value.
|
||||
func (s *CombinedPrimitiveSlice) UpsertRemote(l interface{}) {
|
||||
v := s.upsert(l)
|
||||
v.remote = l
|
||||
v.remoteSet = true
|
||||
}
|
||||
|
||||
// ListItem represents a single value in a slice of maps or types
|
||||
type ListItem struct {
|
||||
// KeyValue is the merge key value of the item
|
||||
KeyValue MergeKeyValue
|
||||
|
||||
// RawElementData contains the field values
|
||||
RawElementData
|
||||
}
|
||||
|
||||
// CombinedMapSlice is a slice of maps or types with merge keys
|
||||
type CombinedMapSlice struct {
|
||||
Items []*ListItem
|
||||
}
|
||||
|
||||
// Lookup returns the ListItem matching the merge key, or nil if not found.
|
||||
func (s *CombinedMapSlice) lookup(v MergeKeyValue) *ListItem {
|
||||
for _, i := range s.Items {
|
||||
if i.KeyValue.Equal(v) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *CombinedMapSlice) upsert(key MergeKeys, l interface{}) (*ListItem, error) {
|
||||
// Get the identity of the item
|
||||
val, err := key.GetMergeKeyValue(l)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return the item if it exists
|
||||
if item := s.lookup(val); item != nil {
|
||||
return item, nil
|
||||
}
|
||||
|
||||
// Otherwise create a new item and append to the list
|
||||
item := &ListItem{
|
||||
KeyValue: val,
|
||||
}
|
||||
s.Items = append(s.Items, item)
|
||||
return item, nil
|
||||
}
|
||||
|
||||
// UpsertRecorded adds l to the slice. If there is already a value of l sharing
|
||||
// l's merge key in the slice for either the local or remote, set l the recorded value
|
||||
// Otherwise append a new item to the list with the recorded value.
|
||||
func (s *CombinedMapSlice) UpsertRecorded(key MergeKeys, l interface{}) error {
|
||||
item, err := s.upsert(key, l)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
item.SetRecorded(l)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpsertLocal adds l to the slice. If there is already a value of l sharing
|
||||
// l's merge key in the slice for either the recorded or remote, set l the local value
|
||||
// Otherwise append a new item to the list with the local value.
|
||||
func (s *CombinedMapSlice) UpsertLocal(key MergeKeys, l interface{}) error {
|
||||
item, err := s.upsert(key, l)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
item.SetLocal(l)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpsertRemote adds l to the slice. If there is already a value of l sharing
|
||||
// l's merge key in the slice for either the recorded or local, set l the remote value
|
||||
// Otherwise append a new item to the list with the remote value.
|
||||
func (s *CombinedMapSlice) UpsertRemote(key MergeKeys, l interface{}) error {
|
||||
item, err := s.upsert(key, l)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
item.SetRemote(l)
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsDrop returns true if the field represented by e should be dropped from the merged object
|
||||
func IsDrop(e Element) bool {
|
||||
// Specified in the last value recorded value and since deleted from the local
|
||||
removed := e.HasRecorded() && !e.HasLocal()
|
||||
|
||||
// Specified locally and explicitly set to null
|
||||
setToNil := e.HasLocal() && e.GetLocal() == nil
|
||||
|
||||
return removed || setToNil
|
||||
}
|
||||
|
||||
// IsAdd returns true if the field represented by e should have the local value directly
|
||||
// added to the merged object instead of merging the recorded, local and remote values
|
||||
func IsAdd(e Element) bool {
|
||||
// If it isn't already present in the remote value and is present in the local value
|
||||
return e.HasLocal() && !e.HasRemote()
|
||||
}
|
||||
|
||||
// NewRawElementData returns a new RawElementData, setting IsSet to true for
|
||||
// non-nil values, and leaving IsSet false for nil values.
|
||||
// Note: use this only when you want a nil-value to be considered "unspecified"
|
||||
// (ignore) and not "unset" (deleted).
|
||||
func NewRawElementData(recorded, local, remote interface{}) RawElementData {
|
||||
data := RawElementData{}
|
||||
if recorded != nil {
|
||||
data.SetRecorded(recorded)
|
||||
}
|
||||
if local != nil {
|
||||
data.SetLocal(local)
|
||||
}
|
||||
if remote != nil {
|
||||
data.SetRemote(remote)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// RawElementData contains the raw recorded, local and remote data
|
||||
// and metadata about whethere or not each was set
|
||||
type RawElementData struct {
|
||||
HasElementData
|
||||
|
||||
recorded interface{}
|
||||
local interface{}
|
||||
remote interface{}
|
||||
}
|
||||
|
||||
// SetRecorded sets the recorded value
|
||||
func (b *RawElementData) SetRecorded(value interface{}) {
|
||||
b.recorded = value
|
||||
b.recordedSet = true
|
||||
}
|
||||
|
||||
// SetLocal sets the local value
|
||||
func (b *RawElementData) SetLocal(value interface{}) {
|
||||
b.local = value
|
||||
b.localSet = true
|
||||
}
|
||||
|
||||
// SetRemote sets the remote value
|
||||
func (b *RawElementData) SetRemote(value interface{}) {
|
||||
b.remote = value
|
||||
b.remoteSet = true
|
||||
}
|
||||
|
||||
// GetRecorded implements Element.GetRecorded
|
||||
func (b RawElementData) GetRecorded() interface{} {
|
||||
// https://golang.org/doc/faq#nil_error
|
||||
if b.recorded == nil {
|
||||
return nil
|
||||
}
|
||||
return b.recorded
|
||||
}
|
||||
|
||||
// GetLocal implements Element.GetLocal
|
||||
func (b RawElementData) GetLocal() interface{} {
|
||||
// https://golang.org/doc/faq#nil_error
|
||||
if b.local == nil {
|
||||
return nil
|
||||
}
|
||||
return b.local
|
||||
}
|
||||
|
||||
// GetRemote implements Element.GetRemote
|
||||
func (b RawElementData) GetRemote() interface{} {
|
||||
// https://golang.org/doc/faq#nil_error
|
||||
if b.remote == nil {
|
||||
return nil
|
||||
}
|
||||
return b.remote
|
||||
}
|
||||
|
||||
// HasElementData contains whether a field was set in the recorded, local and remote sources
|
||||
type HasElementData struct {
|
||||
recordedSet bool
|
||||
localSet bool
|
||||
remoteSet bool
|
||||
}
|
||||
|
||||
// HasRecorded implements Element.HasRecorded
|
||||
func (e HasElementData) HasRecorded() bool {
|
||||
return e.recordedSet
|
||||
}
|
||||
|
||||
// HasLocal implements Element.HasLocal
|
||||
func (e HasElementData) HasLocal() bool {
|
||||
return e.localSet
|
||||
}
|
||||
|
||||
// HasRemote implements Element.HasRemote
|
||||
func (e HasElementData) HasRemote() bool {
|
||||
return e.remoteSet
|
||||
}
|
||||
|
||||
// ConflictDetector defines the capability to detect conflict. An element can examine remote/recorded value to detect conflict.
|
||||
type ConflictDetector interface {
|
||||
HasConflict() error
|
||||
}
|
70
vendor/k8s.io/kubernetes/pkg/kubectl/apply/empty_element.go
generated
vendored
70
vendor/k8s.io/kubernetes/pkg/kubectl/apply/empty_element.go
generated
vendored
@ -1,70 +0,0 @@
|
||||
/*
|
||||
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 apply
|
||||
|
||||
// EmptyElement is a placeholder for when no value is set for a field so its type is unknown
|
||||
type EmptyElement struct {
|
||||
// FieldMetaImpl contains metadata about the field from openapi
|
||||
FieldMetaImpl
|
||||
}
|
||||
|
||||
// Merge implements Element.Merge
|
||||
func (e EmptyElement) Merge(v Strategy) (Result, error) {
|
||||
return v.MergeEmpty(e)
|
||||
}
|
||||
|
||||
// IsAdd implements Element.IsAdd
|
||||
func (e EmptyElement) IsAdd() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsDelete implements Element.IsDelete
|
||||
func (e EmptyElement) IsDelete() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// GetRecorded implements Element.GetRecorded
|
||||
func (e EmptyElement) GetRecorded() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetLocal implements Element.GetLocal
|
||||
func (e EmptyElement) GetLocal() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRemote implements Element.GetRemote
|
||||
func (e EmptyElement) GetRemote() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasRecorded implements Element.HasRecorded
|
||||
func (e EmptyElement) HasRecorded() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// HasLocal implements Element.HasLocal
|
||||
func (e EmptyElement) HasLocal() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// HasRemote implements Element.IsAdd
|
||||
func (e EmptyElement) HasRemote() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
var _ Element = &EmptyElement{}
|
37
vendor/k8s.io/kubernetes/pkg/kubectl/apply/error.go
generated
vendored
37
vendor/k8s.io/kubernetes/pkg/kubectl/apply/error.go
generated
vendored
@ -1,37 +0,0 @@
|
||||
/*
|
||||
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 apply
|
||||
|
||||
import "fmt"
|
||||
|
||||
// ConflictError represents a conflict error occurred during the merge operation.
|
||||
type ConflictError struct {
|
||||
element Element
|
||||
}
|
||||
|
||||
// NewConflictError returns a ConflictError with detailed conflict information in element
|
||||
func NewConflictError(e PrimitiveElement) *ConflictError {
|
||||
return &ConflictError{
|
||||
element: e,
|
||||
}
|
||||
}
|
||||
|
||||
// Error implements error
|
||||
func (c *ConflictError) Error() string {
|
||||
return fmt.Sprintf("conflict detected, recorded value (%+v) and remote value (%+v)",
|
||||
c.element.GetRecorded(), c.element.GetRemote())
|
||||
}
|
81
vendor/k8s.io/kubernetes/pkg/kubectl/apply/list_element.go
generated
vendored
81
vendor/k8s.io/kubernetes/pkg/kubectl/apply/list_element.go
generated
vendored
@ -1,81 +0,0 @@
|
||||
/*
|
||||
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 apply
|
||||
|
||||
// ListElement contains the recorded, local and remote values for a field
|
||||
// of type list
|
||||
type ListElement struct {
|
||||
// FieldMetaImpl contains metadata about the field from openapi
|
||||
FieldMetaImpl
|
||||
|
||||
ListElementData
|
||||
|
||||
// Values contains the combined recorded-local-remote value of each item in the list
|
||||
// Present for lists that can be merged only. Contains the items
|
||||
// from each of the 3 lists merged into single Elements using
|
||||
// the merge-key.
|
||||
Values []Element
|
||||
}
|
||||
|
||||
// Merge implements Element.Merge
|
||||
func (e ListElement) Merge(v Strategy) (Result, error) {
|
||||
return v.MergeList(e)
|
||||
}
|
||||
|
||||
var _ Element = &ListElement{}
|
||||
|
||||
// ListElementData contains the recorded, local and remote data for a list
|
||||
type ListElementData struct {
|
||||
RawElementData
|
||||
}
|
||||
|
||||
// GetRecordedList returns the Recorded value as a list
|
||||
func (e ListElementData) GetRecordedList() []interface{} {
|
||||
return sliceCast(e.recorded)
|
||||
}
|
||||
|
||||
// GetLocalList returns the Local value as a list
|
||||
func (e ListElementData) GetLocalList() []interface{} {
|
||||
return sliceCast(e.local)
|
||||
}
|
||||
|
||||
// GetRemoteList returns the Remote value as a list
|
||||
func (e ListElementData) GetRemoteList() []interface{} {
|
||||
return sliceCast(e.remote)
|
||||
}
|
||||
|
||||
// sliceCast casts i to a slice if it is non-nil, otherwise returns nil
|
||||
func sliceCast(i interface{}) []interface{} {
|
||||
if i == nil {
|
||||
return nil
|
||||
}
|
||||
return i.([]interface{})
|
||||
}
|
||||
|
||||
// HasConflict returns ConflictError if fields in recorded and remote of ListElement conflict
|
||||
func (e ListElement) HasConflict() error {
|
||||
for _, item := range e.Values {
|
||||
if item, ok := item.(ConflictDetector); ok {
|
||||
if err := item.HasConflict(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ ConflictDetector = &ListElement{}
|
86
vendor/k8s.io/kubernetes/pkg/kubectl/apply/map_element.go
generated
vendored
86
vendor/k8s.io/kubernetes/pkg/kubectl/apply/map_element.go
generated
vendored
@ -1,86 +0,0 @@
|
||||
/*
|
||||
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 apply
|
||||
|
||||
// MapElement contains the recorded, local and remote values for a field
|
||||
// of type map
|
||||
type MapElement struct {
|
||||
// FieldMetaImpl contains metadata about the field from openapi
|
||||
FieldMetaImpl
|
||||
|
||||
// MapElementData contains the value a field was set to
|
||||
MapElementData
|
||||
|
||||
// Values contains the combined recorded-local-remote value of each item in the map
|
||||
// Values contains the values in mapElement. Element must contain
|
||||
// a Name matching its key in Values
|
||||
Values map[string]Element
|
||||
}
|
||||
|
||||
// Merge implements Element.Merge
|
||||
func (e MapElement) Merge(v Strategy) (Result, error) {
|
||||
return v.MergeMap(e)
|
||||
}
|
||||
|
||||
// GetValues implements Element.GetValues
|
||||
func (e MapElement) GetValues() map[string]Element {
|
||||
return e.Values
|
||||
}
|
||||
|
||||
var _ Element = &MapElement{}
|
||||
|
||||
// MapElementData contains the recorded, local and remote data for a map or type
|
||||
type MapElementData struct {
|
||||
RawElementData
|
||||
}
|
||||
|
||||
// GetRecordedMap returns the Recorded value as a map
|
||||
func (e MapElementData) GetRecordedMap() map[string]interface{} {
|
||||
return mapCast(e.recorded)
|
||||
}
|
||||
|
||||
// GetLocalMap returns the Local value as a map
|
||||
func (e MapElementData) GetLocalMap() map[string]interface{} {
|
||||
return mapCast(e.local)
|
||||
}
|
||||
|
||||
// GetRemoteMap returns the Remote value as a map
|
||||
func (e MapElementData) GetRemoteMap() map[string]interface{} {
|
||||
return mapCast(e.remote)
|
||||
}
|
||||
|
||||
// mapCast casts i to a map if it is non-nil, otherwise returns nil
|
||||
func mapCast(i interface{}) map[string]interface{} {
|
||||
if i == nil {
|
||||
return nil
|
||||
}
|
||||
return i.(map[string]interface{})
|
||||
}
|
||||
|
||||
// HasConflict returns ConflictError if some elements in map conflict.
|
||||
func (e MapElement) HasConflict() error {
|
||||
for _, item := range e.GetValues() {
|
||||
if item, ok := item.(ConflictDetector); ok {
|
||||
if err := item.HasConflict(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ ConflictDetector = &MapElement{}
|
52
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/BUILD
generated
vendored
52
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/BUILD
generated
vendored
@ -1,52 +0,0 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"factory.go",
|
||||
"item.go",
|
||||
"list_element.go",
|
||||
"map_element.go",
|
||||
"openapi.go",
|
||||
"primitive_element.go",
|
||||
"type_element.go",
|
||||
"util.go",
|
||||
"visitor.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubectl/apply/parse",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/kubectl/apply:go_default_library",
|
||||
"//pkg/kubectl/cmd/util/openapi:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/kube-openapi/pkg/util/proto:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_xtest",
|
||||
srcs = ["suite_test.go"],
|
||||
data = [
|
||||
"//api/openapi-spec:swagger-spec",
|
||||
],
|
||||
deps = [
|
||||
"//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",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
120
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/factory.go
generated
vendored
120
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/factory.go
generated
vendored
@ -1,120 +0,0 @@
|
||||
/*
|
||||
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 parse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/util/proto"
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
|
||||
)
|
||||
|
||||
// Factory creates an Element by combining object values from recorded, local and remote sources with
|
||||
// the metadata from an openapi schema.
|
||||
type Factory struct {
|
||||
// Resources contains the openapi field metadata for the object models
|
||||
Resources openapi.Resources
|
||||
}
|
||||
|
||||
// CreateElement returns an Element by collating the recorded, local and remote field values
|
||||
func (b *Factory) CreateElement(recorded, local, remote map[string]interface{}) (apply.Element, error) {
|
||||
// Create an Item from the 3 values. Use empty name for field
|
||||
visitor := &ElementBuildingVisitor{b.Resources}
|
||||
|
||||
gvk, err := getCommonGroupVersionKind(recorded, local, remote)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get the openapi object metadata
|
||||
s := visitor.resources.LookupResource(gvk)
|
||||
oapiKind, err := getKind(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := apply.NewRawElementData(recorded, local, remote)
|
||||
fieldName := ""
|
||||
item, err := visitor.getItem(oapiKind, fieldName, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Collate each field of the item into a combined Element
|
||||
return item.CreateElement(visitor)
|
||||
}
|
||||
|
||||
// getItem returns the appropriate Item based on the underlying type of the arguments
|
||||
func (v *ElementBuildingVisitor) getItem(s proto.Schema, name string, data apply.RawElementData) (Item, error) {
|
||||
kind, err := getType(data.GetRecorded(), data.GetLocal(), data.GetRemote())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if kind == nil {
|
||||
// All of the items values are nil.
|
||||
return &emptyItem{Name: name}, nil
|
||||
}
|
||||
|
||||
// Create an item matching the type
|
||||
switch kind.Kind() {
|
||||
case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint,
|
||||
reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64,
|
||||
reflect.String:
|
||||
p, err := getPrimitive(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("expected openapi Primitive, was %T for %v (%v)", s, kind, err)
|
||||
}
|
||||
return &primitiveItem{name, p, data}, nil
|
||||
case reflect.Array, reflect.Slice:
|
||||
a, err := getArray(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("expected openapi Array, was %T for %v (%v)", s, kind, err)
|
||||
}
|
||||
return &listItem{
|
||||
Name: name,
|
||||
Array: a,
|
||||
ListElementData: apply.ListElementData{
|
||||
RawElementData: data,
|
||||
},
|
||||
}, nil
|
||||
case reflect.Map:
|
||||
if k, err := getKind(s); err == nil {
|
||||
return &typeItem{
|
||||
Name: name,
|
||||
Type: k,
|
||||
MapElementData: apply.MapElementData{
|
||||
RawElementData: data,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
// If it looks like a map, and no openapi type is found, default to mapItem
|
||||
m, err := getMap(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("expected openapi Kind or Map, was %T for %v (%v)", s, kind, err)
|
||||
}
|
||||
return &mapItem{
|
||||
Name: name,
|
||||
Map: m,
|
||||
MapElementData: apply.MapElementData{
|
||||
RawElementData: data,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unsupported type type %v", kind)
|
||||
}
|
120
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/item.go
generated
vendored
120
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/item.go
generated
vendored
@ -1,120 +0,0 @@
|
||||
/*
|
||||
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 parse
|
||||
|
||||
import (
|
||||
"k8s.io/kube-openapi/pkg/util/proto"
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
)
|
||||
|
||||
// Item wraps values from 3 sources (recorded, local, remote).
|
||||
// The values are not collated
|
||||
type Item interface {
|
||||
// CreateElement merges the values in the item into a combined Element
|
||||
CreateElement(ItemVisitor) (apply.Element, error)
|
||||
}
|
||||
|
||||
// primitiveItem contains a recorded, local, and remote value
|
||||
type primitiveItem struct {
|
||||
Name string
|
||||
Primitive *proto.Primitive
|
||||
|
||||
apply.RawElementData
|
||||
}
|
||||
|
||||
func (i *primitiveItem) CreateElement(v ItemVisitor) (apply.Element, error) {
|
||||
return v.CreatePrimitiveElement(i)
|
||||
}
|
||||
|
||||
func (i *primitiveItem) GetMeta() proto.Schema {
|
||||
// https://golang.org/doc/faq#nil_error
|
||||
if i.Primitive != nil {
|
||||
return i.Primitive
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// listItem contains a recorded, local, and remote list
|
||||
type listItem struct {
|
||||
Name string
|
||||
Array *proto.Array
|
||||
|
||||
apply.ListElementData
|
||||
}
|
||||
|
||||
func (i *listItem) CreateElement(v ItemVisitor) (apply.Element, error) {
|
||||
return v.CreateListElement(i)
|
||||
}
|
||||
|
||||
func (i *listItem) GetMeta() proto.Schema {
|
||||
// https://golang.org/doc/faq#nil_error
|
||||
if i.Array != nil {
|
||||
return i.Array
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// mapItem contains a recorded, local, and remote map
|
||||
type mapItem struct {
|
||||
Name string
|
||||
Map *proto.Map
|
||||
|
||||
apply.MapElementData
|
||||
}
|
||||
|
||||
func (i *mapItem) CreateElement(v ItemVisitor) (apply.Element, error) {
|
||||
return v.CreateMapElement(i)
|
||||
}
|
||||
|
||||
func (i *mapItem) GetMeta() proto.Schema {
|
||||
// https://golang.org/doc/faq#nil_error
|
||||
if i.Map != nil {
|
||||
return i.Map
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// mapItem contains a recorded, local, and remote map
|
||||
type typeItem struct {
|
||||
Name string
|
||||
Type *proto.Kind
|
||||
|
||||
apply.MapElementData
|
||||
}
|
||||
|
||||
func (i *typeItem) GetMeta() proto.Schema {
|
||||
// https://golang.org/doc/faq#nil_error
|
||||
if i.Type != nil {
|
||||
return i.Type
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *typeItem) CreateElement(v ItemVisitor) (apply.Element, error) {
|
||||
return v.CreateTypeElement(i)
|
||||
}
|
||||
|
||||
// emptyItem contains no values
|
||||
type emptyItem struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (i *emptyItem) CreateElement(v ItemVisitor) (apply.Element, error) {
|
||||
e := &apply.EmptyElement{}
|
||||
e.Name = i.Name
|
||||
return e, nil
|
||||
}
|
199
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/list_element.go
generated
vendored
199
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/list_element.go
generated
vendored
@ -1,199 +0,0 @@
|
||||
/*
|
||||
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 parse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/util/proto"
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
)
|
||||
|
||||
// Contains the heavy lifting for finding tuples of matching elements in lists based on the merge key
|
||||
// and then uses the canonical order derived from the orders in the recorded, local and remote lists.
|
||||
|
||||
// replaceListElement builds a ListElement for a listItem.
|
||||
// Uses the "merge" strategy to identify "same" elements across lists by a "merge key"
|
||||
func (v ElementBuildingVisitor) mergeListElement(meta apply.FieldMetaImpl, item *listItem) (*apply.ListElement, error) {
|
||||
subtype := getSchemaType(item.Array.SubType)
|
||||
switch subtype {
|
||||
case "primitive":
|
||||
return v.doPrimitiveList(meta, item)
|
||||
case "map", "kind", "reference":
|
||||
return v.doMapList(meta, item)
|
||||
default:
|
||||
return nil, fmt.Errorf("Cannot merge lists with subtype %s", subtype)
|
||||
}
|
||||
}
|
||||
|
||||
// doPrimitiveList merges 3 lists of primitives together
|
||||
// tries to maintain ordering
|
||||
func (v ElementBuildingVisitor) doPrimitiveList(meta apply.FieldMetaImpl, item *listItem) (*apply.ListElement, error) {
|
||||
result := &apply.ListElement{
|
||||
FieldMetaImpl: apply.FieldMetaImpl{
|
||||
MergeType: apply.MergeStrategy,
|
||||
Name: item.Name,
|
||||
},
|
||||
ListElementData: item.ListElementData,
|
||||
Values: []apply.Element{},
|
||||
}
|
||||
|
||||
// Use locally defined order, then add remote, then add recorded.
|
||||
orderedKeys := &apply.CombinedPrimitiveSlice{}
|
||||
|
||||
// Locally defined items come first and retain their order
|
||||
// as defined locally
|
||||
for _, l := range item.GetLocalList() {
|
||||
orderedKeys.UpsertLocal(l)
|
||||
}
|
||||
// Mixin remote values, adding any that are not present locally
|
||||
for _, l := range item.GetRemoteList() {
|
||||
orderedKeys.UpsertRemote(l)
|
||||
}
|
||||
// Mixin recorded values, adding any that are not present locally
|
||||
// or remotely
|
||||
for _, l := range item.GetRecordedList() {
|
||||
orderedKeys.UpsertRecorded(l)
|
||||
}
|
||||
|
||||
for i, l := range orderedKeys.Items {
|
||||
var s proto.Schema
|
||||
if item.Array != nil && item.Array.SubType != nil {
|
||||
s = item.Array.SubType
|
||||
}
|
||||
|
||||
subitem, err := v.getItem(s, fmt.Sprintf("%d", i), l.RawElementData)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert the Item to an Element
|
||||
newelem, err := subitem.CreateElement(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Append the element to the list
|
||||
result.Values = append(result.Values, newelem)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// doMapList merges 3 lists of maps together by collating their values.
|
||||
// tries to retain ordering
|
||||
func (v ElementBuildingVisitor) doMapList(meta apply.FieldMetaImpl, item *listItem) (*apply.ListElement, error) {
|
||||
key := meta.GetFieldMergeKeys()
|
||||
result := &apply.ListElement{
|
||||
FieldMetaImpl: apply.FieldMetaImpl{
|
||||
MergeType: apply.MergeStrategy,
|
||||
MergeKeys: key,
|
||||
Name: item.Name,
|
||||
},
|
||||
ListElementData: item.ListElementData,
|
||||
Values: []apply.Element{},
|
||||
}
|
||||
|
||||
// Use locally defined order, then add remote, then add recorded.
|
||||
orderedKeys := &apply.CombinedMapSlice{}
|
||||
|
||||
// Locally defined items come first and retain their order
|
||||
// as defined locally
|
||||
for _, l := range item.GetLocalList() {
|
||||
orderedKeys.UpsertLocal(key, l)
|
||||
}
|
||||
// Mixin remote values, adding any that are not present locally
|
||||
for _, l := range item.GetRemoteList() {
|
||||
orderedKeys.UpsertRemote(key, l)
|
||||
}
|
||||
// Mixin recorded values, adding any that are not present locally
|
||||
// or remotely
|
||||
for _, l := range item.GetRecordedList() {
|
||||
orderedKeys.UpsertRecorded(key, l)
|
||||
}
|
||||
|
||||
for i, l := range orderedKeys.Items {
|
||||
var s proto.Schema
|
||||
if item.Array != nil && item.Array.SubType != nil {
|
||||
s = item.Array.SubType
|
||||
}
|
||||
subitem, err := v.getItem(s, fmt.Sprintf("%d", i), l.RawElementData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Build the element fully
|
||||
newelem, err := subitem.CreateElement(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Append the element to the list
|
||||
result.Values = append(result.Values, newelem)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// replaceListElement builds a new ListElement from a listItem
|
||||
// Uses the "replace" strategy and identify "same" elements across lists by their index
|
||||
func (v ElementBuildingVisitor) replaceListElement(meta apply.FieldMetaImpl, item *listItem) (*apply.ListElement, error) {
|
||||
meta.Name = item.Name
|
||||
result := &apply.ListElement{
|
||||
FieldMetaImpl: meta,
|
||||
ListElementData: item.ListElementData,
|
||||
Values: []apply.Element{},
|
||||
}
|
||||
|
||||
// Use the max length to iterate over the slices
|
||||
for i := 0; i < max(len(item.GetRecordedList()), len(item.GetLocalList()), len(item.GetRemoteList())); i++ {
|
||||
|
||||
// Lookup the item from each list
|
||||
data := apply.RawElementData{}
|
||||
if recorded, recordedSet := boundsSafeLookup(i, item.GetRecordedList()); recordedSet {
|
||||
data.SetRecorded(recorded)
|
||||
}
|
||||
if local, localSet := boundsSafeLookup(i, item.GetLocalList()); localSet {
|
||||
data.SetLocal(local)
|
||||
}
|
||||
if remote, remoteSet := boundsSafeLookup(i, item.GetRemoteList()); remoteSet {
|
||||
data.SetRemote(remote)
|
||||
}
|
||||
|
||||
// Create the Item
|
||||
var s proto.Schema
|
||||
if item.Array != nil && item.Array.SubType != nil {
|
||||
s = item.Array.SubType
|
||||
}
|
||||
subitem, err := v.getItem(s, fmt.Sprintf("%d", i), data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Build the element
|
||||
newelem, err := subitem.CreateElement(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Append the element to the list
|
||||
result.Values = append(result.Values, newelem)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
89
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/map_element.go
generated
vendored
89
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/map_element.go
generated
vendored
@ -1,89 +0,0 @@
|
||||
/*
|
||||
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 parse
|
||||
|
||||
import (
|
||||
"k8s.io/kube-openapi/pkg/util/proto"
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
)
|
||||
|
||||
// mapElement builds a new mapElement from a mapItem
|
||||
func (v ElementBuildingVisitor) mapElement(meta apply.FieldMetaImpl, item *mapItem) (*apply.MapElement, error) {
|
||||
// Function to return schema type of the map values
|
||||
var fn schemaFn = func(string) proto.Schema {
|
||||
// All map values share the same schema
|
||||
if item.Map != nil && item.Map.SubType != nil {
|
||||
return item.Map.SubType
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect same fields from multiple maps into a map of elements
|
||||
values, err := v.createMapValues(fn, meta, item.MapElementData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return the result
|
||||
return &apply.MapElement{
|
||||
FieldMetaImpl: meta,
|
||||
MapElementData: item.MapElementData,
|
||||
Values: values,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// schemaFn returns the schema for a field or map value based on its name or key
|
||||
type schemaFn func(key string) proto.Schema
|
||||
|
||||
// createMapValues combines the recorded, local and remote values from
|
||||
// data into a map of elements.
|
||||
func (v ElementBuildingVisitor) createMapValues(
|
||||
schemaFn schemaFn,
|
||||
meta apply.FieldMetaImpl,
|
||||
data apply.MapElementData) (map[string]apply.Element, error) {
|
||||
|
||||
// Collate each key in the map
|
||||
values := map[string]apply.Element{}
|
||||
for _, key := range keysUnion(data.GetRecordedMap(), data.GetLocalMap(), data.GetRemoteMap()) {
|
||||
combined := apply.RawElementData{}
|
||||
if recorded, recordedSet := nilSafeLookup(key, data.GetRecordedMap()); recordedSet {
|
||||
combined.SetRecorded(recorded)
|
||||
}
|
||||
if local, localSet := nilSafeLookup(key, data.GetLocalMap()); localSet {
|
||||
combined.SetLocal(local)
|
||||
}
|
||||
if remote, remoteSet := nilSafeLookup(key, data.GetRemoteMap()); remoteSet {
|
||||
combined.SetRemote(remote)
|
||||
}
|
||||
|
||||
// Create an item for the field
|
||||
field, err := v.getItem(schemaFn(key), key, combined)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Build the element for this field
|
||||
element, err := field.CreateElement(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Add the field element to the map
|
||||
values[key] = element
|
||||
}
|
||||
return values, nil
|
||||
}
|
227
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/openapi.go
generated
vendored
227
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/openapi.go
generated
vendored
@ -1,227 +0,0 @@
|
||||
/*
|
||||
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 parse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/util/proto"
|
||||
)
|
||||
|
||||
// Contains functions for casting openapi interfaces to their underlying types
|
||||
|
||||
// getSchemaType returns the string type of the schema - e.g. array, primitive, map, kind, reference
|
||||
func getSchemaType(schema proto.Schema) string {
|
||||
if schema == nil {
|
||||
return ""
|
||||
}
|
||||
visitor := &baseSchemaVisitor{}
|
||||
schema.Accept(visitor)
|
||||
return visitor.Kind
|
||||
}
|
||||
|
||||
// getKind converts schema to an *proto.Kind object
|
||||
func getKind(schema proto.Schema) (*proto.Kind, error) {
|
||||
if schema == nil {
|
||||
return nil, nil
|
||||
}
|
||||
visitor := &kindSchemaVisitor{}
|
||||
schema.Accept(visitor)
|
||||
return visitor.Result, visitor.Err
|
||||
}
|
||||
|
||||
// getArray converts schema to an *proto.Array object
|
||||
func getArray(schema proto.Schema) (*proto.Array, error) {
|
||||
if schema == nil {
|
||||
return nil, nil
|
||||
}
|
||||
visitor := &arraySchemaVisitor{}
|
||||
schema.Accept(visitor)
|
||||
return visitor.Result, visitor.Err
|
||||
}
|
||||
|
||||
// getMap converts schema to an *proto.Map object
|
||||
func getMap(schema proto.Schema) (*proto.Map, error) {
|
||||
if schema == nil {
|
||||
return nil, nil
|
||||
}
|
||||
visitor := &mapSchemaVisitor{}
|
||||
schema.Accept(visitor)
|
||||
return visitor.Result, visitor.Err
|
||||
}
|
||||
|
||||
// getPrimitive converts schema to an *proto.Primitive object
|
||||
func getPrimitive(schema proto.Schema) (*proto.Primitive, error) {
|
||||
if schema == nil {
|
||||
return nil, nil
|
||||
}
|
||||
visitor := &primitiveSchemaVisitor{}
|
||||
schema.Accept(visitor)
|
||||
return visitor.Result, visitor.Err
|
||||
}
|
||||
|
||||
type baseSchemaVisitor struct {
|
||||
Err error
|
||||
Kind string
|
||||
}
|
||||
|
||||
// VisitArray implements openapi
|
||||
func (v *baseSchemaVisitor) VisitArray(array *proto.Array) {
|
||||
v.Kind = "array"
|
||||
v.Err = fmt.Errorf("Array type not expected")
|
||||
}
|
||||
|
||||
// MergeMap implements openapi
|
||||
func (v *baseSchemaVisitor) VisitMap(*proto.Map) {
|
||||
v.Kind = "map"
|
||||
v.Err = fmt.Errorf("Map type not expected")
|
||||
}
|
||||
|
||||
// MergePrimitive implements openapi
|
||||
func (v *baseSchemaVisitor) VisitPrimitive(*proto.Primitive) {
|
||||
v.Kind = "primitive"
|
||||
v.Err = fmt.Errorf("Primitive type not expected")
|
||||
}
|
||||
|
||||
// VisitKind implements openapi
|
||||
func (v *baseSchemaVisitor) VisitKind(*proto.Kind) {
|
||||
v.Kind = "kind"
|
||||
v.Err = fmt.Errorf("Kind type not expected")
|
||||
}
|
||||
|
||||
// VisitReference implements openapi
|
||||
func (v *baseSchemaVisitor) VisitReference(reference proto.Reference) {
|
||||
v.Kind = "reference"
|
||||
v.Err = fmt.Errorf("Reference type not expected")
|
||||
}
|
||||
|
||||
type kindSchemaVisitor struct {
|
||||
baseSchemaVisitor
|
||||
Result *proto.Kind
|
||||
}
|
||||
|
||||
// VisitKind implements openapi
|
||||
func (v *kindSchemaVisitor) VisitKind(result *proto.Kind) {
|
||||
v.Result = result
|
||||
v.Kind = "kind"
|
||||
}
|
||||
|
||||
// VisitReference implements openapi
|
||||
func (v *kindSchemaVisitor) VisitReference(reference proto.Reference) {
|
||||
reference.SubSchema().Accept(v)
|
||||
if v.Err == nil {
|
||||
v.Err = copyExtensions(reference.GetPath().String(), reference.GetExtensions(), v.Result.Extensions)
|
||||
}
|
||||
}
|
||||
|
||||
func copyExtensions(field string, from, to map[string]interface{}) error {
|
||||
// Copy extensions from field to type for references
|
||||
for key, val := range from {
|
||||
if curr, found := to[key]; found {
|
||||
// Don't allow the same extension to be defined both on the field and on the type
|
||||
return fmt.Errorf("Cannot override value for extension %s on field %s from %v to %v",
|
||||
key, field, curr, val)
|
||||
}
|
||||
to[key] = val
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type mapSchemaVisitor struct {
|
||||
baseSchemaVisitor
|
||||
Result *proto.Map
|
||||
}
|
||||
|
||||
// MergeMap implements openapi
|
||||
func (v *mapSchemaVisitor) VisitMap(result *proto.Map) {
|
||||
v.Result = result
|
||||
v.Kind = "map"
|
||||
}
|
||||
|
||||
// VisitReference implements openapi
|
||||
func (v *mapSchemaVisitor) VisitReference(reference proto.Reference) {
|
||||
reference.SubSchema().Accept(v)
|
||||
if v.Err == nil {
|
||||
v.Err = copyExtensions(reference.GetPath().String(), reference.GetExtensions(), v.Result.Extensions)
|
||||
}
|
||||
}
|
||||
|
||||
type arraySchemaVisitor struct {
|
||||
baseSchemaVisitor
|
||||
Result *proto.Array
|
||||
}
|
||||
|
||||
// VisitArray implements openapi
|
||||
func (v *arraySchemaVisitor) VisitArray(result *proto.Array) {
|
||||
v.Result = result
|
||||
v.Kind = "array"
|
||||
v.Err = copySubElementPatchStrategy(result.Path.String(), result.GetExtensions(), result.SubType.GetExtensions())
|
||||
}
|
||||
|
||||
// copyPatchStrategy copies the strategies to subelements to the subtype
|
||||
// e.g. PodTemplate.Volumes is a []Volume with "x-kubernetes-patch-strategy": "merge,retainKeys"
|
||||
// the "retainKeys" strategy applies to merging Volumes, and must be copied to the sub element
|
||||
func copySubElementPatchStrategy(field string, from, to map[string]interface{}) error {
|
||||
// Check if the parent has a patch strategy extension
|
||||
if ext, found := from["x-kubernetes-patch-strategy"]; found {
|
||||
strategy, ok := ext.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("Expected string value for x-kubernetes-patch-strategy on %s, was %T",
|
||||
field, ext)
|
||||
}
|
||||
// Check of the parent patch strategy has a sub patch strategy, and if so copy to the sub type
|
||||
if strings.Contains(strategy, ",") {
|
||||
strategies := strings.Split(strategy, ",")
|
||||
if len(strategies) != 2 {
|
||||
// Only 1 sub strategy is supported
|
||||
return fmt.Errorf(
|
||||
"Expected between 0 and 2 elements for x-kubernetes-patch-merge-strategy by got %v",
|
||||
strategies)
|
||||
}
|
||||
to["x-kubernetes-patch-strategy"] = strategies[1]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MergePrimitive implements openapi
|
||||
func (v *arraySchemaVisitor) VisitReference(reference proto.Reference) {
|
||||
reference.SubSchema().Accept(v)
|
||||
if v.Err == nil {
|
||||
v.Err = copyExtensions(reference.GetPath().String(), reference.GetExtensions(), v.Result.Extensions)
|
||||
}
|
||||
}
|
||||
|
||||
type primitiveSchemaVisitor struct {
|
||||
baseSchemaVisitor
|
||||
Result *proto.Primitive
|
||||
}
|
||||
|
||||
// MergePrimitive implements openapi
|
||||
func (v *primitiveSchemaVisitor) VisitPrimitive(result *proto.Primitive) {
|
||||
v.Result = result
|
||||
v.Kind = "primitive"
|
||||
}
|
||||
|
||||
// VisitReference implements openapi
|
||||
func (v *primitiveSchemaVisitor) VisitReference(reference proto.Reference) {
|
||||
reference.SubSchema().Accept(v)
|
||||
if v.Err == nil {
|
||||
v.Err = copyExtensions(reference.GetPath().String(), reference.GetExtensions(), v.Result.Extensions)
|
||||
}
|
||||
}
|
28
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/primitive_element.go
generated
vendored
28
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/primitive_element.go
generated
vendored
@ -1,28 +0,0 @@
|
||||
/*
|
||||
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 parse
|
||||
|
||||
import "k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
|
||||
// primitiveElement builds a new primitiveElement from a PrimitiveItem
|
||||
func (v ElementBuildingVisitor) primitiveElement(item *primitiveItem) (*apply.PrimitiveElement, error) {
|
||||
meta := apply.FieldMetaImpl{Name: item.Name}
|
||||
return &apply.PrimitiveElement{
|
||||
FieldMetaImpl: meta,
|
||||
RawElementData: item.RawElementData,
|
||||
}, nil
|
||||
}
|
49
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/suite_test.go
generated
vendored
49
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/suite_test.go
generated
vendored
@ -1,49 +0,0 @@
|
||||
/*
|
||||
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 parse_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/config"
|
||||
. "github.com/onsi/ginkgo/types"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestOpenapi(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecsWithDefaultAndCustomReporters(t, "Openapi 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") }
|
46
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/type_element.go
generated
vendored
46
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/type_element.go
generated
vendored
@ -1,46 +0,0 @@
|
||||
/*
|
||||
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 parse
|
||||
|
||||
import (
|
||||
"k8s.io/kube-openapi/pkg/util/proto"
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
)
|
||||
|
||||
// typeElement builds a new mapElement from a typeItem
|
||||
func (v ElementBuildingVisitor) typeElement(meta apply.FieldMetaImpl, item *typeItem) (*apply.TypeElement, error) {
|
||||
// Function to get the schema of a field from its key
|
||||
var fn schemaFn = func(key string) proto.Schema {
|
||||
if item.Type != nil && item.Type.Fields != nil {
|
||||
return item.Type.Fields[key]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect same fields from multiple maps into a map of elements
|
||||
values, err := v.createMapValues(fn, meta, item.MapElementData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return the result
|
||||
return &apply.TypeElement{
|
||||
FieldMetaImpl: meta,
|
||||
MapElementData: item.MapElementData,
|
||||
Values: values,
|
||||
}, nil
|
||||
}
|
181
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/util.go
generated
vendored
181
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/util.go
generated
vendored
@ -1,181 +0,0 @@
|
||||
/*
|
||||
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 parse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/kube-openapi/pkg/util/proto"
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
)
|
||||
|
||||
// nilSafeLookup returns the value from the map if the map is non-nil
|
||||
func nilSafeLookup(key string, from map[string]interface{}) (interface{}, bool) {
|
||||
if from != nil {
|
||||
value, found := from[key]
|
||||
return value, found
|
||||
}
|
||||
// Not present
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// boundsSafeLookup returns the value from the slice if the slice is non-nil and
|
||||
// the index is in bounds.
|
||||
func boundsSafeLookup(index int, from []interface{}) (interface{}, bool) {
|
||||
if from != nil && len(from) > index {
|
||||
return from[index], true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// keysUnion returns a slice containing the union of the keys present in the arguments
|
||||
func keysUnion(maps ...map[string]interface{}) []string {
|
||||
keys := map[string]interface{}{}
|
||||
for _, m := range maps {
|
||||
for k := range m {
|
||||
keys[k] = nil
|
||||
}
|
||||
}
|
||||
result := []string{}
|
||||
for key := range keys {
|
||||
result = append(result, key)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// max returns the argument with the highest value
|
||||
func max(values ...int) int {
|
||||
v := 0
|
||||
for _, i := range values {
|
||||
if i > v {
|
||||
v = i
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// getType returns the type of the arguments. If the arguments don't have matching
|
||||
// types, getType returns an error. Nil types matching everything.
|
||||
func getType(args ...interface{}) (reflect.Type, error) {
|
||||
var last interface{}
|
||||
for _, next := range args {
|
||||
// Skip nil values
|
||||
if next == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Set the first non-nil value we find and continue
|
||||
if last == nil {
|
||||
last = next
|
||||
continue
|
||||
}
|
||||
|
||||
// Verify the types of the values match
|
||||
if reflect.TypeOf(last).Kind() != reflect.TypeOf(next).Kind() {
|
||||
return nil, fmt.Errorf("missmatching non-nil types for the same field: %T %T", last, next)
|
||||
}
|
||||
}
|
||||
|
||||
return reflect.TypeOf(last), nil
|
||||
}
|
||||
|
||||
// getFieldMeta parses the metadata about the field from the openapi spec
|
||||
func getFieldMeta(s proto.Schema, name string) (apply.FieldMetaImpl, error) {
|
||||
m := apply.FieldMetaImpl{}
|
||||
if s != nil {
|
||||
ext := s.GetExtensions()
|
||||
if e, found := ext["x-kubernetes-patch-strategy"]; found {
|
||||
strategy, ok := e.(string)
|
||||
if !ok {
|
||||
return apply.FieldMetaImpl{}, fmt.Errorf("Expected string for x-kubernetes-patch-strategy by got %T", e)
|
||||
}
|
||||
|
||||
// Take the first strategy if there are substrategies.
|
||||
// Sub strategies are copied to sub types in openapi.go
|
||||
strategies := strings.Split(strategy, ",")
|
||||
if len(strategies) > 2 {
|
||||
return apply.FieldMetaImpl{}, fmt.Errorf("Expected between 0 and 2 elements for x-kubernetes-patch-merge-strategy by got %v", strategies)
|
||||
}
|
||||
// For lists, choose the strategy for this type, not the subtype
|
||||
m.MergeType = strategies[0]
|
||||
}
|
||||
if k, found := ext["x-kubernetes-patch-merge-key"]; found {
|
||||
key, ok := k.(string)
|
||||
if !ok {
|
||||
return apply.FieldMetaImpl{}, fmt.Errorf("Expected string for x-kubernetes-patch-merge-key by got %T", k)
|
||||
}
|
||||
m.MergeKeys = apply.MergeKeys(strings.Split(key, ","))
|
||||
}
|
||||
}
|
||||
m.Name = name
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// getCommonGroupVersionKind verifies that the recorded, local and remote all share
|
||||
// the same GroupVersionKind and returns the value
|
||||
func getCommonGroupVersionKind(recorded, local, remote map[string]interface{}) (schema.GroupVersionKind, error) {
|
||||
recordedGVK, err := getGroupVersionKind(recorded)
|
||||
if err != nil {
|
||||
return schema.GroupVersionKind{}, err
|
||||
}
|
||||
localGVK, err := getGroupVersionKind(local)
|
||||
if err != nil {
|
||||
return schema.GroupVersionKind{}, err
|
||||
}
|
||||
remoteGVK, err := getGroupVersionKind(remote)
|
||||
if err != nil {
|
||||
return schema.GroupVersionKind{}, err
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(recordedGVK, localGVK) || !reflect.DeepEqual(localGVK, remoteGVK) {
|
||||
return schema.GroupVersionKind{},
|
||||
fmt.Errorf("group version kinds do not match (recorded: %v local: %v remote: %v)",
|
||||
recordedGVK, localGVK, remoteGVK)
|
||||
}
|
||||
return recordedGVK, nil
|
||||
}
|
||||
|
||||
// getGroupVersionKind returns the GroupVersionKind of the object
|
||||
func getGroupVersionKind(config map[string]interface{}) (schema.GroupVersionKind, error) {
|
||||
gvk := schema.GroupVersionKind{}
|
||||
if gv, found := config["apiVersion"]; found {
|
||||
casted, ok := gv.(string)
|
||||
if !ok {
|
||||
return gvk, fmt.Errorf("Expected string for apiVersion, found %T", gv)
|
||||
}
|
||||
s := strings.Split(casted, "/")
|
||||
if len(s) != 1 {
|
||||
gvk.Group = s[0]
|
||||
}
|
||||
gvk.Version = s[len(s)-1]
|
||||
} else {
|
||||
return gvk, fmt.Errorf("Missing apiVersion in Kind %v", config)
|
||||
}
|
||||
if k, found := config["kind"]; found {
|
||||
casted, ok := k.(string)
|
||||
if !ok {
|
||||
return gvk, fmt.Errorf("Expected string for kind, found %T", k)
|
||||
}
|
||||
gvk.Kind = casted
|
||||
} else {
|
||||
return gvk, fmt.Errorf("Missing kind in Kind %v", config)
|
||||
}
|
||||
return gvk, nil
|
||||
}
|
79
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/visitor.go
generated
vendored
79
vendor/k8s.io/kubernetes/pkg/kubectl/apply/parse/visitor.go
generated
vendored
@ -1,79 +0,0 @@
|
||||
/*
|
||||
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 parse
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
|
||||
)
|
||||
|
||||
// ItemVisitor provides an interface for Items to Accept and call
|
||||
// the Visit function that corresponds to its actual type.
|
||||
type ItemVisitor interface {
|
||||
// CreatePrimitiveElement builds an Element for a primitiveItem
|
||||
CreatePrimitiveElement(*primitiveItem) (apply.Element, error)
|
||||
|
||||
// CreateListElement builds an Element for a listItem
|
||||
CreateListElement(*listItem) (apply.Element, error)
|
||||
|
||||
// CreateMapElement builds an Element for a mapItem
|
||||
CreateMapElement(*mapItem) (apply.Element, error)
|
||||
|
||||
// CreateTypeElement builds an Element for a typeItem
|
||||
CreateTypeElement(*typeItem) (apply.Element, error)
|
||||
}
|
||||
|
||||
// ElementBuildingVisitor creates an Elements from Items
|
||||
// An Element combines the values from the Item with the field metadata.
|
||||
type ElementBuildingVisitor struct {
|
||||
resources openapi.Resources
|
||||
}
|
||||
|
||||
// CreatePrimitiveElement creates a primitiveElement
|
||||
func (v ElementBuildingVisitor) CreatePrimitiveElement(item *primitiveItem) (apply.Element, error) {
|
||||
return v.primitiveElement(item)
|
||||
}
|
||||
|
||||
// CreateListElement creates a ListElement
|
||||
func (v ElementBuildingVisitor) CreateListElement(item *listItem) (apply.Element, error) {
|
||||
meta, err := getFieldMeta(item.GetMeta(), item.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if meta.GetFieldMergeType() == apply.MergeStrategy {
|
||||
return v.mergeListElement(meta, item)
|
||||
}
|
||||
return v.replaceListElement(meta, item)
|
||||
}
|
||||
|
||||
// CreateMapElement creates a mapElement
|
||||
func (v ElementBuildingVisitor) CreateMapElement(item *mapItem) (apply.Element, error) {
|
||||
meta, err := getFieldMeta(item.GetMeta(), item.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return v.mapElement(meta, item)
|
||||
}
|
||||
|
||||
// CreateTypeElement creates a typeElement
|
||||
func (v ElementBuildingVisitor) CreateTypeElement(item *typeItem) (apply.Element, error) {
|
||||
meta, err := getFieldMeta(item.GetMeta(), item.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return v.typeElement(meta, item)
|
||||
}
|
54
vendor/k8s.io/kubernetes/pkg/kubectl/apply/primitive_element.go
generated
vendored
54
vendor/k8s.io/kubernetes/pkg/kubectl/apply/primitive_element.go
generated
vendored
@ -1,54 +0,0 @@
|
||||
/*
|
||||
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 apply
|
||||
|
||||
import "reflect"
|
||||
|
||||
// PrimitiveElement contains the recorded, local and remote values for a field
|
||||
// of type primitive
|
||||
type PrimitiveElement struct {
|
||||
// FieldMetaImpl contains metadata about the field from openapi
|
||||
FieldMetaImpl
|
||||
|
||||
// RawElementData contains the values the field was set to
|
||||
RawElementData
|
||||
}
|
||||
|
||||
// Merge implements Element.Merge
|
||||
func (e PrimitiveElement) Merge(v Strategy) (Result, error) {
|
||||
return v.MergePrimitive(e)
|
||||
}
|
||||
|
||||
var _ Element = &PrimitiveElement{}
|
||||
|
||||
// HasConflict returns ConflictError if primitive element has conflict field.
|
||||
// Conflicts happen when either of the following conditions:
|
||||
// 1. A field is specified in both recorded and remote values, but does not match.
|
||||
// 2. A field is specified in recorded values, but missing in remote values.
|
||||
func (e PrimitiveElement) HasConflict() error {
|
||||
if e.HasRecorded() && e.HasRemote() {
|
||||
if !reflect.DeepEqual(e.GetRecorded(), e.GetRemote()) {
|
||||
return NewConflictError(e)
|
||||
}
|
||||
}
|
||||
if e.HasRecorded() && !e.HasRemote() {
|
||||
return NewConflictError(e)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ ConflictDetector = &PrimitiveElement{}
|
71
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/BUILD
generated
vendored
71
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/BUILD
generated
vendored
@ -1,71 +0,0 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"merge.go",
|
||||
"merge_visitor.go",
|
||||
"replace_visitor.go",
|
||||
"retain_keys_visitor.go",
|
||||
"strategic_visitor.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubectl/apply/strategy",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//pkg/kubectl/apply:go_default_library"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_xtest",
|
||||
srcs = [
|
||||
"merge_conflict_test.go",
|
||||
"merge_map_list_test.go",
|
||||
"merge_map_test.go",
|
||||
"merge_primitive_list_test.go",
|
||||
"merge_primitive_test.go",
|
||||
"replace_map_list_test.go",
|
||||
"replace_map_test.go",
|
||||
"replace_primitive_list_test.go",
|
||||
"retain_keys_test.go",
|
||||
"suite_test.go",
|
||||
"utils_test.go",
|
||||
],
|
||||
data = [
|
||||
":swagger-spec",
|
||||
"//api/openapi-spec:swagger-spec",
|
||||
],
|
||||
deps = [
|
||||
":go_default_library",
|
||||
"//pkg/kubectl/apply:go_default_library",
|
||||
"//pkg/kubectl/apply/parse:go_default_library",
|
||||
"//pkg/kubectl/cmd/util/openapi:go_default_library",
|
||||
"//pkg/kubectl/cmd/util/openapi/testing:go_default_library",
|
||||
"//vendor/github.com/ghodss/yaml: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",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "swagger-spec",
|
||||
srcs = glob([
|
||||
"**/*.json",
|
||||
]),
|
||||
)
|
17
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/doc.go
generated
vendored
17
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/doc.go
generated
vendored
@ -1,17 +0,0 @@
|
||||
/*
|
||||
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 strategy
|
33
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/merge.go
generated
vendored
33
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/merge.go
generated
vendored
@ -1,33 +0,0 @@
|
||||
/*
|
||||
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 strategy
|
||||
|
||||
import "k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
|
||||
// Options controls how a merge will be executed
|
||||
type Options struct {
|
||||
// FailOnConflict when true will fail patch creation if the recorded and remote
|
||||
// have 2 fields set for the same value that cannot be merged.
|
||||
// e.g. primitive values, list values with replace strategy, and map values with do
|
||||
// strategy
|
||||
FailOnConflict bool
|
||||
}
|
||||
|
||||
// Create returns a new apply.Visitor for merging multiple objects together
|
||||
func Create(options Options) apply.Strategy {
|
||||
return createDelegatingStrategy(options)
|
||||
}
|
206
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/merge_conflict_test.go
generated
vendored
206
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/merge_conflict_test.go
generated
vendored
@ -1,206 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package strategy_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply/strategy"
|
||||
)
|
||||
|
||||
var _ = Describe("Comparing fields of remote and recorded ", func() {
|
||||
Context("Test conflict in map fields of remote and recorded", func() {
|
||||
It("If conflicts found, expected return error", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
foo1: "key1"
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
foo2: "baz2-1"
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
foo1: "baz1-0"
|
||||
`)
|
||||
|
||||
expect := hasConflict
|
||||
// map fields have conflict : recorded {foo1 : "key1"}, remote {foo1 : "baz1-0"}
|
||||
runConflictTest(strategy.Create(strategy.Options{FailOnConflict: true}), recorded, local, remote, expect)
|
||||
})
|
||||
})
|
||||
|
||||
Context("Test conflict in list fields of remote and recorded ", func() {
|
||||
It("If conflicts found, expected return false", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "b"
|
||||
- "d"
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "b"
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "b"
|
||||
- "c"
|
||||
`)
|
||||
expect := hasConflict
|
||||
// primatie lists have conflicts: recorded [a, b, d], remote [a, b, c]
|
||||
runConflictTest(strategy.Create(strategy.Options{FailOnConflict: true}), recorded, local, remote, expect)
|
||||
})
|
||||
})
|
||||
|
||||
Context("Test conflict in Map-List fields of remote and recorded ", func() {
|
||||
It("should leave the item", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item1
|
||||
image: image1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item2
|
||||
image: image2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item1
|
||||
image: image3
|
||||
`)
|
||||
expect := hasConflict
|
||||
// map list has conflict : recorded {containers: [ {name: item1, image: image1} ]} , remote {containers: [ {name: item1, image: image3} ]}
|
||||
runConflictTest(strategy.Create(strategy.Options{FailOnConflict: true}), recorded, local, remote, expect)
|
||||
})
|
||||
})
|
||||
|
||||
Context("Test conflicts in nested map field", func() {
|
||||
It("If conflicts found, expected return error", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
foo1:
|
||||
name: "key1"
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
foo1:
|
||||
name: "baz1-0"
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
foo1:
|
||||
name: "baz1-1"
|
||||
`)
|
||||
expect := hasConflict
|
||||
// nested map has conflict : recorded {foo1: {name: "key1"}}, remote {foo1: {name : "baz1-1"}}
|
||||
runConflictTest(strategy.Create(strategy.Options{FailOnConflict: true}), recorded, local, remote, expect)
|
||||
})
|
||||
})
|
||||
|
||||
Context("Test conflicts in complicated map, list", func() {
|
||||
It("Should catch conflict in key-value in map element", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2020
|
||||
- containerPort: 8080
|
||||
protocol: UDP
|
||||
hostPort: 2022
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2020
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2020
|
||||
- containerPort: 8080
|
||||
protocol: UDP
|
||||
hostPort: 2022
|
||||
hostIP: "127.0.0.1"
|
||||
`)
|
||||
expect := noConflict
|
||||
runConflictTest(strategy.Create(strategy.Options{FailOnConflict: true}), recorded, local, remote, expect)
|
||||
})
|
||||
})
|
||||
})
|
650
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/merge_map_list_test.go
generated
vendored
650
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/merge_map_list_test.go
generated
vendored
@ -1,650 +0,0 @@
|
||||
/*
|
||||
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 strategy_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply/strategy"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
|
||||
tst "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi/testing"
|
||||
)
|
||||
|
||||
var _ = Describe("Merging fields of type list-of-map with openapi", func() {
|
||||
Context("where one of the items has been deleted resulting in the containers being empty", func() {
|
||||
It("should set the containers field to null", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image
|
||||
- name: item2
|
||||
image: image2
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where one of the items has been deleted", func() {
|
||||
It("should be deleted from the result", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item-keep
|
||||
image: image-keep
|
||||
- name: item-delete
|
||||
image: image-delete
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item-keep
|
||||
image: image-keep
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item-keep
|
||||
image: image-keep
|
||||
- name: item-delete
|
||||
image: image-delete
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item-keep
|
||||
image: image-keep
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where one of the items is only in the remote", func() {
|
||||
It("should leave the item", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item2
|
||||
image: image2
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item2
|
||||
image: image2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item2
|
||||
image: image2
|
||||
- name: item
|
||||
image: image
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where one of the items differs from the remote value and is missing from the recorded", func() {
|
||||
It("should update the item", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image:2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image:1
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image:2
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where one of the items differs from the remote value but matches the recorded", func() {
|
||||
It("should update the item", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image:2
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image:2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image:1
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image:2
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where one of the items is missing from the remote but matches the recorded", func() {
|
||||
It("should add the item", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image:2
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image:2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image:2
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where one of the items is missing from the remote and missing from the recorded ", func() {
|
||||
It("should add the item", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image:2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: item
|
||||
image: image:2
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where the order of the resolved, local and remote lists differs", func() {
|
||||
It("should keep the order specified in local and append items appears only in remote", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: recorded-local
|
||||
image: recorded:b
|
||||
timeoutSeconds: 2
|
||||
- name: recorded-remote
|
||||
image: recorded:c
|
||||
timeoutSeconds: 3
|
||||
- name: recorded-local-remote
|
||||
image: recorded:d
|
||||
timeoutSeconds: 4
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: local
|
||||
image: local:a
|
||||
initialDelaySeconds: 15
|
||||
- name: recorded-local-remote
|
||||
image: local:b
|
||||
initialDelaySeconds: 16
|
||||
- name: local-remote
|
||||
image: local:c
|
||||
initialDelaySeconds: 17
|
||||
- name: recorded-local
|
||||
image: local:d
|
||||
initialDelaySeconds: 18
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: remote
|
||||
image: remote:a
|
||||
imagePullPolicy: Always
|
||||
- name: recorded-remote
|
||||
image: remote:b
|
||||
imagePullPolicy: Always
|
||||
- name: local-remote
|
||||
image: remote:c
|
||||
imagePullPolicy: Always
|
||||
- name: recorded-local-remote
|
||||
image: remote:d
|
||||
imagePullPolicy: Always
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: local
|
||||
image: local:a
|
||||
initialDelaySeconds: 15
|
||||
- name: recorded-local-remote
|
||||
image: local:b
|
||||
imagePullPolicy: Always
|
||||
initialDelaySeconds: 16
|
||||
- name: local-remote
|
||||
image: local:c
|
||||
imagePullPolicy: Always
|
||||
initialDelaySeconds: 17
|
||||
- name: recorded-local
|
||||
image: local:d
|
||||
initialDelaySeconds: 18
|
||||
- name: remote
|
||||
image: remote:a
|
||||
imagePullPolicy: Always
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("Merging fields of type list-of-map with openapi containing a multi-field mergekey", func() {
|
||||
var resources openapi.Resources
|
||||
BeforeEach(func() {
|
||||
resources = tst.NewFakeResources("test_swagger.json")
|
||||
})
|
||||
|
||||
Context("where one of the items has been deleted", func() {
|
||||
It("should delete the item", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2020
|
||||
- containerPort: 8080
|
||||
protocol: UDP
|
||||
hostPort: 2022
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2020
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2020
|
||||
- containerPort: 8080
|
||||
protocol: UDP
|
||||
hostPort: 2022
|
||||
hostIP: "127.0.0.1"
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2020
|
||||
`)
|
||||
runWith(strategy.Create(strategy.Options{}), recorded, local, remote, expected, resources)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where one of the items has been updated", func() {
|
||||
It("should merge updates to the item", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2020
|
||||
- containerPort: 8080
|
||||
protocol: UDP
|
||||
hostPort: 2021
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2023
|
||||
- containerPort: 8080
|
||||
protocol: UDP
|
||||
hostPort: 2022
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2020
|
||||
- containerPort: 8080
|
||||
protocol: UDP
|
||||
hostPort: 2021
|
||||
hostIP: "127.0.0.1"
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2023
|
||||
- containerPort: 8080
|
||||
protocol: UDP
|
||||
hostPort: 2022
|
||||
hostIP: "127.0.0.1"
|
||||
`)
|
||||
runWith(strategy.Create(strategy.Options{}), recorded, local, remote, expected, resources)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where one of the items has been added", func() {
|
||||
It("should add the item", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2020
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2020
|
||||
- containerPort: 8080
|
||||
protocol: UDP
|
||||
hostPort: 2022
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2020
|
||||
hostIP: "127.0.0.1"
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 2020
|
||||
hostIP: "127.0.0.1"
|
||||
- containerPort: 8080
|
||||
protocol: UDP
|
||||
hostPort: 2022
|
||||
`)
|
||||
runWith(strategy.Create(strategy.Options{}), recorded, local, remote, expected, resources)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("Merging fields of type list-of-map with openapi", func() {
|
||||
Context("containing a replace-keys sub strategy", func() {
|
||||
It("should apply the replace-key strategy when merging the item", func() {
|
||||
})
|
||||
})
|
||||
})
|
164
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/merge_map_test.go
generated
vendored
164
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/merge_map_test.go
generated
vendored
@ -1,164 +0,0 @@
|
||||
/*
|
||||
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 strategy_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply/strategy"
|
||||
)
|
||||
|
||||
var _ = Describe("Merging fields of type map with openapi for some fields", func() {
|
||||
Context("where a field has been deleted", func() {
|
||||
It("should delete the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
foo1:
|
||||
bar: "baz1"
|
||||
image: "1"
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
foo2: null
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
foo1:
|
||||
bar: "baz1"
|
||||
image: "1"
|
||||
foo2:
|
||||
bar: "baz2"
|
||||
image: "2"
|
||||
foo3:
|
||||
bar: "baz3"
|
||||
image: "3"
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
foo3:
|
||||
bar: "baz3"
|
||||
image: "3"
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where a field is has been added", func() {
|
||||
It("should add the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
foo1:
|
||||
bar: "baz1"
|
||||
image: "1"
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
foo1:
|
||||
bar: "baz1"
|
||||
image: "1"
|
||||
foo2:
|
||||
bar: "baz2"
|
||||
image: "2"
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
foo1:
|
||||
bar: "baz1"
|
||||
image: "1"
|
||||
foo2:
|
||||
bar: "baz2"
|
||||
image: "2"
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where a field is has been updated", func() {
|
||||
It("should update the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
foo1:
|
||||
bar: "baz1=1"
|
||||
image: "1-1"
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
foo1:
|
||||
bar: "baz1-1"
|
||||
image: "1-1"
|
||||
foo2:
|
||||
bar: "baz2-1"
|
||||
image: "2-1"
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 2
|
||||
foo1:
|
||||
bar: "baz1-0"
|
||||
image: "1-0"
|
||||
foo2:
|
||||
bar: "baz2-0"
|
||||
image: "2-0"
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
foo1:
|
||||
bar: "baz1-1"
|
||||
image: "1-1"
|
||||
foo2:
|
||||
bar: "baz2-1"
|
||||
image: "2-1"
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
})
|
189
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/merge_primitive_list_test.go
generated
vendored
189
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/merge_primitive_list_test.go
generated
vendored
@ -1,189 +0,0 @@
|
||||
/*
|
||||
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 strategy_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply/strategy"
|
||||
)
|
||||
|
||||
var _ = Describe("Merging fields of type list-of-primitive with openapi", func() {
|
||||
Context("where one of the items has been deleted", func() {
|
||||
It("should delete the deleted item", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "b"
|
||||
- "c"
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "c"
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "b"
|
||||
- "c"
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "c"
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where one of the items is only on the remote", func() {
|
||||
It("should move the remote-only item to the end but keep it", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "b"
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "b"
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "c"
|
||||
- "b"
|
||||
- "a"
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "b"
|
||||
- "c"
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where one of the items is repeated", func() {
|
||||
It("should de-duplicate the repeated items", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "b"
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "b"
|
||||
- "a"
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "b"
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "b"
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where some items are deleted and others are on remote only", func() {
|
||||
It("should retain the correct items in the correct order", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "b"
|
||||
- "c"
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "c"
|
||||
- "a"
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "d"
|
||||
- "b"
|
||||
- "c"
|
||||
- "a"
|
||||
- "e"
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
finalizers:
|
||||
- "a"
|
||||
- "c"
|
||||
- "d"
|
||||
- "e"
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
})
|
540
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/merge_primitive_test.go
generated
vendored
540
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/merge_primitive_test.go
generated
vendored
@ -1,540 +0,0 @@
|
||||
/*
|
||||
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 strategy_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply/strategy"
|
||||
)
|
||||
|
||||
var _ = Describe("Merging fields of type map with openapi", func() {
|
||||
Context("where a field has been deleted", func() {
|
||||
It("should delete the field when it is the only field in the map", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
# delete - recorded/remote match
|
||||
paused: true
|
||||
# delete - recorded/remote differ
|
||||
progressDeadlineSeconds: 1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
# delete - not present in recorded
|
||||
replicas: null
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
paused: true
|
||||
progressDeadlineSeconds: 2
|
||||
`)
|
||||
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
|
||||
It("should delete the field when there are other fields in the map", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
# delete - recorded/remote match
|
||||
paused: true
|
||||
# delete - recorded/remote differ
|
||||
progressDeadlineSeconds: 1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
# delete - not present in recorded
|
||||
replicas: null
|
||||
# keep
|
||||
revisionHistoryLimit: 1
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
paused: true
|
||||
progressDeadlineSeconds: 2
|
||||
revisionHistoryLimit: 1
|
||||
`)
|
||||
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
revisionHistoryLimit: 1
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where a field is has been added", func() {
|
||||
It("should add the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
paused: true
|
||||
progressDeadlineSeconds: 1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
# Add this - it is missing from recorded and remote
|
||||
replicas: 3
|
||||
# Add this - it is missing from remote but matches recorded
|
||||
paused: true
|
||||
# Add this - it is missing from remote and differs from recorded
|
||||
progressDeadlineSeconds: 2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
paused: true
|
||||
progressDeadlineSeconds: 2
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where a field is has been updated", func() {
|
||||
It("should add the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
paused: true
|
||||
progressDeadlineSeconds: 1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
# Missing from recorded
|
||||
replicas: 3
|
||||
# Matches the recorded
|
||||
paused: true
|
||||
# Differs from recorded
|
||||
progressDeadlineSeconds: 2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 2
|
||||
paused: false
|
||||
progressDeadlineSeconds: 3
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
paused: true
|
||||
progressDeadlineSeconds: 2
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
|
||||
It("should update the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 2
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 2
|
||||
`)
|
||||
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("Merging fields of type map without openapi", func() {
|
||||
Context("where a field has been deleted", func() {
|
||||
It("should delete the field when it is the only field in the map", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
# delete - recorded/remote match
|
||||
paused: true
|
||||
# delete - recorded/remote differ
|
||||
progressDeadlineSeconds: 1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
# delete - not present in recorded
|
||||
replicas: null
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
replicas: 3
|
||||
paused: true
|
||||
progressDeadlineSeconds: 2
|
||||
`)
|
||||
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
|
||||
It("should delete the field when there are other fields in the map", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
# delete - recorded/remote match
|
||||
paused: true
|
||||
# delete - recorded/remote differ
|
||||
progressDeadlineSeconds: 1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
# delete - not present in recorded
|
||||
replicas: null
|
||||
# keep
|
||||
revisionHistoryLimit: 1
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
replicas: 3
|
||||
paused: true
|
||||
progressDeadlineSeconds: 2
|
||||
revisionHistoryLimit: 1
|
||||
`)
|
||||
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
revisionHistoryLimit: 1
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where a field is has been added", func() {
|
||||
It("should add the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
paused: true
|
||||
progressDeadlineSeconds: 1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
# Add this - it is missing from recorded and remote
|
||||
replicas: 3
|
||||
# Add this - it is missing from remote but matches recorded
|
||||
paused: true
|
||||
# Add this - it is missing from remote and differs from recorded
|
||||
progressDeadlineSeconds: 2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
replicas: 3
|
||||
paused: true
|
||||
progressDeadlineSeconds: 2
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where a field is has been updated", func() {
|
||||
It("should add the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
paused: true
|
||||
progressDeadlineSeconds: 1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
# Matches recorded
|
||||
replicas: 3
|
||||
# Matches the recorded
|
||||
paused: true
|
||||
# Differs from recorded
|
||||
progressDeadlineSeconds: 2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
replicas: 2
|
||||
paused: false
|
||||
progressDeadlineSeconds: 3
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
replicas: 3
|
||||
paused: true
|
||||
progressDeadlineSeconds: 2
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
|
||||
It("should update the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
replicas: 2
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
replicas: 3
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
replicas: 2
|
||||
`)
|
||||
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
replicas: 3
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("Merging fields of type map with openapi", func() {
|
||||
Context("where a field has been deleted", func() {
|
||||
It("should delete the field when it is the only field in the map", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
# delete - recorded/remote match
|
||||
foo: true
|
||||
# delete - recorded/remote differ
|
||||
bar: 1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
# delete - not present in recorded
|
||||
baz: null
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
baz: 3
|
||||
foo: true
|
||||
bar: 2
|
||||
`)
|
||||
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
|
||||
It("should delete the field when there are other fields in the map", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
# delete - recorded/remote match
|
||||
foo: true
|
||||
# delete - recorded/remote differ
|
||||
bar: 1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
# delete - not present in recorded
|
||||
baz: null
|
||||
# keep
|
||||
biz: 1
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
bar: 3
|
||||
foo: true
|
||||
baz: 2
|
||||
biz: 1
|
||||
`)
|
||||
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
biz: 1
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where a field is has been added", func() {
|
||||
It("should add the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
foo: true
|
||||
biz: 1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
# Add this - it is missing from recorded and remote
|
||||
baz: 3
|
||||
# Add this - it is missing from remote but matches recorded
|
||||
foo: true
|
||||
# Add this - it is missing from remote and differs from recorded
|
||||
biz: 2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
baz: 3
|
||||
foo: true
|
||||
biz: 2
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where a field is has been updated", func() {
|
||||
It("should add the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
foo: true
|
||||
baz: 1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
# Missing from recorded
|
||||
bar: 3
|
||||
# Matches the recorded
|
||||
foo: true
|
||||
# Differs from recorded
|
||||
baz: 2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
bar: 2
|
||||
foo: false
|
||||
baz: 3
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
bar: 3
|
||||
foo: true
|
||||
baz: 2
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
})
|
162
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/merge_visitor.go
generated
vendored
162
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/merge_visitor.go
generated
vendored
@ -1,162 +0,0 @@
|
||||
/*
|
||||
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 strategy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
)
|
||||
|
||||
func createMergeStrategy(options Options, strategic *delegatingStrategy) mergeStrategy {
|
||||
return mergeStrategy{
|
||||
strategic,
|
||||
options,
|
||||
}
|
||||
}
|
||||
|
||||
// mergeStrategy merges the values in an Element into a single Result
|
||||
type mergeStrategy struct {
|
||||
strategic *delegatingStrategy
|
||||
options Options
|
||||
}
|
||||
|
||||
// MergeList merges the lists in a ListElement into a single Result
|
||||
func (v mergeStrategy) MergeList(e apply.ListElement) (apply.Result, error) {
|
||||
// No merge logic if adding or deleting a field
|
||||
if result, done := v.doAddOrDelete(e); done {
|
||||
return result, nil
|
||||
}
|
||||
// Detect conflict in ListElement
|
||||
if err := v.doConflictDetect(e); err != nil {
|
||||
return apply.Result{}, err
|
||||
}
|
||||
// Merge each item in the list and append it to the list
|
||||
merged := []interface{}{}
|
||||
for _, value := range e.Values {
|
||||
// Recursively merge the list element before adding the value to the list
|
||||
m, err := value.Merge(v.strategic)
|
||||
if err != nil {
|
||||
return apply.Result{}, err
|
||||
}
|
||||
|
||||
switch m.Operation {
|
||||
case apply.SET:
|
||||
// Keep the list item value
|
||||
merged = append(merged, m.MergedResult)
|
||||
case apply.DROP:
|
||||
// Drop the list item value
|
||||
default:
|
||||
panic(fmt.Errorf("Unexpected result operation type %+v", m))
|
||||
}
|
||||
}
|
||||
|
||||
if len(merged) == 0 {
|
||||
// If the list is empty, return a nil entry
|
||||
return apply.Result{Operation: apply.SET, MergedResult: nil}, nil
|
||||
}
|
||||
// Return the merged list, and tell the caller to keep it
|
||||
return apply.Result{Operation: apply.SET, MergedResult: merged}, nil
|
||||
}
|
||||
|
||||
// MergeMap merges the maps in a MapElement into a single Result
|
||||
func (v mergeStrategy) MergeMap(e apply.MapElement) (apply.Result, error) {
|
||||
// No merge logic if adding or deleting a field
|
||||
if result, done := v.doAddOrDelete(e); done {
|
||||
return result, nil
|
||||
}
|
||||
// Detect conflict in MapElement
|
||||
if err := v.doConflictDetect(e); err != nil {
|
||||
return apply.Result{}, err
|
||||
}
|
||||
return v.doMergeMap(e.GetValues())
|
||||
}
|
||||
|
||||
// MergeMap merges the type instances in a TypeElement into a single Result
|
||||
func (v mergeStrategy) MergeType(e apply.TypeElement) (apply.Result, error) {
|
||||
// No merge logic if adding or deleting a field
|
||||
if result, done := v.doAddOrDelete(e); done {
|
||||
return result, nil
|
||||
}
|
||||
// Detect conflict in TypeElement
|
||||
if err := v.doConflictDetect(e); err != nil {
|
||||
return apply.Result{}, err
|
||||
}
|
||||
return v.doMergeMap(e.GetValues())
|
||||
}
|
||||
|
||||
// do merges a recorded, local and remote map into a new object
|
||||
func (v mergeStrategy) doMergeMap(e map[string]apply.Element) (apply.Result, error) {
|
||||
|
||||
// Merge each item in the list
|
||||
merged := map[string]interface{}{}
|
||||
for key, value := range e {
|
||||
// Recursively merge the map element before adding the value to the map
|
||||
result, err := value.Merge(v.strategic)
|
||||
if err != nil {
|
||||
return apply.Result{}, err
|
||||
}
|
||||
|
||||
switch result.Operation {
|
||||
case apply.SET:
|
||||
// Keep the map item value
|
||||
merged[key] = result.MergedResult
|
||||
case apply.DROP:
|
||||
// Drop the map item value
|
||||
default:
|
||||
panic(fmt.Errorf("Unexpected result operation type %+v", result))
|
||||
}
|
||||
}
|
||||
|
||||
// Return the merged map, and tell the caller to keep it
|
||||
if len(merged) == 0 {
|
||||
// Special case the empty map to set the field value to nil, but keep the field key
|
||||
// This is how the tests expect the structures to look when parsed from yaml
|
||||
return apply.Result{Operation: apply.SET, MergedResult: nil}, nil
|
||||
}
|
||||
return apply.Result{Operation: apply.SET, MergedResult: merged}, nil
|
||||
}
|
||||
|
||||
func (v mergeStrategy) doAddOrDelete(e apply.Element) (apply.Result, bool) {
|
||||
if apply.IsAdd(e) {
|
||||
return apply.Result{Operation: apply.SET, MergedResult: e.GetLocal()}, true
|
||||
}
|
||||
|
||||
// Delete the List
|
||||
if apply.IsDrop(e) {
|
||||
return apply.Result{Operation: apply.DROP}, true
|
||||
}
|
||||
|
||||
return apply.Result{}, false
|
||||
}
|
||||
|
||||
// MergePrimitive returns and error. Primitive elements can't be merged, only replaced.
|
||||
func (v mergeStrategy) MergePrimitive(diff apply.PrimitiveElement) (apply.Result, error) {
|
||||
return apply.Result{}, fmt.Errorf("Cannot merge primitive element %v", diff.Name)
|
||||
}
|
||||
|
||||
// MergeEmpty returns an empty result
|
||||
func (v mergeStrategy) MergeEmpty(diff apply.EmptyElement) (apply.Result, error) {
|
||||
return apply.Result{Operation: apply.SET}, nil
|
||||
}
|
||||
|
||||
// doConflictDetect returns error if element has conflict
|
||||
func (v mergeStrategy) doConflictDetect(e apply.Element) error {
|
||||
return v.strategic.doConflictDetect(e)
|
||||
}
|
||||
|
||||
var _ apply.Strategy = &mergeStrategy{}
|
73
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/replace_map_list_test.go
generated
vendored
73
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/replace_map_list_test.go
generated
vendored
@ -1,73 +0,0 @@
|
||||
/*
|
||||
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 strategy_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply/strategy"
|
||||
)
|
||||
|
||||
var _ = Describe("Replacing fields of type list without openapi", func() {
|
||||
Context("where a field is has been updated", func() {
|
||||
It("should replace the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
bar:
|
||||
- name: bar1
|
||||
value: 1
|
||||
- name: bar2
|
||||
value: 2
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
bar:
|
||||
- name: bar1
|
||||
value: 1
|
||||
- name: bar2
|
||||
value: 2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
bar:
|
||||
- name: bar1
|
||||
value: 1
|
||||
- name: bar3
|
||||
value: 3
|
||||
- name: bar4
|
||||
value: 4
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
bar:
|
||||
- name: bar1
|
||||
value: 1
|
||||
- name: bar2
|
||||
value: 2
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
})
|
80
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/replace_map_test.go
generated
vendored
80
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/replace_map_test.go
generated
vendored
@ -1,80 +0,0 @@
|
||||
/*
|
||||
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 strategy_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply/strategy"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
|
||||
tst "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi/testing"
|
||||
)
|
||||
|
||||
var _ = Describe("Replacing fields of type map with openapi for some fields", func() {
|
||||
var resources openapi.Resources
|
||||
BeforeEach(func() {
|
||||
resources = tst.NewFakeResources("test_swagger.json")
|
||||
})
|
||||
|
||||
Context("where a field is has been updated", func() {
|
||||
It("should update the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: ReplicaSet
|
||||
spec:
|
||||
template:
|
||||
containers:
|
||||
- name: container1
|
||||
image: image1
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: ReplicaSet
|
||||
spec:
|
||||
template:
|
||||
containers:
|
||||
- name: container1
|
||||
image: image1
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: ReplicaSet
|
||||
spec:
|
||||
template:
|
||||
containers:
|
||||
- name: container1
|
||||
image: image1
|
||||
- name: container2
|
||||
image: image2
|
||||
- name: container3
|
||||
image: image3
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: ReplicaSet
|
||||
spec:
|
||||
template:
|
||||
containers:
|
||||
- name: container1
|
||||
image: image1
|
||||
`)
|
||||
|
||||
// Use modified swagger for ReplicaSet spec
|
||||
runWith(strategy.Create(strategy.Options{}), recorded, local, remote, expected, resources)
|
||||
})
|
||||
})
|
||||
})
|
723
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/replace_primitive_list_test.go
generated
vendored
723
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/replace_primitive_list_test.go
generated
vendored
@ -1,723 +0,0 @@
|
||||
/*
|
||||
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 strategy_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply/strategy"
|
||||
)
|
||||
|
||||
var _ = Describe("Replacing fields of type list with openapi", func() {
|
||||
Context("where the field has been deleted", func() {
|
||||
It("should delete the field if present in recorded and missing from local.", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- z
|
||||
- "y"
|
||||
`)
|
||||
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
|
||||
It("should delete the field if missing in recorded and set to null in local.", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
`)
|
||||
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- z
|
||||
- "y"
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where the field is has been added", func() {
|
||||
It("should add the field when missing from recorded", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
|
||||
It("should add the field when even when present in recorded", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
|
||||
It("should add the field when the parent field is missing as well", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where a field is has been updated", func() {
|
||||
It("should replace the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- c
|
||||
- e
|
||||
- f
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
- z
|
||||
- "y"
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- c
|
||||
- e
|
||||
- f
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
|
||||
It("should replace the field even if recorded matches", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- c
|
||||
- e
|
||||
- f
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- c
|
||||
- e
|
||||
- f
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
- z
|
||||
- "y"
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- c
|
||||
- e
|
||||
- f
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
|
||||
It("should replace the field even if the only change is ordering", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- e
|
||||
- c
|
||||
- f
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- c
|
||||
- e
|
||||
- f
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- f
|
||||
- e
|
||||
- c
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- c
|
||||
- e
|
||||
- f
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("Replacing fields of type list with openapi for the type, but not the field", func() {
|
||||
Context("where a field is has been updated", func() {
|
||||
It("should replace the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- c
|
||||
- e
|
||||
- f
|
||||
otherstuff:
|
||||
- name: container1
|
||||
command:
|
||||
- e
|
||||
- f
|
||||
- g
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
- z
|
||||
- "y"
|
||||
otherstuff:
|
||||
- name: container1
|
||||
command:
|
||||
- s
|
||||
- d
|
||||
- f
|
||||
- name: container2
|
||||
command:
|
||||
- h
|
||||
- i
|
||||
- j
|
||||
- name: container3
|
||||
command:
|
||||
- k
|
||||
- l
|
||||
- m
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: container
|
||||
command:
|
||||
- c
|
||||
- e
|
||||
- f
|
||||
otherstuff:
|
||||
- name: container1
|
||||
command:
|
||||
- e
|
||||
- f
|
||||
- g
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("Replacing fields of type list without openapi", func() {
|
||||
Context("where the field has been deleted", func() {
|
||||
It("should delete the field.", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
template:
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
template:
|
||||
arguments:
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
template:
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- z
|
||||
- "y"
|
||||
# explicitly delete this
|
||||
arguments:
|
||||
- a
|
||||
- b
|
||||
- z
|
||||
- "y"
|
||||
# keep this
|
||||
env:
|
||||
- a
|
||||
- b
|
||||
- z
|
||||
- "y"
|
||||
`)
|
||||
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
template:
|
||||
env:
|
||||
- a
|
||||
- b
|
||||
- z
|
||||
- "y"
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where the field is has been added", func() {
|
||||
It("should add the field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
template:
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- z
|
||||
- "y"
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
template:
|
||||
# missing from recorded - add
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- z
|
||||
- "y"
|
||||
# missing from recorded - add
|
||||
arguments:
|
||||
- c
|
||||
- d
|
||||
- q
|
||||
- w
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
template:
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- z
|
||||
- "y"
|
||||
arguments:
|
||||
- c
|
||||
- d
|
||||
- q
|
||||
- w
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where a field is has been updated", func() {
|
||||
It("should replace field", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
template:
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
env:
|
||||
- s
|
||||
- "t"
|
||||
- u
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
template:
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- z
|
||||
- "y"
|
||||
arguments:
|
||||
- c
|
||||
- d
|
||||
- q
|
||||
- w
|
||||
env:
|
||||
- s
|
||||
- "t"
|
||||
- u
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
spec:
|
||||
template:
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
- z
|
||||
- "y"
|
||||
arguments:
|
||||
- c
|
||||
- d
|
||||
- i
|
||||
env:
|
||||
- u
|
||||
- s
|
||||
- "t"
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Foo
|
||||
spec:
|
||||
template:
|
||||
command:
|
||||
- a
|
||||
- b
|
||||
- z
|
||||
- "y"
|
||||
arguments:
|
||||
- c
|
||||
- d
|
||||
- q
|
||||
- w
|
||||
env:
|
||||
- s
|
||||
- "t"
|
||||
- u
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
})
|
107
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/replace_visitor.go
generated
vendored
107
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/replace_visitor.go
generated
vendored
@ -1,107 +0,0 @@
|
||||
/*
|
||||
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 strategy
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
)
|
||||
|
||||
// replaceVisitor creates a patch to replace a remote field value with a local field value
|
||||
type replaceStrategy struct {
|
||||
strategic *delegatingStrategy
|
||||
options Options
|
||||
}
|
||||
|
||||
func createReplaceStrategy(options Options, strategic *delegatingStrategy) replaceStrategy {
|
||||
return replaceStrategy{
|
||||
strategic,
|
||||
options,
|
||||
}
|
||||
}
|
||||
|
||||
// MergeList returns a result by merging the recorded, local and remote values
|
||||
// - replacing the remote value with the local value
|
||||
func (v replaceStrategy) MergeList(e apply.ListElement) (apply.Result, error) {
|
||||
return v.doReplace(e)
|
||||
}
|
||||
|
||||
// MergeMap returns a result by merging the recorded, local and remote values
|
||||
// - replacing the remote value with the local value
|
||||
func (v replaceStrategy) MergeMap(e apply.MapElement) (apply.Result, error) {
|
||||
return v.doReplace(e)
|
||||
}
|
||||
|
||||
// MergeType returns a result by merging the recorded, local and remote values
|
||||
// - replacing the remote value with the local value
|
||||
func (v replaceStrategy) MergeType(e apply.TypeElement) (apply.Result, error) {
|
||||
return v.doReplace(e)
|
||||
}
|
||||
|
||||
// MergePrimitive returns a result by merging the recorded, local and remote values
|
||||
// - replacing the remote value with the local value
|
||||
func (v replaceStrategy) MergePrimitive(e apply.PrimitiveElement) (apply.Result, error) {
|
||||
return v.doReplace(e)
|
||||
}
|
||||
|
||||
// MergeEmpty
|
||||
func (v replaceStrategy) MergeEmpty(e apply.EmptyElement) (apply.Result, error) {
|
||||
return apply.Result{Operation: apply.SET}, nil
|
||||
}
|
||||
|
||||
// replace returns the local value if specified, otherwise it returns the remote value
|
||||
// this works regardless of the approach
|
||||
func (v replaceStrategy) doReplace(e apply.Element) (apply.Result, error) {
|
||||
|
||||
if result, done := v.doAddOrDelete(e); done {
|
||||
return result, nil
|
||||
}
|
||||
if err := v.doConflictDetect(e); err != nil {
|
||||
return apply.Result{}, err
|
||||
}
|
||||
if e.HasLocal() {
|
||||
// Specified locally, set the local value
|
||||
return apply.Result{Operation: apply.SET, MergedResult: e.GetLocal()}, nil
|
||||
} else if e.HasRemote() {
|
||||
// Not specified locally, set the remote value
|
||||
return apply.Result{Operation: apply.SET, MergedResult: e.GetRemote()}, nil
|
||||
} else {
|
||||
// Only specified in the recorded, drop the field.
|
||||
return apply.Result{Operation: apply.DROP, MergedResult: e.GetRemote()}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// doAddOrDelete will check if the field should be either added or deleted. If either is true, it will
|
||||
// true the operation and true. Otherwise it will return false.
|
||||
func (v replaceStrategy) doAddOrDelete(e apply.Element) (apply.Result, bool) {
|
||||
if apply.IsAdd(e) {
|
||||
return apply.Result{Operation: apply.SET, MergedResult: e.GetLocal()}, true
|
||||
}
|
||||
|
||||
// Delete the List
|
||||
if apply.IsDrop(e) {
|
||||
return apply.Result{Operation: apply.DROP}, true
|
||||
}
|
||||
|
||||
return apply.Result{}, false
|
||||
}
|
||||
|
||||
// doConflictDetect returns error if element has conflict
|
||||
func (v replaceStrategy) doConflictDetect(e apply.Element) error {
|
||||
return v.strategic.doConflictDetect(e)
|
||||
}
|
||||
|
||||
var _ apply.Strategy = &replaceStrategy{}
|
195
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/retain_keys_test.go
generated
vendored
195
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/retain_keys_test.go
generated
vendored
@ -1,195 +0,0 @@
|
||||
/*
|
||||
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 strategy_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply/strategy"
|
||||
)
|
||||
|
||||
var _ = Describe("Merging fields with the retainkeys strategy", func() {
|
||||
Context("where some fields are only defined remotely", func() {
|
||||
It("should drop those fields ", func() {
|
||||
recorded := create(`
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
strategy:
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxUnavailable: 1
|
||||
maxSurge: 1
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where some fields are defined both locally and remotely", func() {
|
||||
It("should merge those fields", func() {
|
||||
recorded := create(`
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
strategy:
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxUnavailable: 2
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxSurge: 1
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxUnavailable: 2
|
||||
maxSurge: 1
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where the elements are in a list and some fields are only defined remotely", func() {
|
||||
It("should drop those fields ", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
volumes:
|
||||
- name: cache-volume
|
||||
emptyDir:
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
volumes:
|
||||
- name: cache-volume
|
||||
hostPath:
|
||||
path: /tmp/cache-volume
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
volumes:
|
||||
- name: cache-volume
|
||||
emptyDir:
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
|
||||
Context("where the elements are in a list", func() {
|
||||
It("the fields defined both locally and remotely should be merged", func() {
|
||||
recorded := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
`)
|
||||
local := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
volumes:
|
||||
- name: cache-volume
|
||||
hostPath:
|
||||
path: /tmp/cache-volume
|
||||
emptyDir:
|
||||
`)
|
||||
remote := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
volumes:
|
||||
- name: cache-volume
|
||||
hostPath:
|
||||
path: /tmp/cache-volume
|
||||
type: Directory
|
||||
`)
|
||||
expected := create(`
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
volumes:
|
||||
- name: cache-volume
|
||||
hostPath:
|
||||
path: /tmp/cache-volume
|
||||
type: Directory
|
||||
emptyDir:
|
||||
`)
|
||||
run(strategy.Create(strategy.Options{}), recorded, local, remote, expected)
|
||||
})
|
||||
})
|
||||
})
|
77
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/retain_keys_visitor.go
generated
vendored
77
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/retain_keys_visitor.go
generated
vendored
@ -1,77 +0,0 @@
|
||||
/*
|
||||
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 strategy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
)
|
||||
|
||||
func createRetainKeysStrategy(options Options, strategic *delegatingStrategy) retainKeysStrategy {
|
||||
return retainKeysStrategy{
|
||||
&mergeStrategy{strategic, options},
|
||||
strategic,
|
||||
options,
|
||||
}
|
||||
}
|
||||
|
||||
// retainKeysStrategy merges the values in an Element into a single Result,
|
||||
// dropping any fields omitted from the local copy. (but merging values when
|
||||
// defined locally and remotely)
|
||||
type retainKeysStrategy struct {
|
||||
merge *mergeStrategy
|
||||
strategic *delegatingStrategy
|
||||
options Options
|
||||
}
|
||||
|
||||
// MergeMap merges the type instances in a TypeElement into a single Result
|
||||
// keeping only the fields defined locally, but merging their values with
|
||||
// the remote values.
|
||||
func (v retainKeysStrategy) MergeType(e apply.TypeElement) (apply.Result, error) {
|
||||
// No merge logic if adding or deleting a field
|
||||
if result, done := v.merge.doAddOrDelete(&e); done {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
elem := map[string]apply.Element{}
|
||||
for key := range e.GetLocalMap() {
|
||||
elem[key] = e.GetValues()[key]
|
||||
}
|
||||
return v.merge.doMergeMap(elem)
|
||||
}
|
||||
|
||||
// MergeMap returns an error. Only TypeElements can have retainKeys.
|
||||
func (v retainKeysStrategy) MergeMap(e apply.MapElement) (apply.Result, error) {
|
||||
return apply.Result{}, fmt.Errorf("Cannot use retainkeys with map element %v", e.Name)
|
||||
}
|
||||
|
||||
// MergeList returns an error. Only TypeElements can have retainKeys.
|
||||
func (v retainKeysStrategy) MergeList(e apply.ListElement) (apply.Result, error) {
|
||||
return apply.Result{}, fmt.Errorf("Cannot use retainkeys with list element %v", e.Name)
|
||||
}
|
||||
|
||||
// MergePrimitive returns an error. Only TypeElements can have retainKeys.
|
||||
func (v retainKeysStrategy) MergePrimitive(diff apply.PrimitiveElement) (apply.Result, error) {
|
||||
return apply.Result{}, fmt.Errorf("Cannot use retainkeys with primitive element %v", diff.Name)
|
||||
}
|
||||
|
||||
// MergeEmpty returns an empty result
|
||||
func (v retainKeysStrategy) MergeEmpty(diff apply.EmptyElement) (apply.Result, error) {
|
||||
return v.merge.MergeEmpty(diff)
|
||||
}
|
||||
|
||||
var _ apply.Strategy = &retainKeysStrategy{}
|
109
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/strategic_visitor.go
generated
vendored
109
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/strategic_visitor.go
generated
vendored
@ -1,109 +0,0 @@
|
||||
/*
|
||||
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 strategy
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
)
|
||||
|
||||
// delegatingStrategy delegates merging fields to other visitor implementations
|
||||
// based on the merge strategy preferred by the field.
|
||||
type delegatingStrategy struct {
|
||||
options Options
|
||||
merge mergeStrategy
|
||||
replace replaceStrategy
|
||||
retainKeys retainKeysStrategy
|
||||
}
|
||||
|
||||
// createDelegatingStrategy returns a new delegatingStrategy
|
||||
func createDelegatingStrategy(options Options) *delegatingStrategy {
|
||||
v := &delegatingStrategy{
|
||||
options: options,
|
||||
}
|
||||
v.replace = createReplaceStrategy(options, v)
|
||||
v.merge = createMergeStrategy(options, v)
|
||||
v.retainKeys = createRetainKeysStrategy(options, v)
|
||||
return v
|
||||
}
|
||||
|
||||
// MergeList delegates visiting a list based on the field patch strategy.
|
||||
// Defaults to "replace"
|
||||
func (v delegatingStrategy) MergeList(diff apply.ListElement) (apply.Result, error) {
|
||||
switch diff.GetFieldMergeType() {
|
||||
case apply.MergeStrategy:
|
||||
return v.merge.MergeList(diff)
|
||||
case apply.ReplaceStrategy:
|
||||
return v.replace.MergeList(diff)
|
||||
case apply.RetainKeysStrategy:
|
||||
return v.retainKeys.MergeList(diff)
|
||||
default:
|
||||
return v.replace.MergeList(diff)
|
||||
}
|
||||
}
|
||||
|
||||
// MergeMap delegates visiting a map based on the field patch strategy.
|
||||
// Defaults to "merge"
|
||||
func (v delegatingStrategy) MergeMap(diff apply.MapElement) (apply.Result, error) {
|
||||
switch diff.GetFieldMergeType() {
|
||||
case apply.MergeStrategy:
|
||||
return v.merge.MergeMap(diff)
|
||||
case apply.ReplaceStrategy:
|
||||
return v.replace.MergeMap(diff)
|
||||
case apply.RetainKeysStrategy:
|
||||
return v.retainKeys.MergeMap(diff)
|
||||
default:
|
||||
return v.merge.MergeMap(diff)
|
||||
}
|
||||
}
|
||||
|
||||
// MergeType delegates visiting a map based on the field patch strategy.
|
||||
// Defaults to "merge"
|
||||
func (v delegatingStrategy) MergeType(diff apply.TypeElement) (apply.Result, error) {
|
||||
switch diff.GetFieldMergeType() {
|
||||
case apply.MergeStrategy:
|
||||
return v.merge.MergeType(diff)
|
||||
case apply.ReplaceStrategy:
|
||||
return v.replace.MergeType(diff)
|
||||
case apply.RetainKeysStrategy:
|
||||
return v.retainKeys.MergeType(diff)
|
||||
default:
|
||||
return v.merge.MergeType(diff)
|
||||
}
|
||||
}
|
||||
|
||||
// MergePrimitive delegates visiting a primitive to the ReplaceVisitorSingleton.
|
||||
func (v delegatingStrategy) MergePrimitive(diff apply.PrimitiveElement) (apply.Result, error) {
|
||||
// Always replace primitives
|
||||
return v.replace.MergePrimitive(diff)
|
||||
}
|
||||
|
||||
// MergeEmpty
|
||||
func (v delegatingStrategy) MergeEmpty(diff apply.EmptyElement) (apply.Result, error) {
|
||||
return v.merge.MergeEmpty(diff)
|
||||
}
|
||||
|
||||
// doConflictDetect detects conflicts in element when option enabled, return error if conflict happened.
|
||||
func (v delegatingStrategy) doConflictDetect(e apply.Element) error {
|
||||
if v.options.FailOnConflict {
|
||||
if e, ok := e.(apply.ConflictDetector); ok {
|
||||
return e.HasConflict()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ apply.Strategy = &delegatingStrategy{}
|
49
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/suite_test.go
generated
vendored
49
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/suite_test.go
generated
vendored
@ -1,49 +0,0 @@
|
||||
/*
|
||||
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 strategy_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/config"
|
||||
. "github.com/onsi/ginkgo/types"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestOpenapi(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecsWithDefaultAndCustomReporters(t, "Openapi 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") }
|
250
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/test_swagger.json
generated
vendored
250
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/test_swagger.json
generated
vendored
@ -1,250 +0,0 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"title": "Kubernetes",
|
||||
"version": "v1.9.0"
|
||||
},
|
||||
"paths": {
|
||||
},
|
||||
"definitions": {
|
||||
"io.k8s.api.core.v1.Container": {
|
||||
"description": "A single application container that you want to run within a pod.",
|
||||
"required": [
|
||||
"name",
|
||||
"image"
|
||||
],
|
||||
"properties": {
|
||||
"image": {
|
||||
"description": "Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"description": "Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.",
|
||||
"type": "string"
|
||||
},
|
||||
"ports": {
|
||||
"description": "List of ports to expose from the container. Exposing a port here gives the system additional information about the network connections a container uses, but is primarily informational. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \"0.0.0.0\" address inside a container will be accessible from the network. Cannot be updated.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/io.k8s.api.core.v1.ContainerPort"
|
||||
},
|
||||
"x-kubernetes-patch-merge-key": "containerPort,protocol",
|
||||
"x-kubernetes-patch-strategy": "merge"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io.k8s.api.core.v1.ContainerPort": {
|
||||
"description": "ContainerPort represents a network port in a single container.",
|
||||
"required": [
|
||||
"containerPort"
|
||||
],
|
||||
"properties": {
|
||||
"containerPort": {
|
||||
"description": "Number of port to expose on the pod's IP address. This must be a valid port number, 0 \u003c x \u003c 65536.",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"hostIP": {
|
||||
"description": "What host IP to bind the external port to.",
|
||||
"type": "string"
|
||||
},
|
||||
"hostPort": {
|
||||
"description": "Number of port to expose on the host. If specified, this must be a valid port number, 0 \u003c x \u003c 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"name": {
|
||||
"description": "If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.",
|
||||
"type": "string"
|
||||
},
|
||||
"protocol": {
|
||||
"description": "Protocol for port. Must be UDP or TCP. Defaults to \"TCP\".",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io.k8s.api.apps.v1beta1.Deployment": {
|
||||
"description": "DEPRECATED - This group version of Deployment is deprecated by apps/v1beta2/Deployment. See the release notes for more information. Deployment enables declarative updates for Pods and ReplicaSets.",
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources",
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds",
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"description": "Standard object metadata.",
|
||||
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
|
||||
},
|
||||
"spec": {
|
||||
"description": "Specification of the desired behavior of the Deployment.",
|
||||
"$ref": "#/definitions/io.k8s.api.apps.v1beta1.DeploymentSpec"
|
||||
}
|
||||
},
|
||||
"x-kubernetes-group-version-kind": [
|
||||
{
|
||||
"group": "apps",
|
||||
"kind": "Deployment",
|
||||
"version": "v1beta1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"io.k8s.api.apps.v1beta1.DeploymentSpec": {
|
||||
"description": "DeploymentSpec is the specification of the desired behavior of the Deployment.",
|
||||
"required": [
|
||||
"template"
|
||||
],
|
||||
"properties": {
|
||||
"minReadySeconds": {
|
||||
"description": "Minimum number of seconds for which a newly created pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready)",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"paused": {
|
||||
"description": "Indicates that the deployment is paused.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"progressDeadlineSeconds": {
|
||||
"description": "The maximum time in seconds for a deployment to make progress before it is considered to be failed. The deployment controller will continue to process failed deployments and a condition with a ProgressDeadlineExceeded reason will be surfaced in the deployment status. Once autoRollback is implemented, the deployment controller will automatically rollback failed deployments. Note that progress will not be estimated during the time a deployment is paused. Defaults to 600s.",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"replicas": {
|
||||
"description": "Number of desired pods. This is a pointer to distinguish between explicit zero and not specified. Defaults to 1.",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"revisionHistoryLimit": {
|
||||
"description": "The number of old ReplicaSets to retain to allow rollback. This is a pointer to distinguish between explicit zero and not specified. Defaults to 2.",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"template": {
|
||||
"description": "Template describes the pods that will be created.",
|
||||
"$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta": {
|
||||
"description": "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.",
|
||||
"properties": {
|
||||
"annotations": {
|
||||
"description": "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"description": "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"description": "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names",
|
||||
"type": "string"
|
||||
},
|
||||
"namespace": {
|
||||
"description": "Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io.k8s.api.core.v1.PodTemplateSpec": {
|
||||
"description": "PodTemplateSpec describes the data a pod should have when created from a template",
|
||||
"properties": {
|
||||
"metadata": {
|
||||
"description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata",
|
||||
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
|
||||
},
|
||||
"spec": {
|
||||
"description": "Specification of the desired behavior of the pod. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status",
|
||||
"$ref": "#/definitions/io.k8s.api.core.v1.PodSpec"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io.k8s.api.core.v1.PodSpec": {
|
||||
"description": "PodSpec is a description of a pod.",
|
||||
"required": [
|
||||
"containers"
|
||||
],
|
||||
"properties": {
|
||||
"containers": {
|
||||
"description": "List of containers belonging to the pod. Containers cannot currently be added or removed. There must be at least one container in a Pod. Cannot be updated.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/io.k8s.api.core.v1.Container"
|
||||
},
|
||||
"x-kubernetes-patch-merge-key": "name",
|
||||
"x-kubernetes-patch-strategy": "merge"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io.k8s.api.extensions.v1beta1.ReplicaSet": {
|
||||
"description": "DEPRECATED - This group version of ReplicaSet is deprecated by apps/v1beta2/ReplicaSet. See the release notes for more information. ReplicaSet represents the configuration of a ReplicaSet.",
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources",
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds",
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"description": "If the Labels of a ReplicaSet are empty, they are defaulted to be the same as the Pod(s) that the ReplicaSet manages. Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata",
|
||||
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
|
||||
},
|
||||
"spec": {
|
||||
"description": "Spec defines the specification of the desired behavior of the ReplicaSet. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status",
|
||||
"$ref": "#/definitions/io.k8s.api.extensions.v1beta1.ReplicaSetSpec",
|
||||
"x-kubernetes-patch-strategy": "replace"
|
||||
|
||||
}
|
||||
},
|
||||
"x-kubernetes-group-version-kind": [
|
||||
{
|
||||
"group": "extensions",
|
||||
"kind": "ReplicaSet",
|
||||
"version": "v1beta1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"io.k8s.api.extensions.v1beta1.ReplicaSetSpec": {
|
||||
"description": "ReplicaSetSpec is the specification of a ReplicaSet.",
|
||||
"properties": {
|
||||
"minReadySeconds": {
|
||||
"description": "Minimum number of seconds for which a newly created pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready)",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"replicas": {
|
||||
"description": "Replicas is the number of desired replicas. This is a pointer to distinguish between explicit zero and unspecified. Defaults to 1. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller/#what-is-a-replicationcontroller",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"template": {
|
||||
"description": "Template is the object that describes the pod that will be created if insufficient replicas are detected. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#pod-template",
|
||||
"$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
"BearerToken": {
|
||||
"description": "Bearer Token authentication",
|
||||
"type": "apiKey",
|
||||
"name": "authorization",
|
||||
"in": "header"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"BearerToken": []
|
||||
}
|
||||
]
|
||||
}
|
85
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/utils_test.go
generated
vendored
85
vendor/k8s.io/kubernetes/pkg/kubectl/apply/strategy/utils_test.go
generated
vendored
@ -1,85 +0,0 @@
|
||||
/*
|
||||
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 strategy_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply"
|
||||
"k8s.io/kubernetes/pkg/kubectl/apply/parse"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
|
||||
tst "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi/testing"
|
||||
)
|
||||
|
||||
const (
|
||||
hasConflict = true
|
||||
noConflict = false
|
||||
)
|
||||
|
||||
var fakeResources = tst.NewFakeResources(filepath.Join("..", "..", "..", "..", "api", "openapi-spec", "swagger.json"))
|
||||
|
||||
// run parses the openapi and runs the tests
|
||||
func run(instance apply.Strategy, recorded, local, remote, expected map[string]interface{}) {
|
||||
runWith(instance, recorded, local, remote, expected, fakeResources)
|
||||
}
|
||||
|
||||
func runWith(instance apply.Strategy, recorded, local, remote, expected map[string]interface{}, resources openapi.Resources) {
|
||||
parseFactory := parse.Factory{Resources: resources}
|
||||
|
||||
parsed, err := parseFactory.CreateElement(recorded, local, remote)
|
||||
Expect(err).Should(Not(HaveOccurred()))
|
||||
|
||||
merged, err := parsed.Merge(instance)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
Expect(merged.Operation).Should(Equal(apply.SET))
|
||||
Expect(merged.MergedResult).Should(Equal(expected), diff.ObjectDiff(merged.MergedResult, expected))
|
||||
}
|
||||
|
||||
// create parses the yaml string into a map[string]interface{}. Verifies that the string does not have
|
||||
// any tab characters.
|
||||
func create(config string) map[string]interface{} {
|
||||
result := map[string]interface{}{}
|
||||
|
||||
// The yaml parser will throw an obscure error if there are tabs in the yaml. Check for this
|
||||
Expect(strings.Contains(config, "\t")).To(
|
||||
BeFalse(), fmt.Sprintf("Yaml %s cannot contain tabs", config))
|
||||
Expect(yaml.Unmarshal([]byte(config), &result)).Should(
|
||||
Not(HaveOccurred()), fmt.Sprintf("Could not parse config:\n\n%s\n", config))
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func runConflictTest(instance apply.Strategy, recorded, local, remote map[string]interface{}, isConflict bool) {
|
||||
parseFactory := parse.Factory{Resources: fakeResources}
|
||||
parsed, err := parseFactory.CreateElement(recorded, local, remote)
|
||||
Expect(err).Should(Not(HaveOccurred()))
|
||||
|
||||
merged, err := parsed.Merge(instance)
|
||||
if isConflict {
|
||||
Expect(err).Should(HaveOccurred())
|
||||
} else {
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
Expect(merged.Operation).Should(Equal(apply.SET))
|
||||
}
|
||||
}
|
56
vendor/k8s.io/kubernetes/pkg/kubectl/apply/type_element.go
generated
vendored
56
vendor/k8s.io/kubernetes/pkg/kubectl/apply/type_element.go
generated
vendored
@ -1,56 +0,0 @@
|
||||
/*
|
||||
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 apply
|
||||
|
||||
// TypeElement contains the recorded, local and remote values for a field
|
||||
// that is a complex type
|
||||
type TypeElement struct {
|
||||
// FieldMetaImpl contains metadata about the field from openapi
|
||||
FieldMetaImpl
|
||||
|
||||
MapElementData
|
||||
|
||||
// Values contains the combined recorded-local-remote value of each field in the type
|
||||
// Values contains the values in mapElement. Element must contain
|
||||
// a Name matching its key in Values
|
||||
Values map[string]Element
|
||||
}
|
||||
|
||||
// Merge implements Element.Merge
|
||||
func (e TypeElement) Merge(v Strategy) (Result, error) {
|
||||
return v.MergeType(e)
|
||||
}
|
||||
|
||||
// GetValues implements Element.GetValues
|
||||
func (e TypeElement) GetValues() map[string]Element {
|
||||
return e.Values
|
||||
}
|
||||
|
||||
// HasConflict returns ConflictError if some elements in type conflict.
|
||||
func (e TypeElement) HasConflict() error {
|
||||
for _, item := range e.GetValues() {
|
||||
if item, ok := item.(ConflictDetector); ok {
|
||||
if err := item.HasConflict(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ Element = &TypeElement{}
|
||||
var _ ConflictDetector = &TypeElement{}
|
68
vendor/k8s.io/kubernetes/pkg/kubectl/apply/visitor.go
generated
vendored
68
vendor/k8s.io/kubernetes/pkg/kubectl/apply/visitor.go
generated
vendored
@ -1,68 +0,0 @@
|
||||
/*
|
||||
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 apply
|
||||
|
||||
// Strategy implements a strategy for merging recorded, local and remote values contained
|
||||
// in an element and returns the merged result.
|
||||
// Follows the visitor pattern
|
||||
type Strategy interface {
|
||||
// MergeList is invoked by ListElements when Merge is called
|
||||
MergeList(ListElement) (Result, error)
|
||||
|
||||
// MergeMap is invoked by MapElements when Merge is called
|
||||
MergeMap(MapElement) (Result, error)
|
||||
|
||||
// MergeType is invoked by TypeElements when Merge is called
|
||||
MergeType(TypeElement) (Result, error)
|
||||
|
||||
// MergePrimitive is invoked by PrimitiveElements when Merge is called
|
||||
MergePrimitive(PrimitiveElement) (Result, error)
|
||||
|
||||
// MergeEmpty is invoked by EmptyElements when Merge is called
|
||||
MergeEmpty(EmptyElement) (Result, error)
|
||||
}
|
||||
|
||||
// Operation records whether a field should be set or dropped
|
||||
type Operation int
|
||||
|
||||
const (
|
||||
// ERROR is an error during merge
|
||||
ERROR Operation = iota
|
||||
// SET sets the field on an object
|
||||
SET
|
||||
// DROP drops the field from an object
|
||||
DROP
|
||||
)
|
||||
|
||||
// Result is the result of merging fields
|
||||
type Result struct {
|
||||
// Operation is the operation that should be performed for the merged field
|
||||
Operation Operation
|
||||
// MergedResult is the new merged value
|
||||
MergedResult interface{}
|
||||
}
|
||||
|
||||
const (
|
||||
// MergeStrategy is the strategy to merge the local and remote values
|
||||
MergeStrategy = "merge"
|
||||
|
||||
// RetainKeysStrategy is the strategy to merge the local and remote values, but drop any fields not defined locally
|
||||
RetainKeysStrategy = "retainKeys"
|
||||
|
||||
// ReplaceStrategy is the strategy to replace the remote value with the local value
|
||||
ReplaceStrategy = "replace"
|
||||
)
|
42
vendor/k8s.io/kubernetes/pkg/kubectl/apps/BUILD
generated
vendored
42
vendor/k8s.io/kubernetes/pkg/kubectl/apps/BUILD
generated
vendored
@ -1,42 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["kind_visitor.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubectl/apps",
|
||||
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
49
vendor/k8s.io/kubernetes/pkg/kubectl/apps/apps_suite_test.go
generated
vendored
@ -1,49 +0,0 @@
|
||||
/*
|
||||
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") }
|
89
vendor/k8s.io/kubernetes/pkg/kubectl/apps/kind_visitor.go
generated
vendored
89
vendor/k8s.io/kubernetes/pkg/kubectl/apps/kind_visitor.go
generated
vendored
@ -1,89 +0,0 @@
|
||||
/*
|
||||
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)
|
||||
VisitCronJob(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 {
|
||||
switch {
|
||||
case elem.GroupMatch("apps", "extensions") && elem.Kind == "DaemonSet":
|
||||
visitor.VisitDaemonSet(elem)
|
||||
case elem.GroupMatch("apps", "extensions") && elem.Kind == "Deployment":
|
||||
visitor.VisitDeployment(elem)
|
||||
case elem.GroupMatch("batch") && elem.Kind == "Job":
|
||||
visitor.VisitJob(elem)
|
||||
case elem.GroupMatch("", "core") && elem.Kind == "Pod":
|
||||
visitor.VisitPod(elem)
|
||||
case elem.GroupMatch("apps", "extensions") && elem.Kind == "ReplicaSet":
|
||||
visitor.VisitReplicaSet(elem)
|
||||
case elem.GroupMatch("", "core") && elem.Kind == "ReplicationController":
|
||||
visitor.VisitReplicationController(elem)
|
||||
case elem.GroupMatch("apps") && elem.Kind == "StatefulSet":
|
||||
visitor.VisitStatefulSet(elem)
|
||||
case elem.GroupMatch("batch") && elem.Kind == "CronJob":
|
||||
visitor.VisitCronJob(elem)
|
||||
default:
|
||||
return fmt.Errorf("no visitor method exists for %v", elem)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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) {}
|
||||
func (*NoOpKindVisitor) VisitCronJob(kind GroupKindElement) {}
|
185
vendor/k8s.io/kubernetes/pkg/kubectl/apps/kind_visitor_test.go
generated
vendored
185
vendor/k8s.io/kubernetes/pkg/kubectl/apps/kind_visitor_test.go
generated
vendored
@ -1,185 +0,0 @@
|
||||
/*
|
||||
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 Visit CronJob iff the Kind is a CronJob", func() {
|
||||
kind := apps.GroupKindElement{
|
||||
Kind: "CronJob",
|
||||
Group: "batch",
|
||||
}
|
||||
Expect(kind.Accept(visitor)).ShouldNot(HaveOccurred())
|
||||
Expect(visitor.visits).To(Equal(map[string]int{
|
||||
"CronJob": 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) }
|
||||
func (t *TestKindVisitor) VisitCronJob(kind apps.GroupKindElement) { t.Visit(kind) }
|
85
vendor/k8s.io/kubernetes/pkg/kubectl/autoscale.go
generated
vendored
85
vendor/k8s.io/kubernetes/pkg/kubectl/autoscale.go
generated
vendored
@ -1,85 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
autoscalingv1 "k8s.io/api/autoscaling/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// HorizontalPodAutoscalerV1Generator supports stable generation of a horizontal pod autoscaler.
|
||||
type HorizontalPodAutoscalerGeneratorV1 struct {
|
||||
Name string
|
||||
ScaleRefKind string
|
||||
ScaleRefName string
|
||||
ScaleRefApiVersion string
|
||||
MinReplicas int32
|
||||
MaxReplicas int32
|
||||
CPUPercent int32
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters specified during construction.
|
||||
var _ StructuredGenerator = &HorizontalPodAutoscalerGeneratorV1{}
|
||||
|
||||
// StructuredGenerate outputs a horizontal pod autoscaler object using the configured fields.
|
||||
func (s *HorizontalPodAutoscalerGeneratorV1) StructuredGenerate() (runtime.Object, error) {
|
||||
if err := s.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
scaler := autoscalingv1.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: s.Name,
|
||||
},
|
||||
Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
|
||||
ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
|
||||
Kind: s.ScaleRefKind,
|
||||
Name: s.ScaleRefName,
|
||||
APIVersion: s.ScaleRefApiVersion,
|
||||
},
|
||||
MaxReplicas: s.MaxReplicas,
|
||||
},
|
||||
}
|
||||
|
||||
if s.MinReplicas > 0 {
|
||||
v := int32(s.MinReplicas)
|
||||
scaler.Spec.MinReplicas = &v
|
||||
}
|
||||
if s.CPUPercent >= 0 {
|
||||
c := int32(s.CPUPercent)
|
||||
scaler.Spec.TargetCPUUtilizationPercentage = &c
|
||||
}
|
||||
|
||||
return &scaler, nil
|
||||
}
|
||||
|
||||
// validate check if the caller has set the right fields.
|
||||
func (s HorizontalPodAutoscalerGeneratorV1) validate() error {
|
||||
if len(s.Name) == 0 {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
if s.MaxReplicas < 1 {
|
||||
return fmt.Errorf("'max' is a required parameter and must be at least 1")
|
||||
}
|
||||
if s.MinReplicas > s.MaxReplicas {
|
||||
return fmt.Errorf("'max' must be greater than or equal to 'min'")
|
||||
}
|
||||
return nil
|
||||
}
|
128
vendor/k8s.io/kubernetes/pkg/kubectl/autoscale_test.go
generated
vendored
128
vendor/k8s.io/kubernetes/pkg/kubectl/autoscale_test.go
generated
vendored
@ -1,128 +0,0 @@
|
||||
/*
|
||||
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 kubectl
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
autoscalingv1 "k8s.io/api/autoscaling/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
utilpointer "k8s.io/kubernetes/pkg/util/pointer"
|
||||
)
|
||||
|
||||
func TestHPAGenerate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
HPAName string
|
||||
scaleRefKind string
|
||||
scaleRefName string
|
||||
scaleRefApiVersion string
|
||||
minReplicas int32
|
||||
maxReplicas int32
|
||||
CPUPercent int32
|
||||
expected *autoscalingv1.HorizontalPodAutoscaler
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid case",
|
||||
HPAName: "foo",
|
||||
minReplicas: 1,
|
||||
maxReplicas: 10,
|
||||
CPUPercent: 80,
|
||||
scaleRefKind: "kind",
|
||||
scaleRefName: "name",
|
||||
scaleRefApiVersion: "apiVersion",
|
||||
expected: &autoscalingv1.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
|
||||
TargetCPUUtilizationPercentage: utilpointer.Int32Ptr(80),
|
||||
ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
|
||||
Kind: "kind",
|
||||
Name: "name",
|
||||
APIVersion: "apiVersion",
|
||||
},
|
||||
MaxReplicas: int32(10),
|
||||
MinReplicas: utilpointer.Int32Ptr(1),
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "'name' is a required parameter",
|
||||
scaleRefKind: "kind",
|
||||
scaleRefName: "name",
|
||||
scaleRefApiVersion: "apiVersion",
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "'max' is a required parameter",
|
||||
HPAName: "foo",
|
||||
scaleRefKind: "kind",
|
||||
scaleRefName: "name",
|
||||
scaleRefApiVersion: "apiVersion",
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "'max' must be greater than or equal to 'min'",
|
||||
HPAName: "foo",
|
||||
minReplicas: 10,
|
||||
maxReplicas: 1,
|
||||
scaleRefKind: "kind",
|
||||
scaleRefName: "name",
|
||||
scaleRefApiVersion: "apiVersion",
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "'max' must be at least 1",
|
||||
HPAName: "foo",
|
||||
minReplicas: 1,
|
||||
maxReplicas: -10,
|
||||
scaleRefKind: "kind",
|
||||
scaleRefName: "name",
|
||||
scaleRefApiVersion: "apiVersion",
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
generator := HorizontalPodAutoscalerGeneratorV1{
|
||||
Name: test.HPAName,
|
||||
ScaleRefKind: test.scaleRefKind,
|
||||
ScaleRefName: test.scaleRefName,
|
||||
ScaleRefApiVersion: test.scaleRefApiVersion,
|
||||
MinReplicas: test.minReplicas,
|
||||
MaxReplicas: test.maxReplicas,
|
||||
CPUPercent: test.CPUPercent,
|
||||
}
|
||||
obj, err := generator.StructuredGenerate()
|
||||
if test.expectErr && err != nil {
|
||||
continue
|
||||
}
|
||||
if !test.expectErr && err != nil {
|
||||
t.Errorf("[%s] unexpected error: %v", test.name, err)
|
||||
}
|
||||
if test.expectErr && err == nil {
|
||||
t.Errorf("[%s] expect error, got nil", test.name)
|
||||
}
|
||||
if !reflect.DeepEqual(obj.(*autoscalingv1.HorizontalPodAutoscaler), test.expected) {
|
||||
t.Errorf("[%s] want:\n%#v\ngot:\n%#v", test.name, test.expected, obj.(*autoscalingv1.HorizontalPodAutoscaler))
|
||||
}
|
||||
}
|
||||
}
|
158
vendor/k8s.io/kubernetes/pkg/kubectl/clusterrolebinding.go
generated
vendored
158
vendor/k8s.io/kubernetes/pkg/kubectl/clusterrolebinding.go
generated
vendored
@ -1,158 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
rbacv1beta1 "k8s.io/api/rbac/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
)
|
||||
|
||||
// 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 := &rbacv1beta1.ClusterRoleBinding{}
|
||||
clusterRoleBinding.Name = s.Name
|
||||
clusterRoleBinding.RoleRef = rbacv1beta1.RoleRef{
|
||||
APIGroup: rbacv1beta1.GroupName,
|
||||
Kind: "ClusterRole",
|
||||
Name: s.ClusterRole,
|
||||
}
|
||||
for _, user := range sets.NewString(s.Users...).List() {
|
||||
clusterRoleBinding.Subjects = append(clusterRoleBinding.Subjects, rbacv1beta1.Subject{
|
||||
Kind: rbacv1beta1.UserKind,
|
||||
APIGroup: rbacv1beta1.GroupName,
|
||||
Name: user,
|
||||
})
|
||||
}
|
||||
for _, group := range sets.NewString(s.Groups...).List() {
|
||||
clusterRoleBinding.Subjects = append(clusterRoleBinding.Subjects, rbacv1beta1.Subject{
|
||||
Kind: rbacv1beta1.GroupKind,
|
||||
APIGroup: rbacv1beta1.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, rbacv1beta1.Subject{
|
||||
Kind: rbacv1beta1.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
|
||||
}
|
229
vendor/k8s.io/kubernetes/pkg/kubectl/clusterrolebinding_test.go
generated
vendored
229
vendor/k8s.io/kubernetes/pkg/kubectl/clusterrolebinding_test.go
generated
vendored
@ -1,229 +0,0 @@
|
||||
/*
|
||||
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 kubectl
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
rbacv1beta1 "k8s.io/api/rbac/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestClusterRoleBindingGenerate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
params map[string]interface{}
|
||||
expected *rbacv1beta1.ClusterRoleBinding
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid case 1",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"clusterrole": "admin",
|
||||
"user": []string{"user"},
|
||||
"group": []string{"group"},
|
||||
"serviceaccount": []string{"ns1:name1"},
|
||||
},
|
||||
expected: &rbacv1beta1.ClusterRoleBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
RoleRef: rbacv1beta1.RoleRef{
|
||||
APIGroup: rbacv1beta1.GroupName,
|
||||
Kind: "ClusterRole",
|
||||
Name: "admin",
|
||||
},
|
||||
Subjects: []rbacv1beta1.Subject{
|
||||
{
|
||||
APIGroup: rbacv1beta1.GroupName,
|
||||
Kind: rbacv1beta1.UserKind,
|
||||
Name: "user",
|
||||
},
|
||||
{
|
||||
APIGroup: rbacv1beta1.GroupName,
|
||||
Kind: rbacv1beta1.GroupKind,
|
||||
Name: "group",
|
||||
},
|
||||
{
|
||||
Kind: rbacv1beta1.ServiceAccountKind,
|
||||
APIGroup: "",
|
||||
Namespace: "ns1",
|
||||
Name: "name1",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid case 2",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"clusterrole": "admin",
|
||||
"user": []string{"user1", "user2"},
|
||||
"group": []string{"group1", "group2"},
|
||||
"serviceaccount": []string{"ns1:name1", "ns2:name2"},
|
||||
},
|
||||
expected: &rbacv1beta1.ClusterRoleBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
RoleRef: rbacv1beta1.RoleRef{
|
||||
APIGroup: rbacv1beta1.GroupName,
|
||||
Kind: "ClusterRole",
|
||||
Name: "admin",
|
||||
},
|
||||
Subjects: []rbacv1beta1.Subject{
|
||||
{
|
||||
APIGroup: rbacv1beta1.GroupName,
|
||||
Kind: rbacv1beta1.UserKind,
|
||||
Name: "user1",
|
||||
},
|
||||
{
|
||||
APIGroup: rbacv1beta1.GroupName,
|
||||
Kind: rbacv1beta1.UserKind,
|
||||
Name: "user2",
|
||||
},
|
||||
{
|
||||
APIGroup: rbacv1beta1.GroupName,
|
||||
Kind: rbacv1beta1.GroupKind,
|
||||
Name: "group1",
|
||||
},
|
||||
{
|
||||
APIGroup: rbacv1beta1.GroupName,
|
||||
Kind: rbacv1beta1.GroupKind,
|
||||
Name: "group2",
|
||||
},
|
||||
{
|
||||
Kind: rbacv1beta1.ServiceAccountKind,
|
||||
APIGroup: "",
|
||||
Namespace: "ns1",
|
||||
Name: "name1",
|
||||
},
|
||||
{
|
||||
Kind: rbacv1beta1.ServiceAccountKind,
|
||||
APIGroup: "",
|
||||
Namespace: "ns2",
|
||||
Name: "name2",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid case 3",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"clusterrole": "admin",
|
||||
},
|
||||
expected: &rbacv1beta1.ClusterRoleBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
RoleRef: rbacv1beta1.RoleRef{
|
||||
APIGroup: rbacv1beta1.GroupName,
|
||||
Kind: "ClusterRole",
|
||||
Name: "admin",
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid serviceaccount, expected format: <namespace:name>",
|
||||
params: map[string]interface{}{
|
||||
"name": "role",
|
||||
"clusterrole": "admin",
|
||||
"user": []string{"user"},
|
||||
"group": []string{"group"},
|
||||
"serviceaccount": []string{"ns1-name1"},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "name must be specified",
|
||||
params: map[string]interface{}{
|
||||
"name": "",
|
||||
"clusterrole": "admin",
|
||||
"user": []string{"user"},
|
||||
"group": []string{"group"},
|
||||
"serviceaccount": []string{"ns1:name1"},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "clusterrole must be specified",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"clusterrole": "",
|
||||
"user": []string{"user"},
|
||||
"group": []string{"group"},
|
||||
"serviceaccount": []string{"ns1:name1"},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "expected user []string",
|
||||
params: map[string]interface{}{
|
||||
"name": "role",
|
||||
"clusterrole": "admin",
|
||||
"user": "user",
|
||||
"group": []string{"group"},
|
||||
"serviceaccount": []string{"ns1:name1"},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "expected group []string",
|
||||
params: map[string]interface{}{
|
||||
"name": "role",
|
||||
"clusterrole": "admin",
|
||||
"user": []string{"user"},
|
||||
"group": "group",
|
||||
"serviceaccount": []string{"ns1:name1"},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "expected serviceaccount []string",
|
||||
params: map[string]interface{}{
|
||||
"name": "role",
|
||||
"clusterrole": "admin",
|
||||
"user": []string{"user"},
|
||||
"group": []string{"group"},
|
||||
"serviceaccount": "ns1",
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
generator := ClusterRoleBindingGeneratorV1{}
|
||||
for i := range tests {
|
||||
obj, err := generator.Generate(tests[i].params)
|
||||
if !tests[i].expectErr && err != nil {
|
||||
t.Errorf("[%d] unexpected error: %v", i, err)
|
||||
}
|
||||
if tests[i].expectErr && err != nil {
|
||||
continue
|
||||
}
|
||||
if tests[i].expectErr && err == nil {
|
||||
t.Errorf("[%s] expect error, got nil", tests[i].name)
|
||||
}
|
||||
if !reflect.DeepEqual(obj.(*rbacv1beta1.ClusterRoleBinding), tests[i].expected) {
|
||||
t.Errorf("\n[%s] want:\n%#v\ngot:\n%#v", tests[i].name, tests[i].expected, obj.(*rbacv1beta1.ClusterRoleBinding))
|
||||
}
|
||||
}
|
||||
}
|
274
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/BUILD
generated
vendored
274
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/BUILD
generated
vendored
@ -1,274 +0,0 @@
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"alpha.go",
|
||||
"annotate.go",
|
||||
"apiresources.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",
|
||||
"delete.go",
|
||||
"delete_flags.go",
|
||||
"describe.go",
|
||||
"diff.go",
|
||||
"drain.go",
|
||||
"edit.go",
|
||||
"exec.go",
|
||||
"explain.go",
|
||||
"expose.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",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubectl/cmd",
|
||||
visibility = [
|
||||
"//build/visible_to:pkg_kubectl_cmd_CONSUMERS",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/apis/certificates:go_default_library",
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/core/validation:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset: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/kubectl:go_default_library",
|
||||
"//pkg/kubectl/apply/parse:go_default_library",
|
||||
"//pkg/kubectl/apply/strategy:go_default_library",
|
||||
"//pkg/kubectl/cmd/auth:go_default_library",
|
||||
"//pkg/kubectl/cmd/config:go_default_library",
|
||||
"//pkg/kubectl/cmd/create:go_default_library",
|
||||
"//pkg/kubectl/cmd/get:go_default_library",
|
||||
"//pkg/kubectl/cmd/rollout:go_default_library",
|
||||
"//pkg/kubectl/cmd/scalejob: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/cmd/wait:go_default_library",
|
||||
"//pkg/kubectl/explain:go_default_library",
|
||||
"//pkg/kubectl/genericclioptions:go_default_library",
|
||||
"//pkg/kubectl/genericclioptions/printers:go_default_library",
|
||||
"//pkg/kubectl/genericclioptions/resource:go_default_library",
|
||||
"//pkg/kubectl/metricsutil:go_default_library",
|
||||
"//pkg/kubectl/plugins:go_default_library",
|
||||
"//pkg/kubectl/polymorphichelpers:go_default_library",
|
||||
"//pkg/kubectl/proxy:go_default_library",
|
||||
"//pkg/kubectl/scheme:go_default_library",
|
||||
"//pkg/kubectl/util:go_default_library",
|
||||
"//pkg/kubectl/util/i18n:go_default_library",
|
||||
"//pkg/kubectl/util/term:go_default_library",
|
||||
"//pkg/kubectl/validation:go_default_library",
|
||||
"//pkg/printers: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/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/autoscaling/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/policy/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/apis/meta/v1/unstructured/unstructuredscheme: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/net: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/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/dynamic:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/typed/autoscaling/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/scale:go_default_library",
|
||||
"//vendor/k8s.io/client-go/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/kube-openapi/pkg/util/proto:go_default_library",
|
||||
"//vendor/k8s.io/metrics/pkg/apis/metrics:go_default_library",
|
||||
"//vendor/k8s.io/metrics/pkg/apis/metrics/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/metrics/pkg/client/clientset_generated/clientset:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"annotate_test.go",
|
||||
"apply_test.go",
|
||||
"attach_test.go",
|
||||
"clusterinfo_dump_test.go",
|
||||
"cmd_printing_test.go",
|
||||
"cmd_test.go",
|
||||
"convert_test.go",
|
||||
"cp_test.go",
|
||||
"delete_test.go",
|
||||
"describe_test.go",
|
||||
"diff_test.go",
|
||||
"drain_test.go",
|
||||
"edit_test.go",
|
||||
"exec_test.go",
|
||||
"expose_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",
|
||||
"//api/openapi-spec:swagger-spec",
|
||||
"//test/e2e/testing-manifests:all-srcs",
|
||||
"//test/fixtures",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/api/testapi:go_default_library",
|
||||
"//pkg/api/testing:go_default_library",
|
||||
"//pkg/apis/batch:go_default_library",
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset:go_default_library",
|
||||
"//pkg/kubectl/cmd/create: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/genericclioptions:go_default_library",
|
||||
"//pkg/kubectl/genericclioptions/printers:go_default_library",
|
||||
"//pkg/kubectl/genericclioptions/resource:go_default_library",
|
||||
"//pkg/kubectl/plugins:go_default_library",
|
||||
"//pkg/kubectl/polymorphichelpers:go_default_library",
|
||||
"//pkg/kubectl/scheme:go_default_library",
|
||||
"//pkg/kubectl/util/i18n:go_default_library",
|
||||
"//pkg/kubectl/util/term:go_default_library",
|
||||
"//pkg/printers:go_default_library",
|
||||
"//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library",
|
||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||
"//vendor/gopkg.in/yaml.v2:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/policy/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/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/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/strategicpatch/testing:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/version:go_default_library",
|
||||
"//vendor/k8s.io/client-go/dynamic/fake:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest/fake:go_default_library",
|
||||
"//vendor/k8s.io/client-go/testing:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/remotecommand:go_default_library",
|
||||
"//vendor/k8s.io/metrics/pkg/apis/metrics/v1alpha1:go_default_library",
|
||||
"//vendor/k8s.io/metrics/pkg/apis/metrics/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/metrics/pkg/client/clientset_generated/clientset/fake:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
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/create:all-srcs",
|
||||
"//pkg/kubectl/cmd/get:all-srcs",
|
||||
"//pkg/kubectl/cmd/rollout:all-srcs",
|
||||
"//pkg/kubectl/cmd/scalejob: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",
|
||||
"//pkg/kubectl/cmd/wait:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
visibility = [
|
||||
"//build/visible_to:pkg_kubectl_cmd_CONSUMERS",
|
||||
],
|
||||
)
|
50
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/alpha.go
generated
vendored
50
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/alpha.go
generated
vendored
@ -1,50 +0,0 @@
|
||||
/*
|
||||
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 (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
"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, streams genericclioptions.IOStreams) *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))
|
||||
cmd.AddCommand(NewCmdDiff(f, streams))
|
||||
|
||||
// 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
|
||||
}
|
383
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/annotate.go
generated
vendored
383
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/annotate.go
generated
vendored
@ -1,383 +0,0 @@
|
||||
/*
|
||||
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/genericclioptions"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"
|
||||
"k8s.io/kubernetes/pkg/kubectl/scheme"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
// AnnotateOptions have the data required to perform the annotate operation
|
||||
type AnnotateOptions struct {
|
||||
PrintFlags *genericclioptions.PrintFlags
|
||||
PrintObj printers.ResourcePrinterFunc
|
||||
|
||||
// Filename options
|
||||
resource.FilenameOptions
|
||||
RecordFlags *genericclioptions.RecordFlags
|
||||
|
||||
// Common user flags
|
||||
overwrite bool
|
||||
local bool
|
||||
dryrun bool
|
||||
all bool
|
||||
resourceVersion string
|
||||
selector string
|
||||
fieldSelector string
|
||||
outputFormat string
|
||||
|
||||
// results of arg parsing
|
||||
resources []string
|
||||
newAnnotations map[string]string
|
||||
removeAnnotations []string
|
||||
Recorder genericclioptions.Recorder
|
||||
namespace string
|
||||
enforceNamespace bool
|
||||
builder *resource.Builder
|
||||
unstructuredClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error)
|
||||
includeUninitialized bool
|
||||
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
var (
|
||||
annotateLong = templates.LongDesc(`
|
||||
Update the annotations on one or more resources
|
||||
|
||||
All Kubernetes objects support the ability to store additional data with the object as
|
||||
annotations. Annotations are key/value pairs that can be larger than labels and include
|
||||
arbitrary string values such as structured JSON. Tools and system extensions may use
|
||||
annotations to store their own data.
|
||||
|
||||
Attempting to set an annotation that already exists will fail unless --overwrite is set.
|
||||
If --resource-version is specified and does not match the current resource version on
|
||||
the server the command will fail.`)
|
||||
|
||||
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 NewAnnotateOptions(ioStreams genericclioptions.IOStreams) *AnnotateOptions {
|
||||
return &AnnotateOptions{
|
||||
PrintFlags: genericclioptions.NewPrintFlags("annotated").WithTypeSetter(scheme.Scheme),
|
||||
|
||||
RecordFlags: genericclioptions.NewRecordFlags(),
|
||||
Recorder: genericclioptions.NoopRecorder{},
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
func NewCmdAnnotate(parent string, f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewAnnotateOptions(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "annotate [--overwrite] (-f FILENAME | TYPE NAME) KEY_1=VAL_1 ... KEY_N=VAL_N [--resource-version=version]",
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: i18n.T("Update the annotations on a resource"),
|
||||
Long: annotateLong + "\n\n" + cmdutil.SuggestApiResources(parent),
|
||||
Example: annotateExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate())
|
||||
cmdutil.CheckErr(o.RunAnnotate())
|
||||
},
|
||||
}
|
||||
|
||||
// bind flag structs
|
||||
o.RecordFlags.AddFlags(cmd)
|
||||
o.PrintFlags.AddFlags(cmd)
|
||||
|
||||
cmdutil.AddIncludeUninitializedFlag(cmd)
|
||||
cmd.Flags().BoolVar(&o.overwrite, "overwrite", o.overwrite, "If true, allow annotations to be overwritten, otherwise reject annotation updates that overwrite existing annotations.")
|
||||
cmd.Flags().BoolVar(&o.local, "local", o.local, "If true, annotation will NOT contact api-server but run locally.")
|
||||
cmd.Flags().StringVarP(&o.selector, "selector", "l", o.selector, "Selector (label query) to filter on, not including uninitialized ones, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2).")
|
||||
cmd.Flags().StringVar(&o.fieldSelector, "field-selector", o.fieldSelector, "Selector (field query) to filter on, supports '=', '==', and '!='.(e.g. --field-selector key1=value1,key2=value2). The server only supports a limited number of field queries per type.")
|
||||
cmd.Flags().BoolVar(&o.all, "all", o.all, "Select all resources, including uninitialized ones, in the namespace of the specified resource types.")
|
||||
cmd.Flags().StringVar(&o.resourceVersion, "resource-version", o.resourceVersion, i18n.T("If non-empty, the annotation update will only succeed if this is the current resource-version for the object. Only valid when specifying a single resource."))
|
||||
usage := "identifying the resource to update the annotation"
|
||||
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
|
||||
cmdutil.AddDryRunFlag(cmd)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete adapts from the command line args and factory to the data required.
|
||||
func (o *AnnotateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
|
||||
o.RecordFlags.Complete(cmd)
|
||||
o.Recorder, err = o.RecordFlags.ToRecorder()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.outputFormat = cmdutil.GetFlagString(cmd, "output")
|
||||
o.dryrun = cmdutil.GetDryRunFlag(cmd)
|
||||
|
||||
if o.dryrun {
|
||||
o.PrintFlags.Complete("%s (dry run)")
|
||||
}
|
||||
printer, err := o.PrintFlags.ToPrinter()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.PrintObj = func(obj runtime.Object, out io.Writer) error {
|
||||
return printer.PrintObj(obj, out)
|
||||
}
|
||||
|
||||
o.namespace, o.enforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.includeUninitialized = cmdutil.ShouldIncludeUninitialized(cmd, false)
|
||||
o.builder = f.NewBuilder()
|
||||
o.unstructuredClientForMapping = f.UnstructuredClientForMapping
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate checks to the AnnotateOptions to see if there is sufficient information run the command.
|
||||
func (o AnnotateOptions) Validate() error {
|
||||
if o.all && len(o.selector) > 0 {
|
||||
return fmt.Errorf("cannot set --all and --selector at the same time")
|
||||
}
|
||||
if o.all && len(o.fieldSelector) > 0 {
|
||||
return fmt.Errorf("cannot set --all and --field-selector at the same time")
|
||||
}
|
||||
if len(o.resources) < 1 && cmdutil.IsFilenameSliceEmpty(o.Filenames) {
|
||||
return fmt.Errorf("one or more resources must be specified as <resource> <name> or <resource>/<name>")
|
||||
}
|
||||
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() error {
|
||||
b := o.builder.
|
||||
Unstructured().
|
||||
LocalParam(o.local).
|
||||
ContinueOnError().
|
||||
NamespaceParam(o.namespace).DefaultNamespace().
|
||||
FilenameParam(o.enforceNamespace, &o.FilenameOptions).
|
||||
IncludeUninitialized(o.includeUninitialized).
|
||||
Flatten()
|
||||
|
||||
if !o.local {
|
||||
b = b.LabelSelectorParam(o.selector).
|
||||
FieldSelectorParam(o.fieldSelector).
|
||||
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
|
||||
obj := info.Object
|
||||
|
||||
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 err := o.Recorder.Record(info.Object); err != nil {
|
||||
glog.V(4).Infof("error recording current command: %v", err)
|
||||
}
|
||||
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 := o.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
|
||||
}
|
||||
}
|
||||
|
||||
return o.PrintObj(outputObj, o.Out)
|
||||
})
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
643
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/annotate_test.go
generated
vendored
643
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/annotate_test.go
generated
vendored
@ -1,643 +0,0 @@
|
||||
/*
|
||||
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 (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/rest/fake"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
"k8s.io/kubernetes/pkg/kubectl/scheme"
|
||||
)
|
||||
|
||||
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, "fake.kubernetes.io/annotation=" + testJSON},
|
||||
expected: map[string]string{"url": testURL, "fake.kubernetes.io/annotation": 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: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"a": "b"},
|
||||
},
|
||||
},
|
||||
annotations: map[string]string{"a": "b"},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
obj: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"a": "b"},
|
||||
},
|
||||
},
|
||||
annotations: map[string]string{"a": "c"},
|
||||
overwrite: true,
|
||||
expected: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"a": "c"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
obj: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"a": "b"},
|
||||
},
|
||||
},
|
||||
annotations: map[string]string{"c": "d"},
|
||||
expected: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"a": "b", "c": "d"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
obj: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"a": "b"},
|
||||
},
|
||||
},
|
||||
annotations: map[string]string{"c": "d"},
|
||||
version: "2",
|
||||
expected: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"a": "b", "c": "d"},
|
||||
ResourceVersion: "2",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
obj: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"a": "b"},
|
||||
},
|
||||
},
|
||||
annotations: map[string]string{},
|
||||
remove: []string{"a"},
|
||||
expected: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
obj: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"a": "b", "c": "d"},
|
||||
},
|
||||
},
|
||||
annotations: map[string]string{"e": "f"},
|
||||
remove: []string{"a"},
|
||||
expected: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
"c": "d",
|
||||
"e": "f",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
obj: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"a": "b", "c": "d"},
|
||||
},
|
||||
},
|
||||
annotations: map[string]string{"e": "f"},
|
||||
remove: []string{"g"},
|
||||
expected: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
"a": "b",
|
||||
"c": "d",
|
||||
"e": "f",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
obj: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"a": "b", "c": "d"},
|
||||
},
|
||||
},
|
||||
remove: []string{"e"},
|
||||
expected: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
"a": "b",
|
||||
"c": "d",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
obj: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{},
|
||||
},
|
||||
annotations: map[string]string{"a": "b"},
|
||||
expected: &v1.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 {
|
||||
t.Run(k, func(t *testing.T) {
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
tf.ClientConfigVal = defaultClientConfig()
|
||||
|
||||
iostreams, _, bufOut, bufErr := genericclioptions.NewTestIOStreams()
|
||||
cmd := NewCmdAnnotate("kubectl", tf, iostreams)
|
||||
cmd.SetOutput(bufOut)
|
||||
|
||||
for k, v := range testCase.flags {
|
||||
cmd.Flags().Set(k, v)
|
||||
}
|
||||
options := NewAnnotateOptions(iostreams)
|
||||
err := options.Complete(tf, cmd, testCase.args)
|
||||
if err == nil {
|
||||
err = options.Validate()
|
||||
}
|
||||
if !testCase.errFn(err) {
|
||||
t.Errorf("%s: unexpected error: %v", k, err)
|
||||
return
|
||||
}
|
||||
if bufOut.Len() > 0 {
|
||||
t.Errorf("buffer should be empty: %s", string(bufOut.Bytes()))
|
||||
}
|
||||
if bufErr.Len() > 0 {
|
||||
t.Errorf("buffer should be empty: %s", string(bufErr.Bytes()))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnnotateObject(t *testing.T) {
|
||||
pods, _, _ := testData()
|
||||
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
GroupVersion: schema.GroupVersion{Group: "testgroup", Version: "v1"},
|
||||
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.ClientConfigVal = defaultClientConfig()
|
||||
|
||||
iostreams, _, bufOut, _ := genericclioptions.NewTestIOStreams()
|
||||
cmd := NewCmdAnnotate("kubectl", tf, iostreams)
|
||||
cmd.SetOutput(bufOut)
|
||||
options := NewAnnotateOptions(iostreams)
|
||||
args := []string{"pods/foo", "a=b", "c-"}
|
||||
if err := options.Complete(tf, 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(); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnnotateObjectFromFile(t *testing.T) {
|
||||
pods, _, _ := testData()
|
||||
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
GroupVersion: schema.GroupVersion{Group: "testgroup", Version: "v1"},
|
||||
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.ClientConfigVal = defaultClientConfig()
|
||||
|
||||
iostreams, _, bufOut, _ := genericclioptions.NewTestIOStreams()
|
||||
cmd := NewCmdAnnotate("kubectl", tf, iostreams)
|
||||
cmd.SetOutput(bufOut)
|
||||
options := NewAnnotateOptions(iostreams)
|
||||
options.Filenames = []string{"../../../test/e2e/testing-manifests/statefulset/cassandra/controller.yaml"}
|
||||
args := []string{"a=b", "c-"}
|
||||
if err := options.Complete(tf, 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(); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnnotateLocal(t *testing.T) {
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
GroupVersion: schema.GroupVersion{Group: "testgroup", Version: "v1"},
|
||||
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.ClientConfigVal = defaultClientConfig()
|
||||
|
||||
iostreams, _, _, _ := genericclioptions.NewTestIOStreams()
|
||||
cmd := NewCmdAnnotate("kubectl", tf, iostreams)
|
||||
options := NewAnnotateOptions(iostreams)
|
||||
options.local = true
|
||||
options.Filenames = []string{"../../../test/e2e/testing-manifests/statefulset/cassandra/controller.yaml"}
|
||||
args := []string{"a=b"}
|
||||
if err := options.Complete(tf, 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(); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnnotateMultipleObjects(t *testing.T) {
|
||||
pods, _, _ := testData()
|
||||
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
GroupVersion: schema.GroupVersion{Group: "testgroup", Version: "v1"},
|
||||
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.ClientConfigVal = defaultClientConfig()
|
||||
|
||||
iostreams, _, _, _ := genericclioptions.NewTestIOStreams()
|
||||
cmd := NewCmdAnnotate("kubectl", tf, iostreams)
|
||||
cmd.SetOutput(iostreams.Out)
|
||||
options := NewAnnotateOptions(iostreams)
|
||||
options.all = true
|
||||
args := []string{"pods", "a=b", "c-"}
|
||||
if err := options.Complete(tf, 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(); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
229
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/apiresources.go
generated
vendored
229
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/apiresources.go
generated
vendored
@ -1,229 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
)
|
||||
|
||||
var (
|
||||
apiresourcesExample = templates.Examples(`
|
||||
# Print the supported API Resources
|
||||
kubectl api-resources
|
||||
|
||||
# Print the supported API Resources with more information
|
||||
kubectl api-resources -o wide
|
||||
|
||||
# Print the supported namespaced resources
|
||||
kubectl api-resources --namespaced=true
|
||||
|
||||
# Print the supported non-namespaced resources
|
||||
kubectl api-resources --namespaced=false
|
||||
|
||||
# Print the supported API Resources with specific APIGroup
|
||||
kubectl api-resources --api-group=extensions`)
|
||||
)
|
||||
|
||||
// ApiResourcesOptions 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 ApiResourcesOptions struct {
|
||||
Output string
|
||||
APIGroup string
|
||||
Namespaced bool
|
||||
Verbs []string
|
||||
NoHeaders bool
|
||||
Cached bool
|
||||
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
// groupResource contains the APIGroup and APIResource
|
||||
type groupResource struct {
|
||||
APIGroup string
|
||||
APIResource metav1.APIResource
|
||||
}
|
||||
|
||||
func NewAPIResourceOptions(ioStreams genericclioptions.IOStreams) *ApiResourcesOptions {
|
||||
return &ApiResourcesOptions{
|
||||
IOStreams: ioStreams,
|
||||
Namespaced: true,
|
||||
}
|
||||
}
|
||||
|
||||
func NewCmdApiResources(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewAPIResourceOptions(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "api-resources",
|
||||
Short: "Print the supported API resources on the server",
|
||||
Long: "Print the supported API resources on the server",
|
||||
Example: apiresourcesExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Validate(cmd))
|
||||
cmdutil.CheckErr(o.RunApiResources(cmd, f))
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVar(&o.NoHeaders, "no-headers", o.NoHeaders, "When using the default or custom-column output format, don't print headers (default print headers).")
|
||||
cmd.Flags().StringVarP(&o.Output, "output", "o", o.Output, "Output format. One of: wide|name.")
|
||||
|
||||
cmd.Flags().StringVar(&o.APIGroup, "api-group", o.APIGroup, "Limit to resources in the specified API group.")
|
||||
cmd.Flags().BoolVar(&o.Namespaced, "namespaced", o.Namespaced, "If false, non-namespaced resources will be returned, otherwise returning namespaced resources by default.")
|
||||
cmd.Flags().StringSliceVar(&o.Verbs, "verbs", o.Verbs, "Limit to resources that support the specified verbs.")
|
||||
cmd.Flags().BoolVar(&o.Cached, "cached", o.Cached, "Use the cached list of resources if available.")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *ApiResourcesOptions) Validate(cmd *cobra.Command) error {
|
||||
supportedOutputTypes := sets.NewString("", "wide", "name")
|
||||
if !supportedOutputTypes.Has(o.Output) {
|
||||
return fmt.Errorf("--output %v is not available", o.Output)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *ApiResourcesOptions) RunApiResources(cmd *cobra.Command, f cmdutil.Factory) error {
|
||||
w := printers.GetNewTabWriter(o.Out)
|
||||
defer w.Flush()
|
||||
|
||||
discoveryclient, err := f.ToDiscoveryClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !o.Cached {
|
||||
// Always request fresh data from the server
|
||||
discoveryclient.Invalidate()
|
||||
}
|
||||
|
||||
lists, err := discoveryclient.ServerPreferredResources()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resources := []groupResource{}
|
||||
|
||||
groupChanged := cmd.Flags().Changed("api-group")
|
||||
nsChanged := cmd.Flags().Changed("namespaced")
|
||||
|
||||
for _, list := range lists {
|
||||
if len(list.APIResources) == 0 {
|
||||
continue
|
||||
}
|
||||
gv, err := schema.ParseGroupVersion(list.GroupVersion)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, resource := range list.APIResources {
|
||||
if len(resource.Verbs) == 0 {
|
||||
continue
|
||||
}
|
||||
// filter apiGroup
|
||||
if groupChanged && o.APIGroup != gv.Group {
|
||||
continue
|
||||
}
|
||||
// filter namespaced
|
||||
if nsChanged && o.Namespaced != resource.Namespaced {
|
||||
continue
|
||||
}
|
||||
// filter to resources that support the specified verbs
|
||||
if len(o.Verbs) > 0 && !sets.NewString(resource.Verbs...).HasAll(o.Verbs...) {
|
||||
continue
|
||||
}
|
||||
resources = append(resources, groupResource{
|
||||
APIGroup: gv.Group,
|
||||
APIResource: resource,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if o.NoHeaders == false && o.Output != "name" {
|
||||
if err = printContextHeaders(w, o.Output); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
sort.Stable(sortableGroupResource(resources))
|
||||
for _, r := range resources {
|
||||
switch o.Output {
|
||||
case "name":
|
||||
name := r.APIResource.Name
|
||||
if len(r.APIGroup) > 0 {
|
||||
name += "." + r.APIGroup
|
||||
}
|
||||
if _, err := fmt.Fprintf(w, "%s\n", name); err != nil {
|
||||
return err
|
||||
}
|
||||
case "wide":
|
||||
if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%v\t%s\t%v\n",
|
||||
r.APIResource.Name,
|
||||
strings.Join(r.APIResource.ShortNames, ","),
|
||||
r.APIGroup,
|
||||
r.APIResource.Namespaced,
|
||||
r.APIResource.Kind,
|
||||
r.APIResource.Verbs); err != nil {
|
||||
return err
|
||||
}
|
||||
case "":
|
||||
if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%v\t%s\n",
|
||||
r.APIResource.Name,
|
||||
strings.Join(r.APIResource.ShortNames, ","),
|
||||
r.APIGroup,
|
||||
r.APIResource.Namespaced,
|
||||
r.APIResource.Kind); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func printContextHeaders(out io.Writer, output string) error {
|
||||
columnNames := []string{"NAME", "SHORTNAMES", "APIGROUP", "NAMESPACED", "KIND"}
|
||||
if output == "wide" {
|
||||
columnNames = append(columnNames, "VERBS")
|
||||
}
|
||||
_, err := fmt.Fprintf(out, "%s\n", strings.Join(columnNames, "\t"))
|
||||
return err
|
||||
}
|
||||
|
||||
type sortableGroupResource []groupResource
|
||||
|
||||
func (s sortableGroupResource) Len() int { return len(s) }
|
||||
func (s sortableGroupResource) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
func (s sortableGroupResource) Less(i, j int) bool {
|
||||
ret := strings.Compare(s[i].APIGroup, s[j].APIGroup)
|
||||
if ret > 0 {
|
||||
return false
|
||||
} else if ret == 0 {
|
||||
return strings.Compare(s[i].APIResource.Name, s[j].APIResource.Name) < 0
|
||||
}
|
||||
return true
|
||||
}
|
89
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/apiversions.go
generated
vendored
89
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/apiversions.go
generated
vendored
@ -1,89 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
"sort"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
var (
|
||||
apiversionsExample = templates.Examples(i18n.T(`
|
||||
# Print the supported API versions
|
||||
kubectl api-versions`))
|
||||
)
|
||||
|
||||
type ApiVersionsOptions struct {
|
||||
discoveryClient discovery.CachedDiscoveryInterface
|
||||
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
func NewApiVersionsOptions(ioStreams genericclioptions.IOStreams) *ApiVersionsOptions {
|
||||
return &ApiVersionsOptions{
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
func NewCmdApiVersions(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewApiVersionsOptions(ioStreams)
|
||||
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) {
|
||||
cmdutil.CheckErr(o.Complete(f))
|
||||
cmdutil.CheckErr(o.RunApiVersions())
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *ApiVersionsOptions) Complete(f cmdutil.Factory) error {
|
||||
var err error
|
||||
o.discoveryClient, err = f.ToDiscoveryClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *ApiVersionsOptions) RunApiVersions() error {
|
||||
// Always request fresh data from the server
|
||||
o.discoveryClient.Invalidate()
|
||||
|
||||
groupList, err := o.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(o.Out, v)
|
||||
}
|
||||
return nil
|
||||
}
|
791
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/apply.go
generated
vendored
791
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/apply.go
generated
vendored
@ -1,791 +0,0 @@
|
||||
/*
|
||||
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/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"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/client-go/dynamic"
|
||||
oapi "k8s.io/kube-openapi/pkg/util/proto"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"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/openapi"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"
|
||||
"k8s.io/kubernetes/pkg/kubectl/scheme"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
"k8s.io/kubernetes/pkg/kubectl/validation"
|
||||
)
|
||||
|
||||
type ApplyOptions struct {
|
||||
RecordFlags *genericclioptions.RecordFlags
|
||||
Recorder genericclioptions.Recorder
|
||||
|
||||
PrintFlags *genericclioptions.PrintFlags
|
||||
ToPrinter func(string) (printers.ResourcePrinter, error)
|
||||
|
||||
DeleteFlags *DeleteFlags
|
||||
DeleteOptions *DeleteOptions
|
||||
|
||||
Selector string
|
||||
DryRun bool
|
||||
Prune bool
|
||||
PruneResources []pruneResource
|
||||
cmdBaseName string
|
||||
All bool
|
||||
Overwrite bool
|
||||
OpenApiPatch bool
|
||||
PruneWhitelist []string
|
||||
ShouldIncludeUninitialized bool
|
||||
|
||||
Validator validation.Schema
|
||||
Builder *resource.Builder
|
||||
Mapper meta.RESTMapper
|
||||
DynamicClient dynamic.Interface
|
||||
OpenAPISchema openapi.Resources
|
||||
|
||||
Namespace string
|
||||
EnforceNamespace bool
|
||||
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
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 NewApplyOptions(ioStreams genericclioptions.IOStreams) *ApplyOptions {
|
||||
return &ApplyOptions{
|
||||
RecordFlags: genericclioptions.NewRecordFlags(),
|
||||
DeleteFlags: NewDeleteFlags("that contains the configuration to apply"),
|
||||
PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
|
||||
|
||||
Overwrite: true,
|
||||
OpenApiPatch: true,
|
||||
|
||||
Recorder: genericclioptions.NoopRecorder{},
|
||||
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
func NewCmdApply(baseName string, f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewApplyOptions(ioStreams)
|
||||
|
||||
// Store baseName for use in printing warnings / messages involving the base command name.
|
||||
// This is useful for downstream command that wrap this one.
|
||||
o.cmdBaseName = baseName
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "apply -f FILENAME",
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: i18n.T("Apply a configuration to a resource by filename or stdin"),
|
||||
Long: applyLong,
|
||||
Example: applyExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd))
|
||||
cmdutil.CheckErr(validateArgs(cmd, args))
|
||||
cmdutil.CheckErr(validatePruneAll(o.Prune, o.All, o.Selector))
|
||||
cmdutil.CheckErr(o.Run())
|
||||
},
|
||||
}
|
||||
|
||||
// bind flag structs
|
||||
o.DeleteFlags.AddFlags(cmd)
|
||||
o.RecordFlags.AddFlags(cmd)
|
||||
o.PrintFlags.AddFlags(cmd)
|
||||
|
||||
cmd.MarkFlagRequired("filename")
|
||||
cmd.Flags().BoolVar(&o.Overwrite, "overwrite", o.Overwrite, "Automatically resolve conflicts between the modified and live configuration by using values from the modified configuration")
|
||||
cmd.Flags().BoolVar(&o.Prune, "prune", o.Prune, "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.")
|
||||
cmdutil.AddValidateFlags(cmd)
|
||||
cmd.Flags().StringVarP(&o.Selector, "selector", "l", o.Selector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
|
||||
cmd.Flags().BoolVar(&o.All, "all", o.All, "Select all resources in the namespace of the specified resource types.")
|
||||
cmd.Flags().StringArrayVar(&o.PruneWhitelist, "prune-whitelist", o.PruneWhitelist, "Overwrite the default whitelist with <group/version/kind> for --prune")
|
||||
cmd.Flags().BoolVar(&o.OpenApiPatch, "openapi-patch", o.OpenApiPatch, "If true, use openapi to calculate diff when the openapi presents and the resource can be found in the openapi spec. Otherwise, fall back to use baked-in types.")
|
||||
cmdutil.AddDryRunFlag(cmd)
|
||||
cmdutil.AddIncludeUninitializedFlag(cmd)
|
||||
|
||||
// apply subcommands
|
||||
cmd.AddCommand(NewCmdApplyViewLastApplied(f, ioStreams))
|
||||
cmd.AddCommand(NewCmdApplySetLastApplied(f, ioStreams))
|
||||
cmd.AddCommand(NewCmdApplyEditLastApplied(f, ioStreams))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *ApplyOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
|
||||
o.DryRun = cmdutil.GetDryRunFlag(cmd)
|
||||
|
||||
// allow for a success message operation to be specified at print time
|
||||
o.ToPrinter = func(operation string) (printers.ResourcePrinter, error) {
|
||||
o.PrintFlags.NamePrintFlags.Operation = operation
|
||||
if o.DryRun {
|
||||
o.PrintFlags.Complete("%s (dry run)")
|
||||
}
|
||||
|
||||
return o.PrintFlags.ToPrinter()
|
||||
}
|
||||
|
||||
var err error
|
||||
o.RecordFlags.Complete(cmd)
|
||||
o.Recorder, err = o.RecordFlags.ToRecorder()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dynamicClient, err := f.DynamicClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.DeleteOptions = o.DeleteFlags.ToOptions(dynamicClient, o.IOStreams)
|
||||
o.ShouldIncludeUninitialized = cmdutil.ShouldIncludeUninitialized(cmd, o.Prune)
|
||||
|
||||
o.OpenAPISchema, _ = f.OpenAPISchema()
|
||||
o.Validator, err = f.Validator(cmdutil.GetFlagBool(cmd, "validate"))
|
||||
o.Builder = f.NewBuilder()
|
||||
o.Mapper, err = f.ToRESTMapper()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.DynamicClient, err = f.DynamicClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateArgs(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 0 {
|
||||
return cmdutil.UsageErrorf(cmd, "Unexpected args: %v", args)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validatePruneAll(prune, all bool, selector string) error {
|
||||
if all && len(selector) > 0 {
|
||||
return fmt.Errorf("cannot set --all and --selector at the same time")
|
||||
}
|
||||
if prune && !all && selector == "" {
|
||||
return fmt.Errorf("all resources selected for prune without explicitly passing --all. To prune all resources, pass the --all flag. If you did not mean to prune all resources, specify a label selector.")
|
||||
}
|
||||
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 (o *ApplyOptions) Run() error {
|
||||
var openapiSchema openapi.Resources
|
||||
if o.OpenApiPatch {
|
||||
openapiSchema = o.OpenAPISchema
|
||||
}
|
||||
|
||||
// include the uninitialized objects by default if --prune is true
|
||||
// unless explicitly set --include-uninitialized=false
|
||||
r := o.Builder.
|
||||
Unstructured().
|
||||
Schema(o.Validator).
|
||||
ContinueOnError().
|
||||
NamespaceParam(o.Namespace).DefaultNamespace().
|
||||
FilenameParam(o.EnforceNamespace, &o.DeleteOptions.FilenameOptions).
|
||||
LabelSelectorParam(o.Selector).
|
||||
IncludeUninitialized(o.ShouldIncludeUninitialized).
|
||||
Flatten().
|
||||
Do()
|
||||
if err := r.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var err error
|
||||
if o.Prune {
|
||||
o.PruneResources, err = parsePruneResources(o.Mapper, o.PruneWhitelist)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
output := *o.PrintFlags.OutputFormat
|
||||
shortOutput := output == "name"
|
||||
|
||||
visitedUids := sets.NewString()
|
||||
visitedNamespaces := sets.NewString()
|
||||
|
||||
var objs []runtime.Object
|
||||
|
||||
count := 0
|
||||
err = r.Visit(func(info *resource.Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if info.Namespaced() {
|
||||
visitedNamespaces.Insert(info.Namespace)
|
||||
}
|
||||
|
||||
if err := o.Recorder.Record(info.Object); 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.Object, true, unstructured.UnstructuredJSONScheme)
|
||||
if err != nil {
|
||||
return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving modified configuration from:\n%s\nfor:", info.String()), info.Source, err)
|
||||
}
|
||||
|
||||
// Print object only if output format other than "name" is specified
|
||||
printObject := len(output) > 0 && !shortOutput
|
||||
|
||||
if err := info.Get(); err != nil {
|
||||
if !errors.IsNotFound(err) {
|
||||
return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving current configuration of:\n%s\nfrom server for:", info.String()), info.Source, err)
|
||||
}
|
||||
// Create the resource if it doesn't exist
|
||||
// First, update the annotation used by kubectl apply
|
||||
if err := kubectl.CreateApplyAnnotation(info.Object, unstructured.UnstructuredJSONScheme); err != nil {
|
||||
return cmdutil.AddSourceToErr("creating", info.Source, err)
|
||||
}
|
||||
|
||||
if !o.DryRun {
|
||||
// Then create the resource and skip the three-way merge
|
||||
obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object)
|
||||
if err != nil {
|
||||
return cmdutil.AddSourceToErr("creating", info.Source, err)
|
||||
}
|
||||
info.Refresh(obj, true)
|
||||
metadata, err := meta.Accessor(info.Object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
visitedUids.Insert(string(metadata.GetUID()))
|
||||
}
|
||||
|
||||
count++
|
||||
|
||||
if printObject {
|
||||
objs = append(objs, info.Object)
|
||||
return nil
|
||||
}
|
||||
|
||||
printer, err := o.ToPrinter("created")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return printer.PrintObj(info.Object, o.Out)
|
||||
}
|
||||
|
||||
if !o.DryRun {
|
||||
metadata, err := meta.Accessor(info.Object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
annotationMap := metadata.GetAnnotations()
|
||||
if _, ok := annotationMap[api.LastAppliedConfigAnnotation]; !ok {
|
||||
fmt.Fprintf(o.ErrOut, warningNoLastAppliedConfigAnnotation, o.cmdBaseName)
|
||||
}
|
||||
|
||||
helper := resource.NewHelper(info.Client, info.Mapping)
|
||||
patcher := &patcher{
|
||||
mapping: info.Mapping,
|
||||
helper: helper,
|
||||
dynamicClient: o.DynamicClient,
|
||||
overwrite: o.Overwrite,
|
||||
backOff: clockwork.NewRealClock(),
|
||||
force: o.DeleteOptions.ForceDeletion,
|
||||
cascade: o.DeleteOptions.Cascade,
|
||||
timeout: o.DeleteOptions.Timeout,
|
||||
gracePeriod: o.DeleteOptions.GracePeriod,
|
||||
openapiSchema: openapiSchema,
|
||||
}
|
||||
|
||||
patchBytes, patchedObject, err := patcher.patch(info.Object, modified, info.Source, info.Namespace, info.Name, o.ErrOut)
|
||||
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)
|
||||
|
||||
visitedUids.Insert(string(metadata.GetUID()))
|
||||
|
||||
if string(patchBytes) == "{}" && !printObject {
|
||||
count++
|
||||
|
||||
printer, err := o.ToPrinter("unchanged")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return printer.PrintObj(info.Object, o.Out)
|
||||
}
|
||||
}
|
||||
count++
|
||||
|
||||
if printObject {
|
||||
objs = append(objs, info.Object)
|
||||
return nil
|
||||
}
|
||||
|
||||
printer, err := o.ToPrinter("configured")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return printer.PrintObj(info.Object, o.Out)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
return fmt.Errorf("no objects passed to apply")
|
||||
}
|
||||
|
||||
// print objects
|
||||
if len(objs) > 0 {
|
||||
printer, err := o.ToPrinter("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
objToPrint := objs[0]
|
||||
if len(objs) > 1 {
|
||||
list := &v1.List{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "List",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ListMeta: metav1.ListMeta{},
|
||||
}
|
||||
if err := meta.SetList(list, objs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
objToPrint = list
|
||||
}
|
||||
if err := printer.PrintObj(objToPrint, o.Out); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !o.Prune {
|
||||
return nil
|
||||
}
|
||||
|
||||
p := pruner{
|
||||
mapper: o.Mapper,
|
||||
dynamicClient: o.DynamicClient,
|
||||
|
||||
labelSelector: o.Selector,
|
||||
visitedUids: visitedUids,
|
||||
|
||||
cascade: o.DeleteOptions.Cascade,
|
||||
dryRun: o.DryRun,
|
||||
gracePeriod: o.DeleteOptions.GracePeriod,
|
||||
|
||||
toPrinter: o.ToPrinter,
|
||||
|
||||
out: o.Out,
|
||||
}
|
||||
|
||||
namespacedRESTMappings, nonNamespacedRESTMappings, err := getRESTMappings(o.Mapper, &(o.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, o.ShouldIncludeUninitialized); 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, o.ShouldIncludeUninitialized); 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},
|
||||
{"batch", "v1beta1", "CronJob", 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
|
||||
dynamicClient dynamic.Interface
|
||||
|
||||
visitedUids sets.String
|
||||
labelSelector string
|
||||
fieldSelector string
|
||||
|
||||
cascade bool
|
||||
dryRun bool
|
||||
gracePeriod int
|
||||
|
||||
toPrinter func(string) (printers.ResourcePrinter, error)
|
||||
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
func (p *pruner) prune(namespace string, mapping *meta.RESTMapping, includeUninitialized bool) error {
|
||||
objList, err := p.dynamicClient.Resource(mapping.Resource).
|
||||
Namespace(namespace).
|
||||
List(metav1.ListOptions{
|
||||
LabelSelector: p.labelSelector,
|
||||
FieldSelector: p.fieldSelector,
|
||||
IncludeUninitialized: includeUninitialized,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
objs, err := meta.ExtractList(objList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, obj := range objs {
|
||||
metadata, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
annots := metadata.GetAnnotations()
|
||||
if _, ok := annots[api.LastAppliedConfigAnnotation]; !ok {
|
||||
// don't prune resources not created with apply
|
||||
continue
|
||||
}
|
||||
uid := metadata.GetUID()
|
||||
if p.visitedUids.Has(string(uid)) {
|
||||
continue
|
||||
}
|
||||
name := metadata.GetName()
|
||||
if !p.dryRun {
|
||||
if err := p.delete(namespace, name, mapping); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
printer, err := p.toPrinter("pruned")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printer.PrintObj(obj, p.out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *pruner) delete(namespace, name string, mapping *meta.RESTMapping) error {
|
||||
return runDelete(namespace, name, mapping, p.dynamicClient, p.cascade, p.gracePeriod)
|
||||
}
|
||||
|
||||
func runDelete(namespace, name string, mapping *meta.RESTMapping, c dynamic.Interface, cascade bool, gracePeriod int) error {
|
||||
options := &metav1.DeleteOptions{}
|
||||
if gracePeriod >= 0 {
|
||||
options = metav1.NewDeleteOptions(int64(gracePeriod))
|
||||
}
|
||||
policy := metav1.DeletePropagationForeground
|
||||
if !cascade {
|
||||
policy = metav1.DeletePropagationOrphan
|
||||
}
|
||||
options.PropagationPolicy = &policy
|
||||
return c.Resource(mapping.Resource).Namespace(namespace).Delete(name, options)
|
||||
}
|
||||
|
||||
func (p *patcher) delete(namespace, name string) error {
|
||||
return runDelete(namespace, name, p.mapping, p.dynamicClient, p.cascade, p.gracePeriod)
|
||||
}
|
||||
|
||||
type patcher struct {
|
||||
mapping *meta.RESTMapping
|
||||
helper *resource.Helper
|
||||
dynamicClient dynamic.Interface
|
||||
|
||||
overwrite bool
|
||||
backOff clockwork.Clock
|
||||
|
||||
force bool
|
||||
cascade bool
|
||||
timeout time.Duration
|
||||
gracePeriod int
|
||||
|
||||
openapiSchema openapi.Resources
|
||||
}
|
||||
|
||||
func (p *patcher) patchSimple(obj runtime.Object, modified []byte, source, namespace, name string, errOut io.Writer) ([]byte, runtime.Object, error) {
|
||||
// Serialize the current configuration of the object from the server.
|
||||
current, err := runtime.Encode(unstructured.UnstructuredJSONScheme, 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(obj)
|
||||
if err != nil {
|
||||
return nil, nil, cmdutil.AddSourceToErr(fmt.Sprintf("retrieving original configuration from:\n%v\nfor:", obj), source, err)
|
||||
}
|
||||
|
||||
var patchType types.PatchType
|
||||
var patch []byte
|
||||
var lookupPatchMeta strategicpatch.LookupPatchMeta
|
||||
var schema oapi.Schema
|
||||
createPatchErrFormat := "creating patch with:\noriginal:\n%s\nmodified:\n%s\ncurrent:\n%s\nfor:"
|
||||
|
||||
// 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 := scheme.Scheme.New(p.mapping.GroupVersionKind)
|
||||
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
|
||||
|
||||
// Try to use openapi first if the openapi spec is available and can successfully calculate the patch.
|
||||
// Otherwise, fall back to baked-in types.
|
||||
if p.openapiSchema != nil {
|
||||
if schema = p.openapiSchema.LookupResource(p.mapping.GroupVersionKind); schema != nil {
|
||||
lookupPatchMeta = strategicpatch.PatchMetaFromOpenAPI{Schema: schema}
|
||||
if openapiPatch, err := strategicpatch.CreateThreeWayMergePatch(original, modified, current, lookupPatchMeta, p.overwrite); err != nil {
|
||||
fmt.Fprintf(errOut, "warning: error calculating patch from openapi spec: %v\n", err)
|
||||
} else {
|
||||
patchType = types.StrategicMergePatchType
|
||||
patch = openapiPatch
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if patch == nil {
|
||||
lookupPatchMeta, err = strategicpatch.NewPatchMetaFromStruct(versionedObject)
|
||||
if err != nil {
|
||||
return nil, nil, cmdutil.AddSourceToErr(fmt.Sprintf(createPatchErrFormat, original, modified, current), source, err)
|
||||
}
|
||||
patch, err = strategicpatch.CreateThreeWayMergePatch(original, modified, current, lookupPatchMeta, p.overwrite)
|
||||
if err != nil {
|
||||
return nil, nil, cmdutil.AddSourceToErr(fmt.Sprintf(createPatchErrFormat, original, modified, current), source, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if string(patch) == "{}" {
|
||||
return patch, obj, nil
|
||||
}
|
||||
|
||||
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, errOut io.Writer) ([]byte, runtime.Object, error) {
|
||||
var getErr error
|
||||
patchBytes, patchObject, err := p.patchSimple(current, modified, source, namespace, name, errOut)
|
||||
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, errOut)
|
||||
}
|
||||
if err != nil && errors.IsConflict(err) && p.force {
|
||||
patchBytes, patchObject, err = p.deleteAndCreate(current, modified, namespace, name)
|
||||
}
|
||||
return patchBytes, patchObject, err
|
||||
}
|
||||
|
||||
func (p *patcher) deleteAndCreate(original runtime.Object, modified []byte, namespace, name string) ([]byte, runtime.Object, error) {
|
||||
if err := p.delete(namespace, name); err != nil {
|
||||
return modified, nil, err
|
||||
}
|
||||
// TODO: use wait
|
||||
if err := wait.PollImmediate(1*time.Second, p.timeout, func() (bool, error) {
|
||||
if _, err := p.helper.Get(namespace, name, false); !errors.IsNotFound(err) {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}); err != nil {
|
||||
return modified, nil, err
|
||||
}
|
||||
versionedObject, _, err := unstructured.UnstructuredJSONScheme.Decode(modified, nil, nil)
|
||||
if err != nil {
|
||||
return modified, nil, err
|
||||
}
|
||||
createdObject, err := p.helper.Create(namespace, true, versionedObject)
|
||||
if err != nil {
|
||||
// restore the original object if we fail to create the new one
|
||||
// but still propagate and advertise error to user
|
||||
recreated, recreateErr := p.helper.Create(namespace, true, original)
|
||||
if recreateErr != nil {
|
||||
err = fmt.Errorf("An error occurred force-replacing the existing object with the newly provided one:\n\n%v.\n\nAdditionally, an error occurred attempting to restore the original object:\n\n%v\n", err, recreateErr)
|
||||
} else {
|
||||
createdObject = recreated
|
||||
}
|
||||
}
|
||||
return modified, createdObject, err
|
||||
}
|
88
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/apply_edit_last_applied.go
generated
vendored
88
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/apply_edit_last_applied.go
generated
vendored
@ -1,88 +0,0 @@
|
||||
/*
|
||||
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 (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"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/genericclioptions"
|
||||
)
|
||||
|
||||
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, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := editor.NewEditOptions(editor.ApplyEditMode, ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "edit-last-applied (RESOURCE/NAME | -f FILENAME)",
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: "Edit latest last-applied-configuration annotations of a resource/object",
|
||||
Long: applyEditLastAppliedLong,
|
||||
Example: applyEditLastAppliedExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if err := o.Complete(f, args, cmd); err != nil {
|
||||
cmdutil.CheckErr(err)
|
||||
}
|
||||
if err := o.Run(); err != nil {
|
||||
cmdutil.CheckErr(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// bind flag structs
|
||||
o.RecordFlags.AddFlags(cmd)
|
||||
|
||||
usage := "to use to edit the resource"
|
||||
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
|
||||
cmd.Flags().StringVarP(&o.Output, "output", "o", o.Output, "Output format. One of: yaml|json.")
|
||||
cmd.Flags().BoolVar(&o.WindowsLineEndings, "windows-line-endings", o.WindowsLineEndings,
|
||||
"Defaults to the line ending native to your platform.")
|
||||
cmdutil.AddIncludeUninitializedFlag(cmd)
|
||||
|
||||
return cmd
|
||||
}
|
213
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/apply_set_last_applied.go
generated
vendored
213
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/apply_set_last_applied.go
generated
vendored
@ -1,213 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
"fmt"
|
||||
|
||||
"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"
|
||||
"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/genericclioptions"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"
|
||||
"k8s.io/kubernetes/pkg/kubectl/scheme"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
type SetLastAppliedOptions struct {
|
||||
CreateAnnotation bool
|
||||
|
||||
PrintFlags *genericclioptions.PrintFlags
|
||||
PrintObj printers.ResourcePrinterFunc
|
||||
|
||||
FilenameOptions resource.FilenameOptions
|
||||
|
||||
infoList []*resource.Info
|
||||
namespace string
|
||||
enforceNamespace bool
|
||||
dryRun bool
|
||||
shortOutput bool
|
||||
output string
|
||||
patchBufferList []PatchBuffer
|
||||
builder *resource.Builder
|
||||
unstructuredClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error)
|
||||
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
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 NewSetLastAppliedOptions(ioStreams genericclioptions.IOStreams) *SetLastAppliedOptions {
|
||||
return &SetLastAppliedOptions{
|
||||
PrintFlags: genericclioptions.NewPrintFlags("configured").WithTypeSetter(scheme.Scheme),
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
func NewCmdApplySetLastApplied(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewSetLastAppliedOptions(ioStreams)
|
||||
cmd := &cobra.Command{
|
||||
Use: "set-last-applied -f FILENAME",
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: i18n.T("Set the last-applied-configuration annotation on a live object to match the contents of a file."),
|
||||
Long: applySetLastAppliedLong,
|
||||
Example: applySetLastAppliedExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd))
|
||||
cmdutil.CheckErr(o.Validate())
|
||||
cmdutil.CheckErr(o.RunSetLastApplied())
|
||||
},
|
||||
}
|
||||
|
||||
o.PrintFlags.AddFlags(cmd)
|
||||
|
||||
cmdutil.AddDryRunFlag(cmd)
|
||||
cmd.Flags().BoolVar(&o.CreateAnnotation, "create-annotation", o.CreateAnnotation, "Will create 'last-applied-configuration' annotations if current objects doesn't have one")
|
||||
cmdutil.AddJsonFilenameFlag(cmd.Flags(), &o.FilenameOptions.Filenames, "Filename, directory, or URL to files that contains the last-applied-configuration annotations")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *SetLastAppliedOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
|
||||
o.dryRun = cmdutil.GetDryRunFlag(cmd)
|
||||
o.output = cmdutil.GetFlagString(cmd, "output")
|
||||
o.shortOutput = o.output == "name"
|
||||
|
||||
var err error
|
||||
o.namespace, o.enforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.builder = f.NewBuilder()
|
||||
o.unstructuredClientForMapping = f.UnstructuredClientForMapping
|
||||
|
||||
if o.dryRun {
|
||||
// TODO(juanvallejo): This can be cleaned up even further by creating
|
||||
// a PrintFlags struct that binds the --dry-run flag, and whose
|
||||
// ToPrinter method returns a printer that understands how to print
|
||||
// this success message.
|
||||
o.PrintFlags.Complete("%s (dry run)")
|
||||
}
|
||||
printer, err := o.PrintFlags.ToPrinter()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.PrintObj = printer.PrintObj
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *SetLastAppliedOptions) Validate() error {
|
||||
r := o.builder.
|
||||
Unstructured().
|
||||
NamespaceParam(o.namespace).DefaultNamespace().
|
||||
FilenameParam(o.enforceNamespace, &o.FilenameOptions).
|
||||
Flatten().
|
||||
Do()
|
||||
|
||||
err := r.Visit(func(info *resource.Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
patchBuf, diffBuf, patchType, err := editor.GetApplyPatch(info.Object.(runtime.Unstructured))
|
||||
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%s\nfrom server for:", info.String()), info.Source, err)
|
||||
}
|
||||
}
|
||||
originalBuf, err := kubectl.GetOriginalConfiguration(info.Object)
|
||||
if err != nil {
|
||||
return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving current configuration of:\n%s\nfrom server for:", info.String()), info.Source, err)
|
||||
}
|
||||
if originalBuf == nil && !o.CreateAnnotation {
|
||||
return fmt.Errorf("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(originalBuf), 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() error {
|
||||
for i, patch := range o.patchBufferList {
|
||||
info := o.infoList[i]
|
||||
finalObj := info.Object
|
||||
|
||||
if !o.dryRun {
|
||||
mapping := info.ResourceMapping()
|
||||
client, err := o.unstructuredClientForMapping(mapping)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
helper := resource.NewHelper(client, mapping)
|
||||
finalObj, err = helper.Patch(o.namespace, info.Name, patch.PatchType, patch.Patch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := o.PrintObj(finalObj, o.Out); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
1324
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/apply_test.go
generated
vendored
1324
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/apply_test.go
generated
vendored
File diff suppressed because it is too large
Load Diff
166
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/apply_view_last_applied.go
generated
vendored
166
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/apply_view_last_applied.go
generated
vendored
@ -1,166 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
"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/genericclioptions"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/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
|
||||
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
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 NewViewLastAppliedOptions(ioStreams genericclioptions.IOStreams) *ViewLastAppliedOptions {
|
||||
return &ViewLastAppliedOptions{
|
||||
OutputFormat: "yaml",
|
||||
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
func NewCmdApplyViewLastApplied(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
options := NewViewLastAppliedOptions(ioStreams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "view-last-applied (TYPE [NAME | -l label] | TYPE/NAME | -f FILENAME)",
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: i18n.T("View latest last-applied-configuration annotations of a resource/object"),
|
||||
Long: applyViewLastAppliedLong,
|
||||
Example: applyViewLastAppliedExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(options.Complete(cmd, f, args))
|
||||
cmdutil.CheckErr(options.Validate(cmd))
|
||||
cmdutil.CheckErr(options.RunApplyViewLastApplied(cmd))
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&options.OutputFormat, "output", "o", options.OutputFormat, "Output format. Must be one of yaml|json")
|
||||
cmd.Flags().StringVarP(&options.Selector, "selector", "l", options.Selector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
|
||||
cmd.Flags().BoolVar(&options.All, "all", options.All, "Select all resources in the namespace of the specified resource types")
|
||||
usage := "that contains the last-applied-configuration annotations"
|
||||
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *ViewLastAppliedOptions) Complete(cmd *cobra.Command, f cmdutil.Factory, args []string) error {
|
||||
cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r := f.NewBuilder().
|
||||
Unstructured().
|
||||
NamespaceParam(cmdNamespace).DefaultNamespace().
|
||||
FilenameParam(enforceNamespace, &o.FilenameOptions).
|
||||
ResourceTypeOrNameArgs(enforceNamespace, args...).
|
||||
SelectAllParam(o.All).
|
||||
LabelSelectorParam(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.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(cmd *cobra.Command) 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))
|
||||
default:
|
||||
return cmdutil.UsageErrorf(
|
||||
cmd,
|
||||
"Unexpected -o output mode: %s, the flag 'output' must be one of yaml|json",
|
||||
o.OutputFormat)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
341
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/attach.go
generated
vendored
341
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/attach.go
generated
vendored
@ -1,341 +0,0 @@
|
||||
/*
|
||||
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/legacyscheme"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
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/genericclioptions"
|
||||
"k8s.io/kubernetes/pkg/kubectl/polymorphichelpers"
|
||||
"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, streams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := &AttachOptions{
|
||||
StreamOptions: StreamOptions{
|
||||
IOStreams: streams,
|
||||
},
|
||||
|
||||
Attach: &DefaultRemoteAttach{},
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "attach (POD | TYPE/NAME) -c CONTAINER",
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: i18n.T("Attach to a running container"),
|
||||
Long: "Attach to a process that is already running inside an existing container.",
|
||||
Example: attachExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate())
|
||||
cmdutil.CheckErr(o.Run())
|
||||
},
|
||||
}
|
||||
cmdutil.AddPodRunningTimeoutFlag(cmd, defaultPodAttachTimeout)
|
||||
cmd.Flags().StringVarP(&o.ContainerName, "container", "c", o.ContainerName, "Container name. If omitted, the first container in the pod will be chosen")
|
||||
cmd.Flags().BoolVarP(&o.Stdin, "stdin", "i", o.Stdin, "Pass stdin to the container")
|
||||
cmd.Flags().BoolVarP(&o.TTY, "tty", "t", o.TTY, "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
|
||||
SuggestedCmdUsage 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.ToRawKubeConfigLoader().Namespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.GetPodTimeout, err = cmdutil.GetPodRunningTimeoutFlag(cmd)
|
||||
if err != nil {
|
||||
return cmdutil.UsageErrorf(cmd, err.Error())
|
||||
}
|
||||
|
||||
builder := f.NewBuilder().
|
||||
WithScheme(legacyscheme.Scheme).
|
||||
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 := polymorphichelpers.AttachablePodForObjectFn(f, obj, p.GetPodTimeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.PodName = attachablePod.Name
|
||||
p.Namespace = namespace
|
||||
|
||||
fullCmdName := ""
|
||||
cmdParent := cmd.Parent()
|
||||
if cmdParent != nil {
|
||||
fullCmdName = cmdParent.CommandPath()
|
||||
}
|
||||
if len(fullCmdName) > 0 && cmdutil.IsSiblingCommandExists(cmd, "describe") {
|
||||
p.SuggestedCmdUsage = fmt.Sprintf("Use '%s describe pod/%s -n %s' to see all of the containers in this pod.", fullCmdName, p.PodName, p.Namespace)
|
||||
}
|
||||
|
||||
config, err := f.ToRESTConfig()
|
||||
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.ErrOut == 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.ErrOut != nil {
|
||||
fmt.Fprintf(p.ErrOut, "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.ErrOut
|
||||
|
||||
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.ErrOut = nil
|
||||
}
|
||||
|
||||
fn := func() error {
|
||||
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.ErrOut != nil,
|
||||
TTY: t.Raw,
|
||||
}, legacyscheme.ParameterCodec)
|
||||
|
||||
return p.Attach.Attach("POST", req.URL(), p.Config, p.In, p.Out, p.ErrOut, t.Raw, sizeQueue)
|
||||
}
|
||||
|
||||
if !p.Quiet && stderr != nil {
|
||||
fmt.Fprintln(stderr, "If you don't see a command prompt, try pressing enter.")
|
||||
}
|
||||
if err := t.Safe(fn); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
if len(p.SuggestedCmdUsage) > 0 {
|
||||
fmt.Fprintf(p.ErrOut, "Defaulting container name to %s.\n", pod.Spec.Containers[0].Name)
|
||||
fmt.Fprintf(p.ErrOut, "%s\n", p.SuggestedCmdUsage)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
391
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/attach_test.go
generated
vendored
391
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/attach_test.go
generated
vendored
@ -1,391 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
"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/legacyscheme"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
"k8s.io/kubernetes/pkg/kubectl/scheme"
|
||||
)
|
||||
|
||||
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 {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||
ns := legacyscheme.Codecs
|
||||
|
||||
tf.Client = &fake.RESTClient{
|
||||
GroupVersion: schema.GroupVersion{Group: "", Version: "v1"},
|
||||
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.ClientConfigVal = defaultClientConfig()
|
||||
|
||||
cmd := &cobra.Command{}
|
||||
options := test.p
|
||||
cmdutil.AddPodRunningTimeoutFlag(cmd, test.timeout)
|
||||
|
||||
err := options.Complete(tf, 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 {
|
||||
return
|
||||
}
|
||||
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 := "v1"
|
||||
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 {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||
ns := legacyscheme.Codecs
|
||||
|
||||
tf.Client = &fake.RESTClient{
|
||||
GroupVersion: schema.GroupVersion{Group: "", Version: "v1"},
|
||||
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", p, req.Method, req.URL, req)
|
||||
return nil, fmt.Errorf("unexpected request")
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.ClientConfigVal = &restclient.Config{APIPath: "/api", ContentConfig: restclient.ContentConfig{NegotiatedSerializer: legacyscheme.Codecs, GroupVersion: &schema.GroupVersion{Version: test.version}}}
|
||||
remoteAttach := &fakeRemoteAttach{}
|
||||
if test.remoteAttachErr {
|
||||
remoteAttach.err = fmt.Errorf("attach error")
|
||||
}
|
||||
params := &AttachOptions{
|
||||
StreamOptions: StreamOptions{
|
||||
ContainerName: test.container,
|
||||
IOStreams: genericclioptions.NewTestIOStreamsDiscard(),
|
||||
},
|
||||
Attach: remoteAttach,
|
||||
GetPodTimeout: 1000,
|
||||
}
|
||||
cmd := &cobra.Command{}
|
||||
cmdutil.AddPodRunningTimeoutFlag(cmd, 1000)
|
||||
if err := params.Complete(tf, 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)
|
||||
return
|
||||
}
|
||||
if test.exepctedErr == "" && err != nil {
|
||||
t.Errorf("%s: Unexpected error: %v", test.name, err)
|
||||
return
|
||||
}
|
||||
if test.exepctedErr != "" {
|
||||
return
|
||||
}
|
||||
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)
|
||||
return
|
||||
}
|
||||
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 := "v1"
|
||||
tests := []struct {
|
||||
name, container, version, podPath, fetchPodPath, expectedErr 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 {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||
ns := legacyscheme.Codecs
|
||||
|
||||
tf.Client = &fake.RESTClient{
|
||||
GroupVersion: schema.GroupVersion{Group: "", Version: "v1"},
|
||||
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.ClientConfigVal = &restclient.Config{APIPath: "/api", ContentConfig: restclient.ContentConfig{NegotiatedSerializer: legacyscheme.Codecs, GroupVersion: &schema.GroupVersion{Version: test.version}}}
|
||||
streams, _, _, bufErr := genericclioptions.NewTestIOStreams()
|
||||
ex := &fakeRemoteAttach{}
|
||||
params := &AttachOptions{
|
||||
StreamOptions: StreamOptions{
|
||||
ContainerName: test.container,
|
||||
IOStreams: streams,
|
||||
Stdin: test.stdin,
|
||||
TTY: test.tty,
|
||||
},
|
||||
Attach: ex,
|
||||
GetPodTimeout: 1000,
|
||||
}
|
||||
cmd := &cobra.Command{}
|
||||
cmdutil.AddPodRunningTimeoutFlag(cmd, 1000)
|
||||
if err := params.Complete(tf, 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())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
}
|
||||
}
|
64
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/auth/BUILD
generated
vendored
64
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/auth/BUILD
generated
vendored
@ -1,64 +0,0 @@
|
||||
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",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/auth",
|
||||
visibility = [
|
||||
"//build/visible_to:pkg_kubectl_cmd_auth_CONSUMERS",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/apis/authorization:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion:go_default_library",
|
||||
"//pkg/kubectl/cmd/templates:go_default_library",
|
||||
"//pkg/kubectl/cmd/util:go_default_library",
|
||||
"//pkg/kubectl/genericclioptions:go_default_library",
|
||||
"//pkg/kubectl/genericclioptions/printers:go_default_library",
|
||||
"//pkg/kubectl/genericclioptions/resource:go_default_library",
|
||||
"//pkg/kubectl/scheme: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/api/rbac/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/typed/rbac/v1:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
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"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/kubectl/cmd/testing:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest/fake:go_default_library",
|
||||
],
|
||||
)
|
39
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/auth/auth.go
generated
vendored
39
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/auth/auth.go
generated
vendored
@ -1,39 +0,0 @@
|
||||
/*
|
||||
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 (
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
)
|
||||
|
||||
func NewCmdAuth(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
|
||||
// Parent command to which all subcommands are added.
|
||||
cmds := &cobra.Command{
|
||||
Use: "auth",
|
||||
Short: "Inspect authorization",
|
||||
Long: `Inspect authorization`,
|
||||
Run: cmdutil.DefaultSubCommandRun(streams.ErrOut),
|
||||
}
|
||||
|
||||
cmds.AddCommand(NewCmdCanI(f, streams))
|
||||
cmds.AddCommand(NewCmdReconcile(f, streams))
|
||||
|
||||
return cmds
|
||||
}
|
242
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/auth/cani.go
generated
vendored
242
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/auth/cani.go
generated
vendored
@ -1,242 +0,0 @@
|
||||
/*
|
||||
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/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
|
||||
"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
|
||||
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
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, streams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := &CanIOptions{
|
||||
IOStreams: streams,
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "can-i VERB [TYPE | TYPE/NAME | NONRESOURCEURL]",
|
||||
DisableFlagsInUseLine: true,
|
||||
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 !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", o.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, err := f.ToRESTMapper()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
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.ToRawKubeConfigLoader().Namespace()
|
||||
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 ResourceName 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.ErrOut, "Warning: the server doesn't have a resource type '%s'\n", groupResource.Resource)
|
||||
} else {
|
||||
fmt.Fprintf(o.ErrOut, "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
|
||||
}
|
183
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/auth/cani_test.go
generated
vendored
183
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/auth/cani_test.go
generated
vendored
@ -1,183 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/rest/fake"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||
)
|
||||
|
||||
func 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 {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
test.o.Out = ioutil.Discard
|
||||
test.o.ErrOut = ioutil.Discard
|
||||
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
ns := legacyscheme.Codecs
|
||||
|
||||
tf.Client = &fake.RESTClient{
|
||||
GroupVersion: schema.GroupVersion{Group: "", Version: "v1"},
|
||||
NegotiatedSerializer: ns,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
expectPath := "/apis/authorization.k8s.io/v1/selfsubjectaccessreviews"
|
||||
if req.URL.Path != expectPath {
|
||||
t.Errorf("%s: expected %v, got %v", test.name, expectPath, 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.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}}
|
||||
|
||||
if err := test.o.Complete(tf, test.args); err != nil {
|
||||
t.Errorf("%s: %v", test.name, err)
|
||||
return
|
||||
}
|
||||
|
||||
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)
|
||||
return
|
||||
}
|
||||
if actualAllowed != test.allowed {
|
||||
t.Errorf("%s: expected %v, got %v", test.name, test.allowed, actualAllowed)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
243
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/auth/reconcile.go
generated
vendored
243
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/auth/reconcile.go
generated
vendored
@ -1,243 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
rbacv1client "k8s.io/client-go/kubernetes/typed/rbac/v1"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"
|
||||
"k8s.io/kubernetes/pkg/kubectl/scheme"
|
||||
"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 {
|
||||
PrintFlags *genericclioptions.PrintFlags
|
||||
FilenameOptions *resource.FilenameOptions
|
||||
DryRun bool
|
||||
|
||||
Visitor resource.Visitor
|
||||
RBACClient rbacv1client.RbacV1Interface
|
||||
NamespaceClient corev1client.CoreV1Interface
|
||||
|
||||
PrintObject printers.ResourcePrinterFunc
|
||||
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
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 NewReconcileOptions(ioStreams genericclioptions.IOStreams) *ReconcileOptions {
|
||||
return &ReconcileOptions{
|
||||
FilenameOptions: &resource.FilenameOptions{},
|
||||
PrintFlags: genericclioptions.NewPrintFlags("reconciled").WithTypeSetter(scheme.Scheme),
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
func NewCmdReconcile(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewReconcileOptions(streams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "reconcile -f FILENAME",
|
||||
DisableFlagsInUseLine: true,
|
||||
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))
|
||||
cmdutil.CheckErr(o.Validate())
|
||||
cmdutil.CheckErr(o.RunReconcile())
|
||||
},
|
||||
}
|
||||
|
||||
o.PrintFlags.AddFlags(cmd)
|
||||
|
||||
cmdutil.AddFilenameOptionFlags(cmd, o.FilenameOptions, "identifying the resource to reconcile.")
|
||||
cmd.Flags().BoolVar(&o.DryRun, "dry-run", o.DryRun, "If true, display results but do not submit changes")
|
||||
cmd.MarkFlagRequired("filename")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *ReconcileOptions) Complete(cmd *cobra.Command, f cmdutil.Factory, args []string) error {
|
||||
if len(args) > 0 {
|
||||
return errors.New("no arguments are allowed")
|
||||
}
|
||||
|
||||
namespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r := f.NewBuilder().
|
||||
WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...).
|
||||
ContinueOnError().
|
||||
NamespaceParam(namespace).DefaultNamespace().
|
||||
FilenameParam(enforceNamespace, o.FilenameOptions).
|
||||
Flatten().
|
||||
Do()
|
||||
|
||||
if err := r.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
o.Visitor = r
|
||||
|
||||
clientConfig, err := f.ToRESTConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.RBACClient, err = rbacv1client.NewForConfig(clientConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.NamespaceClient, err = corev1client.NewForConfig(clientConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if o.DryRun {
|
||||
o.PrintFlags.Complete("%s (dry run)")
|
||||
}
|
||||
printer, err := o.PrintFlags.ToPrinter()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.PrintObject = printer.PrintObj
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *ReconcileOptions) Validate() error {
|
||||
if o.Visitor == nil {
|
||||
return errors.New("ReconcileOptions.Visitor must be set")
|
||||
}
|
||||
if o.RBACClient == nil {
|
||||
return errors.New("ReconcileOptions.RBACClient must be set")
|
||||
}
|
||||
if o.NamespaceClient == nil {
|
||||
return errors.New("ReconcileOptions.NamespaceClient must be set")
|
||||
}
|
||||
if o.PrintObject == nil {
|
||||
return errors.New("ReconcileOptions.Print must be set")
|
||||
}
|
||||
if o.Out == nil {
|
||||
return errors.New("ReconcileOptions.Out must be set")
|
||||
}
|
||||
if o.ErrOut == nil {
|
||||
return errors.New("ReconcileOptions.Err must be set")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *ReconcileOptions) RunReconcile() error {
|
||||
return o.Visitor.Visit(func(info *resource.Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch t := info.Object.(type) {
|
||||
case *rbacv1.Role:
|
||||
reconcileOptions := reconciliation.ReconcileRoleOptions{
|
||||
Confirm: !o.DryRun,
|
||||
RemoveExtraPermissions: false,
|
||||
Role: reconciliation.RoleRuleOwner{Role: t},
|
||||
Client: reconciliation.RoleModifier{
|
||||
NamespaceClient: o.NamespaceClient.Namespaces(),
|
||||
Client: o.RBACClient,
|
||||
},
|
||||
}
|
||||
result, err := reconcileOptions.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.PrintObject(result.Role.GetObject(), o.Out)
|
||||
|
||||
case *rbacv1.ClusterRole:
|
||||
reconcileOptions := reconciliation.ReconcileRoleOptions{
|
||||
Confirm: !o.DryRun,
|
||||
RemoveExtraPermissions: false,
|
||||
Role: reconciliation.ClusterRoleRuleOwner{ClusterRole: t},
|
||||
Client: reconciliation.ClusterRoleModifier{
|
||||
Client: o.RBACClient.ClusterRoles(),
|
||||
},
|
||||
}
|
||||
result, err := reconcileOptions.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.PrintObject(result.Role.GetObject(), o.Out)
|
||||
|
||||
case *rbacv1.RoleBinding:
|
||||
reconcileOptions := reconciliation.ReconcileRoleBindingOptions{
|
||||
Confirm: !o.DryRun,
|
||||
RemoveExtraSubjects: false,
|
||||
RoleBinding: reconciliation.RoleBindingAdapter{RoleBinding: t},
|
||||
Client: reconciliation.RoleBindingClientAdapter{
|
||||
Client: o.RBACClient,
|
||||
NamespaceClient: o.NamespaceClient.Namespaces(),
|
||||
},
|
||||
}
|
||||
result, err := reconcileOptions.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.PrintObject(result.RoleBinding.GetObject(), o.Out)
|
||||
|
||||
case *rbacv1.ClusterRoleBinding:
|
||||
reconcileOptions := reconciliation.ReconcileRoleBindingOptions{
|
||||
Confirm: !o.DryRun,
|
||||
RemoveExtraSubjects: false,
|
||||
RoleBinding: reconciliation.ClusterRoleBindingAdapter{ClusterRoleBinding: t},
|
||||
Client: reconciliation.ClusterRoleBindingClientAdapter{
|
||||
Client: o.RBACClient.ClusterRoleBindings(),
|
||||
},
|
||||
}
|
||||
result, err := reconcileOptions.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.PrintObject(result.RoleBinding.GetObject(), o.Out)
|
||||
|
||||
default:
|
||||
glog.V(1).Infof("skipping %#v", info.Object.GetObjectKind())
|
||||
// skip ignored resources
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
272
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/autoscale.go
generated
vendored
272
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/autoscale.go
generated
vendored
@ -1,272 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
autoscalingv1 "k8s.io/api/autoscaling/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
autoscalingv1client "k8s.io/client-go/kubernetes/typed/autoscaling/v1"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"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/genericclioptions"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"
|
||||
"k8s.io/kubernetes/pkg/kubectl/polymorphichelpers"
|
||||
"k8s.io/kubernetes/pkg/kubectl/scheme"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
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, no 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`))
|
||||
)
|
||||
|
||||
type AutoscaleOptions struct {
|
||||
FilenameOptions *resource.FilenameOptions
|
||||
|
||||
RecordFlags *genericclioptions.RecordFlags
|
||||
Recorder genericclioptions.Recorder
|
||||
|
||||
PrintFlags *genericclioptions.PrintFlags
|
||||
ToPrinter func(string) (printers.ResourcePrinter, error)
|
||||
|
||||
Name string
|
||||
Generator string
|
||||
Min int32
|
||||
Max int32
|
||||
CpuPercent int32
|
||||
|
||||
createAnnotation bool
|
||||
args []string
|
||||
enforceNamespace bool
|
||||
namespace string
|
||||
dryRun bool
|
||||
builder *resource.Builder
|
||||
canBeAutoscaled polymorphichelpers.CanBeAutoscaledFunc
|
||||
generatorFunc func(string, *meta.RESTMapping) (kubectl.StructuredGenerator, error)
|
||||
|
||||
HPAClient autoscalingv1client.HorizontalPodAutoscalersGetter
|
||||
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
func NewAutoscaleOptions(ioStreams genericclioptions.IOStreams) *AutoscaleOptions {
|
||||
return &AutoscaleOptions{
|
||||
PrintFlags: genericclioptions.NewPrintFlags("autoscaled").WithTypeSetter(scheme.Scheme),
|
||||
FilenameOptions: &resource.FilenameOptions{},
|
||||
RecordFlags: genericclioptions.NewRecordFlags(),
|
||||
Recorder: genericclioptions.NoopRecorder{},
|
||||
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
}
|
||||
|
||||
func NewCmdAutoscale(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := NewAutoscaleOptions(ioStreams)
|
||||
|
||||
validArgs := []string{"deployment", "replicaset", "replicationcontroller"}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "autoscale (-f FILENAME | TYPE NAME | TYPE/NAME) [--min=MINPODS] --max=MAXPODS [--cpu-percent=CPU]",
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: i18n.T("Auto-scale a Deployment, ReplicaSet, or ReplicationController"),
|
||||
Long: autoscaleLong,
|
||||
Example: autoscaleExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate())
|
||||
cmdutil.CheckErr(o.Run())
|
||||
},
|
||||
ValidArgs: validArgs,
|
||||
}
|
||||
|
||||
// bind flag structs
|
||||
o.RecordFlags.AddFlags(cmd)
|
||||
o.PrintFlags.AddFlags(cmd)
|
||||
|
||||
cmd.Flags().StringVar(&o.Generator, "generator", cmdutil.HorizontalPodAutoscalerV1GeneratorName, i18n.T("The name of the API generator to use. Currently there is only 1 generator."))
|
||||
cmd.Flags().Int32Var(&o.Min, "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().Int32Var(&o.Max, "max", -1, "The upper limit for the number of pods that can be set by the autoscaler. Required.")
|
||||
cmd.MarkFlagRequired("max")
|
||||
cmd.Flags().Int32Var(&o.CpuPercent, "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().StringVar(&o.Name, "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)
|
||||
cmdutil.AddFilenameOptionFlags(cmd, o.FilenameOptions, "identifying the resource to autoscale.")
|
||||
cmdutil.AddApplyAnnotationFlags(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *AutoscaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
o.dryRun = cmdutil.GetFlagBool(cmd, "dry-run")
|
||||
o.createAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
|
||||
o.builder = f.NewBuilder()
|
||||
o.canBeAutoscaled = polymorphichelpers.CanBeAutoscaledFn
|
||||
o.args = args
|
||||
o.RecordFlags.Complete(cmd)
|
||||
|
||||
o.Recorder, err = o.RecordFlags.ToRecorder()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
kubeClient, err := f.KubernetesClientSet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.HPAClient = kubeClient.AutoscalingV1()
|
||||
|
||||
// get the generator
|
||||
o.generatorFunc = func(name string, mapping *meta.RESTMapping) (kubectl.StructuredGenerator, error) {
|
||||
switch o.Generator {
|
||||
case cmdutil.HorizontalPodAutoscalerV1GeneratorName:
|
||||
return &kubectl.HorizontalPodAutoscalerGeneratorV1{
|
||||
Name: name,
|
||||
MinReplicas: o.Min,
|
||||
MaxReplicas: o.Max,
|
||||
CPUPercent: o.CpuPercent,
|
||||
ScaleRefName: name,
|
||||
ScaleRefKind: mapping.GroupVersionKind.Kind,
|
||||
ScaleRefApiVersion: mapping.GroupVersionKind.GroupVersion().String(),
|
||||
}, nil
|
||||
default:
|
||||
return nil, cmdutil.UsageErrorf(cmd, "Generator %s not supported. ", o.Generator)
|
||||
}
|
||||
}
|
||||
|
||||
o.namespace, o.enforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.ToPrinter = func(operation string) (printers.ResourcePrinter, error) {
|
||||
o.PrintFlags.NamePrintFlags.Operation = operation
|
||||
if o.dryRun {
|
||||
o.PrintFlags.Complete("%s (dry run)")
|
||||
}
|
||||
|
||||
return o.PrintFlags.ToPrinter()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *AutoscaleOptions) Validate() error {
|
||||
if o.Max < 1 {
|
||||
return fmt.Errorf("--max=MAXPODS is required and must be at least 1, max: %d", o.Max)
|
||||
}
|
||||
if o.Max < o.Min {
|
||||
return fmt.Errorf("--max=MAXPODS must be larger or equal to --min=MINPODS, max: %d, min: %d", o.Max, o.Min)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *AutoscaleOptions) Run() error {
|
||||
r := o.builder.
|
||||
WithScheme(legacyscheme.Scheme).
|
||||
ContinueOnError().
|
||||
NamespaceParam(o.namespace).DefaultNamespace().
|
||||
FilenameParam(o.enforceNamespace, o.FilenameOptions).
|
||||
ResourceTypeOrNameArgs(false, o.args...).
|
||||
Flatten().
|
||||
Do()
|
||||
if err := r.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
count := 0
|
||||
err := r.Visit(func(info *resource.Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mapping := info.ResourceMapping()
|
||||
if err := o.canBeAutoscaled(mapping.GroupVersionKind.GroupKind()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
generator, err := o.generatorFunc(info.Name, mapping)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Generate new object
|
||||
object, err := generator.StructuredGenerate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hpa, ok := object.(*autoscalingv1.HorizontalPodAutoscaler)
|
||||
if !ok {
|
||||
return fmt.Errorf("generator made %T, not autoscalingv1.HorizontalPodAutoscaler", object)
|
||||
}
|
||||
|
||||
if err := o.Recorder.Record(hpa); err != nil {
|
||||
glog.V(4).Infof("error recording current command: %v", err)
|
||||
}
|
||||
|
||||
if o.dryRun {
|
||||
count++
|
||||
|
||||
printer, err := o.ToPrinter("created")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return printer.PrintObj(hpa, o.Out)
|
||||
}
|
||||
|
||||
if err := kubectl.CreateOrUpdateAnnotation(o.createAnnotation, hpa, cmdutil.InternalVersionJSONEncoder()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
actualHPA, err := o.HPAClient.HorizontalPodAutoscalers(o.namespace).Create(hpa)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
count++
|
||||
printer, err := o.ToPrinter("autoscaled")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return printer.PrintObj(actualHPA, o.Out)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count == 0 {
|
||||
return fmt.Errorf("no objects passed to autoscale")
|
||||
}
|
||||
return nil
|
||||
}
|
252
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/certificates.go
generated
vendored
252
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/certificates.go
generated
vendored
@ -1,252 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/apis/certificates"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"
|
||||
"k8s.io/kubernetes/pkg/kubectl/scheme"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func NewCmdCertificate(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "certificate SUBCOMMAND",
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: i18n.T("Modify certificate resources."),
|
||||
Long: "Modify certificate resources.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmd.Help()
|
||||
},
|
||||
}
|
||||
|
||||
cmd.AddCommand(NewCmdCertificateApprove(f, ioStreams))
|
||||
cmd.AddCommand(NewCmdCertificateDeny(f, ioStreams))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
type CertificateOptions struct {
|
||||
resource.FilenameOptions
|
||||
|
||||
PrintFlags *genericclioptions.PrintFlags
|
||||
PrintObj printers.ResourcePrinterFunc
|
||||
|
||||
csrNames []string
|
||||
outputStyle string
|
||||
|
||||
clientSet internalclientset.Interface
|
||||
builder *resource.Builder
|
||||
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
func (o *CertificateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
o.csrNames = args
|
||||
o.outputStyle = cmdutil.GetFlagString(cmd, "output")
|
||||
|
||||
printer, err := o.PrintFlags.ToPrinter()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.PrintObj = func(obj runtime.Object, out io.Writer) error {
|
||||
return printer.PrintObj(obj, out)
|
||||
}
|
||||
|
||||
o.builder = f.NewBuilder()
|
||||
o.clientSet, err = f.ClientSet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *CertificateOptions) Validate() error {
|
||||
if len(o.csrNames) < 1 && cmdutil.IsFilenameSliceEmpty(o.Filenames) {
|
||||
return fmt.Errorf("one or more CSRs must be specified as <name> or -f <filename>")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewCmdCertificateApprove(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
options := CertificateOptions{
|
||||
PrintFlags: genericclioptions.NewPrintFlags("approved").WithTypeSetter(scheme.Scheme),
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "approve (-f FILENAME | NAME)",
|
||||
DisableFlagsInUseLine: true,
|
||||
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(f, cmd, args))
|
||||
cmdutil.CheckErr(options.Validate())
|
||||
cmdutil.CheckErr(options.RunCertificateApprove(cmdutil.GetFlagBool(cmd, "force")))
|
||||
},
|
||||
}
|
||||
|
||||
options.PrintFlags.AddFlags(cmd)
|
||||
|
||||
cmd.Flags().Bool("force", false, "Update the CSR even if it is already approved.")
|
||||
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, "identifying the resource to update")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *CertificateOptions) RunCertificateApprove(force bool) error {
|
||||
return o.modifyCertificateCondition(o.builder, o.clientSet, force, func(csr *certificates.CertificateSigningRequest) (*certificates.CertificateSigningRequest, bool) {
|
||||
var alreadyApproved bool
|
||||
for _, c := range csr.Status.Conditions {
|
||||
if c.Type == certificates.CertificateApproved {
|
||||
alreadyApproved = true
|
||||
}
|
||||
}
|
||||
if alreadyApproved {
|
||||
return csr, true
|
||||
}
|
||||
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, false
|
||||
})
|
||||
}
|
||||
|
||||
func NewCmdCertificateDeny(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
options := CertificateOptions{
|
||||
PrintFlags: genericclioptions.NewPrintFlags("denied").WithTypeSetter(scheme.Scheme),
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "deny (-f FILENAME | NAME)",
|
||||
DisableFlagsInUseLine: true,
|
||||
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(f, cmd, args))
|
||||
cmdutil.CheckErr(options.Validate())
|
||||
cmdutil.CheckErr(options.RunCertificateDeny(cmdutil.GetFlagBool(cmd, "force")))
|
||||
},
|
||||
}
|
||||
|
||||
options.PrintFlags.AddFlags(cmd)
|
||||
|
||||
cmd.Flags().Bool("force", false, "Update the CSR even if it is already denied.")
|
||||
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, "identifying the resource to update")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *CertificateOptions) RunCertificateDeny(force bool) error {
|
||||
return o.modifyCertificateCondition(o.builder, o.clientSet, force, func(csr *certificates.CertificateSigningRequest) (*certificates.CertificateSigningRequest, bool) {
|
||||
var alreadyDenied bool
|
||||
for _, c := range csr.Status.Conditions {
|
||||
if c.Type == certificates.CertificateDenied {
|
||||
alreadyDenied = true
|
||||
}
|
||||
}
|
||||
if alreadyDenied {
|
||||
return csr, true
|
||||
}
|
||||
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, false
|
||||
})
|
||||
}
|
||||
|
||||
func (options *CertificateOptions) modifyCertificateCondition(builder *resource.Builder, clientSet internalclientset.Interface, force bool, modify func(csr *certificates.CertificateSigningRequest) (*certificates.CertificateSigningRequest, bool)) error {
|
||||
var found int
|
||||
r := builder.
|
||||
WithScheme(legacyscheme.Scheme).
|
||||
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
|
||||
}
|
||||
for i := 0; ; i++ {
|
||||
csr := info.Object.(*certificates.CertificateSigningRequest)
|
||||
csr, hasCondition := modify(csr)
|
||||
if !hasCondition || force {
|
||||
csr, err = clientSet.Certificates().
|
||||
CertificateSigningRequests().
|
||||
UpdateApproval(csr)
|
||||
if errors.IsConflict(err) && i < 10 {
|
||||
if err := info.Get(); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
found++
|
||||
|
||||
return options.PrintObj(cmdutil.AsDefaultVersionedOrOriginal(info.Object, info.Mapping), options.Out)
|
||||
})
|
||||
if found == 0 {
|
||||
fmt.Fprintf(options.Out, "No resources found\n")
|
||||
}
|
||||
return err
|
||||
}
|
166
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/clusterinfo.go
generated
vendored
166
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/clusterinfo.go
generated
vendored
@ -1,166 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/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`))
|
||||
)
|
||||
|
||||
type ClusterInfoOptions struct {
|
||||
genericclioptions.IOStreams
|
||||
|
||||
Namespace string
|
||||
|
||||
Builder *resource.Builder
|
||||
Client *restclient.Config
|
||||
}
|
||||
|
||||
func NewCmdClusterInfo(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := &ClusterInfoOptions{
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "cluster-info",
|
||||
Short: i18n.T("Display cluster info"),
|
||||
Long: longDescr,
|
||||
Example: clusterinfoExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd))
|
||||
cmdutil.CheckErr(o.Run())
|
||||
},
|
||||
}
|
||||
cmd.AddCommand(NewCmdClusterInfoDump(f, ioStreams))
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *ClusterInfoOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
|
||||
var err error
|
||||
o.Client, err = f.ToRESTConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmdNamespace := cmdutil.GetFlagString(cmd, "namespace")
|
||||
if cmdNamespace == "" {
|
||||
cmdNamespace = metav1.NamespaceSystem
|
||||
}
|
||||
o.Namespace = cmdNamespace
|
||||
|
||||
o.Builder = f.NewBuilder()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *ClusterInfoOptions) Run() error {
|
||||
printService(o.Out, "Kubernetes master", o.Client.Host)
|
||||
|
||||
// TODO use generalized labels once they are implemented (#341)
|
||||
b := o.Builder.
|
||||
WithScheme(legacyscheme.Scheme).
|
||||
NamespaceParam(o.Namespace).DefaultNamespace().
|
||||
LabelSelectorParam("kubernetes.io/cluster-service=true").
|
||||
ResourceTypeOrNameArgs(false, []string{"services"}...).
|
||||
Latest()
|
||||
err := 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 {
|
||||
name := service.ObjectMeta.Name
|
||||
|
||||
if len(service.Spec.Ports) > 0 {
|
||||
port := service.Spec.Ports[0]
|
||||
|
||||
// guess if the scheme is https
|
||||
scheme := ""
|
||||
if port.Name == "https" || port.Port == 443 {
|
||||
scheme = "https"
|
||||
}
|
||||
|
||||
// format is <scheme>:<service-name>:<service-port-name>
|
||||
name = utilnet.JoinSchemeNamePort(scheme, service.ObjectMeta.Name, port.Name)
|
||||
}
|
||||
|
||||
if len(o.Client.GroupVersion.Group) == 0 {
|
||||
link = o.Client.Host + "/api/" + o.Client.GroupVersion.Version + "/namespaces/" + service.ObjectMeta.Namespace + "/services/" + name + "/proxy"
|
||||
} else {
|
||||
link = o.Client.Host + "/api/" + o.Client.GroupVersion.Group + "/" + o.Client.GroupVersion.Version + "/namespaces/" + service.ObjectMeta.Namespace + "/services/" + name + "/proxy"
|
||||
|
||||
}
|
||||
}
|
||||
name := service.ObjectMeta.Labels["kubernetes.io/name"]
|
||||
if len(name) == 0 {
|
||||
name = service.ObjectMeta.Name
|
||||
}
|
||||
printService(o.Out, name, link)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
o.Out.Write([]byte("\nTo further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.\n"))
|
||||
return err
|
||||
|
||||
// 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.Fprint(out, " is running at ")
|
||||
ct.ChangeColor(ct.Yellow, false, ct.None, false)
|
||||
fmt.Fprint(out, link)
|
||||
ct.ResetColor()
|
||||
fmt.Fprintln(out, "")
|
||||
}
|
269
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/clusterinfo_dump.go
generated
vendored
269
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/clusterinfo_dump.go
generated
vendored
@ -1,269 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers"
|
||||
"k8s.io/kubernetes/pkg/kubectl/polymorphichelpers"
|
||||
"k8s.io/kubernetes/pkg/kubectl/scheme"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
)
|
||||
|
||||
type ClusterInfoDumpOptions struct {
|
||||
PrintFlags *genericclioptions.PrintFlags
|
||||
PrintObj printers.ResourcePrinterFunc
|
||||
|
||||
OutputDir string
|
||||
AllNamespaces bool
|
||||
Namespaces []string
|
||||
|
||||
Timeout time.Duration
|
||||
Clientset internalclientset.Interface
|
||||
Namespace string
|
||||
RESTClientGetter genericclioptions.RESTClientGetter
|
||||
LogsForObject polymorphichelpers.LogsForObjectFunc
|
||||
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
// NewCmdCreateSecret groups subcommands to create various types of secrets
|
||||
func NewCmdClusterInfoDump(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
||||
o := &ClusterInfoDumpOptions{
|
||||
PrintFlags: genericclioptions.NewPrintFlags("").WithTypeSetter(scheme.Scheme),
|
||||
|
||||
IOStreams: ioStreams,
|
||||
}
|
||||
|
||||
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(o.Complete(f, cmd))
|
||||
cmdutil.CheckErr(o.Run())
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVar(&o.OutputDir, "output-directory", o.OutputDir, i18n.T("Where to output the files. If empty or '-' uses stdout, otherwise creates a directory hierarchy in that directory"))
|
||||
cmd.Flags().StringSliceVar(&o.Namespaces, "namespaces", o.Namespaces, "A comma separated list of namespaces to dump.")
|
||||
cmd.Flags().BoolVar(&o.AllNamespaces, "all-namespaces", o.AllNamespaces, "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(dir string, defaultWriter io.Writer, filename string) io.Writer {
|
||||
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 (o *ClusterInfoDumpOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
|
||||
printer, err := o.PrintFlags.ToPrinter()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jsonOutputFmt := "json"
|
||||
o.PrintFlags.OutputFormat = &jsonOutputFmt
|
||||
o.PrintObj = printer.PrintObj
|
||||
|
||||
o.Timeout, err = cmdutil.GetPodRunningTimeoutFlag(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.Clientset, err = f.ClientSet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO this should eventually just be the completed kubeconfigflag struct
|
||||
o.RESTClientGetter = f
|
||||
o.LogsForObject = polymorphichelpers.LogsForObjectFn
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *ClusterInfoDumpOptions) Run() error {
|
||||
nodes, err := o.Clientset.Core().Nodes().List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := o.PrintObj(nodes, setupOutputWriter(o.OutputDir, o.Out, "nodes.json")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var namespaces []string
|
||||
if o.AllNamespaces {
|
||||
namespaceList, err := o.Clientset.Core().Namespaces().List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for ix := range namespaceList.Items {
|
||||
namespaces = append(namespaces, namespaceList.Items[ix].Name)
|
||||
}
|
||||
} else {
|
||||
if len(o.Namespaces) == 0 {
|
||||
namespaces = []string{
|
||||
metav1.NamespaceSystem,
|
||||
o.Namespace,
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, namespace := range namespaces {
|
||||
// TODO: this is repetitive in the extreme. Use reflection or
|
||||
// something to make this a for loop.
|
||||
events, err := o.Clientset.Core().Events(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.PrintObj(events, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "events.json"))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rcs, err := o.Clientset.Core().ReplicationControllers(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.PrintObj(rcs, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "replication-controllers.json"))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
svcs, err := o.Clientset.Core().Services(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.PrintObj(svcs, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "services.json"))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sets, err := o.Clientset.Extensions().DaemonSets(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.PrintObj(sets, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "daemonsets.json"))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deps, err := o.Clientset.Extensions().Deployments(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.PrintObj(deps, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "deployments.json"))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rps, err := o.Clientset.Extensions().ReplicaSets(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.PrintObj(rps, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "replicasets.json"))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pods, err := o.Clientset.Core().Pods(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := o.PrintObj(pods, setupOutputWriter(o.OutputDir, o.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 := o.LogsForObject(o.RESTClientGetter, 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(o.OutputDir, o.Out, path.Join(namespace, pod.Name, "logs.txt"))
|
||||
|
||||
for i := range containers {
|
||||
printContainer(writer, containers[i], pod)
|
||||
}
|
||||
}
|
||||
}
|
||||
if o.OutputDir != "-" {
|
||||
fmt.Fprintf(o.Out, "Cluster info dumped to %s\n", o.OutputDir)
|
||||
}
|
||||
return nil
|
||||
}
|
70
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/clusterinfo_dump_test.go
generated
vendored
70
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/clusterinfo_dump_test.go
generated
vendored
@ -1,70 +0,0 @@
|
||||
/*
|
||||
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/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
)
|
||||
|
||||
func TestSetupOutputWriterNoOp(t *testing.T) {
|
||||
tests := []string{"", "-"}
|
||||
for _, test := range tests {
|
||||
_, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||
f := cmdtesting.NewTestFactory()
|
||||
defer f.Cleanup()
|
||||
|
||||
writer := setupOutputWriter(test, buf, "/some/file/that/should/be/ignored")
|
||||
if writer != buf {
|
||||
t.Errorf("expected: %v, saw: %v", buf, 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)
|
||||
|
||||
_, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||
f := cmdtesting.NewTestFactory()
|
||||
defer f.Cleanup()
|
||||
|
||||
writer := setupOutputWriter(dir, buf, file)
|
||||
if writer == buf {
|
||||
t.Errorf("expected: %v, saw: %v", buf, 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)
|
||||
}
|
||||
}
|
437
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/cmd.go
generated
vendored
437
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/cmd.go
generated
vendored
@ -1,437 +0,0 @@
|
||||
/*
|
||||
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 (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
utilflag "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/create"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/get"
|
||||
"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/cmd/wait"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
)
|
||||
|
||||
const (
|
||||
bashCompletionFunc = `# call kubectl get $1,
|
||||
__kubectl_override_flag_list=(--kubeconfig --cluster --user --context --namespace --server -n -s)
|
||||
__kubectl_override_flags()
|
||||
{
|
||||
local ${__kubectl_override_flag_list[*]##*-} two_word_of of var
|
||||
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
|
||||
done
|
||||
for var in "${__kubectl_override_flag_list[@]##*-}"; do
|
||||
if eval "test -n \"\$${var}\""; then
|
||||
eval "echo \${${var}}"
|
||||
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
|
||||
}
|
||||
|
||||
# $1 is the name of resource (required)
|
||||
# $2 is template string for kubectl get (optional)
|
||||
__kubectl_parse_get()
|
||||
{
|
||||
local template
|
||||
template="${2:-"{{ 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
|
||||
local kubectl_out
|
||||
if kubectl_out=$(kubectl api-resources $(__kubectl_override_flags) -o name --cached --request-timeout=5s --verbs=get 2>/dev/null); then
|
||||
COMPREPLY=( $( compgen -W "${kubectl_out[*]}" -- "$cur" ) )
|
||||
return 0
|
||||
fi
|
||||
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"
|
||||
}
|
||||
|
||||
__kubectl_get_resource_clusterrole()
|
||||
{
|
||||
__kubectl_parse_get "clusterrole"
|
||||
}
|
||||
|
||||
# $1 is the name of the pod we want to get the list of containers inside
|
||||
__kubectl_get_containers()
|
||||
{
|
||||
local template
|
||||
template="{{ range .spec.initContainers }}{{ .name }} {{end}}{{ range .spec.containers }}{{ .name }} {{ end }}"
|
||||
__kubectl_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
|
||||
}
|
||||
|
||||
__kubectl_cp()
|
||||
{
|
||||
if [[ $(type -t compopt) = "builtin" ]]; then
|
||||
compopt -o nospace
|
||||
fi
|
||||
|
||||
case "$cur" in
|
||||
/*|[.~]*) # looks like a path
|
||||
return
|
||||
;;
|
||||
*:*) # TODO: complete remote files in the pod
|
||||
return
|
||||
;;
|
||||
*/*) # complete <namespace>/<pod>
|
||||
local template namespace kubectl_out
|
||||
template="{{ range .items }}{{ .metadata.namespace }}/{{ .metadata.name }}: {{ end }}"
|
||||
namespace="${cur%%/*}"
|
||||
if kubectl_out=( $(kubectl get $(__kubectl_override_flags) --namespace "${namespace}" -o template --template="${template}" pods 2>/dev/null) ); then
|
||||
COMPREPLY=( $(compgen -W "${kubectl_out[*]}" -- "${cur}") )
|
||||
fi
|
||||
return
|
||||
;;
|
||||
*) # complete namespaces, pods, and filedirs
|
||||
__kubectl_parse_get "namespace" "{{ range .items }}{{ .metadata.name }}/ {{ end }}"
|
||||
__kubectl_parse_get "pod" "{{ range .items }}{{ .metadata.name }}: {{ end }}"
|
||||
_filedir
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
__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_apply_edit-last-applied | kubectl_apply_view-last-applied)
|
||||
__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_rename-context)
|
||||
__kubectl_config_get_contexts
|
||||
return
|
||||
;;
|
||||
kubectl_config_delete-cluster)
|
||||
__kubectl_config_get_clusters
|
||||
return
|
||||
;;
|
||||
kubectl_cp)
|
||||
__kubectl_cp
|
||||
return
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
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",
|
||||
}
|
||||
)
|
||||
|
||||
func NewDefaultKubectlCommand() *cobra.Command {
|
||||
return NewKubectlCommand(os.Stdin, os.Stdout, os.Stderr)
|
||||
}
|
||||
|
||||
// NewKubectlCommand creates the `kubectl` command and its nested children.
|
||||
func NewKubectlCommand(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://kubernetes.io/docs/reference/kubectl/overview/`),
|
||||
Run: runHelp,
|
||||
BashCompletionFunction: bashCompletionFunc,
|
||||
}
|
||||
|
||||
flags := cmds.PersistentFlags()
|
||||
flags.SetNormalizeFunc(utilflag.WarnWordSepNormalizeFunc) // Warn for "_" flags
|
||||
|
||||
// Normalize all flags that are coming from other packages or pre-configurations
|
||||
// a.k.a. change all "_" to "-". e.g. glog package
|
||||
flags.SetNormalizeFunc(utilflag.WordSepNormalizeFunc)
|
||||
|
||||
kubeConfigFlags := genericclioptions.NewConfigFlags()
|
||||
kubeConfigFlags.AddFlags(flags)
|
||||
matchVersionKubeConfigFlags := cmdutil.NewMatchVersionFlags(kubeConfigFlags)
|
||||
matchVersionKubeConfigFlags.AddFlags(cmds.PersistentFlags())
|
||||
|
||||
cmds.PersistentFlags().AddGoFlagSet(flag.CommandLine)
|
||||
|
||||
f := cmdutil.NewFactory(matchVersionKubeConfigFlags)
|
||||
|
||||
// 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(utilflag.WarnWordSepNormalizeFunc)
|
||||
|
||||
ioStreams := genericclioptions.IOStreams{In: in, Out: out, ErrOut: err}
|
||||
|
||||
groups := templates.CommandGroups{
|
||||
{
|
||||
Message: "Basic Commands (Beginner):",
|
||||
Commands: []*cobra.Command{
|
||||
create.NewCmdCreate(f, ioStreams),
|
||||
NewCmdExposeService(f, ioStreams),
|
||||
NewCmdRun(f, ioStreams),
|
||||
set.NewCmdSet(f, ioStreams),
|
||||
deprecatedAlias("run-container", NewCmdRun(f, ioStreams)),
|
||||
},
|
||||
},
|
||||
{
|
||||
Message: "Basic Commands (Intermediate):",
|
||||
Commands: []*cobra.Command{
|
||||
NewCmdExplain("kubectl", f, ioStreams),
|
||||
get.NewCmdGet("kubectl", f, ioStreams),
|
||||
NewCmdEdit(f, ioStreams),
|
||||
NewCmdDelete(f, ioStreams),
|
||||
},
|
||||
},
|
||||
{
|
||||
Message: "Deploy Commands:",
|
||||
Commands: []*cobra.Command{
|
||||
rollout.NewCmdRollout(f, ioStreams),
|
||||
NewCmdRollingUpdate(f, ioStreams),
|
||||
NewCmdScale(f, ioStreams),
|
||||
NewCmdAutoscale(f, ioStreams),
|
||||
},
|
||||
},
|
||||
{
|
||||
Message: "Cluster Management Commands:",
|
||||
Commands: []*cobra.Command{
|
||||
NewCmdCertificate(f, ioStreams),
|
||||
NewCmdClusterInfo(f, ioStreams),
|
||||
NewCmdTop(f, ioStreams),
|
||||
NewCmdCordon(f, ioStreams),
|
||||
NewCmdUncordon(f, ioStreams),
|
||||
NewCmdDrain(f, ioStreams),
|
||||
NewCmdTaint(f, ioStreams),
|
||||
},
|
||||
},
|
||||
{
|
||||
Message: "Troubleshooting and Debugging Commands:",
|
||||
Commands: []*cobra.Command{
|
||||
NewCmdDescribe("kubectl", f, ioStreams),
|
||||
NewCmdLogs(f, ioStreams),
|
||||
NewCmdAttach(f, ioStreams),
|
||||
NewCmdExec(f, ioStreams),
|
||||
NewCmdPortForward(f, ioStreams),
|
||||
NewCmdProxy(f, ioStreams),
|
||||
NewCmdCp(f, ioStreams),
|
||||
auth.NewCmdAuth(f, ioStreams),
|
||||
},
|
||||
},
|
||||
{
|
||||
Message: "Advanced Commands:",
|
||||
Commands: []*cobra.Command{
|
||||
NewCmdApply("kubectl", f, ioStreams),
|
||||
NewCmdPatch(f, ioStreams),
|
||||
NewCmdReplace(f, ioStreams),
|
||||
wait.NewCmdWait(f, ioStreams),
|
||||
NewCmdConvert(f, ioStreams),
|
||||
},
|
||||
},
|
||||
{
|
||||
Message: "Settings Commands:",
|
||||
Commands: []*cobra.Command{
|
||||
NewCmdLabel(f, ioStreams),
|
||||
NewCmdAnnotate("kubectl", f, ioStreams),
|
||||
NewCmdCompletion(ioStreams.Out, ""),
|
||||
},
|
||||
},
|
||||
}
|
||||
groups.Add(cmds)
|
||||
|
||||
filters := []string{"options"}
|
||||
|
||||
// Hide the "alpha" subcommand if there are no alpha commands in this build.
|
||||
alpha := NewCmdAlpha(f, ioStreams)
|
||||
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(f, clientcmd.NewDefaultPathOptions(), ioStreams))
|
||||
cmds.AddCommand(NewCmdPlugin(f, ioStreams))
|
||||
cmds.AddCommand(NewCmdVersion(f, ioStreams))
|
||||
cmds.AddCommand(NewCmdApiVersions(f, ioStreams))
|
||||
cmds.AddCommand(NewCmdApiResources(f, ioStreams))
|
||||
cmds.AddCommand(NewCmdOptions(ioStreams.Out))
|
||||
|
||||
return cmds
|
||||
}
|
||||
|
||||
func runHelp(cmd *cobra.Command, args []string) {
|
||||
cmd.Help()
|
||||
}
|
||||
|
||||
func printDeprecationWarning(errOut io.Writer, command, alias string) {
|
||||
fmt.Fprintf(errOut, "%s is DEPRECATED and will be removed in a future version. Use %s instead.\n", 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
|
||||
}
|
||||
|
||||
var metadataAccessor = meta.NewAccessor()
|
234
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/cmd_printing_test.go
generated
vendored
234
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/cmd_printing_test.go
generated
vendored
@ -1,234 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
genericprinters "k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers"
|
||||
"k8s.io/kubernetes/pkg/kubectl/scheme"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
)
|
||||
|
||||
func TestIllegalPackageSourceCheckerThroughPrintFlags(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
expectInternalObjErr bool
|
||||
output string
|
||||
obj runtime.Object
|
||||
expectedOutput string
|
||||
}{
|
||||
{
|
||||
name: "success printer: object containing package path beginning with forbidden prefix is rejected",
|
||||
expectInternalObjErr: true,
|
||||
obj: internalPod(),
|
||||
},
|
||||
{
|
||||
name: "success printer: object containing package path with no forbidden prefix returns no error",
|
||||
expectInternalObjErr: false,
|
||||
obj: externalPod(),
|
||||
output: "",
|
||||
expectedOutput: "pod/foo succeeded\n",
|
||||
},
|
||||
{
|
||||
name: "name printer: object containing package path beginning with forbidden prefix is rejected",
|
||||
expectInternalObjErr: true,
|
||||
output: "name",
|
||||
obj: internalPod(),
|
||||
},
|
||||
{
|
||||
name: "json printer: object containing package path beginning with forbidden prefix is rejected",
|
||||
expectInternalObjErr: true,
|
||||
output: "json",
|
||||
obj: internalPod(),
|
||||
},
|
||||
{
|
||||
name: "json printer: object containing package path with no forbidden prefix returns no error",
|
||||
expectInternalObjErr: false,
|
||||
obj: externalPod(),
|
||||
output: "json",
|
||||
},
|
||||
{
|
||||
name: "yaml printer: object containing package path beginning with forbidden prefix is rejected",
|
||||
expectInternalObjErr: true,
|
||||
output: "yaml",
|
||||
obj: internalPod(),
|
||||
},
|
||||
{
|
||||
name: "yaml printer: object containing package path with no forbidden prefix returns no error",
|
||||
expectInternalObjErr: false,
|
||||
obj: externalPod(),
|
||||
output: "yaml",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
printFlags := genericclioptions.NewPrintFlags("succeeded").WithTypeSetter(scheme.Scheme)
|
||||
printFlags.OutputFormat = &tc.output
|
||||
|
||||
printer, err := printFlags.ToPrinter()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
output := bytes.NewBuffer([]byte{})
|
||||
|
||||
err = printer.PrintObj(tc.obj, output)
|
||||
if err != nil {
|
||||
if !tc.expectInternalObjErr {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
if !genericprinters.IsInternalObjectError(err) {
|
||||
t.Fatalf("unexpected error - expecting internal object printer error, got %q", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if tc.expectInternalObjErr {
|
||||
t.Fatalf("expected internal object printer error, but got no error")
|
||||
}
|
||||
|
||||
if len(tc.expectedOutput) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if tc.expectedOutput != output.String() {
|
||||
t.Fatalf("unexpected output: expecting %q, got %q", tc.expectedOutput, output.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIllegalPackageSourceCheckerDirectlyThroughPrinters(t *testing.T) {
|
||||
jsonPathPrinter, err := genericprinters.NewJSONPathPrinter("{ .metadata.name }")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
goTemplatePrinter, err := genericprinters.NewGoTemplatePrinter([]byte("{{ .metadata.name }}"))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
customColumns, err := printers.NewCustomColumnsPrinterFromSpec("NAME:.metadata.name", scheme.Codecs.UniversalDecoder(), true)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
expectInternalObjErr bool
|
||||
printer genericprinters.ResourcePrinter
|
||||
obj runtime.Object
|
||||
expectedOutput string
|
||||
}{
|
||||
{
|
||||
name: "json printer: object containing package path beginning with forbidden prefix is rejected",
|
||||
expectInternalObjErr: true,
|
||||
printer: &genericprinters.JSONPrinter{},
|
||||
obj: internalPod(),
|
||||
},
|
||||
{
|
||||
name: "yaml printer: object containing package path beginning with forbidden prefix is rejected",
|
||||
expectInternalObjErr: true,
|
||||
printer: &genericprinters.YAMLPrinter{},
|
||||
obj: internalPod(),
|
||||
},
|
||||
{
|
||||
name: "jsonpath printer: object containing package path beginning with forbidden prefix is rejected",
|
||||
expectInternalObjErr: true,
|
||||
printer: jsonPathPrinter,
|
||||
obj: internalPod(),
|
||||
},
|
||||
{
|
||||
name: "go-template printer: object containing package path beginning with forbidden prefix is rejected",
|
||||
expectInternalObjErr: true,
|
||||
printer: goTemplatePrinter,
|
||||
obj: internalPod(),
|
||||
},
|
||||
{
|
||||
name: "go-template printer: object containing package path beginning with forbidden prefix is rejected",
|
||||
expectInternalObjErr: true,
|
||||
printer: goTemplatePrinter,
|
||||
obj: internalPod(),
|
||||
},
|
||||
{
|
||||
name: "custom-columns printer: object containing package path beginning with forbidden prefix is rejected",
|
||||
expectInternalObjErr: true,
|
||||
printer: customColumns,
|
||||
obj: internalPod(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
output := bytes.NewBuffer([]byte{})
|
||||
|
||||
err := tc.printer.PrintObj(tc.obj, output)
|
||||
if err != nil {
|
||||
if !tc.expectInternalObjErr {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
if !genericprinters.IsInternalObjectError(err) {
|
||||
t.Fatalf("unexpected error - expecting internal object printer error, got %q", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if tc.expectInternalObjErr {
|
||||
t.Fatalf("expected internal object printer error, but got no error")
|
||||
}
|
||||
|
||||
if len(tc.expectedOutput) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if tc.expectedOutput != output.String() {
|
||||
t.Fatalf("unexpected output: expecting %q, got %q", tc.expectedOutput, output.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func internalPod() *api.Pod {
|
||||
return &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
{
|
||||
Name: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: api.PodStatus{
|
||||
Phase: api.PodRunning,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func externalPod() *v1.Pod {
|
||||
return &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
}
|
||||
}
|
308
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/cmd_test.go
generated
vendored
308
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/cmd_test.go
generated
vendored
@ -1,308 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
"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/kubernetes/pkg/api/testapi"
|
||||
apitesting "k8s.io/kubernetes/pkg/api/testing"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/scheme"
|
||||
)
|
||||
|
||||
// This init should be removed after switching this command and its tests to user external types.
|
||||
func init() {
|
||||
api.AddToScheme(scheme.Scheme)
|
||||
}
|
||||
|
||||
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: scheme.Codecs,
|
||||
ContentType: runtime.ContentTypeJSON,
|
||||
GroupVersion: &schema.GroupVersion{Version: "v1"},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func testData() (*api.PodList, *api.ServiceList, *api.ReplicationControllerList) {
|
||||
pods := &api.PodList{
|
||||
ListMeta: metav1.ListMeta{
|
||||
ResourceVersion: "15",
|
||||
},
|
||||
Items: []api.Pod{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"},
|
||||
Spec: apitesting.DeepEqualSafePodSpec(),
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "test", ResourceVersion: "11"},
|
||||
Spec: apitesting.DeepEqualSafePodSpec(),
|
||||
},
|
||||
},
|
||||
}
|
||||
svc := &api.ServiceList{
|
||||
ListMeta: metav1.ListMeta{
|
||||
ResourceVersion: "16",
|
||||
},
|
||||
Items: []api.Service{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
|
||||
Spec: api.ServiceSpec{
|
||||
SessionAffinity: "None",
|
||||
Type: api.ServiceTypeClusterIP,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
rc := &api.ReplicationControllerList{
|
||||
ListMeta: metav1.ListMeta{
|
||||
ResourceVersion: "17",
|
||||
},
|
||||
Items: []api.ReplicationController{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "rc1", Namespace: "test", ResourceVersion: "18"},
|
||||
Spec: api.ReplicationControllerSpec{
|
||||
Replicas: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return pods, svc, rc
|
||||
}
|
||||
|
||||
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)))
|
||||
}
|
||||
|
||||
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 TestNormalizationFuncGlobalExistence(t *testing.T) {
|
||||
// This test can be safely deleted when we will not support multiple flag formats
|
||||
root := NewKubectlCommand(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")
|
||||
}
|
||||
}
|
310
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/completion.go
generated
vendored
310
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/completion.go
generated
vendored
@ -1,310 +0,0 @@
|
||||
/*
|
||||
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 evaluated 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",
|
||||
DisableFlagsInUseLine: true,
|
||||
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
|
||||
|
||||
__kubectl_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="," __kubectl_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
|
||||
}
|
91
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/BUILD
generated
vendored
91
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/BUILD
generated
vendored
@ -1,91 +0,0 @@
|
||||
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",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/config",
|
||||
visibility = [
|
||||
"//build/visible_to:pkg_kubectl_cmd_config_CONSUMERS",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/kubectl/cmd/templates:go_default_library",
|
||||
"//pkg/kubectl/cmd/util:go_default_library",
|
||||
"//pkg/kubectl/genericclioptions: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/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",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/kubectl/cmd/util:go_default_library",
|
||||
"//pkg/kubectl/genericclioptions: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",
|
||||
],
|
||||
)
|
92
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/config.go
generated
vendored
92
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/config.go
generated
vendored
@ -1,92 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
"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/genericclioptions"
|
||||
"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(f cmdutil.Factory, pathOptions *clientcmd.PathOptions, streams genericclioptions.IOStreams) *cobra.Command {
|
||||
if len(pathOptions.ExplicitFileFlag) == 0 {
|
||||
pathOptions.ExplicitFileFlag = clientcmd.RecommendedConfigPathFlag
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "config SUBCOMMAND",
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: i18n.T("Modify kubeconfig files"),
|
||||
Long: templates.LongDesc(`
|
||||
Modify kubeconfig files using subcommands like "kubectl config set current-context my-context"
|
||||
|
||||
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(streams.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")
|
||||
|
||||
// TODO(juanvallejo): update all subcommands to work with genericclioptions.IOStreams
|
||||
cmd.AddCommand(NewCmdConfigView(f, streams, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigSetCluster(streams.Out, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigSetAuthInfo(streams.Out, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigSetContext(streams.Out, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigSet(streams.Out, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigUnset(streams.Out, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigCurrentContext(streams.Out, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigUseContext(streams.Out, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigGetContexts(streams, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigGetClusters(streams.Out, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigDeleteCluster(streams.Out, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigDeleteContext(streams.Out, streams.ErrOut, pathOptions))
|
||||
cmd.AddCommand(NewCmdConfigRenameContext(streams.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
|
||||
}
|
||||
|
||||
func helpErrorf(cmd *cobra.Command, format string, args ...interface{}) error {
|
||||
cmd.Help()
|
||||
msg := fmt.Sprintf(format, args...)
|
||||
return fmt.Errorf("%s\n", msg)
|
||||
}
|
949
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/config_test.go
generated
vendored
949
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/config_test.go
generated
vendored
@ -1,949 +0,0 @@
|
||||
/*
|
||||
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/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"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
)
|
||||
|
||||
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...)
|
||||
|
||||
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||
cmd := NewCmdConfig(cmdutil.NewFactory(genericclioptions.NewTestConfigFlags()), clientcmd.NewDefaultPathOptions(), streams)
|
||||
// "context" is a global flag, inherited from base kubectl command in the real world
|
||||
cmd.PersistentFlags().String("context", "", "The name of the kubeconfig context to use")
|
||||
cmd.SetArgs(argsToUse)
|
||||
cmd.Execute()
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
299
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_authinfo.go
generated
vendored
299
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_authinfo.go
generated
vendored
@ -1,299 +0,0 @@
|
||||
/*
|
||||
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),
|
||||
DisableFlagsInUseLine: true,
|
||||
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
260
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_authinfo_test.go
generated
vendored
@ -1,260 +0,0 @@
|
||||
/*
|
||||
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
179
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_cluster.go
generated
vendored
@ -1,179 +0,0 @@
|
||||
/*
|
||||
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),
|
||||
DisableFlagsInUseLine: true,
|
||||
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 {
|
||||
return helpErrorf(cmd, "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
119
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_cluster_test.go
generated
vendored
@ -1,119 +0,0 @@
|
||||
/*
|
||||
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
138
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_context.go
generated
vendored
@ -1,138 +0,0 @@
|
||||
/*
|
||||
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),
|
||||
DisableFlagsInUseLine: true,
|
||||
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 {
|
||||
return helpErrorf(cmd, "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
118
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_context_test.go
generated
vendored
@ -1,118 +0,0 @@
|
||||
/*
|
||||
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
74
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/current_context.go
generated
vendored
@ -1,74 +0,0 @@
|
||||
/*
|
||||
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
93
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/current_context_test.go
generated
vendored
@ -1,93 +0,0 @@
|
||||
/*
|
||||
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)
|
||||
}
|
||||
}
|
84
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/delete_cluster.go
generated
vendored
84
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/delete_cluster.go
generated
vendored
@ -1,84 +0,0 @@
|
||||
/*
|
||||
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",
|
||||
DisableFlagsInUseLine: true,
|
||||
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
97
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/delete_cluster_test.go
generated
vendored
@ -1,97 +0,0 @@
|
||||
/*
|
||||
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)
|
||||
}
|
||||
}
|
88
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/delete_context.go
generated
vendored
88
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/delete_context.go
generated
vendored
@ -1,88 +0,0 @@
|
||||
/*
|
||||
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",
|
||||
DisableFlagsInUseLine: true,
|
||||
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
98
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/delete_context_test.go
generated
vendored
@ -1,98 +0,0 @@
|
||||
/*
|
||||
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
65
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/get_clusters.go
generated
vendored
@ -1,65 +0,0 @@
|
||||
/*
|
||||
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
84
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/get_clusters_test.go
generated
vendored
@ -1,84 +0,0 @@
|
||||
/*
|
||||
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
|
||||
}
|
||||
}
|
180
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/get_contexts.go
generated
vendored
180
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/get_contexts.go
generated
vendored
@ -1,180 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
"sort"
|
||||
"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/genericclioptions"
|
||||
"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
|
||||
|
||||
genericclioptions.IOStreams
|
||||
}
|
||||
|
||||
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(streams genericclioptions.IOStreams, configAccess clientcmd.ConfigAccess) *cobra.Command {
|
||||
options := &GetContextsOptions{
|
||||
configAccess: configAccess,
|
||||
|
||||
IOStreams: streams,
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "get-contexts [(-o|--output=)name)]",
|
||||
DisableFlagsInUseLine: true,
|
||||
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(options.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))
|
||||
cmdutil.CheckErr(options.RunGetContexts())
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().Bool("no-headers", false, "When using the default or custom-column output format, don't print headers (default print headers).")
|
||||
cmd.Flags().StringP("output", "o", "", "Output format. One of: name")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Complete assigns GetContextsOptions from the args.
|
||||
func (o *GetContextsOptions) Complete(cmd *cobra.Command, args []string) error {
|
||||
o.contextNames = args
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(toPrint)
|
||||
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
|
||||
}
|
178
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/get_contexts_test.go
generated
vendored
178
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/get_contexts_test.go
generated
vendored
@ -1,178 +0,0 @@
|
||||
/*
|
||||
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/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
)
|
||||
|
||||
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 TestGetContextsAllSorted(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"},
|
||||
"abc": {AuthInfo: "blue-user", Cluster: "abc-cluster", Namespace: "kube-system"},
|
||||
"xyz": {AuthInfo: "blue-user", Cluster: "xyz-cluster", Namespace: "default"}}}
|
||||
test := getContextsTest{
|
||||
startingConfig: tconf,
|
||||
names: []string{},
|
||||
noHeader: false,
|
||||
nameOnly: false,
|
||||
expectedOut: `CURRENT NAME CLUSTER AUTHINFO NAMESPACE
|
||||
abc abc-cluster blue-user kube-system
|
||||
* shaker-context big-cluster blue-user saw-ns
|
||||
xyz xyz-cluster blue-user default
|
||||
`,
|
||||
}
|
||||
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 = ""
|
||||
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||
options := GetContextsOptions{
|
||||
configAccess: pathOptions,
|
||||
}
|
||||
cmd := NewCmdConfigGetContexts(streams, 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
152
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/navigation_step_parser.go
generated
vendored
@ -1,152 +0,0 @@
|
||||
/*
|
||||
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
96
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/config/navigation_step_parser_test.go
generated
vendored
@ -1,96 +0,0 @@
|
||||
/*
|
||||
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)
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user