rebase: update kubernetes to 1.26.1

update kubernetes and its dependencies
to v1.26.1

Signed-off-by: Madhu Rajanna <madhupr007@gmail.com>
This commit is contained in:
Madhu Rajanna
2023-02-01 18:06:36 +01:00
committed by mergify[bot]
parent e9e33fb851
commit 9c8de9471e
937 changed files with 75539 additions and 33050 deletions

View File

@ -81,7 +81,7 @@ func GVKForObject(obj runtime.Object, scheme *runtime.Scheme) (schema.GroupVersi
// (unstructured, partial, etc)
// check for PartialObjectMetadata, which is analogous to unstructured, but isn't handled by ObjectKinds
_, isPartial := obj.(*metav1.PartialObjectMetadata) //nolint:ifshort
_, isPartial := obj.(*metav1.PartialObjectMetadata)
_, isPartialList := obj.(*metav1.PartialObjectMetadataList)
if isPartial || isPartialList {
// we require that the GVK be populated in order to recognize the object

View File

@ -17,8 +17,8 @@ limitations under the License.
package apiutil
import (
"errors"
"sync"
"sync/atomic"
"golang.org/x/time/rate"
"k8s.io/apimachinery/pkg/api/meta"
@ -38,7 +38,8 @@ type dynamicRESTMapper struct {
lazy bool
// Used for lazy init.
initOnce sync.Once
inited uint32
initMtx sync.Mutex
}
// DynamicRESTMapperOption is a functional option on the dynamicRESTMapper.
@ -125,18 +126,25 @@ func (drm *dynamicRESTMapper) setStaticMapper() error {
// init initializes drm only once if drm is lazy.
func (drm *dynamicRESTMapper) init() (err error) {
drm.initOnce.Do(func() {
if drm.lazy {
err = drm.setStaticMapper()
// 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 that matches the given error, it will attempt to reload
// 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.
//
@ -145,7 +153,7 @@ func (drm *dynamicRESTMapper) init() (err error) {
// 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(needsReloadErr error, checkNeedsReload func() error) error {
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 {
@ -155,10 +163,7 @@ func (drm *dynamicRESTMapper) checkAndReload(needsReloadErr error, checkNeedsRel
return checkNeedsReload()
}()
// NB(directxman12): `Is` and `As` have a confusing relationship --
// `Is` is like `== or does this implement .Is`, whereas `As` says
// `can I type-assert into`
needsReload := errors.As(err, &needsReloadErr)
needsReload := meta.IsNoMatchError(err)
if !needsReload {
return err
}
@ -169,7 +174,7 @@ func (drm *dynamicRESTMapper) checkAndReload(needsReloadErr error, checkNeedsRel
// ... and double-check that we didn't reload in the meantime
err = checkNeedsReload()
needsReload = errors.As(err, &needsReloadErr)
needsReload = meta.IsNoMatchError(err)
if !needsReload {
return err
}
@ -197,7 +202,7 @@ func (drm *dynamicRESTMapper) KindFor(resource schema.GroupVersionResource) (sch
return schema.GroupVersionKind{}, err
}
var gvk schema.GroupVersionKind
err := drm.checkAndReload(&meta.NoResourceMatchError{}, func() error {
err := drm.checkAndReload(func() error {
var err error
gvk, err = drm.staticMapper.KindFor(resource)
return err
@ -210,7 +215,7 @@ func (drm *dynamicRESTMapper) KindsFor(resource schema.GroupVersionResource) ([]
return nil, err
}
var gvks []schema.GroupVersionKind
err := drm.checkAndReload(&meta.NoResourceMatchError{}, func() error {
err := drm.checkAndReload(func() error {
var err error
gvks, err = drm.staticMapper.KindsFor(resource)
return err
@ -224,7 +229,7 @@ func (drm *dynamicRESTMapper) ResourceFor(input schema.GroupVersionResource) (sc
}
var gvr schema.GroupVersionResource
err := drm.checkAndReload(&meta.NoResourceMatchError{}, func() error {
err := drm.checkAndReload(func() error {
var err error
gvr, err = drm.staticMapper.ResourceFor(input)
return err
@ -237,7 +242,7 @@ func (drm *dynamicRESTMapper) ResourcesFor(input schema.GroupVersionResource) ([
return nil, err
}
var gvrs []schema.GroupVersionResource
err := drm.checkAndReload(&meta.NoResourceMatchError{}, func() error {
err := drm.checkAndReload(func() error {
var err error
gvrs, err = drm.staticMapper.ResourcesFor(input)
return err
@ -250,7 +255,7 @@ func (drm *dynamicRESTMapper) RESTMapping(gk schema.GroupKind, versions ...strin
return nil, err
}
var mapping *meta.RESTMapping
err := drm.checkAndReload(&meta.NoKindMatchError{}, func() error {
err := drm.checkAndReload(func() error {
var err error
mapping, err = drm.staticMapper.RESTMapping(gk, versions...)
return err
@ -263,7 +268,7 @@ func (drm *dynamicRESTMapper) RESTMappings(gk schema.GroupKind, versions ...stri
return nil, err
}
var mappings []*meta.RESTMapping
err := drm.checkAndReload(&meta.NoKindMatchError{}, func() error {
err := drm.checkAndReload(func() error {
var err error
mappings, err = drm.staticMapper.RESTMappings(gk, versions...)
return err
@ -276,7 +281,7 @@ func (drm *dynamicRESTMapper) ResourceSingularizer(resource string) (string, err
return "", err
}
var singular string
err := drm.checkAndReload(&meta.NoResourceMatchError{}, func() error {
err := drm.checkAndReload(func() error {
var err error
singular, err = drm.staticMapper.ResourceSingularizer(resource)
return err

View File

@ -18,6 +18,7 @@ package client
import (
"context"
"errors"
"fmt"
"strings"
@ -45,7 +46,7 @@ type WarningHandlerOptions struct {
// AllowDuplicateLogs does not deduplicate the to-be
// logged surfaced warnings messages. See
// log.WarningHandlerOptions for considerations
// regarding deuplication
// regarding deduplication
AllowDuplicateLogs bool
}
@ -88,13 +89,12 @@ func newClient(config *rest.Config, options Options) (*client, error) {
// is log.KubeAPIWarningLogger with deduplication enabled.
// See log.KubeAPIWarningLoggerOptions for considerations
// regarding deduplication.
rest.SetDefaultWarningHandler(
log.NewKubeAPIWarningLogger(
logger,
log.KubeAPIWarningLoggerOptions{
Deduplicate: !options.Opts.AllowDuplicateLogs,
},
),
config = rest.CopyConfig(config)
config.WarningHandler = log.NewKubeAPIWarningLogger(
logger,
log.KubeAPIWarningLoggerOptions{
Deduplicate: !options.Opts.AllowDuplicateLogs,
},
)
}
@ -241,16 +241,16 @@ func (c *client) Patch(ctx context.Context, obj Object, patch Patch, opts ...Pat
}
// Get implements client.Client.
func (c *client) Get(ctx context.Context, key ObjectKey, obj Object) error {
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)
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)
return c.metadataClient.Get(ctx, key, obj, opts...)
default:
return c.typedClient.Get(ctx, key, obj)
return c.typedClient.Get(ctx, key, obj, opts...)
}
}
@ -289,40 +289,194 @@ func (c *client) List(ctx context.Context, obj ObjectList, opts ...ListOption) e
}
// Status implements client.StatusClient.
func (c *client) Status() StatusWriter {
return &statusWriter{client: c}
func (c *client) Status() SubResourceWriter {
return c.SubResource("status")
}
// statusWriter is client.StatusWriter that writes status subresource.
type statusWriter struct {
client *client
func (c *client) SubResource(subResource string) SubResourceClient {
return &subResourceClient{client: c, subResource: subResource}
}
// ensure statusWriter implements client.StatusWriter.
var _ StatusWriter = &statusWriter{}
// subResourceClient is client.SubResourceWriter that writes to subresources.
type subResourceClient struct {
client *client
subResource string
}
// Update implements client.StatusWriter.
func (sw *statusWriter) Update(ctx context.Context, obj Object, opts ...UpdateOption) error {
defer sw.client.resetGroupVersionKind(obj, obj.GetObjectKind().GroupVersionKind())
// ensure subResourceClient implements client.SubResourceClient.
var _ SubResourceClient = &subResourceClient{}
// SubResourceGetOptions holds all the possible configuration
// for a subresource Get request.
type SubResourceGetOptions struct {
Raw *metav1.GetOptions
}
// ApplyToSubResourceGet updates the configuaration to the given get options.
func (getOpt *SubResourceGetOptions) ApplyToSubResourceGet(o *SubResourceGetOptions) {
if getOpt.Raw != nil {
o.Raw = getOpt.Raw
}
}
// ApplyOptions applues the given options.
func (getOpt *SubResourceGetOptions) ApplyOptions(opts []SubResourceGetOption) *SubResourceGetOptions {
for _, o := range opts {
o.ApplyToSubResourceGet(getOpt)
}
return getOpt
}
// AsGetOptions returns the configured options as *metav1.GetOptions.
func (getOpt *SubResourceGetOptions) AsGetOptions() *metav1.GetOptions {
if getOpt.Raw == nil {
return &metav1.GetOptions{}
}
return getOpt.Raw
}
// SubResourceUpdateOptions holds all the possible configuration
// for a subresource update request.
type SubResourceUpdateOptions struct {
UpdateOptions
SubResourceBody Object
}
// ApplyToSubResourceUpdate updates the configuration on the given create options
func (uo *SubResourceUpdateOptions) ApplyToSubResourceUpdate(o *SubResourceUpdateOptions) {
uo.UpdateOptions.ApplyToUpdate(&o.UpdateOptions)
if uo.SubResourceBody != nil {
o.SubResourceBody = uo.SubResourceBody
}
}
// ApplyOptions applies the given options.
func (uo *SubResourceUpdateOptions) ApplyOptions(opts []SubResourceUpdateOption) *SubResourceUpdateOptions {
for _, o := range opts {
o.ApplyToSubResourceUpdate(uo)
}
return uo
}
// SubResourceUpdateAndPatchOption is an option that can be used for either
// a subresource update or patch request.
type SubResourceUpdateAndPatchOption interface {
SubResourceUpdateOption
SubResourcePatchOption
}
// WithSubResourceBody returns an option that uses the given body
// for a subresource Update or Patch operation.
func WithSubResourceBody(body Object) SubResourceUpdateAndPatchOption {
return &withSubresourceBody{body: body}
}
type withSubresourceBody struct {
body Object
}
func (wsr *withSubresourceBody) ApplyToSubResourceUpdate(o *SubResourceUpdateOptions) {
o.SubResourceBody = wsr.body
}
func (wsr *withSubresourceBody) ApplyToSubResourcePatch(o *SubResourcePatchOptions) {
o.SubResourceBody = wsr.body
}
// SubResourceCreateOptions are all the possible configurations for a subresource
// create request.
type SubResourceCreateOptions struct {
CreateOptions
}
// ApplyOptions applies the given options.
func (co *SubResourceCreateOptions) ApplyOptions(opts []SubResourceCreateOption) *SubResourceCreateOptions {
for _, o := range opts {
o.ApplyToSubResourceCreate(co)
}
return co
}
// ApplyToSubresourceCreate applies the the configuration on the given create options.
func (co *SubResourceCreateOptions) ApplyToSubresourceCreate(o *SubResourceCreateOptions) {
co.CreateOptions.ApplyToCreate(&co.CreateOptions)
}
// SubResourcePatchOptions holds all possible configurations for a subresource patch
// request.
type SubResourcePatchOptions struct {
PatchOptions
SubResourceBody Object
}
// ApplyOptions applies the given options.
func (po *SubResourcePatchOptions) ApplyOptions(opts []SubResourcePatchOption) *SubResourcePatchOptions {
for _, o := range opts {
o.ApplyToSubResourcePatch(po)
}
return po
}
// ApplyToSubResourcePatch applies the configuration on the given patch options.
func (po *SubResourcePatchOptions) ApplyToSubResourcePatch(o *SubResourcePatchOptions) {
po.PatchOptions.ApplyToPatch(&o.PatchOptions)
if po.SubResourceBody != nil {
o.SubResourceBody = po.SubResourceBody
}
}
func (sc *subResourceClient) Get(ctx context.Context, obj Object, subResource Object, opts ...SubResourceGetOption) error {
switch obj.(type) {
case *unstructured.Unstructured:
return sw.client.unstructuredClient.UpdateStatus(ctx, obj, opts...)
return sc.client.unstructuredClient.GetSubResource(ctx, obj, subResource, sc.subResource, opts...)
case *metav1.PartialObjectMetadata:
return errors.New("can not get subresource using only metadata")
default:
return sc.client.typedClient.GetSubResource(ctx, obj, subResource, sc.subResource, opts...)
}
}
// Create implements client.SubResourceClient
func (sc *subResourceClient) Create(ctx context.Context, obj Object, subResource Object, opts ...SubResourceCreateOption) error {
defer sc.client.resetGroupVersionKind(obj, obj.GetObjectKind().GroupVersionKind())
defer sc.client.resetGroupVersionKind(subResource, subResource.GetObjectKind().GroupVersionKind())
switch obj.(type) {
case *unstructured.Unstructured:
return sc.client.unstructuredClient.CreateSubResource(ctx, obj, subResource, sc.subResource, 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...)
return sc.client.typedClient.CreateSubResource(ctx, obj, subResource, sc.subResource, 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())
// Update implements client.SubResourceClient
func (sc *subResourceClient) Update(ctx context.Context, obj Object, opts ...SubResourceUpdateOption) error {
defer sc.client.resetGroupVersionKind(obj, obj.GetObjectKind().GroupVersionKind())
switch obj.(type) {
case *unstructured.Unstructured:
return sw.client.unstructuredClient.PatchStatus(ctx, obj, patch, opts...)
return sc.client.unstructuredClient.UpdateSubResource(ctx, obj, sc.subResource, opts...)
case *metav1.PartialObjectMetadata:
return sw.client.metadataClient.PatchStatus(ctx, obj, patch, opts...)
return fmt.Errorf("cannot update status using only metadata -- did you mean to patch?")
default:
return sw.client.typedClient.PatchStatus(ctx, obj, patch, opts...)
return sc.client.typedClient.UpdateSubResource(ctx, obj, sc.subResource, opts...)
}
}
// Patch implements client.SubResourceWriter.
func (sc *subResourceClient) Patch(ctx context.Context, obj Object, patch Patch, opts ...SubResourcePatchOption) error {
defer sc.client.resetGroupVersionKind(obj, obj.GetObjectKind().GroupVersionKind())
switch obj.(type) {
case *unstructured.Unstructured:
return sc.client.unstructuredClient.PatchSubResource(ctx, obj, sc.subResource, patch, opts...)
case *metav1.PartialObjectMetadata:
return sc.client.metadataClient.PatchSubResource(ctx, obj, sc.subResource, patch, opts...)
default:
return sc.client.typedClient.PatchSubResource(ctx, obj, sc.subResource, patch, opts...)
}
}

View File

@ -29,15 +29,32 @@ import (
logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
)
// KubeconfigFlagName is the name of the kubeconfig flag
const KubeconfigFlagName = "kubeconfig"
var (
kubeconfig string
log = logf.RuntimeLog.WithName("client").WithName("config")
)
// init registers the "kubeconfig" flag to the default command line FlagSet.
// TODO: This should be removed, as it potentially leads to redefined flag errors for users, if they already
// have registered the "kubeconfig" flag to the command line FlagSet in other parts of their code.
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.")
RegisterFlags(flag.CommandLine)
}
// RegisterFlags registers flag variables to the given FlagSet if not already registered.
// It uses the default command line FlagSet, if none is provided. Currently, it only registers the kubeconfig flag.
func RegisterFlags(fs *flag.FlagSet) {
if fs == nil {
fs = flag.CommandLine
}
if f := fs.Lookup(KubeconfigFlagName); f != nil {
kubeconfig = f.Value.String()
} else {
fs.StringVar(&kubeconfig, KubeconfigFlagName, "", "Paths to a kubeconfig. Only required if out-of-cluster.")
}
}
// GetConfig creates a *rest.Config for talking to a Kubernetes API server.
@ -47,7 +64,7 @@ func init() {
// It also applies saner defaults for QPS and burst based on the Kubernetes
// controller manager defaults (20 QPS, 30 burst)
//
// Config precedence
// Config precedence:
//
// * --kubeconfig flag pointing at a file
//
@ -67,7 +84,7 @@ func GetConfig() (*rest.Config, error) {
// It also applies saner defaults for QPS and burst based on the Kubernetes
// controller manager defaults (20 QPS, 30 burst)
//
// Config precedence
// Config precedence:
//
// * --kubeconfig flag pointing at a file
//
@ -96,7 +113,7 @@ func GetConfigWithContext(context string) (*rest.Config, error) {
var loadInClusterConfig = rest.InClusterConfig
// loadConfig loads a REST Config as per the rules specified in GetConfig.
func loadConfig(context string) (*rest.Config, error) {
func loadConfig(context string) (config *rest.Config, configErr error) {
// If a flag is specified with the config location, use that
if len(kubeconfig) > 0 {
return loadConfigWithContext("", &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfig}, context)
@ -106,9 +123,16 @@ func loadConfig(context string) (*rest.Config, error) {
// try the in-cluster config.
kubeconfigPath := os.Getenv(clientcmd.RecommendedConfigPathEnvVar)
if len(kubeconfigPath) == 0 {
if c, err := loadInClusterConfig(); err == nil {
c, err := loadInClusterConfig()
if err == nil {
return c, nil
}
defer func() {
if configErr != nil {
log.Error(err, "unable to load in-cluster config")
}
}()
}
// If the recommended kubeconfig env variable is set, or there
@ -123,7 +147,7 @@ func loadConfig(context string) (*rest.Config, error) {
if _, ok := os.LookupEnv("HOME"); !ok {
u, err := user.Current()
if err != nil {
return nil, fmt.Errorf("could not get current user: %v", err)
return nil, fmt.Errorf("could not get current user: %w", err)
}
loadingRules.Precedence = append(loadingRules.Precedence, filepath.Join(u.HomeDir, clientcmd.RecommendedHomeDir, clientcmd.RecommendedFileName))
}

View File

@ -17,7 +17,7 @@ limitations under the License.
// Package client contains functionality for interacting with Kubernetes API
// servers.
//
// Clients
// # Clients
//
// Clients are split into two interfaces -- Readers and Writers. Readers
// get and list, while writers create, update, and delete.
@ -25,18 +25,19 @@ limitations under the License.
// The New function can be used to create a new client that talks directly
// to the API server.
//
// A common pattern in Kubernetes to read from a cache and write to the API
// 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
// # 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
// 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

View File

@ -72,8 +72,8 @@ func (c *dryRunClient) Patch(ctx context.Context, obj Object, patch Patch, opts
}
// Get implements client.Client.
func (c *dryRunClient) Get(ctx context.Context, key ObjectKey, obj Object) error {
return c.client.Get(ctx, key, obj)
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.
@ -82,25 +82,38 @@ func (c *dryRunClient) List(ctx context.Context, obj ObjectList, opts ...ListOpt
}
// Status implements client.StatusClient.
func (c *dryRunClient) Status() StatusWriter {
return &dryRunStatusWriter{client: c.client.Status()}
func (c *dryRunClient) Status() SubResourceWriter {
return c.SubResource("status")
}
// ensure dryRunStatusWriter implements client.StatusWriter.
var _ StatusWriter = &dryRunStatusWriter{}
// SubResource implements client.SubResourceClient.
func (c *dryRunClient) SubResource(subResource string) SubResourceClient {
return &dryRunSubResourceClient{client: c.client.SubResource(subResource)}
}
// dryRunStatusWriter is client.StatusWriter that writes status subresource with dryRun mode
// ensure dryRunSubResourceWriter implements client.SubResourceWriter.
var _ SubResourceWriter = &dryRunSubResourceClient{}
// dryRunSubResourceClient is client.SubResourceWriter that writes status subresource with dryRun mode
// enforced.
type dryRunStatusWriter struct {
client StatusWriter
type dryRunSubResourceClient struct {
client SubResourceClient
}
// Update implements client.StatusWriter.
func (sw *dryRunStatusWriter) Update(ctx context.Context, obj Object, opts ...UpdateOption) error {
func (sw *dryRunSubResourceClient) Get(ctx context.Context, obj, subResource Object, opts ...SubResourceGetOption) error {
return sw.client.Get(ctx, obj, subResource, opts...)
}
func (sw *dryRunSubResourceClient) Create(ctx context.Context, obj, subResource Object, opts ...SubResourceCreateOption) error {
return sw.client.Create(ctx, obj, subResource, append(opts, DryRunAll)...)
}
// Update implements client.SubResourceWriter.
func (sw *dryRunSubResourceClient) Update(ctx context.Context, obj Object, opts ...SubResourceUpdateOption) 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 {
// Patch implements client.SubResourceWriter.
func (sw *dryRunSubResourceClient) Patch(ctx context.Context, obj Object, patch Patch, opts ...SubResourcePatchOption) error {
return sw.client.Patch(ctx, obj, patch, append(opts, DryRunAll)...)
}

View File

@ -50,7 +50,7 @@ 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) error
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
@ -60,7 +60,8 @@ type Reader interface {
// Writer knows how to create, delete, and update Kubernetes objects.
type Writer interface {
// Create saves the object obj in the Kubernetes cluster.
// Create saves the object obj in the Kubernetes cluster. obj must be a
// struct pointer so that obj can be updated with the content returned by the Server.
Create(ctx context.Context, obj Object, opts ...CreateOption) error
// Delete deletes the given obj from Kubernetes cluster.
@ -81,20 +82,80 @@ type Writer interface {
// StatusClient knows how to create a client which can update status subresource
// for kubernetes objects.
type StatusClient interface {
Status() StatusWriter
Status() SubResourceWriter
}
// StatusWriter knows how to update status subresource of a Kubernetes object.
type StatusWriter interface {
// SubResourceClientConstructor knows how to create a client which can update subresource
// for kubernetes objects.
type SubResourceClientConstructor interface {
// SubResourceClientConstructor returns a subresource client for the named subResource. Known
// upstream subResources usages are:
// - ServiceAccount token creation:
// sa := &corev1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"}}
// token := &authenticationv1.TokenRequest{}
// c.SubResourceClient("token").Create(ctx, sa, token)
//
// - Pod eviction creation:
// pod := &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"}}
// c.SubResourceClient("eviction").Create(ctx, pod, &policyv1.Eviction{})
//
// - Pod binding creation:
// pod := &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"}}
// binding := &corev1.Binding{Target: corev1.ObjectReference{Name: "my-node"}}
// c.SubResourceClient("binding").Create(ctx, pod, binding)
//
// - CertificateSigningRequest approval:
// csr := &certificatesv1.CertificateSigningRequest{
// ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"},
// Status: certificatesv1.CertificateSigningRequestStatus{
// Conditions: []certificatesv1.[]CertificateSigningRequestCondition{{
// Type: certificatesv1.CertificateApproved,
// Status: corev1.ConditionTrue,
// }},
// },
// }
// c.SubResourceClient("approval").Update(ctx, csr)
//
// - Scale retrieval:
// dep := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"}}
// scale := &autoscalingv1.Scale{}
// c.SubResourceClient("scale").Get(ctx, dep, scale)
//
// - Scale update:
// dep := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"}}
// scale := &autoscalingv1.Scale{Spec: autoscalingv1.ScaleSpec{Replicas: 2}}
// c.SubResourceClient("scale").Update(ctx, dep, client.WithSubResourceBody(scale))
SubResource(subResource string) SubResourceClient
}
// StatusWriter is kept for backward compatibility.
type StatusWriter = SubResourceWriter
// SubResourceReader knows how to read SubResources
type SubResourceReader interface {
Get(ctx context.Context, obj Object, subResource Object, opts ...SubResourceGetOption) error
}
// SubResourceWriter knows how to update subresource of a Kubernetes object.
type SubResourceWriter interface {
// Create saves the subResource object in the Kubernetes cluster. obj must be a
// struct pointer so that obj can be updated with the content returned by the Server.
Create(ctx context.Context, obj Object, subResource Object, opts ...SubResourceCreateOption) error
// 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
Update(ctx context.Context, obj Object, opts ...SubResourceUpdateOption) 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
Patch(ctx context.Context, obj Object, patch Patch, opts ...SubResourcePatchOption) error
}
// SubResourceClient knows how to perform CRU operations on Kubernetes objects.
type SubResourceClient interface {
SubResourceReader
SubResourceWriter
}
// Client knows how to perform CRUD operations on Kubernetes objects.
@ -102,6 +163,7 @@ type Client interface {
Reader
Writer
StatusClient
SubResourceClientConstructor
// Scheme returns the scheme this client is using.
Scheme() *runtime.Scheme
@ -143,3 +205,13 @@ func IgnoreNotFound(err error) error {
}
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
}

View File

@ -116,7 +116,7 @@ func (mc *metadataClient) Patch(ctx context.Context, obj Object, patch Patch, op
}
// Get implements client.Client.
func (mc *metadataClient) Get(ctx context.Context, key ObjectKey, obj Object) error {
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)
@ -124,12 +124,15 @@ func (mc *metadataClient) Get(ctx context.Context, key ObjectKey, obj Object) er
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, metav1.GetOptions{})
res, err := resInt.Get(ctx, key.Name, *getOpts.AsGetOptions())
if err != nil {
return err
}
@ -146,9 +149,7 @@ func (mc *metadataClient) List(ctx context.Context, obj ObjectList, opts ...List
}
gvk := metadata.GroupVersionKind()
if strings.HasSuffix(gvk.Kind, "List") {
gvk.Kind = gvk.Kind[:len(gvk.Kind)-4]
}
gvk.Kind = strings.TrimSuffix(gvk.Kind, "List")
listOpts := ListOptions{}
listOpts.ApplyOptions(opts)
@ -167,7 +168,7 @@ func (mc *metadataClient) List(ctx context.Context, obj ObjectList, opts ...List
return nil
}
func (mc *metadataClient) PatchStatus(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
func (mc *metadataClient) PatchSubResource(ctx context.Context, obj Object, subResource string, patch Patch, opts ...SubResourcePatchOption) error {
metadata, ok := obj.(*metav1.PartialObjectMetadata)
if !ok {
return fmt.Errorf("metadata client did not understand object: %T", obj)
@ -179,16 +180,24 @@ func (mc *metadataClient) PatchStatus(ctx context.Context, obj Object, patch Pat
return err
}
data, err := patch.Data(obj)
patchOpts := &SubResourcePatchOptions{}
patchOpts.ApplyOptions(opts)
body := obj
if patchOpts.SubResourceBody != nil {
body = patchOpts.SubResourceBody
}
data, err := patch.Data(body)
if err != nil {
return err
}
patchOpts := &PatchOptions{}
res, err := resInt.Patch(ctx, metadata.Name, patch.Type(), data, *patchOpts.AsPatchOptions(), "status")
res, err := resInt.Patch(ctx, metadata.Name, patch.Type(), data, *patchOpts.AsPatchOptions(), subResource)
if err != nil {
return err
}
*metadata = *res
metadata.SetGroupVersionKind(gvk) // restore the GVK, which isn't set on metadata
return nil

View File

@ -52,11 +52,11 @@ func (n *namespacedClient) RESTMapper() meta.RESTMapper {
return n.client.RESTMapper()
}
// Create implements clinet.Client.
// 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: %v", err)
return fmt.Errorf("error finding the scope of the object: %w", err)
}
objectNamespace := obj.GetNamespace()
@ -74,7 +74,7 @@ func (n *namespacedClient) Create(ctx context.Context, obj Object, opts ...Creat
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: %v", err)
return fmt.Errorf("error finding the scope of the object: %w", err)
}
objectNamespace := obj.GetNamespace()
@ -92,7 +92,7 @@ func (n *namespacedClient) Update(ctx context.Context, obj Object, opts ...Updat
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: %v", err)
return fmt.Errorf("error finding the scope of the object: %w", err)
}
objectNamespace := obj.GetNamespace()
@ -110,7 +110,7 @@ func (n *namespacedClient) Delete(ctx context.Context, obj Object, opts ...Delet
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: %v", err)
return fmt.Errorf("error finding the scope of the object: %w", err)
}
if isNamespaceScoped {
@ -123,7 +123,7 @@ func (n *namespacedClient) DeleteAllOf(ctx context.Context, obj Object, opts ...
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: %v", err)
return fmt.Errorf("error finding the scope of the object: %w", err)
}
objectNamespace := obj.GetNamespace()
@ -138,18 +138,18 @@ func (n *namespacedClient) Patch(ctx context.Context, obj Object, patch Patch, o
}
// Get implements client.Client.
func (n *namespacedClient) Get(ctx context.Context, key ObjectKey, obj Object) error {
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: %v", err)
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 namesapce %s on the client", key.Namespace, obj.GetName(), 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)
return n.client.Get(ctx, key, obj, opts...)
}
// List implements client.Client.
@ -161,25 +161,28 @@ func (n *namespacedClient) List(ctx context.Context, obj ObjectList, opts ...Lis
}
// Status implements client.StatusClient.
func (n *namespacedClient) Status() StatusWriter {
return &namespacedClientStatusWriter{StatusClient: n.client.Status(), namespace: n.namespace, namespacedclient: n}
func (n *namespacedClient) Status() SubResourceWriter {
return n.SubResource("status")
}
// ensure namespacedClientStatusWriter implements client.StatusWriter.
var _ StatusWriter = &namespacedClientStatusWriter{}
// SubResource implements client.SubResourceClient.
func (n *namespacedClient) SubResource(subResource string) SubResourceClient {
return &namespacedClientSubResourceClient{client: n.client.SubResource(subResource), namespace: n.namespace, namespacedclient: n}
}
type namespacedClientStatusWriter struct {
StatusClient StatusWriter
// ensure namespacedClientSubResourceClient implements client.SubResourceClient.
var _ SubResourceClient = &namespacedClientSubResourceClient{}
type namespacedClientSubResourceClient struct {
client SubResourceClient
namespace string
namespacedclient Client
}
// Update implements client.StatusWriter.
func (nsw *namespacedClientStatusWriter) Update(ctx context.Context, obj Object, opts ...UpdateOption) error {
func (nsw *namespacedClientSubResourceClient) Get(ctx context.Context, obj, subResource Object, opts ...SubResourceGetOption) 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: %v", err)
return fmt.Errorf("error finding the scope of the object: %w", err)
}
objectNamespace := obj.GetNamespace()
@ -190,15 +193,14 @@ func (nsw *namespacedClientStatusWriter) Update(ctx context.Context, obj Object,
if isNamespaceScoped && objectNamespace == "" {
obj.SetNamespace(nsw.namespace)
}
return nsw.StatusClient.Update(ctx, obj, opts...)
return nsw.client.Get(ctx, obj, subResource, opts...)
}
// Patch implements client.StatusWriter.
func (nsw *namespacedClientStatusWriter) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
func (nsw *namespacedClientSubResourceClient) Create(ctx context.Context, obj, subResource Object, opts ...SubResourceCreateOption) 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: %v", err)
return fmt.Errorf("error finding the scope of the object: %w", err)
}
objectNamespace := obj.GetNamespace()
@ -209,5 +211,43 @@ func (nsw *namespacedClientStatusWriter) Patch(ctx context.Context, obj Object,
if isNamespaceScoped && objectNamespace == "" {
obj.SetNamespace(nsw.namespace)
}
return nsw.StatusClient.Patch(ctx, obj, patch, opts...)
return nsw.client.Create(ctx, obj, subResource, opts...)
}
// Update implements client.SubResourceWriter.
func (nsw *namespacedClientSubResourceClient) Update(ctx context.Context, obj Object, opts ...SubResourceUpdateOption) 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.client.Update(ctx, obj, opts...)
}
// Patch implements client.SubResourceWriter.
func (nsw *namespacedClientSubResourceClient) Patch(ctx context.Context, obj Object, patch Patch, opts ...SubResourcePatchOption) 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.client.Patch(ctx, obj, patch, opts...)
}

View File

@ -37,6 +37,12 @@ type DeleteOption interface {
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.
@ -61,6 +67,29 @@ type DeleteAllOfOption interface {
ApplyToDeleteAllOf(*DeleteAllOfOptions)
}
// SubResourceGetOption modifies options for a SubResource Get request.
type SubResourceGetOption interface {
ApplyToSubResourceGet(*SubResourceGetOptions)
}
// SubResourceUpdateOption is some configuration that modifies options for a update request.
type SubResourceUpdateOption interface {
// ApplyToSubResourceUpdate applies this configuration to the given update options.
ApplyToSubResourceUpdate(*SubResourceUpdateOptions)
}
// SubResourceCreateOption is some configuration that modifies options for a create request.
type SubResourceCreateOption interface {
// ApplyToSubResourceCreate applies this configuration to the given create options.
ApplyToSubResourceCreate(*SubResourceCreateOptions)
}
// SubResourcePatchOption configures a subresource patch request.
type SubResourcePatchOption interface {
// ApplyToSubResourcePatch applies the configuration on the given patch options.
ApplyToSubResourcePatch(*SubResourcePatchOptions)
}
// }}}
// {{{ Multi-Type Options
@ -90,10 +119,23 @@ func (dryRunAll) ApplyToPatch(opts *PatchOptions) {
func (dryRunAll) ApplyToDelete(opts *DeleteOptions) {
opts.DryRun = []string{metav1.DryRunAll}
}
func (dryRunAll) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
opts.DryRun = []string{metav1.DryRunAll}
}
func (dryRunAll) ApplyToSubResourceCreate(opts *SubResourceCreateOptions) {
opts.DryRun = []string{metav1.DryRunAll}
}
func (dryRunAll) ApplyToSubResourceUpdate(opts *SubResourceUpdateOptions) {
opts.DryRun = []string{metav1.DryRunAll}
}
func (dryRunAll) ApplyToSubResourcePatch(opts *SubResourcePatchOptions) {
opts.DryRun = []string{metav1.DryRunAll}
}
// FieldOwner set the field manager name for the given server-side apply patch.
type FieldOwner string
@ -311,6 +353,45 @@ func (p PropagationPolicy) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
// }}}
// {{{ 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.
@ -318,7 +399,7 @@ func (p PropagationPolicy) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) {
// pre-parsed selectors (since generally, selectors will be executed
// against the cache).
type ListOptions struct {
// LabelSelector filters results by label. Use SetLabelSelector to
// 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
@ -341,6 +422,12 @@ type ListOptions struct {
// it has expired. This field is not supported if watch is true in the Raw ListOptions.
Continue string
// UnsafeDisableDeepCopy indicates not to deep copy objects during list objects.
// Be very careful with this, when enabled you must DeepCopy any object before mutating it,
// otherwise you will mutate the object in the cache.
// +optional
UnsafeDisableDeepCopy *bool
// 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.
@ -369,6 +456,9 @@ func (o *ListOptions) ApplyToList(lo *ListOptions) {
if o.Continue != "" {
lo.Continue = o.Continue
}
if o.UnsafeDisableDeepCopy != nil {
lo.UnsafeDisableDeepCopy = o.UnsafeDisableDeepCopy
}
}
// AsListOptions returns these options as a flattened metav1.ListOptions.
@ -511,6 +601,25 @@ func (l Limit) ApplyToList(opts *ListOptions) {
opts.Limit = int64(l)
}
// UnsafeDisableDeepCopyOption indicates not to deep copy objects during list objects.
// Be very careful with this, when enabled you must DeepCopy any object before mutating it,
// otherwise you will mutate the object in the cache.
type UnsafeDisableDeepCopyOption bool
// ApplyToList applies this configuration to the given an List options.
func (d UnsafeDisableDeepCopyOption) ApplyToList(opts *ListOptions) {
definitelyTrue := true
definitelyFalse := false
if d {
opts.UnsafeDisableDeepCopy = &definitelyTrue
} else {
opts.UnsafeDisableDeepCopy = &definitelyFalse
}
}
// UnsafeDisableDeepCopy indicates not to deep copy objects during list objects.
const UnsafeDisableDeepCopy = UnsafeDisableDeepCopyOption(true)
// 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.

View File

@ -19,7 +19,7 @@ package client
import (
"fmt"
jsonpatch "github.com/evanphx/json-patch"
jsonpatch "github.com/evanphx/json-patch/v5"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/apimachinery/pkg/util/strategicpatch"

View File

@ -61,8 +61,9 @@ func NewDelegatingClient(in NewDelegatingClientInput) (Client, error) {
uncachedGVKs: uncachedGVKs,
cacheUnstructured: in.CacheUnstructured,
},
Writer: in.Client,
StatusClient: in.Client,
Writer: in.Client,
StatusClient: in.Client,
SubResourceClientConstructor: in.Client,
}, nil
}
@ -70,6 +71,7 @@ type delegatingClient struct {
Reader
Writer
StatusClient
SubResourceClientConstructor
scheme *runtime.Scheme
mapper meta.RESTMapper
@ -121,13 +123,13 @@ func (d *delegatingReader) shouldBypassCache(obj runtime.Object) (bool, error) {
}
// Get retrieves an obj for a given object key from the Kubernetes Cluster.
func (d *delegatingReader) Get(ctx context.Context, key ObjectKey, obj Object) error {
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)
return d.ClientReader.Get(ctx, key, obj, opts...)
}
return d.CacheReader.Get(ctx, key, obj)
return d.CacheReader.Get(ctx, key, obj, opts...)
}
// List retrieves list of objects for a given namespace and list options.

View File

@ -24,7 +24,6 @@ import (
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.
@ -42,6 +41,7 @@ func (c *typedClient) Create(ctx context.Context, obj Object, opts ...CreateOpti
createOpts := &CreateOptions{}
createOpts.ApplyOptions(opts)
return o.Post().
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
Resource(o.resource()).
@ -60,6 +60,7 @@ func (c *typedClient) Update(ctx context.Context, obj Object, opts ...UpdateOpti
updateOpts := &UpdateOptions{}
updateOpts.ApplyOptions(opts)
return o.Put().
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
Resource(o.resource()).
@ -121,25 +122,30 @@ func (c *typedClient) Patch(ctx context.Context, obj Object, patch Patch, opts .
}
patchOpts := &PatchOptions{}
patchOpts.ApplyOptions(opts)
return o.Patch(patch.Type()).
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
Resource(o.resource()).
Name(o.GetName()).
VersionedParams(patchOpts.ApplyOptions(opts).AsPatchOptions(), c.paramCodec).
VersionedParams(patchOpts.AsPatchOptions(), c.paramCodec).
Body(data).
Do(ctx).
Into(obj)
}
// Get implements client.Client.
func (c *typedClient) Get(ctx context.Context, key ObjectKey, obj Object) error {
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)
}
@ -149,8 +155,10 @@ func (c *typedClient) List(ctx context.Context, obj ObjectList, opts ...ListOpti
if err != nil {
return err
}
listOpts := ListOptions{}
listOpts.ApplyOptions(opts)
return r.Get().
NamespaceIfScoped(listOpts.Namespace, r.isNamespaced()).
Resource(r.resource()).
@ -159,8 +167,55 @@ func (c *typedClient) List(ctx context.Context, obj ObjectList, opts ...ListOpti
Into(obj)
}
// UpdateStatus used by StatusWriter to write status.
func (c *typedClient) UpdateStatus(ctx context.Context, obj Object, opts ...UpdateOption) error {
func (c *typedClient) GetSubResource(ctx context.Context, obj, subResourceObj Object, subResource string, opts ...SubResourceGetOption) error {
o, err := c.cache.getObjMeta(obj)
if err != nil {
return err
}
if subResourceObj.GetName() == "" {
subResourceObj.SetName(obj.GetName())
}
getOpts := &SubResourceGetOptions{}
getOpts.ApplyOptions(opts)
return o.Get().
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
Resource(o.resource()).
Name(o.GetName()).
SubResource(subResource).
VersionedParams(getOpts.AsGetOptions(), c.paramCodec).
Do(ctx).
Into(subResourceObj)
}
func (c *typedClient) CreateSubResource(ctx context.Context, obj Object, subResourceObj Object, subResource string, opts ...SubResourceCreateOption) error {
o, err := c.cache.getObjMeta(obj)
if err != nil {
return err
}
if subResourceObj.GetName() == "" {
subResourceObj.SetName(obj.GetName())
}
createOpts := &SubResourceCreateOptions{}
createOpts.ApplyOptions(opts)
return o.Post().
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
Resource(o.resource()).
Name(o.GetName()).
SubResource(subResource).
Body(subResourceObj).
VersionedParams(createOpts.AsCreateOptions(), c.paramCodec).
Do(ctx).
Into(subResourceObj)
}
// UpdateSubResource used by SubResourceWriter to write status.
func (c *typedClient) UpdateSubResource(ctx context.Context, obj Object, subResource string, opts ...SubResourceUpdateOption) error {
o, err := c.cache.getObjMeta(obj)
if err != nil {
return err
@ -169,37 +224,58 @@ func (c *typedClient) UpdateStatus(ctx context.Context, obj Object, opts ...Upda
// 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
updateOpts := &SubResourceUpdateOptions{}
updateOpts.ApplyOptions(opts)
body := obj
if updateOpts.SubResourceBody != nil {
body = updateOpts.SubResourceBody
}
if body.GetName() == "" {
body.SetName(obj.GetName())
}
if body.GetNamespace() == "" {
body.SetNamespace(obj.GetNamespace())
}
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).
SubResource(subResource).
Body(body).
VersionedParams(updateOpts.AsUpdateOptions(), c.paramCodec).
Do(ctx).
Into(obj)
Into(body)
}
// PatchStatus used by StatusWriter to write status.
func (c *typedClient) PatchStatus(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
// PatchSubResource used by SubResourceWriter to write subresource.
func (c *typedClient) PatchSubResource(ctx context.Context, obj Object, subResource string, patch Patch, opts ...SubResourcePatchOption) error {
o, err := c.cache.getObjMeta(obj)
if err != nil {
return err
}
data, err := patch.Data(obj)
patchOpts := &SubResourcePatchOptions{}
patchOpts.ApplyOptions(opts)
body := obj
if patchOpts.SubResourceBody != nil {
body = patchOpts.SubResourceBody
}
data, err := patch.Data(body)
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").
SubResource(subResource).
Body(data).
VersionedParams(patchOpts.ApplyOptions(opts).AsPatchOptions(), c.paramCodec).
VersionedParams(patchOpts.AsPatchOptions(), c.paramCodec).
Do(ctx).
Into(obj)
Into(body)
}

View File

@ -27,7 +27,6 @@ import (
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.
@ -52,6 +51,7 @@ func (uc *unstructuredClient) Create(ctx context.Context, obj Object, opts ...Cr
createOpts := &CreateOptions{}
createOpts.ApplyOptions(opts)
result := o.Post().
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
Resource(o.resource()).
@ -80,6 +80,7 @@ func (uc *unstructuredClient) Update(ctx context.Context, obj Object, opts ...Up
updateOpts := UpdateOptions{}
updateOpts.ApplyOptions(opts)
result := o.Put().
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
Resource(o.resource()).
@ -95,8 +96,7 @@ func (uc *unstructuredClient) Update(ctx context.Context, obj Object, opts ...Up
// Delete implements client.Client.
func (uc *unstructuredClient) Delete(ctx context.Context, obj Object, opts ...DeleteOption) error {
_, ok := obj.(*unstructured.Unstructured)
if !ok {
if _, ok := obj.(*unstructured.Unstructured); !ok {
return fmt.Errorf("unstructured client did not understand object: %T", obj)
}
@ -107,6 +107,7 @@ func (uc *unstructuredClient) Delete(ctx context.Context, obj Object, opts ...De
deleteOpts := DeleteOptions{}
deleteOpts.ApplyOptions(opts)
return o.Delete().
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
Resource(o.resource()).
@ -118,8 +119,7 @@ func (uc *unstructuredClient) Delete(ctx context.Context, obj Object, opts ...De
// DeleteAllOf implements client.Client.
func (uc *unstructuredClient) DeleteAllOf(ctx context.Context, obj Object, opts ...DeleteAllOfOption) error {
_, ok := obj.(*unstructured.Unstructured)
if !ok {
if _, ok := obj.(*unstructured.Unstructured); !ok {
return fmt.Errorf("unstructured client did not understand object: %T", obj)
}
@ -130,6 +130,7 @@ func (uc *unstructuredClient) DeleteAllOf(ctx context.Context, obj Object, opts
deleteAllOfOpts := DeleteAllOfOptions{}
deleteAllOfOpts.ApplyOptions(opts)
return o.Delete().
NamespaceIfScoped(deleteAllOfOpts.ListOptions.Namespace, o.isNamespaced()).
Resource(o.resource()).
@ -141,8 +142,7 @@ func (uc *unstructuredClient) DeleteAllOf(ctx context.Context, obj Object, opts
// Patch implements client.Client.
func (uc *unstructuredClient) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
_, ok := obj.(*unstructured.Unstructured)
if !ok {
if _, ok := obj.(*unstructured.Unstructured); !ok {
return fmt.Errorf("unstructured client did not understand object: %T", obj)
}
@ -157,18 +157,20 @@ func (uc *unstructuredClient) Patch(ctx context.Context, obj Object, patch Patch
}
patchOpts := &PatchOptions{}
patchOpts.ApplyOptions(opts)
return o.Patch(patch.Type()).
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
Resource(o.resource()).
Name(o.GetName()).
VersionedParams(patchOpts.ApplyOptions(opts).AsPatchOptions(), uc.paramCodec).
VersionedParams(patchOpts.AsPatchOptions(), uc.paramCodec).
Body(data).
Do(ctx).
Into(obj)
}
// Get implements client.Client.
func (uc *unstructuredClient) Get(ctx context.Context, key ObjectKey, obj Object) error {
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)
@ -176,6 +178,9 @@ func (uc *unstructuredClient) Get(ctx context.Context, key ObjectKey, obj Object
gvk := u.GroupVersionKind()
getOpts := GetOptions{}
getOpts.ApplyOptions(opts)
r, err := uc.cache.getResource(obj)
if err != nil {
return err
@ -184,6 +189,7 @@ func (uc *unstructuredClient) Get(ctx context.Context, key ObjectKey, obj Object
result := r.Get().
NamespaceIfScoped(key.Namespace, r.isNamespaced()).
Resource(r.resource()).
VersionedParams(getOpts.AsGetOptions(), uc.paramCodec).
Name(key.Name).
Do(ctx).
Into(obj)
@ -201,18 +207,16 @@ func (uc *unstructuredClient) List(ctx context.Context, obj ObjectList, opts ...
}
gvk := u.GroupVersionKind()
if strings.HasSuffix(gvk.Kind, "List") {
gvk.Kind = gvk.Kind[:len(gvk.Kind)-4]
}
listOpts := ListOptions{}
listOpts.ApplyOptions(opts)
gvk.Kind = strings.TrimSuffix(gvk.Kind, "List")
r, err := uc.cache.getResource(obj)
if err != nil {
return err
}
listOpts := ListOptions{}
listOpts.ApplyOptions(opts)
return r.Get().
NamespaceIfScoped(listOpts.Namespace, r.isNamespaced()).
Resource(r.resource()).
@ -221,9 +225,71 @@ func (uc *unstructuredClient) List(ctx context.Context, obj ObjectList, opts ...
Into(obj)
}
func (uc *unstructuredClient) UpdateStatus(ctx context.Context, obj Object, opts ...UpdateOption) error {
_, ok := obj.(*unstructured.Unstructured)
if !ok {
func (uc *unstructuredClient) GetSubResource(ctx context.Context, obj, subResourceObj Object, subResource string, opts ...SubResourceGetOption) error {
if _, ok := obj.(*unstructured.Unstructured); !ok {
return fmt.Errorf("unstructured client did not understand object: %T", subResource)
}
if _, ok := subResourceObj.(*unstructured.Unstructured); !ok {
return fmt.Errorf("unstructured client did not understand object: %T", obj)
}
if subResourceObj.GetName() == "" {
subResourceObj.SetName(obj.GetName())
}
o, err := uc.cache.getObjMeta(obj)
if err != nil {
return err
}
getOpts := &SubResourceGetOptions{}
getOpts.ApplyOptions(opts)
return o.Get().
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
Resource(o.resource()).
Name(o.GetName()).
SubResource(subResource).
VersionedParams(getOpts.AsGetOptions(), uc.paramCodec).
Do(ctx).
Into(subResourceObj)
}
func (uc *unstructuredClient) CreateSubResource(ctx context.Context, obj, subResourceObj Object, subResource string, opts ...SubResourceCreateOption) error {
if _, ok := obj.(*unstructured.Unstructured); !ok {
return fmt.Errorf("unstructured client did not understand object: %T", subResourceObj)
}
if _, ok := subResourceObj.(*unstructured.Unstructured); !ok {
return fmt.Errorf("unstructured client did not understand object: %T", obj)
}
if subResourceObj.GetName() == "" {
subResourceObj.SetName(obj.GetName())
}
o, err := uc.cache.getObjMeta(obj)
if err != nil {
return err
}
createOpts := &SubResourceCreateOptions{}
createOpts.ApplyOptions(opts)
return o.Post().
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
Resource(o.resource()).
Name(o.GetName()).
SubResource(subResource).
Body(subResourceObj).
VersionedParams(createOpts.AsCreateOptions(), uc.paramCodec).
Do(ctx).
Into(subResourceObj)
}
func (uc *unstructuredClient) UpdateSubResource(ctx context.Context, obj Object, subResource string, opts ...SubResourceUpdateOption) error {
if _, ok := obj.(*unstructured.Unstructured); !ok {
return fmt.Errorf("unstructured client did not understand object: %T", obj)
}
@ -232,18 +298,32 @@ func (uc *unstructuredClient) UpdateStatus(ctx context.Context, obj Object, opts
return err
}
updateOpts := SubResourceUpdateOptions{}
updateOpts.ApplyOptions(opts)
body := obj
if updateOpts.SubResourceBody != nil {
body = updateOpts.SubResourceBody
}
if body.GetName() == "" {
body.SetName(obj.GetName())
}
if body.GetNamespace() == "" {
body.SetNamespace(obj.GetNamespace())
}
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).
SubResource(subResource).
Body(body).
VersionedParams(updateOpts.AsUpdateOptions(), uc.paramCodec).
Do(ctx).
Into(obj)
Into(body)
}
func (uc *unstructuredClient) PatchStatus(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error {
func (uc *unstructuredClient) PatchSubResource(ctx context.Context, obj Object, subResource string, patch Patch, opts ...SubResourcePatchOption) error {
u, ok := obj.(*unstructured.Unstructured)
if !ok {
return fmt.Errorf("unstructured client did not understand object: %T", obj)
@ -256,21 +336,28 @@ func (uc *unstructuredClient) PatchStatus(ctx context.Context, obj Object, patch
return err
}
data, err := patch.Data(obj)
patchOpts := &SubResourcePatchOptions{}
patchOpts.ApplyOptions(opts)
body := obj
if patchOpts.SubResourceBody != nil {
body = patchOpts.SubResourceBody
}
data, err := patch.Data(body)
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").
SubResource(subResource).
Body(data).
VersionedParams(patchOpts.ApplyOptions(opts).AsPatchOptions(), uc.paramCodec).
VersionedParams(patchOpts.AsPatchOptions(), uc.paramCodec).
Do(ctx).
Into(u)
Into(body)
u.SetGroupVersionKind(gvk)
return result

View File

@ -69,9 +69,7 @@ func (w *watchingClient) listOpts(opts ...ListOption) ListOptions {
func (w *watchingClient) metadataWatch(ctx context.Context, obj *metav1.PartialObjectMetadataList, opts ...ListOption) (watch.Interface, error) {
gvk := obj.GroupVersionKind()
if strings.HasSuffix(gvk.Kind, "List") {
gvk.Kind = gvk.Kind[:len(gvk.Kind)-4]
}
gvk.Kind = strings.TrimSuffix(gvk.Kind, "List")
listOpts := w.listOpts(opts...)
@ -85,9 +83,7 @@ func (w *watchingClient) metadataWatch(ctx context.Context, obj *metav1.PartialO
func (w *watchingClient) unstructuredWatch(ctx context.Context, obj *unstructured.UnstructuredList, opts ...ListOption) (watch.Interface, error) {
gvk := obj.GroupVersionKind()
if strings.HasSuffix(gvk.Kind, "List") {
gvk.Kind = gvk.Kind[:len(gvk.Kind)-4]
}
gvk.Kind = strings.TrimSuffix(gvk.Kind, "List")
r, err := w.client.unstructuredClient.cache.getResource(obj)
if err != nil {