Update go dependencies
This commit is contained in:
parent
e0561ddeb9
commit
88a2751234
1970 changed files with 413928 additions and 222867 deletions
179
vendor/k8s.io/kubernetes/pkg/kubectl/BUILD
generated
vendored
179
vendor/k8s.io/kubernetes/pkg/kubectl/BUILD
generated
vendored
|
|
@ -1,179 +0,0 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_binary",
|
||||
"go_library",
|
||||
"go_test",
|
||||
"cgo_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"apply.go",
|
||||
"autoscale.go",
|
||||
"bash_comp_utils.go",
|
||||
"cluster.go",
|
||||
"configmap.go",
|
||||
"custom_column_printer.go",
|
||||
"deployment.go",
|
||||
"describe.go",
|
||||
"doc.go",
|
||||
"explain.go",
|
||||
"generate.go",
|
||||
"history.go",
|
||||
"interfaces.go",
|
||||
"kubectl.go",
|
||||
"namespace.go",
|
||||
"proxy_server.go",
|
||||
"quota.go",
|
||||
"resource_filter.go",
|
||||
"resource_printer.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",
|
||||
"sorted_resource_name_list.go",
|
||||
"sorting_printer.go",
|
||||
"stop.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//federation/apis/federation:go_default_library",
|
||||
"//federation/apis/federation/v1beta1:go_default_library",
|
||||
"//federation/client/clientset_generated/federation_internalclientset:go_default_library",
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/annotations:go_default_library",
|
||||
"//pkg/api/errors:go_default_library",
|
||||
"//pkg/api/events:go_default_library",
|
||||
"//pkg/api/meta:go_default_library",
|
||||
"//pkg/api/resource:go_default_library",
|
||||
"//pkg/api/unversioned:go_default_library",
|
||||
"//pkg/api/util:go_default_library",
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/apis/apps:go_default_library",
|
||||
"//pkg/apis/autoscaling:go_default_library",
|
||||
"//pkg/apis/batch:go_default_library",
|
||||
"//pkg/apis/batch/v1:go_default_library",
|
||||
"//pkg/apis/batch/v2alpha1:go_default_library",
|
||||
"//pkg/apis/certificates:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//pkg/apis/policy:go_default_library",
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/apis/storage:go_default_library",
|
||||
"//pkg/apis/storage/util:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/typed/apps/internalversion:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/typed/batch/internalversion:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion:go_default_library",
|
||||
"//pkg/client/restclient:go_default_library",
|
||||
"//pkg/client/retry:go_default_library",
|
||||
"//pkg/client/unversioned:go_default_library",
|
||||
"//pkg/controller/deployment/util:go_default_library",
|
||||
"//pkg/credentialprovider:go_default_library",
|
||||
"//pkg/fieldpath:go_default_library",
|
||||
"//pkg/fields:go_default_library",
|
||||
"//pkg/kubectl/resource:go_default_library",
|
||||
"//pkg/kubelet/qos:go_default_library",
|
||||
"//pkg/labels:go_default_library",
|
||||
"//pkg/runtime:go_default_library",
|
||||
"//pkg/types:go_default_library",
|
||||
"//pkg/util:go_default_library",
|
||||
"//pkg/util/cert:go_default_library",
|
||||
"//pkg/util/errors:go_default_library",
|
||||
"//pkg/util/integer:go_default_library",
|
||||
"//pkg/util/intstr:go_default_library",
|
||||
"//pkg/util/jsonpath:go_default_library",
|
||||
"//pkg/util/node:go_default_library",
|
||||
"//pkg/util/sets:go_default_library",
|
||||
"//pkg/util/slice:go_default_library",
|
||||
"//pkg/util/uuid:go_default_library",
|
||||
"//pkg/util/validation:go_default_library",
|
||||
"//pkg/util/wait:go_default_library",
|
||||
"//pkg/watch:go_default_library",
|
||||
"//vendor:github.com/emicklei/go-restful/swagger",
|
||||
"//vendor:github.com/ghodss/yaml",
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:github.com/spf13/cobra",
|
||||
"//vendor:github.com/spf13/pflag",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"cluster_test.go",
|
||||
"configmap_test.go",
|
||||
"custom_column_printer_test.go",
|
||||
"deployment_test.go",
|
||||
"describe_test.go",
|
||||
"generate_test.go",
|
||||
"kubectl_test.go",
|
||||
"namespace_test.go",
|
||||
"proxy_server_test.go",
|
||||
"quota_test.go",
|
||||
"resource_printer_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",
|
||||
"sorted_resource_name_list_test.go",
|
||||
"sorting_printer_test.go",
|
||||
"stop_test.go",
|
||||
],
|
||||
library = "go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//federation/apis/federation:go_default_library",
|
||||
"//federation/apis/federation/v1beta1:go_default_library",
|
||||
"//federation/client/clientset_generated/federation_internalclientset/fake:go_default_library",
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/errors:go_default_library",
|
||||
"//pkg/api/resource:go_default_library",
|
||||
"//pkg/api/testapi:go_default_library",
|
||||
"//pkg/api/testing:go_default_library",
|
||||
"//pkg/api/unversioned:go_default_library",
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/apimachinery/registered:go_default_library",
|
||||
"//pkg/apis/batch:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//pkg/apis/policy:go_default_library",
|
||||
"//pkg/apis/storage:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/typed/batch/internalversion:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion:go_default_library",
|
||||
"//pkg/client/restclient:go_default_library",
|
||||
"//pkg/client/restclient/fake:go_default_library",
|
||||
"//pkg/client/testing/core:go_default_library",
|
||||
"//pkg/controller/deployment/util:go_default_library",
|
||||
"//pkg/kubectl/testing:go_default_library",
|
||||
"//pkg/runtime:go_default_library",
|
||||
"//pkg/runtime/serializer/yaml:go_default_library",
|
||||
"//pkg/util/diff:go_default_library",
|
||||
"//pkg/util/intstr:go_default_library",
|
||||
"//pkg/util/sets:go_default_library",
|
||||
"//pkg/util/testing:go_default_library",
|
||||
"//pkg/watch:go_default_library",
|
||||
"//vendor:github.com/ghodss/yaml",
|
||||
"//vendor:github.com/spf13/cobra",
|
||||
],
|
||||
)
|
||||
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
|
||||
190
vendor/k8s.io/kubernetes/pkg/kubectl/apply.go
generated
vendored
190
vendor/k8s.io/kubernetes/pkg/kubectl/apply.go
generated
vendored
|
|
@ -1,190 +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 (
|
||||
"encoding/json"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/annotations"
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
"k8s.io/kubernetes/pkg/kubectl/resource"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
// GetOriginalConfiguration retrieves the original configuration of the object
|
||||
// from the annotation, or nil if no annotation was found.
|
||||
func GetOriginalConfiguration(mapping *meta.RESTMapping, obj runtime.Object) ([]byte, error) {
|
||||
annots, err := mapping.MetadataAccessor.Annotations(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if annots == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
original, ok := annots[annotations.LastAppliedConfigAnnotation]
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return []byte(original), nil
|
||||
}
|
||||
|
||||
// SetOriginalConfiguration sets the original configuration of the object
|
||||
// as the annotation on the object for later use in computing a three way patch.
|
||||
func SetOriginalConfiguration(info *resource.Info, original []byte) error {
|
||||
if len(original) < 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
accessor := info.Mapping.MetadataAccessor
|
||||
annots, err := accessor.Annotations(info.Object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if annots == nil {
|
||||
annots = map[string]string{}
|
||||
}
|
||||
|
||||
annots[annotations.LastAppliedConfigAnnotation] = string(original)
|
||||
if err := info.Mapping.MetadataAccessor.SetAnnotations(info.Object, annots); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetModifiedConfiguration retrieves the modified configuration of the object.
|
||||
// If annotate is true, it embeds the result as an annotation in the modified
|
||||
// configuration. If an object was read from the command input, it will use that
|
||||
// version of the object. Otherwise, it will use the version from the server.
|
||||
func GetModifiedConfiguration(info *resource.Info, annotate bool, codec runtime.Encoder) ([]byte, error) {
|
||||
// First serialize the object without the annotation to prevent recursion,
|
||||
// then add that serialization to it as the annotation and serialize it again.
|
||||
var modified []byte
|
||||
if info.VersionedObject != nil {
|
||||
// If an object was read from input, use that version.
|
||||
accessor, err := meta.Accessor(info.VersionedObject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get the current annotations from the object.
|
||||
annots := accessor.GetAnnotations()
|
||||
if annots == nil {
|
||||
annots = map[string]string{}
|
||||
}
|
||||
|
||||
original := annots[annotations.LastAppliedConfigAnnotation]
|
||||
delete(annots, annotations.LastAppliedConfigAnnotation)
|
||||
accessor.SetAnnotations(annots)
|
||||
// TODO: this needs to be abstracted - there should be no assumption that versioned object
|
||||
// can be marshalled to JSON.
|
||||
modified, err = json.Marshal(info.VersionedObject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if annotate {
|
||||
annots[annotations.LastAppliedConfigAnnotation] = string(modified)
|
||||
accessor.SetAnnotations(annots)
|
||||
// TODO: this needs to be abstracted - there should be no assumption that versioned object
|
||||
// can be marshalled to JSON.
|
||||
modified, err = json.Marshal(info.VersionedObject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the object to its original condition.
|
||||
annots[annotations.LastAppliedConfigAnnotation] = original
|
||||
accessor.SetAnnotations(annots)
|
||||
} else {
|
||||
// Otherwise, use the server side version of the object.
|
||||
accessor := info.Mapping.MetadataAccessor
|
||||
// Get the current annotations from the object.
|
||||
annots, err := accessor.Annotations(info.Object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if annots == nil {
|
||||
annots = map[string]string{}
|
||||
}
|
||||
|
||||
original := annots[annotations.LastAppliedConfigAnnotation]
|
||||
delete(annots, annotations.LastAppliedConfigAnnotation)
|
||||
if err := accessor.SetAnnotations(info.Object, annots); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
modified, err = runtime.Encode(codec, info.Object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if annotate {
|
||||
annots[annotations.LastAppliedConfigAnnotation] = string(modified)
|
||||
if err := info.Mapping.MetadataAccessor.SetAnnotations(info.Object, annots); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
modified, err = runtime.Encode(codec, info.Object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the object to its original condition.
|
||||
annots[annotations.LastAppliedConfigAnnotation] = original
|
||||
if err := info.Mapping.MetadataAccessor.SetAnnotations(info.Object, annots); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return modified, nil
|
||||
}
|
||||
|
||||
// UpdateApplyAnnotation calls CreateApplyAnnotation if the last applied
|
||||
// configuration annotation is already present. Otherwise, it does nothing.
|
||||
func UpdateApplyAnnotation(info *resource.Info, codec runtime.Encoder) error {
|
||||
if original, err := GetOriginalConfiguration(info.Mapping, info.Object); err != nil || len(original) <= 0 {
|
||||
return err
|
||||
}
|
||||
return CreateApplyAnnotation(info, codec)
|
||||
}
|
||||
|
||||
// CreateApplyAnnotation gets the modified configuration of the object,
|
||||
// without embedding it again, and then sets it on the object as the annotation.
|
||||
func CreateApplyAnnotation(info *resource.Info, codec runtime.Encoder) error {
|
||||
modified, err := GetModifiedConfiguration(info, false, codec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return SetOriginalConfiguration(info, modified)
|
||||
}
|
||||
|
||||
// Create the annotation used by kubectl apply only when createAnnotation is true
|
||||
// Otherwise, only update the annotation when it already exists
|
||||
func CreateOrUpdateAnnotation(createAnnotation bool, info *resource.Info, codec runtime.Encoder) error {
|
||||
if createAnnotation {
|
||||
return CreateApplyAnnotation(info, codec)
|
||||
}
|
||||
return UpdateApplyAnnotation(info, codec)
|
||||
}
|
||||
130
vendor/k8s.io/kubernetes/pkg/kubectl/autoscale.go
generated
vendored
130
vendor/k8s.io/kubernetes/pkg/kubectl/autoscale.go
generated
vendored
|
|
@ -1,130 +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"
|
||||
"strconv"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/autoscaling"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
type HorizontalPodAutoscalerV1Beta1 struct{}
|
||||
|
||||
func (HorizontalPodAutoscalerV1Beta1) ParamNames() []GeneratorParam {
|
||||
return []GeneratorParam{
|
||||
{"default-name", true},
|
||||
{"name", false},
|
||||
{"scaleRef-kind", false},
|
||||
{"scaleRef-name", false},
|
||||
{"scaleRef-apiVersion", false},
|
||||
{"scaleRef-subresource", false},
|
||||
{"min", false},
|
||||
{"max", true},
|
||||
{"cpu-percent", false},
|
||||
}
|
||||
}
|
||||
|
||||
func (HorizontalPodAutoscalerV1Beta1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
return generateHPA(genericParams)
|
||||
}
|
||||
|
||||
type HorizontalPodAutoscalerV1 struct{}
|
||||
|
||||
func (HorizontalPodAutoscalerV1) ParamNames() []GeneratorParam {
|
||||
return []GeneratorParam{
|
||||
{"default-name", true},
|
||||
{"name", false},
|
||||
{"scaleRef-kind", false},
|
||||
{"scaleRef-name", false},
|
||||
{"scaleRef-apiVersion", false},
|
||||
{"min", false},
|
||||
{"max", true},
|
||||
{"cpu-percent", false},
|
||||
}
|
||||
}
|
||||
|
||||
func (HorizontalPodAutoscalerV1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
return generateHPA(genericParams)
|
||||
}
|
||||
|
||||
func generateHPA(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
params := map[string]string{}
|
||||
for key, value := range genericParams {
|
||||
strVal, isString := value.(string)
|
||||
if !isString {
|
||||
return nil, fmt.Errorf("expected string, saw %v for '%s'", value, key)
|
||||
}
|
||||
params[key] = strVal
|
||||
}
|
||||
|
||||
name, found := params["name"]
|
||||
if !found || len(name) == 0 {
|
||||
name, found = params["default-name"]
|
||||
if !found || len(name) == 0 {
|
||||
return nil, fmt.Errorf("'name' is a required parameter.")
|
||||
}
|
||||
}
|
||||
minString, found := params["min"]
|
||||
min := -1
|
||||
var err error
|
||||
if found {
|
||||
if min, err = strconv.Atoi(minString); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
maxString, found := params["max"]
|
||||
if !found {
|
||||
return nil, fmt.Errorf("'max' is a required parameter.")
|
||||
}
|
||||
max, err := strconv.Atoi(maxString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cpuString, found := params["cpu-percent"]
|
||||
cpu := -1
|
||||
if found {
|
||||
if cpu, err = strconv.Atoi(cpuString); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
scaler := autoscaling.HorizontalPodAutoscaler{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: autoscaling.HorizontalPodAutoscalerSpec{
|
||||
ScaleTargetRef: autoscaling.CrossVersionObjectReference{
|
||||
Kind: params["scaleRef-kind"],
|
||||
Name: params["scaleRef-name"],
|
||||
APIVersion: params["scaleRef-apiVersion"],
|
||||
},
|
||||
MaxReplicas: int32(max),
|
||||
},
|
||||
}
|
||||
if min > 0 {
|
||||
v := int32(min)
|
||||
scaler.Spec.MinReplicas = &v
|
||||
}
|
||||
if cpu >= 0 {
|
||||
c := int32(cpu)
|
||||
scaler.Spec.TargetCPUUtilizationPercentage = &c
|
||||
}
|
||||
return &scaler, nil
|
||||
}
|
||||
36
vendor/k8s.io/kubernetes/pkg/kubectl/bash_comp_utils.go
generated
vendored
36
vendor/k8s.io/kubernetes/pkg/kubectl/bash_comp_utils.go
generated
vendored
|
|
@ -1,36 +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.
|
||||
*/
|
||||
|
||||
// A set of common functions needed by cmd/kubectl and pkg/kubectl packages.
|
||||
|
||||
package kubectl
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl/resource"
|
||||
)
|
||||
|
||||
func AddJsonFilenameFlag(cmd *cobra.Command, value *[]string, usage string) {
|
||||
cmd.Flags().StringSliceVarP(value, "filename", "f", *value, usage)
|
||||
annotations := make([]string, 0, len(resource.FileExtensions))
|
||||
for _, ext := range resource.FileExtensions {
|
||||
annotations = append(annotations, strings.TrimLeft(ext, "."))
|
||||
}
|
||||
cmd.Flags().SetAnnotation("filename", cobra.BashCompFilenameExt, annotations)
|
||||
}
|
||||
125
vendor/k8s.io/kubernetes/pkg/kubectl/cluster.go
generated
vendored
125
vendor/k8s.io/kubernetes/pkg/kubectl/cluster.go
generated
vendored
|
|
@ -1,125 +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"
|
||||
|
||||
federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
// ClusterGeneratorV1Beta1 supports stable generation of a
|
||||
// federation/cluster resource.
|
||||
type ClusterGeneratorV1Beta1 struct {
|
||||
// Name of the cluster context (required)
|
||||
Name string
|
||||
// ClientCIDR is the CIDR range in which the Kubernetes APIServer
|
||||
// is available for the client (optional)
|
||||
ClientCIDR string
|
||||
// ServerAddress is the APIServer address of the Kubernetes cluster
|
||||
// that is being registered (required)
|
||||
ServerAddress string
|
||||
// SecretName is the name of the secret that stores the credentials
|
||||
// for the Kubernetes cluster that is being registered (optional)
|
||||
SecretName string
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameter
|
||||
// injection.
|
||||
var _ Generator = &ClusterGeneratorV1Beta1{}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters
|
||||
// specified during construction.
|
||||
var _ StructuredGenerator = &ClusterGeneratorV1Beta1{}
|
||||
|
||||
// Generate returns a cluster resource using the specified parameters.
|
||||
func (s ClusterGeneratorV1Beta1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
err := ValidateParams(s.ParamNames(), genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clustergen := &ClusterGeneratorV1Beta1{}
|
||||
params := map[string]string{}
|
||||
for key, value := range genericParams {
|
||||
strVal, isString := value.(string)
|
||||
if !isString {
|
||||
return nil, fmt.Errorf("expected string, saw %v for '%s'", value, key)
|
||||
}
|
||||
params[key] = strVal
|
||||
}
|
||||
clustergen.Name = params["name"]
|
||||
clustergen.ClientCIDR = params["client-cidr"]
|
||||
clustergen.ServerAddress = params["server-address"]
|
||||
clustergen.SecretName = params["secret"]
|
||||
return clustergen.StructuredGenerate()
|
||||
}
|
||||
|
||||
// ParamNames returns the set of supported input parameters when using
|
||||
// the parameter injection generator pattern.
|
||||
func (s ClusterGeneratorV1Beta1) ParamNames() []GeneratorParam {
|
||||
return []GeneratorParam{
|
||||
{"name", true},
|
||||
{"client-cidr", false},
|
||||
{"server-address", true},
|
||||
{"secret", false},
|
||||
}
|
||||
}
|
||||
|
||||
// StructuredGenerate outputs a federation cluster resource object
|
||||
// using the configured fields.
|
||||
func (s ClusterGeneratorV1Beta1) StructuredGenerate() (runtime.Object, error) {
|
||||
if err := s.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if s.ClientCIDR == "" {
|
||||
s.ClientCIDR = "0.0.0.0/0"
|
||||
}
|
||||
if s.SecretName == "" {
|
||||
s.SecretName = s.Name
|
||||
}
|
||||
cluster := &federationapi.Cluster{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: s.Name,
|
||||
},
|
||||
Spec: federationapi.ClusterSpec{
|
||||
ServerAddressByClientCIDRs: []federationapi.ServerAddressByClientCIDR{
|
||||
{
|
||||
ClientCIDR: s.ClientCIDR,
|
||||
ServerAddress: s.ServerAddress,
|
||||
},
|
||||
},
|
||||
SecretRef: &v1.LocalObjectReference{
|
||||
Name: s.SecretName,
|
||||
},
|
||||
},
|
||||
}
|
||||
return cluster, nil
|
||||
}
|
||||
|
||||
// validate validates required fields are set to support structured
|
||||
// generation.
|
||||
func (s ClusterGeneratorV1Beta1) validate() error {
|
||||
if len(s.Name) == 0 {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
if len(s.ServerAddress) == 0 {
|
||||
return fmt.Errorf("server address must be specified")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
111
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/BUILD
generated
vendored
111
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/BUILD
generated
vendored
|
|
@ -1,111 +0,0 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_binary",
|
||||
"go_library",
|
||||
"go_test",
|
||||
"cgo_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"cached_discovery.go",
|
||||
"clientcache.go",
|
||||
"factory.go",
|
||||
"helpers.go",
|
||||
"printing.go",
|
||||
"shortcut_restmapper.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//federation/apis/federation:go_default_library",
|
||||
"//federation/client/clientset_generated/federation_internalclientset:go_default_library",
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/errors:go_default_library",
|
||||
"//pkg/api/meta:go_default_library",
|
||||
"//pkg/api/service:go_default_library",
|
||||
"//pkg/api/unversioned:go_default_library",
|
||||
"//pkg/api/validation:go_default_library",
|
||||
"//pkg/apimachinery/registered:go_default_library",
|
||||
"//pkg/apis/apps:go_default_library",
|
||||
"//pkg/apis/batch:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
|
||||
"//pkg/client/restclient:go_default_library",
|
||||
"//pkg/client/typed/discovery:go_default_library",
|
||||
"//pkg/client/typed/dynamic:go_default_library",
|
||||
"//pkg/client/unversioned:go_default_library",
|
||||
"//pkg/client/unversioned/clientcmd:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//pkg/kubectl:go_default_library",
|
||||
"//pkg/kubectl/resource:go_default_library",
|
||||
"//pkg/labels:go_default_library",
|
||||
"//pkg/registry/extensions/thirdpartyresourcedata:go_default_library",
|
||||
"//pkg/runtime:go_default_library",
|
||||
"//pkg/runtime/serializer/json:go_default_library",
|
||||
"//pkg/util/errors:go_default_library",
|
||||
"//pkg/util/exec:go_default_library",
|
||||
"//pkg/util/flag:go_default_library",
|
||||
"//pkg/util/homedir:go_default_library",
|
||||
"//pkg/util/sets:go_default_library",
|
||||
"//pkg/util/strategicpatch:go_default_library",
|
||||
"//pkg/version:go_default_library",
|
||||
"//pkg/watch:go_default_library",
|
||||
"//vendor:github.com/emicklei/go-restful/swagger",
|
||||
"//vendor:github.com/evanphx/json-patch",
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:github.com/spf13/cobra",
|
||||
"//vendor:github.com/spf13/pflag",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"cached_discovery_test.go",
|
||||
"factory_test.go",
|
||||
"helpers_test.go",
|
||||
"shortcut_restmapper_test.go",
|
||||
],
|
||||
data = [
|
||||
"//api/swagger-spec",
|
||||
],
|
||||
library = "go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/errors:go_default_library",
|
||||
"//pkg/api/meta:go_default_library",
|
||||
"//pkg/api/testapi:go_default_library",
|
||||
"//pkg/api/testing:go_default_library",
|
||||
"//pkg/api/unversioned:go_default_library",
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/api/validation:go_default_library",
|
||||
"//pkg/apimachinery/registered:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
|
||||
"//pkg/client/restclient:go_default_library",
|
||||
"//pkg/client/restclient/fake:go_default_library",
|
||||
"//pkg/client/testing/core:go_default_library",
|
||||
"//pkg/client/typed/discovery:go_default_library",
|
||||
"//pkg/client/unversioned/clientcmd:go_default_library",
|
||||
"//pkg/client/unversioned/clientcmd/api:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//pkg/kubectl:go_default_library",
|
||||
"//pkg/kubectl/resource:go_default_library",
|
||||
"//pkg/labels:go_default_library",
|
||||
"//pkg/runtime:go_default_library",
|
||||
"//pkg/util/exec:go_default_library",
|
||||
"//pkg/util/flag:go_default_library",
|
||||
"//pkg/util/validation/field:go_default_library",
|
||||
"//pkg/version:go_default_library",
|
||||
"//pkg/watch:go_default_library",
|
||||
"//vendor:github.com/emicklei/go-restful/swagger",
|
||||
"//vendor:github.com/stretchr/testify/assert",
|
||||
],
|
||||
)
|
||||
252
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/cached_discovery.go
generated
vendored
252
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/cached_discovery.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 util
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/emicklei/go-restful/swagger"
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
"k8s.io/kubernetes/pkg/client/typed/discovery"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/version"
|
||||
)
|
||||
|
||||
// CachedDiscoveryClient implements the functions that discovery server-supported API groups,
|
||||
// versions and resources.
|
||||
type CachedDiscoveryClient struct {
|
||||
delegate discovery.DiscoveryInterface
|
||||
|
||||
// cacheDirectory is the directory where discovery docs are held. It must be unique per host:port combination to work well.
|
||||
cacheDirectory string
|
||||
|
||||
// ttl is how long the cache should be considered valid
|
||||
ttl time.Duration
|
||||
|
||||
// mutex protects the variables below
|
||||
mutex sync.Mutex
|
||||
|
||||
// ourFiles are all filenames of cache files created by this process
|
||||
ourFiles map[string]struct{}
|
||||
// invalidated is true if all cache files should be ignored that are not ours (e.g. after Invalidate() was called)
|
||||
invalidated bool
|
||||
// fresh is true if all used cache files were ours
|
||||
fresh bool
|
||||
}
|
||||
|
||||
var _ discovery.CachedDiscoveryInterface = &CachedDiscoveryClient{}
|
||||
|
||||
// ServerResourcesForGroupVersion returns the supported resources for a group and version.
|
||||
func (d *CachedDiscoveryClient) ServerResourcesForGroupVersion(groupVersion string) (*unversioned.APIResourceList, error) {
|
||||
filename := filepath.Join(d.cacheDirectory, groupVersion, "serverresources.json")
|
||||
cachedBytes, err := d.getCachedFile(filename)
|
||||
// don't fail on errors, we either don't have a file or won't be able to run the cached check. Either way we can fallback.
|
||||
if err == nil {
|
||||
cachedResources := &unversioned.APIResourceList{}
|
||||
if err := runtime.DecodeInto(api.Codecs.UniversalDecoder(), cachedBytes, cachedResources); err == nil {
|
||||
glog.V(6).Infof("returning cached discovery info from %v", filename)
|
||||
return cachedResources, nil
|
||||
}
|
||||
}
|
||||
|
||||
liveResources, err := d.delegate.ServerResourcesForGroupVersion(groupVersion)
|
||||
if err != nil {
|
||||
return liveResources, err
|
||||
}
|
||||
|
||||
if err := d.writeCachedFile(filename, liveResources); err != nil {
|
||||
glog.V(3).Infof("failed to write cache to %v due to %v", filename, err)
|
||||
}
|
||||
|
||||
return liveResources, nil
|
||||
}
|
||||
|
||||
// ServerResources returns the supported resources for all groups and versions.
|
||||
func (d *CachedDiscoveryClient) ServerResources() (map[string]*unversioned.APIResourceList, error) {
|
||||
apiGroups, err := d.ServerGroups()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
groupVersions := unversioned.ExtractGroupVersions(apiGroups)
|
||||
result := map[string]*unversioned.APIResourceList{}
|
||||
for _, groupVersion := range groupVersions {
|
||||
resources, err := d.ServerResourcesForGroupVersion(groupVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result[groupVersion] = resources
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (d *CachedDiscoveryClient) ServerGroups() (*unversioned.APIGroupList, error) {
|
||||
filename := filepath.Join(d.cacheDirectory, "servergroups.json")
|
||||
cachedBytes, err := d.getCachedFile(filename)
|
||||
// don't fail on errors, we either don't have a file or won't be able to run the cached check. Either way we can fallback.
|
||||
if err == nil {
|
||||
cachedGroups := &unversioned.APIGroupList{}
|
||||
if err := runtime.DecodeInto(api.Codecs.UniversalDecoder(), cachedBytes, cachedGroups); err == nil {
|
||||
glog.V(6).Infof("returning cached discovery info from %v", filename)
|
||||
return cachedGroups, nil
|
||||
}
|
||||
}
|
||||
|
||||
liveGroups, err := d.delegate.ServerGroups()
|
||||
if err != nil {
|
||||
return liveGroups, err
|
||||
}
|
||||
|
||||
if err := d.writeCachedFile(filename, liveGroups); err != nil {
|
||||
glog.V(3).Infof("failed to write cache to %v due to %v", filename, err)
|
||||
}
|
||||
|
||||
return liveGroups, nil
|
||||
}
|
||||
|
||||
func (d *CachedDiscoveryClient) getCachedFile(filename string) ([]byte, error) {
|
||||
// after invalidation ignore cache files not created by this process
|
||||
d.mutex.Lock()
|
||||
_, ourFile := d.ourFiles[filename]
|
||||
if d.invalidated && !ourFile {
|
||||
d.mutex.Unlock()
|
||||
return nil, errors.New("cache invalidated")
|
||||
}
|
||||
d.mutex.Unlock()
|
||||
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fileInfo, err := file.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if time.Now().After(fileInfo.ModTime().Add(d.ttl)) {
|
||||
return nil, errors.New("cache expired")
|
||||
}
|
||||
|
||||
// the cache is present and its valid. Try to read and use it.
|
||||
cachedBytes, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
d.fresh = d.fresh && ourFile
|
||||
|
||||
return cachedBytes, nil
|
||||
}
|
||||
|
||||
func (d *CachedDiscoveryClient) writeCachedFile(filename string, obj runtime.Object) error {
|
||||
if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bytes, err := runtime.Encode(api.Codecs.LegacyCodec(), obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := ioutil.TempFile(filepath.Dir(filename), filepath.Base(filename)+".")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
_, err = f.Write(bytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = f.Chmod(0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name := f.Name()
|
||||
err = f.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// atomic rename
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
err = os.Rename(name, filename)
|
||||
if err == nil {
|
||||
d.ourFiles[filename] = struct{}{}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *CachedDiscoveryClient) RESTClient() restclient.Interface {
|
||||
return d.delegate.RESTClient()
|
||||
}
|
||||
|
||||
func (d *CachedDiscoveryClient) ServerPreferredResources() ([]unversioned.GroupVersionResource, error) {
|
||||
return d.delegate.ServerPreferredResources()
|
||||
}
|
||||
|
||||
func (d *CachedDiscoveryClient) ServerPreferredNamespacedResources() ([]unversioned.GroupVersionResource, error) {
|
||||
return d.delegate.ServerPreferredNamespacedResources()
|
||||
}
|
||||
|
||||
func (d *CachedDiscoveryClient) ServerVersion() (*version.Info, error) {
|
||||
return d.delegate.ServerVersion()
|
||||
}
|
||||
|
||||
func (d *CachedDiscoveryClient) SwaggerSchema(version unversioned.GroupVersion) (*swagger.ApiDeclaration, error) {
|
||||
return d.delegate.SwaggerSchema(version)
|
||||
}
|
||||
|
||||
func (d *CachedDiscoveryClient) Fresh() bool {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
return d.fresh
|
||||
}
|
||||
|
||||
func (d *CachedDiscoveryClient) Invalidate() {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
d.ourFiles = map[string]struct{}{}
|
||||
d.fresh = true
|
||||
d.invalidated = true
|
||||
}
|
||||
|
||||
// NewCachedDiscoveryClient creates a new DiscoveryClient. cacheDirectory is the directory where discovery docs are held. It must be unique per host:port combination to work well.
|
||||
func NewCachedDiscoveryClient(delegate discovery.DiscoveryInterface, cacheDirectory string, ttl time.Duration) *CachedDiscoveryClient {
|
||||
return &CachedDiscoveryClient{
|
||||
delegate: delegate,
|
||||
cacheDirectory: cacheDirectory,
|
||||
ttl: ttl,
|
||||
ourFiles: map[string]struct{}{},
|
||||
fresh: true,
|
||||
}
|
||||
}
|
||||
197
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/clientcache.go
generated
vendored
197
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/clientcache.go
generated
vendored
|
|
@ -1,197 +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 util
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
fed_clientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_internalclientset"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
"k8s.io/kubernetes/pkg/client/typed/discovery"
|
||||
oldclient "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
|
||||
)
|
||||
|
||||
func NewClientCache(loader clientcmd.ClientConfig) *ClientCache {
|
||||
return &ClientCache{
|
||||
clientsets: make(map[unversioned.GroupVersion]*internalclientset.Clientset),
|
||||
configs: make(map[unversioned.GroupVersion]*restclient.Config),
|
||||
fedClientSets: make(map[unversioned.GroupVersion]fed_clientset.Interface),
|
||||
loader: loader,
|
||||
}
|
||||
}
|
||||
|
||||
// ClientCache caches previously loaded clients for reuse, and ensures MatchServerVersion
|
||||
// is invoked only once
|
||||
type ClientCache struct {
|
||||
loader clientcmd.ClientConfig
|
||||
clientsets map[unversioned.GroupVersion]*internalclientset.Clientset
|
||||
fedClientSets map[unversioned.GroupVersion]fed_clientset.Interface
|
||||
configs map[unversioned.GroupVersion]*restclient.Config
|
||||
|
||||
matchVersion bool
|
||||
|
||||
defaultConfigLock sync.Mutex
|
||||
defaultConfig *restclient.Config
|
||||
discoveryClient discovery.DiscoveryInterface
|
||||
}
|
||||
|
||||
// also looks up the discovery client. We can't do this during init because the flags won't have been set
|
||||
// because this is constructed pre-command execution before the command tree is even set up
|
||||
func (c *ClientCache) getDefaultConfig() (restclient.Config, discovery.DiscoveryInterface, error) {
|
||||
c.defaultConfigLock.Lock()
|
||||
defer c.defaultConfigLock.Unlock()
|
||||
|
||||
if c.defaultConfig != nil && c.discoveryClient != nil {
|
||||
return *c.defaultConfig, c.discoveryClient, nil
|
||||
}
|
||||
|
||||
config, err := c.loader.ClientConfig()
|
||||
if err != nil {
|
||||
return restclient.Config{}, nil, err
|
||||
}
|
||||
discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
|
||||
if err != nil {
|
||||
return restclient.Config{}, nil, err
|
||||
}
|
||||
if c.matchVersion {
|
||||
if err := discovery.MatchesServerVersion(discoveryClient); err != nil {
|
||||
return restclient.Config{}, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
c.defaultConfig = config
|
||||
c.discoveryClient = discoveryClient
|
||||
return *c.defaultConfig, c.discoveryClient, nil
|
||||
}
|
||||
|
||||
// ClientConfigForVersion returns the correct config for a server
|
||||
func (c *ClientCache) ClientConfigForVersion(requiredVersion *unversioned.GroupVersion) (*restclient.Config, error) {
|
||||
// TODO: have a better config copy method
|
||||
config, discoveryClient, err := c.getDefaultConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if requiredVersion == nil && config.GroupVersion != nil {
|
||||
// if someone has set the values via flags, our config will have the groupVersion set
|
||||
// that means it is required.
|
||||
requiredVersion = config.GroupVersion
|
||||
}
|
||||
|
||||
// required version may still be nil, since config.GroupVersion may have been nil. Do the check
|
||||
// before looking up from the cache
|
||||
if requiredVersion != nil {
|
||||
if config, ok := c.configs[*requiredVersion]; ok {
|
||||
return config, nil
|
||||
}
|
||||
}
|
||||
|
||||
negotiatedVersion, err := discovery.NegotiateVersion(discoveryClient, requiredVersion, registered.EnabledVersions())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.GroupVersion = negotiatedVersion
|
||||
|
||||
// TODO this isn't what we want. Each clientset should be setting defaults as it sees fit.
|
||||
oldclient.SetKubernetesDefaults(&config)
|
||||
|
||||
if requiredVersion != nil {
|
||||
c.configs[*requiredVersion] = &config
|
||||
}
|
||||
|
||||
// `version` does not necessarily equal `config.Version`. However, we know that we call this method again with
|
||||
// `config.Version`, we should get the config we've just built.
|
||||
configCopy := config
|
||||
c.configs[*config.GroupVersion] = &configCopy
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
// ClientSetForVersion initializes or reuses a clientset for the specified version, or returns an
|
||||
// error if that is not possible
|
||||
func (c *ClientCache) ClientSetForVersion(requiredVersion *unversioned.GroupVersion) (*internalclientset.Clientset, error) {
|
||||
if requiredVersion != nil {
|
||||
if clientset, ok := c.clientsets[*requiredVersion]; ok {
|
||||
return clientset, nil
|
||||
}
|
||||
}
|
||||
config, err := c.ClientConfigForVersion(requiredVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clientset, err := internalclientset.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.clientsets[*config.GroupVersion] = clientset
|
||||
|
||||
// `version` does not necessarily equal `config.Version`. However, we know that if we call this method again with
|
||||
// `version`, we should get a client based on the same config we just found. There's no guarantee that a client
|
||||
// is copiable, so create a new client and save it in the cache.
|
||||
if requiredVersion != nil {
|
||||
configCopy := *config
|
||||
clientset, err := internalclientset.NewForConfig(&configCopy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.clientsets[*requiredVersion] = clientset
|
||||
}
|
||||
|
||||
return clientset, nil
|
||||
}
|
||||
|
||||
func (c *ClientCache) FederationClientSetForVersion(version *unversioned.GroupVersion) (fed_clientset.Interface, error) {
|
||||
if version != nil {
|
||||
if clientSet, found := c.fedClientSets[*version]; found {
|
||||
return clientSet, nil
|
||||
}
|
||||
}
|
||||
config, err := c.ClientConfigForVersion(version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: support multi versions of client with clientset
|
||||
clientSet, err := fed_clientset.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.fedClientSets[*config.GroupVersion] = clientSet
|
||||
|
||||
if version != nil {
|
||||
configCopy := *config
|
||||
clientSet, err := fed_clientset.NewForConfig(&configCopy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.fedClientSets[*version] = clientSet
|
||||
}
|
||||
|
||||
return clientSet, nil
|
||||
}
|
||||
|
||||
func (c *ClientCache) FederationClientForVersion(version *unversioned.GroupVersion) (*restclient.RESTClient, error) {
|
||||
fedClientSet, err := c.FederationClientSetForVersion(version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fedClientSet.Federation().RESTClient().(*restclient.RESTClient), nil
|
||||
}
|
||||
1334
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/factory.go
generated
vendored
1334
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/factory.go
generated
vendored
File diff suppressed because it is too large
Load diff
727
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/helpers.go
generated
vendored
727
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/helpers.go
generated
vendored
|
|
@ -1,727 +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 util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
kerrors "k8s.io/kubernetes/pkg/api/errors"
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/kubectl/resource"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
utilerrors "k8s.io/kubernetes/pkg/util/errors"
|
||||
utilexec "k8s.io/kubernetes/pkg/util/exec"
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
"k8s.io/kubernetes/pkg/util/strategicpatch"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const (
|
||||
ApplyAnnotationsFlag = "save-config"
|
||||
DefaultErrorExitCode = 1
|
||||
)
|
||||
|
||||
type debugError interface {
|
||||
DebugError() (msg string, args []interface{})
|
||||
}
|
||||
|
||||
// AddSourceToErr adds handleResourcePrefix and source string to error message.
|
||||
// verb is the string like "creating", "deleting" etc.
|
||||
// source is the filename or URL to the template file(*.json or *.yaml), or stdin to use to handle the resource.
|
||||
func AddSourceToErr(verb string, source string, err error) error {
|
||||
if source != "" {
|
||||
if statusError, ok := err.(kerrors.APIStatus); ok {
|
||||
status := statusError.Status()
|
||||
status.Message = fmt.Sprintf("error when %s %q: %v", verb, source, status.Message)
|
||||
return &kerrors.StatusError{ErrStatus: status}
|
||||
}
|
||||
return fmt.Errorf("error when %s %q: %v", verb, source, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
var fatalErrHandler = fatal
|
||||
|
||||
// BehaviorOnFatal allows you to override the default behavior when a fatal
|
||||
// error occurs, which is to call os.Exit(code). You can pass 'panic' as a function
|
||||
// here if you prefer the panic() over os.Exit(1).
|
||||
func BehaviorOnFatal(f func(string, int)) {
|
||||
fatalErrHandler = f
|
||||
}
|
||||
|
||||
// DefaultBehaviorOnFatal allows you to undo any previous override. Useful in
|
||||
// tests.
|
||||
func DefaultBehaviorOnFatal() {
|
||||
fatalErrHandler = fatal
|
||||
}
|
||||
|
||||
// fatal prints the message (if provided) and then exits. If V(2) or greater,
|
||||
// glog.Fatal is invoked for extended information.
|
||||
func fatal(msg string, code int) {
|
||||
if glog.V(2) {
|
||||
glog.FatalDepth(2, msg)
|
||||
}
|
||||
if len(msg) > 0 {
|
||||
// add newline if needed
|
||||
if !strings.HasSuffix(msg, "\n") {
|
||||
msg += "\n"
|
||||
}
|
||||
fmt.Fprint(os.Stderr, msg)
|
||||
}
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
// ErrExit may be passed to CheckError to instruct it to output nothing but exit with
|
||||
// status code 1.
|
||||
var ErrExit = fmt.Errorf("exit")
|
||||
|
||||
// CheckErr prints a user friendly error to STDERR and exits with a non-zero
|
||||
// exit code. Unrecognized errors will be printed with an "error: " prefix.
|
||||
//
|
||||
// This method is generic to the command in use and may be used by non-Kubectl
|
||||
// commands.
|
||||
func CheckErr(err error) {
|
||||
checkErr("", err, fatalErrHandler)
|
||||
}
|
||||
|
||||
// checkErrWithPrefix works like CheckErr, but adds a caller-defined prefix to non-nil errors
|
||||
func checkErrWithPrefix(prefix string, err error) {
|
||||
checkErr(prefix, err, fatalErrHandler)
|
||||
}
|
||||
|
||||
// checkErr formats a given error as a string and calls the passed handleErr
|
||||
// func with that string and an kubectl exit code.
|
||||
func checkErr(prefix string, err error, handleErr func(string, int)) {
|
||||
// unwrap aggregates of 1
|
||||
if agg, ok := err.(utilerrors.Aggregate); ok && len(agg.Errors()) == 1 {
|
||||
err = agg.Errors()[0]
|
||||
}
|
||||
|
||||
switch {
|
||||
case err == nil:
|
||||
return
|
||||
case err == ErrExit:
|
||||
handleErr("", DefaultErrorExitCode)
|
||||
return
|
||||
case kerrors.IsInvalid(err):
|
||||
details := err.(*kerrors.StatusError).Status().Details
|
||||
s := fmt.Sprintf("%sThe %s %q is invalid", prefix, details.Kind, details.Name)
|
||||
if len(details.Causes) > 0 {
|
||||
errs := statusCausesToAggrError(details.Causes)
|
||||
handleErr(MultilineError(s+": ", errs), DefaultErrorExitCode)
|
||||
} else {
|
||||
handleErr(s, DefaultErrorExitCode)
|
||||
}
|
||||
case clientcmd.IsConfigurationInvalid(err):
|
||||
handleErr(MultilineError(fmt.Sprintf("%sError in configuration: ", prefix), err), DefaultErrorExitCode)
|
||||
default:
|
||||
switch err := err.(type) {
|
||||
case *meta.NoResourceMatchError:
|
||||
switch {
|
||||
case len(err.PartialResource.Group) > 0 && len(err.PartialResource.Version) > 0:
|
||||
handleErr(fmt.Sprintf("%sthe server doesn't have a resource type %q in group %q and version %q", prefix, err.PartialResource.Resource, err.PartialResource.Group, err.PartialResource.Version), DefaultErrorExitCode)
|
||||
case len(err.PartialResource.Group) > 0:
|
||||
handleErr(fmt.Sprintf("%sthe server doesn't have a resource type %q in group %q", prefix, err.PartialResource.Resource, err.PartialResource.Group), DefaultErrorExitCode)
|
||||
case len(err.PartialResource.Version) > 0:
|
||||
handleErr(fmt.Sprintf("%sthe server doesn't have a resource type %q in version %q", prefix, err.PartialResource.Resource, err.PartialResource.Version), DefaultErrorExitCode)
|
||||
default:
|
||||
handleErr(fmt.Sprintf("%sthe server doesn't have a resource type %q", prefix, err.PartialResource.Resource), DefaultErrorExitCode)
|
||||
}
|
||||
case utilerrors.Aggregate:
|
||||
handleErr(MultipleErrors(prefix, err.Errors()), DefaultErrorExitCode)
|
||||
case utilexec.ExitError:
|
||||
// do not print anything, only terminate with given error
|
||||
handleErr("", err.ExitStatus())
|
||||
default: // for any other error type
|
||||
msg, ok := StandardErrorMessage(err)
|
||||
if !ok {
|
||||
msg = err.Error()
|
||||
if !strings.HasPrefix(msg, "error: ") {
|
||||
msg = fmt.Sprintf("error: %s", msg)
|
||||
}
|
||||
}
|
||||
handleErr(msg, DefaultErrorExitCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func statusCausesToAggrError(scs []unversioned.StatusCause) utilerrors.Aggregate {
|
||||
errs := make([]error, 0, len(scs))
|
||||
errorMsgs := sets.NewString()
|
||||
for _, sc := range scs {
|
||||
// check for duplicate error messages and skip them
|
||||
msg := fmt.Sprintf("%s: %s", sc.Field, sc.Message)
|
||||
if errorMsgs.Has(msg) {
|
||||
continue
|
||||
}
|
||||
errorMsgs.Insert(msg)
|
||||
errs = append(errs, errors.New(msg))
|
||||
}
|
||||
return utilerrors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
// StandardErrorMessage translates common errors into a human readable message, or returns
|
||||
// false if the error is not one of the recognized types. It may also log extended
|
||||
// information to glog.
|
||||
//
|
||||
// This method is generic to the command in use and may be used by non-Kubectl
|
||||
// commands.
|
||||
func StandardErrorMessage(err error) (string, bool) {
|
||||
if debugErr, ok := err.(debugError); ok {
|
||||
glog.V(4).Infof(debugErr.DebugError())
|
||||
}
|
||||
status, isStatus := err.(kerrors.APIStatus)
|
||||
switch {
|
||||
case isStatus:
|
||||
switch s := status.Status(); {
|
||||
case s.Reason == unversioned.StatusReasonUnauthorized:
|
||||
return fmt.Sprintf("error: You must be logged in to the server (%s)", s.Message), true
|
||||
case len(s.Reason) > 0:
|
||||
return fmt.Sprintf("Error from server (%s): %s", s.Reason, err.Error()), true
|
||||
default:
|
||||
return fmt.Sprintf("Error from server: %s", err.Error()), true
|
||||
}
|
||||
case kerrors.IsUnexpectedObjectError(err):
|
||||
return fmt.Sprintf("Server returned an unexpected response: %s", err.Error()), true
|
||||
}
|
||||
switch t := err.(type) {
|
||||
case *url.Error:
|
||||
glog.V(4).Infof("Connection error: %s %s: %v", t.Op, t.URL, t.Err)
|
||||
switch {
|
||||
case strings.Contains(t.Err.Error(), "connection refused"):
|
||||
host := t.URL
|
||||
if server, err := url.Parse(t.URL); err == nil {
|
||||
host = server.Host
|
||||
}
|
||||
return fmt.Sprintf("The connection to the server %s was refused - did you specify the right host or port?", host), true
|
||||
}
|
||||
return fmt.Sprintf("Unable to connect to the server: %v", t.Err), true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// MultilineError returns a string representing an error that splits sub errors into their own
|
||||
// lines. The returned string will end with a newline.
|
||||
func MultilineError(prefix string, err error) string {
|
||||
if agg, ok := err.(utilerrors.Aggregate); ok {
|
||||
errs := utilerrors.Flatten(agg).Errors()
|
||||
buf := &bytes.Buffer{}
|
||||
switch len(errs) {
|
||||
case 0:
|
||||
return fmt.Sprintf("%s%v\n", prefix, err)
|
||||
case 1:
|
||||
return fmt.Sprintf("%s%v\n", prefix, messageForError(errs[0]))
|
||||
default:
|
||||
fmt.Fprintln(buf, prefix)
|
||||
for _, err := range errs {
|
||||
fmt.Fprintf(buf, "* %v\n", messageForError(err))
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("%s%s\n", prefix, err)
|
||||
}
|
||||
|
||||
// PrintErrorWithCauses prints an error's kind, name, and each of the error's causes in a new line.
|
||||
// The returned string will end with a newline.
|
||||
// Returns true if a case exists to handle the error type, or false otherwise.
|
||||
func PrintErrorWithCauses(err error, errOut io.Writer) bool {
|
||||
switch t := err.(type) {
|
||||
case *kerrors.StatusError:
|
||||
errorDetails := t.Status().Details
|
||||
if errorDetails != nil {
|
||||
fmt.Fprintf(errOut, "error: %s %q is invalid\n\n", errorDetails.Kind, errorDetails.Name)
|
||||
for _, cause := range errorDetails.Causes {
|
||||
fmt.Fprintf(errOut, "* %s: %s\n", cause.Field, cause.Message)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(errOut, "error: %v\n", err)
|
||||
return false
|
||||
}
|
||||
|
||||
// MultipleErrors returns a newline delimited string containing
|
||||
// the prefix and referenced errors in standard form.
|
||||
func MultipleErrors(prefix string, errs []error) string {
|
||||
buf := &bytes.Buffer{}
|
||||
for _, err := range errs {
|
||||
fmt.Fprintf(buf, "%s%v\n", prefix, messageForError(err))
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// messageForError returns the string representing the error.
|
||||
func messageForError(err error) string {
|
||||
msg, ok := StandardErrorMessage(err)
|
||||
if !ok {
|
||||
msg = err.Error()
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
func UsageError(cmd *cobra.Command, format string, args ...interface{}) error {
|
||||
msg := fmt.Sprintf(format, args...)
|
||||
return fmt.Errorf("%s\nSee '%s -h' for help and examples.", msg, cmd.CommandPath())
|
||||
}
|
||||
|
||||
func IsFilenameEmpty(filenames []string) bool {
|
||||
if len(filenames) == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Whether this cmd need watching objects.
|
||||
func isWatch(cmd *cobra.Command) bool {
|
||||
if w, err := cmd.Flags().GetBool("watch"); w && err == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if wo, err := cmd.Flags().GetBool("watch-only"); wo && err == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func GetFlagString(cmd *cobra.Command, flag string) string {
|
||||
s, err := cmd.Flags().GetString(flag)
|
||||
if err != nil {
|
||||
glog.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// GetFlagStringSlice can be used to accept multiple argument with flag repetition (e.g. -f arg1,arg2 -f arg3 ...)
|
||||
func GetFlagStringSlice(cmd *cobra.Command, flag string) []string {
|
||||
s, err := cmd.Flags().GetStringSlice(flag)
|
||||
if err != nil {
|
||||
glog.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// GetFlagStringArray can be used to accept multiple argument with flag repetition (e.g. -f arg1 -f arg2 ...)
|
||||
func GetFlagStringArray(cmd *cobra.Command, flag string) []string {
|
||||
s, err := cmd.Flags().GetStringArray(flag)
|
||||
if err != nil {
|
||||
glog.Fatalf("err accessing flag %s for command %s: %v", flag, cmd.Name(), err)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// GetWideFlag is used to determine if "-o wide" is used
|
||||
func GetWideFlag(cmd *cobra.Command) bool {
|
||||
f := cmd.Flags().Lookup("output")
|
||||
if f.Value.String() == "wide" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func GetFlagBool(cmd *cobra.Command, flag string) bool {
|
||||
b, err := cmd.Flags().GetBool(flag)
|
||||
if err != nil {
|
||||
glog.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// Assumes the flag has a default value.
|
||||
func GetFlagInt(cmd *cobra.Command, flag string) int {
|
||||
i, err := cmd.Flags().GetInt(flag)
|
||||
if err != nil {
|
||||
glog.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// Assumes the flag has a default value.
|
||||
func GetFlagInt64(cmd *cobra.Command, flag string) int64 {
|
||||
i, err := cmd.Flags().GetInt64(flag)
|
||||
if err != nil {
|
||||
glog.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func GetFlagDuration(cmd *cobra.Command, flag string) time.Duration {
|
||||
d, err := cmd.Flags().GetDuration(flag)
|
||||
if err != nil {
|
||||
glog.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func AddValidateFlags(cmd *cobra.Command) {
|
||||
cmd.Flags().Bool("validate", true, "If true, use a schema to validate the input before sending it")
|
||||
cmd.Flags().String("schema-cache-dir", fmt.Sprintf("~/%s/%s", clientcmd.RecommendedHomeDir, clientcmd.RecommendedSchemaName), fmt.Sprintf("If non-empty, load/store cached API schemas in this directory, default is '$HOME/%s/%s'", clientcmd.RecommendedHomeDir, clientcmd.RecommendedSchemaName))
|
||||
cmd.MarkFlagFilename("schema-cache-dir")
|
||||
}
|
||||
|
||||
func AddFilenameOptionFlags(cmd *cobra.Command, options *resource.FilenameOptions, usage string) {
|
||||
kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, "Filename, directory, or URL to files "+usage)
|
||||
cmd.Flags().BoolVarP(&options.Recursive, "recursive", "R", options.Recursive, "Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.")
|
||||
}
|
||||
|
||||
// AddDryRunFlag adds dry-run flag to a command. Usually used by mutations.
|
||||
func AddDryRunFlag(cmd *cobra.Command) {
|
||||
cmd.Flags().Bool("dry-run", false, "If true, only print the object that would be sent, without sending it.")
|
||||
}
|
||||
|
||||
func AddApplyAnnotationFlags(cmd *cobra.Command) {
|
||||
cmd.Flags().Bool(ApplyAnnotationsFlag, false, "If true, the configuration of current object will be saved in its annotation. This is useful when you want to perform kubectl apply on this object in the future.")
|
||||
}
|
||||
|
||||
// AddGeneratorFlags adds flags common to resource generation commands
|
||||
// TODO: need to take a pass at other generator commands to use this set of flags
|
||||
func AddGeneratorFlags(cmd *cobra.Command, defaultGenerator string) {
|
||||
cmd.Flags().String("generator", defaultGenerator, "The name of the API generator to use.")
|
||||
AddDryRunFlag(cmd)
|
||||
}
|
||||
|
||||
func ReadConfigDataFromReader(reader io.Reader, source string) ([]byte, error) {
|
||||
data, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(data) == 0 {
|
||||
return nil, fmt.Errorf("Read from %s but no data found", source)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Merge requires JSON serialization
|
||||
// TODO: merge assumes JSON serialization, and does not properly abstract API retrieval
|
||||
func Merge(codec runtime.Codec, dst runtime.Object, fragment, kind string) (runtime.Object, error) {
|
||||
// encode dst into versioned json and apply fragment directly too it
|
||||
target, err := runtime.Encode(codec, dst)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
patched, err := jsonpatch.MergePatch(target, []byte(fragment))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := runtime.Decode(codec, patched)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// DumpReaderToFile writes all data from the given io.Reader to the specified file
|
||||
// (usually for temporary use).
|
||||
func DumpReaderToFile(reader io.Reader, filename string) error {
|
||||
f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
defer f.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buffer := make([]byte, 1024)
|
||||
for {
|
||||
count, err := reader.Read(buffer)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = f.Write(buffer[:count])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateObject updates resource object with updateFn
|
||||
func UpdateObject(info *resource.Info, codec runtime.Codec, updateFn func(runtime.Object) error) (runtime.Object, error) {
|
||||
helper := resource.NewHelper(info.Client, info.Mapping)
|
||||
|
||||
if err := updateFn(info.Object); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Update the annotation used by kubectl apply
|
||||
if err := kubectl.UpdateApplyAnnotation(info, codec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := helper.Replace(info.Namespace, info.Name, true, info.Object); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return info.Object, nil
|
||||
}
|
||||
|
||||
// AddCmdRecordFlag adds --record flag to command
|
||||
func AddRecordFlag(cmd *cobra.Command) {
|
||||
cmd.Flags().Bool("record", false, "Record current kubectl command in the resource annotation. If set to false, do not record the command. If set to true, record the command. If not set, default to updating the existing annotation value only if one already exists.")
|
||||
}
|
||||
|
||||
func GetRecordFlag(cmd *cobra.Command) bool {
|
||||
return GetFlagBool(cmd, "record")
|
||||
}
|
||||
|
||||
func GetDryRunFlag(cmd *cobra.Command) bool {
|
||||
return GetFlagBool(cmd, "dry-run")
|
||||
}
|
||||
|
||||
// RecordChangeCause annotate change-cause to input runtime object.
|
||||
func RecordChangeCause(obj runtime.Object, changeCause string) error {
|
||||
accessor, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
annotations := accessor.GetAnnotations()
|
||||
if annotations == nil {
|
||||
annotations = make(map[string]string)
|
||||
}
|
||||
annotations[kubectl.ChangeCauseAnnotation] = changeCause
|
||||
accessor.SetAnnotations(annotations)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ChangeResourcePatch creates a strategic merge patch between the origin input resource info
|
||||
// and the annotated with change-cause input resource info.
|
||||
func ChangeResourcePatch(info *resource.Info, changeCause string) ([]byte, error) {
|
||||
oldData, err := json.Marshal(info.Object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := RecordChangeCause(info.Object, changeCause); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newData, err := json.Marshal(info.Object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return strategicpatch.CreateTwoWayMergePatch(oldData, newData, info.Object)
|
||||
}
|
||||
|
||||
// containsChangeCause checks if input resource info contains change-cause annotation.
|
||||
func ContainsChangeCause(info *resource.Info) bool {
|
||||
annotations, err := info.Mapping.MetadataAccessor.Annotations(info.Object)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return len(annotations[kubectl.ChangeCauseAnnotation]) > 0
|
||||
}
|
||||
|
||||
// ShouldRecord checks if we should record current change cause
|
||||
func ShouldRecord(cmd *cobra.Command, info *resource.Info) bool {
|
||||
return GetRecordFlag(cmd) || (ContainsChangeCause(info) && !cmd.Flags().Changed("record"))
|
||||
}
|
||||
|
||||
func AddInclude3rdPartyFlags(cmd *cobra.Command) {
|
||||
cmd.Flags().Bool("include-extended-apis", true, "If true, include definitions of new APIs via calls to the API server. [default true]")
|
||||
cmd.Flags().MarkDeprecated("include-extended-apis", "No longer required.")
|
||||
}
|
||||
|
||||
// GetResourcesAndPairs retrieves resources and "KEY=VALUE or KEY-" pair args from given args
|
||||
func GetResourcesAndPairs(args []string, pairType string) (resources []string, pairArgs []string, err error) {
|
||||
foundPair := false
|
||||
for _, s := range args {
|
||||
nonResource := strings.Contains(s, "=") || strings.HasSuffix(s, "-")
|
||||
switch {
|
||||
case !foundPair && nonResource:
|
||||
foundPair = true
|
||||
fallthrough
|
||||
case foundPair && nonResource:
|
||||
pairArgs = append(pairArgs, s)
|
||||
case !foundPair && !nonResource:
|
||||
resources = append(resources, s)
|
||||
case foundPair && !nonResource:
|
||||
err = fmt.Errorf("all resources must be specified before %s changes: %s", pairType, s)
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ParsePairs retrieves new and remove pairs (if supportRemove is true) from "KEY=VALUE or KEY-" pair args
|
||||
func ParsePairs(pairArgs []string, pairType string, supportRemove bool) (newPairs map[string]string, removePairs []string, err error) {
|
||||
newPairs = map[string]string{}
|
||||
if supportRemove {
|
||||
removePairs = []string{}
|
||||
}
|
||||
var invalidBuf bytes.Buffer
|
||||
|
||||
for _, pairArg := range pairArgs {
|
||||
if strings.Index(pairArg, "=") != -1 {
|
||||
parts := strings.SplitN(pairArg, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
if invalidBuf.Len() > 0 {
|
||||
invalidBuf.WriteString(", ")
|
||||
}
|
||||
invalidBuf.WriteString(fmt.Sprintf(pairArg))
|
||||
} else {
|
||||
newPairs[parts[0]] = parts[1]
|
||||
}
|
||||
} else if supportRemove && strings.HasSuffix(pairArg, "-") {
|
||||
removePairs = append(removePairs, pairArg[:len(pairArg)-1])
|
||||
} else {
|
||||
if invalidBuf.Len() > 0 {
|
||||
invalidBuf.WriteString(", ")
|
||||
}
|
||||
invalidBuf.WriteString(fmt.Sprintf(pairArg))
|
||||
}
|
||||
}
|
||||
if invalidBuf.Len() > 0 {
|
||||
err = fmt.Errorf("invalid %s format: %s", pairType, invalidBuf.String())
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// MaybeConvertObject attempts to convert an object to a specific group/version. If the object is
|
||||
// a third party resource it is simply passed through.
|
||||
func MaybeConvertObject(obj runtime.Object, gv unversioned.GroupVersion, converter runtime.ObjectConvertor) (runtime.Object, error) {
|
||||
switch obj.(type) {
|
||||
case *extensions.ThirdPartyResourceData:
|
||||
// conversion is not supported for 3rd party objects
|
||||
return obj, nil
|
||||
default:
|
||||
return converter.ConvertToVersion(obj, gv)
|
||||
}
|
||||
}
|
||||
|
||||
// MustPrintWithKinds determines if printer is dealing
|
||||
// with multiple resource kinds, in which case it will
|
||||
// return true, indicating resource kind will be
|
||||
// included as part of printer output
|
||||
func MustPrintWithKinds(objs []runtime.Object, infos []*resource.Info, sorter *kubectl.RuntimeSort, printAll bool) bool {
|
||||
var lastMap *meta.RESTMapping
|
||||
|
||||
if len(infos) == 1 && printAll {
|
||||
return true
|
||||
}
|
||||
|
||||
for ix := range objs {
|
||||
var mapping *meta.RESTMapping
|
||||
if sorter != nil {
|
||||
mapping = infos[sorter.OriginalPosition(ix)].Mapping
|
||||
} else {
|
||||
mapping = infos[ix].Mapping
|
||||
}
|
||||
|
||||
// display "kind" only if we have mixed resources
|
||||
if lastMap != nil && mapping.Resource != lastMap.Resource {
|
||||
return true
|
||||
}
|
||||
lastMap = mapping
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// FilterResourceList receives a list of runtime objects.
|
||||
// If any objects are filtered, that number is returned along with a modified list.
|
||||
func FilterResourceList(obj runtime.Object, filterFuncs kubectl.Filters, filterOpts *kubectl.PrintOptions) (int, []runtime.Object, error) {
|
||||
items, err := meta.ExtractList(obj)
|
||||
if err != nil {
|
||||
return 0, []runtime.Object{obj}, utilerrors.NewAggregate([]error{err})
|
||||
}
|
||||
if errs := runtime.DecodeList(items, api.Codecs.UniversalDecoder(), runtime.UnstructuredJSONScheme); len(errs) > 0 {
|
||||
return 0, []runtime.Object{obj}, utilerrors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
filterCount := 0
|
||||
list := make([]runtime.Object, 0, len(items))
|
||||
for _, obj := range items {
|
||||
if isFiltered, err := filterFuncs.Filter(obj, filterOpts); !isFiltered {
|
||||
if err != nil {
|
||||
glog.V(2).Infof("Unable to filter resource: %v", err)
|
||||
continue
|
||||
}
|
||||
list = append(list, obj)
|
||||
} else if isFiltered {
|
||||
filterCount++
|
||||
}
|
||||
}
|
||||
return filterCount, list, nil
|
||||
}
|
||||
|
||||
func PrintFilterCount(hiddenObjNum int, resource string, options *kubectl.PrintOptions) {
|
||||
if !options.NoHeaders && !options.ShowAll && hiddenObjNum > 0 {
|
||||
glog.V(2).Infof(" info: %d completed object(s) was(were) not shown in %s list. Pass --show-all to see all objects.\n\n", hiddenObjNum, resource)
|
||||
}
|
||||
}
|
||||
|
||||
// ObjectListToVersionedObject receives a list of api objects and a group version
|
||||
// and squashes the list's items into a single versioned runtime.Object.
|
||||
func ObjectListToVersionedObject(objects []runtime.Object, version unversioned.GroupVersion) (runtime.Object, error) {
|
||||
objectList := &api.List{Items: objects}
|
||||
converted, err := resource.TryConvert(api.Scheme, objectList, version, registered.GroupOrDie(api.GroupName).GroupVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return converted, nil
|
||||
}
|
||||
|
||||
// IsSiblingCommandExists receives a pointer to a cobra command and a target string.
|
||||
// Returns true if the target string is found in the list of sibling commands.
|
||||
func IsSiblingCommandExists(cmd *cobra.Command, targetCmdName string) bool {
|
||||
for _, c := range cmd.Parent().Commands() {
|
||||
if c.Name() == targetCmdName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// DefaultSubCommandRun prints a command's help string to the specified output if no
|
||||
// arguments (sub-commands) are provided, or a usage error otherwise.
|
||||
func DefaultSubCommandRun(out io.Writer) func(c *cobra.Command, args []string) {
|
||||
return func(c *cobra.Command, args []string) {
|
||||
c.SetOutput(out)
|
||||
RequireNoArguments(c, args)
|
||||
c.Help()
|
||||
}
|
||||
}
|
||||
|
||||
// RequireNoArguments exits with a usage error if extra arguments are provided.
|
||||
func RequireNoArguments(c *cobra.Command, args []string) {
|
||||
if len(args) > 0 {
|
||||
CheckErr(UsageError(c, fmt.Sprintf(`unknown command %q`, strings.Join(args, " "))))
|
||||
}
|
||||
}
|
||||
150
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/printing.go
generated
vendored
150
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/printing.go
generated
vendored
|
|
@ -1,150 +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 util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// AddPrinterFlags adds printing related flags to a command (e.g. output format, no headers, template path)
|
||||
func AddPrinterFlags(cmd *cobra.Command) {
|
||||
AddOutputFlags(cmd)
|
||||
cmd.Flags().String("output-version", "", "Output the formatted object with the given group version (for ex: 'extensions/v1beta1').")
|
||||
AddNoHeadersFlags(cmd)
|
||||
cmd.Flags().Bool("show-labels", false, "When printing, show all labels as the last column (default hide labels column)")
|
||||
cmd.Flags().String("template", "", "Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview].")
|
||||
cmd.MarkFlagFilename("template")
|
||||
cmd.Flags().String("sort-by", "", "If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. '{.metadata.name}'). The field in the API resource specified by this JSONPath expression must be an integer or a string.")
|
||||
cmd.Flags().BoolP("show-all", "a", false, "When printing, show all resources (default hide terminated pods.)")
|
||||
}
|
||||
|
||||
// AddOutputFlagsForMutation adds output related flags to a command. Used by mutations only.
|
||||
func AddOutputFlagsForMutation(cmd *cobra.Command) {
|
||||
cmd.Flags().StringP("output", "o", "", "Output mode. Use \"-o name\" for shorter output (resource/name).")
|
||||
}
|
||||
|
||||
// AddOutputFlags adds output related flags to a command.
|
||||
func AddOutputFlags(cmd *cobra.Command) {
|
||||
cmd.Flags().StringP("output", "o", "", "Output format. One of: json|yaml|wide|name|custom-columns=...|custom-columns-file=...|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=... See custom columns [http://kubernetes.io/docs/user-guide/kubectl-overview/#custom-columns], golang template [http://golang.org/pkg/text/template/#pkg-overview] and jsonpath template [http://kubernetes.io/docs/user-guide/jsonpath].")
|
||||
}
|
||||
|
||||
// AddNoHeadersFlags adds no-headers flags to a command.
|
||||
func AddNoHeadersFlags(cmd *cobra.Command) {
|
||||
cmd.Flags().Bool("no-headers", false, "When using the default or custom-column output format, don't print headers.")
|
||||
}
|
||||
|
||||
// PrintSuccess prints message after finishing mutating operations
|
||||
func PrintSuccess(mapper meta.RESTMapper, shortOutput bool, out io.Writer, resource string, name string, dryRun bool, operation string) {
|
||||
resource, _ = mapper.ResourceSingularizer(resource)
|
||||
dryRunMsg := ""
|
||||
if dryRun {
|
||||
dryRunMsg = " (dry run)"
|
||||
}
|
||||
if shortOutput {
|
||||
// -o name: prints resource/name
|
||||
if len(resource) > 0 {
|
||||
fmt.Fprintf(out, "%s/%s\n", resource, name)
|
||||
} else {
|
||||
fmt.Fprintf(out, "%s\n", name)
|
||||
}
|
||||
} else {
|
||||
// understandable output by default
|
||||
if len(resource) > 0 {
|
||||
fmt.Fprintf(out, "%s \"%s\" %s%s\n", resource, name, operation, dryRunMsg)
|
||||
} else {
|
||||
fmt.Fprintf(out, "\"%s\" %s%s\n", name, operation, dryRunMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateOutputArgs validates -o flag args for mutations
|
||||
func ValidateOutputArgs(cmd *cobra.Command) error {
|
||||
outputMode := GetFlagString(cmd, "output")
|
||||
if outputMode != "" && outputMode != "name" {
|
||||
return UsageError(cmd, "Unexpected -o output mode: %v. We only support '-o name'.", outputMode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OutputVersion returns the preferred output version for generic content (JSON, YAML, or templates)
|
||||
// defaultVersion is never mutated. Nil simply allows clean passing in common usage from client.Config
|
||||
func OutputVersion(cmd *cobra.Command, defaultVersion *unversioned.GroupVersion) (unversioned.GroupVersion, error) {
|
||||
outputVersionString := GetFlagString(cmd, "output-version")
|
||||
if len(outputVersionString) == 0 {
|
||||
if defaultVersion == nil {
|
||||
return unversioned.GroupVersion{}, nil
|
||||
}
|
||||
|
||||
return *defaultVersion, nil
|
||||
}
|
||||
|
||||
return unversioned.ParseGroupVersion(outputVersionString)
|
||||
}
|
||||
|
||||
// PrinterForCommand returns the default printer for this command.
|
||||
// Requires that printer flags have been added to cmd (see AddPrinterFlags).
|
||||
func PrinterForCommand(cmd *cobra.Command) (kubectl.ResourcePrinter, bool, error) {
|
||||
outputFormat := GetFlagString(cmd, "output")
|
||||
|
||||
// templates are logically optional for specifying a format.
|
||||
// TODO once https://github.com/kubernetes/kubernetes/issues/12668 is fixed, this should fall back to GetFlagString
|
||||
templateFile, _ := cmd.Flags().GetString("template")
|
||||
if len(outputFormat) == 0 && len(templateFile) != 0 {
|
||||
outputFormat = "template"
|
||||
}
|
||||
|
||||
templateFormat := []string{
|
||||
"go-template=", "go-template-file=", "jsonpath=", "jsonpath-file=", "custom-columns=", "custom-columns-file=",
|
||||
}
|
||||
for _, format := range templateFormat {
|
||||
if strings.HasPrefix(outputFormat, format) {
|
||||
templateFile = outputFormat[len(format):]
|
||||
outputFormat = format[:len(format)-1]
|
||||
}
|
||||
}
|
||||
|
||||
printer, generic, err := kubectl.GetPrinter(outputFormat, templateFile, GetFlagBool(cmd, "no-headers"))
|
||||
if err != nil {
|
||||
return nil, generic, err
|
||||
}
|
||||
|
||||
return maybeWrapSortingPrinter(cmd, printer), generic, nil
|
||||
}
|
||||
|
||||
func maybeWrapSortingPrinter(cmd *cobra.Command, printer kubectl.ResourcePrinter) kubectl.ResourcePrinter {
|
||||
sorting, err := cmd.Flags().GetString("sort-by")
|
||||
if err != nil {
|
||||
// error can happen on missing flag or bad flag type. In either case, this command didn't intent to sort
|
||||
return printer
|
||||
}
|
||||
|
||||
if len(sorting) != 0 {
|
||||
return &kubectl.SortingPrinter{
|
||||
Delegate: printer,
|
||||
SortField: fmt.Sprintf("{%s}", sorting),
|
||||
}
|
||||
}
|
||||
return printer
|
||||
}
|
||||
147
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/shortcut_restmapper.go
generated
vendored
147
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/shortcut_restmapper.go
generated
vendored
|
|
@ -1,147 +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 util
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/client/typed/discovery"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
)
|
||||
|
||||
// ShortcutExpander is a RESTMapper that can be used for Kubernetes resources. It expands the resource first, then invokes the wrapped
|
||||
type ShortcutExpander struct {
|
||||
RESTMapper meta.RESTMapper
|
||||
|
||||
All []unversioned.GroupResource
|
||||
|
||||
discoveryClient discovery.DiscoveryInterface
|
||||
}
|
||||
|
||||
var _ meta.RESTMapper = &ShortcutExpander{}
|
||||
|
||||
func NewShortcutExpander(delegate meta.RESTMapper, client discovery.DiscoveryInterface) ShortcutExpander {
|
||||
return ShortcutExpander{All: userResources, RESTMapper: delegate, discoveryClient: client}
|
||||
}
|
||||
|
||||
func (e ShortcutExpander) getAll() []unversioned.GroupResource {
|
||||
if e.discoveryClient == nil {
|
||||
return e.All
|
||||
}
|
||||
|
||||
// Check if we have access to server resources
|
||||
apiResources, err := e.discoveryClient.ServerResources()
|
||||
if err != nil {
|
||||
return e.All
|
||||
}
|
||||
|
||||
availableResources := []unversioned.GroupVersionResource{}
|
||||
for groupVersionString, resourceList := range apiResources {
|
||||
currVersion, err := unversioned.ParseGroupVersion(groupVersionString)
|
||||
if err != nil {
|
||||
return e.All
|
||||
}
|
||||
|
||||
for _, resource := range resourceList.APIResources {
|
||||
availableResources = append(availableResources, currVersion.WithResource(resource.Name))
|
||||
}
|
||||
}
|
||||
|
||||
availableAll := []unversioned.GroupResource{}
|
||||
for _, requestedResource := range e.All {
|
||||
for _, availableResource := range availableResources {
|
||||
if requestedResource.Group == availableResource.Group &&
|
||||
requestedResource.Resource == availableResource.Resource {
|
||||
availableAll = append(availableAll, requestedResource)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return availableAll
|
||||
}
|
||||
|
||||
func (e ShortcutExpander) KindFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionKind, error) {
|
||||
return e.RESTMapper.KindFor(expandResourceShortcut(resource))
|
||||
}
|
||||
|
||||
func (e ShortcutExpander) KindsFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionKind, error) {
|
||||
return e.RESTMapper.KindsFor(expandResourceShortcut(resource))
|
||||
}
|
||||
|
||||
func (e ShortcutExpander) ResourcesFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionResource, error) {
|
||||
return e.RESTMapper.ResourcesFor(expandResourceShortcut(resource))
|
||||
}
|
||||
|
||||
func (e ShortcutExpander) ResourceFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionResource, error) {
|
||||
return e.RESTMapper.ResourceFor(expandResourceShortcut(resource))
|
||||
}
|
||||
|
||||
func (e ShortcutExpander) ResourceSingularizer(resource string) (string, error) {
|
||||
return e.RESTMapper.ResourceSingularizer(expandResourceShortcut(unversioned.GroupVersionResource{Resource: resource}).Resource)
|
||||
}
|
||||
|
||||
func (e ShortcutExpander) RESTMapping(gk unversioned.GroupKind, versions ...string) (*meta.RESTMapping, error) {
|
||||
return e.RESTMapper.RESTMapping(gk, versions...)
|
||||
}
|
||||
|
||||
func (e ShortcutExpander) RESTMappings(gk unversioned.GroupKind) ([]*meta.RESTMapping, error) {
|
||||
return e.RESTMapper.RESTMappings(gk)
|
||||
}
|
||||
|
||||
// userResources are the resource names that apply to the primary, user facing resources used by
|
||||
// client tools. They are in deletion-first order - dependent resources should be last.
|
||||
var userResources = []unversioned.GroupResource{
|
||||
{Group: "", Resource: "pods"},
|
||||
{Group: "", Resource: "replicationcontrollers"},
|
||||
{Group: "", Resource: "services"},
|
||||
{Group: "apps", Resource: "statefulsets"},
|
||||
{Group: "autoscaling", Resource: "horizontalpodautoscalers"},
|
||||
{Group: "extensions", Resource: "jobs"},
|
||||
{Group: "extensions", Resource: "deployments"},
|
||||
{Group: "extensions", Resource: "replicasets"},
|
||||
}
|
||||
|
||||
// AliasesForResource returns whether a resource has an alias or not
|
||||
func (e ShortcutExpander) AliasesForResource(resource string) ([]string, bool) {
|
||||
if strings.ToLower(resource) == "all" {
|
||||
var resources []unversioned.GroupResource
|
||||
if resources = e.getAll(); len(resources) == 0 {
|
||||
resources = userResources
|
||||
}
|
||||
aliases := []string{}
|
||||
for _, r := range resources {
|
||||
aliases = append(aliases, r.Resource)
|
||||
}
|
||||
return aliases, true
|
||||
}
|
||||
expanded := expandResourceShortcut(unversioned.GroupVersionResource{Resource: resource}).Resource
|
||||
return []string{expanded}, (expanded != resource)
|
||||
}
|
||||
|
||||
// expandResourceShortcut will return the expanded version of resource
|
||||
// (something that a pkg/api/meta.RESTMapper can understand), if it is
|
||||
// indeed a shortcut. Otherwise, will return resource unmodified.
|
||||
func expandResourceShortcut(resource unversioned.GroupVersionResource) unversioned.GroupVersionResource {
|
||||
if expanded, ok := kubectl.ShortForms[resource.Resource]; ok {
|
||||
resource.Resource = expanded
|
||||
return resource
|
||||
}
|
||||
return resource
|
||||
}
|
||||
211
vendor/k8s.io/kubernetes/pkg/kubectl/configmap.go
generated
vendored
211
vendor/k8s.io/kubernetes/pkg/kubectl/configmap.go
generated
vendored
|
|
@ -1,211 +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"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/util/validation"
|
||||
)
|
||||
|
||||
// ConfigMapGeneratorV1 supports stable generation of a configMap.
|
||||
type ConfigMapGeneratorV1 struct {
|
||||
// Name of configMap (required)
|
||||
Name string
|
||||
// Type of configMap (optional)
|
||||
Type string
|
||||
// FileSources to derive the configMap from (optional)
|
||||
FileSources []string
|
||||
// LiteralSources to derive the configMap from (optional)
|
||||
LiteralSources []string
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameter injection.
|
||||
var _ Generator = &ConfigMapGeneratorV1{}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters specified during construction.
|
||||
var _ StructuredGenerator = &ConfigMapGeneratorV1{}
|
||||
|
||||
// Generate returns a configMap using the specified parameters.
|
||||
func (s ConfigMapGeneratorV1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
err := ValidateParams(s.ParamNames(), genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
delegate := &ConfigMapGeneratorV1{}
|
||||
fromFileStrings, found := genericParams["from-file"]
|
||||
if found {
|
||||
fromFileArray, isArray := fromFileStrings.([]string)
|
||||
if !isArray {
|
||||
return nil, fmt.Errorf("expected []string, found :%v", fromFileStrings)
|
||||
}
|
||||
delegate.FileSources = fromFileArray
|
||||
delete(genericParams, "from-file")
|
||||
}
|
||||
fromLiteralStrings, found := genericParams["from-literal"]
|
||||
if found {
|
||||
fromLiteralArray, isArray := fromLiteralStrings.([]string)
|
||||
if !isArray {
|
||||
return nil, fmt.Errorf("expected []string, found :%v", fromFileStrings)
|
||||
}
|
||||
delegate.LiteralSources = fromLiteralArray
|
||||
delete(genericParams, "from-literal")
|
||||
}
|
||||
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.Type = params["type"]
|
||||
return delegate.StructuredGenerate()
|
||||
}
|
||||
|
||||
// ParamNames returns the set of supported input parameters when using the parameter injection generator pattern.
|
||||
func (s ConfigMapGeneratorV1) ParamNames() []GeneratorParam {
|
||||
return []GeneratorParam{
|
||||
{"name", true},
|
||||
{"type", false},
|
||||
{"from-file", false},
|
||||
{"from-literal", false},
|
||||
{"force", false},
|
||||
}
|
||||
}
|
||||
|
||||
// StructuredGenerate outputs a configMap object using the configured fields.
|
||||
func (s ConfigMapGeneratorV1) StructuredGenerate() (runtime.Object, error) {
|
||||
if err := s.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
configMap := &api.ConfigMap{}
|
||||
configMap.Name = s.Name
|
||||
configMap.Data = map[string]string{}
|
||||
if len(s.FileSources) > 0 {
|
||||
if err := handleConfigMapFromFileSources(configMap, s.FileSources); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if len(s.LiteralSources) > 0 {
|
||||
if err := handleConfigMapFromLiteralSources(configMap, s.LiteralSources); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return configMap, nil
|
||||
}
|
||||
|
||||
// validate validates required fields are set to support structured generation.
|
||||
func (s ConfigMapGeneratorV1) validate() error {
|
||||
if len(s.Name) == 0 {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleConfigMapFromLiteralSources adds the specified literal source
|
||||
// information into the provided configMap.
|
||||
func handleConfigMapFromLiteralSources(configMap *api.ConfigMap, literalSources []string) error {
|
||||
for _, literalSource := range literalSources {
|
||||
keyName, value, err := parseLiteralSource(literalSource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = addKeyFromLiteralToConfigMap(configMap, keyName, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleConfigMapFromFileSources adds the specified file source information
|
||||
// into the provided configMap
|
||||
func handleConfigMapFromFileSources(configMap *api.ConfigMap, fileSources []string) error {
|
||||
for _, fileSource := range fileSources {
|
||||
keyName, filePath, err := parseFileSource(fileSource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
switch err := err.(type) {
|
||||
case *os.PathError:
|
||||
return fmt.Errorf("error reading %s: %v", filePath, err.Err)
|
||||
default:
|
||||
return fmt.Errorf("error reading %s: %v", filePath, err)
|
||||
}
|
||||
}
|
||||
if info.IsDir() {
|
||||
if strings.Contains(fileSource, "=") {
|
||||
return fmt.Errorf("cannot give a key name for a directory path.")
|
||||
}
|
||||
fileList, err := ioutil.ReadDir(filePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error listing files in %s: %v", filePath, err)
|
||||
}
|
||||
for _, item := range fileList {
|
||||
itemPath := path.Join(filePath, item.Name())
|
||||
if item.Mode().IsRegular() {
|
||||
keyName = item.Name()
|
||||
err = addKeyFromFileToConfigMap(configMap, keyName, itemPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = addKeyFromFileToConfigMap(configMap, keyName, filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// addKeyFromFileToConfigMap adds a key with the given name to a ConfigMap, populating
|
||||
// the value with the content of the given file path, or returns an error.
|
||||
func addKeyFromFileToConfigMap(configMap *api.ConfigMap, keyName, filePath string) error {
|
||||
data, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return addKeyFromLiteralToConfigMap(configMap, keyName, string(data))
|
||||
}
|
||||
|
||||
// addKeyFromLiteralToConfigMap adds the given key and data to the given config map,
|
||||
// returning an error if the key is not valid or if the key already exists.
|
||||
func addKeyFromLiteralToConfigMap(configMap *api.ConfigMap, keyName, data string) error {
|
||||
// Note, the rules for ConfigMap keys are the exact same as the ones for SecretKeys.
|
||||
if errs := validation.IsConfigMapKey(keyName); len(errs) != 0 {
|
||||
return fmt.Errorf("%q is not a valid key name for a ConfigMap: %s", keyName, strings.Join(errs, ";"))
|
||||
}
|
||||
if _, entryExists := configMap.Data[keyName]; entryExists {
|
||||
return fmt.Errorf("cannot add key %s, another key by that name already exists: %v.", keyName, configMap.Data)
|
||||
}
|
||||
configMap.Data[keyName] = data
|
||||
return nil
|
||||
}
|
||||
241
vendor/k8s.io/kubernetes/pkg/kubectl/custom_column_printer.go
generated
vendored
241
vendor/k8s.io/kubernetes/pkg/kubectl/custom_column_printer.go
generated
vendored
|
|
@ -1,241 +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 (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/util/jsonpath"
|
||||
)
|
||||
|
||||
const (
|
||||
columnwidth = 10
|
||||
tabwidth = 4
|
||||
padding = 3
|
||||
padding_character = ' '
|
||||
flags = 0
|
||||
)
|
||||
|
||||
var jsonRegexp = regexp.MustCompile("^\\{\\.?([^{}]+)\\}$|^\\.?([^{}]+)$")
|
||||
|
||||
// MassageJSONPath attempts to be flexible with JSONPath expressions, it accepts:
|
||||
// * metadata.name (no leading '.' or curly brances '{...}'
|
||||
// * {metadata.name} (no leading '.')
|
||||
// * .metadata.name (no curly braces '{...}')
|
||||
// * {.metadata.name} (complete expression)
|
||||
// And transforms them all into a valid jsonpat expression:
|
||||
// {.metadata.name}
|
||||
func massageJSONPath(pathExpression string) (string, error) {
|
||||
if len(pathExpression) == 0 {
|
||||
return pathExpression, nil
|
||||
}
|
||||
submatches := jsonRegexp.FindStringSubmatch(pathExpression)
|
||||
if submatches == nil {
|
||||
return "", fmt.Errorf("unexpected path string, expected a 'name1.name2' or '.name1.name2' or '{name1.name2}' or '{.name1.name2}'")
|
||||
}
|
||||
if len(submatches) != 3 {
|
||||
return "", fmt.Errorf("unexpected submatch list: %v", submatches)
|
||||
}
|
||||
var fieldSpec string
|
||||
if len(submatches[1]) != 0 {
|
||||
fieldSpec = submatches[1]
|
||||
} else {
|
||||
fieldSpec = submatches[2]
|
||||
}
|
||||
return fmt.Sprintf("{.%s}", fieldSpec), nil
|
||||
}
|
||||
|
||||
// NewCustomColumnsPrinterFromSpec creates a custom columns printer from a comma separated list of <header>:<jsonpath-field-spec> pairs.
|
||||
// e.g. NAME:metadata.name,API_VERSION:apiVersion creates a printer that prints:
|
||||
//
|
||||
// NAME API_VERSION
|
||||
// foo bar
|
||||
func NewCustomColumnsPrinterFromSpec(spec string, decoder runtime.Decoder, noHeaders bool) (*CustomColumnsPrinter, error) {
|
||||
if len(spec) == 0 {
|
||||
return nil, fmt.Errorf("custom-columns format specified but no custom columns given")
|
||||
}
|
||||
parts := strings.Split(spec, ",")
|
||||
columns := make([]Column, len(parts))
|
||||
for ix := range parts {
|
||||
colSpec := strings.Split(parts[ix], ":")
|
||||
if len(colSpec) != 2 {
|
||||
return nil, fmt.Errorf("unexpected custom-columns spec: %s, expected <header>:<json-path-expr>", parts[ix])
|
||||
}
|
||||
spec, err := massageJSONPath(colSpec[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
columns[ix] = Column{Header: colSpec[0], FieldSpec: spec}
|
||||
}
|
||||
return &CustomColumnsPrinter{Columns: columns, Decoder: decoder, NoHeaders: noHeaders}, nil
|
||||
}
|
||||
|
||||
func splitOnWhitespace(line string) []string {
|
||||
lineScanner := bufio.NewScanner(bytes.NewBufferString(line))
|
||||
lineScanner.Split(bufio.ScanWords)
|
||||
result := []string{}
|
||||
for lineScanner.Scan() {
|
||||
result = append(result, lineScanner.Text())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// NewCustomColumnsPrinterFromTemplate creates a custom columns printer from a template stream. The template is expected
|
||||
// to consist of two lines, whitespace separated. The first line is the header line, the second line is the jsonpath field spec
|
||||
// For example, the template below:
|
||||
// NAME API_VERSION
|
||||
// {metadata.name} {apiVersion}
|
||||
func NewCustomColumnsPrinterFromTemplate(templateReader io.Reader, decoder runtime.Decoder) (*CustomColumnsPrinter, error) {
|
||||
scanner := bufio.NewScanner(templateReader)
|
||||
if !scanner.Scan() {
|
||||
return nil, fmt.Errorf("invalid template, missing header line. Expected format is one line of space separated headers, one line of space separated column specs.")
|
||||
}
|
||||
headers := splitOnWhitespace(scanner.Text())
|
||||
|
||||
if !scanner.Scan() {
|
||||
return nil, fmt.Errorf("invalid template, missing spec line. Expected format is one line of space separated headers, one line of space separated column specs.")
|
||||
}
|
||||
specs := splitOnWhitespace(scanner.Text())
|
||||
|
||||
if len(headers) != len(specs) {
|
||||
return nil, fmt.Errorf("number of headers (%d) and field specifications (%d) don't match", len(headers), len(specs))
|
||||
}
|
||||
|
||||
columns := make([]Column, len(headers))
|
||||
for ix := range headers {
|
||||
spec, err := massageJSONPath(specs[ix])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
columns[ix] = Column{
|
||||
Header: headers[ix],
|
||||
FieldSpec: spec,
|
||||
}
|
||||
}
|
||||
return &CustomColumnsPrinter{Columns: columns, Decoder: decoder, NoHeaders: false}, nil
|
||||
}
|
||||
|
||||
// Column represents a user specified column
|
||||
type Column struct {
|
||||
// The header to print above the column, general style is ALL_CAPS
|
||||
Header string
|
||||
// The pointer to the field in the object to print in JSONPath form
|
||||
// e.g. {.ObjectMeta.Name}, see pkg/util/jsonpath for more details.
|
||||
FieldSpec string
|
||||
}
|
||||
|
||||
// CustomColumnPrinter is a printer that knows how to print arbitrary columns
|
||||
// of data from templates specified in the `Columns` array
|
||||
type CustomColumnsPrinter struct {
|
||||
Columns []Column
|
||||
Decoder runtime.Decoder
|
||||
NoHeaders bool
|
||||
}
|
||||
|
||||
func (s *CustomColumnsPrinter) AfterPrint(w io.Writer, res string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *CustomColumnsPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
|
||||
w := tabwriter.NewWriter(out, columnwidth, tabwidth, padding, padding_character, flags)
|
||||
|
||||
if !s.NoHeaders {
|
||||
headers := make([]string, len(s.Columns))
|
||||
for ix := range s.Columns {
|
||||
headers[ix] = s.Columns[ix].Header
|
||||
}
|
||||
fmt.Fprintln(w, strings.Join(headers, "\t"))
|
||||
}
|
||||
parsers := make([]*jsonpath.JSONPath, len(s.Columns))
|
||||
for ix := range s.Columns {
|
||||
parsers[ix] = jsonpath.New(fmt.Sprintf("column%d", ix))
|
||||
if err := parsers[ix].Parse(s.Columns[ix].FieldSpec); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if meta.IsListType(obj) {
|
||||
objs, err := meta.ExtractList(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for ix := range objs {
|
||||
if err := s.printOneObject(objs[ix], parsers, w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := s.printOneObject(obj, parsers, w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return w.Flush()
|
||||
}
|
||||
|
||||
func (s *CustomColumnsPrinter) printOneObject(obj runtime.Object, parsers []*jsonpath.JSONPath, out io.Writer) error {
|
||||
columns := make([]string, len(parsers))
|
||||
switch u := obj.(type) {
|
||||
case *runtime.Unknown:
|
||||
if len(u.Raw) > 0 {
|
||||
var err error
|
||||
if obj, err = runtime.Decode(s.Decoder, u.Raw); err != nil {
|
||||
return fmt.Errorf("can't decode object for printing: %v (%s)", err, u.Raw)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ix := range parsers {
|
||||
parser := parsers[ix]
|
||||
|
||||
var values [][]reflect.Value
|
||||
var err error
|
||||
if unstructured, ok := obj.(*runtime.Unstructured); ok {
|
||||
values, err = parser.FindResults(unstructured.Object)
|
||||
} else {
|
||||
values, err = parser.FindResults(reflect.ValueOf(obj).Elem().Interface())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(values) == 0 || len(values[0]) == 0 {
|
||||
fmt.Fprintf(out, "<none>\t")
|
||||
}
|
||||
valueStrings := []string{}
|
||||
for arrIx := range values {
|
||||
for valIx := range values[arrIx] {
|
||||
valueStrings = append(valueStrings, fmt.Sprintf("%v", values[arrIx][valIx].Interface()))
|
||||
}
|
||||
}
|
||||
columns[ix] = strings.Join(valueStrings, ",")
|
||||
}
|
||||
fmt.Fprintln(out, strings.Join(columns, "\t"))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *CustomColumnsPrinter) HandledResources() []string {
|
||||
return []string{}
|
||||
}
|
||||
107
vendor/k8s.io/kubernetes/pkg/kubectl/deployment.go
generated
vendored
107
vendor/k8s.io/kubernetes/pkg/kubectl/deployment.go
generated
vendored
|
|
@ -1,107 +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"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeploymentGeneratorV1 supports stable generation of a deployment
|
||||
type DeploymentBasicGeneratorV1 struct {
|
||||
Name string
|
||||
Images []string
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters specified during construction
|
||||
var _ StructuredGenerator = &DeploymentBasicGeneratorV1{}
|
||||
|
||||
func (DeploymentBasicGeneratorV1) ParamNames() []GeneratorParam {
|
||||
return []GeneratorParam{
|
||||
{"name", true},
|
||||
{"image", true},
|
||||
}
|
||||
}
|
||||
|
||||
func (s DeploymentBasicGeneratorV1) Generate(params map[string]interface{}) (runtime.Object, error) {
|
||||
err := ValidateParams(s.ParamNames(), params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name, isString := params["name"].(string)
|
||||
if !isString {
|
||||
return nil, fmt.Errorf("expected string, saw %v for 'name'", name)
|
||||
}
|
||||
imageStrings, isArray := params["image"].([]string)
|
||||
if !isArray {
|
||||
return nil, fmt.Errorf("expected []string, found :%v", imageStrings)
|
||||
}
|
||||
delegate := &DeploymentBasicGeneratorV1{Name: name, Images: imageStrings}
|
||||
return delegate.StructuredGenerate()
|
||||
}
|
||||
|
||||
// StructuredGenerate outputs a deployment object using the configured fields
|
||||
func (s *DeploymentBasicGeneratorV1) StructuredGenerate() (runtime.Object, error) {
|
||||
if err := s.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
podSpec := api.PodSpec{Containers: []api.Container{}}
|
||||
for _, imageString := range s.Images {
|
||||
imageSplit := strings.Split(imageString, "/")
|
||||
name := imageSplit[len(imageSplit)-1]
|
||||
podSpec.Containers = append(podSpec.Containers, api.Container{Name: name, Image: imageString})
|
||||
}
|
||||
|
||||
// setup default label and selector
|
||||
labels := map[string]string{}
|
||||
labels["app"] = s.Name
|
||||
selector := unversioned.LabelSelector{MatchLabels: labels}
|
||||
deployment := extensions.Deployment{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: s.Name,
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: extensions.DeploymentSpec{
|
||||
Replicas: 1,
|
||||
Selector: &selector,
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: podSpec,
|
||||
},
|
||||
},
|
||||
}
|
||||
return &deployment, nil
|
||||
}
|
||||
|
||||
// validate validates required fields are set to support structured generation
|
||||
func (s *DeploymentBasicGeneratorV1) validate() error {
|
||||
if len(s.Name) == 0 {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
if len(s.Images) == 0 {
|
||||
return fmt.Errorf("at least one image must be specified")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
2704
vendor/k8s.io/kubernetes/pkg/kubectl/describe.go
generated
vendored
2704
vendor/k8s.io/kubernetes/pkg/kubectl/describe.go
generated
vendored
File diff suppressed because it is too large
Load diff
20
vendor/k8s.io/kubernetes/pkg/kubectl/doc.go
generated
vendored
20
vendor/k8s.io/kubernetes/pkg/kubectl/doc.go
generated
vendored
|
|
@ -1,20 +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 is a set of libraries that are used by the kubectl command line tool.
|
||||
// They are separated out into a library to support unit testing. Most functionality should
|
||||
// be included in this package, and the main kubectl should really just be an entry point.
|
||||
package kubectl
|
||||
251
vendor/k8s.io/kubernetes/pkg/kubectl/explain.go
generated
vendored
251
vendor/k8s.io/kubernetes/pkg/kubectl/explain.go
generated
vendored
|
|
@ -1,251 +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 (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/emicklei/go-restful/swagger"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
apiutil "k8s.io/kubernetes/pkg/api/util"
|
||||
)
|
||||
|
||||
var allModels = make(map[string]*swagger.NamedModel)
|
||||
var recursive = false // this is global for convenience, can become int for multiple levels
|
||||
|
||||
// SplitAndParseResourceRequest separates the users input into a model and fields
|
||||
func SplitAndParseResourceRequest(inResource string, mapper meta.RESTMapper) (string, []string, error) {
|
||||
inResource, fieldsPath := splitDotNotation(inResource)
|
||||
inResource, _ = mapper.ResourceSingularizer(inResource)
|
||||
return inResource, fieldsPath, nil
|
||||
}
|
||||
|
||||
// PrintModelDescription prints the description of a specific model or dot path
|
||||
func PrintModelDescription(inModel string, fieldsPath []string, w io.Writer, swaggerSchema *swagger.ApiDeclaration, r bool) error {
|
||||
recursive = r // this is global for convenience
|
||||
apiVer := apiutil.GetVersion(swaggerSchema.ApiVersion) + "."
|
||||
|
||||
var pointedModel *swagger.NamedModel
|
||||
for i := range swaggerSchema.Models.List {
|
||||
name := swaggerSchema.Models.List[i].Name
|
||||
|
||||
allModels[name] = &swaggerSchema.Models.List[i]
|
||||
if strings.ToLower(name) == strings.ToLower(apiVer+inModel) {
|
||||
pointedModel = &swaggerSchema.Models.List[i]
|
||||
}
|
||||
}
|
||||
if pointedModel == nil {
|
||||
return fmt.Errorf("requested resource %q is not defined", inModel)
|
||||
}
|
||||
|
||||
if len(fieldsPath) == 0 {
|
||||
return printTopLevelResourceInfo(w, pointedModel)
|
||||
}
|
||||
|
||||
var pointedModelAsProp *swagger.NamedModelProperty
|
||||
for _, field := range fieldsPath {
|
||||
if prop, nextModel, isModel := getField(pointedModel, field); prop != nil {
|
||||
if isModel {
|
||||
pointedModelAsProp = prop
|
||||
pointedModel = allModels[nextModel]
|
||||
} else {
|
||||
return printPrimitive(w, prop)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("field %q does not exist", field)
|
||||
}
|
||||
}
|
||||
return printModelInfo(w, pointedModel, pointedModelAsProp)
|
||||
}
|
||||
|
||||
func splitDotNotation(model string) (string, []string) {
|
||||
var fieldsPath []string
|
||||
dotModel := strings.Split(model, ".")
|
||||
if len(dotModel) >= 1 {
|
||||
fieldsPath = dotModel[1:]
|
||||
}
|
||||
return dotModel[0], fieldsPath
|
||||
}
|
||||
|
||||
func getPointedModel(prop *swagger.ModelProperty) (string, bool) {
|
||||
if prop.Ref != nil {
|
||||
return *prop.Ref, true
|
||||
} else if *prop.Type == "array" && prop.Items.Ref != nil {
|
||||
return *prop.Items.Ref, true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func getField(model *swagger.NamedModel, sField string) (*swagger.NamedModelProperty, string, bool) {
|
||||
for _, prop := range model.Model.Properties.List {
|
||||
if prop.Name == sField {
|
||||
pointedModel, isModel := getPointedModel(&prop.Property)
|
||||
return &prop, pointedModel, isModel
|
||||
}
|
||||
}
|
||||
return nil, "", false
|
||||
}
|
||||
|
||||
func printModelInfo(w io.Writer, model *swagger.NamedModel, modelProp *swagger.NamedModelProperty) error {
|
||||
t, _ := getFieldType(&modelProp.Property)
|
||||
fmt.Fprintf(w, "RESOURCE: %s <%s>\n\n", modelProp.Name, t)
|
||||
fieldDesc, _ := wrapAndIndentText(modelProp.Property.Description, " ", 80)
|
||||
fmt.Fprintf(w, "DESCRIPTION:\n%s\n\n%s\n", fieldDesc, indentText(model.Model.Description, " "))
|
||||
return printFields(w, model)
|
||||
}
|
||||
|
||||
func printPrimitive(w io.Writer, field *swagger.NamedModelProperty) error {
|
||||
t, _ := getFieldType(&field.Property)
|
||||
fmt.Fprintf(w, "FIELD: %s <%s>\n\n", field.Name, t)
|
||||
d, _ := wrapAndIndentText(field.Property.Description, " ", 80)
|
||||
fmt.Fprintf(w, "DESCRIPTION:\n%s\n", d)
|
||||
return nil
|
||||
}
|
||||
|
||||
func printTopLevelResourceInfo(w io.Writer, model *swagger.NamedModel) error {
|
||||
fmt.Fprintf(w, "DESCRIPTION:\n%s\n", model.Model.Description)
|
||||
return printFields(w, model)
|
||||
}
|
||||
|
||||
func printFields(w io.Writer, model *swagger.NamedModel) error {
|
||||
fmt.Fprint(w, "\nFIELDS:\n")
|
||||
for _, field := range model.Model.Properties.List {
|
||||
fieldType, err := getFieldType(&field.Property)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if arrayContains(model.Model.Required, field.Name) {
|
||||
fmt.Fprintf(w, " %s\t<%s> -required-\n", field.Name, fieldType)
|
||||
} else {
|
||||
fmt.Fprintf(w, " %s\t<%s>\n", field.Name, fieldType)
|
||||
}
|
||||
|
||||
if recursive {
|
||||
pointedModel, isModel := getPointedModel(&field.Property)
|
||||
if isModel {
|
||||
for _, nestedField := range allModels[pointedModel].Model.Properties.List {
|
||||
t, _ := getFieldType(&nestedField.Property)
|
||||
fmt.Fprintf(w, " %s\t<%s>\n", nestedField.Name, t)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fieldDesc, _ := wrapAndIndentText(field.Property.Description, " ", 80)
|
||||
fmt.Fprintf(w, "%s\n\n", fieldDesc)
|
||||
}
|
||||
}
|
||||
fmt.Fprint(w, "\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
func getFieldType(prop *swagger.ModelProperty) (string, error) {
|
||||
if prop.Type == nil {
|
||||
return "Object", nil
|
||||
} else if *prop.Type == "any" {
|
||||
// Swagger Spec doesn't return information for maps.
|
||||
return "map[string]string", nil
|
||||
} else if *prop.Type == "array" {
|
||||
if prop.Items == nil {
|
||||
return "", fmt.Errorf("error in swagger spec. Property: %v contains an array without type", prop)
|
||||
}
|
||||
if prop.Items.Ref != nil {
|
||||
fieldType := "[]Object"
|
||||
return fieldType, nil
|
||||
}
|
||||
fieldType := "[]" + *prop.Items.Type
|
||||
return fieldType, nil
|
||||
}
|
||||
return *prop.Type, nil
|
||||
}
|
||||
|
||||
func wrapAndIndentText(desc, indent string, lim int) (string, error) {
|
||||
words := strings.Split(strings.Replace(strings.TrimSpace(desc), "\n", " ", -1), " ")
|
||||
n := len(words)
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
if len(words[i]) > lim {
|
||||
if strings.Contains(words[i], "/") {
|
||||
s := breakURL(words[i])
|
||||
words = append(words[:i], append(s, words[i+1:]...)...)
|
||||
i = i + len(s) - 1
|
||||
} else {
|
||||
fmt.Println(len(words[i]))
|
||||
return "", fmt.Errorf("there are words longer that the break limit is")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var lines []string
|
||||
line := []string{indent}
|
||||
lineL := len(indent)
|
||||
for i := 0; i < len(words); i++ {
|
||||
w := words[i]
|
||||
|
||||
if strings.HasSuffix(w, "/") && lineL+len(w)-1 < lim {
|
||||
prev := line[len(line)-1]
|
||||
if strings.HasSuffix(prev, "/") {
|
||||
if i+1 < len(words)-1 && !strings.HasSuffix(words[i+1], "/") {
|
||||
w = strings.TrimSuffix(w, "/")
|
||||
}
|
||||
|
||||
line[len(line)-1] = prev + w
|
||||
lineL += len(w)
|
||||
} else {
|
||||
line = append(line, w)
|
||||
lineL += len(w) + 1
|
||||
}
|
||||
} else if lineL+len(w) < lim {
|
||||
line = append(line, w)
|
||||
lineL += len(w) + 1
|
||||
} else {
|
||||
lines = append(lines, strings.Join(line, " "))
|
||||
line = []string{indent, w}
|
||||
lineL = len(indent) + len(w)
|
||||
}
|
||||
}
|
||||
lines = append(lines, strings.Join(line, " "))
|
||||
|
||||
return strings.Join(lines, "\n"), nil
|
||||
}
|
||||
|
||||
func breakURL(url string) []string {
|
||||
var buf []string
|
||||
for _, part := range strings.Split(url, "/") {
|
||||
buf = append(buf, part+"/")
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
func indentText(text, indent string) string {
|
||||
lines := strings.Split(text, "\n")
|
||||
for i := range lines {
|
||||
lines[i] = indent + lines[i]
|
||||
}
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
func arrayContains(s []string, e string) bool {
|
||||
for _, a := range s {
|
||||
if a == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
200
vendor/k8s.io/kubernetes/pkg/kubectl/generate.go
generated
vendored
200
vendor/k8s.io/kubernetes/pkg/kubectl/generate.go
generated
vendored
|
|
@ -1,200 +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 (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
utilerrors "k8s.io/kubernetes/pkg/util/errors"
|
||||
)
|
||||
|
||||
// GeneratorParam is a parameter for a generator
|
||||
// TODO: facilitate structured json generator input schemes
|
||||
type GeneratorParam struct {
|
||||
Name string
|
||||
Required bool
|
||||
}
|
||||
|
||||
// Generator is an interface for things that can generate API objects from input parameters.
|
||||
type Generator interface {
|
||||
// Generate creates an API object given a set of parameters
|
||||
Generate(params map[string]interface{}) (runtime.Object, error)
|
||||
// ParamNames returns the list of parameters that this generator uses
|
||||
ParamNames() []GeneratorParam
|
||||
}
|
||||
|
||||
// StructuredGenerator is an interface for things that can generate API objects not using parameter injection
|
||||
type StructuredGenerator interface {
|
||||
// StructuredGenerator creates an API object using pre-configured parameters
|
||||
StructuredGenerate() (runtime.Object, error)
|
||||
}
|
||||
|
||||
func IsZero(i interface{}) bool {
|
||||
if i == nil {
|
||||
return true
|
||||
}
|
||||
return reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface())
|
||||
}
|
||||
|
||||
// ValidateParams ensures that all required params are present in the params map
|
||||
func ValidateParams(paramSpec []GeneratorParam, params map[string]interface{}) error {
|
||||
allErrs := []error{}
|
||||
for ix := range paramSpec {
|
||||
if paramSpec[ix].Required {
|
||||
value, found := params[paramSpec[ix].Name]
|
||||
if !found || IsZero(value) {
|
||||
allErrs = append(allErrs, fmt.Errorf("Parameter: %s is required", paramSpec[ix].Name))
|
||||
}
|
||||
}
|
||||
}
|
||||
return utilerrors.NewAggregate(allErrs)
|
||||
}
|
||||
|
||||
// AnnotateFlags annotates all flags that are used by generators.
|
||||
func AnnotateFlags(cmd *cobra.Command, generators map[string]Generator) {
|
||||
// Iterate over all generators and mark any flags used by them.
|
||||
for name, generator := range generators {
|
||||
generatorParams := map[string]struct{}{}
|
||||
for _, param := range generator.ParamNames() {
|
||||
generatorParams[param.Name] = struct{}{}
|
||||
}
|
||||
|
||||
cmd.Flags().VisitAll(func(flag *pflag.Flag) {
|
||||
if _, found := generatorParams[flag.Name]; !found {
|
||||
// This flag is not used by the current generator
|
||||
// so skip it.
|
||||
return
|
||||
}
|
||||
if flag.Annotations == nil {
|
||||
flag.Annotations = map[string][]string{}
|
||||
}
|
||||
if annotations := flag.Annotations["generator"]; annotations == nil {
|
||||
flag.Annotations["generator"] = []string{}
|
||||
}
|
||||
flag.Annotations["generator"] = append(flag.Annotations["generator"], name)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// EnsureFlagsValid ensures that no invalid flags are being used against a generator.
|
||||
func EnsureFlagsValid(cmd *cobra.Command, generators map[string]Generator, generatorInUse string) error {
|
||||
AnnotateFlags(cmd, generators)
|
||||
|
||||
allErrs := []error{}
|
||||
cmd.Flags().VisitAll(func(flag *pflag.Flag) {
|
||||
// If the flag hasn't changed, don't validate it.
|
||||
if !flag.Changed {
|
||||
return
|
||||
}
|
||||
// Look into the flag annotations for the generators that can use it.
|
||||
if annotations := flag.Annotations["generator"]; len(annotations) > 0 {
|
||||
annotationMap := map[string]struct{}{}
|
||||
for _, ann := range annotations {
|
||||
annotationMap[ann] = struct{}{}
|
||||
}
|
||||
// If the current generator is not annotated, then this flag shouldn't
|
||||
// be used with it.
|
||||
if _, found := annotationMap[generatorInUse]; !found {
|
||||
allErrs = append(allErrs, fmt.Errorf("cannot use --%s with --generator=%s", flag.Name, generatorInUse))
|
||||
}
|
||||
}
|
||||
})
|
||||
return utilerrors.NewAggregate(allErrs)
|
||||
}
|
||||
|
||||
// MakeParams is a utility that creates generator parameters from a command line
|
||||
func MakeParams(cmd *cobra.Command, params []GeneratorParam) map[string]interface{} {
|
||||
result := map[string]interface{}{}
|
||||
for ix := range params {
|
||||
f := cmd.Flags().Lookup(params[ix].Name)
|
||||
if f != nil {
|
||||
result[params[ix].Name] = f.Value.String()
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func MakeProtocols(protocols map[string]string) string {
|
||||
out := []string{}
|
||||
for key, value := range protocols {
|
||||
out = append(out, fmt.Sprintf("%s/%s", key, value))
|
||||
}
|
||||
return strings.Join(out, ",")
|
||||
}
|
||||
|
||||
func ParseProtocols(protocols interface{}) (map[string]string, error) {
|
||||
protocolsString, isString := protocols.(string)
|
||||
if !isString {
|
||||
return nil, fmt.Errorf("expected string, found %v", protocols)
|
||||
}
|
||||
if len(protocolsString) == 0 {
|
||||
return nil, fmt.Errorf("no protocols passed")
|
||||
}
|
||||
portProtocolMap := map[string]string{}
|
||||
protocolsSlice := strings.Split(protocolsString, ",")
|
||||
for ix := range protocolsSlice {
|
||||
portProtocol := strings.Split(protocolsSlice[ix], "/")
|
||||
if len(portProtocol) != 2 {
|
||||
return nil, fmt.Errorf("unexpected port protocol mapping: %s", protocolsSlice[ix])
|
||||
}
|
||||
portProtocolMap[portProtocol[0]] = portProtocol[1]
|
||||
}
|
||||
return portProtocolMap, nil
|
||||
}
|
||||
|
||||
func MakeLabels(labels map[string]string) string {
|
||||
out := []string{}
|
||||
for key, value := range labels {
|
||||
out = append(out, fmt.Sprintf("%s=%s", key, value))
|
||||
}
|
||||
return strings.Join(out, ",")
|
||||
}
|
||||
|
||||
// ParseLabels turns a string representation of a label set into a map[string]string
|
||||
func ParseLabels(labelSpec interface{}) (map[string]string, error) {
|
||||
labelString, isString := labelSpec.(string)
|
||||
if !isString {
|
||||
return nil, fmt.Errorf("expected string, found %v", labelSpec)
|
||||
}
|
||||
if len(labelString) == 0 {
|
||||
return nil, fmt.Errorf("no label spec passed")
|
||||
}
|
||||
labels := map[string]string{}
|
||||
labelSpecs := strings.Split(labelString, ",")
|
||||
for ix := range labelSpecs {
|
||||
labelSpec := strings.Split(labelSpecs[ix], "=")
|
||||
if len(labelSpec) != 2 {
|
||||
return nil, fmt.Errorf("unexpected label spec: %s", labelSpecs[ix])
|
||||
}
|
||||
labels[labelSpec[0]] = labelSpec[1]
|
||||
}
|
||||
return labels, nil
|
||||
}
|
||||
|
||||
func GetBool(params map[string]string, key string, defValue bool) (bool, error) {
|
||||
if val, found := params[key]; !found {
|
||||
return defValue, nil
|
||||
} else {
|
||||
return strconv.ParseBool(val)
|
||||
}
|
||||
}
|
||||
129
vendor/k8s.io/kubernetes/pkg/kubectl/history.go
generated
vendored
129
vendor/k8s.io/kubernetes/pkg/kubectl/history.go
generated
vendored
|
|
@ -1,129 +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 (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
sliceutil "k8s.io/kubernetes/pkg/util/slice"
|
||||
)
|
||||
|
||||
const (
|
||||
ChangeCauseAnnotation = "kubernetes.io/change-cause"
|
||||
)
|
||||
|
||||
// HistoryViewer provides an interface for resources have historical information.
|
||||
type HistoryViewer interface {
|
||||
ViewHistory(namespace, name string, revision int64) (string, error)
|
||||
}
|
||||
|
||||
func HistoryViewerFor(kind unversioned.GroupKind, c clientset.Interface) (HistoryViewer, error) {
|
||||
switch kind {
|
||||
case extensions.Kind("Deployment"):
|
||||
return &DeploymentHistoryViewer{c}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("no history viewer has been implemented for %q", kind)
|
||||
}
|
||||
|
||||
type DeploymentHistoryViewer struct {
|
||||
c clientset.Interface
|
||||
}
|
||||
|
||||
// ViewHistory returns a revision-to-replicaset map as the revision history of a deployment
|
||||
func (h *DeploymentHistoryViewer) ViewHistory(namespace, name string, revision int64) (string, error) {
|
||||
deployment, err := h.c.Extensions().Deployments(namespace).Get(name)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to retrieve deployment %s: %v", name, err)
|
||||
}
|
||||
_, allOldRSs, newRS, err := deploymentutil.GetAllReplicaSets(deployment, h.c)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to retrieve replica sets from deployment %s: %v", name, err)
|
||||
}
|
||||
allRSs := allOldRSs
|
||||
if newRS != nil {
|
||||
allRSs = append(allRSs, newRS)
|
||||
}
|
||||
|
||||
historyInfo := make(map[int64]*api.PodTemplateSpec)
|
||||
for _, rs := range allRSs {
|
||||
v, err := deploymentutil.Revision(rs)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
historyInfo[v] = &rs.Spec.Template
|
||||
changeCause := getChangeCause(rs)
|
||||
if historyInfo[v].Annotations == nil {
|
||||
historyInfo[v].Annotations = make(map[string]string)
|
||||
}
|
||||
if len(changeCause) > 0 {
|
||||
historyInfo[v].Annotations[ChangeCauseAnnotation] = changeCause
|
||||
}
|
||||
}
|
||||
|
||||
if len(historyInfo) == 0 {
|
||||
return "No rollout history found.", nil
|
||||
}
|
||||
|
||||
if revision > 0 {
|
||||
// Print details of a specific revision
|
||||
template, ok := historyInfo[revision]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("unable to find the specified revision")
|
||||
}
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
DescribePodTemplate(template, buf)
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
// Sort the revisionToChangeCause map by revision
|
||||
revisions := make([]int64, 0, len(historyInfo))
|
||||
for r := range historyInfo {
|
||||
revisions = append(revisions, r)
|
||||
}
|
||||
sliceutil.SortInts64(revisions)
|
||||
|
||||
return tabbedString(func(out io.Writer) error {
|
||||
fmt.Fprintf(out, "REVISION\tCHANGE-CAUSE\n")
|
||||
for _, r := range revisions {
|
||||
// Find the change-cause of revision r
|
||||
changeCause := historyInfo[r].Annotations[ChangeCauseAnnotation]
|
||||
if len(changeCause) == 0 {
|
||||
changeCause = "<none>"
|
||||
}
|
||||
fmt.Fprintf(out, "%d\t%s\n", r, changeCause)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// getChangeCause returns the change-cause annotation of the input object
|
||||
func getChangeCause(obj runtime.Object) string {
|
||||
accessor, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return accessor.GetAnnotations()[ChangeCauseAnnotation]
|
||||
}
|
||||
32
vendor/k8s.io/kubernetes/pkg/kubectl/interfaces.go
generated
vendored
32
vendor/k8s.io/kubernetes/pkg/kubectl/interfaces.go
generated
vendored
|
|
@ -1,32 +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/kubernetes/pkg/api"
|
||||
client "k8s.io/kubernetes/pkg/client/restclient"
|
||||
)
|
||||
|
||||
// RESTClient is a client helper for dealing with RESTful resources
|
||||
// in a generic way.
|
||||
type RESTClient interface {
|
||||
Get() *client.Request
|
||||
Post() *client.Request
|
||||
Patch(api.PatchType) *client.Request
|
||||
Delete() *client.Request
|
||||
Put() *client.Request
|
||||
}
|
||||
185
vendor/k8s.io/kubernetes/pkg/kubectl/kubectl.go
generated
vendored
185
vendor/k8s.io/kubernetes/pkg/kubectl/kubectl.go
generated
vendored
|
|
@ -1,185 +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.
|
||||
*/
|
||||
|
||||
// A set of common functions needed by cmd/kubectl and pkg/kubectl packages.
|
||||
package kubectl
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
)
|
||||
|
||||
const (
|
||||
kubectlAnnotationPrefix = "kubectl.kubernetes.io/"
|
||||
)
|
||||
|
||||
type NamespaceInfo struct {
|
||||
Namespace string
|
||||
}
|
||||
|
||||
func listOfImages(spec *api.PodSpec) []string {
|
||||
images := make([]string, 0, len(spec.Containers))
|
||||
for _, container := range spec.Containers {
|
||||
images = append(images, container.Image)
|
||||
}
|
||||
return images
|
||||
}
|
||||
|
||||
func makeImageList(spec *api.PodSpec) string {
|
||||
return strings.Join(listOfImages(spec), ",")
|
||||
}
|
||||
|
||||
// OutputVersionMapper is a RESTMapper that will prefer mappings that
|
||||
// correspond to a preferred output version (if feasible)
|
||||
type OutputVersionMapper struct {
|
||||
meta.RESTMapper
|
||||
|
||||
// output versions takes a list of preferred GroupVersions. Only the first
|
||||
// hit for a given group will have effect. This allows different output versions
|
||||
// depending upon the group of the kind being requested
|
||||
OutputVersions []unversioned.GroupVersion
|
||||
}
|
||||
|
||||
// RESTMapping implements meta.RESTMapper by prepending the output version to the preferred version list.
|
||||
func (m OutputVersionMapper) RESTMapping(gk unversioned.GroupKind, versions ...string) (*meta.RESTMapping, error) {
|
||||
for _, preferredVersion := range m.OutputVersions {
|
||||
if gk.Group == preferredVersion.Group {
|
||||
mapping, err := m.RESTMapper.RESTMapping(gk, preferredVersion.Version)
|
||||
if err == nil {
|
||||
return mapping, nil
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return m.RESTMapper.RESTMapping(gk, versions...)
|
||||
}
|
||||
|
||||
// ShortForms is the list of short names to their expanded names
|
||||
var ShortForms = map[string]string{
|
||||
// Please keep this alphabetized
|
||||
// If you add an entry here, please also take a look at pkg/kubectl/cmd/cmd.go
|
||||
// and add an entry to valid_resources when appropriate.
|
||||
"cm": "configmaps",
|
||||
"cs": "componentstatuses",
|
||||
"csr": "certificatesigningrequests",
|
||||
"deploy": "deployments",
|
||||
"ds": "daemonsets",
|
||||
"ep": "endpoints",
|
||||
"ev": "events",
|
||||
"hpa": "horizontalpodautoscalers",
|
||||
"ing": "ingresses",
|
||||
"limits": "limitranges",
|
||||
"no": "nodes",
|
||||
"ns": "namespaces",
|
||||
"po": "pods",
|
||||
"psp": "podSecurityPolicies",
|
||||
"pvc": "persistentvolumeclaims",
|
||||
"pv": "persistentvolumes",
|
||||
"quota": "resourcequotas",
|
||||
"rc": "replicationcontrollers",
|
||||
"rs": "replicasets",
|
||||
"sa": "serviceaccounts",
|
||||
"svc": "services",
|
||||
}
|
||||
|
||||
// ResourceShortFormFor looks up for a short form of resource names.
|
||||
func ResourceShortFormFor(resource string) (string, bool) {
|
||||
var alias string
|
||||
exists := false
|
||||
for k, val := range ShortForms {
|
||||
if val == resource {
|
||||
alias = k
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return alias, exists
|
||||
}
|
||||
|
||||
// ResourceAliases returns the resource shortcuts and plural forms for the given resources.
|
||||
func ResourceAliases(rs []string) []string {
|
||||
as := make([]string, 0, len(rs))
|
||||
plurals := make(map[string]struct{}, len(rs))
|
||||
for _, r := range rs {
|
||||
var plural string
|
||||
switch {
|
||||
case r == "endpoints":
|
||||
plural = r // exception. "endpoint" does not exist. Why?
|
||||
case strings.HasSuffix(r, "y"):
|
||||
plural = r[0:len(r)-1] + "ies"
|
||||
case strings.HasSuffix(r, "s"):
|
||||
plural = r + "es"
|
||||
default:
|
||||
plural = r + "s"
|
||||
}
|
||||
as = append(as, plural)
|
||||
|
||||
plurals[plural] = struct{}{}
|
||||
}
|
||||
|
||||
for sf, r := range ShortForms {
|
||||
if _, found := plurals[r]; found {
|
||||
as = append(as, sf)
|
||||
}
|
||||
}
|
||||
return as
|
||||
}
|
||||
|
||||
// parseFileSource parses the source given. Acceptable formats include:
|
||||
//
|
||||
// 1. source-path: the basename will become the key name
|
||||
// 2. source-name=source-path: the source-name will become the key name and source-path is the path to the key file
|
||||
//
|
||||
// Key names cannot include '='.
|
||||
func parseFileSource(source string) (keyName, filePath string, err error) {
|
||||
numSeparators := strings.Count(source, "=")
|
||||
switch {
|
||||
case numSeparators == 0:
|
||||
return path.Base(source), source, nil
|
||||
case numSeparators == 1 && strings.HasPrefix(source, "="):
|
||||
return "", "", fmt.Errorf("key name for file path %v missing.", strings.TrimPrefix(source, "="))
|
||||
case numSeparators == 1 && strings.HasSuffix(source, "="):
|
||||
return "", "", fmt.Errorf("file path for key name %v missing.", strings.TrimSuffix(source, "="))
|
||||
case numSeparators > 1:
|
||||
return "", "", errors.New("Key names or file paths cannot contain '='.")
|
||||
default:
|
||||
components := strings.Split(source, "=")
|
||||
return components[0], components[1], nil
|
||||
}
|
||||
}
|
||||
|
||||
// parseLiteralSource parses the source key=val pair
|
||||
func parseLiteralSource(source string) (keyName, value string, err error) {
|
||||
// leading equal is invalid
|
||||
if strings.Index(source, "=") == 0 {
|
||||
return "", "", fmt.Errorf("invalid literal source %v, expected key=value", source)
|
||||
}
|
||||
// split after the first equal (so values can have the = character)
|
||||
items := strings.SplitN(source, "=", 2)
|
||||
if len(items) != 2 {
|
||||
return "", "", fmt.Errorf("invalid literal source %v, expected key=value", source)
|
||||
}
|
||||
|
||||
return items[0], items[1], nil
|
||||
}
|
||||
79
vendor/k8s.io/kubernetes/pkg/kubectl/namespace.go
generated
vendored
79
vendor/k8s.io/kubernetes/pkg/kubectl/namespace.go
generated
vendored
|
|
@ -1,79 +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"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
// NamespaceGeneratorV1 supports stable generation of a namespace
|
||||
type NamespaceGeneratorV1 struct {
|
||||
// Name of namespace
|
||||
Name string
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameter injection
|
||||
var _ Generator = &NamespaceGeneratorV1{}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters specified during construction
|
||||
var _ StructuredGenerator = &NamespaceGeneratorV1{}
|
||||
|
||||
// Generate returns a namespace using the specified parameters
|
||||
func (g NamespaceGeneratorV1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
err := ValidateParams(g.ParamNames(), genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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 := &NamespaceGeneratorV1{Name: params["name"]}
|
||||
return delegate.StructuredGenerate()
|
||||
}
|
||||
|
||||
// ParamNames returns the set of supported input parameters when using the parameter injection generator pattern
|
||||
func (g NamespaceGeneratorV1) ParamNames() []GeneratorParam {
|
||||
return []GeneratorParam{
|
||||
{"name", true},
|
||||
}
|
||||
}
|
||||
|
||||
// StructuredGenerate outputs a namespace object using the configured fields
|
||||
func (g *NamespaceGeneratorV1) StructuredGenerate() (runtime.Object, error) {
|
||||
if err := g.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
namespace := &api.Namespace{}
|
||||
namespace.Name = g.Name
|
||||
return namespace, nil
|
||||
}
|
||||
|
||||
// validate validates required fields are set to support structured generation
|
||||
func (g *NamespaceGeneratorV1) validate() error {
|
||||
if len(g.Name) == 0 {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
248
vendor/k8s.io/kubernetes/pkg/kubectl/proxy_server.go
generated
vendored
248
vendor/k8s.io/kubernetes/pkg/kubectl/proxy_server.go
generated
vendored
|
|
@ -1,248 +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 (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
"k8s.io/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultHostAcceptRE = "^localhost$,^127\\.0\\.0\\.1$,^\\[::1\\]$"
|
||||
DefaultPathAcceptRE = "^/.*"
|
||||
DefaultPathRejectRE = "^/api/.*/pods/.*/exec,^/api/.*/pods/.*/attach"
|
||||
DefaultMethodRejectRE = "POST,PUT,PATCH"
|
||||
)
|
||||
|
||||
var (
|
||||
// The reverse proxy will periodically flush the io writer at this frequency.
|
||||
// Only matters for long poll connections like the one used to watch. With an
|
||||
// interval of 0 the reverse proxy will buffer content sent on any connection
|
||||
// with transfer-encoding=chunked.
|
||||
// TODO: Flush after each chunk so the client doesn't suffer a 100ms latency per
|
||||
// watch event.
|
||||
ReverseProxyFlushInterval = 100 * time.Millisecond
|
||||
)
|
||||
|
||||
// FilterServer rejects requests which don't match one of the specified regular expressions
|
||||
type FilterServer struct {
|
||||
// Only paths that match this regexp will be accepted
|
||||
AcceptPaths []*regexp.Regexp
|
||||
// Paths that match this regexp will be rejected, even if they match the above
|
||||
RejectPaths []*regexp.Regexp
|
||||
// Hosts are required to match this list of regexp
|
||||
AcceptHosts []*regexp.Regexp
|
||||
// Methods that match this regexp are rejected
|
||||
RejectMethods []*regexp.Regexp
|
||||
// The delegate to call to handle accepted requests.
|
||||
delegate http.Handler
|
||||
}
|
||||
|
||||
// Splits a comma separated list of regexps into an array of Regexp objects.
|
||||
func MakeRegexpArray(str string) ([]*regexp.Regexp, error) {
|
||||
parts := strings.Split(str, ",")
|
||||
result := make([]*regexp.Regexp, len(parts))
|
||||
for ix := range parts {
|
||||
re, err := regexp.Compile(parts[ix])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result[ix] = re
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func MakeRegexpArrayOrDie(str string) []*regexp.Regexp {
|
||||
result, err := MakeRegexpArray(str)
|
||||
if err != nil {
|
||||
glog.Fatalf("Error compiling re: %v", err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func matchesRegexp(str string, regexps []*regexp.Regexp) bool {
|
||||
for _, re := range regexps {
|
||||
if re.MatchString(str) {
|
||||
glog.V(6).Infof("%v matched %s", str, re)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *FilterServer) accept(method, path, host string) bool {
|
||||
if matchesRegexp(path, f.RejectPaths) {
|
||||
glog.V(3).Infof("Filter rejecting %v %v %v", method, path, host)
|
||||
return false
|
||||
}
|
||||
if matchesRegexp(method, f.RejectMethods) {
|
||||
glog.V(3).Infof("Filter rejecting %v %v %v", method, path, host)
|
||||
return false
|
||||
}
|
||||
if matchesRegexp(path, f.AcceptPaths) && matchesRegexp(host, f.AcceptHosts) {
|
||||
glog.V(3).Infof("Filter accepting %v %v %v", method, path, host)
|
||||
return true
|
||||
}
|
||||
glog.V(3).Infof("Filter rejecting %v %v %v", method, path, host)
|
||||
return false
|
||||
}
|
||||
|
||||
// Make a copy of f which passes requests along to the new delegate.
|
||||
func (f *FilterServer) HandlerFor(delegate http.Handler) *FilterServer {
|
||||
f2 := *f
|
||||
f2.delegate = delegate
|
||||
return &f2
|
||||
}
|
||||
|
||||
// Get host from a host header value like "localhost" or "localhost:8080"
|
||||
func extractHost(header string) (host string) {
|
||||
host, _, err := net.SplitHostPort(header)
|
||||
if err != nil {
|
||||
host = header
|
||||
}
|
||||
return host
|
||||
}
|
||||
|
||||
func (f *FilterServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
host := extractHost(req.Host)
|
||||
if f.accept(req.Method, req.URL.Path, host) {
|
||||
f.delegate.ServeHTTP(rw, req)
|
||||
return
|
||||
}
|
||||
rw.WriteHeader(http.StatusForbidden)
|
||||
rw.Write([]byte("<h3>Unauthorized</h3>"))
|
||||
}
|
||||
|
||||
// ProxyServer is a http.Handler which proxies Kubernetes APIs to remote API server.
|
||||
type ProxyServer struct {
|
||||
handler http.Handler
|
||||
}
|
||||
|
||||
// NewProxyServer creates and installs a new ProxyServer.
|
||||
// It automatically registers the created ProxyServer to http.DefaultServeMux.
|
||||
// 'filter', if non-nil, protects requests to the api only.
|
||||
func NewProxyServer(filebase string, apiProxyPrefix string, staticPrefix string, filter *FilterServer, cfg *restclient.Config) (*ProxyServer, error) {
|
||||
host := cfg.Host
|
||||
if !strings.HasSuffix(host, "/") {
|
||||
host = host + "/"
|
||||
}
|
||||
target, err := url.Parse(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
proxy := newProxy(target)
|
||||
if proxy.Transport, err = restclient.TransportFor(cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
proxyServer := http.Handler(proxy)
|
||||
if filter != nil {
|
||||
proxyServer = filter.HandlerFor(proxyServer)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(apiProxyPrefix, "/api") {
|
||||
proxyServer = stripLeaveSlash(apiProxyPrefix, proxyServer)
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle(apiProxyPrefix, proxyServer)
|
||||
if filebase != "" {
|
||||
// Require user to explicitly request this behavior rather than
|
||||
// serving their working directory by default.
|
||||
mux.Handle(staticPrefix, newFileHandler(staticPrefix, filebase))
|
||||
}
|
||||
return &ProxyServer{handler: mux}, nil
|
||||
}
|
||||
|
||||
// Listen is a simple wrapper around net.Listen.
|
||||
func (s *ProxyServer) Listen(address string, port int) (net.Listener, error) {
|
||||
return net.Listen("tcp", fmt.Sprintf("%s:%d", address, port))
|
||||
}
|
||||
|
||||
// ListenUnix does net.Listen for a unix socket
|
||||
func (s *ProxyServer) ListenUnix(path string) (net.Listener, error) {
|
||||
// Remove any socket, stale or not, but fall through for other files
|
||||
fi, err := os.Stat(path)
|
||||
if err == nil && (fi.Mode()&os.ModeSocket) != 0 {
|
||||
os.Remove(path)
|
||||
}
|
||||
// Default to only user accessible socket, caller can open up later if desired
|
||||
oldmask, _ := util.Umask(0077)
|
||||
l, err := net.Listen("unix", path)
|
||||
util.Umask(oldmask)
|
||||
return l, err
|
||||
}
|
||||
|
||||
// Serve starts the server using given listener, loops forever.
|
||||
func (s *ProxyServer) ServeOnListener(l net.Listener) error {
|
||||
server := http.Server{
|
||||
Handler: s.handler,
|
||||
}
|
||||
return server.Serve(l)
|
||||
}
|
||||
|
||||
func newProxy(target *url.URL) *httputil.ReverseProxy {
|
||||
director := func(req *http.Request) {
|
||||
req.URL.Scheme = target.Scheme
|
||||
req.URL.Host = target.Host
|
||||
req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
|
||||
}
|
||||
return &httputil.ReverseProxy{Director: director, FlushInterval: ReverseProxyFlushInterval}
|
||||
}
|
||||
|
||||
func newFileHandler(prefix, base string) http.Handler {
|
||||
return http.StripPrefix(prefix, http.FileServer(http.Dir(base)))
|
||||
}
|
||||
|
||||
func singleJoiningSlash(a, b string) string {
|
||||
aslash := strings.HasSuffix(a, "/")
|
||||
bslash := strings.HasPrefix(b, "/")
|
||||
switch {
|
||||
case aslash && bslash:
|
||||
return a + b[1:]
|
||||
case !aslash && !bslash:
|
||||
return a + "/" + b
|
||||
}
|
||||
return a + b
|
||||
}
|
||||
|
||||
// like http.StripPrefix, but always leaves an initial slash. (so that our
|
||||
// regexps will work.)
|
||||
func stripLeaveSlash(prefix string, h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
p := strings.TrimPrefix(req.URL.Path, prefix)
|
||||
if len(p) >= len(req.URL.Path) {
|
||||
http.NotFound(w, req)
|
||||
return
|
||||
}
|
||||
if len(p) > 0 && p[:1] != "/" {
|
||||
p = "/" + p
|
||||
}
|
||||
req.URL.Path = p
|
||||
h.ServeHTTP(w, req)
|
||||
})
|
||||
}
|
||||
125
vendor/k8s.io/kubernetes/pkg/kubectl/quota.go
generated
vendored
125
vendor/k8s.io/kubernetes/pkg/kubectl/quota.go
generated
vendored
|
|
@ -1,125 +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"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
// ResourceQuotaGeneratorV1 supports stable generation of a resource quota
|
||||
type ResourceQuotaGeneratorV1 struct {
|
||||
// The name of a quota object.
|
||||
Name string
|
||||
|
||||
// The hard resource limit string before parsing.
|
||||
Hard string
|
||||
|
||||
// The scopes of a quota object before parsing.
|
||||
Scopes string
|
||||
}
|
||||
|
||||
// ParamNames returns the set of supported input parameters when using the parameter injection generator pattern
|
||||
func (g ResourceQuotaGeneratorV1) ParamNames() []GeneratorParam {
|
||||
return []GeneratorParam{
|
||||
{"name", true},
|
||||
{"hard", true},
|
||||
{"scopes", false},
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameter injection
|
||||
var _ Generator = &ResourceQuotaGeneratorV1{}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters specified during construction
|
||||
var _ StructuredGenerator = &ResourceQuotaGeneratorV1{}
|
||||
|
||||
func (g ResourceQuotaGeneratorV1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
err := ValidateParams(g.ParamNames(), genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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 := &ResourceQuotaGeneratorV1{}
|
||||
delegate.Name = params["name"]
|
||||
delegate.Hard = params["hard"]
|
||||
delegate.Scopes = params["scopes"]
|
||||
return delegate.StructuredGenerate()
|
||||
}
|
||||
|
||||
// StructuredGenerate outputs a ResourceQuota object using the configured fields
|
||||
func (g *ResourceQuotaGeneratorV1) StructuredGenerate() (runtime.Object, error) {
|
||||
if err := g.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resourceList, err := populateResourceList(g.Hard)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
scopes, err := parseScopes(g.Scopes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resourceQuota := &api.ResourceQuota{}
|
||||
resourceQuota.Name = g.Name
|
||||
resourceQuota.Spec.Hard = resourceList
|
||||
resourceQuota.Spec.Scopes = scopes
|
||||
return resourceQuota, nil
|
||||
}
|
||||
|
||||
// validate validates required fields are set to support structured generation
|
||||
func (r *ResourceQuotaGeneratorV1) validate() error {
|
||||
if len(r.Name) == 0 {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseScopes(spec string) ([]api.ResourceQuotaScope, error) {
|
||||
// empty input gets a nil response to preserve generator test expected behaviors
|
||||
if spec == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
scopes := strings.Split(spec, ",")
|
||||
result := make([]api.ResourceQuotaScope, 0, len(scopes))
|
||||
for _, scope := range scopes {
|
||||
// intentionally do not verify the scope against the valid scope list. This is done by the apiserver anyway.
|
||||
|
||||
if scope == "" {
|
||||
return nil, fmt.Errorf("invalid resource quota scope \"\"")
|
||||
}
|
||||
|
||||
result = append(result, api.ResourceQuotaScope(scope))
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
80
vendor/k8s.io/kubernetes/pkg/kubectl/resource/BUILD
generated
vendored
80
vendor/k8s.io/kubernetes/pkg/kubectl/resource/BUILD
generated
vendored
|
|
@ -1,80 +0,0 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_binary",
|
||||
"go_library",
|
||||
"go_test",
|
||||
"cgo_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"builder.go",
|
||||
"doc.go",
|
||||
"helper.go",
|
||||
"interfaces.go",
|
||||
"mapper.go",
|
||||
"result.go",
|
||||
"selector.go",
|
||||
"visitor.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/errors:go_default_library",
|
||||
"//pkg/api/meta:go_default_library",
|
||||
"//pkg/api/unversioned:go_default_library",
|
||||
"//pkg/api/validation:go_default_library",
|
||||
"//pkg/apimachinery/registered:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//pkg/client/restclient:go_default_library",
|
||||
"//pkg/labels:go_default_library",
|
||||
"//pkg/runtime:go_default_library",
|
||||
"//pkg/util/errors:go_default_library",
|
||||
"//pkg/util/sets:go_default_library",
|
||||
"//pkg/util/yaml:go_default_library",
|
||||
"//pkg/watch:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"builder_test.go",
|
||||
"helper_test.go",
|
||||
"visitor_test.go",
|
||||
],
|
||||
data = [
|
||||
"//examples:config",
|
||||
"//test/fixtures",
|
||||
],
|
||||
library = "go_default_library",
|
||||
tags = [
|
||||
"automanaged",
|
||||
"skip",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/meta:go_default_library",
|
||||
"//pkg/api/resource:go_default_library",
|
||||
"//pkg/api/testapi:go_default_library",
|
||||
"//pkg/api/testing:go_default_library",
|
||||
"//pkg/api/unversioned:go_default_library",
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/apimachinery/registered:go_default_library",
|
||||
"//pkg/client/restclient/fake:go_default_library",
|
||||
"//pkg/labels:go_default_library",
|
||||
"//pkg/runtime:go_default_library",
|
||||
"//pkg/runtime/serializer/streaming:go_default_library",
|
||||
"//pkg/util/errors:go_default_library",
|
||||
"//pkg/util/testing:go_default_library",
|
||||
"//pkg/watch:go_default_library",
|
||||
"//pkg/watch/versioned:go_default_library",
|
||||
"//vendor:github.com/ghodss/yaml",
|
||||
"//vendor:github.com/stretchr/testify/assert",
|
||||
],
|
||||
)
|
||||
805
vendor/k8s.io/kubernetes/pkg/kubectl/resource/builder.go
generated
vendored
805
vendor/k8s.io/kubernetes/pkg/kubectl/resource/builder.go
generated
vendored
|
|
@ -1,805 +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 resource
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/api/validation"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
utilerrors "k8s.io/kubernetes/pkg/util/errors"
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
)
|
||||
|
||||
var FileExtensions = []string{".json", ".yaml", ".yml"}
|
||||
var InputExtensions = append(FileExtensions, "stdin")
|
||||
|
||||
const defaultHttpGetAttempts int = 3
|
||||
|
||||
// Builder provides convenience functions for taking arguments and parameters
|
||||
// from the command line and converting them to a list of resources to iterate
|
||||
// over using the Visitor interface.
|
||||
type Builder struct {
|
||||
mapper *Mapper
|
||||
|
||||
errs []error
|
||||
|
||||
paths []Visitor
|
||||
stream bool
|
||||
dir bool
|
||||
|
||||
selector labels.Selector
|
||||
selectAll bool
|
||||
|
||||
resources []string
|
||||
|
||||
namespace string
|
||||
allNamespace bool
|
||||
names []string
|
||||
|
||||
resourceTuples []resourceTuple
|
||||
|
||||
defaultNamespace bool
|
||||
requireNamespace bool
|
||||
|
||||
flatten bool
|
||||
latest bool
|
||||
|
||||
requireObject bool
|
||||
|
||||
singleResourceType bool
|
||||
continueOnError bool
|
||||
|
||||
singular bool
|
||||
|
||||
export bool
|
||||
|
||||
schema validation.Schema
|
||||
}
|
||||
|
||||
var missingResourceError = fmt.Errorf(`You must provide one or more resources by argument or filename.
|
||||
Example resource specifications include:
|
||||
'-f rsrc.yaml'
|
||||
'--filename=rsrc.json'
|
||||
'pods my-pod'
|
||||
'services'`)
|
||||
|
||||
// TODO: expand this to include other errors.
|
||||
func IsUsageError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
return err == missingResourceError
|
||||
}
|
||||
|
||||
type FilenameOptions struct {
|
||||
Filenames []string
|
||||
Recursive bool
|
||||
}
|
||||
|
||||
type resourceTuple struct {
|
||||
Resource string
|
||||
Name string
|
||||
}
|
||||
|
||||
// NewBuilder creates a builder that operates on generic objects.
|
||||
func NewBuilder(mapper meta.RESTMapper, typer runtime.ObjectTyper, clientMapper ClientMapper, decoder runtime.Decoder) *Builder {
|
||||
return &Builder{
|
||||
mapper: &Mapper{typer, mapper, clientMapper, decoder},
|
||||
requireObject: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Builder) Schema(schema validation.Schema) *Builder {
|
||||
b.schema = schema
|
||||
return b
|
||||
}
|
||||
|
||||
// FilenameParam groups input in two categories: URLs and files (files, directories, STDIN)
|
||||
// If enforceNamespace is false, namespaces in the specs will be allowed to
|
||||
// override the default namespace. If it is true, namespaces that don't match
|
||||
// will cause an error.
|
||||
// If ContinueOnError() is set prior to this method, objects on the path that are not
|
||||
// recognized will be ignored (but logged at V(2)).
|
||||
func (b *Builder) FilenameParam(enforceNamespace bool, filenameOptions *FilenameOptions) *Builder {
|
||||
recursive := filenameOptions.Recursive
|
||||
paths := filenameOptions.Filenames
|
||||
for _, s := range paths {
|
||||
switch {
|
||||
case s == "-":
|
||||
b.Stdin()
|
||||
case strings.Index(s, "http://") == 0 || strings.Index(s, "https://") == 0:
|
||||
url, err := url.Parse(s)
|
||||
if err != nil {
|
||||
b.errs = append(b.errs, fmt.Errorf("the URL passed to filename %q is not valid: %v", s, err))
|
||||
continue
|
||||
}
|
||||
b.URL(defaultHttpGetAttempts, url)
|
||||
default:
|
||||
if !recursive {
|
||||
b.singular = true
|
||||
}
|
||||
b.Path(recursive, s)
|
||||
}
|
||||
}
|
||||
|
||||
if enforceNamespace {
|
||||
b.RequireNamespace()
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// URL accepts a number of URLs directly.
|
||||
func (b *Builder) URL(httpAttemptCount int, urls ...*url.URL) *Builder {
|
||||
for _, u := range urls {
|
||||
b.paths = append(b.paths, &URLVisitor{
|
||||
URL: u,
|
||||
StreamVisitor: NewStreamVisitor(nil, b.mapper, u.String(), b.schema),
|
||||
HttpAttemptCount: httpAttemptCount,
|
||||
})
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// Stdin will read objects from the standard input. If ContinueOnError() is set
|
||||
// prior to this method being called, objects in the stream that are unrecognized
|
||||
// will be ignored (but logged at V(2)).
|
||||
func (b *Builder) Stdin() *Builder {
|
||||
b.stream = true
|
||||
b.paths = append(b.paths, FileVisitorForSTDIN(b.mapper, b.schema))
|
||||
return b
|
||||
}
|
||||
|
||||
// Stream will read objects from the provided reader, and if an error occurs will
|
||||
// include the name string in the error message. If ContinueOnError() is set
|
||||
// prior to this method being called, objects in the stream that are unrecognized
|
||||
// will be ignored (but logged at V(2)).
|
||||
func (b *Builder) Stream(r io.Reader, name string) *Builder {
|
||||
b.stream = true
|
||||
b.paths = append(b.paths, NewStreamVisitor(r, b.mapper, name, b.schema))
|
||||
return b
|
||||
}
|
||||
|
||||
// Path accepts a set of paths that may be files, directories (all can containing
|
||||
// one or more resources). Creates a FileVisitor for each file and then each
|
||||
// FileVisitor is streaming the content to a StreamVisitor. If ContinueOnError() is set
|
||||
// prior to this method being called, objects on the path that are unrecognized will be
|
||||
// ignored (but logged at V(2)).
|
||||
func (b *Builder) Path(recursive bool, paths ...string) *Builder {
|
||||
for _, p := range paths {
|
||||
_, err := os.Stat(p)
|
||||
if os.IsNotExist(err) {
|
||||
b.errs = append(b.errs, fmt.Errorf("the path %q does not exist", p))
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
b.errs = append(b.errs, fmt.Errorf("the path %q cannot be accessed: %v", p, err))
|
||||
continue
|
||||
}
|
||||
|
||||
visitors, err := ExpandPathsToFileVisitors(b.mapper, p, recursive, FileExtensions, b.schema)
|
||||
if err != nil {
|
||||
b.errs = append(b.errs, fmt.Errorf("error reading %q: %v", p, err))
|
||||
}
|
||||
if len(visitors) > 1 {
|
||||
b.dir = true
|
||||
}
|
||||
|
||||
b.paths = append(b.paths, visitors...)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// ResourceTypes is a list of types of resources to operate on, when listing objects on
|
||||
// the server or retrieving objects that match a selector.
|
||||
func (b *Builder) ResourceTypes(types ...string) *Builder {
|
||||
b.resources = append(b.resources, types...)
|
||||
return b
|
||||
}
|
||||
|
||||
// ResourceNames accepts a default type and one or more names, and creates tuples of
|
||||
// resources
|
||||
func (b *Builder) ResourceNames(resource string, names ...string) *Builder {
|
||||
for _, name := range names {
|
||||
// See if this input string is of type/name format
|
||||
tuple, ok, err := splitResourceTypeName(name)
|
||||
if err != nil {
|
||||
b.errs = append(b.errs, err)
|
||||
return b
|
||||
}
|
||||
|
||||
if ok {
|
||||
b.resourceTuples = append(b.resourceTuples, tuple)
|
||||
continue
|
||||
}
|
||||
if len(resource) == 0 {
|
||||
b.errs = append(b.errs, fmt.Errorf("the argument %q must be RESOURCE/NAME", name))
|
||||
continue
|
||||
}
|
||||
|
||||
// Use the given default type to create a resource tuple
|
||||
b.resourceTuples = append(b.resourceTuples, resourceTuple{Resource: resource, Name: name})
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// SelectorParam defines a selector that should be applied to the object types to load.
|
||||
// This will not affect files loaded from disk or URL. If the parameter is empty it is
|
||||
// a no-op - to select all resources invoke `b.Selector(labels.Everything)`.
|
||||
func (b *Builder) SelectorParam(s string) *Builder {
|
||||
selector, err := labels.Parse(s)
|
||||
if err != nil {
|
||||
b.errs = append(b.errs, fmt.Errorf("the provided selector %q is not valid: %v", s, err))
|
||||
return b
|
||||
}
|
||||
if selector.Empty() {
|
||||
return b
|
||||
}
|
||||
if b.selectAll {
|
||||
b.errs = append(b.errs, fmt.Errorf("found non empty selector %q with previously set 'all' parameter. ", s))
|
||||
return b
|
||||
}
|
||||
return b.Selector(selector)
|
||||
}
|
||||
|
||||
// Selector accepts a selector directly, and if non nil will trigger a list action.
|
||||
func (b *Builder) Selector(selector labels.Selector) *Builder {
|
||||
b.selector = selector
|
||||
return b
|
||||
}
|
||||
|
||||
// ExportParam accepts the export boolean for these resources
|
||||
func (b *Builder) ExportParam(export bool) *Builder {
|
||||
b.export = export
|
||||
return b
|
||||
}
|
||||
|
||||
// NamespaceParam accepts the namespace that these resources should be
|
||||
// considered under from - used by DefaultNamespace() and RequireNamespace()
|
||||
func (b *Builder) NamespaceParam(namespace string) *Builder {
|
||||
b.namespace = namespace
|
||||
return b
|
||||
}
|
||||
|
||||
// DefaultNamespace instructs the builder to set the namespace value for any object found
|
||||
// to NamespaceParam() if empty.
|
||||
func (b *Builder) DefaultNamespace() *Builder {
|
||||
b.defaultNamespace = true
|
||||
return b
|
||||
}
|
||||
|
||||
// AllNamespaces instructs the builder to use NamespaceAll as a namespace to request resources
|
||||
// across all of the namespace. This overrides the namespace set by NamespaceParam().
|
||||
func (b *Builder) AllNamespaces(allNamespace bool) *Builder {
|
||||
if allNamespace {
|
||||
b.namespace = api.NamespaceAll
|
||||
}
|
||||
b.allNamespace = allNamespace
|
||||
return b
|
||||
}
|
||||
|
||||
// RequireNamespace instructs the builder to set the namespace value for any object found
|
||||
// to NamespaceParam() if empty, and if the value on the resource does not match
|
||||
// NamespaceParam() an error will be returned.
|
||||
func (b *Builder) RequireNamespace() *Builder {
|
||||
b.requireNamespace = true
|
||||
return b
|
||||
}
|
||||
|
||||
// SelectEverythingParam
|
||||
func (b *Builder) SelectAllParam(selectAll bool) *Builder {
|
||||
if selectAll && b.selector != nil {
|
||||
b.errs = append(b.errs, fmt.Errorf("setting 'all' parameter but found a non empty selector. "))
|
||||
return b
|
||||
}
|
||||
b.selectAll = selectAll
|
||||
return b
|
||||
}
|
||||
|
||||
// ResourceTypeOrNameArgs indicates that the builder should accept arguments
|
||||
// of the form `(<type1>[,<type2>,...]|<type> <name1>[,<name2>,...])`. When one argument is
|
||||
// received, the types provided will be retrieved from the server (and be comma delimited).
|
||||
// When two or more arguments are received, they must be a single type and resource name(s).
|
||||
// The allowEmptySelector permits to select all the resources (via Everything func).
|
||||
func (b *Builder) ResourceTypeOrNameArgs(allowEmptySelector bool, args ...string) *Builder {
|
||||
args = normalizeMultipleResourcesArgs(args)
|
||||
if ok, err := hasCombinedTypeArgs(args); ok {
|
||||
if err != nil {
|
||||
b.errs = append(b.errs, err)
|
||||
return b
|
||||
}
|
||||
for _, s := range args {
|
||||
tuple, ok, err := splitResourceTypeName(s)
|
||||
if err != nil {
|
||||
b.errs = append(b.errs, err)
|
||||
return b
|
||||
}
|
||||
if ok {
|
||||
b.resourceTuples = append(b.resourceTuples, tuple)
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
if len(args) > 0 {
|
||||
// Try replacing aliases only in types
|
||||
args[0] = b.ReplaceAliases(args[0])
|
||||
}
|
||||
switch {
|
||||
case len(args) > 2:
|
||||
b.names = append(b.names, args[1:]...)
|
||||
b.ResourceTypes(SplitResourceArgument(args[0])...)
|
||||
case len(args) == 2:
|
||||
b.names = append(b.names, args[1])
|
||||
b.ResourceTypes(SplitResourceArgument(args[0])...)
|
||||
case len(args) == 1:
|
||||
b.ResourceTypes(SplitResourceArgument(args[0])...)
|
||||
if b.selector == nil && allowEmptySelector {
|
||||
b.selector = labels.Everything()
|
||||
}
|
||||
case len(args) == 0:
|
||||
default:
|
||||
b.errs = append(b.errs, fmt.Errorf("arguments must consist of a resource or a resource and name"))
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// ReplaceAliases accepts an argument and tries to expand any existing
|
||||
// aliases found in it
|
||||
func (b *Builder) ReplaceAliases(input string) string {
|
||||
replaced := []string{}
|
||||
for _, arg := range strings.Split(input, ",") {
|
||||
if aliases, ok := b.mapper.AliasesForResource(arg); ok {
|
||||
arg = strings.Join(aliases, ",")
|
||||
}
|
||||
replaced = append(replaced, arg)
|
||||
}
|
||||
return strings.Join(replaced, ",")
|
||||
}
|
||||
|
||||
func hasCombinedTypeArgs(args []string) (bool, error) {
|
||||
hasSlash := 0
|
||||
for _, s := range args {
|
||||
if strings.Contains(s, "/") {
|
||||
hasSlash++
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case hasSlash > 0 && hasSlash == len(args):
|
||||
return true, nil
|
||||
case hasSlash > 0 && hasSlash != len(args):
|
||||
baseCmd := "cmd"
|
||||
if len(os.Args) > 0 {
|
||||
baseCmdSlice := strings.Split(os.Args[0], "/")
|
||||
baseCmd = baseCmdSlice[len(baseCmdSlice)-1]
|
||||
}
|
||||
return true, fmt.Errorf("there is no need to specify a resource type as a separate argument when passing arguments in resource/name form (e.g. '%s get resource/<resource_name>' instead of '%s get resource resource/<resource_name>'", baseCmd, baseCmd)
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize args convert multiple resources to resource tuples, a,b,c d
|
||||
// as a transform to a/d b/d c/d
|
||||
func normalizeMultipleResourcesArgs(args []string) []string {
|
||||
if len(args) >= 2 {
|
||||
resources := []string{}
|
||||
resources = append(resources, SplitResourceArgument(args[0])...)
|
||||
if len(resources) > 1 {
|
||||
names := []string{}
|
||||
names = append(names, args[1:]...)
|
||||
newArgs := []string{}
|
||||
for _, resource := range resources {
|
||||
for _, name := range names {
|
||||
newArgs = append(newArgs, strings.Join([]string{resource, name}, "/"))
|
||||
}
|
||||
}
|
||||
return newArgs
|
||||
}
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
// splitResourceTypeName handles type/name resource formats and returns a resource tuple
|
||||
// (empty or not), whether it successfully found one, and an error
|
||||
func splitResourceTypeName(s string) (resourceTuple, bool, error) {
|
||||
if !strings.Contains(s, "/") {
|
||||
return resourceTuple{}, false, nil
|
||||
}
|
||||
seg := strings.Split(s, "/")
|
||||
if len(seg) != 2 {
|
||||
return resourceTuple{}, false, fmt.Errorf("arguments in resource/name form may not have more than one slash")
|
||||
}
|
||||
resource, name := seg[0], seg[1]
|
||||
if len(resource) == 0 || len(name) == 0 || len(SplitResourceArgument(resource)) != 1 {
|
||||
return resourceTuple{}, false, fmt.Errorf("arguments in resource/name form must have a single resource and name")
|
||||
}
|
||||
return resourceTuple{Resource: resource, Name: name}, true, nil
|
||||
}
|
||||
|
||||
// Flatten will convert any objects with a field named "Items" that is an array of runtime.Object
|
||||
// compatible types into individual entries and give them their own items. The original object
|
||||
// is not passed to any visitors.
|
||||
func (b *Builder) Flatten() *Builder {
|
||||
b.flatten = true
|
||||
return b
|
||||
}
|
||||
|
||||
// Latest will fetch the latest copy of any objects loaded from URLs or files from the server.
|
||||
func (b *Builder) Latest() *Builder {
|
||||
b.latest = true
|
||||
return b
|
||||
}
|
||||
|
||||
// RequireObject ensures that resulting infos have an object set. If false, resulting info may not have an object set.
|
||||
func (b *Builder) RequireObject(require bool) *Builder {
|
||||
b.requireObject = require
|
||||
return b
|
||||
}
|
||||
|
||||
// ContinueOnError will attempt to load and visit as many objects as possible, even if some visits
|
||||
// return errors or some objects cannot be loaded. The default behavior is to terminate after
|
||||
// the first error is returned from a VisitorFunc.
|
||||
func (b *Builder) ContinueOnError() *Builder {
|
||||
b.continueOnError = true
|
||||
return b
|
||||
}
|
||||
|
||||
// SingleResourceType will cause the builder to error if the user specifies more than a single type
|
||||
// of resource.
|
||||
func (b *Builder) SingleResourceType() *Builder {
|
||||
b.singleResourceType = true
|
||||
return b
|
||||
}
|
||||
|
||||
// mappingFor returns the RESTMapping for the Kind referenced by the resource.
|
||||
// prefers a fully specified GroupVersionResource match. If we don't have one match on GroupResource
|
||||
func (b *Builder) mappingFor(resourceArg string) (*meta.RESTMapping, error) {
|
||||
fullySpecifiedGVR, groupResource := unversioned.ParseResourceArg(resourceArg)
|
||||
gvk := unversioned.GroupVersionKind{}
|
||||
if fullySpecifiedGVR != nil {
|
||||
gvk, _ = b.mapper.KindFor(*fullySpecifiedGVR)
|
||||
}
|
||||
if gvk.Empty() {
|
||||
var err error
|
||||
gvk, err = b.mapper.KindFor(groupResource.WithVersion(""))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return b.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
|
||||
}
|
||||
|
||||
func (b *Builder) resourceMappings() ([]*meta.RESTMapping, error) {
|
||||
if len(b.resources) > 1 && b.singleResourceType {
|
||||
return nil, fmt.Errorf("you may only specify a single resource type")
|
||||
}
|
||||
mappings := []*meta.RESTMapping{}
|
||||
for _, r := range b.resources {
|
||||
mapping, err := b.mappingFor(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mappings = append(mappings, mapping)
|
||||
}
|
||||
return mappings, nil
|
||||
}
|
||||
|
||||
func (b *Builder) resourceTupleMappings() (map[string]*meta.RESTMapping, error) {
|
||||
mappings := make(map[string]*meta.RESTMapping)
|
||||
canonical := make(map[string]struct{})
|
||||
for _, r := range b.resourceTuples {
|
||||
if _, ok := mappings[r.Resource]; ok {
|
||||
continue
|
||||
}
|
||||
mapping, err := b.mappingFor(r.Resource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mappings[mapping.Resource] = mapping
|
||||
mappings[r.Resource] = mapping
|
||||
canonical[mapping.Resource] = struct{}{}
|
||||
}
|
||||
if len(canonical) > 1 && b.singleResourceType {
|
||||
return nil, fmt.Errorf("you may only specify a single resource type")
|
||||
}
|
||||
return mappings, nil
|
||||
}
|
||||
|
||||
func (b *Builder) visitorResult() *Result {
|
||||
if len(b.errs) > 0 {
|
||||
return &Result{err: utilerrors.NewAggregate(b.errs)}
|
||||
}
|
||||
|
||||
if b.selectAll {
|
||||
b.selector = labels.Everything()
|
||||
}
|
||||
|
||||
// visit items specified by paths
|
||||
if len(b.paths) != 0 {
|
||||
return b.visitByPaths()
|
||||
}
|
||||
|
||||
// visit selectors
|
||||
if b.selector != nil {
|
||||
return b.visitBySelector()
|
||||
}
|
||||
|
||||
// visit items specified by resource and name
|
||||
if len(b.resourceTuples) != 0 {
|
||||
return b.visitByResource()
|
||||
}
|
||||
|
||||
// visit items specified by name
|
||||
if len(b.names) != 0 {
|
||||
return b.visitByName()
|
||||
}
|
||||
|
||||
if len(b.resources) != 0 {
|
||||
return &Result{err: fmt.Errorf("resource(s) were provided, but no name, label selector, or --all flag specified")}
|
||||
}
|
||||
return &Result{err: missingResourceError}
|
||||
}
|
||||
|
||||
func (b *Builder) visitBySelector() *Result {
|
||||
if len(b.names) != 0 {
|
||||
return &Result{err: fmt.Errorf("name cannot be provided when a selector is specified")}
|
||||
}
|
||||
if len(b.resourceTuples) != 0 {
|
||||
return &Result{err: fmt.Errorf("selectors and the all flag cannot be used when passing resource/name arguments")}
|
||||
}
|
||||
if len(b.resources) == 0 {
|
||||
return &Result{err: fmt.Errorf("at least one resource must be specified to use a selector")}
|
||||
}
|
||||
mappings, err := b.resourceMappings()
|
||||
if err != nil {
|
||||
return &Result{err: err}
|
||||
}
|
||||
|
||||
visitors := []Visitor{}
|
||||
for _, mapping := range mappings {
|
||||
client, err := b.mapper.ClientForMapping(mapping)
|
||||
if err != nil {
|
||||
return &Result{err: err}
|
||||
}
|
||||
selectorNamespace := b.namespace
|
||||
if mapping.Scope.Name() != meta.RESTScopeNameNamespace {
|
||||
selectorNamespace = ""
|
||||
}
|
||||
visitors = append(visitors, NewSelector(client, mapping, selectorNamespace, b.selector, b.export))
|
||||
}
|
||||
if b.continueOnError {
|
||||
return &Result{visitor: EagerVisitorList(visitors), sources: visitors}
|
||||
}
|
||||
return &Result{visitor: VisitorList(visitors), sources: visitors}
|
||||
}
|
||||
|
||||
func (b *Builder) visitByResource() *Result {
|
||||
// if b.singular is false, this could be by default, so double-check length
|
||||
// of resourceTuples to determine if in fact it is singular or not
|
||||
isSingular := b.singular
|
||||
if !isSingular {
|
||||
isSingular = len(b.resourceTuples) == 1
|
||||
}
|
||||
|
||||
if len(b.resources) != 0 {
|
||||
return &Result{singular: isSingular, err: fmt.Errorf("you may not specify individual resources and bulk resources in the same call")}
|
||||
}
|
||||
|
||||
// retrieve one client for each resource
|
||||
mappings, err := b.resourceTupleMappings()
|
||||
if err != nil {
|
||||
return &Result{singular: isSingular, err: err}
|
||||
}
|
||||
clients := make(map[string]RESTClient)
|
||||
for _, mapping := range mappings {
|
||||
s := fmt.Sprintf("%s/%s", mapping.GroupVersionKind.GroupVersion().String(), mapping.Resource)
|
||||
if _, ok := clients[s]; ok {
|
||||
continue
|
||||
}
|
||||
client, err := b.mapper.ClientForMapping(mapping)
|
||||
if err != nil {
|
||||
return &Result{err: err}
|
||||
}
|
||||
clients[s] = client
|
||||
}
|
||||
|
||||
items := []Visitor{}
|
||||
for _, tuple := range b.resourceTuples {
|
||||
mapping, ok := mappings[tuple.Resource]
|
||||
if !ok {
|
||||
return &Result{singular: isSingular, err: fmt.Errorf("resource %q is not recognized: %v", tuple.Resource, mappings)}
|
||||
}
|
||||
s := fmt.Sprintf("%s/%s", mapping.GroupVersionKind.GroupVersion().String(), mapping.Resource)
|
||||
client, ok := clients[s]
|
||||
if !ok {
|
||||
return &Result{singular: isSingular, err: fmt.Errorf("could not find a client for resource %q", tuple.Resource)}
|
||||
}
|
||||
|
||||
selectorNamespace := b.namespace
|
||||
if mapping.Scope.Name() != meta.RESTScopeNameNamespace {
|
||||
selectorNamespace = ""
|
||||
} else {
|
||||
if len(b.namespace) == 0 {
|
||||
errMsg := "namespace may not be empty when retrieving a resource by name"
|
||||
if b.allNamespace {
|
||||
errMsg = "a resource cannot be retrieved by name across all namespaces"
|
||||
}
|
||||
return &Result{singular: isSingular, err: fmt.Errorf(errMsg)}
|
||||
}
|
||||
}
|
||||
|
||||
info := NewInfo(client, mapping, selectorNamespace, tuple.Name, b.export)
|
||||
items = append(items, info)
|
||||
}
|
||||
|
||||
var visitors Visitor
|
||||
if b.continueOnError {
|
||||
visitors = EagerVisitorList(items)
|
||||
} else {
|
||||
visitors = VisitorList(items)
|
||||
}
|
||||
return &Result{singular: isSingular, visitor: visitors, sources: items}
|
||||
}
|
||||
|
||||
func (b *Builder) visitByName() *Result {
|
||||
isSingular := len(b.names) == 1
|
||||
|
||||
if len(b.paths) != 0 {
|
||||
return &Result{singular: isSingular, err: fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify a resource by arguments as well")}
|
||||
}
|
||||
if len(b.resources) == 0 {
|
||||
return &Result{singular: isSingular, err: fmt.Errorf("you must provide a resource and a resource name together")}
|
||||
}
|
||||
if len(b.resources) > 1 {
|
||||
return &Result{singular: isSingular, err: fmt.Errorf("you must specify only one resource")}
|
||||
}
|
||||
|
||||
mappings, err := b.resourceMappings()
|
||||
if err != nil {
|
||||
return &Result{singular: isSingular, err: err}
|
||||
}
|
||||
mapping := mappings[0]
|
||||
|
||||
client, err := b.mapper.ClientForMapping(mapping)
|
||||
if err != nil {
|
||||
return &Result{err: err}
|
||||
}
|
||||
|
||||
selectorNamespace := b.namespace
|
||||
if mapping.Scope.Name() != meta.RESTScopeNameNamespace {
|
||||
selectorNamespace = ""
|
||||
} else {
|
||||
if len(b.namespace) == 0 {
|
||||
errMsg := "namespace may not be empty when retrieving a resource by name"
|
||||
if b.allNamespace {
|
||||
errMsg = "a resource cannot be retrieved by name across all namespaces"
|
||||
}
|
||||
return &Result{singular: isSingular, err: fmt.Errorf(errMsg)}
|
||||
}
|
||||
}
|
||||
|
||||
visitors := []Visitor{}
|
||||
for _, name := range b.names {
|
||||
info := NewInfo(client, mapping, selectorNamespace, name, b.export)
|
||||
visitors = append(visitors, info)
|
||||
}
|
||||
return &Result{singular: isSingular, visitor: VisitorList(visitors), sources: visitors}
|
||||
}
|
||||
|
||||
func (b *Builder) visitByPaths() *Result {
|
||||
singular := !b.dir && !b.stream && len(b.paths) == 1
|
||||
if len(b.resources) != 0 {
|
||||
return &Result{singular: singular, err: fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify resource arguments as well")}
|
||||
}
|
||||
if len(b.names) != 0 {
|
||||
return &Result{err: fmt.Errorf("name cannot be provided when a path is specified")}
|
||||
}
|
||||
if len(b.resourceTuples) != 0 {
|
||||
return &Result{err: fmt.Errorf("resource/name arguments cannot be provided when a path is specified")}
|
||||
}
|
||||
|
||||
var visitors Visitor
|
||||
if b.continueOnError {
|
||||
visitors = EagerVisitorList(b.paths)
|
||||
} else {
|
||||
visitors = VisitorList(b.paths)
|
||||
}
|
||||
|
||||
// only items from disk can be refetched
|
||||
if b.latest {
|
||||
// must flatten lists prior to fetching
|
||||
if b.flatten {
|
||||
visitors = NewFlattenListVisitor(visitors, b.mapper)
|
||||
}
|
||||
// must set namespace prior to fetching
|
||||
if b.defaultNamespace {
|
||||
visitors = NewDecoratedVisitor(visitors, SetNamespace(b.namespace))
|
||||
}
|
||||
visitors = NewDecoratedVisitor(visitors, RetrieveLatest)
|
||||
}
|
||||
if b.selector != nil {
|
||||
visitors = NewFilteredVisitor(visitors, FilterBySelector(b.selector))
|
||||
}
|
||||
return &Result{singular: singular, visitor: visitors, sources: b.paths}
|
||||
}
|
||||
|
||||
// Do returns a Result object with a Visitor for the resources identified by the Builder.
|
||||
// The visitor will respect the error behavior specified by ContinueOnError. Note that stream
|
||||
// inputs are consumed by the first execution - use Infos() or Object() on the Result to capture a list
|
||||
// for further iteration.
|
||||
func (b *Builder) Do() *Result {
|
||||
r := b.visitorResult()
|
||||
if r.err != nil {
|
||||
return r
|
||||
}
|
||||
if b.flatten {
|
||||
r.visitor = NewFlattenListVisitor(r.visitor, b.mapper)
|
||||
}
|
||||
helpers := []VisitorFunc{}
|
||||
if b.defaultNamespace {
|
||||
helpers = append(helpers, SetNamespace(b.namespace))
|
||||
}
|
||||
if b.requireNamespace {
|
||||
helpers = append(helpers, RequireNamespace(b.namespace))
|
||||
}
|
||||
helpers = append(helpers, FilterNamespace)
|
||||
if b.requireObject {
|
||||
helpers = append(helpers, RetrieveLazy)
|
||||
}
|
||||
r.visitor = NewDecoratedVisitor(r.visitor, helpers...)
|
||||
if b.continueOnError {
|
||||
r.visitor = ContinueOnErrorVisitor{r.visitor}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// SplitResourceArgument splits the argument with commas and returns unique
|
||||
// strings in the original order.
|
||||
func SplitResourceArgument(arg string) []string {
|
||||
out := []string{}
|
||||
set := sets.NewString()
|
||||
for _, s := range strings.Split(arg, ",") {
|
||||
if set.Has(s) {
|
||||
continue
|
||||
}
|
||||
set.Insert(s)
|
||||
out = append(out, s)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// HasNames returns true if the provided args contain resource names
|
||||
func HasNames(args []string) (bool, error) {
|
||||
args = normalizeMultipleResourcesArgs(args)
|
||||
hasCombinedTypes, err := hasCombinedTypeArgs(args)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return hasCombinedTypes || len(args) > 1, nil
|
||||
}
|
||||
24
vendor/k8s.io/kubernetes/pkg/kubectl/resource/doc.go
generated
vendored
24
vendor/k8s.io/kubernetes/pkg/kubectl/resource/doc.go
generated
vendored
|
|
@ -1,24 +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 resource assists clients in dealing with RESTful objects that match the
|
||||
// Kubernetes API conventions. The Helper object provides simple CRUD operations
|
||||
// on resources. The Visitor interface makes it easy to deal with multiple resources
|
||||
// in bulk for retrieval and operation. The Builder object simplifies converting
|
||||
// standard command line arguments and parameters into a Visitor that can iterate
|
||||
// over all of the identified resources, whether on the server or on the local
|
||||
// filesystem.
|
||||
package resource
|
||||
166
vendor/k8s.io/kubernetes/pkg/kubectl/resource/helper.go
generated
vendored
166
vendor/k8s.io/kubernetes/pkg/kubectl/resource/helper.go
generated
vendored
|
|
@ -1,166 +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 resource
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// Helper provides methods for retrieving or mutating a RESTful
|
||||
// resource.
|
||||
type Helper struct {
|
||||
// The name of this resource as the server would recognize it
|
||||
Resource string
|
||||
// A RESTClient capable of mutating this resource.
|
||||
RESTClient RESTClient
|
||||
// An interface for reading or writing the resource version of this
|
||||
// type.
|
||||
Versioner runtime.ResourceVersioner
|
||||
// True if the resource type is scoped to namespaces
|
||||
NamespaceScoped bool
|
||||
}
|
||||
|
||||
// NewHelper creates a Helper from a ResourceMapping
|
||||
func NewHelper(client RESTClient, mapping *meta.RESTMapping) *Helper {
|
||||
return &Helper{
|
||||
Resource: mapping.Resource,
|
||||
RESTClient: client,
|
||||
Versioner: mapping.MetadataAccessor,
|
||||
NamespaceScoped: mapping.Scope.Name() == meta.RESTScopeNameNamespace,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Helper) Get(namespace, name string, export bool) (runtime.Object, error) {
|
||||
req := m.RESTClient.Get().
|
||||
NamespaceIfScoped(namespace, m.NamespaceScoped).
|
||||
Resource(m.Resource).
|
||||
Name(name)
|
||||
if export {
|
||||
req.Param("export", strconv.FormatBool(export))
|
||||
}
|
||||
return req.Do().Get()
|
||||
}
|
||||
|
||||
// TODO: add field selector
|
||||
func (m *Helper) List(namespace, apiVersion string, selector labels.Selector, export bool) (runtime.Object, error) {
|
||||
req := m.RESTClient.Get().
|
||||
NamespaceIfScoped(namespace, m.NamespaceScoped).
|
||||
Resource(m.Resource).
|
||||
LabelsSelectorParam(selector)
|
||||
if export {
|
||||
req.Param("export", strconv.FormatBool(export))
|
||||
}
|
||||
return req.Do().Get()
|
||||
}
|
||||
|
||||
func (m *Helper) Watch(namespace, resourceVersion, apiVersion string, labelSelector labels.Selector) (watch.Interface, error) {
|
||||
return m.RESTClient.Get().
|
||||
Prefix("watch").
|
||||
NamespaceIfScoped(namespace, m.NamespaceScoped).
|
||||
Resource(m.Resource).
|
||||
Param("resourceVersion", resourceVersion).
|
||||
LabelsSelectorParam(labelSelector).
|
||||
Watch()
|
||||
}
|
||||
|
||||
func (m *Helper) WatchSingle(namespace, name, resourceVersion string) (watch.Interface, error) {
|
||||
return m.RESTClient.Get().
|
||||
Prefix("watch").
|
||||
NamespaceIfScoped(namespace, m.NamespaceScoped).
|
||||
Resource(m.Resource).
|
||||
Name(name).
|
||||
Param("resourceVersion", resourceVersion).
|
||||
Watch()
|
||||
}
|
||||
|
||||
func (m *Helper) Delete(namespace, name string) error {
|
||||
return m.RESTClient.Delete().
|
||||
NamespaceIfScoped(namespace, m.NamespaceScoped).
|
||||
Resource(m.Resource).
|
||||
Name(name).
|
||||
Do().
|
||||
Error()
|
||||
}
|
||||
|
||||
func (m *Helper) Create(namespace string, modify bool, obj runtime.Object) (runtime.Object, error) {
|
||||
if modify {
|
||||
// Attempt to version the object based on client logic.
|
||||
version, err := m.Versioner.ResourceVersion(obj)
|
||||
if err != nil {
|
||||
// We don't know how to clear the version on this object, so send it to the server as is
|
||||
return m.createResource(m.RESTClient, m.Resource, namespace, obj)
|
||||
}
|
||||
if version != "" {
|
||||
if err := m.Versioner.SetResourceVersion(obj, ""); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m.createResource(m.RESTClient, m.Resource, namespace, obj)
|
||||
}
|
||||
|
||||
func (m *Helper) createResource(c RESTClient, resource, namespace string, obj runtime.Object) (runtime.Object, error) {
|
||||
return c.Post().NamespaceIfScoped(namespace, m.NamespaceScoped).Resource(resource).Body(obj).Do().Get()
|
||||
}
|
||||
func (m *Helper) Patch(namespace, name string, pt api.PatchType, data []byte) (runtime.Object, error) {
|
||||
return m.RESTClient.Patch(pt).
|
||||
NamespaceIfScoped(namespace, m.NamespaceScoped).
|
||||
Resource(m.Resource).
|
||||
Name(name).
|
||||
Body(data).
|
||||
Do().
|
||||
Get()
|
||||
}
|
||||
|
||||
func (m *Helper) Replace(namespace, name string, overwrite bool, obj runtime.Object) (runtime.Object, error) {
|
||||
c := m.RESTClient
|
||||
|
||||
// Attempt to version the object based on client logic.
|
||||
version, err := m.Versioner.ResourceVersion(obj)
|
||||
if err != nil {
|
||||
// We don't know how to version this object, so send it to the server as is
|
||||
return m.replaceResource(c, m.Resource, namespace, name, obj)
|
||||
}
|
||||
if version == "" && overwrite {
|
||||
// Retrieve the current version of the object to overwrite the server object
|
||||
serverObj, err := c.Get().NamespaceIfScoped(namespace, m.NamespaceScoped).Resource(m.Resource).Name(name).Do().Get()
|
||||
if err != nil {
|
||||
// The object does not exist, but we want it to be created
|
||||
return m.replaceResource(c, m.Resource, namespace, name, obj)
|
||||
}
|
||||
serverVersion, err := m.Versioner.ResourceVersion(serverObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := m.Versioner.SetResourceVersion(obj, serverVersion); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return m.replaceResource(c, m.Resource, namespace, name, obj)
|
||||
}
|
||||
|
||||
func (m *Helper) replaceResource(c RESTClient, resource, namespace, name string, obj runtime.Object) (runtime.Object, error) {
|
||||
return c.Put().NamespaceIfScoped(namespace, m.NamespaceScoped).Resource(resource).Name(name).Body(obj).Do().Get()
|
||||
}
|
||||
46
vendor/k8s.io/kubernetes/pkg/kubectl/resource/interfaces.go
generated
vendored
46
vendor/k8s.io/kubernetes/pkg/kubectl/resource/interfaces.go
generated
vendored
|
|
@ -1,46 +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 resource
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
client "k8s.io/kubernetes/pkg/client/restclient"
|
||||
)
|
||||
|
||||
// RESTClient is a client helper for dealing with RESTful resources
|
||||
// in a generic way.
|
||||
type RESTClient interface {
|
||||
Get() *client.Request
|
||||
Post() *client.Request
|
||||
Patch(api.PatchType) *client.Request
|
||||
Delete() *client.Request
|
||||
Put() *client.Request
|
||||
}
|
||||
|
||||
// ClientMapper abstracts retrieving a Client for mapped objects.
|
||||
type ClientMapper interface {
|
||||
ClientForMapping(mapping *meta.RESTMapping) (RESTClient, error)
|
||||
}
|
||||
|
||||
// ClientMapperFunc implements ClientMapper for a function
|
||||
type ClientMapperFunc func(mapping *meta.RESTMapping) (RESTClient, error)
|
||||
|
||||
// ClientForMapping implements ClientMapper
|
||||
func (f ClientMapperFunc) ClientForMapping(mapping *meta.RESTMapping) (RESTClient, error) {
|
||||
return f(mapping)
|
||||
}
|
||||
154
vendor/k8s.io/kubernetes/pkg/kubectl/resource/mapper.go
generated
vendored
154
vendor/k8s.io/kubernetes/pkg/kubectl/resource/mapper.go
generated
vendored
|
|
@ -1,154 +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 resource
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
// DisabledClientForMapping allows callers to avoid allowing remote calls when handling
|
||||
// resources.
|
||||
type DisabledClientForMapping struct {
|
||||
ClientMapper
|
||||
}
|
||||
|
||||
func (f DisabledClientForMapping) ClientForMapping(mapping *meta.RESTMapping) (RESTClient, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Mapper is a convenience struct for holding references to the three interfaces
|
||||
// needed to create Info for arbitrary objects.
|
||||
type Mapper struct {
|
||||
runtime.ObjectTyper
|
||||
meta.RESTMapper
|
||||
ClientMapper
|
||||
runtime.Decoder
|
||||
}
|
||||
|
||||
// InfoForData creates an Info object for the given data. An error is returned
|
||||
// if any of the decoding or client lookup steps fail. Name and namespace will be
|
||||
// set into Info if the mapping's MetadataAccessor can retrieve them.
|
||||
func (m *Mapper) InfoForData(data []byte, source string) (*Info, error) {
|
||||
versions := &runtime.VersionedObjects{}
|
||||
_, gvk, err := m.Decode(data, nil, versions)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode %q: %v", source, err)
|
||||
}
|
||||
|
||||
obj, versioned := versions.Last(), versions.First()
|
||||
mapping, err := m.RESTMapping(gvk.GroupKind(), gvk.Version)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to recognize %q: %v", source, err)
|
||||
}
|
||||
|
||||
client, err := m.ClientForMapping(mapping)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to connect to a server to handle %q: %v", mapping.Resource, err)
|
||||
}
|
||||
|
||||
name, _ := mapping.MetadataAccessor.Name(obj)
|
||||
namespace, _ := mapping.MetadataAccessor.Namespace(obj)
|
||||
resourceVersion, _ := mapping.MetadataAccessor.ResourceVersion(obj)
|
||||
|
||||
return &Info{
|
||||
Mapping: mapping,
|
||||
Client: client,
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
Source: source,
|
||||
VersionedObject: versioned,
|
||||
Object: obj,
|
||||
ResourceVersion: resourceVersion,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// InfoForObject creates an Info object for the given Object. An error is returned
|
||||
// if the object cannot be introspected. Name and namespace will be set into Info
|
||||
// if the mapping's MetadataAccessor can retrieve them.
|
||||
func (m *Mapper) InfoForObject(obj runtime.Object, preferredGVKs []unversioned.GroupVersionKind) (*Info, error) {
|
||||
groupVersionKinds, _, err := m.ObjectKinds(obj)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get type info from the object %q: %v", reflect.TypeOf(obj), err)
|
||||
}
|
||||
|
||||
groupVersionKind := groupVersionKinds[0]
|
||||
if len(groupVersionKinds) > 1 && len(preferredGVKs) > 0 {
|
||||
groupVersionKind = preferredObjectKind(groupVersionKinds, preferredGVKs)
|
||||
}
|
||||
|
||||
mapping, err := m.RESTMapping(groupVersionKind.GroupKind(), groupVersionKind.Version)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to recognize %v: %v", groupVersionKind, err)
|
||||
}
|
||||
client, err := m.ClientForMapping(mapping)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to connect to a server to handle %q: %v", mapping.Resource, err)
|
||||
}
|
||||
name, _ := mapping.MetadataAccessor.Name(obj)
|
||||
namespace, _ := mapping.MetadataAccessor.Namespace(obj)
|
||||
resourceVersion, _ := mapping.MetadataAccessor.ResourceVersion(obj)
|
||||
return &Info{
|
||||
Mapping: mapping,
|
||||
Client: client,
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
|
||||
Object: obj,
|
||||
ResourceVersion: resourceVersion,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// preferredObjectKind picks the possibility that most closely matches the priority list in this order:
|
||||
// GroupVersionKind matches (exact match)
|
||||
// GroupKind matches
|
||||
// Group matches
|
||||
func preferredObjectKind(possibilities []unversioned.GroupVersionKind, preferences []unversioned.GroupVersionKind) unversioned.GroupVersionKind {
|
||||
// Exact match
|
||||
for _, priority := range preferences {
|
||||
for _, possibility := range possibilities {
|
||||
if possibility == priority {
|
||||
return possibility
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GroupKind match
|
||||
for _, priority := range preferences {
|
||||
for _, possibility := range possibilities {
|
||||
if possibility.GroupKind() == priority.GroupKind() {
|
||||
return possibility
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Group match
|
||||
for _, priority := range preferences {
|
||||
for _, possibility := range possibilities {
|
||||
if possibility.Group == priority.Group {
|
||||
return possibility
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Just pick the first
|
||||
return possibilities[0]
|
||||
}
|
||||
291
vendor/k8s.io/kubernetes/pkg/kubectl/resource/result.go
generated
vendored
291
vendor/k8s.io/kubernetes/pkg/kubectl/resource/result.go
generated
vendored
|
|
@ -1,291 +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 resource
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
utilerrors "k8s.io/kubernetes/pkg/util/errors"
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// ErrMatchFunc can be used to filter errors that may not be true failures.
|
||||
type ErrMatchFunc func(error) bool
|
||||
|
||||
// Result contains helper methods for dealing with the outcome of a Builder.
|
||||
type Result struct {
|
||||
err error
|
||||
visitor Visitor
|
||||
|
||||
sources []Visitor
|
||||
singular bool
|
||||
|
||||
ignoreErrors []utilerrors.Matcher
|
||||
|
||||
// populated by a call to Infos
|
||||
info []*Info
|
||||
}
|
||||
|
||||
// IgnoreErrors will filter errors that occur when by visiting the result
|
||||
// (but not errors that occur by creating the result in the first place),
|
||||
// eliminating any that match fns. This is best used in combination with
|
||||
// Builder.ContinueOnError(), where the visitors accumulate errors and return
|
||||
// them after visiting as a slice of errors. If no errors remain after
|
||||
// filtering, the various visitor methods on Result will return nil for
|
||||
// err.
|
||||
func (r *Result) IgnoreErrors(fns ...ErrMatchFunc) *Result {
|
||||
for _, fn := range fns {
|
||||
r.ignoreErrors = append(r.ignoreErrors, utilerrors.Matcher(fn))
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Err returns one or more errors (via a util.ErrorList) that occurred prior
|
||||
// to visiting the elements in the visitor. To see all errors including those
|
||||
// that occur during visitation, invoke Infos().
|
||||
func (r *Result) Err() error {
|
||||
return r.err
|
||||
}
|
||||
|
||||
// Visit implements the Visitor interface on the items described in the Builder.
|
||||
// Note that some visitor sources are not traversable more than once, or may
|
||||
// return different results. If you wish to operate on the same set of resources
|
||||
// multiple times, use the Infos() method.
|
||||
func (r *Result) Visit(fn VisitorFunc) error {
|
||||
if r.err != nil {
|
||||
return r.err
|
||||
}
|
||||
err := r.visitor.Visit(fn)
|
||||
return utilerrors.FilterOut(err, r.ignoreErrors...)
|
||||
}
|
||||
|
||||
// IntoSingular sets the provided boolean pointer to true if the Builder input
|
||||
// reflected a single item, or multiple.
|
||||
func (r *Result) IntoSingular(b *bool) *Result {
|
||||
*b = r.singular
|
||||
return r
|
||||
}
|
||||
|
||||
// Infos returns an array of all of the resource infos retrieved via traversal.
|
||||
// Will attempt to traverse the entire set of visitors only once, and will return
|
||||
// a cached list on subsequent calls.
|
||||
func (r *Result) Infos() ([]*Info, error) {
|
||||
if r.err != nil {
|
||||
return nil, r.err
|
||||
}
|
||||
if r.info != nil {
|
||||
return r.info, nil
|
||||
}
|
||||
|
||||
infos := []*Info{}
|
||||
err := r.visitor.Visit(func(info *Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
infos = append(infos, info)
|
||||
return nil
|
||||
})
|
||||
err = utilerrors.FilterOut(err, r.ignoreErrors...)
|
||||
|
||||
r.info, r.err = infos, err
|
||||
return infos, err
|
||||
}
|
||||
|
||||
// Object returns a single object representing the output of a single visit to all
|
||||
// found resources. If the Builder was a singular context (expected to return a
|
||||
// single resource by user input) and only a single resource was found, the resource
|
||||
// will be returned as is. Otherwise, the returned resources will be part of an
|
||||
// api.List. The ResourceVersion of the api.List will be set only if it is identical
|
||||
// across all infos returned.
|
||||
func (r *Result) Object() (runtime.Object, error) {
|
||||
infos, err := r.Infos()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
versions := sets.String{}
|
||||
objects := []runtime.Object{}
|
||||
for _, info := range infos {
|
||||
if info.Object != nil {
|
||||
objects = append(objects, info.Object)
|
||||
versions.Insert(info.ResourceVersion)
|
||||
}
|
||||
}
|
||||
|
||||
if len(objects) == 1 {
|
||||
if r.singular {
|
||||
return objects[0], nil
|
||||
}
|
||||
// if the item is a list already, don't create another list
|
||||
if meta.IsListType(objects[0]) {
|
||||
return objects[0], nil
|
||||
}
|
||||
}
|
||||
|
||||
version := ""
|
||||
if len(versions) == 1 {
|
||||
version = versions.List()[0]
|
||||
}
|
||||
return &api.List{
|
||||
ListMeta: unversioned.ListMeta{
|
||||
ResourceVersion: version,
|
||||
},
|
||||
Items: objects,
|
||||
}, err
|
||||
}
|
||||
|
||||
// ResourceMapping returns a single meta.RESTMapping representing the
|
||||
// resources located by the builder, or an error if more than one
|
||||
// mapping was found.
|
||||
func (r *Result) ResourceMapping() (*meta.RESTMapping, error) {
|
||||
if r.err != nil {
|
||||
return nil, r.err
|
||||
}
|
||||
mappings := map[string]*meta.RESTMapping{}
|
||||
for i := range r.sources {
|
||||
m, ok := r.sources[i].(ResourceMapping)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("a resource mapping could not be loaded from %v", reflect.TypeOf(r.sources[i]))
|
||||
}
|
||||
mapping := m.ResourceMapping()
|
||||
mappings[mapping.Resource] = mapping
|
||||
}
|
||||
if len(mappings) != 1 {
|
||||
return nil, fmt.Errorf("expected only a single resource type")
|
||||
}
|
||||
for _, mapping := range mappings {
|
||||
return mapping, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Watch retrieves changes that occur on the server to the specified resource.
|
||||
// It currently supports watching a single source - if the resource source
|
||||
// (selectors or pure types) can be watched, they will be, otherwise the list
|
||||
// will be visited (equivalent to the Infos() call) and if there is a single
|
||||
// resource present, it will be watched, otherwise an error will be returned.
|
||||
func (r *Result) Watch(resourceVersion string) (watch.Interface, error) {
|
||||
if r.err != nil {
|
||||
return nil, r.err
|
||||
}
|
||||
if len(r.sources) != 1 {
|
||||
return nil, fmt.Errorf("you may only watch a single resource or type of resource at a time")
|
||||
}
|
||||
w, ok := r.sources[0].(Watchable)
|
||||
if !ok {
|
||||
info, err := r.Infos()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(info) != 1 {
|
||||
return nil, fmt.Errorf("watch is only supported on individual resources and resource collections - %d resources were found", len(info))
|
||||
}
|
||||
return info[0].Watch(resourceVersion)
|
||||
}
|
||||
return w.Watch(resourceVersion)
|
||||
}
|
||||
|
||||
// AsVersionedObject converts a list of infos into a single object - either a List containing
|
||||
// the objects as children, or if only a single Object is present, as that object. The provided
|
||||
// version will be preferred as the conversion target, but the Object's mapping version will be
|
||||
// used if that version is not present.
|
||||
func AsVersionedObject(infos []*Info, forceList bool, version unversioned.GroupVersion, encoder runtime.Encoder) (runtime.Object, error) {
|
||||
objects, err := AsVersionedObjects(infos, version, encoder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var object runtime.Object
|
||||
if len(objects) == 1 && !forceList {
|
||||
object = objects[0]
|
||||
} else {
|
||||
object = &api.List{Items: objects}
|
||||
converted, err := TryConvert(api.Scheme, object, version, registered.GroupOrDie(api.GroupName).GroupVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
object = converted
|
||||
}
|
||||
return object, nil
|
||||
}
|
||||
|
||||
// AsVersionedObjects converts a list of infos into versioned objects. The provided
|
||||
// version will be preferred as the conversion target, but the Object's mapping version will be
|
||||
// used if that version is not present.
|
||||
func AsVersionedObjects(infos []*Info, version unversioned.GroupVersion, encoder runtime.Encoder) ([]runtime.Object, error) {
|
||||
objects := []runtime.Object{}
|
||||
for _, info := range infos {
|
||||
if info.Object == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// TODO: use info.VersionedObject as the value?
|
||||
switch obj := info.Object.(type) {
|
||||
case *extensions.ThirdPartyResourceData:
|
||||
objects = append(objects, &runtime.Unknown{Raw: obj.Data})
|
||||
continue
|
||||
}
|
||||
|
||||
// objects that are not part of api.Scheme must be converted to JSON
|
||||
// TODO: convert to map[string]interface{}, attach to runtime.Unknown?
|
||||
if !version.Empty() {
|
||||
if _, _, err := api.Scheme.ObjectKinds(info.Object); runtime.IsNotRegisteredError(err) {
|
||||
// TODO: ideally this would encode to version, but we don't expose multiple codecs here.
|
||||
data, err := runtime.Encode(encoder, info.Object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO: Set ContentEncoding and ContentType.
|
||||
objects = append(objects, &runtime.Unknown{Raw: data})
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
converted, err := TryConvert(info.Mapping.ObjectConvertor, info.Object, version, info.Mapping.GroupVersionKind.GroupVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objects = append(objects, converted)
|
||||
}
|
||||
return objects, nil
|
||||
}
|
||||
|
||||
// TryConvert attempts to convert the given object to the provided versions in order. This function assumes
|
||||
// the object is in internal version.
|
||||
func TryConvert(converter runtime.ObjectConvertor, object runtime.Object, versions ...unversioned.GroupVersion) (runtime.Object, error) {
|
||||
var last error
|
||||
for _, version := range versions {
|
||||
if version.Empty() {
|
||||
return object, nil
|
||||
}
|
||||
obj, err := converter.ConvertToVersion(object, version)
|
||||
if err != nil {
|
||||
last = err
|
||||
continue
|
||||
}
|
||||
return obj, nil
|
||||
}
|
||||
return nil, last
|
||||
}
|
||||
90
vendor/k8s.io/kubernetes/pkg/kubectl/resource/selector.go
generated
vendored
90
vendor/k8s.io/kubernetes/pkg/kubectl/resource/selector.go
generated
vendored
|
|
@ -1,90 +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 resource
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/errors"
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// Selector is a Visitor for resources that match a label selector.
|
||||
type Selector struct {
|
||||
Client RESTClient
|
||||
Mapping *meta.RESTMapping
|
||||
Namespace string
|
||||
Selector labels.Selector
|
||||
Export bool
|
||||
}
|
||||
|
||||
// NewSelector creates a resource selector which hides details of getting items by their label selector.
|
||||
func NewSelector(client RESTClient, mapping *meta.RESTMapping, namespace string, selector labels.Selector, export bool) *Selector {
|
||||
return &Selector{
|
||||
Client: client,
|
||||
Mapping: mapping,
|
||||
Namespace: namespace,
|
||||
Selector: selector,
|
||||
Export: export,
|
||||
}
|
||||
}
|
||||
|
||||
// Visit implements Visitor
|
||||
func (r *Selector) Visit(fn VisitorFunc) error {
|
||||
list, err := NewHelper(r.Client, r.Mapping).List(r.Namespace, r.ResourceMapping().GroupVersionKind.GroupVersion().String(), r.Selector, r.Export)
|
||||
if err != nil {
|
||||
if errors.IsBadRequest(err) || errors.IsNotFound(err) {
|
||||
if se, ok := err.(*errors.StatusError); ok {
|
||||
// modify the message without hiding this is an API error
|
||||
if r.Selector.Empty() {
|
||||
se.ErrStatus.Message = fmt.Sprintf("Unable to list %q: %v", r.Mapping.Resource, se.ErrStatus.Message)
|
||||
} else {
|
||||
se.ErrStatus.Message = fmt.Sprintf("Unable to find %q that match the selector %q: %v", r.Mapping.Resource, r.Selector, se.ErrStatus.Message)
|
||||
}
|
||||
return se
|
||||
}
|
||||
if r.Selector.Empty() {
|
||||
return fmt.Errorf("Unable to list %q: %v", r.Mapping.Resource, err)
|
||||
} else {
|
||||
return fmt.Errorf("Unable to find %q that match the selector %q: %v", r.Mapping.Resource, r.Selector, err)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
accessor := r.Mapping.MetadataAccessor
|
||||
resourceVersion, _ := accessor.ResourceVersion(list)
|
||||
info := &Info{
|
||||
Client: r.Client,
|
||||
Mapping: r.Mapping,
|
||||
Namespace: r.Namespace,
|
||||
|
||||
Object: list,
|
||||
ResourceVersion: resourceVersion,
|
||||
}
|
||||
return fn(info, nil)
|
||||
}
|
||||
|
||||
func (r *Selector) Watch(resourceVersion string) (watch.Interface, error) {
|
||||
return NewHelper(r.Client, r.Mapping).Watch(r.Namespace, resourceVersion, r.ResourceMapping().GroupVersionKind.GroupVersion().String(), r.Selector)
|
||||
}
|
||||
|
||||
// ResourceMapping returns the mapping for this resource and implements ResourceMapping
|
||||
func (r *Selector) ResourceMapping() *meta.RESTMapping {
|
||||
return r.Mapping
|
||||
}
|
||||
690
vendor/k8s.io/kubernetes/pkg/kubectl/resource/visitor.go
generated
vendored
690
vendor/k8s.io/kubernetes/pkg/kubectl/resource/visitor.go
generated
vendored
|
|
@ -1,690 +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 resource
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/errors"
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/api/validation"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
utilerrors "k8s.io/kubernetes/pkg/util/errors"
|
||||
"k8s.io/kubernetes/pkg/util/yaml"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
const (
|
||||
constSTDINstr string = "STDIN"
|
||||
stopValidateMessage = "if you choose to ignore these errors, turn validation off with --validate=false"
|
||||
)
|
||||
|
||||
// Visitor lets clients walk a list of resources.
|
||||
type Visitor interface {
|
||||
Visit(VisitorFunc) error
|
||||
}
|
||||
|
||||
// VisitorFunc implements the Visitor interface for a matching function.
|
||||
// If there was a problem walking a list of resources, the incoming error
|
||||
// will describe the problem and the function can decide how to handle that error.
|
||||
// A nil returned indicates to accept an error to continue loops even when errors happen.
|
||||
// This is useful for ignoring certain kinds of errors or aggregating errors in some way.
|
||||
type VisitorFunc func(*Info, error) error
|
||||
|
||||
// Watchable describes a resource that can be watched for changes that occur on the server,
|
||||
// beginning after the provided resource version.
|
||||
type Watchable interface {
|
||||
Watch(resourceVersion string) (watch.Interface, error)
|
||||
}
|
||||
|
||||
// ResourceMapping allows an object to return the resource mapping associated with
|
||||
// the resource or resources it represents.
|
||||
type ResourceMapping interface {
|
||||
ResourceMapping() *meta.RESTMapping
|
||||
}
|
||||
|
||||
// Info contains temporary info to execute a REST call, or show the results
|
||||
// of an already completed REST call.
|
||||
type Info struct {
|
||||
Client RESTClient
|
||||
Mapping *meta.RESTMapping
|
||||
Namespace string
|
||||
Name string
|
||||
|
||||
// Optional, Source is the filename or URL to template file (.json or .yaml),
|
||||
// or stdin to use to handle the resource
|
||||
Source string
|
||||
// Optional, this is the provided object in a versioned type before defaulting
|
||||
// and conversions into its corresponding internal type. This is useful for
|
||||
// reflecting on user intent which may be lost after defaulting and conversions.
|
||||
VersionedObject interface{}
|
||||
// Optional, this is the most recent value returned by the server if available
|
||||
runtime.Object
|
||||
// Optional, this is the most recent resource version the server knows about for
|
||||
// this type of resource. It may not match the resource version of the object,
|
||||
// but if set it should be equal to or newer than the resource version of the
|
||||
// object (however the server defines resource version).
|
||||
ResourceVersion string
|
||||
// Optional, should this resource be exported, stripped of cluster-specific and instance specific fields
|
||||
Export bool
|
||||
}
|
||||
|
||||
// NewInfo returns a new info object
|
||||
func NewInfo(client RESTClient, mapping *meta.RESTMapping, namespace, name string, export bool) *Info {
|
||||
return &Info{
|
||||
Client: client,
|
||||
Mapping: mapping,
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
Export: export,
|
||||
}
|
||||
}
|
||||
|
||||
// Visit implements Visitor
|
||||
func (i *Info) Visit(fn VisitorFunc) error {
|
||||
return fn(i, nil)
|
||||
}
|
||||
|
||||
// Get retrieves the object from the Namespace and Name fields
|
||||
func (i *Info) Get() (err error) {
|
||||
obj, err := NewHelper(i.Client, i.Mapping).Get(i.Namespace, i.Name, i.Export)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) && len(i.Namespace) > 0 && i.Namespace != api.NamespaceDefault && i.Namespace != api.NamespaceAll {
|
||||
err2 := i.Client.Get().AbsPath("api", "v1", "namespaces", i.Namespace).Do().Error()
|
||||
if err2 != nil && errors.IsNotFound(err2) {
|
||||
return err2
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
i.Object = obj
|
||||
i.ResourceVersion, _ = i.Mapping.MetadataAccessor.ResourceVersion(obj)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Refresh updates the object with another object. If ignoreError is set
|
||||
// the Object will be updated even if name, namespace, or resourceVersion
|
||||
// attributes cannot be loaded from the object.
|
||||
func (i *Info) Refresh(obj runtime.Object, ignoreError bool) error {
|
||||
name, err := i.Mapping.MetadataAccessor.Name(obj)
|
||||
if err != nil {
|
||||
if !ignoreError {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
i.Name = name
|
||||
}
|
||||
namespace, err := i.Mapping.MetadataAccessor.Namespace(obj)
|
||||
if err != nil {
|
||||
if !ignoreError {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
i.Namespace = namespace
|
||||
}
|
||||
version, err := i.Mapping.MetadataAccessor.ResourceVersion(obj)
|
||||
if err != nil {
|
||||
if !ignoreError {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
i.ResourceVersion = version
|
||||
}
|
||||
i.Object = obj
|
||||
return nil
|
||||
}
|
||||
|
||||
// Namespaced returns true if the object belongs to a namespace
|
||||
func (i *Info) Namespaced() bool {
|
||||
return i.Mapping != nil && i.Mapping.Scope.Name() == meta.RESTScopeNameNamespace
|
||||
}
|
||||
|
||||
// Watch returns server changes to this object after it was retrieved.
|
||||
func (i *Info) Watch(resourceVersion string) (watch.Interface, error) {
|
||||
return NewHelper(i.Client, i.Mapping).WatchSingle(i.Namespace, i.Name, resourceVersion)
|
||||
}
|
||||
|
||||
// ResourceMapping returns the mapping for this resource and implements ResourceMapping
|
||||
func (i *Info) ResourceMapping() *meta.RESTMapping {
|
||||
return i.Mapping
|
||||
}
|
||||
|
||||
// VisitorList implements Visit for the sub visitors it contains. The first error
|
||||
// returned from a child Visitor will terminate iteration.
|
||||
type VisitorList []Visitor
|
||||
|
||||
// Visit implements Visitor
|
||||
func (l VisitorList) Visit(fn VisitorFunc) error {
|
||||
for i := range l {
|
||||
if err := l[i].Visit(fn); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EagerVisitorList implements Visit for the sub visitors it contains. All errors
|
||||
// will be captured and returned at the end of iteration.
|
||||
type EagerVisitorList []Visitor
|
||||
|
||||
// Visit implements Visitor, and gathers errors that occur during processing until
|
||||
// all sub visitors have been visited.
|
||||
func (l EagerVisitorList) Visit(fn VisitorFunc) error {
|
||||
errs := []error(nil)
|
||||
for i := range l {
|
||||
if err := l[i].Visit(func(info *Info, err error) error {
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
return nil
|
||||
}
|
||||
if err := fn(info, nil); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
return utilerrors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
func ValidateSchema(data []byte, schema validation.Schema) error {
|
||||
if schema == nil {
|
||||
return nil
|
||||
}
|
||||
if err := schema.ValidateBytes(data); err != nil {
|
||||
return fmt.Errorf("error validating data: %v; %s", err, stopValidateMessage)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// URLVisitor downloads the contents of a URL, and if successful, returns
|
||||
// an info object representing the downloaded object.
|
||||
type URLVisitor struct {
|
||||
URL *url.URL
|
||||
*StreamVisitor
|
||||
HttpAttemptCount int
|
||||
}
|
||||
|
||||
func (v *URLVisitor) Visit(fn VisitorFunc) error {
|
||||
body, err := readHttpWithRetries(httpgetImpl, time.Second, v.URL.String(), v.HttpAttemptCount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer body.Close()
|
||||
v.StreamVisitor.Reader = body
|
||||
return v.StreamVisitor.Visit(fn)
|
||||
}
|
||||
|
||||
// readHttpWithRetries tries to http.Get the v.URL retries times before giving up.
|
||||
func readHttpWithRetries(get httpget, duration time.Duration, u string, attempts int) (io.ReadCloser, error) {
|
||||
var err error
|
||||
var body io.ReadCloser
|
||||
if attempts <= 0 {
|
||||
return nil, fmt.Errorf("http attempts must be greater than 0, was %d", attempts)
|
||||
}
|
||||
for i := 0; i < attempts; i++ {
|
||||
var statusCode int
|
||||
var status string
|
||||
if i > 0 {
|
||||
time.Sleep(duration)
|
||||
}
|
||||
|
||||
// Try to get the URL
|
||||
statusCode, status, body, err = get(u)
|
||||
|
||||
// Retry Errors
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Error - Set the error condition from the StatusCode
|
||||
if statusCode != 200 {
|
||||
err = fmt.Errorf("unable to read URL %q, server reported %s, status code=%d", u, status, statusCode)
|
||||
}
|
||||
|
||||
if statusCode >= 500 && statusCode < 600 {
|
||||
// Retry 500's
|
||||
continue
|
||||
} else {
|
||||
// Don't retry other StatusCodes
|
||||
break
|
||||
}
|
||||
}
|
||||
return body, err
|
||||
}
|
||||
|
||||
// httpget Defines function to retrieve a url and return the results. Exists for unit test stubbing.
|
||||
type httpget func(url string) (int, string, io.ReadCloser, error)
|
||||
|
||||
// httpgetImpl Implements a function to retrieve a url and return the results.
|
||||
func httpgetImpl(url string) (int, string, io.ReadCloser, error) {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return 0, "", nil, err
|
||||
}
|
||||
return resp.StatusCode, resp.Status, resp.Body, nil
|
||||
}
|
||||
|
||||
// DecoratedVisitor will invoke the decorators in order prior to invoking the visitor function
|
||||
// passed to Visit. An error will terminate the visit.
|
||||
type DecoratedVisitor struct {
|
||||
visitor Visitor
|
||||
decorators []VisitorFunc
|
||||
}
|
||||
|
||||
// NewDecoratedVisitor will create a visitor that invokes the provided visitor functions before
|
||||
// the user supplied visitor function is invoked, giving them the opportunity to mutate the Info
|
||||
// object or terminate early with an error.
|
||||
func NewDecoratedVisitor(v Visitor, fn ...VisitorFunc) Visitor {
|
||||
if len(fn) == 0 {
|
||||
return v
|
||||
}
|
||||
return DecoratedVisitor{v, fn}
|
||||
}
|
||||
|
||||
// Visit implements Visitor
|
||||
func (v DecoratedVisitor) Visit(fn VisitorFunc) error {
|
||||
return v.visitor.Visit(func(info *Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range v.decorators {
|
||||
if err := v.decorators[i](info, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return fn(info, nil)
|
||||
})
|
||||
}
|
||||
|
||||
// ContinueOnErrorVisitor visits each item and, if an error occurs on
|
||||
// any individual item, returns an aggregate error after all items
|
||||
// are visited.
|
||||
type ContinueOnErrorVisitor struct {
|
||||
Visitor
|
||||
}
|
||||
|
||||
// Visit returns nil if no error occurs during traversal, a regular
|
||||
// error if one occurs, or if multiple errors occur, an aggregate
|
||||
// error. If the provided visitor fails on any individual item it
|
||||
// will not prevent the remaining items from being visited. An error
|
||||
// returned by the visitor directly may still result in some items
|
||||
// not being visited.
|
||||
func (v ContinueOnErrorVisitor) Visit(fn VisitorFunc) error {
|
||||
errs := []error{}
|
||||
err := v.Visitor.Visit(func(info *Info, err error) error {
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
return nil
|
||||
}
|
||||
if err := fn(info, nil); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if len(errs) == 1 {
|
||||
return errs[0]
|
||||
}
|
||||
return utilerrors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
// FlattenListVisitor flattens any objects that runtime.ExtractList recognizes as a list
|
||||
// - has an "Items" public field that is a slice of runtime.Objects or objects satisfying
|
||||
// that interface - into multiple Infos. An error on any sub item (for instance, if a List
|
||||
// contains an object that does not have a registered client or resource) will terminate
|
||||
// the visit.
|
||||
// TODO: allow errors to be aggregated?
|
||||
type FlattenListVisitor struct {
|
||||
Visitor
|
||||
*Mapper
|
||||
}
|
||||
|
||||
// NewFlattenListVisitor creates a visitor that will expand list style runtime.Objects
|
||||
// into individual items and then visit them individually.
|
||||
func NewFlattenListVisitor(v Visitor, mapper *Mapper) Visitor {
|
||||
return FlattenListVisitor{v, mapper}
|
||||
}
|
||||
|
||||
func (v FlattenListVisitor) Visit(fn VisitorFunc) error {
|
||||
return v.Visitor.Visit(func(info *Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.Object == nil {
|
||||
return fn(info, nil)
|
||||
}
|
||||
items, err := meta.ExtractList(info.Object)
|
||||
if err != nil {
|
||||
return fn(info, nil)
|
||||
}
|
||||
if errs := runtime.DecodeList(items, struct {
|
||||
runtime.ObjectTyper
|
||||
runtime.Decoder
|
||||
}{v.Mapper, v.Mapper.Decoder}); len(errs) > 0 {
|
||||
return utilerrors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
// If we have a GroupVersionKind on the list, prioritize that when asking for info on the objects contained in the list
|
||||
var preferredGVKs []unversioned.GroupVersionKind
|
||||
if info.Mapping != nil && !info.Mapping.GroupVersionKind.Empty() {
|
||||
preferredGVKs = append(preferredGVKs, info.Mapping.GroupVersionKind)
|
||||
}
|
||||
|
||||
for i := range items {
|
||||
item, err := v.InfoForObject(items[i], preferredGVKs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(info.ResourceVersion) != 0 {
|
||||
item.ResourceVersion = info.ResourceVersion
|
||||
}
|
||||
if err := fn(item, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func ignoreFile(path string, extensions []string) bool {
|
||||
if len(extensions) == 0 {
|
||||
return false
|
||||
}
|
||||
ext := filepath.Ext(path)
|
||||
for _, s := range extensions {
|
||||
if s == ext {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// FileVisitorForSTDIN return a special FileVisitor just for STDIN
|
||||
func FileVisitorForSTDIN(mapper *Mapper, schema validation.Schema) Visitor {
|
||||
return &FileVisitor{
|
||||
Path: constSTDINstr,
|
||||
StreamVisitor: NewStreamVisitor(nil, mapper, constSTDINstr, schema),
|
||||
}
|
||||
}
|
||||
|
||||
// ExpandPathsToFileVisitors will return a slice of FileVisitors that will handle files from the provided path.
|
||||
// After FileVisitors open the files, they will pass an io.Reader to a StreamVisitor to do the reading. (stdin
|
||||
// is also taken care of). Paths argument also accepts a single file, and will return a single visitor
|
||||
func ExpandPathsToFileVisitors(mapper *Mapper, paths string, recursive bool, extensions []string, schema validation.Schema) ([]Visitor, error) {
|
||||
var visitors []Visitor
|
||||
err := filepath.Walk(paths, func(path string, fi os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if fi.IsDir() {
|
||||
if path != paths && !recursive {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// Don't check extension if the filepath was passed explicitly
|
||||
if path != paths && ignoreFile(path, extensions) {
|
||||
return nil
|
||||
}
|
||||
|
||||
visitor := &FileVisitor{
|
||||
Path: path,
|
||||
StreamVisitor: NewStreamVisitor(nil, mapper, path, schema),
|
||||
}
|
||||
|
||||
visitors = append(visitors, visitor)
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return visitors, nil
|
||||
}
|
||||
|
||||
// FileVisitor is wrapping around a StreamVisitor, to handle open/close files
|
||||
type FileVisitor struct {
|
||||
Path string
|
||||
*StreamVisitor
|
||||
}
|
||||
|
||||
// Visit in a FileVisitor is just taking care of opening/closing files
|
||||
func (v *FileVisitor) Visit(fn VisitorFunc) error {
|
||||
var f *os.File
|
||||
if v.Path == constSTDINstr {
|
||||
f = os.Stdin
|
||||
} else {
|
||||
var err error
|
||||
if f, err = os.Open(v.Path); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
defer f.Close()
|
||||
v.StreamVisitor.Reader = f
|
||||
|
||||
return v.StreamVisitor.Visit(fn)
|
||||
}
|
||||
|
||||
// StreamVisitor reads objects from an io.Reader and walks them. A stream visitor can only be
|
||||
// visited once.
|
||||
// TODO: depends on objects being in JSON format before being passed to decode - need to implement
|
||||
// a stream decoder method on runtime.Codec to properly handle this.
|
||||
type StreamVisitor struct {
|
||||
io.Reader
|
||||
*Mapper
|
||||
|
||||
Source string
|
||||
Schema validation.Schema
|
||||
}
|
||||
|
||||
// NewStreamVisitor is a helper function that is useful when we want to change the fields of the struct but keep calls the same.
|
||||
func NewStreamVisitor(r io.Reader, mapper *Mapper, source string, schema validation.Schema) *StreamVisitor {
|
||||
return &StreamVisitor{
|
||||
Reader: r,
|
||||
Mapper: mapper,
|
||||
Source: source,
|
||||
Schema: schema,
|
||||
}
|
||||
}
|
||||
|
||||
// Visit implements Visitor over a stream. StreamVisitor is able to distinct multiple resources in one stream.
|
||||
func (v *StreamVisitor) Visit(fn VisitorFunc) error {
|
||||
d := yaml.NewYAMLOrJSONDecoder(v.Reader, 4096)
|
||||
for {
|
||||
ext := runtime.RawExtension{}
|
||||
if err := d.Decode(&ext); err != nil {
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
// TODO: This needs to be able to handle object in other encodings and schemas.
|
||||
ext.Raw = bytes.TrimSpace(ext.Raw)
|
||||
if len(ext.Raw) == 0 || bytes.Equal(ext.Raw, []byte("null")) {
|
||||
continue
|
||||
}
|
||||
if err := ValidateSchema(ext.Raw, v.Schema); err != nil {
|
||||
return fmt.Errorf("error validating %q: %v", v.Source, err)
|
||||
}
|
||||
info, err := v.InfoForData(ext.Raw, v.Source)
|
||||
if err != nil {
|
||||
if fnErr := fn(info, err); fnErr != nil {
|
||||
return fnErr
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err := fn(info, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func UpdateObjectNamespace(info *Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.Object != nil {
|
||||
return info.Mapping.MetadataAccessor.SetNamespace(info.Object, info.Namespace)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FilterNamespace omits the namespace if the object is not namespace scoped
|
||||
func FilterNamespace(info *Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !info.Namespaced() {
|
||||
info.Namespace = ""
|
||||
UpdateObjectNamespace(info, nil)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetNamespace ensures that every Info object visited will have a namespace
|
||||
// set. If info.Object is set, it will be mutated as well.
|
||||
func SetNamespace(namespace string) VisitorFunc {
|
||||
return func(info *Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !info.Namespaced() {
|
||||
return nil
|
||||
}
|
||||
if len(info.Namespace) == 0 {
|
||||
info.Namespace = namespace
|
||||
UpdateObjectNamespace(info, nil)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// RequireNamespace will either set a namespace if none is provided on the
|
||||
// Info object, or if the namespace is set and does not match the provided
|
||||
// value, returns an error. This is intended to guard against administrators
|
||||
// accidentally operating on resources outside their namespace.
|
||||
func RequireNamespace(namespace string) VisitorFunc {
|
||||
return func(info *Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !info.Namespaced() {
|
||||
return nil
|
||||
}
|
||||
if len(info.Namespace) == 0 {
|
||||
info.Namespace = namespace
|
||||
UpdateObjectNamespace(info, nil)
|
||||
return nil
|
||||
}
|
||||
if info.Namespace != namespace {
|
||||
return fmt.Errorf("the namespace from the provided object %q does not match the namespace %q. You must pass '--namespace=%s' to perform this operation.", info.Namespace, namespace, info.Namespace)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// RetrieveLatest updates the Object on each Info by invoking a standard client
|
||||
// Get.
|
||||
func RetrieveLatest(info *Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if meta.IsListType(info.Object) {
|
||||
return fmt.Errorf("watch is only supported on individual resources and resource collections, but a list of resources is found")
|
||||
}
|
||||
if len(info.Name) == 0 {
|
||||
return nil
|
||||
}
|
||||
if info.Namespaced() && len(info.Namespace) == 0 {
|
||||
return fmt.Errorf("no namespace set on resource %s %q", info.Mapping.Resource, info.Name)
|
||||
}
|
||||
return info.Get()
|
||||
}
|
||||
|
||||
// RetrieveLazy updates the object if it has not been loaded yet.
|
||||
func RetrieveLazy(info *Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.Object == nil {
|
||||
return info.Get()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type FilterFunc func(info *Info, err error) (bool, error)
|
||||
|
||||
type FilteredVisitor struct {
|
||||
visitor Visitor
|
||||
filters []FilterFunc
|
||||
}
|
||||
|
||||
func NewFilteredVisitor(v Visitor, fn ...FilterFunc) Visitor {
|
||||
if len(fn) == 0 {
|
||||
return v
|
||||
}
|
||||
return FilteredVisitor{v, fn}
|
||||
}
|
||||
|
||||
func (v FilteredVisitor) Visit(fn VisitorFunc) error {
|
||||
return v.visitor.Visit(func(info *Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, filter := range v.filters {
|
||||
ok, err := filter(info, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fn(info, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func FilterBySelector(s labels.Selector) FilterFunc {
|
||||
return func(info *Info, err error) (bool, error) {
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
a, err := meta.Accessor(info.Object)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !s.Matches(labels.Set(a.GetLabels())) {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
78
vendor/k8s.io/kubernetes/pkg/kubectl/resource_filter.go
generated
vendored
78
vendor/k8s.io/kubernetes/pkg/kubectl/resource_filter.go
generated
vendored
|
|
@ -1,78 +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 (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
// FilterFunc is a function that knows how to filter a specific resource kind.
|
||||
// It receives a generic runtime.Object which must be type-checked by the function.
|
||||
// Returns a boolean value true if a resource is filtered, or false otherwise.
|
||||
type FilterFunc func(runtime.Object, PrintOptions) bool
|
||||
|
||||
// Filters is a collection of filter funcs
|
||||
type Filters []FilterFunc
|
||||
|
||||
func NewResourceFilter() Filters {
|
||||
return []FilterFunc{
|
||||
filterPods,
|
||||
}
|
||||
}
|
||||
|
||||
// filterPods returns true if a pod should be skipped.
|
||||
// defaults to true for terminated pods
|
||||
func filterPods(obj runtime.Object, options PrintOptions) bool {
|
||||
switch p := obj.(type) {
|
||||
case *v1.Pod:
|
||||
reason := string(p.Status.Phase)
|
||||
if p.Status.Reason != "" {
|
||||
reason = p.Status.Reason
|
||||
}
|
||||
return !options.ShowAll && (reason == string(v1.PodSucceeded) || reason == string(v1.PodFailed))
|
||||
case *api.Pod:
|
||||
reason := string(p.Status.Phase)
|
||||
if p.Status.Reason != "" {
|
||||
reason = p.Status.Reason
|
||||
}
|
||||
return !options.ShowAll && (reason == string(api.PodSucceeded) || reason == string(api.PodFailed))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Filter loops through a collection of FilterFuncs until it finds one that can filter the given resource
|
||||
func (f Filters) Filter(obj runtime.Object, opts *PrintOptions) (bool, error) {
|
||||
// check if the object is unstructured. If so, let's attempt to convert it to a type we can understand
|
||||
// before apply filter func.
|
||||
switch obj.(type) {
|
||||
case *runtime.UnstructuredList, *runtime.Unstructured, *runtime.Unknown:
|
||||
if objBytes, err := runtime.Encode(api.Codecs.LegacyCodec(), obj); err == nil {
|
||||
if decodedObj, err := runtime.Decode(api.Codecs.UniversalDecoder(), objBytes); err == nil {
|
||||
obj = decodedObj
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, filter := range f {
|
||||
if ok := filter(obj, *opts); ok {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
2643
vendor/k8s.io/kubernetes/pkg/kubectl/resource_printer.go
generated
vendored
2643
vendor/k8s.io/kubernetes/pkg/kubectl/resource_printer.go
generated
vendored
File diff suppressed because it is too large
Load diff
177
vendor/k8s.io/kubernetes/pkg/kubectl/rollback.go
generated
vendored
177
vendor/k8s.io/kubernetes/pkg/kubectl/rollback.go
generated
vendored
|
|
@ -1,177 +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 (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
sliceutil "k8s.io/kubernetes/pkg/util/slice"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// Rollbacker provides an interface for resources that can be rolled back.
|
||||
type Rollbacker interface {
|
||||
Rollback(obj runtime.Object, updatedAnnotations map[string]string, toRevision int64, dryRun bool) (string, error)
|
||||
}
|
||||
|
||||
func RollbackerFor(kind unversioned.GroupKind, c clientset.Interface) (Rollbacker, error) {
|
||||
switch kind {
|
||||
case extensions.Kind("Deployment"):
|
||||
return &DeploymentRollbacker{c}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("no rollbacker has been implemented for %q", kind)
|
||||
}
|
||||
|
||||
type DeploymentRollbacker struct {
|
||||
c clientset.Interface
|
||||
}
|
||||
|
||||
func (r *DeploymentRollbacker) Rollback(obj runtime.Object, updatedAnnotations map[string]string, toRevision int64, dryRun bool) (string, error) {
|
||||
d, ok := obj.(*extensions.Deployment)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("passed object is not a Deployment: %#v", obj)
|
||||
}
|
||||
if dryRun {
|
||||
return simpleDryRun(d, r.c, toRevision)
|
||||
}
|
||||
if d.Spec.Paused {
|
||||
return "", fmt.Errorf("you cannot rollback a paused deployment; resume it first with 'kubectl rollout resume deployment/%s' and try again", d.Name)
|
||||
}
|
||||
deploymentRollback := &extensions.DeploymentRollback{
|
||||
Name: d.Name,
|
||||
UpdatedAnnotations: updatedAnnotations,
|
||||
RollbackTo: extensions.RollbackConfig{
|
||||
Revision: toRevision,
|
||||
},
|
||||
}
|
||||
result := ""
|
||||
|
||||
// Get current events
|
||||
events, err := r.c.Core().Events(d.Namespace).List(api.ListOptions{})
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
// Do the rollback
|
||||
if err := r.c.Extensions().Deployments(d.Namespace).Rollback(deploymentRollback); err != nil {
|
||||
return result, err
|
||||
}
|
||||
// Watch for the changes of events
|
||||
watch, err := r.c.Core().Events(d.Namespace).Watch(api.ListOptions{Watch: true, ResourceVersion: events.ResourceVersion})
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
result = watchRollbackEvent(watch)
|
||||
return result, err
|
||||
}
|
||||
|
||||
// watchRollbackEvent watches for rollback events and returns rollback result
|
||||
func watchRollbackEvent(w watch.Interface) string {
|
||||
signals := make(chan os.Signal, 1)
|
||||
signal.Notify(signals, os.Interrupt, os.Kill, syscall.SIGTERM)
|
||||
for {
|
||||
select {
|
||||
case event, ok := <-w.ResultChan():
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
obj, ok := event.Object.(*api.Event)
|
||||
if !ok {
|
||||
w.Stop()
|
||||
return ""
|
||||
}
|
||||
isRollback, result := isRollbackEvent(obj)
|
||||
if isRollback {
|
||||
w.Stop()
|
||||
return result
|
||||
}
|
||||
case <-signals:
|
||||
w.Stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// isRollbackEvent checks if the input event is about rollback, and returns true and
|
||||
// related result string back if it is.
|
||||
func isRollbackEvent(e *api.Event) (bool, string) {
|
||||
rollbackEventReasons := []string{deploymentutil.RollbackRevisionNotFound, deploymentutil.RollbackTemplateUnchanged, deploymentutil.RollbackDone}
|
||||
for _, reason := range rollbackEventReasons {
|
||||
if e.Reason == reason {
|
||||
if reason == deploymentutil.RollbackDone {
|
||||
return true, "rolled back"
|
||||
}
|
||||
return true, fmt.Sprintf("skipped rollback (%s: %s)", e.Reason, e.Message)
|
||||
}
|
||||
}
|
||||
return false, ""
|
||||
}
|
||||
|
||||
func simpleDryRun(deployment *extensions.Deployment, c clientset.Interface, toRevision int64) (string, error) {
|
||||
_, allOldRSs, newRS, err := deploymentutil.GetAllReplicaSets(deployment, c)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to retrieve replica sets from deployment %s: %v", deployment.Name, err)
|
||||
}
|
||||
allRSs := allOldRSs
|
||||
if newRS != nil {
|
||||
allRSs = append(allRSs, newRS)
|
||||
}
|
||||
|
||||
revisionToSpec := make(map[int64]*api.PodTemplateSpec)
|
||||
for _, rs := range allRSs {
|
||||
v, err := deploymentutil.Revision(rs)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
revisionToSpec[v] = &rs.Spec.Template
|
||||
}
|
||||
|
||||
if len(revisionToSpec) == 0 {
|
||||
return "No rollout history found.", nil
|
||||
}
|
||||
|
||||
if toRevision > 0 {
|
||||
template, ok := revisionToSpec[toRevision]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("unable to find specified revision")
|
||||
}
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
DescribePodTemplate(template, buf)
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
// Sort the revisionToSpec map by revision
|
||||
revisions := make([]int64, 0, len(revisionToSpec))
|
||||
for r := range revisionToSpec {
|
||||
revisions = append(revisions, r)
|
||||
}
|
||||
sliceutil.SortInts64(revisions)
|
||||
|
||||
template, _ := revisionToSpec[revisions[len(revisions)-1]]
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
buf.WriteString("\n")
|
||||
DescribePodTemplate(template, buf)
|
||||
return buf.String(), nil
|
||||
}
|
||||
835
vendor/k8s.io/kubernetes/pkg/kubectl/rolling_updater.go
generated
vendored
835
vendor/k8s.io/kubernetes/pkg/kubectl/rolling_updater.go
generated
vendored
|
|
@ -1,835 +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 (
|
||||
goerrors "errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/errors"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
|
||||
"k8s.io/kubernetes/pkg/client/retry"
|
||||
client "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/util/integer"
|
||||
"k8s.io/kubernetes/pkg/util/intstr"
|
||||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
)
|
||||
|
||||
const (
|
||||
sourceIdAnnotation = kubectlAnnotationPrefix + "update-source-id"
|
||||
desiredReplicasAnnotation = kubectlAnnotationPrefix + "desired-replicas"
|
||||
originalReplicasAnnotation = kubectlAnnotationPrefix + "original-replicas"
|
||||
nextControllerAnnotation = kubectlAnnotationPrefix + "next-controller-id"
|
||||
)
|
||||
|
||||
// RollingUpdaterConfig is the configuration for a rolling deployment process.
|
||||
type RollingUpdaterConfig struct {
|
||||
// Out is a writer for progress output.
|
||||
Out io.Writer
|
||||
// OldRC is an existing controller to be replaced.
|
||||
OldRc *api.ReplicationController
|
||||
// NewRc is a controller that will take ownership of updated pods (will be
|
||||
// created if needed).
|
||||
NewRc *api.ReplicationController
|
||||
// UpdatePeriod is the time to wait between individual pod updates.
|
||||
UpdatePeriod time.Duration
|
||||
// Interval is the time to wait between polling controller status after
|
||||
// update.
|
||||
Interval time.Duration
|
||||
// Timeout is the time to wait for controller updates before giving up.
|
||||
Timeout time.Duration
|
||||
// MinReadySeconds is the number of seconds to wait after the pods are ready
|
||||
MinReadySeconds int32
|
||||
// CleanupPolicy defines the cleanup action to take after the deployment is
|
||||
// complete.
|
||||
CleanupPolicy RollingUpdaterCleanupPolicy
|
||||
// MaxUnavailable is the maximum number of pods that can be unavailable during the update.
|
||||
// Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%).
|
||||
// Absolute number is calculated from percentage by rounding up.
|
||||
// This can not be 0 if MaxSurge is 0.
|
||||
// By default, a fixed value of 1 is used.
|
||||
// Example: when this is set to 30%, the old RC can be scaled down to 70% of desired pods
|
||||
// immediately when the rolling update starts. Once new pods are ready, old RC
|
||||
// can be scaled down further, followed by scaling up the new RC, ensuring
|
||||
// that the total number of pods available at all times during the update is at
|
||||
// least 70% of desired pods.
|
||||
MaxUnavailable intstr.IntOrString
|
||||
// MaxSurge is the maximum number of pods that can be scheduled above the desired number of pods.
|
||||
// Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%).
|
||||
// This can not be 0 if MaxUnavailable is 0.
|
||||
// Absolute number is calculated from percentage by rounding up.
|
||||
// By default, a value of 1 is used.
|
||||
// Example: when this is set to 30%, the new RC can be scaled up immediately
|
||||
// when the rolling update starts, such that the total number of old and new pods do not exceed
|
||||
// 130% of desired pods. Once old pods have been killed, new RC can be scaled up
|
||||
// further, ensuring that total number of pods running at any time during
|
||||
// the update is atmost 130% of desired pods.
|
||||
MaxSurge intstr.IntOrString
|
||||
// OnProgress is invoked if set during each scale cycle, to allow the caller to perform additional logic or
|
||||
// abort the scale. If an error is returned the cleanup method will not be invoked. The percentage value
|
||||
// is a synthetic "progress" calculation that represents the approximate percentage completion.
|
||||
OnProgress func(oldRc, newRc *api.ReplicationController, percentage int) error
|
||||
}
|
||||
|
||||
// RollingUpdaterCleanupPolicy is a cleanup action to take after the
|
||||
// deployment is complete.
|
||||
type RollingUpdaterCleanupPolicy string
|
||||
|
||||
const (
|
||||
// DeleteRollingUpdateCleanupPolicy means delete the old controller.
|
||||
DeleteRollingUpdateCleanupPolicy RollingUpdaterCleanupPolicy = "Delete"
|
||||
// PreserveRollingUpdateCleanupPolicy means keep the old controller.
|
||||
PreserveRollingUpdateCleanupPolicy RollingUpdaterCleanupPolicy = "Preserve"
|
||||
// RenameRollingUpdateCleanupPolicy means delete the old controller, and rename
|
||||
// the new controller to the name of the old controller.
|
||||
RenameRollingUpdateCleanupPolicy RollingUpdaterCleanupPolicy = "Rename"
|
||||
)
|
||||
|
||||
// RollingUpdater provides methods for updating replicated pods in a predictable,
|
||||
// fault-tolerant way.
|
||||
type RollingUpdater struct {
|
||||
rcClient coreclient.ReplicationControllersGetter
|
||||
podClient coreclient.PodsGetter
|
||||
// Namespace for resources
|
||||
ns string
|
||||
// scaleAndWait scales a controller and returns its updated state.
|
||||
scaleAndWait func(rc *api.ReplicationController, retry *RetryParams, wait *RetryParams) (*api.ReplicationController, error)
|
||||
//getOrCreateTargetController gets and validates an existing controller or
|
||||
//makes a new one.
|
||||
getOrCreateTargetController func(controller *api.ReplicationController, sourceId string) (*api.ReplicationController, bool, error)
|
||||
// cleanup performs post deployment cleanup tasks for newRc and oldRc.
|
||||
cleanup func(oldRc, newRc *api.ReplicationController, config *RollingUpdaterConfig) error
|
||||
// getReadyPods returns the amount of old and new ready pods.
|
||||
getReadyPods func(oldRc, newRc *api.ReplicationController, minReadySeconds int32) (int32, int32, error)
|
||||
// nowFn returns the current time used to calculate the minReadySeconds
|
||||
nowFn func() unversioned.Time
|
||||
}
|
||||
|
||||
// NewRollingUpdater creates a RollingUpdater from a client.
|
||||
func NewRollingUpdater(namespace string, rcClient coreclient.ReplicationControllersGetter, podClient coreclient.PodsGetter) *RollingUpdater {
|
||||
updater := &RollingUpdater{
|
||||
rcClient: rcClient,
|
||||
podClient: podClient,
|
||||
ns: namespace,
|
||||
}
|
||||
// Inject real implementations.
|
||||
updater.scaleAndWait = updater.scaleAndWaitWithScaler
|
||||
updater.getOrCreateTargetController = updater.getOrCreateTargetControllerWithClient
|
||||
updater.getReadyPods = updater.readyPods
|
||||
updater.cleanup = updater.cleanupWithClients
|
||||
updater.nowFn = func() unversioned.Time { return unversioned.Now() }
|
||||
return updater
|
||||
}
|
||||
|
||||
// Update all pods for a ReplicationController (oldRc) by creating a new
|
||||
// controller (newRc) with 0 replicas, and synchronously scaling oldRc and
|
||||
// newRc until oldRc has 0 replicas and newRc has the original # of desired
|
||||
// replicas. Cleanup occurs based on a RollingUpdaterCleanupPolicy.
|
||||
//
|
||||
// Each interval, the updater will attempt to make progress however it can
|
||||
// without violating any availability constraints defined by the config. This
|
||||
// means the amount scaled up or down each interval will vary based on the
|
||||
// timeliness of readiness and the updater will always try to make progress,
|
||||
// even slowly.
|
||||
//
|
||||
// If an update from newRc to oldRc is already in progress, we attempt to
|
||||
// drive it to completion. If an error occurs at any step of the update, the
|
||||
// error will be returned.
|
||||
//
|
||||
// A scaling event (either up or down) is considered progress; if no progress
|
||||
// is made within the config.Timeout, an error is returned.
|
||||
//
|
||||
// TODO: make this handle performing a rollback of a partially completed
|
||||
// rollout.
|
||||
func (r *RollingUpdater) Update(config *RollingUpdaterConfig) error {
|
||||
out := config.Out
|
||||
oldRc := config.OldRc
|
||||
scaleRetryParams := NewRetryParams(config.Interval, config.Timeout)
|
||||
|
||||
// Find an existing controller (for continuing an interrupted update) or
|
||||
// create a new one if necessary.
|
||||
sourceId := fmt.Sprintf("%s:%s", oldRc.Name, oldRc.UID)
|
||||
newRc, existed, err := r.getOrCreateTargetController(config.NewRc, sourceId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existed {
|
||||
fmt.Fprintf(out, "Continuing update with existing controller %s.\n", newRc.Name)
|
||||
} else {
|
||||
fmt.Fprintf(out, "Created %s\n", newRc.Name)
|
||||
}
|
||||
// Extract the desired replica count from the controller.
|
||||
desiredAnnotation, err := strconv.Atoi(newRc.Annotations[desiredReplicasAnnotation])
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to parse annotation for %s: %s=%s",
|
||||
newRc.Name, desiredReplicasAnnotation, newRc.Annotations[desiredReplicasAnnotation])
|
||||
}
|
||||
desired := int32(desiredAnnotation)
|
||||
// Extract the original replica count from the old controller, adding the
|
||||
// annotation if it doesn't yet exist.
|
||||
_, hasOriginalAnnotation := oldRc.Annotations[originalReplicasAnnotation]
|
||||
if !hasOriginalAnnotation {
|
||||
existing, err := r.rcClient.ReplicationControllers(oldRc.Namespace).Get(oldRc.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
originReplicas := strconv.Itoa(int(existing.Spec.Replicas))
|
||||
applyUpdate := func(rc *api.ReplicationController) {
|
||||
if rc.Annotations == nil {
|
||||
rc.Annotations = map[string]string{}
|
||||
}
|
||||
rc.Annotations[originalReplicasAnnotation] = originReplicas
|
||||
}
|
||||
if oldRc, err = updateRcWithRetries(r.rcClient, existing.Namespace, existing, applyUpdate); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// maxSurge is the maximum scaling increment and maxUnavailable are the maximum pods
|
||||
// that can be unavailable during a rollout.
|
||||
maxSurge, maxUnavailable, err := deploymentutil.ResolveFenceposts(&config.MaxSurge, &config.MaxUnavailable, desired)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Validate maximums.
|
||||
if desired > 0 && maxUnavailable == 0 && maxSurge == 0 {
|
||||
return fmt.Errorf("one of maxSurge or maxUnavailable must be specified")
|
||||
}
|
||||
// The minimum pods which must remain available throughout the update
|
||||
// calculated for internal convenience.
|
||||
minAvailable := int32(integer.IntMax(0, int(desired-maxUnavailable)))
|
||||
// If the desired new scale is 0, then the max unavailable is necessarily
|
||||
// the effective scale of the old RC regardless of the configuration
|
||||
// (equivalent to 100% maxUnavailable).
|
||||
if desired == 0 {
|
||||
maxUnavailable = oldRc.Spec.Replicas
|
||||
minAvailable = 0
|
||||
}
|
||||
|
||||
fmt.Fprintf(out, "Scaling up %s from %d to %d, scaling down %s from %d to 0 (keep %d pods available, don't exceed %d pods)\n",
|
||||
newRc.Name, newRc.Spec.Replicas, desired, oldRc.Name, oldRc.Spec.Replicas, minAvailable, desired+maxSurge)
|
||||
|
||||
// give a caller incremental notification and allow them to exit early
|
||||
goal := desired - newRc.Spec.Replicas
|
||||
if goal < 0 {
|
||||
goal = -goal
|
||||
}
|
||||
progress := func(complete bool) error {
|
||||
if config.OnProgress == nil {
|
||||
return nil
|
||||
}
|
||||
progress := desired - newRc.Spec.Replicas
|
||||
if progress < 0 {
|
||||
progress = -progress
|
||||
}
|
||||
percentage := 100
|
||||
if !complete && goal > 0 {
|
||||
percentage = int((goal - progress) * 100 / goal)
|
||||
}
|
||||
return config.OnProgress(oldRc, newRc, percentage)
|
||||
}
|
||||
|
||||
// Scale newRc and oldRc until newRc has the desired number of replicas and
|
||||
// oldRc has 0 replicas.
|
||||
progressDeadline := time.Now().UnixNano() + config.Timeout.Nanoseconds()
|
||||
for newRc.Spec.Replicas != desired || oldRc.Spec.Replicas != 0 {
|
||||
// Store the existing replica counts for progress timeout tracking.
|
||||
newReplicas := newRc.Spec.Replicas
|
||||
oldReplicas := oldRc.Spec.Replicas
|
||||
|
||||
// Scale up as much as possible.
|
||||
scaledRc, err := r.scaleUp(newRc, oldRc, desired, maxSurge, maxUnavailable, scaleRetryParams, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newRc = scaledRc
|
||||
|
||||
// notify the caller if necessary
|
||||
if err := progress(false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait between scaling operations for things to settle.
|
||||
time.Sleep(config.UpdatePeriod)
|
||||
|
||||
// Scale down as much as possible.
|
||||
scaledRc, err = r.scaleDown(newRc, oldRc, desired, minAvailable, maxUnavailable, maxSurge, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oldRc = scaledRc
|
||||
|
||||
// notify the caller if necessary
|
||||
if err := progress(false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If we are making progress, continue to advance the progress deadline.
|
||||
// Otherwise, time out with an error.
|
||||
progressMade := (newRc.Spec.Replicas != newReplicas) || (oldRc.Spec.Replicas != oldReplicas)
|
||||
if progressMade {
|
||||
progressDeadline = time.Now().UnixNano() + config.Timeout.Nanoseconds()
|
||||
} else if time.Now().UnixNano() > progressDeadline {
|
||||
return fmt.Errorf("timed out waiting for any update progress to be made")
|
||||
}
|
||||
}
|
||||
|
||||
// notify the caller if necessary
|
||||
if err := progress(true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Housekeeping and cleanup policy execution.
|
||||
return r.cleanup(oldRc, newRc, config)
|
||||
}
|
||||
|
||||
// scaleUp scales up newRc to desired by whatever increment is possible given
|
||||
// the configured surge threshold. scaleUp will safely no-op as necessary when
|
||||
// it detects redundancy or other relevant conditions.
|
||||
func (r *RollingUpdater) scaleUp(newRc, oldRc *api.ReplicationController, desired, maxSurge, maxUnavailable int32, scaleRetryParams *RetryParams, config *RollingUpdaterConfig) (*api.ReplicationController, error) {
|
||||
// If we're already at the desired, do nothing.
|
||||
if newRc.Spec.Replicas == desired {
|
||||
return newRc, nil
|
||||
}
|
||||
|
||||
// Scale up as far as we can based on the surge limit.
|
||||
increment := (desired + maxSurge) - (oldRc.Spec.Replicas + newRc.Spec.Replicas)
|
||||
// If the old is already scaled down, go ahead and scale all the way up.
|
||||
if oldRc.Spec.Replicas == 0 {
|
||||
increment = desired - newRc.Spec.Replicas
|
||||
}
|
||||
// We can't scale up without violating the surge limit, so do nothing.
|
||||
if increment <= 0 {
|
||||
return newRc, nil
|
||||
}
|
||||
// Increase the replica count, and deal with fenceposts.
|
||||
newRc.Spec.Replicas += increment
|
||||
if newRc.Spec.Replicas > desired {
|
||||
newRc.Spec.Replicas = desired
|
||||
}
|
||||
// Perform the scale-up.
|
||||
fmt.Fprintf(config.Out, "Scaling %s up to %d\n", newRc.Name, newRc.Spec.Replicas)
|
||||
scaledRc, err := r.scaleAndWait(newRc, scaleRetryParams, scaleRetryParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return scaledRc, nil
|
||||
}
|
||||
|
||||
// scaleDown scales down oldRc to 0 at whatever decrement possible given the
|
||||
// thresholds defined on the config. scaleDown will safely no-op as necessary
|
||||
// when it detects redundancy or other relevant conditions.
|
||||
func (r *RollingUpdater) scaleDown(newRc, oldRc *api.ReplicationController, desired, minAvailable, maxUnavailable, maxSurge int32, config *RollingUpdaterConfig) (*api.ReplicationController, error) {
|
||||
// Already scaled down; do nothing.
|
||||
if oldRc.Spec.Replicas == 0 {
|
||||
return oldRc, nil
|
||||
}
|
||||
// Get ready pods. We shouldn't block, otherwise in case both old and new
|
||||
// pods are unavailable then the rolling update process blocks.
|
||||
// Timeout-wise we are already covered by the progress check.
|
||||
_, newAvailable, err := r.getReadyPods(oldRc, newRc, config.MinReadySeconds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// The old controller is considered as part of the total because we want to
|
||||
// maintain minimum availability even with a volatile old controller.
|
||||
// Scale down as much as possible while maintaining minimum availability
|
||||
allPods := oldRc.Spec.Replicas + newRc.Spec.Replicas
|
||||
newUnavailable := newRc.Spec.Replicas - newAvailable
|
||||
decrement := allPods - minAvailable - newUnavailable
|
||||
// The decrement normally shouldn't drop below 0 because the available count
|
||||
// always starts below the old replica count, but the old replica count can
|
||||
// decrement due to externalities like pods death in the replica set. This
|
||||
// will be considered a transient condition; do nothing and try again later
|
||||
// with new readiness values.
|
||||
//
|
||||
// If the most we can scale is 0, it means we can't scale down without
|
||||
// violating the minimum. Do nothing and try again later when conditions may
|
||||
// have changed.
|
||||
if decrement <= 0 {
|
||||
return oldRc, nil
|
||||
}
|
||||
// Reduce the replica count, and deal with fenceposts.
|
||||
oldRc.Spec.Replicas -= decrement
|
||||
if oldRc.Spec.Replicas < 0 {
|
||||
oldRc.Spec.Replicas = 0
|
||||
}
|
||||
// If the new is already fully scaled and available up to the desired size, go
|
||||
// ahead and scale old all the way down.
|
||||
if newRc.Spec.Replicas == desired && newAvailable == desired {
|
||||
oldRc.Spec.Replicas = 0
|
||||
}
|
||||
// Perform the scale-down.
|
||||
fmt.Fprintf(config.Out, "Scaling %s down to %d\n", oldRc.Name, oldRc.Spec.Replicas)
|
||||
retryWait := &RetryParams{config.Interval, config.Timeout}
|
||||
scaledRc, err := r.scaleAndWait(oldRc, retryWait, retryWait)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return scaledRc, nil
|
||||
}
|
||||
|
||||
// scalerScaleAndWait scales a controller using a Scaler and a real client.
|
||||
func (r *RollingUpdater) scaleAndWaitWithScaler(rc *api.ReplicationController, retry *RetryParams, wait *RetryParams) (*api.ReplicationController, error) {
|
||||
scaler := &ReplicationControllerScaler{r.rcClient}
|
||||
if err := scaler.Scale(rc.Namespace, rc.Name, uint(rc.Spec.Replicas), &ScalePrecondition{-1, ""}, retry, wait); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.rcClient.ReplicationControllers(rc.Namespace).Get(rc.Name)
|
||||
}
|
||||
|
||||
// readyPods returns the old and new ready counts for their pods.
|
||||
// If a pod is observed as being ready, it's considered ready even
|
||||
// if it later becomes notReady.
|
||||
func (r *RollingUpdater) readyPods(oldRc, newRc *api.ReplicationController, minReadySeconds int32) (int32, int32, error) {
|
||||
controllers := []*api.ReplicationController{oldRc, newRc}
|
||||
oldReady := int32(0)
|
||||
newReady := int32(0)
|
||||
if r.nowFn == nil {
|
||||
r.nowFn = func() unversioned.Time { return unversioned.Now() }
|
||||
}
|
||||
|
||||
for i := range controllers {
|
||||
controller := controllers[i]
|
||||
selector := labels.Set(controller.Spec.Selector).AsSelector()
|
||||
options := api.ListOptions{LabelSelector: selector}
|
||||
pods, err := r.podClient.Pods(controller.Namespace).List(options)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
for _, pod := range pods.Items {
|
||||
if !deploymentutil.IsPodAvailable(&pod, minReadySeconds, r.nowFn().Time) {
|
||||
continue
|
||||
}
|
||||
switch controller.Name {
|
||||
case oldRc.Name:
|
||||
oldReady++
|
||||
case newRc.Name:
|
||||
newReady++
|
||||
}
|
||||
}
|
||||
}
|
||||
return oldReady, newReady, nil
|
||||
}
|
||||
|
||||
// getOrCreateTargetControllerWithClient looks for an existing controller with
|
||||
// sourceId. If found, the existing controller is returned with true
|
||||
// indicating that the controller already exists. If the controller isn't
|
||||
// found, a new one is created and returned along with false indicating the
|
||||
// controller was created.
|
||||
//
|
||||
// Existing controllers are validated to ensure their sourceIdAnnotation
|
||||
// matches sourceId; if there's a mismatch, an error is returned.
|
||||
func (r *RollingUpdater) getOrCreateTargetControllerWithClient(controller *api.ReplicationController, sourceId string) (*api.ReplicationController, bool, error) {
|
||||
existingRc, err := r.existingController(controller)
|
||||
if err != nil {
|
||||
if !errors.IsNotFound(err) {
|
||||
// There was an error trying to find the controller; don't assume we
|
||||
// should create it.
|
||||
return nil, false, err
|
||||
}
|
||||
if controller.Spec.Replicas <= 0 {
|
||||
return nil, false, fmt.Errorf("Invalid controller spec for %s; required: > 0 replicas, actual: %d\n", controller.Name, controller.Spec.Replicas)
|
||||
}
|
||||
// The controller wasn't found, so create it.
|
||||
if controller.Annotations == nil {
|
||||
controller.Annotations = map[string]string{}
|
||||
}
|
||||
controller.Annotations[desiredReplicasAnnotation] = fmt.Sprintf("%d", controller.Spec.Replicas)
|
||||
controller.Annotations[sourceIdAnnotation] = sourceId
|
||||
controller.Spec.Replicas = 0
|
||||
newRc, err := r.rcClient.ReplicationControllers(r.ns).Create(controller)
|
||||
return newRc, false, err
|
||||
}
|
||||
// Validate and use the existing controller.
|
||||
annotations := existingRc.Annotations
|
||||
source := annotations[sourceIdAnnotation]
|
||||
_, ok := annotations[desiredReplicasAnnotation]
|
||||
if source != sourceId || !ok {
|
||||
return nil, false, fmt.Errorf("Missing/unexpected annotations for controller %s, expected %s : %s", controller.Name, sourceId, annotations)
|
||||
}
|
||||
return existingRc, true, nil
|
||||
}
|
||||
|
||||
// existingController verifies if the controller already exists
|
||||
func (r *RollingUpdater) existingController(controller *api.ReplicationController) (*api.ReplicationController, error) {
|
||||
// without rc name but generate name, there's no existing rc
|
||||
if len(controller.Name) == 0 && len(controller.GenerateName) > 0 {
|
||||
return nil, errors.NewNotFound(api.Resource("replicationcontrollers"), controller.Name)
|
||||
}
|
||||
// controller name is required to get rc back
|
||||
return r.rcClient.ReplicationControllers(controller.Namespace).Get(controller.Name)
|
||||
}
|
||||
|
||||
// cleanupWithClients performs cleanup tasks after the rolling update. Update
|
||||
// process related annotations are removed from oldRc and newRc. The
|
||||
// CleanupPolicy on config is executed.
|
||||
func (r *RollingUpdater) cleanupWithClients(oldRc, newRc *api.ReplicationController, config *RollingUpdaterConfig) error {
|
||||
// Clean up annotations
|
||||
var err error
|
||||
newRc, err = r.rcClient.ReplicationControllers(r.ns).Get(newRc.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
applyUpdate := func(rc *api.ReplicationController) {
|
||||
delete(rc.Annotations, sourceIdAnnotation)
|
||||
delete(rc.Annotations, desiredReplicasAnnotation)
|
||||
}
|
||||
if newRc, err = updateRcWithRetries(r.rcClient, r.ns, newRc, applyUpdate); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = wait.Poll(config.Interval, config.Timeout, client.ControllerHasDesiredReplicas(r.rcClient, newRc)); err != nil {
|
||||
return err
|
||||
}
|
||||
newRc, err = r.rcClient.ReplicationControllers(r.ns).Get(newRc.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch config.CleanupPolicy {
|
||||
case DeleteRollingUpdateCleanupPolicy:
|
||||
// delete old rc
|
||||
fmt.Fprintf(config.Out, "Update succeeded. Deleting %s\n", oldRc.Name)
|
||||
return r.rcClient.ReplicationControllers(r.ns).Delete(oldRc.Name, nil)
|
||||
case RenameRollingUpdateCleanupPolicy:
|
||||
// delete old rc
|
||||
fmt.Fprintf(config.Out, "Update succeeded. Deleting old controller: %s\n", oldRc.Name)
|
||||
if err := r.rcClient.ReplicationControllers(r.ns).Delete(oldRc.Name, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(config.Out, "Renaming %s to %s\n", oldRc.Name, newRc.Name)
|
||||
return Rename(r.rcClient, newRc, oldRc.Name)
|
||||
case PreserveRollingUpdateCleanupPolicy:
|
||||
return nil
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func Rename(c coreclient.ReplicationControllersGetter, rc *api.ReplicationController, newName string) error {
|
||||
oldName := rc.Name
|
||||
rc.Name = newName
|
||||
rc.ResourceVersion = ""
|
||||
// First delete the oldName RC and orphan its pods.
|
||||
trueVar := true
|
||||
err := c.ReplicationControllers(rc.Namespace).Delete(oldName, &api.DeleteOptions{OrphanDependents: &trueVar})
|
||||
if err != nil && !errors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
err = wait.Poll(5*time.Second, 60*time.Second, func() (bool, error) {
|
||||
_, err := c.ReplicationControllers(rc.Namespace).Get(oldName)
|
||||
if err == nil {
|
||||
return false, nil
|
||||
} else if errors.IsNotFound(err) {
|
||||
return true, nil
|
||||
} else {
|
||||
return false, err
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Then create the same RC with the new name.
|
||||
_, err = c.ReplicationControllers(rc.Namespace).Create(rc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func LoadExistingNextReplicationController(c coreclient.ReplicationControllersGetter, namespace, newName string) (*api.ReplicationController, error) {
|
||||
if len(newName) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
newRc, err := c.ReplicationControllers(namespace).Get(newName)
|
||||
if err != nil && errors.IsNotFound(err) {
|
||||
return nil, nil
|
||||
}
|
||||
return newRc, err
|
||||
}
|
||||
|
||||
type NewControllerConfig struct {
|
||||
Namespace string
|
||||
OldName, NewName string
|
||||
Image string
|
||||
Container string
|
||||
DeploymentKey string
|
||||
PullPolicy api.PullPolicy
|
||||
}
|
||||
|
||||
func CreateNewControllerFromCurrentController(rcClient coreclient.ReplicationControllersGetter, codec runtime.Codec, cfg *NewControllerConfig) (*api.ReplicationController, error) {
|
||||
containerIndex := 0
|
||||
// load the old RC into the "new" RC
|
||||
newRc, err := rcClient.ReplicationControllers(cfg.Namespace).Get(cfg.OldName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(cfg.Container) != 0 {
|
||||
containerFound := false
|
||||
|
||||
for i, c := range newRc.Spec.Template.Spec.Containers {
|
||||
if c.Name == cfg.Container {
|
||||
containerIndex = i
|
||||
containerFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !containerFound {
|
||||
return nil, fmt.Errorf("container %s not found in pod", cfg.Container)
|
||||
}
|
||||
}
|
||||
|
||||
if len(newRc.Spec.Template.Spec.Containers) > 1 && len(cfg.Container) == 0 {
|
||||
return nil, goerrors.New("Must specify container to update when updating a multi-container pod")
|
||||
}
|
||||
|
||||
if len(newRc.Spec.Template.Spec.Containers) == 0 {
|
||||
return nil, goerrors.New(fmt.Sprintf("Pod has no containers! (%v)", newRc))
|
||||
}
|
||||
newRc.Spec.Template.Spec.Containers[containerIndex].Image = cfg.Image
|
||||
if len(cfg.PullPolicy) != 0 {
|
||||
newRc.Spec.Template.Spec.Containers[containerIndex].ImagePullPolicy = cfg.PullPolicy
|
||||
}
|
||||
|
||||
newHash, err := api.HashObject(newRc, codec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(cfg.NewName) == 0 {
|
||||
cfg.NewName = fmt.Sprintf("%s-%s", newRc.Name, newHash)
|
||||
}
|
||||
newRc.Name = cfg.NewName
|
||||
|
||||
newRc.Spec.Selector[cfg.DeploymentKey] = newHash
|
||||
newRc.Spec.Template.Labels[cfg.DeploymentKey] = newHash
|
||||
// Clear resource version after hashing so that identical updates get different hashes.
|
||||
newRc.ResourceVersion = ""
|
||||
return newRc, nil
|
||||
}
|
||||
|
||||
func AbortRollingUpdate(c *RollingUpdaterConfig) error {
|
||||
// Swap the controllers
|
||||
tmp := c.OldRc
|
||||
c.OldRc = c.NewRc
|
||||
c.NewRc = tmp
|
||||
|
||||
if c.NewRc.Annotations == nil {
|
||||
c.NewRc.Annotations = map[string]string{}
|
||||
}
|
||||
c.NewRc.Annotations[sourceIdAnnotation] = fmt.Sprintf("%s:%s", c.OldRc.Name, c.OldRc.UID)
|
||||
|
||||
// Use the original value since the replica count change from old to new
|
||||
// could be asymmetric. If we don't know the original count, we can't safely
|
||||
// roll back to a known good size.
|
||||
originalSize, foundOriginal := tmp.Annotations[originalReplicasAnnotation]
|
||||
if !foundOriginal {
|
||||
return fmt.Errorf("couldn't find original replica count of %q", tmp.Name)
|
||||
}
|
||||
fmt.Fprintf(c.Out, "Setting %q replicas to %s\n", c.NewRc.Name, originalSize)
|
||||
c.NewRc.Annotations[desiredReplicasAnnotation] = originalSize
|
||||
c.CleanupPolicy = DeleteRollingUpdateCleanupPolicy
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetNextControllerAnnotation(rc *api.ReplicationController) (string, bool) {
|
||||
res, found := rc.Annotations[nextControllerAnnotation]
|
||||
return res, found
|
||||
}
|
||||
|
||||
func SetNextControllerAnnotation(rc *api.ReplicationController, name string) {
|
||||
if rc.Annotations == nil {
|
||||
rc.Annotations = map[string]string{}
|
||||
}
|
||||
rc.Annotations[nextControllerAnnotation] = name
|
||||
}
|
||||
|
||||
func UpdateExistingReplicationController(rcClient coreclient.ReplicationControllersGetter, podClient coreclient.PodsGetter, oldRc *api.ReplicationController, namespace, newName, deploymentKey, deploymentValue string, out io.Writer) (*api.ReplicationController, error) {
|
||||
if _, found := oldRc.Spec.Selector[deploymentKey]; !found {
|
||||
SetNextControllerAnnotation(oldRc, newName)
|
||||
return AddDeploymentKeyToReplicationController(oldRc, rcClient, podClient, deploymentKey, deploymentValue, namespace, out)
|
||||
} else {
|
||||
// If we didn't need to update the controller for the deployment key, we still need to write
|
||||
// the "next" controller.
|
||||
applyUpdate := func(rc *api.ReplicationController) {
|
||||
SetNextControllerAnnotation(rc, newName)
|
||||
}
|
||||
return updateRcWithRetries(rcClient, namespace, oldRc, applyUpdate)
|
||||
}
|
||||
}
|
||||
|
||||
func AddDeploymentKeyToReplicationController(oldRc *api.ReplicationController, rcClient coreclient.ReplicationControllersGetter, podClient coreclient.PodsGetter, deploymentKey, deploymentValue, namespace string, out io.Writer) (*api.ReplicationController, error) {
|
||||
var err error
|
||||
// First, update the template label. This ensures that any newly created pods will have the new label
|
||||
applyUpdate := func(rc *api.ReplicationController) {
|
||||
if rc.Spec.Template.Labels == nil {
|
||||
rc.Spec.Template.Labels = map[string]string{}
|
||||
}
|
||||
rc.Spec.Template.Labels[deploymentKey] = deploymentValue
|
||||
}
|
||||
if oldRc, err = updateRcWithRetries(rcClient, namespace, oldRc, applyUpdate); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Update all pods managed by the rc to have the new hash label, so they are correctly adopted
|
||||
// TODO: extract the code from the label command and re-use it here.
|
||||
selector := labels.SelectorFromSet(oldRc.Spec.Selector)
|
||||
options := api.ListOptions{LabelSelector: selector}
|
||||
podList, err := podClient.Pods(namespace).List(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for ix := range podList.Items {
|
||||
pod := &podList.Items[ix]
|
||||
applyUpdate := func(p *api.Pod) {
|
||||
if p.Labels == nil {
|
||||
p.Labels = map[string]string{
|
||||
deploymentKey: deploymentValue,
|
||||
}
|
||||
} else {
|
||||
p.Labels[deploymentKey] = deploymentValue
|
||||
}
|
||||
}
|
||||
if pod, err = updatePodWithRetries(podClient, namespace, pod, applyUpdate); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if oldRc.Spec.Selector == nil {
|
||||
oldRc.Spec.Selector = map[string]string{}
|
||||
}
|
||||
// Copy the old selector, so that we can scrub out any orphaned pods
|
||||
selectorCopy := map[string]string{}
|
||||
for k, v := range oldRc.Spec.Selector {
|
||||
selectorCopy[k] = v
|
||||
}
|
||||
applyUpdate = func(rc *api.ReplicationController) {
|
||||
rc.Spec.Selector[deploymentKey] = deploymentValue
|
||||
}
|
||||
// Update the selector of the rc so it manages all the pods we updated above
|
||||
if oldRc, err = updateRcWithRetries(rcClient, namespace, oldRc, applyUpdate); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Clean up any orphaned pods that don't have the new label, this can happen if the rc manager
|
||||
// doesn't see the update to its pod template and creates a new pod with the old labels after
|
||||
// we've finished re-adopting existing pods to the rc.
|
||||
selector = labels.SelectorFromSet(selectorCopy)
|
||||
options = api.ListOptions{LabelSelector: selector}
|
||||
podList, err = podClient.Pods(namespace).List(options)
|
||||
for ix := range podList.Items {
|
||||
pod := &podList.Items[ix]
|
||||
if value, found := pod.Labels[deploymentKey]; !found || value != deploymentValue {
|
||||
if err := podClient.Pods(namespace).Delete(pod.Name, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return oldRc, nil
|
||||
}
|
||||
|
||||
type updateRcFunc func(controller *api.ReplicationController)
|
||||
|
||||
// updateRcWithRetries retries updating the given rc on conflict with the following steps:
|
||||
// 1. Get latest resource
|
||||
// 2. applyUpdate
|
||||
// 3. Update the resource
|
||||
func updateRcWithRetries(rcClient coreclient.ReplicationControllersGetter, namespace string, rc *api.ReplicationController, applyUpdate updateRcFunc) (*api.ReplicationController, error) {
|
||||
// Deep copy the rc in case we failed on Get during retry loop
|
||||
obj, err := api.Scheme.Copy(rc)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to deep copy rc before updating it: %v", err)
|
||||
}
|
||||
oldRc := obj.(*api.ReplicationController)
|
||||
err = retry.RetryOnConflict(retry.DefaultBackoff, func() (e error) {
|
||||
// Apply the update, then attempt to push it to the apiserver.
|
||||
applyUpdate(rc)
|
||||
if rc, e = rcClient.ReplicationControllers(namespace).Update(rc); e == nil {
|
||||
// rc contains the latest controller post update
|
||||
return
|
||||
}
|
||||
updateErr := e
|
||||
// Update the controller with the latest resource version, if the update failed we
|
||||
// can't trust rc so use oldRc.Name.
|
||||
if rc, e = rcClient.ReplicationControllers(namespace).Get(oldRc.Name); e != nil {
|
||||
// The Get failed: Value in rc cannot be trusted.
|
||||
rc = oldRc
|
||||
}
|
||||
// Only return the error from update
|
||||
return updateErr
|
||||
})
|
||||
// If the error is non-nil the returned controller cannot be trusted, if it is nil, the returned
|
||||
// controller contains the applied update.
|
||||
return rc, err
|
||||
}
|
||||
|
||||
type updatePodFunc func(controller *api.Pod)
|
||||
|
||||
// updatePodWithRetries retries updating the given pod on conflict with the following steps:
|
||||
// 1. Get latest resource
|
||||
// 2. applyUpdate
|
||||
// 3. Update the resource
|
||||
func updatePodWithRetries(podClient coreclient.PodsGetter, namespace string, pod *api.Pod, applyUpdate updatePodFunc) (*api.Pod, error) {
|
||||
// Deep copy the pod in case we failed on Get during retry loop
|
||||
obj, err := api.Scheme.Copy(pod)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to deep copy pod before updating it: %v", err)
|
||||
}
|
||||
oldPod := obj.(*api.Pod)
|
||||
err = retry.RetryOnConflict(retry.DefaultBackoff, func() (e error) {
|
||||
// Apply the update, then attempt to push it to the apiserver.
|
||||
applyUpdate(pod)
|
||||
if pod, e = podClient.Pods(namespace).Update(pod); e == nil {
|
||||
return
|
||||
}
|
||||
updateErr := e
|
||||
if pod, e = podClient.Pods(namespace).Get(oldPod.Name); e != nil {
|
||||
pod = oldPod
|
||||
}
|
||||
// Only return the error from update
|
||||
return updateErr
|
||||
})
|
||||
// If the error is non-nil the returned pod cannot be trusted, if it is nil, the returned
|
||||
// controller contains the applied update.
|
||||
return pod, err
|
||||
}
|
||||
|
||||
func FindSourceController(r coreclient.ReplicationControllersGetter, namespace, name string) (*api.ReplicationController, error) {
|
||||
list, err := r.ReplicationControllers(namespace).List(api.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for ix := range list.Items {
|
||||
rc := &list.Items[ix]
|
||||
if rc.Annotations != nil && strings.HasPrefix(rc.Annotations[sourceIdAnnotation], name) {
|
||||
return rc, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("couldn't find a replication controller with source id == %s/%s", namespace, name)
|
||||
}
|
||||
78
vendor/k8s.io/kubernetes/pkg/kubectl/rollout_status.go
generated
vendored
78
vendor/k8s.io/kubernetes/pkg/kubectl/rollout_status.go
generated
vendored
|
|
@ -1,78 +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"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion"
|
||||
"k8s.io/kubernetes/pkg/controller/deployment/util"
|
||||
)
|
||||
|
||||
// StatusViewer provides an interface for resources that have rollout status.
|
||||
type StatusViewer interface {
|
||||
Status(namespace, name string, revision int64) (string, bool, error)
|
||||
}
|
||||
|
||||
func StatusViewerFor(kind unversioned.GroupKind, c internalclientset.Interface) (StatusViewer, error) {
|
||||
switch kind {
|
||||
case extensions.Kind("Deployment"):
|
||||
return &DeploymentStatusViewer{c.Extensions()}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("no status viewer has been implemented for %v", kind)
|
||||
}
|
||||
|
||||
type DeploymentStatusViewer struct {
|
||||
c extensionsclient.DeploymentsGetter
|
||||
}
|
||||
|
||||
// Status returns a message describing deployment status, and a bool value indicating if the status is considered done
|
||||
func (s *DeploymentStatusViewer) Status(namespace, name string, revision int64) (string, bool, error) {
|
||||
deployment, err := s.c.Deployments(namespace).Get(name)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
if revision > 0 {
|
||||
deploymentRev, err := util.Revision(deployment)
|
||||
if err != nil {
|
||||
return "", false, fmt.Errorf("cannot get the revision of deployment %q: %v", deployment.Name, err)
|
||||
}
|
||||
if revision != deploymentRev {
|
||||
return "", false, fmt.Errorf("desired revision (%d) is different from the running revision (%d)", revision, deploymentRev)
|
||||
}
|
||||
}
|
||||
if deployment.Generation <= deployment.Status.ObservedGeneration {
|
||||
cond := util.GetDeploymentCondition(deployment.Status, extensions.DeploymentProgressing)
|
||||
if cond != nil && cond.Reason == util.TimedOutReason {
|
||||
return "", false, fmt.Errorf("deployment %q exceeded its progress deadline", name)
|
||||
}
|
||||
if deployment.Status.UpdatedReplicas < deployment.Spec.Replicas {
|
||||
return fmt.Sprintf("Waiting for rollout to finish: %d out of %d new replicas have been updated...\n", deployment.Status.UpdatedReplicas, deployment.Spec.Replicas), false, nil
|
||||
}
|
||||
if deployment.Status.Replicas > deployment.Status.UpdatedReplicas {
|
||||
return fmt.Sprintf("Waiting for rollout to finish: %d old replicas are pending termination...\n", deployment.Status.Replicas-deployment.Status.UpdatedReplicas), false, nil
|
||||
}
|
||||
if deployment.Status.AvailableReplicas < deployment.Status.UpdatedReplicas {
|
||||
return fmt.Sprintf("Waiting for rollout to finish: %d of %d updated replicas are available...\n", deployment.Status.AvailableReplicas, deployment.Status.UpdatedReplicas), false, nil
|
||||
}
|
||||
return fmt.Sprintf("deployment %q successfully rolled out\n", name), true, nil
|
||||
}
|
||||
return fmt.Sprintf("Waiting for deployment spec update to be observed...\n"), false, nil
|
||||
}
|
||||
995
vendor/k8s.io/kubernetes/pkg/kubectl/run.go
generated
vendored
995
vendor/k8s.io/kubernetes/pkg/kubectl/run.go
generated
vendored
|
|
@ -1,995 +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 (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/resource"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/apis/batch"
|
||||
batchv1 "k8s.io/kubernetes/pkg/apis/batch/v1"
|
||||
batchv2alpha1 "k8s.io/kubernetes/pkg/apis/batch/v2alpha1"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/util/validation"
|
||||
)
|
||||
|
||||
type DeploymentV1Beta1 struct{}
|
||||
|
||||
func (DeploymentV1Beta1) ParamNames() []GeneratorParam {
|
||||
return []GeneratorParam{
|
||||
{"labels", false},
|
||||
{"default-name", false},
|
||||
{"name", true},
|
||||
{"replicas", true},
|
||||
{"image", true},
|
||||
{"image-pull-policy", false},
|
||||
{"port", false},
|
||||
{"hostport", false},
|
||||
{"stdin", false},
|
||||
{"tty", false},
|
||||
{"command", false},
|
||||
{"args", false},
|
||||
{"env", false},
|
||||
{"requests", false},
|
||||
{"limits", false},
|
||||
}
|
||||
}
|
||||
|
||||
func (DeploymentV1Beta1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
args, err := getArgs(genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
envs, err := getEnvs(genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params, err := getParams(genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name, err := getName(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
labels, err := getLabels(params, true, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
count, err := strconv.Atoi(params["replicas"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
podSpec, err := makePodSpec(params, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
imagePullPolicy := api.PullPolicy(params["image-pull-policy"])
|
||||
if err = updatePodContainers(params, args, envs, imagePullPolicy, podSpec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := updatePodPorts(params, podSpec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: use versioned types for generators so that we don't need to
|
||||
// set default values manually (see issue #17384)
|
||||
deployment := extensions.Deployment{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: name,
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: extensions.DeploymentSpec{
|
||||
Replicas: int32(count),
|
||||
Selector: &unversioned.LabelSelector{MatchLabels: labels},
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: *podSpec,
|
||||
},
|
||||
},
|
||||
}
|
||||
return &deployment, nil
|
||||
}
|
||||
|
||||
func getLabels(params map[string]string, defaultRunLabel bool, name string) (map[string]string, error) {
|
||||
labelString, found := params["labels"]
|
||||
var labels map[string]string
|
||||
var err error
|
||||
if found && len(labelString) > 0 {
|
||||
labels, err = ParseLabels(labelString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if defaultRunLabel {
|
||||
labels = map[string]string{
|
||||
"run": name,
|
||||
}
|
||||
}
|
||||
return labels, nil
|
||||
}
|
||||
|
||||
func getName(params map[string]string) (string, error) {
|
||||
name, found := params["name"]
|
||||
if !found || len(name) == 0 {
|
||||
name, found = params["default-name"]
|
||||
if !found || len(name) == 0 {
|
||||
return "", fmt.Errorf("'name' is a required parameter.")
|
||||
}
|
||||
}
|
||||
return name, nil
|
||||
}
|
||||
|
||||
func getParams(genericParams map[string]interface{}) (map[string]string, error) {
|
||||
params := map[string]string{}
|
||||
for key, value := range genericParams {
|
||||
strVal, isString := value.(string)
|
||||
if !isString {
|
||||
return nil, fmt.Errorf("expected string, saw %v for '%s'", value, key)
|
||||
}
|
||||
params[key] = strVal
|
||||
}
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func getArgs(genericParams map[string]interface{}) ([]string, error) {
|
||||
args := []string{}
|
||||
val, found := genericParams["args"]
|
||||
if found {
|
||||
var isArray bool
|
||||
args, isArray = val.([]string)
|
||||
if !isArray {
|
||||
return nil, fmt.Errorf("expected []string, found: %v", val)
|
||||
}
|
||||
delete(genericParams, "args")
|
||||
}
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func getEnvs(genericParams map[string]interface{}) ([]api.EnvVar, error) {
|
||||
var envs []api.EnvVar
|
||||
envStrings, found := genericParams["env"]
|
||||
if found {
|
||||
if envStringArray, isArray := envStrings.([]string); isArray {
|
||||
var err error
|
||||
envs, err = parseEnvs(envStringArray)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
delete(genericParams, "env")
|
||||
} else {
|
||||
return nil, fmt.Errorf("expected []string, found: %v", envStrings)
|
||||
}
|
||||
}
|
||||
return envs, nil
|
||||
}
|
||||
|
||||
func getV1Envs(genericParams map[string]interface{}) ([]v1.EnvVar, error) {
|
||||
var envs []v1.EnvVar
|
||||
envStrings, found := genericParams["env"]
|
||||
if found {
|
||||
if envStringArray, isArray := envStrings.([]string); isArray {
|
||||
var err error
|
||||
envs, err = parseV1Envs(envStringArray)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
delete(genericParams, "env")
|
||||
} else {
|
||||
return nil, fmt.Errorf("expected []string, found: %v", envStrings)
|
||||
}
|
||||
}
|
||||
return envs, nil
|
||||
}
|
||||
|
||||
type JobV1Beta1 struct{}
|
||||
|
||||
func (JobV1Beta1) ParamNames() []GeneratorParam {
|
||||
return []GeneratorParam{
|
||||
{"labels", false},
|
||||
{"default-name", false},
|
||||
{"name", true},
|
||||
{"image", true},
|
||||
{"image-pull-policy", false},
|
||||
{"port", false},
|
||||
{"hostport", false},
|
||||
{"stdin", false},
|
||||
{"leave-stdin-open", false},
|
||||
{"tty", false},
|
||||
{"command", false},
|
||||
{"args", false},
|
||||
{"env", false},
|
||||
{"requests", false},
|
||||
{"limits", false},
|
||||
{"restart", false},
|
||||
}
|
||||
}
|
||||
|
||||
func (JobV1Beta1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
args, err := getArgs(genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
envs, err := getEnvs(genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params, err := getParams(genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name, err := getName(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
labels, err := getLabels(params, true, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
podSpec, err := makePodSpec(params, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
imagePullPolicy := api.PullPolicy(params["image-pull-policy"])
|
||||
if err = updatePodContainers(params, args, envs, imagePullPolicy, podSpec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
leaveStdinOpen, err := GetBool(params, "leave-stdin-open", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
podSpec.Containers[0].StdinOnce = !leaveStdinOpen && podSpec.Containers[0].Stdin
|
||||
|
||||
if err := updatePodPorts(params, podSpec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
restartPolicy := api.RestartPolicy(params["restart"])
|
||||
if len(restartPolicy) == 0 {
|
||||
restartPolicy = api.RestartPolicyNever
|
||||
}
|
||||
podSpec.RestartPolicy = restartPolicy
|
||||
|
||||
job := batch.Job{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: name,
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: batch.JobSpec{
|
||||
Selector: &unversioned.LabelSelector{
|
||||
MatchLabels: labels,
|
||||
},
|
||||
ManualSelector: newBool(true),
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: *podSpec,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return &job, nil
|
||||
}
|
||||
|
||||
type JobV1 struct{}
|
||||
|
||||
func (JobV1) ParamNames() []GeneratorParam {
|
||||
return []GeneratorParam{
|
||||
{"labels", false},
|
||||
{"default-name", false},
|
||||
{"name", true},
|
||||
{"image", true},
|
||||
{"image-pull-policy", false},
|
||||
{"port", false},
|
||||
{"hostport", false},
|
||||
{"stdin", false},
|
||||
{"leave-stdin-open", false},
|
||||
{"tty", false},
|
||||
{"command", false},
|
||||
{"args", false},
|
||||
{"env", false},
|
||||
{"requests", false},
|
||||
{"limits", false},
|
||||
{"restart", false},
|
||||
}
|
||||
}
|
||||
|
||||
func (JobV1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
args, err := getArgs(genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
envs, err := getV1Envs(genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params, err := getParams(genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name, err := getName(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
labels, err := getLabels(params, true, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
podSpec, err := makeV1PodSpec(params, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
imagePullPolicy := v1.PullPolicy(params["image-pull-policy"])
|
||||
if err = updateV1PodContainers(params, args, envs, imagePullPolicy, podSpec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
leaveStdinOpen, err := GetBool(params, "leave-stdin-open", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
podSpec.Containers[0].StdinOnce = !leaveStdinOpen && podSpec.Containers[0].Stdin
|
||||
|
||||
if err := updateV1PodPorts(params, podSpec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
restartPolicy := v1.RestartPolicy(params["restart"])
|
||||
if len(restartPolicy) == 0 {
|
||||
restartPolicy = v1.RestartPolicyNever
|
||||
}
|
||||
podSpec.RestartPolicy = restartPolicy
|
||||
|
||||
job := batchv1.Job{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: name,
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: batchv1.JobSpec{
|
||||
Template: v1.PodTemplateSpec{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: *podSpec,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return &job, nil
|
||||
}
|
||||
|
||||
type CronJobV2Alpha1 struct{}
|
||||
|
||||
func (CronJobV2Alpha1) ParamNames() []GeneratorParam {
|
||||
return []GeneratorParam{
|
||||
{"labels", false},
|
||||
{"default-name", false},
|
||||
{"name", true},
|
||||
{"image", true},
|
||||
{"image-pull-policy", false},
|
||||
{"port", false},
|
||||
{"hostport", false},
|
||||
{"stdin", false},
|
||||
{"leave-stdin-open", false},
|
||||
{"tty", false},
|
||||
{"command", false},
|
||||
{"args", false},
|
||||
{"env", false},
|
||||
{"requests", false},
|
||||
{"limits", false},
|
||||
{"restart", false},
|
||||
{"schedule", true},
|
||||
}
|
||||
}
|
||||
|
||||
func (CronJobV2Alpha1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
args, err := getArgs(genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
envs, err := getV1Envs(genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params, err := getParams(genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name, err := getName(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
labels, err := getLabels(params, true, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
podSpec, err := makeV1PodSpec(params, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
imagePullPolicy := v1.PullPolicy(params["image-pull-policy"])
|
||||
if err = updateV1PodContainers(params, args, envs, imagePullPolicy, podSpec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
leaveStdinOpen, err := GetBool(params, "leave-stdin-open", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
podSpec.Containers[0].StdinOnce = !leaveStdinOpen && podSpec.Containers[0].Stdin
|
||||
|
||||
if err := updateV1PodPorts(params, podSpec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
restartPolicy := v1.RestartPolicy(params["restart"])
|
||||
if len(restartPolicy) == 0 {
|
||||
restartPolicy = v1.RestartPolicyNever
|
||||
}
|
||||
podSpec.RestartPolicy = restartPolicy
|
||||
|
||||
cronJob := batchv2alpha1.CronJob{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: name,
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: batchv2alpha1.CronJobSpec{
|
||||
Schedule: params["schedule"],
|
||||
ConcurrencyPolicy: batchv2alpha1.AllowConcurrent,
|
||||
JobTemplate: batchv2alpha1.JobTemplateSpec{
|
||||
Spec: batchv2alpha1.JobSpec{
|
||||
Template: v1.PodTemplateSpec{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: *podSpec,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return &cronJob, nil
|
||||
}
|
||||
|
||||
type BasicReplicationController struct{}
|
||||
|
||||
func (BasicReplicationController) ParamNames() []GeneratorParam {
|
||||
return []GeneratorParam{
|
||||
{"labels", false},
|
||||
{"default-name", false},
|
||||
{"name", true},
|
||||
{"replicas", true},
|
||||
{"image", true},
|
||||
{"image-pull-policy", false},
|
||||
{"port", false},
|
||||
{"hostport", false},
|
||||
{"stdin", false},
|
||||
{"tty", false},
|
||||
{"command", false},
|
||||
{"args", false},
|
||||
{"env", false},
|
||||
{"requests", false},
|
||||
{"limits", false},
|
||||
}
|
||||
}
|
||||
|
||||
// populateResourceList takes strings of form <resourceName1>=<value1>,<resourceName1>=<value2>
|
||||
func populateResourceList(spec string) (api.ResourceList, error) {
|
||||
// empty input gets a nil response to preserve generator test expected behaviors
|
||||
if spec == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
result := api.ResourceList{}
|
||||
resourceStatements := strings.Split(spec, ",")
|
||||
for _, resourceStatement := range resourceStatements {
|
||||
parts := strings.Split(resourceStatement, "=")
|
||||
if len(parts) != 2 {
|
||||
return nil, fmt.Errorf("Invalid argument syntax %v, expected <resource>=<value>", resourceStatement)
|
||||
}
|
||||
resourceName := api.ResourceName(parts[0])
|
||||
resourceQuantity, err := resource.ParseQuantity(parts[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result[resourceName] = resourceQuantity
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// populateResourceList takes strings of form <resourceName1>=<value1>,<resourceName1>=<value2>
|
||||
func populateV1ResourceList(spec string) (v1.ResourceList, error) {
|
||||
// empty input gets a nil response to preserve generator test expected behaviors
|
||||
if spec == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
result := v1.ResourceList{}
|
||||
resourceStatements := strings.Split(spec, ",")
|
||||
for _, resourceStatement := range resourceStatements {
|
||||
parts := strings.Split(resourceStatement, "=")
|
||||
if len(parts) != 2 {
|
||||
return nil, fmt.Errorf("Invalid argument syntax %v, expected <resource>=<value>", resourceStatement)
|
||||
}
|
||||
resourceName := v1.ResourceName(parts[0])
|
||||
resourceQuantity, err := resource.ParseQuantity(parts[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result[resourceName] = resourceQuantity
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// HandleResourceRequirements parses the limits and requests parameters if specified
|
||||
func HandleResourceRequirements(params map[string]string) (api.ResourceRequirements, error) {
|
||||
result := api.ResourceRequirements{}
|
||||
limits, err := populateResourceList(params["limits"])
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
result.Limits = limits
|
||||
requests, err := populateResourceList(params["requests"])
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
result.Requests = requests
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// HandleResourceRequirements parses the limits and requests parameters if specified
|
||||
func handleV1ResourceRequirements(params map[string]string) (v1.ResourceRequirements, error) {
|
||||
result := v1.ResourceRequirements{}
|
||||
limits, err := populateV1ResourceList(params["limits"])
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
result.Limits = limits
|
||||
requests, err := populateV1ResourceList(params["requests"])
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
result.Requests = requests
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func makePodSpec(params map[string]string, name string) (*api.PodSpec, error) {
|
||||
stdin, err := GetBool(params, "stdin", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tty, err := GetBool(params, "tty", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resourceRequirements, err := HandleResourceRequirements(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
spec := api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
{
|
||||
Name: name,
|
||||
Image: params["image"],
|
||||
Stdin: stdin,
|
||||
TTY: tty,
|
||||
Resources: resourceRequirements,
|
||||
},
|
||||
},
|
||||
}
|
||||
return &spec, nil
|
||||
}
|
||||
|
||||
func makeV1PodSpec(params map[string]string, name string) (*v1.PodSpec, error) {
|
||||
stdin, err := GetBool(params, "stdin", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tty, err := GetBool(params, "tty", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resourceRequirements, err := handleV1ResourceRequirements(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
spec := v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: name,
|
||||
Image: params["image"],
|
||||
Stdin: stdin,
|
||||
TTY: tty,
|
||||
Resources: resourceRequirements,
|
||||
},
|
||||
},
|
||||
}
|
||||
return &spec, nil
|
||||
}
|
||||
|
||||
func (BasicReplicationController) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
args, err := getArgs(genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
envs, err := getEnvs(genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params, err := getParams(genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name, err := getName(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
labels, err := getLabels(params, true, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
count, err := strconv.Atoi(params["replicas"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
podSpec, err := makePodSpec(params, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
imagePullPolicy := api.PullPolicy(params["image-pull-policy"])
|
||||
if err = updatePodContainers(params, args, envs, imagePullPolicy, podSpec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := updatePodPorts(params, podSpec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
controller := api.ReplicationController{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: name,
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: api.ReplicationControllerSpec{
|
||||
Replicas: int32(count),
|
||||
Selector: labels,
|
||||
Template: &api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: *podSpec,
|
||||
},
|
||||
},
|
||||
}
|
||||
return &controller, nil
|
||||
}
|
||||
|
||||
func updatePodContainers(params map[string]string, args []string, envs []api.EnvVar, imagePullPolicy api.PullPolicy, podSpec *api.PodSpec) error {
|
||||
if len(args) > 0 {
|
||||
command, err := GetBool(params, "command", false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if command {
|
||||
podSpec.Containers[0].Command = args
|
||||
} else {
|
||||
podSpec.Containers[0].Args = args
|
||||
}
|
||||
}
|
||||
|
||||
if len(envs) > 0 {
|
||||
podSpec.Containers[0].Env = envs
|
||||
}
|
||||
|
||||
if len(imagePullPolicy) > 0 {
|
||||
// imagePullPolicy should be valid here since we have verified it before.
|
||||
podSpec.Containers[0].ImagePullPolicy = imagePullPolicy
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateV1PodContainers(params map[string]string, args []string, envs []v1.EnvVar, imagePullPolicy v1.PullPolicy, podSpec *v1.PodSpec) error {
|
||||
if len(args) > 0 {
|
||||
command, err := GetBool(params, "command", false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if command {
|
||||
podSpec.Containers[0].Command = args
|
||||
} else {
|
||||
podSpec.Containers[0].Args = args
|
||||
}
|
||||
}
|
||||
|
||||
if len(envs) > 0 {
|
||||
podSpec.Containers[0].Env = envs
|
||||
}
|
||||
|
||||
if len(imagePullPolicy) > 0 {
|
||||
// imagePullPolicy should be valid here since we have verified it before.
|
||||
podSpec.Containers[0].ImagePullPolicy = imagePullPolicy
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updatePodPorts(params map[string]string, podSpec *api.PodSpec) (err error) {
|
||||
port := -1
|
||||
hostPort := -1
|
||||
if len(params["port"]) > 0 {
|
||||
port, err = strconv.Atoi(params["port"])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(params["hostport"]) > 0 {
|
||||
hostPort, err = strconv.Atoi(params["hostport"])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if hostPort > 0 && port < 0 {
|
||||
return fmt.Errorf("--hostport requires --port to be specified")
|
||||
}
|
||||
}
|
||||
|
||||
// Don't include the port if it was not specified.
|
||||
if len(params["port"]) > 0 {
|
||||
podSpec.Containers[0].Ports = []api.ContainerPort{
|
||||
{
|
||||
ContainerPort: int32(port),
|
||||
},
|
||||
}
|
||||
if hostPort > 0 {
|
||||
podSpec.Containers[0].Ports[0].HostPort = int32(hostPort)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateV1PodPorts(params map[string]string, podSpec *v1.PodSpec) (err error) {
|
||||
port := -1
|
||||
hostPort := -1
|
||||
if len(params["port"]) > 0 {
|
||||
port, err = strconv.Atoi(params["port"])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(params["hostport"]) > 0 {
|
||||
hostPort, err = strconv.Atoi(params["hostport"])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if hostPort > 0 && port < 0 {
|
||||
return fmt.Errorf("--hostport requires --port to be specified")
|
||||
}
|
||||
}
|
||||
|
||||
// Don't include the port if it was not specified.
|
||||
if len(params["port"]) > 0 {
|
||||
podSpec.Containers[0].Ports = []v1.ContainerPort{
|
||||
{
|
||||
ContainerPort: int32(port),
|
||||
},
|
||||
}
|
||||
if hostPort > 0 {
|
||||
podSpec.Containers[0].Ports[0].HostPort = int32(hostPort)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type BasicPod struct{}
|
||||
|
||||
func (BasicPod) ParamNames() []GeneratorParam {
|
||||
return []GeneratorParam{
|
||||
{"labels", false},
|
||||
{"default-name", false},
|
||||
{"name", true},
|
||||
{"image", true},
|
||||
{"image-pull-policy", false},
|
||||
{"port", false},
|
||||
{"hostport", false},
|
||||
{"stdin", false},
|
||||
{"leave-stdin-open", false},
|
||||
{"tty", false},
|
||||
{"restart", false},
|
||||
{"command", false},
|
||||
{"args", false},
|
||||
{"env", false},
|
||||
{"requests", false},
|
||||
{"limits", false},
|
||||
}
|
||||
}
|
||||
|
||||
func (BasicPod) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
args, err := getArgs(genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
envs, err := getEnvs(genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params, err := getParams(genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name, err := getName(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
labels, err := getLabels(params, false, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stdin, err := GetBool(params, "stdin", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
leaveStdinOpen, err := GetBool(params, "leave-stdin-open", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tty, err := GetBool(params, "tty", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resourceRequirements, err := HandleResourceRequirements(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
restartPolicy := api.RestartPolicy(params["restart"])
|
||||
if len(restartPolicy) == 0 {
|
||||
restartPolicy = api.RestartPolicyAlways
|
||||
}
|
||||
// TODO: Figure out why we set ImagePullPolicy here, whether we can make it
|
||||
// consistent with the other places imagePullPolicy is set using flag.
|
||||
pod := api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: name,
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
{
|
||||
Name: name,
|
||||
Image: params["image"],
|
||||
ImagePullPolicy: api.PullIfNotPresent,
|
||||
Stdin: stdin,
|
||||
StdinOnce: !leaveStdinOpen && stdin,
|
||||
TTY: tty,
|
||||
Resources: resourceRequirements,
|
||||
},
|
||||
},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
RestartPolicy: restartPolicy,
|
||||
},
|
||||
}
|
||||
imagePullPolicy := api.PullPolicy(params["image-pull-policy"])
|
||||
if err = updatePodContainers(params, args, envs, imagePullPolicy, &pod.Spec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := updatePodPorts(params, &pod.Spec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pod, nil
|
||||
}
|
||||
|
||||
func parseEnvs(envArray []string) ([]api.EnvVar, error) {
|
||||
envs := make([]api.EnvVar, 0, len(envArray))
|
||||
for _, env := range envArray {
|
||||
pos := strings.Index(env, "=")
|
||||
if pos == -1 {
|
||||
return nil, fmt.Errorf("invalid env: %v", env)
|
||||
}
|
||||
name := env[:pos]
|
||||
value := env[pos+1:]
|
||||
if len(name) == 0 {
|
||||
return nil, fmt.Errorf("invalid env: %v", env)
|
||||
}
|
||||
if len(validation.IsCIdentifier(name)) != 0 {
|
||||
return nil, fmt.Errorf("invalid env: %v", env)
|
||||
}
|
||||
envVar := api.EnvVar{Name: name, Value: value}
|
||||
envs = append(envs, envVar)
|
||||
}
|
||||
return envs, nil
|
||||
}
|
||||
|
||||
func parseV1Envs(envArray []string) ([]v1.EnvVar, error) {
|
||||
envs := []v1.EnvVar{}
|
||||
for _, env := range envArray {
|
||||
pos := strings.Index(env, "=")
|
||||
if pos == -1 {
|
||||
return nil, fmt.Errorf("invalid env: %v", env)
|
||||
}
|
||||
name := env[:pos]
|
||||
value := env[pos+1:]
|
||||
if len(name) == 0 || len(validation.IsCIdentifier(name)) != 0 {
|
||||
return nil, fmt.Errorf("invalid env: %v", env)
|
||||
}
|
||||
envVar := v1.EnvVar{Name: name, Value: value}
|
||||
envs = append(envs, envVar)
|
||||
}
|
||||
return envs, nil
|
||||
}
|
||||
|
||||
func newBool(val bool) *bool {
|
||||
p := new(bool)
|
||||
*p = val
|
||||
return p
|
||||
}
|
||||
509
vendor/k8s.io/kubernetes/pkg/kubectl/scale.go
generated
vendored
509
vendor/k8s.io/kubernetes/pkg/kubectl/scale.go
generated
vendored
|
|
@ -1,509 +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 (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/errors"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/apis/apps"
|
||||
"k8s.io/kubernetes/pkg/apis/batch"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
appsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/apps/internalversion"
|
||||
batchclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/batch/internalversion"
|
||||
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
|
||||
extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion"
|
||||
client "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// Scaler provides an interface for resources that can be scaled.
|
||||
type Scaler interface {
|
||||
// Scale scales the named resource after checking preconditions. It optionally
|
||||
// retries in the event of resource version mismatch (if retry is not nil),
|
||||
// and optionally waits until the status of the resource matches newSize (if wait is not nil)
|
||||
Scale(namespace, name string, newSize uint, preconditions *ScalePrecondition, retry, wait *RetryParams) error
|
||||
// ScaleSimple does a simple one-shot attempt at scaling - not useful on its own, but
|
||||
// a necessary building block for Scale
|
||||
ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint) (updatedResourceVersion string, err error)
|
||||
}
|
||||
|
||||
func ScalerFor(kind unversioned.GroupKind, c internalclientset.Interface) (Scaler, error) {
|
||||
switch kind {
|
||||
case api.Kind("ReplicationController"):
|
||||
return &ReplicationControllerScaler{c.Core()}, nil
|
||||
case extensions.Kind("ReplicaSet"):
|
||||
return &ReplicaSetScaler{c.Extensions()}, nil
|
||||
case extensions.Kind("Job"), batch.Kind("Job"):
|
||||
return &JobScaler{c.Batch()}, nil // Either kind of job can be scaled with Batch interface.
|
||||
case apps.Kind("StatefulSet"):
|
||||
return &StatefulSetScaler{c.Apps()}, nil
|
||||
case extensions.Kind("Deployment"):
|
||||
return &DeploymentScaler{c.Extensions()}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("no scaler has been implemented for %q", kind)
|
||||
}
|
||||
|
||||
// ScalePrecondition describes a condition that must be true for the scale to take place
|
||||
// If CurrentSize == -1, it is ignored.
|
||||
// If CurrentResourceVersion is the empty string, it is ignored.
|
||||
// Otherwise they must equal the values in the resource for it to be valid.
|
||||
type ScalePrecondition struct {
|
||||
Size int
|
||||
ResourceVersion string
|
||||
}
|
||||
|
||||
// A PreconditionError is returned when a resource fails to match
|
||||
// the scale preconditions passed to kubectl.
|
||||
type PreconditionError struct {
|
||||
Precondition string
|
||||
ExpectedValue string
|
||||
ActualValue string
|
||||
}
|
||||
|
||||
func (pe PreconditionError) Error() string {
|
||||
return fmt.Sprintf("Expected %s to be %s, was %s", pe.Precondition, pe.ExpectedValue, pe.ActualValue)
|
||||
}
|
||||
|
||||
type ScaleErrorType int
|
||||
|
||||
const (
|
||||
ScaleGetFailure ScaleErrorType = iota
|
||||
ScaleUpdateFailure
|
||||
ScaleUpdateConflictFailure
|
||||
)
|
||||
|
||||
// A ScaleError is returned when a scale request passes
|
||||
// preconditions but fails to actually scale the controller.
|
||||
type ScaleError struct {
|
||||
FailureType ScaleErrorType
|
||||
ResourceVersion string
|
||||
ActualError error
|
||||
}
|
||||
|
||||
func (c ScaleError) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"Scaling the resource failed with: %v; Current resource version %s",
|
||||
c.ActualError, c.ResourceVersion)
|
||||
}
|
||||
|
||||
// RetryParams encapsulates the retry parameters used by kubectl's scaler.
|
||||
type RetryParams struct {
|
||||
Interval, Timeout time.Duration
|
||||
}
|
||||
|
||||
func NewRetryParams(interval, timeout time.Duration) *RetryParams {
|
||||
return &RetryParams{interval, timeout}
|
||||
}
|
||||
|
||||
// ScaleCondition is a closure around Scale that facilitates retries via util.wait
|
||||
func ScaleCondition(r Scaler, precondition *ScalePrecondition, namespace, name string, count uint, updatedResourceVersion *string) wait.ConditionFunc {
|
||||
return func() (bool, error) {
|
||||
rv, err := r.ScaleSimple(namespace, name, precondition, count)
|
||||
if updatedResourceVersion != nil {
|
||||
*updatedResourceVersion = rv
|
||||
}
|
||||
switch e, _ := err.(ScaleError); err.(type) {
|
||||
case nil:
|
||||
return true, nil
|
||||
case ScaleError:
|
||||
// Retry only on update conflicts.
|
||||
if e.FailureType == ScaleUpdateConflictFailure {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateStatefulSet ensures that the preconditions match. Returns nil if they are valid, an error otherwise.
|
||||
func (precondition *ScalePrecondition) ValidateStatefulSet(ps *apps.StatefulSet) error {
|
||||
if precondition.Size != -1 && int(ps.Spec.Replicas) != precondition.Size {
|
||||
return PreconditionError{"replicas", strconv.Itoa(precondition.Size), strconv.Itoa(int(ps.Spec.Replicas))}
|
||||
}
|
||||
if len(precondition.ResourceVersion) != 0 && ps.ResourceVersion != precondition.ResourceVersion {
|
||||
return PreconditionError{"resource version", precondition.ResourceVersion, ps.ResourceVersion}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateReplicationController ensures that the preconditions match. Returns nil if they are valid, an error otherwise
|
||||
func (precondition *ScalePrecondition) ValidateReplicationController(controller *api.ReplicationController) error {
|
||||
if precondition.Size != -1 && int(controller.Spec.Replicas) != precondition.Size {
|
||||
return PreconditionError{"replicas", strconv.Itoa(precondition.Size), strconv.Itoa(int(controller.Spec.Replicas))}
|
||||
}
|
||||
if len(precondition.ResourceVersion) != 0 && controller.ResourceVersion != precondition.ResourceVersion {
|
||||
return PreconditionError{"resource version", precondition.ResourceVersion, controller.ResourceVersion}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ReplicationControllerScaler struct {
|
||||
c coreclient.ReplicationControllersGetter
|
||||
}
|
||||
|
||||
// ScaleSimple does a simple one-shot attempt at scaling. It returns the
|
||||
// resourceVersion of the replication controller if the update is successful.
|
||||
func (scaler *ReplicationControllerScaler) ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint) (string, error) {
|
||||
controller, err := scaler.c.ReplicationControllers(namespace).Get(name)
|
||||
if err != nil {
|
||||
return "", ScaleError{ScaleGetFailure, "Unknown", err}
|
||||
}
|
||||
if preconditions != nil {
|
||||
if err := preconditions.ValidateReplicationController(controller); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
controller.Spec.Replicas = int32(newSize)
|
||||
updatedRC, err := scaler.c.ReplicationControllers(namespace).Update(controller)
|
||||
if err != nil {
|
||||
if errors.IsConflict(err) {
|
||||
return "", ScaleError{ScaleUpdateConflictFailure, controller.ResourceVersion, err}
|
||||
}
|
||||
return "", ScaleError{ScaleUpdateFailure, controller.ResourceVersion, err}
|
||||
}
|
||||
return updatedRC.ObjectMeta.ResourceVersion, nil
|
||||
}
|
||||
|
||||
// Scale updates a ReplicationController to a new size, with optional precondition check (if preconditions is not nil),
|
||||
// optional retries (if retry is not nil), and then optionally waits for it's replica count to reach the new value
|
||||
// (if wait is not nil).
|
||||
func (scaler *ReplicationControllerScaler) Scale(namespace, name string, newSize uint, preconditions *ScalePrecondition, retry, waitForReplicas *RetryParams) error {
|
||||
if preconditions == nil {
|
||||
preconditions = &ScalePrecondition{-1, ""}
|
||||
}
|
||||
if retry == nil {
|
||||
// Make it try only once, immediately
|
||||
retry = &RetryParams{Interval: time.Millisecond, Timeout: time.Millisecond}
|
||||
}
|
||||
var updatedResourceVersion string
|
||||
cond := ScaleCondition(scaler, preconditions, namespace, name, newSize, &updatedResourceVersion)
|
||||
if err := wait.PollImmediate(retry.Interval, retry.Timeout, cond); err != nil {
|
||||
return err
|
||||
}
|
||||
if waitForReplicas != nil {
|
||||
checkRC := func(rc *api.ReplicationController) bool {
|
||||
if uint(rc.Spec.Replicas) != newSize {
|
||||
// the size is changed by other party. Don't need to wait for the new change to complete.
|
||||
return true
|
||||
}
|
||||
return rc.Status.ObservedGeneration >= rc.Generation && rc.Status.Replicas == rc.Spec.Replicas
|
||||
}
|
||||
// If number of replicas doesn't change, then the update may not event
|
||||
// be sent to underlying databse (we don't send no-op changes).
|
||||
// In such case, <updatedResourceVersion> will have value of the most
|
||||
// recent update (which may be far in the past) so we may get "too old
|
||||
// RV" error from watch or potentially no ReplicationController events
|
||||
// will be deliver, since it may already be in the expected state.
|
||||
// To protect from these two, we first issue Get() to ensure that we
|
||||
// are not already in the expected state.
|
||||
currentRC, err := scaler.c.ReplicationControllers(namespace).Get(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !checkRC(currentRC) {
|
||||
watchOptions := api.ListOptions{
|
||||
FieldSelector: fields.OneTermEqualSelector("metadata.name", name),
|
||||
ResourceVersion: updatedResourceVersion,
|
||||
}
|
||||
watcher, err := scaler.c.ReplicationControllers(namespace).Watch(watchOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = watch.Until(waitForReplicas.Timeout, watcher, func(event watch.Event) (bool, error) {
|
||||
if event.Type != watch.Added && event.Type != watch.Modified {
|
||||
return false, nil
|
||||
}
|
||||
return checkRC(event.Object.(*api.ReplicationController)), nil
|
||||
})
|
||||
if err == wait.ErrWaitTimeout {
|
||||
return fmt.Errorf("timed out waiting for %q to be synced", name)
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateReplicaSet ensures that the preconditions match. Returns nil if they are valid, an error otherwise
|
||||
func (precondition *ScalePrecondition) ValidateReplicaSet(replicaSet *extensions.ReplicaSet) error {
|
||||
if precondition.Size != -1 && int(replicaSet.Spec.Replicas) != precondition.Size {
|
||||
return PreconditionError{"replicas", strconv.Itoa(precondition.Size), strconv.Itoa(int(replicaSet.Spec.Replicas))}
|
||||
}
|
||||
if len(precondition.ResourceVersion) != 0 && replicaSet.ResourceVersion != precondition.ResourceVersion {
|
||||
return PreconditionError{"resource version", precondition.ResourceVersion, replicaSet.ResourceVersion}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ReplicaSetScaler struct {
|
||||
c extensionsclient.ReplicaSetsGetter
|
||||
}
|
||||
|
||||
// ScaleSimple does a simple one-shot attempt at scaling. It returns the
|
||||
// resourceVersion of the replicaset if the update is successful.
|
||||
func (scaler *ReplicaSetScaler) ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint) (string, error) {
|
||||
rs, err := scaler.c.ReplicaSets(namespace).Get(name)
|
||||
if err != nil {
|
||||
return "", ScaleError{ScaleGetFailure, "Unknown", err}
|
||||
}
|
||||
if preconditions != nil {
|
||||
if err := preconditions.ValidateReplicaSet(rs); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
rs.Spec.Replicas = int32(newSize)
|
||||
updatedRS, err := scaler.c.ReplicaSets(namespace).Update(rs)
|
||||
if err != nil {
|
||||
if errors.IsConflict(err) {
|
||||
return "", ScaleError{ScaleUpdateConflictFailure, rs.ResourceVersion, err}
|
||||
}
|
||||
return "", ScaleError{ScaleUpdateFailure, rs.ResourceVersion, err}
|
||||
}
|
||||
return updatedRS.ObjectMeta.ResourceVersion, nil
|
||||
}
|
||||
|
||||
// Scale updates a ReplicaSet to a new size, with optional precondition check (if preconditions is
|
||||
// not nil), optional retries (if retry is not nil), and then optionally waits for it's replica
|
||||
// count to reach the new value (if wait is not nil).
|
||||
func (scaler *ReplicaSetScaler) Scale(namespace, name string, newSize uint, preconditions *ScalePrecondition, retry, waitForReplicas *RetryParams) error {
|
||||
if preconditions == nil {
|
||||
preconditions = &ScalePrecondition{-1, ""}
|
||||
}
|
||||
if retry == nil {
|
||||
// Make it try only once, immediately
|
||||
retry = &RetryParams{Interval: time.Millisecond, Timeout: time.Millisecond}
|
||||
}
|
||||
cond := ScaleCondition(scaler, preconditions, namespace, name, newSize, nil)
|
||||
if err := wait.Poll(retry.Interval, retry.Timeout, cond); err != nil {
|
||||
return err
|
||||
}
|
||||
if waitForReplicas != nil {
|
||||
rs, err := scaler.c.ReplicaSets(namespace).Get(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = wait.Poll(waitForReplicas.Interval, waitForReplicas.Timeout, client.ReplicaSetHasDesiredReplicas(scaler.c, rs))
|
||||
|
||||
if err == wait.ErrWaitTimeout {
|
||||
return fmt.Errorf("timed out waiting for %q to be synced", name)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateJob ensures that the preconditions match. Returns nil if they are valid, an error otherwise.
|
||||
func (precondition *ScalePrecondition) ValidateJob(job *batch.Job) error {
|
||||
if precondition.Size != -1 && job.Spec.Parallelism == nil {
|
||||
return PreconditionError{"parallelism", strconv.Itoa(precondition.Size), "nil"}
|
||||
}
|
||||
if precondition.Size != -1 && int(*job.Spec.Parallelism) != precondition.Size {
|
||||
return PreconditionError{"parallelism", strconv.Itoa(precondition.Size), strconv.Itoa(int(*job.Spec.Parallelism))}
|
||||
}
|
||||
if len(precondition.ResourceVersion) != 0 && job.ResourceVersion != precondition.ResourceVersion {
|
||||
return PreconditionError{"resource version", precondition.ResourceVersion, job.ResourceVersion}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type StatefulSetScaler struct {
|
||||
c appsclient.StatefulSetsGetter
|
||||
}
|
||||
|
||||
// ScaleSimple does a simple one-shot attempt at scaling. It returns the
|
||||
// resourceVersion of the statefulset if the update is successful.
|
||||
func (scaler *StatefulSetScaler) ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint) (string, error) {
|
||||
ps, err := scaler.c.StatefulSets(namespace).Get(name)
|
||||
if err != nil {
|
||||
return "", ScaleError{ScaleGetFailure, "Unknown", err}
|
||||
}
|
||||
if preconditions != nil {
|
||||
if err := preconditions.ValidateStatefulSet(ps); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
ps.Spec.Replicas = int32(newSize)
|
||||
updatedStatefulSet, err := scaler.c.StatefulSets(namespace).Update(ps)
|
||||
if err != nil {
|
||||
if errors.IsConflict(err) {
|
||||
return "", ScaleError{ScaleUpdateConflictFailure, ps.ResourceVersion, err}
|
||||
}
|
||||
return "", ScaleError{ScaleUpdateFailure, ps.ResourceVersion, err}
|
||||
}
|
||||
return updatedStatefulSet.ResourceVersion, nil
|
||||
}
|
||||
|
||||
func (scaler *StatefulSetScaler) Scale(namespace, name string, newSize uint, preconditions *ScalePrecondition, retry, waitForReplicas *RetryParams) error {
|
||||
if preconditions == nil {
|
||||
preconditions = &ScalePrecondition{-1, ""}
|
||||
}
|
||||
if retry == nil {
|
||||
// Make it try only once, immediately
|
||||
retry = &RetryParams{Interval: time.Millisecond, Timeout: time.Millisecond}
|
||||
}
|
||||
cond := ScaleCondition(scaler, preconditions, namespace, name, newSize, nil)
|
||||
if err := wait.Poll(retry.Interval, retry.Timeout, cond); err != nil {
|
||||
return err
|
||||
}
|
||||
if waitForReplicas != nil {
|
||||
job, err := scaler.c.StatefulSets(namespace).Get(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = wait.Poll(waitForReplicas.Interval, waitForReplicas.Timeout, client.StatefulSetHasDesiredPets(scaler.c, job))
|
||||
if err == wait.ErrWaitTimeout {
|
||||
return fmt.Errorf("timed out waiting for %q to be synced", name)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type JobScaler struct {
|
||||
c batchclient.JobsGetter
|
||||
}
|
||||
|
||||
// ScaleSimple is responsible for updating job's parallelism. It returns the
|
||||
// resourceVersion of the job if the update is successful.
|
||||
func (scaler *JobScaler) ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint) (string, error) {
|
||||
job, err := scaler.c.Jobs(namespace).Get(name)
|
||||
if err != nil {
|
||||
return "", ScaleError{ScaleGetFailure, "Unknown", err}
|
||||
}
|
||||
if preconditions != nil {
|
||||
if err := preconditions.ValidateJob(job); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
parallelism := int32(newSize)
|
||||
job.Spec.Parallelism = ¶llelism
|
||||
udpatedJob, err := scaler.c.Jobs(namespace).Update(job)
|
||||
if err != nil {
|
||||
if errors.IsConflict(err) {
|
||||
return "", ScaleError{ScaleUpdateConflictFailure, job.ResourceVersion, err}
|
||||
}
|
||||
return "", ScaleError{ScaleUpdateFailure, job.ResourceVersion, err}
|
||||
}
|
||||
return udpatedJob.ObjectMeta.ResourceVersion, nil
|
||||
}
|
||||
|
||||
// Scale updates a Job to a new size, with optional precondition check (if preconditions is not nil),
|
||||
// optional retries (if retry is not nil), and then optionally waits for parallelism to reach desired
|
||||
// number, which can be less than requested based on job's current progress.
|
||||
func (scaler *JobScaler) Scale(namespace, name string, newSize uint, preconditions *ScalePrecondition, retry, waitForReplicas *RetryParams) error {
|
||||
if preconditions == nil {
|
||||
preconditions = &ScalePrecondition{-1, ""}
|
||||
}
|
||||
if retry == nil {
|
||||
// Make it try only once, immediately
|
||||
retry = &RetryParams{Interval: time.Millisecond, Timeout: time.Millisecond}
|
||||
}
|
||||
cond := ScaleCondition(scaler, preconditions, namespace, name, newSize, nil)
|
||||
if err := wait.Poll(retry.Interval, retry.Timeout, cond); err != nil {
|
||||
return err
|
||||
}
|
||||
if waitForReplicas != nil {
|
||||
job, err := scaler.c.Jobs(namespace).Get(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = wait.Poll(waitForReplicas.Interval, waitForReplicas.Timeout, client.JobHasDesiredParallelism(scaler.c, job))
|
||||
if err == wait.ErrWaitTimeout {
|
||||
return fmt.Errorf("timed out waiting for %q to be synced", name)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateDeployment ensures that the preconditions match. Returns nil if they are valid, an error otherwise.
|
||||
func (precondition *ScalePrecondition) ValidateDeployment(deployment *extensions.Deployment) error {
|
||||
if precondition.Size != -1 && int(deployment.Spec.Replicas) != precondition.Size {
|
||||
return PreconditionError{"replicas", strconv.Itoa(precondition.Size), strconv.Itoa(int(deployment.Spec.Replicas))}
|
||||
}
|
||||
if len(precondition.ResourceVersion) != 0 && deployment.ResourceVersion != precondition.ResourceVersion {
|
||||
return PreconditionError{"resource version", precondition.ResourceVersion, deployment.ResourceVersion}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DeploymentScaler struct {
|
||||
c extensionsclient.DeploymentsGetter
|
||||
}
|
||||
|
||||
// ScaleSimple is responsible for updating a deployment's desired replicas
|
||||
// count. It returns the resourceVersion of the deployment if the update is
|
||||
// successful.
|
||||
func (scaler *DeploymentScaler) ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint) (string, error) {
|
||||
deployment, err := scaler.c.Deployments(namespace).Get(name)
|
||||
if err != nil {
|
||||
return "", ScaleError{ScaleGetFailure, "Unknown", err}
|
||||
}
|
||||
if preconditions != nil {
|
||||
if err := preconditions.ValidateDeployment(deployment); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(madhusudancs): Fix this when Scale group issues are resolved (see issue #18528).
|
||||
// For now I'm falling back to regular Deployment update operation.
|
||||
deployment.Spec.Replicas = int32(newSize)
|
||||
updatedDeployment, err := scaler.c.Deployments(namespace).Update(deployment)
|
||||
if err != nil {
|
||||
if errors.IsConflict(err) {
|
||||
return "", ScaleError{ScaleUpdateConflictFailure, deployment.ResourceVersion, err}
|
||||
}
|
||||
return "", ScaleError{ScaleUpdateFailure, deployment.ResourceVersion, err}
|
||||
}
|
||||
return updatedDeployment.ObjectMeta.ResourceVersion, nil
|
||||
}
|
||||
|
||||
// Scale updates a deployment to a new size, with optional precondition check (if preconditions is not nil),
|
||||
// optional retries (if retry is not nil), and then optionally waits for the status to reach desired count.
|
||||
func (scaler *DeploymentScaler) Scale(namespace, name string, newSize uint, preconditions *ScalePrecondition, retry, waitForReplicas *RetryParams) error {
|
||||
if preconditions == nil {
|
||||
preconditions = &ScalePrecondition{-1, ""}
|
||||
}
|
||||
if retry == nil {
|
||||
// Make it try only once, immediately
|
||||
retry = &RetryParams{Interval: time.Millisecond, Timeout: time.Millisecond}
|
||||
}
|
||||
cond := ScaleCondition(scaler, preconditions, namespace, name, newSize, nil)
|
||||
if err := wait.Poll(retry.Interval, retry.Timeout, cond); err != nil {
|
||||
return err
|
||||
}
|
||||
if waitForReplicas != nil {
|
||||
deployment, err := scaler.c.Deployments(namespace).Get(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = wait.Poll(waitForReplicas.Interval, waitForReplicas.Timeout, client.DeploymentHasDesiredReplicas(scaler.c, deployment))
|
||||
if err == wait.ErrWaitTimeout {
|
||||
return fmt.Errorf("timed out waiting for %q to be synced", name)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
208
vendor/k8s.io/kubernetes/pkg/kubectl/secret.go
generated
vendored
208
vendor/k8s.io/kubernetes/pkg/kubectl/secret.go
generated
vendored
|
|
@ -1,208 +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"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/util/validation"
|
||||
)
|
||||
|
||||
// SecretGeneratorV1 supports stable generation of an opaque secret
|
||||
type SecretGeneratorV1 struct {
|
||||
// Name of secret (required)
|
||||
Name string
|
||||
// Type of secret (optional)
|
||||
Type string
|
||||
// FileSources to derive the secret from (optional)
|
||||
FileSources []string
|
||||
// LiteralSources to derive the secret from (optional)
|
||||
LiteralSources []string
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameter injection
|
||||
var _ Generator = &SecretGeneratorV1{}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters specified during construction
|
||||
var _ StructuredGenerator = &SecretGeneratorV1{}
|
||||
|
||||
// Generate returns a secret using the specified parameters
|
||||
func (s SecretGeneratorV1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
err := ValidateParams(s.ParamNames(), genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
delegate := &SecretGeneratorV1{}
|
||||
fromFileStrings, found := genericParams["from-file"]
|
||||
if found {
|
||||
fromFileArray, isArray := fromFileStrings.([]string)
|
||||
if !isArray {
|
||||
return nil, fmt.Errorf("expected []string, found :%v", fromFileStrings)
|
||||
}
|
||||
delegate.FileSources = fromFileArray
|
||||
delete(genericParams, "from-file")
|
||||
}
|
||||
fromLiteralStrings, found := genericParams["from-literal"]
|
||||
if found {
|
||||
fromLiteralArray, isArray := fromLiteralStrings.([]string)
|
||||
if !isArray {
|
||||
return nil, fmt.Errorf("expected []string, found :%v", fromFileStrings)
|
||||
}
|
||||
delegate.LiteralSources = fromLiteralArray
|
||||
delete(genericParams, "from-literal")
|
||||
}
|
||||
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.Type = params["type"]
|
||||
return delegate.StructuredGenerate()
|
||||
}
|
||||
|
||||
// ParamNames returns the set of supported input parameters when using the parameter injection generator pattern
|
||||
func (s SecretGeneratorV1) ParamNames() []GeneratorParam {
|
||||
return []GeneratorParam{
|
||||
{"name", true},
|
||||
{"type", false},
|
||||
{"from-file", false},
|
||||
{"from-literal", false},
|
||||
{"force", false},
|
||||
}
|
||||
}
|
||||
|
||||
// StructuredGenerate outputs a secret object using the configured fields
|
||||
func (s SecretGeneratorV1) StructuredGenerate() (runtime.Object, error) {
|
||||
if err := s.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
secret := &api.Secret{}
|
||||
secret.Name = s.Name
|
||||
secret.Data = map[string][]byte{}
|
||||
if len(s.Type) > 0 {
|
||||
secret.Type = api.SecretType(s.Type)
|
||||
}
|
||||
if len(s.FileSources) > 0 {
|
||||
if err := handleFromFileSources(secret, s.FileSources); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if len(s.LiteralSources) > 0 {
|
||||
if err := handleFromLiteralSources(secret, s.LiteralSources); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return secret, nil
|
||||
}
|
||||
|
||||
// validate validates required fields are set to support structured generation
|
||||
func (s SecretGeneratorV1) validate() error {
|
||||
if len(s.Name) == 0 {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleFromLiteralSources adds the specified literal source information into the provided secret
|
||||
func handleFromLiteralSources(secret *api.Secret, literalSources []string) error {
|
||||
for _, literalSource := range literalSources {
|
||||
keyName, value, err := parseLiteralSource(literalSource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = addKeyFromLiteralToSecret(secret, keyName, []byte(value))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleFromFileSources adds the specified file source information into the provided secret
|
||||
func handleFromFileSources(secret *api.Secret, fileSources []string) error {
|
||||
for _, fileSource := range fileSources {
|
||||
keyName, filePath, err := parseFileSource(fileSource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
switch err := err.(type) {
|
||||
case *os.PathError:
|
||||
return fmt.Errorf("error reading %s: %v", filePath, err.Err)
|
||||
default:
|
||||
return fmt.Errorf("error reading %s: %v", filePath, err)
|
||||
}
|
||||
}
|
||||
if info.IsDir() {
|
||||
if strings.Contains(fileSource, "=") {
|
||||
return fmt.Errorf("cannot give a key name for a directory path.")
|
||||
}
|
||||
fileList, err := ioutil.ReadDir(filePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error listing files in %s: %v", filePath, err)
|
||||
}
|
||||
for _, item := range fileList {
|
||||
itemPath := path.Join(filePath, item.Name())
|
||||
if item.Mode().IsRegular() {
|
||||
keyName = item.Name()
|
||||
err = addKeyFromFileToSecret(secret, keyName, itemPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = addKeyFromFileToSecret(secret, keyName, filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func addKeyFromFileToSecret(secret *api.Secret, keyName, filePath string) error {
|
||||
data, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return addKeyFromLiteralToSecret(secret, keyName, data)
|
||||
}
|
||||
|
||||
func addKeyFromLiteralToSecret(secret *api.Secret, keyName string, data []byte) error {
|
||||
if errs := validation.IsConfigMapKey(keyName); len(errs) != 0 {
|
||||
return fmt.Errorf("%q is not a valid key name for a Secret: %s", keyName, strings.Join(errs, ";"))
|
||||
}
|
||||
|
||||
if _, entryExists := secret.Data[keyName]; entryExists {
|
||||
return fmt.Errorf("cannot add key %s, another key by that name already exists: %v.", keyName, secret.Data)
|
||||
}
|
||||
secret.Data[keyName] = data
|
||||
return nil
|
||||
}
|
||||
131
vendor/k8s.io/kubernetes/pkg/kubectl/secret_for_docker_registry.go
generated
vendored
131
vendor/k8s.io/kubernetes/pkg/kubectl/secret_for_docker_registry.go
generated
vendored
|
|
@ -1,131 +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 (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/credentialprovider"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
// SecretForDockerRegistryGeneratorV1 supports stable generation of a docker registry secret
|
||||
type SecretForDockerRegistryGeneratorV1 struct {
|
||||
// Name of secret (required)
|
||||
Name string
|
||||
// Username for registry (required)
|
||||
Username string
|
||||
// Email for registry (required)
|
||||
Email string
|
||||
// Password for registry (required)
|
||||
Password string
|
||||
// Server for registry (required)
|
||||
Server string
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameter injection
|
||||
var _ Generator = &SecretForDockerRegistryGeneratorV1{}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters specified during construction
|
||||
var _ StructuredGenerator = &SecretForDockerRegistryGeneratorV1{}
|
||||
|
||||
// Generate returns a secret using the specified parameters
|
||||
func (s SecretForDockerRegistryGeneratorV1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
err := ValidateParams(s.ParamNames(), genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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 := &SecretForDockerRegistryGeneratorV1{
|
||||
Name: params["name"],
|
||||
Username: params["docker-username"],
|
||||
Email: params["docker-email"],
|
||||
Password: params["docker-password"],
|
||||
Server: params["docker-server"],
|
||||
}
|
||||
return delegate.StructuredGenerate()
|
||||
}
|
||||
|
||||
// StructuredGenerate outputs a secret object using the configured fields
|
||||
func (s SecretForDockerRegistryGeneratorV1) StructuredGenerate() (runtime.Object, error) {
|
||||
if err := s.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dockercfgContent, err := handleDockercfgContent(s.Username, s.Password, s.Email, s.Server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
secret := &api.Secret{}
|
||||
secret.Name = s.Name
|
||||
secret.Type = api.SecretTypeDockercfg
|
||||
secret.Data = map[string][]byte{}
|
||||
secret.Data[api.DockerConfigKey] = dockercfgContent
|
||||
return secret, nil
|
||||
}
|
||||
|
||||
// ParamNames returns the set of supported input parameters when using the parameter injection generator pattern
|
||||
func (s SecretForDockerRegistryGeneratorV1) ParamNames() []GeneratorParam {
|
||||
return []GeneratorParam{
|
||||
{"name", true},
|
||||
{"docker-username", true},
|
||||
{"docker-email", true},
|
||||
{"docker-password", true},
|
||||
{"docker-server", true},
|
||||
}
|
||||
}
|
||||
|
||||
// validate validates required fields are set to support structured generation
|
||||
func (s SecretForDockerRegistryGeneratorV1) validate() error {
|
||||
if len(s.Name) == 0 {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
if len(s.Username) == 0 {
|
||||
return fmt.Errorf("username must be specified")
|
||||
}
|
||||
if len(s.Email) == 0 {
|
||||
return fmt.Errorf("email must be specified")
|
||||
}
|
||||
if len(s.Password) == 0 {
|
||||
return fmt.Errorf("password must be specified")
|
||||
}
|
||||
if len(s.Server) == 0 {
|
||||
return fmt.Errorf("server must be specified")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleDockercfgContent serializes a dockercfg json file
|
||||
func handleDockercfgContent(username, password, email, server string) ([]byte, error) {
|
||||
dockercfgAuth := credentialprovider.DockerConfigEntry{
|
||||
Username: username,
|
||||
Password: password,
|
||||
Email: email,
|
||||
}
|
||||
|
||||
dockerCfg := map[string]credentialprovider.DockerConfigEntry{server: dockercfgAuth}
|
||||
|
||||
return json.Marshal(dockerCfg)
|
||||
}
|
||||
124
vendor/k8s.io/kubernetes/pkg/kubectl/secret_for_tls.go
generated
vendored
124
vendor/k8s.io/kubernetes/pkg/kubectl/secret_for_tls.go
generated
vendored
|
|
@ -1,124 +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 (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
// SecretForTLSGeneratorV1 supports stable generation of a TLS secret.
|
||||
type SecretForTLSGeneratorV1 struct {
|
||||
// Name is the name of this TLS secret.
|
||||
Name string
|
||||
// Key is the path to the user's private key.
|
||||
Key string
|
||||
// Cert is the path to the user's public key certificate.
|
||||
Cert string
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameter injection
|
||||
var _ Generator = &SecretForTLSGeneratorV1{}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters specified during construction
|
||||
var _ StructuredGenerator = &SecretForTLSGeneratorV1{}
|
||||
|
||||
// Generate returns a secret using the specified parameters
|
||||
func (s SecretForTLSGeneratorV1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
err := ValidateParams(s.ParamNames(), genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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 := &SecretForTLSGeneratorV1{
|
||||
Name: params["name"],
|
||||
Key: params["key"],
|
||||
Cert: params["cert"],
|
||||
}
|
||||
return delegate.StructuredGenerate()
|
||||
}
|
||||
|
||||
// StructuredGenerate outputs a secret object using the configured fields
|
||||
func (s SecretForTLSGeneratorV1) StructuredGenerate() (runtime.Object, error) {
|
||||
if err := s.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsCrt, err := readFile(s.Cert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsKey, err := readFile(s.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
secret := &api.Secret{}
|
||||
secret.Name = s.Name
|
||||
secret.Type = api.SecretTypeTLS
|
||||
secret.Data = map[string][]byte{}
|
||||
secret.Data[api.TLSCertKey] = []byte(tlsCrt)
|
||||
secret.Data[api.TLSPrivateKeyKey] = []byte(tlsKey)
|
||||
return secret, nil
|
||||
}
|
||||
|
||||
// readFile just reads a file into a byte array.
|
||||
func readFile(file string) ([]byte, error) {
|
||||
b, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return []byte{}, fmt.Errorf("Cannot read file %v, %v", file, err)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// ParamNames returns the set of supported input parameters when using the parameter injection generator pattern
|
||||
func (s SecretForTLSGeneratorV1) ParamNames() []GeneratorParam {
|
||||
return []GeneratorParam{
|
||||
{"name", true},
|
||||
{"key", true},
|
||||
{"cert", true},
|
||||
}
|
||||
}
|
||||
|
||||
// validate validates required fields are set to support structured generation
|
||||
func (s SecretForTLSGeneratorV1) validate() error {
|
||||
// TODO: This is not strictly necessary. We can generate a self signed cert
|
||||
// if no key/cert is given. The only requiredment is that we either get both
|
||||
// or none. See test/e2e/ingress_utils for self signed cert generation.
|
||||
if len(s.Key) == 0 {
|
||||
return fmt.Errorf("key must be specified.")
|
||||
}
|
||||
if len(s.Cert) == 0 {
|
||||
return fmt.Errorf("certificate must be specified.")
|
||||
}
|
||||
if _, err := tls.LoadX509KeyPair(s.Cert, s.Key); err != nil {
|
||||
return fmt.Errorf("failed to load key pair %v", err)
|
||||
}
|
||||
// TODO: Add more validation.
|
||||
// 1. If the certificate contains intermediates, it is a valid chain.
|
||||
// 2. Format etc.
|
||||
return nil
|
||||
}
|
||||
237
vendor/k8s.io/kubernetes/pkg/kubectl/service.go
generated
vendored
237
vendor/k8s.io/kubernetes/pkg/kubectl/service.go
generated
vendored
|
|
@ -1,237 +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 (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/util/intstr"
|
||||
)
|
||||
|
||||
// The only difference between ServiceGeneratorV1 and V2 is that the service port is named "default" in V1, while it is left unnamed in V2.
|
||||
type ServiceGeneratorV1 struct{}
|
||||
|
||||
func (ServiceGeneratorV1) ParamNames() []GeneratorParam {
|
||||
return paramNames()
|
||||
}
|
||||
|
||||
func (ServiceGeneratorV1) Generate(params map[string]interface{}) (runtime.Object, error) {
|
||||
params["port-name"] = "default"
|
||||
return generate(params)
|
||||
}
|
||||
|
||||
type ServiceGeneratorV2 struct{}
|
||||
|
||||
func (ServiceGeneratorV2) ParamNames() []GeneratorParam {
|
||||
return paramNames()
|
||||
}
|
||||
|
||||
func (ServiceGeneratorV2) Generate(params map[string]interface{}) (runtime.Object, error) {
|
||||
return generate(params)
|
||||
}
|
||||
|
||||
func paramNames() []GeneratorParam {
|
||||
return []GeneratorParam{
|
||||
{"default-name", true},
|
||||
{"name", false},
|
||||
{"selector", true},
|
||||
// port will be used if a user specifies --port OR the exposed object
|
||||
// has one port
|
||||
{"port", false},
|
||||
// ports will be used iff a user doesn't specify --port AND the
|
||||
// exposed object has multiple ports
|
||||
{"ports", false},
|
||||
{"labels", false},
|
||||
{"external-ip", false},
|
||||
{"create-external-load-balancer", false},
|
||||
{"load-balancer-ip", false},
|
||||
{"type", false},
|
||||
{"protocol", false},
|
||||
// protocols will be used to keep port-protocol mapping derived from
|
||||
// exposed object
|
||||
{"protocols", false},
|
||||
{"container-port", false}, // alias of target-port
|
||||
{"target-port", false},
|
||||
{"port-name", false},
|
||||
{"session-affinity", false},
|
||||
{"cluster-ip", false},
|
||||
}
|
||||
}
|
||||
|
||||
func generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
params := map[string]string{}
|
||||
for key, value := range genericParams {
|
||||
strVal, isString := value.(string)
|
||||
if !isString {
|
||||
return nil, fmt.Errorf("expected string, saw %v for '%s'", value, key)
|
||||
}
|
||||
params[key] = strVal
|
||||
}
|
||||
selectorString, found := params["selector"]
|
||||
if !found || len(selectorString) == 0 {
|
||||
return nil, fmt.Errorf("'selector' is a required parameter.")
|
||||
}
|
||||
selector, err := ParseLabels(selectorString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
labelsString, found := params["labels"]
|
||||
var labels map[string]string
|
||||
if found && len(labelsString) > 0 {
|
||||
labels, err = ParseLabels(labelsString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
name, found := params["name"]
|
||||
if !found || len(name) == 0 {
|
||||
name, found = params["default-name"]
|
||||
if !found || len(name) == 0 {
|
||||
return nil, fmt.Errorf("'name' is a required parameter.")
|
||||
}
|
||||
}
|
||||
ports := []api.ServicePort{}
|
||||
servicePortName, found := params["port-name"]
|
||||
if !found {
|
||||
// Leave the port unnamed.
|
||||
servicePortName = ""
|
||||
}
|
||||
|
||||
protocolsString, found := params["protocols"]
|
||||
var portProtocolMap map[string]string
|
||||
if found && len(protocolsString) > 0 {
|
||||
portProtocolMap, err = ParseProtocols(protocolsString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// ports takes precedence over port since it will be
|
||||
// specified only when the user hasn't specified a port
|
||||
// via --port and the exposed object has multiple ports.
|
||||
var portString string
|
||||
if portString, found = params["ports"]; !found {
|
||||
portString, found = params["port"]
|
||||
if !found {
|
||||
return nil, fmt.Errorf("'port' is a required parameter.")
|
||||
}
|
||||
}
|
||||
|
||||
portStringSlice := strings.Split(portString, ",")
|
||||
for i, stillPortString := range portStringSlice {
|
||||
port, err := strconv.Atoi(stillPortString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name := servicePortName
|
||||
// If we are going to assign multiple ports to a service, we need to
|
||||
// generate a different name for each one.
|
||||
if len(portStringSlice) > 1 {
|
||||
name = fmt.Sprintf("port-%d", i+1)
|
||||
}
|
||||
protocol := params["protocol"]
|
||||
|
||||
switch {
|
||||
case len(protocol) == 0 && len(portProtocolMap) == 0:
|
||||
// Default to TCP, what the flag was doing previously.
|
||||
protocol = "TCP"
|
||||
case len(protocol) > 0 && len(portProtocolMap) > 0:
|
||||
// User has specified the --protocol while exposing a multiprotocol resource
|
||||
// We should stomp multiple protocols with the one specified ie. do nothing
|
||||
case len(protocol) == 0 && len(portProtocolMap) > 0:
|
||||
// no --protocol and we expose a multiprotocol resource
|
||||
protocol = "TCP" // have the default so we can stay sane
|
||||
if exposeProtocol, found := portProtocolMap[stillPortString]; found {
|
||||
protocol = exposeProtocol
|
||||
}
|
||||
}
|
||||
ports = append(ports, api.ServicePort{
|
||||
Name: name,
|
||||
Port: int32(port),
|
||||
Protocol: api.Protocol(protocol),
|
||||
})
|
||||
}
|
||||
|
||||
service := api.Service{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: name,
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: api.ServiceSpec{
|
||||
Selector: selector,
|
||||
Ports: ports,
|
||||
},
|
||||
}
|
||||
targetPortString := params["target-port"]
|
||||
if len(targetPortString) == 0 {
|
||||
targetPortString = params["container-port"]
|
||||
}
|
||||
if len(targetPortString) > 0 {
|
||||
var targetPort intstr.IntOrString
|
||||
if portNum, err := strconv.Atoi(targetPortString); err != nil {
|
||||
targetPort = intstr.FromString(targetPortString)
|
||||
} else {
|
||||
targetPort = intstr.FromInt(portNum)
|
||||
}
|
||||
// Use the same target-port for every port
|
||||
for i := range service.Spec.Ports {
|
||||
service.Spec.Ports[i].TargetPort = targetPort
|
||||
}
|
||||
} else {
|
||||
// If --target-port or --container-port haven't been specified, this
|
||||
// should be the same as Port
|
||||
for i := range service.Spec.Ports {
|
||||
port := service.Spec.Ports[i].Port
|
||||
service.Spec.Ports[i].TargetPort = intstr.FromInt(int(port))
|
||||
}
|
||||
}
|
||||
if params["create-external-load-balancer"] == "true" {
|
||||
service.Spec.Type = api.ServiceTypeLoadBalancer
|
||||
}
|
||||
if len(params["external-ip"]) > 0 {
|
||||
service.Spec.ExternalIPs = []string{params["external-ip"]}
|
||||
}
|
||||
if len(params["type"]) != 0 {
|
||||
service.Spec.Type = api.ServiceType(params["type"])
|
||||
}
|
||||
if service.Spec.Type == api.ServiceTypeLoadBalancer {
|
||||
service.Spec.LoadBalancerIP = params["load-balancer-ip"]
|
||||
}
|
||||
if len(params["session-affinity"]) != 0 {
|
||||
switch api.ServiceAffinity(params["session-affinity"]) {
|
||||
case api.ServiceAffinityNone:
|
||||
service.Spec.SessionAffinity = api.ServiceAffinityNone
|
||||
case api.ServiceAffinityClientIP:
|
||||
service.Spec.SessionAffinity = api.ServiceAffinityClientIP
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown session affinity: %s", params["session-affinity"])
|
||||
}
|
||||
}
|
||||
if len(params["cluster-ip"]) != 0 {
|
||||
if params["cluster-ip"] == "None" {
|
||||
service.Spec.ClusterIP = api.ClusterIPNone
|
||||
} else {
|
||||
service.Spec.ClusterIP = params["cluster-ip"]
|
||||
}
|
||||
}
|
||||
return &service, nil
|
||||
}
|
||||
211
vendor/k8s.io/kubernetes/pkg/kubectl/service_basic.go
generated
vendored
211
vendor/k8s.io/kubernetes/pkg/kubectl/service_basic.go
generated
vendored
|
|
@ -1,211 +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"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/util/intstr"
|
||||
)
|
||||
|
||||
type ServiceCommonGeneratorV1 struct {
|
||||
Name string
|
||||
TCP []string
|
||||
Type api.ServiceType
|
||||
ClusterIP string
|
||||
NodePort int
|
||||
}
|
||||
|
||||
type ServiceClusterIPGeneratorV1 struct {
|
||||
ServiceCommonGeneratorV1
|
||||
}
|
||||
|
||||
type ServiceNodePortGeneratorV1 struct {
|
||||
ServiceCommonGeneratorV1
|
||||
}
|
||||
|
||||
type ServiceLoadBalancerGeneratorV1 struct {
|
||||
ServiceCommonGeneratorV1
|
||||
}
|
||||
|
||||
func (ServiceClusterIPGeneratorV1) ParamNames() []GeneratorParam {
|
||||
return []GeneratorParam{
|
||||
{"name", true},
|
||||
{"tcp", true},
|
||||
{"clusterip", false},
|
||||
}
|
||||
}
|
||||
func (ServiceNodePortGeneratorV1) ParamNames() []GeneratorParam {
|
||||
return []GeneratorParam{
|
||||
{"name", true},
|
||||
{"tcp", true},
|
||||
{"nodeport", true},
|
||||
}
|
||||
}
|
||||
func (ServiceLoadBalancerGeneratorV1) ParamNames() []GeneratorParam {
|
||||
return []GeneratorParam{
|
||||
{"name", true},
|
||||
{"tcp", true},
|
||||
}
|
||||
}
|
||||
|
||||
func parsePorts(portString string) (int32, intstr.IntOrString, error) {
|
||||
portStringSlice := strings.Split(portString, ":")
|
||||
|
||||
port, err := strconv.Atoi(portStringSlice[0])
|
||||
if err != nil {
|
||||
return 0, intstr.FromInt(0), err
|
||||
}
|
||||
if len(portStringSlice) == 1 {
|
||||
return int32(port), intstr.FromInt(int(port)), nil
|
||||
}
|
||||
|
||||
var targetPort intstr.IntOrString
|
||||
if portNum, err := strconv.Atoi(portStringSlice[1]); err != nil {
|
||||
targetPort = intstr.FromString(portStringSlice[1])
|
||||
} else {
|
||||
targetPort = intstr.FromInt(portNum)
|
||||
}
|
||||
return int32(port), targetPort, nil
|
||||
}
|
||||
|
||||
func (s ServiceCommonGeneratorV1) GenerateCommon(params map[string]interface{}) error {
|
||||
name, isString := params["name"].(string)
|
||||
if !isString {
|
||||
return fmt.Errorf("expected string, saw %v for 'name'", name)
|
||||
}
|
||||
tcpStrings, isArray := params["tcp"].([]string)
|
||||
if !isArray {
|
||||
return fmt.Errorf("expected []string, found :%v", tcpStrings)
|
||||
}
|
||||
clusterip, isString := params["clusterip"].(string)
|
||||
if !isString {
|
||||
return fmt.Errorf("expected string, saw %v for 'clusterip'", clusterip)
|
||||
}
|
||||
s.Name = name
|
||||
s.TCP = tcpStrings
|
||||
s.ClusterIP = clusterip
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s ServiceLoadBalancerGeneratorV1) Generate(params map[string]interface{}) (runtime.Object, error) {
|
||||
err := ValidateParams(s.ParamNames(), params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
delegate := &ServiceCommonGeneratorV1{Type: api.ServiceTypeLoadBalancer, ClusterIP: ""}
|
||||
err = delegate.GenerateCommon(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return delegate.StructuredGenerate()
|
||||
}
|
||||
|
||||
func (s ServiceNodePortGeneratorV1) Generate(params map[string]interface{}) (runtime.Object, error) {
|
||||
err := ValidateParams(s.ParamNames(), params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
delegate := &ServiceCommonGeneratorV1{Type: api.ServiceTypeNodePort, ClusterIP: ""}
|
||||
err = delegate.GenerateCommon(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return delegate.StructuredGenerate()
|
||||
}
|
||||
|
||||
func (s ServiceClusterIPGeneratorV1) Generate(params map[string]interface{}) (runtime.Object, error) {
|
||||
err := ValidateParams(s.ParamNames(), params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
delegate := &ServiceCommonGeneratorV1{Type: api.ServiceTypeClusterIP, ClusterIP: ""}
|
||||
err = delegate.GenerateCommon(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return delegate.StructuredGenerate()
|
||||
}
|
||||
|
||||
// validate validates required fields are set to support structured generation
|
||||
func (s ServiceCommonGeneratorV1) validate() error {
|
||||
if len(s.Name) == 0 {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
if len(s.Type) == 0 {
|
||||
return fmt.Errorf("type must be specified")
|
||||
}
|
||||
if s.ClusterIP == api.ClusterIPNone && s.Type != api.ServiceTypeClusterIP {
|
||||
return fmt.Errorf("ClusterIP=None can only be used with ClusterIP service type")
|
||||
}
|
||||
if s.ClusterIP == api.ClusterIPNone && len(s.TCP) > 0 {
|
||||
return fmt.Errorf("can not map ports with clusterip=None")
|
||||
}
|
||||
if s.ClusterIP != api.ClusterIPNone && len(s.TCP) == 0 {
|
||||
return fmt.Errorf("at least one tcp port specifier must be provided")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s ServiceCommonGeneratorV1) StructuredGenerate() (runtime.Object, error) {
|
||||
err := s.validate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ports := []api.ServicePort{}
|
||||
for _, tcpString := range s.TCP {
|
||||
port, targetPort, err := parsePorts(tcpString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
portName := strings.Replace(tcpString, ":", "-", -1)
|
||||
ports = append(ports, api.ServicePort{
|
||||
Name: portName,
|
||||
Port: port,
|
||||
TargetPort: targetPort,
|
||||
Protocol: api.Protocol("TCP"),
|
||||
NodePort: int32(s.NodePort),
|
||||
})
|
||||
}
|
||||
|
||||
// setup default label and selector
|
||||
labels := map[string]string{}
|
||||
labels["app"] = s.Name
|
||||
selector := map[string]string{}
|
||||
selector["app"] = s.Name
|
||||
|
||||
service := api.Service{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: s.Name,
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: api.ServiceSpec{
|
||||
Type: api.ServiceType(s.Type),
|
||||
Selector: selector,
|
||||
Ports: ports,
|
||||
},
|
||||
}
|
||||
if len(s.ClusterIP) > 0 {
|
||||
service.Spec.ClusterIP = s.ClusterIP
|
||||
}
|
||||
return &service, nil
|
||||
}
|
||||
51
vendor/k8s.io/kubernetes/pkg/kubectl/serviceaccount.go
generated
vendored
51
vendor/k8s.io/kubernetes/pkg/kubectl/serviceaccount.go
generated
vendored
|
|
@ -1,51 +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"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
// ServiceAccountGeneratorV1 supports stable generation of a service account
|
||||
type ServiceAccountGeneratorV1 struct {
|
||||
// Name of service account
|
||||
Name string
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters specified during construction
|
||||
var _ StructuredGenerator = &ServiceAccountGeneratorV1{}
|
||||
|
||||
// StructuredGenerate outputs a service account object using the configured fields
|
||||
func (g *ServiceAccountGeneratorV1) StructuredGenerate() (runtime.Object, error) {
|
||||
if err := g.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serviceAccount := &api.ServiceAccount{}
|
||||
serviceAccount.Name = g.Name
|
||||
return serviceAccount, nil
|
||||
}
|
||||
|
||||
// validate validates required fields are set to support structured generation
|
||||
func (g *ServiceAccountGeneratorV1) validate() error {
|
||||
if len(g.Name) == 0 {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
86
vendor/k8s.io/kubernetes/pkg/kubectl/sorted_resource_name_list.go
generated
vendored
86
vendor/k8s.io/kubernetes/pkg/kubectl/sorted_resource_name_list.go
generated
vendored
|
|
@ -1,86 +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 (
|
||||
"sort"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/kubelet/qos"
|
||||
)
|
||||
|
||||
type SortableResourceNames []api.ResourceName
|
||||
|
||||
func (list SortableResourceNames) Len() int {
|
||||
return len(list)
|
||||
}
|
||||
|
||||
func (list SortableResourceNames) Swap(i, j int) {
|
||||
list[i], list[j] = list[j], list[i]
|
||||
}
|
||||
|
||||
func (list SortableResourceNames) Less(i, j int) bool {
|
||||
return list[i] < list[j]
|
||||
}
|
||||
|
||||
// SortedResourceNames returns the sorted resource names of a resource list.
|
||||
func SortedResourceNames(list api.ResourceList) []api.ResourceName {
|
||||
resources := make([]api.ResourceName, 0, len(list))
|
||||
for res := range list {
|
||||
resources = append(resources, res)
|
||||
}
|
||||
sort.Sort(SortableResourceNames(resources))
|
||||
return resources
|
||||
}
|
||||
|
||||
type SortableResourceQuotas []api.ResourceQuota
|
||||
|
||||
func (list SortableResourceQuotas) Len() int {
|
||||
return len(list)
|
||||
}
|
||||
|
||||
func (list SortableResourceQuotas) Swap(i, j int) {
|
||||
list[i], list[j] = list[j], list[i]
|
||||
}
|
||||
|
||||
func (list SortableResourceQuotas) Less(i, j int) bool {
|
||||
return list[i].Name < list[j].Name
|
||||
}
|
||||
|
||||
type SortableVolumeMounts []api.VolumeMount
|
||||
|
||||
func (list SortableVolumeMounts) Len() int {
|
||||
return len(list)
|
||||
}
|
||||
|
||||
func (list SortableVolumeMounts) Swap(i, j int) {
|
||||
list[i], list[j] = list[j], list[i]
|
||||
}
|
||||
|
||||
func (list SortableVolumeMounts) Less(i, j int) bool {
|
||||
return list[i].MountPath < list[j].MountPath
|
||||
}
|
||||
|
||||
// SortedQoSResourceNames returns the sorted resource names of a QoS list.
|
||||
func SortedQoSResourceNames(list qos.QOSList) []api.ResourceName {
|
||||
resources := make([]api.ResourceName, 0, len(list))
|
||||
for res := range list {
|
||||
resources = append(resources, res)
|
||||
}
|
||||
sort.Sort(SortableResourceNames(resources))
|
||||
return resources
|
||||
}
|
||||
302
vendor/k8s.io/kubernetes/pkg/kubectl/sorting_printer.go
generated
vendored
302
vendor/k8s.io/kubernetes/pkg/kubectl/sorting_printer.go
generated
vendored
|
|
@ -1,302 +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 (
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/util/integer"
|
||||
"k8s.io/kubernetes/pkg/util/jsonpath"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// Sorting printer sorts list types before delegating to another printer.
|
||||
// Non-list types are simply passed through
|
||||
type SortingPrinter struct {
|
||||
SortField string
|
||||
Delegate ResourcePrinter
|
||||
Decoder runtime.Decoder
|
||||
}
|
||||
|
||||
func (s *SortingPrinter) AfterPrint(w io.Writer, res string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SortingPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
|
||||
if !meta.IsListType(obj) {
|
||||
return s.Delegate.PrintObj(obj, out)
|
||||
}
|
||||
|
||||
if err := s.sortObj(obj); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.Delegate.PrintObj(obj, out)
|
||||
}
|
||||
|
||||
// TODO: implement HandledResources()
|
||||
func (p *SortingPrinter) HandledResources() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (s *SortingPrinter) sortObj(obj runtime.Object) error {
|
||||
objs, err := meta.ExtractList(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(objs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
sorter, err := SortObjects(s.Decoder, objs, s.SortField)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch list := obj.(type) {
|
||||
case *v1.List:
|
||||
outputList := make([]runtime.RawExtension, len(objs))
|
||||
for ix := range objs {
|
||||
outputList[ix] = list.Items[sorter.OriginalPosition(ix)]
|
||||
}
|
||||
list.Items = outputList
|
||||
return nil
|
||||
}
|
||||
return meta.SetList(obj, objs)
|
||||
}
|
||||
|
||||
func SortObjects(decoder runtime.Decoder, objs []runtime.Object, fieldInput string) (*RuntimeSort, error) {
|
||||
parser := jsonpath.New("sorting")
|
||||
|
||||
field, err := massageJSONPath(fieldInput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := parser.Parse(field); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for ix := range objs {
|
||||
item := objs[ix]
|
||||
switch u := item.(type) {
|
||||
case *runtime.Unknown:
|
||||
var err error
|
||||
if objs[ix], _, err = decoder.Decode(u.Raw, nil, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var values [][]reflect.Value
|
||||
if unstructured, ok := objs[0].(*runtime.Unstructured); ok {
|
||||
values, err = parser.FindResults(unstructured.Object)
|
||||
} else {
|
||||
values, err = parser.FindResults(reflect.ValueOf(objs[0]).Elem().Interface())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(values) == 0 {
|
||||
return nil, fmt.Errorf("couldn't find any field with path: %s", field)
|
||||
}
|
||||
|
||||
sorter := NewRuntimeSort(field, objs)
|
||||
sort.Sort(sorter)
|
||||
return sorter, nil
|
||||
}
|
||||
|
||||
// RuntimeSort is an implementation of the golang sort interface that knows how to sort
|
||||
// lists of runtime.Object
|
||||
type RuntimeSort struct {
|
||||
field string
|
||||
objs []runtime.Object
|
||||
origPosition []int
|
||||
}
|
||||
|
||||
func NewRuntimeSort(field string, objs []runtime.Object) *RuntimeSort {
|
||||
sorter := &RuntimeSort{field: field, objs: objs, origPosition: make([]int, len(objs))}
|
||||
for ix := range objs {
|
||||
sorter.origPosition[ix] = ix
|
||||
}
|
||||
return sorter
|
||||
}
|
||||
|
||||
func (r *RuntimeSort) Len() int {
|
||||
return len(r.objs)
|
||||
}
|
||||
|
||||
func (r *RuntimeSort) Swap(i, j int) {
|
||||
r.objs[i], r.objs[j] = r.objs[j], r.objs[i]
|
||||
r.origPosition[i], r.origPosition[j] = r.origPosition[j], r.origPosition[i]
|
||||
}
|
||||
|
||||
func isLess(i, j reflect.Value) (bool, error) {
|
||||
switch i.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return i.Int() < j.Int(), nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return i.Uint() < j.Uint(), nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return i.Float() < j.Float(), nil
|
||||
case reflect.String:
|
||||
return i.String() < j.String(), nil
|
||||
case reflect.Ptr:
|
||||
return isLess(i.Elem(), j.Elem())
|
||||
case reflect.Struct:
|
||||
// sort unversioned.Time
|
||||
in := i.Interface()
|
||||
if t, ok := in.(unversioned.Time); ok {
|
||||
return t.Before(j.Interface().(unversioned.Time)), nil
|
||||
}
|
||||
// fallback to the fields comparison
|
||||
for idx := 0; idx < i.NumField(); idx++ {
|
||||
less, err := isLess(i.Field(idx), j.Field(idx))
|
||||
if err != nil || !less {
|
||||
return less, err
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
case reflect.Array, reflect.Slice:
|
||||
// note: the length of i and j may be different
|
||||
for idx := 0; idx < integer.IntMin(i.Len(), j.Len()); idx++ {
|
||||
less, err := isLess(i.Index(idx), j.Index(idx))
|
||||
if err != nil || !less {
|
||||
return less, err
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
|
||||
case reflect.Interface:
|
||||
switch itype := i.Interface().(type) {
|
||||
case uint8:
|
||||
if jtype, ok := j.Interface().(uint8); ok {
|
||||
return itype < jtype, nil
|
||||
}
|
||||
case uint16:
|
||||
if jtype, ok := j.Interface().(uint16); ok {
|
||||
return itype < jtype, nil
|
||||
}
|
||||
case uint32:
|
||||
if jtype, ok := j.Interface().(uint32); ok {
|
||||
return itype < jtype, nil
|
||||
}
|
||||
case uint64:
|
||||
if jtype, ok := j.Interface().(uint64); ok {
|
||||
return itype < jtype, nil
|
||||
}
|
||||
case int8:
|
||||
if jtype, ok := j.Interface().(int8); ok {
|
||||
return itype < jtype, nil
|
||||
}
|
||||
case int16:
|
||||
if jtype, ok := j.Interface().(int16); ok {
|
||||
return itype < jtype, nil
|
||||
}
|
||||
case int32:
|
||||
if jtype, ok := j.Interface().(int32); ok {
|
||||
return itype < jtype, nil
|
||||
}
|
||||
case int64:
|
||||
if jtype, ok := j.Interface().(int64); ok {
|
||||
return itype < jtype, nil
|
||||
}
|
||||
case uint:
|
||||
if jtype, ok := j.Interface().(uint); ok {
|
||||
return itype < jtype, nil
|
||||
}
|
||||
case int:
|
||||
if jtype, ok := j.Interface().(int); ok {
|
||||
return itype < jtype, nil
|
||||
}
|
||||
case float32:
|
||||
if jtype, ok := j.Interface().(float32); ok {
|
||||
return itype < jtype, nil
|
||||
}
|
||||
case float64:
|
||||
if jtype, ok := j.Interface().(float64); ok {
|
||||
return itype < jtype, nil
|
||||
}
|
||||
case string:
|
||||
if jtype, ok := j.Interface().(string); ok {
|
||||
return itype < jtype, nil
|
||||
}
|
||||
default:
|
||||
return false, fmt.Errorf("unsortable type: %T", itype)
|
||||
}
|
||||
return false, fmt.Errorf("unsortable interface: %v", i.Kind())
|
||||
|
||||
default:
|
||||
return false, fmt.Errorf("unsortable type: %v", i.Kind())
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RuntimeSort) Less(i, j int) bool {
|
||||
iObj := r.objs[i]
|
||||
jObj := r.objs[j]
|
||||
|
||||
parser := jsonpath.New("sorting")
|
||||
parser.Parse(r.field)
|
||||
|
||||
var iValues [][]reflect.Value
|
||||
var jValues [][]reflect.Value
|
||||
var err error
|
||||
|
||||
if unstructured, ok := iObj.(*runtime.Unstructured); ok {
|
||||
iValues, err = parser.FindResults(unstructured.Object)
|
||||
} else {
|
||||
iValues, err = parser.FindResults(reflect.ValueOf(iObj).Elem().Interface())
|
||||
}
|
||||
if err != nil {
|
||||
glog.Fatalf("Failed to get i values for %#v using %s (%#v)", iObj, r.field, err)
|
||||
}
|
||||
|
||||
if unstructured, ok := jObj.(*runtime.Unstructured); ok {
|
||||
jValues, err = parser.FindResults(unstructured.Object)
|
||||
} else {
|
||||
jValues, err = parser.FindResults(reflect.ValueOf(jObj).Elem().Interface())
|
||||
}
|
||||
if err != nil {
|
||||
glog.Fatalf("Failed to get j values for %#v using %s (%v)", jObj, r.field, err)
|
||||
}
|
||||
|
||||
iField := iValues[0][0]
|
||||
jField := jValues[0][0]
|
||||
|
||||
less, err := isLess(iField, jField)
|
||||
if err != nil {
|
||||
glog.Fatalf("Field %s in %v is an unsortable type: %s, err: %v", r.field, iObj, iField.Kind().String(), err)
|
||||
}
|
||||
return less
|
||||
}
|
||||
|
||||
// Returns the starting (original) position of a particular index. e.g. If OriginalPosition(0) returns 5 than the
|
||||
// the item currently at position 0 was at position 5 in the original unsorted array.
|
||||
func (r *RuntimeSort) OriginalPosition(ix int) int {
|
||||
if ix < 0 || ix > len(r.origPosition) {
|
||||
return -1
|
||||
}
|
||||
return r.origPosition[ix]
|
||||
}
|
||||
511
vendor/k8s.io/kubernetes/pkg/kubectl/stop.go
generated
vendored
511
vendor/k8s.io/kubernetes/pkg/kubectl/stop.go
generated
vendored
|
|
@ -1,511 +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 (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/errors"
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/apis/apps"
|
||||
"k8s.io/kubernetes/pkg/apis/batch"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
appsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/apps/internalversion"
|
||||
batchclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/batch/internalversion"
|
||||
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
|
||||
extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion"
|
||||
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/util"
|
||||
utilerrors "k8s.io/kubernetes/pkg/util/errors"
|
||||
"k8s.io/kubernetes/pkg/util/uuid"
|
||||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
)
|
||||
|
||||
const (
|
||||
Interval = time.Second * 1
|
||||
Timeout = time.Minute * 5
|
||||
)
|
||||
|
||||
// A Reaper handles terminating an object as gracefully as possible.
|
||||
// timeout is how long we'll wait for the termination to be successful
|
||||
// gracePeriod is time given to an API object for it to delete itself cleanly,
|
||||
// e.g., pod shutdown. It may or may not be supported by the API object.
|
||||
type Reaper interface {
|
||||
Stop(namespace, name string, timeout time.Duration, gracePeriod *api.DeleteOptions) error
|
||||
}
|
||||
|
||||
type NoSuchReaperError struct {
|
||||
kind unversioned.GroupKind
|
||||
}
|
||||
|
||||
func (n *NoSuchReaperError) Error() string {
|
||||
return fmt.Sprintf("no reaper has been implemented for %v", n.kind)
|
||||
}
|
||||
|
||||
func IsNoSuchReaperError(err error) bool {
|
||||
_, ok := err.(*NoSuchReaperError)
|
||||
return ok
|
||||
}
|
||||
|
||||
func ReaperFor(kind unversioned.GroupKind, c internalclientset.Interface) (Reaper, error) {
|
||||
switch kind {
|
||||
case api.Kind("ReplicationController"):
|
||||
return &ReplicationControllerReaper{c.Core(), Interval, Timeout}, nil
|
||||
|
||||
case extensions.Kind("ReplicaSet"):
|
||||
return &ReplicaSetReaper{c.Extensions(), Interval, Timeout}, nil
|
||||
|
||||
case extensions.Kind("DaemonSet"):
|
||||
return &DaemonSetReaper{c.Extensions(), Interval, Timeout}, nil
|
||||
|
||||
case api.Kind("Pod"):
|
||||
return &PodReaper{c.Core()}, nil
|
||||
|
||||
case api.Kind("Service"):
|
||||
return &ServiceReaper{c.Core()}, nil
|
||||
|
||||
case extensions.Kind("Job"), batch.Kind("Job"):
|
||||
return &JobReaper{c.Batch(), c.Core(), Interval, Timeout}, nil
|
||||
|
||||
case apps.Kind("StatefulSet"):
|
||||
return &StatefulSetReaper{c.Apps(), c.Core(), Interval, Timeout}, nil
|
||||
|
||||
case extensions.Kind("Deployment"):
|
||||
return &DeploymentReaper{c.Extensions(), c.Extensions(), Interval, Timeout}, nil
|
||||
|
||||
}
|
||||
return nil, &NoSuchReaperError{kind}
|
||||
}
|
||||
|
||||
func ReaperForReplicationController(rcClient coreclient.ReplicationControllersGetter, timeout time.Duration) (Reaper, error) {
|
||||
return &ReplicationControllerReaper{rcClient, Interval, timeout}, nil
|
||||
}
|
||||
|
||||
type ReplicationControllerReaper struct {
|
||||
client coreclient.ReplicationControllersGetter
|
||||
pollInterval, timeout time.Duration
|
||||
}
|
||||
type ReplicaSetReaper struct {
|
||||
client extensionsclient.ReplicaSetsGetter
|
||||
pollInterval, timeout time.Duration
|
||||
}
|
||||
type DaemonSetReaper struct {
|
||||
client extensionsclient.DaemonSetsGetter
|
||||
pollInterval, timeout time.Duration
|
||||
}
|
||||
type JobReaper struct {
|
||||
client batchclient.JobsGetter
|
||||
podClient coreclient.PodsGetter
|
||||
pollInterval, timeout time.Duration
|
||||
}
|
||||
type DeploymentReaper struct {
|
||||
dClient extensionsclient.DeploymentsGetter
|
||||
rsClient extensionsclient.ReplicaSetsGetter
|
||||
pollInterval, timeout time.Duration
|
||||
}
|
||||
type PodReaper struct {
|
||||
client coreclient.PodsGetter
|
||||
}
|
||||
type ServiceReaper struct {
|
||||
client coreclient.ServicesGetter
|
||||
}
|
||||
type StatefulSetReaper struct {
|
||||
client appsclient.StatefulSetsGetter
|
||||
podClient coreclient.PodsGetter
|
||||
pollInterval, timeout time.Duration
|
||||
}
|
||||
|
||||
type objInterface interface {
|
||||
Delete(name string) error
|
||||
Get(name string) (meta.Object, error)
|
||||
}
|
||||
|
||||
// getOverlappingControllers finds rcs that this controller overlaps, as well as rcs overlapping this controller.
|
||||
func getOverlappingControllers(rcClient coreclient.ReplicationControllerInterface, rc *api.ReplicationController) ([]api.ReplicationController, error) {
|
||||
rcs, err := rcClient.List(api.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting replication controllers: %v", err)
|
||||
}
|
||||
var matchingRCs []api.ReplicationController
|
||||
rcLabels := labels.Set(rc.Spec.Selector)
|
||||
for _, controller := range rcs.Items {
|
||||
newRCLabels := labels.Set(controller.Spec.Selector)
|
||||
if labels.SelectorFromSet(newRCLabels).Matches(rcLabels) || labels.SelectorFromSet(rcLabels).Matches(newRCLabels) {
|
||||
matchingRCs = append(matchingRCs, controller)
|
||||
}
|
||||
}
|
||||
return matchingRCs, nil
|
||||
}
|
||||
|
||||
func (reaper *ReplicationControllerReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *api.DeleteOptions) error {
|
||||
rc := reaper.client.ReplicationControllers(namespace)
|
||||
scaler := &ReplicationControllerScaler{reaper.client}
|
||||
ctrl, err := rc.Get(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if timeout == 0 {
|
||||
timeout = Timeout + time.Duration(10*ctrl.Spec.Replicas)*time.Second
|
||||
}
|
||||
|
||||
// The rc manager will try and detect all matching rcs for a pod's labels,
|
||||
// and only sync the oldest one. This means if we have a pod with labels
|
||||
// [(k1: v1), (k2: v2)] and two rcs: rc1 with selector [(k1=v1)], and rc2 with selector [(k1=v1),(k2=v2)],
|
||||
// the rc manager will sync the older of the two rcs.
|
||||
//
|
||||
// If there are rcs with a superset of labels, eg:
|
||||
// deleting: (k1=v1), superset: (k2=v2, k1=v1)
|
||||
// - It isn't safe to delete the rc because there could be a pod with labels
|
||||
// (k1=v1) that isn't managed by the superset rc. We can't scale it down
|
||||
// either, because there could be a pod (k2=v2, k1=v1) that it deletes
|
||||
// causing a fight with the superset rc.
|
||||
// If there are rcs with a subset of labels, eg:
|
||||
// deleting: (k2=v2, k1=v1), subset: (k1=v1), superset: (k2=v2, k1=v1, k3=v3)
|
||||
// - Even if it's safe to delete this rc without a scale down because all it's pods
|
||||
// are being controlled by the subset rc the code returns an error.
|
||||
|
||||
// In theory, creating overlapping controllers is user error, so the loop below
|
||||
// tries to account for this logic only in the common case, where we end up
|
||||
// with multiple rcs that have an exact match on selectors.
|
||||
|
||||
overlappingCtrls, err := getOverlappingControllers(rc, ctrl)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting replication controllers: %v", err)
|
||||
}
|
||||
exactMatchRCs := []api.ReplicationController{}
|
||||
overlapRCs := []string{}
|
||||
for _, overlappingRC := range overlappingCtrls {
|
||||
if len(overlappingRC.Spec.Selector) == len(ctrl.Spec.Selector) {
|
||||
exactMatchRCs = append(exactMatchRCs, overlappingRC)
|
||||
} else {
|
||||
overlapRCs = append(overlapRCs, overlappingRC.Name)
|
||||
}
|
||||
}
|
||||
if len(overlapRCs) > 0 {
|
||||
return fmt.Errorf(
|
||||
"Detected overlapping controllers for rc %v: %v, please manage deletion individually with --cascade=false.",
|
||||
ctrl.Name, strings.Join(overlapRCs, ","))
|
||||
}
|
||||
if len(exactMatchRCs) == 1 {
|
||||
// No overlapping controllers.
|
||||
retry := NewRetryParams(reaper.pollInterval, reaper.timeout)
|
||||
waitForReplicas := NewRetryParams(reaper.pollInterval, timeout)
|
||||
if err = scaler.Scale(namespace, name, 0, nil, retry, waitForReplicas); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
falseVar := false
|
||||
deleteOptions := &api.DeleteOptions{OrphanDependents: &falseVar}
|
||||
return rc.Delete(name, deleteOptions)
|
||||
}
|
||||
|
||||
// TODO(madhusudancs): Implement it when controllerRef is implemented - https://github.com/kubernetes/kubernetes/issues/2210
|
||||
// getOverlappingReplicaSets finds ReplicaSets that this ReplicaSet overlaps, as well as ReplicaSets overlapping this ReplicaSet.
|
||||
func getOverlappingReplicaSets(c extensionsclient.ReplicaSetInterface, rs *extensions.ReplicaSet) ([]extensions.ReplicaSet, []extensions.ReplicaSet, error) {
|
||||
var overlappingRSs, exactMatchRSs []extensions.ReplicaSet
|
||||
return overlappingRSs, exactMatchRSs, nil
|
||||
}
|
||||
|
||||
func (reaper *ReplicaSetReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *api.DeleteOptions) error {
|
||||
rsc := reaper.client.ReplicaSets(namespace)
|
||||
scaler := &ReplicaSetScaler{reaper.client}
|
||||
rs, err := rsc.Get(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if timeout == 0 {
|
||||
timeout = Timeout + time.Duration(10*rs.Spec.Replicas)*time.Second
|
||||
}
|
||||
|
||||
// The ReplicaSet controller will try and detect all matching ReplicaSets
|
||||
// for a pod's labels, and only sync the oldest one. This means if we have
|
||||
// a pod with labels [(k1: v1), (k2: v2)] and two ReplicaSets: rs1 with
|
||||
// selector [(k1=v1)], and rs2 with selector [(k1=v1),(k2=v2)], the
|
||||
// ReplicaSet controller will sync the older of the two ReplicaSets.
|
||||
//
|
||||
// If there are ReplicaSets with a superset of labels, eg:
|
||||
// deleting: (k1=v1), superset: (k2=v2, k1=v1)
|
||||
// - It isn't safe to delete the ReplicaSet because there could be a pod
|
||||
// with labels (k1=v1) that isn't managed by the superset ReplicaSet.
|
||||
// We can't scale it down either, because there could be a pod
|
||||
// (k2=v2, k1=v1) that it deletes causing a fight with the superset
|
||||
// ReplicaSet.
|
||||
// If there are ReplicaSets with a subset of labels, eg:
|
||||
// deleting: (k2=v2, k1=v1), subset: (k1=v1), superset: (k2=v2, k1=v1, k3=v3)
|
||||
// - Even if it's safe to delete this ReplicaSet without a scale down because
|
||||
// all it's pods are being controlled by the subset ReplicaSet the code
|
||||
// returns an error.
|
||||
|
||||
// In theory, creating overlapping ReplicaSets is user error, so the loop below
|
||||
// tries to account for this logic only in the common case, where we end up
|
||||
// with multiple ReplicaSets that have an exact match on selectors.
|
||||
|
||||
// TODO(madhusudancs): Re-evaluate again when controllerRef is implemented -
|
||||
// https://github.com/kubernetes/kubernetes/issues/2210
|
||||
overlappingRSs, exactMatchRSs, err := getOverlappingReplicaSets(rsc, rs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting ReplicaSets: %v", err)
|
||||
}
|
||||
|
||||
if len(overlappingRSs) > 0 {
|
||||
var names []string
|
||||
for _, overlappingRS := range overlappingRSs {
|
||||
names = append(names, overlappingRS.Name)
|
||||
}
|
||||
return fmt.Errorf(
|
||||
"Detected overlapping ReplicaSets for ReplicaSet %v: %v, please manage deletion individually with --cascade=false.",
|
||||
rs.Name, strings.Join(names, ","))
|
||||
}
|
||||
if len(exactMatchRSs) == 0 {
|
||||
// No overlapping ReplicaSets.
|
||||
retry := NewRetryParams(reaper.pollInterval, reaper.timeout)
|
||||
waitForReplicas := NewRetryParams(reaper.pollInterval, timeout)
|
||||
if err = scaler.Scale(namespace, name, 0, nil, retry, waitForReplicas); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
falseVar := false
|
||||
deleteOptions := &api.DeleteOptions{OrphanDependents: &falseVar}
|
||||
return rsc.Delete(name, deleteOptions)
|
||||
}
|
||||
|
||||
func (reaper *DaemonSetReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *api.DeleteOptions) error {
|
||||
ds, err := reaper.client.DaemonSets(namespace).Get(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// We set the nodeSelector to a random label. This label is nearly guaranteed
|
||||
// to not be set on any node so the DameonSetController will start deleting
|
||||
// daemon pods. Once it's done deleting the daemon pods, it's safe to delete
|
||||
// the DaemonSet.
|
||||
ds.Spec.Template.Spec.NodeSelector = map[string]string{
|
||||
string(uuid.NewUUID()): string(uuid.NewUUID()),
|
||||
}
|
||||
// force update to avoid version conflict
|
||||
ds.ResourceVersion = ""
|
||||
|
||||
if ds, err = reaper.client.DaemonSets(namespace).Update(ds); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait for the daemon set controller to kill all the daemon pods.
|
||||
if err := wait.Poll(reaper.pollInterval, reaper.timeout, func() (bool, error) {
|
||||
updatedDS, err := reaper.client.DaemonSets(namespace).Get(name)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return updatedDS.Status.CurrentNumberScheduled+updatedDS.Status.NumberMisscheduled == 0, nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return reaper.client.DaemonSets(namespace).Delete(name, nil)
|
||||
}
|
||||
|
||||
func (reaper *StatefulSetReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *api.DeleteOptions) error {
|
||||
statefulsets := reaper.client.StatefulSets(namespace)
|
||||
scaler := &StatefulSetScaler{reaper.client}
|
||||
ps, err := statefulsets.Get(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if timeout == 0 {
|
||||
numPets := ps.Spec.Replicas
|
||||
timeout = Timeout + time.Duration(10*numPets)*time.Second
|
||||
}
|
||||
retry := NewRetryParams(reaper.pollInterval, reaper.timeout)
|
||||
waitForStatefulSet := NewRetryParams(reaper.pollInterval, reaper.timeout)
|
||||
if err = scaler.Scale(namespace, name, 0, nil, retry, waitForStatefulSet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: This shouldn't be needed, see corresponding TODO in StatefulSetHasDesiredPets.
|
||||
// StatefulSet should track generation number.
|
||||
pods := reaper.podClient.Pods(namespace)
|
||||
selector, _ := unversioned.LabelSelectorAsSelector(ps.Spec.Selector)
|
||||
options := api.ListOptions{LabelSelector: selector}
|
||||
podList, err := pods.List(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
errList := []error{}
|
||||
for _, pod := range podList.Items {
|
||||
if err := pods.Delete(pod.Name, gracePeriod); err != nil {
|
||||
if !errors.IsNotFound(err) {
|
||||
errList = append(errList, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(errList) > 0 {
|
||||
return utilerrors.NewAggregate(errList)
|
||||
}
|
||||
|
||||
// TODO: Cleanup volumes? We don't want to accidentally delete volumes from
|
||||
// stop, so just leave this up to the statefulset.
|
||||
return statefulsets.Delete(name, nil)
|
||||
}
|
||||
|
||||
func (reaper *JobReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *api.DeleteOptions) error {
|
||||
jobs := reaper.client.Jobs(namespace)
|
||||
pods := reaper.podClient.Pods(namespace)
|
||||
scaler := &JobScaler{reaper.client}
|
||||
job, err := jobs.Get(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if timeout == 0 {
|
||||
// we will never have more active pods than job.Spec.Parallelism
|
||||
parallelism := *job.Spec.Parallelism
|
||||
timeout = Timeout + time.Duration(10*parallelism)*time.Second
|
||||
}
|
||||
|
||||
// TODO: handle overlapping jobs
|
||||
retry := NewRetryParams(reaper.pollInterval, reaper.timeout)
|
||||
waitForJobs := NewRetryParams(reaper.pollInterval, timeout)
|
||||
if err = scaler.Scale(namespace, name, 0, nil, retry, waitForJobs); err != nil {
|
||||
return err
|
||||
}
|
||||
// at this point only dead pods are left, that should be removed
|
||||
selector, _ := unversioned.LabelSelectorAsSelector(job.Spec.Selector)
|
||||
options := api.ListOptions{LabelSelector: selector}
|
||||
podList, err := pods.List(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
errList := []error{}
|
||||
for _, pod := range podList.Items {
|
||||
if err := pods.Delete(pod.Name, gracePeriod); err != nil {
|
||||
// ignores the error when the pod isn't found
|
||||
if !errors.IsNotFound(err) {
|
||||
errList = append(errList, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(errList) > 0 {
|
||||
return utilerrors.NewAggregate(errList)
|
||||
}
|
||||
// once we have all the pods removed we can safely remove the job itself
|
||||
return jobs.Delete(name, nil)
|
||||
}
|
||||
|
||||
func (reaper *DeploymentReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *api.DeleteOptions) error {
|
||||
deployments := reaper.dClient.Deployments(namespace)
|
||||
replicaSets := reaper.rsClient.ReplicaSets(namespace)
|
||||
rsReaper := &ReplicaSetReaper{reaper.rsClient, reaper.pollInterval, reaper.timeout}
|
||||
|
||||
deployment, err := reaper.updateDeploymentWithRetries(namespace, name, func(d *extensions.Deployment) {
|
||||
// set deployment's history and scale to 0
|
||||
// TODO replace with patch when available: https://github.com/kubernetes/kubernetes/issues/20527
|
||||
d.Spec.RevisionHistoryLimit = util.Int32Ptr(0)
|
||||
d.Spec.Replicas = 0
|
||||
d.Spec.Paused = true
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Use observedGeneration to determine if the deployment controller noticed the pause.
|
||||
if err := deploymentutil.WaitForObservedDeployment(func() (*extensions.Deployment, error) {
|
||||
return deployments.Get(name)
|
||||
}, deployment.Generation, 1*time.Second, 1*time.Minute); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Do not cascade deletion for overlapping deployments.
|
||||
if len(deployment.Annotations[deploymentutil.OverlapAnnotation]) > 0 {
|
||||
return deployments.Delete(name, nil)
|
||||
}
|
||||
|
||||
// Stop all replica sets.
|
||||
selector, err := unversioned.LabelSelectorAsSelector(deployment.Spec.Selector)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
options := api.ListOptions{LabelSelector: selector}
|
||||
rsList, err := replicaSets.List(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
errList := []error{}
|
||||
for _, rc := range rsList.Items {
|
||||
if err := rsReaper.Stop(rc.Namespace, rc.Name, timeout, gracePeriod); err != nil {
|
||||
scaleGetErr, ok := err.(ScaleError)
|
||||
if errors.IsNotFound(err) || (ok && errors.IsNotFound(scaleGetErr.ActualError)) {
|
||||
continue
|
||||
}
|
||||
errList = append(errList, err)
|
||||
}
|
||||
}
|
||||
if len(errList) > 0 {
|
||||
return utilerrors.NewAggregate(errList)
|
||||
}
|
||||
|
||||
// Delete deployment at the end.
|
||||
// Note: We delete deployment at the end so that if removing RSs fails, we at least have the deployment to retry.
|
||||
return deployments.Delete(name, nil)
|
||||
}
|
||||
|
||||
type updateDeploymentFunc func(d *extensions.Deployment)
|
||||
|
||||
func (reaper *DeploymentReaper) updateDeploymentWithRetries(namespace, name string, applyUpdate updateDeploymentFunc) (deployment *extensions.Deployment, err error) {
|
||||
deployments := reaper.dClient.Deployments(namespace)
|
||||
err = wait.Poll(10*time.Millisecond, 1*time.Minute, func() (bool, error) {
|
||||
if deployment, err = deployments.Get(name); err != nil {
|
||||
return false, err
|
||||
}
|
||||
// Apply the update, then attempt to push it to the apiserver.
|
||||
applyUpdate(deployment)
|
||||
if deployment, err = deployments.Update(deployment); err == nil {
|
||||
return true, nil
|
||||
}
|
||||
// Retry only on update conflict.
|
||||
if errors.IsConflict(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
})
|
||||
return deployment, err
|
||||
}
|
||||
|
||||
func (reaper *PodReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *api.DeleteOptions) error {
|
||||
pods := reaper.client.Pods(namespace)
|
||||
_, err := pods.Get(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return pods.Delete(name, gracePeriod)
|
||||
}
|
||||
|
||||
func (reaper *ServiceReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *api.DeleteOptions) error {
|
||||
services := reaper.client.Services(namespace)
|
||||
_, err := services.Get(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return services.Delete(name, nil)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue