├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── ci.yml ├── .gitignore ├── .golangci.yml ├── CODE_OF_CONDUCT.md ├── Dockerfile ├── LICENSE ├── README.md ├── README.zh-Hans.md ├── SECURITY.md ├── cmd ├── init │ └── init.sh └── install │ └── k8s │ └── deploy.sh ├── control_plane.go ├── go.mod ├── go.sum ├── hack └── boilerplate.go.txt ├── k8s ├── crd │ └── bases │ │ ├── fault-tolerance.opensergo.io_circuitbreakerstrategies.yaml │ │ ├── fault-tolerance.opensergo.io_concurrencylimitstrategies.yaml │ │ ├── fault-tolerance.opensergo.io_faulttolerancerules.yaml │ │ ├── fault-tolerance.opensergo.io_ratelimitstrategies.yaml │ │ ├── fault-tolerance.opensergo.io_throttlingstrategies.yaml │ │ └── traffic.opensergo.io_trafficerouters.yaml ├── namespace.yaml ├── rbac │ └── rbac.yaml └── workload │ └── opensergo-control-plane.yaml ├── pkg ├── api │ └── v1alpha1 │ │ ├── circuit_breaker_strategy_types.go │ │ ├── concurrency_limit_strategy_types.go │ │ ├── fault_tolerance_rule_types.go │ │ ├── groupversion_info.go │ │ ├── rate_limit_strategy_types.go │ │ ├── throttling_strategy_types.go │ │ ├── traffic │ │ ├── groupversion_info.go │ │ ├── traffic_router.go │ │ ├── traffic_router.pb.go │ │ ├── traffic_router_deepcopy.pb.go │ │ └── zz_generated.deepcopy.go │ │ └── zz_generated.deepcopy.go ├── component │ └── component.go ├── controller │ ├── crd_cache.go │ ├── crd_meta.go │ ├── crd_watcher.go │ ├── k8s_operator.go │ ├── log_adapter.go │ └── traffic_router_transform.go ├── main │ └── main.go ├── model │ ├── model.go │ └── subscribe.go ├── proto │ ├── common │ │ └── v1 │ │ │ ├── common.pb.go │ │ │ └── common.proto │ ├── fault_tolerance │ │ └── v1 │ │ │ ├── fault_tolerance.pb.go │ │ │ └── fault_tolerance.proto │ ├── router │ │ └── v1 │ │ │ ├── route_fallback.pb.go │ │ │ └── route_fallback.proto │ └── transport │ │ └── v1 │ │ ├── protocol.pb.go │ │ ├── protocol.proto │ │ └── protocol_grpc.pb.go ├── transport │ └── grpc │ │ ├── connection.go │ │ └── server.go └── util │ ├── check_util.go │ ├── format_util.go │ └── util.go └── samples ├── opensergo_new_sample.yaml └── opensergo_traffic_router_samples.yaml /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a bug report to help us improve 4 | title: "[BUG]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 16 | 17 | ## Issue Description 18 | 19 | Type: *bug report* 20 | 21 | ### Describe what happened 22 | 23 | 24 | ### Describe what you expected to happen 25 | 26 | 27 | ### How to reproduce it (as minimally and precisely as possible) 28 | 29 | 1. 30 | 2. 31 | 3. 32 | 33 | ### Tell us your environment 34 | 35 | 36 | ### Anything else we need to know? -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 16 | 17 | ## Issue Description 18 | 19 | Type: *feature request* 20 | 21 | ### Describe what feature you want 22 | 23 | ### Additional context 24 | 25 | Add any other context or screenshots about the feature request here. -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | ### Describe what this PR does / why we need it 7 | 8 | 9 | ### Does this pull request fix one issue? 10 | 11 | 12 | 13 | ### Describe how you did it 14 | 15 | 16 | ### Describe how to verify it 17 | 18 | 19 | ### Special notes for reviews -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ "*" ] 8 | 9 | jobs: 10 | 11 | lint: 12 | name: Lint check 13 | runs-on: ubuntu-latest 14 | steps: 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v3 18 | with: 19 | go-version: 1.18 20 | 21 | - name: Check out code into the Go module directory 22 | uses: actions/checkout@v3 23 | 24 | - name: Golangci Lint 25 | # https://golangci-lint.run/ 26 | uses: golangci/golangci-lint-action@v3 27 | with: 28 | version: latest 29 | args: "--out-${NO_FUTURE}format colored-line-number" 30 | 31 | build: 32 | name: Build and test - Go ${{ matrix.go_version }} 33 | runs-on: ubuntu-latest 34 | strategy: 35 | # If you want to matrix build , you can append the following list. 36 | matrix: 37 | go_version: 38 | - 1.18 39 | - 1.19 40 | os: 41 | - ubuntu-latest 42 | 43 | steps: 44 | 45 | - name: Set up Go ${{ matrix.go_version }} 46 | uses: actions/setup-go@v3 47 | with: 48 | go-version: ${{ matrix.go_version }} 49 | id: go 50 | 51 | - name: Check out code into the Go module directory 52 | uses: actions/checkout@v3 53 | 54 | - name: Test 55 | run: | 56 | go test -v -race ./... -coverprofile=coverage.txt -covermode=atomic 57 | - name: Coverage 58 | run: bash <(curl -s https://codecov.io/bash) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### JetBrains template 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 5 | 6 | .idea/ 7 | .idea_modules/ 8 | *.iml 9 | 10 | # CMake 11 | cmake-build-*/ 12 | 13 | # File-based project format 14 | *.iws 15 | 16 | # IntelliJ 17 | out/ 18 | 19 | # JIRA plugin 20 | atlassian-ide-plugin.xml 21 | 22 | # Crashlytics plugin (for Android Studio and IntelliJ) 23 | com_crashlytics_export_strings.xml 24 | crashlytics.properties 25 | crashlytics-build.properties 26 | fabric.properties 27 | 28 | ### macOS template 29 | # General 30 | .DS_Store 31 | .AppleDouble 32 | .LSOverride 33 | 34 | # Icon must end with two \r 35 | Icon 36 | 37 | # Thumbnails 38 | ._* 39 | 40 | # Files that might appear in the root of a volume 41 | .DocumentRevisions-V100 42 | .fseventsd 43 | .Spotlight-V100 44 | .TemporaryItems 45 | .Trashes 46 | .VolumeIcon.icns 47 | .com.apple.timemachine.donotpresent 48 | 49 | # Directories potentially created on remote AFP share 50 | .AppleDB 51 | .AppleDesktop 52 | Network Trash Folder 53 | Temporary Items 54 | .apdisk 55 | 56 | ### Go template 57 | # Binaries for programs and plugins 58 | *.exe 59 | *.exe~ 60 | *.dll 61 | *.so 62 | *.dylib 63 | 64 | # Test binary, built with `go test -c` 65 | *.test 66 | 67 | # Output of the go coverage tool, specifically when used with LiteIDE 68 | *.out 69 | 70 | # Dependency directories (remove the comment below to include it) 71 | # vendor/ 72 | 73 | ### VisualStudioCode template 74 | .vscode/* 75 | !.vscode/settings.json 76 | !.vscode/tasks.json 77 | !.vscode/launch.json 78 | !.vscode/extensions.json 79 | *.code-workspace 80 | 81 | # Local History for Visual Studio Code 82 | .history/ 83 | 84 | cover.out -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | # Options for analysis running. 2 | run: 3 | # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ 4 | skip-dirs-use-default: true 5 | skip-dirs: 6 | - pkg/proto 7 | - samples 8 | - pkg/api/v1alpha1 9 | skip-files: 10 | - ".*\\.pb\\.go$" 11 | # output configuration options 12 | output: 13 | format: colored-line-number 14 | # Refer to https://golangci-lint.run/usage/linters 15 | linters-settings: 16 | govet: 17 | # Disable analyzers by name. 18 | # Run `go tool vet help` to see all analyzers. 19 | disable: 20 | - stdmethods 21 | linters: 22 | disable-all: true 23 | enable: 24 | - goimports 25 | - gofmt 26 | - misspell 27 | - govet 28 | - ineffassign 29 | - staticcheck 30 | issues: 31 | exclude-use-default: true -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | opensergo@linux.alibaba.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2022, OpenSergo Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | FROM golang:1.16.6-alpine3.14 AS builder 16 | 17 | LABEL maintainer="Jiangnan Jia " \ 18 | orgnization="OpenSergo" \ 19 | officialURL="https://www.opensergo.io" \ 20 | sourceURl="https://github.com/opensergo/opensergo-control-plane" 21 | 22 | # if you are in China, you can open the follow line to set the GOPROXY. 23 | #ARG GOPROXY=https://goproxy.cn,direct 24 | 25 | # define work dir 26 | WORKDIR /opensergo/src 27 | 28 | # copy source-code 29 | COPY . /opensergo/src 30 | 31 | # build opensergo 32 | RUN cd /opensergo/src/pkg/main && \ 33 | go build -o /opensergo/dist/bin/opensergo && \ 34 | chmod +x /opensergo/dist/bin/opensergo 35 | 36 | 37 | FROM alpine:3.14 AS runtime 38 | 39 | # define work dir 40 | WORKDIR /opensergo 41 | 42 | # copy binary file 43 | COPY --from=builder /opensergo/dist . 44 | 45 | # expose default port 46 | EXPOSE 10246 47 | 48 | # start openser-control-plane 49 | ENTRYPOINT ["./bin/opensergo"] -------------------------------------------------------------------------------- /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 | # OpenSergo Control Plane 2 | 3 | OpenSergo control plane enables unified management for microservice governance rules with OpenSergo CRD (under Kubernetes). 4 | 5 | ![arch](https://user-images.githubusercontent.com/9434884/182856237-8ce85f41-1a1a-4a2a-8f58-db042bd4db42.png) 6 | 7 | # How to Start 8 | detail refers to [quick-start/opensergo-control-plane](https://opensergo.io/docs/quick-start/opensergo-control-plane/) -------------------------------------------------------------------------------- /README.zh-Hans.md: -------------------------------------------------------------------------------- 1 | # OpenSergo 控制面 2 | 3 | OpenSergo 控制平面利用 OpenSergo CRD(在 Kubernetes 中)实现微服务治理规则的统一管理。 4 | 5 | ![arch](https://user-images.githubusercontent.com/9434884/182856237-8ce85f41-1a1a-4a2a-8f58-db042bd4db42.png) 6 | 7 | # 如何开始 8 | 更多信息请参考 [quick-start/opensergo-control-plane](https://opensergo.io/docs/quick-start/opensergo-control-plane/) -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a security issue 4 | 5 | If you think the bug you found is likely to make OpenSergo components vulnerable to an attack, please do not use 6 | our public issue tracker but report it to [ASRC(Alibaba Security Response Center)](https://security.alibaba.com/). 7 | -------------------------------------------------------------------------------- /cmd/init/init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2022, OpenSergo Authors 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | 18 | # Prepare Env 19 | # OPENSERGO_CONTROL_PLANE_HOME 20 | OPENSERGO_CONTROL_PLANE_HOME=$HOME/opensergo/opensergo-control-plane 21 | mkdir -p $OPENSERGO_CONTROL_PLANE_HOME 22 | 23 | 24 | # Uninstall rbac.yaml 25 | kubectl delete -f $OPENSERGO_CONTROL_PLANE_HOME/k8s/rbac/rbac.yaml 26 | # Uninstall Namespace 27 | kubectl delete -f $OPENSERGO_CONTROL_PLANE_HOME/k8s/namespace.yaml 28 | # Uninstall CRDs 29 | kubectl delete -f $OPENSERGO_CONTROL_PLANE_HOME/k8s/crd/bases 30 | 31 | # Download CRDs ./k8s/crd/bases 32 | mkdir -p $OPENSERGO_CONTROL_PLANE_HOME/k8s/crd/bases 33 | wget --no-check-certificate -O $OPENSERGO_CONTROL_PLANE_HOME/k8s/crd/bases/fault-tolerance.opensergo.io_circuitbreakerstrategies.yaml https://raw.githubusercontent.com/opensergo/opensergo-control-plane/main/k8s/crd/bases/fault-tolerance.opensergo.io_circuitbreakerstrategies.yaml 34 | wget --no-check-certificate -O $OPENSERGO_CONTROL_PLANE_HOME/k8s/crd/bases/fault-tolerance.opensergo.io_concurrencylimitstrategies.yaml https://raw.githubusercontent.com/opensergo/opensergo-control-plane/main/k8s/crd/bases/fault-tolerance.opensergo.io_concurrencylimitstrategies.yaml 35 | wget --no-check-certificate -O $OPENSERGO_CONTROL_PLANE_HOME/k8s/crd/bases/fault-tolerance.opensergo.io_faulttolerancerules.yaml https://raw.githubusercontent.com/opensergo/opensergo-control-plane/main/k8s/crd/bases/fault-tolerance.opensergo.io_faulttolerancerules.yaml 36 | wget --no-check-certificate -O $OPENSERGO_CONTROL_PLANE_HOME/k8s/crd/bases/fault-tolerance.opensergo.io_ratelimitstrategies.yaml https://raw.githubusercontent.com/opensergo/opensergo-control-plane/main/k8s/crd/bases/fault-tolerance.opensergo.io_ratelimitstrategies.yaml 37 | wget --no-check-certificate -O $OPENSERGO_CONTROL_PLANE_HOME/k8s/crd/bases/fault-tolerance.opensergo.io_throttlingstrategies.yaml https://raw.githubusercontent.com/opensergo/opensergo-control-plane/main/k8s/crd/bases/fault-tolerance.opensergo.io_throttlingstrategies.yaml 38 | wget --no-check-certificate -O $OPENSERGO_CONTROL_PLANE_HOME/k8s/crd/bases/traffic.opensergo.io_trafficerouters.yaml https://raw.githubusercontent.com/opensergo/opensergo-control-plane/main/k8s/crd/bases/traffic.opensergo.io_trafficerouters.yaml 39 | # Install CRDs 40 | kubectl apply -f $OPENSERGO_CONTROL_PLANE_HOME/k8s/crd/bases 41 | 42 | 43 | # Download Namespace 44 | mkdir -p $OPENSERGO_CONTROL_PLANE_HOME/k8s 45 | wget --no-check-certificate -O $OPENSERGO_CONTROL_PLANE_HOME/k8s/namespace.yaml https://raw.githubusercontent.com/opensergo/opensergo-control-plane/main/k8s/namespace.yaml 46 | # Install Namespace 47 | kubectl apply -f $OPENSERGO_CONTROL_PLANE_HOME/k8s/namespace.yaml 48 | 49 | 50 | # Download rbac.yaml 51 | mkdir -p $OPENSERGO_CONTROL_PLANE_HOME/k8s/rbac 52 | wget --no-check-certificate -O $OPENSERGO_CONTROL_PLANE_HOME/k8s/rbac/rbac.yaml https://raw.githubusercontent.com/opensergo/opensergo-control-plane/main/k8s/rbac/rbac.yaml 53 | # Install rbac.yaml 54 | kubectl apply -f $OPENSERGO_CONTROL_PLANE_HOME/k8s/rbac/rbac.yaml -------------------------------------------------------------------------------- /cmd/install/k8s/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2022, OpenSergo Authors 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | 18 | # Prepare Env 19 | # OPENSERGO_CONTROL_PLANE_HOME 20 | OPENSERGO_CONTROL_PLANE_HOME=$HOME/opensergo/opensergo-control-plane 21 | mkdir -p $OPENSERGO_CONTROL_PLANE_HOME 22 | 23 | 24 | # Uninstall opensergo-control-plane.yaml 25 | kubectl delete -f $OPENSERGO_CONTROL_PLANE_HOME/k8s/workload/opensergo-control-plane.yaml 26 | 27 | # Download opensergo-control-plane.yaml 28 | mkdir -p $OPENSERGO_CONTROL_PLANE_HOME/k8s/workload 29 | wget --no-check-certificate -O $OPENSERGO_CONTROL_PLANE_HOME/k8s/workload/opensergo-control-plane.yaml https://raw.githubusercontent.com/opensergo/opensergo-control-plane/main/k8s/workload/opensergo-control-plane.yaml 30 | # Install opensergo-control-plane.yaml 31 | kubectl apply -f $OPENSERGO_CONTROL_PLANE_HOME/k8s/workload/opensergo-control-plane.yaml -------------------------------------------------------------------------------- /control_plane.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package opensergo 16 | 17 | import ( 18 | "log" 19 | "os" 20 | "sync" 21 | 22 | "github.com/opensergo/opensergo-control-plane/pkg/controller" 23 | "github.com/opensergo/opensergo-control-plane/pkg/model" 24 | trpb "github.com/opensergo/opensergo-control-plane/pkg/proto/transport/v1" 25 | transport "github.com/opensergo/opensergo-control-plane/pkg/transport/grpc" 26 | "github.com/pkg/errors" 27 | ) 28 | 29 | type ControlPlane struct { 30 | operator *controller.KubernetesOperator 31 | server *transport.Server 32 | 33 | protoDesc *trpb.ControlPlaneDesc 34 | 35 | mux sync.RWMutex 36 | } 37 | 38 | func NewControlPlane() (*ControlPlane, error) { 39 | cp := &ControlPlane{} 40 | 41 | operator, err := controller.NewKubernetesOperator(cp.sendMessage) 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | cp.server = transport.NewServer(uint32(10246), []model.SubscribeRequestHandler{cp.handleSubscribeRequest}) 47 | cp.operator = operator 48 | 49 | hostname, herr := os.Hostname() 50 | if herr != nil { 51 | // TODO: log here 52 | hostname = "unknown-host" 53 | } 54 | cp.protoDesc = &trpb.ControlPlaneDesc{Identifier: "osg-" + hostname} 55 | 56 | return cp, nil 57 | } 58 | 59 | func (c *ControlPlane) Start() error { 60 | // Run the Kubernetes operator 61 | err := c.operator.Run() 62 | if err != nil { 63 | return err 64 | } 65 | // Run the transport server 66 | err = c.server.Run() 67 | if err != nil { 68 | return err 69 | } 70 | 71 | return nil 72 | } 73 | 74 | func (c *ControlPlane) sendMessage(namespace, app, kind string, dataWithVersion *trpb.DataWithVersion, status *trpb.Status, respId string) error { 75 | connections, exists := c.server.ConnectionManager().Get(namespace, app, kind) 76 | if !exists || connections == nil { 77 | return errors.New("There is no connection for this kind") 78 | } 79 | for _, connection := range connections { 80 | if connection == nil || !connection.IsValid() { 81 | // TODO: log.Debug 82 | continue 83 | } 84 | err := c.sendMessageToStream(connection.Stream(), namespace, app, kind, dataWithVersion, status, respId) 85 | if err != nil { 86 | // TODO: should not short-break here. Handle partial failure here. 87 | return err 88 | } 89 | } 90 | return nil 91 | } 92 | 93 | func (c *ControlPlane) sendMessageToStream(stream model.OpenSergoTransportStream, namespace, app, kind string, dataWithVersion *trpb.DataWithVersion, status *trpb.Status, respId string) error { 94 | if stream == nil { 95 | return nil 96 | } 97 | return stream.SendMsg(&trpb.SubscribeResponse{ 98 | Status: status, 99 | Ack: "", 100 | Namespace: namespace, 101 | App: app, 102 | Kind: kind, 103 | DataWithVersion: dataWithVersion, 104 | ControlPlane: c.protoDesc, 105 | ResponseId: respId, 106 | }) 107 | } 108 | 109 | func (c *ControlPlane) handleSubscribeRequest(clientIdentifier model.ClientIdentifier, request *trpb.SubscribeRequest, stream model.OpenSergoTransportStream) error { 110 | // var labels []model.LabelKV 111 | // if request.Target.Labels != nil { 112 | // for _, label := range request.Target.Labels { 113 | // labels = append(labels, model.LabelKV{ 114 | // Key: label.Key, 115 | // Value: label.Value, 116 | // }) 117 | // } 118 | // } 119 | for _, kind := range request.Target.Kinds { 120 | crdWatcher, err := c.operator.RegisterWatcher(model.SubscribeTarget{ 121 | Namespace: request.Target.Namespace, 122 | AppName: request.Target.App, 123 | Kind: kind, 124 | }) 125 | if err != nil { 126 | status := &trpb.Status{ 127 | Code: transport.RegisterWatcherError, 128 | Message: "Register watcher error", 129 | Details: nil, 130 | } 131 | err = c.sendMessageToStream(stream, request.Target.Namespace, request.Target.App, kind, nil, status, request.RequestId) 132 | if err != nil { 133 | // TODO: log here 134 | log.Printf("sendMessageToStream failed, err=%s\n", err.Error()) 135 | } 136 | continue 137 | } 138 | _ = c.server.ConnectionManager().Add(request.Target.Namespace, request.Target.App, kind, transport.NewConnection(clientIdentifier, stream)) 139 | // send if the watcher cache is not empty 140 | rules, version := crdWatcher.GetRules(model.NamespacedApp{ 141 | Namespace: request.Target.Namespace, 142 | App: request.Target.App, 143 | }) 144 | if len(rules) > 0 { 145 | status := &trpb.Status{ 146 | Code: transport.Success, 147 | Message: "Get and send rule success", 148 | Details: nil, 149 | } 150 | dataWithVersion := &trpb.DataWithVersion{ 151 | Data: rules, 152 | Version: version, 153 | } 154 | err = c.sendMessageToStream(stream, request.Target.Namespace, request.Target.App, kind, dataWithVersion, status, request.RequestId) 155 | if err != nil { 156 | // TODO: log here 157 | log.Printf("sendMessageToStream failed, err=%s\n", err.Error()) 158 | } 159 | } 160 | } 161 | return nil 162 | } 163 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/opensergo/opensergo-control-plane 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/alibaba/sentinel-golang v1.0.3 7 | github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc 8 | github.com/envoyproxy/go-control-plane v0.10.3-0.20221109183938-2935a23e638f 9 | github.com/envoyproxy/protoc-gen-validate v0.6.7 10 | github.com/go-logr/logr v0.4.0 11 | github.com/golang/protobuf v1.5.2 12 | github.com/json-iterator/go v1.1.12 // indirect 13 | github.com/kr/pretty v0.3.0 // indirect 14 | github.com/pkg/errors v0.9.1 15 | github.com/rogpeppe/go-internal v1.8.0 // indirect 16 | go.uber.org/atomic v1.7.0 17 | google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7 18 | google.golang.org/grpc v1.51.0 19 | google.golang.org/protobuf v1.28.1 20 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect 21 | k8s.io/apimachinery v0.21.4 22 | k8s.io/client-go v0.21.4 23 | sigs.k8s.io/controller-runtime v0.9.7 24 | ) 25 | -------------------------------------------------------------------------------- /hack/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ -------------------------------------------------------------------------------- /k8s/crd/bases/fault-tolerance.opensergo.io_circuitbreakerstrategies.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: (devel) 7 | creationTimestamp: null 8 | name: circuitbreakerstrategies.fault-tolerance.opensergo.io 9 | spec: 10 | group: fault-tolerance.opensergo.io 11 | names: 12 | kind: CircuitBreakerStrategy 13 | listKind: CircuitBreakerStrategyList 14 | plural: circuitbreakerstrategies 15 | singular: circuitbreakerstrategy 16 | scope: Namespaced 17 | versions: 18 | - name: v1alpha1 19 | schema: 20 | openAPIV3Schema: 21 | properties: 22 | apiVersion: 23 | description: 'APIVersion defines the versioned schema of this representation 24 | of an object. Servers should convert recognized schemas to the latest 25 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 26 | type: string 27 | kind: 28 | description: 'Kind is a string value representing the REST resource this 29 | object represents. Servers may infer this from the endpoint the client 30 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 31 | type: string 32 | metadata: 33 | type: object 34 | spec: 35 | description: CircuitBreakerStrategySpec defines the spec of CircuitBreakerStrategy. 36 | properties: 37 | errorConditions: 38 | properties: 39 | representation: 40 | type: string 41 | required: 42 | - representation 43 | type: object 44 | minRequestAmount: 45 | format: int32 46 | minimum: 1 47 | type: integer 48 | recoveryTimeout: 49 | pattern: ^[1-9]\d*(s|ms|m|min|minute|h|d)$ 50 | type: string 51 | slowConditions: 52 | properties: 53 | maxAllowedRt: 54 | pattern: ^[1-9]\d*(s|ms|m|min|minute|h|d)$ 55 | type: string 56 | required: 57 | - maxAllowedRt 58 | type: object 59 | statDuration: 60 | pattern: ^[1-9]\d*(s|ms|m|min|minute|h|d)$ 61 | type: string 62 | strategy: 63 | enum: 64 | - SlowRequestRatio 65 | - ErrorRequestRatio 66 | type: string 67 | triggerRatio: 68 | pattern: ^([1-9]\d?|100|0)%$ 69 | type: string 70 | required: 71 | - minRequestAmount 72 | - recoveryTimeout 73 | - statDuration 74 | - strategy 75 | - triggerRatio 76 | type: object 77 | status: 78 | description: CircuitBreakerStrategyStatus defines the observed state of 79 | CircuitBreakerStrategy. 80 | type: object 81 | type: object 82 | served: true 83 | storage: true 84 | -------------------------------------------------------------------------------- /k8s/crd/bases/fault-tolerance.opensergo.io_concurrencylimitstrategies.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: (devel) 7 | creationTimestamp: null 8 | name: concurrencylimitstrategies.fault-tolerance.opensergo.io 9 | spec: 10 | group: fault-tolerance.opensergo.io 11 | names: 12 | kind: ConcurrencyLimitStrategy 13 | listKind: ConcurrencyLimitStrategyList 14 | plural: concurrencylimitstrategies 15 | singular: concurrencylimitstrategy 16 | scope: Namespaced 17 | versions: 18 | - name: v1alpha1 19 | schema: 20 | openAPIV3Schema: 21 | properties: 22 | apiVersion: 23 | description: 'APIVersion defines the versioned schema of this representation 24 | of an object. Servers should convert recognized schemas to the latest 25 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 26 | type: string 27 | kind: 28 | description: 'Kind is a string value representing the REST resource this 29 | object represents. Servers may infer this from the endpoint the client 30 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 31 | type: string 32 | metadata: 33 | type: object 34 | spec: 35 | description: ConcurrencyLimitStrategySpec defines the spec of ConcurrencyLimitStrategy. 36 | properties: 37 | limitMode: 38 | enum: 39 | - Local 40 | - Global 41 | type: string 42 | maxConcurrency: 43 | format: int64 44 | minimum: 0 45 | type: integer 46 | required: 47 | - limitMode 48 | - maxConcurrency 49 | type: object 50 | status: 51 | description: ConcurrencyLimitStrategyStatus defines the observed state 52 | of ConcurrencyLimitStrategy. 53 | type: object 54 | type: object 55 | served: true 56 | storage: true 57 | -------------------------------------------------------------------------------- /k8s/crd/bases/fault-tolerance.opensergo.io_faulttolerancerules.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: (devel) 7 | creationTimestamp: null 8 | name: faulttolerancerules.fault-tolerance.opensergo.io 9 | spec: 10 | group: fault-tolerance.opensergo.io 11 | names: 12 | kind: FaultToleranceRule 13 | listKind: FaultToleranceRuleList 14 | plural: faulttolerancerules 15 | singular: faulttolerancerule 16 | scope: Namespaced 17 | versions: 18 | - name: v1alpha1 19 | schema: 20 | openAPIV3Schema: 21 | properties: 22 | apiVersion: 23 | description: 'APIVersion defines the versioned schema of this representation 24 | of an object. Servers should convert recognized schemas to the latest 25 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 26 | type: string 27 | kind: 28 | description: 'Kind is a string value representing the REST resource this 29 | object represents. Servers may infer this from the endpoint the client 30 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 31 | type: string 32 | metadata: 33 | type: object 34 | spec: 35 | description: FaultToleranceRuleSpec defines the spec of FaultToleranceRule. 36 | properties: 37 | strategies: 38 | items: 39 | properties: 40 | kind: 41 | enum: 42 | - RateLimitStrategy 43 | - ConcurrencyLimitStrategy 44 | minLength: 1 45 | type: string 46 | name: 47 | minLength: 1 48 | type: string 49 | required: 50 | - kind 51 | - name 52 | type: object 53 | type: array 54 | targets: 55 | items: 56 | properties: 57 | targetResourceName: 58 | maxLength: 1024 59 | minLength: 1 60 | type: string 61 | required: 62 | - targetResourceName 63 | type: object 64 | type: array 65 | required: 66 | - strategies 67 | - targets 68 | type: object 69 | status: 70 | description: FaultToleranceRuleStatus defines the observed state of FaultToleranceRule. 71 | type: object 72 | type: object 73 | served: true 74 | storage: true 75 | -------------------------------------------------------------------------------- /k8s/crd/bases/fault-tolerance.opensergo.io_ratelimitstrategies.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: (devel) 7 | creationTimestamp: null 8 | name: ratelimitstrategies.fault-tolerance.opensergo.io 9 | spec: 10 | group: fault-tolerance.opensergo.io 11 | names: 12 | kind: RateLimitStrategy 13 | listKind: RateLimitStrategyList 14 | plural: ratelimitstrategies 15 | singular: ratelimitstrategy 16 | scope: Namespaced 17 | versions: 18 | - name: v1alpha1 19 | schema: 20 | openAPIV3Schema: 21 | properties: 22 | apiVersion: 23 | description: 'APIVersion defines the versioned schema of this representation 24 | of an object. Servers should convert recognized schemas to the latest 25 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 26 | type: string 27 | kind: 28 | description: 'Kind is a string value representing the REST resource this 29 | object represents. Servers may infer this from the endpoint the client 30 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 31 | type: string 32 | metadata: 33 | type: object 34 | spec: 35 | description: RateLimitStrategySpec defines the spec of RateLimitStrategy. 36 | properties: 37 | limitMode: 38 | enum: 39 | - Local 40 | - Global 41 | type: string 42 | metricType: 43 | enum: 44 | - RequestAmount 45 | type: string 46 | statDurationSeconds: 47 | format: int32 48 | minimum: 1 49 | type: integer 50 | threshold: 51 | format: int64 52 | minimum: 0 53 | type: integer 54 | required: 55 | - limitMode 56 | - metricType 57 | - statDurationSeconds 58 | - threshold 59 | type: object 60 | status: 61 | description: RateLimitStrategyStatus defines the observed state of RateLimitStrategy. 62 | type: object 63 | type: object 64 | served: true 65 | storage: true 66 | -------------------------------------------------------------------------------- /k8s/crd/bases/fault-tolerance.opensergo.io_throttlingstrategies.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: (devel) 7 | creationTimestamp: null 8 | name: throttlingstrategies.fault-tolerance.opensergo.io 9 | spec: 10 | group: fault-tolerance.opensergo.io 11 | names: 12 | kind: ThrottlingStrategy 13 | listKind: ThrottlingStrategyList 14 | plural: throttlingstrategies 15 | singular: throttlingstrategy 16 | scope: Namespaced 17 | versions: 18 | - name: v1alpha1 19 | schema: 20 | openAPIV3Schema: 21 | properties: 22 | apiVersion: 23 | description: 'APIVersion defines the versioned schema of this representation 24 | of an object. Servers should convert recognized schemas to the latest 25 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 26 | type: string 27 | kind: 28 | description: 'Kind is a string value representing the REST resource this 29 | object represents. Servers may infer this from the endpoint the client 30 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 31 | type: string 32 | metadata: 33 | type: object 34 | spec: 35 | description: ThrottlingStrategySpec defines the spec of ThrottlingStrategy. 36 | properties: 37 | minIntervalOfRequests: 38 | pattern: ^\d+(s|ms|m|min|minute|h|d)$ 39 | type: string 40 | queueTimeout: 41 | pattern: ^[1-9]\d*(s|ms|m|min|minute|h|d)$ 42 | type: string 43 | required: 44 | - minIntervalOfRequests 45 | - queueTimeout 46 | type: object 47 | status: 48 | description: ThrottlingStrategyStatus defines the observed state of ThrottlingStrategy. 49 | type: object 50 | type: object 51 | served: true 52 | storage: true 53 | -------------------------------------------------------------------------------- /k8s/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: opensergo-system 5 | labels: 6 | name: opensergo-system -------------------------------------------------------------------------------- /k8s/rbac/rbac.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: opensergo-control-plane 6 | namespace: opensergo-system 7 | 8 | --- 9 | apiVersion: rbac.authorization.k8s.io/v1 10 | kind: ClusterRole 11 | metadata: 12 | name: opensergo-control-plane-role 13 | namespace: opensergo-system 14 | rules: 15 | - apiGroups: 16 | - fault-tolerance.opensergo.io 17 | - traffic.opensergo.io 18 | resources: 19 | - "*" 20 | verbs: 21 | - create 22 | - delete 23 | - get 24 | - list 25 | - patch 26 | - update 27 | - watch 28 | 29 | --- 30 | apiVersion: rbac.authorization.k8s.io/v1 31 | kind: ClusterRoleBinding 32 | metadata: 33 | name: opensergo-control-plane-rolebinding 34 | roleRef: 35 | apiGroup: rbac.authorization.k8s.io 36 | kind: ClusterRole 37 | name: opensergo-control-plane-role 38 | subjects: 39 | - kind: ServiceAccount 40 | name: opensergo-control-plane 41 | namespace: opensergo-system -------------------------------------------------------------------------------- /k8s/workload/opensergo-control-plane.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: opensergo-control-plane 5 | namespace: opensergo-system 6 | labels: 7 | app: opensergo-control-plane 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: opensergo-control-plane 13 | template: 14 | metadata: 15 | labels: 16 | app: opensergo-control-plane 17 | spec: 18 | containers: 19 | - name: opensergo-control-plane 20 | image: opensergo-registry.cn-hangzhou.cr.aliyuncs.com/opensergo/opensergo-control-plane:0.1.0 21 | ports: 22 | - name: grpc 23 | containerPort: 10246 24 | serviceAccountName: opensergo-control-plane 25 | 26 | --- 27 | apiVersion: v1 28 | kind: Service 29 | metadata: 30 | name: opensergo-control-plane 31 | namespace: opensergo-system 32 | spec: 33 | type: ClusterIP 34 | ports: 35 | - port: 10246 36 | protocol: TCP 37 | targetPort: grpc 38 | selector: 39 | app: opensergo-control-plane -------------------------------------------------------------------------------- /pkg/api/v1alpha1/circuit_breaker_strategy_types.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package v1alpha1 16 | 17 | import ( 18 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 19 | ) 20 | 21 | // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! 22 | // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. 23 | 24 | // +kubebuilder:object:root=true 25 | 26 | type CircuitBreakerStrategy struct { 27 | metav1.TypeMeta `json:",inline"` 28 | metav1.ObjectMeta `json:"metadata,omitempty"` 29 | 30 | Spec CircuitBreakerStrategySpec `json:"spec,omitempty"` 31 | 32 | Status CircuitBreakerStrategyStatus `json:"status,omitempty"` 33 | } 34 | 35 | // CircuitBreakerStrategySpec defines the spec of CircuitBreakerStrategy. 36 | type CircuitBreakerStrategySpec struct { 37 | // +kubebuilder:validation:Type=string 38 | // +kubebuilder:validation:Enum=SlowRequestRatio;ErrorRequestRatio 39 | // +kubebuilder:validation:Required 40 | Strategy string `json:"strategy"` 41 | 42 | // +kubebuilder:validation:Type=string 43 | // +kubebuilder:validation:Required 44 | // +kubebuilder:validation:Pattern=^([1-9]\d?|100|0)%$ 45 | TriggerRatio string `json:"triggerRatio"` 46 | 47 | // +kubebuilder:validation:Type=string 48 | // +kubebuilder:validation:Required 49 | // +kubebuilder:validation:Pattern=^[1-9]\d*(s|ms|m|min|minute|h|d)$ 50 | StatDuration string `json:"statDuration"` 51 | 52 | // +kubebuilder:validation:Type=string 53 | // +kubebuilder:validation:Required 54 | // +kubebuilder:validation:Pattern=^[1-9]\d*(s|ms|m|min|minute|h|d)$ 55 | RecoveryTimeout string `json:"recoveryTimeout"` 56 | 57 | // +kubebuilder:validation:Type=integer 58 | // +kubebuilder:validation:Format=int32 59 | // +kubebuilder:validation:Minimum=1 60 | // +kubebuilder:validation:Required 61 | MinRequestAmount int32 `json:"minRequestAmount"` 62 | 63 | SlowConditions SlowConditions `json:"slowConditions,omitempty"` 64 | 65 | ErrorConditions ErrorConditions `json:"errorConditions,omitempty"` 66 | } 67 | 68 | type SlowConditions struct { 69 | // +kubebuilder:validation:Type=string 70 | // +kubebuilder:validation:Required 71 | // +kubebuilder:validation:Pattern=^[1-9]\d*(s|ms|m|min|minute|h|d)$ 72 | MaxAllowedRt string `json:"maxAllowedRt"` 73 | } 74 | 75 | type ErrorConditions struct { 76 | } 77 | 78 | // CircuitBreakerStrategyStatus defines the observed state of CircuitBreakerStrategy. 79 | type CircuitBreakerStrategyStatus struct { 80 | // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster 81 | // Important: Run "make" to regenerate code after modifying this file 82 | } 83 | 84 | // +kubebuilder:object:root=true 85 | 86 | // CircuitBreakerStrategyList contains a list of CircuitBreakerStrategy. 87 | type CircuitBreakerStrategyList struct { 88 | metav1.TypeMeta `json:",inline"` 89 | metav1.ListMeta `json:"metadata,omitempty"` 90 | Items []CircuitBreakerStrategy `json:"items"` 91 | } 92 | 93 | // +kubebuilder:rbac:groups=fault-tolerance.opensergo.io,resources=CircuitBreakerStrategy,verbs=get;list;watch;create;update;patch;delete 94 | // +kubebuilder:rbac:groups=fault-tolerance.opensergo.io,resources=CircuitBreakerStrategy/status,verbs=get;update;patch 95 | 96 | func init() { 97 | SchemeBuilder.Register(&CircuitBreakerStrategy{}, &CircuitBreakerStrategyList{}) 98 | } 99 | -------------------------------------------------------------------------------- /pkg/api/v1alpha1/concurrency_limit_strategy_types.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package v1alpha1 16 | 17 | import ( 18 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 19 | ) 20 | 21 | // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! 22 | // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. 23 | 24 | // +kubebuilder:object:root=true 25 | 26 | type ConcurrencyLimitStrategy struct { 27 | metav1.TypeMeta `json:",inline"` 28 | metav1.ObjectMeta `json:"metadata,omitempty"` 29 | 30 | Spec ConcurrencyLimitStrategySpec `json:"spec,omitempty"` 31 | 32 | Status ConcurrencyLimitStrategyStatus `json:"status,omitempty"` 33 | } 34 | 35 | // ConcurrencyLimitStrategySpec defines the spec of ConcurrencyLimitStrategy. 36 | type ConcurrencyLimitStrategySpec struct { 37 | 38 | // +kubebuilder:validation:Type=integer 39 | // +kubebuilder:validation:Format=int64 40 | // +kubebuilder:validation:Minimum=0 41 | // +kubebuilder:validation:Required 42 | MaxConcurrencyThreshold int64 `json:"maxConcurrency"` 43 | 44 | // +kubebuilder:validation:Type=string 45 | // +kubebuilder:validation:Enum=Local;Global 46 | // +kubebuilder:validation:Required 47 | LimitMode string `json:"limitMode"` 48 | } 49 | 50 | // ConcurrencyLimitStrategyStatus defines the observed state of ConcurrencyLimitStrategy. 51 | type ConcurrencyLimitStrategyStatus struct { 52 | // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster 53 | // Important: Run "make" to regenerate code after modifying this file 54 | } 55 | 56 | // +kubebuilder:object:root=true 57 | 58 | // ConcurrencyLimitStrategyList contains a list of ConcurrencyLimitStrategy. 59 | type ConcurrencyLimitStrategyList struct { 60 | metav1.TypeMeta `json:",inline"` 61 | metav1.ListMeta `json:"metadata,omitempty"` 62 | Items []ConcurrencyLimitStrategy `json:"items"` 63 | } 64 | 65 | // +kubebuilder:rbac:groups=fault-tolerance.opensergo.io,resources=ConcurrencyLimitStrategy,verbs=get;list;watch;create;update;patch;delete 66 | // +kubebuilder:rbac:groups=fault-tolerance.opensergo.io,resources=ConcurrencyLimitStrategy/status,verbs=get;update;patch 67 | 68 | func init() { 69 | SchemeBuilder.Register(&ConcurrencyLimitStrategy{}, &ConcurrencyLimitStrategyList{}) 70 | } 71 | -------------------------------------------------------------------------------- /pkg/api/v1alpha1/fault_tolerance_rule_types.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package v1alpha1 16 | 17 | import ( 18 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 19 | ) 20 | 21 | // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! 22 | // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. 23 | 24 | // +kubebuilder:object:root=true 25 | 26 | type FaultToleranceRule struct { 27 | metav1.TypeMeta `json:",inline"` 28 | metav1.ObjectMeta `json:"metadata,omitempty"` 29 | 30 | Spec FaultToleranceRuleSpec `json:"spec,omitempty"` 31 | 32 | Status FaultToleranceRuleStatus `json:"status,omitempty"` 33 | } 34 | 35 | // FaultToleranceRuleSpec defines the spec of FaultToleranceRule. 36 | type FaultToleranceRuleSpec struct { 37 | // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster 38 | // Important: Run "make" to regenerate code after modifying this file 39 | 40 | // +kubebuilder:validation:Type=array 41 | // +kubebuilder:validation:Required 42 | Targets []FaultToleranceTargetRef `json:"targets"` 43 | 44 | // +kubebuilder:validation:Type=array 45 | // +kubebuilder:validation:Required 46 | Strategies []FaultToleranceStrategyRef `json:"strategies"` 47 | } 48 | 49 | type FaultToleranceTargetRef struct { 50 | // +kubebuilder:validation:Type=string 51 | // +kubebuilder:validation:MinLength=1 52 | // +kubebuilder:validation:MaxLength=1024 53 | // +kubebuilder:validation:Required 54 | TargetResourceName string `json:"targetResourceName"` 55 | } 56 | 57 | const ( 58 | RateLimitStrategyKind string = "RateLimitStrategy" 59 | ConcurrencyLimitStrategyKind string = "ConcurrencyLimitStrategy" 60 | ) 61 | 62 | type FaultToleranceStrategyRef struct { 63 | // +kubebuilder:validation:Type=string 64 | // +kubebuilder:validation:MinLength=1 65 | // +kubebuilder:validation:Required 66 | Name string `json:"name"` 67 | 68 | // +kubebuilder:validation:Type=string 69 | // +kubebuilder:validation:MinLength=1 70 | // +kubebuilder:validation:Required 71 | // +kubebuilder:validation:Enum=RateLimitStrategy;ConcurrencyLimitStrategy 72 | Kind string `json:"kind"` 73 | } 74 | 75 | // FaultToleranceRuleStatus defines the observed state of FaultToleranceRule. 76 | type FaultToleranceRuleStatus struct { 77 | // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster 78 | // Important: Run "make" to regenerate code after modifying this file 79 | } 80 | 81 | // +kubebuilder:object:root=true 82 | 83 | // FaultToleranceRuleList contains a list of FaultToleranceRule. 84 | type FaultToleranceRuleList struct { 85 | metav1.TypeMeta `json:",inline"` 86 | metav1.ListMeta `json:"metadata,omitempty"` 87 | Items []FaultToleranceRule `json:"items"` 88 | } 89 | 90 | // +kubebuilder:rbac:groups=fault-tolerance.opensergo.io,resources=FaultToleranceRule,verbs=get;list;watch;create;update;patch;delete 91 | // +kubebuilder:rbac:groups=fault-tolerance.opensergo.io,resources=FaultToleranceRule/status,verbs=get;update;patch 92 | 93 | func init() { 94 | SchemeBuilder.Register(&FaultToleranceRule{}, &FaultToleranceRuleList{}) 95 | } 96 | -------------------------------------------------------------------------------- /pkg/api/v1alpha1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package v1alpha1 contains API Schema definitions for the OpenSergo fault-tolerance v1alpha1 CRD. 16 | // +kubebuilder:object:generate=true 17 | // +groupName=fault-tolerance.opensergo.io 18 | package v1alpha1 19 | 20 | import ( 21 | "k8s.io/apimachinery/pkg/runtime/schema" 22 | "sigs.k8s.io/controller-runtime/pkg/scheme" 23 | ) 24 | 25 | var ( 26 | // GroupVersion is group version used to register these objects 27 | GroupVersion = schema.GroupVersion{Group: "fault-tolerance.opensergo.io", Version: "v1alpha1"} 28 | 29 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 30 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 31 | 32 | // AddToScheme adds the types in this group-version to the given scheme. 33 | AddToScheme = SchemeBuilder.AddToScheme 34 | ) 35 | -------------------------------------------------------------------------------- /pkg/api/v1alpha1/rate_limit_strategy_types.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package v1alpha1 16 | 17 | import ( 18 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 19 | ) 20 | 21 | // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! 22 | // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. 23 | 24 | // +kubebuilder:object:root=true 25 | 26 | type RateLimitStrategy struct { 27 | metav1.TypeMeta `json:",inline"` 28 | metav1.ObjectMeta `json:"metadata,omitempty"` 29 | 30 | Spec RateLimitStrategySpec `json:"spec,omitempty"` 31 | 32 | Status RateLimitStrategyStatus `json:"status,omitempty"` 33 | } 34 | 35 | const ( 36 | RequestAmountMetricType string = "RequestAmount" 37 | 38 | LocalLimitMode string = "Local" 39 | GlobalLimitMode string = "Global" 40 | ) 41 | 42 | // RateLimitStrategySpec defines the spec of RateLimitStrategy. 43 | type RateLimitStrategySpec struct { 44 | // +kubebuilder:validation:Type=string 45 | // +kubebuilder:validation:Enum=RequestAmount 46 | // +kubebuilder:validation:Required 47 | MetricType string `json:"metricType"` 48 | 49 | // +kubebuilder:validation:Type=string 50 | // +kubebuilder:validation:Enum=Local;Global 51 | // +kubebuilder:validation:Required 52 | LimitMode string `json:"limitMode"` 53 | 54 | // +kubebuilder:validation:Type=integer 55 | // +kubebuilder:validation:Format=int64 56 | // +kubebuilder:validation:Minimum=0 57 | // +kubebuilder:validation:Required 58 | Threshold int64 `json:"threshold"` 59 | 60 | // +kubebuilder:validation:Type=integer 61 | // +kubebuilder:validation:Format=int32 62 | // +kubebuilder:validation:Minimum=1 63 | // +kubebuilder:validation:Required 64 | StatDurationSeconds int32 `json:"statDurationSeconds"` 65 | } 66 | 67 | // RateLimitStrategyStatus defines the observed state of RateLimitStrategy. 68 | type RateLimitStrategyStatus struct { 69 | // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster 70 | // Important: Run "make" to regenerate code after modifying this file 71 | } 72 | 73 | // +kubebuilder:object:root=true 74 | 75 | // RateLimitStrategyList contains a list of RateLimitStrategy. 76 | type RateLimitStrategyList struct { 77 | metav1.TypeMeta `json:",inline"` 78 | metav1.ListMeta `json:"metadata,omitempty"` 79 | Items []RateLimitStrategy `json:"items"` 80 | } 81 | 82 | // +kubebuilder:rbac:groups=fault-tolerance.opensergo.io,resources=RateLimitStrategy,verbs=get;list;watch;create;update;patch;delete 83 | // +kubebuilder:rbac:groups=fault-tolerance.opensergo.io,resources=RateLimitStrategy/status,verbs=get;update;patch 84 | 85 | func init() { 86 | SchemeBuilder.Register(&RateLimitStrategy{}, &RateLimitStrategyList{}) 87 | } 88 | -------------------------------------------------------------------------------- /pkg/api/v1alpha1/throttling_strategy_types.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package v1alpha1 16 | 17 | import ( 18 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 19 | ) 20 | 21 | // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! 22 | // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. 23 | 24 | // +kubebuilder:object:root=true 25 | 26 | type ThrottlingStrategy struct { 27 | metav1.TypeMeta `json:",inline"` 28 | metav1.ObjectMeta `json:"metadata,omitempty"` 29 | 30 | Spec ThrottlingStrategySpec `json:"spec,omitempty"` 31 | 32 | Status ThrottlingStrategyStatus `json:"status,omitempty"` 33 | } 34 | 35 | // ThrottlingStrategySpec defines the spec of ThrottlingStrategy. 36 | type ThrottlingStrategySpec struct { 37 | // +kubebuilder:validation:Type=string 38 | // +kubebuilder:validation:Required 39 | // +kubebuilder:validation:Pattern=^\d+(s|ms|m|min|minute|h|d)$ 40 | MinIntervalOfRequests string `json:"minIntervalOfRequests"` 41 | 42 | // +kubebuilder:validation:Type=string 43 | // +kubebuilder:validation:Required 44 | // +kubebuilder:validation:Pattern=^[1-9]\d*(s|ms|m|min|minute|h|d)$ 45 | QueueTimeout string `json:"queueTimeout"` 46 | } 47 | 48 | // ThrottlingStrategyStatus defines the observed state of ThrottlingStrategy. 49 | type ThrottlingStrategyStatus struct { 50 | // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster 51 | // Important: Run "make" to regenerate code after modifying this file 52 | } 53 | 54 | // +kubebuilder:object:root=true 55 | 56 | // ThrottlingStrategyList contains a list of ThrottlingStrategy. 57 | type ThrottlingStrategyList struct { 58 | metav1.TypeMeta `json:",inline"` 59 | metav1.ListMeta `json:"metadata,omitempty"` 60 | Items []ThrottlingStrategy `json:"items"` 61 | } 62 | 63 | // +kubebuilder:rbac:groups=fault-tolerance.opensergo.io,resources=ThrottlingStrategy,verbs=get;list;watch;create;update;patch;delete 64 | // +kubebuilder:rbac:groups=fault-tolerance.opensergo.io,resources=ThrottlingStrategy/status,verbs=get;update;patch 65 | 66 | func init() { 67 | SchemeBuilder.Register(&ThrottlingStrategy{}, &ThrottlingStrategyList{}) 68 | } 69 | -------------------------------------------------------------------------------- /pkg/api/v1alpha1/traffic/groupversion_info.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package v1alpha1 contains API Schema definitions for the OpenSergo traffic-router v1alpha1 CRD. 16 | // +kubebuilder:object:generate=true 17 | // +groupName=traffic.opensergo.io 18 | // +versionName=v1alpha1 19 | package traffic 20 | 21 | import ( 22 | "k8s.io/apimachinery/pkg/runtime/schema" 23 | "sigs.k8s.io/controller-runtime/pkg/scheme" 24 | ) 25 | 26 | var ( 27 | // GroupVersion is group version used to register these objects 28 | GroupVersion = schema.GroupVersion{Group: "traffic.opensergo.io", Version: "v1alpha1"} 29 | 30 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 31 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 32 | 33 | // AddToScheme adds the types in this group-version to the given scheme. 34 | AddToScheme = SchemeBuilder.AddToScheme 35 | ) 36 | -------------------------------------------------------------------------------- /pkg/api/v1alpha1/traffic/traffic_router.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package traffic 16 | 17 | import ( 18 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 19 | ) 20 | 21 | // +kubebuilder:object:root=true 22 | type TrafficRouter struct { 23 | metav1.TypeMeta `json:",inline"` 24 | metav1.ObjectMeta `json:"metadata,omitempty"` 25 | 26 | Spec TrafficRouterSpec `json:"spec,omitempty"` 27 | 28 | Status TrafficRouterStatus `json:"status,omitempty"` 29 | } 30 | 31 | // HttpRequestMatchRuleList contains a list of HttpRequestMatchRule. 32 | // +kubebuilder:object:root=true 33 | type TrafficRouterList struct { 34 | metav1.TypeMeta `json:",inline"` 35 | metav1.ListMeta `json:"metadata,omitempty"` 36 | Items []TrafficRouter `json:"items"` 37 | } 38 | 39 | // HttpRequestMatchRuleStatus defines the observed state of HttpRequestMatchRule. 40 | type TrafficRouterStatus struct { 41 | // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster 42 | // Important: Run "make" to regenerate code after modifying this file 43 | } 44 | 45 | func init() { 46 | SchemeBuilder.Register(&TrafficRouter{}, &TrafficRouterList{}) 47 | } 48 | -------------------------------------------------------------------------------- /pkg/api/v1alpha1/traffic/traffic_router_deepcopy.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-deepcopy. DO NOT EDIT. 2 | 3 | package traffic 4 | 5 | import ( 6 | proto "google.golang.org/protobuf/proto" 7 | ) 8 | 9 | // DeepCopyInto supports using TrafficRouterSpec within kubernetes types, where deepcopy-gen is used. 10 | func (in *TrafficRouterSpec) DeepCopyInto(out *TrafficRouterSpec) { 11 | p := proto.Clone(in).(*TrafficRouterSpec) 12 | *out = *p 13 | } 14 | 15 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficRouterSpec. Required by controller-gen. 16 | func (in *TrafficRouterSpec) DeepCopy() *TrafficRouterSpec { 17 | if in == nil { 18 | return nil 19 | } 20 | out := new(TrafficRouterSpec) 21 | in.DeepCopyInto(out) 22 | return out 23 | } 24 | 25 | // DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new TrafficRouterSpec. Required by controller-gen. 26 | func (in *TrafficRouterSpec) DeepCopyInterface() interface{} { 27 | return in.DeepCopy() 28 | } 29 | 30 | // DeepCopyInto supports using Destination within kubernetes types, where deepcopy-gen is used. 31 | func (in *Destination) DeepCopyInto(out *Destination) { 32 | p := proto.Clone(in).(*Destination) 33 | *out = *p 34 | } 35 | 36 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Destination. Required by controller-gen. 37 | func (in *Destination) DeepCopy() *Destination { 38 | if in == nil { 39 | return nil 40 | } 41 | out := new(Destination) 42 | in.DeepCopyInto(out) 43 | return out 44 | } 45 | 46 | // DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new Destination. Required by controller-gen. 47 | func (in *Destination) DeepCopyInterface() interface{} { 48 | return in.DeepCopy() 49 | } 50 | 51 | // DeepCopyInto supports using HTTPRoute within kubernetes types, where deepcopy-gen is used. 52 | func (in *HTTPRoute) DeepCopyInto(out *HTTPRoute) { 53 | p := proto.Clone(in).(*HTTPRoute) 54 | *out = *p 55 | } 56 | 57 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRoute. Required by controller-gen. 58 | func (in *HTTPRoute) DeepCopy() *HTTPRoute { 59 | if in == nil { 60 | return nil 61 | } 62 | out := new(HTTPRoute) 63 | in.DeepCopyInto(out) 64 | return out 65 | } 66 | 67 | // DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRoute. Required by controller-gen. 68 | func (in *HTTPRoute) DeepCopyInterface() interface{} { 69 | return in.DeepCopy() 70 | } 71 | 72 | // DeepCopyInto supports using Delegate within kubernetes types, where deepcopy-gen is used. 73 | func (in *Delegate) DeepCopyInto(out *Delegate) { 74 | p := proto.Clone(in).(*Delegate) 75 | *out = *p 76 | } 77 | 78 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Delegate. Required by controller-gen. 79 | func (in *Delegate) DeepCopy() *Delegate { 80 | if in == nil { 81 | return nil 82 | } 83 | out := new(Delegate) 84 | in.DeepCopyInto(out) 85 | return out 86 | } 87 | 88 | // DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new Delegate. Required by controller-gen. 89 | func (in *Delegate) DeepCopyInterface() interface{} { 90 | return in.DeepCopy() 91 | } 92 | 93 | // DeepCopyInto supports using Headers within kubernetes types, where deepcopy-gen is used. 94 | func (in *Headers) DeepCopyInto(out *Headers) { 95 | p := proto.Clone(in).(*Headers) 96 | *out = *p 97 | } 98 | 99 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Headers. Required by controller-gen. 100 | func (in *Headers) DeepCopy() *Headers { 101 | if in == nil { 102 | return nil 103 | } 104 | out := new(Headers) 105 | in.DeepCopyInto(out) 106 | return out 107 | } 108 | 109 | // DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new Headers. Required by controller-gen. 110 | func (in *Headers) DeepCopyInterface() interface{} { 111 | return in.DeepCopy() 112 | } 113 | 114 | // DeepCopyInto supports using TLSRoute within kubernetes types, where deepcopy-gen is used. 115 | func (in *TLSRoute) DeepCopyInto(out *TLSRoute) { 116 | p := proto.Clone(in).(*TLSRoute) 117 | *out = *p 118 | } 119 | 120 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSRoute. Required by controller-gen. 121 | func (in *TLSRoute) DeepCopy() *TLSRoute { 122 | if in == nil { 123 | return nil 124 | } 125 | out := new(TLSRoute) 126 | in.DeepCopyInto(out) 127 | return out 128 | } 129 | 130 | // DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new TLSRoute. Required by controller-gen. 131 | func (in *TLSRoute) DeepCopyInterface() interface{} { 132 | return in.DeepCopy() 133 | } 134 | 135 | // DeepCopyInto supports using TCPRoute within kubernetes types, where deepcopy-gen is used. 136 | func (in *TCPRoute) DeepCopyInto(out *TCPRoute) { 137 | p := proto.Clone(in).(*TCPRoute) 138 | *out = *p 139 | } 140 | 141 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPRoute. Required by controller-gen. 142 | func (in *TCPRoute) DeepCopy() *TCPRoute { 143 | if in == nil { 144 | return nil 145 | } 146 | out := new(TCPRoute) 147 | in.DeepCopyInto(out) 148 | return out 149 | } 150 | 151 | // DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new TCPRoute. Required by controller-gen. 152 | func (in *TCPRoute) DeepCopyInterface() interface{} { 153 | return in.DeepCopy() 154 | } 155 | 156 | // DeepCopyInto supports using HTTPMatchRequest within kubernetes types, where deepcopy-gen is used. 157 | func (in *HTTPMatchRequest) DeepCopyInto(out *HTTPMatchRequest) { 158 | p := proto.Clone(in).(*HTTPMatchRequest) 159 | *out = *p 160 | } 161 | 162 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPMatchRequest. Required by controller-gen. 163 | func (in *HTTPMatchRequest) DeepCopy() *HTTPMatchRequest { 164 | if in == nil { 165 | return nil 166 | } 167 | out := new(HTTPMatchRequest) 168 | in.DeepCopyInto(out) 169 | return out 170 | } 171 | 172 | // DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new HTTPMatchRequest. Required by controller-gen. 173 | func (in *HTTPMatchRequest) DeepCopyInterface() interface{} { 174 | return in.DeepCopy() 175 | } 176 | 177 | // DeepCopyInto supports using HTTPRouteDestination within kubernetes types, where deepcopy-gen is used. 178 | func (in *HTTPRouteDestination) DeepCopyInto(out *HTTPRouteDestination) { 179 | p := proto.Clone(in).(*HTTPRouteDestination) 180 | *out = *p 181 | } 182 | 183 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRouteDestination. Required by controller-gen. 184 | func (in *HTTPRouteDestination) DeepCopy() *HTTPRouteDestination { 185 | if in == nil { 186 | return nil 187 | } 188 | out := new(HTTPRouteDestination) 189 | in.DeepCopyInto(out) 190 | return out 191 | } 192 | 193 | // DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRouteDestination. Required by controller-gen. 194 | func (in *HTTPRouteDestination) DeepCopyInterface() interface{} { 195 | return in.DeepCopy() 196 | } 197 | 198 | // DeepCopyInto supports using Fallback within kubernetes types, where deepcopy-gen is used. 199 | func (in *Fallback) DeepCopyInto(out *Fallback) { 200 | p := proto.Clone(in).(*Fallback) 201 | *out = *p 202 | } 203 | 204 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Fallback. Required by controller-gen. 205 | func (in *Fallback) DeepCopy() *Fallback { 206 | if in == nil { 207 | return nil 208 | } 209 | out := new(Fallback) 210 | in.DeepCopyInto(out) 211 | return out 212 | } 213 | 214 | // DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new Fallback. Required by controller-gen. 215 | func (in *Fallback) DeepCopyInterface() interface{} { 216 | return in.DeepCopy() 217 | } 218 | 219 | // DeepCopyInto supports using RouteDestination within kubernetes types, where deepcopy-gen is used. 220 | func (in *RouteDestination) DeepCopyInto(out *RouteDestination) { 221 | p := proto.Clone(in).(*RouteDestination) 222 | *out = *p 223 | } 224 | 225 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteDestination. Required by controller-gen. 226 | func (in *RouteDestination) DeepCopy() *RouteDestination { 227 | if in == nil { 228 | return nil 229 | } 230 | out := new(RouteDestination) 231 | in.DeepCopyInto(out) 232 | return out 233 | } 234 | 235 | // DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new RouteDestination. Required by controller-gen. 236 | func (in *RouteDestination) DeepCopyInterface() interface{} { 237 | return in.DeepCopy() 238 | } 239 | 240 | // DeepCopyInto supports using L4MatchAttributes within kubernetes types, where deepcopy-gen is used. 241 | func (in *L4MatchAttributes) DeepCopyInto(out *L4MatchAttributes) { 242 | p := proto.Clone(in).(*L4MatchAttributes) 243 | *out = *p 244 | } 245 | 246 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new L4MatchAttributes. Required by controller-gen. 247 | func (in *L4MatchAttributes) DeepCopy() *L4MatchAttributes { 248 | if in == nil { 249 | return nil 250 | } 251 | out := new(L4MatchAttributes) 252 | in.DeepCopyInto(out) 253 | return out 254 | } 255 | 256 | // DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new L4MatchAttributes. Required by controller-gen. 257 | func (in *L4MatchAttributes) DeepCopyInterface() interface{} { 258 | return in.DeepCopy() 259 | } 260 | 261 | // DeepCopyInto supports using TLSMatchAttributes within kubernetes types, where deepcopy-gen is used. 262 | func (in *TLSMatchAttributes) DeepCopyInto(out *TLSMatchAttributes) { 263 | p := proto.Clone(in).(*TLSMatchAttributes) 264 | *out = *p 265 | } 266 | 267 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSMatchAttributes. Required by controller-gen. 268 | func (in *TLSMatchAttributes) DeepCopy() *TLSMatchAttributes { 269 | if in == nil { 270 | return nil 271 | } 272 | out := new(TLSMatchAttributes) 273 | in.DeepCopyInto(out) 274 | return out 275 | } 276 | 277 | // DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new TLSMatchAttributes. Required by controller-gen. 278 | func (in *TLSMatchAttributes) DeepCopyInterface() interface{} { 279 | return in.DeepCopy() 280 | } 281 | 282 | // DeepCopyInto supports using HTTPRedirect within kubernetes types, where deepcopy-gen is used. 283 | func (in *HTTPRedirect) DeepCopyInto(out *HTTPRedirect) { 284 | p := proto.Clone(in).(*HTTPRedirect) 285 | *out = *p 286 | } 287 | 288 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRedirect. Required by controller-gen. 289 | func (in *HTTPRedirect) DeepCopy() *HTTPRedirect { 290 | if in == nil { 291 | return nil 292 | } 293 | out := new(HTTPRedirect) 294 | in.DeepCopyInto(out) 295 | return out 296 | } 297 | 298 | // DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRedirect. Required by controller-gen. 299 | func (in *HTTPRedirect) DeepCopyInterface() interface{} { 300 | return in.DeepCopy() 301 | } 302 | 303 | // DeepCopyInto supports using HTTPDirectResponse within kubernetes types, where deepcopy-gen is used. 304 | func (in *HTTPDirectResponse) DeepCopyInto(out *HTTPDirectResponse) { 305 | p := proto.Clone(in).(*HTTPDirectResponse) 306 | *out = *p 307 | } 308 | 309 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPDirectResponse. Required by controller-gen. 310 | func (in *HTTPDirectResponse) DeepCopy() *HTTPDirectResponse { 311 | if in == nil { 312 | return nil 313 | } 314 | out := new(HTTPDirectResponse) 315 | in.DeepCopyInto(out) 316 | return out 317 | } 318 | 319 | // DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new HTTPDirectResponse. Required by controller-gen. 320 | func (in *HTTPDirectResponse) DeepCopyInterface() interface{} { 321 | return in.DeepCopy() 322 | } 323 | 324 | // DeepCopyInto supports using HTTPBody within kubernetes types, where deepcopy-gen is used. 325 | func (in *HTTPBody) DeepCopyInto(out *HTTPBody) { 326 | p := proto.Clone(in).(*HTTPBody) 327 | *out = *p 328 | } 329 | 330 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPBody. Required by controller-gen. 331 | func (in *HTTPBody) DeepCopy() *HTTPBody { 332 | if in == nil { 333 | return nil 334 | } 335 | out := new(HTTPBody) 336 | in.DeepCopyInto(out) 337 | return out 338 | } 339 | 340 | // DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new HTTPBody. Required by controller-gen. 341 | func (in *HTTPBody) DeepCopyInterface() interface{} { 342 | return in.DeepCopy() 343 | } 344 | 345 | // DeepCopyInto supports using HTTPRewrite within kubernetes types, where deepcopy-gen is used. 346 | func (in *HTTPRewrite) DeepCopyInto(out *HTTPRewrite) { 347 | p := proto.Clone(in).(*HTTPRewrite) 348 | *out = *p 349 | } 350 | 351 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRewrite. Required by controller-gen. 352 | func (in *HTTPRewrite) DeepCopy() *HTTPRewrite { 353 | if in == nil { 354 | return nil 355 | } 356 | out := new(HTTPRewrite) 357 | in.DeepCopyInto(out) 358 | return out 359 | } 360 | 361 | // DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRewrite. Required by controller-gen. 362 | func (in *HTTPRewrite) DeepCopyInterface() interface{} { 363 | return in.DeepCopy() 364 | } 365 | 366 | // DeepCopyInto supports using StringMatch within kubernetes types, where deepcopy-gen is used. 367 | func (in *StringMatch) DeepCopyInto(out *StringMatch) { 368 | p := proto.Clone(in).(*StringMatch) 369 | *out = *p 370 | } 371 | 372 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StringMatch. Required by controller-gen. 373 | func (in *StringMatch) DeepCopy() *StringMatch { 374 | if in == nil { 375 | return nil 376 | } 377 | out := new(StringMatch) 378 | in.DeepCopyInto(out) 379 | return out 380 | } 381 | 382 | // DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new StringMatch. Required by controller-gen. 383 | func (in *StringMatch) DeepCopyInterface() interface{} { 384 | return in.DeepCopy() 385 | } 386 | 387 | // DeepCopyInto supports using HTTPRetry within kubernetes types, where deepcopy-gen is used. 388 | func (in *HTTPRetry) DeepCopyInto(out *HTTPRetry) { 389 | p := proto.Clone(in).(*HTTPRetry) 390 | *out = *p 391 | } 392 | 393 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRetry. Required by controller-gen. 394 | func (in *HTTPRetry) DeepCopy() *HTTPRetry { 395 | if in == nil { 396 | return nil 397 | } 398 | out := new(HTTPRetry) 399 | in.DeepCopyInto(out) 400 | return out 401 | } 402 | 403 | // DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRetry. Required by controller-gen. 404 | func (in *HTTPRetry) DeepCopyInterface() interface{} { 405 | return in.DeepCopy() 406 | } 407 | 408 | // DeepCopyInto supports using CorsPolicy within kubernetes types, where deepcopy-gen is used. 409 | func (in *CorsPolicy) DeepCopyInto(out *CorsPolicy) { 410 | p := proto.Clone(in).(*CorsPolicy) 411 | *out = *p 412 | } 413 | 414 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CorsPolicy. Required by controller-gen. 415 | func (in *CorsPolicy) DeepCopy() *CorsPolicy { 416 | if in == nil { 417 | return nil 418 | } 419 | out := new(CorsPolicy) 420 | in.DeepCopyInto(out) 421 | return out 422 | } 423 | 424 | // DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new CorsPolicy. Required by controller-gen. 425 | func (in *CorsPolicy) DeepCopyInterface() interface{} { 426 | return in.DeepCopy() 427 | } 428 | 429 | // DeepCopyInto supports using HTTPFaultInjection within kubernetes types, where deepcopy-gen is used. 430 | func (in *HTTPFaultInjection) DeepCopyInto(out *HTTPFaultInjection) { 431 | p := proto.Clone(in).(*HTTPFaultInjection) 432 | *out = *p 433 | } 434 | 435 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPFaultInjection. Required by controller-gen. 436 | func (in *HTTPFaultInjection) DeepCopy() *HTTPFaultInjection { 437 | if in == nil { 438 | return nil 439 | } 440 | out := new(HTTPFaultInjection) 441 | in.DeepCopyInto(out) 442 | return out 443 | } 444 | 445 | // DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new HTTPFaultInjection. Required by controller-gen. 446 | func (in *HTTPFaultInjection) DeepCopyInterface() interface{} { 447 | return in.DeepCopy() 448 | } 449 | 450 | // DeepCopyInto supports using PortSelector within kubernetes types, where deepcopy-gen is used. 451 | func (in *PortSelector) DeepCopyInto(out *PortSelector) { 452 | p := proto.Clone(in).(*PortSelector) 453 | *out = *p 454 | } 455 | 456 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PortSelector. Required by controller-gen. 457 | func (in *PortSelector) DeepCopy() *PortSelector { 458 | if in == nil { 459 | return nil 460 | } 461 | out := new(PortSelector) 462 | in.DeepCopyInto(out) 463 | return out 464 | } 465 | 466 | // DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new PortSelector. Required by controller-gen. 467 | func (in *PortSelector) DeepCopyInterface() interface{} { 468 | return in.DeepCopy() 469 | } 470 | 471 | // DeepCopyInto supports using Percent within kubernetes types, where deepcopy-gen is used. 472 | func (in *Percent) DeepCopyInto(out *Percent) { 473 | p := proto.Clone(in).(*Percent) 474 | *out = *p 475 | } 476 | 477 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Percent. Required by controller-gen. 478 | func (in *Percent) DeepCopy() *Percent { 479 | if in == nil { 480 | return nil 481 | } 482 | out := new(Percent) 483 | in.DeepCopyInto(out) 484 | return out 485 | } 486 | 487 | // DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new Percent. Required by controller-gen. 488 | func (in *Percent) DeepCopyInterface() interface{} { 489 | return in.DeepCopy() 490 | } 491 | -------------------------------------------------------------------------------- /pkg/api/v1alpha1/traffic/zz_generated.deepcopy.go: -------------------------------------------------------------------------------- 1 | //go:build !ignore_autogenerated 2 | // +build !ignore_autogenerated 3 | 4 | /* 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | // Code generated by controller-gen. DO NOT EDIT. 19 | 20 | package traffic 21 | 22 | import ( 23 | runtime "k8s.io/apimachinery/pkg/runtime" 24 | ) 25 | 26 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 27 | func (in *TrafficRouter) DeepCopyInto(out *TrafficRouter) { 28 | *out = *in 29 | out.TypeMeta = in.TypeMeta 30 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 31 | in.Spec.DeepCopyInto(&out.Spec) 32 | out.Status = in.Status 33 | } 34 | 35 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficRouterRule. 36 | func (in *TrafficRouter) DeepCopy() *TrafficRouter { 37 | if in == nil { 38 | return nil 39 | } 40 | out := new(TrafficRouter) 41 | in.DeepCopyInto(out) 42 | return out 43 | } 44 | 45 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 46 | func (in *TrafficRouter) DeepCopyObject() runtime.Object { 47 | if c := in.DeepCopy(); c != nil { 48 | return c 49 | } 50 | return nil 51 | } 52 | 53 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 54 | func (in *TrafficRouterList) DeepCopyInto(out *TrafficRouterList) { 55 | *out = *in 56 | out.TypeMeta = in.TypeMeta 57 | in.ListMeta.DeepCopyInto(&out.ListMeta) 58 | if in.Items != nil { 59 | in, out := &in.Items, &out.Items 60 | *out = make([]TrafficRouter, len(*in)) 61 | for i := range *in { 62 | (*in)[i].DeepCopyInto(&(*out)[i]) 63 | } 64 | } 65 | } 66 | 67 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficRouterRuleList. 68 | func (in *TrafficRouterList) DeepCopy() *TrafficRouterList { 69 | if in == nil { 70 | return nil 71 | } 72 | out := new(TrafficRouterList) 73 | in.DeepCopyInto(out) 74 | return out 75 | } 76 | 77 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 78 | func (in *TrafficRouterList) DeepCopyObject() runtime.Object { 79 | if c := in.DeepCopy(); c != nil { 80 | return c 81 | } 82 | return nil 83 | } 84 | -------------------------------------------------------------------------------- /pkg/api/v1alpha1/zz_generated.deepcopy.go: -------------------------------------------------------------------------------- 1 | //go:build !ignore_autogenerated 2 | // +build !ignore_autogenerated 3 | 4 | /* 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | // Code generated by controller-gen. DO NOT EDIT. 19 | 20 | package v1alpha1 21 | 22 | import ( 23 | runtime "k8s.io/apimachinery/pkg/runtime" 24 | ) 25 | 26 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 27 | func (in *CircuitBreakerStrategy) DeepCopyInto(out *CircuitBreakerStrategy) { 28 | *out = *in 29 | out.TypeMeta = in.TypeMeta 30 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 31 | out.Spec = in.Spec 32 | out.Status = in.Status 33 | } 34 | 35 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CircuitBreakerStrategy. 36 | func (in *CircuitBreakerStrategy) DeepCopy() *CircuitBreakerStrategy { 37 | if in == nil { 38 | return nil 39 | } 40 | out := new(CircuitBreakerStrategy) 41 | in.DeepCopyInto(out) 42 | return out 43 | } 44 | 45 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 46 | func (in *CircuitBreakerStrategy) DeepCopyObject() runtime.Object { 47 | if c := in.DeepCopy(); c != nil { 48 | return c 49 | } 50 | return nil 51 | } 52 | 53 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 54 | func (in *CircuitBreakerStrategyList) DeepCopyInto(out *CircuitBreakerStrategyList) { 55 | *out = *in 56 | out.TypeMeta = in.TypeMeta 57 | in.ListMeta.DeepCopyInto(&out.ListMeta) 58 | if in.Items != nil { 59 | in, out := &in.Items, &out.Items 60 | *out = make([]CircuitBreakerStrategy, len(*in)) 61 | for i := range *in { 62 | (*in)[i].DeepCopyInto(&(*out)[i]) 63 | } 64 | } 65 | } 66 | 67 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CircuitBreakerStrategyList. 68 | func (in *CircuitBreakerStrategyList) DeepCopy() *CircuitBreakerStrategyList { 69 | if in == nil { 70 | return nil 71 | } 72 | out := new(CircuitBreakerStrategyList) 73 | in.DeepCopyInto(out) 74 | return out 75 | } 76 | 77 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 78 | func (in *CircuitBreakerStrategyList) DeepCopyObject() runtime.Object { 79 | if c := in.DeepCopy(); c != nil { 80 | return c 81 | } 82 | return nil 83 | } 84 | 85 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 86 | func (in *CircuitBreakerStrategySpec) DeepCopyInto(out *CircuitBreakerStrategySpec) { 87 | *out = *in 88 | out.SlowConditions = in.SlowConditions 89 | out.ErrorConditions = in.ErrorConditions 90 | } 91 | 92 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CircuitBreakerStrategySpec. 93 | func (in *CircuitBreakerStrategySpec) DeepCopy() *CircuitBreakerStrategySpec { 94 | if in == nil { 95 | return nil 96 | } 97 | out := new(CircuitBreakerStrategySpec) 98 | in.DeepCopyInto(out) 99 | return out 100 | } 101 | 102 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 103 | func (in *CircuitBreakerStrategyStatus) DeepCopyInto(out *CircuitBreakerStrategyStatus) { 104 | *out = *in 105 | } 106 | 107 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CircuitBreakerStrategyStatus. 108 | func (in *CircuitBreakerStrategyStatus) DeepCopy() *CircuitBreakerStrategyStatus { 109 | if in == nil { 110 | return nil 111 | } 112 | out := new(CircuitBreakerStrategyStatus) 113 | in.DeepCopyInto(out) 114 | return out 115 | } 116 | 117 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 118 | func (in *ConcurrencyLimitStrategy) DeepCopyInto(out *ConcurrencyLimitStrategy) { 119 | *out = *in 120 | out.TypeMeta = in.TypeMeta 121 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 122 | out.Spec = in.Spec 123 | out.Status = in.Status 124 | } 125 | 126 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConcurrencyLimitStrategy. 127 | func (in *ConcurrencyLimitStrategy) DeepCopy() *ConcurrencyLimitStrategy { 128 | if in == nil { 129 | return nil 130 | } 131 | out := new(ConcurrencyLimitStrategy) 132 | in.DeepCopyInto(out) 133 | return out 134 | } 135 | 136 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 137 | func (in *ConcurrencyLimitStrategy) DeepCopyObject() runtime.Object { 138 | if c := in.DeepCopy(); c != nil { 139 | return c 140 | } 141 | return nil 142 | } 143 | 144 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 145 | func (in *ConcurrencyLimitStrategyList) DeepCopyInto(out *ConcurrencyLimitStrategyList) { 146 | *out = *in 147 | out.TypeMeta = in.TypeMeta 148 | in.ListMeta.DeepCopyInto(&out.ListMeta) 149 | if in.Items != nil { 150 | in, out := &in.Items, &out.Items 151 | *out = make([]ConcurrencyLimitStrategy, len(*in)) 152 | for i := range *in { 153 | (*in)[i].DeepCopyInto(&(*out)[i]) 154 | } 155 | } 156 | } 157 | 158 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConcurrencyLimitStrategyList. 159 | func (in *ConcurrencyLimitStrategyList) DeepCopy() *ConcurrencyLimitStrategyList { 160 | if in == nil { 161 | return nil 162 | } 163 | out := new(ConcurrencyLimitStrategyList) 164 | in.DeepCopyInto(out) 165 | return out 166 | } 167 | 168 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 169 | func (in *ConcurrencyLimitStrategyList) DeepCopyObject() runtime.Object { 170 | if c := in.DeepCopy(); c != nil { 171 | return c 172 | } 173 | return nil 174 | } 175 | 176 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 177 | func (in *ConcurrencyLimitStrategySpec) DeepCopyInto(out *ConcurrencyLimitStrategySpec) { 178 | *out = *in 179 | } 180 | 181 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConcurrencyLimitStrategySpec. 182 | func (in *ConcurrencyLimitStrategySpec) DeepCopy() *ConcurrencyLimitStrategySpec { 183 | if in == nil { 184 | return nil 185 | } 186 | out := new(ConcurrencyLimitStrategySpec) 187 | in.DeepCopyInto(out) 188 | return out 189 | } 190 | 191 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 192 | func (in *ConcurrencyLimitStrategyStatus) DeepCopyInto(out *ConcurrencyLimitStrategyStatus) { 193 | *out = *in 194 | } 195 | 196 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConcurrencyLimitStrategyStatus. 197 | func (in *ConcurrencyLimitStrategyStatus) DeepCopy() *ConcurrencyLimitStrategyStatus { 198 | if in == nil { 199 | return nil 200 | } 201 | out := new(ConcurrencyLimitStrategyStatus) 202 | in.DeepCopyInto(out) 203 | return out 204 | } 205 | 206 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 207 | func (in *ErrorConditions) DeepCopyInto(out *ErrorConditions) { 208 | *out = *in 209 | } 210 | 211 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ErrorConditions. 212 | func (in *ErrorConditions) DeepCopy() *ErrorConditions { 213 | if in == nil { 214 | return nil 215 | } 216 | out := new(ErrorConditions) 217 | in.DeepCopyInto(out) 218 | return out 219 | } 220 | 221 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 222 | func (in *FaultToleranceRule) DeepCopyInto(out *FaultToleranceRule) { 223 | *out = *in 224 | out.TypeMeta = in.TypeMeta 225 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 226 | in.Spec.DeepCopyInto(&out.Spec) 227 | out.Status = in.Status 228 | } 229 | 230 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FaultToleranceRule. 231 | func (in *FaultToleranceRule) DeepCopy() *FaultToleranceRule { 232 | if in == nil { 233 | return nil 234 | } 235 | out := new(FaultToleranceRule) 236 | in.DeepCopyInto(out) 237 | return out 238 | } 239 | 240 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 241 | func (in *FaultToleranceRule) DeepCopyObject() runtime.Object { 242 | if c := in.DeepCopy(); c != nil { 243 | return c 244 | } 245 | return nil 246 | } 247 | 248 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 249 | func (in *FaultToleranceRuleList) DeepCopyInto(out *FaultToleranceRuleList) { 250 | *out = *in 251 | out.TypeMeta = in.TypeMeta 252 | in.ListMeta.DeepCopyInto(&out.ListMeta) 253 | if in.Items != nil { 254 | in, out := &in.Items, &out.Items 255 | *out = make([]FaultToleranceRule, len(*in)) 256 | for i := range *in { 257 | (*in)[i].DeepCopyInto(&(*out)[i]) 258 | } 259 | } 260 | } 261 | 262 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FaultToleranceRuleList. 263 | func (in *FaultToleranceRuleList) DeepCopy() *FaultToleranceRuleList { 264 | if in == nil { 265 | return nil 266 | } 267 | out := new(FaultToleranceRuleList) 268 | in.DeepCopyInto(out) 269 | return out 270 | } 271 | 272 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 273 | func (in *FaultToleranceRuleList) DeepCopyObject() runtime.Object { 274 | if c := in.DeepCopy(); c != nil { 275 | return c 276 | } 277 | return nil 278 | } 279 | 280 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 281 | func (in *FaultToleranceRuleSpec) DeepCopyInto(out *FaultToleranceRuleSpec) { 282 | *out = *in 283 | if in.Targets != nil { 284 | in, out := &in.Targets, &out.Targets 285 | *out = make([]FaultToleranceTargetRef, len(*in)) 286 | copy(*out, *in) 287 | } 288 | if in.Strategies != nil { 289 | in, out := &in.Strategies, &out.Strategies 290 | *out = make([]FaultToleranceStrategyRef, len(*in)) 291 | copy(*out, *in) 292 | } 293 | } 294 | 295 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FaultToleranceRuleSpec. 296 | func (in *FaultToleranceRuleSpec) DeepCopy() *FaultToleranceRuleSpec { 297 | if in == nil { 298 | return nil 299 | } 300 | out := new(FaultToleranceRuleSpec) 301 | in.DeepCopyInto(out) 302 | return out 303 | } 304 | 305 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 306 | func (in *FaultToleranceRuleStatus) DeepCopyInto(out *FaultToleranceRuleStatus) { 307 | *out = *in 308 | } 309 | 310 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FaultToleranceRuleStatus. 311 | func (in *FaultToleranceRuleStatus) DeepCopy() *FaultToleranceRuleStatus { 312 | if in == nil { 313 | return nil 314 | } 315 | out := new(FaultToleranceRuleStatus) 316 | in.DeepCopyInto(out) 317 | return out 318 | } 319 | 320 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 321 | func (in *FaultToleranceStrategyRef) DeepCopyInto(out *FaultToleranceStrategyRef) { 322 | *out = *in 323 | } 324 | 325 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FaultToleranceStrategyRef. 326 | func (in *FaultToleranceStrategyRef) DeepCopy() *FaultToleranceStrategyRef { 327 | if in == nil { 328 | return nil 329 | } 330 | out := new(FaultToleranceStrategyRef) 331 | in.DeepCopyInto(out) 332 | return out 333 | } 334 | 335 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 336 | func (in *FaultToleranceTargetRef) DeepCopyInto(out *FaultToleranceTargetRef) { 337 | *out = *in 338 | } 339 | 340 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FaultToleranceTargetRef. 341 | func (in *FaultToleranceTargetRef) DeepCopy() *FaultToleranceTargetRef { 342 | if in == nil { 343 | return nil 344 | } 345 | out := new(FaultToleranceTargetRef) 346 | in.DeepCopyInto(out) 347 | return out 348 | } 349 | 350 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 351 | func (in *RateLimitStrategy) DeepCopyInto(out *RateLimitStrategy) { 352 | *out = *in 353 | out.TypeMeta = in.TypeMeta 354 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 355 | out.Spec = in.Spec 356 | out.Status = in.Status 357 | } 358 | 359 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitStrategy. 360 | func (in *RateLimitStrategy) DeepCopy() *RateLimitStrategy { 361 | if in == nil { 362 | return nil 363 | } 364 | out := new(RateLimitStrategy) 365 | in.DeepCopyInto(out) 366 | return out 367 | } 368 | 369 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 370 | func (in *RateLimitStrategy) DeepCopyObject() runtime.Object { 371 | if c := in.DeepCopy(); c != nil { 372 | return c 373 | } 374 | return nil 375 | } 376 | 377 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 378 | func (in *RateLimitStrategyList) DeepCopyInto(out *RateLimitStrategyList) { 379 | *out = *in 380 | out.TypeMeta = in.TypeMeta 381 | in.ListMeta.DeepCopyInto(&out.ListMeta) 382 | if in.Items != nil { 383 | in, out := &in.Items, &out.Items 384 | *out = make([]RateLimitStrategy, len(*in)) 385 | for i := range *in { 386 | (*in)[i].DeepCopyInto(&(*out)[i]) 387 | } 388 | } 389 | } 390 | 391 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitStrategyList. 392 | func (in *RateLimitStrategyList) DeepCopy() *RateLimitStrategyList { 393 | if in == nil { 394 | return nil 395 | } 396 | out := new(RateLimitStrategyList) 397 | in.DeepCopyInto(out) 398 | return out 399 | } 400 | 401 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 402 | func (in *RateLimitStrategyList) DeepCopyObject() runtime.Object { 403 | if c := in.DeepCopy(); c != nil { 404 | return c 405 | } 406 | return nil 407 | } 408 | 409 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 410 | func (in *RateLimitStrategySpec) DeepCopyInto(out *RateLimitStrategySpec) { 411 | *out = *in 412 | } 413 | 414 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitStrategySpec. 415 | func (in *RateLimitStrategySpec) DeepCopy() *RateLimitStrategySpec { 416 | if in == nil { 417 | return nil 418 | } 419 | out := new(RateLimitStrategySpec) 420 | in.DeepCopyInto(out) 421 | return out 422 | } 423 | 424 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 425 | func (in *RateLimitStrategyStatus) DeepCopyInto(out *RateLimitStrategyStatus) { 426 | *out = *in 427 | } 428 | 429 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimitStrategyStatus. 430 | func (in *RateLimitStrategyStatus) DeepCopy() *RateLimitStrategyStatus { 431 | if in == nil { 432 | return nil 433 | } 434 | out := new(RateLimitStrategyStatus) 435 | in.DeepCopyInto(out) 436 | return out 437 | } 438 | 439 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 440 | func (in *SlowConditions) DeepCopyInto(out *SlowConditions) { 441 | *out = *in 442 | } 443 | 444 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SlowConditions. 445 | func (in *SlowConditions) DeepCopy() *SlowConditions { 446 | if in == nil { 447 | return nil 448 | } 449 | out := new(SlowConditions) 450 | in.DeepCopyInto(out) 451 | return out 452 | } 453 | 454 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 455 | func (in *ThrottlingStrategy) DeepCopyInto(out *ThrottlingStrategy) { 456 | *out = *in 457 | out.TypeMeta = in.TypeMeta 458 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 459 | out.Spec = in.Spec 460 | out.Status = in.Status 461 | } 462 | 463 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ThrottlingStrategy. 464 | func (in *ThrottlingStrategy) DeepCopy() *ThrottlingStrategy { 465 | if in == nil { 466 | return nil 467 | } 468 | out := new(ThrottlingStrategy) 469 | in.DeepCopyInto(out) 470 | return out 471 | } 472 | 473 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 474 | func (in *ThrottlingStrategy) DeepCopyObject() runtime.Object { 475 | if c := in.DeepCopy(); c != nil { 476 | return c 477 | } 478 | return nil 479 | } 480 | 481 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 482 | func (in *ThrottlingStrategyList) DeepCopyInto(out *ThrottlingStrategyList) { 483 | *out = *in 484 | out.TypeMeta = in.TypeMeta 485 | in.ListMeta.DeepCopyInto(&out.ListMeta) 486 | if in.Items != nil { 487 | in, out := &in.Items, &out.Items 488 | *out = make([]ThrottlingStrategy, len(*in)) 489 | for i := range *in { 490 | (*in)[i].DeepCopyInto(&(*out)[i]) 491 | } 492 | } 493 | } 494 | 495 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ThrottlingStrategyList. 496 | func (in *ThrottlingStrategyList) DeepCopy() *ThrottlingStrategyList { 497 | if in == nil { 498 | return nil 499 | } 500 | out := new(ThrottlingStrategyList) 501 | in.DeepCopyInto(out) 502 | return out 503 | } 504 | 505 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 506 | func (in *ThrottlingStrategyList) DeepCopyObject() runtime.Object { 507 | if c := in.DeepCopy(); c != nil { 508 | return c 509 | } 510 | return nil 511 | } 512 | 513 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 514 | func (in *ThrottlingStrategySpec) DeepCopyInto(out *ThrottlingStrategySpec) { 515 | *out = *in 516 | } 517 | 518 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ThrottlingStrategySpec. 519 | func (in *ThrottlingStrategySpec) DeepCopy() *ThrottlingStrategySpec { 520 | if in == nil { 521 | return nil 522 | } 523 | out := new(ThrottlingStrategySpec) 524 | in.DeepCopyInto(out) 525 | return out 526 | } 527 | 528 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 529 | func (in *ThrottlingStrategyStatus) DeepCopyInto(out *ThrottlingStrategyStatus) { 530 | *out = *in 531 | } 532 | 533 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ThrottlingStrategyStatus. 534 | func (in *ThrottlingStrategyStatus) DeepCopy() *ThrottlingStrategyStatus { 535 | if in == nil { 536 | return nil 537 | } 538 | out := new(ThrottlingStrategyStatus) 539 | in.DeepCopyInto(out) 540 | return out 541 | } 542 | -------------------------------------------------------------------------------- /pkg/component/component.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package component 16 | 17 | // OpenSergoControlPlaneComponent represents a component of OpenSergo control plane. 18 | type OpenSergoControlPlaneComponent interface { 19 | // ComponentName returns the name of the component. 20 | ComponentName() string 21 | 22 | // Run starts the component. 23 | Run() error 24 | } 25 | -------------------------------------------------------------------------------- /pkg/controller/crd_cache.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package controller 16 | 17 | import ( 18 | "sync" 19 | 20 | "github.com/opensergo/opensergo-control-plane/pkg/model" 21 | "k8s.io/apimachinery/pkg/types" 22 | "sigs.k8s.io/controller-runtime/pkg/client" 23 | ) 24 | 25 | type CRDObjectsHolder struct { 26 | objects []client.Object 27 | version int64 28 | } 29 | 30 | // CRDCache caches versioned CRD objects in local. 31 | type CRDCache struct { 32 | kind string 33 | // crdEntityMap represents a map: (namespace, name) -> unique CRD 34 | crdEntityMap map[types.NamespacedName]client.Object 35 | // namespaceAppMap represents a map for CRD group: (namespace, app) -> versionedCRDs 36 | namespaceAppMap map[model.NamespacedApp]*CRDObjectsHolder 37 | 38 | updateMux sync.RWMutex 39 | } 40 | 41 | func NewCRDCache(kind string) *CRDCache { 42 | return &CRDCache{ 43 | kind: kind, 44 | crdEntityMap: make(map[types.NamespacedName]client.Object), 45 | namespaceAppMap: make(map[model.NamespacedApp]*CRDObjectsHolder), 46 | } 47 | } 48 | 49 | func (c *CRDCache) GetByNamespacedName(n types.NamespacedName) (client.Object, bool) { 50 | c.updateMux.RLock() 51 | defer c.updateMux.RUnlock() 52 | 53 | obj, exists := c.crdEntityMap[n] 54 | return obj, exists 55 | } 56 | 57 | func (c *CRDCache) SetByNamespacedName(n types.NamespacedName, object client.Object) { 58 | c.updateMux.Lock() 59 | defer c.updateMux.Unlock() 60 | 61 | c.crdEntityMap[n] = object 62 | } 63 | 64 | func (c *CRDCache) DeleteByNamespacedName(n types.NamespacedName) { 65 | c.updateMux.Lock() 66 | defer c.updateMux.Unlock() 67 | 68 | delete(c.crdEntityMap, n) 69 | } 70 | 71 | func (c *CRDCache) SetByNamespaceApp(n model.NamespacedApp, object client.Object) int { 72 | c.updateMux.Lock() 73 | defer c.updateMux.Unlock() 74 | 75 | o, exists := c.namespaceAppMap[n] 76 | if !exists || o == nil { 77 | c.namespaceAppMap[n] = &CRDObjectsHolder{ 78 | objects: []client.Object{object}, 79 | version: 1, 80 | } 81 | return AddRule 82 | } else { 83 | for index := range o.objects { 84 | // Update object version 85 | if o.objects[index].GetName() == object.GetName() { 86 | o.objects[index] = object 87 | o.version++ 88 | return UpdateRule 89 | } 90 | } 91 | o.objects = append(o.objects, object) 92 | o.version++ 93 | return AddRule 94 | } 95 | } 96 | 97 | func (c *CRDCache) DeleteByNamespaceApp(n model.NamespacedApp, name string) { 98 | c.updateMux.Lock() 99 | defer c.updateMux.Unlock() 100 | 101 | o, exists := c.namespaceAppMap[n] 102 | if !exists || o == nil { 103 | return 104 | } 105 | for index, obj := range o.objects { 106 | if obj.GetLabels()["app"] == n.App && obj.GetName() == name { 107 | // Update object version 108 | o.objects = append(o.objects[:index], o.objects[index+1:]...) 109 | o.version++ 110 | return 111 | } 112 | } 113 | } 114 | 115 | func (c *CRDCache) GetByNamespaceApp(n model.NamespacedApp) ([]client.Object, int64) { 116 | c.updateMux.RLock() 117 | defer c.updateMux.RUnlock() 118 | 119 | o, _ := c.namespaceAppMap[n] 120 | if o == nil { 121 | return []client.Object{}, 0 122 | } 123 | return o.objects, o.version 124 | } 125 | 126 | func (c *CRDCache) GetAppByNamespacedName(n types.NamespacedName) (string, bool) { 127 | c.updateMux.RLock() 128 | defer c.updateMux.RUnlock() 129 | 130 | crd, exists := c.crdEntityMap[n] 131 | if exists { 132 | return crd.GetLabels()["app"], true 133 | } else { 134 | return "", false 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /pkg/controller/crd_meta.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package controller 16 | 17 | import ( 18 | "github.com/opensergo/opensergo-control-plane/pkg/api/v1alpha1" 19 | "github.com/opensergo/opensergo-control-plane/pkg/api/v1alpha1/traffic" 20 | "sigs.k8s.io/controller-runtime/pkg/client" 21 | ) 22 | 23 | type CRDKind = string 24 | 25 | // CRDGenerator represents a generator function of an OpenSergo CRD. 26 | type CRDGenerator = func() client.Object 27 | 28 | type CRDMetadata struct { 29 | kind CRDKind 30 | 31 | generator CRDGenerator 32 | } 33 | 34 | func (m *CRDMetadata) Kind() CRDKind { 35 | return m.kind 36 | } 37 | 38 | func (m *CRDMetadata) Generator() CRDGenerator { 39 | return m.generator 40 | } 41 | 42 | func NewCRDMetadata(kind CRDKind, generator CRDGenerator) *CRDMetadata { 43 | return &CRDMetadata{ 44 | kind: kind, 45 | generator: generator, 46 | } 47 | } 48 | 49 | const ( 50 | FaultToleranceRuleKind = "fault-tolerance.opensergo.io/v1alpha1/FaultToleranceRule" 51 | RateLimitStrategyKind = "fault-tolerance.opensergo.io/v1alpha1/RateLimitStrategy" 52 | ThrottlingStrategyKind = "fault-tolerance.opensergo.io/v1alpha1/ThrottlingStrategy" 53 | ConcurrencyLimitStrategyKind = "fault-tolerance.opensergo.io/v1alpha1/ConcurrencyLimitStrategy" 54 | CircuitBreakerStrategyKind = "fault-tolerance.opensergo.io/v1alpha1/CircuitBreakerStrategy" 55 | TrafficRouterKind = "traffic.opensergo.io/v1alpha1/TrafficRouter" 56 | ) 57 | 58 | var ( 59 | // crdMetadataMap is the universal registry for all OpenSergo CRDs. 60 | crdMetadataMap = map[CRDKind]*CRDMetadata{ 61 | FaultToleranceRuleKind: NewCRDMetadata(FaultToleranceRuleKind, func() client.Object { 62 | return &v1alpha1.FaultToleranceRule{} 63 | }), 64 | RateLimitStrategyKind: NewCRDMetadata(RateLimitStrategyKind, func() client.Object { 65 | return &v1alpha1.RateLimitStrategy{} 66 | }), 67 | ThrottlingStrategyKind: NewCRDMetadata(ThrottlingStrategyKind, func() client.Object { 68 | return &v1alpha1.ThrottlingStrategy{} 69 | }), 70 | ConcurrencyLimitStrategyKind: NewCRDMetadata(ConcurrencyLimitStrategyKind, func() client.Object { 71 | return &v1alpha1.ConcurrencyLimitStrategy{} 72 | }), 73 | CircuitBreakerStrategyKind: NewCRDMetadata(CircuitBreakerStrategyKind, func() client.Object { 74 | return &v1alpha1.CircuitBreakerStrategy{} 75 | }), 76 | TrafficRouterKind: NewCRDMetadata(TrafficRouterKind, func() client.Object { 77 | return &traffic.TrafficRouter{} 78 | }), 79 | } 80 | ) 81 | 82 | func GetCrdMetadata(kind CRDKind) (*CRDMetadata, bool) { 83 | // TODO: should we put a lock here? 84 | data, exists := crdMetadataMap[kind] 85 | return data, exists 86 | } 87 | -------------------------------------------------------------------------------- /pkg/controller/crd_watcher.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package controller 16 | 17 | import ( 18 | "context" 19 | "log" 20 | "net/http" 21 | "strconv" 22 | "sync" 23 | 24 | "github.com/go-logr/logr" 25 | crdv1alpha1 "github.com/opensergo/opensergo-control-plane/pkg/api/v1alpha1" 26 | crdv1alpha1traffic "github.com/opensergo/opensergo-control-plane/pkg/api/v1alpha1/traffic" 27 | "github.com/opensergo/opensergo-control-plane/pkg/model" 28 | pb "github.com/opensergo/opensergo-control-plane/pkg/proto/fault_tolerance/v1" 29 | trpb "github.com/opensergo/opensergo-control-plane/pkg/proto/transport/v1" 30 | "github.com/opensergo/opensergo-control-plane/pkg/util" 31 | "github.com/pkg/errors" 32 | "google.golang.org/protobuf/proto" 33 | "google.golang.org/protobuf/types/known/anypb" 34 | k8sApiError "k8s.io/apimachinery/pkg/api/errors" 35 | "k8s.io/apimachinery/pkg/runtime" 36 | ctrl "sigs.k8s.io/controller-runtime" 37 | "sigs.k8s.io/controller-runtime/pkg/client" 38 | ) 39 | 40 | // CRDWatcher watches a specific kind of CRD. 41 | type CRDWatcher struct { 42 | kind model.SubscribeKind 43 | 44 | client.Client 45 | logger logr.Logger 46 | scheme *runtime.Scheme 47 | 48 | // crdCache represents associated local cache for current kind of CRD. 49 | crdCache *CRDCache 50 | 51 | // subscribedList consists of all subscribed target of current kind of CRD. 52 | subscribedList map[model.SubscribeTarget]bool 53 | subscribedNamespaces map[string]bool 54 | subscribedApps map[model.NamespacedApp]bool 55 | 56 | crdGenerator func() client.Object 57 | sendDataHandler model.DataEntirePushHandler 58 | 59 | updateMux sync.RWMutex 60 | } 61 | 62 | const ( 63 | UpdateRule = 201 64 | DeleteRule = 202 65 | AddRule = 203 66 | ) 67 | 68 | func (r *CRDWatcher) Kind() model.SubscribeKind { 69 | return r.kind 70 | } 71 | 72 | func (r *CRDWatcher) HasSubscribed(target model.SubscribeTarget) bool { 73 | if target.Kind != r.kind { 74 | return false 75 | } 76 | 77 | r.updateMux.RLock() 78 | defer r.updateMux.RUnlock() 79 | 80 | has, _ := r.subscribedList[target] 81 | return has 82 | } 83 | 84 | func (r *CRDWatcher) AddSubscribeTarget(target model.SubscribeTarget) error { 85 | // TODO: validate the target 86 | if target.Kind != r.kind { 87 | return errors.New("target kind mismatch, expected: " + target.Kind + ", r.kind: " + r.kind) 88 | } 89 | r.updateMux.Lock() 90 | defer r.updateMux.Unlock() 91 | 92 | r.subscribedList[target] = true 93 | r.subscribedNamespaces[target.Namespace] = true 94 | r.subscribedApps[target.NamespacedApp()] = true 95 | 96 | return nil 97 | } 98 | 99 | func (r *CRDWatcher) RemoveSubscribeTarget(target model.SubscribeTarget) error { 100 | // TODO: implement me 101 | 102 | return nil 103 | } 104 | 105 | func (r *CRDWatcher) HasAnySubscribedOfNamespace(namespace string) bool { 106 | r.updateMux.RLock() 107 | defer r.updateMux.RUnlock() 108 | 109 | _, exist := r.subscribedNamespaces[namespace] 110 | return exist 111 | } 112 | 113 | func (r *CRDWatcher) HasAnySubscribedOfApp(app model.NamespacedApp) bool { 114 | r.updateMux.RLock() 115 | defer r.updateMux.RUnlock() 116 | 117 | _, exist := r.subscribedApps[app] 118 | return exist 119 | } 120 | 121 | func (r *CRDWatcher) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { 122 | if !r.HasAnySubscribedOfNamespace(req.Namespace) { 123 | // Ignore unmatched namespace 124 | return ctrl.Result{Requeue: false, RequeueAfter: 0}, nil 125 | } 126 | logger := r.logger.WithValues("crdNamespace", req.Namespace, "crdName", req.Name, "kind", r.kind) 127 | 128 | // your logic here 129 | crd := r.crdGenerator() 130 | if err := r.Get(ctx, req.NamespacedName, crd); err != nil { 131 | k8sApiErr, ok := err.(*k8sApiError.StatusError) 132 | if !ok { 133 | logger.Error(err, "Failed to get OpenSergo CRD") 134 | return ctrl.Result{ 135 | Requeue: false, 136 | RequeueAfter: 0, 137 | }, nil 138 | } 139 | if k8sApiErr.Status().Code != http.StatusNotFound { 140 | logger.Error(err, "Failed to get OpenSergo CRD") 141 | return ctrl.Result{ 142 | Requeue: false, 143 | RequeueAfter: 0, 144 | }, nil 145 | } 146 | 147 | // cr had been deleted 148 | crd = nil 149 | } 150 | 151 | app := "" 152 | if crd != nil { 153 | // TODO: bugs here: we need to check for namespace-app group, not only for app. 154 | // And we may also need to check for namespace change of a CRD. 155 | var hasAppLabel bool 156 | app, hasAppLabel = crd.GetLabels()["app"] 157 | namespacedApp := model.NamespacedApp{Namespace: req.Namespace, App: app} 158 | appSubscribed := r.HasAnySubscribedOfApp(namespacedApp) 159 | if !hasAppLabel || !appSubscribed { 160 | if _, prevContains := r.crdCache.GetByNamespacedName(req.NamespacedName); prevContains { 161 | logger.Info("OpenSergo CRD will be deleted because app label has been changed", "newApp", app) 162 | crd = nil 163 | } else { 164 | // Ignore unmatched app label 165 | return ctrl.Result{ 166 | Requeue: false, 167 | RequeueAfter: 0, 168 | }, nil 169 | } 170 | } else { 171 | logger.Info("OpenSergo CRD received", "crd", crd) 172 | } 173 | r.crdCache.SetByNamespaceApp(namespacedApp, crd) 174 | r.crdCache.SetByNamespacedName(req.NamespacedName, crd) 175 | 176 | } else { 177 | app, _ = r.crdCache.GetAppByNamespacedName(req.NamespacedName) 178 | r.crdCache.DeleteByNamespaceApp(model.NamespacedApp{Namespace: req.Namespace, App: app}, req.Name) 179 | r.crdCache.DeleteByNamespacedName(req.NamespacedName) 180 | logger.Info("OpenSergo CRD will be deleted") 181 | } 182 | 183 | nsa := model.NamespacedApp{ 184 | Namespace: req.Namespace, 185 | App: app, 186 | } 187 | // TODO: Now we can do something for the crd object! 188 | rules, version := r.GetRules(nsa) 189 | status := &trpb.Status{ 190 | Code: int32(200), 191 | Message: "Get and send rule success", 192 | Details: nil, 193 | } 194 | dataWithVersion := &trpb.DataWithVersion{Data: rules, Version: version} 195 | err := r.sendDataHandler(req.Namespace, app, r.kind, dataWithVersion, status, "") 196 | if err != nil { 197 | logger.Error(err, "Failed to send rules", "kind", r.kind) 198 | } 199 | return ctrl.Result{}, nil 200 | } 201 | 202 | func (r *CRDWatcher) GetRules(n model.NamespacedApp) ([]*anypb.Any, int64) { 203 | var rules []*anypb.Any 204 | objs, version := r.crdCache.GetByNamespaceApp(n) 205 | for _, obj := range objs { 206 | if obj == nil { 207 | continue 208 | } 209 | rule, err := r.translateCrdToProto(obj) 210 | if err != nil { 211 | return rules, version 212 | } 213 | rules = append(rules, rule) 214 | } 215 | return rules, version 216 | } 217 | 218 | func (r *CRDWatcher) SetupWithManager(mgr ctrl.Manager) error { 219 | return ctrl.NewControllerManagedBy(mgr).For(r.crdGenerator()).Complete(r) 220 | } 221 | 222 | func (r *CRDWatcher) translateCrdToProto(object client.Object) (*anypb.Any, error) { 223 | var packRule *anypb.Any 224 | var err error 225 | var rule proto.Message 226 | switch r.kind { 227 | case FaultToleranceRuleKind: 228 | ftr := object.(*crdv1alpha1.FaultToleranceRule) 229 | var targets []*pb.FaultToleranceRule_FaultToleranceRuleTargetRef 230 | var strategies []*pb.FaultToleranceRule_FaultToleranceStrategyRef 231 | if ftr != nil { 232 | for _, target := range ftr.Spec.Targets { 233 | targets = append(targets, &pb.FaultToleranceRule_FaultToleranceRuleTargetRef{TargetResourceName: target.TargetResourceName}) 234 | } 235 | for _, strategy := range ftr.Spec.Strategies { 236 | strategies = append(strategies, &pb.FaultToleranceRule_FaultToleranceStrategyRef{ 237 | Name: strategy.Name, 238 | Kind: strategy.Kind, 239 | }) 240 | } 241 | } 242 | rule = &pb.FaultToleranceRule{ 243 | Targets: targets, 244 | Strategies: strategies, 245 | Action: nil, 246 | } 247 | 248 | case RateLimitStrategyKind: 249 | rls := object.(*crdv1alpha1.RateLimitStrategy) 250 | mType, _ := strconv.ParseInt(rls.Spec.MetricType, 10, 32) 251 | limitMode, _ := strconv.ParseInt(rls.Spec.LimitMode, 10, 32) 252 | rule = &pb.RateLimitStrategy{ 253 | Name: rls.Name, 254 | MetricType: pb.RateLimitStrategy_MetricType(mType), 255 | LimitMode: pb.RateLimitStrategy_LimitMode(limitMode), 256 | Threshold: rls.Spec.Threshold, 257 | StatDuration: rls.Spec.StatDurationSeconds, 258 | //StatDurationTimeUnit: 2, //todo 2应该为参数 259 | } 260 | case ThrottlingStrategyKind: 261 | ts := object.(*crdv1alpha1.ThrottlingStrategy) 262 | miMill, err := util.Str2MillSeconds(ts.Spec.MinIntervalOfRequests) 263 | if err != nil { 264 | miMill = -1 265 | log.Println("translate to MinIntervalMillisOfRequests error, ", err) 266 | } 267 | qtMill, err := util.Str2MillSeconds(ts.Spec.QueueTimeout) 268 | if err != nil { 269 | qtMill = -1 270 | log.Println("translate to QueueTimeoutMillis error, ", err) 271 | } 272 | rule = &pb.ThrottlingStrategy{ 273 | Name: ts.Name, 274 | MinIntervalMillisOfRequests: miMill, 275 | QueueTimeoutMillis: qtMill, 276 | } 277 | case CircuitBreakerStrategyKind: 278 | cbs := object.(*crdv1alpha1.CircuitBreakerStrategy) 279 | tr, err := util.RatioStr2Float(cbs.Spec.TriggerRatio) 280 | if err != nil { 281 | tr = -1.0 282 | log.Println("translate to TriggerRatio error, ", err) 283 | } 284 | sdMill, err := util.Str2MillSeconds(cbs.Spec.StatDuration) 285 | if err != nil { 286 | sdMill = -1 287 | } 288 | rtMill, err := util.Str2MillSeconds(cbs.Spec.RecoveryTimeout) 289 | if err != nil { 290 | rtMill = -1 291 | } 292 | maMill, err := util.Str2MillSeconds(cbs.Spec.SlowConditions.MaxAllowedRt) 293 | if err != nil { 294 | maMill = -1 295 | } 296 | 297 | rule = &pb.CircuitBreakerStrategy{ 298 | Name: cbs.Name, 299 | Strategy: util.Str2CBStrategy(cbs.Spec.Strategy), 300 | TriggerRatio: tr, 301 | StatDuration: sdMill, // todo int64 or int32 302 | StatDurationTimeUnit: 0, 303 | RecoveryTimeout: int32(rtMill), 304 | RecoveryTimeoutTimeUnit: 0, 305 | MinRequestAmount: cbs.Spec.MinRequestAmount, 306 | SlowCondition: &pb.CircuitBreakerStrategy_CircuitBreakerSlowCondition{MaxAllowedRtMillis: int32(maMill)}, 307 | ErrorCondition: nil, 308 | } 309 | case ConcurrencyLimitStrategyKind: 310 | cls := object.(*crdv1alpha1.ConcurrencyLimitStrategy) 311 | rule = &pb.ConcurrencyLimitStrategy{ 312 | Name: cls.Name, 313 | LimitMode: util.Str2LimitNode(cls.Spec.LimitMode), 314 | MaxConcurrency: cls.Spec.MaxConcurrencyThreshold, 315 | } 316 | case TrafficRouterKind: 317 | cls := object.(*crdv1alpha1traffic.TrafficRouter) 318 | rule = BuildRouteConfiguration(cls) 319 | default: 320 | return nil, nil 321 | } 322 | packRule, err = anypb.New(rule) 323 | if err != nil { 324 | log.Println("pack rule error", err) 325 | return nil, err 326 | } 327 | return packRule, nil 328 | 329 | } 330 | 331 | func NewCRDWatcher(crdManager ctrl.Manager, kind model.SubscribeKind, crdGenerator func() client.Object, sendDataHandler model.DataEntirePushHandler) *CRDWatcher { 332 | return &CRDWatcher{ 333 | kind: kind, 334 | Client: crdManager.GetClient(), 335 | logger: ctrl.Log.WithName("controller").WithName(kind), 336 | scheme: crdManager.GetScheme(), 337 | subscribedList: make(map[model.SubscribeTarget]bool, 4), 338 | subscribedNamespaces: make(map[string]bool), 339 | subscribedApps: make(map[model.NamespacedApp]bool), 340 | crdGenerator: crdGenerator, 341 | crdCache: NewCRDCache(kind), 342 | sendDataHandler: sendDataHandler, 343 | } 344 | } 345 | -------------------------------------------------------------------------------- /pkg/controller/k8s_operator.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package controller 16 | 17 | import ( 18 | "context" 19 | "sync" 20 | "sync/atomic" 21 | 22 | "github.com/alibaba/sentinel-golang/logging" 23 | "github.com/alibaba/sentinel-golang/util" 24 | crdv1alpha1 "github.com/opensergo/opensergo-control-plane/pkg/api/v1alpha1" 25 | crdv1alpha1traffic "github.com/opensergo/opensergo-control-plane/pkg/api/v1alpha1/traffic" 26 | "github.com/opensergo/opensergo-control-plane/pkg/model" 27 | "github.com/pkg/errors" 28 | "k8s.io/apimachinery/pkg/runtime" 29 | clientgoscheme "k8s.io/client-go/kubernetes/scheme" 30 | _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" 31 | ctrl "sigs.k8s.io/controller-runtime" 32 | // +kubebuilder:scaffold:imports 33 | ) 34 | 35 | var ( 36 | scheme = runtime.NewScheme() 37 | setupLog = ctrl.Log.WithName("setup") 38 | ) 39 | 40 | func init() { 41 | _ = clientgoscheme.AddToScheme(scheme) 42 | 43 | _ = crdv1alpha1.AddToScheme(scheme) 44 | // +kubebuilder:scaffold:scheme 45 | 46 | _ = crdv1alpha1traffic.AddToScheme(scheme) 47 | // +kubebuilder:scaffold:scheme 48 | } 49 | 50 | type CRDType int32 51 | 52 | const ( 53 | FaultToleranceRuleCRDType CRDType = iota 54 | RateLimitStrategyCRDType 55 | ) 56 | 57 | func (c CRDType) String() string { 58 | switch c { 59 | case FaultToleranceRuleCRDType: 60 | return "fault-tolerance.opensergo.io/v1alpha1/FaultToleranceRule" 61 | case RateLimitStrategyCRDType: 62 | return "fault-tolerance.opensergo.io/v1alpha1/RateLimitStrategy" 63 | default: 64 | return "Undefined" 65 | } 66 | } 67 | 68 | type KubernetesOperator struct { 69 | crdManager ctrl.Manager 70 | controllers map[string]*CRDWatcher 71 | ctx context.Context 72 | ctxCancel context.CancelFunc 73 | started atomic.Value 74 | 75 | sendDataHandler model.DataEntirePushHandler 76 | 77 | controllerMux sync.RWMutex 78 | } 79 | 80 | // NewKubernetesOperator creates a OpenSergo Kubernetes operator. 81 | func NewKubernetesOperator(sendDataHandler model.DataEntirePushHandler) (*KubernetesOperator, error) { 82 | ctrl.SetLogger(&k8SLogger{ 83 | l: logging.GetGlobalLogger(), 84 | level: logging.GetGlobalLoggerLevel(), 85 | names: make([]string, 0), 86 | keysAndValues: make([]interface{}, 0), 87 | }) 88 | k8sConfig, err := ctrl.GetConfig() 89 | if err != nil { 90 | return nil, err 91 | } 92 | mgr, err := ctrl.NewManager(k8sConfig, ctrl.Options{ 93 | Scheme: scheme, 94 | // disable metric server 95 | MetricsBindAddress: "0", 96 | HealthProbeBindAddress: "0", 97 | LeaderElection: false, 98 | }) 99 | if err != nil { 100 | setupLog.Error(err, "unable to start manager") 101 | return nil, err 102 | } 103 | ctx, cancel := context.WithCancel(context.Background()) 104 | k := &KubernetesOperator{ 105 | crdManager: mgr, 106 | controllers: make(map[string]*CRDWatcher), 107 | ctx: ctx, 108 | ctxCancel: cancel, 109 | sendDataHandler: sendDataHandler, 110 | } 111 | return k, nil 112 | } 113 | 114 | func (k *KubernetesOperator) RegisterControllersAndStart(info model.SubscribeTarget) error { 115 | _, err := k.RegisterWatcher(info) 116 | if err != nil { 117 | return err 118 | } 119 | return k.Run() 120 | } 121 | 122 | // RegisterWatcher registers given CRD type and CRD name. 123 | // For each CRD type, it can be registered only once. 124 | func (k *KubernetesOperator) RegisterWatcher(target model.SubscribeTarget) (*CRDWatcher, error) { 125 | k.controllerMux.Lock() 126 | defer k.controllerMux.Unlock() 127 | 128 | var err error 129 | 130 | existingWatcher, exists := k.controllers[target.Kind] 131 | if exists { 132 | if existingWatcher.HasSubscribed(target) { 133 | // Target has been subscribed 134 | return existingWatcher, nil 135 | } else { 136 | // Add subscribe to existing watcher 137 | err = existingWatcher.AddSubscribeTarget(target) 138 | if err != nil { 139 | return nil, err 140 | } 141 | } 142 | } else { 143 | crdMetadata, crdSupports := GetCrdMetadata(target.Kind) 144 | if !crdSupports { 145 | return nil, errors.New("CRD not supported: " + target.Kind) 146 | } 147 | // This kind of CRD has never been watched. 148 | crdWatcher := NewCRDWatcher(k.crdManager, target.Kind, crdMetadata.Generator(), k.sendDataHandler) 149 | err = crdWatcher.AddSubscribeTarget(target) 150 | if err != nil { 151 | return nil, err 152 | } 153 | err = crdWatcher.SetupWithManager(k.crdManager) 154 | if err != nil { 155 | return nil, err 156 | } 157 | k.controllers[target.Kind] = crdWatcher 158 | } 159 | setupLog.Info("OpenSergo CRD watcher has been registered successfully", "kind", target.Kind, "namespace", target.Namespace, "app", target.AppName) 160 | return k.controllers[target.Kind], nil 161 | } 162 | 163 | func (k *KubernetesOperator) AddWatcher(target model.SubscribeTarget) error { 164 | k.controllerMux.Lock() 165 | defer k.controllerMux.Unlock() 166 | 167 | var err error 168 | 169 | existingWatcher, exists := k.controllers[target.Kind] 170 | if exists && !existingWatcher.HasSubscribed(target) { 171 | // TODO: think more about here 172 | err = existingWatcher.AddSubscribeTarget(target) 173 | if err != nil { 174 | return err 175 | } 176 | } else { 177 | crdMetadata, crdSupports := GetCrdMetadata(target.Kind) 178 | if !crdSupports { 179 | return errors.New("CRD not supported: " + target.Kind) 180 | } 181 | crdWatcher := NewCRDWatcher(k.crdManager, target.Kind, crdMetadata.Generator(), k.sendDataHandler) 182 | err = crdWatcher.AddSubscribeTarget(target) 183 | if err != nil { 184 | return err 185 | } 186 | 187 | crdRunnable, err := ctrl.NewControllerManagedBy(k.crdManager).For(crdMetadata.Generator()()).Build(crdWatcher) 188 | if err != nil { 189 | return err 190 | } 191 | err = k.crdManager.Add(crdRunnable) 192 | if err != nil { 193 | return err 194 | } 195 | // _ = crdRunnable.Start(k.ctx) 196 | k.controllers[target.Kind] = crdWatcher 197 | 198 | } 199 | setupLog.Info("OpenSergo CRD watcher has been added successfully") 200 | return nil 201 | } 202 | 203 | // Close exit the K8S KubernetesOperator 204 | func (k *KubernetesOperator) Close() error { 205 | k.ctxCancel() 206 | return nil 207 | } 208 | 209 | func (k *KubernetesOperator) ComponentName() string { 210 | return "OpenSergoKubernetesOperator" 211 | } 212 | 213 | // Run runs the k8s KubernetesOperator 214 | func (k *KubernetesOperator) Run() error { 215 | 216 | // +kubebuilder:scaffold:builder 217 | go util.RunWithRecover(func() { 218 | setupLog.Info("Starting OpenSergo operator") 219 | if err := k.crdManager.Start(k.ctx); err != nil { 220 | setupLog.Error(err, "problem running OpenSergo operator") 221 | } 222 | setupLog.Info("OpenSergo operator will be closed") 223 | }) 224 | return nil 225 | } 226 | 227 | func (k *KubernetesOperator) GetWatcher(kind string) (*CRDWatcher, bool) { 228 | k.controllerMux.RLock() 229 | defer k.controllerMux.RUnlock() 230 | watcher, exists := k.controllers[kind] 231 | return watcher, exists 232 | } 233 | -------------------------------------------------------------------------------- /pkg/controller/log_adapter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package controller 16 | 17 | import ( 18 | "strings" 19 | 20 | "github.com/alibaba/sentinel-golang/logging" 21 | "github.com/go-logr/logr" 22 | ) 23 | 24 | // noopLogger is a logr.Logger that's always disabled, and does nothing. 25 | type noopLogger struct{} 26 | 27 | func (l *noopLogger) Enabled() bool { return false } 28 | func (l *noopLogger) Info(_ string, _ ...interface{}) {} 29 | func (l *noopLogger) Error(_ error, _ string, _ ...interface{}) {} 30 | func (l *noopLogger) V(_ int) logr.Logger { return l } 31 | func (l *noopLogger) WithValues(_ ...interface{}) logr.Logger { return l } 32 | func (l *noopLogger) WithName(_ string) logr.Logger { return l } 33 | 34 | var disabledLogger = &noopLogger{} 35 | 36 | type k8SLogger struct { 37 | l logging.Logger 38 | level logging.Level 39 | names []string 40 | keysAndValues []interface{} 41 | } 42 | 43 | func (k *k8SLogger) buildNames() string { 44 | size := len(k.names) 45 | if size == 0 { 46 | return "" 47 | } 48 | sb := strings.Builder{} 49 | for i, name := range k.names { 50 | sb.WriteString(name) 51 | if i == size-1 { 52 | continue 53 | } 54 | sb.WriteString(".") 55 | } 56 | sb.WriteString(" ") 57 | return sb.String() 58 | } 59 | 60 | func (k *k8SLogger) Info(msg string, keysAndValues ...interface{}) { 61 | keysAndValues = append(keysAndValues, k.keysAndValues...) 62 | switch k.level { 63 | case logging.WarnLevel: 64 | k.l.Warn(k.buildNames()+msg, keysAndValues...) 65 | case logging.InfoLevel: 66 | k.l.Info(k.buildNames()+msg, keysAndValues...) 67 | case logging.DebugLevel: 68 | k.l.Debug(k.buildNames()+msg, keysAndValues...) 69 | default: 70 | k.l.Info(k.buildNames()+msg, keysAndValues...) 71 | } 72 | } 73 | 74 | func (k *k8SLogger) Enabled() bool { 75 | return true 76 | } 77 | 78 | func (k *k8SLogger) Error(err error, msg string, keysAndValues ...interface{}) { 79 | keysAndValues = append(keysAndValues, k.keysAndValues...) 80 | k.l.Error(err, k.buildNames()+msg, keysAndValues...) 81 | } 82 | 83 | func (k *k8SLogger) V(level int) logr.Logger { 84 | if k.Enabled() { 85 | names := make([]string, len(k.names)) 86 | copy(names, k.names) 87 | kvs := make([]interface{}, len(k.keysAndValues)) 88 | copy(kvs, k.keysAndValues) 89 | return &k8SLogger{ 90 | l: k.l, 91 | level: logging.Level(level), 92 | names: names, 93 | keysAndValues: kvs, 94 | } 95 | } 96 | return disabledLogger 97 | } 98 | 99 | func (k *k8SLogger) WithValues(keysAndValues ...interface{}) logr.Logger { 100 | names := make([]string, len(k.names)) 101 | copy(names, k.names) 102 | kvs := make([]interface{}, len(k.keysAndValues)) 103 | copy(kvs, k.keysAndValues) 104 | kvs = append(kvs, keysAndValues...) 105 | return &k8SLogger{ 106 | l: k.l, 107 | level: k.level, 108 | names: names, 109 | keysAndValues: kvs, 110 | } 111 | } 112 | 113 | func (k *k8SLogger) WithName(name string) logr.Logger { 114 | names := make([]string, len(k.names)) 115 | copy(names, k.names) 116 | names = append(names, name) 117 | kvs := make([]interface{}, len(k.keysAndValues)) 118 | copy(kvs, k.keysAndValues) 119 | return &k8SLogger{ 120 | l: k.l, 121 | level: k.level, 122 | names: names, 123 | keysAndValues: kvs, 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /pkg/controller/traffic_router_transform.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package controller 16 | 17 | import ( 18 | corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" 19 | routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" 20 | matcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" 21 | "github.com/golang/protobuf/ptypes/wrappers" 22 | "github.com/opensergo/opensergo-control-plane/pkg/api/v1alpha1/traffic" 23 | route "github.com/opensergo/opensergo-control-plane/pkg/proto/router/v1" 24 | "github.com/opensergo/opensergo-control-plane/pkg/util" 25 | ) 26 | 27 | const ( 28 | EXTENSION_ROUTE_FALL_BACK = "envoy.router.cluster_specifier_plugin.cluster_fallback" 29 | ) 30 | 31 | // BuildRouteConfiguration for Istio RouteConfiguration 32 | func BuildRouteConfiguration(cls *traffic.TrafficRouter) *routev3.RouteConfiguration { 33 | virtualHost := &routev3.VirtualHost{ 34 | Name: cls.Name, 35 | Routes: []*routev3.Route{}, 36 | } 37 | virtualHost.Routes = append(virtualHost.Routes, buildHTTPRoutes(cls)...) 38 | for _, domain := range cls.Spec.Hosts { 39 | virtualHost.Domains = append(virtualHost.Domains, buildFQDN(domain, cls.Namespace)) 40 | } 41 | rule := &routev3.RouteConfiguration{ 42 | Name: cls.Name, 43 | VirtualHosts: []*routev3.VirtualHost{virtualHost}, 44 | } 45 | return rule 46 | } 47 | 48 | func buildHTTPRoutes(tr *traffic.TrafficRouter) []*routev3.Route { 49 | var routes []*routev3.Route 50 | for _, httpRoute := range tr.Spec.Http { 51 | r := &routev3.Route{ 52 | Match: &routev3.RouteMatch{ 53 | Headers: buildHeaderMatchers(httpRoute.Match), 54 | QueryParameters: buildParamMatchers(httpRoute.Match), 55 | }, 56 | Action: &routev3.Route_Route{ 57 | Route: buildRouteAction(httpRoute, tr), 58 | }, 59 | } 60 | routes = append(routes, r) 61 | } 62 | return routes 63 | } 64 | 65 | func buildUnweightedRouteAction(destination *traffic.HTTPRouteDestination, vs *traffic.TrafficRouter) *routev3.RouteAction { 66 | if destination.Destination.Fallback != nil { 67 | return &routev3.RouteAction{ 68 | ClusterSpecifier: &routev3.RouteAction_InlineClusterSpecifierPlugin{ 69 | InlineClusterSpecifierPlugin: buildClusterSpecifierPlugin(true, buildClusterFallbackConfig(buildRouteActionCluster(destination.Destination.Host, vs.Namespace, destination.Destination.Subset), buildRouterFallbackActionCluster(destination.Destination.Fallback, vs.Namespace))), 70 | }, 71 | } 72 | } else { 73 | return &routev3.RouteAction{ 74 | ClusterSpecifier: &routev3.RouteAction_Cluster{Cluster: buildRouteActionCluster(destination.Destination.Host, vs.Namespace, destination.Destination.Subset)}, 75 | } 76 | } 77 | } 78 | 79 | func buildWeightedRouteAction(destinations []*traffic.HTTPRouteDestination, vs *traffic.TrafficRouter) *routev3.RouteAction { 80 | return &routev3.RouteAction{ 81 | ClusterSpecifier: &routev3.RouteAction_WeightedClusters{ 82 | WeightedClusters: &routev3.WeightedCluster{ 83 | Clusters: buildWeightedClusters(vs.Namespace, destinations), 84 | }, 85 | }, 86 | } 87 | } 88 | 89 | func buildWeightedClusters(namespace string, destinations []*traffic.HTTPRouteDestination) []*routev3.WeightedCluster_ClusterWeight { 90 | var weightedClusters []*routev3.WeightedCluster_ClusterWeight 91 | for _, destination := range destinations { 92 | w := &routev3.WeightedCluster_ClusterWeight{ 93 | Name: buildRouteActionCluster(destination.Destination.Host, namespace, destination.Destination.Subset), 94 | Weight: &wrappers.UInt32Value{Value: uint32(destination.Weight)}, 95 | } 96 | weightedClusters = append(weightedClusters, w) 97 | } 98 | return weightedClusters 99 | } 100 | 101 | func buildRouteAction(httpRoute *traffic.HTTPRoute, vs *traffic.TrafficRouter) *routev3.RouteAction { 102 | if len(httpRoute.Route) == 1 { 103 | // unweighted 104 | return buildUnweightedRouteAction(httpRoute.Route[0], vs) 105 | } else { 106 | // weighted 107 | return buildWeightedRouteAction(httpRoute.Route, vs) 108 | } 109 | } 110 | 111 | func buildRouterFallbackActionCluster(fallback *traffic.Fallback, namespace string) string { 112 | if fallback == nil { 113 | return "" 114 | } 115 | 116 | return buildRouteActionCluster(fallback.Host, namespace, fallback.Subset) 117 | } 118 | 119 | func buildClusterFallbackConfig(cluster string, fallbackCluster string) *route.ClusterFallbackConfig_ClusterConfig { 120 | return &route.ClusterFallbackConfig_ClusterConfig{ 121 | RoutingCluster: cluster, 122 | FallbackCluster: fallbackCluster, 123 | } 124 | } 125 | 126 | func buildClusterSpecifierPlugin(isSupport bool, config *route.ClusterFallbackConfig_ClusterConfig) *routev3.ClusterSpecifierPlugin { 127 | if !isSupport || config == nil { 128 | return nil 129 | } 130 | 131 | return &routev3.ClusterSpecifierPlugin{ 132 | Extension: &corev3.TypedExtensionConfig{ 133 | Name: EXTENSION_ROUTE_FALL_BACK, 134 | TypedConfig: util.MessageToAny(config), 135 | }, 136 | } 137 | } 138 | 139 | func buildRouteActionCluster(serviceName, namespace, version string) string { 140 | return "outbound|" + "|" + version + "|" + buildFQDN(serviceName, namespace) 141 | } 142 | 143 | func buildParamMatchers(matches []*traffic.HTTPMatchRequest) []*routev3.QueryParameterMatcher { 144 | var queryParamMatchers []*routev3.QueryParameterMatcher 145 | for _, match := range matches { 146 | for _, matcher := range match.QueryParams { 147 | queryMatcher := &routev3.QueryParameterMatcher{} 148 | if matcher.GetRegex() != "" { 149 | queryMatcher.QueryParameterMatchSpecifier = &routev3.QueryParameterMatcher_StringMatch{ 150 | StringMatch: &matcherv3.StringMatcher{ 151 | MatchPattern: &matcherv3.StringMatcher_SafeRegex{ 152 | SafeRegex: &matcherv3.RegexMatcher{ 153 | Regex: matcher.GetRegex(), 154 | }, 155 | }, 156 | }} 157 | } 158 | if matcher.GetPrefix() != "" { 159 | queryMatcher.QueryParameterMatchSpecifier = &routev3.QueryParameterMatcher_StringMatch{ 160 | StringMatch: &matcherv3.StringMatcher{ 161 | MatchPattern: &matcherv3.StringMatcher_Prefix{ 162 | Prefix: matcher.GetPrefix(), 163 | }, 164 | }} 165 | } 166 | if matcher.GetExact() != "" { 167 | queryMatcher.QueryParameterMatchSpecifier = &routev3.QueryParameterMatcher_StringMatch{ 168 | StringMatch: &matcherv3.StringMatcher{ 169 | MatchPattern: &matcherv3.StringMatcher_Exact{ 170 | Exact: matcher.GetExact(), 171 | }, 172 | }} 173 | } 174 | queryParamMatchers = append(queryParamMatchers, queryMatcher) 175 | } 176 | } 177 | return queryParamMatchers 178 | } 179 | 180 | func buildHeaderMatchers(matches []*traffic.HTTPMatchRequest) []*routev3.HeaderMatcher { 181 | var headerMatchers []*routev3.HeaderMatcher 182 | for _, match := range matches { 183 | for key, matcher := range match.Headers { 184 | headerMatcher := &routev3.HeaderMatcher{ 185 | Name: key, 186 | } 187 | if matcher.GetRegex() != "" { 188 | headerMatcher.HeaderMatchSpecifier = &routev3.HeaderMatcher_SafeRegexMatch{SafeRegexMatch: &matcherv3.RegexMatcher{Regex: matcher.GetRegex()}} 189 | } 190 | if matcher.GetPrefix() != "" { 191 | headerMatcher.HeaderMatchSpecifier = &routev3.HeaderMatcher_PrefixMatch{PrefixMatch: matcher.GetPrefix()} 192 | } 193 | if matcher.GetExact() != "" { 194 | headerMatcher.HeaderMatchSpecifier = &routev3.HeaderMatcher_ExactMatch{ExactMatch: matcher.GetExact()} 195 | } 196 | headerMatchers = append(headerMatchers, headerMatcher) 197 | } 198 | } 199 | return headerMatchers 200 | } 201 | 202 | func buildFQDN(serviceName, namespace string) string { 203 | return serviceName + "." + namespace + ".svc.cluster.local" 204 | } 205 | -------------------------------------------------------------------------------- /pkg/main/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "log" 19 | 20 | "github.com/opensergo/opensergo-control-plane" 21 | ) 22 | 23 | func main() { 24 | cp, err := opensergo.NewControlPlane() 25 | if err != nil { 26 | log.Fatal(err) 27 | } 28 | err = cp.Start() 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /pkg/model/model.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package model 16 | 17 | import ( 18 | trpb "github.com/opensergo/opensergo-control-plane/pkg/proto/transport/v1" 19 | ) 20 | 21 | type NamespacedApp struct { 22 | Namespace string 23 | App string 24 | } 25 | 26 | // ClientIdentifier represents a unique identifier for an OpenSergo client. 27 | type ClientIdentifier string 28 | 29 | type OpenSergoTransportStream = trpb.OpenSergoUniversalTransportService_SubscribeConfigServer 30 | 31 | type SubscribeRequestHandler func(ClientIdentifier, *trpb.SubscribeRequest, OpenSergoTransportStream) error 32 | 33 | type DataEntirePushHandler func(namespace, app, kind string, dataWithVersion *trpb.DataWithVersion, status *trpb.Status, respId string) error 34 | -------------------------------------------------------------------------------- /pkg/model/subscribe.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package model 16 | 17 | type LabelKV struct { 18 | Key string 19 | Value string 20 | } 21 | 22 | type SubscribeKind = string 23 | 24 | type SubscribeTarget struct { 25 | Namespace string 26 | // AppName represents the target app name. Fast path for "app" label. 27 | AppName string 28 | // Labels []LabelKV 29 | Kind SubscribeKind 30 | } 31 | 32 | func (st SubscribeTarget) NamespacedApp() NamespacedApp { 33 | return NamespacedApp{st.Namespace, st.AppName} 34 | } 35 | 36 | type Instance struct { 37 | ip string 38 | serviceType string 39 | } 40 | -------------------------------------------------------------------------------- /pkg/proto/common/v1/common.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go v1.28.0 4 | // protoc v3.19.4 5 | // source: common/v1/common.proto 6 | 7 | package v1 8 | 9 | import ( 10 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 11 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 12 | reflect "reflect" 13 | sync "sync" 14 | ) 15 | 16 | const ( 17 | // Verify that this generated code is sufficiently up-to-date. 18 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 19 | // Verify that runtime/protoimpl is sufficiently up-to-date. 20 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 21 | ) 22 | 23 | type TimeUnit int32 24 | 25 | const ( 26 | // The time unit is not known. 27 | TimeUnit_UNKNOWN TimeUnit = 0 28 | TimeUnit_MILLISECOND TimeUnit = 1 29 | TimeUnit_SECOND TimeUnit = 2 30 | TimeUnit_MINUTE TimeUnit = 3 31 | TimeUnit_HOUR TimeUnit = 4 32 | TimeUnit_DAY TimeUnit = 5 33 | ) 34 | 35 | // Enum value maps for TimeUnit. 36 | var ( 37 | TimeUnit_name = map[int32]string{ 38 | 0: "UNKNOWN", 39 | 1: "MILLISECOND", 40 | 2: "SECOND", 41 | 3: "MINUTE", 42 | 4: "HOUR", 43 | 5: "DAY", 44 | } 45 | TimeUnit_value = map[string]int32{ 46 | "UNKNOWN": 0, 47 | "MILLISECOND": 1, 48 | "SECOND": 2, 49 | "MINUTE": 3, 50 | "HOUR": 4, 51 | "DAY": 5, 52 | } 53 | ) 54 | 55 | func (x TimeUnit) Enum() *TimeUnit { 56 | p := new(TimeUnit) 57 | *p = x 58 | return p 59 | } 60 | 61 | func (x TimeUnit) String() string { 62 | return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) 63 | } 64 | 65 | func (TimeUnit) Descriptor() protoreflect.EnumDescriptor { 66 | return file_common_v1_common_proto_enumTypes[0].Descriptor() 67 | } 68 | 69 | func (TimeUnit) Type() protoreflect.EnumType { 70 | return &file_common_v1_common_proto_enumTypes[0] 71 | } 72 | 73 | func (x TimeUnit) Number() protoreflect.EnumNumber { 74 | return protoreflect.EnumNumber(x) 75 | } 76 | 77 | // Deprecated: Use TimeUnit.Descriptor instead. 78 | func (TimeUnit) EnumDescriptor() ([]byte, []int) { 79 | return file_common_v1_common_proto_rawDescGZIP(), []int{0} 80 | } 81 | 82 | var File_common_v1_common_proto protoreflect.FileDescriptor 83 | 84 | var file_common_v1_common_proto_rawDesc = []byte{ 85 | 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 86 | 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x69, 0x6f, 0x2e, 0x6f, 0x70, 0x65, 87 | 0x6e, 0x73, 0x65, 0x72, 0x67, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 88 | 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2a, 0x53, 0x0a, 0x08, 0x54, 0x69, 0x6d, 0x65, 0x55, 0x6e, 89 | 0x69, 0x74, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 90 | 0x0f, 0x0a, 0x0b, 0x4d, 0x49, 0x4c, 0x4c, 0x49, 0x53, 0x45, 0x43, 0x4f, 0x4e, 0x44, 0x10, 0x01, 91 | 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x45, 0x43, 0x4f, 0x4e, 0x44, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 92 | 0x4d, 0x49, 0x4e, 0x55, 0x54, 0x45, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x4f, 0x55, 0x52, 93 | 0x10, 0x04, 0x12, 0x07, 0x0a, 0x03, 0x44, 0x41, 0x59, 0x10, 0x05, 0x42, 0x6b, 0x0a, 0x1c, 0x69, 94 | 0x6f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x65, 0x72, 0x67, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 95 | 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x42, 0x0b, 0x43, 0x6f, 0x6d, 96 | 0x6d, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3c, 0x67, 0x69, 0x74, 0x68, 97 | 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x65, 0x72, 0x67, 0x6f, 98 | 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x65, 0x72, 0x67, 0x6f, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 99 | 0x6f, 0x6c, 0x2d, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 100 | 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 101 | } 102 | 103 | var ( 104 | file_common_v1_common_proto_rawDescOnce sync.Once 105 | file_common_v1_common_proto_rawDescData = file_common_v1_common_proto_rawDesc 106 | ) 107 | 108 | func file_common_v1_common_proto_rawDescGZIP() []byte { 109 | file_common_v1_common_proto_rawDescOnce.Do(func() { 110 | file_common_v1_common_proto_rawDescData = protoimpl.X.CompressGZIP(file_common_v1_common_proto_rawDescData) 111 | }) 112 | return file_common_v1_common_proto_rawDescData 113 | } 114 | 115 | var file_common_v1_common_proto_enumTypes = make([]protoimpl.EnumInfo, 1) 116 | var file_common_v1_common_proto_goTypes = []interface{}{ 117 | (TimeUnit)(0), // 0: io.opensergo.proto.common.v1.TimeUnit 118 | } 119 | var file_common_v1_common_proto_depIdxs = []int32{ 120 | 0, // [0:0] is the sub-list for method output_type 121 | 0, // [0:0] is the sub-list for method input_type 122 | 0, // [0:0] is the sub-list for extension type_name 123 | 0, // [0:0] is the sub-list for extension extendee 124 | 0, // [0:0] is the sub-list for field type_name 125 | } 126 | 127 | func init() { file_common_v1_common_proto_init() } 128 | func file_common_v1_common_proto_init() { 129 | if File_common_v1_common_proto != nil { 130 | return 131 | } 132 | type x struct{} 133 | out := protoimpl.TypeBuilder{ 134 | File: protoimpl.DescBuilder{ 135 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 136 | RawDescriptor: file_common_v1_common_proto_rawDesc, 137 | NumEnums: 1, 138 | NumMessages: 0, 139 | NumExtensions: 0, 140 | NumServices: 0, 141 | }, 142 | GoTypes: file_common_v1_common_proto_goTypes, 143 | DependencyIndexes: file_common_v1_common_proto_depIdxs, 144 | EnumInfos: file_common_v1_common_proto_enumTypes, 145 | }.Build() 146 | File_common_v1_common_proto = out.File 147 | file_common_v1_common_proto_rawDesc = nil 148 | file_common_v1_common_proto_goTypes = nil 149 | file_common_v1_common_proto_depIdxs = nil 150 | } 151 | -------------------------------------------------------------------------------- /pkg/proto/common/v1/common.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package io.opensergo.proto.common.v1; 4 | 5 | option java_package = "io.opensergo.proto.common.v1"; 6 | option java_outer_classname = "CommonProto"; 7 | option java_multiple_files = true; 8 | option go_package = "github.com/opensergo/opensergo-control-plane/proto/common/v1"; 9 | 10 | enum TimeUnit { 11 | // The time unit is not known. 12 | UNKNOWN = 0; 13 | 14 | MILLISECOND = 1; 15 | SECOND = 2; 16 | MINUTE = 3; 17 | HOUR = 4; 18 | DAY = 5; 19 | } -------------------------------------------------------------------------------- /pkg/proto/fault_tolerance/v1/fault_tolerance.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package io.opensergo.proto.fault_tolerance.v1; 18 | 19 | option java_package = "io.opensergo.proto.fault_tolerance.v1"; 20 | option java_outer_classname = "FaultToleranceProto"; 21 | option java_multiple_files = true; 22 | option go_package = "github.com/opensergo/opensergo-control-plane/proto/fault_tolerance/v1"; 23 | 24 | import "common/v1/common.proto"; 25 | 26 | import "validate/v1/validate.proto"; 27 | 28 | // FaultToleranceRule 29 | message FaultToleranceRule { 30 | message FaultToleranceRuleTargetRef { 31 | string target_resource_name = 1; 32 | } 33 | 34 | message FaultToleranceStrategyRef { 35 | string name = 1; 36 | string kind = 2; 37 | } 38 | 39 | message FaultToleranceActionRef { 40 | string name = 1; 41 | string kind = 2; 42 | } 43 | 44 | repeated FaultToleranceRuleTargetRef targets = 1; 45 | repeated FaultToleranceStrategyRef strategies = 2; 46 | FaultToleranceActionRef action = 3; 47 | } 48 | 49 | // RateLimitStrategy 50 | message RateLimitStrategy { 51 | enum MetricType { 52 | TYPE_UNKNOWN = 0; 53 | TYPE_REQUEST_AMOUNT = 1; 54 | } 55 | 56 | enum LimitMode { 57 | MODE_UNKNOWN = 0; 58 | MODE_LOCAL = 1; 59 | MODE_GLOBAL = 2; 60 | } 61 | 62 | string name = 1; 63 | 64 | MetricType metric_type = 2; 65 | LimitMode limit_mode = 3; 66 | int64 threshold = 4 [(validate.rules).int64 = {gte: 0}]; 67 | int32 stat_duration = 5 [(validate.rules).int32 = {gt: 0}]; 68 | io.opensergo.proto.common.v1.TimeUnit stat_duration_time_unit = 6; 69 | } 70 | 71 | // ThrottlingStrategy 72 | message ThrottlingStrategy { 73 | string name = 1; 74 | 75 | int64 min_interval_millis_of_requests = 2; 76 | int64 queue_timeout_millis = 3; 77 | } 78 | 79 | // ConcurrencyLimitStrategy 80 | message ConcurrencyLimitStrategy { 81 | enum LimitMode { 82 | MODE_UNKNOWN = 0; 83 | MODE_LOCAL = 1; 84 | MODE_GLOBAL = 2; 85 | } 86 | 87 | string name = 1; 88 | 89 | LimitMode limit_mode = 2; 90 | int64 max_concurrency = 3; 91 | } 92 | 93 | // CircuitBreakerStrategy 94 | message CircuitBreakerStrategy { 95 | enum Strategy { 96 | STRATEGY_UNKNOWN = 0; 97 | STRATEGY_SLOW_REQUEST_RATIO = 1; 98 | STRATEGY_ERROR_REQUEST_RATIO = 2; 99 | } 100 | 101 | message CircuitBreakerSlowCondition { 102 | int32 max_allowed_rt_millis = 1; 103 | } 104 | 105 | message CircuitBreakerErrorCondition { 106 | 107 | } 108 | 109 | string name = 1 [(validate.rules).string = {max_bytes: 1024}]; 110 | 111 | Strategy strategy = 2; 112 | double trigger_ratio = 3 [(validate.rules).double = {gte: 0.0, lte: 1.0}]; 113 | int64 stat_duration = 4 [(validate.rules).int64 = {gt: 0}]; 114 | io.opensergo.proto.common.v1.TimeUnit stat_duration_time_unit = 5; 115 | 116 | int32 recovery_timeout = 6 [(validate.rules).int32 = {gt: 0}]; 117 | io.opensergo.proto.common.v1.TimeUnit recovery_timeout_time_unit = 7; 118 | int32 min_request_amount = 8 [(validate.rules).int32 = {gt: 0}]; 119 | 120 | CircuitBreakerSlowCondition slow_condition = 9; 121 | CircuitBreakerErrorCondition error_condition = 10; 122 | } 123 | 124 | -------------------------------------------------------------------------------- /pkg/proto/router/v1/route_fallback.pb.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Code generated by protoc-gen-go. DO NOT EDIT. 16 | // versions: 17 | // protoc-gen-go v1.28.1 18 | // protoc v3.19.4 19 | // source: route_fallback.proto 20 | 21 | package v1 22 | 23 | import ( 24 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 25 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 26 | reflect "reflect" 27 | sync "sync" 28 | ) 29 | 30 | const ( 31 | // Verify that this generated code is sufficiently up-to-date. 32 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 33 | // Verify that runtime/protoimpl is sufficiently up-to-date. 34 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 35 | ) 36 | 37 | type ClusterFallbackConfig_ClusterConfig struct { 38 | state protoimpl.MessageState 39 | sizeCache protoimpl.SizeCache 40 | unknownFields protoimpl.UnknownFields 41 | 42 | RoutingCluster string `protobuf:"bytes,1,opt,name=RoutingCluster,proto3" json:"RoutingCluster,omitempty"` 43 | FallbackCluster string `protobuf:"bytes,2,opt,name=FallbackCluster,proto3" json:"FallbackCluster,omitempty"` 44 | } 45 | 46 | func (x *ClusterFallbackConfig_ClusterConfig) Reset() { 47 | *x = ClusterFallbackConfig_ClusterConfig{} 48 | if protoimpl.UnsafeEnabled { 49 | mi := &file_route_fallback_proto_msgTypes[0] 50 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 51 | ms.StoreMessageInfo(mi) 52 | } 53 | } 54 | 55 | func (x *ClusterFallbackConfig_ClusterConfig) String() string { 56 | return protoimpl.X.MessageStringOf(x) 57 | } 58 | 59 | func (*ClusterFallbackConfig_ClusterConfig) ProtoMessage() {} 60 | 61 | func (x *ClusterFallbackConfig_ClusterConfig) ProtoReflect() protoreflect.Message { 62 | mi := &file_route_fallback_proto_msgTypes[0] 63 | if protoimpl.UnsafeEnabled && x != nil { 64 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 65 | if ms.LoadMessageInfo() == nil { 66 | ms.StoreMessageInfo(mi) 67 | } 68 | return ms 69 | } 70 | return mi.MessageOf(x) 71 | } 72 | 73 | // Deprecated: Use ClusterFallbackConfig_ClusterConfig.ProtoReflect.Descriptor instead. 74 | func (*ClusterFallbackConfig_ClusterConfig) Descriptor() ([]byte, []int) { 75 | return file_route_fallback_proto_rawDescGZIP(), []int{0} 76 | } 77 | 78 | func (x *ClusterFallbackConfig_ClusterConfig) GetRoutingCluster() string { 79 | if x != nil { 80 | return x.RoutingCluster 81 | } 82 | return "" 83 | } 84 | 85 | func (x *ClusterFallbackConfig_ClusterConfig) GetFallbackCluster() string { 86 | if x != nil { 87 | return x.FallbackCluster 88 | } 89 | return "" 90 | } 91 | 92 | var File_route_fallback_proto protoreflect.FileDescriptor 93 | 94 | var file_route_fallback_proto_rawDesc = []byte{ 95 | 0x0a, 0x14, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 96 | 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x69, 0x6f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 97 | 0x65, 0x72, 0x67, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 98 | 0x72, 0x2e, 0x76, 0x31, 0x22, 0x77, 0x0a, 0x23, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x46, 99 | 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x43, 0x6c, 100 | 0x75, 0x73, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x26, 0x0a, 0x0e, 0x52, 101 | 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 102 | 0x01, 0x28, 0x09, 0x52, 0x0e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6c, 0x75, 0x73, 103 | 0x74, 0x65, 0x72, 0x12, 0x28, 0x0a, 0x0f, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x43, 104 | 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x46, 0x61, 105 | 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x42, 0x77, 0x0a, 106 | 0x1c, 0x69, 0x6f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x65, 0x72, 0x67, 0x6f, 0x2e, 0x70, 0x72, 107 | 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x42, 0x17, 0x4f, 108 | 0x70, 0x65, 0x6e, 0x53, 0x65, 0x72, 0x67, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 109 | 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 110 | 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x65, 0x72, 0x67, 0x6f, 0x2f, 0x6f, 111 | 0x70, 0x65, 0x6e, 0x73, 0x65, 0x72, 0x67, 0x6f, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 112 | 0x2d, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x6f, 0x75, 113 | 0x74, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 114 | } 115 | 116 | var ( 117 | file_route_fallback_proto_rawDescOnce sync.Once 118 | file_route_fallback_proto_rawDescData = file_route_fallback_proto_rawDesc 119 | ) 120 | 121 | func file_route_fallback_proto_rawDescGZIP() []byte { 122 | file_route_fallback_proto_rawDescOnce.Do(func() { 123 | file_route_fallback_proto_rawDescData = protoimpl.X.CompressGZIP(file_route_fallback_proto_rawDescData) 124 | }) 125 | return file_route_fallback_proto_rawDescData 126 | } 127 | 128 | var file_route_fallback_proto_msgTypes = make([]protoimpl.MessageInfo, 1) 129 | var file_route_fallback_proto_goTypes = []interface{}{ 130 | (*ClusterFallbackConfig_ClusterConfig)(nil), // 0: io.opensergo.proto.router.v1.ClusterFallbackConfig_ClusterConfig 131 | } 132 | var file_route_fallback_proto_depIdxs = []int32{ 133 | 0, // [0:0] is the sub-list for method output_type 134 | 0, // [0:0] is the sub-list for method input_type 135 | 0, // [0:0] is the sub-list for extension type_name 136 | 0, // [0:0] is the sub-list for extension extendee 137 | 0, // [0:0] is the sub-list for field type_name 138 | } 139 | 140 | func init() { file_route_fallback_proto_init() } 141 | func file_route_fallback_proto_init() { 142 | if File_route_fallback_proto != nil { 143 | return 144 | } 145 | if !protoimpl.UnsafeEnabled { 146 | file_route_fallback_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 147 | switch v := v.(*ClusterFallbackConfig_ClusterConfig); i { 148 | case 0: 149 | return &v.state 150 | case 1: 151 | return &v.sizeCache 152 | case 2: 153 | return &v.unknownFields 154 | default: 155 | return nil 156 | } 157 | } 158 | } 159 | type x struct{} 160 | out := protoimpl.TypeBuilder{ 161 | File: protoimpl.DescBuilder{ 162 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 163 | RawDescriptor: file_route_fallback_proto_rawDesc, 164 | NumEnums: 0, 165 | NumMessages: 1, 166 | NumExtensions: 0, 167 | NumServices: 0, 168 | }, 169 | GoTypes: file_route_fallback_proto_goTypes, 170 | DependencyIndexes: file_route_fallback_proto_depIdxs, 171 | MessageInfos: file_route_fallback_proto_msgTypes, 172 | }.Build() 173 | File_route_fallback_proto = out.File 174 | file_route_fallback_proto_rawDesc = nil 175 | file_route_fallback_proto_goTypes = nil 176 | file_route_fallback_proto_depIdxs = nil 177 | } 178 | -------------------------------------------------------------------------------- /pkg/proto/router/v1/route_fallback.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package io.opensergo.proto.router.v1; 18 | 19 | option java_package = "io.opensergo.proto.router.v1"; 20 | option java_outer_classname = "OpenSergoTransportProto"; 21 | option java_multiple_files = true; 22 | option go_package = "github.com/opensergo/opensergo-control-plane/proto/router/v1"; 23 | 24 | 25 | message ClusterFallbackConfig_ClusterConfig { 26 | string RoutingCluster = 1; 27 | string FallbackCluster = 2; 28 | } -------------------------------------------------------------------------------- /pkg/proto/transport/v1/protocol.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package io.opensergo.proto.transport.v1; 18 | 19 | option java_package = "io.opensergo.proto.transport.v1"; 20 | option java_outer_classname = "OpenSergoTransportProto"; 21 | option java_multiple_files = true; 22 | option go_package = "github.com/opensergo/opensergo-control-plane/proto/transport/v1"; 23 | 24 | import "google/protobuf/any.proto"; 25 | 26 | // Common 27 | 28 | message Status { 29 | int32 code = 1; 30 | string message = 2; 31 | repeated google.protobuf.Any details = 3; 32 | } 33 | 34 | enum SubscribeOpType { 35 | SUBSCRIBE = 0; 36 | UNSUBSCRIBE = 1; 37 | } 38 | 39 | // SubscribeRequest 40 | 41 | message SubscribeLabelKV { 42 | string key = 1; 43 | string value = 2; 44 | } 45 | 46 | message SubscribeRequestTarget { 47 | string namespace = 1; 48 | string app = 2; 49 | repeated SubscribeLabelKV labels = 3; 50 | repeated string kinds = 4; 51 | } 52 | 53 | message SubscribeRequest { 54 | SubscribeRequestTarget target = 1; 55 | SubscribeOpType op_type = 2; 56 | 57 | string response_ack = 3; 58 | 59 | repeated google.protobuf.Any attachments = 4; 60 | 61 | // client-to-server response status 62 | Status status = 5; 63 | string identifier = 6; 64 | string request_id = 7; 65 | } 66 | 67 | message ControlPlaneDesc { 68 | string identifier = 1; 69 | } 70 | 71 | // SubscribeResponse 72 | 73 | message SubscribeResponse { 74 | Status status = 1; 75 | 76 | string ack = 2; 77 | 78 | string namespace = 3; 79 | string app = 4; 80 | string kind = 5; 81 | DataWithVersion dataWithVersion = 6; 82 | 83 | ControlPlaneDesc control_plane = 7; 84 | string response_id = 8; 85 | } 86 | 87 | message DataWithVersion { 88 | repeated google.protobuf.Any data = 1; 89 | int64 version = 2; 90 | } 91 | 92 | // OpenSergo Universal Transport Service (state-of-the-world) 93 | service OpenSergoUniversalTransportService { 94 | rpc SubscribeConfig(stream SubscribeRequest) returns (stream SubscribeResponse); 95 | } 96 | -------------------------------------------------------------------------------- /pkg/proto/transport/v1/protocol_grpc.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT. 2 | // versions: 3 | // - protoc-gen-go-grpc v1.2.0 4 | // - protoc v3.19.4 5 | // source: protocol.proto 6 | 7 | package v1 8 | 9 | import ( 10 | context "context" 11 | grpc "google.golang.org/grpc" 12 | codes "google.golang.org/grpc/codes" 13 | status "google.golang.org/grpc/status" 14 | ) 15 | 16 | // This is a compile-time assertion to ensure that this generated file 17 | // is compatible with the grpc package it is being compiled against. 18 | // Requires gRPC-Go v1.32.0 or later. 19 | const _ = grpc.SupportPackageIsVersion7 20 | 21 | // OpenSergoUniversalTransportServiceClient is the client API for OpenSergoUniversalTransportService service. 22 | // 23 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. 24 | type OpenSergoUniversalTransportServiceClient interface { 25 | SubscribeConfig(ctx context.Context, opts ...grpc.CallOption) (OpenSergoUniversalTransportService_SubscribeConfigClient, error) 26 | } 27 | 28 | type openSergoUniversalTransportServiceClient struct { 29 | cc grpc.ClientConnInterface 30 | } 31 | 32 | func NewOpenSergoUniversalTransportServiceClient(cc grpc.ClientConnInterface) OpenSergoUniversalTransportServiceClient { 33 | return &openSergoUniversalTransportServiceClient{cc} 34 | } 35 | 36 | func (c *openSergoUniversalTransportServiceClient) SubscribeConfig(ctx context.Context, opts ...grpc.CallOption) (OpenSergoUniversalTransportService_SubscribeConfigClient, error) { 37 | stream, err := c.cc.NewStream(ctx, &OpenSergoUniversalTransportService_ServiceDesc.Streams[0], "/io.opensergo.proto.transport.v1.OpenSergoUniversalTransportService/SubscribeConfig", opts...) 38 | if err != nil { 39 | return nil, err 40 | } 41 | x := &openSergoUniversalTransportServiceSubscribeConfigClient{stream} 42 | return x, nil 43 | } 44 | 45 | type OpenSergoUniversalTransportService_SubscribeConfigClient interface { 46 | Send(*SubscribeRequest) error 47 | Recv() (*SubscribeResponse, error) 48 | grpc.ClientStream 49 | } 50 | 51 | type openSergoUniversalTransportServiceSubscribeConfigClient struct { 52 | grpc.ClientStream 53 | } 54 | 55 | func (x *openSergoUniversalTransportServiceSubscribeConfigClient) Send(m *SubscribeRequest) error { 56 | return x.ClientStream.SendMsg(m) 57 | } 58 | 59 | func (x *openSergoUniversalTransportServiceSubscribeConfigClient) Recv() (*SubscribeResponse, error) { 60 | m := new(SubscribeResponse) 61 | if err := x.ClientStream.RecvMsg(m); err != nil { 62 | return nil, err 63 | } 64 | return m, nil 65 | } 66 | 67 | // OpenSergoUniversalTransportServiceServer is the server API for OpenSergoUniversalTransportService service. 68 | // All implementations must embed UnimplementedOpenSergoUniversalTransportServiceServer 69 | // for forward compatibility 70 | type OpenSergoUniversalTransportServiceServer interface { 71 | SubscribeConfig(OpenSergoUniversalTransportService_SubscribeConfigServer) error 72 | mustEmbedUnimplementedOpenSergoUniversalTransportServiceServer() 73 | } 74 | 75 | // UnimplementedOpenSergoUniversalTransportServiceServer must be embedded to have forward compatible implementations. 76 | type UnimplementedOpenSergoUniversalTransportServiceServer struct { 77 | } 78 | 79 | func (UnimplementedOpenSergoUniversalTransportServiceServer) SubscribeConfig(OpenSergoUniversalTransportService_SubscribeConfigServer) error { 80 | return status.Errorf(codes.Unimplemented, "method SubscribeConfig not implemented") 81 | } 82 | func (UnimplementedOpenSergoUniversalTransportServiceServer) mustEmbedUnimplementedOpenSergoUniversalTransportServiceServer() { 83 | } 84 | 85 | // UnsafeOpenSergoUniversalTransportServiceServer may be embedded to opt out of forward compatibility for this service. 86 | // Use of this interface is not recommended, as added methods to OpenSergoUniversalTransportServiceServer will 87 | // result in compilation errors. 88 | type UnsafeOpenSergoUniversalTransportServiceServer interface { 89 | mustEmbedUnimplementedOpenSergoUniversalTransportServiceServer() 90 | } 91 | 92 | func RegisterOpenSergoUniversalTransportServiceServer(s grpc.ServiceRegistrar, srv OpenSergoUniversalTransportServiceServer) { 93 | s.RegisterService(&OpenSergoUniversalTransportService_ServiceDesc, srv) 94 | } 95 | 96 | func _OpenSergoUniversalTransportService_SubscribeConfig_Handler(srv interface{}, stream grpc.ServerStream) error { 97 | return srv.(OpenSergoUniversalTransportServiceServer).SubscribeConfig(&openSergoUniversalTransportServiceSubscribeConfigServer{stream}) 98 | } 99 | 100 | type OpenSergoUniversalTransportService_SubscribeConfigServer interface { 101 | Send(*SubscribeResponse) error 102 | Recv() (*SubscribeRequest, error) 103 | grpc.ServerStream 104 | } 105 | 106 | type openSergoUniversalTransportServiceSubscribeConfigServer struct { 107 | grpc.ServerStream 108 | } 109 | 110 | func (x *openSergoUniversalTransportServiceSubscribeConfigServer) Send(m *SubscribeResponse) error { 111 | return x.ServerStream.SendMsg(m) 112 | } 113 | 114 | func (x *openSergoUniversalTransportServiceSubscribeConfigServer) Recv() (*SubscribeRequest, error) { 115 | m := new(SubscribeRequest) 116 | if err := x.ServerStream.RecvMsg(m); err != nil { 117 | return nil, err 118 | } 119 | return m, nil 120 | } 121 | 122 | // OpenSergoUniversalTransportService_ServiceDesc is the grpc.ServiceDesc for OpenSergoUniversalTransportService service. 123 | // It's only intended for direct use with grpc.RegisterService, 124 | // and not to be introspected or modified (even as a copy) 125 | var OpenSergoUniversalTransportService_ServiceDesc = grpc.ServiceDesc{ 126 | ServiceName: "io.opensergo.proto.transport.v1.OpenSergoUniversalTransportService", 127 | HandlerType: (*OpenSergoUniversalTransportServiceServer)(nil), 128 | Methods: []grpc.MethodDesc{}, 129 | Streams: []grpc.StreamDesc{ 130 | { 131 | StreamName: "SubscribeConfig", 132 | Handler: _OpenSergoUniversalTransportService_SubscribeConfig_Handler, 133 | ServerStreams: true, 134 | ClientStreams: true, 135 | }, 136 | }, 137 | Metadata: "protocol.proto", 138 | } 139 | -------------------------------------------------------------------------------- /pkg/transport/grpc/connection.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package grpc 16 | 17 | import ( 18 | "sync" 19 | 20 | "github.com/opensergo/opensergo-control-plane/pkg/model" 21 | pb "github.com/opensergo/opensergo-control-plane/pkg/proto/transport/v1" 22 | "github.com/pkg/errors" 23 | ) 24 | 25 | type OpenSergoTransportStream = pb.OpenSergoUniversalTransportService_SubscribeConfigServer 26 | 27 | type Connection struct { 28 | identifier model.ClientIdentifier 29 | stream OpenSergoTransportStream 30 | 31 | valid bool 32 | } 33 | 34 | func (c *Connection) Identifier() model.ClientIdentifier { 35 | return c.identifier 36 | } 37 | 38 | func (c *Connection) Stream() OpenSergoTransportStream { 39 | return c.stream 40 | } 41 | 42 | func (c *Connection) IsValid() bool { 43 | return c.stream != nil && c.valid 44 | } 45 | 46 | type ConnectionMap map[model.ClientIdentifier]*Connection 47 | 48 | func NewConnection(identifier model.ClientIdentifier, stream OpenSergoTransportStream) *Connection { 49 | return &Connection{ 50 | identifier: identifier, 51 | stream: stream, 52 | valid: true, 53 | } 54 | } 55 | 56 | type ConnectionManager struct { 57 | // connectionMap is used to save the connections which subscribed to the same namespace, app and kind. 58 | // (namespace+app, (kind, connections...)) 59 | connectionMap map[model.NamespacedApp]map[string]ConnectionMap 60 | // identifier: NamespaceApp: kinds 61 | // The identifier is used to distinguish the requested process instance and remove stream when disconnected 62 | identifierMap map[model.ClientIdentifier]map[model.NamespacedApp][]string 63 | 64 | updateMux sync.RWMutex 65 | } 66 | 67 | func (c *ConnectionManager) Add(namespace, app, kind string, connection *Connection) error { 68 | if connection == nil { 69 | return errors.New("nil connection") 70 | } 71 | 72 | c.updateMux.Lock() 73 | defer c.updateMux.Unlock() 74 | 75 | nsa := model.NamespacedApp{ 76 | Namespace: namespace, 77 | App: app, 78 | } 79 | if c.connectionMap[nsa] == nil { 80 | c.connectionMap[nsa] = make(map[string]ConnectionMap) 81 | } 82 | connectionMap := c.connectionMap[nsa][kind] 83 | if connectionMap == nil { 84 | connectionMap = make(ConnectionMap) 85 | c.connectionMap[nsa][kind] = connectionMap 86 | } 87 | if connectionMap[connection.identifier] == nil { 88 | connectionMap[connection.identifier] = connection 89 | } 90 | 91 | // TODO: legacy logic, rearrange it later 92 | if c.identifierMap[connection.identifier] == nil { 93 | c.identifierMap[connection.identifier] = make(map[model.NamespacedApp][]string) 94 | } 95 | c.identifierMap[connection.identifier][nsa] = append(c.identifierMap[connection.identifier][nsa], kind) 96 | 97 | return nil 98 | } 99 | 100 | func (c *ConnectionManager) Get(namespace, app, kind string) ([]*Connection, bool) { 101 | c.updateMux.RLock() 102 | defer c.updateMux.RUnlock() 103 | 104 | kindMap, exists := c.connectionMap[model.NamespacedApp{ 105 | Namespace: namespace, 106 | App: app, 107 | }] 108 | if !exists || kindMap == nil { 109 | return nil, false 110 | } 111 | connectionMap, exists := kindMap[kind] 112 | if !exists || connectionMap == nil { 113 | return nil, false 114 | } 115 | connectionList := make([]*Connection, len(connectionMap)) 116 | for _, conn := range connectionMap { 117 | if conn.IsValid() { 118 | connectionList = append(connectionList, conn) 119 | } 120 | } 121 | return connectionList, true 122 | } 123 | 124 | func (c *ConnectionManager) removeInternal(n model.NamespacedApp, kind string, identifier model.ClientIdentifier) error { 125 | // Guarded in the outer function, if a lock is added here, it will deadlock 126 | kindMap, exists := c.connectionMap[n] 127 | if !exists || kindMap == nil { 128 | return nil 129 | } 130 | streams, exists := kindMap[kind] 131 | if !exists || streams == nil { 132 | return nil 133 | } 134 | delete(streams, identifier) 135 | return nil 136 | } 137 | 138 | func (c *ConnectionManager) RemoveByIdentifier(identifier model.ClientIdentifier) error { 139 | c.updateMux.Lock() 140 | defer c.updateMux.Unlock() 141 | 142 | NamespaceAppKinds, exists := c.identifierMap[identifier] 143 | if !exists { 144 | return nil 145 | } 146 | for n, kinds := range NamespaceAppKinds { 147 | for _, kind := range kinds { 148 | err := c.removeInternal(n, kind, identifier) 149 | if err != nil { 150 | return err 151 | } 152 | } 153 | } 154 | return nil 155 | } 156 | 157 | func NewConnectionManager() *ConnectionManager { 158 | return &ConnectionManager{ 159 | connectionMap: make(map[model.NamespacedApp]map[string]ConnectionMap), 160 | identifierMap: make(map[model.ClientIdentifier]map[model.NamespacedApp][]string), 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /pkg/transport/grpc/server.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package grpc 16 | 17 | import ( 18 | "fmt" 19 | "io" 20 | "log" 21 | "net" 22 | 23 | "github.com/opensergo/opensergo-control-plane/pkg/model" 24 | trpb "github.com/opensergo/opensergo-control-plane/pkg/proto/transport/v1" 25 | "github.com/opensergo/opensergo-control-plane/pkg/util" 26 | "go.uber.org/atomic" 27 | "google.golang.org/grpc" 28 | ) 29 | 30 | const ( 31 | ClientIdentifierKey = "OpenSergoClientIdentifier" 32 | ) 33 | 34 | // Server represents the transport server of OpenSergo universal transport service (OUTS). 35 | type Server struct { 36 | transportServer *TransportServer 37 | grpcServer *grpc.Server 38 | 39 | connectionManager *ConnectionManager 40 | 41 | port uint32 42 | started *atomic.Bool 43 | } 44 | 45 | func NewServer(port uint32, subscribeHandlers []model.SubscribeRequestHandler) *Server { 46 | connectionManager := NewConnectionManager() 47 | return &Server{ 48 | transportServer: newTransportServer(connectionManager, subscribeHandlers), 49 | port: port, 50 | grpcServer: grpc.NewServer(), 51 | started: atomic.NewBool(false), 52 | connectionManager: connectionManager, 53 | } 54 | } 55 | 56 | func (s *Server) ConnectionManager() *ConnectionManager { 57 | return s.connectionManager 58 | } 59 | 60 | func (s *Server) ComponentName() string { 61 | return "OpenSergoUniversalTransportServer" 62 | } 63 | 64 | func (s *Server) Run() error { 65 | if s.started.CAS(false, true) { 66 | listener, err := net.Listen("tcp", fmt.Sprintf(":%d", s.port)) 67 | if err != nil { 68 | return err 69 | } 70 | 71 | trpb.RegisterOpenSergoUniversalTransportServiceServer(s.grpcServer, s.transportServer) 72 | err = s.grpcServer.Serve(listener) 73 | if err != nil { 74 | return err 75 | } 76 | } 77 | return nil 78 | } 79 | 80 | // TransportServer represents the gRPC server of OpenSergo universal transport service. 81 | type TransportServer struct { 82 | trpb.OpenSergoUniversalTransportServiceServer 83 | 84 | connectionManager *ConnectionManager 85 | 86 | subscribeHandlers []model.SubscribeRequestHandler 87 | } 88 | 89 | const ( 90 | ACKFlag = "ACK" 91 | NACKFlag = "NACK" 92 | 93 | Success = 1 94 | CheckFormatError = 4001 95 | ReqFormatError = 4002 96 | RegisterWatcherError = 500 97 | ) 98 | 99 | func (s *TransportServer) SubscribeConfig(stream trpb.OpenSergoUniversalTransportService_SubscribeConfigServer) error { 100 | var clientIdentifier model.ClientIdentifier 101 | for { 102 | recvData, err := stream.Recv() 103 | if err == io.EOF { 104 | // Stream EOF 105 | _ = s.connectionManager.RemoveByIdentifier(clientIdentifier) 106 | return nil 107 | } 108 | if err != nil { 109 | // remove stream 110 | _ = s.connectionManager.RemoveByIdentifier(clientIdentifier) 111 | return err 112 | } 113 | 114 | if recvData.ResponseAck == ACKFlag { 115 | // This indicates the received data is a response of push-success. 116 | continue 117 | } else if recvData.ResponseAck == NACKFlag { 118 | // This indicates the received data is a response of push-failure. 119 | if recvData.Status.Code == CheckFormatError { 120 | // TODO: handle here (cannot retry) 121 | log.Println("Client response CheckFormatError") 122 | } else { 123 | // TODO: record error here and do something 124 | log.Printf("Client response NACK, code=%d\n", recvData.Status.Code) 125 | } 126 | } else { 127 | // This indicates the received data is a SubscribeRequest. 128 | if clientIdentifier == "" && recvData.Identifier != "" { 129 | clientIdentifier = model.ClientIdentifier(recvData.Identifier) 130 | } 131 | 132 | if !util.IsValidReq(recvData) { 133 | status := &trpb.Status{ 134 | Code: ReqFormatError, 135 | Message: "Request is invalid", 136 | Details: nil, 137 | } 138 | _ = stream.Send(&trpb.SubscribeResponse{ 139 | Status: status, 140 | Ack: NACKFlag, 141 | ResponseId: recvData.RequestId, 142 | }) 143 | continue 144 | } 145 | 146 | for _, handler := range s.subscribeHandlers { 147 | err = handler(clientIdentifier, recvData, stream) 148 | if err != nil { 149 | // TODO: handle error 150 | log.Printf("Failed to handle SubscribeRequest, err=%s\n", err.Error()) 151 | } 152 | } 153 | } 154 | 155 | } 156 | } 157 | 158 | func newTransportServer(connectionManager *ConnectionManager, subscribeHandlers []model.SubscribeRequestHandler) *TransportServer { 159 | return &TransportServer{ 160 | connectionManager: connectionManager, 161 | subscribeHandlers: subscribeHandlers, 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /pkg/util/check_util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package util 16 | 17 | import pb "github.com/opensergo/opensergo-control-plane/pkg/proto/transport/v1" 18 | 19 | func IsValidReq(req *pb.SubscribeRequest) bool { 20 | if req == nil || req.Target == nil || req.Target.Namespace == "" || req.Target.App == "" || 21 | len(req.Target.Kinds) == 0 { 22 | return false 23 | } 24 | return true 25 | } 26 | -------------------------------------------------------------------------------- /pkg/util/format_util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022, OpenSergo Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package util 16 | 17 | import ( 18 | "strconv" 19 | "strings" 20 | 21 | pb "github.com/opensergo/opensergo-control-plane/pkg/proto/fault_tolerance/v1" 22 | ) 23 | 24 | func Str2MillSeconds(timeStr string) (int64, error) { 25 | if strings.Contains(timeStr, "ms") && len(timeStr) > 2 { 26 | return strconv.ParseInt(timeStr[:len(timeStr)-2], 10, 64) 27 | } else if strings.Contains(timeStr, "s") && len(timeStr) > 1 { 28 | millSec, err := strconv.ParseInt(timeStr[:len(timeStr)-1], 10, 64) 29 | return millSec * 10, err 30 | } else if strings.Contains(timeStr, "minute") && len(timeStr) > 6 { 31 | millSec, err := strconv.ParseInt(timeStr[:len(timeStr)-6], 10, 64) 32 | return millSec * 60 * 10, err 33 | } else if strings.Contains(timeStr, "min") && len(timeStr) > 3 { 34 | millSec, err := strconv.ParseInt(timeStr[:len(timeStr)-3], 10, 64) 35 | return millSec * 60 * 10, err 36 | } else if strings.Contains(timeStr, "m") && len(timeStr) > 1 { 37 | millSec, err := strconv.ParseInt(timeStr[:len(timeStr)-1], 10, 64) 38 | return millSec * 60 * 10, err 39 | } else if strings.Contains(timeStr, "h") && len(timeStr) > 1 { 40 | millSec, err := strconv.ParseInt(timeStr[:len(timeStr)-1], 10, 64) 41 | return millSec * 60 * 60 * 10, err 42 | } 43 | return strconv.ParseInt(timeStr, 10, 64) 44 | } 45 | 46 | func Str2LimitNode(lnStr string) pb.ConcurrencyLimitStrategy_LimitMode { 47 | lnStr = strings.ToUpper(lnStr) 48 | switch lnStr { 49 | case "LOCAL": 50 | return pb.ConcurrencyLimitStrategy_MODE_LOCAL 51 | case "GLOBAL": 52 | return pb.ConcurrencyLimitStrategy_MODE_GLOBAL 53 | default: 54 | return pb.ConcurrencyLimitStrategy_MODE_UNKNOWN 55 | } 56 | } 57 | 58 | func Str2CBStrategy(cbStr string) pb.CircuitBreakerStrategy_Strategy { 59 | cbStr = strings.ToUpper(cbStr) 60 | switch cbStr { 61 | case strings.ToUpper("SlowRequestRatio"): 62 | return pb.CircuitBreakerStrategy_STRATEGY_SLOW_REQUEST_RATIO 63 | case strings.ToUpper("ErrorRequestRatio"): 64 | return pb.CircuitBreakerStrategy_STRATEGY_ERROR_REQUEST_RATIO 65 | default: 66 | return pb.CircuitBreakerStrategy_STRATEGY_UNKNOWN 67 | } 68 | } 69 | 70 | func RatioStr2Float(ratioStr string) (float64, error) { 71 | if strings.Contains(ratioStr, "%") && len(ratioStr) > 1 { 72 | num, err := strconv.ParseFloat(ratioStr[:len(ratioStr)-1], 64) 73 | return num / 100.0, err 74 | } 75 | return strconv.ParseFloat(ratioStr, 64) 76 | } 77 | -------------------------------------------------------------------------------- /pkg/util/util.go: -------------------------------------------------------------------------------- 1 | // Copyright Istio Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package util 16 | 17 | import ( 18 | "google.golang.org/protobuf/proto" 19 | "google.golang.org/protobuf/types/known/anypb" 20 | ) 21 | 22 | // MessageToAnyWithError converts from proto message to proto Any 23 | func MessageToAnyWithError(msg proto.Message) (*anypb.Any, error) { 24 | b, err := proto.MarshalOptions{Deterministic: true}.Marshal(msg) 25 | if err != nil { 26 | return nil, err 27 | } 28 | return &anypb.Any{ 29 | // nolint: staticcheck 30 | TypeUrl: "type.googleapis.com/" + string(msg.ProtoReflect().Descriptor().FullName()), 31 | Value: b, 32 | }, nil 33 | } 34 | 35 | // MessageToAny converts from proto message to proto Any 36 | func MessageToAny(msg proto.Message) *anypb.Any { 37 | out, err := MessageToAnyWithError(msg) 38 | if err != nil { 39 | // todo 40 | return nil 41 | } 42 | return out 43 | } 44 | -------------------------------------------------------------------------------- /samples/opensergo_new_sample.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: fault-tolerance.opensergo.io/v1alpha1 2 | kind: RateLimitStrategy 3 | metadata: 4 | name: rate-limit-foo 5 | labels: 6 | app: foo-app 7 | spec: 8 | metricType: RequestAmount 9 | limitMode: Local 10 | threshold: 3 11 | statDurationSeconds: 5 12 | --- 13 | apiVersion: fault-tolerance.opensergo.io/v1alpha1 14 | kind: FaultToleranceRule 15 | metadata: 16 | name: my-opensergo-rule-1 17 | labels: 18 | app: foo-app 19 | spec: 20 | targets: 21 | - targetResourceName: 'GET:/fooa' 22 | strategies: 23 | - name: rate-limit-foo 24 | kind: RateLimitStrategy 25 | --- 26 | apiVersion: fault-tolerance.opensergo.io/v1alpha1 27 | kind: FaultToleranceRule 28 | metadata: 29 | name: my-opensergo-rule-2 30 | labels: 31 | app: foo-app 32 | spec: 33 | targets: 34 | - targetResourceName: 'GET:/fooc' 35 | strategies: 36 | - name: rate-limit-foo 37 | kind: RateLimitStrategy 38 | 39 | --- 40 | apiVersion: fault-tolerance.opensergo.io/v1alpha1 41 | kind: ConcurrencyLimitStrategy 42 | metadata: 43 | name: concurrency-limit-foo 44 | labels: 45 | app: foo-app 46 | spec: 47 | maxConcurrency: 8 48 | limitMode: 'Local' 49 | 50 | --- 51 | apiVersion: fault-tolerance.opensergo.io/v1alpha1 52 | kind: ThrottlingStrategy 53 | metadata: 54 | name: throttling-foo 55 | labels: 56 | app: foo-app 57 | spec: 58 | minIntervalOfRequests: '20ms' 59 | queueTimeout: '60ms' 60 | 61 | --- 62 | apiVersion: fault-tolerance.opensergo.io/v1alpha1 63 | kind: CircuitBreakerStrategy 64 | metadata: 65 | name: circuit-breaker-slow-foo 66 | labels: 67 | app: foo-app 68 | spec: 69 | strategy: SlowRequestRatio 70 | triggerRatio: '60%' 71 | statDuration: '60s' 72 | recoveryTimeout: '5s' 73 | minRequestAmount: 5 74 | slowConditions: 75 | maxAllowedRt: '500ms' -------------------------------------------------------------------------------- /samples/opensergo_traffic_router_samples.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: traffic.opensergo.io/v1alpha1 2 | kind: TrafficRouter 3 | metadata: 4 | name: service-provider 5 | namespace: default 6 | labels: 7 | app: service-provider 8 | spec: 9 | hosts: 10 | - service-provider 11 | http: 12 | - match: 13 | - headers: 14 | x-mse-tag: 15 | exact: v2 16 | route: 17 | - destination: 18 | host: service-provider 19 | subset: v2 20 | fallback: 21 | host: service-provider 22 | subset: v1 23 | - route: 24 | - destination: 25 | host: service-provider 26 | subset: v1 27 | --------------------------------------------------------------------------------