forked from barak/tarpoon
Add glide.yaml and vendor deps
This commit is contained in:
parent
db918f12ad
commit
5b3d5e81bd
18880 changed files with 5166045 additions and 1 deletions
114
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/BUILD
generated
vendored
Normal file
114
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
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/v1: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/apis/meta/v1: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/schema: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/v1:go_default_library",
|
||||
"//pkg/api/validation:go_default_library",
|
||||
"//pkg/apimachinery/registered:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//pkg/apis/meta/v1: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/runtime/schema: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",
|
||||
],
|
||||
)
|
||||
253
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/cached_discovery.go
generated
vendored
Normal file
253
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/cached_discovery.go
generated
vendored
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
/*
|
||||
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"
|
||||
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
"k8s.io/kubernetes/pkg/client/typed/discovery"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/runtime/schema"
|
||||
"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) (*metav1.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 := &metav1.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]*metav1.APIResourceList, error) {
|
||||
apiGroups, err := d.ServerGroups()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
groupVersions := metav1.ExtractGroupVersions(apiGroups)
|
||||
result := map[string]*metav1.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() (*metav1.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 := &metav1.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() ([]schema.GroupVersionResource, error) {
|
||||
return d.delegate.ServerPreferredResources()
|
||||
}
|
||||
|
||||
func (d *CachedDiscoveryClient) ServerPreferredNamespacedResources() ([]schema.GroupVersionResource, error) {
|
||||
return d.delegate.ServerPreferredNamespacedResources()
|
||||
}
|
||||
|
||||
func (d *CachedDiscoveryClient) ServerVersion() (*version.Info, error) {
|
||||
return d.delegate.ServerVersion()
|
||||
}
|
||||
|
||||
func (d *CachedDiscoveryClient) SwaggerSchema(version schema.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,
|
||||
}
|
||||
}
|
||||
165
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/cached_discovery_test.go
generated
vendored
Normal file
165
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/cached_discovery_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
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 (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/emicklei/go-restful/swagger"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/errors"
|
||||
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
"k8s.io/kubernetes/pkg/client/restclient/fake"
|
||||
"k8s.io/kubernetes/pkg/client/typed/discovery"
|
||||
"k8s.io/kubernetes/pkg/runtime/schema"
|
||||
"k8s.io/kubernetes/pkg/version"
|
||||
)
|
||||
|
||||
func TestCachedDiscoveryClient_Fresh(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
d, err := ioutil.TempDir("", "")
|
||||
assert.NoError(err)
|
||||
defer os.RemoveAll(d)
|
||||
|
||||
c := fakeDiscoveryClient{}
|
||||
cdc := NewCachedDiscoveryClient(&c, d, 60*time.Second)
|
||||
assert.True(cdc.Fresh(), "should be fresh after creation")
|
||||
|
||||
cdc.ServerGroups()
|
||||
assert.True(cdc.Fresh(), "should be fresh after groups call without cache")
|
||||
assert.Equal(c.groupCalls, 1)
|
||||
|
||||
cdc.ServerGroups()
|
||||
assert.True(cdc.Fresh(), "should be fresh after another groups call")
|
||||
assert.Equal(c.groupCalls, 1)
|
||||
|
||||
cdc.ServerResources()
|
||||
assert.True(cdc.Fresh(), "should be fresh after resources call")
|
||||
assert.Equal(c.resourceCalls, 1)
|
||||
|
||||
cdc.ServerResources()
|
||||
assert.True(cdc.Fresh(), "should be fresh after another resources call")
|
||||
assert.Equal(c.resourceCalls, 1)
|
||||
|
||||
cdc = NewCachedDiscoveryClient(&c, d, 60*time.Second)
|
||||
cdc.ServerGroups()
|
||||
assert.False(cdc.Fresh(), "should NOT be fresh after recreation with existing groups cache")
|
||||
assert.Equal(c.groupCalls, 1)
|
||||
|
||||
cdc.ServerResources()
|
||||
assert.False(cdc.Fresh(), "should NOT be fresh after recreation with existing resources cache")
|
||||
assert.Equal(c.resourceCalls, 1)
|
||||
|
||||
cdc.Invalidate()
|
||||
assert.True(cdc.Fresh(), "should be fresh after cache invalidation")
|
||||
|
||||
cdc.ServerResources()
|
||||
assert.True(cdc.Fresh(), "should ignore existing resources cache after invalidation")
|
||||
assert.Equal(c.resourceCalls, 2)
|
||||
}
|
||||
|
||||
func TestNewCachedDiscoveryClient_TTL(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
d, err := ioutil.TempDir("", "")
|
||||
assert.NoError(err)
|
||||
defer os.RemoveAll(d)
|
||||
|
||||
c := fakeDiscoveryClient{}
|
||||
cdc := NewCachedDiscoveryClient(&c, d, 1*time.Nanosecond)
|
||||
cdc.ServerGroups()
|
||||
assert.Equal(c.groupCalls, 1)
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
cdc.ServerGroups()
|
||||
assert.Equal(c.groupCalls, 2)
|
||||
}
|
||||
|
||||
type fakeDiscoveryClient struct {
|
||||
groupCalls int
|
||||
resourceCalls int
|
||||
versionCalls int
|
||||
swaggerCalls int
|
||||
}
|
||||
|
||||
var _ discovery.DiscoveryInterface = &fakeDiscoveryClient{}
|
||||
|
||||
func (c *fakeDiscoveryClient) RESTClient() restclient.Interface {
|
||||
return &fake.RESTClient{}
|
||||
}
|
||||
|
||||
func (c *fakeDiscoveryClient) ServerGroups() (*metav1.APIGroupList, error) {
|
||||
c.groupCalls = c.groupCalls + 1
|
||||
return &metav1.APIGroupList{
|
||||
Groups: []metav1.APIGroup{
|
||||
{
|
||||
Name: "a",
|
||||
Versions: []metav1.GroupVersionForDiscovery{
|
||||
{
|
||||
GroupVersion: "a/v1",
|
||||
Version: "v1",
|
||||
},
|
||||
},
|
||||
PreferredVersion: metav1.GroupVersionForDiscovery{
|
||||
GroupVersion: "a/v1",
|
||||
Version: "v1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *fakeDiscoveryClient) ServerResourcesForGroupVersion(groupVersion string) (*metav1.APIResourceList, error) {
|
||||
c.resourceCalls = c.resourceCalls + 1
|
||||
if groupVersion == "a/v1" {
|
||||
return &metav1.APIResourceList{}, nil
|
||||
}
|
||||
|
||||
return nil, errors.NewNotFound(schema.GroupResource{}, "")
|
||||
}
|
||||
|
||||
func (c *fakeDiscoveryClient) ServerResources() (map[string]*metav1.APIResourceList, error) {
|
||||
c.resourceCalls = c.resourceCalls + 1
|
||||
return map[string]*metav1.APIResourceList{}, nil
|
||||
}
|
||||
|
||||
func (c *fakeDiscoveryClient) ServerPreferredResources() ([]schema.GroupVersionResource, error) {
|
||||
c.resourceCalls = c.resourceCalls + 1
|
||||
return []schema.GroupVersionResource{}, nil
|
||||
}
|
||||
|
||||
func (c *fakeDiscoveryClient) ServerPreferredNamespacedResources() ([]schema.GroupVersionResource, error) {
|
||||
c.resourceCalls = c.resourceCalls + 1
|
||||
return []schema.GroupVersionResource{}, nil
|
||||
}
|
||||
|
||||
func (c *fakeDiscoveryClient) ServerVersion() (*version.Info, error) {
|
||||
c.versionCalls = c.versionCalls + 1
|
||||
return &version.Info{}, nil
|
||||
}
|
||||
|
||||
func (c *fakeDiscoveryClient) SwaggerSchema(version schema.GroupVersion) (*swagger.ApiDeclaration, error) {
|
||||
c.swaggerCalls = c.swaggerCalls + 1
|
||||
return &swagger.ApiDeclaration{}, nil
|
||||
}
|
||||
197
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/clientcache.go
generated
vendored
Normal file
197
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/clientcache.go
generated
vendored
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
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"
|
||||
|
||||
fedclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_internalclientset"
|
||||
"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"
|
||||
"k8s.io/kubernetes/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
func NewClientCache(loader clientcmd.ClientConfig) *ClientCache {
|
||||
return &ClientCache{
|
||||
clientsets: make(map[schema.GroupVersion]*internalclientset.Clientset),
|
||||
configs: make(map[schema.GroupVersion]*restclient.Config),
|
||||
fedClientSets: make(map[schema.GroupVersion]fedclientset.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[schema.GroupVersion]*internalclientset.Clientset
|
||||
fedClientSets map[schema.GroupVersion]fedclientset.Interface
|
||||
configs map[schema.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 *schema.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 *schema.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 *schema.GroupVersion) (fedclientset.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 := fedclientset.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.fedClientSets[*config.GroupVersion] = clientSet
|
||||
|
||||
if version != nil {
|
||||
configCopy := *config
|
||||
clientSet, err := fedclientset.NewForConfig(&configCopy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.fedClientSets[*version] = clientSet
|
||||
}
|
||||
|
||||
return clientSet, nil
|
||||
}
|
||||
|
||||
func (c *ClientCache) FederationClientForVersion(version *schema.GroupVersion) (*restclient.RESTClient, error) {
|
||||
fedClientSet, err := c.FederationClientSetForVersion(version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fedClientSet.Federation().RESTClient().(*restclient.RESTClient), nil
|
||||
}
|
||||
29
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/editor/BUILD
generated
vendored
Normal file
29
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/editor/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
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 = ["editor.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/util/term:go_default_library",
|
||||
"//vendor:github.com/golang/glog",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["editor_test.go"],
|
||||
library = "go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [],
|
||||
)
|
||||
192
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/editor/editor.go
generated
vendored
Normal file
192
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/editor/editor.go
generated
vendored
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
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 editor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/term"
|
||||
)
|
||||
|
||||
const (
|
||||
// sorry, blame Git
|
||||
// TODO: on Windows rely on 'start' to launch the editor associated
|
||||
// with the given file type. If we can't because of the need of
|
||||
// blocking, use a script with 'ftype' and 'assoc' to detect it.
|
||||
defaultEditor = "vi"
|
||||
defaultShell = "/bin/bash"
|
||||
windowsEditor = "notepad"
|
||||
windowsShell = "cmd"
|
||||
)
|
||||
|
||||
type Editor struct {
|
||||
Args []string
|
||||
Shell bool
|
||||
}
|
||||
|
||||
// NewDefaultEditor creates a struct Editor that uses the OS environment to
|
||||
// locate the editor program, looking at EDITOR environment variable to find
|
||||
// the proper command line. If the provided editor has no spaces, or no quotes,
|
||||
// it is treated as a bare command to be loaded. Otherwise, the string will
|
||||
// be passed to the user's shell for execution.
|
||||
func NewDefaultEditor(envs []string) Editor {
|
||||
args, shell := defaultEnvEditor(envs)
|
||||
return Editor{
|
||||
Args: args,
|
||||
Shell: shell,
|
||||
}
|
||||
}
|
||||
|
||||
func defaultEnvShell() []string {
|
||||
shell := os.Getenv("SHELL")
|
||||
if len(shell) == 0 {
|
||||
shell = platformize(defaultShell, windowsShell)
|
||||
}
|
||||
flag := "-c"
|
||||
if shell == windowsShell {
|
||||
flag = "/C"
|
||||
}
|
||||
return []string{shell, flag}
|
||||
}
|
||||
|
||||
func defaultEnvEditor(envs []string) ([]string, bool) {
|
||||
var editor string
|
||||
for _, env := range envs {
|
||||
if len(env) > 0 {
|
||||
editor = os.Getenv(env)
|
||||
}
|
||||
if len(editor) > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(editor) == 0 {
|
||||
editor = platformize(defaultEditor, windowsEditor)
|
||||
}
|
||||
if !strings.Contains(editor, " ") {
|
||||
return []string{editor}, false
|
||||
}
|
||||
if !strings.ContainsAny(editor, "\"'\\") {
|
||||
return strings.Split(editor, " "), false
|
||||
}
|
||||
// rather than parse the shell arguments ourselves, punt to the shell
|
||||
shell := defaultEnvShell()
|
||||
return append(shell, editor), true
|
||||
}
|
||||
|
||||
func (e Editor) args(path string) []string {
|
||||
args := make([]string, len(e.Args))
|
||||
copy(args, e.Args)
|
||||
if e.Shell {
|
||||
last := args[len(args)-1]
|
||||
args[len(args)-1] = fmt.Sprintf("%s %q", last, path)
|
||||
} else {
|
||||
args = append(args, path)
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
// Launch opens the described or returns an error. The TTY will be protected, and
|
||||
// SIGQUIT, SIGTERM, and SIGINT will all be trapped.
|
||||
func (e Editor) Launch(path string) error {
|
||||
if len(e.Args) == 0 {
|
||||
return fmt.Errorf("no editor defined, can't open %s", path)
|
||||
}
|
||||
abs, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
args := e.args(abs)
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdin = os.Stdin
|
||||
glog.V(5).Infof("Opening file with editor %v", args)
|
||||
if err := (term.TTY{In: os.Stdin, TryDev: true}).Safe(cmd.Run); err != nil {
|
||||
if err, ok := err.(*exec.Error); ok {
|
||||
if err.Err == exec.ErrNotFound {
|
||||
return fmt.Errorf("unable to launch the editor %q", strings.Join(e.Args, " "))
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("there was a problem with the editor %q", strings.Join(e.Args, " "))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LaunchTempFile reads the provided stream into a temporary file in the given directory
|
||||
// and file prefix, and then invokes Launch with the path of that file. It will return
|
||||
// the contents of the file after launch, any errors that occur, and the path of the
|
||||
// temporary file so the caller can clean it up as needed.
|
||||
func (e Editor) LaunchTempFile(prefix, suffix string, r io.Reader) ([]byte, string, error) {
|
||||
f, err := tempFile(prefix, suffix)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
defer f.Close()
|
||||
path := f.Name()
|
||||
if _, err := io.Copy(f, r); err != nil {
|
||||
os.Remove(path)
|
||||
return nil, path, err
|
||||
}
|
||||
// This file descriptor needs to close so the next process (Launch) can claim it.
|
||||
f.Close()
|
||||
if err := e.Launch(path); err != nil {
|
||||
return nil, path, err
|
||||
}
|
||||
bytes, err := ioutil.ReadFile(path)
|
||||
return bytes, path, err
|
||||
}
|
||||
|
||||
func tempFile(prefix, suffix string) (f *os.File, err error) {
|
||||
dir := os.TempDir()
|
||||
|
||||
for i := 0; i < 10000; i++ {
|
||||
name := filepath.Join(dir, prefix+randSeq(5)+suffix)
|
||||
f, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
|
||||
if os.IsExist(err) {
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var letters = []rune("abcdefghijklmnopqrstuvwxyz0123456789")
|
||||
|
||||
func randSeq(n int) string {
|
||||
b := make([]rune, n)
|
||||
for i := range b {
|
||||
b[i] = letters[rand.Intn(len(letters))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func platformize(linux, windows string) string {
|
||||
if runtime.GOOS == "windows" {
|
||||
return windows
|
||||
}
|
||||
return linux
|
||||
}
|
||||
63
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/editor/editor_test.go
generated
vendored
Normal file
63
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/editor/editor_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
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 editor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestArgs(t *testing.T) {
|
||||
if e, a := []string{"/bin/bash", "-c \"test\""}, (Editor{Args: []string{"/bin/bash", "-c"}, Shell: true}).args("test"); !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("unexpected args: %v", a)
|
||||
}
|
||||
if e, a := []string{"/bin/bash", "-c", "test"}, (Editor{Args: []string{"/bin/bash", "-c"}, Shell: false}).args("test"); !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("unexpected args: %v", a)
|
||||
}
|
||||
if e, a := []string{"/bin/bash", "-i -c \"test\""}, (Editor{Args: []string{"/bin/bash", "-i -c"}, Shell: true}).args("test"); !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("unexpected args: %v", a)
|
||||
}
|
||||
if e, a := []string{"/test", "test"}, (Editor{Args: []string{"/test"}}).args("test"); !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("unexpected args: %v", a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEditor(t *testing.T) {
|
||||
edit := Editor{Args: []string{"cat"}}
|
||||
testStr := "test something\n"
|
||||
contents, path, err := edit.LaunchTempFile("", "someprefix", bytes.NewBufferString(testStr))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
t.Fatalf("no temp file: %s", path)
|
||||
}
|
||||
defer os.Remove(path)
|
||||
if disk, err := ioutil.ReadFile(path); err != nil || !bytes.Equal(contents, disk) {
|
||||
t.Errorf("unexpected file on disk: %v %s", err, string(disk))
|
||||
}
|
||||
if !bytes.Equal(contents, []byte(testStr)) {
|
||||
t.Errorf("unexpected contents: %s", string(contents))
|
||||
}
|
||||
if !strings.Contains(path, "someprefix") {
|
||||
t.Errorf("path not expected: %s", path)
|
||||
}
|
||||
}
|
||||
1343
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/factory.go
generated
vendored
Normal file
1343
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/factory.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
760
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/factory_test.go
generated
vendored
Normal file
760
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/factory_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
728
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/helpers.go
generated
vendored
Normal file
728
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/helpers.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
381
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/helpers_test.go
generated
vendored
Normal file
381
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/helpers_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,381 @@
|
|||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/errors"
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
apitesting "k8s.io/kubernetes/pkg/api/testing"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/runtime/schema"
|
||||
uexec "k8s.io/kubernetes/pkg/util/exec"
|
||||
"k8s.io/kubernetes/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
func TestMerge(t *testing.T) {
|
||||
grace := int64(30)
|
||||
tests := []struct {
|
||||
obj runtime.Object
|
||||
fragment string
|
||||
expected runtime.Object
|
||||
expectErr bool
|
||||
kind string
|
||||
}{
|
||||
{
|
||||
kind: "Pod",
|
||||
obj: &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
},
|
||||
fragment: fmt.Sprintf(`{ "apiVersion": "%s" }`, registered.GroupOrDie(api.GroupName).GroupVersion.String()),
|
||||
expected: &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: apitesting.DeepEqualSafePodSpec(),
|
||||
},
|
||||
},
|
||||
/* TODO: uncomment this test once Merge is updated to use
|
||||
strategic-merge-patch. See #8449.
|
||||
{
|
||||
kind: "Pod",
|
||||
obj: &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
api.Container{
|
||||
Name: "c1",
|
||||
Image: "red-image",
|
||||
},
|
||||
api.Container{
|
||||
Name: "c2",
|
||||
Image: "blue-image",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
fragment: fmt.Sprintf(`{ "apiVersion": "%s", "spec": { "containers": [ { "name": "c1", "image": "green-image" } ] } }`, registered.GroupOrDie(api.GroupName).GroupVersion.String()),
|
||||
expected: &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
api.Container{
|
||||
Name: "c1",
|
||||
Image: "green-image",
|
||||
},
|
||||
api.Container{
|
||||
Name: "c2",
|
||||
Image: "blue-image",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, */
|
||||
{
|
||||
kind: "Pod",
|
||||
obj: &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
},
|
||||
fragment: fmt.Sprintf(`{ "apiVersion": "%s", "spec": { "volumes": [ {"name": "v1"}, {"name": "v2"} ] } }`, registered.GroupOrDie(api.GroupName).GroupVersion.String()),
|
||||
expected: &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Volumes: []api.Volume{
|
||||
{
|
||||
Name: "v1",
|
||||
VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}},
|
||||
},
|
||||
{
|
||||
Name: "v2",
|
||||
VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}},
|
||||
},
|
||||
},
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
TerminationGracePeriodSeconds: &grace,
|
||||
SecurityContext: &api.PodSecurityContext{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: "Pod",
|
||||
obj: &api.Pod{},
|
||||
fragment: "invalid json",
|
||||
expected: &api.Pod{},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
kind: "Service",
|
||||
obj: &api.Service{},
|
||||
fragment: `{ "apiVersion": "badVersion" }`,
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
kind: "Service",
|
||||
obj: &api.Service{
|
||||
Spec: api.ServiceSpec{},
|
||||
},
|
||||
fragment: fmt.Sprintf(`{ "apiVersion": "%s", "spec": { "ports": [ { "port": 0 } ] } }`, registered.GroupOrDie(api.GroupName).GroupVersion.String()),
|
||||
expected: &api.Service{
|
||||
Spec: api.ServiceSpec{
|
||||
SessionAffinity: "None",
|
||||
Type: api.ServiceTypeClusterIP,
|
||||
Ports: []api.ServicePort{
|
||||
{
|
||||
Protocol: api.ProtocolTCP,
|
||||
Port: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: "Service",
|
||||
obj: &api.Service{
|
||||
Spec: api.ServiceSpec{
|
||||
Selector: map[string]string{
|
||||
"version": "v1",
|
||||
},
|
||||
},
|
||||
},
|
||||
fragment: fmt.Sprintf(`{ "apiVersion": "%s", "spec": { "selector": { "version": "v2" } } }`, registered.GroupOrDie(api.GroupName).GroupVersion.String()),
|
||||
expected: &api.Service{
|
||||
Spec: api.ServiceSpec{
|
||||
SessionAffinity: "None",
|
||||
Type: api.ServiceTypeClusterIP,
|
||||
Selector: map[string]string{
|
||||
"version": "v2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
out, err := Merge(testapi.Default.Codec(), test.obj, test.fragment, test.kind)
|
||||
if !test.expectErr {
|
||||
if err != nil {
|
||||
t.Errorf("testcase[%d], unexpected error: %v", i, err)
|
||||
} else if !reflect.DeepEqual(out, test.expected) {
|
||||
t.Errorf("\n\ntestcase[%d]\nexpected:\n%+v\nsaw:\n%+v", i, test.expected, out)
|
||||
}
|
||||
}
|
||||
if test.expectErr && err == nil {
|
||||
t.Errorf("testcase[%d], unexpected non-error", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type fileHandler struct {
|
||||
data []byte
|
||||
}
|
||||
|
||||
func (f *fileHandler) ServeHTTP(res http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.Path == "/error" {
|
||||
res.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
res.WriteHeader(http.StatusOK)
|
||||
res.Write(f.data)
|
||||
}
|
||||
|
||||
type checkErrTestCase struct {
|
||||
err error
|
||||
expectedErr string
|
||||
expectedCode int
|
||||
}
|
||||
|
||||
func TestCheckInvalidErr(t *testing.T) {
|
||||
testCheckError(t, []checkErrTestCase{
|
||||
{
|
||||
errors.NewInvalid(api.Kind("Invalid1"), "invalidation", field.ErrorList{field.Invalid(field.NewPath("field"), "single", "details")}),
|
||||
"The Invalid1 \"invalidation\" is invalid: field: Invalid value: \"single\": details\n",
|
||||
DefaultErrorExitCode,
|
||||
},
|
||||
{
|
||||
errors.NewInvalid(api.Kind("Invalid2"), "invalidation", field.ErrorList{field.Invalid(field.NewPath("field1"), "multi1", "details"), field.Invalid(field.NewPath("field2"), "multi2", "details")}),
|
||||
"The Invalid2 \"invalidation\" is invalid: \n* field1: Invalid value: \"multi1\": details\n* field2: Invalid value: \"multi2\": details\n",
|
||||
DefaultErrorExitCode,
|
||||
},
|
||||
{
|
||||
errors.NewInvalid(api.Kind("Invalid3"), "invalidation", field.ErrorList{}),
|
||||
"The Invalid3 \"invalidation\" is invalid",
|
||||
DefaultErrorExitCode,
|
||||
},
|
||||
{
|
||||
errors.NewInvalid(api.Kind("Invalid4"), "invalidation", field.ErrorList{field.Invalid(field.NewPath("field4"), "multi4", "details"), field.Invalid(field.NewPath("field4"), "multi4", "details")}),
|
||||
"The Invalid4 \"invalidation\" is invalid: field4: Invalid value: \"multi4\": details\n",
|
||||
DefaultErrorExitCode,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestCheckNoResourceMatchError(t *testing.T) {
|
||||
testCheckError(t, []checkErrTestCase{
|
||||
{
|
||||
&meta.NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "foo"}},
|
||||
`the server doesn't have a resource type "foo"`,
|
||||
DefaultErrorExitCode,
|
||||
},
|
||||
{
|
||||
&meta.NoResourceMatchError{PartialResource: schema.GroupVersionResource{Version: "theversion", Resource: "foo"}},
|
||||
`the server doesn't have a resource type "foo" in version "theversion"`,
|
||||
DefaultErrorExitCode,
|
||||
},
|
||||
{
|
||||
&meta.NoResourceMatchError{PartialResource: schema.GroupVersionResource{Group: "thegroup", Version: "theversion", Resource: "foo"}},
|
||||
`the server doesn't have a resource type "foo" in group "thegroup" and version "theversion"`,
|
||||
DefaultErrorExitCode,
|
||||
},
|
||||
{
|
||||
&meta.NoResourceMatchError{PartialResource: schema.GroupVersionResource{Group: "thegroup", Resource: "foo"}},
|
||||
`the server doesn't have a resource type "foo" in group "thegroup"`,
|
||||
DefaultErrorExitCode,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestCheckExitError(t *testing.T) {
|
||||
testCheckError(t, []checkErrTestCase{
|
||||
{
|
||||
uexec.CodeExitError{Err: fmt.Errorf("pod foo/bar terminated"), Code: 42},
|
||||
"",
|
||||
42,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testCheckError(t *testing.T, tests []checkErrTestCase) {
|
||||
var errReturned string
|
||||
var codeReturned int
|
||||
errHandle := func(err string, code int) {
|
||||
errReturned = err
|
||||
codeReturned = code
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
checkErr("", test.err, errHandle)
|
||||
|
||||
if errReturned != test.expectedErr {
|
||||
t.Fatalf("Got: %s, expected: %s", errReturned, test.expectedErr)
|
||||
}
|
||||
if codeReturned != test.expectedCode {
|
||||
t.Fatalf("Got: %d, expected: %d", codeReturned, test.expectedCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDumpReaderToFile(t *testing.T) {
|
||||
testString := "TEST STRING"
|
||||
tempFile, err := ioutil.TempFile("", "hlpers_test_dump_")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error setting up a temporary file %v", err)
|
||||
}
|
||||
defer syscall.Unlink(tempFile.Name())
|
||||
defer tempFile.Close()
|
||||
defer func() {
|
||||
if !t.Failed() {
|
||||
os.Remove(tempFile.Name())
|
||||
}
|
||||
}()
|
||||
err = DumpReaderToFile(strings.NewReader(testString), tempFile.Name())
|
||||
if err != nil {
|
||||
t.Errorf("error in DumpReaderToFile: %v", err)
|
||||
}
|
||||
data, err := ioutil.ReadFile(tempFile.Name())
|
||||
if err != nil {
|
||||
t.Errorf("error when reading %s: %v", tempFile.Name(), err)
|
||||
}
|
||||
stringData := string(data)
|
||||
if stringData != testString {
|
||||
t.Fatalf("Wrong file content %s != %s", testString, stringData)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaybeConvert(t *testing.T) {
|
||||
tests := []struct {
|
||||
input runtime.Object
|
||||
gv schema.GroupVersion
|
||||
expected runtime.Object
|
||||
}{
|
||||
{
|
||||
input: &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
},
|
||||
gv: schema.GroupVersion{Group: "", Version: "v1"},
|
||||
expected: &v1.Pod{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "Pod",
|
||||
},
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: &extensions.ThirdPartyResourceData{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Data: []byte("this is some data"),
|
||||
},
|
||||
expected: &extensions.ThirdPartyResourceData{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Data: []byte("this is some data"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
obj, err := MaybeConvertObject(test.input, test.gv, testapi.Default.Converter())
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(test.expected, obj) {
|
||||
t.Errorf("expected:\n%#v\nsaw:\n%#v\n", test.expected, obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
23
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/jsonmerge/BUILD
generated
vendored
Normal file
23
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/jsonmerge/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
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 = ["jsonmerge.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/util/strategicpatch:go_default_library",
|
||||
"//pkg/util/yaml:go_default_library",
|
||||
"//vendor:github.com/evanphx/json-patch",
|
||||
"//vendor:github.com/golang/glog",
|
||||
],
|
||||
)
|
||||
193
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/jsonmerge/jsonmerge.go
generated
vendored
Normal file
193
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/jsonmerge/jsonmerge.go
generated
vendored
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
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 jsonmerge
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/evanphx/json-patch"
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/strategicpatch"
|
||||
"k8s.io/kubernetes/pkg/util/yaml"
|
||||
)
|
||||
|
||||
// Delta represents a change between two JSON documents.
|
||||
type Delta struct {
|
||||
original []byte
|
||||
edit []byte
|
||||
|
||||
preconditions []PreconditionFunc
|
||||
}
|
||||
|
||||
// PreconditionFunc is a test to verify that an incompatible change
|
||||
// has occurred before an Apply can be successful.
|
||||
type PreconditionFunc func(interface{}) (hold bool, message string)
|
||||
|
||||
// AddPreconditions adds precondition checks to a change which must
|
||||
// be satisfied before an Apply is considered successful. If a
|
||||
// precondition returns false, the Apply is failed with
|
||||
// ErrPreconditionFailed.
|
||||
func (d *Delta) AddPreconditions(fns ...PreconditionFunc) {
|
||||
d.preconditions = append(d.preconditions, fns...)
|
||||
}
|
||||
|
||||
// RequireKeyUnchanged creates a precondition function that fails
|
||||
// if the provided key is present in the diff (indicating its value
|
||||
// has changed).
|
||||
func RequireKeyUnchanged(key string) PreconditionFunc {
|
||||
return func(diff interface{}) (bool, string) {
|
||||
m, ok := diff.(map[string]interface{})
|
||||
if !ok {
|
||||
return true, ""
|
||||
}
|
||||
// the presence of key in a diff means that its value has been changed, therefore
|
||||
// we should fail the precondition.
|
||||
_, ok = m[key]
|
||||
if ok {
|
||||
return false, key + " should not be changed\n"
|
||||
} else {
|
||||
return true, ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RequireMetadataKeyUnchanged creates a precondition function that fails
|
||||
// if the metadata.key is present in the diff (indicating its value
|
||||
// has changed).
|
||||
func RequireMetadataKeyUnchanged(key string) PreconditionFunc {
|
||||
return func(diff interface{}) (bool, string) {
|
||||
m, ok := diff.(map[string]interface{})
|
||||
if !ok {
|
||||
return true, ""
|
||||
}
|
||||
m1, ok := m["metadata"]
|
||||
if !ok {
|
||||
return true, ""
|
||||
}
|
||||
m2, ok := m1.(map[string]interface{})
|
||||
if !ok {
|
||||
return true, ""
|
||||
}
|
||||
_, ok = m2[key]
|
||||
if ok {
|
||||
return false, "metadata." + key + " should not be changed\n"
|
||||
} else {
|
||||
return true, ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestPreconditions test if preconditions hold given the edit
|
||||
func TestPreconditionsHold(edit []byte, preconditions []PreconditionFunc) (bool, string) {
|
||||
diff := make(map[string]interface{})
|
||||
if err := json.Unmarshal(edit, &diff); err != nil {
|
||||
return false, err.Error()
|
||||
}
|
||||
for _, fn := range preconditions {
|
||||
if hold, msg := fn(diff); !hold {
|
||||
return false, msg
|
||||
}
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
|
||||
// NewDelta accepts two JSON or YAML documents and calculates the difference
|
||||
// between them. It returns a Delta object which can be used to resolve
|
||||
// conflicts against a third version with a common parent, or an error
|
||||
// if either document is in error.
|
||||
func NewDelta(from, to []byte) (*Delta, error) {
|
||||
d := &Delta{}
|
||||
before, err := yaml.ToJSON(from)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
after, err := yaml.ToJSON(to)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
diff, err := jsonpatch.CreateMergePatch(before, after)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
glog.V(6).Infof("Patch created from:\n%s\n%s\n%s", string(before), string(after), string(diff))
|
||||
d.original = before
|
||||
d.edit = diff
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// Apply attempts to apply the changes described by Delta onto latest,
|
||||
// returning an error if the changes cannot be applied cleanly.
|
||||
// IsConflicting will be true if the changes overlap, otherwise a
|
||||
// generic error will be returned.
|
||||
func (d *Delta) Apply(latest []byte) ([]byte, error) {
|
||||
base, err := yaml.ToJSON(latest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
changes, err := jsonpatch.CreateMergePatch(d.original, base)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
diff1 := make(map[string]interface{})
|
||||
if err := json.Unmarshal(d.edit, &diff1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
diff2 := make(map[string]interface{})
|
||||
if err := json.Unmarshal(changes, &diff2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, fn := range d.preconditions {
|
||||
hold1, _ := fn(diff1)
|
||||
hold2, _ := fn(diff2)
|
||||
if !hold1 || !hold2 {
|
||||
return nil, ErrPreconditionFailed
|
||||
}
|
||||
}
|
||||
|
||||
glog.V(6).Infof("Testing for conflict between:\n%s\n%s", string(d.edit), string(changes))
|
||||
hasConflicts, err := strategicpatch.HasConflicts(diff1, diff2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if hasConflicts {
|
||||
return nil, ErrConflict
|
||||
}
|
||||
|
||||
return jsonpatch.MergePatch(base, d.edit)
|
||||
}
|
||||
|
||||
// IsConflicting returns true if the provided error indicates a
|
||||
// conflict exists between the original changes and the applied
|
||||
// changes.
|
||||
func IsConflicting(err error) bool {
|
||||
return err == ErrConflict
|
||||
}
|
||||
|
||||
// IsPreconditionFailed returns true if the provided error indicates
|
||||
// a Delta precondition did not succeed.
|
||||
func IsPreconditionFailed(err error) bool {
|
||||
return err == ErrPreconditionFailed
|
||||
}
|
||||
|
||||
var ErrPreconditionFailed = fmt.Errorf("a precondition failed")
|
||||
var ErrConflict = fmt.Errorf("changes are in conflict")
|
||||
|
||||
func (d *Delta) Edit() []byte {
|
||||
return d.edit
|
||||
}
|
||||
150
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/printing.go
generated
vendored
Normal file
150
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/printing.go
generated
vendored
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
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/kubectl"
|
||||
"k8s.io/kubernetes/pkg/runtime/schema"
|
||||
|
||||
"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 *schema.GroupVersion) (schema.GroupVersion, error) {
|
||||
outputVersionString := GetFlagString(cmd, "output-version")
|
||||
if len(outputVersionString) == 0 {
|
||||
if defaultVersion == nil {
|
||||
return schema.GroupVersion{}, nil
|
||||
}
|
||||
|
||||
return *defaultVersion, nil
|
||||
}
|
||||
|
||||
return schema.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
|
||||
}
|
||||
21
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/sanity/BUILD
generated
vendored
Normal file
21
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/sanity/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
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 = ["cmd_sanity.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/kubectl/cmd/templates:go_default_library",
|
||||
"//vendor:github.com/spf13/cobra",
|
||||
],
|
||||
)
|
||||
94
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/sanity/cmd_sanity.go
generated
vendored
Normal file
94
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/sanity/cmd_sanity.go
generated
vendored
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
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 sanity
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
)
|
||||
|
||||
type CmdCheck func(cmd *cobra.Command) []error
|
||||
|
||||
var (
|
||||
AllCmdChecks = []CmdCheck{
|
||||
CheckLongDesc,
|
||||
CheckExamples,
|
||||
}
|
||||
)
|
||||
|
||||
func CheckCmdTree(cmd *cobra.Command, checks []CmdCheck, skip []string) []error {
|
||||
cmdPath := cmd.CommandPath()
|
||||
|
||||
for _, skipCmdPath := range skip {
|
||||
if cmdPath == skipCmdPath {
|
||||
fmt.Fprintf(os.Stdout, "-----+ skipping command %s\n", cmdPath)
|
||||
return []error{}
|
||||
}
|
||||
}
|
||||
|
||||
errors := []error{}
|
||||
|
||||
if cmd.HasSubCommands() {
|
||||
for _, subCmd := range cmd.Commands() {
|
||||
errors = append(errors, CheckCmdTree(subCmd, checks, skip)...)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stdout, "-----+ checking command %s\n", cmdPath)
|
||||
|
||||
for _, check := range checks {
|
||||
if err := check(cmd); err != nil && len(err) > 0 {
|
||||
errors = append(errors, err...)
|
||||
}
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
func CheckLongDesc(cmd *cobra.Command) []error {
|
||||
cmdPath := cmd.CommandPath()
|
||||
long := cmd.Long
|
||||
if len(long) > 0 {
|
||||
if strings.Trim(long, " \t\n") != long {
|
||||
return []error{fmt.Errorf(`command %q: long description is not normalized
|
||||
↳ make sure you are calling templates.LongDesc (from pkg/cmd/templates) before assigning cmd.Long`, cmdPath)}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func CheckExamples(cmd *cobra.Command) []error {
|
||||
cmdPath := cmd.CommandPath()
|
||||
examples := cmd.Example
|
||||
errors := []error{}
|
||||
if len(examples) > 0 {
|
||||
for _, line := range strings.Split(examples, "\n") {
|
||||
if !strings.HasPrefix(line, templates.Indentation) {
|
||||
errors = append(errors, fmt.Errorf(`command %q: examples are not normalized
|
||||
↳ make sure you are calling templates.Examples (from pkg/cmd/templates) before assigning cmd.Example`, cmdPath))
|
||||
}
|
||||
if trimmed := strings.TrimSpace(line); strings.HasPrefix(trimmed, "//") {
|
||||
errors = append(errors, fmt.Errorf(`command %q: we use # to start comments in examples instead of //`, cmdPath))
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors
|
||||
}
|
||||
147
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/shortcut_restmapper.go
generated
vendored
Normal file
147
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/shortcut_restmapper.go
generated
vendored
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
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/client/typed/discovery"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// 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 []schema.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() []schema.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 := []schema.GroupVersionResource{}
|
||||
for groupVersionString, resourceList := range apiResources {
|
||||
currVersion, err := schema.ParseGroupVersion(groupVersionString)
|
||||
if err != nil {
|
||||
return e.All
|
||||
}
|
||||
|
||||
for _, resource := range resourceList.APIResources {
|
||||
availableResources = append(availableResources, currVersion.WithResource(resource.Name))
|
||||
}
|
||||
}
|
||||
|
||||
availableAll := []schema.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 schema.GroupVersionResource) (schema.GroupVersionKind, error) {
|
||||
return e.RESTMapper.KindFor(expandResourceShortcut(resource))
|
||||
}
|
||||
|
||||
func (e ShortcutExpander) KindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error) {
|
||||
return e.RESTMapper.KindsFor(expandResourceShortcut(resource))
|
||||
}
|
||||
|
||||
func (e ShortcutExpander) ResourcesFor(resource schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
|
||||
return e.RESTMapper.ResourcesFor(expandResourceShortcut(resource))
|
||||
}
|
||||
|
||||
func (e ShortcutExpander) ResourceFor(resource schema.GroupVersionResource) (schema.GroupVersionResource, error) {
|
||||
return e.RESTMapper.ResourceFor(expandResourceShortcut(resource))
|
||||
}
|
||||
|
||||
func (e ShortcutExpander) ResourceSingularizer(resource string) (string, error) {
|
||||
return e.RESTMapper.ResourceSingularizer(expandResourceShortcut(schema.GroupVersionResource{Resource: resource}).Resource)
|
||||
}
|
||||
|
||||
func (e ShortcutExpander) RESTMapping(gk schema.GroupKind, versions ...string) (*meta.RESTMapping, error) {
|
||||
return e.RESTMapper.RESTMapping(gk, versions...)
|
||||
}
|
||||
|
||||
func (e ShortcutExpander) RESTMappings(gk schema.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 = []schema.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 []schema.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(schema.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 schema.GroupVersionResource) schema.GroupVersionResource {
|
||||
if expanded, ok := kubectl.ShortForms[resource.Resource]; ok {
|
||||
resource.Resource = expanded
|
||||
return resource
|
||||
}
|
||||
return resource
|
||||
}
|
||||
61
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/shortcut_restmapper_test.go
generated
vendored
Normal file
61
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/shortcut_restmapper_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
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"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
)
|
||||
|
||||
func TestReplaceAliases(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
arg string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "no-replacement",
|
||||
arg: "service",
|
||||
expected: "service",
|
||||
},
|
||||
{
|
||||
name: "all-replacement",
|
||||
arg: "all",
|
||||
expected: "pods,replicationcontrollers,services,statefulsets,horizontalpodautoscalers,jobs,deployments,replicasets",
|
||||
},
|
||||
{
|
||||
name: "alias-in-comma-separated-arg",
|
||||
arg: "all,secrets",
|
||||
expected: "pods,replicationcontrollers,services,statefulsets,horizontalpodautoscalers,jobs,deployments,replicasets,secrets",
|
||||
},
|
||||
}
|
||||
|
||||
mapper := NewShortcutExpander(testapi.Default.RESTMapper(), nil)
|
||||
|
||||
for _, test := range tests {
|
||||
resources := []string{}
|
||||
for _, arg := range strings.Split(test.arg, ",") {
|
||||
curr, _ := mapper.AliasesForResource(arg)
|
||||
resources = append(resources, curr...)
|
||||
}
|
||||
if strings.Join(resources, ",") != test.expected {
|
||||
t.Errorf("%s: unexpected argument: expected %s, got %s", test.name, test.expected, resources)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue