mirror of
https://github.com/kubevirt/containerized-data-importer.git
synced 2025-06-03 06:30:22 +00:00
add controler runtime dep
This commit is contained in:
parent
d675f2fe76
commit
8dc6469e1f
6
glide.lock
generated
6
glide.lock
generated
@ -1,5 +1,5 @@
|
||||
hash: ba95291a0e4b936a6cf436071416090ef19cfbac8b44b4253792e28d305252ae
|
||||
updated: 2019-01-08T19:15:38.184275352Z
|
||||
hash: 65be6b54091a3194f1d3d60aef81dbf98945f380e58318e661deee0a3880ef88
|
||||
updated: 2019-01-08T20:51:55.484318811Z
|
||||
imports:
|
||||
- name: github.com/beorn7/perks
|
||||
version: 3ac7bf7a47d159a033b107610db8a1b6575507a4
|
||||
@ -514,4 +514,6 @@ imports:
|
||||
subpackages:
|
||||
- pkg/ginkgo-reporters
|
||||
- pkg/polarion-xml
|
||||
- name: sigs.k8s.io/controller-runtime
|
||||
version: 5fd1e9e9fac5261e9ad9d47c375afc014fc31d21
|
||||
testImports: []
|
||||
|
@ -28,3 +28,5 @@ import:
|
||||
version: 0.9.2
|
||||
- package: github.com/emicklei/go-restful-openapi
|
||||
version: ^1.0.0
|
||||
- package: sigs.k8s.io/controller-runtime
|
||||
version: v0.1.7
|
||||
|
18
vendor/sigs.k8s.io/controller-runtime/.gitignore
generated
vendored
Normal file
18
vendor/sigs.k8s.io/controller-runtime/.gitignore
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# editor and IDE paraphernalia
|
||||
.idea
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
26
vendor/sigs.k8s.io/controller-runtime/.travis.yml
generated
vendored
Normal file
26
vendor/sigs.k8s.io/controller-runtime/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
language: go
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
go:
|
||||
- "1.10"
|
||||
|
||||
git:
|
||||
depth: 3
|
||||
|
||||
go_import_path: sigs.k8s.io/controller-runtime
|
||||
|
||||
install:
|
||||
- go get -u github.com/golang/dep/cmd/dep
|
||||
#- go get -u golang.org/x/lint/golint
|
||||
- go get -u gopkg.in/alecthomas/gometalinter.v2 && gometalinter.v2 --install
|
||||
|
||||
script:
|
||||
- TRACE=1 ./hack/check-everything.sh
|
||||
|
||||
|
||||
# TBD. Suppressing for now.
|
||||
notifications:
|
||||
email: false
|
29
vendor/sigs.k8s.io/controller-runtime/CONTRIBUTING.md
generated
vendored
Normal file
29
vendor/sigs.k8s.io/controller-runtime/CONTRIBUTING.md
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
# Contributing guidelines
|
||||
|
||||
## Sign the CLA
|
||||
|
||||
Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests.
|
||||
|
||||
Please see https://git.k8s.io/community/CLA.md for more info
|
||||
|
||||
## Contributing steps
|
||||
|
||||
1. Submit an issue describing your proposed change to the repo in question.
|
||||
1. The [repo owners](OWNERS) will respond to your issue promptly.
|
||||
1. If your proposed change is accepted, and you haven't already done so, sign a Contributor License Agreement (see details above).
|
||||
1. Fork the desired repo, develop and test your code changes.
|
||||
1. Submit a pull request.
|
||||
|
||||
## Test locally
|
||||
|
||||
1. Setup tools
|
||||
```bash
|
||||
$ go get -u github.com/golang/dep/cmd/dep
|
||||
$ go get -u gopkg.in/alecthomas/gometalinter.v2
|
||||
$ gometalinter.v2 --install # if can't load package, refer: https://github.com/alecthomas/gometalinter/issues/404
|
||||
```
|
||||
1. Test
|
||||
```bash
|
||||
TRACE=1 ./hack/check-everything.sh
|
||||
```
|
||||
|
1375
vendor/sigs.k8s.io/controller-runtime/Gopkg.lock
generated
vendored
Normal file
1375
vendor/sigs.k8s.io/controller-runtime/Gopkg.lock
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
77
vendor/sigs.k8s.io/controller-runtime/Gopkg.toml
generated
vendored
Normal file
77
vendor/sigs.k8s.io/controller-runtime/Gopkg.toml
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
# 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.
|
||||
|
||||
# Packages required by users
|
||||
required = ["sigs.k8s.io/testing_frameworks/integration",
|
||||
"k8s.io/client-go/plugin/pkg/client/auth",
|
||||
"github.com/spf13/pflag",
|
||||
"github.com/emicklei/go-restful",
|
||||
"github.com/go-openapi/spec",
|
||||
"k8s.io/kube-openapi/pkg/common",
|
||||
"k8s.io/apiextensions-apiserver",
|
||||
]
|
||||
|
||||
[[constraint]]
|
||||
name = "k8s.io/api"
|
||||
version = "kubernetes-1.11.2"
|
||||
|
||||
[[constraint]]
|
||||
name = "k8s.io/apiextensions-apiserver"
|
||||
version = "kubernetes-1.11.2"
|
||||
|
||||
[[constraint]]
|
||||
name = "k8s.io/apimachinery"
|
||||
version = "kubernetes-1.11.2"
|
||||
|
||||
[[constraint]]
|
||||
name = "k8s.io/client-go"
|
||||
version = "kubernetes-1.11.2"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/onsi/ginkgo"
|
||||
version = "v1.5.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/onsi/gomega"
|
||||
version = "v1.4.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/ghodss/yaml"
|
||||
version = "1.0.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "go.uber.org/zap"
|
||||
version = "1.8.0"
|
||||
|
||||
# these are not listed explicitly until we get version tags,
|
||||
# since dep doesn't like bare revision dependencies
|
||||
|
||||
# [[constraint]]
|
||||
# name = "sigs.k8s.io/testing_frameworks"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/go-logr/logr"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/go-logr/zapr"
|
||||
|
||||
# For dependency below: Refer to issue https://github.com/golang/dep/issues/1799
|
||||
[[override]]
|
||||
name = "gopkg.in/fsnotify.v1"
|
||||
source = "https://github.com/fsnotify/fsnotify.git"
|
||||
version="v1.4.7"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
201
vendor/sigs.k8s.io/controller-runtime/LICENSE
generated
vendored
Normal file
201
vendor/sigs.k8s.io/controller-runtime/LICENSE
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
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.
|
11
vendor/sigs.k8s.io/controller-runtime/OWNERS
generated
vendored
Normal file
11
vendor/sigs.k8s.io/controller-runtime/OWNERS
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
# See the OWNERS docs: https://git.k8s.io/community/contributors/devel/owners.md
|
||||
owners:
|
||||
- directxman12
|
||||
- droot
|
||||
- pwittrock
|
||||
approvers:
|
||||
- controller-runtime-admins
|
||||
- controller-runtime-maintainers
|
||||
reviewers:
|
||||
- controller-runtime-admins
|
||||
- controller-runtime-maintainers
|
8
vendor/sigs.k8s.io/controller-runtime/OWNERS_ALIASES
generated
vendored
Normal file
8
vendor/sigs.k8s.io/controller-runtime/OWNERS_ALIASES
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# See the OWNERS docs: https://git.k8s.io/community/contributors/devel/owners.md
|
||||
|
||||
aliases:
|
||||
controller-runtime-admins:
|
||||
- directxman12
|
||||
- droot
|
||||
- pwittrock
|
||||
controller-runtime-maintainers: []
|
36
vendor/sigs.k8s.io/controller-runtime/README.md
generated
vendored
Normal file
36
vendor/sigs.k8s.io/controller-runtime/README.md
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
[](https://travis-ci.org/kubernetes-sigs/controller-runtime "Travis")
|
||||
[](https://goreportcard.com/report/sigs.k8s.io/controller-runtime)
|
||||
|
||||
# Kubernetes controller-runtime Project
|
||||
|
||||
The Kubernetes controller-runtime Project is a set of go libraries for building Controllers.
|
||||
|
||||
Documentation:
|
||||
|
||||
- [Package overview](https://godoc.org/github.com/kubernetes-sigs/controller-runtime/pkg)
|
||||
- [Basic controller using builder](https://godoc.org/github.com/kubernetes-sigs/controller-runtime/pkg/builder#example-Builder)
|
||||
- [Creating a manager](https://godoc.org/github.com/kubernetes-sigs/controller-runtime/pkg/manager#example-New)
|
||||
- [Creating a controller](https://godoc.org/github.com/kubernetes-sigs/controller-runtime/pkg/controller#example-New)
|
||||
- [Example `main.go`](https://github.com/kubernetes-sigs/controller-runtime/blob/master/example/main.go)
|
||||
|
||||
## Community, discussion, contribution, and support
|
||||
|
||||
Learn how to engage with the Kubernetes community on the [community page](http://kubernetes.io/community/).
|
||||
|
||||
controller-runtime is a subproject of the [kubebuilder](https://github.com/kubernetes-sigs/kubebuilder) project
|
||||
in sig apimachinery.
|
||||
|
||||
You can reach the maintainers of this project at:
|
||||
|
||||
- Slack channel: [#kubebuilder](http://slack.k8s.io/#kubebuilder)
|
||||
- Google Group: [kubebuilder@googlegroups.com](https://groups.google.com/forum/#!forum/kubebuilder)
|
||||
|
||||
## Contributing
|
||||
Contributions are greatly appreciated. The maintainers actively manage the issues list, and try to highlight issues suitable for newcomers.
|
||||
The project follows the typical GitHub pull request model. See [CONTRIBUTING.md](CONTRIBUTING.md) for more details.
|
||||
Before starting any work, please either comment on an existing issue, or file a new one.
|
||||
|
||||
## Code of conduct
|
||||
|
||||
Participation in the Kubernetes community is governed by the [Kubernetes Code of Conduct](code-of-conduct.md).
|
||||
|
8
vendor/sigs.k8s.io/controller-runtime/RELEASE.md
generated
vendored
Normal file
8
vendor/sigs.k8s.io/controller-runtime/RELEASE.md
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# Release Process
|
||||
|
||||
The Kubernetes controller-runtime Project is released on an as-needed basis. The process is as follows:
|
||||
|
||||
1. An issue is proposing a new release with a changelog since the last release
|
||||
1. 2 [OWNERS](OWNERS) must LGTM this release
|
||||
1. An OWNER runs `git tag -s $VERSION` and inserts the changelog and pushes the tag with `git push $VERSION`
|
||||
1. The release issue is closed
|
15
vendor/sigs.k8s.io/controller-runtime/SECURITY_CONTACTS
generated
vendored
Normal file
15
vendor/sigs.k8s.io/controller-runtime/SECURITY_CONTACTS
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
# Defined below are the security contacts for this repo.
|
||||
#
|
||||
# They are the contact point for the Product Security Team to reach out
|
||||
# to for triaging and handling of incoming issues.
|
||||
#
|
||||
# The below names agree to abide by the
|
||||
# [Embargo Policy](https://github.com/kubernetes/sig-release/blob/master/security-release-process-documentation/security-release-process.md#embargo-policy)
|
||||
# and will be removed and replaced if they violate that agreement.
|
||||
#
|
||||
# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE
|
||||
# INSTRUCTIONS AT https://kubernetes.io/security/
|
||||
|
||||
directxman12
|
||||
pwittrock
|
||||
droot
|
3
vendor/sigs.k8s.io/controller-runtime/code-of-conduct.md
generated
vendored
Normal file
3
vendor/sigs.k8s.io/controller-runtime/code-of-conduct.md
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# Kubernetes Community Code of Conduct
|
||||
|
||||
Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md)
|
77
vendor/sigs.k8s.io/controller-runtime/example/controller.go
generated
vendored
Normal file
77
vendor/sigs.k8s.io/controller-runtime/example/controller.go
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
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 main
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
// reconcileReplicaSet reconciles ReplicaSets
|
||||
type reconcileReplicaSet struct {
|
||||
// client can be used to retrieve objects from the APIServer.
|
||||
client client.Client
|
||||
log logr.Logger
|
||||
}
|
||||
|
||||
// Implement reconcile.Reconciler so the controller can reconcile objects
|
||||
var _ reconcile.Reconciler = &reconcileReplicaSet{}
|
||||
|
||||
func (r *reconcileReplicaSet) Reconcile(request reconcile.Request) (reconcile.Result, error) {
|
||||
// set up a convinient log object so we don't have to type request over and over again
|
||||
log := r.log.WithValues("request", request)
|
||||
|
||||
// Fetch the ReplicaSet from the cache
|
||||
rs := &appsv1.ReplicaSet{}
|
||||
err := r.client.Get(context.TODO(), request.NamespacedName, rs)
|
||||
if errors.IsNotFound(err) {
|
||||
log.Error(nil, "Could not find ReplicaSet")
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Error(err, "Could not fetch ReplicaSet")
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
// Print the ReplicaSet
|
||||
log.Info("Reconciling ReplicaSet", "container name", rs.Spec.Template.Spec.Containers[0].Name)
|
||||
|
||||
// Set the label if it is missing
|
||||
if rs.Labels == nil {
|
||||
rs.Labels = map[string]string{}
|
||||
}
|
||||
if rs.Labels["hello"] == "world" {
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
|
||||
// Update the ReplicaSet
|
||||
rs.Labels["hello"] = "world"
|
||||
err = r.client.Update(context.TODO(), rs)
|
||||
if err != nil {
|
||||
log.Error(err, "Could not write ReplicaSet")
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
return reconcile.Result{}, nil
|
||||
}
|
147
vendor/sigs.k8s.io/controller-runtime/example/main.go
generated
vendored
Normal file
147
vendor/sigs.k8s.io/controller-runtime/example/main.go
generated
vendored
Normal file
@ -0,0 +1,147 @@
|
||||
/*
|
||||
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 main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
|
||||
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apitypes "k8s.io/apimachinery/pkg/types"
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/runtime/signals"
|
||||
"sigs.k8s.io/controller-runtime/pkg/source"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission/builder"
|
||||
)
|
||||
|
||||
var log = logf.Log.WithName("example-controller")
|
||||
|
||||
func main() {
|
||||
var disableWebhookConfigInstaller bool
|
||||
flag.BoolVar(&disableWebhookConfigInstaller, "disable-webhook-config-installer", false,
|
||||
"disable the installer in the webhook server, so it won't install webhook configuration resources during bootstrapping")
|
||||
|
||||
flag.Parse()
|
||||
logf.SetLogger(logf.ZapLogger(false))
|
||||
entryLog := log.WithName("entrypoint")
|
||||
|
||||
// Setup a Manager
|
||||
entryLog.Info("setting up manager")
|
||||
mgr, err := manager.New(config.GetConfigOrDie(), manager.Options{})
|
||||
if err != nil {
|
||||
entryLog.Error(err, "unable to set up overall controller manager")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Setup a new controller to Reconciler ReplicaSets
|
||||
entryLog.Info("Setting up controller")
|
||||
c, err := controller.New("foo-controller", mgr, controller.Options{
|
||||
Reconciler: &reconcileReplicaSet{client: mgr.GetClient(), log: log.WithName("reconciler")},
|
||||
})
|
||||
if err != nil {
|
||||
entryLog.Error(err, "unable to set up individual controller")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Watch ReplicaSets and enqueue ReplicaSet object key
|
||||
if err := c.Watch(&source.Kind{Type: &appsv1.ReplicaSet{}}, &handler.EnqueueRequestForObject{}); err != nil {
|
||||
entryLog.Error(err, "unable to watch ReplicaSets")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Watch Pods and enqueue owning ReplicaSet key
|
||||
if err := c.Watch(&source.Kind{Type: &corev1.Pod{}},
|
||||
&handler.EnqueueRequestForOwner{OwnerType: &appsv1.ReplicaSet{}, IsController: true}); err != nil {
|
||||
entryLog.Error(err, "unable to watch Pods")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Setup webhooks
|
||||
entryLog.Info("setting up webhooks")
|
||||
mutatingWebhook, err := builder.NewWebhookBuilder().
|
||||
Name("mutating.k8s.io").
|
||||
Mutating().
|
||||
Operations(admissionregistrationv1beta1.Create, admissionregistrationv1beta1.Update).
|
||||
WithManager(mgr).
|
||||
ForType(&corev1.Pod{}).
|
||||
Handlers(&podAnnotator{}).
|
||||
Build()
|
||||
if err != nil {
|
||||
entryLog.Error(err, "unable to setup mutating webhook")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
validatingWebhook, err := builder.NewWebhookBuilder().
|
||||
Name("validating.k8s.io").
|
||||
Validating().
|
||||
Operations(admissionregistrationv1beta1.Create, admissionregistrationv1beta1.Update).
|
||||
WithManager(mgr).
|
||||
ForType(&corev1.Pod{}).
|
||||
Handlers(&podValidator{}).
|
||||
Build()
|
||||
if err != nil {
|
||||
entryLog.Error(err, "unable to setup validating webhook")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
entryLog.Info("setting up webhook server")
|
||||
as, err := webhook.NewServer("foo-admission-server", mgr, webhook.ServerOptions{
|
||||
Port: 9876,
|
||||
CertDir: "/tmp/cert",
|
||||
DisableWebhookConfigInstaller: &disableWebhookConfigInstaller,
|
||||
BootstrapOptions: &webhook.BootstrapOptions{
|
||||
Secret: &apitypes.NamespacedName{
|
||||
Namespace: "default",
|
||||
Name: "foo-admission-server-secret",
|
||||
},
|
||||
|
||||
Service: &webhook.Service{
|
||||
Namespace: "default",
|
||||
Name: "foo-admission-server-service",
|
||||
// Selectors should select the pods that runs this webhook server.
|
||||
Selectors: map[string]string{
|
||||
"app": "foo-admission-server",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
entryLog.Error(err, "unable to create a new webhook server")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
entryLog.Info("registering webhooks to the webhook server")
|
||||
err = as.Register(mutatingWebhook, validatingWebhook)
|
||||
if err != nil {
|
||||
entryLog.Error(err, "unable to register webhooks in the admission server")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
entryLog.Info("starting manager")
|
||||
if err := mgr.Start(signals.SetupSignalHandler()); err != nil {
|
||||
entryLog.Error(err, "unable to run manager")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
83
vendor/sigs.k8s.io/controller-runtime/example/mutatingwebhook.go
generated
vendored
Normal file
83
vendor/sigs.k8s.io/controller-runtime/example/mutatingwebhook.go
generated
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
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 main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/runtime/inject"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission/types"
|
||||
)
|
||||
|
||||
// podAnnotator annotates Pods
|
||||
type podAnnotator struct {
|
||||
client client.Client
|
||||
decoder types.Decoder
|
||||
}
|
||||
|
||||
// Implement admission.Handler so the controller can handle admission request.
|
||||
var _ admission.Handler = &podAnnotator{}
|
||||
|
||||
// podAnnotator adds an annotation to every incoming pods.
|
||||
func (a *podAnnotator) Handle(ctx context.Context, req types.Request) types.Response {
|
||||
pod := &corev1.Pod{}
|
||||
|
||||
err := a.decoder.Decode(req, pod)
|
||||
if err != nil {
|
||||
return admission.ErrorResponse(http.StatusBadRequest, err)
|
||||
}
|
||||
copy := pod.DeepCopy()
|
||||
|
||||
err = a.mutatePodsFn(ctx, copy)
|
||||
if err != nil {
|
||||
return admission.ErrorResponse(http.StatusInternalServerError, err)
|
||||
}
|
||||
return admission.PatchResponse(pod, copy)
|
||||
}
|
||||
|
||||
// mutatePodsFn add an annotation to the given pod
|
||||
func (a *podAnnotator) mutatePodsFn(ctx context.Context, pod *corev1.Pod) error {
|
||||
if pod.Annotations == nil {
|
||||
pod.Annotations = map[string]string{}
|
||||
}
|
||||
pod.Annotations["example-mutating-admission-webhook"] = "foo"
|
||||
return nil
|
||||
}
|
||||
|
||||
// podValidator implements inject.Client.
|
||||
// A client will be automatically injected.
|
||||
var _ inject.Client = &podValidator{}
|
||||
|
||||
// InjectClient injects the client.
|
||||
func (v *podAnnotator) InjectClient(c client.Client) error {
|
||||
v.client = c
|
||||
return nil
|
||||
}
|
||||
|
||||
// podValidator implements inject.Decoder.
|
||||
// A decoder will be automatically injected.
|
||||
var _ inject.Decoder = &podValidator{}
|
||||
|
||||
// InjectDecoder injects the decoder.
|
||||
func (v *podAnnotator) InjectDecoder(d types.Decoder) error {
|
||||
v.decoder = d
|
||||
return nil
|
||||
}
|
89
vendor/sigs.k8s.io/controller-runtime/example/validatingwebhook.go
generated
vendored
Normal file
89
vendor/sigs.k8s.io/controller-runtime/example/validatingwebhook.go
generated
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
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 main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/runtime/inject"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission/types"
|
||||
)
|
||||
|
||||
// podValidator validates Pods
|
||||
type podValidator struct {
|
||||
client client.Client
|
||||
decoder types.Decoder
|
||||
}
|
||||
|
||||
// Implement admission.Handler so the controller can handle admission request.
|
||||
var _ admission.Handler = &podValidator{}
|
||||
|
||||
// podValidator admits a pod iff a specific annotation exists.
|
||||
func (v *podValidator) Handle(ctx context.Context, req types.Request) types.Response {
|
||||
pod := &corev1.Pod{}
|
||||
|
||||
err := v.decoder.Decode(req, pod)
|
||||
if err != nil {
|
||||
return admission.ErrorResponse(http.StatusBadRequest, err)
|
||||
}
|
||||
|
||||
allowed, reason, err := v.validatePodsFn(ctx, pod)
|
||||
if err != nil {
|
||||
return admission.ErrorResponse(http.StatusInternalServerError, err)
|
||||
}
|
||||
return admission.ValidationResponse(allowed, reason)
|
||||
}
|
||||
|
||||
func (v *podValidator) validatePodsFn(ctx context.Context, pod *corev1.Pod) (bool, string, error) {
|
||||
key := "example-mutating-admission-webhook"
|
||||
anno, found := pod.Annotations[key]
|
||||
switch {
|
||||
case !found:
|
||||
return found, fmt.Sprintf("failed to find annotation with key: %q", key), nil
|
||||
case found && anno == "foo":
|
||||
return found, "", nil
|
||||
case found && anno != "foo":
|
||||
return false,
|
||||
fmt.Sprintf("the value associate with key %q is expected to be %q, but got %q", key, "foo", anno), nil
|
||||
}
|
||||
return false, "", nil
|
||||
}
|
||||
|
||||
// podValidator implements inject.Client.
|
||||
// A client will be automatically injected.
|
||||
var _ inject.Client = &podValidator{}
|
||||
|
||||
// InjectClient injects the client.
|
||||
func (v *podValidator) InjectClient(c client.Client) error {
|
||||
v.client = c
|
||||
return nil
|
||||
}
|
||||
|
||||
// podValidator implements inject.Decoder.
|
||||
// A decoder will be automatically injected.
|
||||
var _ inject.Decoder = &podValidator{}
|
||||
|
||||
// InjectDecoder injects the decoder.
|
||||
func (v *podValidator) InjectDecoder(d types.Decoder) error {
|
||||
v.decoder = d
|
||||
return nil
|
||||
}
|
79
vendor/sigs.k8s.io/controller-runtime/hack/check-everything.sh
generated
vendored
Executable file
79
vendor/sigs.k8s.io/controller-runtime/hack/check-everything.sh
generated
vendored
Executable file
@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# 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.
|
||||
|
||||
set -e
|
||||
|
||||
hack_dir=$(dirname ${BASH_SOURCE})
|
||||
source ${hack_dir}/common.sh
|
||||
|
||||
k8s_version=1.10.1
|
||||
goarch=amd64
|
||||
goos="unknown"
|
||||
|
||||
if [[ "$OSTYPE" == "linux-gnu" ]]; then
|
||||
goos="linux"
|
||||
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
goos="darwin"
|
||||
fi
|
||||
|
||||
if [[ "$goos" == "unknown" ]]; then
|
||||
echo "OS '$OSTYPE' not supported. Aborting." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tmp_root=/tmp
|
||||
kb_root_dir=$tmp_root/kubebuilder
|
||||
|
||||
# Skip fetching and untaring the tools by setting the SKIP_FETCH_TOOLS variable
|
||||
# in your environment to any value:
|
||||
#
|
||||
# $ SKIP_FETCH_TOOLS=1 ./test.sh
|
||||
#
|
||||
# If you skip fetching tools, this script will use the tools already on your
|
||||
# machine, but rebuild the kubebuilder and kubebuilder-bin binaries.
|
||||
SKIP_FETCH_TOOLS=${SKIP_FETCH_TOOLS:-""}
|
||||
|
||||
# fetch k8s API gen tools and make it available under kb_root_dir/bin.
|
||||
function fetch_kb_tools {
|
||||
if [ -n "$SKIP_FETCH_TOOLS" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
header_text "fetching tools"
|
||||
kb_tools_archive_name="kubebuilder-tools-$k8s_version-$goos-$goarch.tar.gz"
|
||||
kb_tools_download_url="https://storage.googleapis.com/kubebuilder-tools/$kb_tools_archive_name"
|
||||
|
||||
kb_tools_archive_path="$tmp_root/$kb_tools_archive_name"
|
||||
if [ ! -f $kb_tools_archive_path ]; then
|
||||
curl -sL ${kb_tools_download_url} -o "$kb_tools_archive_path"
|
||||
fi
|
||||
tar -zvxf "$kb_tools_archive_path" -C "$tmp_root/"
|
||||
}
|
||||
|
||||
header_text "using tools"
|
||||
|
||||
which gometalinter.v2
|
||||
fetch_kb_tools
|
||||
setup_envs
|
||||
|
||||
${hack_dir}/verify.sh
|
||||
${hack_dir}/test-all.sh
|
||||
|
||||
header_text "confirming example compiles (via go install)"
|
||||
go install ./example
|
||||
|
||||
echo "passed"
|
||||
exit 0
|
52
vendor/sigs.k8s.io/controller-runtime/hack/common.sh
generated
vendored
Executable file
52
vendor/sigs.k8s.io/controller-runtime/hack/common.sh
generated
vendored
Executable file
@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# 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.
|
||||
|
||||
set -e
|
||||
|
||||
# Enable tracing in this script off by setting the TRACE variable in your
|
||||
# environment to any value:
|
||||
#
|
||||
# $ TRACE=1 test.sh
|
||||
TRACE=${TRACE:-""}
|
||||
if [ -n "$TRACE" ]; then
|
||||
set -x
|
||||
fi
|
||||
|
||||
# Turn colors in this script off by setting the NO_COLOR variable in your
|
||||
# environment to any value:
|
||||
#
|
||||
# $ NO_COLOR=1 test.sh
|
||||
NO_COLOR=${NO_COLOR:-""}
|
||||
if [ -z "$NO_COLOR" ]; then
|
||||
header=$'\e[1;33m'
|
||||
reset=$'\e[0m'
|
||||
else
|
||||
header=''
|
||||
reset=''
|
||||
fi
|
||||
|
||||
function header_text {
|
||||
echo "$header$*$reset"
|
||||
}
|
||||
|
||||
function setup_envs {
|
||||
header_text "setting up env vars"
|
||||
|
||||
# Setup env vars
|
||||
if [[ -z "${KUBEBUILDER_ASSETS}" ]]; then
|
||||
export KUBEBUILDER_ASSETS=$kb_root_dir/bin
|
||||
fi
|
||||
}
|
37
vendor/sigs.k8s.io/controller-runtime/hack/test-all.sh
generated
vendored
Executable file
37
vendor/sigs.k8s.io/controller-runtime/hack/test-all.sh
generated
vendored
Executable file
@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# 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.
|
||||
|
||||
set -e
|
||||
|
||||
source $(dirname ${BASH_SOURCE})/common.sh
|
||||
|
||||
setup_envs
|
||||
|
||||
header_text "running go test"
|
||||
|
||||
go test ./pkg/... -parallel 4
|
||||
|
||||
header_text "running coverage"
|
||||
|
||||
# Verify no coverage regressions have been introduced. Remove the exception list from here
|
||||
# once the coverage has been brought back up
|
||||
if [[ ! $(go test ./pkg/... -coverprofile cover.out -parallel 4 | grep -v "coverage: 100.0% of statements" | grep "controller-runtime/pkg " | grep -v "controller-runtime/pkg \|controller-runtime/pkg/recorder \|pkg/admission/certprovisioner \|pkg/internal/admission \|pkg/cache\|pkg/client \|pkg/event \|pkg/client/config \|pkg/controller/controllertest \|pkg/reconcile/reconciletest \|pkg/test ") ]]; then
|
||||
echo "ok"
|
||||
else
|
||||
go test ./pkg/... -coverprofile cover.out -parallel 4 | grep -v "coverage: 100.0% of statements" | grep "controller-runtime/pkg " | grep -v "controller-runtime/pkg \|controller-runtime/pkg/recorder \|pkg/admission/certprovisioner \|pkg/internal/admission \|pkg/cache\|pkg/client \|pkg/event \|pkg/client/config \|pkg/controller/controllertest \|pkg/reconcile/reconciletest \|pkg/test "
|
||||
echo "missing test coverage"
|
||||
exit 1
|
||||
fi
|
57
vendor/sigs.k8s.io/controller-runtime/hack/verify.sh
generated
vendored
Executable file
57
vendor/sigs.k8s.io/controller-runtime/hack/verify.sh
generated
vendored
Executable file
@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# 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.
|
||||
|
||||
set -e
|
||||
|
||||
source $(dirname ${BASH_SOURCE})/common.sh
|
||||
|
||||
header_text "running go vet"
|
||||
|
||||
go vet ./pkg/...
|
||||
|
||||
# go get is broken for golint. re-enable this once it is fixed.
|
||||
#header_text "running golint"
|
||||
#
|
||||
#golint -set_exit_status ./pkg/...
|
||||
|
||||
header_text "running gometalinter.v2"
|
||||
|
||||
gometalinter.v2 --disable-all \
|
||||
--deadline 5m \
|
||||
--enable=misspell \
|
||||
--enable=structcheck \
|
||||
--enable=golint \
|
||||
--enable=deadcode \
|
||||
--enable=goimports \
|
||||
--enable=errcheck \
|
||||
--enable=varcheck \
|
||||
--enable=goconst \
|
||||
--enable=unparam \
|
||||
--enable=ineffassign \
|
||||
--enable=nakedret \
|
||||
--enable=interfacer \
|
||||
--enable=misspell \
|
||||
--enable=gocyclo \
|
||||
--line-length=170 \
|
||||
--enable=lll \
|
||||
--dupl-threshold=400 \
|
||||
--enable=dupl \
|
||||
--skip=atomic \
|
||||
./pkg/...
|
||||
# TODO: Enable these as we fix them to make them pass
|
||||
# --enable=gosec \
|
||||
# --enable=maligned \
|
||||
# --enable=safesql \
|
176
vendor/sigs.k8s.io/controller-runtime/pkg/builder/build.go
generated
vendored
Normal file
176
vendor/sigs.k8s.io/controller-runtime/pkg/builder/build.go
generated
vendored
Normal file
@ -0,0 +1,176 @@
|
||||
/*
|
||||
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 builder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
"sigs.k8s.io/controller-runtime/pkg/source"
|
||||
)
|
||||
|
||||
// application is a simple Controller for a single API type. It will create a Manager for itself
|
||||
// if one is not provided.
|
||||
type application struct {
|
||||
mgr manager.Manager
|
||||
ctrl controller.Controller
|
||||
}
|
||||
|
||||
// Supporting mocking out functions for testing
|
||||
var getConfig = config.GetConfig
|
||||
var newController = controller.New
|
||||
var newManager = manager.New
|
||||
var getGvk = apiutil.GVKForObject
|
||||
|
||||
// Builder builds an Application Controller (e.g. Operator) and returns a manager.Manager to start it.
|
||||
type Builder struct {
|
||||
apiType runtime.Object
|
||||
mgr manager.Manager
|
||||
predicates []predicate.Predicate
|
||||
managedObjects []runtime.Object
|
||||
config *rest.Config
|
||||
ctrl controller.Controller
|
||||
}
|
||||
|
||||
// SimpleController returns a new Builder
|
||||
func SimpleController() *Builder {
|
||||
return &Builder{}
|
||||
}
|
||||
|
||||
// ForType sets the ForType that generates other types
|
||||
func (b *Builder) ForType(apiType runtime.Object) *Builder {
|
||||
b.apiType = apiType
|
||||
return b
|
||||
}
|
||||
|
||||
// Owns configures the Application Controller to respond to create / delete / update events for objects it managedObjects
|
||||
// - e.g. creates. apiType is an empty instance of an object matching the managed object type.
|
||||
func (b *Builder) Owns(apiType runtime.Object) *Builder {
|
||||
b.managedObjects = append(b.managedObjects, apiType)
|
||||
return b
|
||||
}
|
||||
|
||||
// WithConfig sets the Config to use for configuring clients. Defaults to the in-cluster config or to ~/.kube/config.
|
||||
func (b *Builder) WithConfig(config *rest.Config) *Builder {
|
||||
b.config = config
|
||||
return b
|
||||
}
|
||||
|
||||
// WithManager sets the Manager to use for registering the Controller. Defaults to a new manager.Manager.
|
||||
func (b *Builder) WithManager(m manager.Manager) *Builder {
|
||||
b.mgr = m
|
||||
return b
|
||||
}
|
||||
|
||||
// WithEventFilter sets the event filters, to filter which create/update/delete/generic events eventually
|
||||
// trigger reconciliations. For example, filtering on whether the resource version has changed.
|
||||
// Defaults to the empty list.
|
||||
func (b *Builder) WithEventFilter(p predicate.Predicate) *Builder {
|
||||
b.predicates = append(b.predicates, p)
|
||||
return b
|
||||
}
|
||||
|
||||
// Build builds the Application Controller and returns the Manager used to start it.
|
||||
func (b *Builder) Build(r reconcile.Reconciler) (manager.Manager, error) {
|
||||
if r == nil {
|
||||
return nil, fmt.Errorf("must call WithReconciler to set Reconciler")
|
||||
}
|
||||
|
||||
// Set the Config
|
||||
if err := b.doConfig(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set the Manager
|
||||
if err := b.doManager(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set the Controller
|
||||
if err := b.doController(r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
a := &application{mgr: b.mgr, ctrl: b.ctrl}
|
||||
|
||||
// Reconcile type
|
||||
s := &source.Kind{Type: b.apiType}
|
||||
h := &handler.EnqueueRequestForObject{}
|
||||
err := a.ctrl.Watch(s, h, b.predicates...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Watch the managed types
|
||||
for _, t := range b.managedObjects {
|
||||
s := &source.Kind{Type: t}
|
||||
h := &handler.EnqueueRequestForOwner{
|
||||
OwnerType: b.apiType,
|
||||
IsController: true,
|
||||
}
|
||||
if err := a.ctrl.Watch(s, h, b.predicates...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return a.mgr, nil
|
||||
}
|
||||
|
||||
func (b *Builder) doConfig() error {
|
||||
if b.config != nil {
|
||||
return nil
|
||||
}
|
||||
var err error
|
||||
b.config, err = getConfig()
|
||||
return err
|
||||
}
|
||||
|
||||
func (b *Builder) doManager() error {
|
||||
if b.mgr != nil {
|
||||
return nil
|
||||
}
|
||||
var err error
|
||||
b.mgr, err = newManager(b.config, manager.Options{})
|
||||
return err
|
||||
}
|
||||
|
||||
func (b *Builder) getControllerName() (string, error) {
|
||||
gvk, err := getGvk(b.apiType, b.mgr.GetScheme())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
name := fmt.Sprintf("%s-application", strings.ToLower(gvk.Kind))
|
||||
return name, nil
|
||||
}
|
||||
|
||||
func (b *Builder) doController(r reconcile.Reconciler) error {
|
||||
name, err := b.getControllerName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.ctrl, err = newController(name, b.mgr, controller.Options{Reconciler: r})
|
||||
return err
|
||||
}
|
216
vendor/sigs.k8s.io/controller-runtime/pkg/builder/build_test.go
generated
vendored
Normal file
216
vendor/sigs.k8s.io/controller-runtime/pkg/builder/build_test.go
generated
vendored
Normal file
@ -0,0 +1,216 @@
|
||||
/*
|
||||
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 builder
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
var _ = Describe("application", func() {
|
||||
var stop chan struct{}
|
||||
|
||||
BeforeEach(func() {
|
||||
stop = make(chan struct{})
|
||||
getConfig = func() (*rest.Config, error) { return cfg, nil }
|
||||
newController = controller.New
|
||||
newManager = manager.New
|
||||
getGvk = apiutil.GVKForObject
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
close(stop)
|
||||
})
|
||||
|
||||
noop := reconcile.Func(func(req reconcile.Request) (reconcile.Result, error) { return reconcile.Result{}, nil })
|
||||
|
||||
Describe("New", func() {
|
||||
It("should return success if given valid objects", func() {
|
||||
instance, err := SimpleController().
|
||||
ForType(&appsv1.ReplicaSet{}).
|
||||
Owns(&appsv1.ReplicaSet{}).
|
||||
Build(noop)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(instance).NotTo(BeNil())
|
||||
})
|
||||
|
||||
It("should return an error if the Config is invalid", func() {
|
||||
getConfig = func() (*rest.Config, error) { return cfg, fmt.Errorf("expected error") }
|
||||
instance, err := SimpleController().
|
||||
ForType(&appsv1.ReplicaSet{}).
|
||||
Owns(&appsv1.ReplicaSet{}).
|
||||
Build(noop)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("expected error"))
|
||||
Expect(instance).To(BeNil())
|
||||
})
|
||||
|
||||
It("should return an error if there is no GVK for an object", func() {
|
||||
instance, err := SimpleController().
|
||||
ForType(&fakeType{}).
|
||||
Owns(&appsv1.ReplicaSet{}).
|
||||
Build(noop)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("no kind is registered for the type builder.fakeType"))
|
||||
Expect(instance).To(BeNil())
|
||||
|
||||
instance, err = SimpleController().
|
||||
ForType(&appsv1.ReplicaSet{}).
|
||||
Owns(&fakeType{}).
|
||||
Build(noop)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("no kind is registered for the type builder.fakeType"))
|
||||
Expect(instance).To(BeNil())
|
||||
})
|
||||
|
||||
It("should return an error if it cannot create the manager", func() {
|
||||
newManager = func(config *rest.Config, options manager.Options) (manager.Manager, error) {
|
||||
return nil, fmt.Errorf("expected error")
|
||||
}
|
||||
instance, err := SimpleController().
|
||||
ForType(&appsv1.ReplicaSet{}).
|
||||
Owns(&appsv1.ReplicaSet{}).
|
||||
Build(noop)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("expected error"))
|
||||
Expect(instance).To(BeNil())
|
||||
})
|
||||
|
||||
It("should return an error if it cannot create the controller", func() {
|
||||
newController = func(name string, mgr manager.Manager, options controller.Options) (
|
||||
controller.Controller, error) {
|
||||
return nil, fmt.Errorf("expected error")
|
||||
}
|
||||
instance, err := SimpleController().
|
||||
ForType(&appsv1.ReplicaSet{}).
|
||||
Owns(&appsv1.ReplicaSet{}).
|
||||
Build(noop)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("expected error"))
|
||||
Expect(instance).To(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Start", func() {
|
||||
It("should Reconcile objects", func(done Done) {
|
||||
By("Creating the application")
|
||||
ch := make(chan reconcile.Request)
|
||||
fn := reconcile.Func(func(req reconcile.Request) (reconcile.Result, error) {
|
||||
defer GinkgoRecover()
|
||||
ch <- req
|
||||
return reconcile.Result{}, nil
|
||||
})
|
||||
|
||||
instance, err := SimpleController().ForType(&appsv1.Deployment{}).
|
||||
WithConfig(cfg).
|
||||
Owns(&appsv1.ReplicaSet{}).
|
||||
Build(fn)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Starting the application")
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
Expect(instance.Start(stop)).NotTo(HaveOccurred())
|
||||
By("Stopping the application")
|
||||
}()
|
||||
|
||||
By("Creating a Deployment")
|
||||
// Expect a Reconcile when the Deployment is managedObjects.
|
||||
dep := &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "default",
|
||||
Name: "deploy-name",
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"foo": "bar"},
|
||||
},
|
||||
Template: corev1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"foo": "bar"}},
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "nginx",
|
||||
Image: "nginx",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
err = instance.GetClient().Create(context.TODO(), dep)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for the Deployment Reconcile")
|
||||
Expect(<-ch).To(Equal(reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{Namespace: "default", Name: "deploy-name"}}))
|
||||
|
||||
By("Creating a ReplicaSet")
|
||||
// Expect a Reconcile when an Owned object is managedObjects.
|
||||
t := true
|
||||
rs := &appsv1.ReplicaSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "default",
|
||||
Name: "rs-name",
|
||||
Labels: dep.Spec.Selector.MatchLabels,
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
{
|
||||
Name: "deploy-name",
|
||||
Kind: "Deployment",
|
||||
APIVersion: "apps/v1",
|
||||
Controller: &t,
|
||||
UID: dep.UID,
|
||||
},
|
||||
},
|
||||
},
|
||||
Spec: appsv1.ReplicaSetSpec{
|
||||
Selector: dep.Spec.Selector,
|
||||
Template: dep.Spec.Template,
|
||||
},
|
||||
}
|
||||
err = instance.GetClient().Create(context.TODO(), rs)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Waiting for the ReplicaSet Reconcile")
|
||||
Expect(<-ch).To(Equal(reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{Namespace: "default", Name: "deploy-name"}}))
|
||||
|
||||
close(done)
|
||||
}, 10)
|
||||
})
|
||||
})
|
||||
|
||||
var _ runtime.Object = &fakeType{}
|
||||
|
||||
type fakeType struct{}
|
||||
|
||||
func (*fakeType) GetObjectKind() schema.ObjectKind { return nil }
|
||||
func (*fakeType) DeepCopyObject() runtime.Object { return nil }
|
51
vendor/sigs.k8s.io/controller-runtime/pkg/builder/builder_suite_test.go
generated
vendored
Normal file
51
vendor/sigs.k8s.io/controller-runtime/pkg/builder/builder_suite_test.go
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
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 builder
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
)
|
||||
|
||||
func TestSource(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecsWithDefaultAndCustomReporters(t, "application Suite", []Reporter{envtest.NewlineReporter{}})
|
||||
}
|
||||
|
||||
var testenv *envtest.Environment
|
||||
var cfg *rest.Config
|
||||
|
||||
var _ = BeforeSuite(func(done Done) {
|
||||
logf.SetLogger(logf.ZapLoggerTo(GinkgoWriter, true))
|
||||
|
||||
testenv = &envtest.Environment{}
|
||||
|
||||
var err error
|
||||
cfg, err = testenv.Start()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
close(done)
|
||||
}, 60)
|
||||
|
||||
var _ = AfterSuite(func() {
|
||||
testenv.Stop()
|
||||
})
|
22
vendor/sigs.k8s.io/controller-runtime/pkg/builder/doc.go
generated
vendored
Normal file
22
vendor/sigs.k8s.io/controller-runtime/pkg/builder/doc.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
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 builder provides wraps other controller-runtime libraries and exposes simple
|
||||
// patterns for building common Controllers.
|
||||
//
|
||||
// Projects built with the builder package can trivially be rebased on top of the underlying
|
||||
// packages if the project requires more customized behavior in the future.
|
||||
package builder
|
98
vendor/sigs.k8s.io/controller-runtime/pkg/builder/example_test.go
generated
vendored
Normal file
98
vendor/sigs.k8s.io/controller-runtime/pkg/builder/example_test.go
generated
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
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 builder_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/runtime/signals"
|
||||
)
|
||||
|
||||
// NB: don't call SetLogger in init(), or else you'll mess up logging in the main suite.
|
||||
var log = logf.Log.WithName("builder-examples")
|
||||
|
||||
// This example creates a simple application Controller that is configured for ReplicaSets and Pods.
|
||||
//
|
||||
// * Create a new application for ReplicaSets that manages Pods owned by the ReplicaSet and calls into
|
||||
// ReplicaSetReconciler.
|
||||
//
|
||||
// * Start the application.
|
||||
func ExampleBuilder() {
|
||||
rs, err := builder.SimpleController().
|
||||
ForType(&appsv1.ReplicaSet{}). // ReplicaSet is the Application API
|
||||
Owns(&corev1.Pod{}). // ReplicaSet owns Pods created by it
|
||||
Build(&ReplicaSetReconciler{}) // Build
|
||||
if err != nil {
|
||||
log.Error(err, "Unable to build controller")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := rs.Start(signals.SetupSignalHandler()); err != nil {
|
||||
log.Error(err, "Unable to run controller")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// ReplicaSetReconciler is a simple Controller example implementation.
|
||||
type ReplicaSetReconciler struct {
|
||||
client.Client
|
||||
}
|
||||
|
||||
// Implement the business logic:
|
||||
// This function will be called when there is a change to a ReplicaSet or a Pod with an OwnerReference
|
||||
// to a ReplicaSet.
|
||||
//
|
||||
// * Read the ReplicaSet
|
||||
// * Read the Pods
|
||||
// * Set a Label on the ReplicaSet with the Pod count
|
||||
func (a *ReplicaSetReconciler) Reconcile(req reconcile.Request) (reconcile.Result, error) {
|
||||
// Read the ReplicaSet
|
||||
rs := &appsv1.ReplicaSet{}
|
||||
err := a.Get(context.TODO(), req.NamespacedName, rs)
|
||||
if err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
// List the Pods matching the PodTemplate Labels
|
||||
pods := &corev1.PodList{}
|
||||
err = a.List(context.TODO(), client.InNamespace(req.Namespace).MatchingLabels(rs.Spec.Template.Labels), pods)
|
||||
if err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
// Update the ReplicaSet
|
||||
rs.Labels["pod-count"] = fmt.Sprintf("%v", len(pods.Items))
|
||||
err = a.Update(context.TODO(), rs)
|
||||
if err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
|
||||
func (a *ReplicaSetReconciler) InjectClient(c client.Client) error {
|
||||
a.Client = c
|
||||
return nil
|
||||
}
|
121
vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go
generated
vendored
Normal file
121
vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go
generated
vendored
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
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 cache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
toolscache "k8s.io/client-go/tools/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/cache/internal"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
)
|
||||
|
||||
var log = logf.KBLog.WithName("object-cache")
|
||||
|
||||
// Cache implements CacheReader by reading objects from a cache populated by InformersMap
|
||||
type Cache interface {
|
||||
// Cache implements the client CacheReader
|
||||
client.Reader
|
||||
|
||||
// Cache implements InformersMap
|
||||
Informers
|
||||
}
|
||||
|
||||
// Informers knows how to create or fetch informers for different group-version-kinds.
|
||||
// It's safe to call GetInformer from multiple threads.
|
||||
type Informers interface {
|
||||
// GetInformer fetches or constructs an informer for the given object that corresponds to a single
|
||||
// API kind and resource.
|
||||
GetInformer(obj runtime.Object) (toolscache.SharedIndexInformer, error)
|
||||
|
||||
// GetInformerForKind is similar to GetInformer, except that it takes a group-version-kind, instead
|
||||
// of the underlying object.
|
||||
GetInformerForKind(gvk schema.GroupVersionKind) (toolscache.SharedIndexInformer, error)
|
||||
|
||||
// Start runs all the informers known to this cache until the given channel is closed.
|
||||
// It blocks.
|
||||
Start(stopCh <-chan struct{}) error
|
||||
|
||||
// WaitForCacheSync waits for all the caches to sync. Returns false if it could not sync a cache.
|
||||
WaitForCacheSync(stop <-chan struct{}) bool
|
||||
|
||||
// IndexField adds an index with the given field name on the given object type
|
||||
// by using the given function to extract the value for that field. If you want
|
||||
// compatibility with the Kubernetes API server, only return one key, and only use
|
||||
// fields that the API server supports. Otherwise, you can return multiple keys,
|
||||
// and "equality" in the field selector means that at least one key matches the value.
|
||||
IndexField(obj runtime.Object, field string, extractValue client.IndexerFunc) error
|
||||
}
|
||||
|
||||
// Options are the optional arguments for creating a new InformersMap object
|
||||
type Options struct {
|
||||
// Scheme is the scheme to use for mapping objects to GroupVersionKinds
|
||||
Scheme *runtime.Scheme
|
||||
|
||||
// Mapper is the RESTMapper to use for mapping GroupVersionKinds to Resources
|
||||
Mapper meta.RESTMapper
|
||||
|
||||
// Resync is the resync period. Defaults to defaultResyncTime.
|
||||
Resync *time.Duration
|
||||
|
||||
// Namespace restricts the cache's ListWatch to the desired namespace
|
||||
// Default watches all namespaces
|
||||
Namespace string
|
||||
}
|
||||
|
||||
var defaultResyncTime = 10 * time.Hour
|
||||
|
||||
// New initializes and returns a new Cache
|
||||
func New(config *rest.Config, opts Options) (Cache, error) {
|
||||
opts, err := defaultOpts(config, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
im := internal.NewInformersMap(config, opts.Scheme, opts.Mapper, *opts.Resync, opts.Namespace)
|
||||
return &informerCache{InformersMap: im}, nil
|
||||
}
|
||||
|
||||
func defaultOpts(config *rest.Config, opts Options) (Options, error) {
|
||||
// Use the default Kubernetes Scheme if unset
|
||||
if opts.Scheme == nil {
|
||||
opts.Scheme = scheme.Scheme
|
||||
}
|
||||
|
||||
// Construct a new Mapper if unset
|
||||
if opts.Mapper == nil {
|
||||
var err error
|
||||
opts.Mapper, err = apiutil.NewDiscoveryRESTMapper(config)
|
||||
if err != nil {
|
||||
log.WithName("setup").Error(err, "Failed to get API Group-Resources")
|
||||
return opts, fmt.Errorf("could not create RESTMapper from config")
|
||||
}
|
||||
}
|
||||
|
||||
// Default the resync period to 10 hours if unset
|
||||
if opts.Resync == nil {
|
||||
opts.Resync = &defaultResyncTime
|
||||
}
|
||||
return opts, nil
|
||||
}
|
56
vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache_suite_test.go
generated
vendored
Normal file
56
vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache_suite_test.go
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
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 cache_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
)
|
||||
|
||||
func TestSource(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecsWithDefaultAndCustomReporters(t, "Cache Suite", []Reporter{envtest.NewlineReporter{}})
|
||||
}
|
||||
|
||||
var testenv *envtest.Environment
|
||||
var cfg *rest.Config
|
||||
var clientset *kubernetes.Clientset
|
||||
|
||||
var _ = BeforeSuite(func(done Done) {
|
||||
logf.SetLogger(logf.ZapLoggerTo(GinkgoWriter, true))
|
||||
|
||||
testenv = &envtest.Environment{}
|
||||
|
||||
var err error
|
||||
cfg, err = testenv.Start()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
clientset, err = kubernetes.NewForConfig(cfg)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
close(done)
|
||||
}, 60)
|
||||
|
||||
var _ = AfterSuite(func() {
|
||||
testenv.Stop()
|
||||
})
|
656
vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache_test.go
generated
vendored
Normal file
656
vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache_test.go
generated
vendored
Normal file
@ -0,0 +1,656 @@
|
||||
/*
|
||||
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 cache_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
kcorev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
kmetav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
kscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
kcache "k8s.io/client-go/tools/cache"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
const testNamespaceOne = "test-namespace-1"
|
||||
const testNamespaceTwo = "test-namespace-2"
|
||||
|
||||
// TODO(community): Pull these helper functions into testenv.
|
||||
// Restart policy is included to allow indexing on that field.
|
||||
func createPod(name, namespace string, restartPolicy kcorev1.RestartPolicy) runtime.Object {
|
||||
three := int64(3)
|
||||
pod := &kcorev1.Pod{
|
||||
ObjectMeta: kmetav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Labels: map[string]string{
|
||||
"test-label": name,
|
||||
},
|
||||
},
|
||||
Spec: kcorev1.PodSpec{
|
||||
Containers: []kcorev1.Container{{Name: "nginx", Image: "nginx"}},
|
||||
RestartPolicy: restartPolicy,
|
||||
ActiveDeadlineSeconds: &three,
|
||||
},
|
||||
}
|
||||
cl, err := client.New(cfg, client.Options{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = cl.Create(context.Background(), pod)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return pod
|
||||
}
|
||||
|
||||
func deletePod(pod runtime.Object) {
|
||||
cl, err := client.New(cfg, client.Options{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = cl.Delete(context.Background(), pod)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
|
||||
var _ = Describe("Informer Cache", func() {
|
||||
|
||||
var (
|
||||
informerCache cache.Cache
|
||||
stop chan struct{}
|
||||
knownPod1 runtime.Object
|
||||
knownPod2 runtime.Object
|
||||
knownPod3 runtime.Object
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
stop = make(chan struct{})
|
||||
Expect(cfg).NotTo(BeNil())
|
||||
|
||||
By("creating three pods")
|
||||
// Includes restart policy since these objects are indexed on this field.
|
||||
knownPod1 = createPod("test-pod-1", testNamespaceOne, kcorev1.RestartPolicyNever)
|
||||
knownPod2 = createPod("test-pod-2", testNamespaceTwo, kcorev1.RestartPolicyAlways)
|
||||
knownPod3 = createPod("test-pod-3", testNamespaceTwo, kcorev1.RestartPolicyOnFailure)
|
||||
|
||||
By("creating the informer cache")
|
||||
var err error
|
||||
informerCache, err = cache.New(cfg, cache.Options{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
By("running the cache and waiting for it to sync")
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
Expect(informerCache.Start(stop)).To(Succeed())
|
||||
}()
|
||||
Expect(informerCache.WaitForCacheSync(stop)).To(BeTrue())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
By("cleaning up created pods")
|
||||
deletePod(knownPod1)
|
||||
deletePod(knownPod2)
|
||||
deletePod(knownPod3)
|
||||
|
||||
close(stop)
|
||||
})
|
||||
|
||||
Describe("as a Reader", func() {
|
||||
Context("with structured objects", func() {
|
||||
|
||||
It("should be able to list objects that haven't been watched previously", func() {
|
||||
By("listing all services in the cluster")
|
||||
listObj := &kcorev1.ServiceList{}
|
||||
Expect(informerCache.List(context.Background(), nil, listObj)).To(Succeed())
|
||||
|
||||
By("verifying that the returned list contains the Kubernetes service")
|
||||
// NB: kubernetes default service is automatically created in testenv.
|
||||
Expect(listObj.Items).NotTo(BeEmpty())
|
||||
hasKubeService := false
|
||||
for _, svc := range listObj.Items {
|
||||
if svc.Namespace == "default" && svc.Name == "kubernetes" {
|
||||
hasKubeService = true
|
||||
break
|
||||
}
|
||||
}
|
||||
Expect(hasKubeService).To(BeTrue())
|
||||
})
|
||||
|
||||
It("should be able to get objects that haven't been watched previously", func() {
|
||||
By("getting the Kubernetes service")
|
||||
svc := &kcorev1.Service{}
|
||||
svcKey := client.ObjectKey{Namespace: "default", Name: "kubernetes"}
|
||||
Expect(informerCache.Get(context.Background(), svcKey, svc)).To(Succeed())
|
||||
|
||||
By("verifying that the returned service looks reasonable")
|
||||
Expect(svc.Name).To(Equal("kubernetes"))
|
||||
Expect(svc.Namespace).To(Equal("default"))
|
||||
})
|
||||
|
||||
It("should support filtering by labels in a single namespace", func() {
|
||||
By("listing pods with a particular label")
|
||||
// NB: each pod has a "test-label": <pod-name>
|
||||
out := kcorev1.PodList{}
|
||||
lo := &client.ListOptions{}
|
||||
lo.InNamespace(testNamespaceTwo)
|
||||
lo.MatchingLabels(map[string]string{"test-label": "test-pod-2"})
|
||||
Expect(informerCache.List(context.Background(), lo, &out)).To(Succeed())
|
||||
|
||||
By("verifying the returned pods have the correct label")
|
||||
Expect(out.Items).NotTo(BeEmpty())
|
||||
Expect(out.Items).Should(HaveLen(1))
|
||||
actual := out.Items[0]
|
||||
Expect(actual.Labels["test-label"]).To(Equal("test-pod-2"))
|
||||
})
|
||||
|
||||
It("should support filtering by labels from multiple namespaces", func() {
|
||||
By("creating another pod with the same label but different namespace")
|
||||
anotherPod := createPod("test-pod-2", testNamespaceOne, kcorev1.RestartPolicyAlways)
|
||||
|
||||
By("listing pods with a particular label")
|
||||
// NB: each pod has a "test-label": <pod-name>
|
||||
out := kcorev1.PodList{}
|
||||
labels := map[string]string{"test-label": "test-pod-2"}
|
||||
lo := &client.ListOptions{}
|
||||
lo.MatchingLabels(labels)
|
||||
Expect(informerCache.List(context.Background(), lo, &out)).To(Succeed())
|
||||
|
||||
By("verifying multiple pods with the same label in different namespaces are returned")
|
||||
Expect(out.Items).NotTo(BeEmpty())
|
||||
Expect(out.Items).Should(HaveLen(2))
|
||||
for _, actual := range out.Items {
|
||||
Expect(actual.Labels["test-label"]).To(Equal("test-pod-2"))
|
||||
}
|
||||
|
||||
deletePod(anotherPod)
|
||||
})
|
||||
|
||||
It("should be able to list objects by namespace", func() {
|
||||
By("listing pods in test-namespace-1")
|
||||
listObj := &kcorev1.PodList{}
|
||||
lo := &client.ListOptions{}
|
||||
lo.InNamespace(testNamespaceOne)
|
||||
Expect(informerCache.List(context.Background(), lo, listObj)).To(Succeed())
|
||||
|
||||
By("verifying that the returned pods are in test-namespace-1")
|
||||
Expect(listObj.Items).NotTo(BeEmpty())
|
||||
Expect(listObj.Items).Should(HaveLen(1))
|
||||
actual := listObj.Items[0]
|
||||
Expect(actual.Namespace).To(Equal(testNamespaceOne))
|
||||
})
|
||||
|
||||
It("should be able to restrict cache to a namespace", func() {
|
||||
By("creating a namespaced cache")
|
||||
namespacedCache, err := cache.New(cfg, cache.Options{Namespace: testNamespaceOne})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("running the cache and waiting for it to sync")
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
Expect(namespacedCache.Start(stop)).To(Succeed())
|
||||
}()
|
||||
Expect(namespacedCache.WaitForCacheSync(stop)).NotTo(BeFalse())
|
||||
|
||||
By("listing pods in all namespaces")
|
||||
out := &kcorev1.PodList{}
|
||||
Expect(namespacedCache.List(context.Background(), nil, out)).To(Succeed())
|
||||
|
||||
By("verifying the returned pod is from the watched namespace")
|
||||
Expect(out.Items).NotTo(BeEmpty())
|
||||
Expect(out.Items).Should(HaveLen(1))
|
||||
Expect(out.Items[0].Namespace).To(Equal(testNamespaceOne))
|
||||
|
||||
By("listing all namespaces - should still be able to get a cluster-scoped resource")
|
||||
namespaceList := &kcorev1.NamespaceList{}
|
||||
Expect(namespacedCache.List(context.Background(), nil, namespaceList)).To(Succeed())
|
||||
|
||||
By("verifying the namespace list is not empty")
|
||||
Expect(namespaceList.Items).NotTo(BeEmpty())
|
||||
})
|
||||
|
||||
It("should deep copy the object unless told otherwise", func() {
|
||||
By("retrieving a specific pod from the cache")
|
||||
out := &kcorev1.Pod{}
|
||||
podKey := client.ObjectKey{Name: "test-pod-2", Namespace: testNamespaceTwo}
|
||||
Expect(informerCache.Get(context.Background(), podKey, out)).To(Succeed())
|
||||
|
||||
By("verifying the retrieved pod is equal to a known pod")
|
||||
Expect(out).To(Equal(knownPod2))
|
||||
|
||||
By("altering a field in the retrieved pod")
|
||||
*out.Spec.ActiveDeadlineSeconds = 4
|
||||
|
||||
By("verifying the pods are no longer equal")
|
||||
Expect(out).NotTo(Equal(knownPod2))
|
||||
})
|
||||
|
||||
It("should return an error if the object is not found", func() {
|
||||
By("getting a service that does not exists")
|
||||
svc := &kcorev1.Service{}
|
||||
svcKey := client.ObjectKey{Namespace: "unknown", Name: "unknown"}
|
||||
|
||||
By("verifying that an error is returned")
|
||||
err := informerCache.Get(context.Background(), svcKey, svc)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(errors.IsNotFound(err)).To(BeTrue())
|
||||
})
|
||||
})
|
||||
Context("with unstructured objects", func() {
|
||||
It("should be able to list objects that haven't been watched previously", func() {
|
||||
By("listing all services in the cluster")
|
||||
listObj := &unstructured.UnstructuredList{}
|
||||
listObj.SetGroupVersionKind(schema.GroupVersionKind{
|
||||
Group: "",
|
||||
Version: "v1",
|
||||
Kind: "ServiceList",
|
||||
})
|
||||
err := informerCache.List(context.Background(), nil, listObj)
|
||||
Expect(err).To(Succeed())
|
||||
|
||||
By("verifying that the returned list contains the Kubernetes service")
|
||||
// NB: kubernetes default service is automatically created in testenv.
|
||||
Expect(listObj.Items).NotTo(BeEmpty())
|
||||
hasKubeService := false
|
||||
for _, svc := range listObj.Items {
|
||||
if svc.GetNamespace() == "default" && svc.GetName() == "kubernetes" {
|
||||
hasKubeService = true
|
||||
break
|
||||
}
|
||||
}
|
||||
Expect(hasKubeService).To(BeTrue())
|
||||
})
|
||||
|
||||
It("should be able to get objects that haven't been watched previously", func() {
|
||||
By("getting the Kubernetes service")
|
||||
svc := &unstructured.Unstructured{}
|
||||
svc.SetGroupVersionKind(schema.GroupVersionKind{
|
||||
Group: "",
|
||||
Version: "v1",
|
||||
Kind: "Service",
|
||||
})
|
||||
svcKey := client.ObjectKey{Namespace: "default", Name: "kubernetes"}
|
||||
Expect(informerCache.Get(context.Background(), svcKey, svc)).To(Succeed())
|
||||
|
||||
By("verifying that the returned service looks reasonable")
|
||||
Expect(svc.GetName()).To(Equal("kubernetes"))
|
||||
Expect(svc.GetNamespace()).To(Equal("default"))
|
||||
})
|
||||
|
||||
It("should support filtering by labels in a single namespace", func() {
|
||||
By("listing pods with a particular label")
|
||||
// NB: each pod has a "test-label": <pod-name>
|
||||
out := unstructured.UnstructuredList{}
|
||||
out.SetGroupVersionKind(schema.GroupVersionKind{
|
||||
Group: "",
|
||||
Version: "v1",
|
||||
Kind: "PodList",
|
||||
})
|
||||
lo := &client.ListOptions{}
|
||||
lo.InNamespace(testNamespaceTwo)
|
||||
lo.MatchingLabels(map[string]string{"test-label": "test-pod-2"})
|
||||
err := informerCache.List(context.Background(), lo, &out)
|
||||
Expect(err).To(Succeed())
|
||||
|
||||
By("verifying the returned pods have the correct label")
|
||||
Expect(out.Items).NotTo(BeEmpty())
|
||||
Expect(out.Items).Should(HaveLen(1))
|
||||
actual := out.Items[0]
|
||||
Expect(actual.GetLabels()["test-label"]).To(Equal("test-pod-2"))
|
||||
})
|
||||
|
||||
It("should support filtering by labels from multiple namespaces", func() {
|
||||
By("creating another pod with the same label but different namespace")
|
||||
anotherPod := createPod("test-pod-2", testNamespaceOne, kcorev1.RestartPolicyAlways)
|
||||
|
||||
By("listing pods with a particular label")
|
||||
// NB: each pod has a "test-label": <pod-name>
|
||||
out := unstructured.UnstructuredList{}
|
||||
out.SetGroupVersionKind(schema.GroupVersionKind{
|
||||
Group: "",
|
||||
Version: "v1",
|
||||
Kind: "PodList",
|
||||
})
|
||||
labels := map[string]string{"test-label": "test-pod-2"}
|
||||
lo := &client.ListOptions{}
|
||||
lo.MatchingLabels(labels)
|
||||
err := informerCache.List(context.Background(), lo, &out)
|
||||
Expect(err).To(Succeed())
|
||||
|
||||
By("verifying multiple pods with the same label in different namespaces are returned")
|
||||
Expect(out.Items).NotTo(BeEmpty())
|
||||
Expect(out.Items).Should(HaveLen(2))
|
||||
for _, actual := range out.Items {
|
||||
Expect(actual.GetLabels()["test-label"]).To(Equal("test-pod-2"))
|
||||
}
|
||||
|
||||
deletePod(anotherPod)
|
||||
})
|
||||
|
||||
It("should be able to list objects by namespace", func() {
|
||||
By("listing pods in test-namespace-1")
|
||||
listObj := &unstructured.UnstructuredList{}
|
||||
listObj.SetGroupVersionKind(schema.GroupVersionKind{
|
||||
Group: "",
|
||||
Version: "v1",
|
||||
Kind: "PodList",
|
||||
})
|
||||
lo := &client.ListOptions{}
|
||||
lo.InNamespace(testNamespaceOne)
|
||||
err := informerCache.List(context.Background(), lo, listObj)
|
||||
Expect(err).To(Succeed())
|
||||
|
||||
By("verifying that the returned pods are in test-namespace-1")
|
||||
Expect(listObj.Items).NotTo(BeEmpty())
|
||||
Expect(listObj.Items).Should(HaveLen(1))
|
||||
actual := listObj.Items[0]
|
||||
Expect(actual.GetNamespace()).To(Equal(testNamespaceOne))
|
||||
})
|
||||
|
||||
It("should be able to restrict cache to a namespace", func() {
|
||||
By("creating a namespaced cache")
|
||||
namespacedCache, err := cache.New(cfg, cache.Options{Namespace: testNamespaceOne})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("running the cache and waiting for it to sync")
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
Expect(namespacedCache.Start(stop)).To(Succeed())
|
||||
}()
|
||||
Expect(namespacedCache.WaitForCacheSync(stop)).NotTo(BeFalse())
|
||||
|
||||
By("listing pods in all namespaces")
|
||||
out := &unstructured.UnstructuredList{}
|
||||
out.SetGroupVersionKind(schema.GroupVersionKind{
|
||||
Group: "",
|
||||
Version: "v1",
|
||||
Kind: "PodList",
|
||||
})
|
||||
Expect(namespacedCache.List(context.Background(), nil, out)).To(Succeed())
|
||||
|
||||
By("verifying the returned pod is from the watched namespace")
|
||||
Expect(out.Items).NotTo(BeEmpty())
|
||||
Expect(out.Items).Should(HaveLen(1))
|
||||
Expect(out.Items[0].GetNamespace()).To(Equal(testNamespaceOne))
|
||||
|
||||
By("listing all namespaces - should still be able to get a cluster-scoped resource")
|
||||
namespaceList := &unstructured.UnstructuredList{}
|
||||
namespaceList.SetGroupVersionKind(schema.GroupVersionKind{
|
||||
Group: "",
|
||||
Version: "v1",
|
||||
Kind: "NamespaceList",
|
||||
})
|
||||
Expect(namespacedCache.List(context.Background(), nil, namespaceList)).To(Succeed())
|
||||
|
||||
By("verifying the namespace list is not empty")
|
||||
Expect(namespaceList.Items).NotTo(BeEmpty())
|
||||
})
|
||||
|
||||
It("should deep copy the object unless told otherwise", func() {
|
||||
By("retrieving a specific pod from the cache")
|
||||
out := &unstructured.Unstructured{}
|
||||
out.SetGroupVersionKind(schema.GroupVersionKind{
|
||||
Group: "",
|
||||
Version: "v1",
|
||||
Kind: "Pod",
|
||||
})
|
||||
uKnownPod2 := &unstructured.Unstructured{}
|
||||
kscheme.Scheme.Convert(knownPod2, uKnownPod2, nil)
|
||||
|
||||
podKey := client.ObjectKey{Name: "test-pod-2", Namespace: testNamespaceTwo}
|
||||
Expect(informerCache.Get(context.Background(), podKey, out)).To(Succeed())
|
||||
|
||||
By("verifying the retrieved pod is equal to a known pod")
|
||||
Expect(out).To(Equal(uKnownPod2))
|
||||
|
||||
By("altering a field in the retrieved pod")
|
||||
m, _ := out.Object["spec"].(map[string]interface{})
|
||||
m["activeDeadlineSeconds"] = 4
|
||||
|
||||
By("verifying the pods are no longer equal")
|
||||
Expect(out).NotTo(Equal(knownPod2))
|
||||
})
|
||||
|
||||
It("should return an error if the object is not found", func() {
|
||||
By("getting a service that does not exists")
|
||||
svc := &unstructured.Unstructured{}
|
||||
svc.SetGroupVersionKind(schema.GroupVersionKind{
|
||||
Group: "",
|
||||
Version: "v1",
|
||||
Kind: "Service",
|
||||
})
|
||||
svcKey := client.ObjectKey{Namespace: "unknown", Name: "unknown"}
|
||||
|
||||
By("verifying that an error is returned")
|
||||
err := informerCache.Get(context.Background(), svcKey, svc)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(errors.IsNotFound(err)).To(BeTrue())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("as an Informer", func() {
|
||||
Context("with structured objects", func() {
|
||||
It("should be able to get informer for the object", func(done Done) {
|
||||
By("getting a shared index informer for a pod")
|
||||
pod := &kcorev1.Pod{
|
||||
ObjectMeta: kmetav1.ObjectMeta{
|
||||
Name: "informer-obj",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: kcorev1.PodSpec{
|
||||
Containers: []kcorev1.Container{
|
||||
{
|
||||
Name: "nginx",
|
||||
Image: "nginx",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
sii, err := informerCache.GetInformer(pod)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(sii).NotTo(BeNil())
|
||||
Expect(sii.HasSynced()).To(BeTrue())
|
||||
|
||||
By("adding an event handler listening for object creation which sends the object to a channel")
|
||||
out := make(chan interface{})
|
||||
addFunc := func(obj interface{}) {
|
||||
out <- obj
|
||||
}
|
||||
sii.AddEventHandler(kcache.ResourceEventHandlerFuncs{AddFunc: addFunc})
|
||||
|
||||
By("adding an object")
|
||||
cl, err := client.New(cfg, client.Options{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(cl.Create(context.Background(), pod)).To(Succeed())
|
||||
|
||||
By("verifying the object is received on the channel")
|
||||
Eventually(out).Should(Receive(Equal(pod)))
|
||||
close(done)
|
||||
})
|
||||
|
||||
// TODO: Add a test for when GVK is not in Scheme. Does code support informer for unstructured object?
|
||||
It("should be able to get an informer by group/version/kind", func(done Done) {
|
||||
By("getting an shared index informer for gvk = core/v1/pod")
|
||||
gvk := schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Pod"}
|
||||
sii, err := informerCache.GetInformerForKind(gvk)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(sii).NotTo(BeNil())
|
||||
Expect(sii.HasSynced()).To(BeTrue())
|
||||
|
||||
By("adding an event handler listening for object creation which sends the object to a channel")
|
||||
out := make(chan interface{})
|
||||
addFunc := func(obj interface{}) {
|
||||
out <- obj
|
||||
}
|
||||
sii.AddEventHandler(kcache.ResourceEventHandlerFuncs{AddFunc: addFunc})
|
||||
|
||||
By("adding an object")
|
||||
cl, err := client.New(cfg, client.Options{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
pod := &kcorev1.Pod{
|
||||
ObjectMeta: kmetav1.ObjectMeta{
|
||||
Name: "informer-gvk",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: kcorev1.PodSpec{
|
||||
Containers: []kcorev1.Container{
|
||||
{
|
||||
Name: "nginx",
|
||||
Image: "nginx",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
Expect(cl.Create(context.Background(), pod)).To(Succeed())
|
||||
|
||||
By("verifying the object is received on the channel")
|
||||
Eventually(out).Should(Receive(Equal(pod)))
|
||||
close(done)
|
||||
})
|
||||
|
||||
It("should be able to index an object field then retrieve objects by that field", func() {
|
||||
By("creating the cache")
|
||||
informer, err := cache.New(cfg, cache.Options{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("indexing the restartPolicy field of the Pod object before starting")
|
||||
pod := &kcorev1.Pod{}
|
||||
indexFunc := func(obj runtime.Object) []string {
|
||||
return []string{string(obj.(*kcorev1.Pod).Spec.RestartPolicy)}
|
||||
}
|
||||
Expect(informer.IndexField(pod, "spec.restartPolicy", indexFunc)).To(Succeed())
|
||||
|
||||
By("running the cache and waiting for it to sync")
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
Expect(informer.Start(stop)).To(Succeed())
|
||||
}()
|
||||
Expect(informer.WaitForCacheSync(stop)).NotTo(BeFalse())
|
||||
|
||||
By("listing Pods with restartPolicyOnFailure")
|
||||
listObj := &kcorev1.PodList{}
|
||||
lo := &client.ListOptions{}
|
||||
lo.MatchingField("spec.restartPolicy", "OnFailure")
|
||||
Expect(informer.List(context.Background(), lo, listObj)).To(Succeed())
|
||||
|
||||
By("verifying that the returned pods have correct restart policy")
|
||||
Expect(listObj.Items).NotTo(BeEmpty())
|
||||
Expect(listObj.Items).Should(HaveLen(1))
|
||||
actual := listObj.Items[0]
|
||||
Expect(actual.Name).To(Equal("test-pod-3"))
|
||||
})
|
||||
})
|
||||
Context("with unstructured objects", func() {
|
||||
It("should be able to get informer for the object", func(done Done) {
|
||||
By("getting a shared index informer for a pod")
|
||||
|
||||
pod := &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"spec": map[string]interface{}{
|
||||
"containers": []map[string]interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "nginx",
|
||||
"image": "nginx",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
pod.SetName("informer-obj2")
|
||||
pod.SetNamespace("default")
|
||||
pod.SetGroupVersionKind(schema.GroupVersionKind{
|
||||
Group: "",
|
||||
Version: "v1",
|
||||
Kind: "Pod",
|
||||
})
|
||||
sii, err := informerCache.GetInformer(pod)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(sii).NotTo(BeNil())
|
||||
Expect(sii.HasSynced()).To(BeTrue())
|
||||
|
||||
By("adding an event handler listening for object creation which sends the object to a channel")
|
||||
out := make(chan interface{})
|
||||
addFunc := func(obj interface{}) {
|
||||
out <- obj
|
||||
}
|
||||
sii.AddEventHandler(kcache.ResourceEventHandlerFuncs{AddFunc: addFunc})
|
||||
|
||||
By("adding an object")
|
||||
cl, err := client.New(cfg, client.Options{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(cl.Create(context.Background(), pod)).To(Succeed())
|
||||
|
||||
By("verifying the object is received on the channel")
|
||||
Eventually(out).Should(Receive(Equal(pod)))
|
||||
close(done)
|
||||
}, 3)
|
||||
|
||||
It("should be able to index an object field then retrieve objects by that field", func() {
|
||||
By("creating the cache")
|
||||
informer, err := cache.New(cfg, cache.Options{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("indexing the restartPolicy field of the Pod object before starting")
|
||||
pod := &unstructured.Unstructured{}
|
||||
pod.SetGroupVersionKind(schema.GroupVersionKind{
|
||||
Group: "",
|
||||
Version: "v1",
|
||||
Kind: "Pod",
|
||||
})
|
||||
indexFunc := func(obj runtime.Object) []string {
|
||||
s, ok := obj.(*unstructured.Unstructured).Object["spec"]
|
||||
if !ok {
|
||||
return []string{}
|
||||
}
|
||||
m, ok := s.(map[string]interface{})
|
||||
if !ok {
|
||||
return []string{}
|
||||
}
|
||||
return []string{fmt.Sprintf("%v", m["restartPolicy"])}
|
||||
}
|
||||
Expect(informer.IndexField(pod, "spec.restartPolicy", indexFunc)).To(Succeed())
|
||||
|
||||
By("running the cache and waiting for it to sync")
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
Expect(informer.Start(stop)).To(Succeed())
|
||||
}()
|
||||
Expect(informer.WaitForCacheSync(stop)).NotTo(BeFalse())
|
||||
|
||||
By("listing Pods with restartPolicyOnFailure")
|
||||
listObj := &unstructured.UnstructuredList{}
|
||||
listObj.SetGroupVersionKind(schema.GroupVersionKind{
|
||||
Group: "",
|
||||
Version: "v1",
|
||||
Kind: "PodList",
|
||||
})
|
||||
lo := &client.ListOptions{}
|
||||
lo.MatchingField("spec.restartPolicy", "OnFailure")
|
||||
err = informer.List(context.Background(), lo, listObj)
|
||||
Expect(err).To(Succeed())
|
||||
|
||||
By("verifying that the returned pods have correct restart policy")
|
||||
Expect(listObj.Items).NotTo(BeEmpty())
|
||||
Expect(listObj.Items).Should(HaveLen(1))
|
||||
actual := listObj.Items[0]
|
||||
Expect(actual.GetName()).To(Equal("test-pod-3"))
|
||||
}, 3)
|
||||
})
|
||||
})
|
||||
})
|
178
vendor/sigs.k8s.io/controller-runtime/pkg/cache/informer_cache.go
generated
vendored
Normal file
178
vendor/sigs.k8s.io/controller-runtime/pkg/cache/informer_cache.go
generated
vendored
Normal file
@ -0,0 +1,178 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/cache/internal"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
||||
)
|
||||
|
||||
var (
|
||||
_ Informers = &informerCache{}
|
||||
_ client.Reader = &informerCache{}
|
||||
_ Cache = &informerCache{}
|
||||
)
|
||||
|
||||
// informerCache is a Kubernetes Object cache populated from InformersMap. informerCache wraps an InformersMap.
|
||||
type informerCache struct {
|
||||
*internal.InformersMap
|
||||
}
|
||||
|
||||
// Get implements Reader
|
||||
func (ip *informerCache) Get(ctx context.Context, key client.ObjectKey, out runtime.Object) error {
|
||||
gvk, err := apiutil.GVKForObject(out, ip.Scheme)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cache, err := ip.InformersMap.Get(gvk, out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cache.Reader.Get(ctx, key, out)
|
||||
}
|
||||
|
||||
// List implements Reader
|
||||
func (ip *informerCache) List(ctx context.Context, opts *client.ListOptions, out runtime.Object) error {
|
||||
gvk, err := apiutil.GVKForObject(out, ip.Scheme)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(gvk.Kind, "List") {
|
||||
return fmt.Errorf("non-list type %T (kind %q) passed as output", out, gvk)
|
||||
}
|
||||
// we need the non-list GVK, so chop off the "List" from the end of the kind
|
||||
gvk.Kind = gvk.Kind[:len(gvk.Kind)-4]
|
||||
_, isUnstructured := out.(*unstructured.UnstructuredList)
|
||||
var cacheTypeObj runtime.Object
|
||||
if isUnstructured {
|
||||
u := &unstructured.Unstructured{}
|
||||
u.SetGroupVersionKind(gvk)
|
||||
cacheTypeObj = u
|
||||
} else {
|
||||
itemsPtr, err := apimeta.GetItemsPtr(out)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
// http://knowyourmeme.com/memes/this-is-fine
|
||||
elemType := reflect.Indirect(reflect.ValueOf(itemsPtr)).Type().Elem()
|
||||
cacheTypeValue := reflect.Zero(reflect.PtrTo(elemType))
|
||||
var ok bool
|
||||
cacheTypeObj, ok = cacheTypeValue.Interface().(runtime.Object)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot get cache for %T, its element %T is not a runtime.Object", out, cacheTypeValue.Interface())
|
||||
}
|
||||
}
|
||||
|
||||
cache, err := ip.InformersMap.Get(gvk, cacheTypeObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return cache.Reader.List(ctx, opts, out)
|
||||
}
|
||||
|
||||
// GetInformerForKind returns the informer for the GroupVersionKind
|
||||
func (ip *informerCache) GetInformerForKind(gvk schema.GroupVersionKind) (cache.SharedIndexInformer, error) {
|
||||
// Map the gvk to an object
|
||||
obj, err := ip.Scheme.New(gvk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
i, err := ip.InformersMap.Get(gvk, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return i.Informer, err
|
||||
}
|
||||
|
||||
// GetInformer returns the informer for the obj
|
||||
func (ip *informerCache) GetInformer(obj runtime.Object) (cache.SharedIndexInformer, error) {
|
||||
gvk, err := apiutil.GVKForObject(obj, ip.Scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
i, err := ip.InformersMap.Get(gvk, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return i.Informer, err
|
||||
}
|
||||
|
||||
// IndexField adds an indexer to the underlying cache, using extraction function to get
|
||||
// value(s) from the given field. This index can then be used by passing a field selector
|
||||
// to List. For one-to-one compatibility with "normal" field selectors, only return one value.
|
||||
// The values may be anything. They will automatically be prefixed with the namespace of the
|
||||
// given object, if present. The objects passed are guaranteed to be objects of the correct type.
|
||||
func (ip *informerCache) IndexField(obj runtime.Object, field string, extractValue client.IndexerFunc) error {
|
||||
informer, err := ip.GetInformer(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return indexByField(informer.GetIndexer(), field, extractValue)
|
||||
}
|
||||
|
||||
func indexByField(indexer cache.Indexer, field string, extractor client.IndexerFunc) error {
|
||||
indexFunc := func(objRaw interface{}) ([]string, error) {
|
||||
// TODO(directxman12): check if this is the correct type?
|
||||
obj, isObj := objRaw.(runtime.Object)
|
||||
if !isObj {
|
||||
return nil, fmt.Errorf("object of type %T is not an Object", objRaw)
|
||||
}
|
||||
meta, err := apimeta.Accessor(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ns := meta.GetNamespace()
|
||||
|
||||
rawVals := extractor(obj)
|
||||
var vals []string
|
||||
if ns == "" {
|
||||
// if we're not doubling the keys for the namespaced case, just re-use what was returned to us
|
||||
vals = rawVals
|
||||
} else {
|
||||
// if we need to add non-namespaced versions too, double the length
|
||||
vals = make([]string, len(rawVals)*2)
|
||||
}
|
||||
for i, rawVal := range rawVals {
|
||||
// save a namespaced variant, so that we can ask
|
||||
// "what are all the object matching a given index *in a given namespace*"
|
||||
vals[i] = internal.KeyToNamespacedKey(ns, rawVal)
|
||||
if ns != "" {
|
||||
// if we have a namespace, also inject a special index key for listing
|
||||
// regardless of the object namespace
|
||||
vals[i+len(rawVals)] = internal.KeyToNamespacedKey("", rawVal)
|
||||
}
|
||||
}
|
||||
|
||||
return vals, nil
|
||||
}
|
||||
|
||||
return indexer.AddIndexers(cache.Indexers{internal.FieldIndexName(field): indexFunc})
|
||||
}
|
141
vendor/sigs.k8s.io/controller-runtime/pkg/cache/informertest/fake_cache.go
generated
vendored
Normal file
141
vendor/sigs.k8s.io/controller-runtime/pkg/cache/informertest/fake_cache.go
generated
vendored
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package informertest
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
toolscache "k8s.io/client-go/tools/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllertest"
|
||||
)
|
||||
|
||||
var _ cache.Cache = &FakeInformers{}
|
||||
|
||||
// FakeInformers is a fake implementation of Informers
|
||||
type FakeInformers struct {
|
||||
InformersByGVK map[schema.GroupVersionKind]toolscache.SharedIndexInformer
|
||||
Scheme *runtime.Scheme
|
||||
Error error
|
||||
Synced *bool
|
||||
}
|
||||
|
||||
// GetInformerForKind implements Informers
|
||||
func (c *FakeInformers) GetInformerForKind(gvk schema.GroupVersionKind) (toolscache.SharedIndexInformer, error) {
|
||||
if c.Scheme == nil {
|
||||
c.Scheme = scheme.Scheme
|
||||
}
|
||||
obj, err := c.Scheme.New(gvk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.informerFor(gvk, obj)
|
||||
}
|
||||
|
||||
// FakeInformerForKind implements Informers
|
||||
func (c *FakeInformers) FakeInformerForKind(gvk schema.GroupVersionKind) (*controllertest.FakeInformer, error) {
|
||||
if c.Scheme == nil {
|
||||
c.Scheme = scheme.Scheme
|
||||
}
|
||||
obj, err := c.Scheme.New(gvk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
i, err := c.informerFor(gvk, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return i.(*controllertest.FakeInformer), nil
|
||||
}
|
||||
|
||||
// GetInformer implements Informers
|
||||
func (c *FakeInformers) GetInformer(obj runtime.Object) (toolscache.SharedIndexInformer, error) {
|
||||
if c.Scheme == nil {
|
||||
c.Scheme = scheme.Scheme
|
||||
}
|
||||
gvks, _, err := c.Scheme.ObjectKinds(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gvk := gvks[0]
|
||||
return c.informerFor(gvk, obj)
|
||||
}
|
||||
|
||||
// WaitForCacheSync implements Informers
|
||||
func (c *FakeInformers) WaitForCacheSync(stop <-chan struct{}) bool {
|
||||
if c.Synced == nil {
|
||||
return true
|
||||
}
|
||||
return *c.Synced
|
||||
}
|
||||
|
||||
// FakeInformerFor implements Informers
|
||||
func (c *FakeInformers) FakeInformerFor(obj runtime.Object) (*controllertest.FakeInformer, error) {
|
||||
if c.Scheme == nil {
|
||||
c.Scheme = scheme.Scheme
|
||||
}
|
||||
gvks, _, err := c.Scheme.ObjectKinds(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gvk := gvks[0]
|
||||
i, err := c.informerFor(gvk, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return i.(*controllertest.FakeInformer), nil
|
||||
}
|
||||
|
||||
func (c *FakeInformers) informerFor(gvk schema.GroupVersionKind, _ runtime.Object) (toolscache.SharedIndexInformer, error) {
|
||||
if c.Error != nil {
|
||||
return nil, c.Error
|
||||
}
|
||||
if c.InformersByGVK == nil {
|
||||
c.InformersByGVK = map[schema.GroupVersionKind]toolscache.SharedIndexInformer{}
|
||||
}
|
||||
informer, ok := c.InformersByGVK[gvk]
|
||||
if ok {
|
||||
return informer, nil
|
||||
}
|
||||
|
||||
c.InformersByGVK[gvk] = &controllertest.FakeInformer{}
|
||||
return c.InformersByGVK[gvk], nil
|
||||
}
|
||||
|
||||
// Start implements Informers
|
||||
func (c *FakeInformers) Start(stopCh <-chan struct{}) error {
|
||||
return c.Error
|
||||
}
|
||||
|
||||
// IndexField implements Cache
|
||||
func (c *FakeInformers) IndexField(obj runtime.Object, field string, extractValue client.IndexerFunc) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get implements Cache
|
||||
func (c *FakeInformers) Get(ctx context.Context, key client.ObjectKey, obj runtime.Object) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// List implements Cache
|
||||
func (c *FakeInformers) List(ctx context.Context, opts *client.ListOptions, list runtime.Object) error {
|
||||
return nil
|
||||
}
|
186
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/cache_reader.go
generated
vendored
Normal file
186
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/cache_reader.go
generated
vendored
Normal file
@ -0,0 +1,186 @@
|
||||
/*
|
||||
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 internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/selection"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
// CacheReader is a CacheReader
|
||||
var _ client.Reader = &CacheReader{}
|
||||
|
||||
// CacheReader wraps a cache.Index to implement the client.CacheReader interface for a single type
|
||||
type CacheReader struct {
|
||||
// indexer is the underlying indexer wrapped by this cache.
|
||||
indexer cache.Indexer
|
||||
|
||||
// groupVersionKind is the group-version-kind of the resource.
|
||||
groupVersionKind schema.GroupVersionKind
|
||||
}
|
||||
|
||||
// Get checks the indexer for the object and writes a copy of it if found
|
||||
func (c *CacheReader) Get(_ context.Context, key client.ObjectKey, out runtime.Object) error {
|
||||
storeKey := objectKeyToStoreKey(key)
|
||||
|
||||
// Lookup the object from the indexer cache
|
||||
obj, exists, err := c.indexer.GetByKey(storeKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Not found, return an error
|
||||
if !exists {
|
||||
// Resource gets transformed into Kind in the error anyway, so this is fine
|
||||
return errors.NewNotFound(schema.GroupResource{
|
||||
Group: c.groupVersionKind.Group,
|
||||
Resource: c.groupVersionKind.Kind,
|
||||
}, key.Name)
|
||||
}
|
||||
|
||||
// Verify the result is a runtime.Object
|
||||
if _, isObj := obj.(runtime.Object); !isObj {
|
||||
// This should never happen
|
||||
return fmt.Errorf("cache contained %T, which is not an Object", obj)
|
||||
}
|
||||
|
||||
// deep copy to avoid mutating cache
|
||||
// TODO(directxman12): revisit the decision to always deepcopy
|
||||
obj = obj.(runtime.Object).DeepCopyObject()
|
||||
|
||||
// Copy the value of the item in the cache to the returned value
|
||||
// TODO(directxman12): this is a terrible hack, pls fix (we should have deepcopyinto)
|
||||
outVal := reflect.ValueOf(out)
|
||||
objVal := reflect.ValueOf(obj)
|
||||
if !objVal.Type().AssignableTo(outVal.Type()) {
|
||||
return fmt.Errorf("cache had type %s, but %s was asked for", objVal.Type(), outVal.Type())
|
||||
}
|
||||
reflect.Indirect(outVal).Set(reflect.Indirect(objVal))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// List lists items out of the indexer and writes them to out
|
||||
func (c *CacheReader) List(ctx context.Context, opts *client.ListOptions, out runtime.Object) error {
|
||||
var objs []interface{}
|
||||
var err error
|
||||
|
||||
if opts != nil && opts.FieldSelector != nil {
|
||||
// TODO(directxman12): support more complicated field selectors by
|
||||
// combining multiple indicies, GetIndexers, etc
|
||||
field, val, requiresExact := requiresExactMatch(opts.FieldSelector)
|
||||
if !requiresExact {
|
||||
return fmt.Errorf("non-exact field matches are not supported by the cache")
|
||||
}
|
||||
// list all objects by the field selector. If this is namespaced and we have one, ask for the
|
||||
// namespaced index key. Otherwise, ask for the non-namespaced variant by using the fake "all namespaces"
|
||||
// namespace.
|
||||
objs, err = c.indexer.ByIndex(FieldIndexName(field), KeyToNamespacedKey(opts.Namespace, val))
|
||||
} else if opts != nil && opts.Namespace != "" {
|
||||
objs, err = c.indexer.ByIndex(cache.NamespaceIndex, opts.Namespace)
|
||||
} else {
|
||||
objs = c.indexer.List()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var labelSel labels.Selector
|
||||
if opts != nil && opts.LabelSelector != nil {
|
||||
labelSel = opts.LabelSelector
|
||||
}
|
||||
|
||||
outItems, err := c.getListItems(objs, labelSel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return apimeta.SetList(out, outItems)
|
||||
}
|
||||
|
||||
func (c *CacheReader) getListItems(objs []interface{}, labelSel labels.Selector) ([]runtime.Object, error) {
|
||||
outItems := make([]runtime.Object, 0, len(objs))
|
||||
for _, item := range objs {
|
||||
obj, isObj := item.(runtime.Object)
|
||||
if !isObj {
|
||||
return nil, fmt.Errorf("cache contained %T, which is not an Object", obj)
|
||||
}
|
||||
meta, err := apimeta.Accessor(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if labelSel != nil {
|
||||
lbls := labels.Set(meta.GetLabels())
|
||||
if !labelSel.Matches(lbls) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
outItems = append(outItems, obj.DeepCopyObject())
|
||||
}
|
||||
return outItems, nil
|
||||
}
|
||||
|
||||
// objectKeyToStorageKey converts an object key to store key.
|
||||
// It's akin to MetaNamespaceKeyFunc. It's separate from
|
||||
// String to allow keeping the key format easily in sync with
|
||||
// MetaNamespaceKeyFunc.
|
||||
func objectKeyToStoreKey(k client.ObjectKey) string {
|
||||
if k.Namespace == "" {
|
||||
return k.Name
|
||||
}
|
||||
return k.Namespace + "/" + k.Name
|
||||
}
|
||||
|
||||
// requiresExactMatch checks if the given field selector is of the form `k=v` or `k==v`.
|
||||
func requiresExactMatch(sel fields.Selector) (field, val string, required bool) {
|
||||
reqs := sel.Requirements()
|
||||
if len(reqs) != 1 {
|
||||
return "", "", false
|
||||
}
|
||||
req := reqs[0]
|
||||
if req.Operator != selection.Equals && req.Operator != selection.DoubleEquals {
|
||||
return "", "", false
|
||||
}
|
||||
return req.Field, req.Value, true
|
||||
}
|
||||
|
||||
// FieldIndexName constructs the name of the index over the given field,
|
||||
// for use with an indexer.
|
||||
func FieldIndexName(field string) string {
|
||||
return "field:" + field
|
||||
}
|
||||
|
||||
// noNamespaceNamespace is used as the "namespace" when we want to list across all namespaces
|
||||
const allNamespacesNamespace = "__all_namespaces"
|
||||
|
||||
// KeyToNamespacedKey prefixes the given index key with a namespace
|
||||
// for use in field selector indexes.
|
||||
func KeyToNamespacedKey(ns string, baseKey string) string {
|
||||
if ns != "" {
|
||||
return ns + "/" + baseKey
|
||||
}
|
||||
return allNamespacesNamespace + "/" + baseKey
|
||||
}
|
96
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/deleg_map.go
generated
vendored
Normal file
96
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/deleg_map.go
generated
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
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 internal
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
// InformersMap create and caches Informers for (runtime.Object, schema.GroupVersionKind) pairs.
|
||||
// It uses a standard parameter codec constructed based on the given generated Scheme.
|
||||
type InformersMap struct {
|
||||
// we abstract over the details of structured vs unstructured with the specificInformerMaps
|
||||
|
||||
structured *specificInformersMap
|
||||
unstructured *specificInformersMap
|
||||
|
||||
// Scheme maps runtime.Objects to GroupVersionKinds
|
||||
Scheme *runtime.Scheme
|
||||
}
|
||||
|
||||
// NewInformersMap creates a new InformersMap that can create informers for
|
||||
// both structured and unstructured objects.
|
||||
func NewInformersMap(config *rest.Config,
|
||||
scheme *runtime.Scheme,
|
||||
mapper meta.RESTMapper,
|
||||
resync time.Duration,
|
||||
namespace string) *InformersMap {
|
||||
|
||||
return &InformersMap{
|
||||
structured: newStructuredInformersMap(config, scheme, mapper, resync, namespace),
|
||||
unstructured: newUnstructuredInformersMap(config, scheme, mapper, resync, namespace),
|
||||
|
||||
Scheme: scheme,
|
||||
}
|
||||
}
|
||||
|
||||
// Start calls Run on each of the informers and sets started to true. Blocks on the stop channel.
|
||||
func (m *InformersMap) Start(stop <-chan struct{}) error {
|
||||
go m.structured.Start(stop)
|
||||
go m.unstructured.Start(stop)
|
||||
<-stop
|
||||
return nil
|
||||
}
|
||||
|
||||
// WaitForCacheSync waits until all the caches have been synced.
|
||||
func (m *InformersMap) WaitForCacheSync(stop <-chan struct{}) bool {
|
||||
syncedFuncs := append([]cache.InformerSynced(nil), m.structured.HasSyncedFuncs()...)
|
||||
syncedFuncs = append(syncedFuncs, m.unstructured.HasSyncedFuncs()...)
|
||||
|
||||
return cache.WaitForCacheSync(stop, syncedFuncs...)
|
||||
}
|
||||
|
||||
// Get will create a new Informer and add it to the map of InformersMap if none exists. Returns
|
||||
// the Informer from the map.
|
||||
func (m *InformersMap) Get(gvk schema.GroupVersionKind, obj runtime.Object) (*MapEntry, error) {
|
||||
_, isUnstructured := obj.(*unstructured.Unstructured)
|
||||
_, isUnstructuredList := obj.(*unstructured.UnstructuredList)
|
||||
isUnstructured = isUnstructured || isUnstructuredList
|
||||
|
||||
if isUnstructured {
|
||||
return m.unstructured.Get(gvk, obj)
|
||||
}
|
||||
|
||||
return m.structured.Get(gvk, obj)
|
||||
}
|
||||
|
||||
// newStructuredInformersMap creates a new InformersMap for structured objects.
|
||||
func newStructuredInformersMap(config *rest.Config, scheme *runtime.Scheme, mapper meta.RESTMapper, resync time.Duration, namespace string) *specificInformersMap {
|
||||
return newSpecificInformersMap(config, scheme, mapper, resync, namespace, createStructuredListWatch)
|
||||
}
|
||||
|
||||
// newUnstructuredInformersMap creates a new InformersMap for unstructured objects.
|
||||
func newUnstructuredInformersMap(config *rest.Config, scheme *runtime.Scheme, mapper meta.RESTMapper, resync time.Duration, namespace string) *specificInformersMap {
|
||||
return newSpecificInformersMap(config, scheme, mapper, resync, namespace, createUnstructuredListWatch)
|
||||
}
|
281
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers_map.go
generated
vendored
Normal file
281
vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers_map.go
generated
vendored
Normal file
@ -0,0 +1,281 @@
|
||||
/*
|
||||
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 internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
||||
)
|
||||
|
||||
// clientListWatcherFunc knows how to create a ListWatcher
|
||||
type createListWatcherFunc func(gvk schema.GroupVersionKind, ip *specificInformersMap) (*cache.ListWatch, error)
|
||||
|
||||
// newSpecificInformersMap returns a new specificInformersMap (like
|
||||
// the generical InformersMap, except that it doesn't implement WaitForCacheSync).
|
||||
func newSpecificInformersMap(config *rest.Config,
|
||||
scheme *runtime.Scheme,
|
||||
mapper meta.RESTMapper,
|
||||
resync time.Duration,
|
||||
namespace string,
|
||||
createListWatcher createListWatcherFunc) *specificInformersMap {
|
||||
ip := &specificInformersMap{
|
||||
config: config,
|
||||
Scheme: scheme,
|
||||
mapper: mapper,
|
||||
informersByGVK: make(map[schema.GroupVersionKind]*MapEntry),
|
||||
codecs: serializer.NewCodecFactory(scheme),
|
||||
paramCodec: runtime.NewParameterCodec(scheme),
|
||||
resync: resync,
|
||||
createListWatcher: createListWatcher,
|
||||
namespace: namespace,
|
||||
}
|
||||
return ip
|
||||
}
|
||||
|
||||
// MapEntry contains the cached data for an Informer
|
||||
type MapEntry struct {
|
||||
// Informer is the cached informer
|
||||
Informer cache.SharedIndexInformer
|
||||
|
||||
// CacheReader wraps Informer and implements the CacheReader interface for a single type
|
||||
Reader CacheReader
|
||||
}
|
||||
|
||||
// specificInformersMap create and caches Informers for (runtime.Object, schema.GroupVersionKind) pairs.
|
||||
// It uses a standard parameter codec constructed based on the given generated Scheme.
|
||||
type specificInformersMap struct {
|
||||
// Scheme maps runtime.Objects to GroupVersionKinds
|
||||
Scheme *runtime.Scheme
|
||||
|
||||
// config is used to talk to the apiserver
|
||||
config *rest.Config
|
||||
|
||||
// mapper maps GroupVersionKinds to Resources
|
||||
mapper meta.RESTMapper
|
||||
|
||||
// informersByGVK is the cache of informers keyed by groupVersionKind
|
||||
informersByGVK map[schema.GroupVersionKind]*MapEntry
|
||||
|
||||
// codecs is used to create a new REST client
|
||||
codecs serializer.CodecFactory
|
||||
|
||||
// paramCodec is used by list and watch
|
||||
paramCodec runtime.ParameterCodec
|
||||
|
||||
// stop is the stop channel to stop informers
|
||||
stop <-chan struct{}
|
||||
|
||||
// resync is the frequency the informers are resynced
|
||||
resync time.Duration
|
||||
|
||||
// mu guards access to the map
|
||||
mu sync.RWMutex
|
||||
|
||||
// start is true if the informers have been started
|
||||
started bool
|
||||
|
||||
// createClient knows how to create a client and a list object,
|
||||
// and allows for abstracting over the particulars of structured vs
|
||||
// unstructured objects.
|
||||
createListWatcher createListWatcherFunc
|
||||
|
||||
// namespace is the namespace that all ListWatches are restricted to
|
||||
// default or empty string means all namespaces
|
||||
namespace string
|
||||
}
|
||||
|
||||
// Start calls Run on each of the informers and sets started to true. Blocks on the stop channel.
|
||||
// It doesn't return start because it can't return an error, and it's not a runnable directly.
|
||||
func (ip *specificInformersMap) Start(stop <-chan struct{}) {
|
||||
func() {
|
||||
ip.mu.Lock()
|
||||
defer ip.mu.Unlock()
|
||||
|
||||
// Set the stop channel so it can be passed to informers that are added later
|
||||
ip.stop = stop
|
||||
|
||||
// Start each informer
|
||||
for _, informer := range ip.informersByGVK {
|
||||
go informer.Informer.Run(stop)
|
||||
}
|
||||
|
||||
// Set started to true so we immediately start any informers added later.
|
||||
ip.started = true
|
||||
}()
|
||||
<-stop
|
||||
}
|
||||
|
||||
// HasSyncedFuncs returns all the HasSynced functions for the informers in this map.
|
||||
func (ip *specificInformersMap) HasSyncedFuncs() []cache.InformerSynced {
|
||||
ip.mu.RLock()
|
||||
defer ip.mu.RUnlock()
|
||||
syncedFuncs := make([]cache.InformerSynced, 0, len(ip.informersByGVK))
|
||||
for _, informer := range ip.informersByGVK {
|
||||
syncedFuncs = append(syncedFuncs, informer.Informer.HasSynced)
|
||||
}
|
||||
return syncedFuncs
|
||||
}
|
||||
|
||||
// Get will create a new Informer and add it to the map of specificInformersMap if none exists. Returns
|
||||
// the Informer from the map.
|
||||
func (ip *specificInformersMap) Get(gvk schema.GroupVersionKind, obj runtime.Object) (*MapEntry, error) {
|
||||
// Return the informer if it is found
|
||||
i, ok := func() (*MapEntry, bool) {
|
||||
ip.mu.RLock()
|
||||
defer ip.mu.RUnlock()
|
||||
i, ok := ip.informersByGVK[gvk]
|
||||
return i, ok
|
||||
}()
|
||||
if ok {
|
||||
return i, nil
|
||||
}
|
||||
|
||||
// Do the mutex part in its own function so we can use defer without blocking pieces that don't
|
||||
// need to be locked
|
||||
var sync bool
|
||||
i, err := func() (*MapEntry, error) {
|
||||
ip.mu.Lock()
|
||||
defer ip.mu.Unlock()
|
||||
|
||||
// Check the cache to see if we already have an Informer. If we do, return the Informer.
|
||||
// This is for the case where 2 routines tried to get the informer when it wasn't in the map
|
||||
// so neither returned early, but the first one created it.
|
||||
var ok bool
|
||||
i, ok := ip.informersByGVK[gvk]
|
||||
if ok {
|
||||
return i, nil
|
||||
}
|
||||
|
||||
// Create a NewSharedIndexInformer and add it to the map.
|
||||
var lw *cache.ListWatch
|
||||
lw, err := ip.createListWatcher(gvk, ip)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ni := cache.NewSharedIndexInformer(lw, obj, ip.resync, cache.Indexers{
|
||||
cache.NamespaceIndex: cache.MetaNamespaceIndexFunc,
|
||||
})
|
||||
i = &MapEntry{
|
||||
Informer: ni,
|
||||
Reader: CacheReader{indexer: ni.GetIndexer(), groupVersionKind: gvk},
|
||||
}
|
||||
ip.informersByGVK[gvk] = i
|
||||
|
||||
// Start the Informer if need by
|
||||
// TODO(seans): write thorough tests and document what happens here - can you add indexers?
|
||||
// can you add eventhandlers?
|
||||
if ip.started {
|
||||
sync = true
|
||||
go i.Informer.Run(ip.stop)
|
||||
}
|
||||
return i, nil
|
||||
}()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if sync {
|
||||
// Wait for it to sync before returning the Informer so that folks don't read from a stale cache.
|
||||
if !cache.WaitForCacheSync(ip.stop, i.Informer.HasSynced) {
|
||||
return nil, fmt.Errorf("failed waiting for %T Informer to sync", obj)
|
||||
}
|
||||
}
|
||||
|
||||
return i, err
|
||||
}
|
||||
|
||||
// newListWatch returns a new ListWatch object that can be used to create a SharedIndexInformer.
|
||||
func createStructuredListWatch(gvk schema.GroupVersionKind, ip *specificInformersMap) (*cache.ListWatch, error) {
|
||||
// Kubernetes APIs work against Resources, not GroupVersionKinds. Map the
|
||||
// groupVersionKind to the Resource API we will use.
|
||||
mapping, err := ip.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client, err := apiutil.RESTClientForGVK(gvk, ip.config, ip.codecs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
listGVK := gvk.GroupVersion().WithKind(gvk.Kind + "List")
|
||||
listObj, err := ip.Scheme.New(listGVK)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create a new ListWatch for the obj
|
||||
return &cache.ListWatch{
|
||||
ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
|
||||
res := listObj.DeepCopyObject()
|
||||
isNamespaceScoped := ip.namespace != "" && mapping.Scope.Name() != meta.RESTScopeNameRoot
|
||||
err := client.Get().NamespaceIfScoped(ip.namespace, isNamespaceScoped).Resource(mapping.Resource.Resource).VersionedParams(&opts, ip.paramCodec).Do().Into(res)
|
||||
return res, err
|
||||
},
|
||||
// Setup the watch function
|
||||
WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
|
||||
// Watch needs to be set to true separately
|
||||
opts.Watch = true
|
||||
isNamespaceScoped := ip.namespace != "" && mapping.Scope.Name() != meta.RESTScopeNameRoot
|
||||
return client.Get().NamespaceIfScoped(ip.namespace, isNamespaceScoped).Resource(mapping.Resource.Resource).VersionedParams(&opts, ip.paramCodec).Watch()
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func createUnstructuredListWatch(gvk schema.GroupVersionKind, ip *specificInformersMap) (*cache.ListWatch, error) {
|
||||
// Kubernetes APIs work against Resources, not GroupVersionKinds. Map the
|
||||
// groupVersionKind to the Resource API we will use.
|
||||
mapping, err := ip.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dynamicClient, err := dynamic.NewForConfig(ip.config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create a new ListWatch for the obj
|
||||
return &cache.ListWatch{
|
||||
ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
|
||||
if ip.namespace != "" && mapping.Scope.Name() != meta.RESTScopeNameRoot {
|
||||
return dynamicClient.Resource(mapping.Resource).Namespace(ip.namespace).List(opts)
|
||||
}
|
||||
return dynamicClient.Resource(mapping.Resource).List(opts)
|
||||
},
|
||||
// Setup the watch function
|
||||
WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
|
||||
// Watch needs to be set to true separately
|
||||
opts.Watch = true
|
||||
if ip.namespace != "" && mapping.Scope.Name() != meta.RESTScopeNameRoot {
|
||||
return dynamicClient.Resource(mapping.Resource).Namespace(ip.namespace).Watch(opts)
|
||||
}
|
||||
return dynamicClient.Resource(mapping.Resource).Watch(opts)
|
||||
},
|
||||
}, nil
|
||||
}
|
88
vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/apimachinery.go
generated
vendored
Normal file
88
vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/apimachinery.go
generated
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apiutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/restmapper"
|
||||
)
|
||||
|
||||
// NewDiscoveryRESTMapper constructs a new RESTMapper based on discovery
|
||||
// information fetched by a new client with the given config.
|
||||
func NewDiscoveryRESTMapper(c *rest.Config) (meta.RESTMapper, error) {
|
||||
// Get a mapper
|
||||
dc := discovery.NewDiscoveryClientForConfigOrDie(c)
|
||||
gr, err := restmapper.GetAPIGroupResources(dc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return restmapper.NewDiscoveryRESTMapper(gr), nil
|
||||
}
|
||||
|
||||
// GVKForObject finds the GroupVersionKind associated with the given object, if there is only a single such GVK.
|
||||
func GVKForObject(obj runtime.Object, scheme *runtime.Scheme) (schema.GroupVersionKind, error) {
|
||||
gvks, isUnversioned, err := scheme.ObjectKinds(obj)
|
||||
if err != nil {
|
||||
return schema.GroupVersionKind{}, err
|
||||
}
|
||||
if isUnversioned {
|
||||
return schema.GroupVersionKind{}, fmt.Errorf("cannot create a new informer for the unversioned type %T", obj)
|
||||
}
|
||||
|
||||
if len(gvks) < 1 {
|
||||
return schema.GroupVersionKind{}, fmt.Errorf("no group-version-kinds associated with type %T", obj)
|
||||
}
|
||||
if len(gvks) > 1 {
|
||||
// this should only trigger for things like metav1.XYZ --
|
||||
// normal versioned types should be fine
|
||||
return schema.GroupVersionKind{}, fmt.Errorf(
|
||||
"multiple group-version-kinds associated with type %T, refusing to guess at one", obj)
|
||||
}
|
||||
return gvks[0], nil
|
||||
}
|
||||
|
||||
// RESTClientForGVK constructs a new rest.Interface capable of accessing the resource associated
|
||||
// with the given GroupVersionKind.
|
||||
func RESTClientForGVK(gvk schema.GroupVersionKind, baseConfig *rest.Config, codecs serializer.CodecFactory) (rest.Interface, error) {
|
||||
cfg := createRestConfig(gvk, baseConfig)
|
||||
cfg.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: codecs}
|
||||
return rest.RESTClientFor(cfg)
|
||||
}
|
||||
|
||||
//createRestConfig copies the base config and updates needed fields for a new rest config
|
||||
func createRestConfig(gvk schema.GroupVersionKind, baseConfig *rest.Config) *rest.Config {
|
||||
gv := gvk.GroupVersion()
|
||||
|
||||
cfg := rest.CopyConfig(baseConfig)
|
||||
cfg.GroupVersion = &gv
|
||||
if gvk.Group == "" {
|
||||
cfg.APIPath = "/api"
|
||||
} else {
|
||||
cfg.APIPath = "/apis"
|
||||
}
|
||||
if cfg.UserAgent == "" {
|
||||
cfg.UserAgent = rest.DefaultKubernetesUserAgent()
|
||||
}
|
||||
return cfg
|
||||
}
|
162
vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go
generated
vendored
Normal file
162
vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go
generated
vendored
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
||||
)
|
||||
|
||||
// Options are creation options for a Client
|
||||
type Options struct {
|
||||
// Scheme, if provided, will be used to map go structs to GroupVersionKinds
|
||||
Scheme *runtime.Scheme
|
||||
|
||||
// Mapper, if provided, will be used to map GroupVersionKinds to Resources
|
||||
Mapper meta.RESTMapper
|
||||
}
|
||||
|
||||
// New returns a new Client using the provided config and Options.
|
||||
func New(config *rest.Config, options Options) (Client, error) {
|
||||
if config == nil {
|
||||
return nil, fmt.Errorf("must provide non-nil rest.Config to client.New")
|
||||
}
|
||||
|
||||
// Init a scheme if none provided
|
||||
if options.Scheme == nil {
|
||||
options.Scheme = scheme.Scheme
|
||||
}
|
||||
|
||||
// Init a Mapper if none provided
|
||||
if options.Mapper == nil {
|
||||
var err error
|
||||
options.Mapper, err = apiutil.NewDiscoveryRESTMapper(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
dynamicClient, err := dynamic.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := &client{
|
||||
typedClient: typedClient{
|
||||
cache: clientCache{
|
||||
config: config,
|
||||
scheme: options.Scheme,
|
||||
mapper: options.Mapper,
|
||||
codecs: serializer.NewCodecFactory(options.Scheme),
|
||||
resourceByType: make(map[reflect.Type]*resourceMeta),
|
||||
},
|
||||
paramCodec: runtime.NewParameterCodec(options.Scheme),
|
||||
},
|
||||
unstructuredClient: unstructuredClient{
|
||||
client: dynamicClient,
|
||||
restMapper: options.Mapper,
|
||||
},
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
var _ Client = &client{}
|
||||
|
||||
// client is a client.Client that reads and writes directly from/to an API server. It lazily initializes
|
||||
// new clients at the time they are used, and caches the client.
|
||||
type client struct {
|
||||
typedClient typedClient
|
||||
unstructuredClient unstructuredClient
|
||||
}
|
||||
|
||||
// Create implements client.Client
|
||||
func (c *client) Create(ctx context.Context, obj runtime.Object) error {
|
||||
_, ok := obj.(*unstructured.Unstructured)
|
||||
if ok {
|
||||
return c.unstructuredClient.Create(ctx, obj)
|
||||
}
|
||||
return c.typedClient.Create(ctx, obj)
|
||||
}
|
||||
|
||||
// Update implements client.Client
|
||||
func (c *client) Update(ctx context.Context, obj runtime.Object) error {
|
||||
_, ok := obj.(*unstructured.Unstructured)
|
||||
if ok {
|
||||
return c.unstructuredClient.Update(ctx, obj)
|
||||
}
|
||||
return c.typedClient.Update(ctx, obj)
|
||||
}
|
||||
|
||||
// Delete implements client.Client
|
||||
func (c *client) Delete(ctx context.Context, obj runtime.Object, opts ...DeleteOptionFunc) error {
|
||||
_, ok := obj.(*unstructured.Unstructured)
|
||||
if ok {
|
||||
return c.unstructuredClient.Delete(ctx, obj, opts...)
|
||||
}
|
||||
return c.typedClient.Delete(ctx, obj, opts...)
|
||||
}
|
||||
|
||||
// Get implements client.Client
|
||||
func (c *client) Get(ctx context.Context, key ObjectKey, obj runtime.Object) error {
|
||||
_, ok := obj.(*unstructured.Unstructured)
|
||||
if ok {
|
||||
return c.unstructuredClient.Get(ctx, key, obj)
|
||||
}
|
||||
return c.typedClient.Get(ctx, key, obj)
|
||||
}
|
||||
|
||||
// List implements client.Client
|
||||
func (c *client) List(ctx context.Context, opts *ListOptions, obj runtime.Object) error {
|
||||
_, ok := obj.(*unstructured.UnstructuredList)
|
||||
if ok {
|
||||
return c.unstructuredClient.List(ctx, opts, obj)
|
||||
}
|
||||
return c.typedClient.List(ctx, opts, obj)
|
||||
}
|
||||
|
||||
// Status implements client.StatusClient
|
||||
func (c *client) Status() StatusWriter {
|
||||
return &statusWriter{client: c}
|
||||
}
|
||||
|
||||
// statusWriter is client.StatusWriter that writes status subresource
|
||||
type statusWriter struct {
|
||||
client *client
|
||||
}
|
||||
|
||||
// ensure statusWriter implements client.StatusWriter
|
||||
var _ StatusWriter = &statusWriter{}
|
||||
|
||||
// Update implements client.StatusWriter
|
||||
func (sw *statusWriter) Update(ctx context.Context, obj runtime.Object) error {
|
||||
_, ok := obj.(*unstructured.Unstructured)
|
||||
if ok {
|
||||
return sw.client.unstructuredClient.UpdateStatus(ctx, obj)
|
||||
}
|
||||
return sw.client.typedClient.UpdateStatus(ctx, obj)
|
||||
}
|
145
vendor/sigs.k8s.io/controller-runtime/pkg/client/client_cache.go
generated
vendored
Normal file
145
vendor/sigs.k8s.io/controller-runtime/pkg/client/client_cache.go
generated
vendored
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
||||
)
|
||||
|
||||
// clientCache creates and caches rest clients and metadata for Kubernetes types
|
||||
type clientCache struct {
|
||||
// config is the rest.Config to talk to an apiserver
|
||||
config *rest.Config
|
||||
|
||||
// scheme maps go structs to GroupVersionKinds
|
||||
scheme *runtime.Scheme
|
||||
|
||||
// mapper maps GroupVersionKinds to Resources
|
||||
mapper meta.RESTMapper
|
||||
|
||||
// codecs are used to create a REST client for a gvk
|
||||
codecs serializer.CodecFactory
|
||||
|
||||
// resourceByType caches type metadata
|
||||
resourceByType map[reflect.Type]*resourceMeta
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// newResource maps obj to a Kubernetes Resource and constructs a client for that Resource.
|
||||
// If the object is a list, the resource represents the item's type instead.
|
||||
func (c *clientCache) newResource(obj runtime.Object) (*resourceMeta, error) {
|
||||
gvk, err := apiutil.GVKForObject(obj, c.scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if strings.HasSuffix(gvk.Kind, "List") && meta.IsListType(obj) {
|
||||
// if this was a list, treat it as a request for the item's resource
|
||||
gvk.Kind = gvk.Kind[:len(gvk.Kind)-4]
|
||||
}
|
||||
|
||||
client, err := apiutil.RESTClientForGVK(gvk, c.config, c.codecs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mapping, err := c.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resourceMeta{Interface: client, mapping: mapping, gvk: gvk}, nil
|
||||
}
|
||||
|
||||
// getResource returns the resource meta information for the given type of object.
|
||||
// If the object is a list, the resource represents the item's type instead.
|
||||
func (c *clientCache) getResource(obj runtime.Object) (*resourceMeta, error) {
|
||||
typ := reflect.TypeOf(obj)
|
||||
|
||||
// It's better to do creation work twice than to not let multiple
|
||||
// people make requests at once
|
||||
c.mu.RLock()
|
||||
r, known := c.resourceByType[typ]
|
||||
c.mu.RUnlock()
|
||||
|
||||
if known {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// Initialize a new Client
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
r, err := c.newResource(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.resourceByType[typ] = r
|
||||
return r, err
|
||||
}
|
||||
|
||||
// getObjMeta returns objMeta containing both type and object metadata and state
|
||||
func (c *clientCache) getObjMeta(obj runtime.Object) (*objMeta, error) {
|
||||
r, err := c.getResource(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &objMeta{resourceMeta: r, Object: m}, err
|
||||
}
|
||||
|
||||
// resourceMeta caches state for a Kubernetes type.
|
||||
type resourceMeta struct {
|
||||
// client is the rest client used to talk to the apiserver
|
||||
rest.Interface
|
||||
// gvk is the GroupVersionKind of the resourceMeta
|
||||
gvk schema.GroupVersionKind
|
||||
// mapping is the rest mapping
|
||||
mapping *meta.RESTMapping
|
||||
}
|
||||
|
||||
// isNamespaced returns true if the type is namespaced
|
||||
func (r *resourceMeta) isNamespaced() bool {
|
||||
if r.mapping.Scope.Name() == meta.RESTScopeNameRoot {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// resource returns the resource name of the type
|
||||
func (r *resourceMeta) resource() string {
|
||||
return r.mapping.Resource.Resource
|
||||
}
|
||||
|
||||
// objMeta stores type and object information about a Kubernetes type
|
||||
type objMeta struct {
|
||||
// resourceMeta contains type information for the object
|
||||
*resourceMeta
|
||||
|
||||
// Object contains meta data for the object instance
|
||||
v1.Object
|
||||
}
|
57
vendor/sigs.k8s.io/controller-runtime/pkg/client/client_suite_test.go
generated
vendored
Normal file
57
vendor/sigs.k8s.io/controller-runtime/pkg/client/client_suite_test.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
)
|
||||
|
||||
func TestSource(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecsWithDefaultAndCustomReporters(t, "Controller Integration Suite", []Reporter{envtest.NewlineReporter{}})
|
||||
}
|
||||
|
||||
var testenv *envtest.Environment
|
||||
var cfg *rest.Config
|
||||
var clientset *kubernetes.Clientset
|
||||
|
||||
var _ = BeforeSuite(func(done Done) {
|
||||
logf.SetLogger(logf.ZapLoggerTo(GinkgoWriter, true))
|
||||
|
||||
testenv = &envtest.Environment{}
|
||||
|
||||
var err error
|
||||
cfg, err = testenv.Start()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
clientset, err = kubernetes.NewForConfig(cfg)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
close(done)
|
||||
}, 60)
|
||||
|
||||
var _ = AfterSuite(func() {
|
||||
testenv.Stop()
|
||||
})
|
1922
vendor/sigs.k8s.io/controller-runtime/pkg/client/client_test.go
generated
vendored
Normal file
1922
vendor/sigs.k8s.io/controller-runtime/pkg/client/client_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
94
vendor/sigs.k8s.io/controller-runtime/pkg/client/config/config.go
generated
vendored
Normal file
94
vendor/sigs.k8s.io/controller-runtime/pkg/client/config/config.go
generated
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
)
|
||||
|
||||
var (
|
||||
kubeconfig, masterURL string
|
||||
log = logf.KBLog.WithName("client").WithName("config")
|
||||
)
|
||||
|
||||
func init() {
|
||||
// TODO: Fix this to allow double vendoring this library but still register flags on behalf of users
|
||||
flag.StringVar(&kubeconfig, "kubeconfig", "",
|
||||
"Paths to a kubeconfig. Only required if out-of-cluster.")
|
||||
|
||||
flag.StringVar(&masterURL, "master", "",
|
||||
"The address of the Kubernetes API server. Overrides any value in kubeconfig. "+
|
||||
"Only required if out-of-cluster.")
|
||||
}
|
||||
|
||||
// GetConfig creates a *rest.Config for talking to a Kubernetes apiserver.
|
||||
// If --kubeconfig is set, will use the kubeconfig file at that location. Otherwise will assume running
|
||||
// in cluster and use the cluster provided kubeconfig.
|
||||
//
|
||||
// Config precedence
|
||||
//
|
||||
// * --kubeconfig flag pointing at a file
|
||||
//
|
||||
// * KUBECONFIG environment variable pointing at a file
|
||||
//
|
||||
// * In-cluster config if running in cluster
|
||||
//
|
||||
// * $HOME/.kube/config if exists
|
||||
func GetConfig() (*rest.Config, error) {
|
||||
// If a flag is specified with the config location, use that
|
||||
if len(kubeconfig) > 0 {
|
||||
return clientcmd.BuildConfigFromFlags(masterURL, kubeconfig)
|
||||
}
|
||||
// If an env variable is specified with the config locaiton, use that
|
||||
if len(os.Getenv("KUBECONFIG")) > 0 {
|
||||
return clientcmd.BuildConfigFromFlags(masterURL, os.Getenv("KUBECONFIG"))
|
||||
}
|
||||
// If no explicit location, try the in-cluster config
|
||||
if c, err := rest.InClusterConfig(); err == nil {
|
||||
return c, nil
|
||||
}
|
||||
// If no in-cluster config, try the default location in the user's home directory
|
||||
if usr, err := user.Current(); err == nil {
|
||||
if c, err := clientcmd.BuildConfigFromFlags(
|
||||
"", filepath.Join(usr.HomeDir, ".kube", "config")); err == nil {
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("could not locate a kubeconfig")
|
||||
}
|
||||
|
||||
// GetConfigOrDie creates a *rest.Config for talking to a Kubernetes apiserver.
|
||||
// If --kubeconfig is set, will use the kubeconfig file at that location. Otherwise will assume running
|
||||
// in cluster and use the cluster provided kubeconfig.
|
||||
//
|
||||
// Will log an error and exit if there is an error creating the rest.Config.
|
||||
func GetConfigOrDie() *rest.Config {
|
||||
config, err := GetConfig()
|
||||
if err != nil {
|
||||
log.Error(err, "unable to get kubeconfig")
|
||||
}
|
||||
return config
|
||||
}
|
18
vendor/sigs.k8s.io/controller-runtime/pkg/client/config/doc.go
generated
vendored
Normal file
18
vendor/sigs.k8s.io/controller-runtime/pkg/client/config/doc.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package config contains libraries for initializing rest configs for talking to the Kubernetes API
|
||||
package config
|
154
vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/client.go
generated
vendored
Normal file
154
vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/client.go
generated
vendored
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fake
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/testing"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
)
|
||||
|
||||
var (
|
||||
log = logf.KBLog.WithName("fake-client")
|
||||
)
|
||||
|
||||
type fakeClient struct {
|
||||
tracker testing.ObjectTracker
|
||||
}
|
||||
|
||||
var _ client.Client = &fakeClient{}
|
||||
|
||||
// NewFakeClient creates a new fake client for testing.
|
||||
// You can choose to initialize it with a slice of runtime.Object.
|
||||
func NewFakeClient(initObjs ...runtime.Object) client.Client {
|
||||
tracker := testing.NewObjectTracker(scheme.Scheme, scheme.Codecs.UniversalDecoder())
|
||||
for _, obj := range initObjs {
|
||||
err := tracker.Add(obj)
|
||||
if err != nil {
|
||||
log.Error(err, "failed to add object", "object", obj)
|
||||
os.Exit(1)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return &fakeClient{
|
||||
tracker: tracker,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *fakeClient) Get(ctx context.Context, key client.ObjectKey, obj runtime.Object) error {
|
||||
gvr, err := getGVRFromObject(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o, err := c.tracker.Get(gvr, key.Namespace, key.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
j, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
decoder := scheme.Codecs.UniversalDecoder()
|
||||
_, _, err = decoder.Decode(j, nil, obj)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *fakeClient) List(ctx context.Context, opts *client.ListOptions, list runtime.Object) error {
|
||||
gvk := opts.Raw.TypeMeta.GroupVersionKind()
|
||||
gvr, _ := meta.UnsafeGuessKindToResource(gvk)
|
||||
o, err := c.tracker.List(gvr, gvk, opts.Namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
j, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
decoder := scheme.Codecs.UniversalDecoder()
|
||||
_, _, err = decoder.Decode(j, nil, list)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *fakeClient) Create(ctx context.Context, obj runtime.Object) error {
|
||||
gvr, err := getGVRFromObject(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
accessor, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.tracker.Create(gvr, obj, accessor.GetNamespace())
|
||||
}
|
||||
|
||||
func (c *fakeClient) Delete(ctx context.Context, obj runtime.Object, opts ...client.DeleteOptionFunc) error {
|
||||
gvr, err := getGVRFromObject(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
accessor, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//TODO: implement propagation
|
||||
return c.tracker.Delete(gvr, accessor.GetNamespace(), accessor.GetName())
|
||||
}
|
||||
|
||||
func (c *fakeClient) Update(ctx context.Context, obj runtime.Object) error {
|
||||
gvr, err := getGVRFromObject(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
accessor, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.tracker.Update(gvr, obj, accessor.GetNamespace())
|
||||
}
|
||||
|
||||
func (c *fakeClient) Status() client.StatusWriter {
|
||||
return &fakeStatusWriter{client: c}
|
||||
}
|
||||
|
||||
func getGVRFromObject(obj runtime.Object) (schema.GroupVersionResource, error) {
|
||||
gvk, err := apiutil.GVKForObject(obj, scheme.Scheme)
|
||||
if err != nil {
|
||||
return schema.GroupVersionResource{}, err
|
||||
}
|
||||
gvr, _ := meta.UnsafeGuessKindToResource(gvk)
|
||||
return gvr, nil
|
||||
}
|
||||
|
||||
type fakeStatusWriter struct {
|
||||
client *fakeClient
|
||||
}
|
||||
|
||||
func (sw *fakeStatusWriter) Update(ctx context.Context, obj runtime.Object) error {
|
||||
// TODO(droot): This results in full update of the obj (spec + status). Need
|
||||
// a way to update status field only.
|
||||
return sw.client.Update(ctx, obj)
|
||||
}
|
37
vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/client_suite_test.go
generated
vendored
Normal file
37
vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/client_suite_test.go
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fake
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
)
|
||||
|
||||
func TestSource(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecsWithDefaultAndCustomReporters(t, "Controller Integration Suite", []Reporter{envtest.NewlineReporter{}})
|
||||
}
|
||||
|
||||
var _ = BeforeSuite(func(done Done) {
|
||||
logf.SetLogger(logf.ZapLoggerTo(GinkgoWriter, true))
|
||||
close(done)
|
||||
}, 60)
|
156
vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/client_test.go
generated
vendored
Normal file
156
vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/client_test.go
generated
vendored
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fake
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
var _ = Describe("Fake client", func() {
|
||||
var dep *appsv1.Deployment
|
||||
var cm *corev1.ConfigMap
|
||||
var cl client.Client
|
||||
|
||||
BeforeEach(func(done Done) {
|
||||
dep = &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-deployment",
|
||||
Namespace: "ns1",
|
||||
},
|
||||
}
|
||||
cm = &corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-cm",
|
||||
Namespace: "ns2",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"test-key": "test-value",
|
||||
},
|
||||
}
|
||||
cl = NewFakeClient(dep, cm)
|
||||
close(done)
|
||||
})
|
||||
|
||||
It("should be able to Get", func() {
|
||||
By("Getting a deployment")
|
||||
namespacedName := types.NamespacedName{
|
||||
Name: "test-deployment",
|
||||
Namespace: "ns1",
|
||||
}
|
||||
obj := &appsv1.Deployment{}
|
||||
err := cl.Get(nil, namespacedName, obj)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(obj).To(Equal(dep))
|
||||
})
|
||||
|
||||
It("should be able to List", func() {
|
||||
By("Listing all deployments in a namespace")
|
||||
list := &metav1.List{}
|
||||
err := cl.List(nil, &client.ListOptions{
|
||||
Namespace: "ns1",
|
||||
Raw: &metav1.ListOptions{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
},
|
||||
}, list)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(list.Items).To(HaveLen(1))
|
||||
j, err := json.Marshal(dep)
|
||||
Expect(err).To(BeNil())
|
||||
expectedDep := runtime.RawExtension{Raw: j}
|
||||
Expect(list.Items).To(ConsistOf(expectedDep))
|
||||
})
|
||||
|
||||
It("should be able to Create", func() {
|
||||
By("Creating a new configmap")
|
||||
newcm := &corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "new-test-cm",
|
||||
Namespace: "ns2",
|
||||
},
|
||||
}
|
||||
err := cl.Create(nil, newcm)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
By("Getting the new configmap")
|
||||
namespacedName := types.NamespacedName{
|
||||
Name: "new-test-cm",
|
||||
Namespace: "ns2",
|
||||
}
|
||||
obj := &corev1.ConfigMap{}
|
||||
err = cl.Get(nil, namespacedName, obj)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(obj).To(Equal(newcm))
|
||||
})
|
||||
|
||||
It("should be able to Update", func() {
|
||||
By("Updating a new configmap")
|
||||
newcm := &corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-cm",
|
||||
Namespace: "ns2",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"test-key": "new-value",
|
||||
},
|
||||
}
|
||||
err := cl.Update(nil, newcm)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
By("Getting the new configmap")
|
||||
namespacedName := types.NamespacedName{
|
||||
Name: "test-cm",
|
||||
Namespace: "ns2",
|
||||
}
|
||||
obj := &corev1.ConfigMap{}
|
||||
err = cl.Get(nil, namespacedName, obj)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(obj).To(Equal(newcm))
|
||||
})
|
||||
|
||||
It("should be able to Delete", func() {
|
||||
By("Deleting a deployment")
|
||||
err := cl.Delete(nil, dep)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
By("Listing all deployments in the namespace")
|
||||
list := &metav1.List{}
|
||||
err = cl.List(nil, &client.ListOptions{
|
||||
Namespace: "ns1",
|
||||
Raw: &metav1.ListOptions{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
},
|
||||
}, list)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(list.Items).To(HaveLen(0))
|
||||
})
|
||||
})
|
27
vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/doc.go
generated
vendored
Normal file
27
vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/doc.go
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Package fake provides a fake client for testing.
|
||||
|
||||
An fake client is backed by its simple object store indexed by GroupVersionResource.
|
||||
You can create a fake client with optional objects.
|
||||
|
||||
client := NewFakeClient(initObjs...) // initObjs is a slice of runtime.Object
|
||||
|
||||
You can invoke the methods defined in the Client interface.
|
||||
*/
|
||||
package fake
|
292
vendor/sigs.k8s.io/controller-runtime/pkg/client/interfaces.go
generated
vendored
Normal file
292
vendor/sigs.k8s.io/controller-runtime/pkg/client/interfaces.go
generated
vendored
Normal file
@ -0,0 +1,292 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
// ObjectKey identifies a Kubernetes Object.
|
||||
type ObjectKey = types.NamespacedName
|
||||
|
||||
// ObjectKeyFromObject returns the ObjectKey given a runtime.Object
|
||||
func ObjectKeyFromObject(obj runtime.Object) (ObjectKey, error) {
|
||||
accessor, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return ObjectKey{}, err
|
||||
}
|
||||
return ObjectKey{Namespace: accessor.GetNamespace(), Name: accessor.GetName()}, nil
|
||||
}
|
||||
|
||||
// TODO(directxman12): is there a sane way to deal with get/delete options?
|
||||
|
||||
// Reader knows how to read and list Kubernetes objects.
|
||||
type Reader interface {
|
||||
// Get retrieves an obj for the given object key from the Kubernetes Cluster.
|
||||
// obj must be a struct pointer so that obj can be updated with the response
|
||||
// returned by the Server.
|
||||
Get(ctx context.Context, key ObjectKey, obj runtime.Object) error
|
||||
|
||||
// List retrieves list of objects for a given namespace and list options. On a
|
||||
// successful call, Items field in the list will be populated with the
|
||||
// result returned from the server.
|
||||
List(ctx context.Context, opts *ListOptions, list runtime.Object) error
|
||||
}
|
||||
|
||||
// Writer knows how to create, delete, and update Kubernetes objects.
|
||||
type Writer interface {
|
||||
// Create saves the object obj in the Kubernetes cluster.
|
||||
Create(ctx context.Context, obj runtime.Object) error
|
||||
|
||||
// Delete deletes the given obj from Kubernetes cluster.
|
||||
Delete(ctx context.Context, obj runtime.Object, opts ...DeleteOptionFunc) error
|
||||
|
||||
// Update updates the given obj in the Kubernetes cluster. obj must be a
|
||||
// struct pointer so that obj can be updated with the content returned by the Server.
|
||||
Update(ctx context.Context, obj runtime.Object) error
|
||||
}
|
||||
|
||||
// StatusClient knows how to create a client which can update status subresource
|
||||
// for kubernetes objects.
|
||||
type StatusClient interface {
|
||||
Status() StatusWriter
|
||||
}
|
||||
|
||||
// StatusWriter knows how to update status subresource of a Kubernetes object.
|
||||
type StatusWriter interface {
|
||||
// Update updates the fields corresponding to the status subresource for the
|
||||
// given obj. obj must be a struct pointer so that obj can be updated
|
||||
// with the content returned by the Server.
|
||||
Update(ctx context.Context, obj runtime.Object) error
|
||||
}
|
||||
|
||||
// Client knows how to perform CRUD operations on Kubernetes objects.
|
||||
type Client interface {
|
||||
Reader
|
||||
Writer
|
||||
StatusClient
|
||||
}
|
||||
|
||||
// IndexerFunc knows how to take an object and turn it into a series
|
||||
// of (non-namespaced) keys for that object.
|
||||
type IndexerFunc func(runtime.Object) []string
|
||||
|
||||
// FieldIndexer knows how to index over a particular "field" such that it
|
||||
// can later be used by a field selector.
|
||||
type FieldIndexer interface {
|
||||
// IndexFields adds an index with the given field name on the given object type
|
||||
// by using the given function to extract the value for that field. If you want
|
||||
// compatibility with the Kubernetes API server, only return one key, and only use
|
||||
// fields that the API server supports. Otherwise, you can return multiple keys,
|
||||
// and "equality" in the field selector means that at least one key matches the value.
|
||||
IndexField(obj runtime.Object, field string, extractValue IndexerFunc) error
|
||||
}
|
||||
|
||||
// DeleteOptions contains options for delete requests. It's generally a subset
|
||||
// of metav1.DeleteOptions.
|
||||
type DeleteOptions struct {
|
||||
// GracePeriodSeconds is the duration in seconds before the object should be
|
||||
// deleted. Value must be non-negative integer. The value zero indicates
|
||||
// delete immediately. If this value is nil, the default grace period for the
|
||||
// specified type will be used.
|
||||
GracePeriodSeconds *int64
|
||||
|
||||
// Preconditions must be fulfilled before a deletion is carried out. If not
|
||||
// possible, a 409 Conflict status will be returned.
|
||||
Preconditions *metav1.Preconditions
|
||||
|
||||
// PropagationPolicy determined whether and how garbage collection will be
|
||||
// performed. Either this field or OrphanDependents may be set, but not both.
|
||||
// The default policy is decided by the existing finalizer set in the
|
||||
// metadata.finalizers and the resource-specific default policy.
|
||||
// Acceptable values are: 'Orphan' - orphan the dependents; 'Background' -
|
||||
// allow the garbage collector to delete the dependents in the background;
|
||||
// 'Foreground' - a cascading policy that deletes all dependents in the
|
||||
// foreground.
|
||||
PropagationPolicy *metav1.DeletionPropagation
|
||||
|
||||
// Raw represents raw DeleteOptions, as passed to the API server.
|
||||
Raw *metav1.DeleteOptions
|
||||
}
|
||||
|
||||
// AsDeleteOptions returns these options as a metav1.DeleteOptions.
|
||||
// This may mutate the Raw field.
|
||||
func (o *DeleteOptions) AsDeleteOptions() *metav1.DeleteOptions {
|
||||
|
||||
if o == nil {
|
||||
return &metav1.DeleteOptions{}
|
||||
}
|
||||
if o.Raw == nil {
|
||||
o.Raw = &metav1.DeleteOptions{}
|
||||
}
|
||||
|
||||
o.Raw.GracePeriodSeconds = o.GracePeriodSeconds
|
||||
o.Raw.Preconditions = o.Preconditions
|
||||
o.Raw.PropagationPolicy = o.PropagationPolicy
|
||||
return o.Raw
|
||||
}
|
||||
|
||||
// ApplyOptions executes the given DeleteOptionFuncs and returns the mutated
|
||||
// DeleteOptions.
|
||||
func (o *DeleteOptions) ApplyOptions(optFuncs []DeleteOptionFunc) *DeleteOptions {
|
||||
for _, optFunc := range optFuncs {
|
||||
optFunc(o)
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
// DeleteOptionFunc is a function that mutates a DeleteOptions struct. It implements
|
||||
// the functional options pattern. See
|
||||
// https://github.com/tmrts/go-patterns/blob/master/idiom/functional-options.md.
|
||||
type DeleteOptionFunc func(*DeleteOptions)
|
||||
|
||||
// GracePeriodSeconds is a functional option that sets the GracePeriodSeconds
|
||||
// field of a DeleteOptions struct.
|
||||
func GracePeriodSeconds(gp int64) DeleteOptionFunc {
|
||||
return func(opts *DeleteOptions) {
|
||||
opts.GracePeriodSeconds = &gp
|
||||
}
|
||||
}
|
||||
|
||||
// Preconditions is a functional option that sets the Preconditions field of a
|
||||
// DeleteOptions struct.
|
||||
func Preconditions(p *metav1.Preconditions) DeleteOptionFunc {
|
||||
return func(opts *DeleteOptions) {
|
||||
opts.Preconditions = p
|
||||
}
|
||||
}
|
||||
|
||||
// PropagationPolicy is a functional option that sets the PropagationPolicy
|
||||
// field of a DeleteOptions struct.
|
||||
func PropagationPolicy(p metav1.DeletionPropagation) DeleteOptionFunc {
|
||||
return func(opts *DeleteOptions) {
|
||||
opts.PropagationPolicy = &p
|
||||
}
|
||||
}
|
||||
|
||||
// ListOptions contains options for limitting or filtering results.
|
||||
// It's generally a subset of metav1.ListOptions, with support for
|
||||
// pre-parsed selectors (since generally, selectors will be executed
|
||||
// against the cache).
|
||||
type ListOptions struct {
|
||||
// LabelSelector filters results by label. Use SetLabelSelector to
|
||||
// set from raw string form.
|
||||
LabelSelector labels.Selector
|
||||
// FieldSelector filters results by a particular field. In order
|
||||
// to use this with cache-based implementations, restrict usage to
|
||||
// a single field-value pair that's been added to the indexers.
|
||||
FieldSelector fields.Selector
|
||||
|
||||
// Namespace represents the namespace to list for, or empty for
|
||||
// non-namespaced objects, or to list across all namespaces.
|
||||
Namespace string
|
||||
|
||||
// Raw represents raw ListOptions, as passed to the API server. Note
|
||||
// that these may not be respected by all implementations of interface,
|
||||
// and the LabelSelector and FieldSelector fields are ignored.
|
||||
Raw *metav1.ListOptions
|
||||
}
|
||||
|
||||
// SetLabelSelector sets this the label selector of these options
|
||||
// from a string form of the selector.
|
||||
func (o *ListOptions) SetLabelSelector(selRaw string) error {
|
||||
sel, err := labels.Parse(selRaw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.LabelSelector = sel
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetFieldSelector sets this the label selector of these options
|
||||
// from a string form of the selector.
|
||||
func (o *ListOptions) SetFieldSelector(selRaw string) error {
|
||||
sel, err := fields.ParseSelector(selRaw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.FieldSelector = sel
|
||||
return nil
|
||||
}
|
||||
|
||||
// AsListOptions returns these options as a flattened metav1.ListOptions.
|
||||
// This may mutate the Raw field.
|
||||
func (o *ListOptions) AsListOptions() *metav1.ListOptions {
|
||||
if o == nil {
|
||||
return &metav1.ListOptions{}
|
||||
}
|
||||
if o.Raw == nil {
|
||||
o.Raw = &metav1.ListOptions{}
|
||||
}
|
||||
if o.LabelSelector != nil {
|
||||
o.Raw.LabelSelector = o.LabelSelector.String()
|
||||
}
|
||||
if o.FieldSelector != nil {
|
||||
o.Raw.FieldSelector = o.FieldSelector.String()
|
||||
}
|
||||
return o.Raw
|
||||
}
|
||||
|
||||
// MatchingLabels is a convenience function that sets the label selector
|
||||
// to match the given labels, and then returns the options.
|
||||
// It mutates the list options.
|
||||
func (o *ListOptions) MatchingLabels(lbls map[string]string) *ListOptions {
|
||||
sel := labels.SelectorFromSet(lbls)
|
||||
o.LabelSelector = sel
|
||||
return o
|
||||
}
|
||||
|
||||
// MatchingField is a convenience function that sets the field selector
|
||||
// to match the given field, and then returns the options.
|
||||
// It mutates the list options.
|
||||
func (o *ListOptions) MatchingField(name, val string) *ListOptions {
|
||||
sel := fields.SelectorFromSet(fields.Set{name: val})
|
||||
o.FieldSelector = sel
|
||||
return o
|
||||
}
|
||||
|
||||
// InNamespace is a convenience function that sets the namespace,
|
||||
// and then returns the options. It mutates the list options.
|
||||
func (o *ListOptions) InNamespace(ns string) *ListOptions {
|
||||
o.Namespace = ns
|
||||
return o
|
||||
}
|
||||
|
||||
// MatchingLabels is a convenience function that constructs list options
|
||||
// to match the given labels.
|
||||
func MatchingLabels(lbls map[string]string) *ListOptions {
|
||||
return (&ListOptions{}).MatchingLabels(lbls)
|
||||
}
|
||||
|
||||
// MatchingField is a convenience function that constructs list options
|
||||
// to match the given field.
|
||||
func MatchingField(name, val string) *ListOptions {
|
||||
return (&ListOptions{}).MatchingField(name, val)
|
||||
}
|
||||
|
||||
// InNamespace is a convenience function that constructs list
|
||||
// options to list in the given namespace.
|
||||
func InNamespace(ns string) *ListOptions {
|
||||
return (&ListOptions{}).InNamespace(ns)
|
||||
}
|
59
vendor/sigs.k8s.io/controller-runtime/pkg/client/split.go
generated
vendored
Normal file
59
vendor/sigs.k8s.io/controller-runtime/pkg/client/split.go
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DelegatingClient forms an interface Client by composing separate
|
||||
// reader, writer and statusclient interfaces. This way, you can have an Client that
|
||||
// reads from a cache and writes to the API server.
|
||||
type DelegatingClient struct {
|
||||
Reader
|
||||
Writer
|
||||
StatusClient
|
||||
}
|
||||
|
||||
// DelegatingReader forms a interface Reader that will cause Get and List
|
||||
// requests for unstructured types to use the ClientReader while
|
||||
// requests for any other type of object with use the CacheReader.
|
||||
type DelegatingReader struct {
|
||||
CacheReader Reader
|
||||
ClientReader Reader
|
||||
}
|
||||
|
||||
// Get retrieves an obj for a given object key from the Kubernetes Cluster.
|
||||
func (d *DelegatingReader) Get(ctx context.Context, key ObjectKey, obj runtime.Object) error {
|
||||
_, isUnstructured := obj.(*unstructured.Unstructured)
|
||||
if isUnstructured {
|
||||
return d.ClientReader.Get(ctx, key, obj)
|
||||
}
|
||||
return d.CacheReader.Get(ctx, key, obj)
|
||||
}
|
||||
|
||||
// List retrieves list of objects for a given namespace and list options.
|
||||
func (d *DelegatingReader) List(ctx context.Context, opts *ListOptions, list runtime.Object) error {
|
||||
_, isUnstructured := list.(*unstructured.UnstructuredList)
|
||||
if isUnstructured {
|
||||
return d.ClientReader.List(ctx, opts, list)
|
||||
}
|
||||
return d.CacheReader.List(ctx, opts, list)
|
||||
}
|
133
vendor/sigs.k8s.io/controller-runtime/pkg/client/typed_client.go
generated
vendored
Normal file
133
vendor/sigs.k8s.io/controller-runtime/pkg/client/typed_client.go
generated
vendored
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// client is a client.Client that reads and writes directly from/to an API server. It lazily initializes
|
||||
// new clients at the time they are used, and caches the client.
|
||||
type typedClient struct {
|
||||
cache clientCache
|
||||
paramCodec runtime.ParameterCodec
|
||||
}
|
||||
|
||||
// Create implements client.Client
|
||||
func (c *typedClient) Create(ctx context.Context, obj runtime.Object) error {
|
||||
o, err := c.cache.getObjMeta(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return o.Post().
|
||||
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
|
||||
Resource(o.resource()).
|
||||
Body(obj).
|
||||
Context(ctx).
|
||||
Do().
|
||||
Into(obj)
|
||||
}
|
||||
|
||||
// Update implements client.Client
|
||||
func (c *typedClient) Update(ctx context.Context, obj runtime.Object) error {
|
||||
o, err := c.cache.getObjMeta(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return o.Put().
|
||||
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
|
||||
Resource(o.resource()).
|
||||
Name(o.GetName()).
|
||||
Body(obj).
|
||||
Context(ctx).
|
||||
Do().
|
||||
Into(obj)
|
||||
}
|
||||
|
||||
// Delete implements client.Client
|
||||
func (c *typedClient) Delete(ctx context.Context, obj runtime.Object, opts ...DeleteOptionFunc) error {
|
||||
o, err := c.cache.getObjMeta(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deleteOpts := DeleteOptions{}
|
||||
return o.Delete().
|
||||
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
|
||||
Resource(o.resource()).
|
||||
Name(o.GetName()).
|
||||
Body(deleteOpts.ApplyOptions(opts).AsDeleteOptions()).
|
||||
Context(ctx).
|
||||
Do().
|
||||
Error()
|
||||
}
|
||||
|
||||
// Get implements client.Client
|
||||
func (c *typedClient) Get(ctx context.Context, key ObjectKey, obj runtime.Object) error {
|
||||
r, err := c.cache.getResource(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return r.Get().
|
||||
NamespaceIfScoped(key.Namespace, r.isNamespaced()).
|
||||
Resource(r.resource()).
|
||||
Context(ctx).
|
||||
Name(key.Name).Do().Into(obj)
|
||||
}
|
||||
|
||||
// List implements client.Client
|
||||
func (c *typedClient) List(ctx context.Context, opts *ListOptions, obj runtime.Object) error {
|
||||
r, err := c.cache.getResource(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
namespace := ""
|
||||
if opts != nil {
|
||||
namespace = opts.Namespace
|
||||
}
|
||||
return r.Get().
|
||||
NamespaceIfScoped(namespace, r.isNamespaced()).
|
||||
Resource(r.resource()).
|
||||
Body(obj).
|
||||
VersionedParams(opts.AsListOptions(), c.paramCodec).
|
||||
Context(ctx).
|
||||
Do().
|
||||
Into(obj)
|
||||
}
|
||||
|
||||
// UpdateStatus used by StatusWriter to write status.
|
||||
func (c *typedClient) UpdateStatus(ctx context.Context, obj runtime.Object) error {
|
||||
o, err := c.cache.getObjMeta(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO(droot): examine the returned error and check if it error needs to be
|
||||
// wrapped to improve the UX ?
|
||||
// It will be nice to receive an error saying the object doesn't implement
|
||||
// status subresource and check CRD definition
|
||||
return o.Put().
|
||||
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
|
||||
Resource(o.resource()).
|
||||
Name(o.GetName()).
|
||||
SubResource("status").
|
||||
Body(obj).
|
||||
Context(ctx).
|
||||
Do().
|
||||
Into(obj)
|
||||
}
|
162
vendor/sigs.k8s.io/controller-runtime/pkg/client/unstructured_client.go
generated
vendored
Normal file
162
vendor/sigs.k8s.io/controller-runtime/pkg/client/unstructured_client.go
generated
vendored
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/dynamic"
|
||||
)
|
||||
|
||||
// client is a client.Client that reads and writes directly from/to an API server. It lazily initializes
|
||||
// new clients at the time they are used, and caches the client.
|
||||
type unstructuredClient struct {
|
||||
client dynamic.Interface
|
||||
restMapper meta.RESTMapper
|
||||
}
|
||||
|
||||
// Create implements client.Client
|
||||
func (uc *unstructuredClient) Create(_ context.Context, obj runtime.Object) error {
|
||||
u, ok := obj.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("unstructured client did not understand object: %T", obj)
|
||||
}
|
||||
r, err := uc.getResourceInterface(u.GroupVersionKind(), u.GetNamespace())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i, err := r.Create(u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.Object = i.Object
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update implements client.Client
|
||||
func (uc *unstructuredClient) Update(_ context.Context, obj runtime.Object) error {
|
||||
u, ok := obj.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("unstructured client did not understand object: %T", obj)
|
||||
}
|
||||
r, err := uc.getResourceInterface(u.GroupVersionKind(), u.GetNamespace())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i, err := r.Update(u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.Object = i.Object
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete implements client.Client
|
||||
func (uc *unstructuredClient) Delete(_ context.Context, obj runtime.Object, opts ...DeleteOptionFunc) error {
|
||||
u, ok := obj.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("unstructured client did not understand object: %T", obj)
|
||||
}
|
||||
r, err := uc.getResourceInterface(u.GroupVersionKind(), u.GetNamespace())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
deleteOpts := DeleteOptions{}
|
||||
err = r.Delete(u.GetName(), deleteOpts.ApplyOptions(opts).AsDeleteOptions())
|
||||
return err
|
||||
}
|
||||
|
||||
// Get implements client.Client
|
||||
func (uc *unstructuredClient) Get(_ context.Context, key ObjectKey, obj runtime.Object) error {
|
||||
u, ok := obj.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("unstructured client did not understand object: %T", obj)
|
||||
}
|
||||
r, err := uc.getResourceInterface(u.GroupVersionKind(), key.Namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i, err := r.Get(key.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.Object = i.Object
|
||||
return nil
|
||||
}
|
||||
|
||||
// List implements client.Client
|
||||
func (uc *unstructuredClient) List(_ context.Context, opts *ListOptions, obj runtime.Object) error {
|
||||
u, ok := obj.(*unstructured.UnstructuredList)
|
||||
if !ok {
|
||||
return fmt.Errorf("unstructured client did not understand object: %T", obj)
|
||||
}
|
||||
gvk := u.GroupVersionKind()
|
||||
if strings.HasSuffix(gvk.Kind, "List") {
|
||||
gvk.Kind = gvk.Kind[:len(gvk.Kind)-4]
|
||||
}
|
||||
namespace := ""
|
||||
if opts != nil {
|
||||
namespace = opts.Namespace
|
||||
}
|
||||
r, err := uc.getResourceInterface(gvk, namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
i, err := r.List(*opts.AsListOptions())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.Items = i.Items
|
||||
u.Object = i.Object
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uc *unstructuredClient) UpdateStatus(_ context.Context, obj runtime.Object) error {
|
||||
u, ok := obj.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
return fmt.Errorf("unstructured client did not understand object: %T", obj)
|
||||
}
|
||||
r, err := uc.getResourceInterface(u.GroupVersionKind(), u.GetNamespace())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i, err := r.UpdateStatus(u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.Object = i.Object
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uc *unstructuredClient) getResourceInterface(gvk schema.GroupVersionKind, ns string) (dynamic.ResourceInterface, error) {
|
||||
mapping, err := uc.restMapper.RESTMapping(gvk.GroupKind(), gvk.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if mapping.Scope.Name() == meta.RESTScopeNameRoot {
|
||||
return uc.client.Resource(mapping.Resource), nil
|
||||
}
|
||||
return uc.client.Resource(mapping.Resource).Namespace(ns), nil
|
||||
}
|
94
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controller.go
generated
vendored
Normal file
94
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controller.go
generated
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
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 controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/internal/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
"sigs.k8s.io/controller-runtime/pkg/source"
|
||||
)
|
||||
|
||||
// Options are the arguments for creating a new Controller
|
||||
type Options struct {
|
||||
// MaxConcurrentReconciles is the maximum number of concurrent Reconciles which can be run. Defaults to 1.
|
||||
MaxConcurrentReconciles int
|
||||
|
||||
// Reconciler reconciles an object
|
||||
Reconciler reconcile.Reconciler
|
||||
}
|
||||
|
||||
// Controller implements a Kubernetes API. A Controller manages a work queue fed reconcile.Requests
|
||||
// from source.Sources. Work is performed through the reconcile.Reconciler for each enqueued item.
|
||||
// Work typically is reads and writes Kubernetes objects to make the system state match the state specified
|
||||
// in the object Spec.
|
||||
type Controller interface {
|
||||
// Reconciler is called to Reconciler an object by Namespace/Name
|
||||
reconcile.Reconciler
|
||||
|
||||
// Watch takes events provided by a Source and uses the EventHandler to enqueue reconcile.Requests in
|
||||
// response to the events.
|
||||
//
|
||||
// Watch may be provided one or more Predicates to filter events before they are given to the EventHandler.
|
||||
// Events will be passed to the EventHandler iff all provided Predicates evaluate to true.
|
||||
Watch(src source.Source, eventhandler handler.EventHandler, predicates ...predicate.Predicate) error
|
||||
|
||||
// Start starts the controller. Start blocks until stop is closed or a controller has an error starting.
|
||||
Start(stop <-chan struct{}) error
|
||||
}
|
||||
|
||||
// New returns a new Controller registered with the Manager. The Manager will ensure that shared Caches have
|
||||
// been synced before the Controller is Started.
|
||||
func New(name string, mgr manager.Manager, options Options) (Controller, error) {
|
||||
if options.Reconciler == nil {
|
||||
return nil, fmt.Errorf("must specify Reconciler")
|
||||
}
|
||||
|
||||
if len(name) == 0 {
|
||||
return nil, fmt.Errorf("must specify Name for Controller")
|
||||
}
|
||||
|
||||
if options.MaxConcurrentReconciles <= 0 {
|
||||
options.MaxConcurrentReconciles = 1
|
||||
}
|
||||
|
||||
// Inject dependencies into Reconciler
|
||||
if err := mgr.SetFields(options.Reconciler); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create controller with dependencies set
|
||||
c := &controller.Controller{
|
||||
Do: options.Reconciler,
|
||||
Cache: mgr.GetCache(),
|
||||
Config: mgr.GetConfig(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
Client: mgr.GetClient(),
|
||||
Recorder: mgr.GetRecorder(name),
|
||||
Queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), name),
|
||||
MaxConcurrentReconciles: options.MaxConcurrentReconciles,
|
||||
Name: name,
|
||||
}
|
||||
|
||||
// Add the controller as a Manager components
|
||||
return c, mgr.Add(c)
|
||||
}
|
162
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controller_integration_test.go
generated
vendored
Normal file
162
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controller_integration_test.go
generated
vendored
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
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 controller_test
|
||||
|
||||
import (
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
"sigs.k8s.io/controller-runtime/pkg/source"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
)
|
||||
|
||||
var _ = Describe("controller", func() {
|
||||
var reconciled chan reconcile.Request
|
||||
var stop chan struct{}
|
||||
|
||||
BeforeEach(func() {
|
||||
stop = make(chan struct{})
|
||||
reconciled = make(chan reconcile.Request)
|
||||
Expect(cfg).NotTo(BeNil())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
close(stop)
|
||||
})
|
||||
|
||||
Describe("controller", func() {
|
||||
// TODO(directxman12): write a whole suite of controller-client interaction tests
|
||||
|
||||
It("should reconcile", func(done Done) {
|
||||
By("Creating the Manager")
|
||||
cm, err := manager.New(cfg, manager.Options{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating the Controller")
|
||||
instance, err := controller.New("foo-controller", cm, controller.Options{
|
||||
Reconciler: reconcile.Func(
|
||||
func(request reconcile.Request) (reconcile.Result, error) {
|
||||
reconciled <- request
|
||||
return reconcile.Result{}, nil
|
||||
}),
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Watching Resources")
|
||||
err = instance.Watch(&source.Kind{Type: &appsv1.ReplicaSet{}}, &handler.EnqueueRequestForOwner{
|
||||
OwnerType: &appsv1.Deployment{},
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = instance.Watch(&source.Kind{Type: &appsv1.Deployment{}}, &handler.EnqueueRequestForObject{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Starting the Manager")
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
Expect(cm.Start(stop)).NotTo(HaveOccurred())
|
||||
}()
|
||||
|
||||
deployment := &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "deployment-name"},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"foo": "bar"},
|
||||
},
|
||||
Template: corev1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"foo": "bar"}},
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "nginx",
|
||||
Image: "nginx",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
expectedReconcileRequest := reconcile.Request{NamespacedName: types.NamespacedName{
|
||||
Namespace: "default",
|
||||
Name: "deployment-name",
|
||||
}}
|
||||
|
||||
By("Invoking Reconciling for Create")
|
||||
deployment, err = clientset.AppsV1().Deployments("default").Create(deployment)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(<-reconciled).To(Equal(expectedReconcileRequest))
|
||||
|
||||
By("Invoking Reconciling for Update")
|
||||
newDeployment := deployment.DeepCopy()
|
||||
newDeployment.Labels = map[string]string{"foo": "bar"}
|
||||
newDeployment, err = clientset.AppsV1().Deployments("default").Update(newDeployment)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(<-reconciled).To(Equal(expectedReconcileRequest))
|
||||
|
||||
By("Invoking Reconciling for an OwnedObject when it is created")
|
||||
replicaset := &appsv1.ReplicaSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "rs-name",
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
*metav1.NewControllerRef(deployment, schema.GroupVersionKind{
|
||||
Group: "apps",
|
||||
Version: "v1",
|
||||
Kind: "Deployment",
|
||||
}),
|
||||
},
|
||||
},
|
||||
Spec: appsv1.ReplicaSetSpec{
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"foo": "bar"},
|
||||
},
|
||||
Template: deployment.Spec.Template,
|
||||
},
|
||||
}
|
||||
replicaset, err = clientset.AppsV1().ReplicaSets("default").Create(replicaset)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(<-reconciled).To(Equal(expectedReconcileRequest))
|
||||
|
||||
By("Invoking Reconciling for an OwnedObject when it is updated")
|
||||
newReplicaset := replicaset.DeepCopy()
|
||||
newReplicaset.Labels = map[string]string{"foo": "bar"}
|
||||
newReplicaset, err = clientset.AppsV1().ReplicaSets("default").Update(newReplicaset)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(<-reconciled).To(Equal(expectedReconcileRequest))
|
||||
|
||||
By("Invoking Reconciling for an OwnedObject when it is deleted")
|
||||
err = clientset.AppsV1().ReplicaSets("default").Delete(replicaset.Name, &metav1.DeleteOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(<-reconciled).To(Equal(expectedReconcileRequest))
|
||||
|
||||
By("Invoking Reconciling for Delete")
|
||||
err = clientset.AppsV1().Deployments("default").
|
||||
Delete("deployment-name", &metav1.DeleteOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(<-reconciled).To(Equal(expectedReconcileRequest))
|
||||
|
||||
close(done)
|
||||
}, 5)
|
||||
})
|
||||
})
|
56
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controller_suite_test.go
generated
vendored
Normal file
56
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controller_suite_test.go
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
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 controller_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
)
|
||||
|
||||
func TestSource(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecsWithDefaultAndCustomReporters(t, "Controller Integration Suite", []Reporter{envtest.NewlineReporter{}})
|
||||
}
|
||||
|
||||
var testenv *envtest.Environment
|
||||
var cfg *rest.Config
|
||||
var clientset *kubernetes.Clientset
|
||||
|
||||
var _ = BeforeSuite(func(done Done) {
|
||||
logf.SetLogger(logf.ZapLoggerTo(GinkgoWriter, true))
|
||||
|
||||
testenv = &envtest.Environment{}
|
||||
|
||||
var err error
|
||||
cfg, err = testenv.Start()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
clientset, err = kubernetes.NewForConfig(cfg)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
close(done)
|
||||
}, 60)
|
||||
|
||||
var _ = AfterSuite(func() {
|
||||
testenv.Stop()
|
||||
})
|
92
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controller_test.go
generated
vendored
Normal file
92
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controller_test.go
generated
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
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 controller_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
"sigs.k8s.io/controller-runtime/pkg/runtime/inject"
|
||||
)
|
||||
|
||||
var _ = Describe("controller.Controller", func() {
|
||||
var stop chan struct{}
|
||||
|
||||
rec := reconcile.Func(func(reconcile.Request) (reconcile.Result, error) {
|
||||
return reconcile.Result{}, nil
|
||||
})
|
||||
BeforeEach(func() {
|
||||
stop = make(chan struct{})
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
close(stop)
|
||||
})
|
||||
|
||||
Describe("New", func() {
|
||||
It("should return an error if Name is not Specified", func(done Done) {
|
||||
m, err := manager.New(cfg, manager.Options{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
c, err := controller.New("", m, controller.Options{Reconciler: rec})
|
||||
Expect(c).To(BeNil())
|
||||
Expect(err.Error()).To(ContainSubstring("must specify Name for Controller"))
|
||||
|
||||
close(done)
|
||||
})
|
||||
|
||||
It("should return an error if Reconciler is not Specified", func(done Done) {
|
||||
m, err := manager.New(cfg, manager.Options{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
c, err := controller.New("foo", m, controller.Options{})
|
||||
Expect(c).To(BeNil())
|
||||
Expect(err.Error()).To(ContainSubstring("must specify Reconciler"))
|
||||
|
||||
close(done)
|
||||
})
|
||||
|
||||
It("NewController should return an error if injecting Reconciler fails", func(done Done) {
|
||||
m, err := manager.New(cfg, manager.Options{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
c, err := controller.New("foo", m, controller.Options{Reconciler: &failRec{}})
|
||||
Expect(c).To(BeNil())
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("expected error"))
|
||||
|
||||
close(done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
var _ reconcile.Reconciler = &failRec{}
|
||||
var _ inject.Client = &failRec{}
|
||||
|
||||
type failRec struct{}
|
||||
|
||||
func (*failRec) Reconcile(reconcile.Request) (reconcile.Result, error) {
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
|
||||
func (*failRec) InjectClient(client.Client) error {
|
||||
return fmt.Errorf("expected error")
|
||||
}
|
18
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllertest/doc.go
generated
vendored
Normal file
18
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllertest/doc.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package controllertest contains fake informers for testing controllers
|
||||
package controllertest
|
62
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllertest/testing.go
generated
vendored
Normal file
62
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllertest/testing.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
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 controllertest
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
)
|
||||
|
||||
var _ runtime.Object = &ErrorType{}
|
||||
|
||||
// ErrorType implements runtime.Object but isn't registered in any scheme and should cause errors in tests as a result.
|
||||
type ErrorType struct{}
|
||||
|
||||
// GetObjectKind implements runtime.Object
|
||||
func (ErrorType) GetObjectKind() schema.ObjectKind { return nil }
|
||||
|
||||
// DeepCopyObject implements runtime.Object
|
||||
func (ErrorType) DeepCopyObject() runtime.Object { return nil }
|
||||
|
||||
var _ workqueue.RateLimitingInterface = Queue{}
|
||||
|
||||
// Queue implements a RateLimiting queue as a non-ratelimited queue for testing.
|
||||
// This helps testing by having functions that use a RateLimiting queue synchronously add items to the queue.
|
||||
type Queue struct {
|
||||
workqueue.Interface
|
||||
}
|
||||
|
||||
// AddAfter implements RateLimitingInterface.
|
||||
func (q Queue) AddAfter(item interface{}, duration time.Duration) {
|
||||
q.Add(item)
|
||||
}
|
||||
|
||||
// AddRateLimited implements RateLimitingInterface. TODO(community): Implement this.
|
||||
func (q Queue) AddRateLimited(item interface{}) {
|
||||
q.Add(item)
|
||||
}
|
||||
|
||||
// Forget implements RateLimitingInterface. TODO(community): Implement this.
|
||||
func (q Queue) Forget(item interface{}) {}
|
||||
|
||||
// NumRequeues implements RateLimitingInterface. TODO(community): Implement this.
|
||||
func (q Queue) NumRequeues(item interface{}) int {
|
||||
return 0
|
||||
}
|
108
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllertest/util.go
generated
vendored
Normal file
108
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllertest/util.go
generated
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
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 controllertest
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
var _ cache.SharedIndexInformer = &FakeInformer{}
|
||||
|
||||
// FakeInformer provides fake Informer functionality for testing
|
||||
type FakeInformer struct {
|
||||
// Synced is returned by the HasSynced functions to implement the Informer interface
|
||||
Synced bool
|
||||
|
||||
// RunCount is incremented each time RunInformersAndControllers is called
|
||||
RunCount int
|
||||
|
||||
handlers []cache.ResourceEventHandler
|
||||
}
|
||||
|
||||
// AddIndexers does nothing. TODO(community): Implement this.
|
||||
func (f *FakeInformer) AddIndexers(indexers cache.Indexers) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetIndexer does nothing. TODO(community): Implement this.
|
||||
func (f *FakeInformer) GetIndexer() cache.Indexer {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Informer returns the fake Informer.
|
||||
func (f *FakeInformer) Informer() cache.SharedIndexInformer {
|
||||
return f
|
||||
}
|
||||
|
||||
// HasSynced implements the Informer interface. Returns f.Synced
|
||||
func (f *FakeInformer) HasSynced() bool {
|
||||
return f.Synced
|
||||
}
|
||||
|
||||
// AddEventHandler implements the Informer interface. Adds an EventHandler to the fake Informers.
|
||||
func (f *FakeInformer) AddEventHandler(handler cache.ResourceEventHandler) {
|
||||
f.handlers = append(f.handlers, handler)
|
||||
}
|
||||
|
||||
// Run implements the Informer interface. Increments f.RunCount
|
||||
func (f *FakeInformer) Run(<-chan struct{}) {
|
||||
f.RunCount++
|
||||
}
|
||||
|
||||
// Add fakes an Add event for obj
|
||||
func (f *FakeInformer) Add(obj metav1.Object) {
|
||||
for _, h := range f.handlers {
|
||||
h.OnAdd(obj)
|
||||
}
|
||||
}
|
||||
|
||||
// Update fakes an Update event for obj
|
||||
func (f *FakeInformer) Update(oldObj, newObj metav1.Object) {
|
||||
for _, h := range f.handlers {
|
||||
h.OnUpdate(oldObj, newObj)
|
||||
}
|
||||
}
|
||||
|
||||
// Delete fakes an Delete event for obj
|
||||
func (f *FakeInformer) Delete(obj metav1.Object) {
|
||||
for _, h := range f.handlers {
|
||||
h.OnDelete(obj)
|
||||
}
|
||||
}
|
||||
|
||||
// AddEventHandlerWithResyncPeriod does nothing. TODO(community): Implement this.
|
||||
func (f *FakeInformer) AddEventHandlerWithResyncPeriod(handler cache.ResourceEventHandler, resyncPeriod time.Duration) {
|
||||
|
||||
}
|
||||
|
||||
// GetStore does nothing. TODO(community): Implement this.
|
||||
func (f *FakeInformer) GetStore() cache.Store {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetController does nothing. TODO(community): Implement this.
|
||||
func (f *FakeInformer) GetController() cache.Controller {
|
||||
return nil
|
||||
}
|
||||
|
||||
// LastSyncResourceVersion does nothing. TODO(community): Implement this.
|
||||
func (f *FakeInformer) LastSyncResourceVersion() string {
|
||||
return ""
|
||||
}
|
178
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/controllerutil.go
generated
vendored
Normal file
178
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/controllerutil.go
generated
vendored
Normal file
@ -0,0 +1,178 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controllerutil
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
||||
)
|
||||
|
||||
// AlreadyOwnedError is an error returned if the object you are trying to assign
|
||||
// a controller reference is already owned by another controller Object is the
|
||||
// subject and Owner is the reference for the current owner
|
||||
type AlreadyOwnedError struct {
|
||||
Object v1.Object
|
||||
Owner v1.OwnerReference
|
||||
}
|
||||
|
||||
func (e *AlreadyOwnedError) Error() string {
|
||||
return fmt.Sprintf("Object %s/%s is already owned by another %s controller %s", e.Object.GetNamespace(), e.Object.GetName(), e.Owner.Kind, e.Owner.Name)
|
||||
}
|
||||
|
||||
func newAlreadyOwnedError(Object v1.Object, Owner v1.OwnerReference) *AlreadyOwnedError {
|
||||
return &AlreadyOwnedError{
|
||||
Object: Object,
|
||||
Owner: Owner,
|
||||
}
|
||||
}
|
||||
|
||||
// SetControllerReference sets owner as a Controller OwnerReference on owned.
|
||||
// This is used for garbage collection of the owned object and for
|
||||
// reconciling the owner object on changes to owned (with a Watch + EnqueueRequestForOwner).
|
||||
// Since only one OwnerReference can be a controller, it returns an error if
|
||||
// there is another OwnerReference with Controller flag set.
|
||||
func SetControllerReference(owner, object v1.Object, scheme *runtime.Scheme) error {
|
||||
ro, ok := owner.(runtime.Object)
|
||||
if !ok {
|
||||
return fmt.Errorf("is not a %T a runtime.Object, cannot call SetControllerReference", owner)
|
||||
}
|
||||
|
||||
gvk, err := apiutil.GVKForObject(ro, scheme)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create a new ref
|
||||
ref := *v1.NewControllerRef(owner, schema.GroupVersionKind{Group: gvk.Group, Version: gvk.Version, Kind: gvk.Kind})
|
||||
|
||||
existingRefs := object.GetOwnerReferences()
|
||||
fi := -1
|
||||
for i, r := range existingRefs {
|
||||
if referSameObject(ref, r) {
|
||||
fi = i
|
||||
} else if r.Controller != nil && *r.Controller {
|
||||
return newAlreadyOwnedError(object, r)
|
||||
}
|
||||
}
|
||||
if fi == -1 {
|
||||
existingRefs = append(existingRefs, ref)
|
||||
} else {
|
||||
existingRefs[fi] = ref
|
||||
}
|
||||
|
||||
// Update owner references
|
||||
object.SetOwnerReferences(existingRefs)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns true if a and b point to the same object
|
||||
func referSameObject(a, b v1.OwnerReference) bool {
|
||||
aGV, err := schema.ParseGroupVersion(a.APIVersion)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
bGV, err := schema.ParseGroupVersion(b.APIVersion)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return aGV == bGV && a.Kind == b.Kind && a.Name == b.Name
|
||||
}
|
||||
|
||||
// OperationResult is the action result of a CreateOrUpdate call
|
||||
type OperationResult string
|
||||
|
||||
const ( // They should complete the sentence "Deployment default/foo has been ..."
|
||||
// OperationResultNone means that the resource has not been changed
|
||||
OperationResultNone OperationResult = "unchanged"
|
||||
// OperationResultCreated means that a new resource is created
|
||||
OperationResultCreated OperationResult = "created"
|
||||
// OperationResultUpdated means that an existing resource is updated
|
||||
OperationResultUpdated OperationResult = "updated"
|
||||
)
|
||||
|
||||
// CreateOrUpdate creates or updates the given object obj in the Kubernetes
|
||||
// cluster. The object's desired state should be reconciled with the existing
|
||||
// state using the passed in ReconcileFn. obj must be a struct pointer so that
|
||||
// obj can be updated with the content returned by the Server.
|
||||
//
|
||||
// It returns the executed operation and an error.
|
||||
func CreateOrUpdate(ctx context.Context, c client.Client, obj runtime.Object, f MutateFn) (OperationResult, error) {
|
||||
// op is the operation we are going to attempt
|
||||
op := OperationResultNone
|
||||
|
||||
// get the existing object meta
|
||||
metaObj, ok := obj.(v1.Object)
|
||||
if !ok {
|
||||
return OperationResultNone, fmt.Errorf("%T does not implement metav1.Object interface", obj)
|
||||
}
|
||||
|
||||
// retrieve the existing object
|
||||
key := client.ObjectKey{
|
||||
Name: metaObj.GetName(),
|
||||
Namespace: metaObj.GetNamespace(),
|
||||
}
|
||||
err := c.Get(ctx, key, obj)
|
||||
|
||||
// reconcile the existing object
|
||||
existing := obj.DeepCopyObject()
|
||||
existingObjMeta := existing.(v1.Object)
|
||||
existingObjMeta.SetName(metaObj.GetName())
|
||||
existingObjMeta.SetNamespace(metaObj.GetNamespace())
|
||||
|
||||
if e := f(obj); e != nil {
|
||||
return OperationResultNone, e
|
||||
}
|
||||
|
||||
if metaObj.GetName() != existingObjMeta.GetName() {
|
||||
return OperationResultNone, fmt.Errorf("ReconcileFn cannot mutate objects name")
|
||||
}
|
||||
|
||||
if metaObj.GetNamespace() != existingObjMeta.GetNamespace() {
|
||||
return OperationResultNone, fmt.Errorf("ReconcileFn cannot mutate objects namespace")
|
||||
}
|
||||
|
||||
if errors.IsNotFound(err) {
|
||||
err = c.Create(ctx, obj)
|
||||
op = OperationResultCreated
|
||||
} else if err == nil {
|
||||
if reflect.DeepEqual(existing, obj) {
|
||||
return OperationResultNone, nil
|
||||
}
|
||||
err = c.Update(ctx, obj)
|
||||
op = OperationResultUpdated
|
||||
} else {
|
||||
return OperationResultNone, err
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
op = OperationResultNone
|
||||
}
|
||||
return op, err
|
||||
}
|
||||
|
||||
// MutateFn is a function which mutates the existing object into it's desired state.
|
||||
type MutateFn func(existing runtime.Object) error
|
53
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/controllerutil_suite_test.go
generated
vendored
Normal file
53
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/controllerutil_suite_test.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
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 controllerutil_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||
)
|
||||
|
||||
func TestControllerutil(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Controllerutil Suite")
|
||||
}
|
||||
|
||||
var t *envtest.Environment
|
||||
var cfg *rest.Config
|
||||
var c client.Client
|
||||
|
||||
var _ = BeforeSuite(func() {
|
||||
var err error
|
||||
|
||||
t = &envtest.Environment{}
|
||||
|
||||
cfg, err = t.Start()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
c, err = client.New(cfg, client.Options{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
var _ = AfterSuite(func() {
|
||||
t.Stop()
|
||||
})
|
275
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/controllerutil_test.go
generated
vendored
Normal file
275
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/controllerutil_test.go
generated
vendored
Normal file
@ -0,0 +1,275 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controllerutil_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
)
|
||||
|
||||
var _ = Describe("Controllerutil", func() {
|
||||
Describe("SetControllerReference", func() {
|
||||
It("should set the OwnerReference if it can find the group version kind", func() {
|
||||
rs := &appsv1.ReplicaSet{}
|
||||
dep := &extensionsv1beta1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", UID: "foo-uid"},
|
||||
}
|
||||
|
||||
Expect(controllerutil.SetControllerReference(dep, rs, scheme.Scheme)).NotTo(HaveOccurred())
|
||||
t := true
|
||||
Expect(rs.OwnerReferences).To(ConsistOf(metav1.OwnerReference{
|
||||
Name: "foo",
|
||||
Kind: "Deployment",
|
||||
APIVersion: "extensions/v1beta1",
|
||||
UID: "foo-uid",
|
||||
Controller: &t,
|
||||
BlockOwnerDeletion: &t,
|
||||
}))
|
||||
})
|
||||
|
||||
It("should return an error if it can't find the group version kind of the owner", func() {
|
||||
rs := &appsv1.ReplicaSet{}
|
||||
dep := &extensionsv1beta1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
|
||||
}
|
||||
Expect(controllerutil.SetControllerReference(dep, rs, runtime.NewScheme())).To(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should return an error if the owner isn't a runtime.Object", func() {
|
||||
rs := &appsv1.ReplicaSet{}
|
||||
Expect(controllerutil.SetControllerReference(&errMetaObj{}, rs, scheme.Scheme)).To(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should return an error if object is already owned by another controller", func() {
|
||||
t := true
|
||||
rsOwners := []metav1.OwnerReference{
|
||||
metav1.OwnerReference{
|
||||
Name: "bar",
|
||||
Kind: "Deployment",
|
||||
APIVersion: "extensions/v1beta1",
|
||||
UID: "bar-uid",
|
||||
Controller: &t,
|
||||
BlockOwnerDeletion: &t,
|
||||
},
|
||||
}
|
||||
rs := &appsv1.ReplicaSet{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default", OwnerReferences: rsOwners}}
|
||||
dep := &extensionsv1beta1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default", UID: "foo-uid"}}
|
||||
|
||||
err := controllerutil.SetControllerReference(dep, rs, scheme.Scheme)
|
||||
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err).To(BeAssignableToTypeOf(&controllerutil.AlreadyOwnedError{}))
|
||||
})
|
||||
|
||||
It("should not duplicate existing owner reference", func() {
|
||||
f := false
|
||||
t := true
|
||||
rsOwners := []metav1.OwnerReference{
|
||||
metav1.OwnerReference{
|
||||
Name: "foo",
|
||||
Kind: "Deployment",
|
||||
APIVersion: "extensions/v1beta1",
|
||||
UID: "foo-uid",
|
||||
Controller: &f,
|
||||
BlockOwnerDeletion: &t,
|
||||
},
|
||||
}
|
||||
rs := &appsv1.ReplicaSet{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default", OwnerReferences: rsOwners}}
|
||||
dep := &extensionsv1beta1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default", UID: "foo-uid"}}
|
||||
|
||||
Expect(controllerutil.SetControllerReference(dep, rs, scheme.Scheme)).NotTo(HaveOccurred())
|
||||
Expect(rs.OwnerReferences).To(ConsistOf(metav1.OwnerReference{
|
||||
Name: "foo",
|
||||
Kind: "Deployment",
|
||||
APIVersion: "extensions/v1beta1",
|
||||
UID: "foo-uid",
|
||||
Controller: &t,
|
||||
BlockOwnerDeletion: &t,
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("CreateOrUpdate", func() {
|
||||
var deploy *appsv1.Deployment
|
||||
var deplSpec appsv1.DeploymentSpec
|
||||
var deplKey types.NamespacedName
|
||||
|
||||
BeforeEach(func() {
|
||||
deploy = &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("deploy-%d", rand.Int31()),
|
||||
Namespace: "default",
|
||||
},
|
||||
}
|
||||
|
||||
deplSpec = appsv1.DeploymentSpec{
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"foo": "bar"},
|
||||
},
|
||||
Template: corev1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
corev1.Container{
|
||||
Name: "busybox",
|
||||
Image: "busybox",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
deplKey = types.NamespacedName{
|
||||
Name: deploy.Name,
|
||||
Namespace: deploy.Namespace,
|
||||
}
|
||||
})
|
||||
|
||||
It("creates a new object if one doesn't exists", func() {
|
||||
op, err := controllerutil.CreateOrUpdate(context.TODO(), c, deploy, deploymentSpecr(deplSpec))
|
||||
|
||||
By("returning OperationResultCreatedd")
|
||||
Expect(op).To(BeEquivalentTo(controllerutil.OperationResultCreated))
|
||||
|
||||
By("returning no error")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("actually having the deployment created")
|
||||
fetched := &appsv1.Deployment{}
|
||||
Expect(c.Get(context.TODO(), deplKey, fetched)).To(Succeed())
|
||||
})
|
||||
|
||||
It("updates existing object", func() {
|
||||
var scale int32 = 2
|
||||
op, err := controllerutil.CreateOrUpdate(context.TODO(), c, deploy, deploymentSpecr(deplSpec))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(op).To(BeEquivalentTo(controllerutil.OperationResultCreated))
|
||||
|
||||
op, err = controllerutil.CreateOrUpdate(context.TODO(), c, deploy, deploymentScaler(scale))
|
||||
By("returning OperationResultUpdatedd")
|
||||
Expect(op).To(BeEquivalentTo(controllerutil.OperationResultUpdated))
|
||||
|
||||
By("returning no error")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("actually having the deployment scaled")
|
||||
fetched := &appsv1.Deployment{}
|
||||
Expect(c.Get(context.TODO(), deplKey, fetched)).To(Succeed())
|
||||
Expect(*fetched.Spec.Replicas).To(Equal(scale))
|
||||
})
|
||||
|
||||
It("updates only changed objects", func() {
|
||||
op, err := controllerutil.CreateOrUpdate(context.TODO(), c, deploy, deploymentSpecr(deplSpec))
|
||||
|
||||
Expect(op).To(BeEquivalentTo(controllerutil.OperationResultCreated))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
op, err = controllerutil.CreateOrUpdate(context.TODO(), c, deploy, deploymentIdentity)
|
||||
|
||||
By("returning OperationResultNone")
|
||||
Expect(op).To(BeEquivalentTo(controllerutil.OperationResultNone))
|
||||
|
||||
By("returning no error")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("errors when reconcile renames an object", func() {
|
||||
op, err := controllerutil.CreateOrUpdate(context.TODO(), c, deploy, deploymentSpecr(deplSpec))
|
||||
|
||||
Expect(op).To(BeEquivalentTo(controllerutil.OperationResultCreated))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
op, err = controllerutil.CreateOrUpdate(context.TODO(), c, deploy, deploymentRenamer)
|
||||
|
||||
By("returning OperationResultNone")
|
||||
Expect(op).To(BeEquivalentTo(controllerutil.OperationResultNone))
|
||||
|
||||
By("returning error")
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
|
||||
It("errors when object namespace changes", func() {
|
||||
op, err := controllerutil.CreateOrUpdate(context.TODO(), c, deploy, deploymentSpecr(deplSpec))
|
||||
|
||||
Expect(op).To(BeEquivalentTo(controllerutil.OperationResultCreated))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
op, err = controllerutil.CreateOrUpdate(context.TODO(), c, deploy, deploymentNamespaceChanger)
|
||||
|
||||
By("returning OperationResultNone")
|
||||
Expect(op).To(BeEquivalentTo(controllerutil.OperationResultNone))
|
||||
|
||||
By("returning error")
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
var _ metav1.Object = &errMetaObj{}
|
||||
|
||||
type errMetaObj struct {
|
||||
metav1.ObjectMeta
|
||||
}
|
||||
|
||||
func deploymentSpecr(spec appsv1.DeploymentSpec) controllerutil.MutateFn {
|
||||
return func(obj runtime.Object) error {
|
||||
deploy := obj.(*appsv1.Deployment)
|
||||
deploy.Spec = spec
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var deploymentIdentity controllerutil.MutateFn = func(obj runtime.Object) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var deploymentRenamer controllerutil.MutateFn = func(obj runtime.Object) error {
|
||||
deploy := obj.(*appsv1.Deployment)
|
||||
deploy.Name = fmt.Sprintf("%s-1", deploy.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
var deploymentNamespaceChanger controllerutil.MutateFn = func(obj runtime.Object) error {
|
||||
deploy := obj.(*appsv1.Deployment)
|
||||
deploy.Namespace = fmt.Sprintf("%s-1", deploy.Namespace)
|
||||
return nil
|
||||
}
|
||||
|
||||
func deploymentScaler(replicas int32) controllerutil.MutateFn {
|
||||
fn := func(obj runtime.Object) error {
|
||||
deploy := obj.(*appsv1.Deployment)
|
||||
deploy.Spec.Replicas = &replicas
|
||||
return nil
|
||||
}
|
||||
return fn
|
||||
}
|
20
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/doc.go
generated
vendored
Normal file
20
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/doc.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
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 controllerutil contains utility functions for working with and implementing Controllers.
|
||||
*/
|
||||
package controllerutil
|
78
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/example_test.go
generated
vendored
Normal file
78
vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/example_test.go
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
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 controllerutil_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
)
|
||||
|
||||
var (
|
||||
log = logf.Log.WithName("controllerutil-examples")
|
||||
)
|
||||
|
||||
// This example creates or updates an existing deployment
|
||||
func ExampleCreateOrUpdate() {
|
||||
// c is client.Client
|
||||
|
||||
// Create or Update the deployment default/foo
|
||||
deployment := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"}}
|
||||
|
||||
op, err := controllerutil.CreateOrUpdate(context.TODO(), c, deployment, func(existing runtime.Object) error {
|
||||
deploy := existing.(*appsv1.Deployment)
|
||||
|
||||
// Deployment selector is immutable so we set this value only if
|
||||
// a new object is going to be created
|
||||
if deploy.ObjectMeta.CreationTimestamp.IsZero() {
|
||||
deploy.Spec.Selector = &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"foo": "bar"},
|
||||
}
|
||||
}
|
||||
|
||||
// update the Deployment pod template
|
||||
deploy.Spec.Template = corev1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
corev1.Container{
|
||||
Name: "busybox",
|
||||
Image: "busybox",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Error(err, "Deployment reconcile failed")
|
||||
} else {
|
||||
log.Info("Deployment successfully reconciled", "operation", op)
|
||||
}
|
||||
}
|
25
vendor/sigs.k8s.io/controller-runtime/pkg/controller/doc.go
generated
vendored
Normal file
25
vendor/sigs.k8s.io/controller-runtime/pkg/controller/doc.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
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 controller provides types and functions for building Controllers. Controllers implement Kubernetes APIs.
|
||||
|
||||
Creation
|
||||
|
||||
To create a new Controller, first create a manager.Manager and pass it to the controller.New function.
|
||||
The Controller MUST be started by calling Manager.Start.
|
||||
*/
|
||||
package controller
|
79
vendor/sigs.k8s.io/controller-runtime/pkg/controller/example_test.go
generated
vendored
Normal file
79
vendor/sigs.k8s.io/controller-runtime/pkg/controller/example_test.go
generated
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
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 controller_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/runtime/signals"
|
||||
"sigs.k8s.io/controller-runtime/pkg/source"
|
||||
)
|
||||
|
||||
var (
|
||||
mgr manager.Manager
|
||||
// NB: don't call SetLogger in init(), or else you'll mess up logging in the main suite.
|
||||
log = logf.Log.WithName("controller-examples")
|
||||
)
|
||||
|
||||
// This example creates a new Controller named "pod-controller" with a no-op reconcile function. The
|
||||
// manager.Manager will be used to Start the Controller, and will provide it a shared Cache and Client.
|
||||
func ExampleNew() {
|
||||
_, err := controller.New("pod-controller", mgr, controller.Options{
|
||||
Reconciler: reconcile.Func(func(o reconcile.Request) (reconcile.Result, error) {
|
||||
// Your business logic to implement the API by creating, updating, deleting objects goes here.
|
||||
return reconcile.Result{}, nil
|
||||
}),
|
||||
})
|
||||
if err != nil {
|
||||
log.Error(err, "unable to create pod-controller")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// This example starts a new Controller named "pod-controller" to Watch Pods and call a no-op Reconciler.
|
||||
func ExampleController() {
|
||||
// mgr is a manager.Manager
|
||||
|
||||
// Create a new Controller that will call the provided Reconciler function in response
|
||||
// to events.
|
||||
c, err := controller.New("pod-controller", mgr, controller.Options{
|
||||
Reconciler: reconcile.Func(func(o reconcile.Request) (reconcile.Result, error) {
|
||||
// Your business logic to implement the API by creating, updating, deleting objects goes here.
|
||||
return reconcile.Result{}, nil
|
||||
}),
|
||||
})
|
||||
if err != nil {
|
||||
log.Error(err, "unable to create pod-controller")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Watch for Pod create / update / delete events and call Reconcile
|
||||
err = c.Watch(&source.Kind{Type: &v1.Pod{}}, &handler.EnqueueRequestForObject{})
|
||||
if err != nil {
|
||||
log.Error(err, "unable to watch pods")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Start the Controller through the manager.
|
||||
mgr.Start(signals.SetupSignalHandler())
|
||||
}
|
207
vendor/sigs.k8s.io/controller-runtime/pkg/doc.go
generated
vendored
Normal file
207
vendor/sigs.k8s.io/controller-runtime/pkg/doc.go
generated
vendored
Normal file
@ -0,0 +1,207 @@
|
||||
/*
|
||||
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 pkg provides libraries for building Controllers. Controllers implement Kubernetes APIs
|
||||
and are foundational to building Operators, Workload APIs, Configuration APIs, Autoscalers, and more.
|
||||
|
||||
Client
|
||||
|
||||
Client provides a Read + Write client for reading and writing Kubernetes objects.
|
||||
|
||||
Cache
|
||||
|
||||
Cache provides a Read client for reading objects from a local cache.
|
||||
A cache may register handlers to respond to events that update the cache.
|
||||
|
||||
Manager
|
||||
|
||||
Manager is required for creating a Controller and provides the Controller shared dependencies such as
|
||||
clients, caches, schemes, etc. Controllers should be Started through the Manager by calling Manager.Start.
|
||||
|
||||
Controller
|
||||
|
||||
Controller implements a Kubernetes API by responding to events (object Create, Update, Delete) and ensuring that
|
||||
the state specified in the Spec of the object matches the state of the system. This is called a Reconciler.
|
||||
If they do not match, the Controller will create / update / delete objects as needed to make them match.
|
||||
|
||||
Controllers are implemented as worker queues that process reconcile.Requests (requests to Reconciler the
|
||||
state for a specific object).
|
||||
|
||||
Unlike http handlers, Controllers DO NOT handle events directly, but enqueue Requests to eventually Reconciler
|
||||
the object. This means the handling of multiple events may be batched together and the full state of the
|
||||
system must be read for each Reconciler.
|
||||
|
||||
* Controllers require a Reconciler to be provided to perform the work pulled from the work queue.
|
||||
|
||||
* Controller require Watches to be configured to enqueue reconcile.Requests in response to events.
|
||||
|
||||
Webhook
|
||||
|
||||
Admission Webhooks are a mechanism for extending kubernetes APIs. Webhooks can be configured with target
|
||||
event type (object Create, Update, Delete), the API server will send AdmissionRequests to them
|
||||
when certain events happen. The webhooks may mutate and (or) validate the object embedded in
|
||||
the AdmissionReview requests and send back the response to the API server.
|
||||
|
||||
There are 2 types of admission webhook: mutating and validating admission webhook.
|
||||
Mutating webhook is used to mutate a core API object or a CRD instance before the API server admits it.
|
||||
Validating webhook is used to validate if an object meets certain requirements.
|
||||
|
||||
* Admission Webhooks require Handler(s) to be provided to process the received AdmissionReview requests.
|
||||
|
||||
Reconciler
|
||||
|
||||
Reconciler is a function provided to a Controller that may be called at anytime with the Name and Namespace of an object.
|
||||
When called, Reconciler will ensure that the state of the system matches what is specified in the object at the
|
||||
time Reconciler is called.
|
||||
|
||||
Example: Reconciler invoked for a ReplicaSet object. The ReplicaSet specifies 5 replicas but only
|
||||
3 Pods exist in the system. Reconciler creates 2 more Pods and sets their OwnerReference to point at the
|
||||
ReplicaSet with controller=true.
|
||||
|
||||
* Reconciler contains all of the business logic of a Controller.
|
||||
|
||||
* Reconciler typically works on a single object type. - e.g. it will only reconcile ReplicaSets. For separate
|
||||
types use separate Controllers. If you wish to trigger reconciles from other objects, you can provide
|
||||
a mapping (e.g. owner references) that maps the object that triggers the reconcile to the object being reconciled.
|
||||
|
||||
* Reconciler is provided the Name / Namespace of the object to reconcile.
|
||||
|
||||
* Reconciler does not care about the event contents or event type responsible for triggering the Reconciler.
|
||||
- e.g. it doesn't matter whether a ReplicaSet was created or updated, Reconciler will always compare the number of
|
||||
Pods in the system against what is specified in the object at the time it is called.
|
||||
|
||||
Source
|
||||
|
||||
resource.Source is an argument to Controller.Watch that provides a stream of events.
|
||||
Events typically come from watching Kubernetes APIs (e.g. Pod Create, Update, Delete).
|
||||
|
||||
Example: source.Kind uses the Kubernetes API Watch endpoint for a GroupVersionKind to provide
|
||||
Create, Update, Delete events.
|
||||
|
||||
* Source provides a stream of events (e.g. object Create, Update, Delete) for Kubernetes objects typically
|
||||
through the Watch API.
|
||||
|
||||
* Users SHOULD only use the provided Source implementations instead of implementing their own for nearly all cases.
|
||||
|
||||
EventHandler
|
||||
|
||||
handler.EventHandler is a argument to Controller.Watch that enqueues reconcile.Requests in response to events.
|
||||
|
||||
Example: a Pod Create event from a Source is provided to the eventhandler.EnqueueHandler, which enqueues a
|
||||
reconcile.Request containing the name / Namespace of the Pod.
|
||||
|
||||
* EventHandlers handle events by enqueueing reconcile.Requests for one or more objects.
|
||||
|
||||
* EventHandlers MAY map an event for an object to a reconcile.Request for an object of the same type.
|
||||
|
||||
* EventHandlers MAY map an event for an object to a reconcile.Request for an object of a different type - e.g.
|
||||
map a Pod event to a reconcile.Request for the owning ReplicaSet.
|
||||
|
||||
* EventHandlers MAY map an event for an object to multiple reconcile.Requests for objects of the same or a different
|
||||
type - e.g. map a Node event to objects that respond to cluster resize events.
|
||||
|
||||
* Users SHOULD only use the provided EventHandler implementations instead of implementing their own for almost
|
||||
all cases.
|
||||
|
||||
Predicate
|
||||
|
||||
predicate.Predicate is an optional argument to Controller.Watch that filters events. This allows common filters to be
|
||||
reused and composed.
|
||||
|
||||
* Predicate takes an event and returns a bool (true to enqueue)
|
||||
|
||||
* Predicates are optional arguments
|
||||
|
||||
* Users SHOULD use the provided Predicate implementations, but MAY implement additional
|
||||
Predicates e.g. generation changed, label selectors changed etc.
|
||||
|
||||
PodController Diagram
|
||||
|
||||
Source provides event:
|
||||
|
||||
* &source.KindSource{&v1.Pod{}} -> (Pod foo/bar Create Event)
|
||||
|
||||
EventHandler enqueues Request:
|
||||
|
||||
* &handler.EnqueueRequestForObject{} -> (reconcile.Request{types.NamespaceName{Name: "foo", Namespace: "bar"}})
|
||||
|
||||
Reconciler is called with the Request:
|
||||
|
||||
* Reconciler(reconcile.Request{types.NamespaceName{Name: "foo", Namespace: "bar"}})
|
||||
|
||||
Usage
|
||||
|
||||
The following example shows creating a new Controller program which Reconciles ReplicaSet objects in response
|
||||
to Pod or ReplicaSet events. The Reconciler function simply adds a label to the ReplicaSet.
|
||||
|
||||
See the example/main.go for a usage example.
|
||||
|
||||
Controller Example
|
||||
|
||||
1. Watch ReplicaSet and Pods Sources
|
||||
|
||||
1.1 ReplicaSet -> handler.EnqueueRequestForObject - enqueue a Request with the ReplicaSet Namespace and Name.
|
||||
|
||||
1.2 Pod (created by ReplicaSet) -> handler.EnqueueRequestForOwnerHandler - enqueue a Request with the
|
||||
Owning ReplicaSet Namespace and Name.
|
||||
|
||||
2. Reconciler ReplicaSet in response to an event
|
||||
|
||||
2.1 ReplicaSet object created -> Read ReplicaSet, try to read Pods -> if is missing create Pods.
|
||||
|
||||
2.2 Reconciler triggered by creation of Pods -> Read ReplicaSet and Pods, do nothing.
|
||||
|
||||
2.3 Reconciler triggered by deletion of Pods from some other actor -> Read ReplicaSet and Pods, create replacement Pods.
|
||||
|
||||
Watching and EventHandling
|
||||
|
||||
Controllers may Watch multiple Kinds of objects (e.g. Pods, ReplicaSets and Deployments), but they Reconciler
|
||||
only a single Type. When one Type of object must be be updated in response to changes in another Type of object,
|
||||
an EnqueueRequestFromMapFunc may be used to map events from one type to another. e.g. Respond to a cluster resize
|
||||
event (add / delete Node) by re-reconciling all instances of some API.
|
||||
|
||||
A Deployment Controller might use an EnqueueRequestForObject and EnqueueRequestForOwner to:
|
||||
|
||||
* Watch for Deployment Events - enqueue the Namespace and Name of the Deployment.
|
||||
|
||||
* Watch for ReplicaSet Events - enqueue the Namespace and Name of the Deployment that created the ReplicaSet
|
||||
(e.g the Owner)
|
||||
|
||||
Note: reconcile.Requests are deduplicated when they are enqueued. Many Pod Events for the same ReplicaSet
|
||||
may trigger only 1 reconcile invocation as each Event results in the Handler trying to enqueue
|
||||
the same reconcile.Request for the ReplicaSet.
|
||||
|
||||
Controller Writing Tips
|
||||
|
||||
Reconciler Runtime Complexity:
|
||||
|
||||
* It is better to write Controllers to perform an O(1) Reconciler N times (e.g. on N different objects) instead of
|
||||
performing an O(N) Reconciler 1 time (e.g. on a single object which manages N other objects).
|
||||
|
||||
* Example: If you need to update all Services in response to a Node being added - Reconciler Services but Watch
|
||||
Nodes (transformed to Service object name / Namespaces) instead of Reconciling Nodes and updating Services
|
||||
|
||||
Event Multiplexing:
|
||||
|
||||
* reconcile.Requests for the same Name / Namespace are batched and deduplicated when they are enqueued. This allows
|
||||
Controllers to gracefully handle a high volume of events for a single object. Multiplexing multiple event Sources to
|
||||
a single object Type will batch requests across events for different object types.
|
||||
|
||||
* Example: Pod events for a ReplicaSet are transformed to a ReplicaSet Name / Namespace, so the ReplicaSet
|
||||
will be Reconciled only 1 time for multiple events from multiple Pods.
|
||||
*/
|
||||
package pkg
|
221
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/crd.go
generated
vendored
Normal file
221
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/crd.go
generated
vendored
Normal file
@ -0,0 +1,221 @@
|
||||
/*
|
||||
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 (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
// CRDInstallOptions are the options for installing CRDs
|
||||
type CRDInstallOptions struct {
|
||||
// Paths is the path to the directory containing CRDs
|
||||
Paths []string
|
||||
|
||||
// CRDs is a list of CRDs to install
|
||||
CRDs []*apiextensionsv1beta1.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
|
||||
}
|
||||
|
||||
const defaultPollInterval = 100 * time.Millisecond
|
||||
const defaultMaxWait = 10 * time.Second
|
||||
|
||||
// InstallCRDs installs a collection of CRDs into a cluster by reading the crd yaml files from a directory
|
||||
func InstallCRDs(config *rest.Config, options CRDInstallOptions) ([]*apiextensionsv1beta1.CustomResourceDefinition, error) {
|
||||
defaultCRDOptions(&options)
|
||||
|
||||
// Read the CRD yamls into options.CRDs
|
||||
if err := readCRDFiles(&options); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create the CRDs in the apiserver
|
||||
if err := CreateCRDs(config, options.CRDs); err != nil {
|
||||
return options.CRDs, err
|
||||
}
|
||||
|
||||
// Wait for the CRDs to appear as Resources in the apiserver
|
||||
if err := WaitForCRDs(config, options.CRDs, options); err != nil {
|
||||
return options.CRDs, 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 {
|
||||
for _, path := range options.Paths {
|
||||
if _, err := os.Stat(path); !options.ErrorIfPathMissing && os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
new, err := readCRDs(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options.CRDs = append(options.CRDs, new...)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// defaultCRDOptions sets the default values for CRDs
|
||||
func defaultCRDOptions(o *CRDInstallOptions) {
|
||||
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 []*apiextensionsv1beta1.CustomResourceDefinition, options CRDInstallOptions) error {
|
||||
// Add each CRD to a map of GroupVersion to Resource
|
||||
waitingFor := map[schema.GroupVersion]*sets.String{}
|
||||
for _, crd := range crds {
|
||||
gv := schema.GroupVersion{Group: crd.Spec.Group, Version: crd.Spec.Version}
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// CreateCRDs creates the CRDs
|
||||
func CreateCRDs(config *rest.Config, crds []*apiextensionsv1beta1.CustomResourceDefinition) error {
|
||||
cs, err := clientset.NewForConfig(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create each CRD
|
||||
for _, crd := range crds {
|
||||
if _, err := cs.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// readCRDs reads the CRDs from files and Unmarshals them into structs
|
||||
func readCRDs(path string) ([]*apiextensionsv1beta1.CustomResourceDefinition, error) {
|
||||
// Get the CRD files
|
||||
var files []os.FileInfo
|
||||
var err error
|
||||
if files, err = ioutil.ReadDir(path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// White list the file extensions that may contain CRDs
|
||||
crdExts := sets.NewString(".json", ".yaml", ".yml")
|
||||
|
||||
var crds []*apiextensionsv1beta1.CustomResourceDefinition
|
||||
for _, file := range files {
|
||||
// Only parse whitelisted file types
|
||||
if !crdExts.Has(filepath.Ext(file.Name())) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Unmarshal the file into a struct
|
||||
b, err := ioutil.ReadFile(filepath.Join(path, file.Name()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
crd := &apiextensionsv1beta1.CustomResourceDefinition{}
|
||||
if err = yaml.Unmarshal(b, crd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check that it is actually a CRD
|
||||
if crd.Spec.Names.Kind == "" || crd.Spec.Group == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
crds = append(crds, crd)
|
||||
}
|
||||
return crds, nil
|
||||
}
|
18
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/doc.go
generated
vendored
Normal file
18
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/doc.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package envtest provides libraries for integration testing by starting a local control plane
|
||||
package envtest
|
47
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/envtest_suite_test.go
generated
vendored
Normal file
47
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/envtest_suite_test.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
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 (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
)
|
||||
|
||||
func TestSource(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecsWithDefaultAndCustomReporters(t, "EnvTest Suite", []Reporter{NewlineReporter{}})
|
||||
}
|
||||
|
||||
var env *Environment
|
||||
|
||||
var _ = BeforeSuite(func(done Done) {
|
||||
logf.SetLogger(logf.ZapLoggerTo(GinkgoWriter, true))
|
||||
env = &Environment{}
|
||||
_, err := env.Start()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
close(done)
|
||||
}, StartTimeout)
|
||||
|
||||
var _ = AfterSuite(func(done Done) {
|
||||
Expect(env.Stop()).NotTo(HaveOccurred())
|
||||
|
||||
close(done)
|
||||
}, StopTimeout)
|
165
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/envtest_test.go
generated
vendored
Normal file
165
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/envtest_test.go
generated
vendored
Normal file
@ -0,0 +1,165 @@
|
||||
/*
|
||||
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 (
|
||||
"context"
|
||||
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
var _ = Describe("Test", func() {
|
||||
var crds []*v1beta1.CustomResourceDefinition
|
||||
var err error
|
||||
var s *runtime.Scheme
|
||||
var c client.Client
|
||||
|
||||
// Initialize the client
|
||||
BeforeEach(func(done Done) {
|
||||
crds = []*v1beta1.CustomResourceDefinition{}
|
||||
s = runtime.NewScheme()
|
||||
err = v1beta1.AddToScheme(s)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
c, err = client.New(env.Config, client.Options{Scheme: s})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
close(done)
|
||||
})
|
||||
|
||||
// Cleanup CRDs
|
||||
AfterEach(func(done Done) {
|
||||
for _, crd := range crds {
|
||||
c.Delete(context.TODO(), crd)
|
||||
}
|
||||
close(done)
|
||||
})
|
||||
|
||||
Describe("InstallCRDs", func() {
|
||||
It("should install the CRDs into the cluster", func(done Done) {
|
||||
|
||||
crds, err = InstallCRDs(env.Config, CRDInstallOptions{
|
||||
Paths: []string{"."},
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Expect to find the CRDs
|
||||
|
||||
crd := &v1beta1.CustomResourceDefinition{}
|
||||
err = c.Get(context.TODO(), types.NamespacedName{Name: "foos.bar.example.com"}, crd)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(crd.Spec.Names.Kind).To(Equal("Foo"))
|
||||
|
||||
crd = &v1beta1.CustomResourceDefinition{}
|
||||
err = c.Get(context.TODO(), types.NamespacedName{Name: "bazs.qux.example.com"}, crd)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(crd.Spec.Names.Kind).To(Equal("Baz"))
|
||||
|
||||
err = WaitForCRDs(env.Config, []*v1beta1.CustomResourceDefinition{
|
||||
{
|
||||
Spec: v1beta1.CustomResourceDefinitionSpec{
|
||||
Group: "qux.example.com",
|
||||
Version: "v1beta1",
|
||||
Names: v1beta1.CustomResourceDefinitionNames{
|
||||
Plural: "bazs",
|
||||
}},
|
||||
},
|
||||
{
|
||||
Spec: v1beta1.CustomResourceDefinitionSpec{
|
||||
Group: "bar.example.com",
|
||||
Version: "v1beta1",
|
||||
Names: v1beta1.CustomResourceDefinitionNames{
|
||||
Plural: "foos",
|
||||
}},
|
||||
}},
|
||||
CRDInstallOptions{maxTime: 50 * time.Millisecond, pollInterval: 15 * time.Millisecond},
|
||||
)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
close(done)
|
||||
}, 5)
|
||||
|
||||
It("should not return an not error if the directory doesn't exist", func(done Done) {
|
||||
crds, err = InstallCRDs(env.Config, CRDInstallOptions{Paths: []string{"fake"}})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
close(done)
|
||||
}, 5)
|
||||
|
||||
It("should return an error if the directory doesn't exist", func(done Done) {
|
||||
crds, err = InstallCRDs(env.Config, CRDInstallOptions{Paths: []string{"fake"}, ErrorIfPathMissing: true})
|
||||
Expect(err).To(HaveOccurred())
|
||||
|
||||
close(done)
|
||||
}, 5)
|
||||
|
||||
It("should return an error if the resource group version isn't found", func(done Done) {
|
||||
// Wait for a CRD where the Group and Version don't exist
|
||||
err := WaitForCRDs(env.Config,
|
||||
[]*v1beta1.CustomResourceDefinition{
|
||||
{
|
||||
Spec: v1beta1.CustomResourceDefinitionSpec{Names: v1beta1.CustomResourceDefinitionNames{
|
||||
Plural: "notfound",
|
||||
}},
|
||||
},
|
||||
},
|
||||
CRDInstallOptions{maxTime: 50 * time.Millisecond, pollInterval: 15 * time.Millisecond},
|
||||
)
|
||||
Expect(err).To(HaveOccurred())
|
||||
|
||||
close(done)
|
||||
}, 5)
|
||||
|
||||
It("should return an error if the resource isn't found in the group version", func(done Done) {
|
||||
crds, err = InstallCRDs(env.Config, CRDInstallOptions{
|
||||
Paths: []string{"."},
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Wait for a CRD that doesn't exist, but the Group and Version do
|
||||
err = WaitForCRDs(env.Config, []*v1beta1.CustomResourceDefinition{
|
||||
{
|
||||
Spec: v1beta1.CustomResourceDefinitionSpec{
|
||||
Group: "qux.example.com",
|
||||
Version: "v1beta1",
|
||||
Names: v1beta1.CustomResourceDefinitionNames{
|
||||
Plural: "bazs",
|
||||
}},
|
||||
},
|
||||
{
|
||||
Spec: v1beta1.CustomResourceDefinitionSpec{
|
||||
Group: "bar.example.com",
|
||||
Version: "v1beta1",
|
||||
Names: v1beta1.CustomResourceDefinitionNames{
|
||||
Plural: "fake",
|
||||
}},
|
||||
}},
|
||||
CRDInstallOptions{maxTime: 50 * time.Millisecond, pollInterval: 15 * time.Millisecond},
|
||||
)
|
||||
Expect(err).To(HaveOccurred())
|
||||
|
||||
close(done)
|
||||
}, 5)
|
||||
})
|
||||
})
|
11
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/examplecrd1.yaml
generated
vendored
Normal file
11
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/examplecrd1.yaml
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: foos.bar.example.com
|
||||
spec:
|
||||
group: bar.example.com
|
||||
names:
|
||||
kind: Foo
|
||||
plural: foos
|
||||
scope: Namespaced
|
||||
version: "v1beta1"
|
11
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/examplecrd2.yaml
generated
vendored
Normal file
11
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/examplecrd2.yaml
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: bazs.qux.example.com
|
||||
spec:
|
||||
group: qux.example.com
|
||||
names:
|
||||
kind: Baz
|
||||
plural: bazs
|
||||
scope: Namespaced
|
||||
version: "v1beta1"
|
11
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/ginkgo.go
generated
vendored
Normal file
11
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/ginkgo.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
package envtest
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
|
||||
)
|
||||
|
||||
// NewlineReporter is Reporter that Prints a newline after the default Reporter output so that the results
|
||||
// are correctly parsed by test automation.
|
||||
// See issue https://github.com/jstemmer/go-junit-report/issues/31
|
||||
// It's re-exported here to avoid compatibility breakage/mass rewrites.
|
||||
type NewlineReporter = printer.NewlineReporter
|
18
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/notcrd.yaml
generated
vendored
Normal file
18
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/notcrd.yaml
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9
|
51
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/printer/ginkgo.go
generated
vendored
Normal file
51
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/printer/ginkgo.go
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package printer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/onsi/ginkgo"
|
||||
"github.com/onsi/ginkgo/config"
|
||||
"github.com/onsi/ginkgo/types"
|
||||
)
|
||||
|
||||
var _ ginkgo.Reporter = NewlineReporter{}
|
||||
|
||||
// NewlineReporter is Reporter that Prints a newline after the default Reporter output so that the results
|
||||
// are correctly parsed by test automation.
|
||||
// See issue https://github.com/jstemmer/go-junit-report/issues/31
|
||||
type NewlineReporter struct{}
|
||||
|
||||
// SpecSuiteWillBegin implements ginkgo.Reporter
|
||||
func (NewlineReporter) SpecSuiteWillBegin(config config.GinkgoConfigType, summary *types.SuiteSummary) {
|
||||
}
|
||||
|
||||
// BeforeSuiteDidRun implements ginkgo.Reporter
|
||||
func (NewlineReporter) BeforeSuiteDidRun(setupSummary *types.SetupSummary) {}
|
||||
|
||||
// AfterSuiteDidRun implements ginkgo.Reporter
|
||||
func (NewlineReporter) AfterSuiteDidRun(setupSummary *types.SetupSummary) {}
|
||||
|
||||
// SpecWillRun implements ginkgo.Reporter
|
||||
func (NewlineReporter) SpecWillRun(specSummary *types.SpecSummary) {}
|
||||
|
||||
// SpecDidComplete implements ginkgo.Reporter
|
||||
func (NewlineReporter) SpecDidComplete(specSummary *types.SpecSummary) {}
|
||||
|
||||
// SpecSuiteDidEnd Prints a newline between "35 Passed | 0 Failed | 0 Pending | 0 Skipped" and "--- PASS:"
|
||||
func (NewlineReporter) SpecSuiteDidEnd(summary *types.SuiteSummary) { fmt.Printf("\n") }
|
214
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/server.go
generated
vendored
Normal file
214
vendor/sigs.k8s.io/controller-runtime/pkg/envtest/server.go
generated
vendored
Normal file
@ -0,0 +1,214 @@
|
||||
/*
|
||||
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"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
"sigs.k8s.io/testing_frameworks/integration"
|
||||
)
|
||||
|
||||
// Default binary path for test framework
|
||||
const (
|
||||
envKubeAPIServerBin = "TEST_ASSET_KUBE_APISERVER"
|
||||
envEtcdBin = "TEST_ASSET_ETCD"
|
||||
envKubectlBin = "TEST_ASSET_KUBECTL"
|
||||
envKubebuilderPath = "KUBEBUILDER_ASSETS"
|
||||
envStartTimeout = "KUBEBUILDER_CONTROLPLANE_START_TIMEOUT"
|
||||
envStopTimeout = "KUBEBUILDER_CONTROLPLANE_STOP_TIMEOUT"
|
||||
defaultKubebuilderPath = "/usr/local/kubebuilder/bin"
|
||||
StartTimeout = 60
|
||||
StopTimeout = 60
|
||||
|
||||
defaultKubebuilderControlPlaneStartTimeout = 20 * time.Second
|
||||
defaultKubebuilderControlPlaneStopTimeout = 20 * time.Second
|
||||
)
|
||||
|
||||
func defaultAssetPath(binary string) string {
|
||||
assetPath := os.Getenv(envKubebuilderPath)
|
||||
if assetPath == "" {
|
||||
assetPath = defaultKubebuilderPath
|
||||
}
|
||||
return filepath.Join(assetPath, binary)
|
||||
|
||||
}
|
||||
|
||||
// APIServerDefaultArgs are flags necessary to bring up apiserver.
|
||||
// TODO: create test framework interface to append flag to default flags.
|
||||
var defaultKubeAPIServerFlags = []string{
|
||||
"--etcd-servers={{ if .EtcdURL }}{{ .EtcdURL.String }}{{ end }}",
|
||||
"--cert-dir={{ .CertDir }}",
|
||||
"--insecure-port={{ if .URL }}{{ .URL.Port }}{{ end }}",
|
||||
"--insecure-bind-address={{ if .URL }}{{ .URL.Hostname }}{{ end }}",
|
||||
"--secure-port=0",
|
||||
"--admission-control=AlwaysAdmit",
|
||||
}
|
||||
|
||||
// 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 integration.ControlPlane
|
||||
|
||||
// Config can be used to talk to the apiserver
|
||||
Config *rest.Config
|
||||
|
||||
// CRDs is a list of CRDs to install
|
||||
CRDs []*apiextensionsv1beta1.CustomResourceDefinition
|
||||
|
||||
// CRDDirectoryPaths is a list of paths containing CRD yaml or json configs.
|
||||
CRDDirectoryPaths []string
|
||||
|
||||
// UseExisting 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 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 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
|
||||
}
|
||||
|
||||
// Stop stops a running server
|
||||
func (te *Environment) Stop() error {
|
||||
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 {
|
||||
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.
|
||||
|
||||
var err error
|
||||
te.Config, err = config.GetConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
te.ControlPlane = integration.ControlPlane{}
|
||||
te.ControlPlane.APIServer = &integration.APIServer{Args: defaultKubeAPIServerFlags}
|
||||
te.ControlPlane.Etcd = &integration.Etcd{}
|
||||
|
||||
if os.Getenv(envKubeAPIServerBin) == "" {
|
||||
te.ControlPlane.APIServer.Path = defaultAssetPath("kube-apiserver")
|
||||
}
|
||||
if os.Getenv(envEtcdBin) == "" {
|
||||
te.ControlPlane.Etcd.Path = defaultAssetPath("etcd")
|
||||
}
|
||||
if os.Getenv(envKubectlBin) == "" {
|
||||
// we can't just set the path manually (it's behind a function), so set the environment variable instead
|
||||
if err := os.Setenv(envKubectlBin, defaultAssetPath("kubectl")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := te.defaultTimeouts(); err != nil {
|
||||
return nil, fmt.Errorf("failed to default controlplane timeouts: %v", err)
|
||||
}
|
||||
te.ControlPlane.Etcd.StartTimeout = te.ControlPlaneStartTimeout
|
||||
te.ControlPlane.Etcd.StopTimeout = te.ControlPlaneStopTimeout
|
||||
te.ControlPlane.APIServer.StartTimeout = te.ControlPlaneStartTimeout
|
||||
te.ControlPlane.APIServer.StopTimeout = te.ControlPlaneStopTimeout
|
||||
|
||||
if err := te.startControlPlane(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create the *rest.Config for creating new clients
|
||||
te.Config = &rest.Config{
|
||||
Host: te.ControlPlane.APIURL().Host,
|
||||
}
|
||||
}
|
||||
|
||||
_, err := InstallCRDs(te.Config, CRDInstallOptions{
|
||||
Paths: te.CRDDirectoryPaths,
|
||||
CRDs: te.CRDs,
|
||||
})
|
||||
return te.Config, err
|
||||
}
|
||||
|
||||
func (te *Environment) startControlPlane() error {
|
||||
numTries, maxRetries := 0, 5
|
||||
for ; numTries < maxRetries; numTries++ {
|
||||
// Start the control plane - retry if it fails
|
||||
err := te.ControlPlane.Start()
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
// code snippet copied from following answer on stackoverflow
|
||||
// https://stackoverflow.com/questions/51151973/catching-bind-address-already-in-use-in-golang
|
||||
if opErr, ok := err.(*net.OpError); ok {
|
||||
if opErr.Op == "listen" && strings.Contains(opErr.Error(), "address already in use") {
|
||||
if stopErr := te.ControlPlane.Stop(); stopErr != nil {
|
||||
return fmt.Errorf("failed to stop controlplane in response to bind error 'address already in use'")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if numTries == maxRetries {
|
||||
return fmt.Errorf("failed to start the controlplane. retried %d times", numTries)
|
||||
}
|
||||
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
|
||||
}
|
25
vendor/sigs.k8s.io/controller-runtime/pkg/event/doc.go
generated
vendored
Normal file
25
vendor/sigs.k8s.io/controller-runtime/pkg/event/doc.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
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 event contains the definitions for the Event types produced by source.Sources and transformed into
|
||||
reconcile.Requests by handler.EventHandler.
|
||||
|
||||
The details of how events are produced and transformed into reconcile.Requests are not something most
|
||||
users should need to use or understand. Instead of working with Events, users should use
|
||||
source.Sources and handler.EventHandlers with Controller.Watch.
|
||||
*/
|
||||
package event
|
73
vendor/sigs.k8s.io/controller-runtime/pkg/event/event.go
generated
vendored
Normal file
73
vendor/sigs.k8s.io/controller-runtime/pkg/event/event.go
generated
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
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 event
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// CreateEvent is an event where a Kubernetes object was created. CreateEvent should be generated
|
||||
// by a source.Source and transformed into a reconcile.Request by an handler.EventHandler.
|
||||
type CreateEvent struct {
|
||||
// Meta is the ObjectMeta of the Kubernetes Type that was created
|
||||
Meta v1.Object
|
||||
|
||||
// Object is the object from the event
|
||||
Object runtime.Object
|
||||
}
|
||||
|
||||
// UpdateEvent is an event where a Kubernetes object was updated. UpdateEvent should be generated
|
||||
// by a source.Source and transformed into a reconcile.Request by an handler.EventHandler.
|
||||
type UpdateEvent struct {
|
||||
// MetaOld is the ObjectMeta of the Kubernetes Type that was updated (before the update)
|
||||
MetaOld v1.Object
|
||||
|
||||
// ObjectOld is the object from the event
|
||||
ObjectOld runtime.Object
|
||||
|
||||
// MetaNew is the ObjectMeta of the Kubernetes Type that was updated (after the update)
|
||||
MetaNew v1.Object
|
||||
|
||||
// ObjectNew is the object from the event
|
||||
ObjectNew runtime.Object
|
||||
}
|
||||
|
||||
// DeleteEvent is an event where a Kubernetes object was deleted. DeleteEvent should be generated
|
||||
// by a source.Source and transformed into a reconcile.Request by an handler.EventHandler.
|
||||
type DeleteEvent struct {
|
||||
// Meta is the ObjectMeta of the Kubernetes Type that was deleted
|
||||
Meta v1.Object
|
||||
|
||||
// Object is the object from the event
|
||||
Object runtime.Object
|
||||
|
||||
// DeleteStateUnknown is true if the Delete event was missed but we identified the object
|
||||
// as having been deleted.
|
||||
DeleteStateUnknown bool
|
||||
}
|
||||
|
||||
// GenericEvent is an event where the operation type is unknown (e.g. polling or event originating outside the cluster).
|
||||
// GenericEvent should be generated by a source.Source and transformed into a reconcile.Request by an
|
||||
// handler.EventHandler.
|
||||
type GenericEvent struct {
|
||||
// Meta is the ObjectMeta of a Kubernetes Type this event is for
|
||||
Meta v1.Object
|
||||
|
||||
// Object is the object from the event
|
||||
Object runtime.Object
|
||||
}
|
36
vendor/sigs.k8s.io/controller-runtime/pkg/handler/doc.go
generated
vendored
Normal file
36
vendor/sigs.k8s.io/controller-runtime/pkg/handler/doc.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
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 handler defines EventHandlers that enqueue reconcile.Requests in response to Create, Update, Deletion Events
|
||||
observed from Watching Kubernetes APIs. Users should provide a source.Source and handler.EventHandler to
|
||||
Controller.Watch in order to generate and enqueue reconcile.Request work items.
|
||||
|
||||
EventHandlers
|
||||
|
||||
EnqueueRequestForObject - Enqueues a reconcile.Request containing the Name and Namespace of the object in the Event. This will
|
||||
cause the object that was the source of the Event (e.g. the created / deleted / updated object) to be
|
||||
reconciled.
|
||||
|
||||
EnqueueRequestForOwner - Enqueues a reconcile.Request containing the Name and Namespace of the Owner of the object in the Event.
|
||||
This will cause owner of the object that was the source of the Event (e.g. the owner object that created the object)
|
||||
to be reconciled.
|
||||
|
||||
EnqueueRequestsFromMapFunc - Enqueues Reconciler.Requests resulting from a user provided transformation function run against the
|
||||
object in the Event. This will cause an arbitrary collection of objects (defined from a transformation of the
|
||||
source object) to be reconciled.
|
||||
*/
|
||||
package handler
|
91
vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue.go
generated
vendored
Normal file
91
vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue.go
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
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 handler
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
)
|
||||
|
||||
var enqueueLog = logf.KBLog.WithName("eventhandler").WithName("EnqueueRequestForObject")
|
||||
|
||||
var _ EventHandler = &EnqueueRequestForObject{}
|
||||
|
||||
// EnqueueRequestForObject enqueues a Request containing the Name and Namespace of the object that is the source of the Event.
|
||||
// (e.g. the created / deleted / updated objects Name and Namespace). handler.EnqueueRequestForObject is used by almost all
|
||||
// Controllers that have associated Resources (e.g. CRDs) to reconcile the associated Resource.
|
||||
type EnqueueRequestForObject struct{}
|
||||
|
||||
// Create implements EventHandler
|
||||
func (e *EnqueueRequestForObject) Create(evt event.CreateEvent, q workqueue.RateLimitingInterface) {
|
||||
if evt.Meta == nil {
|
||||
enqueueLog.Error(nil, "CreateEvent received with no metadata", "CreateEvent", evt)
|
||||
return
|
||||
}
|
||||
q.Add(reconcile.Request{NamespacedName: types.NamespacedName{
|
||||
Name: evt.Meta.GetName(),
|
||||
Namespace: evt.Meta.GetNamespace(),
|
||||
}})
|
||||
}
|
||||
|
||||
// Update implements EventHandler
|
||||
func (e *EnqueueRequestForObject) Update(evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
|
||||
if evt.MetaOld != nil {
|
||||
q.Add(reconcile.Request{NamespacedName: types.NamespacedName{
|
||||
Name: evt.MetaOld.GetName(),
|
||||
Namespace: evt.MetaOld.GetNamespace(),
|
||||
}})
|
||||
} else {
|
||||
enqueueLog.Error(nil, "UpdateEvent received with no old metadata", "UpdateEvent", evt)
|
||||
}
|
||||
|
||||
if evt.MetaNew != nil {
|
||||
q.Add(reconcile.Request{NamespacedName: types.NamespacedName{
|
||||
Name: evt.MetaNew.GetName(),
|
||||
Namespace: evt.MetaNew.GetNamespace(),
|
||||
}})
|
||||
} else {
|
||||
enqueueLog.Error(nil, "UpdateEvent received with no new metadata", "UpdateEvent", evt)
|
||||
}
|
||||
}
|
||||
|
||||
// Delete implements EventHandler
|
||||
func (e *EnqueueRequestForObject) Delete(evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
|
||||
if evt.Meta == nil {
|
||||
enqueueLog.Error(nil, "DeleteEvent received with no metadata", "DeleteEvent", evt)
|
||||
return
|
||||
}
|
||||
q.Add(reconcile.Request{NamespacedName: types.NamespacedName{
|
||||
Name: evt.Meta.GetName(),
|
||||
Namespace: evt.Meta.GetNamespace(),
|
||||
}})
|
||||
}
|
||||
|
||||
// Generic implements EventHandler
|
||||
func (e *EnqueueRequestForObject) Generic(evt event.GenericEvent, q workqueue.RateLimitingInterface) {
|
||||
if evt.Meta == nil {
|
||||
enqueueLog.Error(nil, "GenericEvent received with no metadata", "GenericEvent", evt)
|
||||
return
|
||||
}
|
||||
q.Add(reconcile.Request{NamespacedName: types.NamespacedName{
|
||||
Name: evt.Meta.GetName(),
|
||||
Namespace: evt.Meta.GetNamespace(),
|
||||
}})
|
||||
}
|
94
vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_mapped.go
generated
vendored
Normal file
94
vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_mapped.go
generated
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
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 handler
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
var _ EventHandler = &EnqueueRequestsFromMapFunc{}
|
||||
|
||||
// EnqueueRequestsFromMapFunc enqueues Requests by running a transformation function that outputs a collection
|
||||
// of reconcile.Requests on each Event. The reconcile.Requests may be for an arbitrary set of objects
|
||||
// defined by some user specified transformation of the source Event. (e.g. trigger Reconciler for a set of objects
|
||||
// in response to a cluster resize event caused by adding or deleting a Node)
|
||||
//
|
||||
// EnqueueRequestsFromMapFunc is frequently used to fan-out updates from one object to one or more other
|
||||
// objects of a differing type.
|
||||
//
|
||||
// For UpdateEvents which contain both a new and old object, the transformation function is run on both
|
||||
// objects and both sets of Requests are enqueue.
|
||||
type EnqueueRequestsFromMapFunc struct {
|
||||
// Mapper transforms the argument into a slice of keys to be reconciled
|
||||
ToRequests Mapper
|
||||
}
|
||||
|
||||
// Create implements EventHandler
|
||||
func (e *EnqueueRequestsFromMapFunc) Create(evt event.CreateEvent, q workqueue.RateLimitingInterface) {
|
||||
e.mapAndEnqueue(q, MapObject{Meta: evt.Meta, Object: evt.Object})
|
||||
}
|
||||
|
||||
// Update implements EventHandler
|
||||
func (e *EnqueueRequestsFromMapFunc) Update(evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
|
||||
e.mapAndEnqueue(q, MapObject{Meta: evt.MetaOld, Object: evt.ObjectOld})
|
||||
e.mapAndEnqueue(q, MapObject{Meta: evt.MetaNew, Object: evt.ObjectNew})
|
||||
}
|
||||
|
||||
// Delete implements EventHandler
|
||||
func (e *EnqueueRequestsFromMapFunc) Delete(evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
|
||||
e.mapAndEnqueue(q, MapObject{Meta: evt.Meta, Object: evt.Object})
|
||||
}
|
||||
|
||||
// Generic implements EventHandler
|
||||
func (e *EnqueueRequestsFromMapFunc) Generic(evt event.GenericEvent, q workqueue.RateLimitingInterface) {
|
||||
e.mapAndEnqueue(q, MapObject{Meta: evt.Meta, Object: evt.Object})
|
||||
}
|
||||
|
||||
func (e *EnqueueRequestsFromMapFunc) mapAndEnqueue(q workqueue.RateLimitingInterface, object MapObject) {
|
||||
for _, req := range e.ToRequests.Map(object) {
|
||||
q.Add(req)
|
||||
}
|
||||
}
|
||||
|
||||
// Mapper maps an object to a collection of keys to be enqueued
|
||||
type Mapper interface {
|
||||
// Map maps an object
|
||||
Map(MapObject) []reconcile.Request
|
||||
}
|
||||
|
||||
// MapObject contains information from an event to be transformed into a Request.
|
||||
type MapObject struct {
|
||||
// Meta is the meta data for an object from an event.
|
||||
Meta metav1.Object
|
||||
|
||||
// Object is the object from an event.
|
||||
Object runtime.Object
|
||||
}
|
||||
|
||||
var _ Mapper = ToRequestsFunc(nil)
|
||||
|
||||
// ToRequestsFunc implements Mapper using a function.
|
||||
type ToRequestsFunc func(MapObject) []reconcile.Request
|
||||
|
||||
// Map implements Mapper
|
||||
func (m ToRequestsFunc) Map(i MapObject) []reconcile.Request {
|
||||
return m(i)
|
||||
}
|
165
vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_owner.go
generated
vendored
Normal file
165
vendor/sigs.k8s.io/controller-runtime/pkg/handler/enqueue_owner.go
generated
vendored
Normal file
@ -0,0 +1,165 @@
|
||||
/*
|
||||
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 handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
"sigs.k8s.io/controller-runtime/pkg/runtime/inject"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
)
|
||||
|
||||
var _ EventHandler = &EnqueueRequestForOwner{}
|
||||
|
||||
var log = logf.KBLog.WithName("eventhandler").WithName("EnqueueRequestForOwner")
|
||||
|
||||
// EnqueueRequestForOwner enqueues Requests for the Owners of an object. E.g. the object that created
|
||||
// the object that was the source of the Event.
|
||||
//
|
||||
// If a ReplicaSet creates Pods, users may reconcile the ReplicaSet in response to Pod Events using:
|
||||
//
|
||||
// - a source.Kind Source with Type of Pod.
|
||||
//
|
||||
// - a handler.EnqueueRequestForOwner EventHandler with an OwnerType of ReplicaSet and IsController set to true.
|
||||
type EnqueueRequestForOwner struct {
|
||||
// OwnerType is the type of the Owner object to look for in OwnerReferences. Only Group and Kind are compared.
|
||||
OwnerType runtime.Object
|
||||
|
||||
// IsController if set will only look at the first OwnerReference with Controller: true.
|
||||
IsController bool
|
||||
|
||||
// groupKind is the cached Group and Kind from OwnerType
|
||||
groupKind schema.GroupKind
|
||||
}
|
||||
|
||||
// Create implements EventHandler
|
||||
func (e *EnqueueRequestForOwner) Create(evt event.CreateEvent, q workqueue.RateLimitingInterface) {
|
||||
for _, req := range e.getOwnerReconcileRequest(evt.Meta) {
|
||||
q.Add(req)
|
||||
}
|
||||
}
|
||||
|
||||
// Update implements EventHandler
|
||||
func (e *EnqueueRequestForOwner) Update(evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
|
||||
for _, req := range e.getOwnerReconcileRequest(evt.MetaOld) {
|
||||
q.Add(req)
|
||||
}
|
||||
for _, req := range e.getOwnerReconcileRequest(evt.MetaNew) {
|
||||
q.Add(req)
|
||||
}
|
||||
}
|
||||
|
||||
// Delete implements EventHandler
|
||||
func (e *EnqueueRequestForOwner) Delete(evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
|
||||
for _, req := range e.getOwnerReconcileRequest(evt.Meta) {
|
||||
q.Add(req)
|
||||
}
|
||||
}
|
||||
|
||||
// Generic implements EventHandler
|
||||
func (e *EnqueueRequestForOwner) Generic(evt event.GenericEvent, q workqueue.RateLimitingInterface) {
|
||||
for _, req := range e.getOwnerReconcileRequest(evt.Meta) {
|
||||
q.Add(req)
|
||||
}
|
||||
}
|
||||
|
||||
// parseOwnerTypeGroupKind parses the OwnerType into a Group and Kind and caches the result. Returns false
|
||||
// if the OwnerType could not be parsed using the scheme.
|
||||
func (e *EnqueueRequestForOwner) parseOwnerTypeGroupKind(scheme *runtime.Scheme) error {
|
||||
// Get the kinds of the type
|
||||
kinds, _, err := scheme.ObjectKinds(e.OwnerType)
|
||||
if err != nil {
|
||||
log.Error(err, "Could not get ObjectKinds for OwnerType", "OwnerType", e.OwnerType)
|
||||
return err
|
||||
}
|
||||
// Expect only 1 kind. If there is more than one kind this is probably an edge case such as ListOptions.
|
||||
if len(kinds) != 1 {
|
||||
err := fmt.Errorf("Expected exactly 1 kind for OwnerType %T, but found %s kinds", e.OwnerType, kinds)
|
||||
log.Error(err, "", "OwnerType", e.OwnerType, "Kinds", kinds)
|
||||
return err
|
||||
|
||||
}
|
||||
// Cache the Group and Kind for the OwnerType
|
||||
e.groupKind = schema.GroupKind{Group: kinds[0].Group, Kind: kinds[0].Kind}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getOwnerReconcileRequest looks at object and returns a slice of reconcile.Request to reconcile
|
||||
// owners of object that match e.OwnerType.
|
||||
func (e *EnqueueRequestForOwner) getOwnerReconcileRequest(object metav1.Object) []reconcile.Request {
|
||||
// Iterate through the OwnerReferences looking for a match on Group and Kind against what was requested
|
||||
// by the user
|
||||
var result []reconcile.Request
|
||||
for _, ref := range e.getOwnersReferences(object) {
|
||||
// Parse the Group out of the OwnerReference to compare it to what was parsed out of the requested OwnerType
|
||||
refGV, err := schema.ParseGroupVersion(ref.APIVersion)
|
||||
if err != nil {
|
||||
log.Error(err, "Could not parse OwnerReference GroupVersion",
|
||||
"OwnerReference", ref.APIVersion)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Compare the OwnerReference Group and Kind against the OwnerType Group and Kind specified by the user.
|
||||
// If the two match, create a Request for the objected referred to by
|
||||
// the OwnerReference. Use the Name from the OwnerReference and the Namespace from the
|
||||
// object in the event.
|
||||
if ref.Kind == e.groupKind.Kind && refGV.Group == e.groupKind.Group {
|
||||
// Match found - add a Request for the object referred to in the OwnerReference
|
||||
result = append(result, reconcile.Request{NamespacedName: types.NamespacedName{
|
||||
Namespace: object.GetNamespace(),
|
||||
Name: ref.Name,
|
||||
}})
|
||||
}
|
||||
}
|
||||
|
||||
// Return the matches
|
||||
return result
|
||||
}
|
||||
|
||||
// getOwnersReferences returns the OwnerReferences for an object as specified by the EnqueueRequestForOwner
|
||||
// - if IsController is true: only take the Controller OwnerReference (if found)
|
||||
// - if IsController is false: take all OwnerReferences
|
||||
func (e *EnqueueRequestForOwner) getOwnersReferences(object metav1.Object) []metav1.OwnerReference {
|
||||
if object == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If not filtered as Controller only, then use all the OwnerReferences
|
||||
if !e.IsController {
|
||||
return object.GetOwnerReferences()
|
||||
}
|
||||
// If filtered to a Controller, only take the Controller OwnerReference
|
||||
if ownerRef := metav1.GetControllerOf(object); ownerRef != nil {
|
||||
return []metav1.OwnerReference{*ownerRef}
|
||||
}
|
||||
// No Controller OwnerReference found
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ inject.Scheme = &EnqueueRequestForOwner{}
|
||||
|
||||
// InjectScheme is called by the Controller to provide a singleton scheme to the EnqueueRequestForOwner.
|
||||
func (e *EnqueueRequestForOwner) InjectScheme(s *runtime.Scheme) error {
|
||||
return e.parseOwnerTypeGroupKind(s)
|
||||
}
|
104
vendor/sigs.k8s.io/controller-runtime/pkg/handler/eventhandler.go
generated
vendored
Normal file
104
vendor/sigs.k8s.io/controller-runtime/pkg/handler/eventhandler.go
generated
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
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 handler
|
||||
|
||||
import (
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
)
|
||||
|
||||
// EventHandler enqueues reconcile.Requests in response to events (e.g. Pod Create). EventHandlers map an Event
|
||||
// for one object to trigger Reconciles for either the same object or different objects - e.g. if there is an
|
||||
// Event for object with type Foo (using source.KindSource) then reconcile one or more object(s) with type Bar.
|
||||
//
|
||||
// Identical reconcile.Requests will be batched together through the queuing mechanism before reconcile is called.
|
||||
//
|
||||
// * Use EnqueueRequestForObject to reconcile the object the event is for
|
||||
// - do this for events for the type the Controller Reconciles. (e.g. Deployment for a Deployment Controller)
|
||||
//
|
||||
// * Use EnqueueRequestForOwner to reconcile the owner of the object the event is for
|
||||
// - do this for events for the types the Controller creates. (e.g. ReplicaSets created by a Deployment Controller)
|
||||
//
|
||||
// * Use EnqueueRequestFromMapFunc to transform an event for an object to a reconcile of an object
|
||||
// of a different type - do this for events for types the Controller may be interested in, but doesn't create.
|
||||
// (e.g. If Foo responds to cluster size events, map Node events to Foo objects.)
|
||||
//
|
||||
// Unless you are implementing your own EventHandler, you can ignore the functions on the EventHandler interface.
|
||||
// Most users shouldn't need to implement their own EventHandler.
|
||||
type EventHandler interface {
|
||||
// Create is called in response to an create event - e.g. Pod Creation.
|
||||
Create(event.CreateEvent, workqueue.RateLimitingInterface)
|
||||
|
||||
// Update is called in response to an update event - e.g. Pod Updated.
|
||||
Update(event.UpdateEvent, workqueue.RateLimitingInterface)
|
||||
|
||||
// Delete is called in response to a delete event - e.g. Pod Deleted.
|
||||
Delete(event.DeleteEvent, workqueue.RateLimitingInterface)
|
||||
|
||||
// Generic is called in response to an event of an unknown type or a synthetic event triggered as a cron or
|
||||
// external trigger request - e.g. reconcile Autoscaling, or a Webhook.
|
||||
Generic(event.GenericEvent, workqueue.RateLimitingInterface)
|
||||
}
|
||||
|
||||
var _ EventHandler = Funcs{}
|
||||
|
||||
// Funcs implements EventHandler.
|
||||
type Funcs struct {
|
||||
// Create is called in response to an add event. Defaults to no-op.
|
||||
// RateLimitingInterface is used to enqueue reconcile.Requests.
|
||||
CreateFunc func(event.CreateEvent, workqueue.RateLimitingInterface)
|
||||
|
||||
// Update is called in response to an update event. Defaults to no-op.
|
||||
// RateLimitingInterface is used to enqueue reconcile.Requests.
|
||||
UpdateFunc func(event.UpdateEvent, workqueue.RateLimitingInterface)
|
||||
|
||||
// Delete is called in response to a delete event. Defaults to no-op.
|
||||
// RateLimitingInterface is used to enqueue reconcile.Requests.
|
||||
DeleteFunc func(event.DeleteEvent, workqueue.RateLimitingInterface)
|
||||
|
||||
// GenericFunc is called in response to a generic event. Defaults to no-op.
|
||||
// RateLimitingInterface is used to enqueue reconcile.Requests.
|
||||
GenericFunc func(event.GenericEvent, workqueue.RateLimitingInterface)
|
||||
}
|
||||
|
||||
// Create implements EventHandler
|
||||
func (h Funcs) Create(e event.CreateEvent, q workqueue.RateLimitingInterface) {
|
||||
if h.CreateFunc != nil {
|
||||
h.CreateFunc(e, q)
|
||||
}
|
||||
}
|
||||
|
||||
// Delete implements EventHandler
|
||||
func (h Funcs) Delete(e event.DeleteEvent, q workqueue.RateLimitingInterface) {
|
||||
if h.DeleteFunc != nil {
|
||||
h.DeleteFunc(e, q)
|
||||
}
|
||||
}
|
||||
|
||||
// Update implements EventHandler
|
||||
func (h Funcs) Update(e event.UpdateEvent, q workqueue.RateLimitingInterface) {
|
||||
if h.UpdateFunc != nil {
|
||||
h.UpdateFunc(e, q)
|
||||
}
|
||||
}
|
||||
|
||||
// Generic implements EventHandler
|
||||
func (h Funcs) Generic(e event.GenericEvent, q workqueue.RateLimitingInterface) {
|
||||
if h.GenericFunc != nil {
|
||||
h.GenericFunc(e, q)
|
||||
}
|
||||
}
|
35
vendor/sigs.k8s.io/controller-runtime/pkg/handler/eventhandler_suite_test.go
generated
vendored
Normal file
35
vendor/sigs.k8s.io/controller-runtime/pkg/handler/eventhandler_suite_test.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
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 handler_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
)
|
||||
|
||||
func TestEventhandler(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecsWithDefaultAndCustomReporters(t, "Eventhandler Suite", []Reporter{envtest.NewlineReporter{}})
|
||||
}
|
||||
|
||||
var _ = BeforeSuite(func() {
|
||||
logf.SetLogger(logf.ZapLoggerTo(GinkgoWriter, true))
|
||||
})
|
903
vendor/sigs.k8s.io/controller-runtime/pkg/handler/eventhandler_test.go
generated
vendored
Normal file
903
vendor/sigs.k8s.io/controller-runtime/pkg/handler/eventhandler_test.go
generated
vendored
Normal file
@ -0,0 +1,903 @@
|
||||
/*
|
||||
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 handler_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllertest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
var _ = Describe("Eventhandler", func() {
|
||||
var q workqueue.RateLimitingInterface
|
||||
var instance handler.EnqueueRequestForObject
|
||||
var pod *corev1.Pod
|
||||
t := true
|
||||
BeforeEach(func() {
|
||||
q = controllertest.Queue{Interface: workqueue.New()}
|
||||
instance = handler.EnqueueRequestForObject{}
|
||||
pod = &corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "biz", Name: "baz"},
|
||||
}
|
||||
})
|
||||
|
||||
Describe("EnqueueRequestForObject", func() {
|
||||
It("should enqueue a Request with the Name / Namespace of the object in the CreateEvent.", func(done Done) {
|
||||
evt := event.CreateEvent{
|
||||
Object: pod,
|
||||
Meta: pod.GetObjectMeta(),
|
||||
}
|
||||
instance.Create(evt, q)
|
||||
Expect(q.Len()).To(Equal(1))
|
||||
|
||||
i, _ := q.Get()
|
||||
Expect(i).NotTo(BeNil())
|
||||
req, ok := i.(reconcile.Request)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(req.NamespacedName).To(Equal(types.NamespacedName{Namespace: "biz", Name: "baz"}))
|
||||
|
||||
close(done)
|
||||
})
|
||||
|
||||
It("should enqueue a Request with the Name / Namespace of the object in the DeleteEvent.", func(done Done) {
|
||||
evt := event.DeleteEvent{
|
||||
Object: pod,
|
||||
Meta: pod.GetObjectMeta(),
|
||||
}
|
||||
instance.Delete(evt, q)
|
||||
Expect(q.Len()).To(Equal(1))
|
||||
|
||||
i, _ := q.Get()
|
||||
Expect(i).NotTo(BeNil())
|
||||
req, ok := i.(reconcile.Request)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(req.NamespacedName).To(Equal(types.NamespacedName{Namespace: "biz", Name: "baz"}))
|
||||
|
||||
close(done)
|
||||
})
|
||||
|
||||
It("should enqueue a Request with the Name / Namespace of both objects in the UpdateEvent.",
|
||||
func(done Done) {
|
||||
newPod := pod.DeepCopy()
|
||||
newPod.Name = "baz2"
|
||||
newPod.Namespace = "biz2"
|
||||
|
||||
evt := event.UpdateEvent{
|
||||
ObjectOld: pod,
|
||||
MetaOld: pod.GetObjectMeta(),
|
||||
ObjectNew: newPod,
|
||||
MetaNew: newPod.GetObjectMeta(),
|
||||
}
|
||||
instance.Update(evt, q)
|
||||
Expect(q.Len()).To(Equal(2))
|
||||
|
||||
i, _ := q.Get()
|
||||
Expect(i).NotTo(BeNil())
|
||||
req, ok := i.(reconcile.Request)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(req.NamespacedName).To(Equal(types.NamespacedName{Namespace: "biz", Name: "baz"}))
|
||||
|
||||
i, _ = q.Get()
|
||||
Expect(i).NotTo(BeNil())
|
||||
req, ok = i.(reconcile.Request)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(req.NamespacedName).To(Equal(types.NamespacedName{Namespace: "biz2", Name: "baz2"}))
|
||||
|
||||
close(done)
|
||||
})
|
||||
|
||||
It("should enqueue a Request with the Name / Namespace of the object in the GenericEvent.", func(done Done) {
|
||||
evt := event.GenericEvent{
|
||||
Object: pod,
|
||||
Meta: pod.GetObjectMeta(),
|
||||
}
|
||||
instance.Generic(evt, q)
|
||||
Expect(q.Len()).To(Equal(1))
|
||||
i, _ := q.Get()
|
||||
Expect(i).NotTo(BeNil())
|
||||
req, ok := i.(reconcile.Request)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(req.NamespacedName).To(Equal(types.NamespacedName{Namespace: "biz", Name: "baz"}))
|
||||
|
||||
close(done)
|
||||
})
|
||||
|
||||
Context("for a runtime.Object without Metadata", func() {
|
||||
It("should do nothing if the Metadata is missing for a CreateEvent.", func(done Done) {
|
||||
evt := event.CreateEvent{
|
||||
Object: pod,
|
||||
}
|
||||
instance.Create(evt, q)
|
||||
Expect(q.Len()).To(Equal(0))
|
||||
close(done)
|
||||
})
|
||||
|
||||
It("should do nothing if the Metadata is missing for a UpdateEvent.", func(done Done) {
|
||||
newPod := pod.DeepCopy()
|
||||
newPod.Name = "baz2"
|
||||
newPod.Namespace = "biz2"
|
||||
|
||||
evt := event.UpdateEvent{
|
||||
ObjectNew: newPod,
|
||||
MetaNew: newPod.GetObjectMeta(),
|
||||
ObjectOld: pod,
|
||||
}
|
||||
instance.Update(evt, q)
|
||||
Expect(q.Len()).To(Equal(1))
|
||||
i, _ := q.Get()
|
||||
Expect(i).NotTo(BeNil())
|
||||
req, ok := i.(reconcile.Request)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(req.NamespacedName).To(Equal(types.NamespacedName{Namespace: "biz2", Name: "baz2"}))
|
||||
|
||||
evt.MetaNew = nil
|
||||
evt.MetaOld = pod.GetObjectMeta()
|
||||
instance.Update(evt, q)
|
||||
Expect(q.Len()).To(Equal(1))
|
||||
i, _ = q.Get()
|
||||
Expect(i).NotTo(BeNil())
|
||||
req, ok = i.(reconcile.Request)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(req.NamespacedName).To(Equal(types.NamespacedName{Namespace: "biz", Name: "baz"}))
|
||||
|
||||
close(done)
|
||||
})
|
||||
|
||||
It("should do nothing if the Metadata is missing for a DeleteEvent.", func(done Done) {
|
||||
evt := event.DeleteEvent{
|
||||
Object: pod,
|
||||
}
|
||||
instance.Delete(evt, q)
|
||||
Expect(q.Len()).To(Equal(0))
|
||||
close(done)
|
||||
})
|
||||
|
||||
It("should do nothing if the Metadata is missing for a GenericEvent.", func(done Done) {
|
||||
evt := event.GenericEvent{
|
||||
Object: pod,
|
||||
}
|
||||
instance.Generic(evt, q)
|
||||
Expect(q.Len()).To(Equal(0))
|
||||
close(done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("EnqueueRequestsFromMapFunc", func() {
|
||||
It("should enqueue a Request with the function applied to the CreateEvent.", func() {
|
||||
req := []reconcile.Request{}
|
||||
instance := handler.EnqueueRequestsFromMapFunc{
|
||||
ToRequests: handler.ToRequestsFunc(func(a handler.MapObject) []reconcile.Request {
|
||||
defer GinkgoRecover()
|
||||
Expect(a.Meta).To(Equal(pod.GetObjectMeta()))
|
||||
Expect(a.Object).To(Equal(pod))
|
||||
req = []reconcile.Request{
|
||||
{
|
||||
NamespacedName: types.NamespacedName{Namespace: "foo", Name: "bar"},
|
||||
},
|
||||
{
|
||||
NamespacedName: types.NamespacedName{Namespace: "biz", Name: "baz"},
|
||||
},
|
||||
}
|
||||
return req
|
||||
}),
|
||||
}
|
||||
|
||||
evt := event.CreateEvent{
|
||||
Object: pod,
|
||||
Meta: pod.GetObjectMeta(),
|
||||
}
|
||||
instance.Create(evt, q)
|
||||
Expect(q.Len()).To(Equal(2))
|
||||
|
||||
i, _ := q.Get()
|
||||
Expect(i).To(Equal(reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{Namespace: "foo", Name: "bar"}}))
|
||||
|
||||
i, _ = q.Get()
|
||||
Expect(i).To(Equal(reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{Namespace: "biz", Name: "baz"}}))
|
||||
})
|
||||
|
||||
It("should enqueue a Request with the function applied to the DeleteEvent.", func() {
|
||||
req := []reconcile.Request{}
|
||||
instance := handler.EnqueueRequestsFromMapFunc{
|
||||
ToRequests: handler.ToRequestsFunc(func(a handler.MapObject) []reconcile.Request {
|
||||
defer GinkgoRecover()
|
||||
Expect(a.Meta).To(Equal(pod.GetObjectMeta()))
|
||||
Expect(a.Object).To(Equal(pod))
|
||||
req = []reconcile.Request{
|
||||
{
|
||||
NamespacedName: types.NamespacedName{Namespace: "foo", Name: "bar"},
|
||||
},
|
||||
{
|
||||
NamespacedName: types.NamespacedName{Namespace: "biz", Name: "baz"},
|
||||
},
|
||||
}
|
||||
return req
|
||||
}),
|
||||
}
|
||||
|
||||
evt := event.DeleteEvent{
|
||||
Object: pod,
|
||||
Meta: pod.GetObjectMeta(),
|
||||
}
|
||||
instance.Delete(evt, q)
|
||||
Expect(q.Len()).To(Equal(2))
|
||||
|
||||
i, _ := q.Get()
|
||||
Expect(i).To(Equal(reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{Namespace: "foo", Name: "bar"}}))
|
||||
|
||||
i, _ = q.Get()
|
||||
Expect(i).To(Equal(reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{Namespace: "biz", Name: "baz"}}))
|
||||
})
|
||||
|
||||
It("should enqueue a Request with the function applied to both objects in the UpdateEvent.",
|
||||
func() {
|
||||
newPod := pod.DeepCopy()
|
||||
newPod.Name = pod.Name + "2"
|
||||
newPod.Namespace = pod.Namespace + "2"
|
||||
|
||||
req := []reconcile.Request{}
|
||||
instance := handler.EnqueueRequestsFromMapFunc{
|
||||
ToRequests: handler.ToRequestsFunc(func(a handler.MapObject) []reconcile.Request {
|
||||
defer GinkgoRecover()
|
||||
req = []reconcile.Request{
|
||||
{
|
||||
NamespacedName: types.NamespacedName{Namespace: "foo", Name: a.Meta.GetName() + "-bar"},
|
||||
},
|
||||
{
|
||||
NamespacedName: types.NamespacedName{Namespace: "biz", Name: a.Meta.GetName() + "-baz"},
|
||||
},
|
||||
}
|
||||
return req
|
||||
}),
|
||||
}
|
||||
|
||||
evt := event.UpdateEvent{
|
||||
ObjectOld: pod,
|
||||
MetaOld: pod.GetObjectMeta(),
|
||||
ObjectNew: newPod,
|
||||
MetaNew: newPod.GetObjectMeta(),
|
||||
}
|
||||
instance.Update(evt, q)
|
||||
Expect(q.Len()).To(Equal(4))
|
||||
|
||||
i, _ := q.Get()
|
||||
Expect(i).To(Equal(reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{Namespace: "foo", Name: "baz-bar"}}))
|
||||
|
||||
i, _ = q.Get()
|
||||
Expect(i).To(Equal(reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{Namespace: "biz", Name: "baz-baz"}}))
|
||||
|
||||
i, _ = q.Get()
|
||||
Expect(i).To(Equal(reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{Namespace: "foo", Name: "baz2-bar"}}))
|
||||
|
||||
i, _ = q.Get()
|
||||
Expect(i).To(Equal(reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{Namespace: "biz", Name: "baz2-baz"}}))
|
||||
})
|
||||
|
||||
It("should enqueue a Request with the function applied to the GenericEvent.", func() {
|
||||
req := []reconcile.Request{}
|
||||
instance := handler.EnqueueRequestsFromMapFunc{
|
||||
ToRequests: handler.ToRequestsFunc(func(a handler.MapObject) []reconcile.Request {
|
||||
defer GinkgoRecover()
|
||||
Expect(a.Meta).To(Equal(pod.GetObjectMeta()))
|
||||
Expect(a.Object).To(Equal(pod))
|
||||
req = []reconcile.Request{
|
||||
{
|
||||
NamespacedName: types.NamespacedName{Namespace: "foo", Name: "bar"},
|
||||
},
|
||||
{
|
||||
NamespacedName: types.NamespacedName{Namespace: "biz", Name: "baz"},
|
||||
},
|
||||
}
|
||||
return req
|
||||
}),
|
||||
}
|
||||
|
||||
evt := event.GenericEvent{
|
||||
Object: pod,
|
||||
Meta: pod.GetObjectMeta(),
|
||||
}
|
||||
instance.Generic(evt, q)
|
||||
Expect(q.Len()).To(Equal(2))
|
||||
|
||||
i, _ := q.Get()
|
||||
Expect(i).To(Equal(reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{Namespace: "foo", Name: "bar"}}))
|
||||
|
||||
i, _ = q.Get()
|
||||
Expect(i).To(Equal(reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{Namespace: "biz", Name: "baz"}}))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("EnqueueRequestForOwner", func() {
|
||||
It("should enqueue a Request with the Owner of the object in the CreateEvent.", func() {
|
||||
instance := handler.EnqueueRequestForOwner{
|
||||
OwnerType: &appsv1.ReplicaSet{},
|
||||
}
|
||||
instance.InjectScheme(scheme.Scheme)
|
||||
|
||||
pod.OwnerReferences = []metav1.OwnerReference{
|
||||
{
|
||||
Name: "foo-parent",
|
||||
Kind: "ReplicaSet",
|
||||
APIVersion: "apps/v1",
|
||||
},
|
||||
}
|
||||
evt := event.CreateEvent{
|
||||
Object: pod,
|
||||
Meta: pod.GetObjectMeta(),
|
||||
}
|
||||
instance.Create(evt, q)
|
||||
Expect(q.Len()).To(Equal(1))
|
||||
|
||||
i, _ := q.Get()
|
||||
Expect(i).To(Equal(reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{Namespace: pod.GetNamespace(), Name: "foo-parent"}}))
|
||||
})
|
||||
|
||||
It("should enqueue a Request with the Owner of the object in the DeleteEvent.", func() {
|
||||
instance := handler.EnqueueRequestForOwner{
|
||||
OwnerType: &appsv1.ReplicaSet{},
|
||||
}
|
||||
instance.InjectScheme(scheme.Scheme)
|
||||
|
||||
pod.OwnerReferences = []metav1.OwnerReference{
|
||||
{
|
||||
Name: "foo-parent",
|
||||
Kind: "ReplicaSet",
|
||||
APIVersion: "apps/v1",
|
||||
},
|
||||
}
|
||||
evt := event.DeleteEvent{
|
||||
Object: pod,
|
||||
Meta: pod.GetObjectMeta(),
|
||||
}
|
||||
instance.Delete(evt, q)
|
||||
Expect(q.Len()).To(Equal(1))
|
||||
|
||||
i, _ := q.Get()
|
||||
Expect(i).To(Equal(reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{Namespace: pod.GetNamespace(), Name: "foo-parent"}}))
|
||||
})
|
||||
|
||||
It("should enqueue a Request with the Owners of both objects in the UpdateEvent.", func() {
|
||||
newPod := pod.DeepCopy()
|
||||
newPod.Name = pod.Name + "2"
|
||||
newPod.Namespace = pod.Namespace + "2"
|
||||
|
||||
instance := handler.EnqueueRequestForOwner{
|
||||
OwnerType: &appsv1.ReplicaSet{},
|
||||
}
|
||||
instance.InjectScheme(scheme.Scheme)
|
||||
|
||||
pod.OwnerReferences = []metav1.OwnerReference{
|
||||
{
|
||||
Name: "foo1-parent",
|
||||
Kind: "ReplicaSet",
|
||||
APIVersion: "apps/v1",
|
||||
},
|
||||
}
|
||||
newPod.OwnerReferences = []metav1.OwnerReference{
|
||||
{
|
||||
Name: "foo2-parent",
|
||||
Kind: "ReplicaSet",
|
||||
APIVersion: "apps/v1",
|
||||
},
|
||||
}
|
||||
evt := event.UpdateEvent{
|
||||
ObjectOld: pod,
|
||||
MetaOld: pod.GetObjectMeta(),
|
||||
ObjectNew: newPod,
|
||||
MetaNew: newPod.GetObjectMeta(),
|
||||
}
|
||||
instance.Update(evt, q)
|
||||
Expect(q.Len()).To(Equal(2))
|
||||
|
||||
i, _ := q.Get()
|
||||
Expect(i).To(Equal(reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{Namespace: pod.GetNamespace(), Name: "foo1-parent"}}))
|
||||
|
||||
i, _ = q.Get()
|
||||
Expect(i).To(Equal(reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{Namespace: newPod.GetNamespace(), Name: "foo2-parent"}}))
|
||||
})
|
||||
|
||||
It("should enqueue a Request with the Owner of the object in the GenericEvent.", func() {
|
||||
instance := handler.EnqueueRequestForOwner{
|
||||
OwnerType: &appsv1.ReplicaSet{},
|
||||
}
|
||||
instance.InjectScheme(scheme.Scheme)
|
||||
|
||||
pod.OwnerReferences = []metav1.OwnerReference{
|
||||
{
|
||||
Name: "foo-parent",
|
||||
Kind: "ReplicaSet",
|
||||
APIVersion: "apps/v1",
|
||||
},
|
||||
}
|
||||
evt := event.GenericEvent{
|
||||
Object: pod,
|
||||
Meta: pod.GetObjectMeta(),
|
||||
}
|
||||
instance.Generic(evt, q)
|
||||
Expect(q.Len()).To(Equal(1))
|
||||
|
||||
i, _ := q.Get()
|
||||
Expect(i).To(Equal(reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{Namespace: pod.GetNamespace(), Name: "foo-parent"}}))
|
||||
})
|
||||
|
||||
It("should not enqueue a Request if there are no owners matching Group and Kind.", func() {
|
||||
instance := handler.EnqueueRequestForOwner{
|
||||
OwnerType: &appsv1.ReplicaSet{},
|
||||
IsController: t,
|
||||
}
|
||||
instance.InjectScheme(scheme.Scheme)
|
||||
pod.OwnerReferences = []metav1.OwnerReference{
|
||||
{ // Wrong group
|
||||
Name: "foo1-parent",
|
||||
Kind: "ReplicaSet",
|
||||
APIVersion: "extensions/v1",
|
||||
},
|
||||
{ // Wrong kind
|
||||
Name: "foo2-parent",
|
||||
Kind: "Deployment",
|
||||
APIVersion: "apps/v1",
|
||||
},
|
||||
}
|
||||
evt := event.CreateEvent{
|
||||
Object: pod,
|
||||
Meta: pod.GetObjectMeta(),
|
||||
}
|
||||
instance.Create(evt, q)
|
||||
Expect(q.Len()).To(Equal(0))
|
||||
})
|
||||
|
||||
It("should enqueue a Request if there are owners matching Group "+
|
||||
"and Kind with a different version.", func() {
|
||||
instance := handler.EnqueueRequestForOwner{
|
||||
OwnerType: &appsv1.ReplicaSet{},
|
||||
}
|
||||
instance.InjectScheme(scheme.Scheme)
|
||||
pod.OwnerReferences = []metav1.OwnerReference{
|
||||
{
|
||||
Name: "foo-parent",
|
||||
Kind: "ReplicaSet",
|
||||
APIVersion: "apps/v2",
|
||||
},
|
||||
}
|
||||
evt := event.CreateEvent{
|
||||
Object: pod,
|
||||
Meta: pod.GetObjectMeta(),
|
||||
}
|
||||
instance.Create(evt, q)
|
||||
Expect(q.Len()).To(Equal(1))
|
||||
|
||||
i, _ := q.Get()
|
||||
Expect(i).To(Equal(reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{Namespace: pod.GetNamespace(), Name: "foo-parent"}}))
|
||||
})
|
||||
|
||||
It("should not enqueue a Request if there are no owners.", func() {
|
||||
instance := handler.EnqueueRequestForOwner{
|
||||
OwnerType: &appsv1.ReplicaSet{},
|
||||
}
|
||||
instance.InjectScheme(scheme.Scheme)
|
||||
evt := event.CreateEvent{
|
||||
Object: pod,
|
||||
Meta: pod.GetObjectMeta(),
|
||||
}
|
||||
instance.Create(evt, q)
|
||||
Expect(q.Len()).To(Equal(0))
|
||||
})
|
||||
|
||||
Context("with the Controller field set to true", func() {
|
||||
It("should enqueue reconcile.Requests for only the first the Controller if there are "+
|
||||
"multiple Controller owners.", func() {
|
||||
instance := handler.EnqueueRequestForOwner{
|
||||
OwnerType: &appsv1.ReplicaSet{},
|
||||
IsController: t,
|
||||
}
|
||||
instance.InjectScheme(scheme.Scheme)
|
||||
pod.OwnerReferences = []metav1.OwnerReference{
|
||||
{
|
||||
Name: "foo1-parent",
|
||||
Kind: "ReplicaSet",
|
||||
APIVersion: "apps/v1",
|
||||
},
|
||||
{
|
||||
Name: "foo2-parent",
|
||||
Kind: "ReplicaSet",
|
||||
APIVersion: "apps/v1",
|
||||
Controller: &t,
|
||||
},
|
||||
{
|
||||
Name: "foo3-parent",
|
||||
Kind: "ReplicaSet",
|
||||
APIVersion: "apps/v1",
|
||||
},
|
||||
{
|
||||
Name: "foo4-parent",
|
||||
Kind: "ReplicaSet",
|
||||
APIVersion: "apps/v1",
|
||||
Controller: &t,
|
||||
},
|
||||
{
|
||||
Name: "foo5-parent",
|
||||
Kind: "ReplicaSet",
|
||||
APIVersion: "apps/v1",
|
||||
},
|
||||
}
|
||||
evt := event.CreateEvent{
|
||||
Object: pod,
|
||||
Meta: pod.GetObjectMeta(),
|
||||
}
|
||||
instance.Create(evt, q)
|
||||
Expect(q.Len()).To(Equal(1))
|
||||
i, _ := q.Get()
|
||||
Expect(i).To(Equal(reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{Namespace: pod.GetNamespace(), Name: "foo2-parent"}}))
|
||||
})
|
||||
|
||||
It("should not enqueue reconcile.Requests if there are no Controller owners.", func() {
|
||||
instance := handler.EnqueueRequestForOwner{
|
||||
OwnerType: &appsv1.ReplicaSet{},
|
||||
IsController: t,
|
||||
}
|
||||
instance.InjectScheme(scheme.Scheme)
|
||||
pod.OwnerReferences = []metav1.OwnerReference{
|
||||
{
|
||||
Name: "foo1-parent",
|
||||
Kind: "ReplicaSet",
|
||||
APIVersion: "apps/v1",
|
||||
},
|
||||
{
|
||||
Name: "foo2-parent",
|
||||
Kind: "ReplicaSet",
|
||||
APIVersion: "apps/v1",
|
||||
},
|
||||
{
|
||||
Name: "foo3-parent",
|
||||
Kind: "ReplicaSet",
|
||||
APIVersion: "apps/v1",
|
||||
},
|
||||
}
|
||||
evt := event.CreateEvent{
|
||||
Object: pod,
|
||||
Meta: pod.GetObjectMeta(),
|
||||
}
|
||||
instance.Create(evt, q)
|
||||
Expect(q.Len()).To(Equal(0))
|
||||
})
|
||||
|
||||
It("should not enqueue reconcile.Requests if there are no owners.", func() {
|
||||
instance := handler.EnqueueRequestForOwner{
|
||||
OwnerType: &appsv1.ReplicaSet{},
|
||||
IsController: t,
|
||||
}
|
||||
instance.InjectScheme(scheme.Scheme)
|
||||
evt := event.CreateEvent{
|
||||
Object: pod,
|
||||
Meta: pod.GetObjectMeta(),
|
||||
}
|
||||
instance.Create(evt, q)
|
||||
Expect(q.Len()).To(Equal(0))
|
||||
})
|
||||
})
|
||||
|
||||
Context("with the Controller field set to false", func() {
|
||||
It("should enqueue a reconcile.Requests for all owners.", func() {
|
||||
instance := handler.EnqueueRequestForOwner{
|
||||
OwnerType: &appsv1.ReplicaSet{},
|
||||
}
|
||||
instance.InjectScheme(scheme.Scheme)
|
||||
pod.OwnerReferences = []metav1.OwnerReference{
|
||||
{
|
||||
Name: "foo1-parent",
|
||||
Kind: "ReplicaSet",
|
||||
APIVersion: "apps/v1",
|
||||
},
|
||||
{
|
||||
Name: "foo2-parent",
|
||||
Kind: "ReplicaSet",
|
||||
APIVersion: "apps/v1",
|
||||
},
|
||||
{
|
||||
Name: "foo3-parent",
|
||||
Kind: "ReplicaSet",
|
||||
APIVersion: "apps/v1",
|
||||
},
|
||||
}
|
||||
evt := event.CreateEvent{
|
||||
Object: pod,
|
||||
Meta: pod.GetObjectMeta(),
|
||||
}
|
||||
instance.Create(evt, q)
|
||||
Expect(q.Len()).To(Equal(3))
|
||||
|
||||
i, _ := q.Get()
|
||||
Expect(i).To(Equal(reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{Namespace: pod.GetNamespace(), Name: "foo1-parent"}}))
|
||||
i, _ = q.Get()
|
||||
Expect(i).To(Equal(reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{Namespace: pod.GetNamespace(), Name: "foo2-parent"}}))
|
||||
i, _ = q.Get()
|
||||
Expect(i).To(Equal(reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{Namespace: pod.GetNamespace(), Name: "foo3-parent"}}))
|
||||
})
|
||||
})
|
||||
|
||||
Context("with a nil metadata object", func() {
|
||||
It("should do nothing.", func() {
|
||||
instance := handler.EnqueueRequestForOwner{
|
||||
OwnerType: &appsv1.ReplicaSet{},
|
||||
}
|
||||
instance.InjectScheme(scheme.Scheme)
|
||||
pod.OwnerReferences = []metav1.OwnerReference{
|
||||
{
|
||||
Name: "foo1-parent",
|
||||
Kind: "ReplicaSet",
|
||||
APIVersion: "apps/v1",
|
||||
},
|
||||
}
|
||||
evt := event.CreateEvent{
|
||||
Object: pod,
|
||||
}
|
||||
instance.Create(evt, q)
|
||||
Expect(q.Len()).To(Equal(0))
|
||||
})
|
||||
})
|
||||
|
||||
Context("with a multiple matching kinds", func() {
|
||||
It("should do nothing.", func() {
|
||||
instance := handler.EnqueueRequestForOwner{
|
||||
OwnerType: &metav1.ListOptions{},
|
||||
}
|
||||
instance.InjectScheme(scheme.Scheme)
|
||||
pod.OwnerReferences = []metav1.OwnerReference{
|
||||
{
|
||||
Name: "foo1-parent",
|
||||
Kind: "ListOptions",
|
||||
APIVersion: "meta/v1",
|
||||
},
|
||||
}
|
||||
evt := event.CreateEvent{
|
||||
Object: pod,
|
||||
Meta: pod.GetObjectMeta(),
|
||||
}
|
||||
instance.Create(evt, q)
|
||||
Expect(q.Len()).To(Equal(0))
|
||||
})
|
||||
})
|
||||
Context("with an OwnerType that cannot be resolved", func() {
|
||||
It("should do nothing.", func() {
|
||||
instance := handler.EnqueueRequestForOwner{
|
||||
OwnerType: &controllertest.ErrorType{},
|
||||
}
|
||||
instance.InjectScheme(scheme.Scheme)
|
||||
pod.OwnerReferences = []metav1.OwnerReference{
|
||||
{
|
||||
Name: "foo1-parent",
|
||||
Kind: "ListOptions",
|
||||
APIVersion: "meta/v1",
|
||||
},
|
||||
}
|
||||
evt := event.CreateEvent{
|
||||
Object: pod,
|
||||
Meta: pod.GetObjectMeta(),
|
||||
}
|
||||
instance.Create(evt, q)
|
||||
Expect(q.Len()).To(Equal(0))
|
||||
})
|
||||
})
|
||||
|
||||
Context("with a nil OwnerType", func() {
|
||||
It("should do nothing.", func() {
|
||||
instance := handler.EnqueueRequestForOwner{}
|
||||
instance.InjectScheme(scheme.Scheme)
|
||||
pod.OwnerReferences = []metav1.OwnerReference{
|
||||
{
|
||||
Name: "foo1-parent",
|
||||
Kind: "OwnerType",
|
||||
APIVersion: "meta/v1",
|
||||
},
|
||||
}
|
||||
evt := event.CreateEvent{
|
||||
Object: pod,
|
||||
Meta: pod.GetObjectMeta(),
|
||||
}
|
||||
instance.Create(evt, q)
|
||||
Expect(q.Len()).To(Equal(0))
|
||||
})
|
||||
})
|
||||
|
||||
Context("with an invalid APIVersion in the OwnerReference", func() {
|
||||
It("should do nothing.", func() {
|
||||
instance := handler.EnqueueRequestForOwner{
|
||||
OwnerType: &appsv1.ReplicaSet{},
|
||||
}
|
||||
instance.InjectScheme(scheme.Scheme)
|
||||
pod.OwnerReferences = []metav1.OwnerReference{
|
||||
{
|
||||
Name: "foo1-parent",
|
||||
Kind: "ReplicaSet",
|
||||
APIVersion: "apps/v1/fail",
|
||||
},
|
||||
}
|
||||
evt := event.CreateEvent{
|
||||
Object: pod,
|
||||
Meta: pod.GetObjectMeta(),
|
||||
}
|
||||
instance.Create(evt, q)
|
||||
Expect(q.Len()).To(Equal(0))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Funcs", func() {
|
||||
failingFuncs := handler.Funcs{
|
||||
CreateFunc: func(event.CreateEvent, workqueue.RateLimitingInterface) {
|
||||
defer GinkgoRecover()
|
||||
Fail("Did not expect CreateEvent to be called.")
|
||||
},
|
||||
DeleteFunc: func(event.DeleteEvent, workqueue.RateLimitingInterface) {
|
||||
defer GinkgoRecover()
|
||||
Fail("Did not expect DeleteEvent to be called.")
|
||||
},
|
||||
UpdateFunc: func(event.UpdateEvent, workqueue.RateLimitingInterface) {
|
||||
defer GinkgoRecover()
|
||||
Fail("Did not expect UpdateEvent to be called.")
|
||||
},
|
||||
GenericFunc: func(event.GenericEvent, workqueue.RateLimitingInterface) {
|
||||
defer GinkgoRecover()
|
||||
Fail("Did not expect GenericEvent to be called.")
|
||||
},
|
||||
}
|
||||
|
||||
It("should call CreateFunc for a CreateEvent if provided.", func(done Done) {
|
||||
instance := failingFuncs
|
||||
evt := event.CreateEvent{
|
||||
Object: pod,
|
||||
Meta: pod.GetObjectMeta(),
|
||||
}
|
||||
instance.CreateFunc = func(evt2 event.CreateEvent, q2 workqueue.RateLimitingInterface) {
|
||||
defer GinkgoRecover()
|
||||
Expect(q2).To(Equal(q))
|
||||
Expect(evt2).To(Equal(evt))
|
||||
}
|
||||
instance.Create(evt, q)
|
||||
close(done)
|
||||
})
|
||||
|
||||
It("should NOT call CreateFunc for a CreateEvent if NOT provided.", func(done Done) {
|
||||
instance := failingFuncs
|
||||
instance.CreateFunc = nil
|
||||
evt := event.CreateEvent{
|
||||
Object: pod,
|
||||
Meta: pod.GetObjectMeta(),
|
||||
}
|
||||
instance.Create(evt, q)
|
||||
close(done)
|
||||
})
|
||||
|
||||
It("should call UpdateFunc for an UpdateEvent if provided.", func(done Done) {
|
||||
newPod := pod.DeepCopy()
|
||||
newPod.Name = pod.Name + "2"
|
||||
newPod.Namespace = pod.Namespace + "2"
|
||||
evt := event.UpdateEvent{
|
||||
ObjectOld: pod,
|
||||
MetaOld: pod.GetObjectMeta(),
|
||||
ObjectNew: newPod,
|
||||
MetaNew: newPod.GetObjectMeta(),
|
||||
}
|
||||
|
||||
instance := failingFuncs
|
||||
instance.UpdateFunc = func(evt2 event.UpdateEvent, q2 workqueue.RateLimitingInterface) {
|
||||
defer GinkgoRecover()
|
||||
Expect(q2).To(Equal(q))
|
||||
Expect(evt2).To(Equal(evt))
|
||||
}
|
||||
|
||||
instance.Update(evt, q)
|
||||
close(done)
|
||||
})
|
||||
|
||||
It("should NOT call UpdateFunc for an UpdateEvent if NOT provided.", func(done Done) {
|
||||
newPod := pod.DeepCopy()
|
||||
newPod.Name = pod.Name + "2"
|
||||
newPod.Namespace = pod.Namespace + "2"
|
||||
evt := event.UpdateEvent{
|
||||
ObjectOld: pod,
|
||||
MetaOld: pod.GetObjectMeta(),
|
||||
ObjectNew: newPod,
|
||||
MetaNew: newPod.GetObjectMeta(),
|
||||
}
|
||||
instance.Update(evt, q)
|
||||
close(done)
|
||||
})
|
||||
|
||||
It("should call DeleteFunc for a DeleteEvent if provided.", func(done Done) {
|
||||
instance := failingFuncs
|
||||
evt := event.DeleteEvent{
|
||||
Object: pod,
|
||||
Meta: pod.GetObjectMeta(),
|
||||
}
|
||||
instance.DeleteFunc = func(evt2 event.DeleteEvent, q2 workqueue.RateLimitingInterface) {
|
||||
defer GinkgoRecover()
|
||||
Expect(q2).To(Equal(q))
|
||||
Expect(evt2).To(Equal(evt))
|
||||
}
|
||||
instance.Delete(evt, q)
|
||||
close(done)
|
||||
})
|
||||
|
||||
It("should NOT call DeleteFunc for a DeleteEvent if NOT provided.", func(done Done) {
|
||||
instance := failingFuncs
|
||||
instance.DeleteFunc = nil
|
||||
evt := event.DeleteEvent{
|
||||
Object: pod,
|
||||
Meta: pod.GetObjectMeta(),
|
||||
}
|
||||
instance.Delete(evt, q)
|
||||
close(done)
|
||||
})
|
||||
|
||||
It("should call GenericFunc for a GenericEvent if provided.", func(done Done) {
|
||||
instance := failingFuncs
|
||||
evt := event.GenericEvent{
|
||||
Object: pod,
|
||||
Meta: pod.GetObjectMeta(),
|
||||
}
|
||||
instance.GenericFunc = func(evt2 event.GenericEvent, q2 workqueue.RateLimitingInterface) {
|
||||
defer GinkgoRecover()
|
||||
Expect(q2).To(Equal(q))
|
||||
Expect(evt2).To(Equal(evt))
|
||||
}
|
||||
instance.Generic(evt, q)
|
||||
close(done)
|
||||
})
|
||||
|
||||
It("should NOT call GenericFunc for a GenericEvent if NOT provided.", func(done Done) {
|
||||
instance := failingFuncs
|
||||
instance.GenericFunc = nil
|
||||
evt := event.GenericEvent{
|
||||
Object: pod,
|
||||
Meta: pod.GetObjectMeta(),
|
||||
}
|
||||
instance.Generic(evt, q)
|
||||
close(done)
|
||||
})
|
||||
})
|
||||
})
|
110
vendor/sigs.k8s.io/controller-runtime/pkg/handler/example_test.go
generated
vendored
Normal file
110
vendor/sigs.k8s.io/controller-runtime/pkg/handler/example_test.go
generated
vendored
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
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 handler_test
|
||||
|
||||
import (
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
"sigs.k8s.io/controller-runtime/pkg/source"
|
||||
)
|
||||
|
||||
var c controller.Controller
|
||||
|
||||
// This example watches Pods and enqueues Requests with the Name and Namespace of the Pod from
|
||||
// the Event (i.e. change caused by a Create, Update, Delete).
|
||||
func ExampleEnqueueRequestForObject() {
|
||||
// controller is a controller.controller
|
||||
c.Watch(
|
||||
&source.Kind{Type: &corev1.Pod{}},
|
||||
&handler.EnqueueRequestForObject{},
|
||||
)
|
||||
}
|
||||
|
||||
// This example watches ReplicaSets and enqueues a Request containing the Name and Namespace of the
|
||||
// owning (direct) Deployment responsible for the creation of the ReplicaSet.
|
||||
func ExampleEnqueueRequestForOwner() {
|
||||
// controller is a controller.controller
|
||||
c.Watch(
|
||||
&source.Kind{Type: &appsv1.ReplicaSet{}},
|
||||
&handler.EnqueueRequestForOwner{
|
||||
OwnerType: &appsv1.Deployment{},
|
||||
IsController: true,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// This example watches Deployments and enqueues a Request contain the Name and Namespace of different
|
||||
// objects (of Type: MyKind) using a mapping function defined by the user.
|
||||
func ExampleEnqueueRequestsFromMapFunc() {
|
||||
// controller is a controller.controller
|
||||
c.Watch(
|
||||
&source.Kind{Type: &appsv1.Deployment{}},
|
||||
&handler.EnqueueRequestsFromMapFunc{
|
||||
ToRequests: handler.ToRequestsFunc(func(a handler.MapObject) []reconcile.Request {
|
||||
return []reconcile.Request{
|
||||
{NamespacedName: types.NamespacedName{
|
||||
Name: a.Meta.GetName() + "-1",
|
||||
Namespace: a.Meta.GetNamespace(),
|
||||
}},
|
||||
{NamespacedName: types.NamespacedName{
|
||||
Name: a.Meta.GetName() + "-2",
|
||||
Namespace: a.Meta.GetNamespace(),
|
||||
}},
|
||||
}
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
// This example implements handler.EnqueueRequestForObject.
|
||||
func ExampleFuncs() {
|
||||
// controller is a controller.controller
|
||||
c.Watch(
|
||||
&source.Kind{Type: &corev1.Pod{}},
|
||||
handler.Funcs{
|
||||
CreateFunc: func(e event.CreateEvent, q workqueue.RateLimitingInterface) {
|
||||
q.Add(reconcile.Request{NamespacedName: types.NamespacedName{
|
||||
Name: e.Meta.GetName(),
|
||||
Namespace: e.Meta.GetNamespace(),
|
||||
}})
|
||||
},
|
||||
UpdateFunc: func(e event.UpdateEvent, q workqueue.RateLimitingInterface) {
|
||||
q.Add(reconcile.Request{NamespacedName: types.NamespacedName{
|
||||
Name: e.MetaNew.GetName(),
|
||||
Namespace: e.MetaNew.GetNamespace(),
|
||||
}})
|
||||
},
|
||||
DeleteFunc: func(e event.DeleteEvent, q workqueue.RateLimitingInterface) {
|
||||
q.Add(reconcile.Request{NamespacedName: types.NamespacedName{
|
||||
Name: e.Meta.GetName(),
|
||||
Namespace: e.Meta.GetNamespace(),
|
||||
}})
|
||||
},
|
||||
GenericFunc: func(e event.GenericEvent, q workqueue.RateLimitingInterface) {
|
||||
q.Add(reconcile.Request{NamespacedName: types.NamespacedName{
|
||||
Name: e.Meta.GetName(),
|
||||
Namespace: e.Meta.GetNamespace(),
|
||||
}})
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
48
vendor/sigs.k8s.io/controller-runtime/pkg/internal/admission/decode.go
generated
vendored
Normal file
48
vendor/sigs.k8s.io/controller-runtime/pkg/internal/admission/decode.go
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
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 admission
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/api/admission/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
)
|
||||
|
||||
var (
|
||||
scheme = runtime.NewScheme()
|
||||
codecs = serializer.NewCodecFactory(scheme)
|
||||
)
|
||||
|
||||
// Decode reads the Raw data from review and deserializes it into object returning a non-nil response if there was an
|
||||
// error
|
||||
func Decode(review v1beta1.AdmissionReview, object runtime.Object,
|
||||
resourceType metav1.GroupVersionResource) *v1beta1.AdmissionResponse {
|
||||
if review.Request.Resource != resourceType {
|
||||
return ErrorResponse(fmt.Errorf("expect resource to be %s", resourceType))
|
||||
}
|
||||
|
||||
raw := review.Request.Object.Raw
|
||||
deserializer := codecs.UniversalDeserializer()
|
||||
if _, _, err := deserializer.Decode(raw, nil, object); err != nil {
|
||||
fmt.Printf("%v", err)
|
||||
return ErrorResponse(err)
|
||||
}
|
||||
return nil
|
||||
}
|
18
vendor/sigs.k8s.io/controller-runtime/pkg/internal/admission/doc.go
generated
vendored
Normal file
18
vendor/sigs.k8s.io/controller-runtime/pkg/internal/admission/doc.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package admission provides libraries for creating admission webhooks.
|
||||
package admission
|
42
vendor/sigs.k8s.io/controller-runtime/pkg/internal/admission/example_admissionfunc_test.go
generated
vendored
Normal file
42
vendor/sigs.k8s.io/controller-runtime/pkg/internal/admission/example_admissionfunc_test.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
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 admission_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/api/admission/v1beta1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/internal/admission"
|
||||
)
|
||||
|
||||
func ExampleFunc() {
|
||||
var _ admission.Func = func(review v1beta1.AdmissionReview) *v1beta1.AdmissionResponse {
|
||||
pod := corev1.Pod{}
|
||||
resourceType := metav1.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
|
||||
if errResp := admission.Decode(review, &pod, resourceType); errResp != nil {
|
||||
return errResp
|
||||
}
|
||||
// Business logic for admission decision
|
||||
if len(pod.Spec.Containers) != 1 {
|
||||
return admission.DenyResponse(fmt.Sprintf(
|
||||
"pod %s/%s may only have 1 container.", pod.Namespace, pod.Name))
|
||||
}
|
||||
return admission.AllowResponse()
|
||||
}
|
||||
}
|
47
vendor/sigs.k8s.io/controller-runtime/pkg/internal/admission/example_decode_test.go
generated
vendored
Normal file
47
vendor/sigs.k8s.io/controller-runtime/pkg/internal/admission/example_decode_test.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
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 admission_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/api/admission/v1beta1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/internal/admission"
|
||||
)
|
||||
|
||||
func ExampleDecode() {
|
||||
var review v1beta1.AdmissionReview
|
||||
resourceType := metav1.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
|
||||
pod := corev1.Pod{}
|
||||
if errResp := admission.Decode(review, &pod, resourceType); errResp != nil {
|
||||
// Send error resp
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleErrorResponse() {
|
||||
admission.ErrorResponse(fmt.Errorf("some error explanation"))
|
||||
}
|
||||
|
||||
func ExampleDenyResponse() {
|
||||
admission.DenyResponse(fmt.Sprintf("some deny explanation"))
|
||||
}
|
||||
|
||||
func ExampleAllowResponse() {
|
||||
admission.AllowResponse()
|
||||
}
|
59
vendor/sigs.k8s.io/controller-runtime/pkg/internal/admission/example_handlefunc_test.go
generated
vendored
Normal file
59
vendor/sigs.k8s.io/controller-runtime/pkg/internal/admission/example_handlefunc_test.go
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
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 admission_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/api/admission/v1beta1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/internal/admission"
|
||||
)
|
||||
|
||||
func ExampleHandleFunc() {
|
||||
resourceType := metav1.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
|
||||
admission.HandleFunc("/pod", resourceType, func(review v1beta1.AdmissionReview) *v1beta1.AdmissionResponse {
|
||||
pod := corev1.Pod{}
|
||||
if errResp := admission.Decode(review, &pod, resourceType); errResp != nil {
|
||||
return errResp
|
||||
}
|
||||
// Business logic for admission decision
|
||||
if len(pod.Spec.Containers) != 1 {
|
||||
return admission.DenyResponse(fmt.Sprintf(
|
||||
"pod %s/%s may only have 1 container.", pod.Namespace, pod.Name))
|
||||
}
|
||||
return admission.AllowResponse()
|
||||
})
|
||||
}
|
||||
|
||||
func ExampleManager_HandleFunc() {
|
||||
resourceType := metav1.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
|
||||
ah := admission.Manager{}
|
||||
ah.HandleFunc("/pod", resourceType, func(review v1beta1.AdmissionReview) *v1beta1.AdmissionResponse {
|
||||
pod := corev1.Pod{}
|
||||
if errResp := admission.Decode(review, &pod, resourceType); errResp != nil {
|
||||
return errResp
|
||||
}
|
||||
// Business logic for admission decision
|
||||
if len(pod.Spec.Containers) != 1 {
|
||||
return admission.DenyResponse(fmt.Sprintf(
|
||||
"pod %s/%s may only have 1 container.", pod.Namespace, pod.Name))
|
||||
}
|
||||
return admission.AllowResponse()
|
||||
})
|
||||
}
|
43
vendor/sigs.k8s.io/controller-runtime/pkg/internal/admission/example_test.go
generated
vendored
Normal file
43
vendor/sigs.k8s.io/controller-runtime/pkg/internal/admission/example_test.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
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 admission_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/api/admission/v1beta1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/internal/admission"
|
||||
)
|
||||
|
||||
func Example() {
|
||||
resourceType := metav1.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
|
||||
admission.HandleFunc("/pod", resourceType, func(review v1beta1.AdmissionReview) *v1beta1.AdmissionResponse {
|
||||
pod := corev1.Pod{}
|
||||
if errResp := admission.Decode(review, &pod, resourceType); errResp != nil {
|
||||
return errResp
|
||||
}
|
||||
// Business logic for admission decision
|
||||
if len(pod.Spec.Containers) != 1 {
|
||||
return admission.DenyResponse(fmt.Sprintf(
|
||||
"pod %s/%s may only have 1 container.", pod.Namespace, pod.Name))
|
||||
}
|
||||
return admission.AllowResponse()
|
||||
})
|
||||
admission.ListenAndServeTLS("")
|
||||
}
|
72
vendor/sigs.k8s.io/controller-runtime/pkg/internal/admission/handler.go
generated
vendored
Normal file
72
vendor/sigs.k8s.io/controller-runtime/pkg/internal/admission/handler.go
generated
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
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 admission
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"k8s.io/api/admission/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// Func implements an AdmissionReview operation for a GroupVersionResource
|
||||
type Func func(review v1beta1.AdmissionReview) *v1beta1.AdmissionResponse
|
||||
|
||||
// HandleEntry
|
||||
type admissionHandler struct {
|
||||
GVR metav1.GroupVersionResource
|
||||
Fn Func
|
||||
}
|
||||
|
||||
// handle handles an admission request and returns a result
|
||||
func (ah admissionHandler) handle(review v1beta1.AdmissionReview) *v1beta1.AdmissionResponse {
|
||||
return ah.handle(review)
|
||||
}
|
||||
|
||||
// Manager manages admission controllers
|
||||
type Manager struct {
|
||||
Entries map[string]admissionHandler
|
||||
SMux *http.ServeMux
|
||||
}
|
||||
|
||||
// DefaultAdmissionFns is the default admission control functions registry
|
||||
var DefaultAdmissionFns = &Manager{
|
||||
SMux: http.DefaultServeMux,
|
||||
}
|
||||
|
||||
// HandleFunc registers fn as an admission control webhook callback for the group,version,resources specified
|
||||
func (e *Manager) HandleFunc(path string, gvr metav1.GroupVersionResource, fn Func) {
|
||||
// Register the entry so a Webhook config is created
|
||||
e.Entries[path] = admissionHandler{gvr, fn}
|
||||
|
||||
// Register the handler path
|
||||
e.SMux.Handle(path, httpHandler{fn})
|
||||
}
|
||||
|
||||
// HandleFunc registers fn as an admission control webhook callback for the group,version,resources specified
|
||||
func HandleFunc(path string, gvr metav1.GroupVersionResource, fn Func) {
|
||||
DefaultAdmissionFns.HandleFunc(path, gvr, fn)
|
||||
}
|
||||
|
||||
// ListenAndServeTLS starts the admission HttpServer.
|
||||
func ListenAndServeTLS(addr string) error {
|
||||
server := &http.Server{
|
||||
Addr: addr,
|
||||
TLSConfig: nil, // TODO: Set this
|
||||
}
|
||||
return server.ListenAndServeTLS("", "")
|
||||
}
|
80
vendor/sigs.k8s.io/controller-runtime/pkg/internal/admission/http.go
generated
vendored
Normal file
80
vendor/sigs.k8s.io/controller-runtime/pkg/internal/admission/http.go
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
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 admission
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"k8s.io/api/admission/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
)
|
||||
|
||||
var (
|
||||
// TODO(directxman12): this shouldn't be a global log
|
||||
log = logf.KBLog.WithName("admission").WithName("http-handler")
|
||||
)
|
||||
|
||||
func (h httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
var body []byte
|
||||
if r.Body != nil {
|
||||
if data, err := ioutil.ReadAll(r.Body); err == nil {
|
||||
body = data
|
||||
}
|
||||
}
|
||||
|
||||
// verify the content type is accurate
|
||||
contentType := r.Header.Get("Content-Type")
|
||||
if contentType != "application/json" {
|
||||
log.Error(nil, "invalid content type, expected application/json", "context type", contentType)
|
||||
return
|
||||
}
|
||||
|
||||
var reviewResponse *v1beta1.AdmissionResponse
|
||||
ar := v1beta1.AdmissionReview{}
|
||||
deserializer := codecs.UniversalDeserializer()
|
||||
if _, _, err := deserializer.Decode(body, nil, &ar); err != nil {
|
||||
log.Error(err, "unable to decode request body")
|
||||
reviewResponse = ErrorResponse(err)
|
||||
} else {
|
||||
reviewResponse = h.admit(ar)
|
||||
}
|
||||
|
||||
response := v1beta1.AdmissionReview{}
|
||||
if reviewResponse != nil {
|
||||
response.Response = reviewResponse
|
||||
response.Response.UID = ar.Request.UID
|
||||
}
|
||||
// reset the Object and OldObject, they are not needed in a response.
|
||||
ar.Request.Object = runtime.RawExtension{}
|
||||
ar.Request.OldObject = runtime.RawExtension{}
|
||||
|
||||
resp, err := json.Marshal(response)
|
||||
if err != nil {
|
||||
log.Error(err, "unable to marshal response")
|
||||
return
|
||||
}
|
||||
if _, err := w.Write(resp); err != nil {
|
||||
log.Error(err, "unable to write response")
|
||||
}
|
||||
}
|
||||
|
||||
type httpHandler struct {
|
||||
admit Func
|
||||
}
|
48
vendor/sigs.k8s.io/controller-runtime/pkg/internal/admission/response.go
generated
vendored
Normal file
48
vendor/sigs.k8s.io/controller-runtime/pkg/internal/admission/response.go
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
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 admission
|
||||
|
||||
import (
|
||||
"k8s.io/api/admission/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// ErrorResponse creates a new AdmissionResponse for an error handling the request
|
||||
func ErrorResponse(err error) *v1beta1.AdmissionResponse {
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Result: &metav1.Status{
|
||||
Message: err.Error(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// DenyResponse returns a new response for denying a request
|
||||
func DenyResponse(msg string) *v1beta1.AdmissionResponse {
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: false,
|
||||
Result: &metav1.Status{
|
||||
Reason: metav1.StatusReason(msg),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// AllowResponse returns a new response for admitting a request
|
||||
func AllowResponse() *v1beta1.AdmissionResponse {
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
}
|
||||
}
|
42
vendor/sigs.k8s.io/controller-runtime/pkg/internal/admission/tls.go
generated
vendored
Normal file
42
vendor/sigs.k8s.io/controller-runtime/pkg/internal/admission/tls.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
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 admission
|
||||
|
||||
//type certs struct {
|
||||
// Cert []byte
|
||||
// Key []byte
|
||||
// CACert []byte
|
||||
//}
|
||||
|
||||
//// MakeTLSConfig makes a TLS configuration suitable for use with the server
|
||||
//func makeTLSConfig(certs certs) (*tls.Config, error) {
|
||||
// caCertPool := x509.NewCertPool()
|
||||
// caCertPool.AppendCertsFromPEM(certs.CACert)
|
||||
// //cert, err := tls.X509KeyPair(certs.Cert, certs.Key)
|
||||
// //if err != nil {
|
||||
// // return nil, err
|
||||
// //}
|
||||
// return &tls.Config{
|
||||
// //Certificates: []tls.Certificate{cert},
|
||||
// ClientCAs: caCertPool,
|
||||
// ClientAuth: tls.NoClientCert,
|
||||
// // Note on GKE there apparently is no client cert sent, so this
|
||||
// // does not work on GKE.
|
||||
// // TODO: make this into a configuration option.
|
||||
// // ClientAuth: tls.RequireAndVerifyClientCert,
|
||||
// }, nil
|
||||
//}
|
235
vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go
generated
vendored
Normal file
235
vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go
generated
vendored
Normal file
@ -0,0 +1,235 @@
|
||||
/*
|
||||
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 controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
"sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
"sigs.k8s.io/controller-runtime/pkg/runtime/inject"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/source"
|
||||
)
|
||||
|
||||
var log = logf.KBLog.WithName("controller")
|
||||
|
||||
var _ inject.Injector = &Controller{}
|
||||
|
||||
// Controller implements controller.Controller
|
||||
type Controller struct {
|
||||
// Name is used to uniquely identify a Controller in tracing, logging and monitoring. Name is required.
|
||||
Name string
|
||||
|
||||
// MaxConcurrentReconciles is the maximum number of concurrent Reconciles which can be run. Defaults to 1.
|
||||
MaxConcurrentReconciles int
|
||||
|
||||
// Reconciler is a function that can be called at any time with the Name / Namespace of an object and
|
||||
// ensures that the state of the system matches the state specified in the object.
|
||||
// Defaults to the DefaultReconcileFunc.
|
||||
Do reconcile.Reconciler
|
||||
|
||||
// Client is a lazily initialized Client. The controllerManager will initialize this when Start is called.
|
||||
Client client.Client
|
||||
|
||||
// Scheme is injected by the controllerManager when controllerManager.Start is called
|
||||
Scheme *runtime.Scheme
|
||||
|
||||
// informers are injected by the controllerManager when controllerManager.Start is called
|
||||
Cache cache.Cache
|
||||
|
||||
// Config is the rest.Config used to talk to the apiserver. Defaults to one of in-cluster, environment variable
|
||||
// specified, or the ~/.kube/Config.
|
||||
Config *rest.Config
|
||||
|
||||
// Queue is an listeningQueue that listens for events from Informers and adds object keys to
|
||||
// the Queue for processing
|
||||
Queue workqueue.RateLimitingInterface
|
||||
|
||||
// SetFields is used to inject dependencies into other objects such as Sources, EventHandlers and Predicates
|
||||
SetFields func(i interface{}) error
|
||||
|
||||
// mu is used to synchronize Controller setup
|
||||
mu sync.Mutex
|
||||
|
||||
// JitterPeriod allows tests to reduce the JitterPeriod so they complete faster
|
||||
JitterPeriod time.Duration
|
||||
|
||||
// WaitForCacheSync allows tests to mock out the WaitForCacheSync function to return an error
|
||||
// defaults to Cache.WaitForCacheSync
|
||||
WaitForCacheSync func(stopCh <-chan struct{}) bool
|
||||
|
||||
// Started is true if the Controller has been Started
|
||||
Started bool
|
||||
|
||||
// Recorder is an event recorder for recording Event resources to the
|
||||
// Kubernetes API.
|
||||
Recorder record.EventRecorder
|
||||
|
||||
// TODO(community): Consider initializing a logger with the Controller Name as the tag
|
||||
}
|
||||
|
||||
// Reconcile implements reconcile.Reconciler
|
||||
func (c *Controller) Reconcile(r reconcile.Request) (reconcile.Result, error) {
|
||||
return c.Do.Reconcile(r)
|
||||
}
|
||||
|
||||
// Watch implements controller.Controller
|
||||
func (c *Controller) Watch(src source.Source, evthdler handler.EventHandler, prct ...predicate.Predicate) error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
// Inject Cache into arguments
|
||||
if err := c.SetFields(src); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.SetFields(evthdler); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, pr := range prct {
|
||||
if err := c.SetFields(pr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("Starting EventSource", "Controller", c.Name, "Source", src)
|
||||
return src.Start(evthdler, c.Queue, prct...)
|
||||
}
|
||||
|
||||
// Start implements controller.Controller
|
||||
func (c *Controller) Start(stop <-chan struct{}) error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
// TODO(pwittrock): Reconsider HandleCrash
|
||||
defer utilruntime.HandleCrash()
|
||||
defer c.Queue.ShutDown()
|
||||
|
||||
// Start the SharedIndexInformer factories to begin populating the SharedIndexInformer caches
|
||||
log.Info("Starting Controller", "Controller", c.Name)
|
||||
|
||||
// Wait for the caches to be synced before starting workers
|
||||
if c.WaitForCacheSync == nil {
|
||||
c.WaitForCacheSync = c.Cache.WaitForCacheSync
|
||||
}
|
||||
if ok := c.WaitForCacheSync(stop); !ok {
|
||||
// This code is unreachable right now since WaitForCacheSync will never return an error
|
||||
// Leaving it here because that could happen in the future
|
||||
err := fmt.Errorf("failed to wait for %s caches to sync", c.Name)
|
||||
log.Error(err, "Could not wait for Cache to sync", "Controller", c.Name)
|
||||
return err
|
||||
}
|
||||
|
||||
if c.JitterPeriod == 0 {
|
||||
c.JitterPeriod = 1 * time.Second
|
||||
}
|
||||
|
||||
// Launch workers to process resources
|
||||
log.Info("Starting workers", "Controller", c.Name, "WorkerCount", c.MaxConcurrentReconciles)
|
||||
for i := 0; i < c.MaxConcurrentReconciles; i++ {
|
||||
// Process work items
|
||||
go wait.Until(func() {
|
||||
for c.processNextWorkItem() {
|
||||
}
|
||||
}, c.JitterPeriod, stop)
|
||||
}
|
||||
|
||||
c.Started = true
|
||||
|
||||
<-stop
|
||||
log.Info("Stopping workers", "Controller", c.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// processNextWorkItem will read a single work item off the workqueue and
|
||||
// attempt to process it, by calling the syncHandler.
|
||||
func (c *Controller) processNextWorkItem() bool {
|
||||
// This code copy-pasted from the sample-Controller.
|
||||
|
||||
obj, shutdown := c.Queue.Get()
|
||||
if obj == nil {
|
||||
// Sometimes the Queue gives us nil items when it starts up
|
||||
c.Queue.Forget(obj)
|
||||
}
|
||||
|
||||
if shutdown {
|
||||
// Stop working
|
||||
return false
|
||||
}
|
||||
|
||||
// We call Done here so the workqueue knows we have finished
|
||||
// processing this item. We also must remember to call Forget if we
|
||||
// do not want this work item being re-queued. For example, we do
|
||||
// not call Forget if a transient error occurs, instead the item is
|
||||
// put back on the workqueue and attempted again after a back-off
|
||||
// period.
|
||||
defer c.Queue.Done(obj)
|
||||
var req reconcile.Request
|
||||
var ok bool
|
||||
if req, ok = obj.(reconcile.Request); !ok {
|
||||
// As the item in the workqueue is actually invalid, we call
|
||||
// Forget here else we'd go into a loop of attempting to
|
||||
// process a work item that is invalid.
|
||||
c.Queue.Forget(obj)
|
||||
log.Error(nil, "Queue item was not a Request",
|
||||
"Controller", c.Name, "Type", fmt.Sprintf("%T", obj), "Value", obj)
|
||||
// Return true, don't take a break
|
||||
return true
|
||||
}
|
||||
|
||||
// RunInformersAndControllers the syncHandler, passing it the namespace/Name string of the
|
||||
// resource to be synced.
|
||||
if result, err := c.Do.Reconcile(req); err != nil {
|
||||
c.Queue.AddRateLimited(req)
|
||||
log.Error(err, "Reconciler error", "Controller", c.Name, "Request", req)
|
||||
|
||||
return false
|
||||
} else if result.RequeueAfter > 0 {
|
||||
c.Queue.AddAfter(req, result.RequeueAfter)
|
||||
return true
|
||||
} else if result.Requeue {
|
||||
c.Queue.AddRateLimited(req)
|
||||
return true
|
||||
}
|
||||
|
||||
// Finally, if no error occurs we Forget this item so it does not
|
||||
// get queued again until another change happens.
|
||||
c.Queue.Forget(obj)
|
||||
|
||||
// TODO(directxman12): What does 1 mean? Do we want level constants? Do we want levels at all?
|
||||
log.V(1).Info("Successfully Reconciled", "Controller", c.Name, "Request", req)
|
||||
|
||||
// Return true, don't take a break
|
||||
return true
|
||||
}
|
||||
|
||||
// InjectFunc implement SetFields.Injector
|
||||
func (c *Controller) InjectFunc(f inject.Func) error {
|
||||
c.SetFields = f
|
||||
return nil
|
||||
}
|
56
vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller_suite_test.go
generated
vendored
Normal file
56
vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller_suite_test.go
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
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 controller
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
)
|
||||
|
||||
func TestSource(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecsWithDefaultAndCustomReporters(t, "Controller Integration Suite", []Reporter{envtest.NewlineReporter{}})
|
||||
}
|
||||
|
||||
var testenv *envtest.Environment
|
||||
var cfg *rest.Config
|
||||
var clientset *kubernetes.Clientset
|
||||
|
||||
var _ = BeforeSuite(func(done Done) {
|
||||
logf.SetLogger(logf.ZapLoggerTo(GinkgoWriter, true))
|
||||
|
||||
testenv = &envtest.Environment{}
|
||||
|
||||
var err error
|
||||
cfg, err = testenv.Start()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
clientset, err = kubernetes.NewForConfig(cfg)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
close(done)
|
||||
}, 60)
|
||||
|
||||
var _ = AfterSuite(func() {
|
||||
testenv.Stop()
|
||||
})
|
431
vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller_test.go
generated
vendored
Normal file
431
vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller_test.go
generated
vendored
Normal file
@ -0,0 +1,431 @@
|
||||
/*
|
||||
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 controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
"sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/cache/informertest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllertest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile/reconciletest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/source"
|
||||
)
|
||||
|
||||
var _ = Describe("controller", func() {
|
||||
var fakeReconcile *reconciletest.FakeReconcile
|
||||
var ctrl *Controller
|
||||
var queue *controllertest.Queue
|
||||
var informers *informertest.FakeInformers
|
||||
var stop chan struct{}
|
||||
var reconciled chan reconcile.Request
|
||||
var request = reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{Namespace: "foo", Name: "bar"},
|
||||
}
|
||||
|
||||
BeforeEach(func() {
|
||||
stop = make(chan struct{})
|
||||
reconciled = make(chan reconcile.Request)
|
||||
fakeReconcile = &reconciletest.FakeReconcile{
|
||||
Chan: reconciled,
|
||||
}
|
||||
queue = &controllertest.Queue{
|
||||
Interface: workqueue.New(),
|
||||
}
|
||||
informers = &informertest.FakeInformers{}
|
||||
ctrl = &Controller{
|
||||
MaxConcurrentReconciles: 1,
|
||||
Do: fakeReconcile,
|
||||
Queue: queue,
|
||||
Cache: informers,
|
||||
}
|
||||
ctrl.InjectFunc(func(interface{}) error { return nil })
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
close(stop)
|
||||
})
|
||||
|
||||
Describe("Reconciler", func() {
|
||||
It("should call the Reconciler function", func() {
|
||||
ctrl.Do = reconcile.Func(func(reconcile.Request) (reconcile.Result, error) {
|
||||
return reconcile.Result{Requeue: true}, nil
|
||||
})
|
||||
result, err := ctrl.Reconcile(
|
||||
reconcile.Request{NamespacedName: types.NamespacedName{Namespace: "foo", Name: "bar"}})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(result).To(Equal(reconcile.Result{Requeue: true}))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Start", func() {
|
||||
It("should return an error if there is an error waiting for the informers", func(done Done) {
|
||||
ctrl.WaitForCacheSync = func(<-chan struct{}) bool { return false }
|
||||
ctrl.Name = "foo"
|
||||
err := ctrl.Start(stop)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("failed to wait for foo caches to sync"))
|
||||
|
||||
close(done)
|
||||
})
|
||||
|
||||
It("should wait for each informer to sync", func(done Done) {
|
||||
// Use a stopped channel so Start doesn't block
|
||||
stopped := make(chan struct{})
|
||||
close(stopped)
|
||||
|
||||
c, err := cache.New(cfg, cache.Options{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
c.GetInformer(&v1.Deployment{})
|
||||
c.GetInformer(&v1.ReplicaSet{})
|
||||
ctrl.Cache = c
|
||||
ctrl.WaitForCacheSync = func(<-chan struct{}) bool { return true }
|
||||
|
||||
Expect(ctrl.Start(stopped)).NotTo(HaveOccurred())
|
||||
|
||||
close(done)
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Watch", func() {
|
||||
It("should inject dependencies into the Source", func() {
|
||||
src := &source.Kind{Type: &corev1.Pod{}}
|
||||
src.InjectCache(ctrl.Cache)
|
||||
evthdl := &handler.EnqueueRequestForObject{}
|
||||
found := false
|
||||
ctrl.SetFields = func(i interface{}) error {
|
||||
defer GinkgoRecover()
|
||||
if i == src {
|
||||
found = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Expect(ctrl.Watch(src, evthdl)).NotTo(HaveOccurred())
|
||||
Expect(found).To(BeTrue(), "Source not injected")
|
||||
})
|
||||
|
||||
It("should return an error if there is an error injecting into the Source", func() {
|
||||
src := &source.Kind{Type: &corev1.Pod{}}
|
||||
src.InjectCache(ctrl.Cache)
|
||||
evthdl := &handler.EnqueueRequestForObject{}
|
||||
expected := fmt.Errorf("expect fail source")
|
||||
ctrl.SetFields = func(i interface{}) error {
|
||||
defer GinkgoRecover()
|
||||
if i == src {
|
||||
return expected
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Expect(ctrl.Watch(src, evthdl)).To(Equal(expected))
|
||||
})
|
||||
|
||||
It("should inject dependencies into the EventHandler", func() {
|
||||
src := &source.Kind{Type: &corev1.Pod{}}
|
||||
src.InjectCache(ctrl.Cache)
|
||||
evthdl := &handler.EnqueueRequestForObject{}
|
||||
found := false
|
||||
ctrl.SetFields = func(i interface{}) error {
|
||||
defer GinkgoRecover()
|
||||
if i == evthdl {
|
||||
found = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Expect(ctrl.Watch(src, evthdl)).NotTo(HaveOccurred())
|
||||
Expect(found).To(BeTrue(), "EventHandler not injected")
|
||||
})
|
||||
|
||||
It("should return an error if there is an error injecting into the EventHandler", func() {
|
||||
src := &source.Kind{Type: &corev1.Pod{}}
|
||||
evthdl := &handler.EnqueueRequestForObject{}
|
||||
expected := fmt.Errorf("expect fail eventhandler")
|
||||
ctrl.SetFields = func(i interface{}) error {
|
||||
defer GinkgoRecover()
|
||||
if i == evthdl {
|
||||
return expected
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Expect(ctrl.Watch(src, evthdl)).To(Equal(expected))
|
||||
})
|
||||
|
||||
It("should inject dependencies into the Reconciler", func() {
|
||||
// TODO(community): Write this
|
||||
})
|
||||
|
||||
It("should return an error if there is an error injecting into the Reconciler", func() {
|
||||
// TODO(community): Write this
|
||||
})
|
||||
|
||||
It("should inject dependencies into all of the Predicates", func() {
|
||||
src := &source.Kind{Type: &corev1.Pod{}}
|
||||
src.InjectCache(ctrl.Cache)
|
||||
evthdl := &handler.EnqueueRequestForObject{}
|
||||
pr1 := &predicate.Funcs{}
|
||||
pr2 := &predicate.Funcs{}
|
||||
found1 := false
|
||||
found2 := false
|
||||
ctrl.SetFields = func(i interface{}) error {
|
||||
defer GinkgoRecover()
|
||||
if i == pr1 {
|
||||
found1 = true
|
||||
}
|
||||
if i == pr2 {
|
||||
found2 = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Expect(ctrl.Watch(src, evthdl, pr1, pr2)).NotTo(HaveOccurred())
|
||||
Expect(found1).To(BeTrue(), "First Predicated not injected")
|
||||
Expect(found2).To(BeTrue(), "Second Predicated not injected")
|
||||
})
|
||||
|
||||
It("should return an error if there is an error injecting into any of the Predicates", func() {
|
||||
src := &source.Kind{Type: &corev1.Pod{}}
|
||||
src.InjectCache(ctrl.Cache)
|
||||
evthdl := &handler.EnqueueRequestForObject{}
|
||||
pr1 := &predicate.Funcs{}
|
||||
pr2 := &predicate.Funcs{}
|
||||
expected := fmt.Errorf("expect fail predicate")
|
||||
ctrl.SetFields = func(i interface{}) error {
|
||||
defer GinkgoRecover()
|
||||
if i == pr1 {
|
||||
return expected
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Expect(ctrl.Watch(src, evthdl, pr1, pr2)).To(Equal(expected))
|
||||
|
||||
ctrl.SetFields = func(i interface{}) error {
|
||||
defer GinkgoRecover()
|
||||
if i == pr2 {
|
||||
return expected
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Expect(ctrl.Watch(src, evthdl, pr1, pr2)).To(Equal(expected))
|
||||
})
|
||||
|
||||
It("should call Start the Source with the EventHandler, Queue, and Predicates", func() {
|
||||
pr1 := &predicate.Funcs{}
|
||||
pr2 := &predicate.Funcs{}
|
||||
evthdl := &handler.EnqueueRequestForObject{}
|
||||
src := source.Func(func(e handler.EventHandler, q workqueue.RateLimitingInterface, p ...predicate.Predicate) error {
|
||||
defer GinkgoRecover()
|
||||
Expect(e).To(Equal(evthdl))
|
||||
Expect(q).To(Equal(ctrl.Queue))
|
||||
Expect(p).To(ConsistOf(pr1, pr2))
|
||||
return nil
|
||||
})
|
||||
Expect(ctrl.Watch(src, evthdl, pr1, pr2)).NotTo(HaveOccurred())
|
||||
|
||||
})
|
||||
|
||||
It("should return an error if there is an error starting the Source", func() {
|
||||
err := fmt.Errorf("Expected Error: could not start source")
|
||||
src := source.Func(func(handler.EventHandler,
|
||||
workqueue.RateLimitingInterface,
|
||||
...predicate.Predicate) error {
|
||||
defer GinkgoRecover()
|
||||
return err
|
||||
})
|
||||
Expect(ctrl.Watch(src, &handler.EnqueueRequestForObject{})).To(Equal(err))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Processing queue items from a Controller", func() {
|
||||
It("should call Reconciler if an item is enqueued", func(done Done) {
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
Expect(ctrl.Start(stop)).NotTo(HaveOccurred())
|
||||
}()
|
||||
ctrl.Queue.Add(request)
|
||||
|
||||
By("Invoking Reconciler")
|
||||
Expect(<-reconciled).To(Equal(request))
|
||||
|
||||
By("Removing the item from the queue")
|
||||
Eventually(ctrl.Queue.Len).Should(Equal(0))
|
||||
Eventually(func() int { return ctrl.Queue.NumRequeues(request) }).Should(Equal(0))
|
||||
|
||||
close(done)
|
||||
})
|
||||
|
||||
It("should continue to process additional queue items after the first", func(done Done) {
|
||||
ctrl.Do = reconcile.Func(func(reconcile.Request) (reconcile.Result, error) {
|
||||
defer GinkgoRecover()
|
||||
Fail("Reconciler should not have been called")
|
||||
return reconcile.Result{}, nil
|
||||
})
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
Expect(ctrl.Start(stop)).NotTo(HaveOccurred())
|
||||
}()
|
||||
ctrl.Queue.Add("foo/bar")
|
||||
|
||||
// Don't expect the string to reconciled
|
||||
Expect(ctrl.processNextWorkItem()).To(BeTrue())
|
||||
|
||||
Eventually(ctrl.Queue.Len).Should(Equal(0))
|
||||
Eventually(func() int { return ctrl.Queue.NumRequeues(request) }).Should(Equal(0))
|
||||
|
||||
close(done)
|
||||
})
|
||||
|
||||
It("should forget an item if it is not a Request and continue processing items", func() {
|
||||
// TODO(community): write this test
|
||||
})
|
||||
|
||||
It("should requeue a Request if there is an error and continue processing items", func(done Done) {
|
||||
fakeReconcile.Err = fmt.Errorf("expected error: reconcile")
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
Expect(ctrl.Start(stop)).NotTo(HaveOccurred())
|
||||
}()
|
||||
ctrl.Queue.Add(request)
|
||||
|
||||
// Reduce the jitterperiod so we don't have to wait a second before the reconcile function is rerun.
|
||||
ctrl.JitterPeriod = time.Millisecond
|
||||
|
||||
By("Invoking Reconciler which will give an error")
|
||||
Expect(<-reconciled).To(Equal(request))
|
||||
|
||||
By("Invoking Reconciler a second time without error")
|
||||
fakeReconcile.Err = nil
|
||||
Expect(<-reconciled).To(Equal(request))
|
||||
|
||||
By("Removing the item from the queue")
|
||||
Eventually(ctrl.Queue.Len).Should(Equal(0))
|
||||
Eventually(func() int { return ctrl.Queue.NumRequeues(request) }).Should(Equal(0))
|
||||
|
||||
close(done)
|
||||
}, 1.0)
|
||||
|
||||
It("should requeue a Request if the Result sets Requeue:true and continue processing items", func() {
|
||||
fakeReconcile.Result.Requeue = true
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
Expect(ctrl.Start(stop)).NotTo(HaveOccurred())
|
||||
}()
|
||||
dq := &DelegatingQueue{RateLimitingInterface: ctrl.Queue}
|
||||
ctrl.Queue = dq
|
||||
ctrl.Queue.Add(request)
|
||||
Expect(dq.countAdd).To(Equal(1))
|
||||
Expect(dq.countAddAfter).To(Equal(0))
|
||||
Expect(dq.countAddRateLimited).To(Equal(0))
|
||||
|
||||
By("Invoking Reconciler which will ask for requeue")
|
||||
Expect(<-reconciled).To(Equal(request))
|
||||
Expect(dq.countAdd).To(Equal(1))
|
||||
Expect(dq.countAddAfter).To(Equal(0))
|
||||
Expect(dq.countAddRateLimited).To(Equal(1))
|
||||
|
||||
By("Invoking Reconciler a second time without asking for requeue")
|
||||
fakeReconcile.Result.Requeue = false
|
||||
Expect(<-reconciled).To(Equal(request))
|
||||
Expect(dq.countAdd).To(Equal(1))
|
||||
Expect(dq.countAddAfter).To(Equal(0))
|
||||
Expect(dq.countAddRateLimited).To(Equal(1))
|
||||
|
||||
By("Removing the item from the queue")
|
||||
Eventually(ctrl.Queue.Len).Should(Equal(0))
|
||||
Eventually(func() int { return ctrl.Queue.NumRequeues(request) }).Should(Equal(0))
|
||||
})
|
||||
|
||||
It("should requeue a Request after a duration if the Result sets Requeue:true and "+
|
||||
"RequeueAfter is set", func() {
|
||||
fakeReconcile.Result.RequeueAfter = time.Millisecond * 100
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
Expect(ctrl.Start(stop)).NotTo(HaveOccurred())
|
||||
}()
|
||||
dq := &DelegatingQueue{RateLimitingInterface: ctrl.Queue}
|
||||
ctrl.Queue = dq
|
||||
ctrl.Queue.Add(request)
|
||||
Expect(dq.countAdd).To(Equal(1))
|
||||
Expect(dq.countAddAfter).To(Equal(0))
|
||||
Expect(dq.countAddRateLimited).To(Equal(0))
|
||||
|
||||
By("Invoking Reconciler which will ask for requeue")
|
||||
Expect(<-reconciled).To(Equal(request))
|
||||
Expect(dq.countAdd).To(Equal(1))
|
||||
Expect(dq.countAddAfter).To(Equal(1))
|
||||
Expect(dq.countAddRateLimited).To(Equal(0))
|
||||
|
||||
By("Invoking Reconciler a second time without asking for requeue")
|
||||
fakeReconcile.Result.Requeue = false
|
||||
Expect(<-reconciled).To(Equal(request))
|
||||
Expect(dq.countAdd).To(Equal(1))
|
||||
Expect(dq.countAddAfter).To(Equal(1))
|
||||
Expect(dq.countAddRateLimited).To(Equal(0))
|
||||
|
||||
By("Removing the item from the queue")
|
||||
Eventually(ctrl.Queue.Len).Should(Equal(0))
|
||||
Eventually(func() int { return ctrl.Queue.NumRequeues(request) }).Should(Equal(0))
|
||||
})
|
||||
|
||||
It("should forget the Request if Reconciler is successful", func() {
|
||||
// TODO(community): write this test
|
||||
})
|
||||
|
||||
It("should return if the queue is shutdown", func() {
|
||||
// TODO(community): write this test
|
||||
})
|
||||
|
||||
It("should wait for informers to be synced before processing items", func() {
|
||||
// TODO(community): write this test
|
||||
})
|
||||
|
||||
It("should create a new go routine for MaxConcurrentReconciles", func() {
|
||||
// TODO(community): write this test
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
type DelegatingQueue struct {
|
||||
workqueue.RateLimitingInterface
|
||||
|
||||
countAddRateLimited int
|
||||
countAdd int
|
||||
countAddAfter int
|
||||
}
|
||||
|
||||
func (q *DelegatingQueue) AddRateLimited(item interface{}) {
|
||||
q.countAddRateLimited++
|
||||
q.RateLimitingInterface.AddRateLimited(item)
|
||||
}
|
||||
|
||||
func (q *DelegatingQueue) AddAfter(item interface{}, d time.Duration) {
|
||||
q.countAddAfter++
|
||||
q.RateLimitingInterface.AddAfter(item, d)
|
||||
}
|
||||
|
||||
func (q *DelegatingQueue) Add(item interface{}) {
|
||||
q.countAdd++
|
||||
q.RateLimitingInterface.Add(item)
|
||||
}
|
61
vendor/sigs.k8s.io/controller-runtime/pkg/internal/recorder/recorder.go
generated
vendored
Normal file
61
vendor/sigs.k8s.io/controller-runtime/pkg/internal/recorder/recorder.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package recorder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"sigs.k8s.io/controller-runtime/pkg/recorder"
|
||||
)
|
||||
|
||||
type provider struct {
|
||||
// scheme to specify when creating a recorder
|
||||
scheme *runtime.Scheme
|
||||
// eventBroadcaster to create new recorder instance
|
||||
eventBroadcaster record.EventBroadcaster
|
||||
// logger is the logger to use when logging diagnostic event info
|
||||
logger logr.Logger
|
||||
}
|
||||
|
||||
// NewProvider create a new Provider instance.
|
||||
func NewProvider(config *rest.Config, scheme *runtime.Scheme, logger logr.Logger) (recorder.Provider, error) {
|
||||
clientSet, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to init clientSet: %v", err)
|
||||
}
|
||||
|
||||
p := &provider{scheme: scheme, logger: logger}
|
||||
p.eventBroadcaster = record.NewBroadcaster()
|
||||
p.eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: clientSet.CoreV1().Events("")})
|
||||
p.eventBroadcaster.StartEventWatcher(
|
||||
func(e *corev1.Event) {
|
||||
p.logger.V(1).Info(e.Type, "object", e.InvolvedObject, "reason", e.Reason, "message", e.Message)
|
||||
})
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (p *provider) GetEventRecorderFor(name string) record.EventRecorder {
|
||||
return p.eventBroadcaster.NewRecorder(p.scheme, corev1.EventSource{Component: name})
|
||||
}
|
122
vendor/sigs.k8s.io/controller-runtime/pkg/internal/recorder/recorder_integration_test.go
generated
vendored
Normal file
122
vendor/sigs.k8s.io/controller-runtime/pkg/internal/recorder/recorder_integration_test.go
generated
vendored
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
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 recorder_test
|
||||
|
||||
import (
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
ref "k8s.io/client-go/tools/reference"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
"sigs.k8s.io/controller-runtime/pkg/source"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("recorder", func() {
|
||||
var stop chan struct{}
|
||||
|
||||
BeforeEach(func() {
|
||||
stop = make(chan struct{})
|
||||
Expect(cfg).NotTo(BeNil())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
close(stop)
|
||||
})
|
||||
|
||||
Describe("recorder", func() {
|
||||
It("should publish events", func(done Done) {
|
||||
By("Creating the Manager")
|
||||
cm, err := manager.New(cfg, manager.Options{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Creating the Controller")
|
||||
recorder := cm.GetRecorder("test-recorder")
|
||||
instance, err := controller.New("foo-controller", cm, controller.Options{
|
||||
Reconciler: reconcile.Func(
|
||||
func(request reconcile.Request) (reconcile.Result, error) {
|
||||
dp, err := clientset.AppsV1().Deployments(request.Namespace).Get(request.Name, metav1.GetOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
recorder.Event(dp, corev1.EventTypeNormal, "test-reason", "test-msg")
|
||||
return reconcile.Result{}, nil
|
||||
}),
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Watching Resources")
|
||||
err = instance.Watch(&source.Kind{Type: &appsv1.Deployment{}}, &handler.EnqueueRequestForObject{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Starting the Manager")
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
Expect(cm.Start(stop)).NotTo(HaveOccurred())
|
||||
}()
|
||||
|
||||
deployment := &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "deployment-name"},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"foo": "bar"},
|
||||
},
|
||||
Template: corev1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"foo": "bar"}},
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "nginx",
|
||||
Image: "nginx",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
By("Invoking Reconciling")
|
||||
deployment, err = clientset.AppsV1().Deployments("default").Create(deployment)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Validate event is published as expected")
|
||||
evtWatcher, err := clientset.CoreV1().Events("default").Watch(metav1.ListOptions{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
resultEvent := <-evtWatcher.ResultChan()
|
||||
|
||||
Expect(resultEvent.Type).To(Equal(watch.Added))
|
||||
evt, isEvent := resultEvent.Object.(*corev1.Event)
|
||||
Expect(isEvent).To(BeTrue())
|
||||
|
||||
dpRef, err := ref.GetReference(scheme.Scheme, deployment)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(evt.InvolvedObject).To(Equal(*dpRef))
|
||||
Expect(evt.Type).To(Equal(corev1.EventTypeNormal))
|
||||
Expect(evt.Reason).To(Equal("test-reason"))
|
||||
Expect(evt.Message).To(Equal("test-msg"))
|
||||
|
||||
close(done)
|
||||
})
|
||||
})
|
||||
})
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user