Upgrade k8s package verison (#5358)
* upgrade k8s package version Signed-off-by: hongzhouzi <hongzhouzi@kubesphere.io> * Script upgrade and code formatting. Signed-off-by: hongzhouzi <hongzhouzi@kubesphere.io> Signed-off-by: hongzhouzi <hongzhouzi@kubesphere.io>
This commit is contained in:
122
vendor/k8s.io/cli-runtime/pkg/resource/builder.go
generated
vendored
122
vendor/k8s.io/cli-runtime/pkg/resource/builder.go
generated
vendored
@@ -22,6 +22,7 @@ import (
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -38,13 +39,14 @@ import (
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/restmapper"
|
||||
"sigs.k8s.io/kustomize/api/filesys"
|
||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||
)
|
||||
|
||||
var FileExtensions = []string{".json", ".yaml", ".yml"}
|
||||
var InputExtensions = append(FileExtensions, "stdin")
|
||||
|
||||
const defaultHttpGetAttempts int = 3
|
||||
const defaultHttpGetAttempts = 3
|
||||
const pathNotExistError = "the path %q does not exist"
|
||||
|
||||
// Builder provides convenience functions for taking arguments and parameters
|
||||
// from the command line and converting them to a list of resources to iterate
|
||||
@@ -72,9 +74,10 @@ type Builder struct {
|
||||
|
||||
errs []error
|
||||
|
||||
paths []Visitor
|
||||
stream bool
|
||||
dir bool
|
||||
paths []Visitor
|
||||
stream bool
|
||||
stdinInUse bool
|
||||
dir bool
|
||||
|
||||
labelSelector *string
|
||||
fieldSelector *string
|
||||
@@ -82,7 +85,8 @@ type Builder struct {
|
||||
limitChunks int64
|
||||
requestTransforms []RequestTransform
|
||||
|
||||
resources []string
|
||||
resources []string
|
||||
subresource string
|
||||
|
||||
namespace string
|
||||
allNamespace bool
|
||||
@@ -121,6 +125,8 @@ Example resource specifications include:
|
||||
'-f rsrc.yaml'
|
||||
'--filename=rsrc.json'`)
|
||||
|
||||
var StdinMultiUseError = errors.New("standard input cannot be used for multiple arguments")
|
||||
|
||||
// TODO: expand this to include other errors.
|
||||
func IsUsageError(err error) bool {
|
||||
if err == nil {
|
||||
@@ -209,7 +215,7 @@ func NewBuilder(restClientGetter RESTClientGetter) *Builder {
|
||||
|
||||
return newBuilder(
|
||||
restClientGetter.ToRESTConfig,
|
||||
(&cachingRESTMapperFunc{delegate: restClientGetter.ToRESTMapper}).ToRESTMapper,
|
||||
restClientGetter.ToRESTMapper,
|
||||
(&cachingCategoryExpanderFunc{delegate: categoryExpanderFn}).ToCategoryExpander,
|
||||
)
|
||||
}
|
||||
@@ -252,10 +258,15 @@ func (b *Builder) FilenameParam(enforceNamespace bool, filenameOptions *Filename
|
||||
}
|
||||
b.URL(defaultHttpGetAttempts, url)
|
||||
default:
|
||||
if !recursive {
|
||||
matches, err := expandIfFilePattern(s)
|
||||
if err != nil {
|
||||
b.errs = append(b.errs, err)
|
||||
continue
|
||||
}
|
||||
if !recursive && len(matches) == 1 {
|
||||
b.singleItemImplied = true
|
||||
}
|
||||
b.Path(recursive, s)
|
||||
b.Path(recursive, matches...)
|
||||
}
|
||||
}
|
||||
if filenameOptions.Kustomize != "" {
|
||||
@@ -362,13 +373,31 @@ func (b *Builder) URL(httpAttemptCount int, urls ...*url.URL) *Builder {
|
||||
|
||||
// Stdin will read objects from the standard input. If ContinueOnError() is set
|
||||
// prior to this method being called, objects in the stream that are unrecognized
|
||||
// will be ignored (but logged at V(2)).
|
||||
// will be ignored (but logged at V(2)). If StdinInUse() is set prior to this method
|
||||
// being called, an error will be recorded as there are multiple entities trying to use
|
||||
// the single standard input stream.
|
||||
func (b *Builder) Stdin() *Builder {
|
||||
b.stream = true
|
||||
if b.stdinInUse {
|
||||
b.errs = append(b.errs, StdinMultiUseError)
|
||||
}
|
||||
b.stdinInUse = true
|
||||
b.paths = append(b.paths, FileVisitorForSTDIN(b.mapper, b.schema))
|
||||
return b
|
||||
}
|
||||
|
||||
// StdinInUse will mark standard input as in use by this Builder, and therefore standard
|
||||
// input should not be used by another entity. If Stdin() is set prior to this method
|
||||
// being called, an error will be recorded as there are multiple entities trying to use
|
||||
// the single standard input stream.
|
||||
func (b *Builder) StdinInUse() *Builder {
|
||||
if b.stdinInUse {
|
||||
b.errs = append(b.errs, StdinMultiUseError)
|
||||
}
|
||||
b.stdinInUse = true
|
||||
return b
|
||||
}
|
||||
|
||||
// Stream will read objects from the provided reader, and if an error occurs will
|
||||
// include the name string in the error message. If ContinueOnError() is set
|
||||
// prior to this method being called, objects in the stream that are unrecognized
|
||||
@@ -388,7 +417,7 @@ func (b *Builder) Path(recursive bool, paths ...string) *Builder {
|
||||
for _, p := range paths {
|
||||
_, err := os.Stat(p)
|
||||
if os.IsNotExist(err) {
|
||||
b.errs = append(b.errs, fmt.Errorf("the path %q does not exist", p))
|
||||
b.errs = append(b.errs, fmt.Errorf(pathNotExistError, p))
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
@@ -534,6 +563,13 @@ func (b *Builder) TransformRequests(opts ...RequestTransform) *Builder {
|
||||
return b
|
||||
}
|
||||
|
||||
// Subresource instructs the builder to retrieve the object at the
|
||||
// subresource path instead of the main resource path.
|
||||
func (b *Builder) Subresource(subresource string) *Builder {
|
||||
b.subresource = subresource
|
||||
return b
|
||||
}
|
||||
|
||||
// SelectEverythingParam
|
||||
func (b *Builder) SelectAllParam(selectAll bool) *Builder {
|
||||
if selectAll && (b.labelSelector != nil || b.fieldSelector != nil) {
|
||||
@@ -865,6 +901,10 @@ func (b *Builder) visitBySelector() *Result {
|
||||
if len(b.resources) == 0 {
|
||||
return result.withError(fmt.Errorf("at least one resource must be specified to use a selector"))
|
||||
}
|
||||
if len(b.subresource) != 0 {
|
||||
return result.withError(fmt.Errorf("subresource cannot be used when bulk resources are specified"))
|
||||
}
|
||||
|
||||
mappings, err := b.resourceMappings()
|
||||
if err != nil {
|
||||
result.err = err
|
||||
@@ -911,9 +951,9 @@ func (b *Builder) getClient(gv schema.GroupVersion) (RESTClient, error) {
|
||||
case b.fakeClientFn != nil:
|
||||
client, err = b.fakeClientFn(gv)
|
||||
case b.negotiatedSerializer != nil:
|
||||
client, err = b.clientConfigFn.clientForGroupVersion(gv, b.negotiatedSerializer)
|
||||
client, err = b.clientConfigFn.withStdinUnavailable(b.stdinInUse).clientForGroupVersion(gv, b.negotiatedSerializer)
|
||||
default:
|
||||
client, err = b.clientConfigFn.unstructuredClientForGroupVersion(gv)
|
||||
client, err = b.clientConfigFn.withStdinUnavailable(b.stdinInUse).unstructuredClientForGroupVersion(gv)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -986,10 +1026,11 @@ func (b *Builder) visitByResource() *Result {
|
||||
}
|
||||
|
||||
info := &Info{
|
||||
Client: client,
|
||||
Mapping: mapping,
|
||||
Namespace: selectorNamespace,
|
||||
Name: tuple.Name,
|
||||
Client: client,
|
||||
Mapping: mapping,
|
||||
Namespace: selectorNamespace,
|
||||
Name: tuple.Name,
|
||||
Subresource: b.subresource,
|
||||
}
|
||||
items = append(items, info)
|
||||
}
|
||||
@@ -1050,10 +1091,11 @@ func (b *Builder) visitByName() *Result {
|
||||
visitors := []Visitor{}
|
||||
for _, name := range b.names {
|
||||
info := &Info{
|
||||
Client: client,
|
||||
Mapping: mapping,
|
||||
Namespace: selectorNamespace,
|
||||
Name: name,
|
||||
Client: client,
|
||||
Mapping: mapping,
|
||||
Namespace: selectorNamespace,
|
||||
Name: name,
|
||||
Subresource: b.subresource,
|
||||
}
|
||||
visitors = append(visitors, info)
|
||||
}
|
||||
@@ -1134,10 +1176,9 @@ func (b *Builder) Do() *Result {
|
||||
helpers = append(helpers, RetrieveLazy)
|
||||
}
|
||||
if b.continueOnError {
|
||||
r.visitor = NewDecoratedVisitor(ContinueOnErrorVisitor{r.visitor}, helpers...)
|
||||
} else {
|
||||
r.visitor = NewDecoratedVisitor(r.visitor, helpers...)
|
||||
r.visitor = ContinueOnErrorVisitor{Visitor: r.visitor}
|
||||
}
|
||||
r.visitor = NewDecoratedVisitor(r.visitor, helpers...)
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -1166,26 +1207,21 @@ func HasNames(args []string) (bool, error) {
|
||||
return hasCombinedTypes || len(args) > 1, nil
|
||||
}
|
||||
|
||||
type cachingRESTMapperFunc struct {
|
||||
delegate RESTMapperFunc
|
||||
|
||||
lock sync.Mutex
|
||||
cached meta.RESTMapper
|
||||
}
|
||||
|
||||
func (c *cachingRESTMapperFunc) ToRESTMapper() (meta.RESTMapper, error) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
if c.cached != nil {
|
||||
return c.cached, nil
|
||||
// expandIfFilePattern returns all the filenames that match the input pattern
|
||||
// or the filename if it is a specific filename and not a pattern.
|
||||
// If the input is a pattern and it yields no result it will result in an error.
|
||||
func expandIfFilePattern(pattern string) ([]string, error) {
|
||||
if _, err := os.Stat(pattern); os.IsNotExist(err) {
|
||||
matches, err := filepath.Glob(pattern)
|
||||
if err == nil && len(matches) == 0 {
|
||||
return nil, fmt.Errorf(pathNotExistError, pattern)
|
||||
}
|
||||
if err == filepath.ErrBadPattern {
|
||||
return nil, fmt.Errorf("pattern %q is not valid: %v", pattern, err)
|
||||
}
|
||||
return matches, err
|
||||
}
|
||||
|
||||
ret, err := c.delegate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.cached = ret
|
||||
return c.cached, nil
|
||||
return []string{pattern}, nil
|
||||
}
|
||||
|
||||
type cachingCategoryExpanderFunc struct {
|
||||
|
||||
11
vendor/k8s.io/cli-runtime/pkg/resource/client.go
generated
vendored
11
vendor/k8s.io/cli-runtime/pkg/resource/client.go
generated
vendored
@@ -56,3 +56,14 @@ func (clientConfigFn ClientConfigFunc) unstructuredClientForGroupVersion(gv sche
|
||||
|
||||
return rest.RESTClientFor(cfg)
|
||||
}
|
||||
|
||||
func (clientConfigFn ClientConfigFunc) withStdinUnavailable(stdinUnavailable bool) ClientConfigFunc {
|
||||
return func() (*rest.Config, error) {
|
||||
cfg, err := clientConfigFn()
|
||||
if stdinUnavailable && cfg != nil && cfg.ExecProvider != nil {
|
||||
cfg.ExecProvider.StdinUnavailable = stdinUnavailable
|
||||
cfg.ExecProvider.StdinUnavailableMessage = "used by stdin resource manifest reader"
|
||||
}
|
||||
return cfg, err
|
||||
}
|
||||
}
|
||||
|
||||
2
vendor/k8s.io/cli-runtime/pkg/resource/crd_finder.go
generated
vendored
2
vendor/k8s.io/cli-runtime/pkg/resource/crd_finder.go
generated
vendored
@@ -35,7 +35,7 @@ func CRDFromDynamic(client dynamic.Interface) CRDGetter {
|
||||
return func() ([]schema.GroupKind, error) {
|
||||
list, err := client.Resource(schema.GroupVersionResource{
|
||||
Group: "apiextensions.k8s.io",
|
||||
Version: "v1beta1",
|
||||
Version: "v1",
|
||||
Resource: "customresourcedefinitions",
|
||||
}).List(context.TODO(), metav1.ListOptions{})
|
||||
if err != nil {
|
||||
|
||||
114
vendor/k8s.io/cli-runtime/pkg/resource/dry_run_verifier.go
generated
vendored
114
vendor/k8s.io/cli-runtime/pkg/resource/dry_run_verifier.go
generated
vendored
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
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 resource
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
openapi_v2 "github.com/googleapis/gnostic/openapiv2"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/dynamic"
|
||||
)
|
||||
|
||||
func NewDryRunVerifier(dynamicClient dynamic.Interface, openAPIGetter discovery.OpenAPISchemaInterface) *DryRunVerifier {
|
||||
return &DryRunVerifier{
|
||||
finder: NewCRDFinder(CRDFromDynamic(dynamicClient)),
|
||||
openAPIGetter: openAPIGetter,
|
||||
}
|
||||
}
|
||||
|
||||
func hasGVKExtension(extensions []*openapi_v2.NamedAny, gvk schema.GroupVersionKind) bool {
|
||||
for _, extension := range extensions {
|
||||
if extension.GetValue().GetYaml() == "" ||
|
||||
extension.GetName() != "x-kubernetes-group-version-kind" {
|
||||
continue
|
||||
}
|
||||
var value map[string]string
|
||||
err := yaml.Unmarshal([]byte(extension.GetValue().GetYaml()), &value)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if value["group"] == gvk.Group && value["kind"] == gvk.Kind && value["version"] == gvk.Version {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// DryRunVerifier verifies if a given group-version-kind supports DryRun
|
||||
// against the current server. Sending dryRun requests to apiserver that
|
||||
// don't support it will result in objects being unwillingly persisted.
|
||||
//
|
||||
// It reads the OpenAPI to see if the given GVK supports dryRun. If the
|
||||
// GVK can not be found, we assume that CRDs will have the same level of
|
||||
// support as "namespaces", and non-CRDs will not be supported. We
|
||||
// delay the check for CRDs as much as possible though, since it
|
||||
// requires an extra round-trip to the server.
|
||||
type DryRunVerifier struct {
|
||||
finder CRDFinder
|
||||
openAPIGetter discovery.OpenAPISchemaInterface
|
||||
}
|
||||
|
||||
// HasSupport verifies if the given gvk supports DryRun. An error is
|
||||
// returned if it doesn't.
|
||||
func (v *DryRunVerifier) HasSupport(gvk schema.GroupVersionKind) error {
|
||||
oapi, err := v.openAPIGetter.OpenAPISchema()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to download openapi: %v", err)
|
||||
}
|
||||
supports, err := supportsDryRun(oapi, gvk)
|
||||
if err != nil {
|
||||
// We assume that we couldn't find the type, then check for namespace:
|
||||
supports, _ = supportsDryRun(oapi, schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Namespace"})
|
||||
// If namespace supports dryRun, then we will support dryRun for CRDs only.
|
||||
if supports {
|
||||
supports, err = v.finder.HasCRD(gvk.GroupKind())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check CRD: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !supports {
|
||||
return fmt.Errorf("%v doesn't support dry-run", gvk)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// supportsDryRun is a method that let's us look in the OpenAPI if the
|
||||
// specific group-version-kind supports the dryRun query parameter for
|
||||
// the PATCH end-point.
|
||||
func supportsDryRun(doc *openapi_v2.Document, gvk schema.GroupVersionKind) (bool, error) {
|
||||
for _, path := range doc.GetPaths().GetPath() {
|
||||
// Is this describing the gvk we're looking for?
|
||||
if !hasGVKExtension(path.GetValue().GetPatch().GetVendorExtension(), gvk) {
|
||||
continue
|
||||
}
|
||||
for _, param := range path.GetValue().GetPatch().GetParameters() {
|
||||
if param.GetParameter().GetNonBodyParameter().GetQueryParameterSubSchema().GetName() == "dryRun" {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return false, errors.New("couldn't find GVK in openapi")
|
||||
}
|
||||
86
vendor/k8s.io/cli-runtime/pkg/resource/helper.go
generated
vendored
86
vendor/k8s.io/cli-runtime/pkg/resource/helper.go
generated
vendored
@@ -18,9 +18,12 @@ package resource
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
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/fields"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
@@ -34,6 +37,8 @@ var metadataAccessor = meta.NewAccessor()
|
||||
type Helper struct {
|
||||
// The name of this resource as the server would recognize it
|
||||
Resource string
|
||||
// The name of the subresource as the server would recognize it
|
||||
Subresource string
|
||||
// A RESTClient capable of mutating this resource.
|
||||
RESTClient RESTClient
|
||||
// True if the resource type is scoped to namespaces
|
||||
@@ -49,6 +54,10 @@ type Helper struct {
|
||||
// FieldManager is the name associated with the actor or entity that is making
|
||||
// changes.
|
||||
FieldManager string
|
||||
|
||||
// FieldValidation is the directive used to indicate how the server should perform
|
||||
// field validation (Ignore, Warn, or Strict)
|
||||
FieldValidation string
|
||||
}
|
||||
|
||||
// NewHelper creates a Helper from a ResourceMapping
|
||||
@@ -74,11 +83,25 @@ func (m *Helper) WithFieldManager(fieldManager string) *Helper {
|
||||
return m
|
||||
}
|
||||
|
||||
// WithFieldValidation sets the field validation option to indicate
|
||||
// how the server should perform field validation (Ignore, Warn, or Strict).
|
||||
func (m *Helper) WithFieldValidation(validationDirective string) *Helper {
|
||||
m.FieldValidation = validationDirective
|
||||
return m
|
||||
}
|
||||
|
||||
// Subresource sets the helper to access (<resource>/[ns/<namespace>/]<name>/<subresource>)
|
||||
func (m *Helper) WithSubresource(subresource string) *Helper {
|
||||
m.Subresource = subresource
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *Helper) Get(namespace, name string) (runtime.Object, error) {
|
||||
req := m.RESTClient.Get().
|
||||
NamespaceIfScoped(namespace, m.NamespaceScoped).
|
||||
Resource(m.Resource).
|
||||
Name(name)
|
||||
Name(name).
|
||||
SubResource(m.Subresource)
|
||||
return req.Do(context.TODO()).Get()
|
||||
}
|
||||
|
||||
@@ -90,6 +113,54 @@ func (m *Helper) List(namespace, apiVersion string, options *metav1.ListOptions)
|
||||
return req.Do(context.TODO()).Get()
|
||||
}
|
||||
|
||||
// FollowContinue handles the continue parameter returned by the API server when using list
|
||||
// chunking. To take advantage of this, the initial ListOptions provided by the consumer
|
||||
// should include a non-zero Limit parameter.
|
||||
func FollowContinue(initialOpts *metav1.ListOptions,
|
||||
listFunc func(metav1.ListOptions) (runtime.Object, error)) error {
|
||||
opts := initialOpts
|
||||
for {
|
||||
list, err := listFunc(*opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nextContinueToken, _ := metadataAccessor.Continue(list)
|
||||
if len(nextContinueToken) == 0 {
|
||||
return nil
|
||||
}
|
||||
opts.Continue = nextContinueToken
|
||||
}
|
||||
}
|
||||
|
||||
// EnhanceListError augments errors typically returned by List operations with additional context,
|
||||
// making sure to retain the StatusError type when applicable.
|
||||
func EnhanceListError(err error, opts metav1.ListOptions, subj string) error {
|
||||
if apierrors.IsResourceExpired(err) {
|
||||
return err
|
||||
}
|
||||
if apierrors.IsBadRequest(err) || apierrors.IsNotFound(err) {
|
||||
if se, ok := err.(*apierrors.StatusError); ok {
|
||||
// modify the message without hiding this is an API error
|
||||
if len(opts.LabelSelector) == 0 && len(opts.FieldSelector) == 0 {
|
||||
se.ErrStatus.Message = fmt.Sprintf("Unable to list %q: %v", subj,
|
||||
se.ErrStatus.Message)
|
||||
} else {
|
||||
se.ErrStatus.Message = fmt.Sprintf(
|
||||
"Unable to find %q that match label selector %q, field selector %q: %v", subj,
|
||||
opts.LabelSelector,
|
||||
opts.FieldSelector, se.ErrStatus.Message)
|
||||
}
|
||||
return se
|
||||
}
|
||||
if len(opts.LabelSelector) == 0 && len(opts.FieldSelector) == 0 {
|
||||
return fmt.Errorf("Unable to list %q: %v", subj, err)
|
||||
}
|
||||
return fmt.Errorf("Unable to find %q that match label selector %q, field selector %q: %v",
|
||||
subj, opts.LabelSelector, opts.FieldSelector, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *Helper) Watch(namespace, apiVersion string, options *metav1.ListOptions) (watch.Interface, error) {
|
||||
options.Watch = true
|
||||
return m.RESTClient.Get().
|
||||
@@ -146,6 +217,9 @@ func (m *Helper) CreateWithOptions(namespace string, modify bool, obj runtime.Ob
|
||||
if m.FieldManager != "" {
|
||||
options.FieldManager = m.FieldManager
|
||||
}
|
||||
if m.FieldValidation != "" {
|
||||
options.FieldValidation = m.FieldValidation
|
||||
}
|
||||
if modify {
|
||||
// Attempt to version the object based on client logic.
|
||||
version, err := metadataAccessor.ResourceVersion(obj)
|
||||
@@ -182,10 +256,14 @@ func (m *Helper) Patch(namespace, name string, pt types.PatchType, data []byte,
|
||||
if m.FieldManager != "" {
|
||||
options.FieldManager = m.FieldManager
|
||||
}
|
||||
if m.FieldValidation != "" {
|
||||
options.FieldValidation = m.FieldValidation
|
||||
}
|
||||
return m.RESTClient.Patch(pt).
|
||||
NamespaceIfScoped(namespace, m.NamespaceScoped).
|
||||
Resource(m.Resource).
|
||||
Name(name).
|
||||
SubResource(m.Subresource).
|
||||
VersionedParams(options, metav1.ParameterCodec).
|
||||
Body(data).
|
||||
Do(context.TODO()).
|
||||
@@ -201,6 +279,9 @@ func (m *Helper) Replace(namespace, name string, overwrite bool, obj runtime.Obj
|
||||
if m.FieldManager != "" {
|
||||
options.FieldManager = m.FieldManager
|
||||
}
|
||||
if m.FieldValidation != "" {
|
||||
options.FieldValidation = m.FieldValidation
|
||||
}
|
||||
|
||||
// Attempt to version the object based on client logic.
|
||||
version, err := metadataAccessor.ResourceVersion(obj)
|
||||
@@ -210,7 +291,7 @@ func (m *Helper) Replace(namespace, name string, overwrite bool, obj runtime.Obj
|
||||
}
|
||||
if version == "" && overwrite {
|
||||
// Retrieve the current version of the object to overwrite the server object
|
||||
serverObj, err := c.Get().NamespaceIfScoped(namespace, m.NamespaceScoped).Resource(m.Resource).Name(name).Do(context.TODO()).Get()
|
||||
serverObj, err := c.Get().NamespaceIfScoped(namespace, m.NamespaceScoped).Resource(m.Resource).Name(name).SubResource(m.Subresource).Do(context.TODO()).Get()
|
||||
if err != nil {
|
||||
// The object does not exist, but we want it to be created
|
||||
return m.replaceResource(c, m.Resource, namespace, name, obj, options)
|
||||
@@ -232,6 +313,7 @@ func (m *Helper) replaceResource(c RESTClient, resource, namespace, name string,
|
||||
NamespaceIfScoped(namespace, m.NamespaceScoped).
|
||||
Resource(resource).
|
||||
Name(name).
|
||||
SubResource(m.Subresource).
|
||||
VersionedParams(options, metav1.ParameterCodec).
|
||||
Body(obj).
|
||||
Do(context.TODO()).
|
||||
|
||||
2
vendor/k8s.io/cli-runtime/pkg/resource/kustomizevisitor.go
generated
vendored
2
vendor/k8s.io/cli-runtime/pkg/resource/kustomizevisitor.go
generated
vendored
@@ -19,8 +19,8 @@ package resource
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filesys"
|
||||
"sigs.k8s.io/kustomize/api/krusty"
|
||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||
)
|
||||
|
||||
// KustomizeVisitor handles kustomization.yaml files.
|
||||
|
||||
5
vendor/k8s.io/cli-runtime/pkg/resource/mapper.go
generated
vendored
5
vendor/k8s.io/cli-runtime/pkg/resource/mapper.go
generated
vendored
@@ -20,6 +20,7 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
@@ -64,6 +65,10 @@ func (m *mapper) infoForData(data []byte, source string) (*Info, error) {
|
||||
}
|
||||
mapping, err := restMapper.RESTMapping(gvk.GroupKind(), gvk.Version)
|
||||
if err != nil {
|
||||
if _, ok := err.(*meta.NoKindMatchError); ok {
|
||||
return nil, fmt.Errorf("resource mapping not found for name: %q namespace: %q from %q: %v\nensure CRDs are installed first",
|
||||
name, namespace, source, err)
|
||||
}
|
||||
return nil, fmt.Errorf("unable to recognize %q: %v", source, err)
|
||||
}
|
||||
ret.Mapping = mapping
|
||||
|
||||
7
vendor/k8s.io/cli-runtime/pkg/resource/metadata_decoder.go
generated
vendored
7
vendor/k8s.io/cli-runtime/pkg/resource/metadata_decoder.go
generated
vendored
@@ -20,12 +20,9 @@ import (
|
||||
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/json"
|
||||
utiljson "k8s.io/apimachinery/pkg/util/json"
|
||||
)
|
||||
|
||||
// hold a single instance of the case-sensitive decoder
|
||||
var caseSensitiveJsonIterator = json.CaseSensitiveJSONIterator()
|
||||
|
||||
// metadataValidatingDecoder wraps a decoder and additionally ensures metadata schema fields decode before returning an unstructured object
|
||||
type metadataValidatingDecoder struct {
|
||||
decoder runtime.Decoder
|
||||
@@ -47,7 +44,7 @@ func (m *metadataValidatingDecoder) Decode(data []byte, defaults *schema.GroupVe
|
||||
// make sure the data can decode into ObjectMeta before we return,
|
||||
// so we don't silently truncate schema errors in metadata later with accesser get/set calls
|
||||
v := &metadataOnlyObject{}
|
||||
if typedErr := caseSensitiveJsonIterator.Unmarshal(data, v); typedErr != nil {
|
||||
if typedErr := utiljson.Unmarshal(data, v); typedErr != nil {
|
||||
return obj, gvk, typedErr
|
||||
}
|
||||
return obj, gvk, err
|
||||
|
||||
166
vendor/k8s.io/cli-runtime/pkg/resource/query_param_verifier.go
generated
vendored
Normal file
166
vendor/k8s.io/cli-runtime/pkg/resource/query_param_verifier.go
generated
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
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 resource
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
openapi_v2 "github.com/google/gnostic/openapiv2"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/dynamic"
|
||||
)
|
||||
|
||||
func NewQueryParamVerifier(dynamicClient dynamic.Interface, openAPIGetter discovery.OpenAPISchemaInterface, queryParam VerifiableQueryParam) *QueryParamVerifier {
|
||||
return &QueryParamVerifier{
|
||||
finder: NewCRDFinder(CRDFromDynamic(dynamicClient)),
|
||||
openAPIGetter: openAPIGetter,
|
||||
queryParam: queryParam,
|
||||
}
|
||||
}
|
||||
|
||||
// QueryParamVerifier verifies if a given group-version-kind supports a
|
||||
// given VerifiableQueryParam against the current server.
|
||||
//
|
||||
// Currently supported query params are:
|
||||
// 1. dryRun
|
||||
// 2. fieldValidation
|
||||
//
|
||||
// Support for each of these query params needs to be verified because:
|
||||
//
|
||||
// 1. Sending dryRun requests to apiserver that
|
||||
// don't support it will result in objects being unwillingly persisted.
|
||||
//
|
||||
// 2. We determine whether or not to perform server-side or client-side
|
||||
// schema validation based on whether the fieldValidation query param is
|
||||
// supported or not.
|
||||
//
|
||||
// It reads the OpenAPI to see if the given GVK supports the given query param.
|
||||
// If the GVK can not be found, we assume that CRDs will have the same level of
|
||||
// support as "namespaces", and non-CRDs will not be supported. We
|
||||
// delay the check for CRDs as much as possible though, since it
|
||||
// requires an extra round-trip to the server.
|
||||
type QueryParamVerifier struct {
|
||||
finder CRDFinder
|
||||
openAPIGetter discovery.OpenAPISchemaInterface
|
||||
queryParam VerifiableQueryParam
|
||||
}
|
||||
|
||||
// Verifier is the generic verifier interface used for testing QueryParamVerifier
|
||||
type Verifier interface {
|
||||
HasSupport(gvk schema.GroupVersionKind) error
|
||||
}
|
||||
|
||||
// VerifiableQueryParam is a query parameter who's enablement on the
|
||||
// apiserver can be determined by evaluating the OpenAPI for a specific
|
||||
// GVK.
|
||||
type VerifiableQueryParam string
|
||||
|
||||
const (
|
||||
QueryParamDryRun VerifiableQueryParam = "dryRun"
|
||||
QueryParamFieldValidation VerifiableQueryParam = "fieldValidation"
|
||||
)
|
||||
|
||||
// HasSupport checks if the given gvk supports the query param configured on v
|
||||
func (v *QueryParamVerifier) HasSupport(gvk schema.GroupVersionKind) error {
|
||||
oapi, err := v.openAPIGetter.OpenAPISchema()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to download openapi: %v", err)
|
||||
}
|
||||
supports, err := supportsQueryParam(oapi, gvk, v.queryParam)
|
||||
if err != nil {
|
||||
// We assume that we couldn't find the type, then check for namespace:
|
||||
supports, _ = supportsQueryParam(oapi, schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Namespace"}, v.queryParam)
|
||||
// If namespace supports the query param, then we will support the query param for CRDs only.
|
||||
if supports {
|
||||
supports, err = v.finder.HasCRD(gvk.GroupKind())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check CRD: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !supports {
|
||||
return NewParamUnsupportedError(gvk, v.queryParam)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type paramUnsupportedError struct {
|
||||
gvk schema.GroupVersionKind
|
||||
param VerifiableQueryParam
|
||||
}
|
||||
|
||||
func NewParamUnsupportedError(gvk schema.GroupVersionKind, param VerifiableQueryParam) error {
|
||||
return ¶mUnsupportedError{
|
||||
gvk: gvk,
|
||||
param: param,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *paramUnsupportedError) Error() string {
|
||||
return fmt.Sprintf("%v doesn't support %s", e.gvk, e.param)
|
||||
}
|
||||
|
||||
func IsParamUnsupportedError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := err.(*paramUnsupportedError)
|
||||
return ok
|
||||
}
|
||||
|
||||
func hasGVKExtension(extensions []*openapi_v2.NamedAny, gvk schema.GroupVersionKind) bool {
|
||||
for _, extension := range extensions {
|
||||
if extension.GetValue().GetYaml() == "" ||
|
||||
extension.GetName() != "x-kubernetes-group-version-kind" {
|
||||
continue
|
||||
}
|
||||
var value map[string]string
|
||||
err := yaml.Unmarshal([]byte(extension.GetValue().GetYaml()), &value)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if value["group"] == gvk.Group && value["kind"] == gvk.Kind && value["version"] == gvk.Version {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// supportsQueryParam is a method that let's us look in the OpenAPI if the
|
||||
// specific group-version-kind supports the specific query parameter for
|
||||
// the PATCH end-point.
|
||||
func supportsQueryParam(doc *openapi_v2.Document, gvk schema.GroupVersionKind, queryParam VerifiableQueryParam) (bool, error) {
|
||||
for _, path := range doc.GetPaths().GetPath() {
|
||||
// Is this describing the gvk we're looking for?
|
||||
if !hasGVKExtension(path.GetValue().GetPatch().GetVendorExtension(), gvk) {
|
||||
continue
|
||||
}
|
||||
for _, param := range path.GetValue().GetPatch().GetParameters() {
|
||||
if param.GetParameter().GetNonBodyParameter().GetQueryParameterSubSchema().GetName() == string(queryParam) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return false, errors.New("couldn't find GVK in openapi")
|
||||
}
|
||||
53
vendor/k8s.io/cli-runtime/pkg/resource/selector.go
generated
vendored
53
vendor/k8s.io/cli-runtime/pkg/resource/selector.go
generated
vendored
@@ -17,11 +17,9 @@ limitations under the License.
|
||||
package resource
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
)
|
||||
|
||||
@@ -49,41 +47,23 @@ func NewSelector(client RESTClient, mapping *meta.RESTMapping, namespace, labelS
|
||||
|
||||
// Visit implements Visitor and uses request chunking by default.
|
||||
func (r *Selector) Visit(fn VisitorFunc) error {
|
||||
var continueToken string
|
||||
for {
|
||||
list, err := NewHelper(r.Client, r.Mapping).List(
|
||||
helper := NewHelper(r.Client, r.Mapping)
|
||||
initialOpts := metav1.ListOptions{
|
||||
LabelSelector: r.LabelSelector,
|
||||
FieldSelector: r.FieldSelector,
|
||||
Limit: r.LimitChunks,
|
||||
}
|
||||
return FollowContinue(&initialOpts, func(options metav1.ListOptions) (runtime.Object, error) {
|
||||
list, err := helper.List(
|
||||
r.Namespace,
|
||||
r.ResourceMapping().GroupVersionKind.GroupVersion().String(),
|
||||
&metav1.ListOptions{
|
||||
LabelSelector: r.LabelSelector,
|
||||
FieldSelector: r.FieldSelector,
|
||||
Limit: r.LimitChunks,
|
||||
Continue: continueToken,
|
||||
},
|
||||
&options,
|
||||
)
|
||||
if err != nil {
|
||||
if errors.IsResourceExpired(err) {
|
||||
return err
|
||||
}
|
||||
if errors.IsBadRequest(err) || errors.IsNotFound(err) {
|
||||
if se, ok := err.(*errors.StatusError); ok {
|
||||
// modify the message without hiding this is an API error
|
||||
if len(r.LabelSelector) == 0 && len(r.FieldSelector) == 0 {
|
||||
se.ErrStatus.Message = fmt.Sprintf("Unable to list %q: %v", r.Mapping.Resource, se.ErrStatus.Message)
|
||||
} else {
|
||||
se.ErrStatus.Message = fmt.Sprintf("Unable to find %q that match label selector %q, field selector %q: %v", r.Mapping.Resource, r.LabelSelector, r.FieldSelector, se.ErrStatus.Message)
|
||||
}
|
||||
return se
|
||||
}
|
||||
if len(r.LabelSelector) == 0 && len(r.FieldSelector) == 0 {
|
||||
return fmt.Errorf("Unable to list %q: %v", r.Mapping.Resource, err)
|
||||
}
|
||||
return fmt.Errorf("Unable to find %q that match label selector %q, field selector %q: %v", r.Mapping.Resource, r.LabelSelector, r.FieldSelector, err)
|
||||
}
|
||||
return err
|
||||
return nil, EnhanceListError(err, options, r.Mapping.Resource.String())
|
||||
}
|
||||
resourceVersion, _ := metadataAccessor.ResourceVersion(list)
|
||||
nextContinueToken, _ := metadataAccessor.Continue(list)
|
||||
|
||||
info := &Info{
|
||||
Client: r.Client,
|
||||
Mapping: r.Mapping,
|
||||
@@ -95,13 +75,10 @@ func (r *Selector) Visit(fn VisitorFunc) error {
|
||||
}
|
||||
|
||||
if err := fn(info, nil); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
if len(nextContinueToken) == 0 {
|
||||
return nil
|
||||
}
|
||||
continueToken = nextContinueToken
|
||||
}
|
||||
return list, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Selector) Watch(resourceVersion string) (watch.Interface, error) {
|
||||
|
||||
50
vendor/k8s.io/cli-runtime/pkg/resource/visitor.go
generated
vendored
50
vendor/k8s.io/cli-runtime/pkg/resource/visitor.go
generated
vendored
@@ -78,12 +78,16 @@ type Info struct {
|
||||
// defined. If retrieved from the server, the Builder expects the mapping client to
|
||||
// decide the final form. Use the AsVersioned, AsUnstructured, and AsInternal helpers
|
||||
// to alter the object versions.
|
||||
// If Subresource is specified, this will be the object for the subresource.
|
||||
Object runtime.Object
|
||||
// Optional, this is the most recent resource version the server knows about for
|
||||
// this type of resource. It may not match the resource version of the object,
|
||||
// but if set it should be equal to or newer than the resource version of the
|
||||
// object (however the server defines resource version).
|
||||
ResourceVersion string
|
||||
// Optional, if specified, the object is the most recent value of the subresource
|
||||
// returned by the server if available.
|
||||
Subresource string
|
||||
}
|
||||
|
||||
// Visit implements Visitor
|
||||
@@ -93,7 +97,7 @@ func (i *Info) Visit(fn VisitorFunc) error {
|
||||
|
||||
// Get retrieves the object from the Namespace and Name fields
|
||||
func (i *Info) Get() (err error) {
|
||||
obj, err := NewHelper(i.Client, i.Mapping).Get(i.Namespace, i.Name)
|
||||
obj, err := NewHelper(i.Client, i.Mapping).WithSubresource(i.Subresource).Get(i.Namespace, i.Name)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) && len(i.Namespace) > 0 && i.Namespace != metav1.NamespaceDefault && i.Namespace != metav1.NamespaceAll {
|
||||
err2 := i.Client.Get().AbsPath("api", "v1", "namespaces", i.Namespace).Do(context.TODO()).Error()
|
||||
@@ -204,9 +208,9 @@ type EagerVisitorList []Visitor
|
||||
// Visit implements Visitor, and gathers errors that occur during processing until
|
||||
// all sub visitors have been visited.
|
||||
func (l EagerVisitorList) Visit(fn VisitorFunc) error {
|
||||
errs := []error(nil)
|
||||
var errs []error
|
||||
for i := range l {
|
||||
if err := l[i].Visit(func(info *Info, err error) error {
|
||||
err := l[i].Visit(func(info *Info, err error) error {
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
return nil
|
||||
@@ -215,7 +219,8 @@ func (l EagerVisitorList) Visit(fn VisitorFunc) error {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
})
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
@@ -253,13 +258,15 @@ func (v *URLVisitor) Visit(fn VisitorFunc) error {
|
||||
// readHttpWithRetries tries to http.Get the v.URL retries times before giving up.
|
||||
func readHttpWithRetries(get httpget, duration time.Duration, u string, attempts int) (io.ReadCloser, error) {
|
||||
var err error
|
||||
var body io.ReadCloser
|
||||
if attempts <= 0 {
|
||||
return nil, fmt.Errorf("http attempts must be greater than 0, was %d", attempts)
|
||||
}
|
||||
for i := 0; i < attempts; i++ {
|
||||
var statusCode int
|
||||
var status string
|
||||
var (
|
||||
statusCode int
|
||||
status string
|
||||
body io.ReadCloser
|
||||
)
|
||||
if i > 0 {
|
||||
time.Sleep(duration)
|
||||
}
|
||||
@@ -272,10 +279,12 @@ func readHttpWithRetries(get httpget, duration time.Duration, u string, attempts
|
||||
continue
|
||||
}
|
||||
|
||||
// Error - Set the error condition from the StatusCode
|
||||
if statusCode != http.StatusOK {
|
||||
err = fmt.Errorf("unable to read URL %q, server reported %s, status code=%d", u, status, statusCode)
|
||||
if statusCode == http.StatusOK {
|
||||
return body, nil
|
||||
}
|
||||
body.Close()
|
||||
// Error - Set the error condition from the StatusCode
|
||||
err = fmt.Errorf("unable to read URL %q, server reported %s, status code=%d", u, status, statusCode)
|
||||
|
||||
if statusCode >= 500 && statusCode < 600 {
|
||||
// Retry 500's
|
||||
@@ -285,7 +294,7 @@ func readHttpWithRetries(get httpget, duration time.Duration, u string, attempts
|
||||
break
|
||||
}
|
||||
}
|
||||
return body, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// httpget Defines function to retrieve a url and return the results. Exists for unit test stubbing.
|
||||
@@ -346,7 +355,7 @@ type ContinueOnErrorVisitor struct {
|
||||
// returned by the visitor directly may still result in some items
|
||||
// not being visited.
|
||||
func (v ContinueOnErrorVisitor) Visit(fn VisitorFunc) error {
|
||||
errs := []error{}
|
||||
var errs []error
|
||||
err := v.Visitor.Visit(func(info *Info, err error) error {
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
@@ -420,7 +429,7 @@ func (v FlattenListVisitor) Visit(fn VisitorFunc) error {
|
||||
if info.Mapping != nil && !info.Mapping.GroupVersionKind.Empty() {
|
||||
preferredGVKs = append(preferredGVKs, info.Mapping.GroupVersionKind)
|
||||
}
|
||||
errs := []error{}
|
||||
var errs []error
|
||||
for i := range items {
|
||||
item, err := v.mapper.infoForObject(items[i], v.typer, preferredGVKs)
|
||||
if err != nil {
|
||||
@@ -430,12 +439,15 @@ func (v FlattenListVisitor) Visit(fn VisitorFunc) error {
|
||||
if len(info.ResourceVersion) != 0 {
|
||||
item.ResourceVersion = info.ResourceVersion
|
||||
}
|
||||
// propagate list source to items source
|
||||
if len(info.Source) != 0 {
|
||||
item.Source = info.Source
|
||||
}
|
||||
if err := fn(item, nil); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
return utilerrors.NewAggregate(errs)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@@ -671,16 +683,6 @@ func RetrieveLazy(info *Info, err error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateAndRefresh creates an object from input info and refreshes info with that object
|
||||
func CreateAndRefresh(info *Info) error {
|
||||
obj, err := NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info.Refresh(obj, true)
|
||||
return nil
|
||||
}
|
||||
|
||||
type FilterFunc func(info *Info, err error) (bool, error)
|
||||
|
||||
type FilteredVisitor struct {
|
||||
|
||||
Reference in New Issue
Block a user