summaryrefslogtreecommitdiff
path: root/vendor
diff options
context:
space:
mode:
authorKubeEdge Bot <48982446+kubeedge-bot@users.noreply.github.com>2023-01-16 20:17:36 +0800
committerGitHub <noreply@github.com>2023-01-16 20:17:36 +0800
commit5485fcb16f3e08812031c4593f3e1c2c5a1f5232 (patch)
tree61af346e54a51d5f047cab0dff381ba6dcabaf69 /vendor
parentMerge pull request #4398 from wackxu/revertginkgo (diff)
parentadd integration test to ci workflow (diff)
downloadkubeedge-5485fcb16f3e08812031c4593f3e1c2c5a1f5232.tar.gz
Merge pull request #4256 from Congrool/cm-ut
add integration test for edgeapplication and nodegroup controller
Diffstat (limited to 'vendor')
-rw-r--r--vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/clientset.go111
-rw-r--r--vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/doc.go20
-rw-r--r--vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme/doc.go20
-rw-r--r--vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme/register.go58
-rw-r--r--vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1/apiextensions_client.go89
-rw-r--r--vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1/customresourcedefinition.go184
-rw-r--r--vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1/doc.go20
-rw-r--r--vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1/generated_expansion.go21
-rw-r--r--vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1/apiextensions_client.go89
-rw-r--r--vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1/customresourcedefinition.go184
-rw-r--r--vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1/doc.go20
-rw-r--r--vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1/generated_expansion.go21
-rw-r--r--vendor/k8s.io/client-go/util/retry/OWNERS4
-rw-r--r--vendor/k8s.io/client-go/util/retry/util.go105
-rw-r--r--vendor/modules.txt11
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/envtest/crd.go451
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/envtest/doc.go26
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/envtest/helper.go69
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/envtest/server.go375
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/envtest/webhook.go428
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/internal/flock/doc.go21
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/internal/flock/errors.go24
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/internal/flock/flock_other.go24
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/internal/flock/flock_unix.go47
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/addr/manager.go126
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/certs/tinyca.go223
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane/apiserver.go469
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane/auth.go142
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane/etcd.go202
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane/kubectl.go119
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane/plane.go248
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/process/arguments.go340
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/process/bin_path_finder.go70
-rw-r--r--vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/process/process.go277
34 files changed, 4638 insertions, 0 deletions
diff --git a/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/clientset.go b/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/clientset.go
new file mode 100644
index 000000000..74aca8f56
--- /dev/null
+++ b/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/clientset.go
@@ -0,0 +1,111 @@
+/*
+Copyright 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.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package clientset
+
+import (
+ "fmt"
+
+ apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1"
+ apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1"
+ discovery "k8s.io/client-go/discovery"
+ rest "k8s.io/client-go/rest"
+ flowcontrol "k8s.io/client-go/util/flowcontrol"
+)
+
+type Interface interface {
+ Discovery() discovery.DiscoveryInterface
+ ApiextensionsV1beta1() apiextensionsv1beta1.ApiextensionsV1beta1Interface
+ ApiextensionsV1() apiextensionsv1.ApiextensionsV1Interface
+}
+
+// Clientset contains the clients for groups. Each group has exactly one
+// version included in a Clientset.
+type Clientset struct {
+ *discovery.DiscoveryClient
+ apiextensionsV1beta1 *apiextensionsv1beta1.ApiextensionsV1beta1Client
+ apiextensionsV1 *apiextensionsv1.ApiextensionsV1Client
+}
+
+// ApiextensionsV1beta1 retrieves the ApiextensionsV1beta1Client
+func (c *Clientset) ApiextensionsV1beta1() apiextensionsv1beta1.ApiextensionsV1beta1Interface {
+ return c.apiextensionsV1beta1
+}
+
+// ApiextensionsV1 retrieves the ApiextensionsV1Client
+func (c *Clientset) ApiextensionsV1() apiextensionsv1.ApiextensionsV1Interface {
+ return c.apiextensionsV1
+}
+
+// Discovery retrieves the DiscoveryClient
+func (c *Clientset) Discovery() discovery.DiscoveryInterface {
+ if c == nil {
+ return nil
+ }
+ return c.DiscoveryClient
+}
+
+// NewForConfig creates a new Clientset for the given config.
+// If config's RateLimiter is not set and QPS and Burst are acceptable,
+// NewForConfig will generate a rate-limiter in configShallowCopy.
+func NewForConfig(c *rest.Config) (*Clientset, error) {
+ configShallowCopy := *c
+ if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 {
+ if configShallowCopy.Burst <= 0 {
+ return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0")
+ }
+ configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst)
+ }
+ var cs Clientset
+ var err error
+ cs.apiextensionsV1beta1, err = apiextensionsv1beta1.NewForConfig(&configShallowCopy)
+ if err != nil {
+ return nil, err
+ }
+ cs.apiextensionsV1, err = apiextensionsv1.NewForConfig(&configShallowCopy)
+ if err != nil {
+ return nil, err
+ }
+
+ cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy)
+ if err != nil {
+ return nil, err
+ }
+ return &cs, nil
+}
+
+// NewForConfigOrDie creates a new Clientset for the given config and
+// panics if there is an error in the config.
+func NewForConfigOrDie(c *rest.Config) *Clientset {
+ var cs Clientset
+ cs.apiextensionsV1beta1 = apiextensionsv1beta1.NewForConfigOrDie(c)
+ cs.apiextensionsV1 = apiextensionsv1.NewForConfigOrDie(c)
+
+ cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c)
+ return &cs
+}
+
+// New creates a new Clientset for the given RESTClient.
+func New(c rest.Interface) *Clientset {
+ var cs Clientset
+ cs.apiextensionsV1beta1 = apiextensionsv1beta1.New(c)
+ cs.apiextensionsV1 = apiextensionsv1.New(c)
+
+ cs.DiscoveryClient = discovery.NewDiscoveryClient(c)
+ return &cs
+}
diff --git a/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/doc.go b/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/doc.go
new file mode 100644
index 000000000..ee865e56d
--- /dev/null
+++ b/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/doc.go
@@ -0,0 +1,20 @@
+/*
+Copyright 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.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+// This package has the automatically generated clientset.
+package clientset
diff --git a/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme/doc.go b/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme/doc.go
new file mode 100644
index 000000000..7dc375616
--- /dev/null
+++ b/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme/doc.go
@@ -0,0 +1,20 @@
+/*
+Copyright 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.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+// This package contains the scheme of the automatically generated clientset.
+package scheme
diff --git a/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme/register.go b/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme/register.go
new file mode 100644
index 000000000..144c20666
--- /dev/null
+++ b/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme/register.go
@@ -0,0 +1,58 @@
+/*
+Copyright 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.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package scheme
+
+import (
+ apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+ apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ runtime "k8s.io/apimachinery/pkg/runtime"
+ schema "k8s.io/apimachinery/pkg/runtime/schema"
+ serializer "k8s.io/apimachinery/pkg/runtime/serializer"
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+)
+
+var Scheme = runtime.NewScheme()
+var Codecs = serializer.NewCodecFactory(Scheme)
+var ParameterCodec = runtime.NewParameterCodec(Scheme)
+var localSchemeBuilder = runtime.SchemeBuilder{
+ apiextensionsv1beta1.AddToScheme,
+ apiextensionsv1.AddToScheme,
+}
+
+// AddToScheme adds all types of this clientset into the given scheme. This allows composition
+// of clientsets, like in:
+//
+// import (
+// "k8s.io/client-go/kubernetes"
+// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
+// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
+// )
+//
+// kclientset, _ := kubernetes.NewForConfig(c)
+// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
+//
+// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
+// correctly.
+var AddToScheme = localSchemeBuilder.AddToScheme
+
+func init() {
+ v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})
+ utilruntime.Must(AddToScheme(Scheme))
+}
diff --git a/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1/apiextensions_client.go b/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1/apiextensions_client.go
new file mode 100644
index 000000000..8823cb6a9
--- /dev/null
+++ b/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1/apiextensions_client.go
@@ -0,0 +1,89 @@
+/*
+Copyright 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.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1
+
+import (
+ v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+ "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme"
+ rest "k8s.io/client-go/rest"
+)
+
+type ApiextensionsV1Interface interface {
+ RESTClient() rest.Interface
+ CustomResourceDefinitionsGetter
+}
+
+// ApiextensionsV1Client is used to interact with features provided by the apiextensions.k8s.io group.
+type ApiextensionsV1Client struct {
+ restClient rest.Interface
+}
+
+func (c *ApiextensionsV1Client) CustomResourceDefinitions() CustomResourceDefinitionInterface {
+ return newCustomResourceDefinitions(c)
+}
+
+// NewForConfig creates a new ApiextensionsV1Client for the given config.
+func NewForConfig(c *rest.Config) (*ApiextensionsV1Client, error) {
+ config := *c
+ if err := setConfigDefaults(&config); err != nil {
+ return nil, err
+ }
+ client, err := rest.RESTClientFor(&config)
+ if err != nil {
+ return nil, err
+ }
+ return &ApiextensionsV1Client{client}, nil
+}
+
+// NewForConfigOrDie creates a new ApiextensionsV1Client for the given config and
+// panics if there is an error in the config.
+func NewForConfigOrDie(c *rest.Config) *ApiextensionsV1Client {
+ client, err := NewForConfig(c)
+ if err != nil {
+ panic(err)
+ }
+ return client
+}
+
+// New creates a new ApiextensionsV1Client for the given RESTClient.
+func New(c rest.Interface) *ApiextensionsV1Client {
+ return &ApiextensionsV1Client{c}
+}
+
+func setConfigDefaults(config *rest.Config) error {
+ gv := v1.SchemeGroupVersion
+ config.GroupVersion = &gv
+ config.APIPath = "/apis"
+ config.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
+
+ if config.UserAgent == "" {
+ config.UserAgent = rest.DefaultKubernetesUserAgent()
+ }
+
+ return nil
+}
+
+// RESTClient returns a RESTClient that is used to communicate
+// with API server by this client implementation.
+func (c *ApiextensionsV1Client) RESTClient() rest.Interface {
+ if c == nil {
+ return nil
+ }
+ return c.restClient
+}
diff --git a/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1/customresourcedefinition.go b/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1/customresourcedefinition.go
new file mode 100644
index 000000000..5569b12d9
--- /dev/null
+++ b/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1/customresourcedefinition.go
@@ -0,0 +1,184 @@
+/*
+Copyright 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.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1
+
+import (
+ "context"
+ "time"
+
+ v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+ scheme "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ types "k8s.io/apimachinery/pkg/types"
+ watch "k8s.io/apimachinery/pkg/watch"
+ rest "k8s.io/client-go/rest"
+)
+
+// CustomResourceDefinitionsGetter has a method to return a CustomResourceDefinitionInterface.
+// A group's client should implement this interface.
+type CustomResourceDefinitionsGetter interface {
+ CustomResourceDefinitions() CustomResourceDefinitionInterface
+}
+
+// CustomResourceDefinitionInterface has methods to work with CustomResourceDefinition resources.
+type CustomResourceDefinitionInterface interface {
+ Create(ctx context.Context, customResourceDefinition *v1.CustomResourceDefinition, opts metav1.CreateOptions) (*v1.CustomResourceDefinition, error)
+ Update(ctx context.Context, customResourceDefinition *v1.CustomResourceDefinition, opts metav1.UpdateOptions) (*v1.CustomResourceDefinition, error)
+ UpdateStatus(ctx context.Context, customResourceDefinition *v1.CustomResourceDefinition, opts metav1.UpdateOptions) (*v1.CustomResourceDefinition, error)
+ Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error
+ DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error
+ Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.CustomResourceDefinition, error)
+ List(ctx context.Context, opts metav1.ListOptions) (*v1.CustomResourceDefinitionList, error)
+ Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error)
+ Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.CustomResourceDefinition, err error)
+ CustomResourceDefinitionExpansion
+}
+
+// customResourceDefinitions implements CustomResourceDefinitionInterface
+type customResourceDefinitions struct {
+ client rest.Interface
+}
+
+// newCustomResourceDefinitions returns a CustomResourceDefinitions
+func newCustomResourceDefinitions(c *ApiextensionsV1Client) *customResourceDefinitions {
+ return &customResourceDefinitions{
+ client: c.RESTClient(),
+ }
+}
+
+// Get takes name of the customResourceDefinition, and returns the corresponding customResourceDefinition object, and an error if there is any.
+func (c *customResourceDefinitions) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.CustomResourceDefinition, err error) {
+ result = &v1.CustomResourceDefinition{}
+ err = c.client.Get().
+ Resource("customresourcedefinitions").
+ Name(name).
+ VersionedParams(&options, scheme.ParameterCodec).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// List takes label and field selectors, and returns the list of CustomResourceDefinitions that match those selectors.
+func (c *customResourceDefinitions) List(ctx context.Context, opts metav1.ListOptions) (result *v1.CustomResourceDefinitionList, err error) {
+ var timeout time.Duration
+ if opts.TimeoutSeconds != nil {
+ timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+ }
+ result = &v1.CustomResourceDefinitionList{}
+ err = c.client.Get().
+ Resource("customresourcedefinitions").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Watch returns a watch.Interface that watches the requested customResourceDefinitions.
+func (c *customResourceDefinitions) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
+ var timeout time.Duration
+ if opts.TimeoutSeconds != nil {
+ timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+ }
+ opts.Watch = true
+ return c.client.Get().
+ Resource("customresourcedefinitions").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Watch(ctx)
+}
+
+// Create takes the representation of a customResourceDefinition and creates it. Returns the server's representation of the customResourceDefinition, and an error, if there is any.
+func (c *customResourceDefinitions) Create(ctx context.Context, customResourceDefinition *v1.CustomResourceDefinition, opts metav1.CreateOptions) (result *v1.CustomResourceDefinition, err error) {
+ result = &v1.CustomResourceDefinition{}
+ err = c.client.Post().
+ Resource("customresourcedefinitions").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(customResourceDefinition).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Update takes the representation of a customResourceDefinition and updates it. Returns the server's representation of the customResourceDefinition, and an error, if there is any.
+func (c *customResourceDefinitions) Update(ctx context.Context, customResourceDefinition *v1.CustomResourceDefinition, opts metav1.UpdateOptions) (result *v1.CustomResourceDefinition, err error) {
+ result = &v1.CustomResourceDefinition{}
+ err = c.client.Put().
+ Resource("customresourcedefinitions").
+ Name(customResourceDefinition.Name).
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(customResourceDefinition).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// UpdateStatus was generated because the type contains a Status member.
+// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
+func (c *customResourceDefinitions) UpdateStatus(ctx context.Context, customResourceDefinition *v1.CustomResourceDefinition, opts metav1.UpdateOptions) (result *v1.CustomResourceDefinition, err error) {
+ result = &v1.CustomResourceDefinition{}
+ err = c.client.Put().
+ Resource("customresourcedefinitions").
+ Name(customResourceDefinition.Name).
+ SubResource("status").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(customResourceDefinition).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Delete takes name of the customResourceDefinition and deletes it. Returns an error if one occurs.
+func (c *customResourceDefinitions) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error {
+ return c.client.Delete().
+ Resource("customresourcedefinitions").
+ Name(name).
+ Body(&opts).
+ Do(ctx).
+ Error()
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *customResourceDefinitions) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error {
+ var timeout time.Duration
+ if listOpts.TimeoutSeconds != nil {
+ timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
+ }
+ return c.client.Delete().
+ Resource("customresourcedefinitions").
+ VersionedParams(&listOpts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Body(&opts).
+ Do(ctx).
+ Error()
+}
+
+// Patch applies the patch and returns the patched customResourceDefinition.
+func (c *customResourceDefinitions) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.CustomResourceDefinition, err error) {
+ result = &v1.CustomResourceDefinition{}
+ err = c.client.Patch(pt).
+ Resource("customresourcedefinitions").
+ Name(name).
+ SubResource(subresources...).
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(data).
+ Do(ctx).
+ Into(result)
+ return
+}
diff --git a/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1/doc.go b/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1/doc.go
new file mode 100644
index 000000000..3af5d054f
--- /dev/null
+++ b/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1/doc.go
@@ -0,0 +1,20 @@
+/*
+Copyright 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.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+// This package has the automatically generated typed clients.
+package v1
diff --git a/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1/generated_expansion.go b/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1/generated_expansion.go
new file mode 100644
index 000000000..e594636af
--- /dev/null
+++ b/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1/generated_expansion.go
@@ -0,0 +1,21 @@
+/*
+Copyright 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.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1
+
+type CustomResourceDefinitionExpansion interface{}
diff --git a/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1/apiextensions_client.go b/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1/apiextensions_client.go
new file mode 100644
index 000000000..ff1ec4f25
--- /dev/null
+++ b/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1/apiextensions_client.go
@@ -0,0 +1,89 @@
+/*
+Copyright 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.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1beta1
+
+import (
+ v1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
+ "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme"
+ rest "k8s.io/client-go/rest"
+)
+
+type ApiextensionsV1beta1Interface interface {
+ RESTClient() rest.Interface
+ CustomResourceDefinitionsGetter
+}
+
+// ApiextensionsV1beta1Client is used to interact with features provided by the apiextensions.k8s.io group.
+type ApiextensionsV1beta1Client struct {
+ restClient rest.Interface
+}
+
+func (c *ApiextensionsV1beta1Client) CustomResourceDefinitions() CustomResourceDefinitionInterface {
+ return newCustomResourceDefinitions(c)
+}
+
+// NewForConfig creates a new ApiextensionsV1beta1Client for the given config.
+func NewForConfig(c *rest.Config) (*ApiextensionsV1beta1Client, error) {
+ config := *c
+ if err := setConfigDefaults(&config); err != nil {
+ return nil, err
+ }
+ client, err := rest.RESTClientFor(&config)
+ if err != nil {
+ return nil, err
+ }
+ return &ApiextensionsV1beta1Client{client}, nil
+}
+
+// NewForConfigOrDie creates a new ApiextensionsV1beta1Client for the given config and
+// panics if there is an error in the config.
+func NewForConfigOrDie(c *rest.Config) *ApiextensionsV1beta1Client {
+ client, err := NewForConfig(c)
+ if err != nil {
+ panic(err)
+ }
+ return client
+}
+
+// New creates a new ApiextensionsV1beta1Client for the given RESTClient.
+func New(c rest.Interface) *ApiextensionsV1beta1Client {
+ return &ApiextensionsV1beta1Client{c}
+}
+
+func setConfigDefaults(config *rest.Config) error {
+ gv := v1beta1.SchemeGroupVersion
+ config.GroupVersion = &gv
+ config.APIPath = "/apis"
+ config.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
+
+ if config.UserAgent == "" {
+ config.UserAgent = rest.DefaultKubernetesUserAgent()
+ }
+
+ return nil
+}
+
+// RESTClient returns a RESTClient that is used to communicate
+// with API server by this client implementation.
+func (c *ApiextensionsV1beta1Client) RESTClient() rest.Interface {
+ if c == nil {
+ return nil
+ }
+ return c.restClient
+}
diff --git a/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1/customresourcedefinition.go b/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1/customresourcedefinition.go
new file mode 100644
index 000000000..2d16ca709
--- /dev/null
+++ b/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1/customresourcedefinition.go
@@ -0,0 +1,184 @@
+/*
+Copyright 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.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1beta1
+
+import (
+ "context"
+ "time"
+
+ v1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
+ scheme "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ types "k8s.io/apimachinery/pkg/types"
+ watch "k8s.io/apimachinery/pkg/watch"
+ rest "k8s.io/client-go/rest"
+)
+
+// CustomResourceDefinitionsGetter has a method to return a CustomResourceDefinitionInterface.
+// A group's client should implement this interface.
+type CustomResourceDefinitionsGetter interface {
+ CustomResourceDefinitions() CustomResourceDefinitionInterface
+}
+
+// CustomResourceDefinitionInterface has methods to work with CustomResourceDefinition resources.
+type CustomResourceDefinitionInterface interface {
+ Create(ctx context.Context, customResourceDefinition *v1beta1.CustomResourceDefinition, opts v1.CreateOptions) (*v1beta1.CustomResourceDefinition, error)
+ Update(ctx context.Context, customResourceDefinition *v1beta1.CustomResourceDefinition, opts v1.UpdateOptions) (*v1beta1.CustomResourceDefinition, error)
+ UpdateStatus(ctx context.Context, customResourceDefinition *v1beta1.CustomResourceDefinition, opts v1.UpdateOptions) (*v1beta1.CustomResourceDefinition, error)
+ Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
+ DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
+ Get(ctx context.Context, name string, opts v1.GetOptions) (*v1beta1.CustomResourceDefinition, error)
+ List(ctx context.Context, opts v1.ListOptions) (*v1beta1.CustomResourceDefinitionList, error)
+ Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
+ Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.CustomResourceDefinition, err error)
+ CustomResourceDefinitionExpansion
+}
+
+// customResourceDefinitions implements CustomResourceDefinitionInterface
+type customResourceDefinitions struct {
+ client rest.Interface
+}
+
+// newCustomResourceDefinitions returns a CustomResourceDefinitions
+func newCustomResourceDefinitions(c *ApiextensionsV1beta1Client) *customResourceDefinitions {
+ return &customResourceDefinitions{
+ client: c.RESTClient(),
+ }
+}
+
+// Get takes name of the customResourceDefinition, and returns the corresponding customResourceDefinition object, and an error if there is any.
+func (c *customResourceDefinitions) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.CustomResourceDefinition, err error) {
+ result = &v1beta1.CustomResourceDefinition{}
+ err = c.client.Get().
+ Resource("customresourcedefinitions").
+ Name(name).
+ VersionedParams(&options, scheme.ParameterCodec).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// List takes label and field selectors, and returns the list of CustomResourceDefinitions that match those selectors.
+func (c *customResourceDefinitions) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.CustomResourceDefinitionList, err error) {
+ var timeout time.Duration
+ if opts.TimeoutSeconds != nil {
+ timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+ }
+ result = &v1beta1.CustomResourceDefinitionList{}
+ err = c.client.Get().
+ Resource("customresourcedefinitions").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Watch returns a watch.Interface that watches the requested customResourceDefinitions.
+func (c *customResourceDefinitions) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
+ var timeout time.Duration
+ if opts.TimeoutSeconds != nil {
+ timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+ }
+ opts.Watch = true
+ return c.client.Get().
+ Resource("customresourcedefinitions").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Watch(ctx)
+}
+
+// Create takes the representation of a customResourceDefinition and creates it. Returns the server's representation of the customResourceDefinition, and an error, if there is any.
+func (c *customResourceDefinitions) Create(ctx context.Context, customResourceDefinition *v1beta1.CustomResourceDefinition, opts v1.CreateOptions) (result *v1beta1.CustomResourceDefinition, err error) {
+ result = &v1beta1.CustomResourceDefinition{}
+ err = c.client.Post().
+ Resource("customresourcedefinitions").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(customResourceDefinition).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Update takes the representation of a customResourceDefinition and updates it. Returns the server's representation of the customResourceDefinition, and an error, if there is any.
+func (c *customResourceDefinitions) Update(ctx context.Context, customResourceDefinition *v1beta1.CustomResourceDefinition, opts v1.UpdateOptions) (result *v1beta1.CustomResourceDefinition, err error) {
+ result = &v1beta1.CustomResourceDefinition{}
+ err = c.client.Put().
+ Resource("customresourcedefinitions").
+ Name(customResourceDefinition.Name).
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(customResourceDefinition).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// UpdateStatus was generated because the type contains a Status member.
+// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
+func (c *customResourceDefinitions) UpdateStatus(ctx context.Context, customResourceDefinition *v1beta1.CustomResourceDefinition, opts v1.UpdateOptions) (result *v1beta1.CustomResourceDefinition, err error) {
+ result = &v1beta1.CustomResourceDefinition{}
+ err = c.client.Put().
+ Resource("customresourcedefinitions").
+ Name(customResourceDefinition.Name).
+ SubResource("status").
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(customResourceDefinition).
+ Do(ctx).
+ Into(result)
+ return
+}
+
+// Delete takes name of the customResourceDefinition and deletes it. Returns an error if one occurs.
+func (c *customResourceDefinitions) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
+ return c.client.Delete().
+ Resource("customresourcedefinitions").
+ Name(name).
+ Body(&opts).
+ Do(ctx).
+ Error()
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *customResourceDefinitions) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
+ var timeout time.Duration
+ if listOpts.TimeoutSeconds != nil {
+ timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
+ }
+ return c.client.Delete().
+ Resource("customresourcedefinitions").
+ VersionedParams(&listOpts, scheme.ParameterCodec).
+ Timeout(timeout).
+ Body(&opts).
+ Do(ctx).
+ Error()
+}
+
+// Patch applies the patch and returns the patched customResourceDefinition.
+func (c *customResourceDefinitions) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.CustomResourceDefinition, err error) {
+ result = &v1beta1.CustomResourceDefinition{}
+ err = c.client.Patch(pt).
+ Resource("customresourcedefinitions").
+ Name(name).
+ SubResource(subresources...).
+ VersionedParams(&opts, scheme.ParameterCodec).
+ Body(data).
+ Do(ctx).
+ Into(result)
+ return
+}
diff --git a/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1/doc.go b/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1/doc.go
new file mode 100644
index 000000000..771101956
--- /dev/null
+++ b/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1/doc.go
@@ -0,0 +1,20 @@
+/*
+Copyright 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.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+// This package has the automatically generated typed clients.
+package v1beta1
diff --git a/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1/generated_expansion.go b/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1/generated_expansion.go
new file mode 100644
index 000000000..2a989d4be
--- /dev/null
+++ b/vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1/generated_expansion.go
@@ -0,0 +1,21 @@
+/*
+Copyright 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.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1beta1
+
+type CustomResourceDefinitionExpansion interface{}
diff --git a/vendor/k8s.io/client-go/util/retry/OWNERS b/vendor/k8s.io/client-go/util/retry/OWNERS
new file mode 100644
index 000000000..dec3e88d6
--- /dev/null
+++ b/vendor/k8s.io/client-go/util/retry/OWNERS
@@ -0,0 +1,4 @@
+# See the OWNERS docs at https://go.k8s.io/owners
+
+reviewers:
+- caesarxuchao
diff --git a/vendor/k8s.io/client-go/util/retry/util.go b/vendor/k8s.io/client-go/util/retry/util.go
new file mode 100644
index 000000000..15e2722f3
--- /dev/null
+++ b/vendor/k8s.io/client-go/util/retry/util.go
@@ -0,0 +1,105 @@
+/*
+Copyright 2016 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package retry
+
+import (
+ "time"
+
+ "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/util/wait"
+)
+
+// DefaultRetry is the recommended retry for a conflict where multiple clients
+// are making changes to the same resource.
+var DefaultRetry = wait.Backoff{
+ Steps: 5,
+ Duration: 10 * time.Millisecond,
+ Factor: 1.0,
+ Jitter: 0.1,
+}
+
+// DefaultBackoff is the recommended backoff for a conflict where a client
+// may be attempting to make an unrelated modification to a resource under
+// active management by one or more controllers.
+var DefaultBackoff = wait.Backoff{
+ Steps: 4,
+ Duration: 10 * time.Millisecond,
+ Factor: 5.0,
+ Jitter: 0.1,
+}
+
+// OnError allows the caller to retry fn in case the error returned by fn is retriable
+// according to the provided function. backoff defines the maximum retries and the wait
+// interval between two retries.
+func OnError(backoff wait.Backoff, retriable func(error) bool, fn func() error) error {
+ var lastErr error
+ err := wait.ExponentialBackoff(backoff, func() (bool, error) {
+ err := fn()
+ switch {
+ case err == nil:
+ return true, nil
+ case retriable(err):
+ lastErr = err
+ return false, nil
+ default:
+ return false, err
+ }
+ })
+ if err == wait.ErrWaitTimeout {
+ err = lastErr
+ }
+ return err
+}
+
+// RetryOnConflict is used to make an update to a resource when you have to worry about
+// conflicts caused by other code making unrelated updates to the resource at the same
+// time. fn should fetch the resource to be modified, make appropriate changes to it, try
+// to update it, and return (unmodified) the error from the update function. On a
+// successful update, RetryOnConflict will return nil. If the update function returns a
+// "Conflict" error, RetryOnConflict will wait some amount of time as described by
+// backoff, and then try again. On a non-"Conflict" error, or if it retries too many times
+// and gives up, RetryOnConflict will return an error to the caller.
+//
+// err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
+// // Fetch the resource here; you need to refetch it on every try, since
+// // if you got a conflict on the last update attempt then you need to get
+// // the current version before making your own changes.
+// pod, err := c.Pods("mynamespace").Get(name, metav1.GetOptions{})
+// if err ! nil {
+// return err
+// }
+//
+// // Make whatever updates to the resource are needed
+// pod.Status.Phase = v1.PodFailed
+//
+// // Try to update
+// _, err = c.Pods("mynamespace").UpdateStatus(pod)
+// // You have to return err itself here (not wrapped inside another error)
+// // so that RetryOnConflict can identify it correctly.
+// return err
+// })
+// if err != nil {
+// // May be conflict if max retries were hit, or may be something unrelated
+// // like permissions or a network error
+// return err
+// }
+// ...
+//
+// TODO: Make Backoff an interface?
+func RetryOnConflict(backoff wait.Backoff, fn func() error) error {
+ return OnError(backoff, errors.IsConflict, fn)
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 5278c2b12..f8a8b84c6 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1311,6 +1311,10 @@ k8s.io/api/storage/v1beta1
k8s.io/apiextensions-apiserver/pkg/apis/apiextensions
k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1
k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1
+k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset
+k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme
+k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1
+k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1
k8s.io/apiextensions-apiserver/pkg/crdserverscheme
# k8s.io/apimachinery v0.22.6 => github.com/kubeedge/kubernetes/staging/src/k8s.io/apimachinery v1.22.6-kubeedge3
## explicit; go 1.16
@@ -1795,6 +1799,7 @@ k8s.io/client-go/util/flowcontrol
k8s.io/client-go/util/homedir
k8s.io/client-go/util/jsonpath
k8s.io/client-go/util/keyutil
+k8s.io/client-go/util/retry
k8s.io/client-go/util/workqueue
# k8s.io/cloud-provider v0.22.6 => github.com/kubeedge/kubernetes/staging/src/k8s.io/cloud-provider v1.22.6-kubeedge3
## explicit; go 1.16
@@ -2301,14 +2306,20 @@ sigs.k8s.io/controller-runtime/pkg/config/v1alpha1
sigs.k8s.io/controller-runtime/pkg/controller
sigs.k8s.io/controller-runtime/pkg/controller/controllerutil
sigs.k8s.io/controller-runtime/pkg/conversion
+sigs.k8s.io/controller-runtime/pkg/envtest
sigs.k8s.io/controller-runtime/pkg/event
sigs.k8s.io/controller-runtime/pkg/handler
sigs.k8s.io/controller-runtime/pkg/healthz
sigs.k8s.io/controller-runtime/pkg/internal/controller
sigs.k8s.io/controller-runtime/pkg/internal/controller/metrics
+sigs.k8s.io/controller-runtime/pkg/internal/flock
sigs.k8s.io/controller-runtime/pkg/internal/log
sigs.k8s.io/controller-runtime/pkg/internal/objectutil
sigs.k8s.io/controller-runtime/pkg/internal/recorder
+sigs.k8s.io/controller-runtime/pkg/internal/testing/addr
+sigs.k8s.io/controller-runtime/pkg/internal/testing/certs
+sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane
+sigs.k8s.io/controller-runtime/pkg/internal/testing/process
sigs.k8s.io/controller-runtime/pkg/leaderelection
sigs.k8s.io/controller-runtime/pkg/log
sigs.k8s.io/controller-runtime/pkg/manager
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/crd.go b/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/crd.go
new file mode 100644
index 000000000..51bf36679
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/crd.go
@@ -0,0 +1,451 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package envtest
+
+import (
+ "bufio"
+ "bytes"
+ "context"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "time"
+
+ apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+ "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apimachinery/pkg/util/sets"
+ "k8s.io/apimachinery/pkg/util/wait"
+ k8syaml "k8s.io/apimachinery/pkg/util/yaml"
+ "k8s.io/client-go/kubernetes/scheme"
+ "k8s.io/client-go/rest"
+ "k8s.io/client-go/util/retry"
+ "k8s.io/utils/pointer"
+ "sigs.k8s.io/yaml"
+
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/webhook/conversion"
+)
+
+// CRDInstallOptions are the options for installing CRDs.
+type CRDInstallOptions struct {
+ // Scheme is used to determine if conversion webhooks should be enabled
+ // for a particular CRD / object.
+ //
+ // Conversion webhooks are going to be enabled if an object in the scheme
+ // implements Hub and Spoke conversions.
+ //
+ // If nil, scheme.Scheme is used.
+ Scheme *runtime.Scheme
+
+ // Paths is a list of paths to the directories or files containing CRDs
+ Paths []string
+
+ // CRDs is a list of CRDs to install
+ CRDs []apiextensionsv1.CustomResourceDefinition
+
+ // ErrorIfPathMissing will cause an error if a Path does not exist
+ ErrorIfPathMissing bool
+
+ // MaxTime is the max time to wait
+ MaxTime time.Duration
+
+ // PollInterval is the interval to check
+ PollInterval time.Duration
+
+ // CleanUpAfterUse will cause the CRDs listed for installation to be
+ // uninstalled when terminating the test environment.
+ // Defaults to false.
+ CleanUpAfterUse bool
+
+ // WebhookOptions contains the conversion webhook information to install
+ // on the CRDs. This field is usually inherited by the EnvTest options.
+ //
+ // If you're passing this field manually, you need to make sure that
+ // the CA information and host port is filled in properly.
+ WebhookOptions WebhookInstallOptions
+}
+
+const defaultPollInterval = 100 * time.Millisecond
+const defaultMaxWait = 10 * time.Second
+
+// InstallCRDs installs a collection of CRDs into a cluster by reading the crd yaml files from a directory.
+func InstallCRDs(config *rest.Config, options CRDInstallOptions) ([]apiextensionsv1.CustomResourceDefinition, error) {
+ defaultCRDOptions(&options)
+
+ // Read the CRD yamls into options.CRDs
+ if err := readCRDFiles(&options); err != nil {
+ return nil, fmt.Errorf("unable to read CRD files: %w", err)
+ }
+
+ if err := modifyConversionWebhooks(options.CRDs, options.Scheme, options.WebhookOptions); err != nil {
+ return nil, err
+ }
+
+ // Create the CRDs in the apiserver
+ if err := CreateCRDs(config, options.CRDs); err != nil {
+ return options.CRDs, fmt.Errorf("unable to create CRD instances: %w", err)
+ }
+
+ // Wait for the CRDs to appear as Resources in the apiserver
+ if err := WaitForCRDs(config, options.CRDs, options); err != nil {
+ return options.CRDs, fmt.Errorf("something went wrong waiting for CRDs to appear as API resources: %w", err)
+ }
+
+ return options.CRDs, nil
+}
+
+// readCRDFiles reads the directories of CRDs in options.Paths and adds the CRD structs to options.CRDs.
+func readCRDFiles(options *CRDInstallOptions) error {
+ if len(options.Paths) > 0 {
+ crdList, err := renderCRDs(options)
+ if err != nil {
+ return err
+ }
+
+ options.CRDs = append(options.CRDs, crdList...)
+ }
+ return nil
+}
+
+// defaultCRDOptions sets the default values for CRDs.
+func defaultCRDOptions(o *CRDInstallOptions) {
+ if o.Scheme == nil {
+ o.Scheme = scheme.Scheme
+ }
+ if o.MaxTime == 0 {
+ o.MaxTime = defaultMaxWait
+ }
+ if o.PollInterval == 0 {
+ o.PollInterval = defaultPollInterval
+ }
+}
+
+// WaitForCRDs waits for the CRDs to appear in discovery.
+func WaitForCRDs(config *rest.Config, crds []apiextensionsv1.CustomResourceDefinition, options CRDInstallOptions) error {
+ // Add each CRD to a map of GroupVersion to Resource
+ waitingFor := map[schema.GroupVersion]*sets.String{}
+ for _, crd := range crds {
+ gvs := []schema.GroupVersion{}
+ for _, version := range crd.Spec.Versions {
+ if version.Served {
+ gvs = append(gvs, schema.GroupVersion{Group: crd.Spec.Group, Version: version.Name})
+ }
+ }
+
+ for _, gv := range gvs {
+ log.V(1).Info("adding API in waitlist", "GV", gv)
+ if _, found := waitingFor[gv]; !found {
+ // Initialize the set
+ waitingFor[gv] = &sets.String{}
+ }
+ // Add the Resource
+ waitingFor[gv].Insert(crd.Spec.Names.Plural)
+ }
+ }
+
+ // Poll until all resources are found in discovery
+ p := &poller{config: config, waitingFor: waitingFor}
+ return wait.PollImmediate(options.PollInterval, options.MaxTime, p.poll)
+}
+
+// poller checks if all the resources have been found in discovery, and returns false if not.
+type poller struct {
+ // config is used to get discovery
+ config *rest.Config
+
+ // waitingFor is the map of resources keyed by group version that have not yet been found in discovery
+ waitingFor map[schema.GroupVersion]*sets.String
+}
+
+// poll checks if all the resources have been found in discovery, and returns false if not.
+func (p *poller) poll() (done bool, err error) {
+ // Create a new clientset to avoid any client caching of discovery
+ cs, err := clientset.NewForConfig(p.config)
+ if err != nil {
+ return false, err
+ }
+
+ allFound := true
+ for gv, resources := range p.waitingFor {
+ // All resources found, do nothing
+ if resources.Len() == 0 {
+ delete(p.waitingFor, gv)
+ continue
+ }
+
+ // Get the Resources for this GroupVersion
+ // TODO: Maybe the controller-runtime client should be able to do this...
+ resourceList, err := cs.Discovery().ServerResourcesForGroupVersion(gv.Group + "/" + gv.Version)
+ if err != nil {
+ return false, nil //nolint:nilerr
+ }
+
+ // Remove each found resource from the resources set that we are waiting for
+ for _, resource := range resourceList.APIResources {
+ resources.Delete(resource.Name)
+ }
+
+ // Still waiting on some resources in this group version
+ if resources.Len() != 0 {
+ allFound = false
+ }
+ }
+ return allFound, nil
+}
+
+// UninstallCRDs uninstalls a collection of CRDs by reading the crd yaml files from a directory.
+func UninstallCRDs(config *rest.Config, options CRDInstallOptions) error {
+ // Read the CRD yamls into options.CRDs
+ if err := readCRDFiles(&options); err != nil {
+ return err
+ }
+
+ // Delete the CRDs from the apiserver
+ cs, err := client.New(config, client.Options{})
+ if err != nil {
+ return err
+ }
+
+ // Uninstall each CRD
+ for _, crd := range options.CRDs {
+ crd := crd
+ log.V(1).Info("uninstalling CRD", "crd", crd.GetName())
+ if err := cs.Delete(context.TODO(), &crd); err != nil {
+ // If CRD is not found, we can consider success
+ if !apierrors.IsNotFound(err) {
+ return err
+ }
+ }
+ }
+
+ return nil
+}
+
+// CreateCRDs creates the CRDs.
+func CreateCRDs(config *rest.Config, crds []apiextensionsv1.CustomResourceDefinition) error {
+ cs, err := client.New(config, client.Options{})
+ if err != nil {
+ return fmt.Errorf("unable to create client: %w", err)
+ }
+
+ // Create each CRD
+ for _, crd := range crds {
+ crd := crd
+ log.V(1).Info("installing CRD", "crd", crd.GetName())
+ existingCrd := crd.DeepCopy()
+ err := cs.Get(context.TODO(), client.ObjectKey{Name: crd.GetName()}, existingCrd)
+ switch {
+ case apierrors.IsNotFound(err):
+ if err := cs.Create(context.TODO(), &crd); err != nil {
+ return fmt.Errorf("unable to create CRD %q: %w", crd.GetName(), err)
+ }
+ case err != nil:
+ return fmt.Errorf("unable to get CRD %q to check if it exists: %w", crd.GetName(), err)
+ default:
+ log.V(1).Info("CRD already exists, updating", "crd", crd.GetName())
+ if err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
+ if err := cs.Get(context.TODO(), client.ObjectKey{Name: crd.GetName()}, existingCrd); err != nil {
+ return err
+ }
+ crd.SetResourceVersion(existingCrd.GetResourceVersion())
+ return cs.Update(context.TODO(), &crd)
+ }); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+// renderCRDs iterate through options.Paths and extract all CRD files.
+func renderCRDs(options *CRDInstallOptions) ([]apiextensionsv1.CustomResourceDefinition, error) {
+ var (
+ err error
+ info os.FileInfo
+ files []os.FileInfo
+ )
+
+ type GVKN struct {
+ GVK schema.GroupVersionKind
+ Name string
+ }
+
+ crds := map[GVKN]apiextensionsv1.CustomResourceDefinition{}
+
+ for _, path := range options.Paths {
+ var filePath = path
+
+ // Return the error if ErrorIfPathMissing exists
+ if info, err = os.Stat(path); os.IsNotExist(err) {
+ if options.ErrorIfPathMissing {
+ return nil, err
+ }
+ continue
+ }
+
+ if !info.IsDir() {
+ filePath, files = filepath.Dir(path), []os.FileInfo{info}
+ } else if files, err = ioutil.ReadDir(path); err != nil {
+ return nil, err
+ }
+
+ log.V(1).Info("reading CRDs from path", "path", path)
+ crdList, err := readCRDs(filePath, files)
+ if err != nil {
+ return nil, err
+ }
+
+ for i, crd := range crdList {
+ gvkn := GVKN{GVK: crd.GroupVersionKind(), Name: crd.GetName()}
+ if _, found := crds[gvkn]; found {
+ // Currently, we only print a log when there are duplicates. We may want to error out if that makes more sense.
+ log.Info("there are more than one CRD definitions with the same <Group, Version, Kind, Name>", "GVKN", gvkn)
+ }
+ // We always use the CRD definition that we found last.
+ crds[gvkn] = crdList[i]
+ }
+ }
+
+ // Converting map to a list to return
+ res := []apiextensionsv1.CustomResourceDefinition{}
+ for _, obj := range crds {
+ res = append(res, obj)
+ }
+ return res, nil
+}
+
+// modifyConversionWebhooks takes all the registered CustomResourceDefinitions and applies modifications
+// to conditionally enable webhooks if the type is registered within the scheme.
+func modifyConversionWebhooks(crds []apiextensionsv1.CustomResourceDefinition, scheme *runtime.Scheme, webhookOptions WebhookInstallOptions) error {
+ if len(webhookOptions.LocalServingCAData) == 0 {
+ return nil
+ }
+
+ // Determine all registered convertible types.
+ convertibles := map[schema.GroupKind]struct{}{}
+ for gvk := range scheme.AllKnownTypes() {
+ obj, err := scheme.New(gvk)
+ if err != nil {
+ return err
+ }
+ if ok, err := conversion.IsConvertible(scheme, obj); ok && err == nil {
+ convertibles[gvk.GroupKind()] = struct{}{}
+ }
+ }
+
+ // generate host port.
+ hostPort, err := webhookOptions.generateHostPort()
+ if err != nil {
+ return err
+ }
+ url := pointer.StringPtr(fmt.Sprintf("https://%s/convert", hostPort))
+
+ for i := range crds {
+ // Continue if we're preserving unknown fields.
+ if crds[i].Spec.PreserveUnknownFields {
+ continue
+ }
+ // Continue if the GroupKind isn't registered as being convertible.
+ if _, ok := convertibles[schema.GroupKind{
+ Group: crds[i].Spec.Group,
+ Kind: crds[i].Spec.Names.Kind,
+ }]; !ok {
+ continue
+ }
+ if crds[i].Spec.Conversion == nil {
+ crds[i].Spec.Conversion = &apiextensionsv1.CustomResourceConversion{
+ Webhook: &apiextensionsv1.WebhookConversion{},
+ }
+ }
+ crds[i].Spec.Conversion.Strategy = apiextensionsv1.WebhookConverter
+ crds[i].Spec.Conversion.Webhook.ConversionReviewVersions = []string{"v1", "v1beta1"}
+ crds[i].Spec.Conversion.Webhook.ClientConfig = &apiextensionsv1.WebhookClientConfig{
+ Service: nil,
+ URL: url,
+ CABundle: webhookOptions.LocalServingCAData,
+ }
+ }
+
+ return nil
+}
+
+// readCRDs reads the CRDs from files and Unmarshals them into structs.
+func readCRDs(basePath string, files []os.FileInfo) ([]apiextensionsv1.CustomResourceDefinition, error) {
+ var crds []apiextensionsv1.CustomResourceDefinition
+
+ // White list the file extensions that may contain CRDs
+ crdExts := sets.NewString(".json", ".yaml", ".yml")
+
+ for _, file := range files {
+ // Only parse allowlisted file types
+ if !crdExts.Has(filepath.Ext(file.Name())) {
+ continue
+ }
+
+ // Unmarshal CRDs from file into structs
+ docs, err := readDocuments(filepath.Join(basePath, file.Name()))
+ if err != nil {
+ return nil, err
+ }
+
+ for _, doc := range docs {
+ crd := &apiextensionsv1.CustomResourceDefinition{}
+ if err = yaml.Unmarshal(doc, crd); err != nil {
+ return nil, err
+ }
+
+ if crd.Kind != "CustomResourceDefinition" || crd.Spec.Names.Kind == "" || crd.Spec.Group == "" {
+ continue
+ }
+ crds = append(crds, *crd)
+ }
+
+ log.V(1).Info("read CRDs from file", "file", file.Name())
+ }
+ return crds, nil
+}
+
+// readDocuments reads documents from file.
+func readDocuments(fp string) ([][]byte, error) {
+ b, err := ioutil.ReadFile(fp) //nolint:gosec
+ if err != nil {
+ return nil, err
+ }
+
+ docs := [][]byte{}
+ reader := k8syaml.NewYAMLReader(bufio.NewReader(bytes.NewReader(b)))
+ for {
+ // Read document
+ doc, err := reader.Read()
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+
+ return nil, err
+ }
+
+ docs = append(docs, doc)
+ }
+
+ return docs, nil
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/doc.go
new file mode 100644
index 000000000..412e794cc
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/doc.go
@@ -0,0 +1,26 @@
+/*
+Copyright 2017 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package envtest provides libraries for integration testing by starting a local control plane
+//
+// Control plane binaries (etcd and kube-apiserver) are loaded by default from
+// /usr/local/kubebuilder/bin. This can be overridden by setting the
+// KUBEBUILDER_ASSETS environment variable, or by directly creating a
+// ControlPlane for the Environment to use.
+//
+// Environment can also be configured to work with an existing cluster, and
+// simply load CRDs and provide client configuration.
+package envtest
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/helper.go b/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/helper.go
new file mode 100644
index 000000000..70906db6d
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/helper.go
@@ -0,0 +1,69 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package envtest
+
+import (
+ apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+ "k8s.io/client-go/kubernetes/scheme"
+)
+
+var (
+ crdScheme = scheme.Scheme
+)
+
+// init is required to correctly initialize the crdScheme package variable.
+func init() {
+ _ = apiextensionsv1.AddToScheme(crdScheme)
+}
+
+// mergePaths merges two string slices containing paths.
+// This function makes no guarantees about order of the merged slice.
+func mergePaths(s1, s2 []string) []string {
+ m := make(map[string]struct{})
+ for _, s := range s1 {
+ m[s] = struct{}{}
+ }
+ for _, s := range s2 {
+ m[s] = struct{}{}
+ }
+ merged := make([]string, len(m))
+ i := 0
+ for key := range m {
+ merged[i] = key
+ i++
+ }
+ return merged
+}
+
+// mergeCRDs merges two CRD slices using their names.
+// This function makes no guarantees about order of the merged slice.
+func mergeCRDs(s1, s2 []apiextensionsv1.CustomResourceDefinition) []apiextensionsv1.CustomResourceDefinition {
+ m := make(map[string]apiextensionsv1.CustomResourceDefinition)
+ for _, obj := range s1 {
+ m[obj.GetName()] = obj
+ }
+ for _, obj := range s2 {
+ m[obj.GetName()] = obj
+ }
+ merged := make([]apiextensionsv1.CustomResourceDefinition, len(m))
+ i := 0
+ for _, obj := range m {
+ merged[i] = obj
+ i++
+ }
+ return merged
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/server.go b/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/server.go
new file mode 100644
index 000000000..571f04bf6
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/server.go
@@ -0,0 +1,375 @@
+/*
+Copyright 2016 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package envtest
+
+import (
+ "fmt"
+ "os"
+ "strings"
+ "time"
+
+ apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/client-go/kubernetes/scheme"
+ "k8s.io/client-go/rest"
+
+ "sigs.k8s.io/controller-runtime/pkg/client/config"
+ logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
+ "sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane"
+ "sigs.k8s.io/controller-runtime/pkg/internal/testing/process"
+)
+
+var log = logf.RuntimeLog.WithName("test-env")
+
+/*
+It's possible to override some defaults, by setting the following environment variables:
+ USE_EXISTING_CLUSTER (boolean): if set to true, envtest will use an existing cluster
+ TEST_ASSET_KUBE_APISERVER (string): path to the api-server binary to use
+ TEST_ASSET_ETCD (string): path to the etcd binary to use
+ TEST_ASSET_KUBECTL (string): path to the kubectl binary to use
+ KUBEBUILDER_ASSETS (string): directory containing the binaries to use (api-server, etcd and kubectl). Defaults to /usr/local/kubebuilder/bin.
+ KUBEBUILDER_CONTROLPLANE_START_TIMEOUT (string supported by time.ParseDuration): timeout for test control plane to start. Defaults to 20s.
+ KUBEBUILDER_CONTROLPLANE_STOP_TIMEOUT (string supported by time.ParseDuration): timeout for test control plane to start. Defaults to 20s.
+ KUBEBUILDER_ATTACH_CONTROL_PLANE_OUTPUT (boolean): if set to true, the control plane's stdout and stderr are attached to os.Stdout and os.Stderr
+
+*/
+const (
+ envUseExistingCluster = "USE_EXISTING_CLUSTER"
+ envStartTimeout = "KUBEBUILDER_CONTROLPLANE_START_TIMEOUT"
+ envStopTimeout = "KUBEBUILDER_CONTROLPLANE_STOP_TIMEOUT"
+ envAttachOutput = "KUBEBUILDER_ATTACH_CONTROL_PLANE_OUTPUT"
+ StartTimeout = 60
+ StopTimeout = 60
+
+ defaultKubebuilderControlPlaneStartTimeout = 20 * time.Second
+ defaultKubebuilderControlPlaneStopTimeout = 20 * time.Second
+)
+
+// internal types we expose as part of our public API.
+type (
+ // ControlPlane is the re-exported ControlPlane type from the internal testing package.
+ ControlPlane = controlplane.ControlPlane
+
+ // APIServer is the re-exported APIServer from the internal testing package.
+ APIServer = controlplane.APIServer
+
+ // Etcd is the re-exported Etcd from the internal testing package.
+ Etcd = controlplane.Etcd
+
+ // User represents a Kubernetes user to provision for auth purposes.
+ User = controlplane.User
+
+ // AuthenticatedUser represets a Kubernetes user that's been provisioned.
+ AuthenticatedUser = controlplane.AuthenticatedUser
+
+ // ListenAddr indicates the address and port that the API server should listen on.
+ ListenAddr = process.ListenAddr
+
+ // SecureServing contains details describing how the API server should serve
+ // its secure endpoint.
+ SecureServing = controlplane.SecureServing
+
+ // Authn is an authentication method that can be used with the control plane to
+ // provision users.
+ Authn = controlplane.Authn
+
+ // Arguments allows configuring a process's flags.
+ Arguments = process.Arguments
+
+ // Arg is a single flag with one or more values.
+ Arg = process.Arg
+)
+
+var (
+ // EmptyArguments constructs a new set of flags with nothing set.
+ //
+ // This is mostly useful for testing helper methods -- you'll want to call
+ // Configure on the APIServer (or etcd) to configure their arguments.
+ EmptyArguments = process.EmptyArguments
+)
+
+// Environment creates a Kubernetes test environment that will start / stop the Kubernetes control plane and
+// install extension APIs.
+type Environment struct {
+ // ControlPlane is the ControlPlane including the apiserver and etcd
+ ControlPlane controlplane.ControlPlane
+
+ // Scheme is used to determine if conversion webhooks should be enabled
+ // for a particular CRD / object.
+ //
+ // Conversion webhooks are going to be enabled if an object in the scheme
+ // implements Hub and Spoke conversions.
+ //
+ // If nil, scheme.Scheme is used.
+ Scheme *runtime.Scheme
+
+ // Config can be used to talk to the apiserver. It's automatically
+ // populated if not set using the standard controller-runtime config
+ // loading.
+ Config *rest.Config
+
+ // CRDInstallOptions are the options for installing CRDs.
+ CRDInstallOptions CRDInstallOptions
+
+ // WebhookInstallOptions are the options for installing webhooks.
+ WebhookInstallOptions WebhookInstallOptions
+
+ // ErrorIfCRDPathMissing provides an interface for the underlying
+ // CRDInstallOptions.ErrorIfPathMissing. It prevents silent failures
+ // for missing CRD paths.
+ ErrorIfCRDPathMissing bool
+
+ // CRDs is a list of CRDs to install.
+ // If both this field and CRDs field in CRDInstallOptions are specified, the
+ // values are merged.
+ CRDs []apiextensionsv1.CustomResourceDefinition
+
+ // CRDDirectoryPaths is a list of paths containing CRD yaml or json configs.
+ // If both this field and Paths field in CRDInstallOptions are specified, the
+ // values are merged.
+ CRDDirectoryPaths []string
+
+ // BinaryAssetsDirectory is the path where the binaries required for the envtest are
+ // located in the local environment. This field can be overridden by setting KUBEBUILDER_ASSETS.
+ BinaryAssetsDirectory string
+
+ // UseExistingCluster indicates that this environments should use an
+ // existing kubeconfig, instead of trying to stand up a new control plane.
+ // This is useful in cases that need aggregated API servers and the like.
+ UseExistingCluster *bool
+
+ // ControlPlaneStartTimeout is the maximum duration each controlplane component
+ // may take to start. It defaults to the KUBEBUILDER_CONTROLPLANE_START_TIMEOUT
+ // environment variable or 20 seconds if unspecified
+ ControlPlaneStartTimeout time.Duration
+
+ // ControlPlaneStopTimeout is the maximum duration each controlplane component
+ // may take to stop. It defaults to the KUBEBUILDER_CONTROLPLANE_STOP_TIMEOUT
+ // environment variable or 20 seconds if unspecified
+ ControlPlaneStopTimeout time.Duration
+
+ // KubeAPIServerFlags is the set of flags passed while starting the api server.
+ //
+ // Deprecated: use ControlPlane.GetAPIServer().Configure() instead.
+ KubeAPIServerFlags []string
+
+ // AttachControlPlaneOutput indicates if control plane output will be attached to os.Stdout and os.Stderr.
+ // Enable this to get more visibility of the testing control plane.
+ // It respect KUBEBUILDER_ATTACH_CONTROL_PLANE_OUTPUT environment variable.
+ AttachControlPlaneOutput bool
+}
+
+// Stop stops a running server.
+// Previously installed CRDs, as listed in CRDInstallOptions.CRDs, will be uninstalled
+// if CRDInstallOptions.CleanUpAfterUse are set to true.
+func (te *Environment) Stop() error {
+ if te.CRDInstallOptions.CleanUpAfterUse {
+ if err := UninstallCRDs(te.Config, te.CRDInstallOptions); err != nil {
+ return err
+ }
+ }
+
+ if err := te.WebhookInstallOptions.Cleanup(); err != nil {
+ return err
+ }
+
+ if te.useExistingCluster() {
+ return nil
+ }
+
+ return te.ControlPlane.Stop()
+}
+
+// Start starts a local Kubernetes server and updates te.ApiserverPort with the port it is listening on.
+func (te *Environment) Start() (*rest.Config, error) {
+ if te.useExistingCluster() {
+ log.V(1).Info("using existing cluster")
+ if te.Config == nil {
+ // we want to allow people to pass in their own config, so
+ // only load a config if it hasn't already been set.
+ log.V(1).Info("automatically acquiring client configuration")
+
+ var err error
+ te.Config, err = config.GetConfig()
+ if err != nil {
+ return nil, fmt.Errorf("unable to get configuration for existing cluster: %w", err)
+ }
+ }
+ } else {
+ apiServer := te.ControlPlane.GetAPIServer()
+ if len(apiServer.Args) == 0 { //nolint:staticcheck
+ // pass these through separately from above in case something like
+ // AddUser defaults APIServer.
+ //
+ // TODO(directxman12): if/when we feel like making a bigger
+ // breaking change here, just make APIServer and Etcd non-pointers
+ // in ControlPlane.
+
+ // NB(directxman12): we still pass these in so that things work if the
+ // user manually specifies them, but in most cases we expect them to
+ // be nil so that we use the new .Configure() logic.
+ apiServer.Args = te.KubeAPIServerFlags //nolint:staticcheck
+ }
+ if te.ControlPlane.Etcd == nil {
+ te.ControlPlane.Etcd = &controlplane.Etcd{}
+ }
+
+ if os.Getenv(envAttachOutput) == "true" {
+ te.AttachControlPlaneOutput = true
+ }
+ if apiServer.Out == nil && te.AttachControlPlaneOutput {
+ apiServer.Out = os.Stdout
+ }
+ if apiServer.Err == nil && te.AttachControlPlaneOutput {
+ apiServer.Err = os.Stderr
+ }
+ if te.ControlPlane.Etcd.Out == nil && te.AttachControlPlaneOutput {
+ te.ControlPlane.Etcd.Out = os.Stdout
+ }
+ if te.ControlPlane.Etcd.Err == nil && te.AttachControlPlaneOutput {
+ te.ControlPlane.Etcd.Err = os.Stderr
+ }
+
+ apiServer.Path = process.BinPathFinder("kube-apiserver", te.BinaryAssetsDirectory)
+ te.ControlPlane.Etcd.Path = process.BinPathFinder("etcd", te.BinaryAssetsDirectory)
+ te.ControlPlane.KubectlPath = process.BinPathFinder("kubectl", te.BinaryAssetsDirectory)
+
+ if err := te.defaultTimeouts(); err != nil {
+ return nil, fmt.Errorf("failed to default controlplane timeouts: %w", err)
+ }
+ te.ControlPlane.Etcd.StartTimeout = te.ControlPlaneStartTimeout
+ te.ControlPlane.Etcd.StopTimeout = te.ControlPlaneStopTimeout
+ apiServer.StartTimeout = te.ControlPlaneStartTimeout
+ apiServer.StopTimeout = te.ControlPlaneStopTimeout
+
+ log.V(1).Info("starting control plane")
+ if err := te.startControlPlane(); err != nil {
+ return nil, fmt.Errorf("unable to start control plane itself: %w", err)
+ }
+
+ // Create the *rest.Config for creating new clients
+ baseConfig := &rest.Config{
+ // gotta go fast during tests -- we don't really care about overwhelming our test API server
+ QPS: 1000.0,
+ Burst: 2000.0,
+ }
+
+ adminInfo := User{Name: "admin", Groups: []string{"system:masters"}}
+ adminUser, err := te.ControlPlane.AddUser(adminInfo, baseConfig)
+ if err != nil {
+ return te.Config, fmt.Errorf("unable to provision admin user: %w", err)
+ }
+ te.Config = adminUser.Config()
+ }
+
+ // Set the default scheme if nil.
+ if te.Scheme == nil {
+ te.Scheme = scheme.Scheme
+ }
+
+ // Call PrepWithoutInstalling to setup certificates first
+ // and have them available to patch CRD conversion webhook as well.
+ if err := te.WebhookInstallOptions.PrepWithoutInstalling(); err != nil {
+ return nil, err
+ }
+
+ log.V(1).Info("installing CRDs")
+ te.CRDInstallOptions.CRDs = mergeCRDs(te.CRDInstallOptions.CRDs, te.CRDs)
+ te.CRDInstallOptions.Paths = mergePaths(te.CRDInstallOptions.Paths, te.CRDDirectoryPaths)
+ te.CRDInstallOptions.ErrorIfPathMissing = te.ErrorIfCRDPathMissing
+ te.CRDInstallOptions.WebhookOptions = te.WebhookInstallOptions
+ crds, err := InstallCRDs(te.Config, te.CRDInstallOptions)
+ if err != nil {
+ return te.Config, fmt.Errorf("unable to install CRDs onto control plane: %w", err)
+ }
+ te.CRDs = crds
+
+ log.V(1).Info("installing webhooks")
+ if err := te.WebhookInstallOptions.Install(te.Config); err != nil {
+ return nil, fmt.Errorf("unable to install webhooks onto control plane: %w", err)
+ }
+ return te.Config, nil
+}
+
+// AddUser provisions a new user for connecting to this Environment. The user will
+// have the specified name & belong to the specified groups.
+//
+// If you specify a "base" config, the returned REST Config will contain those
+// settings as well as any required by the authentication method. You can use
+// this to easily specify options like QPS.
+//
+// This is effectively a convinience alias for ControlPlane.AddUser -- see that
+// for more low-level details.
+func (te *Environment) AddUser(user User, baseConfig *rest.Config) (*AuthenticatedUser, error) {
+ return te.ControlPlane.AddUser(user, baseConfig)
+}
+
+func (te *Environment) startControlPlane() error {
+ numTries, maxRetries := 0, 5
+ var err error
+ for ; numTries < maxRetries; numTries++ {
+ // Start the control plane - retry if it fails
+ err = te.ControlPlane.Start()
+ if err == nil {
+ break
+ }
+ log.Error(err, "unable to start the controlplane", "tries", numTries)
+ }
+ if numTries == maxRetries {
+ return fmt.Errorf("failed to start the controlplane. retried %d times: %w", numTries, err)
+ }
+ return nil
+}
+
+func (te *Environment) defaultTimeouts() error {
+ var err error
+ if te.ControlPlaneStartTimeout == 0 {
+ if envVal := os.Getenv(envStartTimeout); envVal != "" {
+ te.ControlPlaneStartTimeout, err = time.ParseDuration(envVal)
+ if err != nil {
+ return err
+ }
+ } else {
+ te.ControlPlaneStartTimeout = defaultKubebuilderControlPlaneStartTimeout
+ }
+ }
+
+ if te.ControlPlaneStopTimeout == 0 {
+ if envVal := os.Getenv(envStopTimeout); envVal != "" {
+ te.ControlPlaneStopTimeout, err = time.ParseDuration(envVal)
+ if err != nil {
+ return err
+ }
+ } else {
+ te.ControlPlaneStopTimeout = defaultKubebuilderControlPlaneStopTimeout
+ }
+ }
+ return nil
+}
+
+func (te *Environment) useExistingCluster() bool {
+ if te.UseExistingCluster == nil {
+ return strings.ToLower(os.Getenv(envUseExistingCluster)) == "true"
+ }
+ return *te.UseExistingCluster
+}
+
+// DefaultKubeAPIServerFlags exposes the default args for the APIServer so that
+// you can use those to append your own additional arguments.
+//
+// Deprecated: use APIServer.Configure() instead.
+var DefaultKubeAPIServerFlags = controlplane.APIServerDefaultArgs //nolint:staticcheck
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/webhook.go b/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/webhook.go
new file mode 100644
index 000000000..bdd160bb2
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/envtest/webhook.go
@@ -0,0 +1,428 @@
+/*
+Copyright 2019 The Kubernetes Authors.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package envtest
+
+import (
+ "context"
+ "fmt"
+ "io/ioutil"
+ "net"
+ "os"
+ "path/filepath"
+ "time"
+
+ admissionv1 "k8s.io/api/admissionregistration/v1"
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apimachinery/pkg/util/sets"
+ "k8s.io/apimachinery/pkg/util/wait"
+ "k8s.io/client-go/kubernetes/scheme"
+ "k8s.io/client-go/rest"
+ "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
+ "sigs.k8s.io/yaml"
+
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/internal/testing/addr"
+ "sigs.k8s.io/controller-runtime/pkg/internal/testing/certs"
+)
+
+// WebhookInstallOptions are the options for installing mutating or validating webhooks.
+type WebhookInstallOptions struct {
+ // Paths is a list of paths to the directories or files containing the mutating or validating webhooks yaml or json configs.
+ Paths []string
+
+ // MutatingWebhooks is a list of MutatingWebhookConfigurations to install
+ MutatingWebhooks []admissionv1.MutatingWebhookConfiguration
+
+ // ValidatingWebhooks is a list of ValidatingWebhookConfigurations to install
+ ValidatingWebhooks []admissionv1.ValidatingWebhookConfiguration
+
+ // IgnoreErrorIfPathMissing will ignore an error if a DirectoryPath does not exist when set to true
+ IgnoreErrorIfPathMissing bool
+
+ // LocalServingHost is the host for serving webhooks on.
+ // it will be automatically populated
+ LocalServingHost string
+
+ // LocalServingPort is the allocated port for serving webhooks on.
+ // it will be automatically populated by a random available local port
+ LocalServingPort int
+
+ // LocalServingCertDir is the allocated directory for serving certificates.
+ // it will be automatically populated by the local temp dir
+ LocalServingCertDir string
+
+ // CAData is the CA that can be used to trust the serving certificates in LocalServingCertDir.
+ LocalServingCAData []byte
+
+ // LocalServingHostExternalName is the hostname to use to reach the webhook server.
+ LocalServingHostExternalName string
+
+ // MaxTime is the max time to wait
+ MaxTime time.Duration
+
+ // PollInterval is the interval to check
+ PollInterval time.Duration
+}
+
+// ModifyWebhookDefinitions modifies webhook definitions by:
+// - applying CABundle based on the provided tinyca
+// - if webhook client config uses service spec, it's removed and replaced with direct url.
+func (o *WebhookInstallOptions) ModifyWebhookDefinitions() error {
+ caData := o.LocalServingCAData
+
+ // generate host port.
+ hostPort, err := o.generateHostPort()
+ if err != nil {
+ return err
+ }
+
+ for i := range o.MutatingWebhooks {
+ for j := range o.MutatingWebhooks[i].Webhooks {
+ updateClientConfig(&o.MutatingWebhooks[i].Webhooks[j].ClientConfig, hostPort, caData)
+ }
+ }
+
+ for i := range o.ValidatingWebhooks {
+ for j := range o.ValidatingWebhooks[i].Webhooks {
+ updateClientConfig(&o.ValidatingWebhooks[i].Webhooks[j].ClientConfig, hostPort, caData)
+ }
+ }
+ return nil
+}
+
+func updateClientConfig(cc *admissionv1.WebhookClientConfig, hostPort string, caData []byte) {
+ cc.CABundle = caData
+ if cc.Service != nil && cc.Service.Path != nil {
+ url := fmt.Sprintf("https://%s/%s", hostPort, *cc.Service.Path)
+ cc.URL = &url
+ cc.Service = nil
+ }
+}
+
+func (o *WebhookInstallOptions) generateHostPort() (string, error) {
+ if o.LocalServingPort == 0 {
+ port, host, err := addr.Suggest(o.LocalServingHost)
+ if err != nil {
+ return "", fmt.Errorf("unable to grab random port for serving webhooks on: %v", err)
+ }
+ o.LocalServingPort = port
+ o.LocalServingHost = host
+ }
+ host := o.LocalServingHostExternalName
+ if host == "" {
+ host = o.LocalServingHost
+ }
+ return net.JoinHostPort(host, fmt.Sprintf("%d", o.LocalServingPort)), nil
+}
+
+// PrepWithoutInstalling does the setup parts of Install (populating host-port,
+// setting up CAs, etc), without actually truing to do anything with webhook
+// definitions. This is largely useful for internal testing of
+// controller-runtime, where we need a random host-port & caData for webhook
+// tests, but may be useful in similar scenarios.
+func (o *WebhookInstallOptions) PrepWithoutInstalling() error {
+ if err := o.setupCA(); err != nil {
+ return err
+ }
+
+ if err := parseWebhook(o); err != nil {
+ return err
+ }
+
+ return o.ModifyWebhookDefinitions()
+}
+
+// Install installs specified webhooks to the API server.
+func (o *WebhookInstallOptions) Install(config *rest.Config) error {
+ if len(o.LocalServingCAData) == 0 {
+ if err := o.PrepWithoutInstalling(); err != nil {
+ return err
+ }
+ }
+
+ if err := createWebhooks(config, o.MutatingWebhooks, o.ValidatingWebhooks); err != nil {
+ return err
+ }
+
+ return WaitForWebhooks(config, o.MutatingWebhooks, o.ValidatingWebhooks, *o)
+}
+
+// Cleanup cleans up cert directories.
+func (o *WebhookInstallOptions) Cleanup() error {
+ if o.LocalServingCertDir != "" {
+ return os.RemoveAll(o.LocalServingCertDir)
+ }
+ return nil
+}
+
+// WaitForWebhooks waits for the Webhooks to be available through API server.
+func WaitForWebhooks(config *rest.Config,
+ mutatingWebhooks []admissionv1.MutatingWebhookConfiguration,
+ validatingWebhooks []admissionv1.ValidatingWebhookConfiguration,
+ options WebhookInstallOptions) error {
+ waitingFor := map[schema.GroupVersionKind]*sets.String{}
+
+ for _, hook := range mutatingWebhooks {
+ h := hook
+ gvk, err := apiutil.GVKForObject(&h, scheme.Scheme)
+ if err != nil {
+ return fmt.Errorf("unable to get gvk for MutatingWebhookConfiguration %s: %v", hook.GetName(), err)
+ }
+
+ if _, ok := waitingFor[gvk]; !ok {
+ waitingFor[gvk] = &sets.String{}
+ }
+ waitingFor[gvk].Insert(h.GetName())
+ }
+
+ for _, hook := range validatingWebhooks {
+ h := hook
+ gvk, err := apiutil.GVKForObject(&h, scheme.Scheme)
+ if err != nil {
+ return fmt.Errorf("unable to get gvk for ValidatingWebhookConfiguration %s: %v", hook.GetName(), err)
+ }
+
+ if _, ok := waitingFor[gvk]; !ok {
+ waitingFor[gvk] = &sets.String{}
+ }
+ waitingFor[gvk].Insert(hook.GetName())
+ }
+
+ // Poll until all resources are found in discovery
+ p := &webhookPoller{config: config, waitingFor: waitingFor}
+ return wait.PollImmediate(options.PollInterval, options.MaxTime, p.poll)
+}
+
+// poller checks if all the resources have been found in discovery, and returns false if not.
+type webhookPoller struct {
+ // config is used to get discovery
+ config *rest.Config
+
+ // waitingFor is the map of resources keyed by group version that have not yet been found in discovery
+ waitingFor map[schema.GroupVersionKind]*sets.String
+}
+
+// poll checks if all the resources have been found in discovery, and returns false if not.
+func (p *webhookPoller) poll() (done bool, err error) {
+ // Create a new clientset to avoid any client caching of discovery
+ c, err := client.New(p.config, client.Options{})
+ if err != nil {
+ return false, err
+ }
+
+ allFound := true
+ for gvk, names := range p.waitingFor {
+ if names.Len() == 0 {
+ delete(p.waitingFor, gvk)
+ continue
+ }
+ for _, name := range names.List() {
+ var obj = &unstructured.Unstructured{}
+ obj.SetGroupVersionKind(gvk)
+ err := c.Get(context.Background(), client.ObjectKey{
+ Namespace: "",
+ Name: name,
+ }, obj)
+
+ if err == nil {
+ names.Delete(name)
+ }
+
+ if apierrors.IsNotFound(err) {
+ allFound = false
+ }
+ if err != nil {
+ return false, err
+ }
+ }
+ }
+ return allFound, nil
+}
+
+// setupCA creates CA for testing and writes them to disk.
+func (o *WebhookInstallOptions) setupCA() error {
+ hookCA, err := certs.NewTinyCA()
+ if err != nil {
+ return fmt.Errorf("unable to set up webhook CA: %v", err)
+ }
+
+ names := []string{"localhost", o.LocalServingHost, o.LocalServingHostExternalName}
+ hookCert, err := hookCA.NewServingCert(names...)
+ if err != nil {
+ return fmt.Errorf("unable to set up webhook serving certs: %v", err)
+ }
+
+ localServingCertsDir, err := ioutil.TempDir("", "envtest-serving-certs-")
+ o.LocalServingCertDir = localServingCertsDir
+ if err != nil {
+ return fmt.Errorf("unable to create directory for webhook serving certs: %v", err)
+ }
+
+ certData, keyData, err := hookCert.AsBytes()
+ if err != nil {
+ return fmt.Errorf("unable to marshal webhook serving certs: %v", err)
+ }
+
+ if err := ioutil.WriteFile(filepath.Join(localServingCertsDir, "tls.crt"), certData, 0640); err != nil { //nolint:gosec
+ return fmt.Errorf("unable to write webhook serving cert to disk: %v", err)
+ }
+ if err := ioutil.WriteFile(filepath.Join(localServingCertsDir, "tls.key"), keyData, 0640); err != nil { //nolint:gosec
+ return fmt.Errorf("unable to write webhook serving key to disk: %v", err)
+ }
+
+ o.LocalServingCAData = certData
+ return err
+}
+
+func createWebhooks(config *rest.Config, mutHooks []admissionv1.MutatingWebhookConfiguration, valHooks []admissionv1.ValidatingWebhookConfiguration) error {
+ cs, err := client.New(config, client.Options{})
+ if err != nil {
+ return err
+ }
+
+ // Create each webhook
+ for _, hook := range mutHooks {
+ hook := hook
+ log.V(1).Info("installing mutating webhook", "webhook", hook.GetName())
+ if err := ensureCreated(cs, &hook); err != nil {
+ return err
+ }
+ }
+ for _, hook := range valHooks {
+ hook := hook
+ log.V(1).Info("installing validating webhook", "webhook", hook.GetName())
+ if err := ensureCreated(cs, &hook); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// ensureCreated creates or update object if already exists in the cluster.
+func ensureCreated(cs client.Client, obj client.Object) error {
+ existing := obj.DeepCopyObject().(client.Object)
+ err := cs.Get(context.Background(), client.ObjectKey{Name: obj.GetName()}, existing)
+ switch {
+ case apierrors.IsNotFound(err):
+ if err := cs.Create(context.Background(), obj); err != nil {
+ return err
+ }
+ case err != nil:
+ return err
+ default:
+ log.V(1).Info("Webhook configuration already exists, updating", "webhook", obj.GetName())
+ obj.SetResourceVersion(existing.GetResourceVersion())
+ if err := cs.Update(context.Background(), obj); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// parseWebhook reads the directories or files of Webhooks in options.Paths and adds the Webhook structs to options.
+func parseWebhook(options *WebhookInstallOptions) error {
+ if len(options.Paths) > 0 {
+ for _, path := range options.Paths {
+ _, err := os.Stat(path)
+ if options.IgnoreErrorIfPathMissing && os.IsNotExist(err) {
+ continue // skip this path
+ }
+ if !options.IgnoreErrorIfPathMissing && os.IsNotExist(err) {
+ return err // treat missing path as error
+ }
+ mutHooks, valHooks, err := readWebhooks(path)
+ if err != nil {
+ return err
+ }
+ options.MutatingWebhooks = append(options.MutatingWebhooks, mutHooks...)
+ options.ValidatingWebhooks = append(options.ValidatingWebhooks, valHooks...)
+ }
+ }
+ return nil
+}
+
+// readWebhooks reads the Webhooks from files and Unmarshals them into structs
+// returns slice of mutating and validating webhook configurations.
+func readWebhooks(path string) ([]admissionv1.MutatingWebhookConfiguration, []admissionv1.ValidatingWebhookConfiguration, error) {
+ // Get the webhook files
+ var files []os.FileInfo
+ var err error
+ log.V(1).Info("reading Webhooks from path", "path", path)
+ info, err := os.Stat(path)
+ if err != nil {
+ return nil, nil, err
+ }
+ if !info.IsDir() {
+ path, files = filepath.Dir(path), []os.FileInfo{info}
+ } else if files, err = ioutil.ReadDir(path); err != nil {
+ return nil, nil, err
+ }
+
+ // file extensions that may contain Webhooks
+ resourceExtensions := sets.NewString(".json", ".yaml", ".yml")
+
+ var mutHooks []admissionv1.MutatingWebhookConfiguration
+ var valHooks []admissionv1.ValidatingWebhookConfiguration
+ for _, file := range files {
+ // Only parse allowlisted file types
+ if !resourceExtensions.Has(filepath.Ext(file.Name())) {
+ continue
+ }
+
+ // Unmarshal Webhooks from file into structs
+ docs, err := readDocuments(filepath.Join(path, file.Name()))
+ if err != nil {
+ return nil, nil, err
+ }
+
+ for _, doc := range docs {
+ var generic metav1.PartialObjectMetadata
+ if err = yaml.Unmarshal(doc, &generic); err != nil {
+ return nil, nil, err
+ }
+
+ const (
+ admissionregv1 = "admissionregistration.k8s.io/v1"
+ )
+ switch {
+ case generic.Kind == "MutatingWebhookConfiguration":
+ if generic.APIVersion != admissionregv1 {
+ return nil, nil, fmt.Errorf("only v1 is supported right now for MutatingWebhookConfiguration (name: %s)", generic.Name)
+ }
+ hook := admissionv1.MutatingWebhookConfiguration{}
+ if err := yaml.Unmarshal(doc, &hook); err != nil {
+ return nil, nil, err
+ }
+ mutHooks = append(mutHooks, hook)
+ case generic.Kind == "ValidatingWebhookConfiguration":
+ if generic.APIVersion != admissionregv1 {
+ return nil, nil, fmt.Errorf("only v1 is supported right now for ValidatingWebhookConfiguration (name: %s)", generic.Name)
+ }
+ hook := admissionv1.ValidatingWebhookConfiguration{}
+ if err := yaml.Unmarshal(doc, &hook); err != nil {
+ return nil, nil, err
+ }
+ valHooks = append(valHooks, hook)
+ default:
+ continue
+ }
+ }
+
+ log.V(1).Info("read webhooks from file", "file", file.Name())
+ }
+ return mutHooks, valHooks, nil
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/flock/doc.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/flock/doc.go
new file mode 100644
index 000000000..11e39823e
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/flock/doc.go
@@ -0,0 +1,21 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package flock is copied from k8s.io/kubernetes/pkg/util/flock to avoid
+// importing k8s.io/kubernetes as a dependency.
+//
+// Provides file locking functionalities on unix systems.
+package flock
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/flock/errors.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/flock/errors.go
new file mode 100644
index 000000000..ee7a43437
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/flock/errors.go
@@ -0,0 +1,24 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package flock
+
+import "errors"
+
+var (
+ // ErrAlreadyLocked is returned when the file is already locked.
+ ErrAlreadyLocked = errors.New("the file is already locked")
+)
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/flock/flock_other.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/flock/flock_other.go
new file mode 100644
index 000000000..069a5b3a2
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/flock/flock_other.go
@@ -0,0 +1,24 @@
+// +build !linux,!darwin,!freebsd,!openbsd,!netbsd,!dragonfly
+
+/*
+Copyright 2016 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package flock
+
+// Acquire is not implemented on non-unix systems.
+func Acquire(path string) error {
+ return nil
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/flock/flock_unix.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/flock/flock_unix.go
new file mode 100644
index 000000000..3a904f3f5
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/flock/flock_unix.go
@@ -0,0 +1,47 @@
+// +build linux darwin freebsd openbsd netbsd dragonfly
+
+/*
+Copyright 2016 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package flock
+
+import (
+ "errors"
+ "fmt"
+ "os"
+
+ "golang.org/x/sys/unix"
+)
+
+// Acquire acquires a lock on a file for the duration of the process. This method
+// is reentrant.
+func Acquire(path string) error {
+ fd, err := unix.Open(path, unix.O_CREAT|unix.O_RDWR|unix.O_CLOEXEC, 0600)
+ if err != nil {
+ if errors.Is(err, os.ErrExist) {
+ return fmt.Errorf("cannot lock file %q: %w", path, ErrAlreadyLocked)
+ }
+ return err
+ }
+
+ // We don't need to close the fd since we should hold
+ // it until the process exits.
+ err = unix.Flock(fd, unix.LOCK_NB|unix.LOCK_EX)
+ if errors.Is(err, unix.EWOULDBLOCK) { // This condition requires LOCK_NB.
+ return fmt.Errorf("cannot lock file %q: %w", path, ErrAlreadyLocked)
+ }
+ return err
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/addr/manager.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/addr/manager.go
new file mode 100644
index 000000000..caaafa262
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/addr/manager.go
@@ -0,0 +1,126 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package addr
+
+import (
+ "errors"
+ "fmt"
+ "io/fs"
+ "net"
+ "os"
+ "path/filepath"
+ "strings"
+ "time"
+
+ "sigs.k8s.io/controller-runtime/pkg/internal/flock"
+)
+
+// TODO(directxman12): interface / release functionality for external port managers
+
+const (
+ portReserveTime = 2 * time.Minute
+ portConflictRetry = 100
+ portFilePrefix = "port-"
+)
+
+var (
+ cacheDir string
+)
+
+func init() {
+ baseDir, err := os.UserCacheDir()
+ if err != nil {
+ baseDir = os.TempDir()
+ }
+ cacheDir = filepath.Join(baseDir, "kubebuilder-envtest")
+ if err := os.MkdirAll(cacheDir, 0750); err != nil {
+ panic(err)
+ }
+}
+
+type portCache struct{}
+
+func (c *portCache) add(port int) (bool, error) {
+ // Remove outdated ports.
+ if err := fs.WalkDir(os.DirFS(cacheDir), ".", func(path string, d fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+ if d.IsDir() || !d.Type().IsRegular() || !strings.HasPrefix(path, portFilePrefix) {
+ return nil
+ }
+ info, err := d.Info()
+ if err != nil {
+ return err
+ }
+ if time.Since(info.ModTime()) > portReserveTime {
+ if err := os.Remove(filepath.Join(cacheDir, path)); err != nil {
+ return err
+ }
+ }
+ return nil
+ }); err != nil {
+ return false, err
+ }
+ // Try allocating new port, by acquiring a file.
+ path := fmt.Sprintf("%s/%s%d", cacheDir, portFilePrefix, port)
+ if err := flock.Acquire(path); errors.Is(err, flock.ErrAlreadyLocked) {
+ return false, nil
+ } else if err != nil {
+ return false, err
+ }
+ return true, nil
+}
+
+var cache = &portCache{}
+
+func suggest(listenHost string) (*net.TCPListener, int, string, error) {
+ if listenHost == "" {
+ listenHost = "localhost"
+ }
+ addr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(listenHost, "0"))
+ if err != nil {
+ return nil, -1, "", err
+ }
+ l, err := net.ListenTCP("tcp", addr)
+ if err != nil {
+ return nil, -1, "", err
+ }
+ return l, l.Addr().(*net.TCPAddr).Port,
+ addr.IP.String(),
+ nil
+}
+
+// Suggest suggests an address a process can listen on. It returns
+// a tuple consisting of a free port and the hostname resolved to its IP.
+// It makes sure that new port allocated does not conflict with old ports
+// allocated within 1 minute.
+func Suggest(listenHost string) (int, string, error) {
+ for i := 0; i < portConflictRetry; i++ {
+ listener, port, resolvedHost, err := suggest(listenHost)
+ if err != nil {
+ return -1, "", err
+ }
+ defer listener.Close()
+ if ok, err := cache.add(port); ok {
+ return port, resolvedHost, nil
+ } else if err != nil {
+ return -1, "", err
+ }
+ }
+ return -1, "", fmt.Errorf("no free ports found after %d retries", portConflictRetry)
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/certs/tinyca.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/certs/tinyca.go
new file mode 100644
index 000000000..ed3c2da65
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/certs/tinyca.go
@@ -0,0 +1,223 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package certs
+
+// NB(directxman12): nothing has verified that this has good settings. In fact,
+// the setting generated here are probably terrible, but they're fine for integration
+// tests. These ABSOLUTELY SHOULD NOT ever be exposed in the public API. They're
+// ONLY for use with envtest's ability to configure webhook testing.
+// If I didn't otherwise not want to add a dependency on cfssl, I'd just use that.
+
+import (
+ "crypto"
+ crand "crypto/rand"
+ "crypto/rsa"
+ "crypto/x509"
+ "crypto/x509/pkix"
+ "encoding/pem"
+ "fmt"
+ "math/big"
+ "net"
+ "time"
+
+ certutil "k8s.io/client-go/util/cert"
+)
+
+var (
+ rsaKeySize = 2048 // a decent number, as of 2019
+ bigOne = big.NewInt(1)
+)
+
+// CertPair is a private key and certificate for use for client auth, as a CA, or serving.
+type CertPair struct {
+ Key crypto.Signer
+ Cert *x509.Certificate
+}
+
+// CertBytes returns the PEM-encoded version of the certificate for this pair.
+func (k CertPair) CertBytes() []byte {
+ return pem.EncodeToMemory(&pem.Block{
+ Type: "CERTIFICATE",
+ Bytes: k.Cert.Raw,
+ })
+}
+
+// AsBytes encodes keypair in the appropriate formats for on-disk storage (PEM and
+// PKCS8, respectively).
+func (k CertPair) AsBytes() (cert []byte, key []byte, err error) {
+ cert = k.CertBytes()
+
+ rawKeyData, err := x509.MarshalPKCS8PrivateKey(k.Key)
+ if err != nil {
+ return nil, nil, fmt.Errorf("unable to encode private key: %v", err)
+ }
+
+ key = pem.EncodeToMemory(&pem.Block{
+ Type: "PRIVATE KEY",
+ Bytes: rawKeyData,
+ })
+
+ return cert, key, nil
+}
+
+// TinyCA supports signing serving certs and client-certs,
+// and can be used as an auth mechanism with envtest.
+type TinyCA struct {
+ CA CertPair
+ orgName string
+
+ nextSerial *big.Int
+}
+
+// newPrivateKey generates a new private key of a relatively sane size (see
+// rsaKeySize).
+func newPrivateKey() (crypto.Signer, error) {
+ return rsa.GenerateKey(crand.Reader, rsaKeySize)
+}
+
+// NewTinyCA creates a new a tiny CA utility for provisioning serving certs and client certs FOR TESTING ONLY.
+// Don't use this for anything else!
+func NewTinyCA() (*TinyCA, error) {
+ caPrivateKey, err := newPrivateKey()
+ if err != nil {
+ return nil, fmt.Errorf("unable to generate private key for CA: %v", err)
+ }
+ caCfg := certutil.Config{CommonName: "envtest-environment", Organization: []string{"envtest"}}
+ caCert, err := certutil.NewSelfSignedCACert(caCfg, caPrivateKey)
+ if err != nil {
+ return nil, fmt.Errorf("unable to generate certificate for CA: %v", err)
+ }
+
+ return &TinyCA{
+ CA: CertPair{Key: caPrivateKey, Cert: caCert},
+ orgName: "envtest",
+ nextSerial: big.NewInt(1),
+ }, nil
+}
+
+func (c *TinyCA) makeCert(cfg certutil.Config) (CertPair, error) {
+ now := time.Now()
+
+ key, err := newPrivateKey()
+ if err != nil {
+ return CertPair{}, fmt.Errorf("unable to create private key: %v", err)
+ }
+
+ serial := new(big.Int).Set(c.nextSerial)
+ c.nextSerial.Add(c.nextSerial, bigOne)
+
+ template := x509.Certificate{
+ Subject: pkix.Name{CommonName: cfg.CommonName, Organization: cfg.Organization},
+ DNSNames: cfg.AltNames.DNSNames,
+ IPAddresses: cfg.AltNames.IPs,
+ SerialNumber: serial,
+
+ KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
+ ExtKeyUsage: cfg.Usages,
+
+ // technically not necessary for testing, but let's set anyway just in case.
+ NotBefore: now.UTC(),
+ // 1 week -- the default for cfssl, and just long enough for a
+ // long-term test, but not too long that anyone would try to use this
+ // seriously.
+ NotAfter: now.Add(168 * time.Hour).UTC(),
+ }
+
+ certRaw, err := x509.CreateCertificate(crand.Reader, &template, c.CA.Cert, key.Public(), c.CA.Key)
+ if err != nil {
+ return CertPair{}, fmt.Errorf("unable to create certificate: %v", err)
+ }
+
+ cert, err := x509.ParseCertificate(certRaw)
+ if err != nil {
+ return CertPair{}, fmt.Errorf("generated invalid certificate, could not parse: %v", err)
+ }
+
+ return CertPair{
+ Key: key,
+ Cert: cert,
+ }, nil
+}
+
+// NewServingCert returns a new CertPair for a serving HTTPS on localhost (or other specified names).
+func (c *TinyCA) NewServingCert(names ...string) (CertPair, error) {
+ if len(names) == 0 {
+ names = []string{"localhost"}
+ }
+ dnsNames, ips, err := resolveNames(names)
+ if err != nil {
+ return CertPair{}, err
+ }
+
+ return c.makeCert(certutil.Config{
+ CommonName: "localhost",
+ Organization: []string{c.orgName},
+ AltNames: certutil.AltNames{
+ DNSNames: dnsNames,
+ IPs: ips,
+ },
+ Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
+ })
+}
+
+// ClientInfo describes some Kubernetes user for the purposes of creating
+// client certificates.
+type ClientInfo struct {
+ // Name is the user name (embedded as the cert's CommonName)
+ Name string
+ // Groups are the groups to which this user belongs (embedded as the cert's
+ // Organization)
+ Groups []string
+}
+
+// NewClientCert produces a new CertPair suitable for use with Kubernetes
+// client cert auth with an API server validating based on this CA.
+func (c *TinyCA) NewClientCert(user ClientInfo) (CertPair, error) {
+ return c.makeCert(certutil.Config{
+ CommonName: user.Name,
+ Organization: user.Groups,
+ Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
+ })
+}
+
+func resolveNames(names []string) ([]string, []net.IP, error) {
+ dnsNames := []string{}
+ ips := []net.IP{}
+ for _, name := range names {
+ if name == "" {
+ continue
+ }
+ ip := net.ParseIP(name)
+ if ip == nil {
+ dnsNames = append(dnsNames, name)
+ // Also resolve to IPs.
+ nameIPs, err := net.LookupHost(name)
+ if err != nil {
+ return nil, nil, err
+ }
+ for _, nameIP := range nameIPs {
+ ip = net.ParseIP(nameIP)
+ if ip != nil {
+ ips = append(ips, ip)
+ }
+ }
+ } else {
+ ips = append(ips, ip)
+ }
+ }
+ return dnsNames, ips, nil
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane/apiserver.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane/apiserver.go
new file mode 100644
index 000000000..ece5efd65
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane/apiserver.go
@@ -0,0 +1,469 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package controlplane
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/url"
+ "os"
+ "path/filepath"
+ "strconv"
+ "time"
+
+ "sigs.k8s.io/controller-runtime/pkg/internal/testing/addr"
+ "sigs.k8s.io/controller-runtime/pkg/internal/testing/certs"
+ "sigs.k8s.io/controller-runtime/pkg/internal/testing/process"
+)
+
+const (
+ // saKeyFile is the name of the service account signing private key file.
+ saKeyFile = "sa-signer.key"
+ // saKeyFile is the name of the service account signing public key (cert) file.
+ saCertFile = "sa-signer.crt"
+)
+
+// SecureServing provides/configures how the API server serves on the secure port.
+type SecureServing struct {
+ // ListenAddr contains the host & port to serve on.
+ //
+ // Configurable. If unset, it will be defaulted.
+ process.ListenAddr
+ // CA contains the CA that signed the API server's serving certificates.
+ //
+ // Read-only.
+ CA []byte
+ // Authn can be used to provision users, and override what type of
+ // authentication is used to provision users.
+ //
+ // Configurable. If unset, it will be defaulted.
+ Authn
+}
+
+// APIServer knows how to run a kubernetes apiserver.
+type APIServer struct {
+ // URL is the address the ApiServer should listen on for client
+ // connections.
+ //
+ // If set, this will configure the *insecure* serving details.
+ // If unset, it will contain the insecure port if insecure serving is enabled,
+ // and otherwise will contain the secure port.
+ //
+ // If this is not specified, we default to a random free port on localhost.
+ //
+ // Deprecated: use InsecureServing (for the insecure URL) or SecureServing, ideally.
+ URL *url.URL
+
+ // SecurePort is the additional secure port that the APIServer should listen on.
+ //
+ // If set, this will override SecureServing.Port.
+ //
+ // Deprecated: use SecureServing.
+ SecurePort int
+
+ // SecureServing indicates how the API server will serve on the secure port.
+ //
+ // Some parts are configurable. Will be defaulted if unset.
+ SecureServing
+
+ // InsecureServing indicates how the API server will serve on the insecure port.
+ //
+ // If unset, the insecure port will be disabled. Set to an empty struct to get
+ // default values.
+ //
+ // Deprecated: does not work with Kubernetes versions 1.20 and above. Use secure
+ // serving instead.
+ InsecureServing *process.ListenAddr
+
+ // Path is the path to the apiserver binary.
+ //
+ // If this is left as the empty string, we will attempt to locate a binary,
+ // by checking for the TEST_ASSET_KUBE_APISERVER environment variable, and
+ // the default test assets directory. See the "Binaries" section above (in
+ // doc.go) for details.
+ Path string
+
+ // Args is a list of arguments which will passed to the APIServer binary.
+ // Before they are passed on, they will be evaluated as go-template strings.
+ // This means you can use fields which are defined and exported on this
+ // APIServer struct (e.g. "--cert-dir={{ .Dir }}").
+ // Those templates will be evaluated after the defaulting of the APIServer's
+ // fields has already happened and just before the binary actually gets
+ // started. Thus you have access to calculated fields like `URL` and others.
+ //
+ // If not specified, the minimal set of arguments to run the APIServer will
+ // be used.
+ //
+ // They will be loaded into the same argument set as Configure. Each flag
+ // will be Append-ed to the configured arguments just before launch.
+ //
+ // Deprecated: use Configure instead.
+ Args []string
+
+ // CertDir is a path to a directory containing whatever certificates the
+ // APIServer will need.
+ //
+ // If left unspecified, then the Start() method will create a fresh temporary
+ // directory, and the Stop() method will clean it up.
+ CertDir string
+
+ // EtcdURL is the URL of the Etcd the APIServer should use.
+ //
+ // If this is not specified, the Start() method will return an error.
+ EtcdURL *url.URL
+
+ // StartTimeout, StopTimeout specify the time the APIServer is allowed to
+ // take when starting and stoppping before an error is emitted.
+ //
+ // If not specified, these default to 20 seconds.
+ StartTimeout time.Duration
+ StopTimeout time.Duration
+
+ // Out, Err specify where APIServer should write its StdOut, StdErr to.
+ //
+ // If not specified, the output will be discarded.
+ Out io.Writer
+ Err io.Writer
+
+ processState *process.State
+
+ // args contains the structured arguments to use for running the API server
+ // Lazily initialized by .Configure(), Defaulted eventually with .defaultArgs()
+ args *process.Arguments
+}
+
+// Configure returns Arguments that may be used to customize the
+// flags used to launch the API server. A set of defaults will
+// be applied underneath.
+func (s *APIServer) Configure() *process.Arguments {
+ if s.args == nil {
+ s.args = process.EmptyArguments()
+ }
+ return s.args
+}
+
+// Start starts the apiserver, waits for it to come up, and returns an error,
+// if occurred.
+func (s *APIServer) Start() error {
+ if err := s.prepare(); err != nil {
+ return err
+ }
+ return s.processState.Start(s.Out, s.Err)
+}
+
+func (s *APIServer) prepare() error {
+ if err := s.setProcessState(); err != nil {
+ return err
+ }
+ return s.Authn.Start()
+}
+
+// configurePorts configures the serving ports for this API server.
+//
+// Most of this method currently deals with making the deprecated fields
+// take precedence over the new fields.
+func (s *APIServer) configurePorts() error {
+ // prefer the old fields to the new fields if a user set one,
+ // otherwise, default the new fields and populate the old ones.
+
+ // Insecure: URL, InsecureServing
+ if s.URL != nil {
+ s.InsecureServing = &process.ListenAddr{
+ Address: s.URL.Hostname(),
+ Port: s.URL.Port(),
+ }
+ } else if insec := s.InsecureServing; insec != nil {
+ if insec.Port == "" || insec.Address == "" {
+ port, host, err := addr.Suggest("")
+ if err != nil {
+ return fmt.Errorf("unable to provision unused insecure port: %w", err)
+ }
+ s.InsecureServing.Port = strconv.Itoa(port)
+ s.InsecureServing.Address = host
+ }
+ s.URL = s.InsecureServing.URL("http", "")
+ }
+
+ // Secure: SecurePort, SecureServing
+ if s.SecurePort != 0 {
+ s.SecureServing.Port = strconv.Itoa(s.SecurePort)
+ // if we don't have an address, try the insecure address, and otherwise
+ // default to loopback.
+ if s.SecureServing.Address == "" {
+ if s.InsecureServing != nil {
+ s.SecureServing.Address = s.InsecureServing.Address
+ } else {
+ s.SecureServing.Address = "127.0.0.1"
+ }
+ }
+ } else if s.SecureServing.Port == "" || s.SecureServing.Address == "" {
+ port, host, err := addr.Suggest("")
+ if err != nil {
+ return fmt.Errorf("unable to provision unused secure port: %w", err)
+ }
+ s.SecureServing.Port = strconv.Itoa(port)
+ s.SecureServing.Address = host
+ s.SecurePort = port
+ }
+
+ return nil
+}
+
+func (s *APIServer) setProcessState() error {
+ if s.EtcdURL == nil {
+ return fmt.Errorf("expected EtcdURL to be configured")
+ }
+
+ var err error
+
+ // unconditionally re-set this so we can successfully restart
+ // TODO(directxman12): we supported this in the past, but do we actually
+ // want to support re-using an API server object to restart? The loss
+ // of provisioned users is surprising to say the least.
+ s.processState = &process.State{
+ Dir: s.CertDir,
+ Path: s.Path,
+ StartTimeout: s.StartTimeout,
+ StopTimeout: s.StopTimeout,
+ }
+ if err := s.processState.Init("kube-apiserver"); err != nil {
+ return err
+ }
+
+ if err := s.configurePorts(); err != nil {
+ return err
+ }
+
+ // the secure port will always be on, so use that
+ s.processState.HealthCheck.URL = *s.SecureServing.URL("https", "/healthz")
+
+ s.CertDir = s.processState.Dir
+ s.Path = s.processState.Path
+ s.StartTimeout = s.processState.StartTimeout
+ s.StopTimeout = s.processState.StopTimeout
+
+ if err := s.populateAPIServerCerts(); err != nil {
+ return err
+ }
+
+ if s.SecureServing.Authn == nil {
+ authn, err := NewCertAuthn()
+ if err != nil {
+ return err
+ }
+ s.SecureServing.Authn = authn
+ }
+
+ if err := s.Authn.Configure(s.CertDir, s.Configure()); err != nil {
+ return err
+ }
+
+ // NB(directxman12): insecure port is a mess:
+ // - 1.19 and below have the `--insecure-port` flag, and require it to be set to zero to
+ // disable it, otherwise the default will be used and we'll conflict.
+ // - 1.20 requires the flag to be unset or set to zero, and yells at you if you configure it
+ // - 1.24 won't have the flag at all...
+ //
+ // In an effort to automatically do the right thing during this mess, we do feature discovery
+ // on the flags, and hope that we've "parsed" them properly.
+ //
+ // TODO(directxman12): once we support 1.20 as the min version (might be when 1.24 comes out,
+ // might be around 1.25 or 1.26), remove this logic and the corresponding line in API server's
+ // default args.
+ if err := s.discoverFlags(); err != nil {
+ return err
+ }
+
+ s.processState.Args, s.Args, err = process.TemplateAndArguments(s.Args, s.Configure(), process.TemplateDefaults{ //nolint:staticcheck
+ Data: s,
+ Defaults: s.defaultArgs(),
+ MinimalDefaults: map[string][]string{
+ // as per kubernetes-sigs/controller-runtime#641, we need this (we
+ // probably need other stuff too, but this is the only thing that was
+ // previously considered a "minimal default")
+ "service-cluster-ip-range": {"10.0.0.0/24"},
+
+ // we need *some* authorization mode for health checks on the secure port,
+ // so default to RBAC unless the user set something else (in which case
+ // this'll be ignored due to SliceToArguments using AppendNoDefaults).
+ "authorization-mode": {"RBAC"},
+ },
+ })
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// discoverFlags checks for certain flags that *must* be set in certain
+// versions, and *must not* be set in others.
+func (s *APIServer) discoverFlags() error {
+ // Present: <1.24, Absent: >= 1.24
+ present, err := s.processState.CheckFlag("insecure-port")
+ if err != nil {
+ return err
+ }
+
+ if !present {
+ s.Configure().Disable("insecure-port")
+ }
+
+ return nil
+}
+
+func (s *APIServer) defaultArgs() map[string][]string {
+ args := map[string][]string{
+ "service-cluster-ip-range": {"10.0.0.0/24"},
+ "allow-privileged": {"true"},
+ // we're keeping this disabled because if enabled, default SA is
+ // missing which would force all tests to create one in normal
+ // apiserver operation this SA is created by controller, but that is
+ // not run in integration environment
+ "disable-admission-plugins": {"ServiceAccount"},
+ "cert-dir": {s.CertDir},
+ "authorization-mode": {"RBAC"},
+ "secure-port": {s.SecureServing.Port},
+ // NB(directxman12): previously we didn't set the bind address for the secure
+ // port. It *shouldn't* make a difference unless people are doing something really
+ // funky, but if you start to get bug reports look here ;-)
+ "bind-address": {s.SecureServing.Address},
+
+ // required on 1.20+, fine to leave on for <1.20
+ "service-account-issuer": {s.SecureServing.URL("https", "/").String()},
+ "service-account-key-file": {filepath.Join(s.CertDir, saCertFile)},
+ "service-account-signing-key-file": {filepath.Join(s.CertDir, saKeyFile)},
+ }
+ if s.EtcdURL != nil {
+ args["etcd-servers"] = []string{s.EtcdURL.String()}
+ }
+ if s.URL != nil {
+ args["insecure-port"] = []string{s.URL.Port()}
+ args["insecure-bind-address"] = []string{s.URL.Hostname()}
+ } else {
+ // TODO(directxman12): remove this once 1.21 is the lowest version we support
+ // (this might be a while, but this line'll break as of 1.24, so see the comment
+ // in Start
+ args["insecure-port"] = []string{"0"}
+ }
+ return args
+}
+
+func (s *APIServer) populateAPIServerCerts() error {
+ _, statErr := os.Stat(filepath.Join(s.CertDir, "apiserver.crt"))
+ if !os.IsNotExist(statErr) {
+ return statErr
+ }
+
+ ca, err := certs.NewTinyCA()
+ if err != nil {
+ return err
+ }
+
+ servingCerts, err := ca.NewServingCert()
+ if err != nil {
+ return err
+ }
+
+ certData, keyData, err := servingCerts.AsBytes()
+ if err != nil {
+ return err
+ }
+
+ if err := ioutil.WriteFile(filepath.Join(s.CertDir, "apiserver.crt"), certData, 0640); err != nil { //nolint:gosec
+ return err
+ }
+ if err := ioutil.WriteFile(filepath.Join(s.CertDir, "apiserver.key"), keyData, 0640); err != nil { //nolint:gosec
+ return err
+ }
+
+ s.SecureServing.CA = ca.CA.CertBytes()
+
+ // service account signing files too
+ saCA, err := certs.NewTinyCA()
+ if err != nil {
+ return err
+ }
+
+ saCert, saKey, err := saCA.CA.AsBytes()
+ if err != nil {
+ return err
+ }
+
+ if err := ioutil.WriteFile(filepath.Join(s.CertDir, saCertFile), saCert, 0640); err != nil { //nolint:gosec
+ return err
+ }
+ return ioutil.WriteFile(filepath.Join(s.CertDir, saKeyFile), saKey, 0640) //nolint:gosec
+}
+
+// Stop stops this process gracefully, waits for its termination, and cleans up
+// the CertDir if necessary.
+func (s *APIServer) Stop() error {
+ if s.processState.DirNeedsCleaning {
+ s.CertDir = "" // reset the directory if it was randomly allocated, so that we can safely restart
+ }
+ if s.processState != nil {
+ if err := s.processState.Stop(); err != nil {
+ return err
+ }
+ }
+ return s.Authn.Stop()
+}
+
+// APIServerDefaultArgs exposes the default args for the APIServer so that you
+// can use those to append your own additional arguments.
+//
+// Note that these arguments don't handle newer API servers well to due the more
+// complex feature detection neeeded. It's recommended that you switch to .Configure
+// as you upgrade API server versions.
+//
+// Deprecated: use APIServer.Configure().
+var APIServerDefaultArgs = []string{
+ "--advertise-address=127.0.0.1",
+ "--etcd-servers={{ if .EtcdURL }}{{ .EtcdURL.String }}{{ end }}",
+ "--cert-dir={{ .CertDir }}",
+ "--insecure-port={{ if .URL }}{{ .URL.Port }}{{else}}0{{ end }}",
+ "{{ if .URL }}--insecure-bind-address={{ .URL.Hostname }}{{ end }}",
+ "--secure-port={{ if .SecurePort }}{{ .SecurePort }}{{ end }}",
+ // we're keeping this disabled because if enabled, default SA is missing which would force all tests to create one
+ // in normal apiserver operation this SA is created by controller, but that is not run in integration environment
+ "--disable-admission-plugins=ServiceAccount",
+ "--service-cluster-ip-range=10.0.0.0/24",
+ "--allow-privileged=true",
+ // NB(directxman12): we also enable RBAC if nothing else was enabled
+}
+
+// PrepareAPIServer is an internal-only (NEVER SHOULD BE EXPOSED)
+// function that sets up the API server just before starting it,
+// without actually starting it. This saves time on tests.
+//
+// NB(directxman12): do not expose this outside of internal -- it's unsafe to
+// use, because things like port allocation could race even more than they
+// currently do if you later call start!
+func PrepareAPIServer(s *APIServer) error {
+ return s.prepare()
+}
+
+// APIServerArguments is an internal-only (NEVER SHOULD BE EXPOSED)
+// function that sets up the API server just before starting it,
+// without actually starting it. It's public to make testing easier.
+//
+// NB(directxman12): do not expose this outside of internal.
+func APIServerArguments(s *APIServer) []string {
+ return s.processState.Args
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane/auth.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane/auth.go
new file mode 100644
index 000000000..b2cd4e5e0
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane/auth.go
@@ -0,0 +1,142 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package controlplane
+
+import (
+ "fmt"
+ "io/ioutil"
+ "path/filepath"
+
+ "k8s.io/client-go/rest"
+ "sigs.k8s.io/controller-runtime/pkg/internal/testing/certs"
+ "sigs.k8s.io/controller-runtime/pkg/internal/testing/process"
+)
+
+// User represents a Kubernetes user.
+type User struct {
+ // Name is the user's Name.
+ Name string
+ // Groups are the groups to which the user belongs.
+ Groups []string
+}
+
+// Authn knows how to configure an API server for a particular type of authentication,
+// and provision users under that authentication scheme.
+//
+// The methods must be called in the following order (as presented below in the interface
+// for a mnemonic):
+//
+// 1. Configure
+// 2. Start
+// 3. AddUsers (0+ calls)
+// 4. Stop.
+type Authn interface {
+ // Configure provides the working directory to this authenticator,
+ // and configures the given API server arguments to make use of this authenticator.
+ //
+ // Should be called first.
+ Configure(workDir string, args *process.Arguments) error
+ // Start runs this authenticator. Will be called just before API server start.
+ //
+ // Must be called after Configure.
+ Start() error
+ // AddUser provisions a user, returning a copy of the given base rest.Config
+ // configured to authenticate as that users.
+ //
+ // May only be called while the authenticator is "running".
+ AddUser(user User, baseCfg *rest.Config) (*rest.Config, error)
+ // Stop shuts down this authenticator.
+ Stop() error
+}
+
+// CertAuthn is an authenticator (Authn) that makes use of client certificate authn.
+type CertAuthn struct {
+ // ca is the CA used to sign the client certs
+ ca *certs.TinyCA
+ // certDir is the directory used to write the CA crt file
+ // so that the API server can read it.
+ certDir string
+}
+
+// NewCertAuthn creates a new client-cert-based Authn with a new CA.
+func NewCertAuthn() (*CertAuthn, error) {
+ ca, err := certs.NewTinyCA()
+ if err != nil {
+ return nil, fmt.Errorf("unable to provision client certificate auth CA: %w", err)
+ }
+ return &CertAuthn{
+ ca: ca,
+ }, nil
+}
+
+// AddUser provisions a new user that's authenticated via certificates, with
+// the given uesrname and groups embedded in the certificate as expected by the
+// API server.
+func (c *CertAuthn) AddUser(user User, baseCfg *rest.Config) (*rest.Config, error) {
+ certs, err := c.ca.NewClientCert(certs.ClientInfo{
+ Name: user.Name,
+ Groups: user.Groups,
+ })
+ if err != nil {
+ return nil, fmt.Errorf("unable to create client certificates for %s: %w", user.Name, err)
+ }
+
+ crt, key, err := certs.AsBytes()
+ if err != nil {
+ return nil, fmt.Errorf("unable to serialize client certificates for %s: %w", user.Name, err)
+ }
+
+ cfg := rest.CopyConfig(baseCfg)
+ cfg.CertData = crt
+ cfg.KeyData = key
+
+ return cfg, nil
+}
+
+// caCrtPath returns the path to the on-disk client-cert CA crt file.
+func (c *CertAuthn) caCrtPath() string {
+ return filepath.Join(c.certDir, "client-cert-auth-ca.crt")
+}
+
+// Configure provides the working directory to this authenticator,
+// and configures the given API server arguments to make use of this authenticator.
+func (c *CertAuthn) Configure(workDir string, args *process.Arguments) error {
+ c.certDir = workDir
+ args.Set("client-ca-file", c.caCrtPath())
+ return nil
+}
+
+// Start runs this authenticator. Will be called just before API server start.
+//
+// Must be called after Configure.
+func (c *CertAuthn) Start() error {
+ if len(c.certDir) == 0 {
+ return fmt.Errorf("start called before configure")
+ }
+ caCrt := c.ca.CA.CertBytes()
+ if err := ioutil.WriteFile(c.caCrtPath(), caCrt, 0640); err != nil { //nolint:gosec
+ return fmt.Errorf("unable to save the client certificate CA to %s: %w", c.caCrtPath(), err)
+ }
+
+ return nil
+}
+
+// Stop shuts down this authenticator.
+func (c *CertAuthn) Stop() error {
+ // no-op -- our workdir is cleaned up for us automatically
+ return nil
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane/etcd.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane/etcd.go
new file mode 100644
index 000000000..c6d625173
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane/etcd.go
@@ -0,0 +1,202 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package controlplane
+
+import (
+ "io"
+ "net"
+ "net/url"
+ "strconv"
+ "time"
+
+ "sigs.k8s.io/controller-runtime/pkg/internal/testing/addr"
+ "sigs.k8s.io/controller-runtime/pkg/internal/testing/process"
+)
+
+// Etcd knows how to run an etcd server.
+type Etcd struct {
+ // URL is the address the Etcd should listen on for client connections.
+ //
+ // If this is not specified, we default to a random free port on localhost.
+ URL *url.URL
+
+ // Path is the path to the etcd binary.
+ //
+ // If this is left as the empty string, we will attempt to locate a binary,
+ // by checking for the TEST_ASSET_ETCD environment variable, and the default
+ // test assets directory. See the "Binaries" section above (in doc.go) for
+ // details.
+ Path string
+
+ // Args is a list of arguments which will passed to the Etcd binary. Before
+ // they are passed on, the`y will be evaluated as go-template strings. This
+ // means you can use fields which are defined and exported on this Etcd
+ // struct (e.g. "--data-dir={{ .Dir }}").
+ // Those templates will be evaluated after the defaulting of the Etcd's
+ // fields has already happened and just before the binary actually gets
+ // started. Thus you have access to calculated fields like `URL` and others.
+ //
+ // If not specified, the minimal set of arguments to run the Etcd will be
+ // used.
+ //
+ // They will be loaded into the same argument set as Configure. Each flag
+ // will be Append-ed to the configured arguments just before launch.
+ //
+ // Deprecated: use Configure instead.
+ Args []string
+
+ // DataDir is a path to a directory in which etcd can store its state.
+ //
+ // If left unspecified, then the Start() method will create a fresh temporary
+ // directory, and the Stop() method will clean it up.
+ DataDir string
+
+ // StartTimeout, StopTimeout specify the time the Etcd is allowed to
+ // take when starting and stopping before an error is emitted.
+ //
+ // If not specified, these default to 20 seconds.
+ StartTimeout time.Duration
+ StopTimeout time.Duration
+
+ // Out, Err specify where Etcd should write its StdOut, StdErr to.
+ //
+ // If not specified, the output will be discarded.
+ Out io.Writer
+ Err io.Writer
+
+ // processState contains the actual details about this running process
+ processState *process.State
+
+ // args contains the structured arguments to use for running etcd.
+ // Lazily initialized by .Configure(), Defaulted eventually with .defaultArgs()
+ args *process.Arguments
+
+ // listenPeerURL is the address the Etcd should listen on for peer connections.
+ // It's automatically generated and a random port is picked during execution.
+ listenPeerURL *url.URL
+}
+
+// Start starts the etcd, waits for it to come up, and returns an error, if one
+// occoured.
+func (e *Etcd) Start() error {
+ if err := e.setProcessState(); err != nil {
+ return err
+ }
+ return e.processState.Start(e.Out, e.Err)
+}
+
+func (e *Etcd) setProcessState() error {
+ e.processState = &process.State{
+ Dir: e.DataDir,
+ Path: e.Path,
+ StartTimeout: e.StartTimeout,
+ StopTimeout: e.StopTimeout,
+ }
+
+ // unconditionally re-set this so we can successfully restart
+ // TODO(directxman12): we supported this in the past, but do we actually
+ // want to support re-using an API server object to restart? The loss
+ // of provisioned users is surprising to say the least.
+ if err := e.processState.Init("etcd"); err != nil {
+ return err
+ }
+
+ // Set the listen url.
+ if e.URL == nil {
+ port, host, err := addr.Suggest("")
+ if err != nil {
+ return err
+ }
+ e.URL = &url.URL{
+ Scheme: "http",
+ Host: net.JoinHostPort(host, strconv.Itoa(port)),
+ }
+ }
+
+ // Set the listen peer URL.
+ {
+ port, host, err := addr.Suggest("")
+ if err != nil {
+ return err
+ }
+ e.listenPeerURL = &url.URL{
+ Scheme: "http",
+ Host: net.JoinHostPort(host, strconv.Itoa(port)),
+ }
+ }
+
+ // can use /health as of etcd 3.3.0
+ e.processState.HealthCheck.URL = *e.URL
+ e.processState.HealthCheck.Path = "/health"
+
+ e.DataDir = e.processState.Dir
+ e.Path = e.processState.Path
+ e.StartTimeout = e.processState.StartTimeout
+ e.StopTimeout = e.processState.StopTimeout
+
+ var err error
+ e.processState.Args, e.Args, err = process.TemplateAndArguments(e.Args, e.Configure(), process.TemplateDefaults{ //nolint:staticcheck
+ Data: e,
+ Defaults: e.defaultArgs(),
+ })
+ return err
+}
+
+// Stop stops this process gracefully, waits for its termination, and cleans up
+// the DataDir if necessary.
+func (e *Etcd) Stop() error {
+ if e.processState.DirNeedsCleaning {
+ e.DataDir = "" // reset the directory if it was randomly allocated, so that we can safely restart
+ }
+ return e.processState.Stop()
+}
+
+func (e *Etcd) defaultArgs() map[string][]string {
+ args := map[string][]string{
+ "listen-peer-urls": {e.listenPeerURL.String()},
+ "data-dir": {e.DataDir},
+ }
+ if e.URL != nil {
+ args["advertise-client-urls"] = []string{e.URL.String()}
+ args["listen-client-urls"] = []string{e.URL.String()}
+ }
+
+ // Add unsafe no fsync, available from etcd 3.5
+ if ok, _ := e.processState.CheckFlag("unsafe-no-fsync"); ok {
+ args["unsafe-no-fsync"] = []string{"true"}
+ }
+ return args
+}
+
+// Configure returns Arguments that may be used to customize the
+// flags used to launch etcd. A set of defaults will
+// be applied underneath.
+func (e *Etcd) Configure() *process.Arguments {
+ if e.args == nil {
+ e.args = process.EmptyArguments()
+ }
+ return e.args
+}
+
+// EtcdDefaultArgs exposes the default args for Etcd so that you
+// can use those to append your own additional arguments.
+var EtcdDefaultArgs = []string{
+ "--listen-peer-urls=http://localhost:0",
+ "--advertise-client-urls={{ if .URL }}{{ .URL.String }}{{ end }}",
+ "--listen-client-urls={{ if .URL }}{{ .URL.String }}{{ end }}",
+ "--data-dir={{ .DataDir }}",
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane/kubectl.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane/kubectl.go
new file mode 100644
index 000000000..a27b7a0ff
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane/kubectl.go
@@ -0,0 +1,119 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package controlplane
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "net/url"
+ "os/exec"
+
+ "k8s.io/client-go/rest"
+ "k8s.io/client-go/tools/clientcmd"
+ kcapi "k8s.io/client-go/tools/clientcmd/api"
+
+ "sigs.k8s.io/controller-runtime/pkg/internal/testing/process"
+)
+
+const (
+ envtestName = "envtest"
+)
+
+// KubeConfigFromREST reverse-engineers a kubeconfig file from a rest.Config.
+// The options are tailored towards the rest.Configs we generate, so they're
+// not broadly applicable.
+//
+// This is not intended to be exposed beyond internal for the above reasons.
+func KubeConfigFromREST(cfg *rest.Config) ([]byte, error) {
+ kubeConfig := kcapi.NewConfig()
+ protocol := "https"
+ if !rest.IsConfigTransportTLS(*cfg) {
+ protocol = "http"
+ }
+
+ // cfg.Host is a URL, so we need to parse it so we can properly append the API path
+ baseURL, err := url.Parse(cfg.Host)
+ if err != nil {
+ return nil, fmt.Errorf("unable to interpret config's host value as a URL: %w", err)
+ }
+
+ kubeConfig.Clusters[envtestName] = &kcapi.Cluster{
+ // TODO(directxman12): if client-go ever decides to expose defaultServerUrlFor(config),
+ // we can just use that. Note that this is not the same as the public DefaultServerURL,
+ // which requires us to pass a bunch of stuff in manually.
+ Server: (&url.URL{Scheme: protocol, Host: baseURL.Host, Path: cfg.APIPath}).String(),
+ CertificateAuthorityData: cfg.CAData,
+ }
+ kubeConfig.AuthInfos[envtestName] = &kcapi.AuthInfo{
+ // try to cover all auth strategies that aren't plugins
+ ClientCertificateData: cfg.CertData,
+ ClientKeyData: cfg.KeyData,
+ Token: cfg.BearerToken,
+ Username: cfg.Username,
+ Password: cfg.Password,
+ }
+ kcCtx := kcapi.NewContext()
+ kcCtx.Cluster = envtestName
+ kcCtx.AuthInfo = envtestName
+ kubeConfig.Contexts[envtestName] = kcCtx
+ kubeConfig.CurrentContext = envtestName
+
+ contents, err := clientcmd.Write(*kubeConfig)
+ if err != nil {
+ return nil, fmt.Errorf("unable to serialize kubeconfig file: %w", err)
+ }
+ return contents, nil
+}
+
+// KubeCtl is a wrapper around the kubectl binary.
+type KubeCtl struct {
+ // Path where the kubectl binary can be found.
+ //
+ // If this is left empty, we will attempt to locate a binary, by checking for
+ // the TEST_ASSET_KUBECTL environment variable, and the default test assets
+ // directory. See the "Binaries" section above (in doc.go) for details.
+ Path string
+
+ // Opts can be used to configure additional flags which will be used each
+ // time the wrapped binary is called.
+ //
+ // For example, you might want to use this to set the URL of the APIServer to
+ // connect to.
+ Opts []string
+}
+
+// Run executes the wrapped binary with some preconfigured options and the
+// arguments given to this method. It returns Readers for the stdout and
+// stderr.
+func (k *KubeCtl) Run(args ...string) (stdout, stderr io.Reader, err error) {
+ if k.Path == "" {
+ k.Path = process.BinPathFinder("kubectl", "")
+ }
+
+ stdoutBuffer := &bytes.Buffer{}
+ stderrBuffer := &bytes.Buffer{}
+ allArgs := append(k.Opts, args...)
+
+ cmd := exec.Command(k.Path, allArgs...)
+ cmd.Stdout = stdoutBuffer
+ cmd.Stderr = stderrBuffer
+
+ err = cmd.Run()
+
+ return stdoutBuffer, stderrBuffer, err
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane/plane.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane/plane.go
new file mode 100644
index 000000000..36fd3c630
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane/plane.go
@@ -0,0 +1,248 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package controlplane
+
+import (
+ "fmt"
+ "net/url"
+ "os"
+
+ kerrors "k8s.io/apimachinery/pkg/util/errors"
+ "k8s.io/client-go/rest"
+ "sigs.k8s.io/controller-runtime/pkg/internal/testing/certs"
+)
+
+// NewTinyCA creates a new a tiny CA utility for provisioning serving certs and client certs FOR TESTING ONLY.
+// Don't use this for anything else!
+var NewTinyCA = certs.NewTinyCA
+
+// ControlPlane is a struct that knows how to start your test control plane.
+//
+// Right now, that means Etcd and your APIServer. This is likely to increase in
+// future.
+type ControlPlane struct {
+ APIServer *APIServer
+ Etcd *Etcd
+
+ // Kubectl will override the default asset search path for kubectl
+ KubectlPath string
+
+ // for the deprecated methods (Kubectl, etc)
+ defaultUserCfg *rest.Config
+ defaultUserKubectl *KubeCtl
+}
+
+// Start will start your control plane processes. To stop them, call Stop().
+func (f *ControlPlane) Start() error {
+ if f.Etcd == nil {
+ f.Etcd = &Etcd{}
+ }
+ if err := f.Etcd.Start(); err != nil {
+ return err
+ }
+
+ if f.APIServer == nil {
+ f.APIServer = &APIServer{}
+ }
+ f.APIServer.EtcdURL = f.Etcd.URL
+ if err := f.APIServer.Start(); err != nil {
+ return err
+ }
+
+ // provision the default user -- can be removed when the related
+ // methods are removed. The default user has admin permissions to
+ // mimic legacy no-authz setups.
+ user, err := f.AddUser(User{Name: "default", Groups: []string{"system:masters"}}, &rest.Config{})
+ if err != nil {
+ return fmt.Errorf("unable to provision the default (legacy) user: %w", err)
+ }
+ kubectl, err := user.Kubectl()
+ if err != nil {
+ return fmt.Errorf("unable to provision the default (legacy) kubeconfig: %w", err)
+ }
+ f.defaultUserCfg = user.Config()
+ f.defaultUserKubectl = kubectl
+ return nil
+}
+
+// Stop will stop your control plane processes, and clean up their data.
+func (f *ControlPlane) Stop() error {
+ var errList []error
+
+ if f.APIServer != nil {
+ if err := f.APIServer.Stop(); err != nil {
+ errList = append(errList, err)
+ }
+ }
+ if f.Etcd != nil {
+ if err := f.Etcd.Stop(); err != nil {
+ errList = append(errList, err)
+ }
+ }
+
+ return kerrors.NewAggregate(errList)
+}
+
+// APIURL returns the URL you should connect to to talk to your API server.
+//
+// If insecure serving is configured, this will contain the insecure port.
+// Otherwise, it will contain the secure port.
+//
+// Deprecated: use AddUser instead, or APIServer.{Ins|S}ecureServing.URL if
+// you really want just the URL.
+func (f *ControlPlane) APIURL() *url.URL {
+ return f.APIServer.URL
+}
+
+// KubeCtl returns a pre-configured KubeCtl, ready to connect to this
+// ControlPlane.
+//
+// Deprecated: use AddUser & AuthenticatedUser.Kubectl instead.
+func (f *ControlPlane) KubeCtl() *KubeCtl {
+ return f.defaultUserKubectl
+}
+
+// RESTClientConfig returns a pre-configured restconfig, ready to connect to
+// this ControlPlane.
+//
+// Deprecated: use AddUser & AuthenticatedUser.Config instead.
+func (f *ControlPlane) RESTClientConfig() (*rest.Config, error) {
+ return f.defaultUserCfg, nil
+}
+
+// AuthenticatedUser contains access information for an provisioned user,
+// including REST config, kubeconfig contents, and access to a KubeCtl instance.
+//
+// It's not "safe" to use the methods on this till after the API server has been
+// started (due to certificate initialization and such). The various methods will
+// panic if this is done.
+type AuthenticatedUser struct {
+ // cfg is the rest.Config for connecting to the API server. It's lazily initialized.
+ cfg *rest.Config
+ // cfgIsComplete indicates the cfg has had late-initialized fields (e.g.
+ // API server CA data) initialized.
+ cfgIsComplete bool
+
+ // apiServer is a handle to the APIServer that's used when finalizing cfg
+ // and producing the kubectl instance.
+ plane *ControlPlane
+
+ // kubectl is our existing, provisioned kubectl. We don't provision one
+ // till someone actually asks for it.
+ kubectl *KubeCtl
+}
+
+// Config returns the REST config that can be used to connect to the API server
+// as this user.
+//
+// Will panic if used before the API server is started.
+func (u *AuthenticatedUser) Config() *rest.Config {
+ // NB(directxman12): we choose to panic here for ergonomics sake, and because there's
+ // not really much you can do to "handle" this error. This machinery is intended to be
+ // used in tests anyway, so panicing is not a particularly big deal.
+ if u.cfgIsComplete {
+ return u.cfg
+ }
+ if len(u.plane.APIServer.SecureServing.CA) == 0 {
+ panic("the API server has not yet been started, please do that before accessing connection details")
+ }
+
+ u.cfg.CAData = u.plane.APIServer.SecureServing.CA
+ u.cfg.Host = u.plane.APIServer.SecureServing.URL("https", "/").String()
+ u.cfgIsComplete = true
+ return u.cfg
+}
+
+// KubeConfig returns a KubeConfig that's roughly equivalent to this user's REST config.
+//
+// Will panic if used before the API server is started.
+func (u AuthenticatedUser) KubeConfig() ([]byte, error) {
+ // NB(directxman12): we don't return the actual API object to avoid yet another
+ // piece of kubernetes API in our public API, and also because generally the thing
+ // you want to do with this is just write it out to a file for external debugging
+ // purposes, etc.
+ return KubeConfigFromREST(u.Config())
+}
+
+// Kubectl returns a KubeCtl instance for talking to the API server as this user. It uses
+// a kubeconfig equivalent to that returned by .KubeConfig.
+//
+// Will panic if used before the API server is started.
+func (u *AuthenticatedUser) Kubectl() (*KubeCtl, error) {
+ if u.kubectl != nil {
+ return u.kubectl, nil
+ }
+ if len(u.plane.APIServer.CertDir) == 0 {
+ panic("the API server has not yet been started, please do that before accessing connection details")
+ }
+
+ // cleaning this up is handled when our tmpDir is deleted
+ out, err := os.CreateTemp(u.plane.APIServer.CertDir, "*.kubecfg")
+ if err != nil {
+ return nil, fmt.Errorf("unable to create file for kubeconfig: %w", err)
+ }
+ defer out.Close()
+ contents, err := KubeConfigFromREST(u.Config())
+ if err != nil {
+ return nil, err
+ }
+ if _, err := out.Write(contents); err != nil {
+ return nil, fmt.Errorf("unable to write kubeconfig to disk at %s: %w", out.Name(), err)
+ }
+ k := &KubeCtl{
+ Path: u.plane.KubectlPath,
+ }
+ k.Opts = append(k.Opts, fmt.Sprintf("--kubeconfig=%s", out.Name()))
+ u.kubectl = k
+ return k, nil
+}
+
+// AddUser provisions a new user in the cluster. It uses the APIServer's authentication
+// strategy -- see APIServer.SecureServing.Authn.
+//
+// Unlike AddUser, it's safe to pass a nil rest.Config here if you have no
+// particular opinions about the config.
+//
+// The default authentication strategy is not guaranteed to any specific strategy, but it is
+// guaranteed to be callable both before and after Start has been called (but, as noted in the
+// AuthenticatedUser docs, the given user objects are only valid after Start has been called).
+func (f *ControlPlane) AddUser(user User, baseConfig *rest.Config) (*AuthenticatedUser, error) {
+ if f.GetAPIServer().SecureServing.Authn == nil {
+ return nil, fmt.Errorf("no API server authentication is configured yet. The API server defaults one when Start is called, did you mean to use that?")
+ }
+
+ if baseConfig == nil {
+ baseConfig = &rest.Config{}
+ }
+ cfg, err := f.GetAPIServer().SecureServing.AddUser(user, baseConfig)
+ if err != nil {
+ return nil, err
+ }
+
+ return &AuthenticatedUser{
+ cfg: cfg,
+ plane: f,
+ }, nil
+}
+
+// GetAPIServer returns this ControlPlane's APIServer, initializing it if necessary.
+func (f *ControlPlane) GetAPIServer() *APIServer {
+ if f.APIServer == nil {
+ f.APIServer = &APIServer{}
+ }
+ return f.APIServer
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/process/arguments.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/process/arguments.go
new file mode 100644
index 000000000..6c2c91e14
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/process/arguments.go
@@ -0,0 +1,340 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package process
+
+import (
+ "bytes"
+ "html/template"
+ "sort"
+ "strings"
+)
+
+// RenderTemplates returns an []string to render the templates
+//
+// Deprecated: will be removed in favor of Arguments.
+func RenderTemplates(argTemplates []string, data interface{}) (args []string, err error) {
+ var t *template.Template
+
+ for _, arg := range argTemplates {
+ t, err = template.New(arg).Parse(arg)
+ if err != nil {
+ args = nil
+ return
+ }
+
+ buf := &bytes.Buffer{}
+ err = t.Execute(buf, data)
+ if err != nil {
+ args = nil
+ return
+ }
+ args = append(args, buf.String())
+ }
+
+ return
+}
+
+// SliceToArguments converts a slice of arguments to structured arguments,
+// appending each argument that starts with `--` and contains an `=` to the
+// argument set (ignoring defaults), returning the rest.
+//
+// Deprecated: will be removed when RenderTemplates is removed.
+func SliceToArguments(sliceArgs []string, args *Arguments) []string {
+ var rest []string
+ for i, arg := range sliceArgs {
+ if arg == "--" {
+ rest = append(rest, sliceArgs[i:]...)
+ return rest
+ }
+ // skip non-flag arguments, skip arguments w/o equals because we
+ // can't tell if the next argument should take a value
+ if !strings.HasPrefix(arg, "--") || !strings.Contains(arg, "=") {
+ rest = append(rest, arg)
+ continue
+ }
+
+ parts := strings.SplitN(arg[2:], "=", 2)
+ name := parts[0]
+ val := parts[1]
+
+ args.AppendNoDefaults(name, val)
+ }
+
+ return rest
+}
+
+// TemplateDefaults specifies defaults to be used for joining structured arguments with templates.
+//
+// Deprecated: will be removed when RenderTemplates is removed.
+type TemplateDefaults struct {
+ // Data will be used to render the template.
+ Data interface{}
+ // Defaults will be used to default structured arguments if no template is passed.
+ Defaults map[string][]string
+ // MinimalDefaults will be used to default structured arguments if a template is passed.
+ // Use this for flags which *must* be present.
+ MinimalDefaults map[string][]string // for api server service-cluster-ip-range
+}
+
+// TemplateAndArguments joins structured arguments and non-structured arguments, preserving existing
+// behavior. Namely:
+//
+// 1. if templ has len > 0, it will be rendered against data
+// 2. the rendered template values that look like `--foo=bar` will be split
+// and appended to args, the rest will be kept around
+// 3. the given args will be rendered as string form. If a template is given,
+// no defaults will be used, otherwise defaults will be used
+// 4. a result of [args..., rest...] will be returned
+//
+// It returns the resulting rendered arguments, plus the arguments that were
+// not transferred to `args` during rendering.
+//
+// Deprecated: will be removed when RenderTemplates is removed.
+func TemplateAndArguments(templ []string, args *Arguments, data TemplateDefaults) (allArgs []string, nonFlagishArgs []string, err error) {
+ if len(templ) == 0 { // 3 & 4 (no template case)
+ return args.AsStrings(data.Defaults), nil, nil
+ }
+
+ // 1: render the template
+ rendered, err := RenderTemplates(templ, data.Data)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // 2: filter out structured args and add them to args
+ rest := SliceToArguments(rendered, args)
+
+ // 3 (template case): render structured args, no defaults (matching the
+ // legacy case where if Args was specified, no defaults were used)
+ res := args.AsStrings(data.MinimalDefaults)
+
+ // 4: return the rendered structured args + all non-structured args
+ return append(res, rest...), rest, nil
+}
+
+// EmptyArguments constructs an empty set of flags with no defaults.
+func EmptyArguments() *Arguments {
+ return &Arguments{
+ values: make(map[string]Arg),
+ }
+}
+
+// Arguments are structured, overridable arguments.
+// Each Arguments object contains some set of default arguments, which may
+// be appended to, or overridden.
+//
+// When ready, you can serialize them to pass to exec.Command and friends using
+// AsStrings.
+//
+// All flag-setting methods return the *same* instance of Arguments so that you
+// can chain calls.
+type Arguments struct {
+ // values contains the user-set values for the arguments.
+ // `values[key] = dontPass` means "don't pass this flag"
+ // `values[key] = passAsName` means "pass this flag without args like --key`
+ // `values[key] = []string{a, b, c}` means "--key=a --key=b --key=c`
+ // any values not explicitly set here will be copied from defaults on final rendering.
+ values map[string]Arg
+}
+
+// Arg is an argument that has one or more values,
+// and optionally falls back to default values.
+type Arg interface {
+ // Append adds new values to this argument, returning
+ // a new instance contain the new value. The intermediate
+ // argument should generally be assumed to be consumed.
+ Append(vals ...string) Arg
+ // Get returns the full set of values, optionally including
+ // the passed in defaults. If it returns nil, this will be
+ // skipped. If it returns a non-nil empty slice, it'll be
+ // assumed that the argument should be passed as name-only.
+ Get(defaults []string) []string
+}
+
+type userArg []string
+
+func (a userArg) Append(vals ...string) Arg {
+ return userArg(append(a, vals...)) //nolint:unconvert
+}
+func (a userArg) Get(_ []string) []string {
+ return []string(a)
+}
+
+type defaultedArg []string
+
+func (a defaultedArg) Append(vals ...string) Arg {
+ return defaultedArg(append(a, vals...)) //nolint:unconvert
+}
+func (a defaultedArg) Get(defaults []string) []string {
+ res := append([]string(nil), defaults...)
+ return append(res, a...)
+}
+
+type dontPassArg struct{}
+
+func (a dontPassArg) Append(vals ...string) Arg {
+ return userArg(vals)
+}
+func (dontPassArg) Get(_ []string) []string {
+ return nil
+}
+
+type passAsNameArg struct{}
+
+func (a passAsNameArg) Append(_ ...string) Arg {
+ return passAsNameArg{}
+}
+func (passAsNameArg) Get(_ []string) []string {
+ return []string{}
+}
+
+var (
+ // DontPass indicates that the given argument will not actually be
+ // rendered.
+ DontPass Arg = dontPassArg{}
+ // PassAsName indicates that the given flag will be passed as `--key`
+ // without any value.
+ PassAsName Arg = passAsNameArg{}
+)
+
+// AsStrings serializes this set of arguments to a slice of strings appropriate
+// for passing to exec.Command and friends, making use of the given defaults
+// as indicated for each particular argument.
+//
+// - Any flag in defaults that's not in Arguments will be present in the output
+// - Any flag that's present in Arguments will be passed the corresponding
+// defaults to do with as it will (ignore, append-to, suppress, etc).
+func (a *Arguments) AsStrings(defaults map[string][]string) []string {
+ // sort for deterministic ordering
+ keysInOrder := make([]string, 0, len(defaults)+len(a.values))
+ for key := range defaults {
+ if _, userSet := a.values[key]; userSet {
+ continue
+ }
+ keysInOrder = append(keysInOrder, key)
+ }
+ for key := range a.values {
+ keysInOrder = append(keysInOrder, key)
+ }
+ sort.Strings(keysInOrder)
+
+ var res []string
+ for _, key := range keysInOrder {
+ vals := a.Get(key).Get(defaults[key])
+ switch {
+ case vals == nil: // don't pass
+ continue
+ case len(vals) == 0: // pass as name
+ res = append(res, "--"+key)
+ default:
+ for _, val := range vals {
+ res = append(res, "--"+key+"="+val)
+ }
+ }
+ }
+
+ return res
+}
+
+// Get returns the value of the given flag. If nil,
+// it will not be passed in AsString, otherwise:
+//
+// len == 0 --> `--key`, len > 0 --> `--key=val1 --key=val2 ...`.
+func (a *Arguments) Get(key string) Arg {
+ if vals, ok := a.values[key]; ok {
+ return vals
+ }
+ return defaultedArg(nil)
+}
+
+// Enable configures the given key to be passed as a "name-only" flag,
+// like, `--key`.
+func (a *Arguments) Enable(key string) *Arguments {
+ a.values[key] = PassAsName
+ return a
+}
+
+// Disable prevents this flag from be passed.
+func (a *Arguments) Disable(key string) *Arguments {
+ a.values[key] = DontPass
+ return a
+}
+
+// Append adds additional values to this flag. If this flag has
+// yet to be set, initial values will include defaults. If you want
+// to intentionally ignore defaults/start from scratch, call AppendNoDefaults.
+//
+// Multiple values will look like `--key=value1 --key=value2 ...`.
+func (a *Arguments) Append(key string, values ...string) *Arguments {
+ vals, present := a.values[key]
+ if !present {
+ vals = defaultedArg{}
+ }
+ a.values[key] = vals.Append(values...)
+ return a
+}
+
+// AppendNoDefaults adds additional values to this flag. However,
+// unlike Append, it will *not* copy values from defaults.
+func (a *Arguments) AppendNoDefaults(key string, values ...string) *Arguments {
+ vals, present := a.values[key]
+ if !present {
+ vals = userArg{}
+ }
+ a.values[key] = vals.Append(values...)
+ return a
+}
+
+// Set resets the given flag to the specified values, ignoring any existing
+// values or defaults.
+func (a *Arguments) Set(key string, values ...string) *Arguments {
+ a.values[key] = userArg(values)
+ return a
+}
+
+// SetRaw sets the given flag to the given Arg value directly. Use this if
+// you need to do some complicated deferred logic or something.
+//
+// Otherwise behaves like Set.
+func (a *Arguments) SetRaw(key string, val Arg) *Arguments {
+ a.values[key] = val
+ return a
+}
+
+// FuncArg is a basic implementation of Arg that can be used for custom argument logic,
+// like pulling values out of APIServer, or dynamically calculating values just before
+// launch.
+//
+// The given function will be mapped directly to Arg#Get, and will generally be
+// used in conjunction with SetRaw. For example, to set `--some-flag` to the
+// API server's CertDir, you could do:
+//
+// server.Configure().SetRaw("--some-flag", FuncArg(func(defaults []string) []string {
+// return []string{server.CertDir}
+// }))
+//
+// FuncArg ignores Appends; if you need to support appending values too, consider implementing
+// Arg directly.
+type FuncArg func([]string) []string
+
+// Append is a no-op for FuncArg, and just returns itself.
+func (a FuncArg) Append(vals ...string) Arg { return a }
+
+// Get delegates functionality to the FuncArg function itself.
+func (a FuncArg) Get(defaults []string) []string {
+ return a(defaults)
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/process/bin_path_finder.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/process/bin_path_finder.go
new file mode 100644
index 000000000..e1428aa6e
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/process/bin_path_finder.go
@@ -0,0 +1,70 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package process
+
+import (
+ "os"
+ "path/filepath"
+ "regexp"
+ "strings"
+)
+
+const (
+ // EnvAssetsPath is the environment variable that stores the global test
+ // binary location override.
+ EnvAssetsPath = "KUBEBUILDER_ASSETS"
+ // EnvAssetOverridePrefix is the environment variable prefix for per-binary
+ // location overrides.
+ EnvAssetOverridePrefix = "TEST_ASSET_"
+ // AssetsDefaultPath is the default location to look for test binaries in,
+ // if no override was provided.
+ AssetsDefaultPath = "/usr/local/kubebuilder/bin"
+)
+
+// BinPathFinder finds the path to the given named binary, using the following locations
+// in order of precedence (highest first). Notice that the various env vars only need
+// to be set -- the asset is not checked for existence on the filesystem.
+//
+// 1. TEST_ASSET_{tr/a-z-/A-Z_/} (if set; asset overrides -- EnvAssetOverridePrefix)
+// 1. KUBEBUILDER_ASSETS (if set; global asset path -- EnvAssetsPath)
+// 3. assetDirectory (if set; per-config asset directory)
+// 4. /usr/local/kubebuilder/bin (AssetsDefaultPath).
+func BinPathFinder(symbolicName, assetDirectory string) (binPath string) {
+ punctuationPattern := regexp.MustCompile("[^A-Z0-9]+")
+ sanitizedName := punctuationPattern.ReplaceAllString(strings.ToUpper(symbolicName), "_")
+ leadingNumberPattern := regexp.MustCompile("^[0-9]+")
+ sanitizedName = leadingNumberPattern.ReplaceAllString(sanitizedName, "")
+ envVar := EnvAssetOverridePrefix + sanitizedName
+
+ // TEST_ASSET_XYZ
+ if val, ok := os.LookupEnv(envVar); ok {
+ return val
+ }
+
+ // KUBEBUILDER_ASSETS
+ if val, ok := os.LookupEnv(EnvAssetsPath); ok {
+ return filepath.Join(val, symbolicName)
+ }
+
+ // assetDirectory
+ if assetDirectory != "" {
+ return filepath.Join(assetDirectory, symbolicName)
+ }
+
+ // default path
+ return filepath.Join(AssetsDefaultPath, symbolicName)
+}
diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/process/process.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/process/process.go
new file mode 100644
index 000000000..4675f9b94
--- /dev/null
+++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/testing/process/process.go
@@ -0,0 +1,277 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package process
+
+import (
+ "crypto/tls"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "net/url"
+ "os"
+ "os/exec"
+ "path"
+ "regexp"
+ "sync"
+ "syscall"
+ "time"
+)
+
+// ListenAddr represents some listening address and port.
+type ListenAddr struct {
+ Address string
+ Port string
+}
+
+// URL returns a URL for this address with the given scheme and subpath.
+func (l *ListenAddr) URL(scheme string, path string) *url.URL {
+ return &url.URL{
+ Scheme: scheme,
+ Host: l.HostPort(),
+ Path: path,
+ }
+}
+
+// HostPort returns the joined host-port pair for this address.
+func (l *ListenAddr) HostPort() string {
+ return net.JoinHostPort(l.Address, l.Port)
+}
+
+// HealthCheck describes the information needed to health-check a process via
+// some health-check URL.
+type HealthCheck struct {
+ url.URL
+
+ // HealthCheckPollInterval is the interval which will be used for polling the
+ // endpoint described by Host, Port, and Path.
+ //
+ // If left empty it will default to 100 Milliseconds.
+ PollInterval time.Duration
+}
+
+// State define the state of the process.
+type State struct {
+ Cmd *exec.Cmd
+
+ // HealthCheck describes how to check if this process is up. If we get an http.StatusOK,
+ // we assume the process is ready to operate.
+ //
+ // For example, the /healthz endpoint of the k8s API server, or the /health endpoint of etcd.
+ HealthCheck HealthCheck
+
+ Args []string
+
+ StopTimeout time.Duration
+ StartTimeout time.Duration
+
+ Dir string
+ DirNeedsCleaning bool
+ Path string
+
+ // ready holds wether the process is currently in ready state (hit the ready condition) or not.
+ // It will be set to true on a successful `Start()` and set to false on a successful `Stop()`
+ ready bool
+
+ // waitDone is closed when our call to wait finishes up, and indicates that
+ // our process has terminated.
+ waitDone chan struct{}
+ errMu sync.Mutex
+ exitErr error
+ exited bool
+}
+
+// Init sets up this process, configuring binary paths if missing, initializing
+// temporary directories, etc.
+//
+// This defaults all defaultable fields.
+func (ps *State) Init(name string) error {
+ if ps.Path == "" {
+ if name == "" {
+ return fmt.Errorf("must have at least one of name or path")
+ }
+ ps.Path = BinPathFinder(name, "")
+ }
+
+ if ps.Dir == "" {
+ newDir, err := ioutil.TempDir("", "k8s_test_framework_")
+ if err != nil {
+ return err
+ }
+ ps.Dir = newDir
+ ps.DirNeedsCleaning = true
+ }
+
+ if ps.StartTimeout == 0 {
+ ps.StartTimeout = 20 * time.Second
+ }
+
+ if ps.StopTimeout == 0 {
+ ps.StopTimeout = 20 * time.Second
+ }
+ return nil
+}
+
+type stopChannel chan struct{}
+
+// CheckFlag checks the help output of this command for the presence of the given flag, specified
+// without the leading `--` (e.g. `CheckFlag("insecure-port")` checks for `--insecure-port`),
+// returning true if the flag is present.
+func (ps *State) CheckFlag(flag string) (bool, error) {
+ cmd := exec.Command(ps.Path, "--help")
+ outContents, err := cmd.CombinedOutput()
+ if err != nil {
+ return false, fmt.Errorf("unable to run command %q to check for flag %q: %w", ps.Path, flag, err)
+ }
+ pat := `(?m)^\s*--` + flag + `\b` // (m --> multi-line --> ^ matches start of line)
+ matched, err := regexp.Match(pat, outContents)
+ if err != nil {
+ return false, fmt.Errorf("unable to check command %q for flag %q in help output: %w", ps.Path, flag, err)
+ }
+ return matched, nil
+}
+
+// Start starts the apiserver, waits for it to come up, and returns an error,
+// if occurred.
+func (ps *State) Start(stdout, stderr io.Writer) (err error) {
+ if ps.ready {
+ return nil
+ }
+
+ ps.Cmd = exec.Command(ps.Path, ps.Args...)
+ ps.Cmd.Stdout = stdout
+ ps.Cmd.Stderr = stderr
+
+ ready := make(chan bool)
+ timedOut := time.After(ps.StartTimeout)
+ pollerStopCh := make(stopChannel)
+ go pollURLUntilOK(ps.HealthCheck.URL, ps.HealthCheck.PollInterval, ready, pollerStopCh)
+
+ ps.waitDone = make(chan struct{})
+
+ if err := ps.Cmd.Start(); err != nil {
+ ps.errMu.Lock()
+ defer ps.errMu.Unlock()
+ ps.exited = true
+ return err
+ }
+ go func() {
+ defer close(ps.waitDone)
+ err := ps.Cmd.Wait()
+
+ ps.errMu.Lock()
+ defer ps.errMu.Unlock()
+ ps.exitErr = err
+ ps.exited = true
+ }()
+
+ select {
+ case <-ready:
+ ps.ready = true
+ return nil
+ case <-ps.waitDone:
+ if pollerStopCh != nil {
+ close(pollerStopCh)
+ }
+ return fmt.Errorf("timeout waiting for process %s to start successfully "+
+ "(it may have failed to start, or stopped unexpectedly before becoming ready)",
+ path.Base(ps.Path))
+ case <-timedOut:
+ if pollerStopCh != nil {
+ close(pollerStopCh)
+ }
+ if ps.Cmd != nil {
+ // intentionally ignore this -- we might've crashed, failed to start, etc
+ ps.Cmd.Process.Signal(syscall.SIGTERM) //nolint:errcheck
+ }
+ return fmt.Errorf("timeout waiting for process %s to start", path.Base(ps.Path))
+ }
+}
+
+// Exited returns true if the process exited, and may also
+// return an error (as per Cmd.Wait) if the process did not
+// exit with error code 0.
+func (ps *State) Exited() (bool, error) {
+ ps.errMu.Lock()
+ defer ps.errMu.Unlock()
+ return ps.exited, ps.exitErr
+}
+
+func pollURLUntilOK(url url.URL, interval time.Duration, ready chan bool, stopCh stopChannel) {
+ client := &http.Client{
+ Transport: &http.Transport{
+ TLSClientConfig: &tls.Config{
+ // there's probably certs *somewhere*,
+ // but it's fine to just skip validating
+ // them for health checks during testing
+ InsecureSkipVerify: true, //nolint:gosec
+ },
+ },
+ }
+ if interval <= 0 {
+ interval = 100 * time.Millisecond
+ }
+ for {
+ res, err := client.Get(url.String())
+ if err == nil {
+ res.Body.Close()
+ if res.StatusCode == http.StatusOK {
+ ready <- true
+ return
+ }
+ }
+
+ select {
+ case <-stopCh:
+ return
+ default:
+ time.Sleep(interval)
+ }
+ }
+}
+
+// Stop stops this process gracefully, waits for its termination, and cleans up
+// the CertDir if necessary.
+func (ps *State) Stop() error {
+ // Always clear the directory if we need to.
+ defer func() {
+ if ps.DirNeedsCleaning {
+ _ = os.RemoveAll(ps.Dir)
+ }
+ }()
+ if ps.Cmd == nil {
+ return nil
+ }
+ if done, _ := ps.Exited(); done {
+ return nil
+ }
+ if err := ps.Cmd.Process.Signal(syscall.SIGTERM); err != nil {
+ return fmt.Errorf("unable to signal for process %s to stop: %w", ps.Path, err)
+ }
+
+ timedOut := time.After(ps.StopTimeout)
+
+ select {
+ case <-ps.waitDone:
+ break
+ case <-timedOut:
+ return fmt.Errorf("timeout waiting for process %s to stop", path.Base(ps.Path))
+ }
+ ps.ready = false
+ return nil
+}