├── .gitignore ├── LICENSE ├── README.md ├── archive └── dive-into-kubernetes-informer │ ├── 1-list-watcher │ ├── clientset.go │ └── main.go │ ├── 10-sloth-controller │ └── main.go │ ├── 11-crd-informer │ ├── api │ │ └── stable.wbsnail.com │ │ │ └── v1 │ │ │ ├── doc.go │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ └── zz_generated.deepcopy.go │ ├── boilerplate.go.txt │ ├── client │ │ ├── clientset │ │ │ └── versioned │ │ │ │ ├── clientset.go │ │ │ │ ├── doc.go │ │ │ │ ├── fake │ │ │ │ ├── clientset_generated.go │ │ │ │ ├── doc.go │ │ │ │ └── register.go │ │ │ │ ├── scheme │ │ │ │ ├── doc.go │ │ │ │ └── register.go │ │ │ │ └── typed │ │ │ │ └── stable.wbsnail.com │ │ │ │ └── v1 │ │ │ │ ├── doc.go │ │ │ │ ├── fake │ │ │ │ ├── doc.go │ │ │ │ ├── fake_rabbit.go │ │ │ │ └── fake_stable.wbsnail.com_client.go │ │ │ │ ├── generated_expansion.go │ │ │ │ ├── rabbit.go │ │ │ │ └── stable.wbsnail.com_client.go │ │ ├── informers │ │ │ └── externalversions │ │ │ │ ├── factory.go │ │ │ │ ├── generic.go │ │ │ │ ├── internalinterfaces │ │ │ │ └── factory_interfaces.go │ │ │ │ └── stable.wbsnail.com │ │ │ │ ├── interface.go │ │ │ │ └── v1 │ │ │ │ ├── interface.go │ │ │ │ └── rabbit.go │ │ └── listers │ │ │ └── stable.wbsnail.com │ │ │ └── v1 │ │ │ ├── expansion_generated.go │ │ │ └── rabbit.go │ ├── crd.yaml │ ├── custom-resource.yaml │ ├── generate-groups.sh │ └── main.go │ ├── 12-dynamic-informer │ └── main.go │ ├── 2-reflector │ ├── clientset.go │ ├── lw.go │ └── main.go │ ├── 3-controller │ ├── clientset.go │ ├── lw.go │ ├── main.go │ └── store.go │ ├── 4-informer │ ├── clientset.go │ ├── lw.go │ └── main.go │ ├── 5-shared-informer │ ├── clientset.go │ ├── lw.go │ └── main.go │ ├── 6-indexer │ ├── clientset.go │ ├── lw.go │ └── main.go │ ├── 7-indexer-informer │ ├── clientset.go │ ├── lw.go │ ├── main.go │ └── probe-server.go │ ├── 8-shared-index-informer │ ├── clientset.go │ ├── lw.go │ ├── main.go │ └── probe-server.go │ ├── 9-shared-informer-factory │ ├── clientset.go │ └── main.go │ ├── go.mod │ ├── go.sum │ ├── informer.drawio │ └── probe │ └── main.go ├── go.mod ├── go.sum └── lab ├── csrf-attack ├── attacker-website │ └── main.go ├── go.mod ├── go.sum ├── server-protected │ └── main.go └── server-volunable │ └── main.go ├── shadow-string ├── go.mod ├── go.sum └── main.go ├── unix-socket-broadcast ├── client │ └── main.go ├── go.mod ├── go.sum └── server │ └── main.go ├── unix-socket-echo ├── client │ └── main.go ├── go.mod ├── go.sum └── server │ └── main.go ├── upgrade-plan └── main.go └── xss-attack ├── go.mod ├── go.sum └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/go,node,macos,linux,vscode,windows,jetbrains 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=go,node,macos,linux,vscode,windows,jetbrains 4 | 5 | ### Go ### 6 | # Binaries for programs and plugins 7 | *.exe 8 | *.exe~ 9 | *.dll 10 | *.so 11 | *.dylib 12 | 13 | # Test binary, built with `go test -c` 14 | *.test 15 | 16 | # Output of the go coverage tool, specifically when used with LiteIDE 17 | *.out 18 | 19 | # Dependency directories (remove the comment below to include it) 20 | # vendor/ 21 | 22 | ### Go Patch ### 23 | /vendor/ 24 | /Godeps/ 25 | 26 | ### JetBrains ### 27 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 28 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 29 | 30 | # User-specific stuff 31 | .idea/**/workspace.xml 32 | .idea/**/tasks.xml 33 | .idea/**/usage.statistics.xml 34 | .idea/**/dictionaries 35 | .idea/**/shelf 36 | 37 | # Generated files 38 | .idea/**/contentModel.xml 39 | 40 | # Sensitive or high-churn files 41 | .idea/**/dataSources/ 42 | .idea/**/dataSources.ids 43 | .idea/**/dataSources.local.xml 44 | .idea/**/sqlDataSources.xml 45 | .idea/**/dynamic.xml 46 | .idea/**/uiDesigner.xml 47 | .idea/**/dbnavigator.xml 48 | 49 | # Gradle 50 | .idea/**/gradle.xml 51 | .idea/**/libraries 52 | 53 | # Gradle and Maven with auto-import 54 | # When using Gradle or Maven with auto-import, you should exclude module files, 55 | # since they will be recreated, and may cause churn. Uncomment if using 56 | # auto-import. 57 | # .idea/artifacts 58 | # .idea/compiler.xml 59 | # .idea/jarRepositories.xml 60 | # .idea/modules.xml 61 | # .idea/*.iml 62 | # .idea/modules 63 | # *.iml 64 | # *.ipr 65 | 66 | # CMake 67 | cmake-build-*/ 68 | 69 | # Mongo Explorer plugin 70 | .idea/**/mongoSettings.xml 71 | 72 | # File-based project format 73 | *.iws 74 | 75 | # IntelliJ 76 | out/ 77 | 78 | # mpeltonen/sbt-idea plugin 79 | .idea_modules/ 80 | 81 | # JIRA plugin 82 | atlassian-ide-plugin.xml 83 | 84 | # Cursive Clojure plugin 85 | .idea/replstate.xml 86 | 87 | # Crashlytics plugin (for Android Studio and IntelliJ) 88 | com_crashlytics_export_strings.xml 89 | crashlytics.properties 90 | crashlytics-build.properties 91 | fabric.properties 92 | 93 | # Editor-based Rest Client 94 | .idea/httpRequests 95 | 96 | # Android studio 3.1+ serialized cache file 97 | .idea/caches/build_file_checksums.ser 98 | 99 | ### JetBrains Patch ### 100 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 101 | 102 | # *.iml 103 | # modules.xml 104 | # .idea/misc.xml 105 | # *.ipr 106 | 107 | # Sonarlint plugin 108 | # https://plugins.jetbrains.com/plugin/7973-sonarlint 109 | .idea/**/sonarlint/ 110 | 111 | # SonarQube Plugin 112 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin 113 | .idea/**/sonarIssues.xml 114 | 115 | # Markdown Navigator plugin 116 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced 117 | .idea/**/markdown-navigator.xml 118 | .idea/**/markdown-navigator-enh.xml 119 | .idea/**/markdown-navigator/ 120 | 121 | # Cache file creation bug 122 | # See https://youtrack.jetbrains.com/issue/JBR-2257 123 | .idea/$CACHE_FILE$ 124 | 125 | # CodeStream plugin 126 | # https://plugins.jetbrains.com/plugin/12206-codestream 127 | .idea/codestream.xml 128 | 129 | ### Linux ### 130 | *~ 131 | 132 | # temporary files which can be created if a process still has a handle open of a deleted file 133 | .fuse_hidden* 134 | 135 | # KDE directory preferences 136 | .directory 137 | 138 | # Linux trash folder which might appear on any partition or disk 139 | .Trash-* 140 | 141 | # .nfs files are created when an open file is removed but is still being accessed 142 | .nfs* 143 | 144 | ### macOS ### 145 | # General 146 | .DS_Store 147 | .AppleDouble 148 | .LSOverride 149 | 150 | # Icon must end with two \r 151 | Icon 152 | 153 | 154 | # Thumbnails 155 | ._* 156 | 157 | # Files that might appear in the root of a volume 158 | .DocumentRevisions-V100 159 | .fseventsd 160 | .Spotlight-V100 161 | .TemporaryItems 162 | .Trashes 163 | .VolumeIcon.icns 164 | .com.apple.timemachine.donotpresent 165 | 166 | # Directories potentially created on remote AFP share 167 | .AppleDB 168 | .AppleDesktop 169 | Network Trash Folder 170 | Temporary Items 171 | .apdisk 172 | 173 | ### Node ### 174 | # Logs 175 | logs 176 | *.log 177 | npm-debug.log* 178 | yarn-debug.log* 179 | yarn-error.log* 180 | lerna-debug.log* 181 | 182 | # Diagnostic reports (https://nodejs.org/api/report.html) 183 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 184 | 185 | # Runtime data 186 | pids 187 | *.pid 188 | *.seed 189 | *.pid.lock 190 | 191 | # Directory for instrumented libs generated by jscoverage/JSCover 192 | lib-cov 193 | 194 | # Coverage directory used by tools like istanbul 195 | coverage 196 | *.lcov 197 | 198 | # nyc test coverage 199 | .nyc_output 200 | 201 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 202 | .grunt 203 | 204 | # Bower dependency directory (https://bower.io/) 205 | bower_components 206 | 207 | # node-waf configuration 208 | .lock-wscript 209 | 210 | # Compiled binary addons (https://nodejs.org/api/addons.html) 211 | build/Release 212 | 213 | # Dependency directories 214 | node_modules/ 215 | jspm_packages/ 216 | 217 | # TypeScript v1 declaration files 218 | typings/ 219 | 220 | # TypeScript cache 221 | *.tsbuildinfo 222 | 223 | # Optional npm cache directory 224 | .npm 225 | 226 | # Optional eslint cache 227 | .eslintcache 228 | 229 | # Optional stylelint cache 230 | .stylelintcache 231 | 232 | # Microbundle cache 233 | .rpt2_cache/ 234 | .rts2_cache_cjs/ 235 | .rts2_cache_es/ 236 | .rts2_cache_umd/ 237 | 238 | # Optional REPL history 239 | .node_repl_history 240 | 241 | # Output of 'npm pack' 242 | *.tgz 243 | 244 | # Yarn Integrity file 245 | .yarn-integrity 246 | 247 | # dotenv environment variables file 248 | .env 249 | .env.test 250 | .env*.local 251 | 252 | # parcel-bundler cache (https://parceljs.org/) 253 | .cache 254 | .parcel-cache 255 | 256 | # Next.js build output 257 | .next 258 | 259 | # Nuxt.js build / generate output 260 | .nuxt 261 | dist 262 | 263 | # Storybook build outputs 264 | .out 265 | .storybook-out 266 | storybook-static 267 | 268 | # rollup.js default build output 269 | dist/ 270 | 271 | # Gatsby files 272 | .cache/ 273 | # Comment in the public line in if your project uses Gatsby and not Next.js 274 | # https://nextjs.org/blog/next-9-1#public-directory-support 275 | # public 276 | 277 | # vuepress build output 278 | .vuepress/dist 279 | 280 | # Serverless directories 281 | .serverless/ 282 | 283 | # FuseBox cache 284 | .fusebox/ 285 | 286 | # DynamoDB Local files 287 | .dynamodb/ 288 | 289 | # TernJS port file 290 | .tern-port 291 | 292 | # Stores VSCode versions used for testing VSCode extensions 293 | .vscode-test 294 | 295 | # Temporary folders 296 | tmp/ 297 | temp/ 298 | 299 | ### vscode ### 300 | .vscode/* 301 | !.vscode/settings.json 302 | !.vscode/tasks.json 303 | !.vscode/launch.json 304 | !.vscode/extensions.json 305 | *.code-workspace 306 | 307 | ### Windows ### 308 | # Windows thumbnail cache files 309 | Thumbs.db 310 | Thumbs.db:encryptable 311 | ehthumbs.db 312 | ehthumbs_vista.db 313 | 314 | # Dump file 315 | *.stackdump 316 | 317 | # Folder config file 318 | [Dd]esktop.ini 319 | 320 | # Recycle Bin used on file shares 321 | $RECYCLE.BIN/ 322 | 323 | # Windows Installer files 324 | *.cab 325 | *.msi 326 | *.msix 327 | *.msm 328 | *.msp 329 | 330 | # Windows shortcuts 331 | *.lnk 332 | 333 | # End of https://www.toptal.com/developers/gitignore/api/go,node,macos,linux,vscode,windows,jetbrains -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # articles 2 | Articles written by me. 3 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/1-list-watcher/clientset.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/spongeprojects/magicconch" 5 | "k8s.io/client-go/kubernetes" 6 | "k8s.io/client-go/tools/clientcmd" 7 | "os" 8 | ) 9 | 10 | func mustClientset() kubernetes.Interface { 11 | kubeconfig := os.Getenv("KUBECONFIG") 12 | 13 | config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) 14 | magicconch.Must(err) 15 | 16 | clientset, err := kubernetes.NewForConfig(config) 17 | magicconch.Must(err) 18 | 19 | return clientset 20 | } 21 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/1-list-watcher/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/spongeprojects/magicconch" 6 | corev1 "k8s.io/api/core/v1" 7 | "k8s.io/apimachinery/pkg/api/meta" 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | "k8s.io/apimachinery/pkg/fields" 10 | "k8s.io/client-go/tools/cache" 11 | "os" 12 | "os/signal" 13 | ) 14 | 15 | // newConfigMapsListerWatcher 用于创建 tmp namespace 下 configmaps 资源的 ListerWatcher 实例 16 | func newConfigMapsListerWatcher() cache.ListerWatcher { 17 | clientset := mustClientset() // 前面有说明 18 | client := clientset.CoreV1().RESTClient() // 客户端,请求器 19 | resource := "configmaps" // GET 请求参数之一 20 | namespace := "tmp" // GET 请求参数之一 21 | selector := fields.Everything() // GET 请求参数之一 22 | lw := cache.NewListWatchFromClient(client, resource, namespace, selector) 23 | return lw 24 | } 25 | 26 | func main() { 27 | fmt.Println("----- 1-list-watcher -----") 28 | 29 | lw := newConfigMapsListerWatcher() 30 | 31 | // list 的类型为 runtime.Object, 需要经过反射或类型转换才能使用, 32 | // 传入的 ListOptions 中的 FieldSelector 始终会被替换为前面的 selector 33 | list, err := lw.List(metav1.ListOptions{}) 34 | magicconch.Must(err) 35 | 36 | // meta 包封装了一些处理 runtime.Object 对象的方法,屏蔽了反射和类型转换的过程, 37 | // 提取出的 items 类型为 []runtime.Object 38 | items, err := meta.ExtractList(list) 39 | magicconch.Must(err) 40 | 41 | fmt.Println("Initial list:") 42 | 43 | for _, item := range items { 44 | configMap, ok := item.(*corev1.ConfigMap) 45 | if !ok { 46 | return 47 | } 48 | fmt.Println(configMap.Name) 49 | 50 | // 如果只关注 meta 信息,也可以不进行类型转换,而是使用 meta.Accessor 方法 51 | // accessor, err := meta.Accessor(item) 52 | // magicconch.Must(err) 53 | // fmt.Println(accessor.GetName()) 54 | } 55 | 56 | listMetaInterface, err := meta.ListAccessor(list) 57 | magicconch.Must(err) 58 | 59 | // resourceVersion 在同步过程中非常重要,看下面它在 Watch 接口中的使用 60 | resourceVersion := listMetaInterface.GetResourceVersion() 61 | 62 | // w 的类型为 watch.Interface,提供 ResultChan 方法读取事件, 63 | // 和 List 一样,传入的 ListOptions 中的 FieldSelector 始终会被替换为前面的 selector, 64 | // ResourceVersion 是 Watch 时非常重要的参数, 65 | // 它代表一次客户端与服务器进行交互时对应的资源版本, 66 | // 结合另一个参数 ResourceVersionMatch,表示本次请求对 ResourceVersion 的筛选, 67 | // 比如以下请求表示:获取版本新于 resourceVersion 的事件。 68 | // 在考虑连接中断和定期重新同步 (resync) 的情况下, 69 | // 对 ResourceVersion 的管理就变得更为复杂,我们先不考虑这些情况。 70 | w, err := lw.Watch(metav1.ListOptions{ 71 | ResourceVersion: resourceVersion, 72 | }) 73 | magicconch.Must(err) 74 | 75 | stopCh := make(chan os.Signal) 76 | signal.Notify(stopCh, os.Interrupt) 77 | 78 | fmt.Println("Start watching...") 79 | 80 | loop: 81 | for { 82 | select { 83 | case <-stopCh: 84 | fmt.Println("Interrupted") 85 | break loop 86 | case event, ok := <-w.ResultChan(): 87 | if !ok { 88 | fmt.Println("Broken channel") 89 | break loop 90 | } 91 | configMap, ok := event.Object.(*corev1.ConfigMap) 92 | if !ok { 93 | return 94 | } 95 | fmt.Printf("%s: %s\n", event.Type, configMap.Name) 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/10-sloth-controller/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/spongeprojects/magicconch" 7 | "k8s.io/apimachinery/pkg/util/rand" 8 | "k8s.io/apimachinery/pkg/util/runtime" 9 | "k8s.io/apimachinery/pkg/util/wait" 10 | "k8s.io/client-go/informers" 11 | "k8s.io/client-go/kubernetes" 12 | listerscorev1 "k8s.io/client-go/listers/core/v1" 13 | "k8s.io/client-go/tools/cache" 14 | "k8s.io/client-go/tools/clientcmd" 15 | "k8s.io/client-go/util/workqueue" 16 | "k8s.io/klog/v2" 17 | "os" 18 | "os/signal" 19 | "sync" 20 | "time" 21 | ) 22 | 23 | // SlothController 树懒控制器! 24 | type SlothController struct { 25 | factory informers.SharedInformerFactory 26 | configMapLister listerscorev1.ConfigMapLister 27 | configMapInformer cache.SharedIndexInformer 28 | queue workqueue.RateLimitingInterface 29 | processingItems *sync.WaitGroup 30 | 31 | // maxRetries 树懒们需要重试多少次才会放弃 32 | maxRetries int 33 | // chanceOfFailure 树懒们处理任务有多少概率失败(百分比) 34 | chanceOfFailure int 35 | // nap 树懒睡一觉要多久 36 | nap time.Duration 37 | } 38 | 39 | func NewController( 40 | factory informers.SharedInformerFactory, 41 | queue workqueue.RateLimitingInterface, 42 | chanceOfFailure int, 43 | nap time.Duration, 44 | ) *SlothController { 45 | configMapLister := factory.Core().V1().ConfigMaps().Lister() 46 | configMapInformer := factory.Core().V1().ConfigMaps().Informer() 47 | configMapInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ 48 | UpdateFunc: func(old interface{}, new interface{}) { 49 | key, err := cache.MetaNamespaceKeyFunc(new) 50 | if err == nil { 51 | // 只简单地把 key 放到队列中 52 | klog.Infof("[%s] received", key) 53 | queue.Add(key) 54 | } 55 | }, 56 | }) 57 | 58 | return &SlothController{ 59 | factory: factory, 60 | configMapLister: configMapLister, 61 | configMapInformer: configMapInformer, 62 | queue: queue, 63 | maxRetries: 3, 64 | chanceOfFailure: chanceOfFailure, 65 | nap: nap, 66 | processingItems: &sync.WaitGroup{}, 67 | } 68 | } 69 | 70 | // Run 开始运行控制器直到出错或 stopCh 关闭 71 | func (c *SlothController) Run(sloths int, stopCh chan struct{}) error { 72 | // runtime.HandleCrash 是 Kubernetes 官方提供的 panic recover 方法, 73 | // 提供一个 panic recover 的统一入口, 74 | // 默认只是记录日志,该 panic 还是 panic。 75 | defer runtime.HandleCrash() 76 | // 退出前关闭队列让树懒们不要再接手新任务 77 | defer c.queue.ShutDown() 78 | 79 | klog.Info("SlothController starting...") 80 | 81 | go c.factory.Start(stopCh) 82 | 83 | // 等待首次同步完成 84 | for _, ok := range c.factory.WaitForCacheSync(stopCh) { 85 | if !ok { 86 | return fmt.Errorf("timed out waiting for caches to sync") 87 | } 88 | } 89 | 90 | for i := 0; i < sloths; i++ { 91 | go wait.Until(c.runWorker, time.Second, stopCh) 92 | } 93 | 94 | // 等待 stopCh 关闭 95 | <-stopCh 96 | 97 | // 等待正在执行中的任务完成 98 | klog.Info("waiting for processing items to finish...") 99 | c.processingItems.Wait() 100 | 101 | return nil 102 | } 103 | 104 | func (c *SlothController) runWorker() { 105 | for c.processNextItem() { 106 | } 107 | } 108 | 109 | // processNextItem 用于等待和处理队列中的新任务 110 | func (c *SlothController) processNextItem() bool { 111 | // 阻塞住,等待新任务中... 112 | key, shutdown := c.queue.Get() 113 | if shutdown { 114 | return false // 队列已进入退出状态,不要继续处理 115 | } 116 | 117 | c.processingItems.Add(1) 118 | 119 | // 任务完成后,无论成功与否,都记得标记完成,因为尽管有多只树懒, 120 | // 但对相同 key 的多个任务是不会并行处理的, 121 | // 如果相同 key 有多个事件,不要阻塞处理。 122 | defer c.queue.Done(key) 123 | 124 | result := c.processItem(key) 125 | c.handleErr(key, result) 126 | 127 | c.processingItems.Done() 128 | 129 | return true 130 | } 131 | 132 | // processItem 用于同步处理一个任务 133 | func (c *SlothController) processItem(key interface{}) error { 134 | // 处理任务很慢,因为树懒很懒 135 | time.Sleep(c.nap) 136 | 137 | if rand.Intn(100) < c.chanceOfFailure { 138 | // 睡过头啦! 139 | return fmt.Errorf("zzz... ") 140 | } 141 | 142 | klog.Infof("[%s] processed.", key) 143 | return nil 144 | } 145 | 146 | // handleErr 用于检查任务处理结果,在必要的时候重试 147 | func (c *SlothController) handleErr(key interface{}, result error) { 148 | if result == nil { 149 | // 每次执行成功后清空重试记录。 150 | c.queue.Forget(key) 151 | return 152 | } 153 | 154 | if c.queue.NumRequeues(key) < c.maxRetries { 155 | klog.Warningf("error processing %s: %v", key, result) 156 | // 执行失败,重试 157 | c.queue.AddRateLimited(key) 158 | return 159 | } 160 | 161 | // 重试次数过多,日志记录错误,同时也别忘了清空重试记录 162 | c.queue.Forget(key) 163 | // runtime.HandleError 是 Kubernetes 官方提供的错误响应方法, 164 | // 提供一个错误响应的统一入口。 165 | runtime.HandleError(fmt.Errorf("max retries exceeded, "+ 166 | "dropping item %s out of the queue: %v", key, result)) 167 | } 168 | 169 | func main() { 170 | fmt.Println("----- 10-sloth-controller -----") 171 | 172 | var sloths int 173 | var chanceOfFailure int 174 | var napInSecond int 175 | flag.IntVar(&sloths, "sloths", 1, 176 | "number of sloths") 177 | flag.IntVar(&chanceOfFailure, "chance-of-failure", 0, 178 | "chance of failure in percentage") 179 | flag.IntVar(&napInSecond, "nap", 0, 180 | "how long should the sloth nap (in seconds)") 181 | flag.Parse() 182 | 183 | kubeconfig := os.Getenv("KUBECONFIG") 184 | config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) 185 | magicconch.Must(err) 186 | 187 | clientset, err := kubernetes.NewForConfig(config) 188 | magicconch.Must(err) 189 | 190 | // 创建 SharedInformerFactory 191 | defaultResyncPeriod := time.Hour * 12 192 | informerFactory := informers.NewSharedInformerFactoryWithOptions( 193 | clientset, defaultResyncPeriod, informers.WithNamespace("tmp")) 194 | 195 | // 使用默认配置创建 RateLimitingQueue,这种队列支持重试,同时会记录重试次数 196 | rateLimiter := workqueue.DefaultControllerRateLimiter() 197 | queue := workqueue.NewRateLimitingQueue(rateLimiter) 198 | 199 | controller := NewController(informerFactory, queue, chanceOfFailure, 200 | time.Duration(napInSecond)*time.Second) 201 | 202 | stopCh := make(chan struct{}) 203 | 204 | // 响应中断信号 (Ctrl+C) 205 | interrupted := make(chan os.Signal) 206 | signal.Notify(interrupted, os.Interrupt) 207 | 208 | go func() { 209 | <-interrupted // 第 1 次收到中断信号时关闭 stopCh 210 | close(stopCh) 211 | <-interrupted // 第 2 次收到中断信号时直接退出 212 | os.Exit(1) 213 | }() 214 | 215 | if err := controller.Run(sloths, stopCh); err != nil { 216 | klog.Errorf("SlothController exit with error: %s", err) 217 | } else { 218 | klog.Info("SlothController exit") 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/api/stable.wbsnail.com/v1/doc.go: -------------------------------------------------------------------------------- 1 | // +k8s:deepcopy-gen=package 2 | 3 | package v1 4 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/api/stable.wbsnail.com/v1/register.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 5 | "k8s.io/apimachinery/pkg/runtime" 6 | "k8s.io/apimachinery/pkg/runtime/schema" 7 | ) 8 | 9 | // SchemeGroupVersion is group version used to register these objects 10 | var SchemeGroupVersion = schema.GroupVersion{Group: "stable.wbsnail.com", Version: "v1"} 11 | 12 | // Kind takes an unqualified kind and returns back a Group qualified GroupKind 13 | func Kind(kind string) schema.GroupKind { 14 | return SchemeGroupVersion.WithKind(kind).GroupKind() 15 | } 16 | 17 | // Resource takes an unqualified resource and returns a Group qualified GroupResource 18 | func Resource(resource string) schema.GroupResource { 19 | return SchemeGroupVersion.WithResource(resource).GroupResource() 20 | } 21 | 22 | var ( 23 | // SchemeBuilder initializes a scheme builder 24 | SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) 25 | // AddToScheme is a global function that registers this API group & version to a scheme 26 | AddToScheme = SchemeBuilder.AddToScheme 27 | ) 28 | 29 | // Adds the list of known types to Scheme. 30 | func addKnownTypes(scheme *runtime.Scheme) error { 31 | scheme.AddKnownTypes(SchemeGroupVersion, 32 | &Rabbit{}, 33 | &RabbitList{}, 34 | ) 35 | metav1.AddToGroupVersion(scheme, SchemeGroupVersion) 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/api/stable.wbsnail.com/v1/types.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 4 | 5 | // +genclient 6 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 7 | 8 | // Rabbit is a top-level type 9 | type Rabbit struct { 10 | metav1.TypeMeta `json:",inline"` 11 | // +optional 12 | metav1.ObjectMeta `json:"metadata,omitempty"` 13 | 14 | // This is where you can define 15 | // your own custom spec 16 | Spec RabbitSpec `json:"spec,omitempty"` 17 | } 18 | 19 | type RabbitSpec struct { 20 | Color string `json:"color,omitempty"` 21 | } 22 | 23 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 24 | 25 | // RabbitList no client needed for list as it's been created in above 26 | type RabbitList struct { 27 | metav1.TypeMeta `json:",inline"` 28 | // +optional 29 | metav1.ListMeta `son:"metadata,omitempty"` 30 | 31 | Items []Rabbit `json:"items"` 32 | } 33 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/api/stable.wbsnail.com/v1/zz_generated.deepcopy.go: -------------------------------------------------------------------------------- 1 | // +build !ignore_autogenerated 2 | 3 | /* 4 | Copyright wbsnail.com. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | // Code generated by deepcopy-gen. DO NOT EDIT. 20 | 21 | package v1 22 | 23 | import ( 24 | runtime "k8s.io/apimachinery/pkg/runtime" 25 | ) 26 | 27 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 28 | func (in *Rabbit) DeepCopyInto(out *Rabbit) { 29 | *out = *in 30 | out.TypeMeta = in.TypeMeta 31 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 32 | out.Spec = in.Spec 33 | return 34 | } 35 | 36 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rabbit. 37 | func (in *Rabbit) DeepCopy() *Rabbit { 38 | if in == nil { 39 | return nil 40 | } 41 | out := new(Rabbit) 42 | in.DeepCopyInto(out) 43 | return out 44 | } 45 | 46 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 47 | func (in *Rabbit) DeepCopyObject() runtime.Object { 48 | if c := in.DeepCopy(); c != nil { 49 | return c 50 | } 51 | return nil 52 | } 53 | 54 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 55 | func (in *RabbitList) DeepCopyInto(out *RabbitList) { 56 | *out = *in 57 | out.TypeMeta = in.TypeMeta 58 | in.ListMeta.DeepCopyInto(&out.ListMeta) 59 | if in.Items != nil { 60 | in, out := &in.Items, &out.Items 61 | *out = make([]Rabbit, len(*in)) 62 | for i := range *in { 63 | (*in)[i].DeepCopyInto(&(*out)[i]) 64 | } 65 | } 66 | return 67 | } 68 | 69 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RabbitList. 70 | func (in *RabbitList) DeepCopy() *RabbitList { 71 | if in == nil { 72 | return nil 73 | } 74 | out := new(RabbitList) 75 | in.DeepCopyInto(out) 76 | return out 77 | } 78 | 79 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 80 | func (in *RabbitList) DeepCopyObject() runtime.Object { 81 | if c := in.DeepCopy(); c != nil { 82 | return c 83 | } 84 | return nil 85 | } 86 | 87 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 88 | func (in *RabbitSpec) DeepCopyInto(out *RabbitSpec) { 89 | *out = *in 90 | return 91 | } 92 | 93 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RabbitSpec. 94 | func (in *RabbitSpec) DeepCopy() *RabbitSpec { 95 | if in == nil { 96 | return nil 97 | } 98 | out := new(RabbitSpec) 99 | in.DeepCopyInto(out) 100 | return out 101 | } 102 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright wbsnail.com. 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 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/client/clientset/versioned/clientset.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright wbsnail.com. 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" 23 | 24 | stablev1 "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/client/clientset/versioned/typed/stable.wbsnail.com/v1" 25 | discovery "k8s.io/client-go/discovery" 26 | rest "k8s.io/client-go/rest" 27 | flowcontrol "k8s.io/client-go/util/flowcontrol" 28 | ) 29 | 30 | type Interface interface { 31 | Discovery() discovery.DiscoveryInterface 32 | StableV1() stablev1.StableV1Interface 33 | } 34 | 35 | // Clientset contains the clients for groups. Each group has exactly one 36 | // version included in a Clientset. 37 | type Clientset struct { 38 | *discovery.DiscoveryClient 39 | stableV1 *stablev1.StableV1Client 40 | } 41 | 42 | // StableV1 retrieves the StableV1Client 43 | func (c *Clientset) StableV1() stablev1.StableV1Interface { 44 | return c.stableV1 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 | func NewForConfig(c *rest.Config) (*Clientset, error) { 59 | configShallowCopy := *c 60 | if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { 61 | if configShallowCopy.Burst <= 0 { 62 | 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") 63 | } 64 | configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) 65 | } 66 | var cs Clientset 67 | var err error 68 | cs.stableV1, err = stablev1.NewForConfig(&configShallowCopy) 69 | if err != nil { 70 | return nil, err 71 | } 72 | 73 | cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) 74 | if err != nil { 75 | return nil, err 76 | } 77 | return &cs, nil 78 | } 79 | 80 | // NewForConfigOrDie creates a new Clientset for the given config and 81 | // panics if there is an error in the config. 82 | func NewForConfigOrDie(c *rest.Config) *Clientset { 83 | var cs Clientset 84 | cs.stableV1 = stablev1.NewForConfigOrDie(c) 85 | 86 | cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) 87 | return &cs 88 | } 89 | 90 | // New creates a new Clientset for the given RESTClient. 91 | func New(c rest.Interface) *Clientset { 92 | var cs Clientset 93 | cs.stableV1 = stablev1.New(c) 94 | 95 | cs.DiscoveryClient = discovery.NewDiscoveryClient(c) 96 | return &cs 97 | } 98 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/client/clientset/versioned/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright wbsnail.com. 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 clientset. 20 | package versioned 21 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/client/clientset/versioned/fake/clientset_generated.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright wbsnail.com. 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 | clientset "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/client/clientset/versioned" 23 | stablev1 "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/client/clientset/versioned/typed/stable.wbsnail.com/v1" 24 | fakestablev1 "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/client/clientset/versioned/typed/stable.wbsnail.com/v1/fake" 25 | "k8s.io/apimachinery/pkg/runtime" 26 | "k8s.io/apimachinery/pkg/watch" 27 | "k8s.io/client-go/discovery" 28 | fakediscovery "k8s.io/client-go/discovery/fake" 29 | "k8s.io/client-go/testing" 30 | ) 31 | 32 | // NewSimpleClientset returns a clientset that will respond with the provided objects. 33 | // It's backed by a very simple object tracker that processes creates, updates and deletions as-is, 34 | // without applying any validations and/or defaults. It shouldn't be considered a replacement 35 | // for a real clientset and is mostly useful in simple unit tests. 36 | func NewSimpleClientset(objects ...runtime.Object) *Clientset { 37 | o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) 38 | for _, obj := range objects { 39 | if err := o.Add(obj); err != nil { 40 | panic(err) 41 | } 42 | } 43 | 44 | cs := &Clientset{tracker: o} 45 | cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} 46 | cs.AddReactor("*", "*", testing.ObjectReaction(o)) 47 | cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { 48 | gvr := action.GetResource() 49 | ns := action.GetNamespace() 50 | watch, err := o.Watch(gvr, ns) 51 | if err != nil { 52 | return false, nil, err 53 | } 54 | return true, watch, nil 55 | }) 56 | 57 | return cs 58 | } 59 | 60 | // Clientset implements clientset.Interface. Meant to be embedded into a 61 | // struct to get a default implementation. This makes faking out just the method 62 | // you want to test easier. 63 | type Clientset struct { 64 | testing.Fake 65 | discovery *fakediscovery.FakeDiscovery 66 | tracker testing.ObjectTracker 67 | } 68 | 69 | func (c *Clientset) Discovery() discovery.DiscoveryInterface { 70 | return c.discovery 71 | } 72 | 73 | func (c *Clientset) Tracker() testing.ObjectTracker { 74 | return c.tracker 75 | } 76 | 77 | var _ clientset.Interface = &Clientset{} 78 | 79 | // StableV1 retrieves the StableV1Client 80 | func (c *Clientset) StableV1() stablev1.StableV1Interface { 81 | return &fakestablev1.FakeStableV1{Fake: &c.Fake} 82 | } 83 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/client/clientset/versioned/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright wbsnail.com. 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 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/client/clientset/versioned/fake/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright wbsnail.com. 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 | stablev1 "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/api/stable.wbsnail.com/v1" 23 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 | runtime "k8s.io/apimachinery/pkg/runtime" 25 | schema "k8s.io/apimachinery/pkg/runtime/schema" 26 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 27 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 28 | ) 29 | 30 | var scheme = runtime.NewScheme() 31 | var codecs = serializer.NewCodecFactory(scheme) 32 | 33 | var localSchemeBuilder = runtime.SchemeBuilder{ 34 | stablev1.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 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/client/clientset/versioned/scheme/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright wbsnail.com. 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 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/client/clientset/versioned/scheme/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright wbsnail.com. 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 | stablev1 "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/api/stable.wbsnail.com/v1" 23 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 | runtime "k8s.io/apimachinery/pkg/runtime" 25 | schema "k8s.io/apimachinery/pkg/runtime/schema" 26 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 27 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 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 | stablev1.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 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/client/clientset/versioned/typed/stable.wbsnail.com/v1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright wbsnail.com. 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 v1 21 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/client/clientset/versioned/typed/stable.wbsnail.com/v1/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright wbsnail.com. 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 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/client/clientset/versioned/typed/stable.wbsnail.com/v1/fake/fake_rabbit.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright wbsnail.com. 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 | "context" 23 | 24 | stablewbsnailcomv1 "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/api/stable.wbsnail.com/v1" 25 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 | labels "k8s.io/apimachinery/pkg/labels" 27 | schema "k8s.io/apimachinery/pkg/runtime/schema" 28 | types "k8s.io/apimachinery/pkg/types" 29 | watch "k8s.io/apimachinery/pkg/watch" 30 | testing "k8s.io/client-go/testing" 31 | ) 32 | 33 | // FakeRabbits implements RabbitInterface 34 | type FakeRabbits struct { 35 | Fake *FakeStableV1 36 | ns string 37 | } 38 | 39 | var rabbitsResource = schema.GroupVersionResource{Group: "stable.wbsnail.com", Version: "v1", Resource: "rabbits"} 40 | 41 | var rabbitsKind = schema.GroupVersionKind{Group: "stable.wbsnail.com", Version: "v1", Kind: "Rabbit"} 42 | 43 | // Get takes name of the rabbit, and returns the corresponding rabbit object, and an error if there is any. 44 | func (c *FakeRabbits) Get(ctx context.Context, name string, options v1.GetOptions) (result *stablewbsnailcomv1.Rabbit, err error) { 45 | obj, err := c.Fake. 46 | Invokes(testing.NewGetAction(rabbitsResource, c.ns, name), &stablewbsnailcomv1.Rabbit{}) 47 | 48 | if obj == nil { 49 | return nil, err 50 | } 51 | return obj.(*stablewbsnailcomv1.Rabbit), err 52 | } 53 | 54 | // List takes label and field selectors, and returns the list of Rabbits that match those selectors. 55 | func (c *FakeRabbits) List(ctx context.Context, opts v1.ListOptions) (result *stablewbsnailcomv1.RabbitList, err error) { 56 | obj, err := c.Fake. 57 | Invokes(testing.NewListAction(rabbitsResource, rabbitsKind, c.ns, opts), &stablewbsnailcomv1.RabbitList{}) 58 | 59 | if obj == nil { 60 | return nil, err 61 | } 62 | 63 | label, _, _ := testing.ExtractFromListOptions(opts) 64 | if label == nil { 65 | label = labels.Everything() 66 | } 67 | list := &stablewbsnailcomv1.RabbitList{ListMeta: obj.(*stablewbsnailcomv1.RabbitList).ListMeta} 68 | for _, item := range obj.(*stablewbsnailcomv1.RabbitList).Items { 69 | if label.Matches(labels.Set(item.Labels)) { 70 | list.Items = append(list.Items, item) 71 | } 72 | } 73 | return list, err 74 | } 75 | 76 | // Watch returns a watch.Interface that watches the requested rabbits. 77 | func (c *FakeRabbits) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { 78 | return c.Fake. 79 | InvokesWatch(testing.NewWatchAction(rabbitsResource, c.ns, opts)) 80 | 81 | } 82 | 83 | // Create takes the representation of a rabbit and creates it. Returns the server's representation of the rabbit, and an error, if there is any. 84 | func (c *FakeRabbits) Create(ctx context.Context, rabbit *stablewbsnailcomv1.Rabbit, opts v1.CreateOptions) (result *stablewbsnailcomv1.Rabbit, err error) { 85 | obj, err := c.Fake. 86 | Invokes(testing.NewCreateAction(rabbitsResource, c.ns, rabbit), &stablewbsnailcomv1.Rabbit{}) 87 | 88 | if obj == nil { 89 | return nil, err 90 | } 91 | return obj.(*stablewbsnailcomv1.Rabbit), err 92 | } 93 | 94 | // Update takes the representation of a rabbit and updates it. Returns the server's representation of the rabbit, and an error, if there is any. 95 | func (c *FakeRabbits) Update(ctx context.Context, rabbit *stablewbsnailcomv1.Rabbit, opts v1.UpdateOptions) (result *stablewbsnailcomv1.Rabbit, err error) { 96 | obj, err := c.Fake. 97 | Invokes(testing.NewUpdateAction(rabbitsResource, c.ns, rabbit), &stablewbsnailcomv1.Rabbit{}) 98 | 99 | if obj == nil { 100 | return nil, err 101 | } 102 | return obj.(*stablewbsnailcomv1.Rabbit), err 103 | } 104 | 105 | // Delete takes name of the rabbit and deletes it. Returns an error if one occurs. 106 | func (c *FakeRabbits) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { 107 | _, err := c.Fake. 108 | Invokes(testing.NewDeleteAction(rabbitsResource, c.ns, name), &stablewbsnailcomv1.Rabbit{}) 109 | 110 | return err 111 | } 112 | 113 | // DeleteCollection deletes a collection of objects. 114 | func (c *FakeRabbits) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { 115 | action := testing.NewDeleteCollectionAction(rabbitsResource, c.ns, listOpts) 116 | 117 | _, err := c.Fake.Invokes(action, &stablewbsnailcomv1.RabbitList{}) 118 | return err 119 | } 120 | 121 | // Patch applies the patch and returns the patched rabbit. 122 | func (c *FakeRabbits) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *stablewbsnailcomv1.Rabbit, err error) { 123 | obj, err := c.Fake. 124 | Invokes(testing.NewPatchSubresourceAction(rabbitsResource, c.ns, name, pt, data, subresources...), &stablewbsnailcomv1.Rabbit{}) 125 | 126 | if obj == nil { 127 | return nil, err 128 | } 129 | return obj.(*stablewbsnailcomv1.Rabbit), err 130 | } 131 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/client/clientset/versioned/typed/stable.wbsnail.com/v1/fake/fake_stable.wbsnail.com_client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright wbsnail.com. 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 "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/client/clientset/versioned/typed/stable.wbsnail.com/v1" 23 | rest "k8s.io/client-go/rest" 24 | testing "k8s.io/client-go/testing" 25 | ) 26 | 27 | type FakeStableV1 struct { 28 | *testing.Fake 29 | } 30 | 31 | func (c *FakeStableV1) Rabbits(namespace string) v1.RabbitInterface { 32 | return &FakeRabbits{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 *FakeStableV1) RESTClient() rest.Interface { 38 | var ret *rest.RESTClient 39 | return ret 40 | } 41 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/client/clientset/versioned/typed/stable.wbsnail.com/v1/generated_expansion.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright wbsnail.com. 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 v1 20 | 21 | type RabbitExpansion interface{} 22 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/client/clientset/versioned/typed/stable.wbsnail.com/v1/rabbit.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright wbsnail.com. 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 v1 20 | 21 | import ( 22 | "context" 23 | "time" 24 | 25 | v1 "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/api/stable.wbsnail.com/v1" 26 | scheme "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/client/clientset/versioned/scheme" 27 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 | types "k8s.io/apimachinery/pkg/types" 29 | watch "k8s.io/apimachinery/pkg/watch" 30 | rest "k8s.io/client-go/rest" 31 | ) 32 | 33 | // RabbitsGetter has a method to return a RabbitInterface. 34 | // A group's client should implement this interface. 35 | type RabbitsGetter interface { 36 | Rabbits(namespace string) RabbitInterface 37 | } 38 | 39 | // RabbitInterface has methods to work with Rabbit resources. 40 | type RabbitInterface interface { 41 | Create(ctx context.Context, rabbit *v1.Rabbit, opts metav1.CreateOptions) (*v1.Rabbit, error) 42 | Update(ctx context.Context, rabbit *v1.Rabbit, opts metav1.UpdateOptions) (*v1.Rabbit, error) 43 | Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error 44 | DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error 45 | Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.Rabbit, error) 46 | List(ctx context.Context, opts metav1.ListOptions) (*v1.RabbitList, error) 47 | Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) 48 | Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.Rabbit, err error) 49 | RabbitExpansion 50 | } 51 | 52 | // rabbits implements RabbitInterface 53 | type rabbits struct { 54 | client rest.Interface 55 | ns string 56 | } 57 | 58 | // newRabbits returns a Rabbits 59 | func newRabbits(c *StableV1Client, namespace string) *rabbits { 60 | return &rabbits{ 61 | client: c.RESTClient(), 62 | ns: namespace, 63 | } 64 | } 65 | 66 | // Get takes name of the rabbit, and returns the corresponding rabbit object, and an error if there is any. 67 | func (c *rabbits) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.Rabbit, err error) { 68 | result = &v1.Rabbit{} 69 | err = c.client.Get(). 70 | Namespace(c.ns). 71 | Resource("rabbits"). 72 | Name(name). 73 | VersionedParams(&options, scheme.ParameterCodec). 74 | Do(ctx). 75 | Into(result) 76 | return 77 | } 78 | 79 | // List takes label and field selectors, and returns the list of Rabbits that match those selectors. 80 | func (c *rabbits) List(ctx context.Context, opts metav1.ListOptions) (result *v1.RabbitList, err error) { 81 | var timeout time.Duration 82 | if opts.TimeoutSeconds != nil { 83 | timeout = time.Duration(*opts.TimeoutSeconds) * time.Second 84 | } 85 | result = &v1.RabbitList{} 86 | err = c.client.Get(). 87 | Namespace(c.ns). 88 | Resource("rabbits"). 89 | VersionedParams(&opts, scheme.ParameterCodec). 90 | Timeout(timeout). 91 | Do(ctx). 92 | Into(result) 93 | return 94 | } 95 | 96 | // Watch returns a watch.Interface that watches the requested rabbits. 97 | func (c *rabbits) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { 98 | var timeout time.Duration 99 | if opts.TimeoutSeconds != nil { 100 | timeout = time.Duration(*opts.TimeoutSeconds) * time.Second 101 | } 102 | opts.Watch = true 103 | return c.client.Get(). 104 | Namespace(c.ns). 105 | Resource("rabbits"). 106 | VersionedParams(&opts, scheme.ParameterCodec). 107 | Timeout(timeout). 108 | Watch(ctx) 109 | } 110 | 111 | // Create takes the representation of a rabbit and creates it. Returns the server's representation of the rabbit, and an error, if there is any. 112 | func (c *rabbits) Create(ctx context.Context, rabbit *v1.Rabbit, opts metav1.CreateOptions) (result *v1.Rabbit, err error) { 113 | result = &v1.Rabbit{} 114 | err = c.client.Post(). 115 | Namespace(c.ns). 116 | Resource("rabbits"). 117 | VersionedParams(&opts, scheme.ParameterCodec). 118 | Body(rabbit). 119 | Do(ctx). 120 | Into(result) 121 | return 122 | } 123 | 124 | // Update takes the representation of a rabbit and updates it. Returns the server's representation of the rabbit, and an error, if there is any. 125 | func (c *rabbits) Update(ctx context.Context, rabbit *v1.Rabbit, opts metav1.UpdateOptions) (result *v1.Rabbit, err error) { 126 | result = &v1.Rabbit{} 127 | err = c.client.Put(). 128 | Namespace(c.ns). 129 | Resource("rabbits"). 130 | Name(rabbit.Name). 131 | VersionedParams(&opts, scheme.ParameterCodec). 132 | Body(rabbit). 133 | Do(ctx). 134 | Into(result) 135 | return 136 | } 137 | 138 | // Delete takes name of the rabbit and deletes it. Returns an error if one occurs. 139 | func (c *rabbits) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { 140 | return c.client.Delete(). 141 | Namespace(c.ns). 142 | Resource("rabbits"). 143 | Name(name). 144 | Body(&opts). 145 | Do(ctx). 146 | Error() 147 | } 148 | 149 | // DeleteCollection deletes a collection of objects. 150 | func (c *rabbits) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { 151 | var timeout time.Duration 152 | if listOpts.TimeoutSeconds != nil { 153 | timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second 154 | } 155 | return c.client.Delete(). 156 | Namespace(c.ns). 157 | Resource("rabbits"). 158 | VersionedParams(&listOpts, scheme.ParameterCodec). 159 | Timeout(timeout). 160 | Body(&opts). 161 | Do(ctx). 162 | Error() 163 | } 164 | 165 | // Patch applies the patch and returns the patched rabbit. 166 | func (c *rabbits) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.Rabbit, err error) { 167 | result = &v1.Rabbit{} 168 | err = c.client.Patch(pt). 169 | Namespace(c.ns). 170 | Resource("rabbits"). 171 | Name(name). 172 | SubResource(subresources...). 173 | VersionedParams(&opts, scheme.ParameterCodec). 174 | Body(data). 175 | Do(ctx). 176 | Into(result) 177 | return 178 | } 179 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/client/clientset/versioned/typed/stable.wbsnail.com/v1/stable.wbsnail.com_client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright wbsnail.com. 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 v1 20 | 21 | import ( 22 | v1 "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/api/stable.wbsnail.com/v1" 23 | "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/client/clientset/versioned/scheme" 24 | rest "k8s.io/client-go/rest" 25 | ) 26 | 27 | type StableV1Interface interface { 28 | RESTClient() rest.Interface 29 | RabbitsGetter 30 | } 31 | 32 | // StableV1Client is used to interact with features provided by the stable.wbsnail.com group. 33 | type StableV1Client struct { 34 | restClient rest.Interface 35 | } 36 | 37 | func (c *StableV1Client) Rabbits(namespace string) RabbitInterface { 38 | return newRabbits(c, namespace) 39 | } 40 | 41 | // NewForConfig creates a new StableV1Client for the given config. 42 | func NewForConfig(c *rest.Config) (*StableV1Client, error) { 43 | config := *c 44 | if err := setConfigDefaults(&config); err != nil { 45 | return nil, err 46 | } 47 | client, err := rest.RESTClientFor(&config) 48 | if err != nil { 49 | return nil, err 50 | } 51 | return &StableV1Client{client}, nil 52 | } 53 | 54 | // NewForConfigOrDie creates a new StableV1Client for the given config and 55 | // panics if there is an error in the config. 56 | func NewForConfigOrDie(c *rest.Config) *StableV1Client { 57 | client, err := NewForConfig(c) 58 | if err != nil { 59 | panic(err) 60 | } 61 | return client 62 | } 63 | 64 | // New creates a new StableV1Client for the given RESTClient. 65 | func New(c rest.Interface) *StableV1Client { 66 | return &StableV1Client{c} 67 | } 68 | 69 | func setConfigDefaults(config *rest.Config) error { 70 | gv := v1.SchemeGroupVersion 71 | config.GroupVersion = &gv 72 | config.APIPath = "/apis" 73 | config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() 74 | 75 | if config.UserAgent == "" { 76 | config.UserAgent = rest.DefaultKubernetesUserAgent() 77 | } 78 | 79 | return nil 80 | } 81 | 82 | // RESTClient returns a RESTClient that is used to communicate 83 | // with API server by this client implementation. 84 | func (c *StableV1Client) RESTClient() rest.Interface { 85 | if c == nil { 86 | return nil 87 | } 88 | return c.restClient 89 | } 90 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/client/informers/externalversions/factory.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright wbsnail.com. 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 | versioned "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/client/clientset/versioned" 27 | internalinterfaces "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/client/informers/externalversions/internalinterfaces" 28 | stablewbsnailcom "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/client/informers/externalversions/stable.wbsnail.com" 29 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 | runtime "k8s.io/apimachinery/pkg/runtime" 31 | schema "k8s.io/apimachinery/pkg/runtime/schema" 32 | cache "k8s.io/client-go/tools/cache" 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 | 46 | informers map[reflect.Type]cache.SharedIndexInformer 47 | // startedInformers is used for tracking which informers have been started. 48 | // This allows Start() to be called multiple times safely. 49 | startedInformers map[reflect.Type]bool 50 | } 51 | 52 | // WithCustomResyncConfig sets a custom resync period for the specified informer types. 53 | func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption { 54 | return func(factory *sharedInformerFactory) *sharedInformerFactory { 55 | for k, v := range resyncConfig { 56 | factory.customResync[reflect.TypeOf(k)] = v 57 | } 58 | return factory 59 | } 60 | } 61 | 62 | // WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory. 63 | func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption { 64 | return func(factory *sharedInformerFactory) *sharedInformerFactory { 65 | factory.tweakListOptions = tweakListOptions 66 | return factory 67 | } 68 | } 69 | 70 | // WithNamespace limits the SharedInformerFactory to the specified namespace. 71 | func WithNamespace(namespace string) SharedInformerOption { 72 | return func(factory *sharedInformerFactory) *sharedInformerFactory { 73 | factory.namespace = namespace 74 | return factory 75 | } 76 | } 77 | 78 | // NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces. 79 | func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory { 80 | return NewSharedInformerFactoryWithOptions(client, defaultResync) 81 | } 82 | 83 | // NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. 84 | // Listers obtained via this SharedInformerFactory will be subject to the same filters 85 | // as specified here. 86 | // Deprecated: Please use NewSharedInformerFactoryWithOptions instead 87 | func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory { 88 | return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions)) 89 | } 90 | 91 | // NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options. 92 | func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory { 93 | factory := &sharedInformerFactory{ 94 | client: client, 95 | namespace: v1.NamespaceAll, 96 | defaultResync: defaultResync, 97 | informers: make(map[reflect.Type]cache.SharedIndexInformer), 98 | startedInformers: make(map[reflect.Type]bool), 99 | customResync: make(map[reflect.Type]time.Duration), 100 | } 101 | 102 | // Apply all options 103 | for _, opt := range options { 104 | factory = opt(factory) 105 | } 106 | 107 | return factory 108 | } 109 | 110 | // Start initializes all requested informers. 111 | func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { 112 | f.lock.Lock() 113 | defer f.lock.Unlock() 114 | 115 | for informerType, informer := range f.informers { 116 | if !f.startedInformers[informerType] { 117 | go informer.Run(stopCh) 118 | f.startedInformers[informerType] = true 119 | } 120 | } 121 | } 122 | 123 | // WaitForCacheSync waits for all started informers' cache were synced. 124 | func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { 125 | informers := func() map[reflect.Type]cache.SharedIndexInformer { 126 | f.lock.Lock() 127 | defer f.lock.Unlock() 128 | 129 | informers := map[reflect.Type]cache.SharedIndexInformer{} 130 | for informerType, informer := range f.informers { 131 | if f.startedInformers[informerType] { 132 | informers[informerType] = informer 133 | } 134 | } 135 | return informers 136 | }() 137 | 138 | res := map[reflect.Type]bool{} 139 | for informType, informer := range informers { 140 | res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) 141 | } 142 | return res 143 | } 144 | 145 | // InternalInformerFor returns the SharedIndexInformer for obj using an internal 146 | // client. 147 | func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { 148 | f.lock.Lock() 149 | defer f.lock.Unlock() 150 | 151 | informerType := reflect.TypeOf(obj) 152 | informer, exists := f.informers[informerType] 153 | if exists { 154 | return informer 155 | } 156 | 157 | resyncPeriod, exists := f.customResync[informerType] 158 | if !exists { 159 | resyncPeriod = f.defaultResync 160 | } 161 | 162 | informer = newFunc(f.client, resyncPeriod) 163 | f.informers[informerType] = informer 164 | 165 | return informer 166 | } 167 | 168 | // SharedInformerFactory provides shared informers for resources in all known 169 | // API group versions. 170 | type SharedInformerFactory interface { 171 | internalinterfaces.SharedInformerFactory 172 | ForResource(resource schema.GroupVersionResource) (GenericInformer, error) 173 | WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool 174 | 175 | Stable() stablewbsnailcom.Interface 176 | } 177 | 178 | func (f *sharedInformerFactory) Stable() stablewbsnailcom.Interface { 179 | return stablewbsnailcom.New(f, f.namespace, f.tweakListOptions) 180 | } 181 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/client/informers/externalversions/generic.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright wbsnail.com. 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" 23 | 24 | v1 "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/api/stable.wbsnail.com/v1" 25 | schema "k8s.io/apimachinery/pkg/runtime/schema" 26 | cache "k8s.io/client-go/tools/cache" 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=stable.wbsnail.com, Version=v1 56 | case v1.SchemeGroupVersion.WithResource("rabbits"): 57 | return &genericInformer{resource: resource.GroupResource(), informer: f.Stable().V1().Rabbits().Informer()}, nil 58 | 59 | } 60 | 61 | return nil, fmt.Errorf("no informer found for %v", resource) 62 | } 63 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/client/informers/externalversions/internalinterfaces/factory_interfaces.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright wbsnail.com. 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 | versioned "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/client/clientset/versioned" 25 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 | runtime "k8s.io/apimachinery/pkg/runtime" 27 | cache "k8s.io/client-go/tools/cache" 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 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/client/informers/externalversions/stable.wbsnail.com/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright wbsnail.com. 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 stable 20 | 21 | import ( 22 | internalinterfaces "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/client/informers/externalversions/internalinterfaces" 23 | v1 "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/client/informers/externalversions/stable.wbsnail.com/v1" 24 | ) 25 | 26 | // Interface provides access to each of this group's versions. 27 | type Interface interface { 28 | // V1 provides access to shared informers for resources in V1. 29 | V1() v1.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 | // V1 returns a new v1.Interface. 44 | func (g *group) V1() v1.Interface { 45 | return v1.New(g.factory, g.namespace, g.tweakListOptions) 46 | } 47 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/client/informers/externalversions/stable.wbsnail.com/v1/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright wbsnail.com. 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 v1 20 | 21 | import ( 22 | internalinterfaces "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/client/informers/externalversions/internalinterfaces" 23 | ) 24 | 25 | // Interface provides access to all the informers in this group version. 26 | type Interface interface { 27 | // Rabbits returns a RabbitInformer. 28 | Rabbits() RabbitInformer 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 | // Rabbits returns a RabbitInformer. 43 | func (v *version) Rabbits() RabbitInformer { 44 | return &rabbitInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} 45 | } 46 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/client/informers/externalversions/stable.wbsnail.com/v1/rabbit.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright wbsnail.com. 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 v1 20 | 21 | import ( 22 | "context" 23 | time "time" 24 | 25 | stablewbsnailcomv1 "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/api/stable.wbsnail.com/v1" 26 | versioned "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/client/clientset/versioned" 27 | internalinterfaces "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/client/informers/externalversions/internalinterfaces" 28 | v1 "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/client/listers/stable.wbsnail.com/v1" 29 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 | runtime "k8s.io/apimachinery/pkg/runtime" 31 | watch "k8s.io/apimachinery/pkg/watch" 32 | cache "k8s.io/client-go/tools/cache" 33 | ) 34 | 35 | // RabbitInformer provides access to a shared informer and lister for 36 | // Rabbits. 37 | type RabbitInformer interface { 38 | Informer() cache.SharedIndexInformer 39 | Lister() v1.RabbitLister 40 | } 41 | 42 | type rabbitInformer struct { 43 | factory internalinterfaces.SharedInformerFactory 44 | tweakListOptions internalinterfaces.TweakListOptionsFunc 45 | namespace string 46 | } 47 | 48 | // NewRabbitInformer constructs a new informer for Rabbit 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 NewRabbitInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { 52 | return NewFilteredRabbitInformer(client, namespace, resyncPeriod, indexers, nil) 53 | } 54 | 55 | // NewFilteredRabbitInformer constructs a new informer for Rabbit 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 NewFilteredRabbitInformer(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 metav1.ListOptions) (runtime.Object, error) { 62 | if tweakListOptions != nil { 63 | tweakListOptions(&options) 64 | } 65 | return client.StableV1().Rabbits(namespace).List(context.TODO(), options) 66 | }, 67 | WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { 68 | if tweakListOptions != nil { 69 | tweakListOptions(&options) 70 | } 71 | return client.StableV1().Rabbits(namespace).Watch(context.TODO(), options) 72 | }, 73 | }, 74 | &stablewbsnailcomv1.Rabbit{}, 75 | resyncPeriod, 76 | indexers, 77 | ) 78 | } 79 | 80 | func (f *rabbitInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { 81 | return NewFilteredRabbitInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) 82 | } 83 | 84 | func (f *rabbitInformer) Informer() cache.SharedIndexInformer { 85 | return f.factory.InformerFor(&stablewbsnailcomv1.Rabbit{}, f.defaultInformer) 86 | } 87 | 88 | func (f *rabbitInformer) Lister() v1.RabbitLister { 89 | return v1.NewRabbitLister(f.Informer().GetIndexer()) 90 | } 91 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/client/listers/stable.wbsnail.com/v1/expansion_generated.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright wbsnail.com. 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 v1 20 | 21 | // RabbitListerExpansion allows custom methods to be added to 22 | // RabbitLister. 23 | type RabbitListerExpansion interface{} 24 | 25 | // RabbitNamespaceListerExpansion allows custom methods to be added to 26 | // RabbitNamespaceLister. 27 | type RabbitNamespaceListerExpansion interface{} 28 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/client/listers/stable.wbsnail.com/v1/rabbit.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright wbsnail.com. 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 v1 20 | 21 | import ( 22 | v1 "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/api/stable.wbsnail.com/v1" 23 | "k8s.io/apimachinery/pkg/api/errors" 24 | "k8s.io/apimachinery/pkg/labels" 25 | "k8s.io/client-go/tools/cache" 26 | ) 27 | 28 | // RabbitLister helps list Rabbits. 29 | // All objects returned here must be treated as read-only. 30 | type RabbitLister interface { 31 | // List lists all Rabbits in the indexer. 32 | // Objects returned here must be treated as read-only. 33 | List(selector labels.Selector) (ret []*v1.Rabbit, err error) 34 | // Rabbits returns an object that can list and get Rabbits. 35 | Rabbits(namespace string) RabbitNamespaceLister 36 | RabbitListerExpansion 37 | } 38 | 39 | // rabbitLister implements the RabbitLister interface. 40 | type rabbitLister struct { 41 | indexer cache.Indexer 42 | } 43 | 44 | // NewRabbitLister returns a new RabbitLister. 45 | func NewRabbitLister(indexer cache.Indexer) RabbitLister { 46 | return &rabbitLister{indexer: indexer} 47 | } 48 | 49 | // List lists all Rabbits in the indexer. 50 | func (s *rabbitLister) List(selector labels.Selector) (ret []*v1.Rabbit, err error) { 51 | err = cache.ListAll(s.indexer, selector, func(m interface{}) { 52 | ret = append(ret, m.(*v1.Rabbit)) 53 | }) 54 | return ret, err 55 | } 56 | 57 | // Rabbits returns an object that can list and get Rabbits. 58 | func (s *rabbitLister) Rabbits(namespace string) RabbitNamespaceLister { 59 | return rabbitNamespaceLister{indexer: s.indexer, namespace: namespace} 60 | } 61 | 62 | // RabbitNamespaceLister helps list and get Rabbits. 63 | // All objects returned here must be treated as read-only. 64 | type RabbitNamespaceLister interface { 65 | // List lists all Rabbits in the indexer for a given namespace. 66 | // Objects returned here must be treated as read-only. 67 | List(selector labels.Selector) (ret []*v1.Rabbit, err error) 68 | // Get retrieves the Rabbit from the indexer for a given namespace and name. 69 | // Objects returned here must be treated as read-only. 70 | Get(name string) (*v1.Rabbit, error) 71 | RabbitNamespaceListerExpansion 72 | } 73 | 74 | // rabbitNamespaceLister implements the RabbitNamespaceLister 75 | // interface. 76 | type rabbitNamespaceLister struct { 77 | indexer cache.Indexer 78 | namespace string 79 | } 80 | 81 | // List lists all Rabbits in the indexer for a given namespace. 82 | func (s rabbitNamespaceLister) List(selector labels.Selector) (ret []*v1.Rabbit, err error) { 83 | err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { 84 | ret = append(ret, m.(*v1.Rabbit)) 85 | }) 86 | return ret, err 87 | } 88 | 89 | // Get retrieves the Rabbit from the indexer for a given namespace and name. 90 | func (s rabbitNamespaceLister) Get(name string) (*v1.Rabbit, error) { 91 | obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) 92 | if err != nil { 93 | return nil, err 94 | } 95 | if !exists { 96 | return nil, errors.NewNotFound(v1.Resource("rabbit"), name) 97 | } 98 | return obj.(*v1.Rabbit), nil 99 | } 100 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/crd.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: rabbits.stable.wbsnail.com 5 | spec: 6 | group: stable.wbsnail.com 7 | versions: 8 | - name: v1 9 | served: true 10 | storage: true 11 | schema: 12 | openAPIV3Schema: 13 | type: object 14 | properties: 15 | spec: 16 | type: object 17 | properties: 18 | color: 19 | type: string 20 | additionalPrinterColumns: 21 | - name: Color 22 | type: string 23 | description: Color 24 | jsonPath: .spec.color 25 | - name: Age 26 | type: date 27 | description: Age 28 | jsonPath: .metadata.creationTimestamp 29 | scope: Namespaced 30 | names: 31 | plural: rabbits 32 | singular: rabbit 33 | kind: Rabbit 34 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/custom-resource.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "stable.wbsnail.com/v1" 2 | kind: Rabbit 3 | metadata: 4 | name: judy 5 | namespace: tmp 6 | spec: 7 | color: white 8 | --- 9 | apiVersion: "stable.wbsnail.com/v1" 10 | kind: Rabbit 11 | metadata: 12 | name: bugs 13 | namespace: tmp 14 | spec: 15 | color: gray -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/generate-groups.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | scriptPath=${SCRIPT_PATH} 4 | 5 | if [ -z ${scriptPath} ]; then 6 | echo "Tell me, where is the file? (SCRIPT_PATH)" 7 | echo "Maybe 'export SCRIPT_PATH=~/projects/code-generator/generate-groups.sh'?" 8 | exit 1 9 | fi 10 | 11 | ${scriptPath} \ 12 | all \ 13 | github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/client \ 14 | github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/api \ 15 | stable.wbsnail.com:v1 \ 16 | --go-header-file ./boilerplate.go.txt 17 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/11-crd-informer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/spongeprojects/magicconch" 6 | v1 "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/api/stable.wbsnail.com/v1" 7 | "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/client/clientset/versioned" 8 | "github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/11-crd-informer/client/informers/externalversions" 9 | "k8s.io/client-go/tools/cache" 10 | "k8s.io/client-go/tools/clientcmd" 11 | "os" 12 | ) 13 | 14 | func main() { 15 | kubeconfig := os.Getenv("KUBECONFIG") 16 | config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) 17 | magicconch.Must(err) 18 | 19 | clientset, err := versioned.NewForConfig(config) 20 | magicconch.Must(err) 21 | 22 | informerFactory := externalversions.NewSharedInformerFactory(clientset, 0) 23 | rabbitInformer := informerFactory.Stable().V1().Rabbits().Informer() 24 | rabbitInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ 25 | AddFunc: func(obj interface{}) { 26 | rabbit, ok := obj.(*v1.Rabbit) 27 | if !ok { 28 | return 29 | } 30 | fmt.Printf("A rabbit is created: %s\n", rabbit.Name) 31 | }, 32 | UpdateFunc: func(oldObj, newObj interface{}) { 33 | newRabbit, ok := oldObj.(*v1.Rabbit) 34 | if !ok { 35 | return 36 | } 37 | fmt.Printf("A rabbit is updated: %s\n", newRabbit.Name) 38 | }, 39 | DeleteFunc: func(obj interface{}) { 40 | rabbit, ok := obj.(*v1.Rabbit) 41 | if !ok { 42 | return 43 | } 44 | fmt.Printf("A rabbit is deleted: %s\n", rabbit.Name) 45 | }, 46 | }) 47 | 48 | stopCh := make(chan struct{}) 49 | defer close(stopCh) 50 | 51 | fmt.Println("Start syncing....") 52 | 53 | go informerFactory.Start(stopCh) 54 | 55 | <-stopCh 56 | } 57 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/12-dynamic-informer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/spongeprojects/magicconch" 6 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 7 | "k8s.io/apimachinery/pkg/runtime/schema" 8 | "k8s.io/client-go/dynamic" 9 | "k8s.io/client-go/dynamic/dynamicinformer" 10 | "k8s.io/client-go/tools/cache" 11 | "k8s.io/client-go/tools/clientcmd" 12 | "os" 13 | ) 14 | 15 | func main() { 16 | if len(os.Args) < 2 { 17 | fmt.Println("resource missing") 18 | return 19 | } 20 | // 资源,比如 "configmaps.v1.", "deployments.v1.apps", "rabbits.v1.stable.wbsnail.com" 21 | resource := os.Args[1] 22 | 23 | kubeconfig := os.Getenv("KUBECONFIG") 24 | config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) 25 | magicconch.Must(err) 26 | 27 | // 注意创建了 dynamicClient, 而不是 clientset 28 | dynamicClient, err := dynamic.NewForConfig(config) 29 | magicconch.Must(err) 30 | 31 | // 同样这里也是 DynamicSharedInformerFactory, 而不是 SharedInformerFactory 32 | informerFactory := dynamicinformer.NewFilteredDynamicSharedInformerFactory( 33 | dynamicClient, 0, "tmp", nil) 34 | 35 | // 通过 schema 包提供的 ParseResourceArg 由资源描述字符串解析出 GroupVersionResource 36 | gvr, _ := schema.ParseResourceArg(resource) 37 | if gvr == nil { 38 | fmt.Println("cannot parse gvr") 39 | return 40 | } 41 | // 使用 gvr 动态生成 Informer 42 | informer := informerFactory.ForResource(*gvr).Informer() 43 | // 熟悉的代码,熟悉的味道,只是收到的 obj 类型好像不太一样 44 | informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ 45 | AddFunc: func(obj interface{}) { 46 | // *unstructured.Unstructured 类是所有 Kubernetes 资源类型公共方法的抽象, 47 | // 提供所有对公共属性的访问方法,像 GetName, GetNamespace, GetLabels 等等, 48 | s, ok := obj.(*unstructured.Unstructured) 49 | if !ok { 50 | return 51 | } 52 | fmt.Printf("created: %s\n", s.GetName()) 53 | }, 54 | UpdateFunc: func(oldObj, newObj interface{}) { 55 | oldS, ok1 := oldObj.(*unstructured.Unstructured) 56 | newS, ok2 := newObj.(*unstructured.Unstructured) 57 | if !ok1 || !ok2 { 58 | return 59 | } 60 | // 要访问公共属性外的字段,可以借助 unstructured 包提供的一些助手方法: 61 | oldColor, ok1, err1 := unstructured.NestedString(oldS.Object, "spec", "color") 62 | newColor, ok2, err2 := unstructured.NestedString(newS.Object, "spec", "color") 63 | if !ok1 || !ok2 || err1 != nil || err2 != nil { 64 | fmt.Printf("updated: %s\n", newS.GetName()) 65 | } 66 | fmt.Printf("updated: %s, old color: %s, new color: %s\n", newS.GetName(), oldColor, newColor) 67 | }, 68 | DeleteFunc: func(obj interface{}) { 69 | s, ok := obj.(*unstructured.Unstructured) 70 | if !ok { 71 | return 72 | } 73 | fmt.Printf("deleted: %s\n", s.GetName()) 74 | }, 75 | }) 76 | 77 | stopCh := make(chan struct{}) 78 | defer close(stopCh) 79 | 80 | fmt.Println("Start syncing....") 81 | 82 | go informerFactory.Start(stopCh) 83 | 84 | <-stopCh 85 | } 86 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/2-reflector/clientset.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/spongeprojects/magicconch" 5 | "k8s.io/client-go/kubernetes" 6 | "k8s.io/client-go/tools/clientcmd" 7 | "os" 8 | ) 9 | 10 | func mustClientset() kubernetes.Interface { 11 | kubeconfig := os.Getenv("KUBECONFIG") 12 | 13 | config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) 14 | magicconch.Must(err) 15 | 16 | clientset, err := kubernetes.NewForConfig(config) 17 | magicconch.Must(err) 18 | 19 | return clientset 20 | } 21 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/2-reflector/lw.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "k8s.io/apimachinery/pkg/fields" 5 | "k8s.io/client-go/tools/cache" 6 | ) 7 | 8 | // newConfigMapsListerWatcher 用于创建 tmp namespace 下 configmaps 资源的 ListerWatcher 实例 9 | func newConfigMapsListerWatcher() cache.ListerWatcher { 10 | clientset := mustClientset() // 前面有说明 11 | client := clientset.CoreV1().RESTClient() // 客户端,请求器 12 | resource := "configmaps" // GET 请求参数之一 13 | namespace := "tmp" // GET 请求参数之一 14 | selector := fields.Everything() // GET 请求参数之一 15 | lw := cache.NewListWatchFromClient(client, resource, namespace, selector) 16 | return lw 17 | } 18 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/2-reflector/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/spongeprojects/magicconch" 6 | corev1 "k8s.io/api/core/v1" 7 | "k8s.io/apimachinery/pkg/util/wait" 8 | "k8s.io/client-go/tools/cache" 9 | "time" 10 | ) 11 | 12 | // newStore 用于创建一个 cache.Store 对象,作为当前资源状态的对象存储 13 | func newStore() cache.Store { 14 | return cache.NewStore(cache.MetaNamespaceKeyFunc) 15 | } 16 | 17 | // newQueue 用于创建一个 cache.Queue 对象,这里实现为 FIFO 先进先出队列, 18 | // 注意在初始化时 store 作为 KnownObjects 参数传入其中, 19 | // 因为在重新同步 (resync) 操作中 Reflector 需要知道当前的资源状态, 20 | // 另外在计算变更 (Delta) 时,也需要对比当前的资源状态。 21 | // 这个 KnownObjects 对队列,以及对 Reflector 都是只读的,用户需要自己维护好 store 的状态。 22 | func newQueue(store cache.Store) cache.Queue { 23 | return cache.NewDeltaFIFOWithOptions(cache.DeltaFIFOOptions{ 24 | KnownObjects: store, 25 | EmitDeltaTypeReplaced: true, 26 | }) 27 | } 28 | 29 | // newConfigMapsReflector 用于创建一个 cache.Reflector 对象, 30 | // 当 Reflector 开始运行 (Run) 后,队列中就会推入新收到的事件。 31 | func newConfigMapsReflector(queue cache.Queue) *cache.Reflector { 32 | lw := newConfigMapsListerWatcher() // 前面有说明 33 | // 第 2 个参数是 expectedType, 用此参数限制进入队列的事件, 34 | // 当然在 List 和 Watch 操作时返回的数据就只有一种类型,这个参数只起校验的作用; 35 | // 第 4 个参数是 resyncPeriod, 36 | // 这里传了 0,表示从不重新同步(除非连接超时或者中断), 37 | // 如果传了非 0 值,会定期进行全量同步,避免累积和服务器的不一致, 38 | // 同步过程中会产生 SYNC 类型的事件。 39 | return cache.NewReflector(lw, &corev1.ConfigMap{}, queue, 0) 40 | } 41 | 42 | func main() { 43 | fmt.Println("----- 2-reflector -----") 44 | 45 | store := newStore() 46 | queue := newQueue(store) 47 | reflector := newConfigMapsReflector(queue) 48 | 49 | stopCh := make(chan struct{}) 50 | defer close(stopCh) 51 | 52 | // reflector 开始运行后,队列中就会推入新收到的事件 53 | go reflector.Run(stopCh) 54 | 55 | // 注意处理事件过程中维护好 store 状态,包括 Add, Update, Delete 操作, 56 | // 否则会出现不同步问题,在 Informer 当中这些逻辑都已经被封装好了,但目前我们还需要关心一下。 57 | processObj := func(obj interface{}) error { 58 | // 最先收到的事件会被最先处理 59 | for _, d := range obj.(cache.Deltas) { 60 | switch d.Type { 61 | case cache.Sync, cache.Replaced, cache.Added, cache.Updated: 62 | if _, exists, err := store.Get(d.Object); err == nil && exists { 63 | if err := store.Update(d.Object); err != nil { 64 | return err 65 | } 66 | } else { 67 | if err := store.Add(d.Object); err != nil { 68 | return err 69 | } 70 | } 71 | case cache.Deleted: 72 | if err := store.Delete(d.Object); err != nil { 73 | return err 74 | } 75 | } 76 | configMap, ok := d.Object.(*corev1.ConfigMap) 77 | if !ok { 78 | return fmt.Errorf("not config: %T", d.Object) 79 | } 80 | fmt.Printf("%s: %s\n", d.Type, configMap.Name) 81 | } 82 | return nil 83 | } 84 | 85 | fmt.Println("Start syncing...") 86 | 87 | // 持续运行直到 stopCh 关闭 88 | wait.Until(func() { 89 | for { 90 | _, err := queue.Pop(processObj) 91 | magicconch.Must(err) 92 | } 93 | }, time.Second, stopCh) 94 | } 95 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/3-controller/clientset.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/spongeprojects/magicconch" 5 | "k8s.io/client-go/kubernetes" 6 | "k8s.io/client-go/tools/clientcmd" 7 | "os" 8 | ) 9 | 10 | func mustClientset() kubernetes.Interface { 11 | kubeconfig := os.Getenv("KUBECONFIG") 12 | 13 | config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) 14 | magicconch.Must(err) 15 | 16 | clientset, err := kubernetes.NewForConfig(config) 17 | magicconch.Must(err) 18 | 19 | return clientset 20 | } 21 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/3-controller/lw.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "k8s.io/apimachinery/pkg/fields" 5 | "k8s.io/client-go/tools/cache" 6 | ) 7 | 8 | // newConfigMapsListerWatcher 用于创建 tmp namespace 下 configmaps 资源的 ListerWatcher 实例 9 | func newConfigMapsListerWatcher() cache.ListerWatcher { 10 | clientset := mustClientset() // 前面有说明 11 | client := clientset.CoreV1().RESTClient() // 客户端,请求器 12 | resource := "configmaps" // GET 请求参数之一 13 | namespace := "tmp" // GET 请求参数之一 14 | selector := fields.Everything() // GET 请求参数之一 15 | lw := cache.NewListWatchFromClient(client, resource, namespace, selector) 16 | return lw 17 | } 18 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/3-controller/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | corev1 "k8s.io/api/core/v1" 6 | "k8s.io/client-go/tools/cache" 7 | ) 8 | 9 | func newController() cache.Controller { 10 | fmt.Println("----- 3-controller -----") 11 | 12 | lw := newConfigMapsListerWatcher() 13 | store := newStore() 14 | queue := newQueue(store) 15 | cfg := &cache.Config{ 16 | Queue: queue, 17 | ListerWatcher: lw, 18 | ObjectType: &corev1.ConfigMap{}, 19 | FullResyncPeriod: 0, 20 | RetryOnError: false, 21 | Process: func(obj interface{}) error { 22 | for _, d := range obj.(cache.Deltas) { 23 | switch d.Type { 24 | case cache.Sync, cache.Replaced, cache.Added, cache.Updated: 25 | if _, exists, err := store.Get(d.Object); err == nil && exists { 26 | if err := store.Update(d.Object); err != nil { 27 | return err 28 | } 29 | } else { 30 | if err := store.Add(d.Object); err != nil { 31 | return err 32 | } 33 | } 34 | case cache.Deleted: 35 | if err := store.Delete(d.Object); err != nil { 36 | return err 37 | } 38 | } 39 | configMap, ok := d.Object.(*corev1.ConfigMap) 40 | if !ok { 41 | return fmt.Errorf("not config: %T", d.Object) 42 | } 43 | fmt.Printf("%s: %s\n", d.Type, configMap.Name) 44 | } 45 | return nil 46 | }, 47 | } 48 | return cache.New(cfg) 49 | } 50 | 51 | func main() { 52 | controller := newController() 53 | 54 | stopCh := make(chan struct{}) 55 | defer close(stopCh) 56 | 57 | fmt.Println("Start syncing....") 58 | 59 | go controller.Run(stopCh) 60 | 61 | <-stopCh 62 | } 63 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/3-controller/store.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "k8s.io/client-go/tools/cache" 5 | ) 6 | 7 | // newStore 用于创建一个 cache.Store 对象,作为当前资源状态的对象存储 8 | func newStore() cache.Store { 9 | return cache.NewStore(cache.MetaNamespaceKeyFunc) 10 | } 11 | 12 | // newQueue 用于创建一个 cache.Queue 对象,这里实现为 FIFO 先进先出队列, 13 | // 注意在初始化时 store 作为 KnownObjects 参数传入其中, 14 | // 因为在重新同步 (resync) 操作中 Reflector 需要知道当前的资源状态, 15 | // 另外在计算变更 (Delta) 时,也需要对比当前的资源状态。 16 | // 这个 KnownObjects 对队列,以及对 Reflector 都是只读的,用户需要自己维护好 store 的状态。 17 | func newQueue(store cache.Store) cache.Queue { 18 | return cache.NewDeltaFIFOWithOptions(cache.DeltaFIFOOptions{ 19 | KnownObjects: store, 20 | EmitDeltaTypeReplaced: true, 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/4-informer/clientset.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/spongeprojects/magicconch" 5 | "k8s.io/client-go/kubernetes" 6 | "k8s.io/client-go/tools/clientcmd" 7 | "os" 8 | ) 9 | 10 | func mustClientset() kubernetes.Interface { 11 | kubeconfig := os.Getenv("KUBECONFIG") 12 | 13 | config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) 14 | magicconch.Must(err) 15 | 16 | clientset, err := kubernetes.NewForConfig(config) 17 | magicconch.Must(err) 18 | 19 | return clientset 20 | } 21 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/4-informer/lw.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "k8s.io/apimachinery/pkg/fields" 5 | "k8s.io/client-go/tools/cache" 6 | ) 7 | 8 | // newConfigMapsListerWatcher 用于创建 tmp namespace 下 configmaps 资源的 ListerWatcher 实例 9 | func newConfigMapsListerWatcher() cache.ListerWatcher { 10 | clientset := mustClientset() // 前面有说明 11 | client := clientset.CoreV1().RESTClient() // 客户端,请求器 12 | resource := "configmaps" // GET 请求参数之一 13 | namespace := "tmp" // GET 请求参数之一 14 | selector := fields.Everything() // GET 请求参数之一 15 | lw := cache.NewListWatchFromClient(client, resource, namespace, selector) 16 | return lw 17 | } 18 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/4-informer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | corev1 "k8s.io/api/core/v1" 6 | "k8s.io/client-go/tools/cache" 7 | ) 8 | 9 | func main() { 10 | fmt.Println("----- 4-informer -----") 11 | 12 | lw := newConfigMapsListerWatcher() 13 | // 第一个返回的参数是 cache.Store,这里暂时用不到所以直接丢弃 14 | _, controller := cache.NewInformer(lw, &corev1.ConfigMap{}, 0, cache.ResourceEventHandlerFuncs{ 15 | AddFunc: func(obj interface{}) { 16 | configMap, ok := obj.(*corev1.ConfigMap) 17 | if !ok { 18 | return 19 | } 20 | fmt.Printf("created: %s\n", configMap.Name) 21 | }, 22 | UpdateFunc: func(old, new interface{}) { 23 | configMap, ok := old.(*corev1.ConfigMap) 24 | if !ok { 25 | return 26 | } 27 | fmt.Printf("updated: %s\n", configMap.Name) 28 | }, 29 | DeleteFunc: func(obj interface{}) { 30 | configMap, ok := obj.(*corev1.ConfigMap) 31 | if !ok { 32 | return 33 | } 34 | fmt.Printf("deleted: %s\n", configMap.Name) 35 | }, 36 | }) 37 | 38 | stopCh := make(chan struct{}) 39 | defer close(stopCh) 40 | 41 | fmt.Println("Start syncing....") 42 | 43 | go controller.Run(stopCh) 44 | 45 | <-stopCh 46 | } 47 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/5-shared-informer/clientset.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/spongeprojects/magicconch" 5 | "k8s.io/client-go/kubernetes" 6 | "k8s.io/client-go/tools/clientcmd" 7 | "os" 8 | ) 9 | 10 | func mustClientset() kubernetes.Interface { 11 | kubeconfig := os.Getenv("KUBECONFIG") 12 | 13 | config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) 14 | magicconch.Must(err) 15 | 16 | clientset, err := kubernetes.NewForConfig(config) 17 | magicconch.Must(err) 18 | 19 | return clientset 20 | } 21 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/5-shared-informer/lw.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "k8s.io/apimachinery/pkg/fields" 5 | "k8s.io/client-go/tools/cache" 6 | ) 7 | 8 | // newConfigMapsListerWatcher 用于创建 tmp namespace 下 configmaps 资源的 ListerWatcher 实例 9 | func newConfigMapsListerWatcher() cache.ListerWatcher { 10 | clientset := mustClientset() // 前面有说明 11 | client := clientset.CoreV1().RESTClient() // 客户端,请求器 12 | resource := "configmaps" // GET 请求参数之一 13 | namespace := "tmp" // GET 请求参数之一 14 | selector := fields.Everything() // GET 请求参数之一 15 | lw := cache.NewListWatchFromClient(client, resource, namespace, selector) 16 | return lw 17 | } 18 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/5-shared-informer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | corev1 "k8s.io/api/core/v1" 6 | "k8s.io/client-go/tools/cache" 7 | ) 8 | 9 | func main() { 10 | fmt.Println("----- 5-shared-informer -----") 11 | 12 | lw := newConfigMapsListerWatcher() 13 | sharedInformer := cache.NewSharedInformer(lw, &corev1.ConfigMap{}, 0) 14 | // 添加一个处理程序 15 | sharedInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ 16 | AddFunc: func(obj interface{}) { 17 | configMap, ok := obj.(*corev1.ConfigMap) 18 | if !ok { 19 | return 20 | } 21 | fmt.Printf("created, printing namespace: %s\n", configMap.Namespace) 22 | }, 23 | }) 24 | // 添加另一个处理程序 25 | sharedInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ 26 | AddFunc: func(obj interface{}) { 27 | configMap, ok := obj.(*corev1.ConfigMap) 28 | if !ok { 29 | return 30 | } 31 | fmt.Printf("created, printing name: %s\n", configMap.Name) 32 | }, 33 | }) 34 | 35 | stopCh := make(chan struct{}) 36 | defer close(stopCh) 37 | 38 | fmt.Println("Start syncing....") 39 | 40 | go sharedInformer.Run(stopCh) 41 | 42 | <-stopCh 43 | } 44 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/6-indexer/clientset.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/spongeprojects/magicconch" 5 | "k8s.io/client-go/kubernetes" 6 | "k8s.io/client-go/tools/clientcmd" 7 | "os" 8 | ) 9 | 10 | func mustClientset() kubernetes.Interface { 11 | kubeconfig := os.Getenv("KUBECONFIG") 12 | 13 | config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) 14 | magicconch.Must(err) 15 | 16 | clientset, err := kubernetes.NewForConfig(config) 17 | magicconch.Must(err) 18 | 19 | return clientset 20 | } 21 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/6-indexer/lw.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "k8s.io/apimachinery/pkg/fields" 5 | "k8s.io/client-go/tools/cache" 6 | ) 7 | 8 | // newConfigMapsListerWatcher 用于创建 tmp namespace 下 configmaps 资源的 ListerWatcher 实例 9 | func newConfigMapsListerWatcher() cache.ListerWatcher { 10 | clientset := mustClientset() // 前面有说明 11 | client := clientset.CoreV1().RESTClient() // 客户端,请求器 12 | resource := "configmaps" // GET 请求参数之一 13 | namespace := "tmp" // GET 请求参数之一 14 | selector := fields.Everything() // GET 请求参数之一 15 | lw := cache.NewListWatchFromClient(client, resource, namespace, selector) 16 | return lw 17 | } 18 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/6-indexer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/spongeprojects/magicconch" 6 | corev1 "k8s.io/api/core/v1" 7 | "k8s.io/client-go/tools/cache" 8 | ) 9 | 10 | func main() { 11 | fmt.Println("----- 6-indexer -----") 12 | 13 | lw := newConfigMapsListerWatcher() 14 | indexers := cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc} 15 | // 仅演示用,只关心 indexer,不处理事件,所以传一个空的 HandlerFunc, 16 | // 实际使用中一般不会这样做 17 | indexer, informer := cache.NewIndexerInformer( 18 | lw, &corev1.ConfigMap{}, 0, cache.ResourceEventHandlerFuncs{}, indexers) 19 | 20 | stopCh := make(chan struct{}) 21 | defer close(stopCh) 22 | 23 | fmt.Println("Start syncing....") 24 | 25 | go informer.Run(stopCh) 26 | 27 | // 在 informer 首次同步完成后再操作 28 | if !cache.WaitForCacheSync(stopCh, informer.HasSynced) { 29 | panic("timed out waiting for caches to sync") 30 | } 31 | 32 | // 获取 cache.NamespaceIndex 索引下,索引值为 "tmp" 中的所有键 33 | keys, err := indexer.IndexKeys(cache.NamespaceIndex, "tmp") 34 | magicconch.Must(err) 35 | for _, k := range keys { 36 | fmt.Println(k) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/7-indexer-informer/clientset.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/spongeprojects/magicconch" 5 | "k8s.io/client-go/kubernetes" 6 | "k8s.io/client-go/tools/clientcmd" 7 | "os" 8 | ) 9 | 10 | func mustClientset() kubernetes.Interface { 11 | kubeconfig := os.Getenv("KUBECONFIG") 12 | 13 | config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) 14 | magicconch.Must(err) 15 | 16 | clientset, err := kubernetes.NewForConfig(config) 17 | magicconch.Must(err) 18 | 19 | return clientset 20 | } 21 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/7-indexer-informer/lw.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "k8s.io/apimachinery/pkg/fields" 5 | "k8s.io/client-go/tools/cache" 6 | ) 7 | 8 | // newConfigMapsListerWatcher 用于创建 tmp namespace 下 configmaps 资源的 ListerWatcher 实例 9 | func newConfigMapsListerWatcher() cache.ListerWatcher { 10 | clientset := mustClientset() // 前面有说明 11 | client := clientset.CoreV1().RESTClient() // 客户端,请求器 12 | resource := "configmaps" // GET 请求参数之一 13 | namespace := "tmp" // GET 请求参数之一 14 | selector := fields.Everything() // GET 请求参数之一 15 | lw := cache.NewListWatchFromClient(client, resource, namespace, selector) 16 | return lw 17 | } 18 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/7-indexer-informer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/spongeprojects/magicconch" 6 | corev1 "k8s.io/api/core/v1" 7 | "k8s.io/client-go/tools/cache" 8 | "strings" 9 | ) 10 | 11 | func main() { 12 | fmt.Println("----- 7-indexer-informer -----") 13 | 14 | lw := newConfigMapsListerWatcher() 15 | indexers := cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc} 16 | indexer, informer := cache.NewIndexerInformer(lw, &corev1.ConfigMap{}, 0, cache.ResourceEventHandlerFuncs{ 17 | AddFunc: func(obj interface{}) { 18 | configMap, ok := obj.(*corev1.ConfigMap) 19 | if !ok { 20 | return 21 | } 22 | fmt.Printf("created: %s\n", configMap.Name) 23 | }, 24 | }, indexers) 25 | 26 | stopCh := make(chan struct{}) 27 | defer close(stopCh) 28 | 29 | fmt.Println("Start syncing....") 30 | 31 | go informer.Run(stopCh) 32 | 33 | if !cache.WaitForCacheSync(stopCh, informer.HasSynced) { 34 | panic("timed out waiting for caches to sync") 35 | } 36 | 37 | keys, err := indexer.IndexKeys(cache.NamespaceIndex, "tmp") 38 | magicconch.Must(err) 39 | for _, k := range keys { 40 | fmt.Println(k) 41 | } 42 | 43 | startProbeServer(func(message string) string { 44 | keys, err := indexer.IndexKeys(cache.NamespaceIndex, "tmp") 45 | if err != nil { 46 | return "Error: " + err.Error() 47 | } 48 | return strings.Join(keys, ", ") 49 | }) 50 | 51 | <-stopCh 52 | } 53 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/7-indexer-informer/probe-server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pkg/errors" 6 | "github.com/spongeprojects/magicconch" 7 | "net" 8 | ) 9 | 10 | type ProbeHandlerFunc func(string) string 11 | 12 | type ProbeServer struct { 13 | handlerFunc ProbeHandlerFunc 14 | clients map[*ProbeClient]bool 15 | registerCh chan *ProbeClient 16 | unregisterCh chan *ProbeClient 17 | } 18 | 19 | type ProbeClient struct { 20 | socket net.Conn 21 | data chan []byte 22 | } 23 | 24 | func (server *ProbeServer) start() { 25 | for { 26 | select { 27 | case client := <-server.registerCh: 28 | server.clients[client] = true 29 | case client := <-server.unregisterCh: 30 | if _, ok := server.clients[client]; ok { 31 | client.socket.Close() 32 | delete(server.clients, client) 33 | } 34 | } 35 | } 36 | } 37 | 38 | func (server *ProbeServer) receive(client *ProbeClient) { 39 | for { 40 | message := make([]byte, 4096) 41 | l, err := client.socket.Read(message) 42 | if err != nil { 43 | server.unregisterCh <- client 44 | break 45 | } 46 | _, err = client.socket.Write([]byte(server.handlerFunc(string(message[:l])))) 47 | if err != nil { 48 | server.unregisterCh <- client 49 | break 50 | } 51 | } 52 | } 53 | 54 | func startProbeServer(handlerFunc ProbeHandlerFunc) { 55 | fmt.Println("Starting probe server...") 56 | 57 | listener, err := net.Listen("tcp", ":12345") 58 | magicconch.Must(err) 59 | 60 | manager := ProbeServer{ 61 | handlerFunc: handlerFunc, 62 | clients: make(map[*ProbeClient]bool), 63 | registerCh: make(chan *ProbeClient), 64 | unregisterCh: make(chan *ProbeClient), 65 | } 66 | 67 | go manager.start() 68 | 69 | for { 70 | conn, err := listener.Accept() 71 | if err != nil { 72 | fmt.Println(errors.Wrap(err, "accept connection error")) 73 | } 74 | client := &ProbeClient{socket: conn} 75 | manager.registerCh <- client 76 | go manager.receive(client) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/8-shared-index-informer/clientset.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/spongeprojects/magicconch" 5 | "k8s.io/client-go/kubernetes" 6 | "k8s.io/client-go/tools/clientcmd" 7 | "os" 8 | ) 9 | 10 | func mustClientset() kubernetes.Interface { 11 | kubeconfig := os.Getenv("KUBECONFIG") 12 | 13 | config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) 14 | magicconch.Must(err) 15 | 16 | clientset, err := kubernetes.NewForConfig(config) 17 | magicconch.Must(err) 18 | 19 | return clientset 20 | } 21 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/8-shared-index-informer/lw.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "k8s.io/apimachinery/pkg/fields" 5 | "k8s.io/client-go/tools/cache" 6 | ) 7 | 8 | // newConfigMapsListerWatcher 用于创建 tmp namespace 下 configmaps 资源的 ListerWatcher 实例 9 | func newConfigMapsListerWatcher() cache.ListerWatcher { 10 | clientset := mustClientset() // 前面有说明 11 | client := clientset.CoreV1().RESTClient() // 客户端,请求器 12 | resource := "configmaps" // GET 请求参数之一 13 | namespace := "tmp" // GET 请求参数之一 14 | selector := fields.Everything() // GET 请求参数之一 15 | lw := cache.NewListWatchFromClient(client, resource, namespace, selector) 16 | return lw 17 | } 18 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/8-shared-index-informer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/spongeprojects/magicconch" 6 | corev1 "k8s.io/api/core/v1" 7 | "k8s.io/client-go/tools/cache" 8 | "strings" 9 | ) 10 | 11 | func main() { 12 | fmt.Println("----- 8-shared-indexer-informer -----") 13 | 14 | lw := newConfigMapsListerWatcher() 15 | indexers := cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc} 16 | informer := cache.NewSharedIndexInformer(lw, &corev1.ConfigMap{}, 0, indexers) 17 | informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ 18 | AddFunc: func(obj interface{}) { 19 | configMap, ok := obj.(*corev1.ConfigMap) 20 | if !ok { 21 | return 22 | } 23 | fmt.Printf("created: %s\n", configMap.Name) 24 | }, 25 | }) 26 | 27 | stopCh := make(chan struct{}) 28 | defer close(stopCh) 29 | 30 | fmt.Println("Start syncing....") 31 | 32 | go informer.Run(stopCh) 33 | 34 | if !cache.WaitForCacheSync(stopCh, informer.HasSynced) { 35 | panic("timed out waiting for caches to sync") 36 | } 37 | 38 | keys, err := informer.GetIndexer().IndexKeys(cache.NamespaceIndex, "tmp") 39 | magicconch.Must(err) 40 | for _, k := range keys { 41 | fmt.Println(k) 42 | } 43 | 44 | startProbeServer(func(message string) string { 45 | keys, err := informer.GetIndexer().IndexKeys(cache.NamespaceIndex, "tmp") 46 | if err != nil { 47 | return "Error: " + err.Error() 48 | } 49 | return strings.Join(keys, ", ") 50 | }) 51 | 52 | <-stopCh 53 | } 54 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/8-shared-index-informer/probe-server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pkg/errors" 6 | "github.com/spongeprojects/magicconch" 7 | "net" 8 | ) 9 | 10 | type ProbeHandlerFunc func(string) string 11 | 12 | type ProbeServer struct { 13 | handlerFunc ProbeHandlerFunc 14 | clients map[*ProbeClient]bool 15 | registerCh chan *ProbeClient 16 | unregisterCh chan *ProbeClient 17 | } 18 | 19 | type ProbeClient struct { 20 | socket net.Conn 21 | data chan []byte 22 | } 23 | 24 | func (server *ProbeServer) start() { 25 | for { 26 | select { 27 | case client := <-server.registerCh: 28 | server.clients[client] = true 29 | case client := <-server.unregisterCh: 30 | if _, ok := server.clients[client]; ok { 31 | client.socket.Close() 32 | delete(server.clients, client) 33 | } 34 | } 35 | } 36 | } 37 | 38 | func (server *ProbeServer) receive(client *ProbeClient) { 39 | for { 40 | message := make([]byte, 4096) 41 | l, err := client.socket.Read(message) 42 | if err != nil { 43 | server.unregisterCh <- client 44 | break 45 | } 46 | _, err = client.socket.Write([]byte(server.handlerFunc(string(message[:l])))) 47 | if err != nil { 48 | server.unregisterCh <- client 49 | break 50 | } 51 | } 52 | } 53 | 54 | func startProbeServer(handlerFunc ProbeHandlerFunc) { 55 | fmt.Println("Starting probe server...") 56 | 57 | listener, err := net.Listen("tcp", ":12345") 58 | magicconch.Must(err) 59 | 60 | manager := ProbeServer{ 61 | handlerFunc: handlerFunc, 62 | clients: make(map[*ProbeClient]bool), 63 | registerCh: make(chan *ProbeClient), 64 | unregisterCh: make(chan *ProbeClient), 65 | } 66 | 67 | go manager.start() 68 | 69 | for { 70 | conn, err := listener.Accept() 71 | if err != nil { 72 | fmt.Println(errors.Wrap(err, "accept connection error")) 73 | } 74 | client := &ProbeClient{socket: conn} 75 | manager.registerCh <- client 76 | go manager.receive(client) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/9-shared-informer-factory/clientset.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/spongeprojects/magicconch" 5 | "k8s.io/client-go/kubernetes" 6 | "k8s.io/client-go/tools/clientcmd" 7 | "os" 8 | ) 9 | 10 | func mustClientset() kubernetes.Interface { 11 | kubeconfig := os.Getenv("KUBECONFIG") 12 | 13 | config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) 14 | magicconch.Must(err) 15 | 16 | clientset, err := kubernetes.NewForConfig(config) 17 | magicconch.Must(err) 18 | 19 | return clientset 20 | } 21 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/9-shared-informer-factory/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | corev1 "k8s.io/api/core/v1" 6 | "k8s.io/client-go/informers" 7 | "k8s.io/client-go/tools/cache" 8 | ) 9 | 10 | func main() { 11 | fmt.Println("----- 9-shared-informer-factory -----") 12 | 13 | // mustClientset 用于创建 kubernetes.Interface 实例, 14 | // 代码在前一部分中; 15 | // 第 2 个参数是 defaultResync,是构建新 Informer 时默认的 resyncPeriod, 16 | // resyncPeriod 在前一部分中介绍过了; 17 | informerFactory := informers.NewSharedInformerFactoryWithOptions( 18 | mustClientset(), 0, informers.WithNamespace("tmp")) 19 | configMapsInformer := informerFactory.Core().V1().ConfigMaps().Informer() 20 | configMapsInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ 21 | AddFunc: func(obj interface{}) { 22 | configMap, ok := obj.(*corev1.ConfigMap) 23 | if !ok { 24 | return 25 | } 26 | fmt.Printf("created: %s\n", configMap.Name) 27 | }, 28 | UpdateFunc: func(oldObj, newObj interface{}) { 29 | configMap, ok := newObj.(*corev1.ConfigMap) 30 | if !ok { 31 | return 32 | } 33 | fmt.Printf("updated: %s\n", configMap.Name) 34 | }, 35 | DeleteFunc: func(obj interface{}) { 36 | configMap, ok := obj.(*corev1.ConfigMap) 37 | if !ok { 38 | return 39 | } 40 | fmt.Printf("deleted: %s\n", configMap.Name) 41 | }, 42 | }) 43 | 44 | stopCh := make(chan struct{}) 45 | defer close(stopCh) 46 | 47 | fmt.Println("Start syncing....") 48 | 49 | go informerFactory.Start(stopCh) 50 | 51 | <-stopCh 52 | } 53 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/wbsnail/articles/archive/dive-into-kubernetes-informer 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/pkg/errors v0.9.1 7 | github.com/spongeprojects/magicconch v0.0.6 8 | k8s.io/api v0.21.0 9 | k8s.io/apimachinery v0.21.0 10 | k8s.io/client-go v0.21.0 11 | k8s.io/klog/v2 v2.8.0 12 | ) 13 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/informer.drawio: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | -------------------------------------------------------------------------------- /archive/dive-into-kubernetes-informer/probe/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pkg/errors" 6 | "github.com/spongeprojects/magicconch" 7 | "net" 8 | "os" 9 | ) 10 | 11 | func main() { 12 | if len(os.Args) < 2 { 13 | fmt.Println("missing argument: message") 14 | return 15 | } 16 | 17 | message := os.Args[1] 18 | 19 | conn, err := net.Dial("tcp", "localhost:12345") 20 | magicconch.Must(err) 21 | 22 | _, err = conn.Write([]byte(message)) 23 | if err != nil { 24 | fmt.Println(errors.Wrap(err, "send message error")) 25 | } 26 | 27 | response := make([]byte, 4096) 28 | _, err = conn.Read(response) 29 | magicconch.Must(err) 30 | 31 | fmt.Println("Current state:\n") 32 | fmt.Println(string(response)) 33 | } 34 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/basic-controller 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/pkg/errors v0.9.1 7 | github.com/spongeprojects/magicconch v0.0.6 8 | ) 9 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 4 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 5 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 6 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 7 | github.com/spongeprojects/magicconch v0.0.6 h1:mSfokxxWC9hglSCM5FUrhdBg7tUFGbEePGWEo+3Ol8c= 8 | github.com/spongeprojects/magicconch v0.0.6/go.mod h1:Ad8z6fYdCtlbl9gKgQ4p+W4NgIKyMyruipD0/H7S3n8= 9 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 10 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 11 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 12 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 13 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 14 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 15 | -------------------------------------------------------------------------------- /lab/csrf-attack/attacker-website/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/spongeprojects/magicconch" 6 | "html/template" 7 | ) 8 | 9 | var tmpl = template.Must(template.New("index").Parse(` 10 | 11 | 12 | Welcome 13 | 14 | 15 |

