Update go dependencies
This commit is contained in:
parent
a46126a034
commit
3eafaa35a1
1108 changed files with 32555 additions and 83490 deletions
97
vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/apimachinery.go
generated
vendored
Normal file
97
vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/apimachinery.go
generated
vendored
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package apiutil contains utilities for working with raw Kubernetes
|
||||
// API machinery, such as creating RESTMappers and raw REST clients,
|
||||
// and extracting the GVK of an object.
|
||||
package apiutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/restmapper"
|
||||
)
|
||||
|
||||
// NewDiscoveryRESTMapper constructs a new RESTMapper based on discovery
|
||||
// information fetched by a new client with the given config.
|
||||
func NewDiscoveryRESTMapper(c *rest.Config) (meta.RESTMapper, error) {
|
||||
// Get a mapper
|
||||
dc, err := discovery.NewDiscoveryClientForConfig(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gr, err := restmapper.GetAPIGroupResources(dc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return restmapper.NewDiscoveryRESTMapper(gr), nil
|
||||
}
|
||||
|
||||
// GVKForObject finds the GroupVersionKind associated with the given object, if there is only a single such GVK.
|
||||
func GVKForObject(obj runtime.Object, scheme *runtime.Scheme) (schema.GroupVersionKind, error) {
|
||||
gvks, isUnversioned, err := scheme.ObjectKinds(obj)
|
||||
if err != nil {
|
||||
return schema.GroupVersionKind{}, err
|
||||
}
|
||||
if isUnversioned {
|
||||
return schema.GroupVersionKind{}, fmt.Errorf("cannot create a new informer for the unversioned type %T", obj)
|
||||
}
|
||||
|
||||
if len(gvks) < 1 {
|
||||
return schema.GroupVersionKind{}, fmt.Errorf("no group-version-kinds associated with type %T", obj)
|
||||
}
|
||||
if len(gvks) > 1 {
|
||||
// this should only trigger for things like metav1.XYZ --
|
||||
// normal versioned types should be fine
|
||||
return schema.GroupVersionKind{}, fmt.Errorf(
|
||||
"multiple group-version-kinds associated with type %T, refusing to guess at one", obj)
|
||||
}
|
||||
return gvks[0], nil
|
||||
}
|
||||
|
||||
// RESTClientForGVK constructs a new rest.Interface capable of accessing the resource associated
|
||||
// with the given GroupVersionKind. The REST client will be configured to use the negotiated serializer from
|
||||
// baseConfig, if set, otherwise a default serializer will be set.
|
||||
func RESTClientForGVK(gvk schema.GroupVersionKind, baseConfig *rest.Config, codecs serializer.CodecFactory) (rest.Interface, error) {
|
||||
cfg := createRestConfig(gvk, baseConfig)
|
||||
if cfg.NegotiatedSerializer == nil {
|
||||
cfg.NegotiatedSerializer = serializer.WithoutConversionCodecFactory{CodecFactory: codecs}
|
||||
}
|
||||
return rest.RESTClientFor(cfg)
|
||||
}
|
||||
|
||||
//createRestConfig copies the base config and updates needed fields for a new rest config
|
||||
func createRestConfig(gvk schema.GroupVersionKind, baseConfig *rest.Config) *rest.Config {
|
||||
gv := gvk.GroupVersion()
|
||||
|
||||
cfg := rest.CopyConfig(baseConfig)
|
||||
cfg.GroupVersion = &gv
|
||||
if gvk.Group == "" {
|
||||
cfg.APIPath = "/api"
|
||||
} else {
|
||||
cfg.APIPath = "/apis"
|
||||
}
|
||||
if cfg.UserAgent == "" {
|
||||
cfg.UserAgent = rest.DefaultKubernetesUserAgent()
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
323
vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/dynamicrestmapper.go
generated
vendored
Normal file
323
vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/dynamicrestmapper.go
generated
vendored
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
/*
|
||||
Copyright 2019 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 apiutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/time/rate"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/restmapper"
|
||||
)
|
||||
|
||||
// ErrRateLimited is returned by a RESTMapper method if the number of API
|
||||
// calls has exceeded a limit within a certain time period.
|
||||
type ErrRateLimited struct {
|
||||
// Duration to wait until the next API call can be made.
|
||||
Delay time.Duration
|
||||
}
|
||||
|
||||
func (e ErrRateLimited) Error() string {
|
||||
return "too many API calls to the RESTMapper within a timeframe"
|
||||
}
|
||||
|
||||
// DelayIfRateLimited returns the delay time until the next API call is
|
||||
// allowed and true if err is of type ErrRateLimited. The zero
|
||||
// time.Duration value and false are returned if err is not a ErrRateLimited.
|
||||
func DelayIfRateLimited(err error) (time.Duration, bool) {
|
||||
var rlerr ErrRateLimited
|
||||
if errors.As(err, &rlerr) {
|
||||
return rlerr.Delay, true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// dynamicRESTMapper is a RESTMapper that dynamically discovers resource
|
||||
// types at runtime.
|
||||
type dynamicRESTMapper struct {
|
||||
mu sync.RWMutex // protects the following fields
|
||||
staticMapper meta.RESTMapper
|
||||
limiter *dynamicLimiter
|
||||
newMapper func() (meta.RESTMapper, error)
|
||||
|
||||
lazy bool
|
||||
// Used for lazy init.
|
||||
initOnce sync.Once
|
||||
}
|
||||
|
||||
// DynamicRESTMapperOption is a functional option on the dynamicRESTMapper
|
||||
type DynamicRESTMapperOption func(*dynamicRESTMapper) error
|
||||
|
||||
// WithLimiter sets the RESTMapper's underlying limiter to lim.
|
||||
func WithLimiter(lim *rate.Limiter) DynamicRESTMapperOption {
|
||||
return func(drm *dynamicRESTMapper) error {
|
||||
drm.limiter = &dynamicLimiter{lim}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithLazyDiscovery prevents the RESTMapper from discovering REST mappings
|
||||
// until an API call is made.
|
||||
var WithLazyDiscovery DynamicRESTMapperOption = func(drm *dynamicRESTMapper) error {
|
||||
drm.lazy = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithCustomMapper supports setting a custom RESTMapper refresher instead of
|
||||
// the default method, which uses a discovery client.
|
||||
//
|
||||
// This exists mainly for testing, but can be useful if you need tighter control
|
||||
// over how discovery is performed, which discovery endpoints are queried, etc.
|
||||
func WithCustomMapper(newMapper func() (meta.RESTMapper, error)) DynamicRESTMapperOption {
|
||||
return func(drm *dynamicRESTMapper) error {
|
||||
drm.newMapper = newMapper
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// NewDynamicRESTMapper returns a dynamic RESTMapper for cfg. The dynamic
|
||||
// RESTMapper dynamically discovers resource types at runtime. opts
|
||||
// configure the RESTMapper.
|
||||
func NewDynamicRESTMapper(cfg *rest.Config, opts ...DynamicRESTMapperOption) (meta.RESTMapper, error) {
|
||||
client, err := discovery.NewDiscoveryClientForConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
drm := &dynamicRESTMapper{
|
||||
limiter: &dynamicLimiter{
|
||||
rate.NewLimiter(rate.Limit(defaultRefillRate), defaultLimitSize),
|
||||
},
|
||||
newMapper: func() (meta.RESTMapper, error) {
|
||||
groupResources, err := restmapper.GetAPIGroupResources(client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return restmapper.NewDiscoveryRESTMapper(groupResources), nil
|
||||
},
|
||||
}
|
||||
for _, opt := range opts {
|
||||
if err = opt(drm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if !drm.lazy {
|
||||
if err := drm.setStaticMapper(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return drm, nil
|
||||
}
|
||||
|
||||
var (
|
||||
// defaultRefilRate is the default rate at which potential calls are
|
||||
// added back to the "bucket" of allowed calls.
|
||||
defaultRefillRate = 5
|
||||
// defaultLimitSize is the default starting/max number of potential calls
|
||||
// per second. Once a call is used, it's added back to the bucket at a rate
|
||||
// of defaultRefillRate per second.
|
||||
defaultLimitSize = 5
|
||||
)
|
||||
|
||||
// setStaticMapper sets drm's staticMapper by querying its client, regardless
|
||||
// of reload backoff.
|
||||
func (drm *dynamicRESTMapper) setStaticMapper() error {
|
||||
newMapper, err := drm.newMapper()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
drm.staticMapper = newMapper
|
||||
return nil
|
||||
}
|
||||
|
||||
// init initializes drm only once if drm is lazy.
|
||||
func (drm *dynamicRESTMapper) init() (err error) {
|
||||
drm.initOnce.Do(func() {
|
||||
if drm.lazy {
|
||||
err = drm.setStaticMapper()
|
||||
}
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// checkAndReload attempts to call the given callback, which is assumed to be dependent
|
||||
// on the data in the restmapper.
|
||||
//
|
||||
// If the callback returns a NoKindMatchError, it will attempt to reload
|
||||
// the RESTMapper's data and re-call the callback once that's occurred.
|
||||
// If the callback returns any other error, the function will return immediately regardless.
|
||||
//
|
||||
// It will take care
|
||||
// ensuring that reloads are rate-limitted and that extraneous calls aren't made.
|
||||
// It's thread-safe, and worries about thread-safety for the callback (so the callback does
|
||||
// not need to attempt to lock the restmapper).
|
||||
func (drm *dynamicRESTMapper) checkAndReload(needsReloadErr error, checkNeedsReload func() error) error {
|
||||
// first, check the common path -- data is fresh enough
|
||||
// (use an IIFE for the lock's defer)
|
||||
err := func() error {
|
||||
drm.mu.RLock()
|
||||
defer drm.mu.RUnlock()
|
||||
|
||||
return checkNeedsReload()
|
||||
}()
|
||||
|
||||
// NB(directxman12): `Is` and `As` have a confusing relationship --
|
||||
// `Is` is like `== or does this implement .Is`, whereas `As` says
|
||||
// `can I type-assert into`
|
||||
needsReload := errors.As(err, &needsReloadErr)
|
||||
if !needsReload {
|
||||
return err
|
||||
}
|
||||
|
||||
// if the data wasn't fresh, we'll need to try and update it, so grab the lock...
|
||||
drm.mu.Lock()
|
||||
defer drm.mu.Unlock()
|
||||
|
||||
// ... and double-check that we didn't reload in the meantime
|
||||
err = checkNeedsReload()
|
||||
needsReload = errors.As(err, &needsReloadErr)
|
||||
if !needsReload {
|
||||
return err
|
||||
}
|
||||
|
||||
// we're still stale, so grab a rate-limit token if we can...
|
||||
if err := drm.limiter.checkRate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// ...reload...
|
||||
if err := drm.setStaticMapper(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// ...and return the results of the closure regardless
|
||||
return checkNeedsReload()
|
||||
}
|
||||
|
||||
// TODO: wrap reload errors on NoKindMatchError with go 1.13 errors.
|
||||
|
||||
func (drm *dynamicRESTMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
|
||||
if err := drm.init(); err != nil {
|
||||
return schema.GroupVersionKind{}, err
|
||||
}
|
||||
var gvk schema.GroupVersionKind
|
||||
err := drm.checkAndReload(&meta.NoResourceMatchError{}, func() error {
|
||||
var err error
|
||||
gvk, err = drm.staticMapper.KindFor(resource)
|
||||
return err
|
||||
})
|
||||
return gvk, err
|
||||
}
|
||||
|
||||
func (drm *dynamicRESTMapper) KindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error) {
|
||||
if err := drm.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var gvks []schema.GroupVersionKind
|
||||
err := drm.checkAndReload(&meta.NoResourceMatchError{}, func() error {
|
||||
var err error
|
||||
gvks, err = drm.staticMapper.KindsFor(resource)
|
||||
return err
|
||||
})
|
||||
return gvks, err
|
||||
}
|
||||
|
||||
func (drm *dynamicRESTMapper) ResourceFor(input schema.GroupVersionResource) (schema.GroupVersionResource, error) {
|
||||
if err := drm.init(); err != nil {
|
||||
return schema.GroupVersionResource{}, err
|
||||
}
|
||||
|
||||
var gvr schema.GroupVersionResource
|
||||
err := drm.checkAndReload(&meta.NoResourceMatchError{}, func() error {
|
||||
var err error
|
||||
gvr, err = drm.staticMapper.ResourceFor(input)
|
||||
return err
|
||||
})
|
||||
return gvr, err
|
||||
}
|
||||
|
||||
func (drm *dynamicRESTMapper) ResourcesFor(input schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
|
||||
if err := drm.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var gvrs []schema.GroupVersionResource
|
||||
err := drm.checkAndReload(&meta.NoResourceMatchError{}, func() error {
|
||||
var err error
|
||||
gvrs, err = drm.staticMapper.ResourcesFor(input)
|
||||
return err
|
||||
})
|
||||
return gvrs, err
|
||||
}
|
||||
|
||||
func (drm *dynamicRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*meta.RESTMapping, error) {
|
||||
if err := drm.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var mapping *meta.RESTMapping
|
||||
err := drm.checkAndReload(&meta.NoKindMatchError{}, func() error {
|
||||
var err error
|
||||
mapping, err = drm.staticMapper.RESTMapping(gk, versions...)
|
||||
return err
|
||||
})
|
||||
return mapping, err
|
||||
}
|
||||
|
||||
func (drm *dynamicRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*meta.RESTMapping, error) {
|
||||
if err := drm.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var mappings []*meta.RESTMapping
|
||||
err := drm.checkAndReload(&meta.NoKindMatchError{}, func() error {
|
||||
var err error
|
||||
mappings, err = drm.staticMapper.RESTMappings(gk, versions...)
|
||||
return err
|
||||
})
|
||||
return mappings, err
|
||||
}
|
||||
|
||||
func (drm *dynamicRESTMapper) ResourceSingularizer(resource string) (string, error) {
|
||||
if err := drm.init(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
var singular string
|
||||
err := drm.checkAndReload(&meta.NoResourceMatchError{}, func() error {
|
||||
var err error
|
||||
singular, err = drm.staticMapper.ResourceSingularizer(resource)
|
||||
return err
|
||||
})
|
||||
return singular, err
|
||||
}
|
||||
|
||||
// dynamicLimiter holds a rate limiter used to throttle chatty RESTMapper users.
|
||||
type dynamicLimiter struct {
|
||||
*rate.Limiter
|
||||
}
|
||||
|
||||
// checkRate returns an ErrRateLimited if too many API calls have been made
|
||||
// within the set limit.
|
||||
func (b *dynamicLimiter) checkRate() error {
|
||||
res := b.Reserve()
|
||||
if res.Delay() == 0 {
|
||||
return nil
|
||||
}
|
||||
res.Cancel()
|
||||
return ErrRateLimited{res.Delay()}
|
||||
}
|
||||
208
vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go
generated
vendored
Normal file
208
vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go
generated
vendored
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
||||
)
|
||||
|
||||
// Options are creation options for a Client
|
||||
type Options struct {
|
||||
// Scheme, if provided, will be used to map go structs to GroupVersionKinds
|
||||
Scheme *runtime.Scheme
|
||||
|
||||
// Mapper, if provided, will be used to map GroupVersionKinds to Resources
|
||||
Mapper meta.RESTMapper
|
||||
}
|
||||
|
||||
// New returns a new Client using the provided config and Options.
|
||||
// The returned client reads *and* writes directly from the server
|
||||
// (it doesn't use object caches). It understands how to work with
|
||||
// normal types (both custom resources and aggregated/built-in resources),
|
||||
// as well as unstructured types.
|
||||
//
|
||||
// In the case of normal types, the scheme will be used to look up the
|
||||
// corresponding group, version, and kind for the given type. In the
|
||||
// case of unstructured types, the group, version, and kind will be extracted
|
||||
// from the corresponding fields on the object.
|
||||
func New(config *rest.Config, options Options) (Client, error) {
|
||||
if config == nil {
|
||||
return nil, fmt.Errorf("must provide non-nil rest.Config to client.New")
|
||||
}
|
||||
|
||||
// Init a scheme if none provided
|
||||
if options.Scheme == nil {
|
||||
options.Scheme = scheme.Scheme
|
||||
}
|
||||
|
||||
// Init a Mapper if none provided
|
||||
if options.Mapper == nil {
|
||||
var err error
|
||||
options.Mapper, err = apiutil.NewDynamicRESTMapper(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
clientcache := &clientCache{
|
||||
config: config,
|
||||
scheme: options.Scheme,
|
||||
mapper: options.Mapper,
|
||||
codecs: serializer.NewCodecFactory(options.Scheme),
|
||||
resourceByType: make(map[schema.GroupVersionKind]*resourceMeta),
|
||||
}
|
||||
|
||||
c := &client{
|
||||
typedClient: typedClient{
|
||||
cache: clientcache,
|
||||
paramCodec: runtime.NewParameterCodec(options.Scheme),
|
||||
},
|
||||
unstructuredClient: unstructuredClient{
|
||||
cache: clientcache,
|
||||
paramCodec: noConversionParamCodec{},
|
||||
},
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
var _ Client = &client{}
|
||||
|
||||
// client is a client.Client that reads and writes directly from/to an API server. It lazily initializes
|
||||
// new clients at the time they are used, and caches the client.
|
||||
type client struct {
|
||||
typedClient typedClient
|
||||
unstructuredClient unstructuredClient
|
||||
}
|
||||
|
||||
// resetGroupVersionKind is a helper function to restore and preserve GroupVersionKind on an object.
|
||||
// TODO(vincepri): Remove this function and its calls once controller-runtime dependencies are upgraded to 1.16?
|
||||
func (c *client) resetGroupVersionKind(obj runtime.Object, gvk schema.GroupVersionKind) {
|
||||
if gvk != schema.EmptyObjectKind.GroupVersionKind() {
|
||||
if v, ok := obj.(schema.ObjectKind); ok {
|
||||
v.SetGroupVersionKind(gvk)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create implements client.Client
|
||||
func (c *client) Create(ctx context.Context, obj runtime.Object, opts ...CreateOption) error {
|
||||
_, ok := obj.(*unstructured.Unstructured)
|
||||
if ok {
|
||||
return c.unstructuredClient.Create(ctx, obj, opts...)
|
||||
}
|
||||
return c.typedClient.Create(ctx, obj, opts...)
|
||||
}
|
||||
|
||||
// Update implements client.Client
|
||||
func (c *client) Update(ctx context.Context, obj runtime.Object, opts ...UpdateOption) error {
|
||||
defer c.resetGroupVersionKind(obj, obj.GetObjectKind().GroupVersionKind())
|
||||
_, ok := obj.(*unstructured.Unstructured)
|
||||
if ok {
|
||||
return c.unstructuredClient.Update(ctx, obj, opts...)
|
||||
}
|
||||
return c.typedClient.Update(ctx, obj, opts...)
|
||||
}
|
||||
|
||||
// Delete implements client.Client
|
||||
func (c *client) Delete(ctx context.Context, obj runtime.Object, opts ...DeleteOption) error {
|
||||
_, ok := obj.(*unstructured.Unstructured)
|
||||
if ok {
|
||||
return c.unstructuredClient.Delete(ctx, obj, opts...)
|
||||
}
|
||||
return c.typedClient.Delete(ctx, obj, opts...)
|
||||
}
|
||||
|
||||
// DeleteAllOf implements client.Client
|
||||
func (c *client) DeleteAllOf(ctx context.Context, obj runtime.Object, opts ...DeleteAllOfOption) error {
|
||||
_, ok := obj.(*unstructured.Unstructured)
|
||||
if ok {
|
||||
return c.unstructuredClient.DeleteAllOf(ctx, obj, opts...)
|
||||
}
|
||||
return c.typedClient.DeleteAllOf(ctx, obj, opts...)
|
||||
}
|
||||
|
||||
// Patch implements client.Client
|
||||
func (c *client) Patch(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOption) error {
|
||||
defer c.resetGroupVersionKind(obj, obj.GetObjectKind().GroupVersionKind())
|
||||
_, ok := obj.(*unstructured.Unstructured)
|
||||
if ok {
|
||||
return c.unstructuredClient.Patch(ctx, obj, patch, opts...)
|
||||
}
|
||||
return c.typedClient.Patch(ctx, obj, patch, opts...)
|
||||
}
|
||||
|
||||
// Get implements client.Client
|
||||
func (c *client) Get(ctx context.Context, key ObjectKey, obj runtime.Object) error {
|
||||
_, ok := obj.(*unstructured.Unstructured)
|
||||
if ok {
|
||||
return c.unstructuredClient.Get(ctx, key, obj)
|
||||
}
|
||||
return c.typedClient.Get(ctx, key, obj)
|
||||
}
|
||||
|
||||
// List implements client.Client
|
||||
func (c *client) List(ctx context.Context, obj runtime.Object, opts ...ListOption) error {
|
||||
_, ok := obj.(*unstructured.UnstructuredList)
|
||||
if ok {
|
||||
return c.unstructuredClient.List(ctx, obj, opts...)
|
||||
}
|
||||
return c.typedClient.List(ctx, obj, opts...)
|
||||
}
|
||||
|
||||
// Status implements client.StatusClient
|
||||
func (c *client) Status() StatusWriter {
|
||||
return &statusWriter{client: c}
|
||||
}
|
||||
|
||||
// statusWriter is client.StatusWriter that writes status subresource
|
||||
type statusWriter struct {
|
||||
client *client
|
||||
}
|
||||
|
||||
// ensure statusWriter implements client.StatusWriter
|
||||
var _ StatusWriter = &statusWriter{}
|
||||
|
||||
// Update implements client.StatusWriter
|
||||
func (sw *statusWriter) Update(ctx context.Context, obj runtime.Object, opts ...UpdateOption) error {
|
||||
defer sw.client.resetGroupVersionKind(obj, obj.GetObjectKind().GroupVersionKind())
|
||||
_, ok := obj.(*unstructured.Unstructured)
|
||||
if ok {
|
||||
return sw.client.unstructuredClient.UpdateStatus(ctx, obj, opts...)
|
||||
}
|
||||
return sw.client.typedClient.UpdateStatus(ctx, obj, opts...)
|
||||
}
|
||||
|
||||
// Patch implements client.Client
|
||||
func (sw *statusWriter) Patch(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOption) error {
|
||||
defer sw.client.resetGroupVersionKind(obj, obj.GetObjectKind().GroupVersionKind())
|
||||
_, ok := obj.(*unstructured.Unstructured)
|
||||
if ok {
|
||||
return sw.client.unstructuredClient.PatchStatus(ctx, obj, patch, opts...)
|
||||
}
|
||||
return sw.client.typedClient.PatchStatus(ctx, obj, patch, opts...)
|
||||
}
|
||||
140
vendor/sigs.k8s.io/controller-runtime/pkg/client/client_cache.go
generated
vendored
Normal file
140
vendor/sigs.k8s.io/controller-runtime/pkg/client/client_cache.go
generated
vendored
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
||||
)
|
||||
|
||||
// clientCache creates and caches rest clients and metadata for Kubernetes types
|
||||
type clientCache struct {
|
||||
// config is the rest.Config to talk to an apiserver
|
||||
config *rest.Config
|
||||
|
||||
// scheme maps go structs to GroupVersionKinds
|
||||
scheme *runtime.Scheme
|
||||
|
||||
// mapper maps GroupVersionKinds to Resources
|
||||
mapper meta.RESTMapper
|
||||
|
||||
// codecs are used to create a REST client for a gvk
|
||||
codecs serializer.CodecFactory
|
||||
|
||||
// resourceByType caches type metadata
|
||||
resourceByType map[schema.GroupVersionKind]*resourceMeta
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// newResource maps obj to a Kubernetes Resource and constructs a client for that Resource.
|
||||
// If the object is a list, the resource represents the item's type instead.
|
||||
func (c *clientCache) newResource(gvk schema.GroupVersionKind, isList bool) (*resourceMeta, error) {
|
||||
if strings.HasSuffix(gvk.Kind, "List") && isList {
|
||||
// if this was a list, treat it as a request for the item's resource
|
||||
gvk.Kind = gvk.Kind[:len(gvk.Kind)-4]
|
||||
}
|
||||
|
||||
client, err := apiutil.RESTClientForGVK(gvk, c.config, c.codecs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mapping, err := c.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resourceMeta{Interface: client, mapping: mapping, gvk: gvk}, nil
|
||||
}
|
||||
|
||||
// getResource returns the resource meta information for the given type of object.
|
||||
// If the object is a list, the resource represents the item's type instead.
|
||||
func (c *clientCache) getResource(obj runtime.Object) (*resourceMeta, error) {
|
||||
gvk, err := apiutil.GVKForObject(obj, c.scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// It's better to do creation work twice than to not let multiple
|
||||
// people make requests at once
|
||||
c.mu.RLock()
|
||||
r, known := c.resourceByType[gvk]
|
||||
c.mu.RUnlock()
|
||||
|
||||
if known {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// Initialize a new Client
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
r, err = c.newResource(gvk, meta.IsListType(obj))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.resourceByType[gvk] = r
|
||||
return r, err
|
||||
}
|
||||
|
||||
// getObjMeta returns objMeta containing both type and object metadata and state
|
||||
func (c *clientCache) getObjMeta(obj runtime.Object) (*objMeta, error) {
|
||||
r, err := c.getResource(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &objMeta{resourceMeta: r, Object: m}, err
|
||||
}
|
||||
|
||||
// resourceMeta caches state for a Kubernetes type.
|
||||
type resourceMeta struct {
|
||||
// client is the rest client used to talk to the apiserver
|
||||
rest.Interface
|
||||
// gvk is the GroupVersionKind of the resourceMeta
|
||||
gvk schema.GroupVersionKind
|
||||
// mapping is the rest mapping
|
||||
mapping *meta.RESTMapping
|
||||
}
|
||||
|
||||
// isNamespaced returns true if the type is namespaced
|
||||
func (r *resourceMeta) isNamespaced() bool {
|
||||
return r.mapping.Scope.Name() != meta.RESTScopeNameRoot
|
||||
|
||||
}
|
||||
|
||||
// resource returns the resource name of the type
|
||||
func (r *resourceMeta) resource() string {
|
||||
return r.mapping.Resource.Resource
|
||||
}
|
||||
|
||||
// objMeta stores type and object information about a Kubernetes type
|
||||
type objMeta struct {
|
||||
// resourceMeta contains type information for the object
|
||||
*resourceMeta
|
||||
|
||||
// Object contains meta data for the object instance
|
||||
metav1.Object
|
||||
}
|
||||
24
vendor/sigs.k8s.io/controller-runtime/pkg/client/codec.go
generated
vendored
Normal file
24
vendor/sigs.k8s.io/controller-runtime/pkg/client/codec.go
generated
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
|
||||
"k8s.io/apimachinery/pkg/conversion/queryparams"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
var _ runtime.ParameterCodec = noConversionParamCodec{}
|
||||
|
||||
// noConversionParamCodec is a no-conversion codec for serializing parameters into URL query strings.
|
||||
// it's useful in scenarios with the unstructured client and arbitrary resouces.
|
||||
type noConversionParamCodec struct{}
|
||||
|
||||
func (noConversionParamCodec) EncodeParameters(obj runtime.Object, to schema.GroupVersion) (url.Values, error) {
|
||||
return queryparams.Convert(obj)
|
||||
}
|
||||
|
||||
func (noConversionParamCodec) DecodeParameters(parameters url.Values, from schema.GroupVersion, into runtime.Object) error {
|
||||
return errors.New("DecodeParameters not implemented on noConversionParamCodec")
|
||||
}
|
||||
18
vendor/sigs.k8s.io/controller-runtime/pkg/client/config/config.go
generated
vendored
18
vendor/sigs.k8s.io/controller-runtime/pkg/client/config/config.go
generated
vendored
|
|
@ -20,6 +20,8 @@ import (
|
|||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
|
@ -117,7 +119,21 @@ func loadConfig(context string) (*rest.Config, error) {
|
|||
|
||||
// If the recommended kubeconfig env variable is set, or there
|
||||
// is no in-cluster config, try the default recommended locations.
|
||||
if c, err := loadConfigWithContext(apiServerURL, clientcmd.NewDefaultClientConfigLoadingRules(), context); err == nil {
|
||||
//
|
||||
// NOTE: For default config file locations, upstream only checks
|
||||
// $HOME for the user's home directory, but we can also try
|
||||
// os/user.HomeDir when $HOME is unset.
|
||||
//
|
||||
// TODO(jlanford): could this be done upstream?
|
||||
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
|
||||
if _, ok := os.LookupEnv("HOME"); !ok {
|
||||
u, err := user.Current()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get current user: %v", err)
|
||||
}
|
||||
loadingRules.Precedence = append(loadingRules.Precedence, path.Join(u.HomeDir, clientcmd.RecommendedHomeDir, clientcmd.RecommendedFileName))
|
||||
}
|
||||
if c, err := loadConfigWithContext(apiServerURL, loadingRules, context); err == nil {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
49
vendor/sigs.k8s.io/controller-runtime/pkg/client/doc.go
generated
vendored
Normal file
49
vendor/sigs.k8s.io/controller-runtime/pkg/client/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package client contains functionality for interacting with Kubernetes API
|
||||
// servers.
|
||||
//
|
||||
// Clients
|
||||
//
|
||||
// Clients are split into two interfaces -- Readers and Writers. Readers
|
||||
// get and list, while writers create, update, and delete.
|
||||
//
|
||||
// The New function can be used to create a new client that talks directly
|
||||
// to the API server.
|
||||
//
|
||||
// A common pattern in Kubernetes to read from a cache and write to the API
|
||||
// server. This pattern is covered by the DelegatingClient type, which can
|
||||
// be used to have a client whose Reader is different from the Writer.
|
||||
//
|
||||
// Options
|
||||
//
|
||||
// Many client operations in Kubernetes support options. These options are
|
||||
// represented as variadic arguments at the end of a given method call.
|
||||
// For instance, to use a label selector on list, you can call
|
||||
// err := someReader.List(context.Background(), &podList, client.MatchingLabels{"somelabel": "someval"})
|
||||
//
|
||||
// Indexing
|
||||
//
|
||||
// Indexes may be added to caches using a FieldIndexer. This allows you to easily
|
||||
// and efficiently look up objects with certain properties. You can then make
|
||||
// use of the index by specifying a field selector on calls to List on the Reader
|
||||
// corresponding to the given Cache.
|
||||
//
|
||||
// For instance, a Secret controller might have an index on the
|
||||
// `.spec.volumes.secret.secretName` field in Pod objects, so that it could
|
||||
// easily look up all pods that reference a given secret.
|
||||
package client
|
||||
135
vendor/sigs.k8s.io/controller-runtime/pkg/client/interfaces.go
generated
vendored
Normal file
135
vendor/sigs.k8s.io/controller-runtime/pkg/client/interfaces.go
generated
vendored
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
// ObjectKey identifies a Kubernetes Object.
|
||||
type ObjectKey = types.NamespacedName
|
||||
|
||||
// ObjectKeyFromObject returns the ObjectKey given a runtime.Object
|
||||
func ObjectKeyFromObject(obj runtime.Object) (ObjectKey, error) {
|
||||
accessor, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return ObjectKey{}, err
|
||||
}
|
||||
return ObjectKey{Namespace: accessor.GetNamespace(), Name: accessor.GetName()}, nil
|
||||
}
|
||||
|
||||
// Patch is a patch that can be applied to a Kubernetes object.
|
||||
type Patch interface {
|
||||
// Type is the PatchType of the patch.
|
||||
Type() types.PatchType
|
||||
// Data is the raw data representing the patch.
|
||||
Data(obj runtime.Object) ([]byte, error)
|
||||
}
|
||||
|
||||
// TODO(directxman12): is there a sane way to deal with get/delete options?
|
||||
|
||||
// Reader knows how to read and list Kubernetes objects.
|
||||
type Reader interface {
|
||||
// Get retrieves an obj for the given object key from the Kubernetes Cluster.
|
||||
// obj must be a struct pointer so that obj can be updated with the response
|
||||
// returned by the Server.
|
||||
Get(ctx context.Context, key ObjectKey, obj runtime.Object) error
|
||||
|
||||
// List retrieves list of objects for a given namespace and list options. On a
|
||||
// successful call, Items field in the list will be populated with the
|
||||
// result returned from the server.
|
||||
List(ctx context.Context, list runtime.Object, opts ...ListOption) error
|
||||
}
|
||||
|
||||
// Writer knows how to create, delete, and update Kubernetes objects.
|
||||
type Writer interface {
|
||||
// Create saves the object obj in the Kubernetes cluster.
|
||||
Create(ctx context.Context, obj runtime.Object, opts ...CreateOption) error
|
||||
|
||||
// Delete deletes the given obj from Kubernetes cluster.
|
||||
Delete(ctx context.Context, obj runtime.Object, opts ...DeleteOption) error
|
||||
|
||||
// Update updates the given obj in the Kubernetes cluster. obj must be a
|
||||
// struct pointer so that obj can be updated with the content returned by the Server.
|
||||
Update(ctx context.Context, obj runtime.Object, opts ...UpdateOption) error
|
||||
|
||||
// Patch patches the given obj in the Kubernetes cluster. obj must be a
|
||||
// struct pointer so that obj can be updated with the content returned by the Server.
|
||||
Patch(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOption) error
|
||||
|
||||
// DeleteAllOf deletes all objects of the given type matching the given options.
|
||||
DeleteAllOf(ctx context.Context, obj runtime.Object, opts ...DeleteAllOfOption) error
|
||||
}
|
||||
|
||||
// StatusClient knows how to create a client which can update status subresource
|
||||
// for kubernetes objects.
|
||||
type StatusClient interface {
|
||||
Status() StatusWriter
|
||||
}
|
||||
|
||||
// StatusWriter knows how to update status subresource of a Kubernetes object.
|
||||
type StatusWriter interface {
|
||||
// Update updates the fields corresponding to the status subresource for the
|
||||
// given obj. obj must be a struct pointer so that obj can be updated
|
||||
// with the content returned by the Server.
|
||||
Update(ctx context.Context, obj runtime.Object, opts ...UpdateOption) error
|
||||
|
||||
// Patch patches the given object's subresource. obj must be a struct
|
||||
// pointer so that obj can be updated with the content returned by the
|
||||
// Server.
|
||||
Patch(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOption) error
|
||||
}
|
||||
|
||||
// Client knows how to perform CRUD operations on Kubernetes objects.
|
||||
type Client interface {
|
||||
Reader
|
||||
Writer
|
||||
StatusClient
|
||||
}
|
||||
|
||||
// IndexerFunc knows how to take an object and turn it into a series
|
||||
// of non-namespaced keys. Namespaced objects are automatically given
|
||||
// namespaced and non-spaced variants, so keys do not need to include namespace.
|
||||
type IndexerFunc func(runtime.Object) []string
|
||||
|
||||
// FieldIndexer knows how to index over a particular "field" such that it
|
||||
// can later be used by a field selector.
|
||||
type FieldIndexer interface {
|
||||
// IndexFields adds an index with the given field name on the given object type
|
||||
// by using the given function to extract the value for that field. If you want
|
||||
// compatibility with the Kubernetes API server, only return one key, and only use
|
||||
// fields that the API server supports. Otherwise, you can return multiple keys,
|
||||
// and "equality" in the field selector means that at least one key matches the value.
|
||||
// The FieldIndexer will automatically take care of indexing over namespace
|
||||
// and supporting efficient all-namespace queries.
|
||||
IndexField(ctx context.Context, obj runtime.Object, field string, extractValue IndexerFunc) error
|
||||
}
|
||||
|
||||
// IgnoreNotFound returns nil on NotFound errors.
|
||||
// All other values that are not NotFound errors or nil are returned unmodified.
|
||||
func IgnoreNotFound(err error) error {
|
||||
if apierrors.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
675
vendor/sigs.k8s.io/controller-runtime/pkg/client/options.go
generated
vendored
Normal file
675
vendor/sigs.k8s.io/controller-runtime/pkg/client/options.go
generated
vendored
Normal file
|
|
@ -0,0 +1,675 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/selection"
|
||||
)
|
||||
|
||||
// {{{ "Functional" Option Interfaces
|
||||
|
||||
// CreateOption is some configuration that modifies options for a create request.
|
||||
type CreateOption interface {
|
||||
// ApplyToCreate applies this configuration to the given create options.
|
||||
ApplyToCreate(*CreateOptions)
|
||||
}
|
||||
|
||||
// DeleteOption is some configuration that modifies options for a delete request.
|
||||
type DeleteOption interface {
|
||||
// ApplyToDelete applies this configuration to the given delete options.
|
||||
ApplyToDelete(*DeleteOptions)
|
||||
}
|
||||
|
||||
// ListOption is some configuration that modifies options for a list request.
|
||||
type ListOption interface {
|
||||
// ApplyToList applies this configuration to the given list options.
|
||||
ApplyToList(*ListOptions)
|
||||
}
|
||||
|
||||
// UpdateOption is some configuration that modifies options for a update request.
|
||||
type UpdateOption interface {
|
||||
// ApplyToUpdate applies this configuration to the given update options.
|
||||
ApplyToUpdate(*UpdateOptions)
|
||||
}
|
||||
|
||||
// PatchOption is some configuration that modifies options for a patch request.
|
||||
type PatchOption interface {
|
||||
// ApplyToPatch applies this configuration to the given patch options.
|
||||
ApplyToPatch(*PatchOptions)
|
||||
}
|
||||
|
||||
// DeleteAllOfOption is some configuration that modifies options for a delete request.
|
||||
type DeleteAllOfOption interface {
|
||||
// ApplyToDeleteAllOf applies this configuration to the given deletecollection options.
|
||||
ApplyToDeleteAllOf(*DeleteAllOfOptions)
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
// {{{ Multi-Type Options
|
||||
|
||||
// DryRunAll sets the "dry run" option to "all", executing all
|
||||
// validation, etc without persisting the change to storage.
|
||||
var DryRunAll = dryRunAll{}
|
||||
|
||||
type dryRunAll struct{}
|
||||
|
||||
func (dryRunAll) ApplyToCreate(opts *CreateOptions) {
|
||||
opts.DryRun = []string{metav1.DryRunAll}
|
||||
}
|
||||
func (dryRunAll) ApplyToUpdate(opts *UpdateOptions) {
|
||||
opts.DryRun = []string{metav1.DryRunAll}
|
||||
}
|
||||
func (dryRunAll) ApplyToPatch(opts *PatchOptions) {
|
||||
opts.DryRun = []string{metav1.DryRunAll}
|
||||
}
|
||||
func (dryRunAll) ApplyToDelete(opts *DeleteOptions) {
|
||||
opts.DryRun = []string{metav1.DryRunAll}
|
||||
}
|
||||
|
||||
// FieldOwner set the field manager name for the given server-side apply patch.
|
||||
type FieldOwner string
|
||||
|
||||
func (f FieldOwner) ApplyToPatch(opts *PatchOptions) {
|
||||
opts.FieldManager = string(f)
|
||||
}
|
||||
func (f FieldOwner) ApplyToCreate(opts *CreateOptions) {
|
||||
opts.FieldManager = string(f)
|
||||
}
|
||||
func (f FieldOwner) ApplyToUpdate(opts *UpdateOptions) {
|
||||
opts.FieldManager = string(f)
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
// {{{ Create Options
|
||||
|
||||
// CreateOptions contains options for create requests. It's generally a subset
|
||||
// of metav1.CreateOptions.
|
||||
type CreateOptions struct {
|
||||
// When present, indicates that modifications should not be
|
||||
// persisted. An invalid or unrecognized dryRun directive will
|
||||
// result in an error response and no further processing of the
|
||||
// request. Valid values are:
|
||||
// - All: all dry run stages will be processed
|
||||
DryRun []string
|
||||
|
||||
// FieldManager is the name of the user or component submitting
|
||||
// this request. It must be set with server-side apply.
|
||||
FieldManager string
|
||||
|
||||
// Raw represents raw CreateOptions, as passed to the API server.
|
||||
Raw *metav1.CreateOptions
|
||||
}
|
||||
|
||||
// AsCreateOptions returns these options as a metav1.CreateOptions.
|
||||
// This may mutate the Raw field.
|
||||
func (o *CreateOptions) AsCreateOptions() *metav1.CreateOptions {
|
||||
if o == nil {
|
||||
return &metav1.CreateOptions{}
|
||||
}
|
||||
if o.Raw == nil {
|
||||
o.Raw = &metav1.CreateOptions{}
|
||||
}
|
||||
|
||||
o.Raw.DryRun = o.DryRun
|
||||
o.Raw.FieldManager = o.FieldManager
|
||||
return o.Raw
|
||||
}
|
||||
|
||||
// ApplyOptions applies the given create options on these options,
|
||||
// and then returns itself (for convenient chaining).
|
||||
func (o *CreateOptions) ApplyOptions(opts []CreateOption) *CreateOptions {
|
||||
for _, opt := range opts {
|
||||
opt.ApplyToCreate(o)
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
// ApplyToCreate implements CreateOption
|
||||
func (o *CreateOptions) ApplyToCreate(co *CreateOptions) {
|
||||
if o.DryRun != nil {
|
||||
co.DryRun = o.DryRun
|
||||
}
|
||||
if o.FieldManager != "" {
|
||||
co.FieldManager = o.FieldManager
|
||||
}
|
||||
if o.Raw != nil {
|
||||
co.Raw = o.Raw
|
||||
}
|
||||
}
|
||||
|
||||
var _ CreateOption = &CreateOptions{}
|
||||
|
||||
// CreateDryRunAll sets the "dry run" option to "all".
|
||||
//
|
||||
// Deprecated: Use DryRunAll
|
||||
var CreateDryRunAll = DryRunAll
|
||||
|
||||
// }}}
|
||||
|
||||
// {{{ Delete Options
|
||||
|
||||
// DeleteOptions contains options for delete requests. It's generally a subset
|
||||
// of metav1.DeleteOptions.
|
||||
type DeleteOptions struct {
|
||||
// GracePeriodSeconds is the duration in seconds before the object should be
|
||||
// deleted. Value must be non-negative integer. The value zero indicates
|
||||
// delete immediately. If this value is nil, the default grace period for the
|
||||
// specified type will be used.
|
||||
GracePeriodSeconds *int64
|
||||
|
||||
// Preconditions must be fulfilled before a deletion is carried out. If not
|
||||
// possible, a 409 Conflict status will be returned.
|
||||
Preconditions *metav1.Preconditions
|
||||
|
||||
// PropagationPolicy determined whether and how garbage collection will be
|
||||
// performed. Either this field or OrphanDependents may be set, but not both.
|
||||
// The default policy is decided by the existing finalizer set in the
|
||||
// metadata.finalizers and the resource-specific default policy.
|
||||
// Acceptable values are: 'Orphan' - orphan the dependents; 'Background' -
|
||||
// allow the garbage collector to delete the dependents in the background;
|
||||
// 'Foreground' - a cascading policy that deletes all dependents in the
|
||||
// foreground.
|
||||
PropagationPolicy *metav1.DeletionPropagation
|
||||
|
||||
// Raw represents raw DeleteOptions, as passed to the API server.
|
||||
Raw *metav1.DeleteOptions
|
||||
|
||||
// When present, indicates that modifications should not be
|
||||
// persisted. An invalid or unrecognized dryRun directive will
|
||||
// result in an error response and no further processing of the
|
||||
// request. Valid values are:
|
||||
// - All: all dry run stages will be processed
|
||||
DryRun []string
|
||||
}
|
||||
|
||||
// AsDeleteOptions returns these options as a metav1.DeleteOptions.
|
||||
// This may mutate the Raw field.
|
||||
func (o *DeleteOptions) AsDeleteOptions() *metav1.DeleteOptions {
|
||||
if o == nil {
|
||||
return &metav1.DeleteOptions{}
|
||||
}
|
||||
if o.Raw == nil {
|
||||
o.Raw = &metav1.DeleteOptions{}
|
||||
}
|
||||
|
||||
o.Raw.GracePeriodSeconds = o.GracePeriodSeconds
|
||||
o.Raw.Preconditions = o.Preconditions
|
||||
o.Raw.PropagationPolicy = o.PropagationPolicy
|
||||
o.Raw.DryRun = o.DryRun
|
||||
return o.Raw
|
||||
}
|
||||
|
||||
// ApplyOptions applies the given delete options on these options,
|
||||
// and then returns itself (for convenient chaining).
|
||||
func (o *DeleteOptions) ApplyOptions(opts []DeleteOption) *DeleteOptions {
|
||||
for _, opt := range opts {
|
||||
opt.ApplyToDelete(o)
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
var _ DeleteOption = &DeleteOptions{}
|
||||
|
||||
// ApplyToDelete implements DeleteOption
|
||||
func (o *DeleteOptions) ApplyToDelete(do *DeleteOptions) {
|
||||
if o.GracePeriodSeconds != nil {
|
||||
do.GracePeriodSeconds = o.GracePeriodSeconds
|
||||
}
|
||||
if o.Preconditions != nil {
|
||||
do.Preconditions = o.Preconditions
|
||||
}
|
||||
if o.PropagationPolicy != nil {
|
||||
do.PropagationPolicy = o.PropagationPolicy
|
||||
}
|
||||
if o.Raw != nil {
|
||||
do.Raw = o.Raw
|
||||
}
|
||||
if o.DryRun != nil {
|
||||
do.DryRun = o.DryRun
|
||||
}
|
||||
}
|
||||
|
||||
// GracePeriodSeconds sets the grace period for the deletion
|
||||
// to the given number of seconds.
|
||||
type GracePeriodSeconds int64
|
||||
|
||||
func (s GracePeriodSeconds) ApplyToDelete(opts *DeleteOptions) {
|
||||
secs := int64(s)
|
||||
opts.GracePeriodSeconds = &secs
|
||||
}
|
||||
|
||||
func (s GracePeriodSeconds) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
|
||||
s.ApplyToDelete(&opts.DeleteOptions)
|
||||
}
|
||||
|
||||
type Preconditions metav1.Preconditions
|
||||
|
||||
func (p Preconditions) ApplyToDelete(opts *DeleteOptions) {
|
||||
preconds := metav1.Preconditions(p)
|
||||
opts.Preconditions = &preconds
|
||||
}
|
||||
|
||||
func (p Preconditions) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
|
||||
p.ApplyToDelete(&opts.DeleteOptions)
|
||||
}
|
||||
|
||||
type PropagationPolicy metav1.DeletionPropagation
|
||||
|
||||
func (p PropagationPolicy) ApplyToDelete(opts *DeleteOptions) {
|
||||
policy := metav1.DeletionPropagation(p)
|
||||
opts.PropagationPolicy = &policy
|
||||
}
|
||||
|
||||
func (p PropagationPolicy) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
|
||||
p.ApplyToDelete(&opts.DeleteOptions)
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
// {{{ List Options
|
||||
|
||||
// ListOptions contains options for limiting or filtering results.
|
||||
// It's generally a subset of metav1.ListOptions, with support for
|
||||
// pre-parsed selectors (since generally, selectors will be executed
|
||||
// against the cache).
|
||||
type ListOptions struct {
|
||||
// LabelSelector filters results by label. Use SetLabelSelector to
|
||||
// set from raw string form.
|
||||
LabelSelector labels.Selector
|
||||
// FieldSelector filters results by a particular field. In order
|
||||
// to use this with cache-based implementations, restrict usage to
|
||||
// a single field-value pair that's been added to the indexers.
|
||||
FieldSelector fields.Selector
|
||||
|
||||
// Namespace represents the namespace to list for, or empty for
|
||||
// non-namespaced objects, or to list across all namespaces.
|
||||
Namespace string
|
||||
|
||||
// Limit specifies the maximum number of results to return from the server. The server may
|
||||
// not support this field on all resource types, but if it does and more results remain it
|
||||
// will set the continue field on the returned list object. This field is not supported if watch
|
||||
// is true in the Raw ListOptions.
|
||||
Limit int64
|
||||
// Continue is a token returned by the server that lets a client retrieve chunks of results
|
||||
// from the server by specifying limit. The server may reject requests for continuation tokens
|
||||
// it does not recognize and will return a 410 error if the token can no longer be used because
|
||||
// it has expired. This field is not supported if watch is true in the Raw ListOptions.
|
||||
Continue string
|
||||
|
||||
// Raw represents raw ListOptions, as passed to the API server. Note
|
||||
// that these may not be respected by all implementations of interface,
|
||||
// and the LabelSelector, FieldSelector, Limit and Continue fields are ignored.
|
||||
Raw *metav1.ListOptions
|
||||
}
|
||||
|
||||
var _ ListOption = &ListOptions{}
|
||||
|
||||
// ApplyToList implements ListOption for ListOptions
|
||||
func (o *ListOptions) ApplyToList(lo *ListOptions) {
|
||||
if o.LabelSelector != nil {
|
||||
lo.LabelSelector = o.LabelSelector
|
||||
}
|
||||
if o.FieldSelector != nil {
|
||||
lo.FieldSelector = o.FieldSelector
|
||||
}
|
||||
if o.Namespace != "" {
|
||||
lo.Namespace = o.Namespace
|
||||
}
|
||||
if o.Raw != nil {
|
||||
lo.Raw = o.Raw
|
||||
}
|
||||
if o.Limit > 0 {
|
||||
lo.Limit = o.Limit
|
||||
}
|
||||
if o.Continue != "" {
|
||||
lo.Continue = o.Continue
|
||||
}
|
||||
}
|
||||
|
||||
// AsListOptions returns these options as a flattened metav1.ListOptions.
|
||||
// This may mutate the Raw field.
|
||||
func (o *ListOptions) AsListOptions() *metav1.ListOptions {
|
||||
if o == nil {
|
||||
return &metav1.ListOptions{}
|
||||
}
|
||||
if o.Raw == nil {
|
||||
o.Raw = &metav1.ListOptions{}
|
||||
}
|
||||
if o.LabelSelector != nil {
|
||||
o.Raw.LabelSelector = o.LabelSelector.String()
|
||||
}
|
||||
if o.FieldSelector != nil {
|
||||
o.Raw.FieldSelector = o.FieldSelector.String()
|
||||
}
|
||||
if !o.Raw.Watch {
|
||||
o.Raw.Limit = o.Limit
|
||||
o.Raw.Continue = o.Continue
|
||||
}
|
||||
return o.Raw
|
||||
}
|
||||
|
||||
// ApplyOptions applies the given list options on these options,
|
||||
// and then returns itself (for convenient chaining).
|
||||
func (o *ListOptions) ApplyOptions(opts []ListOption) *ListOptions {
|
||||
for _, opt := range opts {
|
||||
opt.ApplyToList(o)
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
// MatchingLabels filters the list/delete operation on the given set of labels.
|
||||
type MatchingLabels map[string]string
|
||||
|
||||
func (m MatchingLabels) ApplyToList(opts *ListOptions) {
|
||||
// TODO(directxman12): can we avoid reserializing this over and over?
|
||||
sel := labels.SelectorFromSet(map[string]string(m))
|
||||
opts.LabelSelector = sel
|
||||
}
|
||||
|
||||
func (m MatchingLabels) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
|
||||
m.ApplyToList(&opts.ListOptions)
|
||||
}
|
||||
|
||||
// HasLabels filters the list/delete operation checking if the set of labels exists
|
||||
// without checking their values.
|
||||
type HasLabels []string
|
||||
|
||||
func (m HasLabels) ApplyToList(opts *ListOptions) {
|
||||
sel := labels.NewSelector()
|
||||
for _, label := range m {
|
||||
r, err := labels.NewRequirement(label, selection.Exists, nil)
|
||||
if err == nil {
|
||||
sel = sel.Add(*r)
|
||||
}
|
||||
}
|
||||
opts.LabelSelector = sel
|
||||
}
|
||||
|
||||
func (m HasLabels) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
|
||||
m.ApplyToList(&opts.ListOptions)
|
||||
}
|
||||
|
||||
// MatchingLabelsSelector filters the list/delete operation on the given label
|
||||
// selector (or index in the case of cached lists). A struct is used because
|
||||
// labels.Selector is an interface, which cannot be aliased.
|
||||
type MatchingLabelsSelector struct {
|
||||
labels.Selector
|
||||
}
|
||||
|
||||
func (m MatchingLabelsSelector) ApplyToList(opts *ListOptions) {
|
||||
opts.LabelSelector = m
|
||||
}
|
||||
|
||||
func (m MatchingLabelsSelector) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
|
||||
m.ApplyToList(&opts.ListOptions)
|
||||
}
|
||||
|
||||
// MatchingField filters the list operation on the given field selector
|
||||
// (or index in the case of cached lists).
|
||||
//
|
||||
// Deprecated: Use MatchingFields
|
||||
func MatchingField(name, val string) MatchingFields {
|
||||
return MatchingFields{name: val}
|
||||
}
|
||||
|
||||
// MatchingFields filters the list/delete operation on the given field Set
|
||||
// (or index in the case of cached lists).
|
||||
type MatchingFields fields.Set
|
||||
|
||||
func (m MatchingFields) ApplyToList(opts *ListOptions) {
|
||||
// TODO(directxman12): can we avoid re-serializing this?
|
||||
sel := fields.Set(m).AsSelector()
|
||||
opts.FieldSelector = sel
|
||||
}
|
||||
|
||||
func (m MatchingFields) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
|
||||
m.ApplyToList(&opts.ListOptions)
|
||||
}
|
||||
|
||||
// MatchingFieldsSelector filters the list/delete operation on the given field
|
||||
// selector (or index in the case of cached lists). A struct is used because
|
||||
// fields.Selector is an interface, which cannot be aliased.
|
||||
type MatchingFieldsSelector struct {
|
||||
fields.Selector
|
||||
}
|
||||
|
||||
func (m MatchingFieldsSelector) ApplyToList(opts *ListOptions) {
|
||||
opts.FieldSelector = m
|
||||
}
|
||||
|
||||
func (m MatchingFieldsSelector) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
|
||||
m.ApplyToList(&opts.ListOptions)
|
||||
}
|
||||
|
||||
// InNamespace restricts the list/delete operation to the given namespace.
|
||||
type InNamespace string
|
||||
|
||||
func (n InNamespace) ApplyToList(opts *ListOptions) {
|
||||
opts.Namespace = string(n)
|
||||
}
|
||||
|
||||
func (n InNamespace) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
|
||||
n.ApplyToList(&opts.ListOptions)
|
||||
}
|
||||
|
||||
// Limit specifies the maximum number of results to return from the server.
|
||||
// Limit does not implement DeleteAllOfOption interface because the server
|
||||
// does not support setting it for deletecollection operations.
|
||||
type Limit int64
|
||||
|
||||
func (l Limit) ApplyToList(opts *ListOptions) {
|
||||
opts.Limit = int64(l)
|
||||
}
|
||||
|
||||
// Continue sets a continuation token to retrieve chunks of results when using limit.
|
||||
// Continue does not implement DeleteAllOfOption interface because the server
|
||||
// does not support setting it for deletecollection operations.
|
||||
type Continue string
|
||||
|
||||
func (c Continue) ApplyToList(opts *ListOptions) {
|
||||
opts.Continue = string(c)
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
// {{{ Update Options
|
||||
|
||||
// UpdateOptions contains options for create requests. It's generally a subset
|
||||
// of metav1.UpdateOptions.
|
||||
type UpdateOptions struct {
|
||||
// When present, indicates that modifications should not be
|
||||
// persisted. An invalid or unrecognized dryRun directive will
|
||||
// result in an error response and no further processing of the
|
||||
// request. Valid values are:
|
||||
// - All: all dry run stages will be processed
|
||||
DryRun []string
|
||||
|
||||
// FieldManager is the name of the user or component submitting
|
||||
// this request. It must be set with server-side apply.
|
||||
FieldManager string
|
||||
|
||||
// Raw represents raw UpdateOptions, as passed to the API server.
|
||||
Raw *metav1.UpdateOptions
|
||||
}
|
||||
|
||||
// AsUpdateOptions returns these options as a metav1.UpdateOptions.
|
||||
// This may mutate the Raw field.
|
||||
func (o *UpdateOptions) AsUpdateOptions() *metav1.UpdateOptions {
|
||||
if o == nil {
|
||||
return &metav1.UpdateOptions{}
|
||||
}
|
||||
if o.Raw == nil {
|
||||
o.Raw = &metav1.UpdateOptions{}
|
||||
}
|
||||
|
||||
o.Raw.DryRun = o.DryRun
|
||||
o.Raw.FieldManager = o.FieldManager
|
||||
return o.Raw
|
||||
}
|
||||
|
||||
// ApplyOptions applies the given update options on these options,
|
||||
// and then returns itself (for convenient chaining).
|
||||
func (o *UpdateOptions) ApplyOptions(opts []UpdateOption) *UpdateOptions {
|
||||
for _, opt := range opts {
|
||||
opt.ApplyToUpdate(o)
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
var _ UpdateOption = &UpdateOptions{}
|
||||
|
||||
// ApplyToUpdate implements UpdateOption
|
||||
func (o *UpdateOptions) ApplyToUpdate(uo *UpdateOptions) {
|
||||
if o.DryRun != nil {
|
||||
uo.DryRun = o.DryRun
|
||||
}
|
||||
if o.FieldManager != "" {
|
||||
uo.FieldManager = o.FieldManager
|
||||
}
|
||||
if o.Raw != nil {
|
||||
uo.Raw = o.Raw
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateDryRunAll sets the "dry run" option to "all".
|
||||
//
|
||||
// Deprecated: Use DryRunAll
|
||||
var UpdateDryRunAll = DryRunAll
|
||||
|
||||
// }}}
|
||||
|
||||
// {{{ Patch Options
|
||||
|
||||
// PatchOptions contains options for patch requests.
|
||||
type PatchOptions struct {
|
||||
// When present, indicates that modifications should not be
|
||||
// persisted. An invalid or unrecognized dryRun directive will
|
||||
// result in an error response and no further processing of the
|
||||
// request. Valid values are:
|
||||
// - All: all dry run stages will be processed
|
||||
DryRun []string
|
||||
|
||||
// Force is going to "force" Apply requests. It means user will
|
||||
// re-acquire conflicting fields owned by other people. Force
|
||||
// flag must be unset for non-apply patch requests.
|
||||
// +optional
|
||||
Force *bool
|
||||
|
||||
// FieldManager is the name of the user or component submitting
|
||||
// this request. It must be set with server-side apply.
|
||||
FieldManager string
|
||||
|
||||
// Raw represents raw PatchOptions, as passed to the API server.
|
||||
Raw *metav1.PatchOptions
|
||||
}
|
||||
|
||||
// ApplyOptions applies the given patch options on these options,
|
||||
// and then returns itself (for convenient chaining).
|
||||
func (o *PatchOptions) ApplyOptions(opts []PatchOption) *PatchOptions {
|
||||
for _, opt := range opts {
|
||||
opt.ApplyToPatch(o)
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
// AsPatchOptions returns these options as a metav1.PatchOptions.
|
||||
// This may mutate the Raw field.
|
||||
func (o *PatchOptions) AsPatchOptions() *metav1.PatchOptions {
|
||||
if o == nil {
|
||||
return &metav1.PatchOptions{}
|
||||
}
|
||||
if o.Raw == nil {
|
||||
o.Raw = &metav1.PatchOptions{}
|
||||
}
|
||||
|
||||
o.Raw.DryRun = o.DryRun
|
||||
o.Raw.Force = o.Force
|
||||
o.Raw.FieldManager = o.FieldManager
|
||||
return o.Raw
|
||||
}
|
||||
|
||||
var _ PatchOption = &PatchOptions{}
|
||||
|
||||
// ApplyToPatch implements PatchOptions
|
||||
func (o *PatchOptions) ApplyToPatch(po *PatchOptions) {
|
||||
if o.DryRun != nil {
|
||||
po.DryRun = o.DryRun
|
||||
}
|
||||
if o.Force != nil {
|
||||
po.Force = o.Force
|
||||
}
|
||||
if o.FieldManager != "" {
|
||||
po.FieldManager = o.FieldManager
|
||||
}
|
||||
if o.Raw != nil {
|
||||
po.Raw = o.Raw
|
||||
}
|
||||
}
|
||||
|
||||
// ForceOwnership indicates that in case of conflicts with server-side apply,
|
||||
// the client should acquire ownership of the conflicting field. Most
|
||||
// controllers should use this.
|
||||
var ForceOwnership = forceOwnership{}
|
||||
|
||||
type forceOwnership struct{}
|
||||
|
||||
func (forceOwnership) ApplyToPatch(opts *PatchOptions) {
|
||||
definitelyTrue := true
|
||||
opts.Force = &definitelyTrue
|
||||
}
|
||||
|
||||
// PatchDryRunAll sets the "dry run" option to "all".
|
||||
//
|
||||
// Deprecated: Use DryRunAll
|
||||
var PatchDryRunAll = DryRunAll
|
||||
|
||||
// }}}
|
||||
|
||||
// {{{ DeleteAllOf Options
|
||||
|
||||
// these are all just delete options and list options
|
||||
|
||||
// DeleteAllOfOptions contains options for deletecollection (deleteallof) requests.
|
||||
// It's just list and delete options smooshed together.
|
||||
type DeleteAllOfOptions struct {
|
||||
ListOptions
|
||||
DeleteOptions
|
||||
}
|
||||
|
||||
// ApplyOptions applies the given deleteallof options on these options,
|
||||
// and then returns itself (for convenient chaining).
|
||||
func (o *DeleteAllOfOptions) ApplyOptions(opts []DeleteAllOfOption) *DeleteAllOfOptions {
|
||||
for _, opt := range opts {
|
||||
opt.ApplyToDeleteAllOf(o)
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
var _ DeleteAllOfOption = &DeleteAllOfOptions{}
|
||||
|
||||
// ApplyToDeleteAllOf implements DeleteAllOfOption
|
||||
func (o *DeleteAllOfOptions) ApplyToDeleteAllOf(do *DeleteAllOfOptions) {
|
||||
o.ApplyToList(&do.ListOptions)
|
||||
o.ApplyToDelete(&do.DeleteOptions)
|
||||
}
|
||||
|
||||
// }}}
|
||||
123
vendor/sigs.k8s.io/controller-runtime/pkg/client/patch.go
generated
vendored
Normal file
123
vendor/sigs.k8s.io/controller-runtime/pkg/client/patch.go
generated
vendored
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/json"
|
||||
)
|
||||
|
||||
var (
|
||||
// Apply uses server-side apply to patch the given object.
|
||||
Apply = applyPatch{}
|
||||
|
||||
// Merge uses the raw object as a merge patch, without modifications.
|
||||
// Use MergeFrom if you wish to compute a diff instead.
|
||||
Merge = mergePatch{}
|
||||
)
|
||||
|
||||
type patch struct {
|
||||
patchType types.PatchType
|
||||
data []byte
|
||||
}
|
||||
|
||||
// Type implements Patch.
|
||||
func (s *patch) Type() types.PatchType {
|
||||
return s.patchType
|
||||
}
|
||||
|
||||
// Data implements Patch.
|
||||
func (s *patch) Data(obj runtime.Object) ([]byte, error) {
|
||||
return s.data, nil
|
||||
}
|
||||
|
||||
// RawPatch constructs a new Patch with the given PatchType and data.
|
||||
func RawPatch(patchType types.PatchType, data []byte) Patch {
|
||||
return &patch{patchType, data}
|
||||
}
|
||||
|
||||
// ConstantPatch constructs a new Patch with the given PatchType and data.
|
||||
//
|
||||
// Deprecated: use RawPatch instead
|
||||
func ConstantPatch(patchType types.PatchType, data []byte) Patch {
|
||||
return RawPatch(patchType, data)
|
||||
}
|
||||
|
||||
type mergeFromPatch struct {
|
||||
from runtime.Object
|
||||
}
|
||||
|
||||
// Type implements patch.
|
||||
func (s *mergeFromPatch) Type() types.PatchType {
|
||||
return types.MergePatchType
|
||||
}
|
||||
|
||||
// Data implements Patch.
|
||||
func (s *mergeFromPatch) Data(obj runtime.Object) ([]byte, error) {
|
||||
originalJSON, err := json.Marshal(s.from)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
modifiedJSON, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return jsonpatch.CreateMergePatch(originalJSON, modifiedJSON)
|
||||
}
|
||||
|
||||
// MergeFrom creates a Patch that patches using the merge-patch strategy with the given object as base.
|
||||
func MergeFrom(obj runtime.Object) Patch {
|
||||
return &mergeFromPatch{obj}
|
||||
}
|
||||
|
||||
// mergePatch uses a raw merge strategy to patch the object.
|
||||
type mergePatch struct{}
|
||||
|
||||
// Type implements Patch.
|
||||
func (p mergePatch) Type() types.PatchType {
|
||||
return types.MergePatchType
|
||||
}
|
||||
|
||||
// Data implements Patch.
|
||||
func (p mergePatch) Data(obj runtime.Object) ([]byte, error) {
|
||||
// NB(directxman12): we might technically want to be using an actual encoder
|
||||
// here (in case some more performant encoder is introduced) but this is
|
||||
// correct and sufficient for our uses (it's what the JSON serializer in
|
||||
// client-go does, more-or-less).
|
||||
return json.Marshal(obj)
|
||||
}
|
||||
|
||||
// applyPatch uses server-side apply to patch the object.
|
||||
type applyPatch struct{}
|
||||
|
||||
// Type implements Patch.
|
||||
func (p applyPatch) Type() types.PatchType {
|
||||
return types.ApplyPatchType
|
||||
}
|
||||
|
||||
// Data implements Patch.
|
||||
func (p applyPatch) Data(obj runtime.Object) ([]byte, error) {
|
||||
// NB(directxman12): we might technically want to be using an actual encoder
|
||||
// here (in case some more performant encoder is introduced) but this is
|
||||
// correct and sufficient for our uses (it's what the JSON serializer in
|
||||
// client-go does, more-or-less).
|
||||
return json.Marshal(obj)
|
||||
}
|
||||
61
vendor/sigs.k8s.io/controller-runtime/pkg/client/split.go
generated
vendored
Normal file
61
vendor/sigs.k8s.io/controller-runtime/pkg/client/split.go
generated
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DelegatingClient forms a Client by composing separate reader, writer and
|
||||
// statusclient interfaces. This way, you can have an Client that reads from a
|
||||
// cache and writes to the API server.
|
||||
type DelegatingClient struct {
|
||||
Reader
|
||||
Writer
|
||||
StatusClient
|
||||
}
|
||||
|
||||
// DelegatingReader forms a Reader that will cause Get and List requests for
|
||||
// unstructured types to use the ClientReader while requests for any other type
|
||||
// of object with use the CacheReader. This avoids accidentally caching the
|
||||
// entire cluster in the common case of loading arbitrary unstructured objects
|
||||
// (e.g. from OwnerReferences).
|
||||
type DelegatingReader struct {
|
||||
CacheReader Reader
|
||||
ClientReader Reader
|
||||
}
|
||||
|
||||
// Get retrieves an obj for a given object key from the Kubernetes Cluster.
|
||||
func (d *DelegatingReader) Get(ctx context.Context, key ObjectKey, obj runtime.Object) error {
|
||||
_, isUnstructured := obj.(*unstructured.Unstructured)
|
||||
if isUnstructured {
|
||||
return d.ClientReader.Get(ctx, key, obj)
|
||||
}
|
||||
return d.CacheReader.Get(ctx, key, obj)
|
||||
}
|
||||
|
||||
// List retrieves list of objects for a given namespace and list options.
|
||||
func (d *DelegatingReader) List(ctx context.Context, list runtime.Object, opts ...ListOption) error {
|
||||
_, isUnstructured := list.(*unstructured.UnstructuredList)
|
||||
if isUnstructured {
|
||||
return d.ClientReader.List(ctx, list, opts...)
|
||||
}
|
||||
return d.CacheReader.List(ctx, list, opts...)
|
||||
}
|
||||
201
vendor/sigs.k8s.io/controller-runtime/pkg/client/typed_client.go
generated
vendored
Normal file
201
vendor/sigs.k8s.io/controller-runtime/pkg/client/typed_client.go
generated
vendored
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// client is a client.Client that reads and writes directly from/to an API server. It lazily initializes
|
||||
// new clients at the time they are used, and caches the client.
|
||||
type typedClient struct {
|
||||
cache *clientCache
|
||||
paramCodec runtime.ParameterCodec
|
||||
}
|
||||
|
||||
// Create implements client.Client
|
||||
func (c *typedClient) Create(ctx context.Context, obj runtime.Object, opts ...CreateOption) error {
|
||||
o, err := c.cache.getObjMeta(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
createOpts := &CreateOptions{}
|
||||
createOpts.ApplyOptions(opts)
|
||||
return o.Post().
|
||||
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
|
||||
Resource(o.resource()).
|
||||
Body(obj).
|
||||
VersionedParams(createOpts.AsCreateOptions(), c.paramCodec).
|
||||
Do(ctx).
|
||||
Into(obj)
|
||||
}
|
||||
|
||||
// Update implements client.Client
|
||||
func (c *typedClient) Update(ctx context.Context, obj runtime.Object, opts ...UpdateOption) error {
|
||||
o, err := c.cache.getObjMeta(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
updateOpts := &UpdateOptions{}
|
||||
updateOpts.ApplyOptions(opts)
|
||||
return o.Put().
|
||||
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
|
||||
Resource(o.resource()).
|
||||
Name(o.GetName()).
|
||||
Body(obj).
|
||||
VersionedParams(updateOpts.AsUpdateOptions(), c.paramCodec).
|
||||
Do(ctx).
|
||||
Into(obj)
|
||||
}
|
||||
|
||||
// Delete implements client.Client
|
||||
func (c *typedClient) Delete(ctx context.Context, obj runtime.Object, opts ...DeleteOption) error {
|
||||
o, err := c.cache.getObjMeta(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deleteOpts := DeleteOptions{}
|
||||
deleteOpts.ApplyOptions(opts)
|
||||
|
||||
return o.Delete().
|
||||
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
|
||||
Resource(o.resource()).
|
||||
Name(o.GetName()).
|
||||
Body(deleteOpts.AsDeleteOptions()).
|
||||
Do(ctx).
|
||||
Error()
|
||||
}
|
||||
|
||||
// DeleteAllOf implements client.Client
|
||||
func (c *typedClient) DeleteAllOf(ctx context.Context, obj runtime.Object, opts ...DeleteAllOfOption) error {
|
||||
o, err := c.cache.getObjMeta(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deleteAllOfOpts := DeleteAllOfOptions{}
|
||||
deleteAllOfOpts.ApplyOptions(opts)
|
||||
|
||||
return o.Delete().
|
||||
NamespaceIfScoped(deleteAllOfOpts.ListOptions.Namespace, o.isNamespaced()).
|
||||
Resource(o.resource()).
|
||||
VersionedParams(deleteAllOfOpts.AsListOptions(), c.paramCodec).
|
||||
Body(deleteAllOfOpts.AsDeleteOptions()).
|
||||
Do(ctx).
|
||||
Error()
|
||||
}
|
||||
|
||||
// Patch implements client.Client
|
||||
func (c *typedClient) Patch(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOption) error {
|
||||
o, err := c.cache.getObjMeta(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data, err := patch.Data(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
patchOpts := &PatchOptions{}
|
||||
return o.Patch(patch.Type()).
|
||||
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
|
||||
Resource(o.resource()).
|
||||
Name(o.GetName()).
|
||||
VersionedParams(patchOpts.ApplyOptions(opts).AsPatchOptions(), c.paramCodec).
|
||||
Body(data).
|
||||
Do(ctx).
|
||||
Into(obj)
|
||||
}
|
||||
|
||||
// Get implements client.Client
|
||||
func (c *typedClient) Get(ctx context.Context, key ObjectKey, obj runtime.Object) error {
|
||||
r, err := c.cache.getResource(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return r.Get().
|
||||
NamespaceIfScoped(key.Namespace, r.isNamespaced()).
|
||||
Resource(r.resource()).
|
||||
Name(key.Name).Do(ctx).Into(obj)
|
||||
}
|
||||
|
||||
// List implements client.Client
|
||||
func (c *typedClient) List(ctx context.Context, obj runtime.Object, opts ...ListOption) error {
|
||||
r, err := c.cache.getResource(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
listOpts := ListOptions{}
|
||||
listOpts.ApplyOptions(opts)
|
||||
return r.Get().
|
||||
NamespaceIfScoped(listOpts.Namespace, r.isNamespaced()).
|
||||
Resource(r.resource()).
|
||||
VersionedParams(listOpts.AsListOptions(), c.paramCodec).
|
||||
Do(ctx).
|
||||
Into(obj)
|
||||
}
|
||||
|
||||
// UpdateStatus used by StatusWriter to write status.
|
||||
func (c *typedClient) UpdateStatus(ctx context.Context, obj runtime.Object, opts ...UpdateOption) error {
|
||||
o, err := c.cache.getObjMeta(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO(droot): examine the returned error and check if it error needs to be
|
||||
// wrapped to improve the UX ?
|
||||
// It will be nice to receive an error saying the object doesn't implement
|
||||
// status subresource and check CRD definition
|
||||
return o.Put().
|
||||
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
|
||||
Resource(o.resource()).
|
||||
Name(o.GetName()).
|
||||
SubResource("status").
|
||||
Body(obj).
|
||||
VersionedParams((&UpdateOptions{}).ApplyOptions(opts).AsUpdateOptions(), c.paramCodec).
|
||||
Do(ctx).
|
||||
Into(obj)
|
||||
}
|
||||
|
||||
// PatchStatus used by StatusWriter to write status.
|
||||
func (c *typedClient) PatchStatus(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOption) error {
|
||||
o, err := c.cache.getObjMeta(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data, err := patch.Data(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
patchOpts := &PatchOptions{}
|
||||
return o.Patch(patch.Type()).
|
||||
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
|
||||
Resource(o.resource()).
|
||||
Name(o.GetName()).
|
||||
SubResource("status").
|
||||
Body(data).
|
||||
VersionedParams(patchOpts.ApplyOptions(opts).AsPatchOptions(), c.paramCodec).
|
||||
Do(ctx).
|
||||
Into(obj)
|
||||
}
|
||||
273
vendor/sigs.k8s.io/controller-runtime/pkg/client/unstructured_client.go
generated
vendored
Normal file
273
vendor/sigs.k8s.io/controller-runtime/pkg/client/unstructured_client.go
generated
vendored
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// client is a client.Client that reads and writes directly from/to an API server. It lazily initializes
|
||||
// new clients at the time they are used, and caches the client.
|
||||
type unstructuredClient struct {
|
||||
cache *clientCache
|
||||
paramCodec runtime.ParameterCodec
|
||||
}
|
||||
|
||||
// Create implements client.Client
|
||||
func (uc *unstructuredClient) Create(ctx context.Context, obj runtime.Object, opts ...CreateOption) error {
|
||||
u, ok := obj.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("unstructured client did not understand object: %T", obj)
|
||||
}
|
||||
|
||||
gvk := u.GroupVersionKind()
|
||||
|
||||
o, err := uc.cache.getObjMeta(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
createOpts := &CreateOptions{}
|
||||
createOpts.ApplyOptions(opts)
|
||||
result := o.Post().
|
||||
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
|
||||
Resource(o.resource()).
|
||||
Body(obj).
|
||||
VersionedParams(createOpts.AsCreateOptions(), uc.paramCodec).
|
||||
Do(ctx).
|
||||
Into(obj)
|
||||
|
||||
u.SetGroupVersionKind(gvk)
|
||||
return result
|
||||
}
|
||||
|
||||
// Update implements client.Client
|
||||
func (uc *unstructuredClient) Update(ctx context.Context, obj runtime.Object, opts ...UpdateOption) error {
|
||||
u, ok := obj.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("unstructured client did not understand object: %T", obj)
|
||||
}
|
||||
|
||||
gvk := u.GroupVersionKind()
|
||||
|
||||
o, err := uc.cache.getObjMeta(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
updateOpts := UpdateOptions{}
|
||||
updateOpts.ApplyOptions(opts)
|
||||
result := o.Put().
|
||||
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
|
||||
Resource(o.resource()).
|
||||
Name(o.GetName()).
|
||||
Body(obj).
|
||||
VersionedParams(updateOpts.AsUpdateOptions(), uc.paramCodec).
|
||||
Do(ctx).
|
||||
Into(obj)
|
||||
|
||||
u.SetGroupVersionKind(gvk)
|
||||
return result
|
||||
}
|
||||
|
||||
// Delete implements client.Client
|
||||
func (uc *unstructuredClient) Delete(ctx context.Context, obj runtime.Object, opts ...DeleteOption) error {
|
||||
_, ok := obj.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("unstructured client did not understand object: %T", obj)
|
||||
}
|
||||
|
||||
o, err := uc.cache.getObjMeta(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deleteOpts := DeleteOptions{}
|
||||
deleteOpts.ApplyOptions(opts)
|
||||
return o.Delete().
|
||||
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
|
||||
Resource(o.resource()).
|
||||
Name(o.GetName()).
|
||||
Body(deleteOpts.AsDeleteOptions()).
|
||||
Do(ctx).
|
||||
Error()
|
||||
}
|
||||
|
||||
// DeleteAllOf implements client.Client
|
||||
func (uc *unstructuredClient) DeleteAllOf(ctx context.Context, obj runtime.Object, opts ...DeleteAllOfOption) error {
|
||||
_, ok := obj.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("unstructured client did not understand object: %T", obj)
|
||||
}
|
||||
|
||||
o, err := uc.cache.getObjMeta(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deleteAllOfOpts := DeleteAllOfOptions{}
|
||||
deleteAllOfOpts.ApplyOptions(opts)
|
||||
return o.Delete().
|
||||
NamespaceIfScoped(deleteAllOfOpts.ListOptions.Namespace, o.isNamespaced()).
|
||||
Resource(o.resource()).
|
||||
VersionedParams(deleteAllOfOpts.AsListOptions(), uc.paramCodec).
|
||||
Body(deleteAllOfOpts.AsDeleteOptions()).
|
||||
Do(ctx).
|
||||
Error()
|
||||
}
|
||||
|
||||
// Patch implements client.Client
|
||||
func (uc *unstructuredClient) Patch(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOption) error {
|
||||
_, ok := obj.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("unstructured client did not understand object: %T", obj)
|
||||
}
|
||||
|
||||
o, err := uc.cache.getObjMeta(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data, err := patch.Data(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
patchOpts := &PatchOptions{}
|
||||
return o.Patch(patch.Type()).
|
||||
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
|
||||
Resource(o.resource()).
|
||||
Name(o.GetName()).
|
||||
VersionedParams(patchOpts.ApplyOptions(opts).AsPatchOptions(), uc.paramCodec).
|
||||
Body(data).
|
||||
Do(ctx).
|
||||
Into(obj)
|
||||
}
|
||||
|
||||
// Get implements client.Client
|
||||
func (uc *unstructuredClient) Get(ctx context.Context, key ObjectKey, obj runtime.Object) error {
|
||||
u, ok := obj.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("unstructured client did not understand object: %T", obj)
|
||||
}
|
||||
|
||||
gvk := u.GroupVersionKind()
|
||||
|
||||
r, err := uc.cache.getResource(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
result := r.Get().
|
||||
NamespaceIfScoped(key.Namespace, r.isNamespaced()).
|
||||
Resource(r.resource()).
|
||||
Name(key.Name).
|
||||
Do(ctx).
|
||||
Into(obj)
|
||||
|
||||
u.SetGroupVersionKind(gvk)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// List implements client.Client
|
||||
func (uc *unstructuredClient) List(ctx context.Context, obj runtime.Object, opts ...ListOption) error {
|
||||
u, ok := obj.(*unstructured.UnstructuredList)
|
||||
if !ok {
|
||||
return fmt.Errorf("unstructured client did not understand object: %T", obj)
|
||||
}
|
||||
|
||||
gvk := u.GroupVersionKind()
|
||||
if strings.HasSuffix(gvk.Kind, "List") {
|
||||
gvk.Kind = gvk.Kind[:len(gvk.Kind)-4]
|
||||
}
|
||||
|
||||
listOpts := ListOptions{}
|
||||
listOpts.ApplyOptions(opts)
|
||||
|
||||
r, err := uc.cache.getResource(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.Get().
|
||||
NamespaceIfScoped(listOpts.Namespace, r.isNamespaced()).
|
||||
Resource(r.resource()).
|
||||
VersionedParams(listOpts.AsListOptions(), uc.paramCodec).
|
||||
Do(ctx).
|
||||
Into(obj)
|
||||
}
|
||||
|
||||
func (uc *unstructuredClient) UpdateStatus(ctx context.Context, obj runtime.Object, opts ...UpdateOption) error {
|
||||
_, ok := obj.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("unstructured client did not understand object: %T", obj)
|
||||
}
|
||||
|
||||
o, err := uc.cache.getObjMeta(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return o.Put().
|
||||
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
|
||||
Resource(o.resource()).
|
||||
Name(o.GetName()).
|
||||
SubResource("status").
|
||||
Body(obj).
|
||||
VersionedParams((&UpdateOptions{}).ApplyOptions(opts).AsUpdateOptions(), uc.paramCodec).
|
||||
Do(ctx).
|
||||
Into(obj)
|
||||
}
|
||||
|
||||
func (uc *unstructuredClient) PatchStatus(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOption) error {
|
||||
u, ok := obj.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("unstructured client did not understand object: %T", obj)
|
||||
}
|
||||
|
||||
gvk := u.GroupVersionKind()
|
||||
|
||||
o, err := uc.cache.getObjMeta(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data, err := patch.Data(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
patchOpts := &PatchOptions{}
|
||||
result := o.Patch(patch.Type()).
|
||||
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
|
||||
Resource(o.resource()).
|
||||
Name(o.GetName()).
|
||||
SubResource("status").
|
||||
Body(data).
|
||||
VersionedParams(patchOpts.ApplyOptions(opts).AsPatchOptions(), uc.paramCodec).
|
||||
Do(ctx).
|
||||
Into(u)
|
||||
|
||||
u.SetGroupVersionKind(gvk)
|
||||
return result
|
||||
}
|
||||
198
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/crd.go
generated
vendored
198
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/crd.go
generated
vendored
|
|
@ -19,29 +19,33 @@ package envtest
|
|||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
k8syaml "k8s.io/apimachinery/pkg/util/yaml"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
// CRDInstallOptions are the options for installing CRDs
|
||||
type CRDInstallOptions struct {
|
||||
// Paths is a list of paths to the directories containing CRDs
|
||||
// Paths is a list of paths to the directories or files containing CRDs
|
||||
Paths []string
|
||||
|
||||
// CRDs is a list of CRDs to install
|
||||
CRDs []*apiextensionsv1beta1.CustomResourceDefinition
|
||||
CRDs []runtime.Object
|
||||
|
||||
// ErrorIfPathMissing will cause an error if a Path does not exist
|
||||
ErrorIfPathMissing bool
|
||||
|
|
@ -51,13 +55,18 @@ type CRDInstallOptions struct {
|
|||
|
||||
// PollInterval is the interval to check
|
||||
PollInterval time.Duration
|
||||
|
||||
// CleanUpAfterUse will cause the CRDs listed for installation to be
|
||||
// uninstalled when terminating the test environment.
|
||||
// Defaults to false.
|
||||
CleanUpAfterUse bool
|
||||
}
|
||||
|
||||
const defaultPollInterval = 100 * time.Millisecond
|
||||
const defaultMaxWait = 10 * time.Second
|
||||
|
||||
// InstallCRDs installs a collection of CRDs into a cluster by reading the crd yaml files from a directory
|
||||
func InstallCRDs(config *rest.Config, options CRDInstallOptions) ([]*apiextensionsv1beta1.CustomResourceDefinition, error) {
|
||||
func InstallCRDs(config *rest.Config, options CRDInstallOptions) ([]runtime.Object, error) {
|
||||
defaultCRDOptions(&options)
|
||||
|
||||
// Read the CRD yamls into options.CRDs
|
||||
|
|
@ -81,16 +90,12 @@ func InstallCRDs(config *rest.Config, options CRDInstallOptions) ([]*apiextensio
|
|||
// readCRDFiles reads the directories of CRDs in options.Paths and adds the CRD structs to options.CRDs
|
||||
func readCRDFiles(options *CRDInstallOptions) error {
|
||||
if len(options.Paths) > 0 {
|
||||
for _, path := range options.Paths {
|
||||
if _, err := os.Stat(path); !options.ErrorIfPathMissing && os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
new, err := readCRDs(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options.CRDs = append(options.CRDs, new...)
|
||||
crdList, err := renderCRDs(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
options.CRDs = append(options.CRDs, crdList...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -106,19 +111,49 @@ func defaultCRDOptions(o *CRDInstallOptions) {
|
|||
}
|
||||
|
||||
// WaitForCRDs waits for the CRDs to appear in discovery
|
||||
func WaitForCRDs(config *rest.Config, crds []*apiextensionsv1beta1.CustomResourceDefinition, options CRDInstallOptions) error {
|
||||
func WaitForCRDs(config *rest.Config, crds []runtime.Object, options CRDInstallOptions) error {
|
||||
// Add each CRD to a map of GroupVersion to Resource
|
||||
waitingFor := map[schema.GroupVersion]*sets.String{}
|
||||
for _, crd := range crds {
|
||||
for _, crd := range runtimeCRDListToUnstructured(crds) {
|
||||
gvs := []schema.GroupVersion{}
|
||||
if crd.Spec.Version != "" {
|
||||
gvs = append(gvs, schema.GroupVersion{Group: crd.Spec.Group, Version: crd.Spec.Version})
|
||||
crdGroup, _, err := unstructured.NestedString(crd.Object, "spec", "group")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, ver := range crd.Spec.Versions {
|
||||
if ver.Served {
|
||||
gvs = append(gvs, schema.GroupVersion{Group: crd.Spec.Group, Version: ver.Name})
|
||||
crdPlural, _, err := unstructured.NestedString(crd.Object, "spec", "names", "plural")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
crdVersion, _, err := unstructured.NestedString(crd.Object, "spec", "version")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if crdVersion != "" {
|
||||
gvs = append(gvs, schema.GroupVersion{Group: crdGroup, Version: crdVersion})
|
||||
}
|
||||
|
||||
versions, _, err := unstructured.NestedSlice(crd.Object, "spec", "versions")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, version := range versions {
|
||||
versionMap, ok := version.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
served, _, err := unstructured.NestedBool(versionMap, "served")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if served {
|
||||
versionName, _, err := unstructured.NestedString(versionMap, "name")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gvs = append(gvs, schema.GroupVersion{Group: crdGroup, Version: versionName})
|
||||
}
|
||||
}
|
||||
|
||||
for _, gv := range gvs {
|
||||
log.V(1).Info("adding API in waitlist", "GV", gv)
|
||||
if _, found := waitingFor[gv]; !found {
|
||||
|
|
@ -126,7 +161,7 @@ func WaitForCRDs(config *rest.Config, crds []*apiextensionsv1beta1.CustomResourc
|
|||
waitingFor[gv] = &sets.String{}
|
||||
}
|
||||
// Add the Resource
|
||||
waitingFor[gv].Insert(crd.Spec.Names.Plural)
|
||||
waitingFor[gv].Insert(crdPlural)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -180,37 +215,115 @@ func (p *poller) poll() (done bool, err error) {
|
|||
return allFound, nil
|
||||
}
|
||||
|
||||
// UninstallCRDs uninstalls a collection of CRDs by reading the crd yaml files from a directory
|
||||
func UninstallCRDs(config *rest.Config, options CRDInstallOptions) error {
|
||||
|
||||
// Read the CRD yamls into options.CRDs
|
||||
if err := readCRDFiles(&options); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete the CRDs from the apiserver
|
||||
cs, err := client.New(config, client.Options{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Uninstall each CRD
|
||||
for _, crd := range runtimeCRDListToUnstructured(options.CRDs) {
|
||||
log.V(1).Info("uninstalling CRD", "crd", crd.GetName())
|
||||
if err := cs.Delete(context.TODO(), crd); err != nil {
|
||||
// If CRD is not found, we can consider success
|
||||
if !apierrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateCRDs creates the CRDs
|
||||
func CreateCRDs(config *rest.Config, crds []*apiextensionsv1beta1.CustomResourceDefinition) error {
|
||||
cs, err := clientset.NewForConfig(config)
|
||||
func CreateCRDs(config *rest.Config, crds []runtime.Object) error {
|
||||
cs, err := client.New(config, client.Options{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create each CRD
|
||||
for _, crd := range crds {
|
||||
log.V(1).Info("installing CRD", "crd", crd.Name)
|
||||
if _, err := cs.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd); err != nil {
|
||||
for _, crd := range runtimeCRDListToUnstructured(crds) {
|
||||
log.V(1).Info("installing CRD", "crd", crd.GetName())
|
||||
existingCrd := crd.DeepCopy()
|
||||
err := cs.Get(context.TODO(), client.ObjectKey{Name: crd.GetName()}, existingCrd)
|
||||
switch {
|
||||
case apierrors.IsNotFound(err):
|
||||
if err := cs.Create(context.TODO(), crd); err != nil {
|
||||
return err
|
||||
}
|
||||
case err != nil:
|
||||
return err
|
||||
default:
|
||||
log.V(1).Info("CRD already exists, updating", "crd", crd.GetName())
|
||||
crd.SetResourceVersion(existingCrd.GetResourceVersion())
|
||||
if err := cs.Update(context.TODO(), crd); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// readCRDs reads the CRDs from files and Unmarshals them into structs
|
||||
func readCRDs(path string) ([]*apiextensionsv1beta1.CustomResourceDefinition, error) {
|
||||
// Get the CRD files
|
||||
var files []os.FileInfo
|
||||
var err error
|
||||
log.V(1).Info("reading CRDs from path", "path", path)
|
||||
if files, err = ioutil.ReadDir(path); err != nil {
|
||||
return nil, err
|
||||
// renderCRDs iterate through options.Paths and extract all CRD files.
|
||||
func renderCRDs(options *CRDInstallOptions) ([]runtime.Object, error) {
|
||||
var (
|
||||
err error
|
||||
info os.FileInfo
|
||||
crds []*unstructured.Unstructured
|
||||
files []os.FileInfo
|
||||
)
|
||||
|
||||
for _, path := range options.Paths {
|
||||
var filePath = path
|
||||
|
||||
// Return the error if ErrorIfPathMissing exists
|
||||
if info, err = os.Stat(path); os.IsNotExist(err) {
|
||||
if options.ErrorIfPathMissing {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if !info.IsDir() {
|
||||
filePath, files = filepath.Dir(path), append(files, info)
|
||||
} else {
|
||||
if files, err = ioutil.ReadDir(path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
log.V(1).Info("reading CRDs from path", "path", path)
|
||||
crdList, err := readCRDs(filePath, files)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If CRD already in the list, skip it.
|
||||
if existsUnstructured(crds, crdList) {
|
||||
continue
|
||||
}
|
||||
crds = append(crds, crdList...)
|
||||
}
|
||||
|
||||
return unstructuredCRDListToRuntime(crds), nil
|
||||
}
|
||||
|
||||
// readCRDs reads the CRDs from files and Unmarshals them into structs
|
||||
func readCRDs(basePath string, files []os.FileInfo) ([]*unstructured.Unstructured, error) {
|
||||
var crds []*unstructured.Unstructured
|
||||
|
||||
// White list the file extensions that may contain CRDs
|
||||
crdExts := sets.NewString(".json", ".yaml", ".yml")
|
||||
|
||||
var crds []*apiextensionsv1beta1.CustomResourceDefinition
|
||||
for _, file := range files {
|
||||
// Only parse whitelisted file types
|
||||
if !crdExts.Has(filepath.Ext(file.Name())) {
|
||||
|
|
@ -218,19 +331,28 @@ func readCRDs(path string) ([]*apiextensionsv1beta1.CustomResourceDefinition, er
|
|||
}
|
||||
|
||||
// Unmarshal CRDs from file into structs
|
||||
docs, err := readDocuments(filepath.Join(path, file.Name()))
|
||||
docs, err := readDocuments(filepath.Join(basePath, file.Name()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, doc := range docs {
|
||||
crd := &apiextensionsv1beta1.CustomResourceDefinition{}
|
||||
crd := &unstructured.Unstructured{}
|
||||
if err = yaml.Unmarshal(doc, crd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check that it is actually a CRD
|
||||
if crd.Spec.Names.Kind == "" || crd.Spec.Group == "" {
|
||||
crdKind, _, err := unstructured.NestedString(crd.Object, "spec", "names", "kind")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
crdGroup, _, err := unstructured.NestedString(crd.Object, "spec", "group")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if crd.GetKind() != "CustomResourceDefinition" || crdKind == "" || crdGroup == "" {
|
||||
continue
|
||||
}
|
||||
crds = append(crds, crd)
|
||||
|
|
|
|||
11
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/ginkgo.go
generated
vendored
11
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/ginkgo.go
generated
vendored
|
|
@ -1,11 +0,0 @@
|
|||
package envtest
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
|
||||
)
|
||||
|
||||
// NewlineReporter is Reporter that Prints a newline after the default Reporter output so that the results
|
||||
// are correctly parsed by test automation.
|
||||
// See issue https://github.com/jstemmer/go-junit-report/issues/31
|
||||
// It's re-exported here to avoid compatibility breakage/mass rewrites.
|
||||
type NewlineReporter = printer.NewlineReporter
|
||||
70
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/helper.go
generated
vendored
70
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/helper.go
generated
vendored
|
|
@ -1,6 +1,23 @@
|
|||
package envtest
|
||||
|
||||
import apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
var (
|
||||
crdScheme = runtime.NewScheme()
|
||||
)
|
||||
|
||||
// init is required to correctly initialize the crdScheme package variable.
|
||||
func init() {
|
||||
_ = apiextensionsv1.AddToScheme(crdScheme)
|
||||
_ = apiextensionsv1beta1.AddToScheme(crdScheme)
|
||||
}
|
||||
|
||||
// mergePaths merges two string slices containing paths.
|
||||
// This function makes no guarantees about order of the merged slice.
|
||||
|
|
@ -23,19 +40,52 @@ func mergePaths(s1, s2 []string) []string {
|
|||
|
||||
// mergeCRDs merges two CRD slices using their names.
|
||||
// This function makes no guarantees about order of the merged slice.
|
||||
func mergeCRDs(s1, s2 []*apiextensionsv1beta1.CustomResourceDefinition) []*apiextensionsv1beta1.CustomResourceDefinition {
|
||||
m := make(map[string]*apiextensionsv1beta1.CustomResourceDefinition)
|
||||
for _, crd := range s1 {
|
||||
m[crd.Name] = crd
|
||||
func mergeCRDs(s1, s2 []runtime.Object) []runtime.Object {
|
||||
m := make(map[string]*unstructured.Unstructured)
|
||||
for _, obj := range runtimeCRDListToUnstructured(s1) {
|
||||
m[obj.GetName()] = obj
|
||||
}
|
||||
for _, crd := range s2 {
|
||||
m[crd.Name] = crd
|
||||
for _, obj := range runtimeCRDListToUnstructured(s2) {
|
||||
m[obj.GetName()] = obj
|
||||
}
|
||||
merged := make([]*apiextensionsv1beta1.CustomResourceDefinition, len(m))
|
||||
merged := make([]runtime.Object, len(m))
|
||||
i := 0
|
||||
for _, crd := range m {
|
||||
merged[i] = crd
|
||||
for _, obj := range m {
|
||||
merged[i] = obj
|
||||
i++
|
||||
}
|
||||
return merged
|
||||
}
|
||||
|
||||
// existsUnstructured verify if a any item is common between two lists.
|
||||
func existsUnstructured(s1, s2 []*unstructured.Unstructured) bool {
|
||||
for _, s1obj := range s1 {
|
||||
for _, s2obj := range s2 {
|
||||
if reflect.DeepEqual(s1obj, s2obj) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func runtimeCRDListToUnstructured(l []runtime.Object) []*unstructured.Unstructured {
|
||||
res := []*unstructured.Unstructured{}
|
||||
for _, obj := range l {
|
||||
u := &unstructured.Unstructured{}
|
||||
if err := crdScheme.Convert(obj, u, nil); err != nil {
|
||||
log.Error(err, "error converting to unstructured object", "object-kind", obj.GetObjectKind())
|
||||
continue
|
||||
}
|
||||
res = append(res, u)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func unstructuredCRDListToRuntime(l []*unstructured.Unstructured) []runtime.Object {
|
||||
res := []runtime.Object{}
|
||||
for _, obj := range l {
|
||||
res = append(res, obj.DeepCopy())
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
|
|
|||
53
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/printer/ginkgo.go
generated
vendored
53
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/printer/ginkgo.go
generated
vendored
|
|
@ -1,53 +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 printer contains setup for a friendlier Ginkgo printer that's easier
|
||||
// to parse by test automation.
|
||||
package printer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/onsi/ginkgo"
|
||||
"github.com/onsi/ginkgo/config"
|
||||
"github.com/onsi/ginkgo/types"
|
||||
)
|
||||
|
||||
var _ ginkgo.Reporter = NewlineReporter{}
|
||||
|
||||
// NewlineReporter is Reporter that Prints a newline after the default Reporter output so that the results
|
||||
// are correctly parsed by test automation.
|
||||
// See issue https://github.com/jstemmer/go-junit-report/issues/31
|
||||
type NewlineReporter struct{}
|
||||
|
||||
// SpecSuiteWillBegin implements ginkgo.Reporter
|
||||
func (NewlineReporter) SpecSuiteWillBegin(config config.GinkgoConfigType, summary *types.SuiteSummary) {
|
||||
}
|
||||
|
||||
// BeforeSuiteDidRun implements ginkgo.Reporter
|
||||
func (NewlineReporter) BeforeSuiteDidRun(setupSummary *types.SetupSummary) {}
|
||||
|
||||
// AfterSuiteDidRun implements ginkgo.Reporter
|
||||
func (NewlineReporter) AfterSuiteDidRun(setupSummary *types.SetupSummary) {}
|
||||
|
||||
// SpecWillRun implements ginkgo.Reporter
|
||||
func (NewlineReporter) SpecWillRun(specSummary *types.SpecSummary) {}
|
||||
|
||||
// SpecDidComplete implements ginkgo.Reporter
|
||||
func (NewlineReporter) SpecDidComplete(specSummary *types.SpecSummary) {}
|
||||
|
||||
// SpecSuiteDidEnd Prints a newline between "35 Passed | 0 Failed | 0 Pending | 0 Skipped" and "--- PASS:"
|
||||
func (NewlineReporter) SpecSuiteDidEnd(summary *types.SuiteSummary) { fmt.Printf("\n") }
|
||||
62
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/server.go
generated
vendored
62
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/server.go
generated
vendored
|
|
@ -23,10 +23,10 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
"sigs.k8s.io/testing_frameworks/integration"
|
||||
"sigs.k8s.io/controller-runtime/pkg/internal/testing/integration"
|
||||
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
|
||||
)
|
||||
|
|
@ -72,20 +72,6 @@ func defaultAssetPath(binary string) string {
|
|||
|
||||
}
|
||||
|
||||
// DefaultKubeAPIServerFlags are default flags necessary to bring up apiserver.
|
||||
var DefaultKubeAPIServerFlags = []string{
|
||||
// Allow tests to run offline, by preventing API server from attempting to
|
||||
// use default route to determine its --advertise-address
|
||||
"--advertise-address=127.0.0.1",
|
||||
"--etcd-servers={{ if .EtcdURL }}{{ .EtcdURL.String }}{{ end }}",
|
||||
"--cert-dir={{ .CertDir }}",
|
||||
"--insecure-port={{ if .URL }}{{ .URL.Port }}{{ end }}",
|
||||
"--insecure-bind-address={{ if .URL }}{{ .URL.Hostname }}{{ end }}",
|
||||
"--secure-port={{ if .SecurePort }}{{ .SecurePort }}{{ end }}",
|
||||
"--admission-control=AlwaysAdmit",
|
||||
"--service-cluster-ip-range=10.0.0.0/24",
|
||||
}
|
||||
|
||||
// Environment creates a Kubernetes test environment that will start / stop the Kubernetes control plane and
|
||||
// install extension APIs
|
||||
type Environment struct {
|
||||
|
|
@ -100,10 +86,18 @@ type Environment struct {
|
|||
// CRDInstallOptions are the options for installing CRDs.
|
||||
CRDInstallOptions CRDInstallOptions
|
||||
|
||||
// CRDInstallOptions are the options for installing webhooks.
|
||||
WebhookInstallOptions WebhookInstallOptions
|
||||
|
||||
// ErrorIfCRDPathMissing provides an interface for the underlying
|
||||
// CRDInstallOptions.ErrorIfPathMissing. It prevents silent failures
|
||||
// for missing CRD paths.
|
||||
ErrorIfCRDPathMissing bool
|
||||
|
||||
// CRDs is a list of CRDs to install.
|
||||
// If both this field and CRDs field in CRDInstallOptions are specified, the
|
||||
// values are merged.
|
||||
CRDs []*apiextensionsv1beta1.CustomResourceDefinition
|
||||
CRDs []runtime.Object
|
||||
|
||||
// CRDDirectoryPaths is a list of paths containing CRD yaml or json configs.
|
||||
// If both this field and Paths field in CRDInstallOptions are specified, the
|
||||
|
|
@ -135,19 +129,30 @@ type Environment struct {
|
|||
}
|
||||
|
||||
// Stop stops a running server.
|
||||
// If USE_EXISTING_CLUSTER is set to true, this method is a no-op.
|
||||
// Previously installed CRDs, as listed in CRDInstallOptions.CRDs, will be uninstalled
|
||||
// if CRDInstallOptions.CleanUpAfterUse are set to true.
|
||||
func (te *Environment) Stop() error {
|
||||
if te.CRDInstallOptions.CleanUpAfterUse {
|
||||
if err := UninstallCRDs(te.Config, te.CRDInstallOptions); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if te.useExistingCluster() {
|
||||
return nil
|
||||
}
|
||||
err := te.WebhookInstallOptions.Cleanup()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return te.ControlPlane.Stop()
|
||||
}
|
||||
|
||||
// getAPIServerFlags returns flags to be used with the Kubernetes API server.
|
||||
// it returns empty slice for api server defined defaults to be applied if no args specified
|
||||
func (te Environment) getAPIServerFlags() []string {
|
||||
// Set default API server flags if not set.
|
||||
if len(te.KubeAPIServerFlags) == 0 {
|
||||
return DefaultKubeAPIServerFlags
|
||||
return []string{}
|
||||
}
|
||||
// Check KubeAPIServerFlags contains service-cluster-ip-range, if not, set default value to service-cluster-ip-range
|
||||
containServiceClusterIPRange := false
|
||||
|
|
@ -216,7 +221,7 @@ func (te *Environment) Start() (*rest.Config, error) {
|
|||
}
|
||||
|
||||
if err := te.defaultTimeouts(); err != nil {
|
||||
return nil, fmt.Errorf("failed to default controlplane timeouts: %v", err)
|
||||
return nil, fmt.Errorf("failed to default controlplane timeouts: %w", err)
|
||||
}
|
||||
te.ControlPlane.Etcd.StartTimeout = te.ControlPlaneStartTimeout
|
||||
te.ControlPlane.Etcd.StopTimeout = te.ControlPlaneStopTimeout
|
||||
|
|
@ -240,7 +245,16 @@ func (te *Environment) Start() (*rest.Config, error) {
|
|||
log.V(1).Info("installing CRDs")
|
||||
te.CRDInstallOptions.CRDs = mergeCRDs(te.CRDInstallOptions.CRDs, te.CRDs)
|
||||
te.CRDInstallOptions.Paths = mergePaths(te.CRDInstallOptions.Paths, te.CRDDirectoryPaths)
|
||||
_, err := InstallCRDs(te.Config, te.CRDInstallOptions)
|
||||
te.CRDInstallOptions.ErrorIfPathMissing = te.ErrorIfCRDPathMissing
|
||||
crds, err := InstallCRDs(te.Config, te.CRDInstallOptions)
|
||||
if err != nil {
|
||||
return te.Config, err
|
||||
}
|
||||
te.CRDs = crds
|
||||
|
||||
log.V(1).Info("installing webhooks")
|
||||
err = te.WebhookInstallOptions.Install(te.Config)
|
||||
|
||||
return te.Config, err
|
||||
}
|
||||
|
||||
|
|
@ -256,7 +270,7 @@ func (te *Environment) startControlPlane() error {
|
|||
log.Error(err, "unable to start the controlplane", "tries", numTries)
|
||||
}
|
||||
if numTries == maxRetries {
|
||||
return fmt.Errorf("failed to start the controlplane. retried %d times: %v", numTries, err)
|
||||
return fmt.Errorf("failed to start the controlplane. retried %d times: %w", numTries, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -293,3 +307,7 @@ func (te *Environment) useExistingCluster() bool {
|
|||
}
|
||||
return *te.UseExistingCluster
|
||||
}
|
||||
|
||||
// DefaultKubeAPIServerFlags exposes the default args for the APIServer so that
|
||||
// you can use those to append your own additional arguments.
|
||||
var DefaultKubeAPIServerFlags = integration.APIServerDefaultArgs
|
||||
|
|
|
|||
423
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/webhook.go
generated
vendored
Normal file
423
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/webhook.go
generated
vendored
Normal file
|
|
@ -0,0 +1,423 @@
|
|||
/*
|
||||
Copyright 2019 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 envtest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/internal/testing/integration"
|
||||
"sigs.k8s.io/controller-runtime/pkg/internal/testing/integration/addr"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
// WebhookInstallOptions are the options for installing mutating or validating webhooks
|
||||
type WebhookInstallOptions struct {
|
||||
// Paths is a list of paths to the directories containing the mutating or validating webhooks yaml or json configs.
|
||||
DirectoryPaths []string
|
||||
|
||||
// MutatingWebhooks is a list of MutatingWebhookConfigurations to install
|
||||
MutatingWebhooks []runtime.Object
|
||||
|
||||
// ValidatingWebhooks is a list of ValidatingWebhookConfigurations to install
|
||||
ValidatingWebhooks []runtime.Object
|
||||
|
||||
// IgnoreErrorIfPathMissing will ignore an error if a DirectoryPath does not exist when set to true
|
||||
IgnoreErrorIfPathMissing bool
|
||||
|
||||
// LocalServingHost is the host for serving webhooks on.
|
||||
// it will be automatically populated
|
||||
LocalServingHost string
|
||||
|
||||
// LocalServingPort is the allocated port for serving webhooks on.
|
||||
// it will be automatically populated by a random available local port
|
||||
LocalServingPort int
|
||||
|
||||
// LocalServingCertDir is the allocated directory for serving certificates.
|
||||
// it will be automatically populated by the local temp dir
|
||||
LocalServingCertDir string
|
||||
|
||||
// MaxTime is the max time to wait
|
||||
MaxTime time.Duration
|
||||
|
||||
// PollInterval is the interval to check
|
||||
PollInterval time.Duration
|
||||
}
|
||||
|
||||
// ModifyWebhookDefinitions modifies webhook definitions by:
|
||||
// - applying CABundle based on the provided tinyca
|
||||
// - if webhook client config uses service spec, it's removed and replaced with direct url
|
||||
func (o *WebhookInstallOptions) ModifyWebhookDefinitions(caData []byte) error {
|
||||
hostPort, err := o.generateHostPort()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, unstructuredHook := range runtimeListToUnstructured(o.MutatingWebhooks) {
|
||||
webhooks, found, err := unstructured.NestedSlice(unstructuredHook.Object, "webhooks")
|
||||
if !found || err != nil {
|
||||
return fmt.Errorf("unexpected object, %v", err)
|
||||
}
|
||||
for j := range webhooks {
|
||||
webhook, err := modifyWebhook(webhooks[j].(map[string]interface{}), caData, hostPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
webhooks[j] = webhook
|
||||
unstructuredHook.Object["webhooks"] = webhooks
|
||||
o.MutatingWebhooks[i] = unstructuredHook
|
||||
}
|
||||
}
|
||||
|
||||
for i, unstructuredHook := range runtimeListToUnstructured(o.ValidatingWebhooks) {
|
||||
webhooks, found, err := unstructured.NestedSlice(unstructuredHook.Object, "webhooks")
|
||||
if !found || err != nil {
|
||||
return fmt.Errorf("unexpected object, %v", err)
|
||||
}
|
||||
for j := range webhooks {
|
||||
webhook, err := modifyWebhook(webhooks[j].(map[string]interface{}), caData, hostPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
webhooks[j] = webhook
|
||||
unstructuredHook.Object["webhooks"] = webhooks
|
||||
o.ValidatingWebhooks[i] = unstructuredHook
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func modifyWebhook(webhook map[string]interface{}, caData []byte, hostPort string) (map[string]interface{}, error) {
|
||||
clientConfig, found, err := unstructured.NestedMap(webhook, "clientConfig")
|
||||
if !found || err != nil {
|
||||
return nil, fmt.Errorf("cannot find clientconfig: %v", err)
|
||||
}
|
||||
clientConfig["caBundle"] = base64.StdEncoding.EncodeToString(caData)
|
||||
servicePath, found, err := unstructured.NestedString(clientConfig, "service", "path")
|
||||
if found && err == nil {
|
||||
// we cannot use service in integration tests since we're running controller outside cluster
|
||||
// the intent here is that we swap out service for raw address because we don't have an actually standard kube service network.
|
||||
// We want to users to be able to use your standard config though
|
||||
url := fmt.Sprintf("https://%s/%s", hostPort, servicePath)
|
||||
clientConfig["url"] = url
|
||||
clientConfig["service"] = nil
|
||||
}
|
||||
webhook["clientConfig"] = clientConfig
|
||||
return webhook, nil
|
||||
}
|
||||
|
||||
func (o *WebhookInstallOptions) generateHostPort() (string, error) {
|
||||
port, host, err := addr.Suggest()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to grab random port for serving webhooks on: %v", err)
|
||||
}
|
||||
o.LocalServingPort = port
|
||||
o.LocalServingHost = host
|
||||
return net.JoinHostPort(host, fmt.Sprintf("%d", port)), nil
|
||||
}
|
||||
|
||||
// Install installs specified webhooks to the API server
|
||||
func (o *WebhookInstallOptions) Install(config *rest.Config) error {
|
||||
hookCA, err := o.setupCA()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := parseWebhookDirs(o); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = o.ModifyWebhookDefinitions(hookCA)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := createWebhooks(config, o.MutatingWebhooks, o.ValidatingWebhooks); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := WaitForWebhooks(config, o.MutatingWebhooks, o.ValidatingWebhooks, *o); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Cleanup cleans up cert directories
|
||||
func (o *WebhookInstallOptions) Cleanup() error {
|
||||
if o.LocalServingCertDir != "" {
|
||||
return os.RemoveAll(o.LocalServingCertDir)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WaitForWebhooks waits for the Webhooks to be available through API server
|
||||
func WaitForWebhooks(config *rest.Config,
|
||||
mutatingWebhooks []runtime.Object,
|
||||
validatingWebhooks []runtime.Object,
|
||||
options WebhookInstallOptions) error {
|
||||
|
||||
waitingFor := map[schema.GroupVersionKind]*sets.String{}
|
||||
|
||||
for _, hook := range runtimeListToUnstructured(append(validatingWebhooks, mutatingWebhooks...)) {
|
||||
if _, ok := waitingFor[hook.GroupVersionKind()]; !ok {
|
||||
waitingFor[hook.GroupVersionKind()] = &sets.String{}
|
||||
}
|
||||
waitingFor[hook.GroupVersionKind()].Insert(hook.GetName())
|
||||
}
|
||||
|
||||
// Poll until all resources are found in discovery
|
||||
p := &webhookPoller{config: config, waitingFor: waitingFor}
|
||||
return wait.PollImmediate(options.PollInterval, options.MaxTime, p.poll)
|
||||
}
|
||||
|
||||
// poller checks if all the resources have been found in discovery, and returns false if not
|
||||
type webhookPoller struct {
|
||||
// config is used to get discovery
|
||||
config *rest.Config
|
||||
|
||||
// waitingFor is the map of resources keyed by group version that have not yet been found in discovery
|
||||
waitingFor map[schema.GroupVersionKind]*sets.String
|
||||
}
|
||||
|
||||
// poll checks if all the resources have been found in discovery, and returns false if not
|
||||
func (p *webhookPoller) poll() (done bool, err error) {
|
||||
// Create a new clientset to avoid any client caching of discovery
|
||||
c, err := client.New(p.config, client.Options{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
allFound := true
|
||||
for gvk, names := range p.waitingFor {
|
||||
if names.Len() == 0 {
|
||||
delete(p.waitingFor, gvk)
|
||||
continue
|
||||
}
|
||||
for _, name := range names.List() {
|
||||
var obj = &unstructured.Unstructured{}
|
||||
obj.SetGroupVersionKind(gvk)
|
||||
err := c.Get(context.Background(), client.ObjectKey{
|
||||
Namespace: "",
|
||||
Name: name,
|
||||
}, obj)
|
||||
|
||||
if err == nil {
|
||||
names.Delete(name)
|
||||
}
|
||||
|
||||
if errors.IsNotFound(err) {
|
||||
allFound = false
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return allFound, nil
|
||||
}
|
||||
|
||||
// setupCA creates CA for testing and writes them to disk
|
||||
func (o *WebhookInstallOptions) setupCA() ([]byte, error) {
|
||||
hookCA, err := integration.NewTinyCA()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to set up webhook CA: %v", err)
|
||||
}
|
||||
|
||||
hookCert, err := hookCA.NewServingCert()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to set up webhook serving certs: %v", err)
|
||||
}
|
||||
|
||||
localServingCertsDir, err := ioutil.TempDir("", "envtest-serving-certs-")
|
||||
o.LocalServingCertDir = localServingCertsDir
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create directory for webhook serving certs: %v", err)
|
||||
}
|
||||
|
||||
certData, keyData, err := hookCert.AsBytes()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to marshal webhook serving certs: %v", err)
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(filepath.Join(localServingCertsDir, "tls.crt"), certData, 0640); err != nil {
|
||||
return nil, fmt.Errorf("unable to write webhook serving cert to disk: %v", err)
|
||||
}
|
||||
if err := ioutil.WriteFile(filepath.Join(localServingCertsDir, "tls.key"), keyData, 0640); err != nil {
|
||||
return nil, fmt.Errorf("unable to write webhook serving key to disk: %v", err)
|
||||
}
|
||||
|
||||
return certData, nil
|
||||
}
|
||||
|
||||
func createWebhooks(config *rest.Config, mutHooks []runtime.Object, valHooks []runtime.Object) error {
|
||||
cs, err := client.New(config, client.Options{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create each webhook
|
||||
for _, hook := range runtimeListToUnstructured(mutHooks) {
|
||||
log.V(1).Info("installing mutating webhook", "webhook", hook.GetName())
|
||||
if err := ensureCreated(cs, hook); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, hook := range runtimeListToUnstructured(valHooks) {
|
||||
log.V(1).Info("installing validating webhook", "webhook", hook.GetName())
|
||||
if err := ensureCreated(cs, hook); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ensureCreated creates or update object if already exists in the cluster
|
||||
func ensureCreated(cs client.Client, obj *unstructured.Unstructured) error {
|
||||
existing := obj.DeepCopy()
|
||||
err := cs.Get(context.Background(), client.ObjectKey{Name: obj.GetName()}, existing)
|
||||
switch {
|
||||
case apierrors.IsNotFound(err):
|
||||
if err := cs.Create(context.Background(), obj); err != nil {
|
||||
return err
|
||||
}
|
||||
case err != nil:
|
||||
return err
|
||||
default:
|
||||
log.V(1).Info("Webhook configuration already exists, updating", "webhook", obj.GetName())
|
||||
obj.SetResourceVersion(existing.GetResourceVersion())
|
||||
if err := cs.Update(context.Background(), obj); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseWebhookDirs reads the directories of Webhooks in options.DirectoryPaths and adds the Webhook structs to options
|
||||
func parseWebhookDirs(options *WebhookInstallOptions) error {
|
||||
if len(options.DirectoryPaths) > 0 {
|
||||
for _, path := range options.DirectoryPaths {
|
||||
_, err := os.Stat(path)
|
||||
if options.IgnoreErrorIfPathMissing && os.IsNotExist(err) {
|
||||
continue // skip this path
|
||||
}
|
||||
if !options.IgnoreErrorIfPathMissing && os.IsNotExist(err) {
|
||||
return err // treat missing path as error
|
||||
}
|
||||
mutHooks, valHooks, err := readWebhooks(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options.MutatingWebhooks = append(options.MutatingWebhooks, mutHooks...)
|
||||
options.ValidatingWebhooks = append(options.ValidatingWebhooks, valHooks...)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// readWebhooks reads the Webhooks from files and Unmarshals them into structs
|
||||
// returns slice of mutating and validating webhook configurations
|
||||
func readWebhooks(path string) ([]runtime.Object, []runtime.Object, error) {
|
||||
// Get the webhook files
|
||||
var files []os.FileInfo
|
||||
var err error
|
||||
log.V(1).Info("reading Webhooks from path", "path", path)
|
||||
if files, err = ioutil.ReadDir(path); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// file extensions that may contain Webhooks
|
||||
resourceExtensions := sets.NewString(".json", ".yaml", ".yml")
|
||||
|
||||
var mutHooks []runtime.Object
|
||||
var valHooks []runtime.Object
|
||||
for _, file := range files {
|
||||
// Only parse whitelisted file types
|
||||
if !resourceExtensions.Has(filepath.Ext(file.Name())) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Unmarshal Webhooks from file into structs
|
||||
docs, err := readDocuments(filepath.Join(path, file.Name()))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for _, doc := range docs {
|
||||
var generic metav1.PartialObjectMetadata
|
||||
if err = yaml.Unmarshal(doc, &generic); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
const (
|
||||
admissionregv1 = "admissionregistration.k8s.io/v1beta1"
|
||||
admissionregv1beta1 = "admissionregistration.k8s.io/v1"
|
||||
)
|
||||
switch {
|
||||
case generic.Kind == "MutatingWebhookConfiguration":
|
||||
if generic.APIVersion != admissionregv1beta1 && generic.APIVersion != admissionregv1 {
|
||||
return nil, nil, fmt.Errorf("only v1beta1 and v1 are supported right now for MutatingWebhookConfiguration (name: %s)", generic.Name)
|
||||
}
|
||||
hook := &unstructured.Unstructured{}
|
||||
if err := yaml.Unmarshal(doc, &hook); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
mutHooks = append(mutHooks, hook)
|
||||
case generic.Kind == "ValidatingWebhookConfiguration":
|
||||
if generic.APIVersion != admissionregv1beta1 && generic.APIVersion != admissionregv1 {
|
||||
return nil, nil, fmt.Errorf("only v1beta1 and v1 are supported right now for ValidatingWebhookConfiguration (name: %s)", generic.Name)
|
||||
}
|
||||
hook := &unstructured.Unstructured{}
|
||||
if err := yaml.Unmarshal(doc, &hook); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
valHooks = append(valHooks, hook)
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
log.V(1).Info("read webhooks from file", "file", file.Name())
|
||||
}
|
||||
return mutHooks, valHooks, nil
|
||||
}
|
||||
|
||||
func runtimeListToUnstructured(l []runtime.Object) []*unstructured.Unstructured {
|
||||
res := []*unstructured.Unstructured{}
|
||||
for _, obj := range l {
|
||||
m, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj.DeepCopyObject())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
res = append(res, &unstructured.Unstructured{
|
||||
Object: m,
|
||||
})
|
||||
}
|
||||
return res
|
||||
}
|
||||
10
vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/integration/README.md
generated
vendored
Normal file
10
vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/integration/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# Integration Testing Framework
|
||||
|
||||
This package has been moved from [https://github.com/kubernetes-sigs/testing_frameworks/tree/master/integration](https://github.com/kubernetes-sigs/testing_frameworks/tree/master/integration).
|
||||
|
||||
A framework for integration testing components of kubernetes. This framework is
|
||||
intended to work properly both in CI, and on a local dev machine. It therefore
|
||||
explicitly supports both Linux and Darwin.
|
||||
|
||||
For detailed documentation see the
|
||||
[](https://godoc.org/github.com/kubernetes-sigs/controller-runtime/pkg/internal/testing/integration).
|
||||
74
vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/integration/addr/manager.go
generated
vendored
Normal file
74
vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/integration/addr/manager.go
generated
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
package addr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
portReserveTime = 1 * time.Minute
|
||||
portConflictRetry = 100
|
||||
)
|
||||
|
||||
type portCache struct {
|
||||
lock sync.Mutex
|
||||
ports map[int]time.Time
|
||||
}
|
||||
|
||||
func (c *portCache) add(port int) bool {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
// remove outdated port
|
||||
for p, t := range c.ports {
|
||||
if time.Since(t) > portReserveTime {
|
||||
delete(c.ports, p)
|
||||
}
|
||||
}
|
||||
// try allocating new port
|
||||
if _, ok := c.ports[port]; ok {
|
||||
return false
|
||||
}
|
||||
c.ports[port] = time.Now()
|
||||
return true
|
||||
}
|
||||
|
||||
var cache = &portCache{
|
||||
ports: make(map[int]time.Time),
|
||||
}
|
||||
|
||||
func suggest() (port int, resolvedHost string, err error) {
|
||||
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
l, err := net.ListenTCP("tcp", addr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
port = l.Addr().(*net.TCPAddr).Port
|
||||
defer func() {
|
||||
err = l.Close()
|
||||
}()
|
||||
resolvedHost = addr.IP.String()
|
||||
return
|
||||
}
|
||||
|
||||
// Suggest suggests an address a process can listen on. It returns
|
||||
// a tuple consisting of a free port and the hostname resolved to its IP.
|
||||
// It makes sure that new port allocated does not conflict with old ports
|
||||
// allocated within 1 minute.
|
||||
func Suggest() (port int, resolvedHost string, err error) {
|
||||
for i := 0; i < portConflictRetry; i++ {
|
||||
port, resolvedHost, err = suggest()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if cache.add(port) {
|
||||
return
|
||||
}
|
||||
}
|
||||
err = fmt.Errorf("no free ports found after %d retries", portConflictRetry)
|
||||
return
|
||||
}
|
||||
|
|
@ -3,11 +3,14 @@ package integration
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"sigs.k8s.io/testing_frameworks/integration/addr"
|
||||
"sigs.k8s.io/testing_frameworks/integration/internal"
|
||||
"sigs.k8s.io/controller-runtime/pkg/internal/testing/integration/addr"
|
||||
"sigs.k8s.io/controller-runtime/pkg/internal/testing/integration/internal"
|
||||
)
|
||||
|
||||
// APIServer knows how to run a kubernetes apiserver.
|
||||
|
|
@ -34,7 +37,7 @@ type APIServer struct {
|
|||
// APIServer struct (e.g. "--cert-dir={{ .Dir }}").
|
||||
// Those templates will be evaluated after the defaulting of the APIServer's
|
||||
// fields has already happened and just before the binary actually gets
|
||||
// started. Thus you have access to caluclated fields like `URL` and others.
|
||||
// started. Thus you have access to calculated fields like `URL` and others.
|
||||
//
|
||||
// If not specified, the minimal set of arguments to run the APIServer will
|
||||
// be used.
|
||||
|
|
@ -116,21 +119,59 @@ func (s *APIServer) setProcessState() error {
|
|||
s.StartTimeout = s.processState.StartTimeout
|
||||
s.StopTimeout = s.processState.StopTimeout
|
||||
|
||||
if err := s.populateAPIServerCerts(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.processState.Args, err = internal.RenderTemplates(
|
||||
internal.DoAPIServerArgDefaulting(s.Args), s,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *APIServer) populateAPIServerCerts() error {
|
||||
_, statErr := os.Stat(filepath.Join(s.CertDir, "apiserver.crt"))
|
||||
if !os.IsNotExist(statErr) {
|
||||
return statErr
|
||||
}
|
||||
|
||||
ca, err := internal.NewTinyCA()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
certs, err := ca.NewServingCert()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
certData, keyData, err := certs.AsBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(filepath.Join(s.CertDir, "apiserver.crt"), certData, 0640); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(filepath.Join(s.CertDir, "apiserver.key"), keyData, 0640); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop stops this process gracefully, waits for its termination, and cleans up
|
||||
// the CertDir if necessary.
|
||||
func (s *APIServer) Stop() error {
|
||||
return s.processState.Stop()
|
||||
if s.processState != nil {
|
||||
return s.processState.Stop()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// APIServerDefaultArgs exposes the default args for the APIServer so that you
|
||||
// can use those to append your own additional arguments.
|
||||
//
|
||||
// The internal default arguments are explicitely copied here, we don't want to
|
||||
// The internal default arguments are explicitly copied here, we don't want to
|
||||
// allow users to change the internal ones.
|
||||
var APIServerDefaultArgs = append([]string{}, internal.APIServerDefaultArgs...)
|
||||
|
|
@ -3,8 +3,18 @@ package integration
|
|||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/internal/testing/integration/internal"
|
||||
)
|
||||
|
||||
// NewTinyCA creates a new a tiny CA utility for provisioning serving certs and client certs FOR TESTING ONLY.
|
||||
// Don't use this for anything else!
|
||||
var NewTinyCA = internal.NewTinyCA
|
||||
|
||||
// ControlPlane is a struct that knows how to start your test control plane.
|
||||
//
|
||||
// Right now, that means Etcd and your APIServer. This is likely to increase in
|
||||
|
|
@ -57,3 +67,16 @@ func (f *ControlPlane) KubeCtl() *KubeCtl {
|
|||
k.Opts = append(k.Opts, fmt.Sprintf("--server=%s", f.APIURL()))
|
||||
return k
|
||||
}
|
||||
|
||||
// RESTClientConfig returns a pre-configured restconfig, ready to connect to
|
||||
// this ControlPlane.
|
||||
func (f *ControlPlane) RESTClientConfig() (*rest.Config, error) {
|
||||
c := &rest.Config{
|
||||
Host: f.APIURL().String(),
|
||||
ContentConfig: rest.ContentConfig{
|
||||
NegotiatedSerializer: serializer.WithoutConversionCodecFactory{CodecFactory: scheme.Codecs},
|
||||
},
|
||||
}
|
||||
err := rest.SetKubernetesDefaults(c)
|
||||
return c, err
|
||||
}
|
||||
|
|
@ -8,9 +8,7 @@ needed to provide this API is managed by this framework.
|
|||
|
||||
Quickstart
|
||||
|
||||
If you want to test a kubernetes client against the latest kubernetes APIServer
|
||||
and Etcd, you can use `./scripts/download-binaries.sh` to download APIServer
|
||||
and Etcd binaries for your platform. Then add something like the following to
|
||||
Add something like the following to
|
||||
your tests:
|
||||
|
||||
cp := &integration.ControlPlane{}
|
||||
|
|
@ -71,11 +69,6 @@ APIServer, Etcd or KubeCtl.
|
|||
framework tries to use the binaries `kube-apiserver`, `etcd` or `kubectl` in
|
||||
the directory `${FRAMEWORK_DIR}/assets/bin/`.
|
||||
|
||||
For convenience this framework ships with
|
||||
`${FRAMEWORK_DIR}/scripts/download-binaries.sh` which can be used to download
|
||||
pre-compiled versions of the needed binaries and place them in the default
|
||||
location (`${FRAMEWORK_DIR}/assets/bin/`).
|
||||
|
||||
Arguments for Etcd and APIServer
|
||||
|
||||
Those components will start without any configuration. However, if you want or
|
||||
|
|
@ -6,7 +6,7 @@ import (
|
|||
|
||||
"net/url"
|
||||
|
||||
"sigs.k8s.io/testing_frameworks/integration/internal"
|
||||
"sigs.k8s.io/controller-runtime/pkg/internal/testing/integration/internal"
|
||||
)
|
||||
|
||||
// Etcd knows how to run an etcd server.
|
||||
|
|
@ -30,7 +30,7 @@ type Etcd struct {
|
|||
// struct (e.g. "--data-dir={{ .Dir }}").
|
||||
// Those templates will be evaluated after the defaulting of the Etcd's
|
||||
// fields has already happened and just before the binary actually gets
|
||||
// started. Thus you have access to caluclated fields like `URL` and others.
|
||||
// started. Thus you have access to calculated fields like `URL` and others.
|
||||
//
|
||||
// If not specified, the minimal set of arguments to run the Etcd will be
|
||||
// used.
|
||||
|
|
@ -109,6 +109,6 @@ func (e *Etcd) Stop() error {
|
|||
// EtcdDefaultArgs exposes the default args for Etcd so that you
|
||||
// can use those to append your own additional arguments.
|
||||
//
|
||||
// The internal default arguments are explicitely copied here, we don't want to
|
||||
// The internal default arguments are explicitly copied here, we don't want to
|
||||
// allow users to change the internal ones.
|
||||
var EtcdDefaultArgs = append([]string{}, internal.EtcdDefaultArgs...)
|
||||
|
|
@ -1,11 +1,17 @@
|
|||
package internal
|
||||
|
||||
var APIServerDefaultArgs = []string{
|
||||
// Allow tests to run offline, by preventing API server from attempting to
|
||||
// use default route to determine its --advertise-address
|
||||
"--advertise-address=127.0.0.1",
|
||||
"--etcd-servers={{ if .EtcdURL }}{{ .EtcdURL.String }}{{ end }}",
|
||||
"--cert-dir={{ .CertDir }}",
|
||||
"--insecure-port={{ if .URL }}{{ .URL.Port }}{{ end }}",
|
||||
"--insecure-bind-address={{ if .URL }}{{ .URL.Hostname }}{{ end }}",
|
||||
"--secure-port={{ if .SecurePort }}{{ .SecurePort }}{{ end }}",
|
||||
"--disable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,TaintNodesByCondition,Priority,DefaultTolerationSeconds,DefaultStorageClass,StorageObjectInUseProtection,PersistentVolumeClaimResize,ResourceQuota", //nolint
|
||||
"--service-cluster-ip-range=10.0.0.0/24",
|
||||
"--allow-privileged=true",
|
||||
}
|
||||
|
||||
func DoAPIServerArgDefaulting(args []string) []string {
|
||||
|
|
@ -27,8 +27,8 @@ func isSecureScheme(scheme string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func GetEtcdStartMessage(listenUrl url.URL) string {
|
||||
if isSecureScheme(listenUrl.Scheme) {
|
||||
func GetEtcdStartMessage(listenURL url.URL) string {
|
||||
if isSecureScheme(listenURL.Scheme) {
|
||||
// https://github.com/coreos/etcd/blob/a7f1fbe00ec216fcb3a1919397a103b41dca8413/embed/serve.go#L167
|
||||
return "serving client requests on "
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@ import (
|
|||
"github.com/onsi/gomega/gbytes"
|
||||
"github.com/onsi/gomega/gexec"
|
||||
|
||||
"sigs.k8s.io/testing_frameworks/integration/addr"
|
||||
"sigs.k8s.io/controller-runtime/pkg/internal/testing/integration/addr"
|
||||
)
|
||||
|
||||
type ProcessState struct {
|
||||
|
|
@ -30,7 +30,7 @@ type ProcessState struct {
|
|||
// HealthCheckEndpoint.
|
||||
// If left empty it will default to 100 Milliseconds.
|
||||
HealthCheckPollInterval time.Duration
|
||||
// StartMessage is the message to wait for on stderr. If we recieve this
|
||||
// StartMessage is the message to wait for on stderr. If we receive this
|
||||
// message, we assume the process is ready to operate. Ignored if
|
||||
// HealthCheckEndpoint is specified.
|
||||
//
|
||||
|
|
@ -57,7 +57,7 @@ type DefaultedProcessInput struct {
|
|||
|
||||
func DoDefaulting(
|
||||
name string,
|
||||
listenUrl *url.URL,
|
||||
listenURL *url.URL,
|
||||
dir string,
|
||||
path string,
|
||||
startTimeout time.Duration,
|
||||
|
|
@ -70,7 +70,7 @@ func DoDefaulting(
|
|||
StopTimeout: stopTimeout,
|
||||
}
|
||||
|
||||
if listenUrl == nil {
|
||||
if listenURL == nil {
|
||||
port, host, err := addr.Suggest()
|
||||
if err != nil {
|
||||
return DefaultedProcessInput{}, err
|
||||
|
|
@ -80,7 +80,7 @@ func DoDefaulting(
|
|||
Host: net.JoinHostPort(host, strconv.Itoa(port)),
|
||||
}
|
||||
} else {
|
||||
defaults.URL = *listenUrl
|
||||
defaults.URL = *listenURL
|
||||
}
|
||||
|
||||
if dir == "" {
|
||||
|
|
@ -170,9 +170,12 @@ func pollURLUntilOK(url url.URL, interval time.Duration, ready chan bool, stopCh
|
|||
}
|
||||
for {
|
||||
res, err := http.Get(url.String())
|
||||
if err == nil && res.StatusCode == http.StatusOK {
|
||||
ready <- true
|
||||
return
|
||||
if err == nil {
|
||||
res.Body.Close()
|
||||
if res.StatusCode == http.StatusOK {
|
||||
ready <- true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
149
vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/integration/internal/tinyca.go
generated
vendored
Normal file
149
vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/integration/internal/tinyca.go
generated
vendored
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
package internal
|
||||
|
||||
// NB(directxman12): nothing has verified that this has good settings. In fact,
|
||||
// the setting generated here are probably terrible, but they're fine for integration
|
||||
// tests. These ABSOLUTELY SHOULD NOT ever be exposed in the public API. They're
|
||||
// ONLY for use with envtest's ability to configure webhook testing.
|
||||
// If I didn't otherwise not want to add a dependency on cfssl, I'd just use that.
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
crand "crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
)
|
||||
|
||||
var (
|
||||
rsaKeySize = 2048 // a decent number, as of 2019
|
||||
bigOne = big.NewInt(1)
|
||||
)
|
||||
|
||||
// CertPair is a private key and certificate for use for client auth, as a CA, or serving.
|
||||
type CertPair struct {
|
||||
Key crypto.Signer
|
||||
Cert *x509.Certificate
|
||||
}
|
||||
|
||||
// CertBytes returns the PEM-encoded version of the certificate for this pair.
|
||||
func (k CertPair) CertBytes() []byte {
|
||||
return pem.EncodeToMemory(&pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: k.Cert.Raw,
|
||||
})
|
||||
}
|
||||
|
||||
// AsBytes encodes keypair in the appropriate formats for on-disk storage (PEM and
|
||||
// PKCS8, respectively).
|
||||
func (k CertPair) AsBytes() (cert []byte, key []byte, err error) {
|
||||
cert = k.CertBytes()
|
||||
|
||||
rawKeyData, err := x509.MarshalPKCS8PrivateKey(k.Key)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to encode private key: %v", err)
|
||||
}
|
||||
|
||||
key = pem.EncodeToMemory(&pem.Block{
|
||||
Type: "PRIVATE KEY",
|
||||
Bytes: rawKeyData,
|
||||
})
|
||||
|
||||
return cert, key, nil
|
||||
}
|
||||
|
||||
// TinyCA supports signing serving certs and client-certs,
|
||||
// and can be used as an auth mechanism with envtest.
|
||||
type TinyCA struct {
|
||||
CA CertPair
|
||||
orgName string
|
||||
|
||||
nextSerial *big.Int
|
||||
}
|
||||
|
||||
// newPrivateKey generates a new private key of a relatively sane size (see
|
||||
// rsaKeySize).
|
||||
func newPrivateKey() (crypto.Signer, error) {
|
||||
return rsa.GenerateKey(crand.Reader, rsaKeySize)
|
||||
}
|
||||
|
||||
func NewTinyCA() (*TinyCA, error) {
|
||||
caPrivateKey, err := newPrivateKey()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to generate private key for CA: %v", err)
|
||||
}
|
||||
caCfg := certutil.Config{CommonName: "envtest-environment", Organization: []string{"envtest"}}
|
||||
caCert, err := certutil.NewSelfSignedCACert(caCfg, caPrivateKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to generate certificate for CA: %v", err)
|
||||
}
|
||||
|
||||
return &TinyCA{
|
||||
CA: CertPair{Key: caPrivateKey, Cert: caCert},
|
||||
orgName: "envtest",
|
||||
nextSerial: big.NewInt(1),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *TinyCA) makeCert(cfg certutil.Config) (CertPair, error) {
|
||||
now := time.Now()
|
||||
|
||||
key, err := newPrivateKey()
|
||||
if err != nil {
|
||||
return CertPair{}, fmt.Errorf("unable to create private key: %v", err)
|
||||
}
|
||||
|
||||
serial := new(big.Int).Set(c.nextSerial)
|
||||
c.nextSerial.Add(c.nextSerial, bigOne)
|
||||
|
||||
template := x509.Certificate{
|
||||
Subject: pkix.Name{CommonName: cfg.CommonName, Organization: cfg.Organization},
|
||||
DNSNames: cfg.AltNames.DNSNames,
|
||||
IPAddresses: cfg.AltNames.IPs,
|
||||
SerialNumber: serial,
|
||||
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: cfg.Usages,
|
||||
|
||||
// technically not necessary for testing, but let's set anyway just in case.
|
||||
NotBefore: now.UTC(),
|
||||
// 1 week -- the default for cfssl, and just long enough for a
|
||||
// long-term test, but not too long that anyone would try to use this
|
||||
// seriously.
|
||||
NotAfter: now.Add(168 * time.Hour).UTC(),
|
||||
}
|
||||
|
||||
certRaw, err := x509.CreateCertificate(crand.Reader, &template, c.CA.Cert, key.Public(), c.CA.Key)
|
||||
if err != nil {
|
||||
return CertPair{}, fmt.Errorf("unable to create certificate: %v", err)
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(certRaw)
|
||||
if err != nil {
|
||||
return CertPair{}, fmt.Errorf("generated invalid certificate, could not parse: %v", err)
|
||||
}
|
||||
|
||||
return CertPair{
|
||||
Key: key,
|
||||
Cert: cert,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewServingCert returns a new CertPair for a serving HTTPS on localhost.
|
||||
func (c *TinyCA) NewServingCert() (CertPair, error) {
|
||||
return c.makeCert(certutil.Config{
|
||||
CommonName: "localhost",
|
||||
Organization: []string{c.orgName},
|
||||
AltNames: certutil.AltNames{
|
||||
DNSNames: []string{"localhost"},
|
||||
IPs: []net.IP{net.IPv4(127, 0, 0, 1), net.IPv6loopback},
|
||||
},
|
||||
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
})
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import (
|
|||
"io"
|
||||
"os/exec"
|
||||
|
||||
"sigs.k8s.io/testing_frameworks/integration/internal"
|
||||
"sigs.k8s.io/controller-runtime/pkg/internal/testing/integration/internal"
|
||||
)
|
||||
|
||||
// KubeCtl is a wrapper around the kubectl binary.
|
||||
|
|
@ -186,7 +186,7 @@
|
|||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2018 The Kubernetes Authors
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
203
vendor/sigs.k8s.io/structured-merge-diff/v3/value/allocator.go
generated
vendored
Normal file
203
vendor/sigs.k8s.io/structured-merge-diff/v3/value/allocator.go
generated
vendored
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
Copyright 2020 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 value
|
||||
|
||||
// Allocator provides a value object allocation strategy.
|
||||
// Value objects can be allocated by passing an allocator to the "Using"
|
||||
// receiver functions on the value interfaces, e.g. Map.ZipUsing(allocator, ...).
|
||||
// Value objects returned from "Using" functions should be given back to the allocator
|
||||
// once longer needed by calling Allocator.Free(Value).
|
||||
type Allocator interface {
|
||||
// Free gives the allocator back any value objects returned by the "Using"
|
||||
// receiver functions on the value interfaces.
|
||||
// interface{} may be any of: Value, Map, List or Range.
|
||||
Free(interface{})
|
||||
|
||||
// The unexported functions are for "Using" receiver functions of the value types
|
||||
// to request what they need from the allocator.
|
||||
allocValueUnstructured() *valueUnstructured
|
||||
allocListUnstructuredRange() *listUnstructuredRange
|
||||
allocValueReflect() *valueReflect
|
||||
allocMapReflect() *mapReflect
|
||||
allocStructReflect() *structReflect
|
||||
allocListReflect() *listReflect
|
||||
allocListReflectRange() *listReflectRange
|
||||
}
|
||||
|
||||
// HeapAllocator simply allocates objects to the heap. It is the default
|
||||
// allocator used receiver functions on the value interfaces that do not accept
|
||||
// an allocator and should be used whenever allocating objects that will not
|
||||
// be given back to an allocator by calling Allocator.Free(Value).
|
||||
var HeapAllocator = &heapAllocator{}
|
||||
|
||||
type heapAllocator struct{}
|
||||
|
||||
func (p *heapAllocator) allocValueUnstructured() *valueUnstructured {
|
||||
return &valueUnstructured{}
|
||||
}
|
||||
|
||||
func (p *heapAllocator) allocListUnstructuredRange() *listUnstructuredRange {
|
||||
return &listUnstructuredRange{vv: &valueUnstructured{}}
|
||||
}
|
||||
|
||||
func (p *heapAllocator) allocValueReflect() *valueReflect {
|
||||
return &valueReflect{}
|
||||
}
|
||||
|
||||
func (p *heapAllocator) allocStructReflect() *structReflect {
|
||||
return &structReflect{}
|
||||
}
|
||||
|
||||
func (p *heapAllocator) allocMapReflect() *mapReflect {
|
||||
return &mapReflect{}
|
||||
}
|
||||
|
||||
func (p *heapAllocator) allocListReflect() *listReflect {
|
||||
return &listReflect{}
|
||||
}
|
||||
|
||||
func (p *heapAllocator) allocListReflectRange() *listReflectRange {
|
||||
return &listReflectRange{vr: &valueReflect{}}
|
||||
}
|
||||
|
||||
func (p *heapAllocator) Free(_ interface{}) {}
|
||||
|
||||
// NewFreelistAllocator creates freelist based allocator.
|
||||
// This allocator provides fast allocation and freeing of short lived value objects.
|
||||
//
|
||||
// The freelists are bounded in size by freelistMaxSize. If more than this amount of value objects is
|
||||
// allocated at once, the excess will be returned to the heap for garbage collection when freed.
|
||||
//
|
||||
// This allocator is unsafe and must not be accessed concurrently by goroutines.
|
||||
//
|
||||
// This allocator works well for traversal of value data trees. Typical usage is to acquire
|
||||
// a freelist at the beginning of the traversal and use it through out
|
||||
// for all temporary value access.
|
||||
func NewFreelistAllocator() Allocator {
|
||||
return &freelistAllocator{
|
||||
valueUnstructured: &freelist{new: func() interface{} {
|
||||
return &valueUnstructured{}
|
||||
}},
|
||||
listUnstructuredRange: &freelist{new: func() interface{} {
|
||||
return &listUnstructuredRange{vv: &valueUnstructured{}}
|
||||
}},
|
||||
valueReflect: &freelist{new: func() interface{} {
|
||||
return &valueReflect{}
|
||||
}},
|
||||
mapReflect: &freelist{new: func() interface{} {
|
||||
return &mapReflect{}
|
||||
}},
|
||||
structReflect: &freelist{new: func() interface{} {
|
||||
return &structReflect{}
|
||||
}},
|
||||
listReflect: &freelist{new: func() interface{} {
|
||||
return &listReflect{}
|
||||
}},
|
||||
listReflectRange: &freelist{new: func() interface{} {
|
||||
return &listReflectRange{vr: &valueReflect{}}
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
// Bound memory usage of freelists. This prevents the processing of very large lists from leaking memory.
|
||||
// This limit is large enough for endpoints objects containing 1000 IP address entries. Freed objects
|
||||
// that don't fit into the freelist are orphaned on the heap to be garbage collected.
|
||||
const freelistMaxSize = 1000
|
||||
|
||||
type freelistAllocator struct {
|
||||
valueUnstructured *freelist
|
||||
listUnstructuredRange *freelist
|
||||
valueReflect *freelist
|
||||
mapReflect *freelist
|
||||
structReflect *freelist
|
||||
listReflect *freelist
|
||||
listReflectRange *freelist
|
||||
}
|
||||
|
||||
type freelist struct {
|
||||
list []interface{}
|
||||
new func() interface{}
|
||||
}
|
||||
|
||||
func (f *freelist) allocate() interface{} {
|
||||
var w2 interface{}
|
||||
if n := len(f.list); n > 0 {
|
||||
w2, f.list = f.list[n-1], f.list[:n-1]
|
||||
} else {
|
||||
w2 = f.new()
|
||||
}
|
||||
return w2
|
||||
}
|
||||
|
||||
func (f *freelist) free(v interface{}) {
|
||||
if len(f.list) < freelistMaxSize {
|
||||
f.list = append(f.list, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *freelistAllocator) Free(value interface{}) {
|
||||
switch v := value.(type) {
|
||||
case *valueUnstructured:
|
||||
v.Value = nil // don't hold references to unstructured objects
|
||||
w.valueUnstructured.free(v)
|
||||
case *listUnstructuredRange:
|
||||
v.vv.Value = nil // don't hold references to unstructured objects
|
||||
w.listUnstructuredRange.free(v)
|
||||
case *valueReflect:
|
||||
v.ParentMapKey = nil
|
||||
v.ParentMap = nil
|
||||
w.valueReflect.free(v)
|
||||
case *mapReflect:
|
||||
w.mapReflect.free(v)
|
||||
case *structReflect:
|
||||
w.structReflect.free(v)
|
||||
case *listReflect:
|
||||
w.listReflect.free(v)
|
||||
case *listReflectRange:
|
||||
v.vr.ParentMapKey = nil
|
||||
v.vr.ParentMap = nil
|
||||
w.listReflectRange.free(v)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *freelistAllocator) allocValueUnstructured() *valueUnstructured {
|
||||
return w.valueUnstructured.allocate().(*valueUnstructured)
|
||||
}
|
||||
|
||||
func (w *freelistAllocator) allocListUnstructuredRange() *listUnstructuredRange {
|
||||
return w.listUnstructuredRange.allocate().(*listUnstructuredRange)
|
||||
}
|
||||
|
||||
func (w *freelistAllocator) allocValueReflect() *valueReflect {
|
||||
return w.valueReflect.allocate().(*valueReflect)
|
||||
}
|
||||
|
||||
func (w *freelistAllocator) allocStructReflect() *structReflect {
|
||||
return w.structReflect.allocate().(*structReflect)
|
||||
}
|
||||
|
||||
func (w *freelistAllocator) allocMapReflect() *mapReflect {
|
||||
return w.mapReflect.allocate().(*mapReflect)
|
||||
}
|
||||
|
||||
func (w *freelistAllocator) allocListReflect() *listReflect {
|
||||
return w.listReflect.allocate().(*listReflect)
|
||||
}
|
||||
|
||||
func (w *freelistAllocator) allocListReflectRange() *listReflectRange {
|
||||
return w.listReflectRange.allocate().(*listReflectRange)
|
||||
}
|
||||
21
vendor/sigs.k8s.io/structured-merge-diff/v3/value/doc.go
generated
vendored
Normal file
21
vendor/sigs.k8s.io/structured-merge-diff/v3/value/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package value defines types for an in-memory representation of yaml or json
|
||||
// objects, organized for convenient comparison with a schema (as defined by
|
||||
// the sibling schema package). Functions for reading and writing the objects
|
||||
// are also provided.
|
||||
package value
|
||||
97
vendor/sigs.k8s.io/structured-merge-diff/v3/value/fields.go
generated
vendored
Normal file
97
vendor/sigs.k8s.io/structured-merge-diff/v3/value/fields.go
generated
vendored
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
Copyright 2019 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 value
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Field is an individual key-value pair.
|
||||
type Field struct {
|
||||
Name string
|
||||
Value Value
|
||||
}
|
||||
|
||||
// FieldList is a list of key-value pairs. Each field is expected to
|
||||
// have a different name.
|
||||
type FieldList []Field
|
||||
|
||||
// Sort sorts the field list by Name.
|
||||
func (f FieldList) Sort() {
|
||||
if len(f) < 2 {
|
||||
return
|
||||
}
|
||||
if len(f) == 2 {
|
||||
if f[1].Name < f[0].Name {
|
||||
f[0], f[1] = f[1], f[0]
|
||||
}
|
||||
return
|
||||
}
|
||||
sort.SliceStable(f, func(i, j int) bool {
|
||||
return f[i].Name < f[j].Name
|
||||
})
|
||||
}
|
||||
|
||||
// Less compares two lists lexically.
|
||||
func (f FieldList) Less(rhs FieldList) bool {
|
||||
return f.Compare(rhs) == -1
|
||||
}
|
||||
|
||||
// Compare compares two lists lexically. The result will be 0 if f==rhs, -1
|
||||
// if f < rhs, and +1 if f > rhs.
|
||||
func (f FieldList) Compare(rhs FieldList) int {
|
||||
i := 0
|
||||
for {
|
||||
if i >= len(f) && i >= len(rhs) {
|
||||
// Maps are the same length and all items are equal.
|
||||
return 0
|
||||
}
|
||||
if i >= len(f) {
|
||||
// F is shorter.
|
||||
return -1
|
||||
}
|
||||
if i >= len(rhs) {
|
||||
// RHS is shorter.
|
||||
return 1
|
||||
}
|
||||
if c := strings.Compare(f[i].Name, rhs[i].Name); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := Compare(f[i].Value, rhs[i].Value); c != 0 {
|
||||
return c
|
||||
}
|
||||
// The items are equal; continue.
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// Equals returns true if the two fieldslist are equals, false otherwise.
|
||||
func (f FieldList) Equals(rhs FieldList) bool {
|
||||
if len(f) != len(rhs) {
|
||||
return false
|
||||
}
|
||||
for i := range f {
|
||||
if f[i].Name != rhs[i].Name {
|
||||
return false
|
||||
}
|
||||
if !Equals(f[i].Value, rhs[i].Value) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
91
vendor/sigs.k8s.io/structured-merge-diff/v3/value/jsontagutil.go
generated
vendored
Normal file
91
vendor/sigs.k8s.io/structured-merge-diff/v3/value/jsontagutil.go
generated
vendored
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
Copyright 2019 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 value
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TODO: This implements the same functionality as https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/runtime/converter.go#L236
|
||||
// but is based on the highly efficient approach from https://golang.org/src/encoding/json/encode.go
|
||||
|
||||
func lookupJsonTags(f reflect.StructField) (name string, omit bool, inline bool, omitempty bool) {
|
||||
tag := f.Tag.Get("json")
|
||||
if tag == "-" {
|
||||
return "", true, false, false
|
||||
}
|
||||
name, opts := parseTag(tag)
|
||||
if name == "" {
|
||||
name = f.Name
|
||||
}
|
||||
return name, false, opts.Contains("inline"), opts.Contains("omitempty")
|
||||
}
|
||||
|
||||
func isZero(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
||||
return v.Len() == 0
|
||||
case reflect.Bool:
|
||||
return !v.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return v.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return v.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float() == 0
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
return v.IsNil()
|
||||
case reflect.Chan, reflect.Func:
|
||||
panic(fmt.Sprintf("unsupported type: %v", v.Type()))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type tagOptions string
|
||||
|
||||
// parseTag splits a struct field's json tag into its name and
|
||||
// comma-separated options.
|
||||
func parseTag(tag string) (string, tagOptions) {
|
||||
if idx := strings.Index(tag, ","); idx != -1 {
|
||||
return tag[:idx], tagOptions(tag[idx+1:])
|
||||
}
|
||||
return tag, tagOptions("")
|
||||
}
|
||||
|
||||
// Contains reports whether a comma-separated list of options
|
||||
// contains a particular substr flag. substr must be surrounded by a
|
||||
// string boundary or commas.
|
||||
func (o tagOptions) Contains(optionName string) bool {
|
||||
if len(o) == 0 {
|
||||
return false
|
||||
}
|
||||
s := string(o)
|
||||
for s != "" {
|
||||
var next string
|
||||
i := strings.Index(s, ",")
|
||||
if i >= 0 {
|
||||
s, next = s[:i], s[i+1:]
|
||||
}
|
||||
if s == optionName {
|
||||
return true
|
||||
}
|
||||
s = next
|
||||
}
|
||||
return false
|
||||
}
|
||||
139
vendor/sigs.k8s.io/structured-merge-diff/v3/value/list.go
generated
vendored
Normal file
139
vendor/sigs.k8s.io/structured-merge-diff/v3/value/list.go
generated
vendored
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
Copyright 2019 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 value
|
||||
|
||||
// List represents a list object.
|
||||
type List interface {
|
||||
// Length returns how many items can be found in the map.
|
||||
Length() int
|
||||
// At returns the item at the given position in the map. It will
|
||||
// panic if the index is out of range.
|
||||
At(int) Value
|
||||
// AtUsing uses the provided allocator and returns the item at the given
|
||||
// position in the map. It will panic if the index is out of range.
|
||||
// The returned Value should be given back to the Allocator when no longer needed
|
||||
// by calling Allocator.Free(Value).
|
||||
AtUsing(Allocator, int) Value
|
||||
// Range returns a ListRange for iterating over the items in the list.
|
||||
Range() ListRange
|
||||
// RangeUsing uses the provided allocator and returns a ListRange for
|
||||
// iterating over the items in the list.
|
||||
// The returned Range should be given back to the Allocator when no longer needed
|
||||
// by calling Allocator.Free(Value).
|
||||
RangeUsing(Allocator) ListRange
|
||||
// Equals compares the two lists, and return true if they are the same, false otherwise.
|
||||
// Implementations can use ListEquals as a general implementation for this methods.
|
||||
Equals(List) bool
|
||||
// EqualsUsing uses the provided allocator and compares the two lists, and return true if
|
||||
// they are the same, false otherwise. Implementations can use ListEqualsUsing as a general
|
||||
// implementation for this methods.
|
||||
EqualsUsing(Allocator, List) bool
|
||||
}
|
||||
|
||||
// ListRange represents a single iteration across the items of a list.
|
||||
type ListRange interface {
|
||||
// Next increments to the next item in the range, if there is one, and returns true, or returns false if there are no more items.
|
||||
Next() bool
|
||||
// Item returns the index and value of the current item in the range. or panics if there is no current item.
|
||||
// For efficiency, Item may reuse the values returned by previous Item calls. Callers should be careful avoid holding
|
||||
// pointers to the value returned by Item() that escape the iteration loop since they become invalid once either
|
||||
// Item() or Allocator.Free() is called.
|
||||
Item() (index int, value Value)
|
||||
}
|
||||
|
||||
var EmptyRange = &emptyRange{}
|
||||
|
||||
type emptyRange struct{}
|
||||
|
||||
func (_ *emptyRange) Next() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (_ *emptyRange) Item() (index int, value Value) {
|
||||
panic("Item called on empty ListRange")
|
||||
}
|
||||
|
||||
// ListEquals compares two lists lexically.
|
||||
// WARN: This is a naive implementation, calling lhs.Equals(rhs) is typically the most efficient.
|
||||
func ListEquals(lhs, rhs List) bool {
|
||||
return ListEqualsUsing(HeapAllocator, lhs, rhs)
|
||||
}
|
||||
|
||||
// ListEqualsUsing uses the provided allocator and compares two lists lexically.
|
||||
// WARN: This is a naive implementation, calling lhs.EqualsUsing(allocator, rhs) is typically the most efficient.
|
||||
func ListEqualsUsing(a Allocator, lhs, rhs List) bool {
|
||||
if lhs.Length() != rhs.Length() {
|
||||
return false
|
||||
}
|
||||
|
||||
lhsRange := lhs.RangeUsing(a)
|
||||
defer a.Free(lhsRange)
|
||||
rhsRange := rhs.RangeUsing(a)
|
||||
defer a.Free(rhsRange)
|
||||
|
||||
for lhsRange.Next() && rhsRange.Next() {
|
||||
_, lv := lhsRange.Item()
|
||||
_, rv := rhsRange.Item()
|
||||
if !EqualsUsing(a, lv, rv) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ListLess compares two lists lexically.
|
||||
func ListLess(lhs, rhs List) bool {
|
||||
return ListCompare(lhs, rhs) == -1
|
||||
}
|
||||
|
||||
// ListCompare compares two lists lexically. The result will be 0 if l==rhs, -1
|
||||
// if l < rhs, and +1 if l > rhs.
|
||||
func ListCompare(lhs, rhs List) int {
|
||||
return ListCompareUsing(HeapAllocator, lhs, rhs)
|
||||
}
|
||||
|
||||
// ListCompareUsing uses the provided allocator and compares two lists lexically. The result will be 0 if l==rhs, -1
|
||||
// if l < rhs, and +1 if l > rhs.
|
||||
func ListCompareUsing(a Allocator, lhs, rhs List) int {
|
||||
lhsRange := lhs.RangeUsing(a)
|
||||
defer a.Free(lhsRange)
|
||||
rhsRange := rhs.RangeUsing(a)
|
||||
defer a.Free(rhsRange)
|
||||
|
||||
for {
|
||||
lhsOk := lhsRange.Next()
|
||||
rhsOk := rhsRange.Next()
|
||||
if !lhsOk && !rhsOk {
|
||||
// Lists are the same length and all items are equal.
|
||||
return 0
|
||||
}
|
||||
if !lhsOk {
|
||||
// LHS is shorter.
|
||||
return -1
|
||||
}
|
||||
if !rhsOk {
|
||||
// RHS is shorter.
|
||||
return 1
|
||||
}
|
||||
_, lv := lhsRange.Item()
|
||||
_, rv := rhsRange.Item()
|
||||
if c := CompareUsing(a, lv, rv); c != 0 {
|
||||
return c
|
||||
}
|
||||
// The items are equal; continue.
|
||||
}
|
||||
}
|
||||
98
vendor/sigs.k8s.io/structured-merge-diff/v3/value/listreflect.go
generated
vendored
Normal file
98
vendor/sigs.k8s.io/structured-merge-diff/v3/value/listreflect.go
generated
vendored
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
Copyright 2019 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 value
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type listReflect struct {
|
||||
Value reflect.Value
|
||||
}
|
||||
|
||||
func (r listReflect) Length() int {
|
||||
val := r.Value
|
||||
return val.Len()
|
||||
}
|
||||
|
||||
func (r listReflect) At(i int) Value {
|
||||
val := r.Value
|
||||
return mustWrapValueReflect(val.Index(i), nil, nil)
|
||||
}
|
||||
|
||||
func (r listReflect) AtUsing(a Allocator, i int) Value {
|
||||
val := r.Value
|
||||
return a.allocValueReflect().mustReuse(val.Index(i), nil, nil, nil)
|
||||
}
|
||||
|
||||
func (r listReflect) Unstructured() interface{} {
|
||||
l := r.Length()
|
||||
result := make([]interface{}, l)
|
||||
for i := 0; i < l; i++ {
|
||||
result[i] = r.At(i).Unstructured()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (r listReflect) Range() ListRange {
|
||||
return r.RangeUsing(HeapAllocator)
|
||||
}
|
||||
|
||||
func (r listReflect) RangeUsing(a Allocator) ListRange {
|
||||
length := r.Value.Len()
|
||||
if length == 0 {
|
||||
return EmptyRange
|
||||
}
|
||||
rr := a.allocListReflectRange()
|
||||
rr.list = r.Value
|
||||
rr.i = -1
|
||||
rr.entry = TypeReflectEntryOf(r.Value.Type().Elem())
|
||||
return rr
|
||||
}
|
||||
|
||||
func (r listReflect) Equals(other List) bool {
|
||||
return r.EqualsUsing(HeapAllocator, other)
|
||||
}
|
||||
func (r listReflect) EqualsUsing(a Allocator, other List) bool {
|
||||
if otherReflectList, ok := other.(*listReflect); ok {
|
||||
return reflect.DeepEqual(r.Value.Interface(), otherReflectList.Value.Interface())
|
||||
}
|
||||
return ListEqualsUsing(a, &r, other)
|
||||
}
|
||||
|
||||
type listReflectRange struct {
|
||||
list reflect.Value
|
||||
vr *valueReflect
|
||||
i int
|
||||
entry *TypeReflectCacheEntry
|
||||
}
|
||||
|
||||
func (r *listReflectRange) Next() bool {
|
||||
r.i += 1
|
||||
return r.i < r.list.Len()
|
||||
}
|
||||
|
||||
func (r *listReflectRange) Item() (index int, value Value) {
|
||||
if r.i < 0 {
|
||||
panic("Item() called before first calling Next()")
|
||||
}
|
||||
if r.i >= r.list.Len() {
|
||||
panic("Item() called on ListRange with no more items")
|
||||
}
|
||||
v := r.list.Index(r.i)
|
||||
return r.i, r.vr.mustReuse(v, r.entry, nil, nil)
|
||||
}
|
||||
74
vendor/sigs.k8s.io/structured-merge-diff/v3/value/listunstructured.go
generated
vendored
Normal file
74
vendor/sigs.k8s.io/structured-merge-diff/v3/value/listunstructured.go
generated
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
Copyright 2019 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 value
|
||||
|
||||
type listUnstructured []interface{}
|
||||
|
||||
func (l listUnstructured) Length() int {
|
||||
return len(l)
|
||||
}
|
||||
|
||||
func (l listUnstructured) At(i int) Value {
|
||||
return NewValueInterface(l[i])
|
||||
}
|
||||
|
||||
func (l listUnstructured) AtUsing(a Allocator, i int) Value {
|
||||
return a.allocValueUnstructured().reuse(l[i])
|
||||
}
|
||||
|
||||
func (l listUnstructured) Equals(other List) bool {
|
||||
return l.EqualsUsing(HeapAllocator, other)
|
||||
}
|
||||
|
||||
func (l listUnstructured) EqualsUsing(a Allocator, other List) bool {
|
||||
return ListEqualsUsing(a, &l, other)
|
||||
}
|
||||
|
||||
func (l listUnstructured) Range() ListRange {
|
||||
return l.RangeUsing(HeapAllocator)
|
||||
}
|
||||
|
||||
func (l listUnstructured) RangeUsing(a Allocator) ListRange {
|
||||
if len(l) == 0 {
|
||||
return EmptyRange
|
||||
}
|
||||
r := a.allocListUnstructuredRange()
|
||||
r.list = l
|
||||
r.i = -1
|
||||
return r
|
||||
}
|
||||
|
||||
type listUnstructuredRange struct {
|
||||
list listUnstructured
|
||||
vv *valueUnstructured
|
||||
i int
|
||||
}
|
||||
|
||||
func (r *listUnstructuredRange) Next() bool {
|
||||
r.i += 1
|
||||
return r.i < len(r.list)
|
||||
}
|
||||
|
||||
func (r *listUnstructuredRange) Item() (index int, value Value) {
|
||||
if r.i < 0 {
|
||||
panic("Item() called before first calling Next()")
|
||||
}
|
||||
if r.i >= len(r.list) {
|
||||
panic("Item() called on ListRange with no more items")
|
||||
}
|
||||
return r.i, r.vv.reuse(r.list[r.i])
|
||||
}
|
||||
270
vendor/sigs.k8s.io/structured-merge-diff/v3/value/map.go
generated
vendored
Normal file
270
vendor/sigs.k8s.io/structured-merge-diff/v3/value/map.go
generated
vendored
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
Copyright 2019 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 value
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Map represents a Map or go structure.
|
||||
type Map interface {
|
||||
// Set changes or set the value of the given key.
|
||||
Set(key string, val Value)
|
||||
// Get returns the value for the given key, if present, or (nil, false) otherwise.
|
||||
Get(key string) (Value, bool)
|
||||
// GetUsing uses the provided allocator and returns the value for the given key,
|
||||
// if present, or (nil, false) otherwise.
|
||||
// The returned Value should be given back to the Allocator when no longer needed
|
||||
// by calling Allocator.Free(Value).
|
||||
GetUsing(a Allocator, key string) (Value, bool)
|
||||
// Has returns true if the key is present, or false otherwise.
|
||||
Has(key string) bool
|
||||
// Delete removes the key from the map.
|
||||
Delete(key string)
|
||||
// Equals compares the two maps, and return true if they are the same, false otherwise.
|
||||
// Implementations can use MapEquals as a general implementation for this methods.
|
||||
Equals(other Map) bool
|
||||
// EqualsUsing uses the provided allocator and compares the two maps, and return true if
|
||||
// they are the same, false otherwise. Implementations can use MapEqualsUsing as a general
|
||||
// implementation for this methods.
|
||||
EqualsUsing(a Allocator, other Map) bool
|
||||
// Iterate runs the given function for each key/value in the
|
||||
// map. Returning false in the closure prematurely stops the
|
||||
// iteration.
|
||||
Iterate(func(key string, value Value) bool) bool
|
||||
// IterateUsing uses the provided allocator and runs the given function for each key/value
|
||||
// in the map. Returning false in the closure prematurely stops the iteration.
|
||||
IterateUsing(Allocator, func(key string, value Value) bool) bool
|
||||
// Length returns the number of items in the map.
|
||||
Length() int
|
||||
// Empty returns true if the map is empty.
|
||||
Empty() bool
|
||||
// Zip iterates over the entries of two maps together. If both maps contain a value for a given key, fn is called
|
||||
// with the values from both maps, otherwise it is called with the value of the map that contains the key and nil
|
||||
// for the map that does not contain the key. Returning false in the closure prematurely stops the iteration.
|
||||
Zip(other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool
|
||||
// ZipUsing uses the provided allocator and iterates over the entries of two maps together. If both maps
|
||||
// contain a value for a given key, fn is called with the values from both maps, otherwise it is called with
|
||||
// the value of the map that contains the key and nil for the map that does not contain the key. Returning
|
||||
// false in the closure prematurely stops the iteration.
|
||||
ZipUsing(a Allocator, other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool
|
||||
}
|
||||
|
||||
// MapTraverseOrder defines the map traversal ordering available.
|
||||
type MapTraverseOrder int
|
||||
|
||||
const (
|
||||
// Unordered indicates that the map traversal has no ordering requirement.
|
||||
Unordered = iota
|
||||
// LexicalKeyOrder indicates that the map traversal is ordered by key, lexically.
|
||||
LexicalKeyOrder
|
||||
)
|
||||
|
||||
// MapZip iterates over the entries of two maps together. If both maps contain a value for a given key, fn is called
|
||||
// with the values from both maps, otherwise it is called with the value of the map that contains the key and nil
|
||||
// for the other map. Returning false in the closure prematurely stops the iteration.
|
||||
func MapZip(lhs, rhs Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
return MapZipUsing(HeapAllocator, lhs, rhs, order, fn)
|
||||
}
|
||||
|
||||
// MapZipUsing uses the provided allocator and iterates over the entries of two maps together. If both maps
|
||||
// contain a value for a given key, fn is called with the values from both maps, otherwise it is called with
|
||||
// the value of the map that contains the key and nil for the other map. Returning false in the closure
|
||||
// prematurely stops the iteration.
|
||||
func MapZipUsing(a Allocator, lhs, rhs Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
if lhs != nil {
|
||||
return lhs.ZipUsing(a, rhs, order, fn)
|
||||
}
|
||||
if rhs != nil {
|
||||
return rhs.ZipUsing(a, lhs, order, func(key string, rhs, lhs Value) bool { // arg positions of lhs and rhs deliberately swapped
|
||||
return fn(key, lhs, rhs)
|
||||
})
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// defaultMapZip provides a default implementation of Zip for implementations that do not need to provide
|
||||
// their own optimized implementation.
|
||||
func defaultMapZip(a Allocator, lhs, rhs Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
switch order {
|
||||
case Unordered:
|
||||
return unorderedMapZip(a, lhs, rhs, fn)
|
||||
case LexicalKeyOrder:
|
||||
return lexicalKeyOrderedMapZip(a, lhs, rhs, fn)
|
||||
default:
|
||||
panic("Unsupported map order")
|
||||
}
|
||||
}
|
||||
|
||||
func unorderedMapZip(a Allocator, lhs, rhs Map, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
if (lhs == nil || lhs.Empty()) && (rhs == nil || rhs.Empty()) {
|
||||
return true
|
||||
}
|
||||
|
||||
if lhs != nil {
|
||||
ok := lhs.IterateUsing(a, func(key string, lhsValue Value) bool {
|
||||
var rhsValue Value
|
||||
if rhs != nil {
|
||||
if item, ok := rhs.GetUsing(a, key); ok {
|
||||
rhsValue = item
|
||||
defer a.Free(rhsValue)
|
||||
}
|
||||
}
|
||||
return fn(key, lhsValue, rhsValue)
|
||||
})
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if rhs != nil {
|
||||
return rhs.IterateUsing(a, func(key string, rhsValue Value) bool {
|
||||
if lhs == nil || !lhs.Has(key) {
|
||||
return fn(key, nil, rhsValue)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func lexicalKeyOrderedMapZip(a Allocator, lhs, rhs Map, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
var lhsLength, rhsLength int
|
||||
var orderedLength int // rough estimate of length of union of map keys
|
||||
if lhs != nil {
|
||||
lhsLength = lhs.Length()
|
||||
orderedLength = lhsLength
|
||||
}
|
||||
if rhs != nil {
|
||||
rhsLength = rhs.Length()
|
||||
if rhsLength > orderedLength {
|
||||
orderedLength = rhsLength
|
||||
}
|
||||
}
|
||||
if lhsLength == 0 && rhsLength == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
ordered := make([]string, 0, orderedLength)
|
||||
if lhs != nil {
|
||||
lhs.IterateUsing(a, func(key string, _ Value) bool {
|
||||
ordered = append(ordered, key)
|
||||
return true
|
||||
})
|
||||
}
|
||||
if rhs != nil {
|
||||
rhs.IterateUsing(a, func(key string, _ Value) bool {
|
||||
if lhs == nil || !lhs.Has(key) {
|
||||
ordered = append(ordered, key)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
sort.Strings(ordered)
|
||||
for _, key := range ordered {
|
||||
var litem, ritem Value
|
||||
if lhs != nil {
|
||||
litem, _ = lhs.GetUsing(a, key)
|
||||
}
|
||||
if rhs != nil {
|
||||
ritem, _ = rhs.GetUsing(a, key)
|
||||
}
|
||||
ok := fn(key, litem, ritem)
|
||||
if litem != nil {
|
||||
a.Free(litem)
|
||||
}
|
||||
if ritem != nil {
|
||||
a.Free(ritem)
|
||||
}
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// MapLess compares two maps lexically.
|
||||
func MapLess(lhs, rhs Map) bool {
|
||||
return MapCompare(lhs, rhs) == -1
|
||||
}
|
||||
|
||||
// MapCompare compares two maps lexically.
|
||||
func MapCompare(lhs, rhs Map) int {
|
||||
return MapCompareUsing(HeapAllocator, lhs, rhs)
|
||||
}
|
||||
|
||||
// MapCompareUsing uses the provided allocator and compares two maps lexically.
|
||||
func MapCompareUsing(a Allocator, lhs, rhs Map) int {
|
||||
c := 0
|
||||
var llength, rlength int
|
||||
if lhs != nil {
|
||||
llength = lhs.Length()
|
||||
}
|
||||
if rhs != nil {
|
||||
rlength = rhs.Length()
|
||||
}
|
||||
if llength == 0 && rlength == 0 {
|
||||
return 0
|
||||
}
|
||||
i := 0
|
||||
MapZipUsing(a, lhs, rhs, LexicalKeyOrder, func(key string, lhs, rhs Value) bool {
|
||||
switch {
|
||||
case i == llength:
|
||||
c = -1
|
||||
case i == rlength:
|
||||
c = 1
|
||||
case lhs == nil:
|
||||
c = 1
|
||||
case rhs == nil:
|
||||
c = -1
|
||||
default:
|
||||
c = CompareUsing(a, lhs, rhs)
|
||||
}
|
||||
i++
|
||||
return c == 0
|
||||
})
|
||||
return c
|
||||
}
|
||||
|
||||
// MapEquals returns true if lhs == rhs, false otherwise. This function
|
||||
// acts on generic types and should not be used by callers, but can help
|
||||
// implement Map.Equals.
|
||||
// WARN: This is a naive implementation, calling lhs.Equals(rhs) is typically the most efficient.
|
||||
func MapEquals(lhs, rhs Map) bool {
|
||||
return MapEqualsUsing(HeapAllocator, lhs, rhs)
|
||||
}
|
||||
|
||||
// MapEqualsUsing uses the provided allocator and returns true if lhs == rhs,
|
||||
// false otherwise. This function acts on generic types and should not be used
|
||||
// by callers, but can help implement Map.Equals.
|
||||
// WARN: This is a naive implementation, calling lhs.EqualsUsing(allocator, rhs) is typically the most efficient.
|
||||
func MapEqualsUsing(a Allocator, lhs, rhs Map) bool {
|
||||
if lhs == nil && rhs == nil {
|
||||
return true
|
||||
}
|
||||
if lhs == nil || rhs == nil {
|
||||
return false
|
||||
}
|
||||
if lhs.Length() != rhs.Length() {
|
||||
return false
|
||||
}
|
||||
return MapZipUsing(a, lhs, rhs, Unordered, func(key string, lhs, rhs Value) bool {
|
||||
if lhs == nil || rhs == nil {
|
||||
return false
|
||||
}
|
||||
return EqualsUsing(a, lhs, rhs)
|
||||
})
|
||||
}
|
||||
209
vendor/sigs.k8s.io/structured-merge-diff/v3/value/mapreflect.go
generated
vendored
Normal file
209
vendor/sigs.k8s.io/structured-merge-diff/v3/value/mapreflect.go
generated
vendored
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
/*
|
||||
Copyright 2019 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 value
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type mapReflect struct {
|
||||
valueReflect
|
||||
}
|
||||
|
||||
func (r mapReflect) Length() int {
|
||||
val := r.Value
|
||||
return val.Len()
|
||||
}
|
||||
|
||||
func (r mapReflect) Empty() bool {
|
||||
val := r.Value
|
||||
return val.Len() == 0
|
||||
}
|
||||
|
||||
func (r mapReflect) Get(key string) (Value, bool) {
|
||||
return r.GetUsing(HeapAllocator, key)
|
||||
}
|
||||
|
||||
func (r mapReflect) GetUsing(a Allocator, key string) (Value, bool) {
|
||||
k, v, ok := r.get(key)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
return a.allocValueReflect().mustReuse(v, nil, &r.Value, &k), true
|
||||
}
|
||||
|
||||
func (r mapReflect) get(k string) (key, value reflect.Value, ok bool) {
|
||||
mapKey := r.toMapKey(k)
|
||||
val := r.Value.MapIndex(mapKey)
|
||||
return mapKey, val, val.IsValid() && val != reflect.Value{}
|
||||
}
|
||||
|
||||
func (r mapReflect) Has(key string) bool {
|
||||
var val reflect.Value
|
||||
val = r.Value.MapIndex(r.toMapKey(key))
|
||||
if !val.IsValid() {
|
||||
return false
|
||||
}
|
||||
return val != reflect.Value{}
|
||||
}
|
||||
|
||||
func (r mapReflect) Set(key string, val Value) {
|
||||
r.Value.SetMapIndex(r.toMapKey(key), reflect.ValueOf(val.Unstructured()))
|
||||
}
|
||||
|
||||
func (r mapReflect) Delete(key string) {
|
||||
val := r.Value
|
||||
val.SetMapIndex(r.toMapKey(key), reflect.Value{})
|
||||
}
|
||||
|
||||
// TODO: Do we need to support types that implement json.Marshaler and are used as string keys?
|
||||
func (r mapReflect) toMapKey(key string) reflect.Value {
|
||||
val := r.Value
|
||||
return reflect.ValueOf(key).Convert(val.Type().Key())
|
||||
}
|
||||
|
||||
func (r mapReflect) Iterate(fn func(string, Value) bool) bool {
|
||||
return r.IterateUsing(HeapAllocator, fn)
|
||||
}
|
||||
|
||||
func (r mapReflect) IterateUsing(a Allocator, fn func(string, Value) bool) bool {
|
||||
if r.Value.Len() == 0 {
|
||||
return true
|
||||
}
|
||||
v := a.allocValueReflect()
|
||||
defer a.Free(v)
|
||||
return eachMapEntry(r.Value, func(e *TypeReflectCacheEntry, key reflect.Value, value reflect.Value) bool {
|
||||
return fn(key.String(), v.mustReuse(value, e, &r.Value, &key))
|
||||
})
|
||||
}
|
||||
|
||||
func eachMapEntry(val reflect.Value, fn func(*TypeReflectCacheEntry, reflect.Value, reflect.Value) bool) bool {
|
||||
iter := val.MapRange()
|
||||
entry := TypeReflectEntryOf(val.Type().Elem())
|
||||
for iter.Next() {
|
||||
next := iter.Value()
|
||||
if !next.IsValid() {
|
||||
continue
|
||||
}
|
||||
if !fn(entry, iter.Key(), next) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (r mapReflect) Unstructured() interface{} {
|
||||
result := make(map[string]interface{}, r.Length())
|
||||
r.Iterate(func(s string, value Value) bool {
|
||||
result[s] = value.Unstructured()
|
||||
return true
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
func (r mapReflect) Equals(m Map) bool {
|
||||
return r.EqualsUsing(HeapAllocator, m)
|
||||
}
|
||||
|
||||
func (r mapReflect) EqualsUsing(a Allocator, m Map) bool {
|
||||
lhsLength := r.Length()
|
||||
rhsLength := m.Length()
|
||||
if lhsLength != rhsLength {
|
||||
return false
|
||||
}
|
||||
if lhsLength == 0 {
|
||||
return true
|
||||
}
|
||||
vr := a.allocValueReflect()
|
||||
defer a.Free(vr)
|
||||
entry := TypeReflectEntryOf(r.Value.Type().Elem())
|
||||
return m.Iterate(func(key string, value Value) bool {
|
||||
_, lhsVal, ok := r.get(key)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return Equals(vr.mustReuse(lhsVal, entry, nil, nil), value)
|
||||
})
|
||||
}
|
||||
|
||||
func (r mapReflect) Zip(other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
return r.ZipUsing(HeapAllocator, other, order, fn)
|
||||
}
|
||||
|
||||
func (r mapReflect) ZipUsing(a Allocator, other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
if otherMapReflect, ok := other.(*mapReflect); ok && order == Unordered {
|
||||
return r.unorderedReflectZip(a, otherMapReflect, fn)
|
||||
}
|
||||
return defaultMapZip(a, &r, other, order, fn)
|
||||
}
|
||||
|
||||
// unorderedReflectZip provides an optimized unordered zip for mapReflect types.
|
||||
func (r mapReflect) unorderedReflectZip(a Allocator, other *mapReflect, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
if r.Empty() && (other == nil || other.Empty()) {
|
||||
return true
|
||||
}
|
||||
|
||||
lhs := r.Value
|
||||
lhsEntry := TypeReflectEntryOf(lhs.Type().Elem())
|
||||
|
||||
// map lookup via reflection is expensive enough that it is better to keep track of visited keys
|
||||
visited := map[string]struct{}{}
|
||||
|
||||
vlhs, vrhs := a.allocValueReflect(), a.allocValueReflect()
|
||||
defer a.Free(vlhs)
|
||||
defer a.Free(vrhs)
|
||||
|
||||
if other != nil {
|
||||
rhs := other.Value
|
||||
rhsEntry := TypeReflectEntryOf(rhs.Type().Elem())
|
||||
iter := rhs.MapRange()
|
||||
|
||||
for iter.Next() {
|
||||
key := iter.Key()
|
||||
keyString := key.String()
|
||||
next := iter.Value()
|
||||
if !next.IsValid() {
|
||||
continue
|
||||
}
|
||||
rhsVal := vrhs.mustReuse(next, rhsEntry, &rhs, &key)
|
||||
visited[keyString] = struct{}{}
|
||||
var lhsVal Value
|
||||
if _, v, ok := r.get(keyString); ok {
|
||||
lhsVal = vlhs.mustReuse(v, lhsEntry, &lhs, &key)
|
||||
}
|
||||
if !fn(keyString, lhsVal, rhsVal) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iter := lhs.MapRange()
|
||||
for iter.Next() {
|
||||
key := iter.Key()
|
||||
if _, ok := visited[key.String()]; ok {
|
||||
continue
|
||||
}
|
||||
next := iter.Value()
|
||||
if !next.IsValid() {
|
||||
continue
|
||||
}
|
||||
if !fn(key.String(), vlhs.mustReuse(next, lhsEntry, &lhs, &key), nil) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
190
vendor/sigs.k8s.io/structured-merge-diff/v3/value/mapunstructured.go
generated
vendored
Normal file
190
vendor/sigs.k8s.io/structured-merge-diff/v3/value/mapunstructured.go
generated
vendored
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
Copyright 2019 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 value
|
||||
|
||||
type mapUnstructuredInterface map[interface{}]interface{}
|
||||
|
||||
func (m mapUnstructuredInterface) Set(key string, val Value) {
|
||||
m[key] = val.Unstructured()
|
||||
}
|
||||
|
||||
func (m mapUnstructuredInterface) Get(key string) (Value, bool) {
|
||||
return m.GetUsing(HeapAllocator, key)
|
||||
}
|
||||
|
||||
func (m mapUnstructuredInterface) GetUsing(a Allocator, key string) (Value, bool) {
|
||||
if v, ok := m[key]; !ok {
|
||||
return nil, false
|
||||
} else {
|
||||
return a.allocValueUnstructured().reuse(v), true
|
||||
}
|
||||
}
|
||||
|
||||
func (m mapUnstructuredInterface) Has(key string) bool {
|
||||
_, ok := m[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (m mapUnstructuredInterface) Delete(key string) {
|
||||
delete(m, key)
|
||||
}
|
||||
|
||||
func (m mapUnstructuredInterface) Iterate(fn func(key string, value Value) bool) bool {
|
||||
return m.IterateUsing(HeapAllocator, fn)
|
||||
}
|
||||
|
||||
func (m mapUnstructuredInterface) IterateUsing(a Allocator, fn func(key string, value Value) bool) bool {
|
||||
if len(m) == 0 {
|
||||
return true
|
||||
}
|
||||
vv := a.allocValueUnstructured()
|
||||
defer a.Free(vv)
|
||||
for k, v := range m {
|
||||
if ks, ok := k.(string); !ok {
|
||||
continue
|
||||
} else {
|
||||
if !fn(ks, vv.reuse(v)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (m mapUnstructuredInterface) Length() int {
|
||||
return len(m)
|
||||
}
|
||||
|
||||
func (m mapUnstructuredInterface) Empty() bool {
|
||||
return len(m) == 0
|
||||
}
|
||||
|
||||
func (m mapUnstructuredInterface) Equals(other Map) bool {
|
||||
return m.EqualsUsing(HeapAllocator, other)
|
||||
}
|
||||
|
||||
func (m mapUnstructuredInterface) EqualsUsing(a Allocator, other Map) bool {
|
||||
lhsLength := m.Length()
|
||||
rhsLength := other.Length()
|
||||
if lhsLength != rhsLength {
|
||||
return false
|
||||
}
|
||||
if lhsLength == 0 {
|
||||
return true
|
||||
}
|
||||
vv := a.allocValueUnstructured()
|
||||
defer a.Free(vv)
|
||||
return other.Iterate(func(key string, value Value) bool {
|
||||
lhsVal, ok := m[key]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return Equals(vv.reuse(lhsVal), value)
|
||||
})
|
||||
}
|
||||
|
||||
func (m mapUnstructuredInterface) Zip(other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
return m.ZipUsing(HeapAllocator, other, order, fn)
|
||||
}
|
||||
|
||||
func (m mapUnstructuredInterface) ZipUsing(a Allocator, other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
return defaultMapZip(a, m, other, order, fn)
|
||||
}
|
||||
|
||||
type mapUnstructuredString map[string]interface{}
|
||||
|
||||
func (m mapUnstructuredString) Set(key string, val Value) {
|
||||
m[key] = val.Unstructured()
|
||||
}
|
||||
|
||||
func (m mapUnstructuredString) Get(key string) (Value, bool) {
|
||||
return m.GetUsing(HeapAllocator, key)
|
||||
}
|
||||
func (m mapUnstructuredString) GetUsing(a Allocator, key string) (Value, bool) {
|
||||
if v, ok := m[key]; !ok {
|
||||
return nil, false
|
||||
} else {
|
||||
return a.allocValueUnstructured().reuse(v), true
|
||||
}
|
||||
}
|
||||
|
||||
func (m mapUnstructuredString) Has(key string) bool {
|
||||
_, ok := m[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (m mapUnstructuredString) Delete(key string) {
|
||||
delete(m, key)
|
||||
}
|
||||
|
||||
func (m mapUnstructuredString) Iterate(fn func(key string, value Value) bool) bool {
|
||||
return m.IterateUsing(HeapAllocator, fn)
|
||||
}
|
||||
|
||||
func (m mapUnstructuredString) IterateUsing(a Allocator, fn func(key string, value Value) bool) bool {
|
||||
if len(m) == 0 {
|
||||
return true
|
||||
}
|
||||
vv := a.allocValueUnstructured()
|
||||
defer a.Free(vv)
|
||||
for k, v := range m {
|
||||
if !fn(k, vv.reuse(v)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (m mapUnstructuredString) Length() int {
|
||||
return len(m)
|
||||
}
|
||||
|
||||
func (m mapUnstructuredString) Equals(other Map) bool {
|
||||
return m.EqualsUsing(HeapAllocator, other)
|
||||
}
|
||||
|
||||
func (m mapUnstructuredString) EqualsUsing(a Allocator, other Map) bool {
|
||||
lhsLength := m.Length()
|
||||
rhsLength := other.Length()
|
||||
if lhsLength != rhsLength {
|
||||
return false
|
||||
}
|
||||
if lhsLength == 0 {
|
||||
return true
|
||||
}
|
||||
vv := a.allocValueUnstructured()
|
||||
defer a.Free(vv)
|
||||
return other.Iterate(func(key string, value Value) bool {
|
||||
lhsVal, ok := m[key]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return Equals(vv.reuse(lhsVal), value)
|
||||
})
|
||||
}
|
||||
|
||||
func (m mapUnstructuredString) Zip(other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
return m.ZipUsing(HeapAllocator, other, order, fn)
|
||||
}
|
||||
|
||||
func (m mapUnstructuredString) ZipUsing(a Allocator, other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
return defaultMapZip(a, m, other, order, fn)
|
||||
}
|
||||
|
||||
func (m mapUnstructuredString) Empty() bool {
|
||||
return len(m) == 0
|
||||
}
|
||||
463
vendor/sigs.k8s.io/structured-merge-diff/v3/value/reflectcache.go
generated
vendored
Normal file
463
vendor/sigs.k8s.io/structured-merge-diff/v3/value/reflectcache.go
generated
vendored
Normal file
|
|
@ -0,0 +1,463 @@
|
|||
/*
|
||||
Copyright 2020 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 value
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// UnstructuredConverter defines how a type can be converted directly to unstructured.
|
||||
// Types that implement json.Marshaler may also optionally implement this interface to provide a more
|
||||
// direct and more efficient conversion. All types that choose to implement this interface must still
|
||||
// implement this same conversion via json.Marshaler.
|
||||
type UnstructuredConverter interface {
|
||||
json.Marshaler // require that json.Marshaler is implemented
|
||||
|
||||
// ToUnstructured returns the unstructured representation.
|
||||
ToUnstructured() interface{}
|
||||
}
|
||||
|
||||
// TypeReflectCacheEntry keeps data gathered using reflection about how a type is converted to/from unstructured.
|
||||
type TypeReflectCacheEntry struct {
|
||||
isJsonMarshaler bool
|
||||
ptrIsJsonMarshaler bool
|
||||
isJsonUnmarshaler bool
|
||||
ptrIsJsonUnmarshaler bool
|
||||
isStringConvertable bool
|
||||
ptrIsStringConvertable bool
|
||||
|
||||
structFields map[string]*FieldCacheEntry
|
||||
orderedStructFields []*FieldCacheEntry
|
||||
}
|
||||
|
||||
// FieldCacheEntry keeps data gathered using reflection about how the field of a struct is converted to/from
|
||||
// unstructured.
|
||||
type FieldCacheEntry struct {
|
||||
// JsonName returns the name of the field according to the json tags on the struct field.
|
||||
JsonName string
|
||||
// isOmitEmpty is true if the field has the json 'omitempty' tag.
|
||||
isOmitEmpty bool
|
||||
// fieldPath is a list of field indices (see FieldByIndex) to lookup the value of
|
||||
// a field in a reflect.Value struct. The field indices in the list form a path used
|
||||
// to traverse through intermediary 'inline' fields.
|
||||
fieldPath [][]int
|
||||
|
||||
fieldType reflect.Type
|
||||
TypeEntry *TypeReflectCacheEntry
|
||||
}
|
||||
|
||||
func (f *FieldCacheEntry) CanOmit(fieldVal reflect.Value) bool {
|
||||
return f.isOmitEmpty && (safeIsNil(fieldVal) || isZero(fieldVal))
|
||||
}
|
||||
|
||||
// GetUsing returns the field identified by this FieldCacheEntry from the provided struct.
|
||||
func (f *FieldCacheEntry) GetFrom(structVal reflect.Value) reflect.Value {
|
||||
// field might be nested within 'inline' structs
|
||||
for _, elem := range f.fieldPath {
|
||||
structVal = structVal.FieldByIndex(elem)
|
||||
}
|
||||
return structVal
|
||||
}
|
||||
|
||||
var marshalerType = reflect.TypeOf(new(json.Marshaler)).Elem()
|
||||
var unmarshalerType = reflect.TypeOf(new(json.Unmarshaler)).Elem()
|
||||
var unstructuredConvertableType = reflect.TypeOf(new(UnstructuredConverter)).Elem()
|
||||
var defaultReflectCache = newReflectCache()
|
||||
|
||||
// TypeReflectEntryOf returns the TypeReflectCacheEntry of the provided reflect.Type.
|
||||
func TypeReflectEntryOf(t reflect.Type) *TypeReflectCacheEntry {
|
||||
cm := defaultReflectCache.get()
|
||||
if record, ok := cm[t]; ok {
|
||||
return record
|
||||
}
|
||||
updates := reflectCacheMap{}
|
||||
result := typeReflectEntryOf(cm, t, updates)
|
||||
if len(updates) > 0 {
|
||||
defaultReflectCache.update(updates)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// TypeReflectEntryOf returns all updates needed to add provided reflect.Type, and the types its fields transitively
|
||||
// depend on, to the cache.
|
||||
func typeReflectEntryOf(cm reflectCacheMap, t reflect.Type, updates reflectCacheMap) *TypeReflectCacheEntry {
|
||||
if record, ok := cm[t]; ok {
|
||||
return record
|
||||
}
|
||||
if record, ok := updates[t]; ok {
|
||||
return record
|
||||
}
|
||||
typeEntry := &TypeReflectCacheEntry{
|
||||
isJsonMarshaler: t.Implements(marshalerType),
|
||||
ptrIsJsonMarshaler: reflect.PtrTo(t).Implements(marshalerType),
|
||||
isJsonUnmarshaler: reflect.PtrTo(t).Implements(unmarshalerType),
|
||||
isStringConvertable: t.Implements(unstructuredConvertableType),
|
||||
ptrIsStringConvertable: reflect.PtrTo(t).Implements(unstructuredConvertableType),
|
||||
}
|
||||
if t.Kind() == reflect.Struct {
|
||||
fieldEntries := map[string]*FieldCacheEntry{}
|
||||
buildStructCacheEntry(t, fieldEntries, nil)
|
||||
typeEntry.structFields = fieldEntries
|
||||
sortedByJsonName := make([]*FieldCacheEntry, len(fieldEntries))
|
||||
i := 0
|
||||
for _, entry := range fieldEntries {
|
||||
sortedByJsonName[i] = entry
|
||||
i++
|
||||
}
|
||||
sort.Slice(sortedByJsonName, func(i, j int) bool {
|
||||
return sortedByJsonName[i].JsonName < sortedByJsonName[j].JsonName
|
||||
})
|
||||
typeEntry.orderedStructFields = sortedByJsonName
|
||||
}
|
||||
|
||||
// cyclic type references are allowed, so we must add the typeEntry to the updates map before resolving
|
||||
// the field.typeEntry references, or creating them if they are not already in the cache
|
||||
updates[t] = typeEntry
|
||||
|
||||
for _, field := range typeEntry.structFields {
|
||||
if field.TypeEntry == nil {
|
||||
field.TypeEntry = typeReflectEntryOf(cm, field.fieldType, updates)
|
||||
}
|
||||
}
|
||||
return typeEntry
|
||||
}
|
||||
|
||||
func buildStructCacheEntry(t reflect.Type, infos map[string]*FieldCacheEntry, fieldPath [][]int) {
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
jsonName, omit, isInline, isOmitempty := lookupJsonTags(field)
|
||||
if omit {
|
||||
continue
|
||||
}
|
||||
if isInline {
|
||||
buildStructCacheEntry(field.Type, infos, append(fieldPath, field.Index))
|
||||
continue
|
||||
}
|
||||
info := &FieldCacheEntry{JsonName: jsonName, isOmitEmpty: isOmitempty, fieldPath: append(fieldPath, field.Index), fieldType: field.Type}
|
||||
infos[jsonName] = info
|
||||
}
|
||||
}
|
||||
|
||||
// Fields returns a map of JSON field name to FieldCacheEntry for structs, or nil for non-structs.
|
||||
func (e TypeReflectCacheEntry) Fields() map[string]*FieldCacheEntry {
|
||||
return e.structFields
|
||||
}
|
||||
|
||||
// Fields returns a map of JSON field name to FieldCacheEntry for structs, or nil for non-structs.
|
||||
func (e TypeReflectCacheEntry) OrderedFields() []*FieldCacheEntry {
|
||||
return e.orderedStructFields
|
||||
}
|
||||
|
||||
// CanConvertToUnstructured returns true if this TypeReflectCacheEntry can convert values of its type to unstructured.
|
||||
func (e TypeReflectCacheEntry) CanConvertToUnstructured() bool {
|
||||
return e.isJsonMarshaler || e.ptrIsJsonMarshaler || e.isStringConvertable || e.ptrIsStringConvertable
|
||||
}
|
||||
|
||||
// ToUnstructured converts the provided value to unstructured and returns it.
|
||||
func (e TypeReflectCacheEntry) ToUnstructured(sv reflect.Value) (interface{}, error) {
|
||||
// This is based on https://github.com/kubernetes/kubernetes/blob/82c9e5c814eb7acc6cc0a090c057294d0667ad66/staging/src/k8s.io/apimachinery/pkg/runtime/converter.go#L505
|
||||
// and is intended to replace it.
|
||||
|
||||
// Check if the object has a custom string converter and use it if available, since it is much more efficient
|
||||
// than round tripping through json.
|
||||
if converter, ok := e.getUnstructuredConverter(sv); ok {
|
||||
return converter.ToUnstructured(), nil
|
||||
}
|
||||
// Check if the object has a custom JSON marshaller/unmarshaller.
|
||||
if marshaler, ok := e.getJsonMarshaler(sv); ok {
|
||||
if sv.Kind() == reflect.Ptr && sv.IsNil() {
|
||||
// We're done - we don't need to store anything.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
data, err := marshaler.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch {
|
||||
case len(data) == 0:
|
||||
return nil, fmt.Errorf("error decoding from json: empty value")
|
||||
|
||||
case bytes.Equal(data, nullBytes):
|
||||
// We're done - we don't need to store anything.
|
||||
return nil, nil
|
||||
|
||||
case bytes.Equal(data, trueBytes):
|
||||
return true, nil
|
||||
|
||||
case bytes.Equal(data, falseBytes):
|
||||
return false, nil
|
||||
|
||||
case data[0] == '"':
|
||||
var result string
|
||||
err := unmarshal(data, &result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding string from json: %v", err)
|
||||
}
|
||||
return result, nil
|
||||
|
||||
case data[0] == '{':
|
||||
result := make(map[string]interface{})
|
||||
err := unmarshal(data, &result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding object from json: %v", err)
|
||||
}
|
||||
return result, nil
|
||||
|
||||
case data[0] == '[':
|
||||
result := make([]interface{}, 0)
|
||||
err := unmarshal(data, &result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding array from json: %v", err)
|
||||
}
|
||||
return result, nil
|
||||
|
||||
default:
|
||||
var (
|
||||
resultInt int64
|
||||
resultFloat float64
|
||||
err error
|
||||
)
|
||||
if err = unmarshal(data, &resultInt); err == nil {
|
||||
return resultInt, nil
|
||||
} else if err = unmarshal(data, &resultFloat); err == nil {
|
||||
return resultFloat, nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("error decoding number from json: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("provided type cannot be converted: %v", sv.Type())
|
||||
}
|
||||
|
||||
// CanConvertFromUnstructured returns true if this TypeReflectCacheEntry can convert objects of the type from unstructured.
|
||||
func (e TypeReflectCacheEntry) CanConvertFromUnstructured() bool {
|
||||
return e.isJsonUnmarshaler
|
||||
}
|
||||
|
||||
// FromUnstructured converts the provided source value from unstructured into the provided destination value.
|
||||
func (e TypeReflectCacheEntry) FromUnstructured(sv, dv reflect.Value) error {
|
||||
// TODO: this could be made much more efficient using direct conversions like
|
||||
// UnstructuredConverter.ToUnstructured provides.
|
||||
st := dv.Type()
|
||||
data, err := json.Marshal(sv.Interface())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error encoding %s to json: %v", st.String(), err)
|
||||
}
|
||||
if unmarshaler, ok := e.getJsonUnmarshaler(dv); ok {
|
||||
return unmarshaler.UnmarshalJSON(data)
|
||||
}
|
||||
return fmt.Errorf("unable to unmarshal %v into %v", sv.Type(), dv.Type())
|
||||
}
|
||||
|
||||
var (
|
||||
nullBytes = []byte("null")
|
||||
trueBytes = []byte("true")
|
||||
falseBytes = []byte("false")
|
||||
)
|
||||
|
||||
func (e TypeReflectCacheEntry) getJsonMarshaler(v reflect.Value) (json.Marshaler, bool) {
|
||||
if e.isJsonMarshaler {
|
||||
return v.Interface().(json.Marshaler), true
|
||||
}
|
||||
if e.ptrIsJsonMarshaler {
|
||||
// Check pointer receivers if v is not a pointer
|
||||
if v.Kind() != reflect.Ptr && v.CanAddr() {
|
||||
v = v.Addr()
|
||||
return v.Interface().(json.Marshaler), true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (e TypeReflectCacheEntry) getJsonUnmarshaler(v reflect.Value) (json.Unmarshaler, bool) {
|
||||
if !e.isJsonUnmarshaler {
|
||||
return nil, false
|
||||
}
|
||||
return v.Addr().Interface().(json.Unmarshaler), true
|
||||
}
|
||||
|
||||
func (e TypeReflectCacheEntry) getUnstructuredConverter(v reflect.Value) (UnstructuredConverter, bool) {
|
||||
if e.isStringConvertable {
|
||||
return v.Interface().(UnstructuredConverter), true
|
||||
}
|
||||
if e.ptrIsStringConvertable {
|
||||
// Check pointer receivers if v is not a pointer
|
||||
if v.CanAddr() {
|
||||
v = v.Addr()
|
||||
return v.Interface().(UnstructuredConverter), true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
type typeReflectCache struct {
|
||||
// use an atomic and copy-on-write since there are a fixed (typically very small) number of structs compiled into any
|
||||
// go program using this cache
|
||||
value atomic.Value
|
||||
// mu is held by writers when performing load/modify/store operations on the cache, readers do not need to hold a
|
||||
// read-lock since the atomic value is always read-only
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func newReflectCache() *typeReflectCache {
|
||||
cache := &typeReflectCache{}
|
||||
cache.value.Store(make(reflectCacheMap))
|
||||
return cache
|
||||
}
|
||||
|
||||
type reflectCacheMap map[reflect.Type]*TypeReflectCacheEntry
|
||||
|
||||
// get returns the reflectCacheMap.
|
||||
func (c *typeReflectCache) get() reflectCacheMap {
|
||||
return c.value.Load().(reflectCacheMap)
|
||||
}
|
||||
|
||||
// update merges the provided updates into the cache.
|
||||
func (c *typeReflectCache) update(updates reflectCacheMap) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
currentCacheMap := c.value.Load().(reflectCacheMap)
|
||||
|
||||
hasNewEntries := false
|
||||
for t := range updates {
|
||||
if _, ok := currentCacheMap[t]; !ok {
|
||||
hasNewEntries = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasNewEntries {
|
||||
// Bail if the updates have been set while waiting for lock acquisition.
|
||||
// This is safe since setting entries is idempotent.
|
||||
return
|
||||
}
|
||||
|
||||
newCacheMap := make(reflectCacheMap, len(currentCacheMap)+len(updates))
|
||||
for k, v := range currentCacheMap {
|
||||
newCacheMap[k] = v
|
||||
}
|
||||
for t, update := range updates {
|
||||
newCacheMap[t] = update
|
||||
}
|
||||
c.value.Store(newCacheMap)
|
||||
}
|
||||
|
||||
// Below json Unmarshal is fromk8s.io/apimachinery/pkg/util/json
|
||||
// to handle number conversions as expected by Kubernetes
|
||||
|
||||
// limit recursive depth to prevent stack overflow errors
|
||||
const maxDepth = 10000
|
||||
|
||||
// unmarshal unmarshals the given data
|
||||
// If v is a *map[string]interface{}, numbers are converted to int64 or float64
|
||||
func unmarshal(data []byte, v interface{}) error {
|
||||
switch v := v.(type) {
|
||||
case *map[string]interface{}:
|
||||
// Build a decoder from the given data
|
||||
decoder := json.NewDecoder(bytes.NewBuffer(data))
|
||||
// Preserve numbers, rather than casting to float64 automatically
|
||||
decoder.UseNumber()
|
||||
// Run the decode
|
||||
if err := decoder.Decode(v); err != nil {
|
||||
return err
|
||||
}
|
||||
// If the decode succeeds, post-process the map to convert json.Number objects to int64 or float64
|
||||
return convertMapNumbers(*v, 0)
|
||||
|
||||
case *[]interface{}:
|
||||
// Build a decoder from the given data
|
||||
decoder := json.NewDecoder(bytes.NewBuffer(data))
|
||||
// Preserve numbers, rather than casting to float64 automatically
|
||||
decoder.UseNumber()
|
||||
// Run the decode
|
||||
if err := decoder.Decode(v); err != nil {
|
||||
return err
|
||||
}
|
||||
// If the decode succeeds, post-process the map to convert json.Number objects to int64 or float64
|
||||
return convertSliceNumbers(*v, 0)
|
||||
|
||||
default:
|
||||
return json.Unmarshal(data, v)
|
||||
}
|
||||
}
|
||||
|
||||
// convertMapNumbers traverses the map, converting any json.Number values to int64 or float64.
|
||||
// values which are map[string]interface{} or []interface{} are recursively visited
|
||||
func convertMapNumbers(m map[string]interface{}, depth int) error {
|
||||
if depth > maxDepth {
|
||||
return fmt.Errorf("exceeded max depth of %d", maxDepth)
|
||||
}
|
||||
|
||||
var err error
|
||||
for k, v := range m {
|
||||
switch v := v.(type) {
|
||||
case json.Number:
|
||||
m[k], err = convertNumber(v)
|
||||
case map[string]interface{}:
|
||||
err = convertMapNumbers(v, depth+1)
|
||||
case []interface{}:
|
||||
err = convertSliceNumbers(v, depth+1)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// convertSliceNumbers traverses the slice, converting any json.Number values to int64 or float64.
|
||||
// values which are map[string]interface{} or []interface{} are recursively visited
|
||||
func convertSliceNumbers(s []interface{}, depth int) error {
|
||||
if depth > maxDepth {
|
||||
return fmt.Errorf("exceeded max depth of %d", maxDepth)
|
||||
}
|
||||
|
||||
var err error
|
||||
for i, v := range s {
|
||||
switch v := v.(type) {
|
||||
case json.Number:
|
||||
s[i], err = convertNumber(v)
|
||||
case map[string]interface{}:
|
||||
err = convertMapNumbers(v, depth+1)
|
||||
case []interface{}:
|
||||
err = convertSliceNumbers(v, depth+1)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// convertNumber converts a json.Number to an int64 or float64, or returns an error
|
||||
func convertNumber(n json.Number) (interface{}, error) {
|
||||
// Attempt to convert to an int64 first
|
||||
if i, err := n.Int64(); err == nil {
|
||||
return i, nil
|
||||
}
|
||||
// Return a float64 (default json.Decode() behavior)
|
||||
// An overflow will return an error
|
||||
return n.Float64()
|
||||
}
|
||||
50
vendor/sigs.k8s.io/structured-merge-diff/v3/value/scalar.go
generated
vendored
Normal file
50
vendor/sigs.k8s.io/structured-merge-diff/v3/value/scalar.go
generated
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
Copyright 2019 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 value
|
||||
|
||||
// Compare compares floats. The result will be 0 if lhs==rhs, -1 if f <
|
||||
// rhs, and +1 if f > rhs.
|
||||
func FloatCompare(lhs, rhs float64) int {
|
||||
if lhs > rhs {
|
||||
return 1
|
||||
} else if lhs < rhs {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// IntCompare compares integers. The result will be 0 if i==rhs, -1 if i <
|
||||
// rhs, and +1 if i > rhs.
|
||||
func IntCompare(lhs, rhs int64) int {
|
||||
if lhs > rhs {
|
||||
return 1
|
||||
} else if lhs < rhs {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Compare compares booleans. The result will be 0 if b==rhs, -1 if b <
|
||||
// rhs, and +1 if b > rhs.
|
||||
func BoolCompare(lhs, rhs bool) int {
|
||||
if lhs == rhs {
|
||||
return 0
|
||||
} else if lhs == false {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
}
|
||||
208
vendor/sigs.k8s.io/structured-merge-diff/v3/value/structreflect.go
generated
vendored
Normal file
208
vendor/sigs.k8s.io/structured-merge-diff/v3/value/structreflect.go
generated
vendored
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
Copyright 2019 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 value
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type structReflect struct {
|
||||
valueReflect
|
||||
}
|
||||
|
||||
func (r structReflect) Length() int {
|
||||
i := 0
|
||||
eachStructField(r.Value, func(_ *TypeReflectCacheEntry, s string, value reflect.Value) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
return i
|
||||
}
|
||||
|
||||
func (r structReflect) Empty() bool {
|
||||
return eachStructField(r.Value, func(_ *TypeReflectCacheEntry, s string, value reflect.Value) bool {
|
||||
return false // exit early if the struct is non-empty
|
||||
})
|
||||
}
|
||||
|
||||
func (r structReflect) Get(key string) (Value, bool) {
|
||||
return r.GetUsing(HeapAllocator, key)
|
||||
}
|
||||
|
||||
func (r structReflect) GetUsing(a Allocator, key string) (Value, bool) {
|
||||
if val, ok := r.findJsonNameField(key); ok {
|
||||
return a.allocValueReflect().mustReuse(val, nil, nil, nil), true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (r structReflect) Has(key string) bool {
|
||||
_, ok := r.findJsonNameField(key)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (r structReflect) Set(key string, val Value) {
|
||||
fieldEntry, ok := TypeReflectEntryOf(r.Value.Type()).Fields()[key]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("key %s may not be set on struct %T: field does not exist", key, r.Value.Interface()))
|
||||
}
|
||||
oldVal := fieldEntry.GetFrom(r.Value)
|
||||
newVal := reflect.ValueOf(val.Unstructured())
|
||||
r.update(fieldEntry, key, oldVal, newVal)
|
||||
}
|
||||
|
||||
func (r structReflect) Delete(key string) {
|
||||
fieldEntry, ok := TypeReflectEntryOf(r.Value.Type()).Fields()[key]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("key %s may not be deleted on struct %T: field does not exist", key, r.Value.Interface()))
|
||||
}
|
||||
oldVal := fieldEntry.GetFrom(r.Value)
|
||||
if oldVal.Kind() != reflect.Ptr && !fieldEntry.isOmitEmpty {
|
||||
panic(fmt.Sprintf("key %s may not be deleted on struct: %T: value is neither a pointer nor an omitempty field", key, r.Value.Interface()))
|
||||
}
|
||||
r.update(fieldEntry, key, oldVal, reflect.Zero(oldVal.Type()))
|
||||
}
|
||||
|
||||
func (r structReflect) update(fieldEntry *FieldCacheEntry, key string, oldVal, newVal reflect.Value) {
|
||||
if oldVal.CanSet() {
|
||||
oldVal.Set(newVal)
|
||||
return
|
||||
}
|
||||
|
||||
// map items are not addressable, so if a struct is contained in a map, the only way to modify it is
|
||||
// to write a replacement fieldEntry into the map.
|
||||
if r.ParentMap != nil {
|
||||
if r.ParentMapKey == nil {
|
||||
panic("ParentMapKey must not be nil if ParentMap is not nil")
|
||||
}
|
||||
replacement := reflect.New(r.Value.Type()).Elem()
|
||||
fieldEntry.GetFrom(replacement).Set(newVal)
|
||||
r.ParentMap.SetMapIndex(*r.ParentMapKey, replacement)
|
||||
return
|
||||
}
|
||||
|
||||
// This should never happen since NewValueReflect ensures that the root object reflected on is a pointer and map
|
||||
// item replacement is handled above.
|
||||
panic(fmt.Sprintf("key %s may not be modified on struct: %T: struct is not settable", key, r.Value.Interface()))
|
||||
}
|
||||
|
||||
func (r structReflect) Iterate(fn func(string, Value) bool) bool {
|
||||
return r.IterateUsing(HeapAllocator, fn)
|
||||
}
|
||||
|
||||
func (r structReflect) IterateUsing(a Allocator, fn func(string, Value) bool) bool {
|
||||
vr := a.allocValueReflect()
|
||||
defer a.Free(vr)
|
||||
return eachStructField(r.Value, func(e *TypeReflectCacheEntry, s string, value reflect.Value) bool {
|
||||
return fn(s, vr.mustReuse(value, e, nil, nil))
|
||||
})
|
||||
}
|
||||
|
||||
func eachStructField(structVal reflect.Value, fn func(*TypeReflectCacheEntry, string, reflect.Value) bool) bool {
|
||||
for _, fieldCacheEntry := range TypeReflectEntryOf(structVal.Type()).OrderedFields() {
|
||||
fieldVal := fieldCacheEntry.GetFrom(structVal)
|
||||
if fieldCacheEntry.CanOmit(fieldVal) {
|
||||
// omit it
|
||||
continue
|
||||
}
|
||||
ok := fn(fieldCacheEntry.TypeEntry, fieldCacheEntry.JsonName, fieldVal)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (r structReflect) Unstructured() interface{} {
|
||||
// Use number of struct fields as a cheap way to rough estimate map size
|
||||
result := make(map[string]interface{}, r.Value.NumField())
|
||||
r.Iterate(func(s string, value Value) bool {
|
||||
result[s] = value.Unstructured()
|
||||
return true
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
func (r structReflect) Equals(m Map) bool {
|
||||
return r.EqualsUsing(HeapAllocator, m)
|
||||
}
|
||||
|
||||
func (r structReflect) EqualsUsing(a Allocator, m Map) bool {
|
||||
// MapEquals uses zip and is fairly efficient for structReflect
|
||||
return MapEqualsUsing(a, &r, m)
|
||||
}
|
||||
|
||||
func (r structReflect) findJsonNameFieldAndNotEmpty(jsonName string) (reflect.Value, bool) {
|
||||
structCacheEntry, ok := TypeReflectEntryOf(r.Value.Type()).Fields()[jsonName]
|
||||
if !ok {
|
||||
return reflect.Value{}, false
|
||||
}
|
||||
fieldVal := structCacheEntry.GetFrom(r.Value)
|
||||
return fieldVal, !structCacheEntry.CanOmit(fieldVal)
|
||||
}
|
||||
|
||||
func (r structReflect) findJsonNameField(jsonName string) (val reflect.Value, ok bool) {
|
||||
structCacheEntry, ok := TypeReflectEntryOf(r.Value.Type()).Fields()[jsonName]
|
||||
if !ok {
|
||||
return reflect.Value{}, false
|
||||
}
|
||||
fieldVal := structCacheEntry.GetFrom(r.Value)
|
||||
return fieldVal, !structCacheEntry.CanOmit(fieldVal)
|
||||
}
|
||||
|
||||
func (r structReflect) Zip(other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
return r.ZipUsing(HeapAllocator, other, order, fn)
|
||||
}
|
||||
|
||||
func (r structReflect) ZipUsing(a Allocator, other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
if otherStruct, ok := other.(*structReflect); ok && r.Value.Type() == otherStruct.Value.Type() {
|
||||
lhsvr, rhsvr := a.allocValueReflect(), a.allocValueReflect()
|
||||
defer a.Free(lhsvr)
|
||||
defer a.Free(rhsvr)
|
||||
return r.structZip(otherStruct, lhsvr, rhsvr, fn)
|
||||
}
|
||||
return defaultMapZip(a, &r, other, order, fn)
|
||||
}
|
||||
|
||||
// structZip provides an optimized zip for structReflect types. The zip is always lexical key ordered since there is
|
||||
// no additional cost to ordering the zip for structured types.
|
||||
func (r structReflect) structZip(other *structReflect, lhsvr, rhsvr *valueReflect, fn func(key string, lhs, rhs Value) bool) bool {
|
||||
lhsVal := r.Value
|
||||
rhsVal := other.Value
|
||||
|
||||
for _, fieldCacheEntry := range TypeReflectEntryOf(lhsVal.Type()).OrderedFields() {
|
||||
lhsFieldVal := fieldCacheEntry.GetFrom(lhsVal)
|
||||
rhsFieldVal := fieldCacheEntry.GetFrom(rhsVal)
|
||||
lhsOmit := fieldCacheEntry.CanOmit(lhsFieldVal)
|
||||
rhsOmit := fieldCacheEntry.CanOmit(rhsFieldVal)
|
||||
if lhsOmit && rhsOmit {
|
||||
continue
|
||||
}
|
||||
var lhsVal, rhsVal Value
|
||||
if !lhsOmit {
|
||||
lhsVal = lhsvr.mustReuse(lhsFieldVal, fieldCacheEntry.TypeEntry, nil, nil)
|
||||
}
|
||||
if !rhsOmit {
|
||||
rhsVal = rhsvr.mustReuse(rhsFieldVal, fieldCacheEntry.TypeEntry, nil, nil)
|
||||
}
|
||||
if !fn(fieldCacheEntry.JsonName, lhsVal, rhsVal) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
347
vendor/sigs.k8s.io/structured-merge-diff/v3/value/value.go
generated
vendored
Normal file
347
vendor/sigs.k8s.io/structured-merge-diff/v3/value/value.go
generated
vendored
Normal file
|
|
@ -0,0 +1,347 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package value
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var (
|
||||
readPool = jsoniter.NewIterator(jsoniter.ConfigCompatibleWithStandardLibrary).Pool()
|
||||
writePool = jsoniter.NewStream(jsoniter.ConfigCompatibleWithStandardLibrary, nil, 1024).Pool()
|
||||
)
|
||||
|
||||
// A Value corresponds to an 'atom' in the schema. It should return true
|
||||
// for at least one of the IsXXX methods below, or the value is
|
||||
// considered "invalid"
|
||||
type Value interface {
|
||||
// IsMap returns true if the Value is a Map, false otherwise.
|
||||
IsMap() bool
|
||||
// IsList returns true if the Value is a List, false otherwise.
|
||||
IsList() bool
|
||||
// IsBool returns true if the Value is a bool, false otherwise.
|
||||
IsBool() bool
|
||||
// IsInt returns true if the Value is a int64, false otherwise.
|
||||
IsInt() bool
|
||||
// IsFloat returns true if the Value is a float64, false
|
||||
// otherwise.
|
||||
IsFloat() bool
|
||||
// IsString returns true if the Value is a string, false
|
||||
// otherwise.
|
||||
IsString() bool
|
||||
// IsMap returns true if the Value is null, false otherwise.
|
||||
IsNull() bool
|
||||
|
||||
// AsMap converts the Value into a Map (or panic if the type
|
||||
// doesn't allow it).
|
||||
AsMap() Map
|
||||
// AsMapUsing uses the provided allocator and converts the Value
|
||||
// into a Map (or panic if the type doesn't allow it).
|
||||
AsMapUsing(Allocator) Map
|
||||
// AsList converts the Value into a List (or panic if the type
|
||||
// doesn't allow it).
|
||||
AsList() List
|
||||
// AsListUsing uses the provided allocator and converts the Value
|
||||
// into a List (or panic if the type doesn't allow it).
|
||||
AsListUsing(Allocator) List
|
||||
// AsBool converts the Value into a bool (or panic if the type
|
||||
// doesn't allow it).
|
||||
AsBool() bool
|
||||
// AsInt converts the Value into an int64 (or panic if the type
|
||||
// doesn't allow it).
|
||||
AsInt() int64
|
||||
// AsFloat converts the Value into a float64 (or panic if the type
|
||||
// doesn't allow it).
|
||||
AsFloat() float64
|
||||
// AsString converts the Value into a string (or panic if the type
|
||||
// doesn't allow it).
|
||||
AsString() string
|
||||
|
||||
// Unstructured converts the Value into an Unstructured interface{}.
|
||||
Unstructured() interface{}
|
||||
}
|
||||
|
||||
// FromJSON is a helper function for reading a JSON document.
|
||||
func FromJSON(input []byte) (Value, error) {
|
||||
return FromJSONFast(input)
|
||||
}
|
||||
|
||||
// FromJSONFast is a helper function for reading a JSON document.
|
||||
func FromJSONFast(input []byte) (Value, error) {
|
||||
iter := readPool.BorrowIterator(input)
|
||||
defer readPool.ReturnIterator(iter)
|
||||
return ReadJSONIter(iter)
|
||||
}
|
||||
|
||||
// ToJSON is a helper function for producing a JSon document.
|
||||
func ToJSON(v Value) ([]byte, error) {
|
||||
buf := bytes.Buffer{}
|
||||
stream := writePool.BorrowStream(&buf)
|
||||
defer writePool.ReturnStream(stream)
|
||||
WriteJSONStream(v, stream)
|
||||
b := stream.Buffer()
|
||||
err := stream.Flush()
|
||||
// Help jsoniter manage its buffers--without this, the next
|
||||
// use of the stream is likely to require an allocation. Look
|
||||
// at the jsoniter stream code to understand why. They were probably
|
||||
// optimizing for folks using the buffer directly.
|
||||
stream.SetBuffer(b[:0])
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
// ReadJSONIter reads a Value from a JSON iterator.
|
||||
func ReadJSONIter(iter *jsoniter.Iterator) (Value, error) {
|
||||
v := iter.Read()
|
||||
if iter.Error != nil && iter.Error != io.EOF {
|
||||
return nil, iter.Error
|
||||
}
|
||||
return NewValueInterface(v), nil
|
||||
}
|
||||
|
||||
// WriteJSONStream writes a value into a JSON stream.
|
||||
func WriteJSONStream(v Value, stream *jsoniter.Stream) {
|
||||
stream.WriteVal(v.Unstructured())
|
||||
}
|
||||
|
||||
// ToYAML marshals a value as YAML.
|
||||
func ToYAML(v Value) ([]byte, error) {
|
||||
return yaml.Marshal(v.Unstructured())
|
||||
}
|
||||
|
||||
// Equals returns true iff the two values are equal.
|
||||
func Equals(lhs, rhs Value) bool {
|
||||
return EqualsUsing(HeapAllocator, lhs, rhs)
|
||||
}
|
||||
|
||||
// EqualsUsing uses the provided allocator and returns true iff the two values are equal.
|
||||
func EqualsUsing(a Allocator, lhs, rhs Value) bool {
|
||||
if lhs.IsFloat() || rhs.IsFloat() {
|
||||
var lf float64
|
||||
if lhs.IsFloat() {
|
||||
lf = lhs.AsFloat()
|
||||
} else if lhs.IsInt() {
|
||||
lf = float64(lhs.AsInt())
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
var rf float64
|
||||
if rhs.IsFloat() {
|
||||
rf = rhs.AsFloat()
|
||||
} else if rhs.IsInt() {
|
||||
rf = float64(rhs.AsInt())
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return lf == rf
|
||||
}
|
||||
if lhs.IsInt() {
|
||||
if rhs.IsInt() {
|
||||
return lhs.AsInt() == rhs.AsInt()
|
||||
}
|
||||
return false
|
||||
} else if rhs.IsInt() {
|
||||
return false
|
||||
}
|
||||
if lhs.IsString() {
|
||||
if rhs.IsString() {
|
||||
return lhs.AsString() == rhs.AsString()
|
||||
}
|
||||
return false
|
||||
} else if rhs.IsString() {
|
||||
return false
|
||||
}
|
||||
if lhs.IsBool() {
|
||||
if rhs.IsBool() {
|
||||
return lhs.AsBool() == rhs.AsBool()
|
||||
}
|
||||
return false
|
||||
} else if rhs.IsBool() {
|
||||
return false
|
||||
}
|
||||
if lhs.IsList() {
|
||||
if rhs.IsList() {
|
||||
lhsList := lhs.AsListUsing(a)
|
||||
defer a.Free(lhsList)
|
||||
rhsList := rhs.AsListUsing(a)
|
||||
defer a.Free(rhsList)
|
||||
return lhsList.EqualsUsing(a, rhsList)
|
||||
}
|
||||
return false
|
||||
} else if rhs.IsList() {
|
||||
return false
|
||||
}
|
||||
if lhs.IsMap() {
|
||||
if rhs.IsMap() {
|
||||
lhsList := lhs.AsMapUsing(a)
|
||||
defer a.Free(lhsList)
|
||||
rhsList := rhs.AsMapUsing(a)
|
||||
defer a.Free(rhsList)
|
||||
return lhsList.EqualsUsing(a, rhsList)
|
||||
}
|
||||
return false
|
||||
} else if rhs.IsMap() {
|
||||
return false
|
||||
}
|
||||
if lhs.IsNull() {
|
||||
if rhs.IsNull() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
} else if rhs.IsNull() {
|
||||
return false
|
||||
}
|
||||
// No field is set, on either objects.
|
||||
return true
|
||||
}
|
||||
|
||||
// ToString returns a human-readable representation of the value.
|
||||
func ToString(v Value) string {
|
||||
if v.IsNull() {
|
||||
return "null"
|
||||
}
|
||||
switch {
|
||||
case v.IsFloat():
|
||||
return fmt.Sprintf("%v", v.AsFloat())
|
||||
case v.IsInt():
|
||||
return fmt.Sprintf("%v", v.AsInt())
|
||||
case v.IsString():
|
||||
return fmt.Sprintf("%q", v.AsString())
|
||||
case v.IsBool():
|
||||
return fmt.Sprintf("%v", v.AsBool())
|
||||
case v.IsList():
|
||||
strs := []string{}
|
||||
list := v.AsList()
|
||||
for i := 0; i < list.Length(); i++ {
|
||||
strs = append(strs, ToString(list.At(i)))
|
||||
}
|
||||
return "[" + strings.Join(strs, ",") + "]"
|
||||
case v.IsMap():
|
||||
strs := []string{}
|
||||
v.AsMap().Iterate(func(k string, v Value) bool {
|
||||
strs = append(strs, fmt.Sprintf("%v=%v", k, ToString(v)))
|
||||
return true
|
||||
})
|
||||
return strings.Join(strs, "")
|
||||
}
|
||||
// No field is set, on either objects.
|
||||
return "{{undefined}}"
|
||||
}
|
||||
|
||||
// Less provides a total ordering for Value (so that they can be sorted, even
|
||||
// if they are of different types).
|
||||
func Less(lhs, rhs Value) bool {
|
||||
return Compare(lhs, rhs) == -1
|
||||
}
|
||||
|
||||
// Compare provides a total ordering for Value (so that they can be
|
||||
// sorted, even if they are of different types). The result will be 0 if
|
||||
// v==rhs, -1 if v < rhs, and +1 if v > rhs.
|
||||
func Compare(lhs, rhs Value) int {
|
||||
return CompareUsing(HeapAllocator, lhs, rhs)
|
||||
}
|
||||
|
||||
// CompareUsing uses the provided allocator and provides a total
|
||||
// ordering for Value (so that they can be sorted, even if they
|
||||
// are of different types). The result will be 0 if v==rhs, -1
|
||||
// if v < rhs, and +1 if v > rhs.
|
||||
func CompareUsing(a Allocator, lhs, rhs Value) int {
|
||||
if lhs.IsFloat() {
|
||||
if !rhs.IsFloat() {
|
||||
// Extra: compare floats and ints numerically.
|
||||
if rhs.IsInt() {
|
||||
return FloatCompare(lhs.AsFloat(), float64(rhs.AsInt()))
|
||||
}
|
||||
return -1
|
||||
}
|
||||
return FloatCompare(lhs.AsFloat(), rhs.AsFloat())
|
||||
} else if rhs.IsFloat() {
|
||||
// Extra: compare floats and ints numerically.
|
||||
if lhs.IsInt() {
|
||||
return FloatCompare(float64(lhs.AsInt()), rhs.AsFloat())
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
if lhs.IsInt() {
|
||||
if !rhs.IsInt() {
|
||||
return -1
|
||||
}
|
||||
return IntCompare(lhs.AsInt(), rhs.AsInt())
|
||||
} else if rhs.IsInt() {
|
||||
return 1
|
||||
}
|
||||
|
||||
if lhs.IsString() {
|
||||
if !rhs.IsString() {
|
||||
return -1
|
||||
}
|
||||
return strings.Compare(lhs.AsString(), rhs.AsString())
|
||||
} else if rhs.IsString() {
|
||||
return 1
|
||||
}
|
||||
|
||||
if lhs.IsBool() {
|
||||
if !rhs.IsBool() {
|
||||
return -1
|
||||
}
|
||||
return BoolCompare(lhs.AsBool(), rhs.AsBool())
|
||||
} else if rhs.IsBool() {
|
||||
return 1
|
||||
}
|
||||
|
||||
if lhs.IsList() {
|
||||
if !rhs.IsList() {
|
||||
return -1
|
||||
}
|
||||
lhsList := lhs.AsListUsing(a)
|
||||
defer a.Free(lhsList)
|
||||
rhsList := rhs.AsListUsing(a)
|
||||
defer a.Free(rhsList)
|
||||
return ListCompareUsing(a, lhsList, rhsList)
|
||||
} else if rhs.IsList() {
|
||||
return 1
|
||||
}
|
||||
if lhs.IsMap() {
|
||||
if !rhs.IsMap() {
|
||||
return -1
|
||||
}
|
||||
lhsMap := lhs.AsMapUsing(a)
|
||||
defer a.Free(lhsMap)
|
||||
rhsMap := rhs.AsMapUsing(a)
|
||||
defer a.Free(rhsMap)
|
||||
return MapCompareUsing(a, lhsMap, rhsMap)
|
||||
} else if rhs.IsMap() {
|
||||
return 1
|
||||
}
|
||||
if lhs.IsNull() {
|
||||
if !rhs.IsNull() {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
} else if rhs.IsNull() {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Invalid Value-- nothing is set.
|
||||
return 0
|
||||
}
|
||||
294
vendor/sigs.k8s.io/structured-merge-diff/v3/value/valuereflect.go
generated
vendored
Normal file
294
vendor/sigs.k8s.io/structured-merge-diff/v3/value/valuereflect.go
generated
vendored
Normal file
|
|
@ -0,0 +1,294 @@
|
|||
/*
|
||||
Copyright 2019 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 value
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// NewValueReflect creates a Value backed by an "interface{}" type,
|
||||
// typically an structured object in Kubernetes world that is uses reflection to expose.
|
||||
// The provided "interface{}" value must be a pointer so that the value can be modified via reflection.
|
||||
// The provided "interface{}" may contain structs and types that are converted to Values
|
||||
// by the jsonMarshaler interface.
|
||||
func NewValueReflect(value interface{}) (Value, error) {
|
||||
if value == nil {
|
||||
return NewValueInterface(nil), nil
|
||||
}
|
||||
v := reflect.ValueOf(value)
|
||||
if v.Kind() != reflect.Ptr {
|
||||
// The root value to reflect on must be a pointer so that map.Set() and map.Delete() operations are possible.
|
||||
return nil, fmt.Errorf("value provided to NewValueReflect must be a pointer")
|
||||
}
|
||||
return wrapValueReflect(v, nil, nil)
|
||||
}
|
||||
|
||||
// wrapValueReflect wraps the provide reflect.Value as a value. If parent in the data tree is a map, parentMap
|
||||
// and parentMapKey must be provided so that the returned value may be set and deleted.
|
||||
func wrapValueReflect(value reflect.Value, parentMap, parentMapKey *reflect.Value) (Value, error) {
|
||||
val := HeapAllocator.allocValueReflect()
|
||||
return val.reuse(value, nil, parentMap, parentMapKey)
|
||||
}
|
||||
|
||||
// wrapValueReflect wraps the provide reflect.Value as a value, and panics if there is an error. If parent in the data
|
||||
// tree is a map, parentMap and parentMapKey must be provided so that the returned value may be set and deleted.
|
||||
func mustWrapValueReflect(value reflect.Value, parentMap, parentMapKey *reflect.Value) Value {
|
||||
v, err := wrapValueReflect(value, parentMap, parentMapKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// the value interface doesn't care about the type for value.IsNull, so we can use a constant
|
||||
var nilType = reflect.TypeOf(&struct{}{})
|
||||
|
||||
// reuse replaces the value of the valueReflect. If parent in the data tree is a map, parentMap and parentMapKey
|
||||
// must be provided so that the returned value may be set and deleted.
|
||||
func (r *valueReflect) reuse(value reflect.Value, cacheEntry *TypeReflectCacheEntry, parentMap, parentMapKey *reflect.Value) (Value, error) {
|
||||
if cacheEntry == nil {
|
||||
cacheEntry = TypeReflectEntryOf(value.Type())
|
||||
}
|
||||
if cacheEntry.CanConvertToUnstructured() {
|
||||
u, err := cacheEntry.ToUnstructured(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if u == nil {
|
||||
value = reflect.Zero(nilType)
|
||||
} else {
|
||||
value = reflect.ValueOf(u)
|
||||
}
|
||||
}
|
||||
r.Value = dereference(value)
|
||||
r.ParentMap = parentMap
|
||||
r.ParentMapKey = parentMapKey
|
||||
r.kind = kind(r.Value)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// mustReuse replaces the value of the valueReflect and panics if there is an error. If parent in the data tree is a
|
||||
// map, parentMap and parentMapKey must be provided so that the returned value may be set and deleted.
|
||||
func (r *valueReflect) mustReuse(value reflect.Value, cacheEntry *TypeReflectCacheEntry, parentMap, parentMapKey *reflect.Value) Value {
|
||||
v, err := r.reuse(value, cacheEntry, parentMap, parentMapKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func dereference(val reflect.Value) reflect.Value {
|
||||
kind := val.Kind()
|
||||
if (kind == reflect.Interface || kind == reflect.Ptr) && !safeIsNil(val) {
|
||||
return val.Elem()
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
type valueReflect struct {
|
||||
ParentMap *reflect.Value
|
||||
ParentMapKey *reflect.Value
|
||||
Value reflect.Value
|
||||
kind reflectType
|
||||
}
|
||||
|
||||
func (r valueReflect) IsMap() bool {
|
||||
return r.kind == mapType || r.kind == structMapType
|
||||
}
|
||||
|
||||
func (r valueReflect) IsList() bool {
|
||||
return r.kind == listType
|
||||
}
|
||||
|
||||
func (r valueReflect) IsBool() bool {
|
||||
return r.kind == boolType
|
||||
}
|
||||
|
||||
func (r valueReflect) IsInt() bool {
|
||||
return r.kind == intType || r.kind == uintType
|
||||
}
|
||||
|
||||
func (r valueReflect) IsFloat() bool {
|
||||
return r.kind == floatType
|
||||
}
|
||||
|
||||
func (r valueReflect) IsString() bool {
|
||||
return r.kind == stringType || r.kind == byteStringType
|
||||
}
|
||||
|
||||
func (r valueReflect) IsNull() bool {
|
||||
return r.kind == nullType
|
||||
}
|
||||
|
||||
type reflectType = int
|
||||
|
||||
const (
|
||||
mapType = iota
|
||||
structMapType
|
||||
listType
|
||||
intType
|
||||
uintType
|
||||
floatType
|
||||
stringType
|
||||
byteStringType
|
||||
boolType
|
||||
nullType
|
||||
)
|
||||
|
||||
func kind(v reflect.Value) reflectType {
|
||||
typ := v.Type()
|
||||
rk := typ.Kind()
|
||||
switch rk {
|
||||
case reflect.Map:
|
||||
if v.IsNil() {
|
||||
return nullType
|
||||
}
|
||||
return mapType
|
||||
case reflect.Struct:
|
||||
return structMapType
|
||||
case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8:
|
||||
return intType
|
||||
case reflect.Uint, reflect.Uint32, reflect.Uint16, reflect.Uint8:
|
||||
// Uint64 deliberately excluded, see valueUnstructured.Int.
|
||||
return uintType
|
||||
case reflect.Float64, reflect.Float32:
|
||||
return floatType
|
||||
case reflect.String:
|
||||
return stringType
|
||||
case reflect.Bool:
|
||||
return boolType
|
||||
case reflect.Slice:
|
||||
if v.IsNil() {
|
||||
return nullType
|
||||
}
|
||||
elemKind := typ.Elem().Kind()
|
||||
if elemKind == reflect.Uint8 {
|
||||
return byteStringType
|
||||
}
|
||||
return listType
|
||||
case reflect.Chan, reflect.Func, reflect.Ptr, reflect.UnsafePointer, reflect.Interface:
|
||||
if v.IsNil() {
|
||||
return nullType
|
||||
}
|
||||
panic(fmt.Sprintf("unsupported type: %v", v.Type()))
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported type: %v", v.Type()))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO find a cleaner way to avoid panics from reflect.IsNil()
|
||||
func safeIsNil(v reflect.Value) bool {
|
||||
k := v.Kind()
|
||||
switch k {
|
||||
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice:
|
||||
return v.IsNil()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (r valueReflect) AsMap() Map {
|
||||
return r.AsMapUsing(HeapAllocator)
|
||||
}
|
||||
|
||||
func (r valueReflect) AsMapUsing(a Allocator) Map {
|
||||
switch r.kind {
|
||||
case structMapType:
|
||||
v := a.allocStructReflect()
|
||||
v.valueReflect = r
|
||||
return v
|
||||
case mapType:
|
||||
v := a.allocMapReflect()
|
||||
v.valueReflect = r
|
||||
return v
|
||||
default:
|
||||
panic("value is not a map or struct")
|
||||
}
|
||||
}
|
||||
|
||||
func (r valueReflect) AsList() List {
|
||||
return r.AsListUsing(HeapAllocator)
|
||||
}
|
||||
|
||||
func (r valueReflect) AsListUsing(a Allocator) List {
|
||||
if r.IsList() {
|
||||
v := a.allocListReflect()
|
||||
v.Value = r.Value
|
||||
return v
|
||||
}
|
||||
panic("value is not a list")
|
||||
}
|
||||
|
||||
func (r valueReflect) AsBool() bool {
|
||||
if r.IsBool() {
|
||||
return r.Value.Bool()
|
||||
}
|
||||
panic("value is not a bool")
|
||||
}
|
||||
|
||||
func (r valueReflect) AsInt() int64 {
|
||||
if r.kind == intType {
|
||||
return r.Value.Int()
|
||||
}
|
||||
if r.kind == uintType {
|
||||
return int64(r.Value.Uint())
|
||||
}
|
||||
|
||||
panic("value is not an int")
|
||||
}
|
||||
|
||||
func (r valueReflect) AsFloat() float64 {
|
||||
if r.IsFloat() {
|
||||
return r.Value.Float()
|
||||
}
|
||||
panic("value is not a float")
|
||||
}
|
||||
|
||||
func (r valueReflect) AsString() string {
|
||||
switch r.kind {
|
||||
case stringType:
|
||||
return r.Value.String()
|
||||
case byteStringType:
|
||||
return base64.StdEncoding.EncodeToString(r.Value.Bytes())
|
||||
}
|
||||
panic("value is not a string")
|
||||
}
|
||||
|
||||
func (r valueReflect) Unstructured() interface{} {
|
||||
val := r.Value
|
||||
switch {
|
||||
case r.IsNull():
|
||||
return nil
|
||||
case val.Kind() == reflect.Struct:
|
||||
return structReflect{r}.Unstructured()
|
||||
case val.Kind() == reflect.Map:
|
||||
return mapReflect{valueReflect: r}.Unstructured()
|
||||
case r.IsList():
|
||||
return listReflect{r.Value}.Unstructured()
|
||||
case r.IsString():
|
||||
return r.AsString()
|
||||
case r.IsInt():
|
||||
return r.AsInt()
|
||||
case r.IsBool():
|
||||
return r.AsBool()
|
||||
case r.IsFloat():
|
||||
return r.AsFloat()
|
||||
default:
|
||||
panic(fmt.Sprintf("value of type %s is not a supported by value reflector", val.Type()))
|
||||
}
|
||||
}
|
||||
178
vendor/sigs.k8s.io/structured-merge-diff/v3/value/valueunstructured.go
generated
vendored
Normal file
178
vendor/sigs.k8s.io/structured-merge-diff/v3/value/valueunstructured.go
generated
vendored
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package value
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// NewValueInterface creates a Value backed by an "interface{}" type,
|
||||
// typically an unstructured object in Kubernetes world.
|
||||
// interface{} must be one of: map[string]interface{}, map[interface{}]interface{}, []interface{}, int types, float types,
|
||||
// string or boolean. Nested interface{} must also be one of these types.
|
||||
func NewValueInterface(v interface{}) Value {
|
||||
return Value(HeapAllocator.allocValueUnstructured().reuse(v))
|
||||
}
|
||||
|
||||
type valueUnstructured struct {
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// reuse replaces the value of the valueUnstructured.
|
||||
func (vi *valueUnstructured) reuse(value interface{}) Value {
|
||||
vi.Value = value
|
||||
return vi
|
||||
}
|
||||
|
||||
func (v valueUnstructured) IsMap() bool {
|
||||
if _, ok := v.Value.(map[string]interface{}); ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := v.Value.(map[interface{}]interface{}); ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (v valueUnstructured) AsMap() Map {
|
||||
return v.AsMapUsing(HeapAllocator)
|
||||
}
|
||||
|
||||
func (v valueUnstructured) AsMapUsing(_ Allocator) Map {
|
||||
if v.Value == nil {
|
||||
panic("invalid nil")
|
||||
}
|
||||
switch t := v.Value.(type) {
|
||||
case map[string]interface{}:
|
||||
return mapUnstructuredString(t)
|
||||
case map[interface{}]interface{}:
|
||||
return mapUnstructuredInterface(t)
|
||||
}
|
||||
panic(fmt.Errorf("not a map: %#v", v))
|
||||
}
|
||||
|
||||
func (v valueUnstructured) IsList() bool {
|
||||
if v.Value == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := v.Value.([]interface{})
|
||||
return ok
|
||||
}
|
||||
|
||||
func (v valueUnstructured) AsList() List {
|
||||
return v.AsListUsing(HeapAllocator)
|
||||
}
|
||||
|
||||
func (v valueUnstructured) AsListUsing(_ Allocator) List {
|
||||
return listUnstructured(v.Value.([]interface{}))
|
||||
}
|
||||
|
||||
func (v valueUnstructured) IsFloat() bool {
|
||||
if v.Value == nil {
|
||||
return false
|
||||
} else if _, ok := v.Value.(float64); ok {
|
||||
return true
|
||||
} else if _, ok := v.Value.(float32); ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (v valueUnstructured) AsFloat() float64 {
|
||||
if f, ok := v.Value.(float32); ok {
|
||||
return float64(f)
|
||||
}
|
||||
return v.Value.(float64)
|
||||
}
|
||||
|
||||
func (v valueUnstructured) IsInt() bool {
|
||||
if v.Value == nil {
|
||||
return false
|
||||
} else if _, ok := v.Value.(int); ok {
|
||||
return true
|
||||
} else if _, ok := v.Value.(int8); ok {
|
||||
return true
|
||||
} else if _, ok := v.Value.(int16); ok {
|
||||
return true
|
||||
} else if _, ok := v.Value.(int32); ok {
|
||||
return true
|
||||
} else if _, ok := v.Value.(int64); ok {
|
||||
return true
|
||||
} else if _, ok := v.Value.(uint); ok {
|
||||
return true
|
||||
} else if _, ok := v.Value.(uint8); ok {
|
||||
return true
|
||||
} else if _, ok := v.Value.(uint16); ok {
|
||||
return true
|
||||
} else if _, ok := v.Value.(uint32); ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (v valueUnstructured) AsInt() int64 {
|
||||
if i, ok := v.Value.(int); ok {
|
||||
return int64(i)
|
||||
} else if i, ok := v.Value.(int8); ok {
|
||||
return int64(i)
|
||||
} else if i, ok := v.Value.(int16); ok {
|
||||
return int64(i)
|
||||
} else if i, ok := v.Value.(int32); ok {
|
||||
return int64(i)
|
||||
} else if i, ok := v.Value.(uint); ok {
|
||||
return int64(i)
|
||||
} else if i, ok := v.Value.(uint8); ok {
|
||||
return int64(i)
|
||||
} else if i, ok := v.Value.(uint16); ok {
|
||||
return int64(i)
|
||||
} else if i, ok := v.Value.(uint32); ok {
|
||||
return int64(i)
|
||||
}
|
||||
return v.Value.(int64)
|
||||
}
|
||||
|
||||
func (v valueUnstructured) IsString() bool {
|
||||
if v.Value == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := v.Value.(string)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (v valueUnstructured) AsString() string {
|
||||
return v.Value.(string)
|
||||
}
|
||||
|
||||
func (v valueUnstructured) IsBool() bool {
|
||||
if v.Value == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := v.Value.(bool)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (v valueUnstructured) AsBool() bool {
|
||||
return v.Value.(bool)
|
||||
}
|
||||
|
||||
func (v valueUnstructured) IsNull() bool {
|
||||
return v.Value == nil
|
||||
}
|
||||
|
||||
func (v valueUnstructured) Unstructured() interface{} {
|
||||
return v.Value
|
||||
}
|
||||
8
vendor/sigs.k8s.io/testing_frameworks/integration/README.md
generated
vendored
8
vendor/sigs.k8s.io/testing_frameworks/integration/README.md
generated
vendored
|
|
@ -1,8 +0,0 @@
|
|||
# Integration Testing Framework
|
||||
|
||||
A framework for integration testing components of kubernetes. This framework is
|
||||
intended to work properly both in CI, and on a local dev machine. It therefore
|
||||
explicitly supports both Linux and Darwin.
|
||||
|
||||
For detailed documentation see the
|
||||
[](https://godoc.org/github.com/kubernetes-sigs/testing_frameworks/integration).
|
||||
24
vendor/sigs.k8s.io/testing_frameworks/integration/addr/manager.go
generated
vendored
24
vendor/sigs.k8s.io/testing_frameworks/integration/addr/manager.go
generated
vendored
|
|
@ -1,24 +0,0 @@
|
|||
package addr
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// Suggest suggests a address a process can listen on. It returns
|
||||
// a tuple consisting of a free port and the hostname resolved to its IP.
|
||||
func Suggest() (port int, resolvedHost string, err error) {
|
||||
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
l, err := net.ListenTCP("tcp", addr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
port = l.Addr().(*net.TCPAddr).Port
|
||||
defer func() {
|
||||
err = l.Close()
|
||||
}()
|
||||
resolvedHost = addr.IP.String()
|
||||
return
|
||||
}
|
||||
15
vendor/sigs.k8s.io/yaml/.travis.yml
generated
vendored
15
vendor/sigs.k8s.io/yaml/.travis.yml
generated
vendored
|
|
@ -1,14 +1,13 @@
|
|||
language: go
|
||||
dist: xenial
|
||||
go:
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
script:
|
||||
- go get -t -v ./...
|
||||
- diff -u <(echo -n) <(gofmt -d .)
|
||||
- diff -u <(echo -n) <(gofmt -d *.go)
|
||||
- diff -u <(echo -n) <(golint $(go list -e ./...) | grep -v YAMLToJSON)
|
||||
- go tool vet .
|
||||
- go test -v -race ./...
|
||||
- GO111MODULE=on go vet .
|
||||
- GO111MODULE=on go test -v -race ./...
|
||||
- git diff --exit-code
|
||||
install:
|
||||
- go get golang.org/x/lint/golint
|
||||
- GO111MODULE=off go get golang.org/x/lint/golint
|
||||
|
|
|
|||
2
vendor/sigs.k8s.io/yaml/OWNERS
generated
vendored
2
vendor/sigs.k8s.io/yaml/OWNERS
generated
vendored
|
|
@ -1,3 +1,5 @@
|
|||
# See the OWNERS docs at https://go.k8s.io/owners
|
||||
|
||||
approvers:
|
||||
- dims
|
||||
- lavalamp
|
||||
|
|
|
|||
14
vendor/sigs.k8s.io/yaml/README.md
generated
vendored
14
vendor/sigs.k8s.io/yaml/README.md
generated
vendored
|
|
@ -1,12 +1,14 @@
|
|||
# YAML marshaling and unmarshaling support for Go
|
||||
|
||||
[](https://travis-ci.org/ghodss/yaml)
|
||||
[](https://travis-ci.org/kubernetes-sigs/yaml)
|
||||
|
||||
kubernetes-sigs/yaml is a permanent fork of [ghodss/yaml](https://github.com/ghodss/yaml).
|
||||
|
||||
## Introduction
|
||||
|
||||
A wrapper around [go-yaml](https://github.com/go-yaml/yaml) designed to enable a better way of handling YAML when marshaling to and from structs.
|
||||
|
||||
In short, this library first converts YAML to JSON using go-yaml and then uses `json.Marshal` and `json.Unmarshal` to convert to or from the struct. This means that it effectively reuses the JSON struct tags as well as the custom JSON methods `MarshalJSON` and `UnmarshalJSON` unlike go-yaml. For a detailed overview of the rationale behind this method, [see this blog post](http://ghodss.com/2014/the-right-way-to-handle-yaml-in-golang/).
|
||||
In short, this library first converts YAML to JSON using go-yaml and then uses `json.Marshal` and `json.Unmarshal` to convert to or from the struct. This means that it effectively reuses the JSON struct tags as well as the custom JSON methods `MarshalJSON` and `UnmarshalJSON` unlike go-yaml. For a detailed overview of the rationale behind this method, [see this blog post](http://web.archive.org/web/20190603050330/http://ghodss.com/2014/the-right-way-to-handle-yaml-in-golang/).
|
||||
|
||||
## Compatibility
|
||||
|
||||
|
|
@ -32,13 +34,13 @@ GOOD:
|
|||
To install, run:
|
||||
|
||||
```
|
||||
$ go get github.com/ghodss/yaml
|
||||
$ go get sigs.k8s.io/yaml
|
||||
```
|
||||
|
||||
And import using:
|
||||
|
||||
```
|
||||
import "github.com/ghodss/yaml"
|
||||
import "sigs.k8s.io/yaml"
|
||||
```
|
||||
|
||||
Usage is very similar to the JSON library:
|
||||
|
|
@ -49,7 +51,7 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
type Person struct {
|
||||
|
|
@ -93,7 +95,7 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
|
|
|||
8
vendor/sigs.k8s.io/yaml/go.mod
generated
vendored
Normal file
8
vendor/sigs.k8s.io/yaml/go.mod
generated
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
module sigs.k8s.io/yaml
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
gopkg.in/yaml.v2 v2.2.8
|
||||
)
|
||||
9
vendor/sigs.k8s.io/yaml/go.sum
generated
vendored
Normal file
9
vendor/sigs.k8s.io/yaml/go.sum
generated
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
61
vendor/sigs.k8s.io/yaml/yaml.go
generated
vendored
61
vendor/sigs.k8s.io/yaml/yaml.go
generated
vendored
|
|
@ -317,3 +317,64 @@ func convertToJSONableObject(yamlObj interface{}, jsonTarget *reflect.Value) (in
|
|||
return yamlObj, nil
|
||||
}
|
||||
}
|
||||
|
||||
// JSONObjectToYAMLObject converts an in-memory JSON object into a YAML in-memory MapSlice,
|
||||
// without going through a byte representation. A nil or empty map[string]interface{} input is
|
||||
// converted to an empty map, i.e. yaml.MapSlice(nil).
|
||||
//
|
||||
// interface{} slices stay interface{} slices. map[string]interface{} becomes yaml.MapSlice.
|
||||
//
|
||||
// int64 and float64 are down casted following the logic of github.com/go-yaml/yaml:
|
||||
// - float64s are down-casted as far as possible without data-loss to int, int64, uint64.
|
||||
// - int64s are down-casted to int if possible without data-loss.
|
||||
//
|
||||
// Big int/int64/uint64 do not lose precision as in the json-yaml roundtripping case.
|
||||
//
|
||||
// string, bool and any other types are unchanged.
|
||||
func JSONObjectToYAMLObject(j map[string]interface{}) yaml.MapSlice {
|
||||
if len(j) == 0 {
|
||||
return nil
|
||||
}
|
||||
ret := make(yaml.MapSlice, 0, len(j))
|
||||
for k, v := range j {
|
||||
ret = append(ret, yaml.MapItem{Key: k, Value: jsonToYAMLValue(v)})
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func jsonToYAMLValue(j interface{}) interface{} {
|
||||
switch j := j.(type) {
|
||||
case map[string]interface{}:
|
||||
if j == nil {
|
||||
return interface{}(nil)
|
||||
}
|
||||
return JSONObjectToYAMLObject(j)
|
||||
case []interface{}:
|
||||
if j == nil {
|
||||
return interface{}(nil)
|
||||
}
|
||||
ret := make([]interface{}, len(j))
|
||||
for i := range j {
|
||||
ret[i] = jsonToYAMLValue(j[i])
|
||||
}
|
||||
return ret
|
||||
case float64:
|
||||
// replicate the logic in https://github.com/go-yaml/yaml/blob/51d6538a90f86fe93ac480b35f37b2be17fef232/resolve.go#L151
|
||||
if i64 := int64(j); j == float64(i64) {
|
||||
if i := int(i64); i64 == int64(i) {
|
||||
return i
|
||||
}
|
||||
return i64
|
||||
}
|
||||
if ui64 := uint64(j); j == float64(ui64) {
|
||||
return ui64
|
||||
}
|
||||
return j
|
||||
case int64:
|
||||
if i := int(j); j == int64(i) {
|
||||
return i
|
||||
}
|
||||
return j
|
||||
}
|
||||
return j
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue