├── .github └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── OWNERS ├── README.md ├── SECURITY_CONTACTS ├── artifacts └── examples │ ├── crd-status-subresource.yaml │ ├── crd.yaml │ └── example-foo.yaml ├── code-of-conduct.md ├── controller.go ├── controller_test.go ├── docs ├── controller-client-go.md └── images │ └── client-go-controller-interaction.jpeg ├── go.mod ├── go.sum ├── hack ├── boilerplate.go.txt ├── custom-boilerplate.go.txt ├── tools.go ├── update-codegen.sh └── verify-codegen.sh ├── main.go └── pkg ├── apis └── samplecontroller │ ├── register.go │ └── v1alpha1 │ ├── doc.go │ ├── register.go │ ├── types.go │ └── zz_generated.deepcopy.go ├── generated ├── clientset │ └── versioned │ │ ├── clientset.go │ │ ├── fake │ │ ├── clientset_generated.go │ │ ├── doc.go │ │ └── register.go │ │ ├── scheme │ │ ├── doc.go │ │ └── register.go │ │ └── typed │ │ └── samplecontroller │ │ └── v1alpha1 │ │ ├── doc.go │ │ ├── fake │ │ ├── doc.go │ │ ├── fake_foo.go │ │ └── fake_samplecontroller_client.go │ │ ├── foo.go │ │ ├── generated_expansion.go │ │ └── samplecontroller_client.go ├── informers │ └── externalversions │ │ ├── factory.go │ │ ├── generic.go │ │ ├── internalinterfaces │ │ └── factory_interfaces.go │ │ └── samplecontroller │ │ ├── interface.go │ │ └── v1alpha1 │ │ ├── foo.go │ │ └── interface.go └── listers │ └── samplecontroller │ └── v1alpha1 │ ├── expansion_generated.go │ └── foo.go └── signals ├── signal.go ├── signal_posix.go └── signal_windows.go /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Sorry, we do not accept changes directly against this repository. Please see 2 | CONTRIBUTING.md for information on where and how to contribute instead. 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | sample-controller 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing guidelines 2 | 3 | Do not open pull requests directly against this repository, they will be ignored. Instead, please open pull requests against [kubernetes/kubernetes](https://git.k8s.io/kubernetes/). Please follow the same [contributing guide](https://git.k8s.io/kubernetes/CONTRIBUTING.md) you would follow for any other pull request made to kubernetes/kubernetes. 4 | 5 | This repository is published from [kubernetes/kubernetes/staging/src/k8s.io/sample-controller](https://git.k8s.io/kubernetes/staging/src/k8s.io/sample-controller) by the [kubernetes publishing-bot](https://git.k8s.io/publishing-bot). 6 | 7 | Please see [Staging Directory and Publishing](https://git.k8s.io/community/contributors/devel/sig-architecture/staging.md) for more information 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /OWNERS: -------------------------------------------------------------------------------- 1 | # See the OWNERS docs at https://go.k8s.io/owners 2 | 3 | approvers: 4 | - deads2k 5 | - jpbetz 6 | - sttts 7 | - munnerz 8 | reviewers: 9 | - deads2k 10 | - sttts 11 | - munnerz 12 | - nikhita 13 | labels: 14 | - sig/api-machinery 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sample-controller 2 | 3 | This repository implements a simple controller for watching Foo resources as 4 | defined with a CustomResourceDefinition (CRD). 5 | 6 | **Note:** go-get or vendor this package as `k8s.io/sample-controller`. 7 | 8 | This particular example demonstrates how to perform basic operations such as: 9 | 10 | * How to register a new custom resource (custom resource type) of type `Foo` using a CustomResourceDefinition. 11 | * How to create/get/list instances of your new resource type `Foo`. 12 | * How to setup a controller on resource handling create/update/delete events. 13 | 14 | It makes use of the generators in [k8s.io/code-generator](https://github.com/kubernetes/code-generator) 15 | to generate a typed client, informers, listers and deep-copy functions. You can 16 | do this yourself using the `./hack/update-codegen.sh` script. 17 | 18 | The `update-codegen` script will automatically generate the following files & 19 | directories: 20 | 21 | * `pkg/apis/samplecontroller/v1alpha1/zz_generated.deepcopy.go` 22 | * `pkg/generated/` 23 | 24 | Changes should not be made to these files manually, and when creating your own 25 | controller based off of this implementation you should not copy these files and 26 | instead run the `update-codegen` script to generate your own. 27 | 28 | ## Details 29 | 30 | The sample controller uses [client-go library](https://github.com/kubernetes/client-go/tree/master/tools/cache) extensively. 31 | The details of interaction points of the sample controller with various mechanisms from this library are 32 | explained [here](docs/controller-client-go.md). 33 | 34 | ## Fetch sample-controller and its dependencies 35 | 36 | Issue the following commands --- starting in whatever working directory you 37 | like. 38 | 39 | ```sh 40 | git clone https://github.com/kubernetes/sample-controller 41 | cd sample-controller 42 | ``` 43 | 44 | Note, however, that if you intend to 45 | generate code then you will also need the 46 | code-generator repo to exist in an old-style location. One easy way 47 | to do this is to use the command `go mod vendor` to create and 48 | populate the `vendor` directory. 49 | 50 | ### A Note on kubernetes/kubernetes 51 | 52 | If you are developing Kubernetes according to 53 | https://github.com/kubernetes/community/blob/master/contributors/guide/github-workflow.md 54 | then you already have a copy of this demo in 55 | `kubernetes/staging/src/k8s.io/sample-controller` and its dependencies 56 | --- including the code generator --- are in usable locations 57 | (valid for all Go versions). 58 | 59 | ## Purpose 60 | 61 | This is an example of how to build a kube-like controller with a single type. 62 | 63 | ## Running 64 | 65 | **Prerequisite**: Since the sample-controller uses `apps/v1` deployments, the Kubernetes cluster version should be greater than 1.9. 66 | 67 | ```sh 68 | # assumes you have a working kubeconfig, not required if operating in-cluster 69 | go build -o sample-controller . 70 | ./sample-controller -kubeconfig=$HOME/.kube/config 71 | 72 | # create a CustomResourceDefinition 73 | kubectl create -f artifacts/examples/crd-status-subresource.yaml 74 | 75 | # create a custom resource of type Foo 76 | kubectl create -f artifacts/examples/example-foo.yaml 77 | 78 | # check deployments created through the custom resource 79 | kubectl get deployments 80 | ``` 81 | 82 | ## Use Cases 83 | 84 | CustomResourceDefinitions can be used to implement custom resource types for your Kubernetes cluster. 85 | These act like most other Resources in Kubernetes, and may be `kubectl apply`'d, etc. 86 | 87 | Some example use cases: 88 | 89 | * Provisioning/Management of external datastores/databases (eg. CloudSQL/RDS instances) 90 | * Higher level abstractions around Kubernetes primitives (eg. a single Resource to define an etcd cluster, backed by a Service and a ReplicationController) 91 | 92 | ## Defining types 93 | 94 | Each instance of your custom resource has an attached Spec, which should be defined via a `struct{}` to provide data format validation. 95 | In practice, this Spec is arbitrary key-value data that specifies the configuration/behavior of your Resource. 96 | 97 | For example, if you were implementing a custom resource for a Database, you might provide a DatabaseSpec like the following: 98 | 99 | ``` go 100 | type DatabaseSpec struct { 101 | Databases []string `json:"databases"` 102 | Users []User `json:"users"` 103 | Version string `json:"version"` 104 | } 105 | 106 | type User struct { 107 | Name string `json:"name"` 108 | Password string `json:"password"` 109 | } 110 | ``` 111 | 112 | Note, the JSON tag `json:` is required on all user facing fields within your type. Typically API types contain only user facing fields. When the JSON tag is omitted from the field, Kubernetes generators consider the field to be internal and will not expose the field in their generated external output. For example, this means that the field would not be included in a generated CRD schema. 113 | 114 | ## Validation 115 | 116 | To validate custom resources, use the [`CustomResourceValidation`](https://kubernetes.io/docs/tasks/access-kubernetes-api/extend-api-custom-resource-definitions/#validation) feature. Validation in the form of a [structured schema](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#specifying-a-structural-schema) is mandatory to be provided for `apiextensions.k8s.io/v1`. 117 | 118 | ### Example 119 | 120 | The schema in [`crd.yaml`](./artifacts/examples/crd.yaml) applies the following validation on the custom resource: 121 | `spec.replicas` must be an integer and must have a minimum value of 1 and a maximum value of 10. 122 | 123 | ## Subresources 124 | 125 | Custom Resources support `/status` and `/scale` [subresources](https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/#subresources). The `CustomResourceSubresources` feature is in GA from v1.16. 126 | 127 | ### Example 128 | 129 | The CRD in [`crd-status-subresource.yaml`](./artifacts/examples/crd-status-subresource.yaml) enables the `/status` subresource for custom resources. 130 | This means that [`UpdateStatus`](./controller.go) can be used by the controller to update only the status part of the custom resource. 131 | 132 | To understand why only the status part of the custom resource should be updated, please refer to the [Kubernetes API conventions](https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status). 133 | 134 | In the above steps, use `crd-status-subresource.yaml` to create the CRD: 135 | 136 | ```sh 137 | # create a CustomResourceDefinition supporting the status subresource 138 | kubectl create -f artifacts/examples/crd-status-subresource.yaml 139 | ``` 140 | 141 | ## A Note on the API version 142 | The [group](https://kubernetes.io/docs/reference/using-api/#api-groups) version of the custom resource in `crd.yaml` is `v1alpha`, this can be evolved to a stable API version, `v1`, using [CRD Versioning](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/). 143 | 144 | ## Cleanup 145 | 146 | You can clean up the created CustomResourceDefinition with: 147 | ```sh 148 | kubectl delete crd foos.samplecontroller.k8s.io 149 | ``` 150 | 151 | ## Compatibility 152 | 153 | HEAD of this repository will match HEAD of k8s.io/apimachinery and 154 | k8s.io/client-go. 155 | 156 | ## Where does it come from? 157 | 158 | `sample-controller` is synced from 159 | https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/sample-controller. 160 | Code changes are made in that location, merged into k8s.io/kubernetes and 161 | later synced here. 162 | 163 | -------------------------------------------------------------------------------- /SECURITY_CONTACTS: -------------------------------------------------------------------------------- 1 | # Defined below are the security contacts for this repo. 2 | # 3 | # They are the contact point for the Product Security Committee to reach out 4 | # to for triaging and handling of incoming issues. 5 | # 6 | # The below names agree to abide by the 7 | # [Embargo Policy](https://git.k8s.io/security/private-distributors-list.md#embargo-policy) 8 | # and will be removed and replaced if they violate that agreement. 9 | # 10 | # DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE 11 | # INSTRUCTIONS AT https://kubernetes.io/security/ 12 | 13 | cheftako 14 | deads2k 15 | lavalamp 16 | sttts 17 | -------------------------------------------------------------------------------- /artifacts/examples/crd-status-subresource.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: foos.samplecontroller.k8s.io 5 | # for more information on the below annotation, please see 6 | # https://github.com/kubernetes/enhancements/blob/master/keps/sig-api-machinery/2337-k8s.io-group-protection/README.md 7 | annotations: 8 | "api-approved.kubernetes.io": "unapproved, experimental-only; please get an approval from Kubernetes API reviewers if you're trying to develop a CRD in the *.k8s.io or *.kubernetes.io groups" 9 | spec: 10 | group: samplecontroller.k8s.io 11 | versions: 12 | - name: v1alpha1 13 | served: true 14 | storage: true 15 | schema: 16 | # schema used for validation 17 | openAPIV3Schema: 18 | type: object 19 | properties: 20 | spec: 21 | type: object 22 | properties: 23 | deploymentName: 24 | type: string 25 | replicas: 26 | type: integer 27 | minimum: 1 28 | maximum: 10 29 | status: 30 | type: object 31 | properties: 32 | availableReplicas: 33 | type: integer 34 | # subresources for the custom resource 35 | subresources: 36 | # enables the status subresource 37 | status: {} 38 | names: 39 | kind: Foo 40 | plural: foos 41 | scope: Namespaced 42 | -------------------------------------------------------------------------------- /artifacts/examples/crd.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: foos.samplecontroller.k8s.io 5 | # for more information on the below annotation, please see 6 | # https://github.com/kubernetes/enhancements/blob/master/keps/sig-api-machinery/2337-k8s.io-group-protection/README.md 7 | annotations: 8 | "api-approved.kubernetes.io": "unapproved, experimental-only; please get an approval from Kubernetes API reviewers if you're trying to develop a CRD in the *.k8s.io or *.kubernetes.io groups" 9 | spec: 10 | group: samplecontroller.k8s.io 11 | versions: 12 | - name: v1alpha1 13 | served: true 14 | storage: true 15 | schema: 16 | # schema used for validation 17 | openAPIV3Schema: 18 | type: object 19 | properties: 20 | spec: 21 | type: object 22 | properties: 23 | deploymentName: 24 | type: string 25 | replicas: 26 | type: integer 27 | minimum: 1 28 | maximum: 10 29 | status: 30 | type: object 31 | properties: 32 | availableReplicas: 33 | type: integer 34 | names: 35 | kind: Foo 36 | plural: foos 37 | scope: Namespaced 38 | -------------------------------------------------------------------------------- /artifacts/examples/example-foo.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: samplecontroller.k8s.io/v1alpha1 2 | kind: Foo 3 | metadata: 4 | name: example-foo 5 | spec: 6 | deploymentName: example-foo 7 | replicas: 1 8 | -------------------------------------------------------------------------------- /code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # Kubernetes Community Code of Conduct 2 | 3 | Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md) 4 | -------------------------------------------------------------------------------- /controller.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | "time" 23 | 24 | "golang.org/x/time/rate" 25 | 26 | appsv1 "k8s.io/api/apps/v1" 27 | corev1 "k8s.io/api/core/v1" 28 | "k8s.io/apimachinery/pkg/api/errors" 29 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 31 | "k8s.io/apimachinery/pkg/util/wait" 32 | appsinformers "k8s.io/client-go/informers/apps/v1" 33 | "k8s.io/client-go/kubernetes" 34 | "k8s.io/client-go/kubernetes/scheme" 35 | typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" 36 | appslisters "k8s.io/client-go/listers/apps/v1" 37 | "k8s.io/client-go/tools/cache" 38 | "k8s.io/client-go/tools/record" 39 | "k8s.io/client-go/util/workqueue" 40 | "k8s.io/klog/v2" 41 | 42 | samplev1alpha1 "k8s.io/sample-controller/pkg/apis/samplecontroller/v1alpha1" 43 | clientset "k8s.io/sample-controller/pkg/generated/clientset/versioned" 44 | samplescheme "k8s.io/sample-controller/pkg/generated/clientset/versioned/scheme" 45 | informers "k8s.io/sample-controller/pkg/generated/informers/externalversions/samplecontroller/v1alpha1" 46 | listers "k8s.io/sample-controller/pkg/generated/listers/samplecontroller/v1alpha1" 47 | ) 48 | 49 | const controllerAgentName = "sample-controller" 50 | 51 | const ( 52 | // SuccessSynced is used as part of the Event 'reason' when a Foo is synced 53 | SuccessSynced = "Synced" 54 | // ErrResourceExists is used as part of the Event 'reason' when a Foo fails 55 | // to sync due to a Deployment of the same name already existing. 56 | ErrResourceExists = "ErrResourceExists" 57 | 58 | // MessageResourceExists is the message used for Events when a resource 59 | // fails to sync due to a Deployment already existing 60 | MessageResourceExists = "Resource %q already exists and is not managed by Foo" 61 | // MessageResourceSynced is the message used for an Event fired when a Foo 62 | // is synced successfully 63 | MessageResourceSynced = "Foo synced successfully" 64 | // FieldManager distinguishes this controller from other things writing to API objects 65 | FieldManager = controllerAgentName 66 | ) 67 | 68 | // Controller is the controller implementation for Foo resources 69 | type Controller struct { 70 | // kubeclientset is a standard kubernetes clientset 71 | kubeclientset kubernetes.Interface 72 | // sampleclientset is a clientset for our own API group 73 | sampleclientset clientset.Interface 74 | 75 | deploymentsLister appslisters.DeploymentLister 76 | deploymentsSynced cache.InformerSynced 77 | foosLister listers.FooLister 78 | foosSynced cache.InformerSynced 79 | 80 | // workqueue is a rate limited work queue. This is used to queue work to be 81 | // processed instead of performing it as soon as a change happens. This 82 | // means we can ensure we only process a fixed amount of resources at a 83 | // time, and makes it easy to ensure we are never processing the same item 84 | // simultaneously in two different workers. 85 | workqueue workqueue.TypedRateLimitingInterface[cache.ObjectName] 86 | // recorder is an event recorder for recording Event resources to the 87 | // Kubernetes API. 88 | recorder record.EventRecorder 89 | } 90 | 91 | // NewController returns a new sample controller 92 | func NewController( 93 | ctx context.Context, 94 | kubeclientset kubernetes.Interface, 95 | sampleclientset clientset.Interface, 96 | deploymentInformer appsinformers.DeploymentInformer, 97 | fooInformer informers.FooInformer) *Controller { 98 | logger := klog.FromContext(ctx) 99 | 100 | // Create event broadcaster 101 | // Add sample-controller types to the default Kubernetes Scheme so Events can be 102 | // logged for sample-controller types. 103 | utilruntime.Must(samplescheme.AddToScheme(scheme.Scheme)) 104 | logger.V(4).Info("Creating event broadcaster") 105 | 106 | eventBroadcaster := record.NewBroadcaster(record.WithContext(ctx)) 107 | eventBroadcaster.StartStructuredLogging(0) 108 | eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: kubeclientset.CoreV1().Events("")}) 109 | recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerAgentName}) 110 | ratelimiter := workqueue.NewTypedMaxOfRateLimiter( 111 | workqueue.NewTypedItemExponentialFailureRateLimiter[cache.ObjectName](5*time.Millisecond, 1000*time.Second), 112 | &workqueue.TypedBucketRateLimiter[cache.ObjectName]{Limiter: rate.NewLimiter(rate.Limit(50), 300)}, 113 | ) 114 | 115 | controller := &Controller{ 116 | kubeclientset: kubeclientset, 117 | sampleclientset: sampleclientset, 118 | deploymentsLister: deploymentInformer.Lister(), 119 | deploymentsSynced: deploymentInformer.Informer().HasSynced, 120 | foosLister: fooInformer.Lister(), 121 | foosSynced: fooInformer.Informer().HasSynced, 122 | workqueue: workqueue.NewTypedRateLimitingQueue(ratelimiter), 123 | recorder: recorder, 124 | } 125 | 126 | logger.Info("Setting up event handlers") 127 | // Set up an event handler for when Foo resources change 128 | fooInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ 129 | AddFunc: controller.enqueueFoo, 130 | UpdateFunc: func(old, new interface{}) { 131 | controller.enqueueFoo(new) 132 | }, 133 | }) 134 | // Set up an event handler for when Deployment resources change. This 135 | // handler will lookup the owner of the given Deployment, and if it is 136 | // owned by a Foo resource then the handler will enqueue that Foo resource for 137 | // processing. This way, we don't need to implement custom logic for 138 | // handling Deployment resources. More info on this pattern: 139 | // https://github.com/kubernetes/community/blob/8cafef897a22026d42f5e5bb3f104febe7e29830/contributors/devel/controllers.md 140 | deploymentInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ 141 | AddFunc: controller.handleObject, 142 | UpdateFunc: func(old, new interface{}) { 143 | newDepl := new.(*appsv1.Deployment) 144 | oldDepl := old.(*appsv1.Deployment) 145 | if newDepl.ResourceVersion == oldDepl.ResourceVersion { 146 | // Periodic resync will send update events for all known Deployments. 147 | // Two different versions of the same Deployment will always have different RVs. 148 | return 149 | } 150 | controller.handleObject(new) 151 | }, 152 | DeleteFunc: controller.handleObject, 153 | }) 154 | 155 | return controller 156 | } 157 | 158 | // Run will set up the event handlers for types we are interested in, as well 159 | // as syncing informer caches and starting workers. It will block until stopCh 160 | // is closed, at which point it will shutdown the workqueue and wait for 161 | // workers to finish processing their current work items. 162 | func (c *Controller) Run(ctx context.Context, workers int) error { 163 | defer utilruntime.HandleCrash() 164 | defer c.workqueue.ShutDown() 165 | logger := klog.FromContext(ctx) 166 | 167 | // Start the informer factories to begin populating the informer caches 168 | logger.Info("Starting Foo controller") 169 | 170 | // Wait for the caches to be synced before starting workers 171 | logger.Info("Waiting for informer caches to sync") 172 | 173 | if ok := cache.WaitForCacheSync(ctx.Done(), c.deploymentsSynced, c.foosSynced); !ok { 174 | return fmt.Errorf("failed to wait for caches to sync") 175 | } 176 | 177 | logger.Info("Starting workers", "count", workers) 178 | // Launch two workers to process Foo resources 179 | for i := 0; i < workers; i++ { 180 | go wait.UntilWithContext(ctx, c.runWorker, time.Second) 181 | } 182 | 183 | logger.Info("Started workers") 184 | <-ctx.Done() 185 | logger.Info("Shutting down workers") 186 | 187 | return nil 188 | } 189 | 190 | // runWorker is a long-running function that will continually call the 191 | // processNextWorkItem function in order to read and process a message on the 192 | // workqueue. 193 | func (c *Controller) runWorker(ctx context.Context) { 194 | for c.processNextWorkItem(ctx) { 195 | } 196 | } 197 | 198 | // processNextWorkItem will read a single work item off the workqueue and 199 | // attempt to process it, by calling the syncHandler. 200 | func (c *Controller) processNextWorkItem(ctx context.Context) bool { 201 | objRef, shutdown := c.workqueue.Get() 202 | logger := klog.FromContext(ctx) 203 | 204 | if shutdown { 205 | return false 206 | } 207 | 208 | // We call Done at the end of this func so the workqueue knows we have 209 | // finished processing this item. We also must remember to call Forget 210 | // if we do not want this work item being re-queued. For example, we do 211 | // not call Forget if a transient error occurs, instead the item is 212 | // put back on the workqueue and attempted again after a back-off 213 | // period. 214 | defer c.workqueue.Done(objRef) 215 | 216 | // Run the syncHandler, passing it the structured reference to the object to be synced. 217 | err := c.syncHandler(ctx, objRef) 218 | if err == nil { 219 | // If no error occurs then we Forget this item so it does not 220 | // get queued again until another change happens. 221 | c.workqueue.Forget(objRef) 222 | logger.Info("Successfully synced", "objectName", objRef) 223 | return true 224 | } 225 | // there was a failure so be sure to report it. This method allows for 226 | // pluggable error handling which can be used for things like 227 | // cluster-monitoring. 228 | utilruntime.HandleErrorWithContext(ctx, err, "Error syncing; requeuing for later retry", "objectReference", objRef) 229 | // since we failed, we should requeue the item to work on later. This 230 | // method will add a backoff to avoid hotlooping on particular items 231 | // (they're probably still not going to work right away) and overall 232 | // controller protection (everything I've done is broken, this controller 233 | // needs to calm down or it can starve other useful work) cases. 234 | c.workqueue.AddRateLimited(objRef) 235 | return true 236 | } 237 | 238 | // syncHandler compares the actual state with the desired, and attempts to 239 | // converge the two. It then updates the Status block of the Foo resource 240 | // with the current status of the resource. 241 | func (c *Controller) syncHandler(ctx context.Context, objectRef cache.ObjectName) error { 242 | logger := klog.LoggerWithValues(klog.FromContext(ctx), "objectRef", objectRef) 243 | 244 | // Get the Foo resource with this namespace/name 245 | foo, err := c.foosLister.Foos(objectRef.Namespace).Get(objectRef.Name) 246 | if err != nil { 247 | // The Foo resource may no longer exist, in which case we stop 248 | // processing. 249 | if errors.IsNotFound(err) { 250 | utilruntime.HandleErrorWithContext(ctx, err, "Foo referenced by item in work queue no longer exists", "objectReference", objectRef) 251 | return nil 252 | } 253 | 254 | return err 255 | } 256 | 257 | deploymentName := foo.Spec.DeploymentName 258 | if deploymentName == "" { 259 | // We choose to absorb the error here as the worker would requeue the 260 | // resource otherwise. Instead, the next time the resource is updated 261 | // the resource will be queued again. 262 | utilruntime.HandleErrorWithContext(ctx, nil, "Deployment name missing from object reference", "objectReference", objectRef) 263 | return nil 264 | } 265 | 266 | // Get the deployment with the name specified in Foo.spec 267 | deployment, err := c.deploymentsLister.Deployments(foo.Namespace).Get(deploymentName) 268 | // If the resource doesn't exist, we'll create it 269 | if errors.IsNotFound(err) { 270 | deployment, err = c.kubeclientset.AppsV1().Deployments(foo.Namespace).Create(ctx, newDeployment(foo), metav1.CreateOptions{FieldManager: FieldManager}) 271 | } 272 | 273 | // If an error occurs during Get/Create, we'll requeue the item so we can 274 | // attempt processing again later. This could have been caused by a 275 | // temporary network failure, or any other transient reason. 276 | if err != nil { 277 | return err 278 | } 279 | 280 | // If the Deployment is not controlled by this Foo resource, we should log 281 | // a warning to the event recorder and return error msg. 282 | if !metav1.IsControlledBy(deployment, foo) { 283 | msg := fmt.Sprintf(MessageResourceExists, deployment.Name) 284 | c.recorder.Event(foo, corev1.EventTypeWarning, ErrResourceExists, msg) 285 | return fmt.Errorf("%s", msg) 286 | } 287 | 288 | // If this number of the replicas on the Foo resource is specified, and the 289 | // number does not equal the current desired replicas on the Deployment, we 290 | // should update the Deployment resource. 291 | if foo.Spec.Replicas != nil && *foo.Spec.Replicas != *deployment.Spec.Replicas { 292 | logger.V(4).Info("Update deployment resource", "currentReplicas", *deployment.Spec.Replicas, "desiredReplicas", *foo.Spec.Replicas) 293 | deployment, err = c.kubeclientset.AppsV1().Deployments(foo.Namespace).Update(ctx, newDeployment(foo), metav1.UpdateOptions{FieldManager: FieldManager}) 294 | } 295 | 296 | // If an error occurs during Update, we'll requeue the item so we can 297 | // attempt processing again later. This could have been caused by a 298 | // temporary network failure, or any other transient reason. 299 | if err != nil { 300 | return err 301 | } 302 | 303 | // Finally, we update the status block of the Foo resource to reflect the 304 | // current state of the world 305 | err = c.updateFooStatus(ctx, foo, deployment) 306 | if err != nil { 307 | return err 308 | } 309 | 310 | c.recorder.Event(foo, corev1.EventTypeNormal, SuccessSynced, MessageResourceSynced) 311 | return nil 312 | } 313 | 314 | func (c *Controller) updateFooStatus(ctx context.Context, foo *samplev1alpha1.Foo, deployment *appsv1.Deployment) error { 315 | // NEVER modify objects from the store. It's a read-only, local cache. 316 | // You can use DeepCopy() to make a deep copy of original object and modify this copy 317 | // Or create a copy manually for better performance 318 | fooCopy := foo.DeepCopy() 319 | fooCopy.Status.AvailableReplicas = deployment.Status.AvailableReplicas 320 | // If the CustomResourceSubresources feature gate is not enabled, 321 | // we must use Update instead of UpdateStatus to update the Status block of the Foo resource. 322 | // UpdateStatus will not allow changes to the Spec of the resource, 323 | // which is ideal for ensuring nothing other than resource status has been updated. 324 | _, err := c.sampleclientset.SamplecontrollerV1alpha1().Foos(foo.Namespace).UpdateStatus(ctx, fooCopy, metav1.UpdateOptions{FieldManager: FieldManager}) 325 | return err 326 | } 327 | 328 | // enqueueFoo takes a Foo resource and converts it into a namespace/name 329 | // string which is then put onto the work queue. This method should *not* be 330 | // passed resources of any type other than Foo. 331 | func (c *Controller) enqueueFoo(obj interface{}) { 332 | if objectRef, err := cache.ObjectToName(obj); err != nil { 333 | utilruntime.HandleError(err) 334 | return 335 | } else { 336 | c.workqueue.Add(objectRef) 337 | } 338 | } 339 | 340 | // handleObject will take any resource implementing metav1.Object and attempt 341 | // to find the Foo resource that 'owns' it. It does this by looking at the 342 | // objects metadata.ownerReferences field for an appropriate OwnerReference. 343 | // It then enqueues that Foo resource to be processed. If the object does not 344 | // have an appropriate OwnerReference, it will simply be skipped. 345 | func (c *Controller) handleObject(obj interface{}) { 346 | var object metav1.Object 347 | var ok bool 348 | logger := klog.FromContext(context.Background()) 349 | if object, ok = obj.(metav1.Object); !ok { 350 | tombstone, ok := obj.(cache.DeletedFinalStateUnknown) 351 | if !ok { 352 | // If the object value is not too big and does not contain sensitive information then 353 | // it may be useful to include it. 354 | utilruntime.HandleErrorWithContext(context.Background(), nil, "Error decoding object, invalid type", "type", fmt.Sprintf("%T", obj)) 355 | return 356 | } 357 | object, ok = tombstone.Obj.(metav1.Object) 358 | if !ok { 359 | // If the object value is not too big and does not contain sensitive information then 360 | // it may be useful to include it. 361 | utilruntime.HandleErrorWithContext(context.Background(), nil, "Error decoding object tombstone, invalid type", "type", fmt.Sprintf("%T", tombstone.Obj)) 362 | return 363 | } 364 | logger.V(4).Info("Recovered deleted object", "resourceName", object.GetName()) 365 | } 366 | logger.V(4).Info("Processing object", "object", klog.KObj(object)) 367 | if ownerRef := metav1.GetControllerOf(object); ownerRef != nil { 368 | // If this object is not owned by a Foo, we should not do anything more 369 | // with it. 370 | if ownerRef.Kind != "Foo" { 371 | return 372 | } 373 | 374 | foo, err := c.foosLister.Foos(object.GetNamespace()).Get(ownerRef.Name) 375 | if err != nil { 376 | logger.V(4).Info("Ignore orphaned object", "object", klog.KObj(object), "foo", ownerRef.Name) 377 | return 378 | } 379 | 380 | c.enqueueFoo(foo) 381 | return 382 | } 383 | } 384 | 385 | // newDeployment creates a new Deployment for a Foo resource. It also sets 386 | // the appropriate OwnerReferences on the resource so handleObject can discover 387 | // the Foo resource that 'owns' it. 388 | func newDeployment(foo *samplev1alpha1.Foo) *appsv1.Deployment { 389 | labels := map[string]string{ 390 | "app": "nginx", 391 | "controller": foo.Name, 392 | } 393 | return &appsv1.Deployment{ 394 | ObjectMeta: metav1.ObjectMeta{ 395 | Name: foo.Spec.DeploymentName, 396 | Namespace: foo.Namespace, 397 | OwnerReferences: []metav1.OwnerReference{ 398 | *metav1.NewControllerRef(foo, samplev1alpha1.SchemeGroupVersion.WithKind("Foo")), 399 | }, 400 | }, 401 | Spec: appsv1.DeploymentSpec{ 402 | Replicas: foo.Spec.Replicas, 403 | Selector: &metav1.LabelSelector{ 404 | MatchLabels: labels, 405 | }, 406 | Template: corev1.PodTemplateSpec{ 407 | ObjectMeta: metav1.ObjectMeta{ 408 | Labels: labels, 409 | }, 410 | Spec: corev1.PodSpec{ 411 | Containers: []corev1.Container{ 412 | { 413 | Name: "nginx", 414 | Image: "nginx:latest", 415 | }, 416 | }, 417 | }, 418 | }, 419 | }, 420 | } 421 | } 422 | -------------------------------------------------------------------------------- /controller_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | "reflect" 23 | "testing" 24 | "time" 25 | 26 | apps "k8s.io/api/apps/v1" 27 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 | "k8s.io/apimachinery/pkg/runtime" 29 | "k8s.io/apimachinery/pkg/runtime/schema" 30 | "k8s.io/apimachinery/pkg/util/diff" 31 | kubeinformers "k8s.io/client-go/informers" 32 | k8sfake "k8s.io/client-go/kubernetes/fake" 33 | core "k8s.io/client-go/testing" 34 | "k8s.io/client-go/tools/cache" 35 | "k8s.io/client-go/tools/record" 36 | "k8s.io/klog/v2/ktesting" 37 | 38 | samplecontroller "k8s.io/sample-controller/pkg/apis/samplecontroller/v1alpha1" 39 | "k8s.io/sample-controller/pkg/generated/clientset/versioned/fake" 40 | informers "k8s.io/sample-controller/pkg/generated/informers/externalversions" 41 | ) 42 | 43 | var ( 44 | alwaysReady = func() bool { return true } 45 | noResyncPeriodFunc = func() time.Duration { return 0 } 46 | ) 47 | 48 | type fixture struct { 49 | t *testing.T 50 | 51 | client *fake.Clientset 52 | kubeclient *k8sfake.Clientset 53 | // Objects to put in the store. 54 | fooLister []*samplecontroller.Foo 55 | deploymentLister []*apps.Deployment 56 | // Actions expected to happen on the client. 57 | kubeactions []core.Action 58 | actions []core.Action 59 | // Objects from here preloaded into NewSimpleFake. 60 | kubeobjects []runtime.Object 61 | objects []runtime.Object 62 | } 63 | 64 | func newFixture(t *testing.T) *fixture { 65 | f := &fixture{} 66 | f.t = t 67 | f.objects = []runtime.Object{} 68 | f.kubeobjects = []runtime.Object{} 69 | return f 70 | } 71 | 72 | func newFoo(name string, replicas *int32) *samplecontroller.Foo { 73 | return &samplecontroller.Foo{ 74 | TypeMeta: metav1.TypeMeta{APIVersion: samplecontroller.SchemeGroupVersion.String()}, 75 | ObjectMeta: metav1.ObjectMeta{ 76 | Name: name, 77 | Namespace: metav1.NamespaceDefault, 78 | }, 79 | Spec: samplecontroller.FooSpec{ 80 | DeploymentName: fmt.Sprintf("%s-deployment", name), 81 | Replicas: replicas, 82 | }, 83 | } 84 | } 85 | 86 | func (f *fixture) newController(ctx context.Context) (*Controller, informers.SharedInformerFactory, kubeinformers.SharedInformerFactory) { 87 | f.client = fake.NewSimpleClientset(f.objects...) 88 | f.kubeclient = k8sfake.NewSimpleClientset(f.kubeobjects...) 89 | 90 | i := informers.NewSharedInformerFactory(f.client, noResyncPeriodFunc()) 91 | k8sI := kubeinformers.NewSharedInformerFactory(f.kubeclient, noResyncPeriodFunc()) 92 | 93 | c := NewController(ctx, f.kubeclient, f.client, 94 | k8sI.Apps().V1().Deployments(), i.Samplecontroller().V1alpha1().Foos()) 95 | 96 | c.foosSynced = alwaysReady 97 | c.deploymentsSynced = alwaysReady 98 | c.recorder = &record.FakeRecorder{} 99 | 100 | for _, f := range f.fooLister { 101 | i.Samplecontroller().V1alpha1().Foos().Informer().GetIndexer().Add(f) 102 | } 103 | 104 | for _, d := range f.deploymentLister { 105 | k8sI.Apps().V1().Deployments().Informer().GetIndexer().Add(d) 106 | } 107 | 108 | return c, i, k8sI 109 | } 110 | 111 | func (f *fixture) run(ctx context.Context, fooRef cache.ObjectName) { 112 | f.runController(ctx, fooRef, true, false) 113 | } 114 | 115 | func (f *fixture) runExpectError(ctx context.Context, fooRef cache.ObjectName) { 116 | f.runController(ctx, fooRef, true, true) 117 | } 118 | 119 | func (f *fixture) runController(ctx context.Context, fooRef cache.ObjectName, startInformers bool, expectError bool) { 120 | c, i, k8sI := f.newController(ctx) 121 | if startInformers { 122 | i.Start(ctx.Done()) 123 | k8sI.Start(ctx.Done()) 124 | } 125 | 126 | err := c.syncHandler(ctx, fooRef) 127 | if !expectError && err != nil { 128 | f.t.Errorf("error syncing foo: %v", err) 129 | } else if expectError && err == nil { 130 | f.t.Error("expected error syncing foo, got nil") 131 | } 132 | 133 | actions := filterInformerActions(f.client.Actions()) 134 | for i, action := range actions { 135 | if len(f.actions) < i+1 { 136 | f.t.Errorf("%d unexpected actions: %+v", len(actions)-len(f.actions), actions[i:]) 137 | break 138 | } 139 | 140 | expectedAction := f.actions[i] 141 | checkAction(expectedAction, action, f.t) 142 | } 143 | 144 | if len(f.actions) > len(actions) { 145 | f.t.Errorf("%d additional expected actions:%+v", len(f.actions)-len(actions), f.actions[len(actions):]) 146 | } 147 | 148 | k8sActions := filterInformerActions(f.kubeclient.Actions()) 149 | for i, action := range k8sActions { 150 | if len(f.kubeactions) < i+1 { 151 | f.t.Errorf("%d unexpected actions: %+v", len(k8sActions)-len(f.kubeactions), k8sActions[i:]) 152 | break 153 | } 154 | 155 | expectedAction := f.kubeactions[i] 156 | checkAction(expectedAction, action, f.t) 157 | } 158 | 159 | if len(f.kubeactions) > len(k8sActions) { 160 | f.t.Errorf("%d additional expected actions:%+v", len(f.kubeactions)-len(k8sActions), f.kubeactions[len(k8sActions):]) 161 | } 162 | } 163 | 164 | // checkAction verifies that expected and actual actions are equal and both have 165 | // same attached resources 166 | func checkAction(expected, actual core.Action, t *testing.T) { 167 | if !(expected.Matches(actual.GetVerb(), actual.GetResource().Resource) && actual.GetSubresource() == expected.GetSubresource()) { 168 | t.Errorf("Expected\n\t%#v\ngot\n\t%#v", expected, actual) 169 | return 170 | } 171 | 172 | if reflect.TypeOf(actual) != reflect.TypeOf(expected) { 173 | t.Errorf("Action has wrong type. Expected: %t. Got: %t", expected, actual) 174 | return 175 | } 176 | 177 | switch a := actual.(type) { 178 | case core.CreateActionImpl: 179 | e, _ := expected.(core.CreateActionImpl) 180 | expObject := e.GetObject() 181 | object := a.GetObject() 182 | 183 | if !reflect.DeepEqual(expObject, object) { 184 | t.Errorf("Action %s %s has wrong object\nDiff:\n %s", 185 | a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expObject, object)) 186 | } 187 | case core.UpdateActionImpl: 188 | e, _ := expected.(core.UpdateActionImpl) 189 | expObject := e.GetObject() 190 | object := a.GetObject() 191 | 192 | if !reflect.DeepEqual(expObject, object) { 193 | t.Errorf("Action %s %s has wrong object\nDiff:\n %s", 194 | a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expObject, object)) 195 | } 196 | case core.PatchActionImpl: 197 | e, _ := expected.(core.PatchActionImpl) 198 | expPatch := e.GetPatch() 199 | patch := a.GetPatch() 200 | 201 | if !reflect.DeepEqual(expPatch, patch) { 202 | t.Errorf("Action %s %s has wrong patch\nDiff:\n %s", 203 | a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expPatch, patch)) 204 | } 205 | default: 206 | t.Errorf("Uncaptured Action %s %s, you should explicitly add a case to capture it", 207 | actual.GetVerb(), actual.GetResource().Resource) 208 | } 209 | } 210 | 211 | // filterInformerActions filters list and watch actions for testing resources. 212 | // Since list and watch don't change resource state we can filter it to lower 213 | // nose level in our tests. 214 | func filterInformerActions(actions []core.Action) []core.Action { 215 | ret := []core.Action{} 216 | for _, action := range actions { 217 | if len(action.GetNamespace()) == 0 && 218 | (action.Matches("list", "foos") || 219 | action.Matches("watch", "foos") || 220 | action.Matches("list", "deployments") || 221 | action.Matches("watch", "deployments")) { 222 | continue 223 | } 224 | ret = append(ret, action) 225 | } 226 | 227 | return ret 228 | } 229 | 230 | func (f *fixture) expectCreateDeploymentAction(d *apps.Deployment) { 231 | f.kubeactions = append(f.kubeactions, core.NewCreateAction(schema.GroupVersionResource{Resource: "deployments"}, d.Namespace, d)) 232 | } 233 | 234 | func (f *fixture) expectUpdateDeploymentAction(d *apps.Deployment) { 235 | f.kubeactions = append(f.kubeactions, core.NewUpdateAction(schema.GroupVersionResource{Resource: "deployments"}, d.Namespace, d)) 236 | } 237 | 238 | func (f *fixture) expectUpdateFooStatusAction(foo *samplecontroller.Foo) { 239 | action := core.NewUpdateSubresourceAction(schema.GroupVersionResource{Resource: "foos"}, "status", foo.Namespace, foo) 240 | f.actions = append(f.actions, action) 241 | } 242 | 243 | func getRef(foo *samplecontroller.Foo, t *testing.T) cache.ObjectName { 244 | ref := cache.MetaObjectToName(foo) 245 | return ref 246 | } 247 | 248 | func TestCreatesDeployment(t *testing.T) { 249 | f := newFixture(t) 250 | foo := newFoo("test", int32Ptr(1)) 251 | _, ctx := ktesting.NewTestContext(t) 252 | 253 | f.fooLister = append(f.fooLister, foo) 254 | f.objects = append(f.objects, foo) 255 | 256 | expDeployment := newDeployment(foo) 257 | f.expectCreateDeploymentAction(expDeployment) 258 | f.expectUpdateFooStatusAction(foo) 259 | 260 | f.run(ctx, getRef(foo, t)) 261 | } 262 | 263 | func TestDoNothing(t *testing.T) { 264 | f := newFixture(t) 265 | foo := newFoo("test", int32Ptr(1)) 266 | _, ctx := ktesting.NewTestContext(t) 267 | 268 | d := newDeployment(foo) 269 | 270 | f.fooLister = append(f.fooLister, foo) 271 | f.objects = append(f.objects, foo) 272 | f.deploymentLister = append(f.deploymentLister, d) 273 | f.kubeobjects = append(f.kubeobjects, d) 274 | 275 | f.expectUpdateFooStatusAction(foo) 276 | f.run(ctx, getRef(foo, t)) 277 | } 278 | 279 | func TestUpdateDeployment(t *testing.T) { 280 | f := newFixture(t) 281 | foo := newFoo("test", int32Ptr(1)) 282 | _, ctx := ktesting.NewTestContext(t) 283 | 284 | d := newDeployment(foo) 285 | 286 | // Update replicas 287 | foo.Spec.Replicas = int32Ptr(2) 288 | expDeployment := newDeployment(foo) 289 | 290 | f.fooLister = append(f.fooLister, foo) 291 | f.objects = append(f.objects, foo) 292 | f.deploymentLister = append(f.deploymentLister, d) 293 | f.kubeobjects = append(f.kubeobjects, d) 294 | 295 | f.expectUpdateFooStatusAction(foo) 296 | f.expectUpdateDeploymentAction(expDeployment) 297 | f.run(ctx, getRef(foo, t)) 298 | } 299 | 300 | func TestNotControlledByUs(t *testing.T) { 301 | f := newFixture(t) 302 | foo := newFoo("test", int32Ptr(1)) 303 | _, ctx := ktesting.NewTestContext(t) 304 | 305 | d := newDeployment(foo) 306 | 307 | d.ObjectMeta.OwnerReferences = []metav1.OwnerReference{} 308 | 309 | f.fooLister = append(f.fooLister, foo) 310 | f.objects = append(f.objects, foo) 311 | f.deploymentLister = append(f.deploymentLister, d) 312 | f.kubeobjects = append(f.kubeobjects, d) 313 | 314 | f.runExpectError(ctx, getRef(foo, t)) 315 | } 316 | 317 | func int32Ptr(i int32) *int32 { return &i } 318 | -------------------------------------------------------------------------------- /docs/controller-client-go.md: -------------------------------------------------------------------------------- 1 | # client-go under the hood 2 | 3 | The [client-go](https://github.com/kubernetes/client-go/) library contains various mechanisms that you can use when 4 | developing your custom controllers. These mechanisms are defined in the 5 | [tools/cache folder](https://github.com/kubernetes/client-go/tree/master/tools/cache) of the library. 6 | 7 | Here is a pictorial representation showing how the various components in 8 | the client-go library work and their interaction points with the custom 9 | controller code that you will write. 10 | 11 |

12 | 13 |

14 | 15 | ## client-go components 16 | 17 | * Reflector: A reflector, which is defined in [type *Reflector* inside package *cache*](https://github.com/kubernetes/client-go/blob/master/tools/cache/reflector.go), 18 | watches the Kubernetes API for the specified resource type (kind). 19 | The function in which this is done is *ListAndWatch*. 20 | The watch could be for an in-built resource or it could be for a custom resource. 21 | When the reflector receives notification about existence of new 22 | resource instance through the watch API, it gets the newly created object 23 | using the corresponding listing API and puts it in the Delta Fifo queue 24 | inside the *watchHandler* function. 25 | 26 | 27 | * Informer: An informer defined in the [base controller inside package *cache*](https://github.com/kubernetes/client-go/blob/master/tools/cache/controller.go) pops objects from the Delta Fifo queue. 28 | The function in which this is done is *processLoop*. The job of this base controller 29 | is to save the object for later retrieval, and to invoke our controller passing it the object. 30 | 31 | * Indexer: An indexer provides indexing functionality over objects. 32 | It is defined in [type *Indexer* inside package *cache*](https://github.com/kubernetes/client-go/blob/master/tools/cache/index.go). A typical indexing use-case is to create an index based on object labels. Indexer can 33 | maintain indexes based on several indexing functions. 34 | Indexer uses a thread-safe data store to store objects and their keys. 35 | There is a default function named *MetaNamespaceKeyFunc* defined in [type *Store* inside package *cache*](https://github.com/kubernetes/client-go/blob/master/tools/cache/store.go) 36 | that generates an object’s key as `/` combination for that object. 37 | 38 | 39 | ## Custom Controller components 40 | 41 | * Informer reference: This is the reference to the Informer instance that knows 42 | how to work with your custom resource objects. Your custom controller code needs 43 | to create the appropriate Informer. 44 | 45 | * Indexer reference: This is the reference to the Indexer instance that knows 46 | how to work with your custom resource objects. Your custom controller code needs 47 | to create this. You will be using this reference for retrieving objects for 48 | later processing. 49 | 50 | The base controller in client-go provides the *NewIndexerInformer* function to create Informer and Indexer. 51 | In your code you can either [directly invoke this function](https://github.com/kubernetes/client-go/blob/master/examples/workqueue/main.go#L174) or [use factory methods for creating an informer.](https://github.com/kubernetes/sample-controller/blob/master/main.go#L61) 52 | 53 | * Resource Event Handlers: These are the callback functions which will be called by 54 | the Informer when it wants to deliver an object to your controller. The typical 55 | pattern to write these functions is to obtain the dispatched object’s key 56 | and enqueue that key in a work queue for further processing. 57 | 58 | * Work queue: This is the queue that you create in your controller code to decouple 59 | delivery of an object from its processing. Resource event handler functions are written 60 | to extract the delivered object’s key and add that to the work queue. 61 | 62 | * Process Item: This is the function that you create in your code which processes items 63 | from the work queue. There can be one or more other functions that do the actual processing. 64 | These functions will typically use the [Indexer reference](https://github.com/kubernetes/client-go/blob/master/examples/workqueue/main.go#L73), or a Listing wrapper to retrieve the object corresponding to the key. -------------------------------------------------------------------------------- /docs/images/client-go-controller-interaction.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kubernetes/sample-controller/099b2a3118ac16d7f9036befe1c0bf1e71a56c8f/docs/images/client-go-controller-interaction.jpeg -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | // This is a generated file. Do not edit directly. 2 | 3 | module k8s.io/sample-controller 4 | 5 | go 1.24.0 6 | 7 | godebug default=go1.24 8 | 9 | require ( 10 | golang.org/x/time v0.9.0 11 | k8s.io/api v0.0.0-20250516033257-ffe08c772d5b 12 | k8s.io/apimachinery v0.0.0-20250516032956-da3bba90543c 13 | k8s.io/client-go v0.0.0-20250516033711-025e06660a23 14 | k8s.io/code-generator v0.0.0-20250516034928-3a6fa0a71475 15 | k8s.io/klog/v2 v2.130.1 16 | ) 17 | 18 | require ( 19 | github.com/davecgh/go-spew v1.1.1 // indirect 20 | github.com/emicklei/go-restful/v3 v3.11.0 // indirect 21 | github.com/fxamacker/cbor/v2 v2.8.0 // indirect 22 | github.com/go-logr/logr v1.4.2 // indirect 23 | github.com/go-openapi/jsonpointer v0.21.0 // indirect 24 | github.com/go-openapi/jsonreference v0.20.2 // indirect 25 | github.com/go-openapi/swag v0.23.0 // indirect 26 | github.com/gogo/protobuf v1.3.2 // indirect 27 | github.com/google/gnostic-models v0.6.9 // indirect 28 | github.com/google/go-cmp v0.7.0 // indirect 29 | github.com/google/uuid v1.6.0 // indirect 30 | github.com/josharian/intern v1.0.0 // indirect 31 | github.com/json-iterator/go v1.1.12 // indirect 32 | github.com/mailru/easyjson v0.7.7 // indirect 33 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 34 | github.com/modern-go/reflect2 v1.0.2 // indirect 35 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 36 | github.com/pkg/errors v0.9.1 // indirect 37 | github.com/spf13/pflag v1.0.6 // indirect 38 | github.com/x448/float16 v0.8.4 // indirect 39 | golang.org/x/mod v0.21.0 // indirect 40 | golang.org/x/net v0.38.0 // indirect 41 | golang.org/x/oauth2 v0.27.0 // indirect 42 | golang.org/x/sync v0.12.0 // indirect 43 | golang.org/x/sys v0.31.0 // indirect 44 | golang.org/x/term v0.30.0 // indirect 45 | golang.org/x/text v0.23.0 // indirect 46 | golang.org/x/tools v0.26.0 // indirect 47 | google.golang.org/protobuf v1.36.5 // indirect 48 | gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect 49 | gopkg.in/inf.v0 v0.9.1 // indirect 50 | gopkg.in/yaml.v3 v3.0.1 // indirect 51 | k8s.io/gengo/v2 v2.0.0-20250207200755-1244d31929d7 // indirect 52 | k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect 53 | k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 // indirect 54 | sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect 55 | sigs.k8s.io/randfill v1.0.0 // indirect 56 | sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect 57 | sigs.k8s.io/yaml v1.4.0 // indirect 58 | ) 59 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= 6 | github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= 7 | github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU= 8 | github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= 9 | github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= 10 | github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 11 | github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= 12 | github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= 13 | github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= 14 | github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= 15 | github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= 16 | github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= 17 | github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= 18 | github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= 19 | github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= 20 | github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= 21 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 22 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 23 | github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= 24 | github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= 25 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 26 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 27 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 28 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 29 | github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= 30 | github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= 31 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 32 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 33 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 34 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 35 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 36 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 37 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 38 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 39 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 40 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 41 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 42 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 43 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 44 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 45 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 46 | github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= 47 | github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 48 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 49 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 50 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 51 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 52 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 53 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= 54 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 55 | github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= 56 | github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= 57 | github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= 58 | github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= 59 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 60 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 61 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 62 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 63 | github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= 64 | github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= 65 | github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= 66 | github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 67 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 68 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 69 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 70 | github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= 71 | github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 72 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 73 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 74 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 75 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 76 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 77 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 78 | github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= 79 | github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= 80 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 81 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 82 | go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= 83 | go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 84 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 85 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 86 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 87 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 88 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 89 | golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= 90 | golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= 91 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 92 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 93 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 94 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 95 | golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= 96 | golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= 97 | golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= 98 | golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= 99 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 100 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 101 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 102 | golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= 103 | golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 104 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 105 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 106 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 107 | golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= 108 | golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 109 | golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= 110 | golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= 111 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 112 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 113 | golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= 114 | golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= 115 | golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= 116 | golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 117 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 118 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 119 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 120 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 121 | golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= 122 | golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= 123 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 124 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 125 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 126 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 127 | google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= 128 | google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 129 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 130 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 131 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 132 | gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= 133 | gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= 134 | gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= 135 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 136 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 137 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 138 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 139 | k8s.io/api v0.0.0-20250516033257-ffe08c772d5b h1:LwK7OunFjfg9xoa3X/I03H2D1JIlpSbNBIPCowQfo/g= 140 | k8s.io/api v0.0.0-20250516033257-ffe08c772d5b/go.mod h1:xqViZ9G2V/f3Ft2zh5qGrluqUiREXWND/qSDpxuAvUA= 141 | k8s.io/apimachinery v0.0.0-20250516032956-da3bba90543c h1:32RPTmIMyn/cz7/8xxQ4BhJTkvQE1nnzR0YR4ODcVRs= 142 | k8s.io/apimachinery v0.0.0-20250516032956-da3bba90543c/go.mod h1:pJRnLHx/rdGhRBHKhKq/NczIcMw4cPylIe+hff1zJaU= 143 | k8s.io/client-go v0.0.0-20250516033711-025e06660a23 h1:h8NujnSdqX3mcu0g7Tcls57Oy5AX1saqB8+mEX0vkbk= 144 | k8s.io/client-go v0.0.0-20250516033711-025e06660a23/go.mod h1:70YvuiC0JxRnkbXLUExjxqfqaPzpA0RvkM0mUmvQ2Fo= 145 | k8s.io/code-generator v0.0.0-20250516034928-3a6fa0a71475 h1:FAS4zfStDhEUeh9k3ZrTSQP7+ogg/QmQ5f+wtlD4CNQ= 146 | k8s.io/code-generator v0.0.0-20250516034928-3a6fa0a71475/go.mod h1:NQ3v9ly7Z0E0ZIlcYXZgT2TOewfR7LIo4dGKKbB7wXI= 147 | k8s.io/gengo/v2 v2.0.0-20250207200755-1244d31929d7 h1:2OX19X59HxDprNCVrWi6jb7LW1PoqTlYqEq5H2oetog= 148 | k8s.io/gengo/v2 v2.0.0-20250207200755-1244d31929d7/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU= 149 | k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= 150 | k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= 151 | k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= 152 | k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= 153 | k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 h1:jgJW5IePPXLGB8e/1wvd0Ich9QE97RvvF3a8J3fP/Lg= 154 | k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= 155 | sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= 156 | sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= 157 | sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= 158 | sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= 159 | sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= 160 | sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI= 161 | sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= 162 | sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= 163 | sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= 164 | -------------------------------------------------------------------------------- /hack/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | -------------------------------------------------------------------------------- /hack/custom-boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright YEAR The Kubernetes sample-controller Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | -------------------------------------------------------------------------------- /hack/tools.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | // +build tools 3 | 4 | /* 5 | Copyright 2019 The Kubernetes Authors. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | */ 19 | 20 | // This package imports things required by build scripts, to force `go mod` to see them as dependencies 21 | package tools 22 | 23 | import _ "k8s.io/code-generator" 24 | -------------------------------------------------------------------------------- /hack/update-codegen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2017 The Kubernetes Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -o errexit 18 | set -o nounset 19 | set -o pipefail 20 | 21 | SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. 22 | CODEGEN_PKG=${CODEGEN_PKG:-$(cd "${SCRIPT_ROOT}"; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo ../code-generator)} 23 | 24 | source "${CODEGEN_PKG}/kube_codegen.sh" 25 | 26 | THIS_PKG="k8s.io/sample-controller" 27 | 28 | kube::codegen::gen_helpers \ 29 | --boilerplate "${SCRIPT_ROOT}/hack/boilerplate.go.txt" \ 30 | "${SCRIPT_ROOT}/pkg/apis" 31 | 32 | kube::codegen::gen_client \ 33 | --with-watch \ 34 | --output-dir "${SCRIPT_ROOT}/pkg/generated" \ 35 | --output-pkg "${THIS_PKG}/pkg/generated" \ 36 | --boilerplate "${SCRIPT_ROOT}/hack/boilerplate.go.txt" \ 37 | "${SCRIPT_ROOT}/pkg/apis" 38 | -------------------------------------------------------------------------------- /hack/verify-codegen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2017 The Kubernetes Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -o errexit 18 | set -o nounset 19 | set -o pipefail 20 | 21 | SCRIPT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" 22 | DIFFROOT="${SCRIPT_ROOT}/pkg" 23 | TMP_DIFFROOT="$(mktemp -d -t "$(basename "$0").XXXXXX")/pkg" 24 | 25 | cleanup() { 26 | rm -rf "${TMP_DIFFROOT}" 27 | } 28 | trap "cleanup" EXIT SIGINT 29 | 30 | cleanup 31 | 32 | mkdir -p "${TMP_DIFFROOT}" 33 | cp -a "${DIFFROOT}"/* "${TMP_DIFFROOT}" 34 | 35 | "${SCRIPT_ROOT}/hack/update-codegen.sh" 36 | echo "diffing ${DIFFROOT} against freshly generated codegen" 37 | ret=0 38 | diff -Naupr "${DIFFROOT}" "${TMP_DIFFROOT}" || ret=$? 39 | if [[ $ret -eq 0 ]]; then 40 | echo "${DIFFROOT} up to date." 41 | else 42 | echo "${DIFFROOT} is out of date. Please run hack/update-codegen.sh" 43 | fi 44 | exit $ret 45 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "flag" 21 | "time" 22 | 23 | kubeinformers "k8s.io/client-go/informers" 24 | "k8s.io/client-go/kubernetes" 25 | "k8s.io/client-go/tools/clientcmd" 26 | "k8s.io/klog/v2" 27 | "k8s.io/sample-controller/pkg/signals" 28 | // Uncomment the following line to load the gcp plugin (only required to authenticate against GKE clusters). 29 | // _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" 30 | 31 | clientset "k8s.io/sample-controller/pkg/generated/clientset/versioned" 32 | informers "k8s.io/sample-controller/pkg/generated/informers/externalversions" 33 | ) 34 | 35 | var ( 36 | masterURL string 37 | kubeconfig string 38 | ) 39 | 40 | func main() { 41 | klog.InitFlags(nil) 42 | flag.Parse() 43 | 44 | // set up signals so we handle the shutdown signal gracefully 45 | ctx := signals.SetupSignalHandler() 46 | logger := klog.FromContext(ctx) 47 | 48 | cfg, err := clientcmd.BuildConfigFromFlags(masterURL, kubeconfig) 49 | if err != nil { 50 | logger.Error(err, "Error building kubeconfig") 51 | klog.FlushAndExit(klog.ExitFlushTimeout, 1) 52 | } 53 | 54 | kubeClient, err := kubernetes.NewForConfig(cfg) 55 | if err != nil { 56 | logger.Error(err, "Error building kubernetes clientset") 57 | klog.FlushAndExit(klog.ExitFlushTimeout, 1) 58 | } 59 | 60 | exampleClient, err := clientset.NewForConfig(cfg) 61 | if err != nil { 62 | logger.Error(err, "Error building kubernetes clientset") 63 | klog.FlushAndExit(klog.ExitFlushTimeout, 1) 64 | } 65 | 66 | kubeInformerFactory := kubeinformers.NewSharedInformerFactory(kubeClient, time.Second*30) 67 | exampleInformerFactory := informers.NewSharedInformerFactory(exampleClient, time.Second*30) 68 | 69 | controller := NewController(ctx, kubeClient, exampleClient, 70 | kubeInformerFactory.Apps().V1().Deployments(), 71 | exampleInformerFactory.Samplecontroller().V1alpha1().Foos()) 72 | 73 | // notice that there is no need to run Start methods in a separate goroutine. (i.e. go kubeInformerFactory.Start(ctx.done()) 74 | // Start method is non-blocking and runs all registered informers in a dedicated goroutine. 75 | kubeInformerFactory.Start(ctx.Done()) 76 | exampleInformerFactory.Start(ctx.Done()) 77 | 78 | if err = controller.Run(ctx, 2); err != nil { 79 | logger.Error(err, "Error running controller") 80 | klog.FlushAndExit(klog.ExitFlushTimeout, 1) 81 | } 82 | } 83 | 84 | func init() { 85 | flag.StringVar(&kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.") 86 | flag.StringVar(&masterURL, "master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.") 87 | } 88 | -------------------------------------------------------------------------------- /pkg/apis/samplecontroller/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package samplecontroller 18 | 19 | // GroupName is the group name used in this package 20 | const ( 21 | GroupName = "samplecontroller.k8s.io" 22 | ) 23 | -------------------------------------------------------------------------------- /pkg/apis/samplecontroller/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // +k8s:deepcopy-gen=package 18 | // +groupName=samplecontroller.k8s.io 19 | 20 | // Package v1alpha1 is the v1alpha1 version of the API. 21 | package v1alpha1 22 | -------------------------------------------------------------------------------- /pkg/apis/samplecontroller/v1alpha1/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | "k8s.io/apimachinery/pkg/runtime" 22 | "k8s.io/apimachinery/pkg/runtime/schema" 23 | 24 | samplecontroller "k8s.io/sample-controller/pkg/apis/samplecontroller" 25 | ) 26 | 27 | // SchemeGroupVersion is group version used to register these objects 28 | var SchemeGroupVersion = schema.GroupVersion{Group: samplecontroller.GroupName, Version: "v1alpha1"} 29 | 30 | // Kind takes an unqualified kind and returns back a Group qualified GroupKind 31 | func Kind(kind string) schema.GroupKind { 32 | return SchemeGroupVersion.WithKind(kind).GroupKind() 33 | } 34 | 35 | // Resource takes an unqualified resource and returns a Group qualified GroupResource 36 | func Resource(resource string) schema.GroupResource { 37 | return SchemeGroupVersion.WithResource(resource).GroupResource() 38 | } 39 | 40 | var ( 41 | // SchemeBuilder initializes a scheme builder 42 | SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) 43 | // AddToScheme is a global function that registers this API group & version to a scheme 44 | AddToScheme = SchemeBuilder.AddToScheme 45 | ) 46 | 47 | // Adds the list of known types to Scheme. 48 | func addKnownTypes(scheme *runtime.Scheme) error { 49 | scheme.AddKnownTypes(SchemeGroupVersion, 50 | &Foo{}, 51 | &FooList{}, 52 | ) 53 | metav1.AddToGroupVersion(scheme, SchemeGroupVersion) 54 | return nil 55 | } 56 | -------------------------------------------------------------------------------- /pkg/apis/samplecontroller/v1alpha1/types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | ) 22 | 23 | // +genclient 24 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 25 | 26 | // Foo is a specification for a Foo resource 27 | type Foo struct { 28 | metav1.TypeMeta `json:",inline"` 29 | metav1.ObjectMeta `json:"metadata,omitempty"` 30 | 31 | Spec FooSpec `json:"spec"` 32 | Status FooStatus `json:"status"` 33 | } 34 | 35 | // FooSpec is the spec for a Foo resource 36 | type FooSpec struct { 37 | DeploymentName string `json:"deploymentName"` 38 | Replicas *int32 `json:"replicas"` 39 | } 40 | 41 | // FooStatus is the status for a Foo resource 42 | type FooStatus struct { 43 | AvailableReplicas int32 `json:"availableReplicas"` 44 | } 45 | 46 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 47 | 48 | // FooList is a list of Foo resources 49 | type FooList struct { 50 | metav1.TypeMeta `json:",inline"` 51 | metav1.ListMeta `json:"metadata"` 52 | 53 | Items []Foo `json:"items"` 54 | } 55 | -------------------------------------------------------------------------------- /pkg/apis/samplecontroller/v1alpha1/zz_generated.deepcopy.go: -------------------------------------------------------------------------------- 1 | //go:build !ignore_autogenerated 2 | // +build !ignore_autogenerated 3 | 4 | /* 5 | Copyright The Kubernetes Authors. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | */ 19 | 20 | // Code generated by deepcopy-gen. DO NOT EDIT. 21 | 22 | package v1alpha1 23 | 24 | import ( 25 | runtime "k8s.io/apimachinery/pkg/runtime" 26 | ) 27 | 28 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 29 | func (in *Foo) DeepCopyInto(out *Foo) { 30 | *out = *in 31 | out.TypeMeta = in.TypeMeta 32 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 33 | in.Spec.DeepCopyInto(&out.Spec) 34 | out.Status = in.Status 35 | return 36 | } 37 | 38 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Foo. 39 | func (in *Foo) DeepCopy() *Foo { 40 | if in == nil { 41 | return nil 42 | } 43 | out := new(Foo) 44 | in.DeepCopyInto(out) 45 | return out 46 | } 47 | 48 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 49 | func (in *Foo) DeepCopyObject() runtime.Object { 50 | if c := in.DeepCopy(); c != nil { 51 | return c 52 | } 53 | return nil 54 | } 55 | 56 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 57 | func (in *FooList) DeepCopyInto(out *FooList) { 58 | *out = *in 59 | out.TypeMeta = in.TypeMeta 60 | in.ListMeta.DeepCopyInto(&out.ListMeta) 61 | if in.Items != nil { 62 | in, out := &in.Items, &out.Items 63 | *out = make([]Foo, len(*in)) 64 | for i := range *in { 65 | (*in)[i].DeepCopyInto(&(*out)[i]) 66 | } 67 | } 68 | return 69 | } 70 | 71 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FooList. 72 | func (in *FooList) DeepCopy() *FooList { 73 | if in == nil { 74 | return nil 75 | } 76 | out := new(FooList) 77 | in.DeepCopyInto(out) 78 | return out 79 | } 80 | 81 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 82 | func (in *FooList) DeepCopyObject() runtime.Object { 83 | if c := in.DeepCopy(); c != nil { 84 | return c 85 | } 86 | return nil 87 | } 88 | 89 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 90 | func (in *FooSpec) DeepCopyInto(out *FooSpec) { 91 | *out = *in 92 | if in.Replicas != nil { 93 | in, out := &in.Replicas, &out.Replicas 94 | *out = new(int32) 95 | **out = **in 96 | } 97 | return 98 | } 99 | 100 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FooSpec. 101 | func (in *FooSpec) DeepCopy() *FooSpec { 102 | if in == nil { 103 | return nil 104 | } 105 | out := new(FooSpec) 106 | in.DeepCopyInto(out) 107 | return out 108 | } 109 | 110 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 111 | func (in *FooStatus) DeepCopyInto(out *FooStatus) { 112 | *out = *in 113 | return 114 | } 115 | 116 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FooStatus. 117 | func (in *FooStatus) DeepCopy() *FooStatus { 118 | if in == nil { 119 | return nil 120 | } 121 | out := new(FooStatus) 122 | in.DeepCopyInto(out) 123 | return out 124 | } 125 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/clientset.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package versioned 20 | 21 | import ( 22 | fmt "fmt" 23 | http "net/http" 24 | 25 | discovery "k8s.io/client-go/discovery" 26 | rest "k8s.io/client-go/rest" 27 | flowcontrol "k8s.io/client-go/util/flowcontrol" 28 | samplecontrollerv1alpha1 "k8s.io/sample-controller/pkg/generated/clientset/versioned/typed/samplecontroller/v1alpha1" 29 | ) 30 | 31 | type Interface interface { 32 | Discovery() discovery.DiscoveryInterface 33 | SamplecontrollerV1alpha1() samplecontrollerv1alpha1.SamplecontrollerV1alpha1Interface 34 | } 35 | 36 | // Clientset contains the clients for groups. 37 | type Clientset struct { 38 | *discovery.DiscoveryClient 39 | samplecontrollerV1alpha1 *samplecontrollerv1alpha1.SamplecontrollerV1alpha1Client 40 | } 41 | 42 | // SamplecontrollerV1alpha1 retrieves the SamplecontrollerV1alpha1Client 43 | func (c *Clientset) SamplecontrollerV1alpha1() samplecontrollerv1alpha1.SamplecontrollerV1alpha1Interface { 44 | return c.samplecontrollerV1alpha1 45 | } 46 | 47 | // Discovery retrieves the DiscoveryClient 48 | func (c *Clientset) Discovery() discovery.DiscoveryInterface { 49 | if c == nil { 50 | return nil 51 | } 52 | return c.DiscoveryClient 53 | } 54 | 55 | // NewForConfig creates a new Clientset for the given config. 56 | // If config's RateLimiter is not set and QPS and Burst are acceptable, 57 | // NewForConfig will generate a rate-limiter in configShallowCopy. 58 | // NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), 59 | // where httpClient was generated with rest.HTTPClientFor(c). 60 | func NewForConfig(c *rest.Config) (*Clientset, error) { 61 | configShallowCopy := *c 62 | 63 | if configShallowCopy.UserAgent == "" { 64 | configShallowCopy.UserAgent = rest.DefaultKubernetesUserAgent() 65 | } 66 | 67 | // share the transport between all clients 68 | httpClient, err := rest.HTTPClientFor(&configShallowCopy) 69 | if err != nil { 70 | return nil, err 71 | } 72 | 73 | return NewForConfigAndClient(&configShallowCopy, httpClient) 74 | } 75 | 76 | // NewForConfigAndClient creates a new Clientset for the given config and http client. 77 | // Note the http client provided takes precedence over the configured transport values. 78 | // If config's RateLimiter is not set and QPS and Burst are acceptable, 79 | // NewForConfigAndClient will generate a rate-limiter in configShallowCopy. 80 | func NewForConfigAndClient(c *rest.Config, httpClient *http.Client) (*Clientset, error) { 81 | configShallowCopy := *c 82 | if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { 83 | if configShallowCopy.Burst <= 0 { 84 | return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0") 85 | } 86 | configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) 87 | } 88 | 89 | var cs Clientset 90 | var err error 91 | cs.samplecontrollerV1alpha1, err = samplecontrollerv1alpha1.NewForConfigAndClient(&configShallowCopy, httpClient) 92 | if err != nil { 93 | return nil, err 94 | } 95 | 96 | cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfigAndClient(&configShallowCopy, httpClient) 97 | if err != nil { 98 | return nil, err 99 | } 100 | return &cs, nil 101 | } 102 | 103 | // NewForConfigOrDie creates a new Clientset for the given config and 104 | // panics if there is an error in the config. 105 | func NewForConfigOrDie(c *rest.Config) *Clientset { 106 | cs, err := NewForConfig(c) 107 | if err != nil { 108 | panic(err) 109 | } 110 | return cs 111 | } 112 | 113 | // New creates a new Clientset for the given RESTClient. 114 | func New(c rest.Interface) *Clientset { 115 | var cs Clientset 116 | cs.samplecontrollerV1alpha1 = samplecontrollerv1alpha1.New(c) 117 | 118 | cs.DiscoveryClient = discovery.NewDiscoveryClient(c) 119 | return &cs 120 | } 121 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/fake/clientset_generated.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 | "k8s.io/apimachinery/pkg/runtime" 24 | "k8s.io/apimachinery/pkg/watch" 25 | "k8s.io/client-go/discovery" 26 | fakediscovery "k8s.io/client-go/discovery/fake" 27 | "k8s.io/client-go/testing" 28 | clientset "k8s.io/sample-controller/pkg/generated/clientset/versioned" 29 | samplecontrollerv1alpha1 "k8s.io/sample-controller/pkg/generated/clientset/versioned/typed/samplecontroller/v1alpha1" 30 | fakesamplecontrollerv1alpha1 "k8s.io/sample-controller/pkg/generated/clientset/versioned/typed/samplecontroller/v1alpha1/fake" 31 | ) 32 | 33 | // NewSimpleClientset returns a clientset that will respond with the provided objects. 34 | // It's backed by a very simple object tracker that processes creates, updates and deletions as-is, 35 | // without applying any field management, validations and/or defaults. It shouldn't be considered a replacement 36 | // for a real clientset and is mostly useful in simple unit tests. 37 | // 38 | // DEPRECATED: NewClientset replaces this with support for field management, which significantly improves 39 | // server side apply testing. NewClientset is only available when apply configurations are generated (e.g. 40 | // via --with-applyconfig). 41 | func NewSimpleClientset(objects ...runtime.Object) *Clientset { 42 | o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) 43 | for _, obj := range objects { 44 | if err := o.Add(obj); err != nil { 45 | panic(err) 46 | } 47 | } 48 | 49 | cs := &Clientset{tracker: o} 50 | cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} 51 | cs.AddReactor("*", "*", testing.ObjectReaction(o)) 52 | cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { 53 | var opts metav1.ListOptions 54 | if watchActcion, ok := action.(testing.WatchActionImpl); ok { 55 | opts = watchActcion.ListOptions 56 | } 57 | gvr := action.GetResource() 58 | ns := action.GetNamespace() 59 | watch, err := o.Watch(gvr, ns, opts) 60 | if err != nil { 61 | return false, nil, err 62 | } 63 | return true, watch, nil 64 | }) 65 | 66 | return cs 67 | } 68 | 69 | // Clientset implements clientset.Interface. Meant to be embedded into a 70 | // struct to get a default implementation. This makes faking out just the method 71 | // you want to test easier. 72 | type Clientset struct { 73 | testing.Fake 74 | discovery *fakediscovery.FakeDiscovery 75 | tracker testing.ObjectTracker 76 | } 77 | 78 | func (c *Clientset) Discovery() discovery.DiscoveryInterface { 79 | return c.discovery 80 | } 81 | 82 | func (c *Clientset) Tracker() testing.ObjectTracker { 83 | return c.tracker 84 | } 85 | 86 | var ( 87 | _ clientset.Interface = &Clientset{} 88 | _ testing.FakeClient = &Clientset{} 89 | ) 90 | 91 | // SamplecontrollerV1alpha1 retrieves the SamplecontrollerV1alpha1Client 92 | func (c *Clientset) SamplecontrollerV1alpha1() samplecontrollerv1alpha1.SamplecontrollerV1alpha1Interface { 93 | return &fakesamplecontrollerv1alpha1.FakeSamplecontrollerV1alpha1{Fake: &c.Fake} 94 | } 95 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package has the automatically generated fake clientset. 20 | package fake 21 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/fake/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 | runtime "k8s.io/apimachinery/pkg/runtime" 24 | schema "k8s.io/apimachinery/pkg/runtime/schema" 25 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 26 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 27 | samplecontrollerv1alpha1 "k8s.io/sample-controller/pkg/apis/samplecontroller/v1alpha1" 28 | ) 29 | 30 | var scheme = runtime.NewScheme() 31 | var codecs = serializer.NewCodecFactory(scheme) 32 | 33 | var localSchemeBuilder = runtime.SchemeBuilder{ 34 | samplecontrollerv1alpha1.AddToScheme, 35 | } 36 | 37 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 38 | // of clientsets, like in: 39 | // 40 | // import ( 41 | // "k8s.io/client-go/kubernetes" 42 | // clientsetscheme "k8s.io/client-go/kubernetes/scheme" 43 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 44 | // ) 45 | // 46 | // kclientset, _ := kubernetes.NewForConfig(c) 47 | // _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 48 | // 49 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 50 | // correctly. 51 | var AddToScheme = localSchemeBuilder.AddToScheme 52 | 53 | func init() { 54 | v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) 55 | utilruntime.Must(AddToScheme(scheme)) 56 | } 57 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/scheme/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package contains the scheme of the automatically generated clientset. 20 | package scheme 21 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/scheme/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package scheme 20 | 21 | import ( 22 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 | runtime "k8s.io/apimachinery/pkg/runtime" 24 | schema "k8s.io/apimachinery/pkg/runtime/schema" 25 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 26 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 27 | samplecontrollerv1alpha1 "k8s.io/sample-controller/pkg/apis/samplecontroller/v1alpha1" 28 | ) 29 | 30 | var Scheme = runtime.NewScheme() 31 | var Codecs = serializer.NewCodecFactory(Scheme) 32 | var ParameterCodec = runtime.NewParameterCodec(Scheme) 33 | var localSchemeBuilder = runtime.SchemeBuilder{ 34 | samplecontrollerv1alpha1.AddToScheme, 35 | } 36 | 37 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 38 | // of clientsets, like in: 39 | // 40 | // import ( 41 | // "k8s.io/client-go/kubernetes" 42 | // clientsetscheme "k8s.io/client-go/kubernetes/scheme" 43 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 44 | // ) 45 | // 46 | // kclientset, _ := kubernetes.NewForConfig(c) 47 | // _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 48 | // 49 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 50 | // correctly. 51 | var AddToScheme = localSchemeBuilder.AddToScheme 52 | 53 | func init() { 54 | v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) 55 | utilruntime.Must(AddToScheme(Scheme)) 56 | } 57 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/samplecontroller/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package has the automatically generated typed clients. 20 | package v1alpha1 21 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/samplecontroller/v1alpha1/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // Package fake has the automatically generated clients. 20 | package fake 21 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/samplecontroller/v1alpha1/fake/fake_foo.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | gentype "k8s.io/client-go/gentype" 23 | v1alpha1 "k8s.io/sample-controller/pkg/apis/samplecontroller/v1alpha1" 24 | samplecontrollerv1alpha1 "k8s.io/sample-controller/pkg/generated/clientset/versioned/typed/samplecontroller/v1alpha1" 25 | ) 26 | 27 | // fakeFoos implements FooInterface 28 | type fakeFoos struct { 29 | *gentype.FakeClientWithList[*v1alpha1.Foo, *v1alpha1.FooList] 30 | Fake *FakeSamplecontrollerV1alpha1 31 | } 32 | 33 | func newFakeFoos(fake *FakeSamplecontrollerV1alpha1, namespace string) samplecontrollerv1alpha1.FooInterface { 34 | return &fakeFoos{ 35 | gentype.NewFakeClientWithList[*v1alpha1.Foo, *v1alpha1.FooList]( 36 | fake.Fake, 37 | namespace, 38 | v1alpha1.SchemeGroupVersion.WithResource("foos"), 39 | v1alpha1.SchemeGroupVersion.WithKind("Foo"), 40 | func() *v1alpha1.Foo { return &v1alpha1.Foo{} }, 41 | func() *v1alpha1.FooList { return &v1alpha1.FooList{} }, 42 | func(dst, src *v1alpha1.FooList) { dst.ListMeta = src.ListMeta }, 43 | func(list *v1alpha1.FooList) []*v1alpha1.Foo { return gentype.ToPointerSlice(list.Items) }, 44 | func(list *v1alpha1.FooList, items []*v1alpha1.Foo) { list.Items = gentype.FromPointerSlice(items) }, 45 | ), 46 | fake, 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/samplecontroller/v1alpha1/fake/fake_samplecontroller_client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | rest "k8s.io/client-go/rest" 23 | testing "k8s.io/client-go/testing" 24 | v1alpha1 "k8s.io/sample-controller/pkg/generated/clientset/versioned/typed/samplecontroller/v1alpha1" 25 | ) 26 | 27 | type FakeSamplecontrollerV1alpha1 struct { 28 | *testing.Fake 29 | } 30 | 31 | func (c *FakeSamplecontrollerV1alpha1) Foos(namespace string) v1alpha1.FooInterface { 32 | return newFakeFoos(c, namespace) 33 | } 34 | 35 | // RESTClient returns a RESTClient that is used to communicate 36 | // with API server by this client implementation. 37 | func (c *FakeSamplecontrollerV1alpha1) RESTClient() rest.Interface { 38 | var ret *rest.RESTClient 39 | return ret 40 | } 41 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/samplecontroller/v1alpha1/foo.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package v1alpha1 20 | 21 | import ( 22 | context "context" 23 | 24 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 | types "k8s.io/apimachinery/pkg/types" 26 | watch "k8s.io/apimachinery/pkg/watch" 27 | gentype "k8s.io/client-go/gentype" 28 | samplecontrollerv1alpha1 "k8s.io/sample-controller/pkg/apis/samplecontroller/v1alpha1" 29 | scheme "k8s.io/sample-controller/pkg/generated/clientset/versioned/scheme" 30 | ) 31 | 32 | // FoosGetter has a method to return a FooInterface. 33 | // A group's client should implement this interface. 34 | type FoosGetter interface { 35 | Foos(namespace string) FooInterface 36 | } 37 | 38 | // FooInterface has methods to work with Foo resources. 39 | type FooInterface interface { 40 | Create(ctx context.Context, foo *samplecontrollerv1alpha1.Foo, opts v1.CreateOptions) (*samplecontrollerv1alpha1.Foo, error) 41 | Update(ctx context.Context, foo *samplecontrollerv1alpha1.Foo, opts v1.UpdateOptions) (*samplecontrollerv1alpha1.Foo, error) 42 | // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). 43 | UpdateStatus(ctx context.Context, foo *samplecontrollerv1alpha1.Foo, opts v1.UpdateOptions) (*samplecontrollerv1alpha1.Foo, error) 44 | Delete(ctx context.Context, name string, opts v1.DeleteOptions) error 45 | DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error 46 | Get(ctx context.Context, name string, opts v1.GetOptions) (*samplecontrollerv1alpha1.Foo, error) 47 | List(ctx context.Context, opts v1.ListOptions) (*samplecontrollerv1alpha1.FooList, error) 48 | Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) 49 | Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *samplecontrollerv1alpha1.Foo, err error) 50 | FooExpansion 51 | } 52 | 53 | // foos implements FooInterface 54 | type foos struct { 55 | *gentype.ClientWithList[*samplecontrollerv1alpha1.Foo, *samplecontrollerv1alpha1.FooList] 56 | } 57 | 58 | // newFoos returns a Foos 59 | func newFoos(c *SamplecontrollerV1alpha1Client, namespace string) *foos { 60 | return &foos{ 61 | gentype.NewClientWithList[*samplecontrollerv1alpha1.Foo, *samplecontrollerv1alpha1.FooList]( 62 | "foos", 63 | c.RESTClient(), 64 | scheme.ParameterCodec, 65 | namespace, 66 | func() *samplecontrollerv1alpha1.Foo { return &samplecontrollerv1alpha1.Foo{} }, 67 | func() *samplecontrollerv1alpha1.FooList { return &samplecontrollerv1alpha1.FooList{} }, 68 | ), 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/samplecontroller/v1alpha1/generated_expansion.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package v1alpha1 20 | 21 | type FooExpansion interface{} 22 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/samplecontroller/v1alpha1/samplecontroller_client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package v1alpha1 20 | 21 | import ( 22 | http "net/http" 23 | 24 | rest "k8s.io/client-go/rest" 25 | samplecontrollerv1alpha1 "k8s.io/sample-controller/pkg/apis/samplecontroller/v1alpha1" 26 | scheme "k8s.io/sample-controller/pkg/generated/clientset/versioned/scheme" 27 | ) 28 | 29 | type SamplecontrollerV1alpha1Interface interface { 30 | RESTClient() rest.Interface 31 | FoosGetter 32 | } 33 | 34 | // SamplecontrollerV1alpha1Client is used to interact with features provided by the samplecontroller.k8s.io group. 35 | type SamplecontrollerV1alpha1Client struct { 36 | restClient rest.Interface 37 | } 38 | 39 | func (c *SamplecontrollerV1alpha1Client) Foos(namespace string) FooInterface { 40 | return newFoos(c, namespace) 41 | } 42 | 43 | // NewForConfig creates a new SamplecontrollerV1alpha1Client for the given config. 44 | // NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), 45 | // where httpClient was generated with rest.HTTPClientFor(c). 46 | func NewForConfig(c *rest.Config) (*SamplecontrollerV1alpha1Client, error) { 47 | config := *c 48 | setConfigDefaults(&config) 49 | httpClient, err := rest.HTTPClientFor(&config) 50 | if err != nil { 51 | return nil, err 52 | } 53 | return NewForConfigAndClient(&config, httpClient) 54 | } 55 | 56 | // NewForConfigAndClient creates a new SamplecontrollerV1alpha1Client for the given config and http client. 57 | // Note the http client provided takes precedence over the configured transport values. 58 | func NewForConfigAndClient(c *rest.Config, h *http.Client) (*SamplecontrollerV1alpha1Client, error) { 59 | config := *c 60 | setConfigDefaults(&config) 61 | client, err := rest.RESTClientForConfigAndClient(&config, h) 62 | if err != nil { 63 | return nil, err 64 | } 65 | return &SamplecontrollerV1alpha1Client{client}, nil 66 | } 67 | 68 | // NewForConfigOrDie creates a new SamplecontrollerV1alpha1Client for the given config and 69 | // panics if there is an error in the config. 70 | func NewForConfigOrDie(c *rest.Config) *SamplecontrollerV1alpha1Client { 71 | client, err := NewForConfig(c) 72 | if err != nil { 73 | panic(err) 74 | } 75 | return client 76 | } 77 | 78 | // New creates a new SamplecontrollerV1alpha1Client for the given RESTClient. 79 | func New(c rest.Interface) *SamplecontrollerV1alpha1Client { 80 | return &SamplecontrollerV1alpha1Client{c} 81 | } 82 | 83 | func setConfigDefaults(config *rest.Config) { 84 | gv := samplecontrollerv1alpha1.SchemeGroupVersion 85 | config.GroupVersion = &gv 86 | config.APIPath = "/apis" 87 | config.NegotiatedSerializer = rest.CodecFactoryForGeneratedClient(scheme.Scheme, scheme.Codecs).WithoutConversion() 88 | 89 | if config.UserAgent == "" { 90 | config.UserAgent = rest.DefaultKubernetesUserAgent() 91 | } 92 | } 93 | 94 | // RESTClient returns a RESTClient that is used to communicate 95 | // with API server by this client implementation. 96 | func (c *SamplecontrollerV1alpha1Client) RESTClient() rest.Interface { 97 | if c == nil { 98 | return nil 99 | } 100 | return c.restClient 101 | } 102 | -------------------------------------------------------------------------------- /pkg/generated/informers/externalversions/factory.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package externalversions 20 | 21 | import ( 22 | reflect "reflect" 23 | sync "sync" 24 | time "time" 25 | 26 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 | runtime "k8s.io/apimachinery/pkg/runtime" 28 | schema "k8s.io/apimachinery/pkg/runtime/schema" 29 | cache "k8s.io/client-go/tools/cache" 30 | versioned "k8s.io/sample-controller/pkg/generated/clientset/versioned" 31 | internalinterfaces "k8s.io/sample-controller/pkg/generated/informers/externalversions/internalinterfaces" 32 | samplecontroller "k8s.io/sample-controller/pkg/generated/informers/externalversions/samplecontroller" 33 | ) 34 | 35 | // SharedInformerOption defines the functional option type for SharedInformerFactory. 36 | type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory 37 | 38 | type sharedInformerFactory struct { 39 | client versioned.Interface 40 | namespace string 41 | tweakListOptions internalinterfaces.TweakListOptionsFunc 42 | lock sync.Mutex 43 | defaultResync time.Duration 44 | customResync map[reflect.Type]time.Duration 45 | transform cache.TransformFunc 46 | 47 | informers map[reflect.Type]cache.SharedIndexInformer 48 | // startedInformers is used for tracking which informers have been started. 49 | // This allows Start() to be called multiple times safely. 50 | startedInformers map[reflect.Type]bool 51 | // wg tracks how many goroutines were started. 52 | wg sync.WaitGroup 53 | // shuttingDown is true when Shutdown has been called. It may still be running 54 | // because it needs to wait for goroutines. 55 | shuttingDown bool 56 | } 57 | 58 | // WithCustomResyncConfig sets a custom resync period for the specified informer types. 59 | func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption { 60 | return func(factory *sharedInformerFactory) *sharedInformerFactory { 61 | for k, v := range resyncConfig { 62 | factory.customResync[reflect.TypeOf(k)] = v 63 | } 64 | return factory 65 | } 66 | } 67 | 68 | // WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory. 69 | func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption { 70 | return func(factory *sharedInformerFactory) *sharedInformerFactory { 71 | factory.tweakListOptions = tweakListOptions 72 | return factory 73 | } 74 | } 75 | 76 | // WithNamespace limits the SharedInformerFactory to the specified namespace. 77 | func WithNamespace(namespace string) SharedInformerOption { 78 | return func(factory *sharedInformerFactory) *sharedInformerFactory { 79 | factory.namespace = namespace 80 | return factory 81 | } 82 | } 83 | 84 | // WithTransform sets a transform on all informers. 85 | func WithTransform(transform cache.TransformFunc) SharedInformerOption { 86 | return func(factory *sharedInformerFactory) *sharedInformerFactory { 87 | factory.transform = transform 88 | return factory 89 | } 90 | } 91 | 92 | // NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces. 93 | func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory { 94 | return NewSharedInformerFactoryWithOptions(client, defaultResync) 95 | } 96 | 97 | // NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. 98 | // Listers obtained via this SharedInformerFactory will be subject to the same filters 99 | // as specified here. 100 | // Deprecated: Please use NewSharedInformerFactoryWithOptions instead 101 | func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory { 102 | return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions)) 103 | } 104 | 105 | // NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options. 106 | func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory { 107 | factory := &sharedInformerFactory{ 108 | client: client, 109 | namespace: v1.NamespaceAll, 110 | defaultResync: defaultResync, 111 | informers: make(map[reflect.Type]cache.SharedIndexInformer), 112 | startedInformers: make(map[reflect.Type]bool), 113 | customResync: make(map[reflect.Type]time.Duration), 114 | } 115 | 116 | // Apply all options 117 | for _, opt := range options { 118 | factory = opt(factory) 119 | } 120 | 121 | return factory 122 | } 123 | 124 | func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { 125 | f.lock.Lock() 126 | defer f.lock.Unlock() 127 | 128 | if f.shuttingDown { 129 | return 130 | } 131 | 132 | for informerType, informer := range f.informers { 133 | if !f.startedInformers[informerType] { 134 | f.wg.Add(1) 135 | // We need a new variable in each loop iteration, 136 | // otherwise the goroutine would use the loop variable 137 | // and that keeps changing. 138 | informer := informer 139 | go func() { 140 | defer f.wg.Done() 141 | informer.Run(stopCh) 142 | }() 143 | f.startedInformers[informerType] = true 144 | } 145 | } 146 | } 147 | 148 | func (f *sharedInformerFactory) Shutdown() { 149 | f.lock.Lock() 150 | f.shuttingDown = true 151 | f.lock.Unlock() 152 | 153 | // Will return immediately if there is nothing to wait for. 154 | f.wg.Wait() 155 | } 156 | 157 | func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { 158 | informers := func() map[reflect.Type]cache.SharedIndexInformer { 159 | f.lock.Lock() 160 | defer f.lock.Unlock() 161 | 162 | informers := map[reflect.Type]cache.SharedIndexInformer{} 163 | for informerType, informer := range f.informers { 164 | if f.startedInformers[informerType] { 165 | informers[informerType] = informer 166 | } 167 | } 168 | return informers 169 | }() 170 | 171 | res := map[reflect.Type]bool{} 172 | for informType, informer := range informers { 173 | res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) 174 | } 175 | return res 176 | } 177 | 178 | // InformerFor returns the SharedIndexInformer for obj using an internal 179 | // client. 180 | func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { 181 | f.lock.Lock() 182 | defer f.lock.Unlock() 183 | 184 | informerType := reflect.TypeOf(obj) 185 | informer, exists := f.informers[informerType] 186 | if exists { 187 | return informer 188 | } 189 | 190 | resyncPeriod, exists := f.customResync[informerType] 191 | if !exists { 192 | resyncPeriod = f.defaultResync 193 | } 194 | 195 | informer = newFunc(f.client, resyncPeriod) 196 | informer.SetTransform(f.transform) 197 | f.informers[informerType] = informer 198 | 199 | return informer 200 | } 201 | 202 | // SharedInformerFactory provides shared informers for resources in all known 203 | // API group versions. 204 | // 205 | // It is typically used like this: 206 | // 207 | // ctx, cancel := context.Background() 208 | // defer cancel() 209 | // factory := NewSharedInformerFactory(client, resyncPeriod) 210 | // defer factory.WaitForStop() // Returns immediately if nothing was started. 211 | // genericInformer := factory.ForResource(resource) 212 | // typedInformer := factory.SomeAPIGroup().V1().SomeType() 213 | // factory.Start(ctx.Done()) // Start processing these informers. 214 | // synced := factory.WaitForCacheSync(ctx.Done()) 215 | // for v, ok := range synced { 216 | // if !ok { 217 | // fmt.Fprintf(os.Stderr, "caches failed to sync: %v", v) 218 | // return 219 | // } 220 | // } 221 | // 222 | // // Creating informers can also be created after Start, but then 223 | // // Start must be called again: 224 | // anotherGenericInformer := factory.ForResource(resource) 225 | // factory.Start(ctx.Done()) 226 | type SharedInformerFactory interface { 227 | internalinterfaces.SharedInformerFactory 228 | 229 | // Start initializes all requested informers. They are handled in goroutines 230 | // which run until the stop channel gets closed. 231 | // Warning: Start does not block. When run in a go-routine, it will race with a later WaitForCacheSync. 232 | Start(stopCh <-chan struct{}) 233 | 234 | // Shutdown marks a factory as shutting down. At that point no new 235 | // informers can be started anymore and Start will return without 236 | // doing anything. 237 | // 238 | // In addition, Shutdown blocks until all goroutines have terminated. For that 239 | // to happen, the close channel(s) that they were started with must be closed, 240 | // either before Shutdown gets called or while it is waiting. 241 | // 242 | // Shutdown may be called multiple times, even concurrently. All such calls will 243 | // block until all goroutines have terminated. 244 | Shutdown() 245 | 246 | // WaitForCacheSync blocks until all started informers' caches were synced 247 | // or the stop channel gets closed. 248 | WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool 249 | 250 | // ForResource gives generic access to a shared informer of the matching type. 251 | ForResource(resource schema.GroupVersionResource) (GenericInformer, error) 252 | 253 | // InformerFor returns the SharedIndexInformer for obj using an internal 254 | // client. 255 | InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer 256 | 257 | Samplecontroller() samplecontroller.Interface 258 | } 259 | 260 | func (f *sharedInformerFactory) Samplecontroller() samplecontroller.Interface { 261 | return samplecontroller.New(f, f.namespace, f.tweakListOptions) 262 | } 263 | -------------------------------------------------------------------------------- /pkg/generated/informers/externalversions/generic.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package externalversions 20 | 21 | import ( 22 | fmt "fmt" 23 | 24 | schema "k8s.io/apimachinery/pkg/runtime/schema" 25 | cache "k8s.io/client-go/tools/cache" 26 | v1alpha1 "k8s.io/sample-controller/pkg/apis/samplecontroller/v1alpha1" 27 | ) 28 | 29 | // GenericInformer is type of SharedIndexInformer which will locate and delegate to other 30 | // sharedInformers based on type 31 | type GenericInformer interface { 32 | Informer() cache.SharedIndexInformer 33 | Lister() cache.GenericLister 34 | } 35 | 36 | type genericInformer struct { 37 | informer cache.SharedIndexInformer 38 | resource schema.GroupResource 39 | } 40 | 41 | // Informer returns the SharedIndexInformer. 42 | func (f *genericInformer) Informer() cache.SharedIndexInformer { 43 | return f.informer 44 | } 45 | 46 | // Lister returns the GenericLister. 47 | func (f *genericInformer) Lister() cache.GenericLister { 48 | return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) 49 | } 50 | 51 | // ForResource gives generic access to a shared informer of the matching type 52 | // TODO extend this to unknown resources with a client pool 53 | func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { 54 | switch resource { 55 | // Group=samplecontroller.k8s.io, Version=v1alpha1 56 | case v1alpha1.SchemeGroupVersion.WithResource("foos"): 57 | return &genericInformer{resource: resource.GroupResource(), informer: f.Samplecontroller().V1alpha1().Foos().Informer()}, nil 58 | 59 | } 60 | 61 | return nil, fmt.Errorf("no informer found for %v", resource) 62 | } 63 | -------------------------------------------------------------------------------- /pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package internalinterfaces 20 | 21 | import ( 22 | time "time" 23 | 24 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 | runtime "k8s.io/apimachinery/pkg/runtime" 26 | cache "k8s.io/client-go/tools/cache" 27 | versioned "k8s.io/sample-controller/pkg/generated/clientset/versioned" 28 | ) 29 | 30 | // NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer. 31 | type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer 32 | 33 | // SharedInformerFactory a small interface to allow for adding an informer without an import cycle 34 | type SharedInformerFactory interface { 35 | Start(stopCh <-chan struct{}) 36 | InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer 37 | } 38 | 39 | // TweakListOptionsFunc is a function that transforms a v1.ListOptions. 40 | type TweakListOptionsFunc func(*v1.ListOptions) 41 | -------------------------------------------------------------------------------- /pkg/generated/informers/externalversions/samplecontroller/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package samplecontroller 20 | 21 | import ( 22 | internalinterfaces "k8s.io/sample-controller/pkg/generated/informers/externalversions/internalinterfaces" 23 | v1alpha1 "k8s.io/sample-controller/pkg/generated/informers/externalversions/samplecontroller/v1alpha1" 24 | ) 25 | 26 | // Interface provides access to each of this group's versions. 27 | type Interface interface { 28 | // V1alpha1 provides access to shared informers for resources in V1alpha1. 29 | V1alpha1() v1alpha1.Interface 30 | } 31 | 32 | type group struct { 33 | factory internalinterfaces.SharedInformerFactory 34 | namespace string 35 | tweakListOptions internalinterfaces.TweakListOptionsFunc 36 | } 37 | 38 | // New returns a new Interface. 39 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 40 | return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 41 | } 42 | 43 | // V1alpha1 returns a new v1alpha1.Interface. 44 | func (g *group) V1alpha1() v1alpha1.Interface { 45 | return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) 46 | } 47 | -------------------------------------------------------------------------------- /pkg/generated/informers/externalversions/samplecontroller/v1alpha1/foo.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package v1alpha1 20 | 21 | import ( 22 | context "context" 23 | time "time" 24 | 25 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 | runtime "k8s.io/apimachinery/pkg/runtime" 27 | watch "k8s.io/apimachinery/pkg/watch" 28 | cache "k8s.io/client-go/tools/cache" 29 | apissamplecontrollerv1alpha1 "k8s.io/sample-controller/pkg/apis/samplecontroller/v1alpha1" 30 | versioned "k8s.io/sample-controller/pkg/generated/clientset/versioned" 31 | internalinterfaces "k8s.io/sample-controller/pkg/generated/informers/externalversions/internalinterfaces" 32 | samplecontrollerv1alpha1 "k8s.io/sample-controller/pkg/generated/listers/samplecontroller/v1alpha1" 33 | ) 34 | 35 | // FooInformer provides access to a shared informer and lister for 36 | // Foos. 37 | type FooInformer interface { 38 | Informer() cache.SharedIndexInformer 39 | Lister() samplecontrollerv1alpha1.FooLister 40 | } 41 | 42 | type fooInformer struct { 43 | factory internalinterfaces.SharedInformerFactory 44 | tweakListOptions internalinterfaces.TweakListOptionsFunc 45 | namespace string 46 | } 47 | 48 | // NewFooInformer constructs a new informer for Foo type. 49 | // Always prefer using an informer factory to get a shared informer instead of getting an independent 50 | // one. This reduces memory footprint and number of connections to the server. 51 | func NewFooInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { 52 | return NewFilteredFooInformer(client, namespace, resyncPeriod, indexers, nil) 53 | } 54 | 55 | // NewFilteredFooInformer constructs a new informer for Foo type. 56 | // Always prefer using an informer factory to get a shared informer instead of getting an independent 57 | // one. This reduces memory footprint and number of connections to the server. 58 | func NewFilteredFooInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { 59 | return cache.NewSharedIndexInformer( 60 | &cache.ListWatch{ 61 | ListFunc: func(options v1.ListOptions) (runtime.Object, error) { 62 | if tweakListOptions != nil { 63 | tweakListOptions(&options) 64 | } 65 | return client.SamplecontrollerV1alpha1().Foos(namespace).List(context.Background(), options) 66 | }, 67 | WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { 68 | if tweakListOptions != nil { 69 | tweakListOptions(&options) 70 | } 71 | return client.SamplecontrollerV1alpha1().Foos(namespace).Watch(context.Background(), options) 72 | }, 73 | ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { 74 | if tweakListOptions != nil { 75 | tweakListOptions(&options) 76 | } 77 | return client.SamplecontrollerV1alpha1().Foos(namespace).List(ctx, options) 78 | }, 79 | WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { 80 | if tweakListOptions != nil { 81 | tweakListOptions(&options) 82 | } 83 | return client.SamplecontrollerV1alpha1().Foos(namespace).Watch(ctx, options) 84 | }, 85 | }, 86 | &apissamplecontrollerv1alpha1.Foo{}, 87 | resyncPeriod, 88 | indexers, 89 | ) 90 | } 91 | 92 | func (f *fooInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { 93 | return NewFilteredFooInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) 94 | } 95 | 96 | func (f *fooInformer) Informer() cache.SharedIndexInformer { 97 | return f.factory.InformerFor(&apissamplecontrollerv1alpha1.Foo{}, f.defaultInformer) 98 | } 99 | 100 | func (f *fooInformer) Lister() samplecontrollerv1alpha1.FooLister { 101 | return samplecontrollerv1alpha1.NewFooLister(f.Informer().GetIndexer()) 102 | } 103 | -------------------------------------------------------------------------------- /pkg/generated/informers/externalversions/samplecontroller/v1alpha1/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package v1alpha1 20 | 21 | import ( 22 | internalinterfaces "k8s.io/sample-controller/pkg/generated/informers/externalversions/internalinterfaces" 23 | ) 24 | 25 | // Interface provides access to all the informers in this group version. 26 | type Interface interface { 27 | // Foos returns a FooInformer. 28 | Foos() FooInformer 29 | } 30 | 31 | type version struct { 32 | factory internalinterfaces.SharedInformerFactory 33 | namespace string 34 | tweakListOptions internalinterfaces.TweakListOptionsFunc 35 | } 36 | 37 | // New returns a new Interface. 38 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 39 | return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 40 | } 41 | 42 | // Foos returns a FooInformer. 43 | func (v *version) Foos() FooInformer { 44 | return &fooInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} 45 | } 46 | -------------------------------------------------------------------------------- /pkg/generated/listers/samplecontroller/v1alpha1/expansion_generated.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by lister-gen. DO NOT EDIT. 18 | 19 | package v1alpha1 20 | 21 | // FooListerExpansion allows custom methods to be added to 22 | // FooLister. 23 | type FooListerExpansion interface{} 24 | 25 | // FooNamespaceListerExpansion allows custom methods to be added to 26 | // FooNamespaceLister. 27 | type FooNamespaceListerExpansion interface{} 28 | -------------------------------------------------------------------------------- /pkg/generated/listers/samplecontroller/v1alpha1/foo.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by lister-gen. DO NOT EDIT. 18 | 19 | package v1alpha1 20 | 21 | import ( 22 | labels "k8s.io/apimachinery/pkg/labels" 23 | listers "k8s.io/client-go/listers" 24 | cache "k8s.io/client-go/tools/cache" 25 | samplecontrollerv1alpha1 "k8s.io/sample-controller/pkg/apis/samplecontroller/v1alpha1" 26 | ) 27 | 28 | // FooLister helps list Foos. 29 | // All objects returned here must be treated as read-only. 30 | type FooLister interface { 31 | // List lists all Foos in the indexer. 32 | // Objects returned here must be treated as read-only. 33 | List(selector labels.Selector) (ret []*samplecontrollerv1alpha1.Foo, err error) 34 | // Foos returns an object that can list and get Foos. 35 | Foos(namespace string) FooNamespaceLister 36 | FooListerExpansion 37 | } 38 | 39 | // fooLister implements the FooLister interface. 40 | type fooLister struct { 41 | listers.ResourceIndexer[*samplecontrollerv1alpha1.Foo] 42 | } 43 | 44 | // NewFooLister returns a new FooLister. 45 | func NewFooLister(indexer cache.Indexer) FooLister { 46 | return &fooLister{listers.New[*samplecontrollerv1alpha1.Foo](indexer, samplecontrollerv1alpha1.Resource("foo"))} 47 | } 48 | 49 | // Foos returns an object that can list and get Foos. 50 | func (s *fooLister) Foos(namespace string) FooNamespaceLister { 51 | return fooNamespaceLister{listers.NewNamespaced[*samplecontrollerv1alpha1.Foo](s.ResourceIndexer, namespace)} 52 | } 53 | 54 | // FooNamespaceLister helps list and get Foos. 55 | // All objects returned here must be treated as read-only. 56 | type FooNamespaceLister interface { 57 | // List lists all Foos in the indexer for a given namespace. 58 | // Objects returned here must be treated as read-only. 59 | List(selector labels.Selector) (ret []*samplecontrollerv1alpha1.Foo, err error) 60 | // Get retrieves the Foo from the indexer for a given namespace and name. 61 | // Objects returned here must be treated as read-only. 62 | Get(name string) (*samplecontrollerv1alpha1.Foo, error) 63 | FooNamespaceListerExpansion 64 | } 65 | 66 | // fooNamespaceLister implements the FooNamespaceLister 67 | // interface. 68 | type fooNamespaceLister struct { 69 | listers.ResourceIndexer[*samplecontrollerv1alpha1.Foo] 70 | } 71 | -------------------------------------------------------------------------------- /pkg/signals/signal.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package signals 18 | 19 | import ( 20 | "context" 21 | "os" 22 | "os/signal" 23 | ) 24 | 25 | var onlyOneSignalHandler = make(chan struct{}) 26 | 27 | // SetupSignalHandler registered for SIGTERM and SIGINT. A context is returned 28 | // which is cancelled on one of these signals. If a second signal is caught, 29 | // the program is terminated with exit code 1. 30 | func SetupSignalHandler() context.Context { 31 | close(onlyOneSignalHandler) // panics when called twice 32 | 33 | c := make(chan os.Signal, 2) 34 | ctx, cancel := context.WithCancel(context.Background()) 35 | signal.Notify(c, shutdownSignals...) 36 | go func() { 37 | <-c 38 | cancel() 39 | <-c 40 | os.Exit(1) // second signal. Exit directly. 41 | }() 42 | 43 | return ctx 44 | } 45 | -------------------------------------------------------------------------------- /pkg/signals/signal_posix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | // +build !windows 3 | 4 | /* 5 | Copyright 2017 The Kubernetes Authors. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | */ 19 | 20 | package signals 21 | 22 | import ( 23 | "os" 24 | "syscall" 25 | ) 26 | 27 | var shutdownSignals = []os.Signal{os.Interrupt, syscall.SIGTERM} 28 | -------------------------------------------------------------------------------- /pkg/signals/signal_windows.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package signals 18 | 19 | import ( 20 | "os" 21 | ) 22 | 23 | var shutdownSignals = []os.Signal{os.Interrupt} 24 | --------------------------------------------------------------------------------