点击按钮,免费获取 50000 元现金奖励:

16 |
17 | 18 | 19 |
20 | 21 | `)) 22 | 23 | func main() { 24 | r := gin.Default() 25 | r.SetHTMLTemplate(tmpl) 26 | r.GET("/", func(c *gin.Context) { 27 | c.HTML(200, "index", nil) 28 | }) 29 | err := r.Run("0.0.0.0:8081") 30 | magicconch.Must(err) 31 | } 32 | -------------------------------------------------------------------------------- /lab/csrf-attack/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/basic-controller 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/gin-gonic/gin v1.7.1 7 | github.com/go-playground/validator/v10 v10.6.0 // indirect 8 | github.com/golang/protobuf v1.5.2 // indirect 9 | github.com/json-iterator/go v1.1.11 // indirect 10 | github.com/leodido/go-urn v1.2.1 // indirect 11 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 12 | github.com/modern-go/reflect2 v1.0.1 // indirect 13 | github.com/spongeprojects/magicconch v0.0.6 14 | github.com/ugorji/go v1.2.5 // indirect 15 | golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf // indirect 16 | golang.org/x/sys v0.0.0-20210507161434-a76c4d0a0096 // indirect 17 | golang.org/x/text v0.3.6 // indirect 18 | gopkg.in/yaml.v2 v2.4.0 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /lab/csrf-attack/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 5 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 6 | github.com/gin-gonic/gin v1.7.1 h1:qC89GU3p8TvKWMAVhEpmpB2CIb1hnqt2UdKZaP93mS8= 7 | github.com/gin-gonic/gin v1.7.1/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= 8 | github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= 9 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 10 | github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= 11 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= 12 | github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= 13 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= 14 | github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= 15 | github.com/go-playground/validator/v10 v10.6.0 h1:UGIt4xR++fD9QrBOoo/ascJfGe3AGHEB9s6COnss4Rk= 16 | github.com/go-playground/validator/v10 v10.6.0/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk= 17 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 18 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 19 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 20 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 21 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= 22 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 23 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 24 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 25 | github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= 26 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 27 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= 28 | github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= 29 | github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= 30 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 31 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 32 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 33 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 34 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 35 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 36 | github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= 37 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 38 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 39 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 40 | github.com/spongeprojects/magicconch v0.0.6 h1:mSfokxxWC9hglSCM5FUrhdBg7tUFGbEePGWEo+3Ol8c= 41 | github.com/spongeprojects/magicconch v0.0.6/go.mod h1:Ad8z6fYdCtlbl9gKgQ4p+W4NgIKyMyruipD0/H7S3n8= 42 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 43 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 44 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 45 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 46 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 47 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 48 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 49 | github.com/ugorji/go v1.2.5 h1:NozRHfUeEta89taVkyfsDVSy2f7v89Frft4pjnWuGuc= 50 | github.com/ugorji/go v1.2.5/go.mod h1:gat2tIT8KJG8TVI8yv77nEO/KYT6dV7JE1gfUa8Xuls= 51 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 52 | github.com/ugorji/go/codec v1.2.5 h1:8WobZKAk18Msm2CothY2jnztY56YVY8kF1oQrj21iis= 53 | github.com/ugorji/go/codec v1.2.5/go.mod h1:QPxoTbPKSEAlAHPYt02++xp/en9B/wUdwFCz+hj5caA= 54 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 55 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 56 | golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf h1:B2n+Zi5QeYRDAEodEu72OS36gmTWjgpXr2+cWcBW90o= 57 | golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= 58 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 59 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 60 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 61 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 62 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 63 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 64 | golang.org/x/sys v0.0.0-20210507161434-a76c4d0a0096 h1:5PbJGn5Sp3GEUjJ61aYbUP6RIo3Z3r2E4Tv9y2z8UHo= 65 | golang.org/x/sys v0.0.0-20210507161434-a76c4d0a0096/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 66 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 67 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 68 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 69 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 70 | golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= 71 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 72 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 73 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 74 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 75 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 76 | google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= 77 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 78 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 79 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 80 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 81 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 82 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 83 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 84 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 85 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 86 | -------------------------------------------------------------------------------- /lab/csrf-attack/server-protected/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/spongeprojects/magicconch" 6 | "html/template" 7 | ) 8 | 9 | var tmpl = template.Must(template.New("index").Parse(` 10 | 11 | 12 | CSRF Victim 13 | 14 | 15 |

