mirror of
https://github.com/1Password/onepassword-operator.git
synced 2025-10-23 07:58:04 +00:00
Update packages and add vendor directory
This commit is contained in:
196
vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/apimachinery.go
generated
vendored
Normal file
196
vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/apimachinery.go
generated
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
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"
|
||||
"reflect"
|
||||
"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/discovery"
|
||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/restmapper"
|
||||
)
|
||||
|
||||
var (
|
||||
protobufScheme = runtime.NewScheme()
|
||||
protobufSchemeLock sync.RWMutex
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Currently only enabled for built-in resources which are guaranteed to implement Protocol Buffers.
|
||||
// For custom resources, CRDs can not support Protocol Buffers but Aggregated API can.
|
||||
// See doc: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/#advanced-features-and-flexibility
|
||||
if err := clientgoscheme.AddToScheme(protobufScheme); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// AddToProtobufScheme add the given SchemeBuilder into protobufScheme, which should
|
||||
// be additional types that do support protobuf.
|
||||
func AddToProtobufScheme(addToScheme func(*runtime.Scheme) error) error {
|
||||
protobufSchemeLock.Lock()
|
||||
defer protobufSchemeLock.Unlock()
|
||||
return addToScheme(protobufScheme)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// TODO(directxman12): do we want to generalize this to arbitrary container types?
|
||||
// I think we'd need a generalized form of scheme or something. It's a
|
||||
// shame there's not a reliable "GetGVK" interface that works by default
|
||||
// for unpopulated static types and populated "dynamic" types
|
||||
// (unstructured, partial, etc)
|
||||
|
||||
// check for PartialObjectMetadata, which is analogous to unstructured, but isn't handled by ObjectKinds
|
||||
_, isPartial := obj.(*metav1.PartialObjectMetadata) //nolint:ifshort
|
||||
_, isPartialList := obj.(*metav1.PartialObjectMetadataList)
|
||||
if isPartial || isPartialList {
|
||||
// we require that the GVK be populated in order to recognize the object
|
||||
gvk := obj.GetObjectKind().GroupVersionKind()
|
||||
if len(gvk.Kind) == 0 {
|
||||
return schema.GroupVersionKind{}, runtime.NewMissingKindErr("unstructured object has no kind")
|
||||
}
|
||||
if len(gvk.Version) == 0 {
|
||||
return schema.GroupVersionKind{}, runtime.NewMissingVersionErr("unstructured object has no version")
|
||||
}
|
||||
return gvk, nil
|
||||
}
|
||||
|
||||
gvks, isUnversioned, err := scheme.ObjectKinds(obj)
|
||||
if err != nil {
|
||||
return schema.GroupVersionKind{}, err
|
||||
}
|
||||
if isUnversioned {
|
||||
return schema.GroupVersionKind{}, fmt.Errorf("cannot create group-version-kind for 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, isUnstructured bool, baseConfig *rest.Config, codecs serializer.CodecFactory) (rest.Interface, error) {
|
||||
return rest.RESTClientFor(createRestConfig(gvk, isUnstructured, baseConfig, codecs))
|
||||
}
|
||||
|
||||
// serializerWithDecodedGVK is a CodecFactory that overrides the DecoderToVersion of a WithoutConversionCodecFactory
|
||||
// in order to avoid clearing the GVK from the decoded object.
|
||||
//
|
||||
// See https://github.com/kubernetes/kubernetes/issues/80609.
|
||||
type serializerWithDecodedGVK struct {
|
||||
serializer.WithoutConversionCodecFactory
|
||||
}
|
||||
|
||||
// DecoderToVersion returns an decoder that does not do conversion.
|
||||
func (f serializerWithDecodedGVK) DecoderToVersion(serializer runtime.Decoder, _ runtime.GroupVersioner) runtime.Decoder {
|
||||
return serializer
|
||||
}
|
||||
|
||||
// createRestConfig copies the base config and updates needed fields for a new rest config.
|
||||
func createRestConfig(gvk schema.GroupVersionKind, isUnstructured bool, baseConfig *rest.Config, codecs serializer.CodecFactory) *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()
|
||||
}
|
||||
// TODO(FillZpp): In the long run, we want to check discovery or something to make sure that this is actually true.
|
||||
if cfg.ContentType == "" && !isUnstructured {
|
||||
protobufSchemeLock.RLock()
|
||||
if protobufScheme.Recognizes(gvk) {
|
||||
cfg.ContentType = runtime.ContentTypeProtobuf
|
||||
}
|
||||
protobufSchemeLock.RUnlock()
|
||||
}
|
||||
|
||||
if isUnstructured {
|
||||
// If the object is unstructured, we need to preserve the GVK information.
|
||||
// Use our own custom serializer.
|
||||
cfg.NegotiatedSerializer = serializerWithDecodedGVK{serializer.WithoutConversionCodecFactory{CodecFactory: codecs}}
|
||||
} else {
|
||||
cfg.NegotiatedSerializer = serializerWithTargetZeroingDecode{NegotiatedSerializer: serializer.WithoutConversionCodecFactory{CodecFactory: codecs}}
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
type serializerWithTargetZeroingDecode struct {
|
||||
runtime.NegotiatedSerializer
|
||||
}
|
||||
|
||||
func (s serializerWithTargetZeroingDecode) DecoderToVersion(serializer runtime.Decoder, r runtime.GroupVersioner) runtime.Decoder {
|
||||
return targetZeroingDecoder{upstream: s.NegotiatedSerializer.DecoderToVersion(serializer, r)}
|
||||
}
|
||||
|
||||
type targetZeroingDecoder struct {
|
||||
upstream runtime.Decoder
|
||||
}
|
||||
|
||||
func (t targetZeroingDecoder) Decode(data []byte, defaults *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
|
||||
zero(into)
|
||||
return t.upstream.Decode(data, defaults, into)
|
||||
}
|
||||
|
||||
// zero zeros the value of a pointer.
|
||||
func zero(x interface{}) {
|
||||
if x == nil {
|
||||
return
|
||||
}
|
||||
res := reflect.ValueOf(x).Elem()
|
||||
res.Set(reflect.Zero(res.Type()))
|
||||
}
|
290
vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/dynamicrestmapper.go
generated
vendored
Normal file
290
vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/dynamicrestmapper.go
generated
vendored
Normal file
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
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 (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// 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 *rate.Limiter
|
||||
newMapper func() (meta.RESTMapper, error)
|
||||
|
||||
lazy bool
|
||||
// Used for lazy init.
|
||||
inited uint32
|
||||
initMtx sync.Mutex
|
||||
}
|
||||
|
||||
// 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 = 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: 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) {
|
||||
// skip init if drm is not lazy or has initialized
|
||||
if !drm.lazy || atomic.LoadUint32(&drm.inited) != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
drm.initMtx.Lock()
|
||||
defer drm.initMtx.Unlock()
|
||||
if drm.inited == 0 {
|
||||
if err = drm.setStaticMapper(); err == nil {
|
||||
atomic.StoreUint32(&drm.inited, 1)
|
||||
}
|
||||
}
|
||||
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 an error matching meta.IsNoMatchErr, 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 of ensuring that reloads are rate-limited and that extraneous calls
|
||||
// aren't made. If a reload would exceed the limiters rate, it returns the error return by
|
||||
// the callback.
|
||||
// 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(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()
|
||||
}()
|
||||
|
||||
needsReload := meta.IsNoMatchError(err)
|
||||
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 = meta.IsNoMatchError(err)
|
||||
if !needsReload {
|
||||
return err
|
||||
}
|
||||
|
||||
// we're still stale, so grab a rate-limit token if we can...
|
||||
if !drm.limiter.Allow() {
|
||||
// return error from static mapper here, we have refreshed often enough (exceeding rate of provided limiter)
|
||||
// so that client's can handle this the same way as a "normal" NoResourceMatchError / NoKindMatchError
|
||||
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(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(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(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(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(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(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(func() error {
|
||||
var err error
|
||||
singular, err = drm.staticMapper.ResourceSingularizer(resource)
|
||||
return err
|
||||
})
|
||||
return singular, err
|
||||
}
|
327
vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go
generated
vendored
Normal file
327
vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go
generated
vendored
Normal file
@@ -0,0 +1,327 @@
|
||||
/*
|
||||
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/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/metadata"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
)
|
||||
|
||||
// WarningHandlerOptions are options for configuring a
|
||||
// warning handler for the client which is responsible
|
||||
// for surfacing API Server warnings.
|
||||
type WarningHandlerOptions struct {
|
||||
// SuppressWarnings decides if the warnings from the
|
||||
// API server are suppressed or surfaced in the client.
|
||||
SuppressWarnings bool
|
||||
// AllowDuplicateLogs does not deduplicate the to-be
|
||||
// logged surfaced warnings messages. See
|
||||
// log.WarningHandlerOptions for considerations
|
||||
// regarding deduplication
|
||||
AllowDuplicateLogs bool
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// Opts is used to configure the warning handler responsible for
|
||||
// surfacing and handling warnings messages sent by the API server.
|
||||
Opts WarningHandlerOptions
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return newClient(config, options)
|
||||
}
|
||||
|
||||
func newClient(config *rest.Config, options Options) (*client, error) {
|
||||
if config == nil {
|
||||
return nil, fmt.Errorf("must provide non-nil rest.Config to client.New")
|
||||
}
|
||||
|
||||
if !options.Opts.SuppressWarnings {
|
||||
// surface warnings
|
||||
logger := log.Log.WithName("KubeAPIWarningLogger")
|
||||
// Set a WarningHandler, the default WarningHandler
|
||||
// is log.KubeAPIWarningLogger with deduplication enabled.
|
||||
// See log.KubeAPIWarningLoggerOptions for considerations
|
||||
// regarding deduplication.
|
||||
config = rest.CopyConfig(config)
|
||||
config.WarningHandler = log.NewKubeAPIWarningLogger(
|
||||
logger,
|
||||
log.KubeAPIWarningLoggerOptions{
|
||||
Deduplicate: !options.Opts.AllowDuplicateLogs,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// 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),
|
||||
|
||||
structuredResourceByType: make(map[schema.GroupVersionKind]*resourceMeta),
|
||||
unstructuredResourceByType: make(map[schema.GroupVersionKind]*resourceMeta),
|
||||
}
|
||||
|
||||
rawMetaClient, err := metadata.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to construct metadata-only client for use as part of client: %w", err)
|
||||
}
|
||||
|
||||
c := &client{
|
||||
typedClient: typedClient{
|
||||
cache: clientcache,
|
||||
paramCodec: runtime.NewParameterCodec(options.Scheme),
|
||||
},
|
||||
unstructuredClient: unstructuredClient{
|
||||
cache: clientcache,
|
||||
paramCodec: noConversionParamCodec{},
|
||||
},
|
||||
metadataClient: metadataClient{
|
||||
client: rawMetaClient,
|
||||
restMapper: options.Mapper,
|
||||
},
|
||||
scheme: options.Scheme,
|
||||
mapper: options.Mapper,
|
||||
}
|
||||
|
||||
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
|
||||
metadataClient metadataClient
|
||||
scheme *runtime.Scheme
|
||||
mapper meta.RESTMapper
|
||||
}
|
||||
|
||||
// resetGroupVersionKind is a helper function to restore and preserve GroupVersionKind on an object.
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scheme returns the scheme this client is using.
|
||||
func (c *client) Scheme() *runtime.Scheme {
|
||||
return c.scheme
|
||||
}
|
||||
|
||||
// RESTMapper returns the scheme this client is using.
|
||||
func (c *client) RESTMapper() meta.RESTMapper {
|
||||
return c.mapper
|
||||
}
|
||||
|
||||
// Create implements client.Client.
|
||||
func (c *client) Create(ctx context.Context, obj Object, opts ...CreateOption) error {
|
||||
switch obj.(type) {
|
||||
case *unstructured.Unstructured:
|
||||
return c.unstructuredClient.Create(ctx, obj, opts...)
|
||||
case *metav1.PartialObjectMetadata:
|
||||
return fmt.Errorf("cannot create using only metadata")
|
||||
default:
|
||||
return c.typedClient.Create(ctx, obj, opts...)
|
||||
}
|
||||
}
|
||||
|
||||
// Update implements client.Client.
|
||||
func (c *client) Update(ctx context.Context, obj Object, opts ...UpdateOption) error {
|
||||
defer c.resetGroupVersionKind(obj, obj.GetObjectKind().GroupVersionKind())
|
||||
switch obj.(type) {
|
||||
case *unstructured.Unstructured:
|
||||
return c.unstructuredClient.Update(ctx, obj, opts...)
|
||||
case *metav1.PartialObjectMetadata:
|
||||
return fmt.Errorf("cannot update using only metadata -- did you mean to patch?")
|
||||
default:
|
||||
return c.typedClient.Update(ctx, obj, opts...)
|
||||
}
|
||||
}
|
||||
|
||||
// Delete implements client.Client.
|
||||
func (c *client) Delete(ctx context.Context, obj Object, opts ...DeleteOption) error {
|
||||
switch obj.(type) {
|
||||
case *unstructured.Unstructured:
|
||||
return c.unstructuredClient.Delete(ctx, obj, opts...)
|
||||
case *metav1.PartialObjectMetadata:
|
||||
return c.metadataClient.Delete(ctx, obj, opts...)
|
||||
default:
|
||||
return c.typedClient.Delete(ctx, obj, opts...)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteAllOf implements client.Client.
|
||||
func (c *client) DeleteAllOf(ctx context.Context, obj Object, opts ...DeleteAllOfOption) error {
|
||||
switch obj.(type) {
|
||||
case *unstructured.Unstructured:
|
||||
return c.unstructuredClient.DeleteAllOf(ctx, obj, opts...)
|
||||
case *metav1.PartialObjectMetadata:
|
||||
return c.metadataClient.DeleteAllOf(ctx, obj, opts...)
|
||||
default:
|
||||
return c.typedClient.DeleteAllOf(ctx, obj, opts...)
|
||||
}
|
||||
}
|
||||
|
||||
// Patch implements client.Client.
|
||||
func (c *client) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
|
||||
defer c.resetGroupVersionKind(obj, obj.GetObjectKind().GroupVersionKind())
|
||||
switch obj.(type) {
|
||||
case *unstructured.Unstructured:
|
||||
return c.unstructuredClient.Patch(ctx, obj, patch, opts...)
|
||||
case *metav1.PartialObjectMetadata:
|
||||
return c.metadataClient.Patch(ctx, obj, patch, opts...)
|
||||
default:
|
||||
return c.typedClient.Patch(ctx, obj, patch, opts...)
|
||||
}
|
||||
}
|
||||
|
||||
// Get implements client.Client.
|
||||
func (c *client) Get(ctx context.Context, key ObjectKey, obj Object, opts ...GetOption) error {
|
||||
switch obj.(type) {
|
||||
case *unstructured.Unstructured:
|
||||
return c.unstructuredClient.Get(ctx, key, obj, opts...)
|
||||
case *metav1.PartialObjectMetadata:
|
||||
// Metadata only object should always preserve the GVK coming in from the caller.
|
||||
defer c.resetGroupVersionKind(obj, obj.GetObjectKind().GroupVersionKind())
|
||||
return c.metadataClient.Get(ctx, key, obj, opts...)
|
||||
default:
|
||||
return c.typedClient.Get(ctx, key, obj, opts...)
|
||||
}
|
||||
}
|
||||
|
||||
// List implements client.Client.
|
||||
func (c *client) List(ctx context.Context, obj ObjectList, opts ...ListOption) error {
|
||||
switch x := obj.(type) {
|
||||
case *unstructured.UnstructuredList:
|
||||
return c.unstructuredClient.List(ctx, obj, opts...)
|
||||
case *metav1.PartialObjectMetadataList:
|
||||
// Metadata only object should always preserve the GVK.
|
||||
gvk := obj.GetObjectKind().GroupVersionKind()
|
||||
defer c.resetGroupVersionKind(obj, gvk)
|
||||
|
||||
// Call the list client.
|
||||
if err := c.metadataClient.List(ctx, obj, opts...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Restore the GVK for each item in the list.
|
||||
itemGVK := schema.GroupVersionKind{
|
||||
Group: gvk.Group,
|
||||
Version: gvk.Version,
|
||||
// TODO: this is producing unsafe guesses that don't actually work,
|
||||
// but it matches ~99% of the cases out there.
|
||||
Kind: strings.TrimSuffix(gvk.Kind, "List"),
|
||||
}
|
||||
for i := range x.Items {
|
||||
item := &x.Items[i]
|
||||
item.SetGroupVersionKind(itemGVK)
|
||||
}
|
||||
|
||||
return nil
|
||||
default:
|
||||
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 Object, opts ...UpdateOption) error {
|
||||
defer sw.client.resetGroupVersionKind(obj, obj.GetObjectKind().GroupVersionKind())
|
||||
switch obj.(type) {
|
||||
case *unstructured.Unstructured:
|
||||
return sw.client.unstructuredClient.UpdateStatus(ctx, obj, opts...)
|
||||
case *metav1.PartialObjectMetadata:
|
||||
return fmt.Errorf("cannot update status using only metadata -- did you mean to patch?")
|
||||
default:
|
||||
return sw.client.typedClient.UpdateStatus(ctx, obj, opts...)
|
||||
}
|
||||
}
|
||||
|
||||
// Patch implements client.Client.
|
||||
func (sw *statusWriter) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
|
||||
defer sw.client.resetGroupVersionKind(obj, obj.GetObjectKind().GroupVersionKind())
|
||||
switch obj.(type) {
|
||||
case *unstructured.Unstructured:
|
||||
return sw.client.unstructuredClient.PatchStatus(ctx, obj, patch, opts...)
|
||||
case *metav1.PartialObjectMetadata:
|
||||
return sw.client.metadataClient.PatchStatus(ctx, obj, patch, opts...)
|
||||
default:
|
||||
return sw.client.typedClient.PatchStatus(ctx, obj, patch, opts...)
|
||||
}
|
||||
}
|
150
vendor/sigs.k8s.io/controller-runtime/pkg/client/client_cache.go
generated
vendored
Normal file
150
vendor/sigs.k8s.io/controller-runtime/pkg/client/client_cache.go
generated
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
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/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/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
|
||||
|
||||
// structuredResourceByType caches structured type metadata
|
||||
structuredResourceByType map[schema.GroupVersionKind]*resourceMeta
|
||||
// unstructuredResourceByType caches unstructured type metadata
|
||||
unstructuredResourceByType 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, isUnstructured 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, isUnstructured, 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
|
||||
}
|
||||
|
||||
_, isUnstructured := obj.(*unstructured.Unstructured)
|
||||
_, isUnstructuredList := obj.(*unstructured.UnstructuredList)
|
||||
isUnstructured = isUnstructured || isUnstructuredList
|
||||
|
||||
// It's better to do creation work twice than to not let multiple
|
||||
// people make requests at once
|
||||
c.mu.RLock()
|
||||
resourceByType := c.structuredResourceByType
|
||||
if isUnstructured {
|
||||
resourceByType = c.unstructuredResourceByType
|
||||
}
|
||||
r, known := 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), isUnstructured)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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
|
||||
}
|
40
vendor/sigs.k8s.io/controller-runtime/pkg/client/codec.go
generated
vendored
Normal file
40
vendor/sigs.k8s.io/controller-runtime/pkg/client/codec.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
Copyright 2021 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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 (
|
||||
"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 resources.
|
||||
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")
|
||||
}
|
157
vendor/sigs.k8s.io/controller-runtime/pkg/client/config/config.go
generated
vendored
Normal file
157
vendor/sigs.k8s.io/controller-runtime/pkg/client/config/config.go
generated
vendored
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
|
||||
)
|
||||
|
||||
var (
|
||||
kubeconfig string
|
||||
log = logf.RuntimeLog.WithName("client").WithName("config")
|
||||
)
|
||||
|
||||
func init() {
|
||||
// TODO: Fix this to allow double vendoring this library but still register flags on behalf of users
|
||||
flag.StringVar(&kubeconfig, "kubeconfig", "",
|
||||
"Paths to a kubeconfig. Only required if out-of-cluster.")
|
||||
}
|
||||
|
||||
// GetConfig creates a *rest.Config for talking to a Kubernetes API server.
|
||||
// If --kubeconfig is set, will use the kubeconfig file at that location. Otherwise will assume running
|
||||
// in cluster and use the cluster provided kubeconfig.
|
||||
//
|
||||
// It also applies saner defaults for QPS and burst based on the Kubernetes
|
||||
// controller manager defaults (20 QPS, 30 burst)
|
||||
//
|
||||
// Config precedence:
|
||||
//
|
||||
// * --kubeconfig flag pointing at a file
|
||||
//
|
||||
// * KUBECONFIG environment variable pointing at a file
|
||||
//
|
||||
// * In-cluster config if running in cluster
|
||||
//
|
||||
// * $HOME/.kube/config if exists.
|
||||
func GetConfig() (*rest.Config, error) {
|
||||
return GetConfigWithContext("")
|
||||
}
|
||||
|
||||
// GetConfigWithContext creates a *rest.Config for talking to a Kubernetes API server with a specific context.
|
||||
// If --kubeconfig is set, will use the kubeconfig file at that location. Otherwise will assume running
|
||||
// in cluster and use the cluster provided kubeconfig.
|
||||
//
|
||||
// It also applies saner defaults for QPS and burst based on the Kubernetes
|
||||
// controller manager defaults (20 QPS, 30 burst)
|
||||
//
|
||||
// Config precedence:
|
||||
//
|
||||
// * --kubeconfig flag pointing at a file
|
||||
//
|
||||
// * KUBECONFIG environment variable pointing at a file
|
||||
//
|
||||
// * In-cluster config if running in cluster
|
||||
//
|
||||
// * $HOME/.kube/config if exists.
|
||||
func GetConfigWithContext(context string) (*rest.Config, error) {
|
||||
cfg, err := loadConfig(context)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cfg.QPS == 0.0 {
|
||||
cfg.QPS = 20.0
|
||||
cfg.Burst = 30.0
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// loadInClusterConfig is a function used to load the in-cluster
|
||||
// Kubernetes client config. This variable makes is possible to
|
||||
// test the precedence of loading the config.
|
||||
var loadInClusterConfig = rest.InClusterConfig
|
||||
|
||||
// loadConfig loads a REST Config as per the rules specified in GetConfig.
|
||||
func loadConfig(context string) (*rest.Config, error) {
|
||||
// If a flag is specified with the config location, use that
|
||||
if len(kubeconfig) > 0 {
|
||||
return loadConfigWithContext("", &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfig}, context)
|
||||
}
|
||||
|
||||
// If the recommended kubeconfig env variable is not specified,
|
||||
// try the in-cluster config.
|
||||
kubeconfigPath := os.Getenv(clientcmd.RecommendedConfigPathEnvVar)
|
||||
if len(kubeconfigPath) == 0 {
|
||||
if c, err := loadInClusterConfig(); err == nil {
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
|
||||
// If the recommended kubeconfig env variable is set, or there
|
||||
// is no in-cluster config, try the default recommended locations.
|
||||
//
|
||||
// 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: %w", err)
|
||||
}
|
||||
loadingRules.Precedence = append(loadingRules.Precedence, filepath.Join(u.HomeDir, clientcmd.RecommendedHomeDir, clientcmd.RecommendedFileName))
|
||||
}
|
||||
|
||||
return loadConfigWithContext("", loadingRules, context)
|
||||
}
|
||||
|
||||
func loadConfigWithContext(apiServerURL string, loader clientcmd.ClientConfigLoader, context string) (*rest.Config, error) {
|
||||
return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
|
||||
loader,
|
||||
&clientcmd.ConfigOverrides{
|
||||
ClusterInfo: clientcmdapi.Cluster{
|
||||
Server: apiServerURL,
|
||||
},
|
||||
CurrentContext: context,
|
||||
}).ClientConfig()
|
||||
}
|
||||
|
||||
// GetConfigOrDie creates a *rest.Config for talking to a Kubernetes apiserver.
|
||||
// If --kubeconfig is set, will use the kubeconfig file at that location. Otherwise will assume running
|
||||
// in cluster and use the cluster provided kubeconfig.
|
||||
//
|
||||
// Will log an error and exit if there is an error creating the rest.Config.
|
||||
func GetConfigOrDie() *rest.Config {
|
||||
config, err := GetConfig()
|
||||
if err != nil {
|
||||
log.Error(err, "unable to get kubeconfig")
|
||||
os.Exit(1)
|
||||
}
|
||||
return config
|
||||
}
|
18
vendor/sigs.k8s.io/controller-runtime/pkg/client/config/doc.go
generated
vendored
Normal file
18
vendor/sigs.k8s.io/controller-runtime/pkg/client/config/doc.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package config contains libraries for initializing REST configs for talking to the Kubernetes API
|
||||
package config
|
50
vendor/sigs.k8s.io/controller-runtime/pkg/client/doc.go
generated
vendored
Normal file
50
vendor/sigs.k8s.io/controller-runtime/pkg/client/doc.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
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.
|
||||
//
|
||||
// It is 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
|
106
vendor/sigs.k8s.io/controller-runtime/pkg/client/dryrun.go
generated
vendored
Normal file
106
vendor/sigs.k8s.io/controller-runtime/pkg/client/dryrun.go
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
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 client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// NewDryRunClient wraps an existing client and enforces DryRun mode
|
||||
// on all mutating api calls.
|
||||
func NewDryRunClient(c Client) Client {
|
||||
return &dryRunClient{client: c}
|
||||
}
|
||||
|
||||
var _ Client = &dryRunClient{}
|
||||
|
||||
// dryRunClient is a Client that wraps another Client in order to enforce DryRun mode.
|
||||
type dryRunClient struct {
|
||||
client Client
|
||||
}
|
||||
|
||||
// Scheme returns the scheme this client is using.
|
||||
func (c *dryRunClient) Scheme() *runtime.Scheme {
|
||||
return c.client.Scheme()
|
||||
}
|
||||
|
||||
// RESTMapper returns the rest mapper this client is using.
|
||||
func (c *dryRunClient) RESTMapper() meta.RESTMapper {
|
||||
return c.client.RESTMapper()
|
||||
}
|
||||
|
||||
// Create implements client.Client.
|
||||
func (c *dryRunClient) Create(ctx context.Context, obj Object, opts ...CreateOption) error {
|
||||
return c.client.Create(ctx, obj, append(opts, DryRunAll)...)
|
||||
}
|
||||
|
||||
// Update implements client.Client.
|
||||
func (c *dryRunClient) Update(ctx context.Context, obj Object, opts ...UpdateOption) error {
|
||||
return c.client.Update(ctx, obj, append(opts, DryRunAll)...)
|
||||
}
|
||||
|
||||
// Delete implements client.Client.
|
||||
func (c *dryRunClient) Delete(ctx context.Context, obj Object, opts ...DeleteOption) error {
|
||||
return c.client.Delete(ctx, obj, append(opts, DryRunAll)...)
|
||||
}
|
||||
|
||||
// DeleteAllOf implements client.Client.
|
||||
func (c *dryRunClient) DeleteAllOf(ctx context.Context, obj Object, opts ...DeleteAllOfOption) error {
|
||||
return c.client.DeleteAllOf(ctx, obj, append(opts, DryRunAll)...)
|
||||
}
|
||||
|
||||
// Patch implements client.Client.
|
||||
func (c *dryRunClient) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
|
||||
return c.client.Patch(ctx, obj, patch, append(opts, DryRunAll)...)
|
||||
}
|
||||
|
||||
// Get implements client.Client.
|
||||
func (c *dryRunClient) Get(ctx context.Context, key ObjectKey, obj Object, opts ...GetOption) error {
|
||||
return c.client.Get(ctx, key, obj, opts...)
|
||||
}
|
||||
|
||||
// List implements client.Client.
|
||||
func (c *dryRunClient) List(ctx context.Context, obj ObjectList, opts ...ListOption) error {
|
||||
return c.client.List(ctx, obj, opts...)
|
||||
}
|
||||
|
||||
// Status implements client.StatusClient.
|
||||
func (c *dryRunClient) Status() StatusWriter {
|
||||
return &dryRunStatusWriter{client: c.client.Status()}
|
||||
}
|
||||
|
||||
// ensure dryRunStatusWriter implements client.StatusWriter.
|
||||
var _ StatusWriter = &dryRunStatusWriter{}
|
||||
|
||||
// dryRunStatusWriter is client.StatusWriter that writes status subresource with dryRun mode
|
||||
// enforced.
|
||||
type dryRunStatusWriter struct {
|
||||
client StatusWriter
|
||||
}
|
||||
|
||||
// Update implements client.StatusWriter.
|
||||
func (sw *dryRunStatusWriter) Update(ctx context.Context, obj Object, opts ...UpdateOption) error {
|
||||
return sw.client.Update(ctx, obj, append(opts, DryRunAll)...)
|
||||
}
|
||||
|
||||
// Patch implements client.StatusWriter.
|
||||
func (sw *dryRunStatusWriter) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
|
||||
return sw.client.Patch(ctx, obj, patch, append(opts, DryRunAll)...)
|
||||
}
|
787
vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/client.go
generated
vendored
Normal file
787
vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/client.go
generated
vendored
Normal file
@@ -0,0 +1,787 @@
|
||||
/*
|
||||
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 fake
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
utilrand "k8s.io/apimachinery/pkg/util/rand"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/testing"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/internal/objectutil"
|
||||
)
|
||||
|
||||
type versionedTracker struct {
|
||||
testing.ObjectTracker
|
||||
scheme *runtime.Scheme
|
||||
}
|
||||
|
||||
type fakeClient struct {
|
||||
tracker versionedTracker
|
||||
scheme *runtime.Scheme
|
||||
restMapper meta.RESTMapper
|
||||
schemeWriteLock sync.Mutex
|
||||
}
|
||||
|
||||
var _ client.WithWatch = &fakeClient{}
|
||||
|
||||
const (
|
||||
maxNameLength = 63
|
||||
randomLength = 5
|
||||
maxGeneratedNameLength = maxNameLength - randomLength
|
||||
)
|
||||
|
||||
// NewFakeClient creates a new fake client for testing.
|
||||
// You can choose to initialize it with a slice of runtime.Object.
|
||||
//
|
||||
// Deprecated: Please use NewClientBuilder instead.
|
||||
func NewFakeClient(initObjs ...runtime.Object) client.WithWatch {
|
||||
return NewClientBuilder().WithRuntimeObjects(initObjs...).Build()
|
||||
}
|
||||
|
||||
// NewFakeClientWithScheme creates a new fake client with the given scheme
|
||||
// for testing.
|
||||
// You can choose to initialize it with a slice of runtime.Object.
|
||||
//
|
||||
// Deprecated: Please use NewClientBuilder instead.
|
||||
func NewFakeClientWithScheme(clientScheme *runtime.Scheme, initObjs ...runtime.Object) client.WithWatch {
|
||||
return NewClientBuilder().WithScheme(clientScheme).WithRuntimeObjects(initObjs...).Build()
|
||||
}
|
||||
|
||||
// NewClientBuilder returns a new builder to create a fake client.
|
||||
func NewClientBuilder() *ClientBuilder {
|
||||
return &ClientBuilder{}
|
||||
}
|
||||
|
||||
// ClientBuilder builds a fake client.
|
||||
type ClientBuilder struct {
|
||||
scheme *runtime.Scheme
|
||||
restMapper meta.RESTMapper
|
||||
initObject []client.Object
|
||||
initLists []client.ObjectList
|
||||
initRuntimeObjects []runtime.Object
|
||||
objectTracker testing.ObjectTracker
|
||||
}
|
||||
|
||||
// WithScheme sets this builder's internal scheme.
|
||||
// If not set, defaults to client-go's global scheme.Scheme.
|
||||
func (f *ClientBuilder) WithScheme(scheme *runtime.Scheme) *ClientBuilder {
|
||||
f.scheme = scheme
|
||||
return f
|
||||
}
|
||||
|
||||
// WithRESTMapper sets this builder's restMapper.
|
||||
// The restMapper is directly set as mapper in the Client. This can be used for example
|
||||
// with a meta.DefaultRESTMapper to provide a static rest mapping.
|
||||
// If not set, defaults to an empty meta.DefaultRESTMapper.
|
||||
func (f *ClientBuilder) WithRESTMapper(restMapper meta.RESTMapper) *ClientBuilder {
|
||||
f.restMapper = restMapper
|
||||
return f
|
||||
}
|
||||
|
||||
// WithObjects can be optionally used to initialize this fake client with client.Object(s).
|
||||
func (f *ClientBuilder) WithObjects(initObjs ...client.Object) *ClientBuilder {
|
||||
f.initObject = append(f.initObject, initObjs...)
|
||||
return f
|
||||
}
|
||||
|
||||
// WithLists can be optionally used to initialize this fake client with client.ObjectList(s).
|
||||
func (f *ClientBuilder) WithLists(initLists ...client.ObjectList) *ClientBuilder {
|
||||
f.initLists = append(f.initLists, initLists...)
|
||||
return f
|
||||
}
|
||||
|
||||
// WithRuntimeObjects can be optionally used to initialize this fake client with runtime.Object(s).
|
||||
func (f *ClientBuilder) WithRuntimeObjects(initRuntimeObjs ...runtime.Object) *ClientBuilder {
|
||||
f.initRuntimeObjects = append(f.initRuntimeObjects, initRuntimeObjs...)
|
||||
return f
|
||||
}
|
||||
|
||||
// WithObjectTracker can be optionally used to initialize this fake client with testing.ObjectTracker.
|
||||
func (f *ClientBuilder) WithObjectTracker(ot testing.ObjectTracker) *ClientBuilder {
|
||||
f.objectTracker = ot
|
||||
return f
|
||||
}
|
||||
|
||||
// Build builds and returns a new fake client.
|
||||
func (f *ClientBuilder) Build() client.WithWatch {
|
||||
if f.scheme == nil {
|
||||
f.scheme = scheme.Scheme
|
||||
}
|
||||
if f.restMapper == nil {
|
||||
f.restMapper = meta.NewDefaultRESTMapper([]schema.GroupVersion{})
|
||||
}
|
||||
|
||||
var tracker versionedTracker
|
||||
|
||||
if f.objectTracker == nil {
|
||||
tracker = versionedTracker{ObjectTracker: testing.NewObjectTracker(f.scheme, scheme.Codecs.UniversalDecoder()), scheme: f.scheme}
|
||||
} else {
|
||||
tracker = versionedTracker{ObjectTracker: f.objectTracker, scheme: f.scheme}
|
||||
}
|
||||
|
||||
for _, obj := range f.initObject {
|
||||
if err := tracker.Add(obj); err != nil {
|
||||
panic(fmt.Errorf("failed to add object %v to fake client: %w", obj, err))
|
||||
}
|
||||
}
|
||||
for _, obj := range f.initLists {
|
||||
if err := tracker.Add(obj); err != nil {
|
||||
panic(fmt.Errorf("failed to add list %v to fake client: %w", obj, err))
|
||||
}
|
||||
}
|
||||
for _, obj := range f.initRuntimeObjects {
|
||||
if err := tracker.Add(obj); err != nil {
|
||||
panic(fmt.Errorf("failed to add runtime object %v to fake client: %w", obj, err))
|
||||
}
|
||||
}
|
||||
return &fakeClient{
|
||||
tracker: tracker,
|
||||
scheme: f.scheme,
|
||||
restMapper: f.restMapper,
|
||||
}
|
||||
}
|
||||
|
||||
const trackerAddResourceVersion = "999"
|
||||
|
||||
func (t versionedTracker) Add(obj runtime.Object) error {
|
||||
var objects []runtime.Object
|
||||
if meta.IsListType(obj) {
|
||||
var err error
|
||||
objects, err = meta.ExtractList(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
objects = []runtime.Object{obj}
|
||||
}
|
||||
for _, obj := range objects {
|
||||
accessor, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get accessor for object: %w", err)
|
||||
}
|
||||
if accessor.GetResourceVersion() == "" {
|
||||
// We use a "magic" value of 999 here because this field
|
||||
// is parsed as uint and and 0 is already used in Update.
|
||||
// As we can't go lower, go very high instead so this can
|
||||
// be recognized
|
||||
accessor.SetResourceVersion(trackerAddResourceVersion)
|
||||
}
|
||||
|
||||
obj, err = convertFromUnstructuredIfNecessary(t.scheme, obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := t.ObjectTracker.Add(obj); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t versionedTracker) Create(gvr schema.GroupVersionResource, obj runtime.Object, ns string) error {
|
||||
accessor, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get accessor for object: %w", err)
|
||||
}
|
||||
if accessor.GetName() == "" {
|
||||
return apierrors.NewInvalid(
|
||||
obj.GetObjectKind().GroupVersionKind().GroupKind(),
|
||||
accessor.GetName(),
|
||||
field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")})
|
||||
}
|
||||
if accessor.GetResourceVersion() != "" {
|
||||
return apierrors.NewBadRequest("resourceVersion can not be set for Create requests")
|
||||
}
|
||||
accessor.SetResourceVersion("1")
|
||||
obj, err = convertFromUnstructuredIfNecessary(t.scheme, obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := t.ObjectTracker.Create(gvr, obj, ns); err != nil {
|
||||
accessor.SetResourceVersion("")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// convertFromUnstructuredIfNecessary will convert *unstructured.Unstructured for a GVK that is recocnized
|
||||
// by the schema into the whatever the schema produces with New() for said GVK.
|
||||
// This is required because the tracker unconditionally saves on manipulations, but its List() implementation
|
||||
// tries to assign whatever it finds into a ListType it gets from schema.New() - Thus we have to ensure
|
||||
// we save as the very same type, otherwise subsequent List requests will fail.
|
||||
func convertFromUnstructuredIfNecessary(s *runtime.Scheme, o runtime.Object) (runtime.Object, error) {
|
||||
u, isUnstructured := o.(*unstructured.Unstructured)
|
||||
if !isUnstructured || !s.Recognizes(u.GroupVersionKind()) {
|
||||
return o, nil
|
||||
}
|
||||
|
||||
typed, err := s.New(u.GroupVersionKind())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("scheme recognizes %s but failed to produce an object for it: %w", u.GroupVersionKind().String(), err)
|
||||
}
|
||||
|
||||
unstructuredSerialized, err := json.Marshal(u)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to serialize %T: %w", unstructuredSerialized, err)
|
||||
}
|
||||
if err := json.Unmarshal(unstructuredSerialized, typed); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal the content of %T into %T: %w", u, typed, err)
|
||||
}
|
||||
|
||||
return typed, nil
|
||||
}
|
||||
|
||||
func (t versionedTracker) Update(gvr schema.GroupVersionResource, obj runtime.Object, ns string) error {
|
||||
accessor, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get accessor for object: %w", err)
|
||||
}
|
||||
|
||||
if accessor.GetName() == "" {
|
||||
return apierrors.NewInvalid(
|
||||
obj.GetObjectKind().GroupVersionKind().GroupKind(),
|
||||
accessor.GetName(),
|
||||
field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")})
|
||||
}
|
||||
|
||||
gvk := obj.GetObjectKind().GroupVersionKind()
|
||||
if gvk.Empty() {
|
||||
gvk, err = apiutil.GVKForObject(obj, t.scheme)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
oldObject, err := t.ObjectTracker.Get(gvr, ns, accessor.GetName())
|
||||
if err != nil {
|
||||
// If the resource is not found and the resource allows create on update, issue a
|
||||
// create instead.
|
||||
if apierrors.IsNotFound(err) && allowsCreateOnUpdate(gvk) {
|
||||
return t.Create(gvr, obj, ns)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
oldAccessor, err := meta.Accessor(oldObject)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If the new object does not have the resource version set and it allows unconditional update,
|
||||
// default it to the resource version of the existing resource
|
||||
if accessor.GetResourceVersion() == "" && allowsUnconditionalUpdate(gvk) {
|
||||
accessor.SetResourceVersion(oldAccessor.GetResourceVersion())
|
||||
}
|
||||
if accessor.GetResourceVersion() != oldAccessor.GetResourceVersion() {
|
||||
return apierrors.NewConflict(gvr.GroupResource(), accessor.GetName(), errors.New("object was modified"))
|
||||
}
|
||||
if oldAccessor.GetResourceVersion() == "" {
|
||||
oldAccessor.SetResourceVersion("0")
|
||||
}
|
||||
intResourceVersion, err := strconv.ParseUint(oldAccessor.GetResourceVersion(), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can not convert resourceVersion %q to int: %w", oldAccessor.GetResourceVersion(), err)
|
||||
}
|
||||
intResourceVersion++
|
||||
accessor.SetResourceVersion(strconv.FormatUint(intResourceVersion, 10))
|
||||
if !accessor.GetDeletionTimestamp().IsZero() && len(accessor.GetFinalizers()) == 0 {
|
||||
return t.ObjectTracker.Delete(gvr, accessor.GetNamespace(), accessor.GetName())
|
||||
}
|
||||
obj, err = convertFromUnstructuredIfNecessary(t.scheme, obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return t.ObjectTracker.Update(gvr, obj, ns)
|
||||
}
|
||||
|
||||
func (c *fakeClient) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
|
||||
gvr, err := getGVRFromObject(obj, c.scheme)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o, err := c.tracker.Get(gvr, key.Namespace, key.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gvk, err := apiutil.GVKForObject(obj, c.scheme)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ta, err := meta.TypeAccessor(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ta.SetKind(gvk.Kind)
|
||||
ta.SetAPIVersion(gvk.GroupVersion().String())
|
||||
|
||||
j, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
decoder := scheme.Codecs.UniversalDecoder()
|
||||
zero(obj)
|
||||
_, _, err = decoder.Decode(j, nil, obj)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *fakeClient) Watch(ctx context.Context, list client.ObjectList, opts ...client.ListOption) (watch.Interface, error) {
|
||||
gvk, err := apiutil.GVKForObject(list, c.scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gvk.Kind = strings.TrimSuffix(gvk.Kind, "List")
|
||||
|
||||
listOpts := client.ListOptions{}
|
||||
listOpts.ApplyOptions(opts)
|
||||
|
||||
gvr, _ := meta.UnsafeGuessKindToResource(gvk)
|
||||
return c.tracker.Watch(gvr, listOpts.Namespace)
|
||||
}
|
||||
|
||||
func (c *fakeClient) List(ctx context.Context, obj client.ObjectList, opts ...client.ListOption) error {
|
||||
gvk, err := apiutil.GVKForObject(obj, c.scheme)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
originalKind := gvk.Kind
|
||||
|
||||
gvk.Kind = strings.TrimSuffix(gvk.Kind, "List")
|
||||
|
||||
if _, isUnstructuredList := obj.(*unstructured.UnstructuredList); isUnstructuredList && !c.scheme.Recognizes(gvk) {
|
||||
// We need to register the ListKind with UnstructuredList:
|
||||
// https://github.com/kubernetes/kubernetes/blob/7b2776b89fb1be28d4e9203bdeec079be903c103/staging/src/k8s.io/client-go/dynamic/fake/simple.go#L44-L51
|
||||
c.schemeWriteLock.Lock()
|
||||
c.scheme.AddKnownTypeWithName(gvk.GroupVersion().WithKind(gvk.Kind+"List"), &unstructured.UnstructuredList{})
|
||||
c.schemeWriteLock.Unlock()
|
||||
}
|
||||
|
||||
listOpts := client.ListOptions{}
|
||||
listOpts.ApplyOptions(opts)
|
||||
|
||||
gvr, _ := meta.UnsafeGuessKindToResource(gvk)
|
||||
o, err := c.tracker.List(gvr, gvk, listOpts.Namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ta, err := meta.TypeAccessor(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ta.SetKind(originalKind)
|
||||
ta.SetAPIVersion(gvk.GroupVersion().String())
|
||||
|
||||
j, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
decoder := scheme.Codecs.UniversalDecoder()
|
||||
zero(obj)
|
||||
_, _, err = decoder.Decode(j, nil, obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if listOpts.LabelSelector != nil {
|
||||
objs, err := meta.ExtractList(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filteredObjs, err := objectutil.FilterWithLabels(objs, listOpts.LabelSelector)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = meta.SetList(obj, filteredObjs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *fakeClient) Scheme() *runtime.Scheme {
|
||||
return c.scheme
|
||||
}
|
||||
|
||||
func (c *fakeClient) RESTMapper() meta.RESTMapper {
|
||||
return c.restMapper
|
||||
}
|
||||
|
||||
func (c *fakeClient) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error {
|
||||
createOptions := &client.CreateOptions{}
|
||||
createOptions.ApplyOptions(opts)
|
||||
|
||||
for _, dryRunOpt := range createOptions.DryRun {
|
||||
if dryRunOpt == metav1.DryRunAll {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
gvr, err := getGVRFromObject(obj, c.scheme)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
accessor, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if accessor.GetName() == "" && accessor.GetGenerateName() != "" {
|
||||
base := accessor.GetGenerateName()
|
||||
if len(base) > maxGeneratedNameLength {
|
||||
base = base[:maxGeneratedNameLength]
|
||||
}
|
||||
accessor.SetName(fmt.Sprintf("%s%s", base, utilrand.String(randomLength)))
|
||||
}
|
||||
|
||||
return c.tracker.Create(gvr, obj, accessor.GetNamespace())
|
||||
}
|
||||
|
||||
func (c *fakeClient) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error {
|
||||
gvr, err := getGVRFromObject(obj, c.scheme)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
accessor, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
delOptions := client.DeleteOptions{}
|
||||
delOptions.ApplyOptions(opts)
|
||||
|
||||
for _, dryRunOpt := range delOptions.DryRun {
|
||||
if dryRunOpt == metav1.DryRunAll {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Check the ResourceVersion if that Precondition was specified.
|
||||
if delOptions.Preconditions != nil && delOptions.Preconditions.ResourceVersion != nil {
|
||||
name := accessor.GetName()
|
||||
dbObj, err := c.tracker.Get(gvr, accessor.GetNamespace(), name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oldAccessor, err := meta.Accessor(dbObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
actualRV := oldAccessor.GetResourceVersion()
|
||||
expectRV := *delOptions.Preconditions.ResourceVersion
|
||||
if actualRV != expectRV {
|
||||
msg := fmt.Sprintf(
|
||||
"the ResourceVersion in the precondition (%s) does not match the ResourceVersion in record (%s). "+
|
||||
"The object might have been modified",
|
||||
expectRV, actualRV)
|
||||
return apierrors.NewConflict(gvr.GroupResource(), name, errors.New(msg))
|
||||
}
|
||||
}
|
||||
|
||||
return c.deleteObject(gvr, accessor)
|
||||
}
|
||||
|
||||
func (c *fakeClient) DeleteAllOf(ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption) error {
|
||||
gvk, err := apiutil.GVKForObject(obj, c.scheme)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dcOptions := client.DeleteAllOfOptions{}
|
||||
dcOptions.ApplyOptions(opts)
|
||||
|
||||
for _, dryRunOpt := range dcOptions.DryRun {
|
||||
if dryRunOpt == metav1.DryRunAll {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
gvr, _ := meta.UnsafeGuessKindToResource(gvk)
|
||||
o, err := c.tracker.List(gvr, gvk, dcOptions.Namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
objs, err := meta.ExtractList(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filteredObjs, err := objectutil.FilterWithLabels(objs, dcOptions.LabelSelector)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, o := range filteredObjs {
|
||||
accessor, err := meta.Accessor(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.deleteObject(gvr, accessor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *fakeClient) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error {
|
||||
updateOptions := &client.UpdateOptions{}
|
||||
updateOptions.ApplyOptions(opts)
|
||||
|
||||
for _, dryRunOpt := range updateOptions.DryRun {
|
||||
if dryRunOpt == metav1.DryRunAll {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
gvr, err := getGVRFromObject(obj, c.scheme)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
accessor, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.tracker.Update(gvr, obj, accessor.GetNamespace())
|
||||
}
|
||||
|
||||
func (c *fakeClient) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error {
|
||||
patchOptions := &client.PatchOptions{}
|
||||
patchOptions.ApplyOptions(opts)
|
||||
|
||||
for _, dryRunOpt := range patchOptions.DryRun {
|
||||
if dryRunOpt == metav1.DryRunAll {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
gvr, err := getGVRFromObject(obj, c.scheme)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
accessor, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := patch.Data(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
reaction := testing.ObjectReaction(c.tracker)
|
||||
handled, o, err := reaction(testing.NewPatchAction(gvr, accessor.GetNamespace(), accessor.GetName(), patch.Type(), data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !handled {
|
||||
panic("tracker could not handle patch method")
|
||||
}
|
||||
|
||||
gvk, err := apiutil.GVKForObject(obj, c.scheme)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ta, err := meta.TypeAccessor(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ta.SetKind(gvk.Kind)
|
||||
ta.SetAPIVersion(gvk.GroupVersion().String())
|
||||
|
||||
j, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
decoder := scheme.Codecs.UniversalDecoder()
|
||||
zero(obj)
|
||||
_, _, err = decoder.Decode(j, nil, obj)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *fakeClient) Status() client.StatusWriter {
|
||||
return &fakeStatusWriter{client: c}
|
||||
}
|
||||
|
||||
func (c *fakeClient) deleteObject(gvr schema.GroupVersionResource, accessor metav1.Object) error {
|
||||
old, err := c.tracker.Get(gvr, accessor.GetNamespace(), accessor.GetName())
|
||||
if err == nil {
|
||||
oldAccessor, err := meta.Accessor(old)
|
||||
if err == nil {
|
||||
if len(oldAccessor.GetFinalizers()) > 0 {
|
||||
now := metav1.Now()
|
||||
oldAccessor.SetDeletionTimestamp(&now)
|
||||
return c.tracker.Update(gvr, old, accessor.GetNamespace())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: implement propagation
|
||||
return c.tracker.Delete(gvr, accessor.GetNamespace(), accessor.GetName())
|
||||
}
|
||||
|
||||
func getGVRFromObject(obj runtime.Object, scheme *runtime.Scheme) (schema.GroupVersionResource, error) {
|
||||
gvk, err := apiutil.GVKForObject(obj, scheme)
|
||||
if err != nil {
|
||||
return schema.GroupVersionResource{}, err
|
||||
}
|
||||
gvr, _ := meta.UnsafeGuessKindToResource(gvk)
|
||||
return gvr, nil
|
||||
}
|
||||
|
||||
type fakeStatusWriter struct {
|
||||
client *fakeClient
|
||||
}
|
||||
|
||||
func (sw *fakeStatusWriter) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error {
|
||||
// TODO(droot): This results in full update of the obj (spec + status). Need
|
||||
// a way to update status field only.
|
||||
return sw.client.Update(ctx, obj, opts...)
|
||||
}
|
||||
|
||||
func (sw *fakeStatusWriter) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error {
|
||||
// TODO(droot): This results in full update of the obj (spec + status). Need
|
||||
// a way to update status field only.
|
||||
return sw.client.Patch(ctx, obj, patch, opts...)
|
||||
}
|
||||
|
||||
func allowsUnconditionalUpdate(gvk schema.GroupVersionKind) bool {
|
||||
switch gvk.Group {
|
||||
case "apps":
|
||||
switch gvk.Kind {
|
||||
case "ControllerRevision", "DaemonSet", "Deployment", "ReplicaSet", "StatefulSet":
|
||||
return true
|
||||
}
|
||||
case "autoscaling":
|
||||
switch gvk.Kind {
|
||||
case "HorizontalPodAutoscaler":
|
||||
return true
|
||||
}
|
||||
case "batch":
|
||||
switch gvk.Kind {
|
||||
case "CronJob", "Job":
|
||||
return true
|
||||
}
|
||||
case "certificates":
|
||||
switch gvk.Kind {
|
||||
case "Certificates":
|
||||
return true
|
||||
}
|
||||
case "flowcontrol":
|
||||
switch gvk.Kind {
|
||||
case "FlowSchema", "PriorityLevelConfiguration":
|
||||
return true
|
||||
}
|
||||
case "networking":
|
||||
switch gvk.Kind {
|
||||
case "Ingress", "IngressClass", "NetworkPolicy":
|
||||
return true
|
||||
}
|
||||
case "policy":
|
||||
switch gvk.Kind {
|
||||
case "PodSecurityPolicy":
|
||||
return true
|
||||
}
|
||||
case "rbac":
|
||||
switch gvk.Kind {
|
||||
case "ClusterRole", "ClusterRoleBinding", "Role", "RoleBinding":
|
||||
return true
|
||||
}
|
||||
case "scheduling":
|
||||
switch gvk.Kind {
|
||||
case "PriorityClass":
|
||||
return true
|
||||
}
|
||||
case "settings":
|
||||
switch gvk.Kind {
|
||||
case "PodPreset":
|
||||
return true
|
||||
}
|
||||
case "storage":
|
||||
switch gvk.Kind {
|
||||
case "StorageClass":
|
||||
return true
|
||||
}
|
||||
case "":
|
||||
switch gvk.Kind {
|
||||
case "ConfigMap", "Endpoint", "Event", "LimitRange", "Namespace", "Node",
|
||||
"PersistentVolume", "PersistentVolumeClaim", "Pod", "PodTemplate",
|
||||
"ReplicationController", "ResourceQuota", "Secret", "Service",
|
||||
"ServiceAccount", "EndpointSlice":
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func allowsCreateOnUpdate(gvk schema.GroupVersionKind) bool {
|
||||
switch gvk.Group {
|
||||
case "coordination":
|
||||
switch gvk.Kind {
|
||||
case "Lease":
|
||||
return true
|
||||
}
|
||||
case "node":
|
||||
switch gvk.Kind {
|
||||
case "RuntimeClass":
|
||||
return true
|
||||
}
|
||||
case "rbac":
|
||||
switch gvk.Kind {
|
||||
case "ClusterRole", "ClusterRoleBinding", "Role", "RoleBinding":
|
||||
return true
|
||||
}
|
||||
case "":
|
||||
switch gvk.Kind {
|
||||
case "Endpoint", "Event", "LimitRange", "Service":
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// zero zeros the value of a pointer.
|
||||
func zero(x interface{}) {
|
||||
if x == nil {
|
||||
return
|
||||
}
|
||||
res := reflect.ValueOf(x).Elem()
|
||||
res.Set(reflect.Zero(res.Type()))
|
||||
}
|
38
vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/doc.go
generated
vendored
Normal file
38
vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/doc.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
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 fake provides a fake client for testing.
|
||||
|
||||
A fake client is backed by its simple object store indexed by GroupVersionResource.
|
||||
You can create a fake client with optional objects.
|
||||
|
||||
client := NewFakeClientWithScheme(scheme, initObjs...) // initObjs is a slice of runtime.Object
|
||||
|
||||
You can invoke the methods defined in the Client interface.
|
||||
|
||||
When in doubt, it's almost always better not to use this package and instead use
|
||||
envtest.Environment with a real client and API server.
|
||||
|
||||
WARNING: ⚠️ Current Limitations / Known Issues with the fake Client ⚠️
|
||||
- This client does not have a way to inject specific errors to test handled vs. unhandled errors.
|
||||
- There is some support for sub resources which can cause issues with tests if you're trying to update
|
||||
e.g. metadata and status in the same reconcile.
|
||||
- No OpenAPI validation is performed when creating or updating objects.
|
||||
- ObjectMeta's `Generation` and `ResourceVersion` don't behave properly, Patch or Update
|
||||
operations that rely on these fields will fail, or give false positives.
|
||||
*/
|
||||
package fake
|
155
vendor/sigs.k8s.io/controller-runtime/pkg/client/interfaces.go
generated
vendored
Normal file
155
vendor/sigs.k8s.io/controller-runtime/pkg/client/interfaces.go
generated
vendored
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
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"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
)
|
||||
|
||||
// ObjectKey identifies a Kubernetes Object.
|
||||
type ObjectKey = types.NamespacedName
|
||||
|
||||
// ObjectKeyFromObject returns the ObjectKey given a runtime.Object.
|
||||
func ObjectKeyFromObject(obj Object) ObjectKey {
|
||||
return ObjectKey{Namespace: obj.GetNamespace(), Name: obj.GetName()}
|
||||
}
|
||||
|
||||
// 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 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 Object, opts ...GetOption) 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 ObjectList, 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 Object, opts ...CreateOption) error
|
||||
|
||||
// Delete deletes the given obj from Kubernetes cluster.
|
||||
Delete(ctx context.Context, obj 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 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 Object, patch Patch, opts ...PatchOption) error
|
||||
|
||||
// DeleteAllOf deletes all objects of the given type matching the given options.
|
||||
DeleteAllOf(ctx context.Context, obj 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 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 Object, patch Patch, opts ...PatchOption) error
|
||||
}
|
||||
|
||||
// Client knows how to perform CRUD operations on Kubernetes objects.
|
||||
type Client interface {
|
||||
Reader
|
||||
Writer
|
||||
StatusClient
|
||||
|
||||
// Scheme returns the scheme this client is using.
|
||||
Scheme() *runtime.Scheme
|
||||
// RESTMapper returns the rest this client is using.
|
||||
RESTMapper() meta.RESTMapper
|
||||
}
|
||||
|
||||
// WithWatch supports Watch on top of the CRUD operations supported by
|
||||
// the normal Client. Its intended use-case are CLI apps that need to wait for
|
||||
// events.
|
||||
type WithWatch interface {
|
||||
Client
|
||||
Watch(ctx context.Context, obj ObjectList, opts ...ListOption) (watch.Interface, error)
|
||||
}
|
||||
|
||||
// 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(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 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
|
||||
}
|
||||
|
||||
// IgnoreAlreadyExists returns nil on AlreadyExists errors.
|
||||
// All other values that are not AlreadyExists errors or nil are returned unmodified.
|
||||
func IgnoreAlreadyExists(err error) error {
|
||||
if apierrors.IsAlreadyExists(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
196
vendor/sigs.k8s.io/controller-runtime/pkg/client/metadata_client.go
generated
vendored
Normal file
196
vendor/sigs.k8s.io/controller-runtime/pkg/client/metadata_client.go
generated
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
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 client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/metadata"
|
||||
)
|
||||
|
||||
// TODO(directxman12): we could rewrite this on top of the low-level REST
|
||||
// client to avoid the extra shallow copy at the end, but I'm not sure it's
|
||||
// worth it -- the metadata client deals with falling back to loading the whole
|
||||
// object on older API servers, etc, and we'd have to reproduce that.
|
||||
|
||||
// metadataClient is a client that reads & writes metadata-only requests to/from the API server.
|
||||
type metadataClient struct {
|
||||
client metadata.Interface
|
||||
restMapper meta.RESTMapper
|
||||
}
|
||||
|
||||
func (mc *metadataClient) getResourceInterface(gvk schema.GroupVersionKind, ns string) (metadata.ResourceInterface, error) {
|
||||
mapping, err := mc.restMapper.RESTMapping(gvk.GroupKind(), gvk.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if mapping.Scope.Name() == meta.RESTScopeNameRoot {
|
||||
return mc.client.Resource(mapping.Resource), nil
|
||||
}
|
||||
return mc.client.Resource(mapping.Resource).Namespace(ns), nil
|
||||
}
|
||||
|
||||
// Delete implements client.Client.
|
||||
func (mc *metadataClient) Delete(ctx context.Context, obj Object, opts ...DeleteOption) error {
|
||||
metadata, ok := obj.(*metav1.PartialObjectMetadata)
|
||||
if !ok {
|
||||
return fmt.Errorf("metadata client did not understand object: %T", obj)
|
||||
}
|
||||
|
||||
resInt, err := mc.getResourceInterface(metadata.GroupVersionKind(), metadata.Namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deleteOpts := DeleteOptions{}
|
||||
deleteOpts.ApplyOptions(opts)
|
||||
|
||||
return resInt.Delete(ctx, metadata.Name, *deleteOpts.AsDeleteOptions())
|
||||
}
|
||||
|
||||
// DeleteAllOf implements client.Client.
|
||||
func (mc *metadataClient) DeleteAllOf(ctx context.Context, obj Object, opts ...DeleteAllOfOption) error {
|
||||
metadata, ok := obj.(*metav1.PartialObjectMetadata)
|
||||
if !ok {
|
||||
return fmt.Errorf("metadata client did not understand object: %T", obj)
|
||||
}
|
||||
|
||||
deleteAllOfOpts := DeleteAllOfOptions{}
|
||||
deleteAllOfOpts.ApplyOptions(opts)
|
||||
|
||||
resInt, err := mc.getResourceInterface(metadata.GroupVersionKind(), deleteAllOfOpts.ListOptions.Namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return resInt.DeleteCollection(ctx, *deleteAllOfOpts.AsDeleteOptions(), *deleteAllOfOpts.AsListOptions())
|
||||
}
|
||||
|
||||
// Patch implements client.Client.
|
||||
func (mc *metadataClient) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
|
||||
metadata, ok := obj.(*metav1.PartialObjectMetadata)
|
||||
if !ok {
|
||||
return fmt.Errorf("metadata client did not understand object: %T", obj)
|
||||
}
|
||||
|
||||
gvk := metadata.GroupVersionKind()
|
||||
resInt, err := mc.getResourceInterface(gvk, metadata.Namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data, err := patch.Data(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
patchOpts := &PatchOptions{}
|
||||
patchOpts.ApplyOptions(opts)
|
||||
|
||||
res, err := resInt.Patch(ctx, metadata.Name, patch.Type(), data, *patchOpts.AsPatchOptions())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*metadata = *res
|
||||
metadata.SetGroupVersionKind(gvk) // restore the GVK, which isn't set on metadata
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get implements client.Client.
|
||||
func (mc *metadataClient) Get(ctx context.Context, key ObjectKey, obj Object, opts ...GetOption) error {
|
||||
metadata, ok := obj.(*metav1.PartialObjectMetadata)
|
||||
if !ok {
|
||||
return fmt.Errorf("metadata client did not understand object: %T", obj)
|
||||
}
|
||||
|
||||
gvk := metadata.GroupVersionKind()
|
||||
|
||||
getOpts := GetOptions{}
|
||||
getOpts.ApplyOptions(opts)
|
||||
|
||||
resInt, err := mc.getResourceInterface(gvk, key.Namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := resInt.Get(ctx, key.Name, *getOpts.AsGetOptions())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*metadata = *res
|
||||
metadata.SetGroupVersionKind(gvk) // restore the GVK, which isn't set on metadata
|
||||
return nil
|
||||
}
|
||||
|
||||
// List implements client.Client.
|
||||
func (mc *metadataClient) List(ctx context.Context, obj ObjectList, opts ...ListOption) error {
|
||||
metadata, ok := obj.(*metav1.PartialObjectMetadataList)
|
||||
if !ok {
|
||||
return fmt.Errorf("metadata client did not understand object: %T", obj)
|
||||
}
|
||||
|
||||
gvk := metadata.GroupVersionKind()
|
||||
gvk.Kind = strings.TrimSuffix(gvk.Kind, "List")
|
||||
|
||||
listOpts := ListOptions{}
|
||||
listOpts.ApplyOptions(opts)
|
||||
|
||||
resInt, err := mc.getResourceInterface(gvk, listOpts.Namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := resInt.List(ctx, *listOpts.AsListOptions())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*metadata = *res
|
||||
metadata.SetGroupVersionKind(gvk) // restore the GVK, which isn't set on metadata
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mc *metadataClient) PatchStatus(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
|
||||
metadata, ok := obj.(*metav1.PartialObjectMetadata)
|
||||
if !ok {
|
||||
return fmt.Errorf("metadata client did not understand object: %T", obj)
|
||||
}
|
||||
|
||||
gvk := metadata.GroupVersionKind()
|
||||
resInt, err := mc.getResourceInterface(gvk, metadata.Namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data, err := patch.Data(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
patchOpts := &PatchOptions{}
|
||||
res, err := resInt.Patch(ctx, metadata.Name, patch.Type(), data, *patchOpts.AsPatchOptions(), "status")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*metadata = *res
|
||||
metadata.SetGroupVersionKind(gvk) // restore the GVK, which isn't set on metadata
|
||||
return nil
|
||||
}
|
213
vendor/sigs.k8s.io/controller-runtime/pkg/client/namespaced_client.go
generated
vendored
Normal file
213
vendor/sigs.k8s.io/controller-runtime/pkg/client/namespaced_client.go
generated
vendored
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
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 client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/internal/objectutil"
|
||||
)
|
||||
|
||||
// NewNamespacedClient wraps an existing client enforcing the namespace value.
|
||||
// All functions using this client will have the same namespace declared here.
|
||||
func NewNamespacedClient(c Client, ns string) Client {
|
||||
return &namespacedClient{
|
||||
client: c,
|
||||
namespace: ns,
|
||||
}
|
||||
}
|
||||
|
||||
var _ Client = &namespacedClient{}
|
||||
|
||||
// namespacedClient is a Client that wraps another Client in order to enforce the specified namespace value.
|
||||
type namespacedClient struct {
|
||||
namespace string
|
||||
client Client
|
||||
}
|
||||
|
||||
// Scheme returns the scheme this client is using.
|
||||
func (n *namespacedClient) Scheme() *runtime.Scheme {
|
||||
return n.client.Scheme()
|
||||
}
|
||||
|
||||
// RESTMapper returns the scheme this client is using.
|
||||
func (n *namespacedClient) RESTMapper() meta.RESTMapper {
|
||||
return n.client.RESTMapper()
|
||||
}
|
||||
|
||||
// Create implements client.Client.
|
||||
func (n *namespacedClient) Create(ctx context.Context, obj Object, opts ...CreateOption) error {
|
||||
isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, n.Scheme(), n.RESTMapper())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error finding the scope of the object: %w", err)
|
||||
}
|
||||
|
||||
objectNamespace := obj.GetNamespace()
|
||||
if objectNamespace != n.namespace && objectNamespace != "" {
|
||||
return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), n.namespace)
|
||||
}
|
||||
|
||||
if isNamespaceScoped && objectNamespace == "" {
|
||||
obj.SetNamespace(n.namespace)
|
||||
}
|
||||
return n.client.Create(ctx, obj, opts...)
|
||||
}
|
||||
|
||||
// Update implements client.Client.
|
||||
func (n *namespacedClient) Update(ctx context.Context, obj Object, opts ...UpdateOption) error {
|
||||
isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, n.Scheme(), n.RESTMapper())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error finding the scope of the object: %w", err)
|
||||
}
|
||||
|
||||
objectNamespace := obj.GetNamespace()
|
||||
if objectNamespace != n.namespace && objectNamespace != "" {
|
||||
return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), n.namespace)
|
||||
}
|
||||
|
||||
if isNamespaceScoped && objectNamespace == "" {
|
||||
obj.SetNamespace(n.namespace)
|
||||
}
|
||||
return n.client.Update(ctx, obj, opts...)
|
||||
}
|
||||
|
||||
// Delete implements client.Client.
|
||||
func (n *namespacedClient) Delete(ctx context.Context, obj Object, opts ...DeleteOption) error {
|
||||
isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, n.Scheme(), n.RESTMapper())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error finding the scope of the object: %w", err)
|
||||
}
|
||||
|
||||
objectNamespace := obj.GetNamespace()
|
||||
if objectNamespace != n.namespace && objectNamespace != "" {
|
||||
return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), n.namespace)
|
||||
}
|
||||
|
||||
if isNamespaceScoped && objectNamespace == "" {
|
||||
obj.SetNamespace(n.namespace)
|
||||
}
|
||||
return n.client.Delete(ctx, obj, opts...)
|
||||
}
|
||||
|
||||
// DeleteAllOf implements client.Client.
|
||||
func (n *namespacedClient) DeleteAllOf(ctx context.Context, obj Object, opts ...DeleteAllOfOption) error {
|
||||
isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, n.Scheme(), n.RESTMapper())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error finding the scope of the object: %w", err)
|
||||
}
|
||||
|
||||
if isNamespaceScoped {
|
||||
opts = append(opts, InNamespace(n.namespace))
|
||||
}
|
||||
return n.client.DeleteAllOf(ctx, obj, opts...)
|
||||
}
|
||||
|
||||
// Patch implements client.Client.
|
||||
func (n *namespacedClient) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
|
||||
isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, n.Scheme(), n.RESTMapper())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error finding the scope of the object: %w", err)
|
||||
}
|
||||
|
||||
objectNamespace := obj.GetNamespace()
|
||||
if objectNamespace != n.namespace && objectNamespace != "" {
|
||||
return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), n.namespace)
|
||||
}
|
||||
|
||||
if isNamespaceScoped && objectNamespace == "" {
|
||||
obj.SetNamespace(n.namespace)
|
||||
}
|
||||
return n.client.Patch(ctx, obj, patch, opts...)
|
||||
}
|
||||
|
||||
// Get implements client.Client.
|
||||
func (n *namespacedClient) Get(ctx context.Context, key ObjectKey, obj Object, opts ...GetOption) error {
|
||||
isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, n.Scheme(), n.RESTMapper())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error finding the scope of the object: %w", err)
|
||||
}
|
||||
if isNamespaceScoped {
|
||||
if key.Namespace != "" && key.Namespace != n.namespace {
|
||||
return fmt.Errorf("namespace %s provided for the object %s does not match the namespace %s on the client", key.Namespace, obj.GetName(), n.namespace)
|
||||
}
|
||||
key.Namespace = n.namespace
|
||||
}
|
||||
return n.client.Get(ctx, key, obj, opts...)
|
||||
}
|
||||
|
||||
// List implements client.Client.
|
||||
func (n *namespacedClient) List(ctx context.Context, obj ObjectList, opts ...ListOption) error {
|
||||
if n.namespace != "" {
|
||||
opts = append(opts, InNamespace(n.namespace))
|
||||
}
|
||||
return n.client.List(ctx, obj, opts...)
|
||||
}
|
||||
|
||||
// Status implements client.StatusClient.
|
||||
func (n *namespacedClient) Status() StatusWriter {
|
||||
return &namespacedClientStatusWriter{StatusClient: n.client.Status(), namespace: n.namespace, namespacedclient: n}
|
||||
}
|
||||
|
||||
// ensure namespacedClientStatusWriter implements client.StatusWriter.
|
||||
var _ StatusWriter = &namespacedClientStatusWriter{}
|
||||
|
||||
type namespacedClientStatusWriter struct {
|
||||
StatusClient StatusWriter
|
||||
namespace string
|
||||
namespacedclient Client
|
||||
}
|
||||
|
||||
// Update implements client.StatusWriter.
|
||||
func (nsw *namespacedClientStatusWriter) Update(ctx context.Context, obj Object, opts ...UpdateOption) error {
|
||||
isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, nsw.namespacedclient.Scheme(), nsw.namespacedclient.RESTMapper())
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("error finding the scope of the object: %w", err)
|
||||
}
|
||||
|
||||
objectNamespace := obj.GetNamespace()
|
||||
if objectNamespace != nsw.namespace && objectNamespace != "" {
|
||||
return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), nsw.namespace)
|
||||
}
|
||||
|
||||
if isNamespaceScoped && objectNamespace == "" {
|
||||
obj.SetNamespace(nsw.namespace)
|
||||
}
|
||||
return nsw.StatusClient.Update(ctx, obj, opts...)
|
||||
}
|
||||
|
||||
// Patch implements client.StatusWriter.
|
||||
func (nsw *namespacedClientStatusWriter) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
|
||||
isNamespaceScoped, err := objectutil.IsAPINamespaced(obj, nsw.namespacedclient.Scheme(), nsw.namespacedclient.RESTMapper())
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("error finding the scope of the object: %w", err)
|
||||
}
|
||||
|
||||
objectNamespace := obj.GetNamespace()
|
||||
if objectNamespace != nsw.namespace && objectNamespace != "" {
|
||||
return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), nsw.namespace)
|
||||
}
|
||||
|
||||
if isNamespaceScoped && objectNamespace == "" {
|
||||
obj.SetNamespace(nsw.namespace)
|
||||
}
|
||||
return nsw.StatusClient.Patch(ctx, obj, patch, opts...)
|
||||
}
|
77
vendor/sigs.k8s.io/controller-runtime/pkg/client/object.go
generated
vendored
Normal file
77
vendor/sigs.k8s.io/controller-runtime/pkg/client/object.go
generated
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
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 client
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// Object is a Kubernetes object, allows functions to work indistinctly with
|
||||
// any resource that implements both Object interfaces.
|
||||
//
|
||||
// Semantically, these are objects which are both serializable (runtime.Object)
|
||||
// and identifiable (metav1.Object) -- think any object which you could write
|
||||
// as YAML or JSON, and then `kubectl create`.
|
||||
//
|
||||
// Code-wise, this means that any object which embeds both ObjectMeta (which
|
||||
// provides metav1.Object) and TypeMeta (which provides half of runtime.Object)
|
||||
// and has a `DeepCopyObject` implementation (the other half of runtime.Object)
|
||||
// will implement this by default.
|
||||
//
|
||||
// For example, nearly all the built-in types are Objects, as well as all
|
||||
// KubeBuilder-generated CRDs (unless you do something real funky to them).
|
||||
//
|
||||
// By and large, most things that implement runtime.Object also implement
|
||||
// Object -- it's very rare to have *just* a runtime.Object implementation (the
|
||||
// cases tend to be funky built-in types like Webhook payloads that don't have
|
||||
// a `metadata` field).
|
||||
//
|
||||
// Notice that XYZList types are distinct: they implement ObjectList instead.
|
||||
type Object interface {
|
||||
metav1.Object
|
||||
runtime.Object
|
||||
}
|
||||
|
||||
// ObjectList is a Kubernetes object list, allows functions to work
|
||||
// indistinctly with any resource that implements both runtime.Object and
|
||||
// metav1.ListInterface interfaces.
|
||||
//
|
||||
// Semantically, this is any object which may be serialized (ObjectMeta), and
|
||||
// is a kubernetes list wrapper (has items, pagination fields, etc) -- think
|
||||
// the wrapper used in a response from a `kubectl list --output yaml` call.
|
||||
//
|
||||
// Code-wise, this means that any object which embedds both ListMeta (which
|
||||
// provides metav1.ListInterface) and TypeMeta (which provides half of
|
||||
// runtime.Object) and has a `DeepCopyObject` implementation (the other half of
|
||||
// runtime.Object) will implement this by default.
|
||||
//
|
||||
// For example, nearly all the built-in XYZList types are ObjectLists, as well
|
||||
// as the XYZList types for all KubeBuilder-generated CRDs (unless you do
|
||||
// something real funky to them).
|
||||
//
|
||||
// By and large, most things that are XYZList and implement runtime.Object also
|
||||
// implement ObjectList -- it's very rare to have *just* a runtime.Object
|
||||
// implementation (the cases tend to be funky built-in types like Webhook
|
||||
// payloads that don't have a `metadata` field).
|
||||
//
|
||||
// This is similar to Object, which is almost always implemented by the items
|
||||
// in the list themselves.
|
||||
type ObjectList interface {
|
||||
metav1.ListInterface
|
||||
runtime.Object
|
||||
}
|
742
vendor/sigs.k8s.io/controller-runtime/pkg/client/options.go
generated
vendored
Normal file
742
vendor/sigs.k8s.io/controller-runtime/pkg/client/options.go
generated
vendored
Normal file
@@ -0,0 +1,742 @@
|
||||
/*
|
||||
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)
|
||||
}
|
||||
|
||||
// GetOption is some configuration that modifies options for a get request.
|
||||
type GetOption interface {
|
||||
// ApplyToGet applies this configuration to the given get options.
|
||||
ApplyToGet(*GetOptions)
|
||||
}
|
||||
|
||||
// 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{}
|
||||
|
||||
// ApplyToCreate applies this configuration to the given create options.
|
||||
func (dryRunAll) ApplyToCreate(opts *CreateOptions) {
|
||||
opts.DryRun = []string{metav1.DryRunAll}
|
||||
}
|
||||
|
||||
// ApplyToUpdate applies this configuration to the given update options.
|
||||
func (dryRunAll) ApplyToUpdate(opts *UpdateOptions) {
|
||||
opts.DryRun = []string{metav1.DryRunAll}
|
||||
}
|
||||
|
||||
// ApplyToPatch applies this configuration to the given patch options.
|
||||
func (dryRunAll) ApplyToPatch(opts *PatchOptions) {
|
||||
opts.DryRun = []string{metav1.DryRunAll}
|
||||
}
|
||||
|
||||
// ApplyToPatch applies this configuration to the given delete options.
|
||||
func (dryRunAll) ApplyToDelete(opts *DeleteOptions) {
|
||||
opts.DryRun = []string{metav1.DryRunAll}
|
||||
}
|
||||
func (dryRunAll) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
|
||||
opts.DryRun = []string{metav1.DryRunAll}
|
||||
}
|
||||
|
||||
// FieldOwner set the field manager name for the given server-side apply patch.
|
||||
type FieldOwner string
|
||||
|
||||
// ApplyToPatch applies this configuration to the given patch options.
|
||||
func (f FieldOwner) ApplyToPatch(opts *PatchOptions) {
|
||||
opts.FieldManager = string(f)
|
||||
}
|
||||
|
||||
// ApplyToCreate applies this configuration to the given create options.
|
||||
func (f FieldOwner) ApplyToCreate(opts *CreateOptions) {
|
||||
opts.FieldManager = string(f)
|
||||
}
|
||||
|
||||
// ApplyToUpdate applies this configuration to the given update options.
|
||||
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{}
|
||||
|
||||
// }}}
|
||||
|
||||
// {{{ 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
|
||||
|
||||
// ApplyToDelete applies this configuration to the given delete options.
|
||||
func (s GracePeriodSeconds) ApplyToDelete(opts *DeleteOptions) {
|
||||
secs := int64(s)
|
||||
opts.GracePeriodSeconds = &secs
|
||||
}
|
||||
|
||||
// ApplyToDeleteAllOf applies this configuration to the given an List options.
|
||||
func (s GracePeriodSeconds) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
|
||||
s.ApplyToDelete(&opts.DeleteOptions)
|
||||
}
|
||||
|
||||
// Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.
|
||||
type Preconditions metav1.Preconditions
|
||||
|
||||
// ApplyToDelete applies this configuration to the given delete options.
|
||||
func (p Preconditions) ApplyToDelete(opts *DeleteOptions) {
|
||||
preconds := metav1.Preconditions(p)
|
||||
opts.Preconditions = &preconds
|
||||
}
|
||||
|
||||
// ApplyToDeleteAllOf applies this configuration to the given an List options.
|
||||
func (p Preconditions) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
|
||||
p.ApplyToDelete(&opts.DeleteOptions)
|
||||
}
|
||||
|
||||
// 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.
|
||||
type PropagationPolicy metav1.DeletionPropagation
|
||||
|
||||
// ApplyToDelete applies the given delete options on these options.
|
||||
// It will propagate to the dependents of the object to let the garbage collector handle it.
|
||||
func (p PropagationPolicy) ApplyToDelete(opts *DeleteOptions) {
|
||||
policy := metav1.DeletionPropagation(p)
|
||||
opts.PropagationPolicy = &policy
|
||||
}
|
||||
|
||||
// ApplyToDeleteAllOf applies this configuration to the given an List options.
|
||||
func (p PropagationPolicy) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
|
||||
p.ApplyToDelete(&opts.DeleteOptions)
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
// {{{ Get Options
|
||||
|
||||
// GetOptions contains options for get operation.
|
||||
// Now it only has a Raw field, with support for specific resourceVersion.
|
||||
type GetOptions struct {
|
||||
// Raw represents raw GetOptions, as passed to the API server. Note
|
||||
// that these may not be respected by all implementations of interface.
|
||||
Raw *metav1.GetOptions
|
||||
}
|
||||
|
||||
var _ GetOption = &GetOptions{}
|
||||
|
||||
// ApplyToGet implements GetOption for GetOptions.
|
||||
func (o *GetOptions) ApplyToGet(lo *GetOptions) {
|
||||
if o.Raw != nil {
|
||||
lo.Raw = o.Raw
|
||||
}
|
||||
}
|
||||
|
||||
// AsGetOptions returns these options as a flattened metav1.GetOptions.
|
||||
// This may mutate the Raw field.
|
||||
func (o *GetOptions) AsGetOptions() *metav1.GetOptions {
|
||||
if o == nil || o.Raw == nil {
|
||||
return &metav1.GetOptions{}
|
||||
}
|
||||
return o.Raw
|
||||
}
|
||||
|
||||
// ApplyOptions applies the given get options on these options,
|
||||
// and then returns itself (for convenient chaining).
|
||||
func (o *GetOptions) ApplyOptions(opts []GetOption) *GetOptions {
|
||||
for _, opt := range opts {
|
||||
opt.ApplyToGet(o)
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
// {{{ 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 labels.Parse() 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
|
||||
|
||||
// ApplyToList applies this configuration to the given list options.
|
||||
func (m MatchingLabels) ApplyToList(opts *ListOptions) {
|
||||
// TODO(directxman12): can we avoid reserializing this over and over?
|
||||
sel := labels.SelectorFromValidatedSet(map[string]string(m))
|
||||
opts.LabelSelector = sel
|
||||
}
|
||||
|
||||
// ApplyToDeleteAllOf applies this configuration to the given an List options.
|
||||
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
|
||||
|
||||
// ApplyToList applies this configuration to the given list options.
|
||||
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
|
||||
}
|
||||
|
||||
// ApplyToDeleteAllOf applies this configuration to the given an List options.
|
||||
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
|
||||
}
|
||||
|
||||
// ApplyToList applies this configuration to the given list options.
|
||||
func (m MatchingLabelsSelector) ApplyToList(opts *ListOptions) {
|
||||
opts.LabelSelector = m
|
||||
}
|
||||
|
||||
// ApplyToDeleteAllOf applies this configuration to the given an List options.
|
||||
func (m MatchingLabelsSelector) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
|
||||
m.ApplyToList(&opts.ListOptions)
|
||||
}
|
||||
|
||||
// MatchingFields filters the list/delete operation on the given field Set
|
||||
// (or index in the case of cached lists).
|
||||
type MatchingFields fields.Set
|
||||
|
||||
// ApplyToList applies this configuration to the given list options.
|
||||
func (m MatchingFields) ApplyToList(opts *ListOptions) {
|
||||
// TODO(directxman12): can we avoid re-serializing this?
|
||||
sel := fields.Set(m).AsSelector()
|
||||
opts.FieldSelector = sel
|
||||
}
|
||||
|
||||
// ApplyToDeleteAllOf applies this configuration to the given an List options.
|
||||
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
|
||||
}
|
||||
|
||||
// ApplyToList applies this configuration to the given list options.
|
||||
func (m MatchingFieldsSelector) ApplyToList(opts *ListOptions) {
|
||||
opts.FieldSelector = m
|
||||
}
|
||||
|
||||
// ApplyToDeleteAllOf applies this configuration to the given an List options.
|
||||
func (m MatchingFieldsSelector) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
|
||||
m.ApplyToList(&opts.ListOptions)
|
||||
}
|
||||
|
||||
// InNamespace restricts the list/delete operation to the given namespace.
|
||||
type InNamespace string
|
||||
|
||||
// ApplyToList applies this configuration to the given list options.
|
||||
func (n InNamespace) ApplyToList(opts *ListOptions) {
|
||||
opts.Namespace = string(n)
|
||||
}
|
||||
|
||||
// ApplyToDeleteAllOf applies this configuration to the given an List options.
|
||||
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
|
||||
|
||||
// ApplyToList applies this configuration to the given an list options.
|
||||
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
|
||||
|
||||
// ApplyToList applies this configuration to the given an List options.
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
// {{{ 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
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
// {{{ 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)
|
||||
}
|
||||
|
||||
// }}}
|
213
vendor/sigs.k8s.io/controller-runtime/pkg/client/patch.go
generated
vendored
Normal file
213
vendor/sigs.k8s.io/controller-runtime/pkg/client/patch.go
generated
vendored
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch/v5"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/json"
|
||||
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||
)
|
||||
|
||||
var (
|
||||
// Apply uses server-side apply to patch the given object.
|
||||
Apply Patch = applyPatch{}
|
||||
|
||||
// Merge uses the raw object as a merge patch, without modifications.
|
||||
// Use MergeFrom if you wish to compute a diff instead.
|
||||
Merge Patch = 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 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}
|
||||
}
|
||||
|
||||
// MergeFromWithOptimisticLock can be used if clients want to make sure a patch
|
||||
// is being applied to the latest resource version of an object.
|
||||
//
|
||||
// The behavior is similar to what an Update would do, without the need to send the
|
||||
// whole object. Usually this method is useful if you might have multiple clients
|
||||
// acting on the same object and the same API version, but with different versions of the Go structs.
|
||||
//
|
||||
// For example, an "older" copy of a Widget that has fields A and B, and a "newer" copy with A, B, and C.
|
||||
// Sending an update using the older struct definition results in C being dropped, whereas using a patch does not.
|
||||
type MergeFromWithOptimisticLock struct{}
|
||||
|
||||
// ApplyToMergeFrom applies this configuration to the given patch options.
|
||||
func (m MergeFromWithOptimisticLock) ApplyToMergeFrom(in *MergeFromOptions) {
|
||||
in.OptimisticLock = true
|
||||
}
|
||||
|
||||
// MergeFromOption is some configuration that modifies options for a merge-from patch data.
|
||||
type MergeFromOption interface {
|
||||
// ApplyToMergeFrom applies this configuration to the given patch options.
|
||||
ApplyToMergeFrom(*MergeFromOptions)
|
||||
}
|
||||
|
||||
// MergeFromOptions contains options to generate a merge-from patch data.
|
||||
type MergeFromOptions struct {
|
||||
// OptimisticLock, when true, includes `metadata.resourceVersion` into the final
|
||||
// patch data. If the `resourceVersion` field doesn't match what's stored,
|
||||
// the operation results in a conflict and clients will need to try again.
|
||||
OptimisticLock bool
|
||||
}
|
||||
|
||||
type mergeFromPatch struct {
|
||||
patchType types.PatchType
|
||||
createPatch func(originalJSON, modifiedJSON []byte, dataStruct interface{}) ([]byte, error)
|
||||
from Object
|
||||
opts MergeFromOptions
|
||||
}
|
||||
|
||||
// Type implements Patch.
|
||||
func (s *mergeFromPatch) Type() types.PatchType {
|
||||
return s.patchType
|
||||
}
|
||||
|
||||
// Data implements Patch.
|
||||
func (s *mergeFromPatch) Data(obj Object) ([]byte, error) {
|
||||
original := s.from
|
||||
modified := obj
|
||||
|
||||
if s.opts.OptimisticLock {
|
||||
version := original.GetResourceVersion()
|
||||
if len(version) == 0 {
|
||||
return nil, fmt.Errorf("cannot use OptimisticLock, object %q does not have any resource version we can use", original)
|
||||
}
|
||||
|
||||
original = original.DeepCopyObject().(Object)
|
||||
original.SetResourceVersion("")
|
||||
|
||||
modified = modified.DeepCopyObject().(Object)
|
||||
modified.SetResourceVersion(version)
|
||||
}
|
||||
|
||||
originalJSON, err := json.Marshal(original)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
modifiedJSON, err := json.Marshal(modified)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := s.createPatch(originalJSON, modifiedJSON, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func createMergePatch(originalJSON, modifiedJSON []byte, _ interface{}) ([]byte, error) {
|
||||
return jsonpatch.CreateMergePatch(originalJSON, modifiedJSON)
|
||||
}
|
||||
|
||||
func createStrategicMergePatch(originalJSON, modifiedJSON []byte, dataStruct interface{}) ([]byte, error) {
|
||||
return strategicpatch.CreateTwoWayMergePatch(originalJSON, modifiedJSON, dataStruct)
|
||||
}
|
||||
|
||||
// MergeFrom creates a Patch that patches using the merge-patch strategy with the given object as base.
|
||||
// The difference between MergeFrom and StrategicMergeFrom lays in the handling of modified list fields.
|
||||
// When using MergeFrom, existing lists will be completely replaced by new lists.
|
||||
// When using StrategicMergeFrom, the list field's `patchStrategy` is respected if specified in the API type,
|
||||
// e.g. the existing list is not replaced completely but rather merged with the new one using the list's `patchMergeKey`.
|
||||
// See https://kubernetes.io/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/ for more details on
|
||||
// the difference between merge-patch and strategic-merge-patch.
|
||||
func MergeFrom(obj Object) Patch {
|
||||
return &mergeFromPatch{patchType: types.MergePatchType, createPatch: createMergePatch, from: obj}
|
||||
}
|
||||
|
||||
// MergeFromWithOptions creates a Patch that patches using the merge-patch strategy with the given object as base.
|
||||
// See MergeFrom for more details.
|
||||
func MergeFromWithOptions(obj Object, opts ...MergeFromOption) Patch {
|
||||
options := &MergeFromOptions{}
|
||||
for _, opt := range opts {
|
||||
opt.ApplyToMergeFrom(options)
|
||||
}
|
||||
return &mergeFromPatch{patchType: types.MergePatchType, createPatch: createMergePatch, from: obj, opts: *options}
|
||||
}
|
||||
|
||||
// StrategicMergeFrom creates a Patch that patches using the strategic-merge-patch strategy with the given object as base.
|
||||
// The difference between MergeFrom and StrategicMergeFrom lays in the handling of modified list fields.
|
||||
// When using MergeFrom, existing lists will be completely replaced by new lists.
|
||||
// When using StrategicMergeFrom, the list field's `patchStrategy` is respected if specified in the API type,
|
||||
// e.g. the existing list is not replaced completely but rather merged with the new one using the list's `patchMergeKey`.
|
||||
// See https://kubernetes.io/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/ for more details on
|
||||
// the difference between merge-patch and strategic-merge-patch.
|
||||
// Please note, that CRDs don't support strategic-merge-patch, see
|
||||
// https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/#advanced-features-and-flexibility
|
||||
func StrategicMergeFrom(obj Object, opts ...MergeFromOption) Patch {
|
||||
options := &MergeFromOptions{}
|
||||
for _, opt := range opts {
|
||||
opt.ApplyToMergeFrom(options)
|
||||
}
|
||||
return &mergeFromPatch{patchType: types.StrategicMergePatchType, createPatch: createStrategicMergePatch, from: obj, opts: *options}
|
||||
}
|
||||
|
||||
// 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 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 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)
|
||||
}
|
141
vendor/sigs.k8s.io/controller-runtime/pkg/client/split.go
generated
vendored
Normal file
141
vendor/sigs.k8s.io/controller-runtime/pkg/client/split.go
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
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"
|
||||
"strings"
|
||||
|
||||
"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"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
||||
)
|
||||
|
||||
// NewDelegatingClientInput encapsulates the input parameters to create a new delegating client.
|
||||
type NewDelegatingClientInput struct {
|
||||
CacheReader Reader
|
||||
Client Client
|
||||
UncachedObjects []Object
|
||||
CacheUnstructured bool
|
||||
}
|
||||
|
||||
// NewDelegatingClient creates a new delegating client.
|
||||
//
|
||||
// A delegating client 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.
|
||||
func NewDelegatingClient(in NewDelegatingClientInput) (Client, error) {
|
||||
uncachedGVKs := map[schema.GroupVersionKind]struct{}{}
|
||||
for _, obj := range in.UncachedObjects {
|
||||
gvk, err := apiutil.GVKForObject(obj, in.Client.Scheme())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uncachedGVKs[gvk] = struct{}{}
|
||||
}
|
||||
|
||||
return &delegatingClient{
|
||||
scheme: in.Client.Scheme(),
|
||||
mapper: in.Client.RESTMapper(),
|
||||
Reader: &delegatingReader{
|
||||
CacheReader: in.CacheReader,
|
||||
ClientReader: in.Client,
|
||||
scheme: in.Client.Scheme(),
|
||||
uncachedGVKs: uncachedGVKs,
|
||||
cacheUnstructured: in.CacheUnstructured,
|
||||
},
|
||||
Writer: in.Client,
|
||||
StatusClient: in.Client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type delegatingClient struct {
|
||||
Reader
|
||||
Writer
|
||||
StatusClient
|
||||
|
||||
scheme *runtime.Scheme
|
||||
mapper meta.RESTMapper
|
||||
}
|
||||
|
||||
// Scheme returns the scheme this client is using.
|
||||
func (d *delegatingClient) Scheme() *runtime.Scheme {
|
||||
return d.scheme
|
||||
}
|
||||
|
||||
// RESTMapper returns the rest mapper this client is using.
|
||||
func (d *delegatingClient) RESTMapper() meta.RESTMapper {
|
||||
return d.mapper
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
uncachedGVKs map[schema.GroupVersionKind]struct{}
|
||||
scheme *runtime.Scheme
|
||||
cacheUnstructured bool
|
||||
}
|
||||
|
||||
func (d *delegatingReader) shouldBypassCache(obj runtime.Object) (bool, error) {
|
||||
gvk, err := apiutil.GVKForObject(obj, d.scheme)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// TODO: this is producing unsafe guesses that don't actually work,
|
||||
// but it matches ~99% of the cases out there.
|
||||
if meta.IsListType(obj) {
|
||||
gvk.Kind = strings.TrimSuffix(gvk.Kind, "List")
|
||||
}
|
||||
if _, isUncached := d.uncachedGVKs[gvk]; isUncached {
|
||||
return true, nil
|
||||
}
|
||||
if !d.cacheUnstructured {
|
||||
_, isUnstructured := obj.(*unstructured.Unstructured)
|
||||
_, isUnstructuredList := obj.(*unstructured.UnstructuredList)
|
||||
return isUnstructured || isUnstructuredList, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Get retrieves an obj for a given object key from the Kubernetes Cluster.
|
||||
func (d *delegatingReader) Get(ctx context.Context, key ObjectKey, obj Object, opts ...GetOption) error {
|
||||
if isUncached, err := d.shouldBypassCache(obj); err != nil {
|
||||
return err
|
||||
} else if isUncached {
|
||||
return d.ClientReader.Get(ctx, key, obj, opts...)
|
||||
}
|
||||
return d.CacheReader.Get(ctx, key, obj, opts...)
|
||||
}
|
||||
|
||||
// List retrieves list of objects for a given namespace and list options.
|
||||
func (d *delegatingReader) List(ctx context.Context, list ObjectList, opts ...ListOption) error {
|
||||
if isUncached, err := d.shouldBypassCache(list); err != nil {
|
||||
return err
|
||||
} else if isUncached {
|
||||
return d.ClientReader.List(ctx, list, opts...)
|
||||
}
|
||||
return d.CacheReader.List(ctx, list, opts...)
|
||||
}
|
208
vendor/sigs.k8s.io/controller-runtime/pkg/client/typed_client.go
generated
vendored
Normal file
208
vendor/sigs.k8s.io/controller-runtime/pkg/client/typed_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"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
var _ Reader = &typedClient{}
|
||||
var _ Writer = &typedClient{}
|
||||
var _ StatusWriter = &typedClient{}
|
||||
|
||||
// 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 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 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 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 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 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 Object, opts ...GetOption) error {
|
||||
r, err := c.cache.getResource(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
getOpts := GetOptions{}
|
||||
getOpts.ApplyOptions(opts)
|
||||
return r.Get().
|
||||
NamespaceIfScoped(key.Namespace, r.isNamespaced()).
|
||||
Resource(r.resource()).
|
||||
VersionedParams(getOpts.AsGetOptions(), c.paramCodec).
|
||||
Name(key.Name).Do(ctx).Into(obj)
|
||||
}
|
||||
|
||||
// List implements client.Client.
|
||||
func (c *typedClient) List(ctx context.Context, obj ObjectList, 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 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 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)
|
||||
}
|
275
vendor/sigs.k8s.io/controller-runtime/pkg/client/unstructured_client.go
generated
vendored
Normal file
275
vendor/sigs.k8s.io/controller-runtime/pkg/client/unstructured_client.go
generated
vendored
Normal file
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
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"
|
||||
)
|
||||
|
||||
var _ Reader = &unstructuredClient{}
|
||||
var _ Writer = &unstructuredClient{}
|
||||
var _ StatusWriter = &unstructuredClient{}
|
||||
|
||||
// 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 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 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 Object, opts ...DeleteOption) error {
|
||||
if _, ok := obj.(*unstructured.Unstructured); !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 Object, opts ...DeleteAllOfOption) error {
|
||||
if _, ok := obj.(*unstructured.Unstructured); !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 Object, patch Patch, opts ...PatchOption) error {
|
||||
if _, ok := obj.(*unstructured.Unstructured); !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 Object, opts ...GetOption) error {
|
||||
u, ok := obj.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("unstructured client did not understand object: %T", obj)
|
||||
}
|
||||
|
||||
gvk := u.GroupVersionKind()
|
||||
|
||||
getOpts := GetOptions{}
|
||||
getOpts.ApplyOptions(opts)
|
||||
|
||||
r, err := uc.cache.getResource(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
result := r.Get().
|
||||
NamespaceIfScoped(key.Namespace, r.isNamespaced()).
|
||||
Resource(r.resource()).
|
||||
VersionedParams(getOpts.AsGetOptions(), uc.paramCodec).
|
||||
Name(key.Name).
|
||||
Do(ctx).
|
||||
Into(obj)
|
||||
|
||||
u.SetGroupVersionKind(gvk)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// List implements client.Client.
|
||||
func (uc *unstructuredClient) List(ctx context.Context, obj ObjectList, opts ...ListOption) error {
|
||||
u, ok := obj.(*unstructured.UnstructuredList)
|
||||
if !ok {
|
||||
return fmt.Errorf("unstructured client did not understand object: %T", obj)
|
||||
}
|
||||
|
||||
gvk := u.GroupVersionKind()
|
||||
gvk.Kind = strings.TrimSuffix(gvk.Kind, "List")
|
||||
|
||||
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 Object, opts ...UpdateOption) error {
|
||||
if _, ok := obj.(*unstructured.Unstructured); !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 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
|
||||
}
|
114
vendor/sigs.k8s.io/controller-runtime/pkg/client/watch.go
generated
vendored
Normal file
114
vendor/sigs.k8s.io/controller-runtime/pkg/client/watch.go
generated
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
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 client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
// NewWithWatch returns a new WithWatch.
|
||||
func NewWithWatch(config *rest.Config, options Options) (WithWatch, error) {
|
||||
client, err := newClient(config, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dynamicClient, err := dynamic.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &watchingClient{client: client, dynamic: dynamicClient}, nil
|
||||
}
|
||||
|
||||
type watchingClient struct {
|
||||
*client
|
||||
dynamic dynamic.Interface
|
||||
}
|
||||
|
||||
func (w *watchingClient) Watch(ctx context.Context, list ObjectList, opts ...ListOption) (watch.Interface, error) {
|
||||
switch l := list.(type) {
|
||||
case *unstructured.UnstructuredList:
|
||||
return w.unstructuredWatch(ctx, l, opts...)
|
||||
case *metav1.PartialObjectMetadataList:
|
||||
return w.metadataWatch(ctx, l, opts...)
|
||||
default:
|
||||
return w.typedWatch(ctx, l, opts...)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *watchingClient) listOpts(opts ...ListOption) ListOptions {
|
||||
listOpts := ListOptions{}
|
||||
listOpts.ApplyOptions(opts)
|
||||
if listOpts.Raw == nil {
|
||||
listOpts.Raw = &metav1.ListOptions{}
|
||||
}
|
||||
listOpts.Raw.Watch = true
|
||||
|
||||
return listOpts
|
||||
}
|
||||
|
||||
func (w *watchingClient) metadataWatch(ctx context.Context, obj *metav1.PartialObjectMetadataList, opts ...ListOption) (watch.Interface, error) {
|
||||
gvk := obj.GroupVersionKind()
|
||||
gvk.Kind = strings.TrimSuffix(gvk.Kind, "List")
|
||||
|
||||
listOpts := w.listOpts(opts...)
|
||||
|
||||
resInt, err := w.client.metadataClient.getResourceInterface(gvk, listOpts.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resInt.Watch(ctx, *listOpts.AsListOptions())
|
||||
}
|
||||
|
||||
func (w *watchingClient) unstructuredWatch(ctx context.Context, obj *unstructured.UnstructuredList, opts ...ListOption) (watch.Interface, error) {
|
||||
gvk := obj.GroupVersionKind()
|
||||
gvk.Kind = strings.TrimSuffix(gvk.Kind, "List")
|
||||
|
||||
r, err := w.client.unstructuredClient.cache.getResource(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
listOpts := w.listOpts(opts...)
|
||||
|
||||
if listOpts.Namespace != "" && r.isNamespaced() {
|
||||
return w.dynamic.Resource(r.mapping.Resource).Namespace(listOpts.Namespace).Watch(ctx, *listOpts.AsListOptions())
|
||||
}
|
||||
return w.dynamic.Resource(r.mapping.Resource).Watch(ctx, *listOpts.AsListOptions())
|
||||
}
|
||||
|
||||
func (w *watchingClient) typedWatch(ctx context.Context, obj ObjectList, opts ...ListOption) (watch.Interface, error) {
|
||||
r, err := w.client.typedClient.cache.getResource(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
listOpts := w.listOpts(opts...)
|
||||
|
||||
return r.Get().
|
||||
NamespaceIfScoped(listOpts.Namespace, r.isNamespaced()).
|
||||
Resource(r.resource()).
|
||||
VersionedParams(listOpts.AsListOptions(), w.client.typedClient.paramCodec).
|
||||
Watch(ctx)
|
||||
}
|
Reference in New Issue
Block a user