账户余额:

16 | 21 | 22 |
23 | 24 | {{if .LoggedIn}} 25 |

[退出] 你好, {{.Username}}

26 |
27 | 28 | 29 | 30 |
31 | {{else}} 32 |

登录:

33 |
34 | 35 | 36 |
37 | {{end}} 38 | 39 | `)) 40 | 41 | var users = make(map[string]int) 42 | var sessions = make(map[string]string) 43 | var csrfSessions = make(map[string]string) 44 | 45 | func main() { 46 | r := gin.Default() 47 | r.SetHTMLTemplate(tmpl) 48 | r.GET("/", func(c *gin.Context) { 49 | // 获取会话 ID 50 | sessionID, _ := c.Cookie("sessionID") 51 | // 根据会话 ID 查找用户 52 | username, loggedIn := sessions[sessionID] 53 | csrfToken := magicconch.StringRand(32) 54 | csrfSessions[sessionID] = csrfToken 55 | 56 | c.HTML(200, "index", gin.H{ 57 | "LoggedIn": loggedIn, 58 | "Username": username, 59 | "Users": users, 60 | "CSRFToken": csrfToken, 61 | }) 62 | }) 63 | r.POST("/login", func(c *gin.Context) { 64 | username := c.PostForm("username") 65 | users[username] = 1000 66 | // 生成并保存新会话 67 | sessionID := magicconch.StringRand(32) 68 | sessions[sessionID] = username 69 | // 保存会话 ID 到客户端 70 | c.SetCookie("sessionID", sessionID, 3600, "/", "", false, false) 71 | // 登录后回到首页(不能直接把 POST 请求重定向到首页) 72 | c.Header("Content-Type", "text/html") 73 | c.String(200, "") 74 | }) 75 | r.POST("/transfer", func(c *gin.Context) { 76 | // 获取会话 ID 77 | sessionID, _ := c.Cookie("sessionID") 78 | // 根据会话 ID 查找用户 79 | username, loggedIn := sessions[sessionID] 80 | if !loggedIn { 81 | c.String(403, "你没有登录") 82 | return 83 | } 84 | amount := magicconch.StringToInt(c.PostForm("amount")) 85 | csrfToken := c.PostForm("csrf_token") 86 | if csrfSessions[sessionID] != csrfToken { 87 | c.String(403, "CSRF 攻击,抓到你啦!(如果你不是恶意用户,请刷新页面重试)") 88 | return 89 | } 90 | users[username] -= amount 91 | // 转账完成后回到首页(不能直接把 POST 请求重定向到首页) 92 | c.Header("Content-Type", "text/html") 93 | c.String(200, "") 94 | }) 95 | r.GET("/logout", func(c *gin.Context) { 96 | // 清除会话 97 | c.SetCookie("sessionID", "", 0, "/", "", false, false) 98 | // 退出登录后回到首页 99 | c.Redirect(307, "/") 100 | }) 101 | err := r.Run("0.0.0.0:8080") 102 | magicconch.Must(err) 103 | } 104 | -------------------------------------------------------------------------------- /lab/csrf-attack/server-volunable/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/spongeprojects/magicconch" 6 | "html/template" 7 | ) 8 | 9 | var tmpl = template.Must(template.New("index").Parse(` 10 | 11 | 12 | CSRF Victim 13 | 14 | 15 |

账户余额:

16 | 21 | 22 |
23 | 24 | {{if .LoggedIn}} 25 |

[退出] 你好, {{.Username}}

26 |
27 | 28 | 29 |
30 | {{else}} 31 |

登录:

32 |
33 | 34 | 35 |
36 | {{end}} 37 | 38 | `)) 39 | 40 | var users = make(map[string]int) 41 | var sessions = make(map[string]string) 42 | 43 | func main() { 44 | r := gin.Default() 45 | r.SetHTMLTemplate(tmpl) 46 | r.GET("/", func(c *gin.Context) { 47 | // 获取会话 ID 48 | sessionID, _ := c.Cookie("sessionID") 49 | // 根据会话 ID 查找用户 50 | username, loggedIn := sessions[sessionID] 51 | 52 | c.HTML(200, "index", gin.H{ 53 | "LoggedIn": loggedIn, 54 | "Username": username, 55 | "Users": users, 56 | }) 57 | }) 58 | r.POST("/login", func(c *gin.Context) { 59 | username := c.PostForm("username") 60 | users[username] = 1000 61 | // 生成并保存新会话 62 | sessionID := magicconch.StringRand(32) 63 | sessions[sessionID] = username 64 | // 保存会话 ID 到客户端 65 | c.SetCookie("sessionID", sessionID, 3600, "/", "", false, false) 66 | // 登录后回到首页(不能直接把 POST 请求重定向到首页) 67 | c.Header("Content-Type", "text/html") 68 | c.String(200, "") 69 | }) 70 | r.POST("/transfer", func(c *gin.Context) { 71 | // 获取会话 ID 72 | sessionID, _ := c.Cookie("sessionID") 73 | // 根据会话 ID 查找用户 74 | username, loggedIn := sessions[sessionID] 75 | if !loggedIn { 76 | c.String(403, "你没有登录") 77 | return 78 | } 79 | amount := magicconch.StringToInt(c.PostForm("amount")) 80 | users[username] -= amount 81 | // 转账完成后回到首页(不能直接把 POST 请求重定向到首页) 82 | c.Header("Content-Type", "text/html") 83 | c.String(200, "") 84 | }) 85 | r.GET("/logout", func(c *gin.Context) { 86 | // 清除会话 87 | c.SetCookie("sessionID", "", 0, "/", "", false, false) 88 | // 退出登录后回到首页 89 | c.Redirect(307, "/") 90 | }) 91 | err := r.Run("0.0.0.0:8080") 92 | magicconch.Must(err) 93 | } 94 | -------------------------------------------------------------------------------- /lab/shadow-string/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/basic-controller 2 | 3 | go 1.16 4 | -------------------------------------------------------------------------------- /lab/shadow-string/go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junchaw/articles/0fb09b5689e2cbcdfeb88790eaafead50d0fac89/lab/shadow-string/go.sum -------------------------------------------------------------------------------- /lab/shadow-string/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | func main() { 9 | a := make([]byte, 16) 10 | 11 | r := strings.NewReader("test") 12 | _, _ = r.Read(a) 13 | 14 | b := "test" 15 | 16 | fmt.Println(string(a), b) 17 | fmt.Println(string(a) == b) 18 | fmt.Println(len(string(a)), len(b)) 19 | fmt.Println(a, []byte(b)) 20 | } 21 | -------------------------------------------------------------------------------- /lab/unix-socket-broadcast/client/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "github.com/pkg/errors" 7 | "github.com/spongeprojects/magicconch" 8 | "net" 9 | "os" 10 | "strings" 11 | ) 12 | 13 | type Client struct { 14 | socket net.Conn 15 | } 16 | 17 | func (client *Client) receive() { 18 | defer func(socket net.Conn) { 19 | err := socket.Close() 20 | if err != nil { 21 | fmt.Println(errors.Wrap(err, "close socket error")) 22 | } 23 | }(client.socket) 24 | 25 | for { 26 | message := make([]byte, 4096) 27 | length, err := client.socket.Read(message) 28 | if err != nil { 29 | break 30 | } 31 | if length > 0 { 32 | fmt.Println("[RECEIVED]: " + string(message)) 33 | } 34 | } 35 | } 36 | 37 | func main() { 38 | fmt.Println("Starting client...") 39 | 40 | conn, err := net.Dial("tcp", "localhost:12345") 41 | magicconch.Must(err) 42 | 43 | client := &Client{socket: conn} 44 | 45 | go client.receive() 46 | 47 | fmt.Println("[WAITING]") 48 | for { 49 | reader := bufio.NewReader(os.Stdin) 50 | message, _ := reader.ReadString('\n') 51 | message = strings.Trim(message, "\n") 52 | fmt.Println("[SENDING]: " + message) 53 | _, err := conn.Write([]byte(message)) 54 | if err != nil { 55 | fmt.Println(errors.Wrap(err, "send message error")) 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lab/unix-socket-broadcast/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/basic-controller 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/pkg/errors v0.9.1 7 | github.com/spongeprojects/magicconch v0.0.6 8 | ) 9 | -------------------------------------------------------------------------------- /lab/unix-socket-broadcast/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 4 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 5 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 6 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 7 | github.com/spongeprojects/magicconch v0.0.6 h1:mSfokxxWC9hglSCM5FUrhdBg7tUFGbEePGWEo+3Ol8c= 8 | github.com/spongeprojects/magicconch v0.0.6/go.mod h1:Ad8z6fYdCtlbl9gKgQ4p+W4NgIKyMyruipD0/H7S3n8= 9 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 10 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 11 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 12 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 13 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 14 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 15 | -------------------------------------------------------------------------------- /lab/unix-socket-broadcast/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pkg/errors" 6 | "github.com/spongeprojects/magicconch" 7 | "net" 8 | ) 9 | 10 | type ClientManager struct { 11 | clients map[*Client]bool 12 | broadcastCh chan []byte 13 | registerCh chan *Client 14 | unregisterCh chan *Client 15 | } 16 | 17 | type Client struct { 18 | socket net.Conn 19 | data chan []byte 20 | } 21 | 22 | func (manager *ClientManager) start() { 23 | for { 24 | select { 25 | case client := <-manager.registerCh: 26 | manager.clients[client] = true 27 | fmt.Println("[REGISTERED]: Client registered!") 28 | case client := <-manager.unregisterCh: 29 | if _, ok := manager.clients[client]; ok { 30 | close(client.data) 31 | delete(manager.clients, client) 32 | fmt.Println("[UNREGISTERED]: Client unregistered!") 33 | } 34 | case message := <-manager.broadcastCh: 35 | fmt.Println("[BROADCASTING]: " + string(message)) 36 | for client := range manager.clients { 37 | select { 38 | case client.data <- message: 39 | default: 40 | close(client.data) 41 | delete(manager.clients, client) 42 | } 43 | } 44 | } 45 | } 46 | } 47 | 48 | func (manager *ClientManager) receive(client *Client) { 49 | for { 50 | message := make([]byte, 4096) 51 | length, err := client.socket.Read(message) 52 | if err != nil { 53 | manager.unregisterCh <- client 54 | client.socket.Close() 55 | break 56 | } 57 | if length > 0 { 58 | fmt.Println("[RECEIVED]: " + string(message)) 59 | manager.broadcastCh <- message 60 | } 61 | } 62 | } 63 | 64 | func (manager *ClientManager) send(client *Client) { 65 | defer client.socket.Close() 66 | for { 67 | select { 68 | case message, ok := <-client.data: 69 | if !ok { 70 | return 71 | } 72 | client.socket.Write(message) 73 | } 74 | } 75 | } 76 | 77 | func main() { 78 | fmt.Println("Starting server...") 79 | 80 | listener, err := net.Listen("tcp", ":12345") 81 | magicconch.Must(err) 82 | 83 | manager := ClientManager{ 84 | clients: make(map[*Client]bool), 85 | broadcastCh: make(chan []byte), 86 | registerCh: make(chan *Client), 87 | unregisterCh: make(chan *Client), 88 | } 89 | 90 | go manager.start() 91 | 92 | fmt.Println("[WAITING]") 93 | for { 94 | conn, err := listener.Accept() 95 | if err != nil { 96 | fmt.Println(errors.Wrap(err, "accept connection error")) 97 | } 98 | client := &Client{socket: conn, data: make(chan []byte)} 99 | manager.registerCh <- client 100 | go manager.receive(client) 101 | go manager.send(client) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /lab/unix-socket-echo/client/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "github.com/pkg/errors" 7 | "github.com/spongeprojects/magicconch" 8 | "net" 9 | "os" 10 | "strings" 11 | ) 12 | 13 | type Client struct { 14 | socket net.Conn 15 | } 16 | 17 | func (client *Client) receive() { 18 | defer func(socket net.Conn) { 19 | err := socket.Close() 20 | if err != nil { 21 | fmt.Println(errors.Wrap(err, "close socket error")) 22 | } 23 | }(client.socket) 24 | 25 | for { 26 | message := make([]byte, 4096) 27 | length, err := client.socket.Read(message) 28 | if err != nil { 29 | break 30 | } 31 | if length > 0 { 32 | fmt.Println("[RECEIVED]: " + string(message)) 33 | } 34 | } 35 | } 36 | 37 | func main() { 38 | fmt.Println("Starting client...") 39 | 40 | conn, err := net.Dial("tcp", "localhost:12345") 41 | magicconch.Must(err) 42 | 43 | client := &Client{socket: conn} 44 | 45 | go client.receive() 46 | 47 | fmt.Println("[WAITING]") 48 | for { 49 | reader := bufio.NewReader(os.Stdin) 50 | message, _ := reader.ReadString('\n') 51 | message = strings.Trim(message, "\n") 52 | fmt.Println("[SENDING]: " + message) 53 | _, err := conn.Write([]byte(message)) 54 | if err != nil { 55 | fmt.Println(errors.Wrap(err, "send message error")) 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lab/unix-socket-echo/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/basic-controller 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/pkg/errors v0.9.1 7 | github.com/spongeprojects/magicconch v0.0.6 8 | ) 9 | -------------------------------------------------------------------------------- /lab/unix-socket-echo/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 4 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 5 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 6 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 7 | github.com/spongeprojects/magicconch v0.0.6 h1:mSfokxxWC9hglSCM5FUrhdBg7tUFGbEePGWEo+3Ol8c= 8 | github.com/spongeprojects/magicconch v0.0.6/go.mod h1:Ad8z6fYdCtlbl9gKgQ4p+W4NgIKyMyruipD0/H7S3n8= 9 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 10 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 11 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 12 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 13 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 14 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 15 | -------------------------------------------------------------------------------- /lab/unix-socket-echo/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pkg/errors" 6 | "github.com/spongeprojects/magicconch" 7 | "net" 8 | ) 9 | 10 | type ClientManager struct { 11 | clients map[*Client]bool 12 | registerCh chan *Client 13 | unregisterCh chan *Client 14 | } 15 | 16 | type Client struct { 17 | socket net.Conn 18 | data chan []byte 19 | } 20 | 21 | func (manager *ClientManager) start() { 22 | for { 23 | select { 24 | case client := <-manager.registerCh: 25 | manager.clients[client] = true 26 | fmt.Println("[REGISTERED]: Client registered!") 27 | case client := <-manager.unregisterCh: 28 | if _, ok := manager.clients[client]; ok { 29 | close(client.data) 30 | delete(manager.clients, client) 31 | fmt.Println("[UNREGISTERED]: Client unregistered!") 32 | } 33 | } 34 | } 35 | } 36 | 37 | func (manager *ClientManager) receive(client *Client) { 38 | for { 39 | message := make([]byte, 4096) 40 | _, err := client.socket.Read(message) 41 | if err != nil { 42 | manager.unregisterCh <- client 43 | client.socket.Close() 44 | break 45 | } 46 | select { 47 | case client.data <- message: 48 | default: 49 | close(client.data) 50 | delete(manager.clients, client) 51 | } 52 | } 53 | } 54 | 55 | func (manager *ClientManager) send(client *Client) { 56 | defer client.socket.Close() 57 | for { 58 | select { 59 | case message, ok := <-client.data: 60 | if !ok { 61 | return 62 | } 63 | client.socket.Write(message) 64 | } 65 | } 66 | } 67 | 68 | func main() { 69 | fmt.Println("Starting server...") 70 | 71 | listener, err := net.Listen("tcp", ":12345") 72 | magicconch.Must(err) 73 | 74 | manager := ClientManager{ 75 | clients: make(map[*Client]bool), 76 | registerCh: make(chan *Client), 77 | unregisterCh: make(chan *Client), 78 | } 79 | 80 | go manager.start() 81 | 82 | fmt.Println("[WAITING]") 83 | for { 84 | conn, err := listener.Accept() 85 | if err != nil { 86 | fmt.Println(errors.Wrap(err, "accept connection error")) 87 | } 88 | client := &Client{socket: conn, data: make(chan []byte)} 89 | manager.registerCh <- client 90 | go manager.receive(client) 91 | go manager.send(client) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /lab/upgrade-plan/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "math/rand" 7 | "os" 8 | "strconv" 9 | "time" 10 | ) 11 | 12 | var debug = os.Getenv("DEBUG") != "" 13 | var counter = 0 14 | 15 | type DisruptionBudget struct { 16 | AppName string 17 | DisruptionAllowed int 18 | } 19 | 20 | type Node struct { 21 | NodeName string 22 | } 23 | 24 | // Application represents an instance, like a Pod 25 | type Application struct { 26 | AppName string 27 | NodeName string 28 | } 29 | 30 | type Calculator struct { 31 | pods map[string]map[string]bool 32 | memo map[[16]byte][]string 33 | } 34 | 35 | // calculateStep finds nodes that can be upgraded at once 36 | func (c *Calculator) calculateStep(nodes []string, budgets map[string]int) (steps []string) { 37 | if debug { 38 | counter++ 39 | log.Println(counter, nodes, budgets) 40 | } 41 | 42 | budgetsLeft := budgets 43 | for _, node := range nodes { 44 | canUpgrade := true 45 | appsOnNode := c.pods[node] 46 | budgetsIfUpgrade := make(map[string]int) 47 | for app := range budgetsLeft { 48 | if appsOnNode[app] { 49 | budgetsIfUpgrade[app] = budgetsLeft[app] - 1 50 | if budgetsIfUpgrade[app] < 0 { 51 | canUpgrade = false 52 | break 53 | } 54 | } else { 55 | budgetsIfUpgrade[app] = budgetsLeft[app] 56 | } 57 | } 58 | 59 | if canUpgrade { 60 | budgetsLeft = budgetsIfUpgrade 61 | steps = append(steps, node) 62 | } 63 | } 64 | return steps 65 | } 66 | 67 | // calculate generates an upgrade plan 68 | func (c *Calculator) calculate(nodes []string, budgets map[string]int) [][]string { 69 | log.Println("calculating...") 70 | var plan [][]string 71 | for len(nodes) > 0 { 72 | var nodesLeft []string 73 | step := c.calculateStep(nodes, budgets) 74 | log.Printf("step calculated: %v", step) 75 | for _, node := range nodes { 76 | if !stringInSlice(node, step) { 77 | nodesLeft = append(nodesLeft, node) 78 | } 79 | } 80 | plan = append(plan, step) 81 | if len(nodes) == len(nodesLeft) { 82 | log.Fatalf("no nodes can be upgraded: %v", nodes) 83 | } 84 | nodes = nodesLeft 85 | } 86 | return plan 87 | } 88 | 89 | // GeneratePlan generates an upgrade plan 90 | func (c *Calculator) GeneratePlan(nodes []Node, pods []Application, budgets []DisruptionBudget) [][]string { 91 | log.Println("preparing...") 92 | var nodeNames []string 93 | for _, node := range nodes { 94 | nodeNames = append(nodeNames, node.NodeName) 95 | } 96 | podsOnNode := make(map[string]map[string]bool) 97 | for _, pod := range pods { 98 | if podsOnNode[pod.NodeName] == nil { 99 | podsOnNode[pod.NodeName] = make(map[string]bool) 100 | } 101 | podsOnNode[pod.NodeName][pod.AppName] = true 102 | } 103 | budgetMap := make(map[string]int) 104 | for _, budget := range budgets { 105 | budgetMap[budget.AppName] = budget.DisruptionAllowed 106 | } 107 | c.pods = podsOnNode 108 | return c.calculate(nodeNames, budgetMap) 109 | } 110 | 111 | func stringInSlice(str string, slice []string) bool { 112 | for _, s := range slice { 113 | if s == str { 114 | return true 115 | } 116 | } 117 | return false 118 | } 119 | 120 | type Testcase struct { 121 | Nodes []Node 122 | Pods []Application 123 | Budgets []DisruptionBudget 124 | } 125 | 126 | var testcases = []Testcase{ 127 | { 128 | Nodes: []Node{ 129 | {NodeName: "n1"}, 130 | {NodeName: "n2"}, 131 | {NodeName: "n3"}, 132 | }, 133 | Pods: []Application{ 134 | {AppName: "app1", NodeName: "n1"}, 135 | {AppName: "app1", NodeName: "n2"}, 136 | {AppName: "app2", NodeName: "n1"}, 137 | {AppName: "app2", NodeName: "n2"}, 138 | {AppName: "app3", NodeName: "n2"}, 139 | {AppName: "app3", NodeName: "n3"}, 140 | }, 141 | Budgets: []DisruptionBudget{ 142 | {AppName: "app1", DisruptionAllowed: 1}, 143 | {AppName: "app2", DisruptionAllowed: 1}, 144 | {AppName: "app3", DisruptionAllowed: 1}, 145 | }, 146 | }, 147 | { 148 | Nodes: []Node{ 149 | {NodeName: "n1"}, 150 | {NodeName: "n2"}, 151 | {NodeName: "n3"}, 152 | {NodeName: "n4"}, 153 | {NodeName: "n5"}, 154 | {NodeName: "n6"}, 155 | {NodeName: "n7"}, 156 | {NodeName: "n8"}, 157 | {NodeName: "n9"}, 158 | {NodeName: "n10"}, 159 | }, 160 | Pods: []Application{ 161 | {NodeName: "n1", AppName: "app1"}, 162 | {NodeName: "n1", AppName: "app3"}, 163 | {NodeName: "n1", AppName: "app4"}, 164 | {NodeName: "n2", AppName: "app1"}, 165 | {NodeName: "n2", AppName: "app2"}, 166 | {NodeName: "n3", AppName: "app1"}, 167 | {NodeName: "n3", AppName: "app2"}, 168 | {NodeName: "n3", AppName: "app3"}, 169 | {NodeName: "n3", AppName: "app6"}, 170 | {NodeName: "n4", AppName: "app1"}, 171 | {NodeName: "n4", AppName: "app2"}, 172 | {NodeName: "n4", AppName: "app3"}, 173 | {NodeName: "n4", AppName: "app4"}, 174 | {NodeName: "n5", AppName: "app1"}, 175 | {NodeName: "n5", AppName: "app2"}, 176 | {NodeName: "n5", AppName: "app3"}, 177 | {NodeName: "n6", AppName: "app1"}, 178 | {NodeName: "n6", AppName: "app2"}, 179 | {NodeName: "n6", AppName: "app3"}, 180 | {NodeName: "n6", AppName: "app4"}, 181 | {NodeName: "n6", AppName: "app6"}, 182 | {NodeName: "n7", AppName: "app1"}, 183 | {NodeName: "n7", AppName: "app3"}, 184 | {NodeName: "n8", AppName: "app1"}, 185 | {NodeName: "n8", AppName: "app2"}, 186 | {NodeName: "n8", AppName: "app3"}, 187 | {NodeName: "n8", AppName: "app5"}, 188 | {NodeName: "n9", AppName: "app1"}, 189 | {NodeName: "n9", AppName: "app2"}, 190 | {NodeName: "n9", AppName: "app3"}, 191 | {NodeName: "n9", AppName: "app5"}, 192 | {NodeName: "n9", AppName: "app6"}, 193 | {NodeName: "n10", AppName: "app1"}, 194 | {NodeName: "n10", AppName: "app2"}, 195 | {NodeName: "n10", AppName: "app5"}, 196 | {NodeName: "n10", AppName: "app6"}, 197 | }, 198 | Budgets: []DisruptionBudget{ 199 | {AppName: "app1", DisruptionAllowed: 4}, 200 | {AppName: "app2", DisruptionAllowed: 8}, 201 | {AppName: "app3", DisruptionAllowed: 2}, 202 | {AppName: "app4", DisruptionAllowed: 2}, 203 | {AppName: "app5", DisruptionAllowed: 2}, 204 | {AppName: "app6", DisruptionAllowed: 2}, 205 | }, 206 | }, 207 | } 208 | 209 | // main 210 | // 211 | // To be considered: 212 | // - pods may be changed (scaled, rescheduled) during operation; 213 | // - apps may have more complex disruption restrictions; 214 | func main() { 215 | rand.Seed(time.Now().Unix()) 216 | 217 | if len(os.Args) < 3 { 218 | fmt.Println("usage: go run . [action]\n" + 219 | "\n" + 220 | "go run . testcase 0 # test specific testcase, index: 0\n" + 221 | "go run . random 10 5 # test random generated testcase, 10 nodes, 5 apps") 222 | return 223 | } 224 | 225 | action := os.Args[1] 226 | 227 | var testcase Testcase 228 | switch action { 229 | case "testcase": 230 | // test specific testcase 231 | if len(os.Args) < 3 { 232 | fmt.Println("arg missing") 233 | return 234 | } 235 | 236 | n, _ := strconv.Atoi(os.Args[2]) 237 | 238 | if n < 0 || n > len(testcases)-1 { 239 | fmt.Printf("undefined testcase: %s\n", os.Args[2]) 240 | return 241 | } 242 | testcase = testcases[n] 243 | case "random": 244 | // test random generated testcase 245 | if len(os.Args) < 4 { 246 | fmt.Println("arg missing") 247 | return 248 | } 249 | 250 | nNodes, _ := strconv.Atoi(os.Args[2]) 251 | nApps, _ := strconv.Atoi(os.Args[3]) 252 | 253 | fmt.Println("generating random testcase...") 254 | var nodes []Node 255 | var pods []Application 256 | var budgets []DisruptionBudget 257 | for i := 0; i < nNodes; i++ { 258 | nodes = append(nodes, Node{NodeName: fmt.Sprintf("n%d", i+1)}) 259 | } 260 | for i := 0; i < nApps; i++ { 261 | var expectNumberOfPods int 262 | if nNodes < 200 { 263 | expectNumberOfPods = rand.Intn(nNodes) // like 2/3, 3/5 264 | } else { 265 | expectNumberOfPods = rand.Intn(200) // like 60/200, 80/5000, not too many 266 | } 267 | actualNumberOfPods := 0 268 | for j := 0; j < nNodes; j++ { 269 | if rand.Intn(nNodes) < expectNumberOfPods { 270 | pods = append(pods, Application{ 271 | AppName: fmt.Sprintf("app%d", i+1), 272 | NodeName: fmt.Sprintf("n%d", j+1), 273 | }) 274 | actualNumberOfPods += 1 275 | } 276 | } 277 | if actualNumberOfPods > 0 { 278 | var disruptionAllowed int 279 | if actualNumberOfPods < 100 { 280 | disruptionAllowed = rand.Intn(actualNumberOfPods) + 1 281 | } else { 282 | disruptionAllowed = rand.Intn(100) + 1 283 | } 284 | budgets = append(budgets, DisruptionBudget{ 285 | AppName: fmt.Sprintf("app%d", i+1), DisruptionAllowed: disruptionAllowed}) 286 | } 287 | } 288 | testcase = Testcase{ 289 | Nodes: nodes, 290 | Pods: pods, 291 | Budgets: budgets, 292 | } 293 | default: 294 | fmt.Printf("unknown action: %s\n", action) 295 | return 296 | } 297 | 298 | calculator := Calculator{ 299 | memo: make(map[[16]byte][]string), 300 | } 301 | 302 | fmt.Printf("\nnodes:\n") 303 | for _, node := range testcase.Nodes { 304 | var podsOnNode []string 305 | for _, pod := range testcase.Pods { 306 | if pod.NodeName == node.NodeName { 307 | podsOnNode = append(podsOnNode, pod.AppName) 308 | } 309 | } 310 | fmt.Printf(" %s: %v\n", node.NodeName, podsOnNode) 311 | } 312 | fmt.Println("budgets:") 313 | for _, budget := range testcase.Budgets { 314 | fmt.Printf(" %s: %d\n", budget.AppName, budget.DisruptionAllowed) 315 | } 316 | fmt.Println() 317 | 318 | start := time.Now() 319 | _ = calculator.GeneratePlan(testcase.Nodes, testcase.Pods, testcase.Budgets) 320 | end := time.Now() 321 | 322 | fmt.Printf("\ntime spent: %v\n", end.Sub(start)) 323 | } 324 | -------------------------------------------------------------------------------- /lab/xss-attack/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/wbsnail/articles/archive/dive-into-kubernetes-informer/basic-controller 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/gin-gonic/gin v1.7.1 7 | github.com/go-playground/validator/v10 v10.6.0 // indirect 8 | github.com/golang/protobuf v1.5.2 // indirect 9 | github.com/json-iterator/go v1.1.11 // indirect 10 | github.com/leodido/go-urn v1.2.1 // indirect 11 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 12 | github.com/modern-go/reflect2 v1.0.1 // indirect 13 | github.com/spongeprojects/magicconch v0.0.6 14 | github.com/ugorji/go v1.2.5 // indirect 15 | golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf // indirect 16 | golang.org/x/sys v0.0.0-20210507161434-a76c4d0a0096 // indirect 17 | golang.org/x/text v0.3.6 // indirect 18 | gopkg.in/yaml.v2 v2.4.0 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /lab/xss-attack/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 5 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 6 | github.com/gin-gonic/gin v1.7.1 h1:qC89GU3p8TvKWMAVhEpmpB2CIb1hnqt2UdKZaP93mS8= 7 | github.com/gin-gonic/gin v1.7.1/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= 8 | github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= 9 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 10 | github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= 11 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= 12 | github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= 13 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= 14 | github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= 15 | github.com/go-playground/validator/v10 v10.6.0 h1:UGIt4xR++fD9QrBOoo/ascJfGe3AGHEB9s6COnss4Rk= 16 | github.com/go-playground/validator/v10 v10.6.0/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk= 17 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 18 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 19 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 20 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 21 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= 22 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 23 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 24 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 25 | github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= 26 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 27 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= 28 | github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= 29 | github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= 30 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 31 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 32 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 33 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 34 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 35 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 36 | github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= 37 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 38 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 39 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 40 | github.com/spongeprojects/magicconch v0.0.6 h1:mSfokxxWC9hglSCM5FUrhdBg7tUFGbEePGWEo+3Ol8c= 41 | github.com/spongeprojects/magicconch v0.0.6/go.mod h1:Ad8z6fYdCtlbl9gKgQ4p+W4NgIKyMyruipD0/H7S3n8= 42 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 43 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 44 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 45 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 46 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 47 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 48 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 49 | github.com/ugorji/go v1.2.5 h1:NozRHfUeEta89taVkyfsDVSy2f7v89Frft4pjnWuGuc= 50 | github.com/ugorji/go v1.2.5/go.mod h1:gat2tIT8KJG8TVI8yv77nEO/KYT6dV7JE1gfUa8Xuls= 51 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 52 | github.com/ugorji/go/codec v1.2.5 h1:8WobZKAk18Msm2CothY2jnztY56YVY8kF1oQrj21iis= 53 | github.com/ugorji/go/codec v1.2.5/go.mod h1:QPxoTbPKSEAlAHPYt02++xp/en9B/wUdwFCz+hj5caA= 54 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 55 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 56 | golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf h1:B2n+Zi5QeYRDAEodEu72OS36gmTWjgpXr2+cWcBW90o= 57 | golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= 58 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 59 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 60 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 61 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 62 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 63 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 64 | golang.org/x/sys v0.0.0-20210507161434-a76c4d0a0096 h1:5PbJGn5Sp3GEUjJ61aYbUP6RIo3Z3r2E4Tv9y2z8UHo= 65 | golang.org/x/sys v0.0.0-20210507161434-a76c4d0a0096/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 66 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 67 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 68 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 69 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 70 | golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= 71 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 72 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 73 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 74 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 75 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 76 | google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= 77 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 78 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 79 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 80 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 81 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 82 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 83 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 84 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 85 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 86 | -------------------------------------------------------------------------------- /lab/xss-attack/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/spongeprojects/magicconch" 6 | "html/template" 7 | ) 8 | 9 | var tmpl = template.Must(template.New("index").Parse(` 10 | 11 | 12 | XSS Victim 13 | 14 | 15 |

注册用户列表:

16 | 21 | 22 |
23 | 24 | {{if .LoggedIn}} 25 |

[退出] 你好, {{.Username}}

26 | {{else}} 27 |

登录:

28 |
29 | 30 | 31 |
32 | {{end}} 33 | 34 | `)) 35 | 36 | var users = make([]string, 0) 37 | var sessions = make(map[string]string) 38 | 39 | func main() { 40 | r := gin.Default() 41 | r.SetHTMLTemplate(tmpl) 42 | r.GET("/", func(c *gin.Context) { 43 | // 获取会话 ID 44 | sessionID, _ := c.Cookie("sessionID") 45 | // 根据会话 ID 查找用户 46 | username, loggedIn := sessions[sessionID] 47 | 48 | // 为了演示 XSS 攻击我们需要特地将 username 包装为 template.HTML, 49 | // 因为 html/template 包默认会帮我们对特殊字符进行转义 50 | var usersHTML []template.HTML 51 | for _, user := range users { 52 | usersHTML = append(usersHTML, template.HTML(user)) 53 | } 54 | c.HTML(200, "index", gin.H{ 55 | "LoggedIn": loggedIn, 56 | "Username": username, 57 | "Users": usersHTML, 58 | }) 59 | }) 60 | r.POST("/login", func(c *gin.Context) { 61 | username := c.PostForm("username") 62 | if !magicconch.StringInSlice(username, users) { 63 | users = append(users, username) 64 | } 65 | // 生成并保存新会话 66 | sessionID := magicconch.StringRand(32) 67 | sessions[sessionID] = username 68 | // 保存会话 ID 到客户端 69 | c.SetCookie("sessionID", sessionID, 3600, "/", "", false, false) 70 | // 登录后回到首页(不能直接把 POST 请求重定向到首页) 71 | c.Header("Content-Type", "text/html") 72 | c.String(200, "") 73 | }) 74 | r.GET("/logout", func(c *gin.Context) { 75 | // 清除会话 76 | c.SetCookie("sessionID", "", 0, "/", "", false, false) 77 | // 退出登录后回到首页 78 | c.Redirect(307, "/") 79 | }) 80 | err := r.Run("0.0.0.0:8080") 81 | magicconch.Must(err) 82 | } 83 | --------------------------------------------------------------------------------