├── .github ├── ISSUE_TEMPLATE │ └── feature_request.md └── workflows │ ├── build.yml │ ├── release-multiarch.yaml │ └── release.yaml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── MAINTAINERS ├── Makefile ├── README.md ├── artifacts └── examples │ ├── crd.yaml │ ├── example-tapp-volume.yaml │ └── example-tapp.yaml ├── build ├── docker │ ├── Dockerfile │ ├── Dockerfile_arch │ ├── ca.crt │ ├── tls.crt │ └── tls.key └── lib │ ├── common.mk │ ├── create-manifest.sh │ ├── image.mk │ └── install-buildx.sh ├── deployment └── tapp-controller.yaml ├── doc └── tutorial.md ├── go.mod ├── go.sum ├── hack ├── build-image.sh ├── build.sh ├── format.sh ├── gencerts.sh ├── import_checker.go ├── lib │ ├── golang.sh │ ├── lib.sh │ ├── logging.sh │ └── version.sh ├── push-image.sh ├── test-go.sh ├── update-codegen.sh ├── update-gofmt.sh ├── verify-all.sh ├── verify-codegen.sh └── verify-gofmt.sh ├── main.go └── pkg ├── admission ├── admission.go ├── util.go └── validation.go ├── apis └── tappcontroller │ ├── BUILD │ ├── register.go │ └── v1 │ ├── BUILD │ ├── doc.go │ ├── register.go │ ├── types.go │ └── zz_generated.deepcopy.go ├── client ├── clientset │ └── versioned │ │ ├── clientset.go │ │ ├── doc.go │ │ ├── fake │ │ ├── clientset_generated.go │ │ ├── doc.go │ │ └── register.go │ │ ├── scheme │ │ ├── doc.go │ │ └── register.go │ │ └── typed │ │ └── tappcontroller │ │ └── v1 │ │ ├── doc.go │ │ ├── fake │ │ ├── doc.go │ │ ├── fake_tapp.go │ │ └── fake_tappcontroller_client.go │ │ ├── generated_expansion.go │ │ ├── tapp.go │ │ └── tappcontroller_client.go ├── informers │ └── externalversions │ │ ├── factory.go │ │ ├── generic.go │ │ ├── internalinterfaces │ │ └── factory_interfaces.go │ │ └── tappcontroller │ │ ├── interface.go │ │ └── v1 │ │ ├── interface.go │ │ └── tapp.go └── listers │ └── tappcontroller │ └── v1 │ ├── expansion_generated.go │ └── tapp.go ├── hash ├── hash.go └── hash_test.go ├── tapp ├── controller.go ├── controller_test.go ├── crd.go ├── fakes.go ├── identity_mappers.go ├── identity_mappers_test.go ├── instance.go ├── instance_test.go ├── tapp_test.go └── tapp_utils.go ├── testutil └── utils.go ├── util └── utils.go └── version ├── .gitattributes ├── base.go ├── verflag └── verflag.go └── version.go /.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 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-18.04 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | 13 | - uses: actions/cache@v1 14 | with: 15 | path: ~/go/pkg/mod 16 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 17 | restore-keys: | 18 | ${{ runner.os }}-go- 19 | 20 | - run: | 21 | make 22 | -------------------------------------------------------------------------------- /.github/workflows/release-multiarch.yaml: -------------------------------------------------------------------------------- 1 | name: release-multiarch 2 | 3 | on: 4 | create: 5 | tags: 6 | - v* 7 | 8 | jobs: 9 | release-multiarch: 10 | 11 | runs-on: ubuntu-18.04 12 | 13 | steps: 14 | - uses: actions/checkout@v1 15 | 16 | - uses: actions/cache@v1 17 | with: 18 | path: ~/go/pkg/mod 19 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 20 | restore-keys: | 21 | ${{ runner.os }}-go- 22 | 23 | - uses: azure/docker-login@v1 24 | with: 25 | username: ${{ secrets.REGISTRY_USERNAME }} 26 | password: ${{ secrets.REGISTRY_PASSWORD }} 27 | 28 | - name: Set up Docker Buildx 29 | id: buildx 30 | uses: crazy-max/ghaction-docker-buildx@v1 31 | with: 32 | buildx-version: latest 33 | qemu-version: latest 34 | 35 | - run: | 36 | make release.multiarch 37 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | create: 5 | tags: 6 | - v* 7 | 8 | jobs: 9 | release: 10 | 11 | runs-on: ubuntu-18.04 12 | 13 | steps: 14 | - uses: actions/checkout@v1 15 | 16 | - uses: actions/cache@v1 17 | with: 18 | path: ~/go/pkg/mod 19 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 20 | restore-keys: | 21 | ${{ runner.os }}-go- 22 | 23 | - uses: azure/docker-login@v1 24 | with: 25 | username: ${{ secrets.REGISTRY_USERNAME }} 26 | password: ${{ secrets.REGISTRY_PASSWORD }} 27 | 28 | - run: | 29 | make release -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .version-defs 2 | bin/**/* 3 | go/**/* 4 | /.idea -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # CONTRIBUTING 2 | 3 | Welcome to [report Issues](https://github.com/tkestack/tapp/issues) or [pull requests](https://github.com/tkestack/tapp/pulls). It's recommended to read the following Contributing Guide first before contributing. 4 | 5 | This document provides a set of best practices for open source contributions - bug reports, code submissions / pull requests, etc. 6 | 7 | ## Issues 8 | 9 | We use Github Issues to track public bugs and feature requests. 10 | 11 | ### Due diligence 12 | 13 | Before submitting a issue, please do the following: 14 | 15 | * Perform **basic troubleshooting** steps: 16 | * Make sure you’re on the latest version. If you’re not on the most recent version, your problem may have been solved already! Upgrading is always the best first step. 17 | * Try older versions. If you’re already on the latest release, try rolling back a few minor versions (e.g. if on 1.7, try 1.5 or 1.6) and see if the problem goes away. This will help the devs narrow down when the problem first arose in the commit log. 18 | * Try switching up dependency versions. If the software in question has dependencies (other libraries, etc) try upgrading/downgrading those as well. 19 | * Search the project’s bug/issue tracker to make sure it’s not a known issue. 20 | * If you don’t find a pre-existing issue, consider checking with the mailing list and/or IRC channel in case the problem is non-bug-related. 21 | 22 | ### What to put in your bug report 23 | 24 | Make sure your report gets the attention it deserves: bug reports with missing information may be ignored or punted back to you, delaying a fix. The below constitutes a bare minimum; more info is almost always better: 25 | 26 | * What version of the core programming language interpreter/compiler are you using? For example, if it’s a Golang project, are you using Golang 1.13? Golang 1.12? 27 | * What operating system are you on? Windows? (32-bit? 64-bit?) Mac OS X? (10.14? 10.10?) Linux? (Which distro? Which version of that distro? 32 or 64 bits?) Again, more detail is better. 28 | * Which version or versions of the software are you using? Ideally, you followed the advice above and have ruled out (or verified that the problem exists in) a few different versions. 29 | * How can the developers recreate the bug on their end? If possible, include a copy of your code, the command you used to invoke it, and the full output of your run (if applicable.) A common tactic is to pare down your code until a simple (but still bug-causing) “base case” remains. Not only can this help you identify problems which aren’t real bugs, but it means the developer can get to fixing the bug faster. 30 | 31 | ## Pull Requests 32 | 33 | We strongly welcome your pull request to make TKEStack project better. 34 | 35 | ### Licensing of contributed material 36 | 37 | Keep in mind as you contribute, that code, docs and other material submitted to open source projects are usually considered licensed under the same terms as the rest of the work. 38 | 39 | Anything submitted to a project falls under the licensing terms in the repository’s top level LICENSE file. Per-file copyright/license headers are typically extraneous and undesirable. Please don’t add your own copyright headers to new files unless the project’s license actually requires them! 40 | 41 | ### Branch Management 42 | 43 | There are three main branches here: 44 | 45 | 1. `master` branch. 46 | 1. It is the latest (pre-)release branch. We use `master` for tags, with version number `1.1.0`, `1.2.0`, `1.3.0`... 47 | 2. **Don't submit any PR on `master` branch.** 48 | 2. `dev` branch. 49 | 1. It is our stable developing branch. After full testing, `dev` will be merged to `master` branch for the next release. 50 | 2. **You are recommended to submit bugfix or feature PR on `dev` branch.** 51 | 3. `hotfix` branch. 52 | 1. It is the latest tag version for hot fix. If we accept your pull request, we may just tag with version number `1.1.1`, `1.2.3`. 53 | 2. **Only submit urgent PR on `hotfix` branch for next specific release.** 54 | 55 | Normal bugfix or feature request should be submitted to `dev` branch. After full testing, we will merge them to `master` branch for the next release. 56 | 57 | If you have some urgent bugfixes on a published version, but the `master` branch have already far away with the latest tag version, you can submit a PR on hotfix. And it will be cherry picked to `dev` branch if it is possible. 58 | 59 | ``` 60 | master 61 | ↑ 62 | dev <--- hotfix PR 63 | ↑ 64 | feature/bugfix PR 65 | ``` 66 | 67 | ### Make Pull Requests 68 | 69 | The code team will monitor all pull request, we run some code check and test on it. After all tests passed, we will accecpt this PR. But it won't merge to `master` branch at once, which have some delay. 70 | 71 | Before submitting a pull request, please make sure the followings are done: 72 | 73 | 1. Fork the repo and create your branch from `master` or `hotfix`. 74 | 2. Update code or documentation if you have changed APIs. 75 | 3. Add the copyright notice to the top of any new files you've added. 76 | 4. Check your code lints and checkstyles. 77 | 5. Test and test again your code. 78 | 6. Now, you can submit your pull request on `dev` or `hotfix` branch. 79 | 80 | ## Code Conventions 81 | 82 | Use [Kubernetes Code Conventions](https://github.com/kubernetes/community/blob/master/contributors/guide/coding-conventions.md) for all projects in the TKEStack organization. 83 | 84 | ## Documentation isn’t optional 85 | 86 | It’s not! Patches without documentation will be returned to sender. By “documentation” we mean: 87 | 88 | * Docstrings must be created or updated for public API functions/methods/etc. (This step is optional for some bugfixes.) 89 | * New features should ideally include updates to prose documentation, including useful example code snippets. 90 | * All submissions should have a changelog entry crediting the contributor and/or any individuals instrumental in identifying the problem. 91 | 92 | ## Tests aren’t optional 93 | 94 | Any bugfix that doesn’t include a test proving the existence of the bug being fixed, may be suspect. Ditto for new features that can’t prove they actually work. 95 | 96 | We’ve found that test-first development really helps make features better architected and identifies potential edge cases earlier instead of later. Writing tests before the implementation is strongly encouraged. 97 | -------------------------------------------------------------------------------- /MAINTAINERS: -------------------------------------------------------------------------------- 1 | Jun Gong (@hex108) 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: verify-gofmt build test 3 | 4 | .PHONY: clean 5 | clean: 6 | rm -rf bin/ _output/ go .version-defs 7 | 8 | .PHONY: build 9 | build: 10 | hack/build.sh 11 | 12 | # ============================================================================== 13 | # Includes 14 | 15 | include build/lib/common.mk 16 | include build/lib/image.mk 17 | 18 | # Run test 19 | # 20 | # Args: 21 | # TFLAGS: test flags 22 | # Example: 23 | # make test TFLAGS='-check.f xxx' 24 | .PHONY: test 25 | test: 26 | TESTFLAGS=$(TFLAGS) hack/test-go.sh 27 | 28 | .PHONY: verify 29 | verify: 30 | hack/verify-all.sh 31 | 32 | .PHONY: verify-gofmt 33 | verify-gofmt: 34 | hack/verify-gofmt.sh 35 | 36 | format: 37 | hack/format.sh 38 | 39 | build-image: verify-gofmt 40 | hack/build-image.sh tkestack/tapp-controller:latest 41 | 42 | push-image: 43 | hack/push-image.sh tkestack/tapp-controller:latest 44 | 45 | release: build-image push-image 46 | 47 | ## release.multiarch: Build docker images for multiple platforms and push manifest lists to registry. 48 | .PHONY: release.multiarch 49 | release.multiarch: 50 | @$(MAKE) image.manifest.push.multiarch BINS="tapp-controller" 51 | 52 | # vim: set ts=2 sw=2 tw=0 noet : 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TAPP 2 | 3 | TAPP is a CustomResourceDefinition(CRD) based app kind, it contains most features of kubernetes `deployment` and `statefulset`, and it is easy for users to run legacy applications on Kubernetes. Nowadays, many customers want to adopt Kubernetes, and migrate their legacy applications to Kubernetes. However they could not use Kubernetes’ workloads(e.g. `deployment`, `statefulset`) directly, and it will take a lot of effort to transform those applications to microservices. TAPP could solve these problems. 4 | 5 | ## Features 6 | 7 | * Support unique index for each instance(same as `statefulset`) 8 | 9 | * Support operating(start/stop/upgrade) on specific instances(pods) 10 | 11 | It is more suitable for traditional operation and maintenance, e.g. when administor want to stop one machine, he could just stop instances on that machine, and do not affect instances on other machines. 12 | 13 | * Support in place update for instances 14 | 15 | While many stateless workloads are designed to withstand such a disruption, some are more sensitive, a Pod restart is a serious disruption, resulting in lower availability or higher cost of running. 16 | 17 | * Support multi versions of instances 18 | 19 | Instances use different images or different config. 20 | 21 | * Support Horizontal Pod Autoscaler(HPA) according to a lot kinds of metrics(e.g. CPU, memory, custom metrics) 22 | 23 | * Support rolling update, rolling back 24 | 25 | ## Usage 26 | 27 | Find more usage at [tutorial.md](doc/tutorial.md). 28 | 29 | ## Build 30 | 31 | ``` sh 32 | $ make build 33 | ``` 34 | 35 | ## Run 36 | 37 | ```sh 38 | # assumes you have a working kubeconfig, not required if operating in-cluster 39 | $ bin/tapp-controller --master=127.0.0.1:8080 // Assume 127.0.0.1:8080 is k8s master ip:port 40 | or 41 | $ bin/tapp-controller --kubeconfig=$HOME/.kube/config 42 | 43 | # create a custom resource of type TApp 44 | $ kubectl create -f artifacts/examples/example-tapp.yaml 45 | 46 | # check pods created through the custom resource 47 | $ kubectl get pods 48 | ``` 49 | 50 | ## Cleanup 51 | 52 | You can clean up the created CustomResourceDefinition with: 53 | 54 | $ kubectl delete crd tapps.apps.tkestack.io 55 | -------------------------------------------------------------------------------- /artifacts/examples/crd.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1beta1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: tapps.apps.tkestack.io 5 | spec: 6 | group: apps.tkestack.io 7 | version: v1 8 | names: 9 | kind: TApp 10 | listKind: TAppList 11 | plural: tapps 12 | singular: tapp 13 | scope: Namespaced 14 | subresources: 15 | status: {} 16 | scale: 17 | labelSelectorPath: .status.scaleLabelSelector 18 | specReplicasPath: .spec.replicas 19 | statusReplicasPath: .status.replicas 20 | -------------------------------------------------------------------------------- /artifacts/examples/example-tapp-volume.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps.tkestack.io/v1 2 | kind: TApp 3 | metadata: 4 | name: example-tapp 5 | spec: 6 | replicas: 2 7 | template: 8 | metadata: 9 | labels: 10 | app: example-tapp 11 | spec: 12 | containers: 13 | - name: nginx 14 | image: nginx:1.7.9 15 | volumeMounts: 16 | - name: www 17 | mountPath: /usr/share/nginx/html 18 | volumeClaimTemplates: 19 | - metadata: 20 | name: www 21 | labels: 22 | app: example-tapp 23 | spec: 24 | accessModes: [ "ReadWriteOnce" ] 25 | storageClassName: xfs-rbd 26 | resources: 27 | requests: 28 | storage: 1Gi 29 | -------------------------------------------------------------------------------- /artifacts/examples/example-tapp.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps.tkestack.io/v1 2 | kind: TApp 3 | metadata: 4 | name: example-tapp 5 | spec: 6 | replicas: 3 7 | template: 8 | metadata: 9 | labels: 10 | app: example-tapp 11 | spec: 12 | containers: 13 | - name: nginx 14 | image: nginx:1.7.9 15 | -------------------------------------------------------------------------------- /build/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | From alpine:3.10 2 | 3 | ADD tapp-controller /usr/local/bin 4 | RUN mkdir /etc/certs 5 | ADD ca.crt /etc/certs 6 | ADD tls.crt /etc/certs 7 | ADD tls.key /etc/certs 8 | 9 | ENTRYPOINT ["/usr/local/bin/tapp-controller"] 10 | -------------------------------------------------------------------------------- /build/docker/Dockerfile_arch: -------------------------------------------------------------------------------- 1 | # Tencent is pleased to support the open source community by making TKEStack 2 | # available. 3 | # 4 | # Copyright (C) 2012-2019 Tencent. All Rights Reserved. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not use 7 | # this file except in compliance with the License. You may obtain a copy of the 8 | # License at 9 | # 10 | # https://opensource.org/licenses/Apache-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | # WARRANTIES OF ANY KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations under the License. 16 | 17 | FROM golang:1.14.4 AS builder 18 | ARG TARGETPLATFORM 19 | RUN echo "building for ${TARGETPLATFORM}" 20 | ARG WORKDIR="/go/src/tkestack.io/tapp/" 21 | RUN mkdir -p ${WORKDIR} 22 | WORKDIR ${WORKDIR} 23 | ## cache dependancies if we won't change mod/sum 24 | COPY go.mod go.sum ${WORKDIR} 25 | RUN go mod download 26 | 27 | COPY . ${WORKDIR} 28 | RUN make build 29 | 30 | 31 | FROM alpine:3.10 32 | 33 | RUN mkdir /etc/certs 34 | ADD build/docker/ca.crt /etc/certs 35 | ADD build/docker/tls.crt /etc/certs 36 | ADD build/docker/tls.key /etc/certs 37 | COPY --from=builder /go/src/tkestack.io/tapp/bin/tapp-controller /usr/local/bin 38 | 39 | ENTRYPOINT ["/usr/local/bin/tapp-controller"] 40 | -------------------------------------------------------------------------------- /build/docker/ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICyjCCAbICCQCvul1DxkEzIzANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtB 3 | ZG1pc3Npb24gV2ViaG9vayBTZXJ2ZXIgQ0EwIBcNMjEwMTIwMTExNjA0WhgPMjI5 4 | NDExMDUxMTE2MDRaMCYxJDAiBgNVBAMMG0FkbWlzc2lvbiBXZWJob29rIFNlcnZl 5 | ciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKfJGUxPH/jmz/dp 6 | uMvNQaw13l23Dbxny66mhCGoTpFbWovaEe8774qF9jsjOTIBJPUxEV4rVlO2po6M 7 | uH1v3Rw9daC4mlzfZAyCygZsY4zCNoAVOw8kZ7d62mPK3RZdrPxBkKwaauIBQsdw 8 | dgm3oxwouwZ2MIEdg4Cp/ZnQiDx06689J+FLMBcp4kIlHJJn/mNnfj23ahHAD3uW 9 | pfSivrv0pQOchLe/+p5tcvNEUehDcOtagIEI4HCOG5cdx7mgsrf4DTVY8ZS7iPH/ 10 | Ymcb8/bofyA6xA3tGS2q2WIkBLp1hjp3VeQSrrAxV7ViJ0VUeuAuq2DUW3UOYKs5 11 | Dt7XyKUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAB25Z5RxqI4VaID8Ut88Qm1uB 12 | 9UfVpdY3SkNgeT8ucsGo+2RPx9vXymdHKvdzu6E8xfAVgYe83C8sLh8XrwrIkdJ/ 13 | scsfQwW7b+TdDsBKtx572GhvXOyQueTffDNy20gnTiKaMW3w5iEETs6yglFkmENk 14 | IvN8lTh8UT15mg30jOA5iZY4llLAab0Q827YCpS+Y1G4Vcn0ef/49tNBW07dDvsu 15 | IQsQWX1Jp1UU3L3CaqUJgB++kYf2oEymG9OXmNWx6i+042BvI1ICcr9m6kOyl/Ca 16 | L68EfXf2AJjOnvnpKwIXNuQ9NhP1dnX/VTq99aP2It0oTEyOeCw43WxctbXO6Q== 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /build/docker/tls.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDUjCCAjqgAwIBAgIJALiiZ6FAH0h7MA0GCSqGSIb3DQEBBQUAMCYxJDAiBgNV 3 | BAMMG0FkbWlzc2lvbiBXZWJob29rIFNlcnZlciBDQTAgFw0yMTAxMjAxMTE2MDRa 4 | GA8yMjk0MTEwNTExMTYwNFowSjELMAkGA1UEBhMCQ04xETAPBgNVBAoMCHRrZXN0 5 | YWNrMSgwJgYDVQQDDB90YXBwLWNvbnRyb2xsZXIua3ViZS1zeXN0ZW0uc3ZjMIIB 6 | IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu8wZaTuq2vYdxWq/VDxnAMqI 7 | BKinyTtxRmgJhA8e7p1Qpxs9omLbiX79HbLaazuZGGqi+aR4+8ixmjPOCJltdGlk 8 | gc5UVppKyrMVUoQsB+5BbCbjSvTMM0FMTdOj0tFXWH0TLvzfDKL37JFmeEiGiUS6 9 | 16AYYg0i153PigjwHMXKRtz/LyPALNlUNnAP+Ql8TVPLb2JhF19E+blMb6wqIrVx 10 | xEOihzcHYzHs6OR09+xJUNGKlqxFyKm5Brby3NzpkZzuqU2SaRy7E69v4hW53358 11 | HJ80ZEUlN6Ttahd0LnhJ14qwP2Mu0O1+FNpd1Y0zSZGZiIGH0OCXBXtkemN32wID 12 | AQABo10wWzAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsG 13 | AQUFBwMBMCoGA1UdEQQjMCGCH3RhcHAtY29udHJvbGxlci5rdWJlLXN5c3RlbS5z 14 | dmMwDQYJKoZIhvcNAQEFBQADggEBAHY1WmXIxOx4hkYuSi2Amf+hWqeIluYBLclg 15 | olIJrEP3s/b8XQPVv8bM1R9+cTvV/p5LFjeLZugquNiEDSDMlGCSXbvMMoYytZL7 16 | T8u77Ou61JcMBX65XAYKv0hZ3pgNaJVmU6hA+WIzY9tCrnIo2dwHMo4VroGUaC2p 17 | Ce/sopBDK3cJWXqYkxvbqukDXxdMfeZtWMJFQncYSkLExZlVdaoED0OYOmmvcfWK 18 | uVG6QZ4Fel/sKi3cw4AXsgGZuqFNZFSD4kbK8gf8FOpbmQ6GVctLz2enEx8BHbi0 19 | DlziHIkIikTQRcgYN4zcLFljggd5LERyHdsLRSuj0G8opc32GyE= 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /build/docker/tls.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEAu8wZaTuq2vYdxWq/VDxnAMqIBKinyTtxRmgJhA8e7p1Qpxs9 3 | omLbiX79HbLaazuZGGqi+aR4+8ixmjPOCJltdGlkgc5UVppKyrMVUoQsB+5BbCbj 4 | SvTMM0FMTdOj0tFXWH0TLvzfDKL37JFmeEiGiUS616AYYg0i153PigjwHMXKRtz/ 5 | LyPALNlUNnAP+Ql8TVPLb2JhF19E+blMb6wqIrVxxEOihzcHYzHs6OR09+xJUNGK 6 | lqxFyKm5Brby3NzpkZzuqU2SaRy7E69v4hW53358HJ80ZEUlN6Ttahd0LnhJ14qw 7 | P2Mu0O1+FNpd1Y0zSZGZiIGH0OCXBXtkemN32wIDAQABAoIBAAUWFw4ZlpwNlGrX 8 | ZE00wZls4tg1dS3nFT9R7AgOnMjbq+aKv1WZldaYgOSABphm6dOWd8mJIdm36s+B 9 | XbAv7536iMVXQEOENEhfJ1Gv0L16P31dZESQcsNknltxQvufvdzgrldUc/oUo+Bd 10 | Y8gYNsSa/vB+HorxTiNG2+siKNaunNY1WBklK2BO1a8m/DOuAHx9hijQl/XVPY5y 11 | ISMT3E0ldDS7D/zX1Ol4tDZvMSHKPqZ/pAwT9xZRFzo6dbTpxGUDew7MtBphDikZ 12 | HRvZpbunYt1yLyuY6mIhKkK+OHDiu3wyyBwW20qCue/WvK9AWcDZJRg/aupf/M0f 13 | 37jQfRECgYEA3UkqUXLJe5VlLbEWtptwMkUHhdYCMiDoFYbabdnP9AHgAZppHd8G 14 | jKOgCzmQNHVLn1Gd3B/QLmv9aTSIWVOb2fDEeoMM3bGM9EmMGHRuryQxKrlute36 15 | q5One5sur+QvpsmKq161BSqCJWpRIJjcdZ8Sg41riPfqsiDsqfoPjzMCgYEA2UII 16 | 4ZYYIzTq2Vy5o/p8kHzUnYcYP1fJ6h8e9zfTW+oQOzTGp9yRuQKLubbqKihustTm 17 | 3/lhw6uu1FFvE+3/06mEXCrnAwT84JV0zItl6YQw1KgfPgZcEs0ozNPjQnjX8YX1 18 | A3Q6S3108eVtVAa+uuWkvKYQM/PM6KBiww43FLkCgYBAVR4Ncc6rtCInJ33P9t6m 19 | brUiLraStkhiwcLM/u7bJJRoQujee1FfH87OlJyc86DZn5PCRCl05YZVyKT/OzRr 20 | JvkoGONrrEurVZp6HZd0KZR1tFVAZQBkU1f0soffMPq9hYhgC/eameIeWItfa7Fk 21 | LKvoPPwPZOwBf1rui7lsSwKBgCaqqgn8PK+Ha4TkC+YzXpgYn36p3JbpePZCM7Cs 22 | LuHc9qaS7ghSKV1UJqoW8/Ys3AbX/X8/UzcQBz2igLJ7WVzCftwohpTy/k66St9c 23 | r/avoEE46taPKzPtb6WE0J20BDroLINA3F8zJO0oeBzMVoXM+VTZ+WhFq+J7KRiu 24 | Hv+BAoGAUGUo4gEvrqSDhWflS/HGPjrV+OFI0oSkuusxEqB7KR6Nla3ktHvIbHmQ 25 | /S2NsrhbJFRvEKcTTULjVBs+mqmelqeU0RbA8EXrLqJC50qfuCv2zsNU0oqpUcD+ 26 | HSneW4Iske38XOEdwIfwwKwN/51VicOOaoFqPnoCMHDybI5fh9o= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /build/lib/common.mk: -------------------------------------------------------------------------------- 1 | # Tencent is pleased to support the open source community by making TKEStack 2 | # available. 3 | # 4 | # Copyright (C) 2012-2019 Tencent. All Rights Reserved. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not use 7 | # this file except in compliance with the License. You may obtain a copy of the 8 | # License at 9 | # 10 | # https://opensource.org/licenses/Apache-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | # WARRANTIES OF ANY KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations under the License. 16 | 17 | SHELL := /bin/bash 18 | 19 | # include the common make file 20 | COMMON_SELF_DIR := $(dir $(lastword $(MAKEFILE_LIST))) 21 | 22 | ifeq ($(origin ROOT_DIR),undefined) 23 | ROOT_DIR := $(abspath $(shell cd $(COMMON_SELF_DIR)/../.. && pwd -P)) 24 | endif 25 | ifeq ($(origin OUTPUT_DIR),undefined) 26 | OUTPUT_DIR := $(ROOT_DIR)/_output 27 | $(shell mkdir -p $(OUTPUT_DIR)) 28 | endif 29 | ifeq ($(origin TOOLS_DIR),undefined) 30 | TOOLS_DIR := $(OUTPUT_DIR)/tools 31 | $(shell mkdir -p $(TOOLS_DIR)) 32 | endif 33 | ifeq ($(origin TMP_DIR),undefined) 34 | TMP_DIR := $(OUTPUT_DIR)/tmp 35 | $(shell mkdir -p $(TMP_DIR)) 36 | endif 37 | 38 | # set the version number. you should not need to do this 39 | # for the majority of scenarios. 40 | ifeq ($(origin VERSION), undefined) 41 | VERSION := $(shell git describe --dirty --always --tags | sed 's/-/./g') 42 | endif 43 | GIT_COMMIT:=$(shell git log --first-parent -1 --oneline | awk '{print $$1}') 44 | 45 | PLATFORMS ?= linux_amd64 linux_arm64 46 | 47 | # Set a specific PLATFORM 48 | # Target OS must be linux 49 | ifeq ($(origin PLATFORM), undefined) 50 | ifeq ($(origin GOOS), undefined) 51 | GOOS := linux 52 | endif 53 | ifeq ($(origin GOARCH), undefined) 54 | GOARCH := $(shell go env GOARCH) 55 | endif 56 | PLATFORM := $(GOOS)_$(GOARCH) 57 | else 58 | GOOS := $(word 1, $(subst _, ,$(PLATFORM))) 59 | GOARCH := $(word 2, $(subst _, ,$(PLATFORM))) 60 | endif 61 | 62 | COMMA := , 63 | SPACE := 64 | SPACE += 65 | -------------------------------------------------------------------------------- /build/lib/create-manifest.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Tencent is pleased to support the open source community by making TKEStack 4 | # available. 5 | # 6 | # Copyright (C) 2012-2019 Tencent. All Rights Reserved. 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not use 9 | # this file except in compliance with the License. You may obtain a copy of the 10 | # License at 11 | # 12 | # https://opensource.org/licenses/Apache-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 16 | # WARRANTIES OF ANY KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations under the License. 18 | 19 | set -o errexit 20 | set -o nounset 21 | set -o pipefail 22 | 23 | REGISTRY_PREFIX=${REGISTRY_PREFIX:-"tkestack"} 24 | PLATFORMS=${PLATFORMS:-"linux_amd64 linux_arm64"} 25 | 26 | if [ -z ${IMAGE} ]; then 27 | echo "Please provide IMAGE." 28 | exit 1 29 | fi 30 | 31 | if [ -z ${VERSION} ]; then 32 | echo "Please provide VERSION." 33 | exit 1 34 | fi 35 | 36 | rm -rf ${HOME}/.docker/manifests/docker.io_${REGISTRY_PREFIX}_${IMAGE}-${VERSION} 37 | DES_REGISTRY=${REGISTRY_PREFIX}/${IMAGE} 38 | for platform in ${PLATFORMS}; do 39 | os=${platform%_*} 40 | arch=${platform#*_} 41 | variant="" 42 | if [ ${arch} == "arm64" ]; then 43 | variant="--variant v8" 44 | fi 45 | 46 | docker manifest create --amend ${DES_REGISTRY}:${VERSION} \ 47 | ${DES_REGISTRY}-${arch}:${VERSION} 48 | 49 | docker manifest annotate ${DES_REGISTRY}:${VERSION} \ 50 | ${DES_REGISTRY}-${arch}:${VERSION} \ 51 | --os ${os} --arch ${arch} ${variant} 52 | done 53 | docker manifest push --purge ${DES_REGISTRY}:${VERSION} 54 | -------------------------------------------------------------------------------- /build/lib/image.mk: -------------------------------------------------------------------------------- 1 | # Tencent is pleased to support the open source community by making TKEStack 2 | # available. 3 | # 4 | # Copyright (C) 2012-2019 Tencent. All Rights Reserved. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not use 7 | # this file except in compliance with the License. You may obtain a copy of the 8 | # License at 9 | # 10 | # https://opensource.org/licenses/Apache-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | # WARRANTIES OF ANY KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations under the License. 16 | 17 | # ============================================================================== 18 | # Makefile helper functions for docker image 19 | 20 | DOCKER := DOCKER_CLI_EXPERIMENTAL=enabled docker 21 | DOCKER_SUPPORTED_API_VERSION ?= 1.40 22 | DOCKER_VERSION ?= 19.03 23 | 24 | REGISTRY_PREFIX ?= tkestack 25 | 26 | EXTRA_ARGS ?= 27 | _DOCKER_BUILD_EXTRA_ARGS := 28 | 29 | ifdef HTTP_PROXY 30 | _DOCKER_BUILD_EXTRA_ARGS += --build-arg http_proxy=${HTTP_PROXY} 31 | endif 32 | ifdef HTTPS_PROXY 33 | _DOCKER_BUILD_EXTRA_ARGS += --build-arg https_proxy=${HTTPS_PROXY} 34 | endif 35 | 36 | ifneq ($(EXTRA_ARGS), ) 37 | _DOCKER_BUILD_EXTRA_ARGS += $(EXTRA_ARGS) 38 | endif 39 | 40 | .PHONY: image.verify 41 | image.verify: 42 | $(eval API_VERSION := $(shell $(DOCKER) version | grep -E 'API version: {1,6}[0-9]' | head -n1 | awk '{print $$3} END { if (NR==0) print 0}' )) 43 | $(eval PASS := $(shell echo "$(API_VERSION) >= $(DOCKER_SUPPORTED_API_VERSION)" | bc)) 44 | @if [ $(PASS) -ne 1 ]; then \ 45 | $(DOCKER) -v ;\ 46 | echo "Unsupported docker version. Docker API version should be greater than $(DOCKER_SUPPORTED_API_VERSION) (Or docker version: $(DOCKER_VERSION))"; \ 47 | exit 1; \ 48 | fi 49 | 50 | .PHONY: image.buildx.verify 51 | image.buildx.verify: image.verify 52 | $(eval PASS := $(shell $(DOCKER) buildx version > /dev/null && echo 1 || echo 0)) 53 | @if [ $(PASS) -ne 1 ]; then \ 54 | $(MAKE) image.buildx.install; \ 55 | fi 56 | 57 | .PHONY: image.buildx.install 58 | image.buildx.install: 59 | @$(ROOT_DIR)/build/lib/install-buildx.sh 60 | 61 | .PHONY: image.build 62 | image.build: image.buildx.verify $(addprefix image.build., $(addprefix $(PLATFORM)., $(BINS))) 63 | 64 | .PHONY: image.build.multiarch 65 | image.build.multiarch: image.buildx.verify $(foreach p,$(PLATFORMS),$(addprefix image.build., $(addprefix $(p)., $(BINS)))) 66 | 67 | .PHONY: image.build.% 68 | image.build.%: 69 | $(eval PLATFORM := $(word 1,$(subst ., ,$*))) 70 | $(eval IMAGE := $(word 2,$(subst ., ,$*))) 71 | $(eval OS := $(word 1,$(subst _, ,$(PLATFORM)))) 72 | $(eval ARCH := $(word 2,$(subst _, ,$(PLATFORM)))) 73 | $(eval IMAGE_PLAT := $(subst _,/,$(PLATFORM))) 74 | $(eval IMAGE_NAME := $(REGISTRY_PREFIX)/$(IMAGE)-$(ARCH):$(VERSION)) 75 | @echo "===========> Building docker image $(IMAGE) $(VERSION) for $(IMAGE_PLAT)" 76 | $(DOCKER) buildx build --platform $(IMAGE_PLAT) --load -t $(IMAGE_NAME) $(_DOCKER_BUILD_EXTRA_ARGS) \ 77 | -f $(ROOT_DIR)/build/docker/Dockerfile_arch $(ROOT_DIR) 78 | 79 | .PHONY: image.push 80 | image.push: image.buildx.verify $(addprefix image.push., $(addprefix $(PLATFORM)., $(BINS))) 81 | 82 | .PHONY: image.push.multiarch 83 | image.push.multiarch: image.buildx.verify $(foreach p,$(PLATFORMS),$(addprefix image.push., $(addprefix $(p)., $(BINS)))) 84 | 85 | .PHONY: image.push.% 86 | image.push.%: image.build.% 87 | @echo "===========> Pushing image $(IMAGE) $(VERSION) to $(REGISTRY_PREFIX)" 88 | $(DOCKER) push $(REGISTRY_PREFIX)/$(IMAGE)-$(ARCH):$(VERSION) 89 | 90 | .PHONY: image.manifest.push 91 | image.manifest.push: export DOCKER_CLI_EXPERIMENTAL := enabled 92 | image.manifest.push: image.buildx.verify $(addprefix image.manifest.push., $(addprefix $(PLATFORM)., $(BINS))) 93 | 94 | .PHONY: image.manifest.push.% 95 | image.manifest.push.%: image.push.% image.manifest.remove.% 96 | @echo "===========> Pushing manifest $(IMAGE) $(VERSION) to $(REGISTRY_PREFIX) and then remove the local manifest list" 97 | @$(DOCKER) manifest create $(REGISTRY_PREFIX)/$(IMAGE):$(VERSION) \ 98 | $(REGISTRY_PREFIX)/$(IMAGE)-$(ARCH):$(VERSION) 99 | @$(DOCKER) manifest annotate $(REGISTRY_PREFIX)/$(IMAGE):$(VERSION) \ 100 | $(REGISTRY_PREFIX)/$(IMAGE)-$(ARCH):$(VERSION) \ 101 | --os $(OS) --arch ${ARCH} 102 | @$(DOCKER) manifest push --purge $(REGISTRY_PREFIX)/$(IMAGE):$(VERSION) 103 | 104 | # Docker cli has a bug: https://github.com/docker/cli/issues/954 105 | # If you find your manifests were not updated, 106 | # Please manually delete them in $HOME/.docker/manifests/ 107 | # and re-run. 108 | .PHONY: image.manifest.remove.% 109 | image.manifest.remove.%: 110 | @rm -rf ${HOME}/.docker/manifests/docker.io_$(REGISTRY_PREFIX)_$(IMAGE)-$(VERSION) 111 | 112 | .PHONY: image.manifest.push.multiarch 113 | image.manifest.push.multiarch: image.push.multiarch $(addprefix image.manifest.push.multiarch., $(BINS)) 114 | 115 | .PHONY: image.manifest.push.multiarch.% 116 | image.manifest.push.multiarch.%: 117 | @echo "===========> Pushing manifest $* $(VERSION) to $(REGISTRY_PREFIX) and then remove the local manifest list" 118 | REGISTRY_PREFIX=$(REGISTRY_PREFIX) PLATFROMS="$(PLATFORMS)" IMAGE=$* VERSION=$(VERSION) DOCKER_CLI_EXPERIMENTAL=enabled \ 119 | $(ROOT_DIR)/build/lib/create-manifest.sh 120 | -------------------------------------------------------------------------------- /build/lib/install-buildx.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Tencent is pleased to support the open source community by making TKEStack 4 | # available. 5 | # 6 | # Copyright (C) 2012-2019 Tencent. All Rights Reserved. 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not use 9 | # this file except in compliance with the License. You may obtain a copy of the 10 | # License at 11 | # 12 | # https://opensource.org/licenses/Apache-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 16 | # WARRANTIES OF ANY KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations under the License. 18 | 19 | set -o errexit 20 | set -o nounset 21 | set -o pipefail 22 | 23 | BUILDX_VERSION=${BUILDX_VERSION:-"v0.4.1"} 24 | ARCH=${ARCH:-"amd64"} 25 | BUILDX_BIN="https://github.com/docker/buildx/releases/download/${BUILDX_VERSION}/buildx-${BUILDX_VERSION}.linux-${ARCH}" 26 | 27 | echo "Downloading docker-buildx" 28 | wget -c ${BUILDX_BIN} -O ./docker-buildx 29 | mkdir -p ~/.docker/cli-plugins/ 30 | mv ./docker-buildx ~/.docker/cli-plugins/ 31 | chmod a+x ~/.docker/cli-plugins/docker-buildx 32 | docker buildx version 33 | -------------------------------------------------------------------------------- /deployment/tapp-controller.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: tapp-controller 6 | namespace: kube-system 7 | 8 | --- 9 | kind: ClusterRoleBinding 10 | apiVersion: rbac.authorization.k8s.io/v1 11 | metadata: 12 | name: tapp-controller-role-binding 13 | subjects: 14 | - kind: ServiceAccount 15 | name: tapp-controller 16 | namespace: kube-system 17 | roleRef: 18 | kind: ClusterRole 19 | name: cluster-admin 20 | apiGroup: rbac.authorization.k8s.io 21 | 22 | --- 23 | kind: Deployment 24 | apiVersion: apps/v1 25 | metadata: 26 | name: tapp-controller 27 | namespace: kube-system 28 | spec: 29 | selector: 30 | matchLabels: 31 | app: tapp-controller 32 | replicas: 1 33 | template: 34 | metadata: 35 | labels: 36 | app: tapp-controller 37 | spec: 38 | serviceAccountName: tapp-controller 39 | containers: 40 | - name: tapp-controller 41 | image: tkestack/tapp-controller:v1.1.1 42 | args: 43 | - "--v=3" 44 | - "--register-admission=true" 45 | imagePullPolicy: "Always" 46 | resources: 47 | requests: 48 | "cpu": "1" 49 | "memory": "512Mi" 50 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module tkestack.io/tapp 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/evanphx/json-patch v4.5.0+incompatible // indirect 7 | github.com/gogo/protobuf v1.3.1 // indirect 8 | github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 // indirect 9 | github.com/googleapis/gnostic v0.3.1 // indirect 10 | github.com/hashicorp/golang-lru v0.5.3 // indirect 11 | github.com/imdario/mergo v0.3.8 // indirect 12 | github.com/json-iterator/go v1.1.8 // indirect 13 | github.com/mitchellh/gox v1.0.1 // indirect 14 | github.com/modern-go/reflect2 v1.0.1 // indirect 15 | github.com/onsi/ginkgo v1.10.3 // indirect 16 | github.com/onsi/gomega v1.7.1 // indirect 17 | github.com/pkg/errors v0.8.1 // indirect 18 | github.com/spf13/pflag v1.0.5 19 | golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 // indirect 20 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6 // indirect 21 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect 22 | gopkg.in/inf.v0 v0.9.1 // indirect 23 | k8s.io/api v0.0.0-20191004102349-159aefb8556b 24 | k8s.io/apiextensions-apiserver v0.0.0-20191212015246-8fe0c124fb40 25 | k8s.io/apimachinery v0.0.0-20191004074956-c5d2f014d689 26 | k8s.io/client-go v11.0.1-0.20191029005444-8e4128053008+incompatible 27 | k8s.io/component-base v0.0.0-20191212015026-7bd5511f1bb2 28 | k8s.io/klog v1.0.0 29 | k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a // indirect 30 | k8s.io/kubernetes v1.14.10 31 | k8s.io/utils v0.0.0-20191114200735-6ca3b61696b6 // indirect 32 | ) 33 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= 3 | github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 4 | github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= 5 | github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 7 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 9 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 10 | github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= 11 | github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= 12 | github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= 13 | github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= 14 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= 15 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 16 | github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 17 | github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= 18 | github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= 19 | github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= 20 | github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= 21 | github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= 22 | github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= 23 | github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= 24 | github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 h1:uHTyIjqVhYRhLbJ8nIiOJHkEZZ+5YoOsAbD3sk82NiE= 25 | github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 26 | github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 27 | github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= 28 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 29 | github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= 30 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 31 | github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= 32 | github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= 33 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 34 | github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k= 35 | github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= 36 | github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk= 37 | github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= 38 | github.com/hashicorp/go-version v1.0.0 h1:21MVWPKDphxa7ineQQTrCU5brh7OuVVAzGOCnnCPtE8= 39 | github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 40 | github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= 41 | github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= 42 | github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= 43 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 44 | github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= 45 | github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= 46 | github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 47 | github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= 48 | github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 49 | github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 50 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 51 | github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 52 | github.com/mitchellh/gox v1.0.1 h1:x0jD3dcHk9a9xPSDN6YEL4xL6Qz0dvNYm8yZqui5chI= 53 | github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4= 54 | github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= 55 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 56 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 57 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 58 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 59 | github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 60 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 61 | github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= 62 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 63 | github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 64 | github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 65 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 66 | github.com/onsi/ginkgo v1.10.3 h1:OoxbjfXVZyod1fmWYhI7SEyaD8B00ynP3T+D5GiyHOY= 67 | github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 68 | github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= 69 | github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ= 70 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 71 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 72 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 73 | github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 74 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 75 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 76 | github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= 77 | github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 78 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 79 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 80 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 81 | github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 82 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 83 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 84 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 85 | golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g= 86 | golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 87 | golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 88 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 89 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= 90 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 91 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 92 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= 93 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 94 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6 h1:pE8b58s1HRDMi8RDc79m0HISf9D4TzseP40cEA6IGfs= 95 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 96 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 97 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 98 | golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 99 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 100 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 101 | golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= 102 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 103 | golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 104 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 105 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 106 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= 107 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 108 | golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 109 | golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 110 | google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= 111 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 112 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 113 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 114 | gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= 115 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 116 | gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= 117 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 118 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 119 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 120 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 121 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 122 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 123 | gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= 124 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 125 | k8s.io/api v0.0.0-20191004102349-159aefb8556b h1:mja4wDOEhOlKPQ47X/wU/8SUKoakPfOImcZr0Jp4Ilg= 126 | k8s.io/api v0.0.0-20191004102349-159aefb8556b/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= 127 | k8s.io/apiextensions-apiserver v0.0.0-20191212015246-8fe0c124fb40 h1:oYShjyFmoEZy/xPVKlZt5PGvITFECyM/Yo3vaQah7Jk= 128 | k8s.io/apiextensions-apiserver v0.0.0-20191212015246-8fe0c124fb40/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE= 129 | k8s.io/apimachinery v0.0.0-20191004074956-c5d2f014d689 h1:q9CWH+mCm21qUeXH537D0Q9K1jdEkreNSRU5E7jh+QM= 130 | k8s.io/apimachinery v0.0.0-20191004074956-c5d2f014d689/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= 131 | k8s.io/client-go v11.0.1-0.20191029005444-8e4128053008+incompatible h1:RfzLv39xcqH6QZ+6hgbCsHeH7cXvCA+hwLGHwz8Qg7c= 132 | k8s.io/client-go v11.0.1-0.20191029005444-8e4128053008+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= 133 | k8s.io/component-base v0.0.0-20191212015026-7bd5511f1bb2 h1:ATxRahX+o2621GX/gwzXzSDm596lltaoHk9/CdC/v0k= 134 | k8s.io/component-base v0.0.0-20191212015026-7bd5511f1bb2/go.mod h1:DMaomcf3j3MM2j1FsvlLVVlc7wA2jPytEur3cP9zRxQ= 135 | k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= 136 | k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= 137 | k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= 138 | k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= 139 | k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= 140 | k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU= 141 | k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= 142 | k8s.io/kubernetes v1.14.10 h1:P9qsSUUdx0MZKtJH9kvTHFTZamOH/lZjdcpmDhx0C9g= 143 | k8s.io/kubernetes v1.14.10/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= 144 | k8s.io/utils v0.0.0-20191114200735-6ca3b61696b6 h1:p0Ai3qVtkbCG/Af26dBmU0E1W58NID3hSSh7cMyylpM= 145 | k8s.io/utils v0.0.0-20191114200735-6ca3b61696b6/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= 146 | sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= 147 | sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= 148 | sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= 149 | -------------------------------------------------------------------------------- /hack/build-image.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | 7 | IMAGE=$1 8 | 9 | ROOT=$(cd $(dirname "${BASH_SOURCE}")/.. && pwd -P) 10 | GIT_VERSION_FILE="${ROOT}/.version-defs" 11 | BIN="build/docker/tapp-controller" 12 | 13 | source "${ROOT}/hack/lib/version.sh" 14 | source "${ROOT}/hack/lib/golang.sh" 15 | 16 | if [[ -f ${GIT_VERSION_FILE} ]]; then 17 | api::version::load_version_vars "${GIT_VERSION_FILE}" 18 | else 19 | api::version::get_version_vars 20 | api::version::save_version_vars "${GIT_VERSION_FILE}" 21 | fi 22 | 23 | export PATH=$PATH:$(go env GOPATH)/bin 24 | go get github.com/mitchellh/gox@v1.0.1 25 | 26 | cd $ROOT 27 | CGO_ENABLED=0 gox -osarch="linux/amd64" -ldflags "$(api::version::ldflags)" \ 28 | -output=$BIN 29 | docker build build/docker/ -t $IMAGE 30 | rm $BIN 31 | -------------------------------------------------------------------------------- /hack/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | 7 | ROOT=$(cd $(dirname "${BASH_SOURCE}")/.. && pwd -P) 8 | GIT_VERSION_FILE="${ROOT}/.version-defs" 9 | 10 | source "${ROOT}/hack/lib/version.sh" 11 | source "${ROOT}/hack/lib/golang.sh" 12 | 13 | if [[ -f ${GIT_VERSION_FILE} ]]; then 14 | api::version::load_version_vars "${GIT_VERSION_FILE}" 15 | else 16 | api::version::get_version_vars 17 | api::version::save_version_vars "${GIT_VERSION_FILE}" 18 | fi 19 | 20 | cd $ROOT 21 | CGO_ENABLED=0 go build -o bin/tapp-controller -ldflags "$(api::version::ldflags)" . 22 | if [ $? -eq 0 ]; then 23 | echo "Build success!" 24 | else 25 | echo "Faild to build!" 26 | fi 27 | -------------------------------------------------------------------------------- /hack/format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o nounset 4 | set -o pipefail 5 | 6 | ROOT=$(cd $(dirname "${BASH_SOURCE}")/.. && pwd -P) 7 | source "${ROOT}/hack/lib/lib.sh" 8 | 9 | GOIMPORT="gofmt -s -d -w" 10 | find_files | xargs $GOIMPORT 11 | if [ $? -ne 0 ]; then 12 | echo "Failed to format" 13 | else 14 | echo "Format successfully" 15 | fi 16 | -------------------------------------------------------------------------------- /hack/gencerts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | set -x 5 | 6 | if [ $# -ne 2 ]; then 7 | echo "Usage: $0 $key_dir $namespace" 8 | echo " key_dir: used to put certs in" 9 | echo " namespace: the namespace that webhook-server will be deployed in" 10 | exit 1 11 | fi 12 | 13 | key_dir="$1" 14 | namespace="$2" 15 | 16 | mkdir -p $key_dir 17 | chmod 0700 $key_dir 18 | cd $key_dir 19 | 20 | SANCNF=san.cnf 21 | 22 | cat << EOF > ${SANCNF} 23 | [req] 24 | distinguished_name = req_distinguished_name 25 | req_extensions = v3_req 26 | prompt = no 27 | 28 | [req_distinguished_name] 29 | C = CN 30 | O = tkestack 31 | CN = tapp-controller.kube-system.svc 32 | 33 | [v3_req] 34 | keyUsage = critical, digitalSignature, keyEncipherment 35 | extendedKeyUsage = clientAuth, serverAuth 36 | subjectAltName = @alt_names 37 | [alt_names] 38 | DNS.1=tapp-controller.kube-system.svc 39 | EOF 40 | 41 | 42 | # Generate the CA cert and private key 43 | openssl req -nodes -new -x509 -days 100000 -keyout ca.key -out ca.crt -subj "/CN=Admission Webhook Server CA" 44 | # Generate the private key for the webhook server 45 | openssl genrsa -out tls.key 2048 46 | # Generate a Certificate Signing Request (CSR) for the private key, and sign it with the private key of the CA. 47 | openssl req -new -sha256 -days 100000 -key tls.key -subj "/CN=tapp-controller.kube-system.svc" -reqexts v3_req -config ${SANCNF} \ 48 | | openssl x509 -req -days 100000 -CA ca.crt -CAkey ca.key -CAcreateserial -extensions v3_req -extfile ${SANCNF} -out tls.crt 49 | -------------------------------------------------------------------------------- /hack/import_checker.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack available. 3 | * 4 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 7 | * this file except in compliance with the License. You may obtain a copy of the 8 | * License at 9 | * 10 | * https://opensource.org/licenses/Apache-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations under the License. 16 | */ 17 | 18 | package main 19 | 20 | import ( 21 | "bytes" 22 | "fmt" 23 | "go/ast" 24 | "go/parser" 25 | "go/token" 26 | "io/ioutil" 27 | "os" 28 | "strings" 29 | ) 30 | 31 | type ImportData struct { 32 | name *ast.Ident 33 | path string 34 | } 35 | 36 | func usage() { 37 | fmt.Printf("Usage: \n") 38 | fmt.Printf("\timport_checker ...\n") 39 | } 40 | 41 | func check(selfPackageName string, path string, data *ast.File) error { 42 | var ( 43 | buf bytes.Buffer 44 | system, self, thirrdparty []ImportData 45 | ) 46 | 47 | for _, importData := range data.Imports { 48 | if strings.Contains(importData.Path.Value, selfPackageName) { 49 | self = append(self, ImportData{ 50 | name: importData.Name, 51 | path: importData.Path.Value, 52 | }) 53 | } else if strings.Contains(importData.Path.Value, ".") { 54 | thirrdparty = append(thirrdparty, ImportData{ 55 | name: importData.Name, 56 | path: importData.Path.Value, 57 | }) 58 | } else { 59 | system = append(system, ImportData{ 60 | name: importData.Name, 61 | path: importData.Path.Value, 62 | }) 63 | } 64 | } 65 | 66 | array := [][]ImportData{} 67 | if len(system) > 0 { 68 | array = append(array, system) 69 | } 70 | 71 | if len(self) > 0 { 72 | array = append(array, self) 73 | } 74 | 75 | if len(thirrdparty) > 0 { 76 | array = append(array, thirrdparty) 77 | } 78 | 79 | if len(array) == 0 { 80 | return nil 81 | } 82 | 83 | buf.WriteString("import (\n") 84 | for i, idata := range array { 85 | for _, pkg := range idata { 86 | buf.WriteString(generatePath(pkg.name, pkg.path)) 87 | } 88 | 89 | if i+1 < len(array) { 90 | buf.WriteString("\n") 91 | } 92 | } 93 | 94 | buf.WriteString(")\n") 95 | 96 | standard := buf.String() 97 | originFile, err := os.Open(path) 98 | if err != nil { 99 | return err 100 | } 101 | defer originFile.Close() 102 | 103 | contents, err := ioutil.ReadAll(originFile) 104 | if err != nil { 105 | return err 106 | } 107 | 108 | if !strings.Contains(string(contents), standard) { 109 | return fmt.Errorf("%s\nImport is not standardized, should be\n%s", path, standard) 110 | } 111 | 112 | return nil 113 | } 114 | 115 | func generatePath(name *ast.Ident, path string) string { 116 | if name == nil { 117 | return fmt.Sprintf("\t%s\n", path) 118 | } 119 | 120 | return fmt.Sprintf("\t%s %s\n", name, path) 121 | } 122 | 123 | func main() { 124 | if len(os.Args) < 1 { 125 | usage() 126 | os.Exit(0) 127 | } 128 | 129 | selfPackage := os.Getenv("PACKAGE_NAME") 130 | 131 | if selfPackage == "" { 132 | fmt.Printf("Please set PACKAGE_NAME\n") 133 | os.Exit(1) 134 | } 135 | 136 | for _, file := range os.Args[1:] { 137 | fset := token.NewFileSet() 138 | 139 | data, err := parser.ParseFile(fset, file, nil, parser.ImportsOnly) 140 | if err != nil { 141 | fmt.Printf("Can not parse %s, %+v\n", file, err) 142 | os.Exit(1) 143 | } 144 | 145 | if err := check(selfPackage, file, data); err != nil { 146 | fmt.Printf("You need to format your code\n%+v\n", err) 147 | os.Exit(1) 148 | } 149 | } 150 | 151 | os.Exit(0) 152 | } 153 | -------------------------------------------------------------------------------- /hack/lib/golang.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | readonly API_GO_PACKAGE=tkestack.io 4 | readonly API_GOPATH="${ROOT}/go" 5 | -------------------------------------------------------------------------------- /hack/lib/lib.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | find_files() { 4 | find . -not \( \ 5 | \( \ 6 | -wholename './output' \ 7 | -o -wholename './_output' \ 8 | -o -wholename './_gopath' \ 9 | -o -wholename './release' \ 10 | -o -wholename './target' \ 11 | -o -wholename '*/third_party/*' \ 12 | -o -wholename '*/vendor/*' \ 13 | -o -wholename './staging' \ 14 | -o -wholename './pkg/client' \ 15 | \) -prune \ 16 | \) -name '*.go' 17 | } 18 | -------------------------------------------------------------------------------- /hack/lib/logging.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | api::log::usage() { 4 | echo >&2 5 | local message 6 | for message; do 7 | echo "$message" >&2 8 | done 9 | echo >&2 10 | } 11 | 12 | # Print a status line. Formatted to show up in a stream of output. 13 | api::log::status() { 14 | timestamp=$(date +"[%m%d %H:%M:%S]") 15 | echo "+++ $timestamp $1" 16 | shift 17 | for message; do 18 | echo " $message" 19 | done 20 | } 21 | # vim: set ts=2 sw=2 tw=0 et : 22 | -------------------------------------------------------------------------------- /hack/lib/version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | readonly GO_PACKAGE=tkestack.io/tapp 4 | 5 | # ----------------------------------------------------------------------------- 6 | # Version management helpers. These functions help to set, save and load the 7 | # following variables: 8 | # 9 | # GIT_COMMIT - The git commit id corresponding to this 10 | # source code. 11 | # GIT_TREE_STATE - "clean" indicates no changes since the git commit id 12 | # "dirty" indicates source code changes after the git commit id 13 | # "archive" indicates the tree was produced by 'git archive' 14 | # GIT_VERSION - "vX.Y" used to indicate the last release version. 15 | # GIT_MAJOR - The major part of the version 16 | # GIT_MINOR - The minor component of the version 17 | 18 | # Grovels through git to set a set of env variables. 19 | # 20 | # If GIT_VERSION_FILE, this function will load from that file instead of 21 | # querying git. 22 | api::version::get_version_vars() { 23 | if [[ -n ${GIT_VERSION_FILE-} ]] && [[ -f ${GIT_VERSION_FILE-} ]]; then 24 | api::version::load_version_vars "${GIT_VERSION_FILE}" 25 | return 26 | fi 27 | 28 | # If the tapp-controller source was exported through git archive, then 29 | # we likely don't have a git tree, but these magic values may be filled in. 30 | if [[ '$Format:%%$' == "%" ]]; then 31 | GIT_COMMIT='$Format:%H$' 32 | GIT_TREE_STATE="archive" 33 | # When a 'git archive' is exported, the '$Format:%D$' below will look 34 | # something like 'HEAD -> release-1.8, tag: v1.8.3' where then 'tag: ' 35 | # can be extracted from it. 36 | if [[ '$Format:%D$' =~ tag:\ (v[^ ]+) ]]; then 37 | GIT_VERSION="${BASH_REMATCH[1]}" 38 | fi 39 | fi 40 | 41 | local git=(git --work-tree "${ROOT}") 42 | 43 | if [[ -n ${GIT_COMMIT-} ]] || GIT_COMMIT=$("${git[@]}" rev-parse "HEAD^{commit}" 2>/dev/null); then 44 | if [[ -z ${GIT_TREE_STATE-} ]]; then 45 | # Check if the tree is dirty. default to dirty 46 | if git_status=$("${git[@]}" status --porcelain 2>/dev/null) && [[ -z ${git_status} ]]; then 47 | GIT_TREE_STATE="clean" 48 | else 49 | GIT_TREE_STATE="dirty" 50 | fi 51 | fi 52 | 53 | # Use git branch to find the branch of current commit. 54 | if [[ -z ${GIT_BRANCH-} ]] ; then 55 | GIT_BRANCH=$("${git[@]}" branch | sed -n -e 's/^\* \(.*\)/\1/p' | tr -d "[:blank:]" 2>/dev/null) 56 | fi 57 | 58 | # Use git describe to find the version based on annotated tags. 59 | if [[ -n ${GIT_VERSION-} ]] || GIT_VERSION=$("${git[@]}" describe --tags --abbrev=14 "${GIT_COMMIT}^{commit}" 2>/dev/null); then 60 | # This translates the "git describe" to an actual semver.org 61 | # compatible semantic version that looks something like this: 62 | # v1.1.0-alpha.0.6+84c76d1142ea4d 63 | # 64 | # TODO: We continue calling this "git version" because so many 65 | # downstream consumers are expecting it there. 66 | DASHES_IN_VERSION=$(echo "${GIT_VERSION}" | sed "s/[^-]//g") 67 | if [[ "${DASHES_IN_VERSION}" == "---" ]] ; then 68 | # We have distance to subversion (v1.1.0-subversion-1-gCommitHash) 69 | GIT_VERSION=$(echo "${GIT_VERSION}" | sed "s/-\([0-9]\{1,\}\)-g\([0-9a-f]\{14\}\)$/.\1\+\2/") 70 | elif [[ "${DASHES_IN_VERSION}" == "--" ]] ; then 71 | # We have distance to base tag (v1.1.0-1-gCommitHash) 72 | GIT_VERSION=$(echo "${GIT_VERSION}" | sed "s/-g\([0-9a-f]\{14\}\)$/+\1/") 73 | fi 74 | if [[ "${GIT_TREE_STATE}" == "dirty" ]]; then 75 | # git describe --dirty only considers changes to existing files, but 76 | # that is problematic since new untracked .go files affect the build, 77 | # so use our idea of "dirty" from git status instead. 78 | GIT_VERSION+="-dirty" 79 | fi 80 | 81 | # Try to match the "git describe" output to a regex to try to extract 82 | # the "major" and "minor" versions and whether this is the exact tagged 83 | # version or whether the tree is between two tagged versions. 84 | if [[ "${GIT_VERSION}" =~ ^v([0-9]+)\.([0-9]+)(\.[0-9]+)?([-].*)?$ ]]; then 85 | GIT_MAJOR=${BASH_REMATCH[1]} 86 | GIT_MINOR=${BASH_REMATCH[2]} 87 | if [[ -n "${BASH_REMATCH[4]}" ]]; then 88 | GIT_MINOR+="+" 89 | fi 90 | fi 91 | fi 92 | fi 93 | } 94 | 95 | # Saves the environment flags to $1 96 | api::version::save_version_vars() { 97 | local version_file=${1-} 98 | [[ -n ${version_file} ]] || { 99 | echo "!!! Internal error. No file specified in api::version::save_version_vars" 100 | return 1 101 | } 102 | 103 | cat <"${version_file}" 104 | GIT_BRANCH='${GIT_BRANCH-}' 105 | GIT_COMMIT='${GIT_COMMIT-}' 106 | GIT_TREE_STATE='${GIT_TREE_STATE-}' 107 | GIT_VERSION='${GIT_VERSION-}' 108 | GIT_MAJOR='${GIT_MAJOR-}' 109 | GIT_MINOR='${GIT_MINOR-}' 110 | EOF 111 | } 112 | 113 | # Loads up the version variables from file $1 114 | api::version::load_version_vars() { 115 | local version_file=${1-} 116 | [[ -n ${version_file} ]] || { 117 | echo "!!! Internal error. No file specified in api::version::load_version_vars" 118 | return 1 119 | } 120 | 121 | source "${version_file}" 122 | } 123 | 124 | api::version::ldflag() { 125 | local key=${1} 126 | local val=${2} 127 | 128 | # If you update these, also update the list pkg/version/def.bzl. 129 | echo "-X ${GO_PACKAGE}/pkg/version.${key}=${val}" 130 | } 131 | 132 | # Prints the value that needs to be passed to the -ldflags parameter of go build 133 | # in order to set the Kubernetes based on the git tree status. 134 | # IMPORTANT: if you update any of these, also update the lists in 135 | # pkg/version/def.bzl and hack/print-workspace-status.sh. 136 | api::version::ldflags() { 137 | api::version::get_version_vars 138 | 139 | local buildDate= 140 | [[ -z ${SOURCE_DATE_EPOCH-} ]] || buildDate="--date=@${SOURCE_DATE_EPOCH}" 141 | local -a ldflags=($(api::version::ldflag "buildDate" "$(date ${buildDate} -u +'%Y-%m-%dT%H:%M:%SZ')")) 142 | if [[ -n ${GIT_BRANCH-} ]]; then 143 | ldflags+=($(api::version::ldflag "gitBranch" "${GIT_BRANCH}")) 144 | fi 145 | 146 | if [[ -n ${GIT_COMMIT-} ]]; then 147 | ldflags+=($(api::version::ldflag "gitCommit" "${GIT_COMMIT}")) 148 | ldflags+=($(api::version::ldflag "gitTreeState" "${GIT_TREE_STATE}")) 149 | fi 150 | 151 | if [[ -n ${GIT_VERSION-} ]]; then 152 | ldflags+=($(api::version::ldflag "gitVersion" "${GIT_VERSION}")) 153 | fi 154 | 155 | if [[ -n ${GIT_MAJOR-} && -n ${GIT_MINOR-} ]]; then 156 | ldflags+=( 157 | $(api::version::ldflag "gitMajor" "${GIT_MAJOR}") 158 | $(api::version::ldflag "gitMinor" "${GIT_MINOR}") 159 | ) 160 | fi 161 | 162 | # The -ldflags parameter takes a single string, so join the output. 163 | echo "${ldflags[*]-}" 164 | } 165 | -------------------------------------------------------------------------------- /hack/push-image.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | 7 | IMAGE=$1 8 | docker push $IMAGE 9 | -------------------------------------------------------------------------------- /hack/test-go.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | 7 | ROOT=$(cd $(dirname "${BASH_SOURCE}")/.. && pwd -P) 8 | 9 | source "${ROOT}/hack/lib/golang.sh" 10 | 11 | TIMEOUT=${TIMEOUT:-5m} 12 | 13 | cd $ROOT 14 | 15 | packages=$(find . -name *_test.go -print0 | xargs -0n1 dirname | sed 's|^\./||' | sort -u | grep -v e2e) 16 | for i in $packages 17 | do 18 | echo tkestack.io/tapp/$i 19 | go test -v -cover -test.timeout=${TIMEOUT} tkestack.io/tapp/$i 20 | done 21 | cd - 22 | -------------------------------------------------------------------------------- /hack/update-codegen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | 7 | SCRIPT_ROOT=$(dirname ${BASH_SOURCE})/.. 8 | CODEGEN_PKG=${CODEGEN_PKG:-$(cd ${SCRIPT_ROOT}; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo ../../../k8s.io/code-generator)} 9 | 10 | # generate the code with: 11 | # --output-base because this script should also be able to run inside the vendor dir of 12 | # k8s.io/kubernetes. The output-base is needed for the generators to output into the vendor dir 13 | # instead of the $GOPATH directly. For normal projects this can be dropped. 14 | ${CODEGEN_PKG}/generate-groups.sh "deepcopy,client,informer,lister" \ 15 | tkestack.io/tapp/pkg/client tkestack.io/tapp/pkg/apis \ 16 | tappcontroller:v1 \ 17 | --output-base "$(dirname ${BASH_SOURCE})/../../../.." 18 | 19 | # To use your own boilerplate text append: 20 | # --go-header-file ${SCRIPT_ROOT}/hack/custom-boilerplate.go.txt 21 | -------------------------------------------------------------------------------- /hack/update-gofmt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | 7 | ROOT=$(cd $(dirname "${BASH_SOURCE}")/.. && pwd -P) 8 | source $ROOT/hack/lib/lib.sh 9 | 10 | echo $(find_files) | xargs -n 1 -I {} goimports -w {} 11 | -------------------------------------------------------------------------------- /hack/verify-all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # GoFmt apparently is changing @ head... 4 | 5 | set -o errexit 6 | set -o nounset 7 | set -o pipefail 8 | 9 | ROOT=$(cd $(dirname "${BASH_SOURCE}")/.. && pwd -P) 10 | 11 | # verify gofmt 12 | ${ROOT}/hack/verify-gofmt.sh 13 | 14 | # verify codegen 15 | ${ROOT}/hack/verify-codegen.sh 16 | -------------------------------------------------------------------------------- /hack/verify-codegen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | 7 | SCRIPT_ROOT=$(dirname "${BASH_SOURCE}")/.. 8 | 9 | DIFFROOT="${SCRIPT_ROOT}/pkg" 10 | TMP_DIFFROOT="${SCRIPT_ROOT}/_tmp/pkg" 11 | _tmp="${SCRIPT_ROOT}/_tmp" 12 | 13 | cleanup() { 14 | rm -rf "${_tmp}" 15 | } 16 | trap "cleanup" EXIT SIGINT 17 | 18 | cleanup 19 | 20 | mkdir -p "${TMP_DIFFROOT}" 21 | cp -a "${DIFFROOT}"/* "${TMP_DIFFROOT}" 22 | 23 | "${SCRIPT_ROOT}/hack/update-codegen.sh" 24 | echo "diffing ${DIFFROOT} against freshly generated codegen" 25 | ret=0 26 | diff -Naupr "${DIFFROOT}" "${TMP_DIFFROOT}" || ret=$? 27 | cp -a "${TMP_DIFFROOT}"/* "${DIFFROOT}" 28 | if [[ $ret -eq 0 ]] 29 | then 30 | echo "${DIFFROOT} up to date." 31 | else 32 | echo "${DIFFROOT} is out of date. Please run hack/update-codegen.sh" 33 | exit 1 34 | fi 35 | -------------------------------------------------------------------------------- /hack/verify-gofmt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # GoFmt apparently is changing @ head... 4 | 5 | set -o errexit 6 | set -o nounset 7 | set -o pipefail 8 | 9 | ROOT=$(cd $(dirname "${BASH_SOURCE}")/.. && pwd -P) 10 | source $ROOT/hack/lib/lib.sh 11 | 12 | GOFMT="gofmt -s" 13 | bad_files=$(find_files | xargs $GOFMT -l) 14 | if [[ -n "${bad_files}" ]]; then 15 | echo "!!! '$GOFMT -w' needs to be run on the following files: " 16 | echo "${bad_files}" 17 | echo "run 'git ls-files -m | grep .go | xargs -n1 gofmt -s -w' to format your own code" 18 | exit 1 19 | fi 20 | 21 | go build -o /tmp/import_checker ${ROOT}/hack/import_checker.go 22 | 23 | ( 24 | export PACKAGE_NAME="tkestack.io/tapp" 25 | /tmp/import_checker $(find_files) 26 | ) 27 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack available. 3 | * 4 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 7 | * this file except in compliance with the License. You may obtain a copy of the 8 | * License at 9 | * 10 | * https://opensource.org/licenses/Apache-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations under the License. 16 | */ 17 | 18 | package main 19 | 20 | import ( 21 | "context" 22 | "flag" 23 | "os" 24 | "time" 25 | 26 | "tkestack.io/tapp/pkg/admission" 27 | clientset "tkestack.io/tapp/pkg/client/clientset/versioned" 28 | informers "tkestack.io/tapp/pkg/client/informers/externalversions" 29 | "tkestack.io/tapp/pkg/tapp" 30 | "tkestack.io/tapp/pkg/version/verflag" 31 | 32 | "github.com/spf13/pflag" 33 | apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" 34 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 35 | "k8s.io/apimachinery/pkg/util/wait" 36 | kubeinformers "k8s.io/client-go/informers" 37 | "k8s.io/client-go/kubernetes" 38 | restclient "k8s.io/client-go/rest" 39 | "k8s.io/client-go/tools/clientcmd" 40 | "k8s.io/client-go/tools/leaderelection" 41 | "k8s.io/client-go/tools/leaderelection/resourcelock" 42 | componentbaseconfig "k8s.io/component-base/config" 43 | "k8s.io/component-base/logs" 44 | "k8s.io/klog" 45 | "k8s.io/kubernetes/pkg/client/leaderelectionconfig" 46 | ) 47 | 48 | var ( 49 | masterURL string 50 | kubeconfig string 51 | createCRD bool 52 | // If true, delete instance pod after app finishes, otherwise delete pod once pod finishes. 53 | deletePodAfterAppFinish bool 54 | // kubeAPIQPS is the QPS to use while talking with kubernetes apiserver. 55 | kubeAPIQPS float32 56 | // kubeAPIBurst is the burst to use while talking with kubernetes apiserver. 57 | kubeAPIBurst int 58 | // TApp sync worker number 59 | worker int 60 | 61 | // Admission related config 62 | registerAdmission bool 63 | tlsCAfile string 64 | tlsCertFile string 65 | tlsKeyFile string 66 | listenAddress string 67 | // namespace to deploy tapp controller 68 | namespace string 69 | 70 | // leaderElection defines the configuration of leader election client. 71 | // The default value refers k8s.io/component-base/config/v1alpha1/defaults.go 72 | leaderElection = componentbaseconfig.LeaderElectionConfiguration{ 73 | LeaderElect: false, 74 | LeaseDuration: metav1.Duration{Duration: 15 * time.Second}, 75 | RenewDeadline: metav1.Duration{Duration: 10 * time.Second}, 76 | RetryPeriod: metav1.Duration{Duration: 2 * time.Second}, 77 | ResourceLock: "endpoints", 78 | } 79 | ) 80 | 81 | const ( 82 | defaultWorkerNumber = 5 83 | defaultKubeAPIQPS = 2000 84 | defaultKubeAPIBurst = 2500 85 | ) 86 | 87 | func main() { 88 | cfg, err := clientcmd.BuildConfigFromFlags(masterURL, kubeconfig) 89 | if err != nil { 90 | klog.Fatalf("Error building kubeconfig: %s", err.Error()) 91 | } 92 | cfg.QPS = kubeAPIQPS 93 | cfg.Burst = kubeAPIBurst 94 | 95 | kubeClient, err := kubernetes.NewForConfig(cfg) 96 | if err != nil { 97 | klog.Fatalf("Error building kubernetes clientset: %s", err.Error()) 98 | } 99 | 100 | tappClient, err := clientset.NewForConfig(cfg) 101 | if err != nil { 102 | klog.Fatalf("Error building example clientset: %s", err.Error()) 103 | } 104 | 105 | extensionsClient, err := apiextensionsclient.NewForConfig(cfg) 106 | if err != nil { 107 | klog.Fatalf("Error instantiating apiextensions client: %s", err.Error()) 108 | } 109 | 110 | kubeInformerFactory := kubeinformers.NewSharedInformerFactory(kubeClient, time.Second*30) 111 | tappInformerFactory := informers.NewSharedInformerFactory(tappClient, time.Second*30) 112 | 113 | controller := tapp.NewController(kubeClient, tappClient, kubeInformerFactory, tappInformerFactory) 114 | run := func(ctx context.Context) { 115 | stop := ctx.Done() 116 | if createCRD { 117 | wait.PollImmediateUntil(time.Second*5, func() (bool, error) { return tapp.EnsureCRDCreated(extensionsClient) }, stop) 118 | } 119 | 120 | if registerAdmission { 121 | wait.PollImmediateUntil(time.Second*5, func() (bool, error) { 122 | return admission.Register(kubeClient, namespace, tlsCAfile) 123 | }, stop) 124 | server, err := admission.NewServer(listenAddress, tlsCertFile, tlsKeyFile) 125 | if err != nil { 126 | klog.Fatalf("Error new admission server: %v", err) 127 | } 128 | go server.Run(stop) 129 | } 130 | 131 | go kubeInformerFactory.Start(stop) 132 | go tappInformerFactory.Start(stop) 133 | 134 | tapp.SetDeletePodAfterAppFinish(deletePodAfterAppFinish) 135 | 136 | if err = controller.Run(worker, stop); err != nil { 137 | klog.Fatalf("Error running controller: %s", err.Error()) 138 | } 139 | } 140 | 141 | if !leaderElection.LeaderElect { 142 | run(context.TODO()) 143 | panic("unreachable") 144 | } 145 | 146 | id, err := os.Hostname() 147 | if err != nil { 148 | klog.Fatalf("Failed to get hostname: %s", err.Error()) 149 | } 150 | 151 | leaderElectionClient := kubernetes.NewForConfigOrDie(restclient.AddUserAgent(cfg, "leader-election")) 152 | rl, err := resourcelock.New(leaderElection.ResourceLock, 153 | "kube-system", 154 | "tapp-controller", 155 | leaderElectionClient.CoreV1(), 156 | leaderElectionClient.CoordinationV1(), 157 | resourcelock.ResourceLockConfig{ 158 | Identity: id, 159 | EventRecorder: controller.GetEventRecorder(), 160 | }) 161 | if err != nil { 162 | klog.Fatalf("error creating lock: %v", err) 163 | } 164 | 165 | leaderelection.RunOrDie(context.TODO(), leaderelection.LeaderElectionConfig{ 166 | Lock: rl, 167 | LeaseDuration: leaderElection.LeaseDuration.Duration, 168 | RenewDeadline: leaderElection.RenewDeadline.Duration, 169 | RetryPeriod: leaderElection.RetryPeriod.Duration, 170 | Callbacks: leaderelection.LeaderCallbacks{ 171 | OnStartedLeading: run, 172 | OnStoppedLeading: func() { 173 | klog.Fatalf("leaderelection lost") 174 | }, 175 | }, 176 | }) 177 | panic("unreachable") 178 | } 179 | 180 | func init() { 181 | pflag.CommandLine.AddGoFlagSet(flag.CommandLine) 182 | addFlags(pflag.CommandLine) 183 | pflag.Parse() 184 | 185 | logs.InitLogs() 186 | defer logs.FlushLogs() 187 | 188 | verflag.PrintAndExitIfRequested() 189 | } 190 | 191 | func addFlags(fs *pflag.FlagSet) { 192 | fs.StringVar(&kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.") 193 | fs.StringVar(&masterURL, "master", "", 194 | "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.") 195 | fs.BoolVar(&createCRD, "create-crd", true, "Create TApp CRD if it does not exist") 196 | fs.BoolVar(&deletePodAfterAppFinish, "delete-pod-after-app-finish", true, 197 | "If true, delete instance pod after app finishes, otherwise delete pod once pod finishes") 198 | fs.Float32Var(&kubeAPIQPS, "kube-api-qps", defaultKubeAPIQPS, "QPS to use while talking with kubernetes apiserver") 199 | fs.IntVar(&kubeAPIBurst, "kube-api-burst", defaultKubeAPIBurst, "Burst to use while talking with kubernetes apiserver") 200 | fs.IntVar(&worker, "worker", defaultWorkerNumber, "TApp sync worker number, default: 5") 201 | 202 | // Admission related 203 | fs.BoolVar(®isterAdmission, "register-admission", false, "Register admission for tapp controller") 204 | fs.StringVar(&tlsCAfile, "tlsCAFile", "/etc/certs/ca.crt", "File containing the x509 CA for HTTPS") 205 | fs.StringVar(&listenAddress, "listen-address", ":8443", "The address to listen on for HTTP requests.") 206 | fs.StringVar(&tlsCertFile, "tlsCertFile", "/etc/certs/tls.crt", "File containing the x509 Certificate for HTTPS.") 207 | fs.StringVar(&tlsKeyFile, "tlsKeyFile", "/etc/certs/tls.key", "File containing the x509 private key to for HTTPS.") 208 | fs.StringVar(&namespace, "namespace", "kube-system", "Namespace to deploy tapp controller") 209 | 210 | leaderelectionconfig.BindFlags(&leaderElection, fs) 211 | } 212 | -------------------------------------------------------------------------------- /pkg/admission/admission.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack available. 3 | * 4 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 7 | * this file except in compliance with the License. You may obtain a copy of the 8 | * License at 9 | * 10 | * https://opensource.org/licenses/Apache-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations under the License. 16 | */ 17 | 18 | package admission 19 | 20 | import ( 21 | "encoding/json" 22 | "fmt" 23 | "io/ioutil" 24 | "net/http" 25 | "reflect" 26 | 27 | "tkestack.io/tapp/pkg/apis/tappcontroller" 28 | tappv1 "tkestack.io/tapp/pkg/apis/tappcontroller/v1" 29 | 30 | admissionv1beta1 "k8s.io/api/admission/v1beta1" 31 | admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1" 32 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 33 | "k8s.io/client-go/kubernetes" 34 | "k8s.io/klog" 35 | ) 36 | 37 | const ( 38 | validatingWebhookConfiguration = "tapp-admission" 39 | ) 40 | 41 | var ( 42 | validatePath = "/validate/tapp" 43 | failPolicy admissionregistrationv1beta1.FailurePolicyType = "Fail" 44 | ) 45 | 46 | // Register registers the validatingWebhookConfiguration to kube-apiserver 47 | // Note: always return err as nil, it will be used by wait.PollUntil(). 48 | func Register(clientset *kubernetes.Clientset, namespace string, caFile string) (bool, error) { 49 | klog.Infof("Starting to register validatingWebhookConfiguration") 50 | defer func() { 51 | klog.Infof("Finished registering validatingWebhookConfiguration") 52 | }() 53 | 54 | caCert, err := ioutil.ReadFile(caFile) 55 | if err != nil { 56 | klog.Errorf("Failed to read certificate authority from %s: %v", caFile, err) 57 | return false, nil 58 | } 59 | 60 | webhookConfig := &admissionregistrationv1beta1.ValidatingWebhookConfiguration{ 61 | ObjectMeta: metav1.ObjectMeta{ 62 | Name: validatingWebhookConfiguration, 63 | }, 64 | Webhooks: []admissionregistrationv1beta1.Webhook{ 65 | { 66 | Name: fmt.Sprintf("tapp-controller.%s.svc", namespace), 67 | Rules: []admissionregistrationv1beta1.RuleWithOperations{{ 68 | Operations: []admissionregistrationv1beta1.OperationType{admissionregistrationv1beta1.Create, 69 | admissionregistrationv1beta1.Update}, 70 | Rule: admissionregistrationv1beta1.Rule{ 71 | APIGroups: []string{tappcontroller.GroupName}, 72 | APIVersions: []string{"v1"}, 73 | Resources: []string{"tapps"}, 74 | }, 75 | }}, 76 | FailurePolicy: &failPolicy, 77 | ClientConfig: admissionregistrationv1beta1.WebhookClientConfig{ 78 | Service: &admissionregistrationv1beta1.ServiceReference{ 79 | Namespace: namespace, 80 | Name: "tapp-controller", 81 | Path: &validatePath, 82 | }, 83 | CABundle: caCert, 84 | }, 85 | }, 86 | }, 87 | } 88 | 89 | client := clientset.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations() 90 | if present, err := client.Get(validatingWebhookConfiguration, metav1.GetOptions{}); err == nil { 91 | if !reflect.DeepEqual(present.Webhooks, webhookConfig.Webhooks) { 92 | klog.V(1).Infof("Update validationWebhookConfiguration from %+v to %+v", present, webhookConfig) 93 | webhookConfig.ResourceVersion = present.ResourceVersion 94 | if _, err := client.Update(webhookConfig); err != nil { 95 | klog.Errorf("Failed to update validationWebhookConfiguration: %v", err) 96 | return false, nil 97 | } 98 | } 99 | } else { 100 | if _, err := client.Create(webhookConfig); err != nil { 101 | klog.Errorf("Failed to create validatingWebhookConfiguration: %v", err) 102 | return false, nil 103 | } 104 | } 105 | 106 | return true, nil 107 | } 108 | 109 | // Server will start a https server for admitting. 110 | type Server struct { 111 | listenAddress string 112 | certFile string 113 | keyFile string 114 | } 115 | 116 | // NewServer create a new Server for admitting. 117 | func NewServer(listenAddress, certFile, keyFile string) (*Server, error) { 118 | server := &Server{ 119 | listenAddress: listenAddress, 120 | certFile: certFile, 121 | keyFile: keyFile, 122 | } 123 | 124 | return server, nil 125 | } 126 | 127 | // Run starts informers, and listens for accepting request. 128 | func (ws *Server) Run(stopCh <-chan struct{}) { 129 | mux := http.NewServeMux() 130 | mux.HandleFunc(validatePath, func(writer http.ResponseWriter, request *http.Request) { 131 | Serve(writer, request, validateHandler) 132 | }) 133 | 134 | server := &http.Server{ 135 | Addr: ws.listenAddress, 136 | Handler: mux, 137 | } 138 | klog.Fatal(server.ListenAndServeTLS(ws.certFile, ws.keyFile)) 139 | } 140 | 141 | func validateHandler(ar *admissionv1beta1.AdmissionReview) *admissionv1beta1.AdmissionResponse { 142 | klog.V(4).Info("Admitting tapp") 143 | 144 | reviewResponse := &admissionv1beta1.AdmissionResponse{} 145 | reviewResponse.Allowed = true 146 | 147 | var tapp tappv1.TApp 148 | raw := ar.Request.Object.Raw 149 | if err := json.Unmarshal(raw, &tapp); err != nil { 150 | klog.Errorf("Failed to unmarshal tapp from %s: %v", raw, err) 151 | return ToAdmissionResponse(err) 152 | } 153 | 154 | if err := validate(&tapp); err != nil { 155 | klog.Errorf("Failed to validate tapp %v: %v", tapp, err) 156 | return ToAdmissionResponse(err) 157 | } 158 | 159 | return reviewResponse 160 | } 161 | -------------------------------------------------------------------------------- /pkg/admission/util.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack available. 3 | * 4 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 7 | * this file except in compliance with the License. You may obtain a copy of the 8 | * License at 9 | * 10 | * https://opensource.org/licenses/Apache-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations under the License. 16 | */ 17 | 18 | package admission 19 | 20 | import ( 21 | "encoding/json" 22 | "io/ioutil" 23 | "net/http" 24 | 25 | "k8s.io/api/admission/v1beta1" 26 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 | "k8s.io/klog" 28 | ) 29 | 30 | // ToAdmissionResponse is a helper function to create an AdmissionResponse 31 | // with an embedded error 32 | func ToAdmissionResponse(err error) *v1beta1.AdmissionResponse { 33 | return &v1beta1.AdmissionResponse{ 34 | Allowed: false, 35 | Result: &metav1.Status{ 36 | Message: err.Error(), 37 | }, 38 | } 39 | } 40 | 41 | // Serve handles the http portion of a request prior to handing to an admit 42 | // function 43 | func Serve(w http.ResponseWriter, r *http.Request, admitter func(*v1beta1.AdmissionReview) *v1beta1.AdmissionResponse) { 44 | var body []byte 45 | if r.Body != nil { 46 | if data, err := ioutil.ReadAll(r.Body); err == nil { 47 | body = data 48 | } 49 | } 50 | 51 | // verify the content type is accurate 52 | contentType := r.Header.Get("Content-Type") 53 | if contentType != "application/json" { 54 | klog.Errorf("contentType=%s, expect application/json", contentType) 55 | w.WriteHeader(http.StatusBadRequest) 56 | return 57 | } 58 | 59 | klog.V(4).Infof("Handling request: %s", body) 60 | 61 | // The AdmissionReview that was sent to the webhook 62 | requestedAdmissionReview := v1beta1.AdmissionReview{} 63 | 64 | // The AdmissionReview that will be returned 65 | responseAdmissionReview := v1beta1.AdmissionReview{} 66 | 67 | if err := json.Unmarshal(body, &requestedAdmissionReview); err != nil { 68 | klog.Errorf("Failed to unmarshal %s: %v", body, err) 69 | responseAdmissionReview.Response = ToAdmissionResponse(err) 70 | } else { 71 | // pass to admitFunc 72 | responseAdmissionReview.Response = admitter(&requestedAdmissionReview) 73 | } 74 | 75 | // Return the same UID 76 | responseAdmissionReview.Response.UID = requestedAdmissionReview.Request.UID 77 | 78 | klog.V(4).Infof("Sending response: %v", responseAdmissionReview.Response) 79 | 80 | respBytes, err := json.Marshal(responseAdmissionReview) 81 | if err != nil { 82 | klog.Errorf("Failed to marshal response %+v: %v", responseAdmissionReview, err) 83 | } 84 | if _, err := w.Write(respBytes); err != nil { 85 | klog.Errorf("Failed to write response %s: %v", respBytes, err) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /pkg/admission/validation.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack available. 3 | * 4 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 7 | * this file except in compliance with the License. You may obtain a copy of the 8 | * License at 9 | * 10 | * https://opensource.org/licenses/Apache-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations under the License. 16 | */ 17 | 18 | package admission 19 | 20 | import ( 21 | "strconv" 22 | 23 | tappv1 "tkestack.io/tapp/pkg/apis/tappcontroller/v1" 24 | 25 | apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation" 26 | "k8s.io/apimachinery/pkg/util/intstr" 27 | validation "k8s.io/apimachinery/pkg/util/validation" 28 | "k8s.io/apimachinery/pkg/util/validation/field" 29 | ) 30 | 31 | func validate(tapp *tappv1.TApp) error { 32 | allErrs := field.ErrorList{} 33 | allErrs = append(allErrs, validateTAppSpec(&tapp.Spec, field.NewPath("spec"))...) 34 | if len(allErrs) > 0 { 35 | return allErrs.ToAggregate() 36 | } 37 | return nil 38 | } 39 | 40 | func validateTAppSpec(spec *tappv1.TAppSpec, fldPath *field.Path) field.ErrorList { 41 | allErrs := field.ErrorList{} 42 | 43 | allErrs = append(allErrs, validateTAppUpdateStrategy(&spec.UpdateStrategy, fldPath.Child("updateStrategy"))...) 44 | 45 | return allErrs 46 | } 47 | 48 | func validateTAppUpdateStrategy(strategy *tappv1.TAppUpdateStrategy, fldPath *field.Path) field.ErrorList { 49 | var allErrs field.ErrorList 50 | if strategy.MaxUnavailable != nil { 51 | allErrs = append(allErrs, validateIntOrPercent(*strategy.MaxUnavailable, fldPath.Child("maxUnavailable"))...) 52 | } 53 | if strategy.ForceUpdate.MaxUnavailable != nil { 54 | allErrs = append(allErrs, validateIntOrPercent(*strategy.ForceUpdate.MaxUnavailable, fldPath.Child("forceUpdate").Child("maxUnavailable"))...) 55 | } 56 | 57 | return allErrs 58 | } 59 | 60 | func validateIntOrPercent(intOrPercent intstr.IntOrString, fldPath *field.Path) field.ErrorList { 61 | var allErrs field.ErrorList 62 | allErrs = append(allErrs, validatePositiveIntOrPercent(intOrPercent, fldPath)...) 63 | allErrs = append(allErrs, isNotMoreThan100Percent(intOrPercent, fldPath)...) 64 | allErrs = append(allErrs, isNotZero(intOrPercent, fldPath)...) 65 | return allErrs 66 | } 67 | 68 | // validatePositiveIntOrPercent tests if a given value is a valid int or percentage. 69 | func validatePositiveIntOrPercent(intOrPercent intstr.IntOrString, fldPath *field.Path) field.ErrorList { 70 | allErrs := field.ErrorList{} 71 | switch intOrPercent.Type { 72 | case intstr.String: 73 | for _, msg := range validation.IsValidPercent(intOrPercent.StrVal) { 74 | allErrs = append(allErrs, field.Invalid(fldPath, intOrPercent, msg)) 75 | } 76 | case intstr.Int: 77 | allErrs = append(allErrs, apimachineryvalidation.ValidateNonnegativeField(int64(intOrPercent.IntValue()), fldPath)...) 78 | default: 79 | allErrs = append(allErrs, field.Invalid(fldPath, intOrPercent, "must be an integer or percentage (e.g '5%')")) 80 | } 81 | return allErrs 82 | } 83 | 84 | // isNotMoreThan100Percent tests is a value can be represented as a percentage and if this value is not more than 100%. 85 | func isNotMoreThan100Percent(intOrStringValue intstr.IntOrString, fldPath *field.Path) field.ErrorList { 86 | allErrs := field.ErrorList{} 87 | value, isPercent := getPercentValue(intOrStringValue) 88 | if !isPercent || value <= 100 { 89 | return nil 90 | } 91 | allErrs = append(allErrs, field.Invalid(fldPath, intOrStringValue, "must not be greater than 100%")) 92 | return allErrs 93 | } 94 | 95 | // isNotZero tests if a given value is not zero 96 | func isNotZero(intOrStringValue intstr.IntOrString, fldPath *field.Path) field.ErrorList { 97 | allErrs := field.ErrorList{} 98 | value, isPercent := getPercentValue(intOrStringValue) 99 | if (!isPercent && intOrStringValue.IntVal == 0) || (isPercent && value == 0) { 100 | allErrs = append(allErrs, field.Invalid(fldPath, intOrStringValue, "must not be zero")) 101 | } 102 | return allErrs 103 | } 104 | 105 | func getPercentValue(intOrStringValue intstr.IntOrString) (int, bool) { 106 | if intOrStringValue.Type != intstr.String { 107 | return 0, false 108 | } 109 | if len(validation.IsValidPercent(intOrStringValue.StrVal)) != 0 { 110 | return 0, false 111 | } 112 | value, _ := strconv.Atoi(intOrStringValue.StrVal[:len(intOrStringValue.StrVal)-1]) 113 | return value, true 114 | } 115 | -------------------------------------------------------------------------------- /pkg/apis/tappcontroller/BUILD: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "go_default_library", 5 | srcs = ["register.go"], 6 | importpath = "tkestack.io/tapp/pkg/apis/tappcontroller", 7 | visibility = ["//visibility:public"], 8 | ) 9 | 10 | filegroup( 11 | name = "package-srcs", 12 | srcs = glob(["**"]), 13 | tags = ["automanaged"], 14 | visibility = ["//visibility:private"], 15 | ) 16 | 17 | filegroup( 18 | name = "all-srcs", 19 | srcs = [ 20 | ":package-srcs", 21 | "//staging/src/tkestack.io/tapp/pkg/apis/tappcontroller/v1:all-srcs", 22 | ], 23 | tags = ["automanaged"], 24 | visibility = ["//visibility:public"], 25 | ) 26 | -------------------------------------------------------------------------------- /pkg/apis/tappcontroller/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack available. 3 | * 4 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 7 | * this file except in compliance with the License. You may obtain a copy of the 8 | * License at 9 | * 10 | * https://opensource.org/licenses/Apache-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations under the License. 16 | */ 17 | 18 | package tappcontroller 19 | 20 | const ( 21 | GroupName = "apps.tkestack.io" 22 | ) 23 | -------------------------------------------------------------------------------- /pkg/apis/tappcontroller/v1/BUILD: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "go_default_library", 5 | srcs = [ 6 | "doc.go", 7 | "register.go", 8 | "types.go", 9 | "zz_generated.deepcopy.go", 10 | ], 11 | importpath = "tkestack.io/tapp/pkg/apis/tappcontroller/v1", 12 | visibility = ["//visibility:public"], 13 | deps = [ 14 | "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", 15 | "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", 16 | "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", 17 | "//vendor/tkestack.io/tapp/pkg/apis/tappcontroller:go_default_library", 18 | ], 19 | ) 20 | 21 | filegroup( 22 | name = "package-srcs", 23 | srcs = glob(["**"]), 24 | tags = ["automanaged"], 25 | visibility = ["//visibility:private"], 26 | ) 27 | 28 | filegroup( 29 | name = "all-srcs", 30 | srcs = [":package-srcs"], 31 | tags = ["automanaged"], 32 | visibility = ["//visibility:public"], 33 | ) 34 | -------------------------------------------------------------------------------- /pkg/apis/tappcontroller/v1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack available. 3 | * 4 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 7 | * this file except in compliance with the License. You may obtain a copy of the 8 | * License at 9 | * 10 | * https://opensource.org/licenses/Apache-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations under the License. 16 | */ 17 | 18 | // +k8s:deepcopy-gen=package 19 | // +k8s:openapi-gen=true 20 | 21 | // Package v1 is the v1 version of the API. 22 | // +groupName=tappcontroller.k8s.io 23 | package v1 24 | -------------------------------------------------------------------------------- /pkg/apis/tappcontroller/v1/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack available. 3 | * 4 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 7 | * this file except in compliance with the License. You may obtain a copy of the 8 | * License at 9 | * 10 | * https://opensource.org/licenses/Apache-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations under the License. 16 | */ 17 | 18 | package v1 19 | 20 | import ( 21 | "tkestack.io/tapp/pkg/apis/tappcontroller" 22 | 23 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 | "k8s.io/apimachinery/pkg/runtime" 25 | "k8s.io/apimachinery/pkg/runtime/schema" 26 | ) 27 | 28 | // SchemeGroupVersion is group version used to register these objects 29 | var SchemeGroupVersion = schema.GroupVersion{Group: tappcontroller.GroupName, Version: "v1"} 30 | 31 | // Kind takes an unqualified kind and returns back a Group qualified GroupKind 32 | func Kind(kind string) schema.GroupKind { 33 | return SchemeGroupVersion.WithKind(kind).GroupKind() 34 | } 35 | 36 | // Resource takes an unqualified resource and returns a Group qualified GroupResource 37 | func Resource(resource string) schema.GroupResource { 38 | return SchemeGroupVersion.WithResource(resource).GroupResource() 39 | } 40 | 41 | var ( 42 | SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) 43 | AddToScheme = SchemeBuilder.AddToScheme 44 | ) 45 | 46 | // Adds the list of known types to Scheme. 47 | func addKnownTypes(scheme *runtime.Scheme) error { 48 | scheme.AddKnownTypes(SchemeGroupVersion, 49 | &TApp{}, 50 | &TAppList{}, 51 | ) 52 | metav1.AddToGroupVersion(scheme, SchemeGroupVersion) 53 | return nil 54 | } 55 | -------------------------------------------------------------------------------- /pkg/apis/tappcontroller/v1/types.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack available. 3 | * 4 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 7 | * this file except in compliance with the License. You may obtain a copy of the 8 | * License at 9 | * 10 | * https://opensource.org/licenses/Apache-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations under the License. 16 | */ 17 | 18 | package v1 19 | 20 | import ( 21 | corev1 "k8s.io/api/core/v1" 22 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 | "k8s.io/apimachinery/pkg/util/intstr" 24 | ) 25 | 26 | const ( 27 | DefaultTemplateName = "default" 28 | TAppInstanceKey = "tapp_instance_key" 29 | 30 | // If its value is false, it means pod is being updated. 31 | InPlaceUpdateReady corev1.PodConditionType = "tkestack.io/InPlaceUpdate" 32 | 33 | // DefaultMaxUnavailable is the default value for .Spec.UpdateStrategy.MaxUnavailable 34 | DefaultMaxUnavailable = 1 35 | 36 | // DefaultMaxUnavailable is the default value for .Spec.UpdateStrategy.ForceUpdateStrategy.MaxUnavailable 37 | DefaultMaxUnavailableForceUpdate = "100%" 38 | ) 39 | 40 | // +genclient 41 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 42 | 43 | // TApp represents a set of pods with consistent identities. 44 | type TApp struct { 45 | metav1.TypeMeta `json:",inline"` 46 | metav1.ObjectMeta `json:"metadata,omitempty"` 47 | 48 | // Spec defines the desired identities of pods in this tapp. 49 | Spec TAppSpec `json:"spec,omitempty"` 50 | 51 | // Status is the current status of pods in this TApp. This data 52 | // may be out of date by some window of time. 53 | Status TAppStatus `json:"status,omitempty"` 54 | } 55 | 56 | // A TAppSpec is the specification of a TApp. 57 | type TAppSpec struct { 58 | // Replicas is the desired number of replicas of the given Template. 59 | // These are replicas in the sense that they are instantiations of the 60 | // same Template, but individual replicas also have a consistent identity. 61 | Replicas int32 `json:"replicas"` 62 | 63 | // Selector is a label query over pods that should match the replica count. 64 | // If empty, defaulted to labels on the pod template. 65 | // More info: http://releases.k8s.io/release-1.4/docs/user-guide/labels.md#label-selectors 66 | Selector *metav1.LabelSelector `json:"selector,omitempty"` 67 | 68 | // Template is the object that describes the pod that will be initial created/default scaled 69 | // it should be added to TemplatePool 70 | Template corev1.PodTemplateSpec `json:"template"` 71 | 72 | // TemplatePool stores a map whose key is template name and value is podTemplate 73 | TemplatePool map[string]corev1.PodTemplateSpec `json:"templatePool,omitempty"` 74 | 75 | // Statuses stores desired instance status instanceID --> desiredStatus 76 | Statuses map[string]InstanceStatus `json:"statuses,omitempty"` 77 | 78 | // Templates stores instanceID --> template name 79 | Templates map[string]string `json:"templates,omitempty"` 80 | 81 | // UpdateStrategy indicates the TappUpdateStrategy that will be 82 | // employed to update Pods in the TApp 83 | UpdateStrategy TAppUpdateStrategy `json:"updateStrategy,omitempty"` 84 | 85 | // ForceDeletePod indicates whether force delete pods when it is being deleted because of NodeLost. 86 | // Default values is false. 87 | ForceDeletePod bool `json:"forceDeletePod,omitempty"` 88 | 89 | // AutoDeleteUnusedTemplate indicates whether auto delete templates when it is unused. 90 | // Default values is false. 91 | AutoDeleteUnusedTemplate bool `json:"autoDeleteUnusedTemplate,omitempty"` 92 | 93 | // NeverMigrate indicates whether to migrate pods. If it is true, pods will never be migrated to 94 | // other nodes, otherwise it depends on other conditions(e.g. pod restart policy). 95 | NeverMigrate bool `json:"neverMigrate,omitempty"` 96 | 97 | // volumeClaimTemplates is a list of claims that pods are allowed to reference. 98 | // The TApp controller is responsible for mapping network identities to 99 | // claims in a way that maintains the identity of a pod. Every claim in 100 | // this list must have at least one matching (by name) volumeMount in one 101 | // container in the template. A claim in this list takes precedence over 102 | // any volumes in the template, with the same name. 103 | // TODO: Define the behavior if a claim already exists with the same name. 104 | VolumeClaimTemplates []corev1.PersistentVolumeClaim `json:"volumeClaimTemplates,omitempty"` 105 | 106 | // ServiceName is the name of the service that governs this TApp. 107 | // This service must exist before the TApp, and is responsible for 108 | // the network identity of the set. Pods get DNS/hostnames that follow the 109 | // pattern: pod-specific-string.serviceName.default.svc.cluster.local 110 | // where "pod-specific-string" is managed by the TApp controller. 111 | ServiceName string `json:"serviceName,omitempty"` 112 | 113 | //DefaultTemplateName is the default template name for scale 114 | DefaultTemplateName string `json:"defaultTemplateName"` 115 | } 116 | 117 | // TApp update strategy 118 | type TAppUpdateStrategy struct { 119 | // Following fields are rolling update related configuration. 120 | // Template is the rolling update template name 121 | Template string `json:"template,omitempty"` 122 | // MaxUnavailable is the max unavailable number when tapp is rolling update, default is 1. 123 | MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty"` 124 | 125 | // Following fields are force update related configuration. 126 | ForceUpdate ForceUpdateStrategy `json:"forceUpdate,omitempty"` 127 | } 128 | 129 | type ForceUpdateStrategy struct { 130 | // MaxUnavailable is the max unavailable number when tapp is forced update, default is 100%. 131 | MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty"` 132 | } 133 | 134 | type InstanceStatus string 135 | 136 | const ( 137 | InstanceNotCreated InstanceStatus = "NotCreated" 138 | InstancePending InstanceStatus = "Pending" 139 | InstanceRunning InstanceStatus = "Running" 140 | InstanceUpdating InstanceStatus = "Updating" 141 | InstancePodFailed InstanceStatus = "PodFailed" 142 | InstancePodSucc InstanceStatus = "PodSucc" 143 | InstanceKilling InstanceStatus = "Killing" 144 | InstanceKilled InstanceStatus = "Killed" 145 | InstanceFailed InstanceStatus = "Failed" 146 | InstanceSucc InstanceStatus = "Succ" 147 | InstanceUnknown InstanceStatus = "Unknown" 148 | ) 149 | 150 | var InstanceStatusAll = []InstanceStatus{InstanceNotCreated, InstancePending, InstanceRunning, InstanceUpdating, 151 | InstanceFailed, InstanceKilling, InstanceSucc, InstanceKilled} 152 | 153 | type AppStatus string 154 | 155 | const ( 156 | AppPending AppStatus = "Pending" 157 | AppRunning AppStatus = "Running" 158 | AppFailed AppStatus = "Failed" 159 | AppSucc AppStatus = "Succ" 160 | AppKilled AppStatus = "Killed" 161 | ) 162 | 163 | // TAppStatus represents the current state of a TApp. 164 | type TAppStatus struct { 165 | // most recent generation observed by controller. 166 | ObservedGeneration int64 `json:"observedGeneration,omitempty"` 167 | 168 | // Replicas is the number of actual replicas. 169 | Replicas int32 `json:"replicas"` 170 | 171 | // ReadyReplicas is the number of running replicas 172 | ReadyReplicas int32 `json:"readyReplicas"` 173 | 174 | // ScaleSelector is a label for query over pods that should match the replica count used by HPA. 175 | ScaleLabelSelector string `json:"scaleLabelSelector,omitempty"` 176 | 177 | // AppStatus describe the current TApp state 178 | AppStatus AppStatus `json:"appStatus,omitempty"` 179 | 180 | // Statues stores actual instanceID --> InstanceStatus 181 | Statuses map[string]InstanceStatus `json:"statuses,omitempty"` 182 | } 183 | 184 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 185 | 186 | // TAppList is a collection of TApp. 187 | type TAppList struct { 188 | metav1.TypeMeta `json:",inline"` 189 | metav1.ListMeta `json:"metadata,omitempty"` 190 | Items []TApp `json:"items"` 191 | } 192 | -------------------------------------------------------------------------------- /pkg/apis/tappcontroller/v1/zz_generated.deepcopy.go: -------------------------------------------------------------------------------- 1 | // +build !ignore_autogenerated 2 | 3 | /* 4 | Copyright The Kubernetes Authors. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | // Code generated by deepcopy-gen. DO NOT EDIT. 20 | 21 | package v1 22 | 23 | import ( 24 | corev1 "k8s.io/api/core/v1" 25 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 | runtime "k8s.io/apimachinery/pkg/runtime" 27 | ) 28 | 29 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 30 | func (in *TApp) DeepCopyInto(out *TApp) { 31 | *out = *in 32 | out.TypeMeta = in.TypeMeta 33 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 34 | in.Spec.DeepCopyInto(&out.Spec) 35 | in.Status.DeepCopyInto(&out.Status) 36 | return 37 | } 38 | 39 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TApp. 40 | func (in *TApp) DeepCopy() *TApp { 41 | if in == nil { 42 | return nil 43 | } 44 | out := new(TApp) 45 | in.DeepCopyInto(out) 46 | return out 47 | } 48 | 49 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 50 | func (in *TApp) DeepCopyObject() runtime.Object { 51 | if c := in.DeepCopy(); c != nil { 52 | return c 53 | } 54 | return nil 55 | } 56 | 57 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 58 | func (in *TAppList) DeepCopyInto(out *TAppList) { 59 | *out = *in 60 | out.TypeMeta = in.TypeMeta 61 | out.ListMeta = in.ListMeta 62 | if in.Items != nil { 63 | in, out := &in.Items, &out.Items 64 | *out = make([]TApp, len(*in)) 65 | for i := range *in { 66 | (*in)[i].DeepCopyInto(&(*out)[i]) 67 | } 68 | } 69 | return 70 | } 71 | 72 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TAppList. 73 | func (in *TAppList) DeepCopy() *TAppList { 74 | if in == nil { 75 | return nil 76 | } 77 | out := new(TAppList) 78 | in.DeepCopyInto(out) 79 | return out 80 | } 81 | 82 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 83 | func (in *TAppList) DeepCopyObject() runtime.Object { 84 | if c := in.DeepCopy(); c != nil { 85 | return c 86 | } 87 | return nil 88 | } 89 | 90 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 91 | func (in *TAppSpec) DeepCopyInto(out *TAppSpec) { 92 | *out = *in 93 | if in.Selector != nil { 94 | in, out := &in.Selector, &out.Selector 95 | *out = new(metav1.LabelSelector) 96 | (*in).DeepCopyInto(*out) 97 | } 98 | in.Template.DeepCopyInto(&out.Template) 99 | if in.TemplatePool != nil { 100 | in, out := &in.TemplatePool, &out.TemplatePool 101 | *out = make(map[string]corev1.PodTemplateSpec, len(*in)) 102 | for key, val := range *in { 103 | (*out)[key] = *val.DeepCopy() 104 | } 105 | } 106 | if in.Statuses != nil { 107 | in, out := &in.Statuses, &out.Statuses 108 | *out = make(map[string]InstanceStatus, len(*in)) 109 | for key, val := range *in { 110 | (*out)[key] = val 111 | } 112 | } 113 | if in.Templates != nil { 114 | in, out := &in.Templates, &out.Templates 115 | *out = make(map[string]string, len(*in)) 116 | for key, val := range *in { 117 | (*out)[key] = val 118 | } 119 | } 120 | in.UpdateStrategy.DeepCopyInto(&out.UpdateStrategy) 121 | if in.VolumeClaimTemplates != nil { 122 | in, out := &in.VolumeClaimTemplates, &out.VolumeClaimTemplates 123 | *out = make([]corev1.PersistentVolumeClaim, len(*in)) 124 | for i := range *in { 125 | (*in)[i].DeepCopyInto(&(*out)[i]) 126 | } 127 | } 128 | return 129 | } 130 | 131 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TAppSpec. 132 | func (in *TAppSpec) DeepCopy() *TAppSpec { 133 | if in == nil { 134 | return nil 135 | } 136 | out := new(TAppSpec) 137 | in.DeepCopyInto(out) 138 | return out 139 | } 140 | 141 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 142 | func (in *TAppStatus) DeepCopyInto(out *TAppStatus) { 143 | *out = *in 144 | if in.Statuses != nil { 145 | in, out := &in.Statuses, &out.Statuses 146 | *out = make(map[string]InstanceStatus, len(*in)) 147 | for key, val := range *in { 148 | (*out)[key] = val 149 | } 150 | } 151 | return 152 | } 153 | 154 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TAppStatus. 155 | func (in *TAppStatus) DeepCopy() *TAppStatus { 156 | if in == nil { 157 | return nil 158 | } 159 | out := new(TAppStatus) 160 | in.DeepCopyInto(out) 161 | return out 162 | } 163 | 164 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 165 | func (in *TAppUpdateStrategy) DeepCopyInto(out *TAppUpdateStrategy) { 166 | *out = *in 167 | out.MaxUnavailable = in.MaxUnavailable 168 | return 169 | } 170 | 171 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TAppUpdateStrategy. 172 | func (in *TAppUpdateStrategy) DeepCopy() *TAppUpdateStrategy { 173 | if in == nil { 174 | return nil 175 | } 176 | out := new(TAppUpdateStrategy) 177 | in.DeepCopyInto(out) 178 | return out 179 | } 180 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/clientset.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package versioned 20 | 21 | import ( 22 | tappcontrollerv1 "tkestack.io/tapp/pkg/client/clientset/versioned/typed/tappcontroller/v1" 23 | discovery "k8s.io/client-go/discovery" 24 | rest "k8s.io/client-go/rest" 25 | flowcontrol "k8s.io/client-go/util/flowcontrol" 26 | ) 27 | 28 | type Interface interface { 29 | Discovery() discovery.DiscoveryInterface 30 | TappcontrollerV1() tappcontrollerv1.TappcontrollerV1Interface 31 | } 32 | 33 | // Clientset contains the clients for groups. Each group has exactly one 34 | // version included in a Clientset. 35 | type Clientset struct { 36 | *discovery.DiscoveryClient 37 | tappcontrollerV1 *tappcontrollerv1.TappcontrollerV1Client 38 | } 39 | 40 | // TappcontrollerV1 retrieves the TappcontrollerV1Client 41 | func (c *Clientset) TappcontrollerV1() tappcontrollerv1.TappcontrollerV1Interface { 42 | return c.tappcontrollerV1 43 | } 44 | 45 | // Discovery retrieves the DiscoveryClient 46 | func (c *Clientset) Discovery() discovery.DiscoveryInterface { 47 | if c == nil { 48 | return nil 49 | } 50 | return c.DiscoveryClient 51 | } 52 | 53 | // NewForConfig creates a new Clientset for the given config. 54 | func NewForConfig(c *rest.Config) (*Clientset, error) { 55 | configShallowCopy := *c 56 | if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { 57 | configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) 58 | } 59 | var cs Clientset 60 | var err error 61 | cs.tappcontrollerV1, err = tappcontrollerv1.NewForConfig(&configShallowCopy) 62 | if err != nil { 63 | return nil, err 64 | } 65 | 66 | cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) 67 | if err != nil { 68 | return nil, err 69 | } 70 | return &cs, nil 71 | } 72 | 73 | // NewForConfigOrDie creates a new Clientset for the given config and 74 | // panics if there is an error in the config. 75 | func NewForConfigOrDie(c *rest.Config) *Clientset { 76 | var cs Clientset 77 | cs.tappcontrollerV1 = tappcontrollerv1.NewForConfigOrDie(c) 78 | 79 | cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) 80 | return &cs 81 | } 82 | 83 | // New creates a new Clientset for the given RESTClient. 84 | func New(c rest.Interface) *Clientset { 85 | var cs Clientset 86 | cs.tappcontrollerV1 = tappcontrollerv1.New(c) 87 | 88 | cs.DiscoveryClient = discovery.NewDiscoveryClient(c) 89 | return &cs 90 | } 91 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package has the automatically generated clientset. 20 | package versioned 21 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/fake/clientset_generated.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | clientset "tkestack.io/tapp/pkg/client/clientset/versioned" 23 | tappcontrollerv1 "tkestack.io/tapp/pkg/client/clientset/versioned/typed/tappcontroller/v1" 24 | faketappcontrollerv1 "tkestack.io/tapp/pkg/client/clientset/versioned/typed/tappcontroller/v1/fake" 25 | "k8s.io/apimachinery/pkg/runtime" 26 | "k8s.io/apimachinery/pkg/watch" 27 | "k8s.io/client-go/discovery" 28 | fakediscovery "k8s.io/client-go/discovery/fake" 29 | "k8s.io/client-go/testing" 30 | ) 31 | 32 | // NewSimpleClientset returns a clientset that will respond with the provided objects. 33 | // It's backed by a very simple object tracker that processes creates, updates and deletions as-is, 34 | // without applying any validations and/or defaults. It shouldn't be considered a replacement 35 | // for a real clientset and is mostly useful in simple unit tests. 36 | func NewSimpleClientset(objects ...runtime.Object) *Clientset { 37 | o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) 38 | for _, obj := range objects { 39 | if err := o.Add(obj); err != nil { 40 | panic(err) 41 | } 42 | } 43 | 44 | cs := &Clientset{} 45 | cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} 46 | cs.AddReactor("*", "*", testing.ObjectReaction(o)) 47 | cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { 48 | gvr := action.GetResource() 49 | ns := action.GetNamespace() 50 | watch, err := o.Watch(gvr, ns) 51 | if err != nil { 52 | return false, nil, err 53 | } 54 | return true, watch, nil 55 | }) 56 | 57 | return cs 58 | } 59 | 60 | // Clientset implements clientset.Interface. Meant to be embedded into a 61 | // struct to get a default implementation. This makes faking out just the method 62 | // you want to test easier. 63 | type Clientset struct { 64 | testing.Fake 65 | discovery *fakediscovery.FakeDiscovery 66 | } 67 | 68 | func (c *Clientset) Discovery() discovery.DiscoveryInterface { 69 | return c.discovery 70 | } 71 | 72 | var _ clientset.Interface = &Clientset{} 73 | 74 | // TappcontrollerV1 retrieves the TappcontrollerV1Client 75 | func (c *Clientset) TappcontrollerV1() tappcontrollerv1.TappcontrollerV1Interface { 76 | return &faketappcontrollerv1.FakeTappcontrollerV1{Fake: &c.Fake} 77 | } 78 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package has the automatically generated fake clientset. 20 | package fake 21 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/fake/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | tappcontrollerv1 "tkestack.io/tapp/pkg/apis/tappcontroller/v1" 23 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 | runtime "k8s.io/apimachinery/pkg/runtime" 25 | schema "k8s.io/apimachinery/pkg/runtime/schema" 26 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 27 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 28 | ) 29 | 30 | var scheme = runtime.NewScheme() 31 | var codecs = serializer.NewCodecFactory(scheme) 32 | var parameterCodec = runtime.NewParameterCodec(scheme) 33 | var localSchemeBuilder = runtime.SchemeBuilder{ 34 | tappcontrollerv1.AddToScheme, 35 | } 36 | 37 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 38 | // of clientsets, like in: 39 | // 40 | // import ( 41 | // "k8s.io/client-go/kubernetes" 42 | // clientsetscheme "k8s.io/client-go/kubernetes/scheme" 43 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 44 | // ) 45 | // 46 | // kclientset, _ := kubernetes.NewForConfig(c) 47 | // _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 48 | // 49 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 50 | // correctly. 51 | var AddToScheme = localSchemeBuilder.AddToScheme 52 | 53 | func init() { 54 | v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) 55 | utilruntime.Must(AddToScheme(scheme)) 56 | } 57 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/scheme/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package contains the scheme of the automatically generated clientset. 20 | package scheme 21 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/scheme/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package scheme 20 | 21 | import ( 22 | tappcontrollerv1 "tkestack.io/tapp/pkg/apis/tappcontroller/v1" 23 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 | runtime "k8s.io/apimachinery/pkg/runtime" 25 | schema "k8s.io/apimachinery/pkg/runtime/schema" 26 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 27 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 28 | ) 29 | 30 | var Scheme = runtime.NewScheme() 31 | var Codecs = serializer.NewCodecFactory(Scheme) 32 | var ParameterCodec = runtime.NewParameterCodec(Scheme) 33 | var localSchemeBuilder = runtime.SchemeBuilder{ 34 | tappcontrollerv1.AddToScheme, 35 | } 36 | 37 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 38 | // of clientsets, like in: 39 | // 40 | // import ( 41 | // "k8s.io/client-go/kubernetes" 42 | // clientsetscheme "k8s.io/client-go/kubernetes/scheme" 43 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 44 | // ) 45 | // 46 | // kclientset, _ := kubernetes.NewForConfig(c) 47 | // _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 48 | // 49 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 50 | // correctly. 51 | var AddToScheme = localSchemeBuilder.AddToScheme 52 | 53 | func init() { 54 | v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) 55 | utilruntime.Must(AddToScheme(Scheme)) 56 | } 57 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/typed/tappcontroller/v1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package has the automatically generated typed clients. 20 | package v1 21 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/typed/tappcontroller/v1/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // Package fake has the automatically generated clients. 20 | package fake 21 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/typed/tappcontroller/v1/fake/fake_tapp.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | tappcontrollerv1 "tkestack.io/tapp/pkg/apis/tappcontroller/v1" 23 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 | labels "k8s.io/apimachinery/pkg/labels" 25 | schema "k8s.io/apimachinery/pkg/runtime/schema" 26 | types "k8s.io/apimachinery/pkg/types" 27 | watch "k8s.io/apimachinery/pkg/watch" 28 | testing "k8s.io/client-go/testing" 29 | ) 30 | 31 | // FakeTApps implements TAppInterface 32 | type FakeTApps struct { 33 | Fake *FakeTappcontrollerV1 34 | ns string 35 | } 36 | 37 | var tappsResource = schema.GroupVersionResource{Group: "tappcontroller.k8s.io", Version: "v1", Resource: "tapps"} 38 | 39 | var tappsKind = schema.GroupVersionKind{Group: "tappcontroller.k8s.io", Version: "v1", Kind: "TApp"} 40 | 41 | // Get takes name of the tApp, and returns the corresponding tApp object, and an error if there is any. 42 | func (c *FakeTApps) Get(name string, options v1.GetOptions) (result *tappcontrollerv1.TApp, err error) { 43 | obj, err := c.Fake. 44 | Invokes(testing.NewGetAction(tappsResource, c.ns, name), &tappcontrollerv1.TApp{}) 45 | 46 | if obj == nil { 47 | return nil, err 48 | } 49 | return obj.(*tappcontrollerv1.TApp), err 50 | } 51 | 52 | // List takes label and field selectors, and returns the list of TApps that match those selectors. 53 | func (c *FakeTApps) List(opts v1.ListOptions) (result *tappcontrollerv1.TAppList, err error) { 54 | obj, err := c.Fake. 55 | Invokes(testing.NewListAction(tappsResource, tappsKind, c.ns, opts), &tappcontrollerv1.TAppList{}) 56 | 57 | if obj == nil { 58 | return nil, err 59 | } 60 | 61 | label, _, _ := testing.ExtractFromListOptions(opts) 62 | if label == nil { 63 | label = labels.Everything() 64 | } 65 | list := &tappcontrollerv1.TAppList{ListMeta: obj.(*tappcontrollerv1.TAppList).ListMeta} 66 | for _, item := range obj.(*tappcontrollerv1.TAppList).Items { 67 | if label.Matches(labels.Set(item.Labels)) { 68 | list.Items = append(list.Items, item) 69 | } 70 | } 71 | return list, err 72 | } 73 | 74 | // Watch returns a watch.Interface that watches the requested tApps. 75 | func (c *FakeTApps) Watch(opts v1.ListOptions) (watch.Interface, error) { 76 | return c.Fake. 77 | InvokesWatch(testing.NewWatchAction(tappsResource, c.ns, opts)) 78 | 79 | } 80 | 81 | // Create takes the representation of a tApp and creates it. Returns the server's representation of the tApp, and an error, if there is any. 82 | func (c *FakeTApps) Create(tApp *tappcontrollerv1.TApp) (result *tappcontrollerv1.TApp, err error) { 83 | obj, err := c.Fake. 84 | Invokes(testing.NewCreateAction(tappsResource, c.ns, tApp), &tappcontrollerv1.TApp{}) 85 | 86 | if obj == nil { 87 | return nil, err 88 | } 89 | return obj.(*tappcontrollerv1.TApp), err 90 | } 91 | 92 | // Update takes the representation of a tApp and updates it. Returns the server's representation of the tApp, and an error, if there is any. 93 | func (c *FakeTApps) Update(tApp *tappcontrollerv1.TApp) (result *tappcontrollerv1.TApp, err error) { 94 | obj, err := c.Fake. 95 | Invokes(testing.NewUpdateAction(tappsResource, c.ns, tApp), &tappcontrollerv1.TApp{}) 96 | 97 | if obj == nil { 98 | return nil, err 99 | } 100 | return obj.(*tappcontrollerv1.TApp), err 101 | } 102 | 103 | // UpdateStatus was generated because the type contains a Status member. 104 | // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). 105 | func (c *FakeTApps) UpdateStatus(tApp *tappcontrollerv1.TApp) (*tappcontrollerv1.TApp, error) { 106 | obj, err := c.Fake. 107 | Invokes(testing.NewUpdateSubresourceAction(tappsResource, "status", c.ns, tApp), &tappcontrollerv1.TApp{}) 108 | 109 | if obj == nil { 110 | return nil, err 111 | } 112 | return obj.(*tappcontrollerv1.TApp), err 113 | } 114 | 115 | // Delete takes name of the tApp and deletes it. Returns an error if one occurs. 116 | func (c *FakeTApps) Delete(name string, options *v1.DeleteOptions) error { 117 | _, err := c.Fake. 118 | Invokes(testing.NewDeleteAction(tappsResource, c.ns, name), &tappcontrollerv1.TApp{}) 119 | 120 | return err 121 | } 122 | 123 | // DeleteCollection deletes a collection of objects. 124 | func (c *FakeTApps) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { 125 | action := testing.NewDeleteCollectionAction(tappsResource, c.ns, listOptions) 126 | 127 | _, err := c.Fake.Invokes(action, &tappcontrollerv1.TAppList{}) 128 | return err 129 | } 130 | 131 | // Patch applies the patch and returns the patched tApp. 132 | func (c *FakeTApps) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *tappcontrollerv1.TApp, err error) { 133 | obj, err := c.Fake. 134 | Invokes(testing.NewPatchSubresourceAction(tappsResource, c.ns, name, pt, data, subresources...), &tappcontrollerv1.TApp{}) 135 | 136 | if obj == nil { 137 | return nil, err 138 | } 139 | return obj.(*tappcontrollerv1.TApp), err 140 | } 141 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/typed/tappcontroller/v1/fake/fake_tappcontroller_client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | v1 "tkestack.io/tapp/pkg/client/clientset/versioned/typed/tappcontroller/v1" 23 | rest "k8s.io/client-go/rest" 24 | testing "k8s.io/client-go/testing" 25 | ) 26 | 27 | type FakeTappcontrollerV1 struct { 28 | *testing.Fake 29 | } 30 | 31 | func (c *FakeTappcontrollerV1) TApps(namespace string) v1.TAppInterface { 32 | return &FakeTApps{c, namespace} 33 | } 34 | 35 | // RESTClient returns a RESTClient that is used to communicate 36 | // with API server by this client implementation. 37 | func (c *FakeTappcontrollerV1) RESTClient() rest.Interface { 38 | var ret *rest.RESTClient 39 | return ret 40 | } 41 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/typed/tappcontroller/v1/generated_expansion.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package v1 20 | 21 | type TAppExpansion interface{} 22 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/typed/tappcontroller/v1/tapp.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package v1 20 | 21 | import ( 22 | "time" 23 | 24 | v1 "tkestack.io/tapp/pkg/apis/tappcontroller/v1" 25 | scheme "tkestack.io/tapp/pkg/client/clientset/versioned/scheme" 26 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 | types "k8s.io/apimachinery/pkg/types" 28 | watch "k8s.io/apimachinery/pkg/watch" 29 | rest "k8s.io/client-go/rest" 30 | ) 31 | 32 | // TAppsGetter has a method to return a TAppInterface. 33 | // A group's client should implement this interface. 34 | type TAppsGetter interface { 35 | TApps(namespace string) TAppInterface 36 | } 37 | 38 | // TAppInterface has methods to work with TApp resources. 39 | type TAppInterface interface { 40 | Create(*v1.TApp) (*v1.TApp, error) 41 | Update(*v1.TApp) (*v1.TApp, error) 42 | UpdateStatus(*v1.TApp) (*v1.TApp, error) 43 | Delete(name string, options *metav1.DeleteOptions) error 44 | DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error 45 | Get(name string, options metav1.GetOptions) (*v1.TApp, error) 46 | List(opts metav1.ListOptions) (*v1.TAppList, error) 47 | Watch(opts metav1.ListOptions) (watch.Interface, error) 48 | Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.TApp, err error) 49 | TAppExpansion 50 | } 51 | 52 | // tApps implements TAppInterface 53 | type tApps struct { 54 | client rest.Interface 55 | ns string 56 | } 57 | 58 | // newTApps returns a TApps 59 | func newTApps(c *TappcontrollerV1Client, namespace string) *tApps { 60 | return &tApps{ 61 | client: c.RESTClient(), 62 | ns: namespace, 63 | } 64 | } 65 | 66 | // Get takes name of the tApp, and returns the corresponding tApp object, and an error if there is any. 67 | func (c *tApps) Get(name string, options metav1.GetOptions) (result *v1.TApp, err error) { 68 | result = &v1.TApp{} 69 | err = c.client.Get(). 70 | Namespace(c.ns). 71 | Resource("tapps"). 72 | Name(name). 73 | VersionedParams(&options, scheme.ParameterCodec). 74 | Do(). 75 | Into(result) 76 | return 77 | } 78 | 79 | // List takes label and field selectors, and returns the list of TApps that match those selectors. 80 | func (c *tApps) List(opts metav1.ListOptions) (result *v1.TAppList, err error) { 81 | var timeout time.Duration 82 | if opts.TimeoutSeconds != nil { 83 | timeout = time.Duration(*opts.TimeoutSeconds) * time.Second 84 | } 85 | result = &v1.TAppList{} 86 | err = c.client.Get(). 87 | Namespace(c.ns). 88 | Resource("tapps"). 89 | VersionedParams(&opts, scheme.ParameterCodec). 90 | Timeout(timeout). 91 | Do(). 92 | Into(result) 93 | return 94 | } 95 | 96 | // Watch returns a watch.Interface that watches the requested tApps. 97 | func (c *tApps) Watch(opts metav1.ListOptions) (watch.Interface, error) { 98 | var timeout time.Duration 99 | if opts.TimeoutSeconds != nil { 100 | timeout = time.Duration(*opts.TimeoutSeconds) * time.Second 101 | } 102 | opts.Watch = true 103 | return c.client.Get(). 104 | Namespace(c.ns). 105 | Resource("tapps"). 106 | VersionedParams(&opts, scheme.ParameterCodec). 107 | Timeout(timeout). 108 | Watch() 109 | } 110 | 111 | // Create takes the representation of a tApp and creates it. Returns the server's representation of the tApp, and an error, if there is any. 112 | func (c *tApps) Create(tApp *v1.TApp) (result *v1.TApp, err error) { 113 | result = &v1.TApp{} 114 | err = c.client.Post(). 115 | Namespace(c.ns). 116 | Resource("tapps"). 117 | Body(tApp). 118 | Do(). 119 | Into(result) 120 | return 121 | } 122 | 123 | // Update takes the representation of a tApp and updates it. Returns the server's representation of the tApp, and an error, if there is any. 124 | func (c *tApps) Update(tApp *v1.TApp) (result *v1.TApp, err error) { 125 | result = &v1.TApp{} 126 | err = c.client.Put(). 127 | Namespace(c.ns). 128 | Resource("tapps"). 129 | Name(tApp.Name). 130 | Body(tApp). 131 | Do(). 132 | Into(result) 133 | return 134 | } 135 | 136 | // UpdateStatus was generated because the type contains a Status member. 137 | // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). 138 | 139 | func (c *tApps) UpdateStatus(tApp *v1.TApp) (result *v1.TApp, err error) { 140 | result = &v1.TApp{} 141 | err = c.client.Put(). 142 | Namespace(c.ns). 143 | Resource("tapps"). 144 | Name(tApp.Name). 145 | SubResource("status"). 146 | Body(tApp). 147 | Do(). 148 | Into(result) 149 | return 150 | } 151 | 152 | // Delete takes name of the tApp and deletes it. Returns an error if one occurs. 153 | func (c *tApps) Delete(name string, options *metav1.DeleteOptions) error { 154 | return c.client.Delete(). 155 | Namespace(c.ns). 156 | Resource("tapps"). 157 | Name(name). 158 | Body(options). 159 | Do(). 160 | Error() 161 | } 162 | 163 | // DeleteCollection deletes a collection of objects. 164 | func (c *tApps) DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error { 165 | var timeout time.Duration 166 | if listOptions.TimeoutSeconds != nil { 167 | timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second 168 | } 169 | return c.client.Delete(). 170 | Namespace(c.ns). 171 | Resource("tapps"). 172 | VersionedParams(&listOptions, scheme.ParameterCodec). 173 | Timeout(timeout). 174 | Body(options). 175 | Do(). 176 | Error() 177 | } 178 | 179 | // Patch applies the patch and returns the patched tApp. 180 | func (c *tApps) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.TApp, err error) { 181 | result = &v1.TApp{} 182 | err = c.client.Patch(pt). 183 | Namespace(c.ns). 184 | Resource("tapps"). 185 | SubResource(subresources...). 186 | Name(name). 187 | Body(data). 188 | Do(). 189 | Into(result) 190 | return 191 | } 192 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/typed/tappcontroller/v1/tappcontroller_client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package v1 20 | 21 | import ( 22 | v1 "tkestack.io/tapp/pkg/apis/tappcontroller/v1" 23 | "tkestack.io/tapp/pkg/client/clientset/versioned/scheme" 24 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 25 | rest "k8s.io/client-go/rest" 26 | ) 27 | 28 | type TappcontrollerV1Interface interface { 29 | RESTClient() rest.Interface 30 | TAppsGetter 31 | } 32 | 33 | // TappcontrollerV1Client is used to interact with features provided by the tappcontroller.k8s.io group. 34 | type TappcontrollerV1Client struct { 35 | restClient rest.Interface 36 | } 37 | 38 | func (c *TappcontrollerV1Client) TApps(namespace string) TAppInterface { 39 | return newTApps(c, namespace) 40 | } 41 | 42 | // NewForConfig creates a new TappcontrollerV1Client for the given config. 43 | func NewForConfig(c *rest.Config) (*TappcontrollerV1Client, error) { 44 | config := *c 45 | if err := setConfigDefaults(&config); err != nil { 46 | return nil, err 47 | } 48 | client, err := rest.RESTClientFor(&config) 49 | if err != nil { 50 | return nil, err 51 | } 52 | return &TappcontrollerV1Client{client}, nil 53 | } 54 | 55 | // NewForConfigOrDie creates a new TappcontrollerV1Client for the given config and 56 | // panics if there is an error in the config. 57 | func NewForConfigOrDie(c *rest.Config) *TappcontrollerV1Client { 58 | client, err := NewForConfig(c) 59 | if err != nil { 60 | panic(err) 61 | } 62 | return client 63 | } 64 | 65 | // New creates a new TappcontrollerV1Client for the given RESTClient. 66 | func New(c rest.Interface) *TappcontrollerV1Client { 67 | return &TappcontrollerV1Client{c} 68 | } 69 | 70 | func setConfigDefaults(config *rest.Config) error { 71 | gv := v1.SchemeGroupVersion 72 | config.GroupVersion = &gv 73 | config.APIPath = "/apis" 74 | config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} 75 | 76 | if config.UserAgent == "" { 77 | config.UserAgent = rest.DefaultKubernetesUserAgent() 78 | } 79 | 80 | return nil 81 | } 82 | 83 | // RESTClient returns a RESTClient that is used to communicate 84 | // with API server by this client implementation. 85 | func (c *TappcontrollerV1Client) RESTClient() rest.Interface { 86 | if c == nil { 87 | return nil 88 | } 89 | return c.restClient 90 | } 91 | -------------------------------------------------------------------------------- /pkg/client/informers/externalversions/factory.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package externalversions 20 | 21 | import ( 22 | reflect "reflect" 23 | sync "sync" 24 | time "time" 25 | 26 | versioned "tkestack.io/tapp/pkg/client/clientset/versioned" 27 | internalinterfaces "tkestack.io/tapp/pkg/client/informers/externalversions/internalinterfaces" 28 | tappcontroller "tkestack.io/tapp/pkg/client/informers/externalversions/tappcontroller" 29 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 | runtime "k8s.io/apimachinery/pkg/runtime" 31 | schema "k8s.io/apimachinery/pkg/runtime/schema" 32 | cache "k8s.io/client-go/tools/cache" 33 | ) 34 | 35 | // SharedInformerOption defines the functional option type for SharedInformerFactory. 36 | type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory 37 | 38 | type sharedInformerFactory struct { 39 | client versioned.Interface 40 | namespace string 41 | tweakListOptions internalinterfaces.TweakListOptionsFunc 42 | lock sync.Mutex 43 | defaultResync time.Duration 44 | customResync map[reflect.Type]time.Duration 45 | 46 | informers map[reflect.Type]cache.SharedIndexInformer 47 | // startedInformers is used for tracking which informers have been started. 48 | // This allows Start() to be called multiple times safely. 49 | startedInformers map[reflect.Type]bool 50 | } 51 | 52 | // WithCustomResyncConfig sets a custom resync period for the specified informer types. 53 | func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption { 54 | return func(factory *sharedInformerFactory) *sharedInformerFactory { 55 | for k, v := range resyncConfig { 56 | factory.customResync[reflect.TypeOf(k)] = v 57 | } 58 | return factory 59 | } 60 | } 61 | 62 | // WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory. 63 | func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption { 64 | return func(factory *sharedInformerFactory) *sharedInformerFactory { 65 | factory.tweakListOptions = tweakListOptions 66 | return factory 67 | } 68 | } 69 | 70 | // WithNamespace limits the SharedInformerFactory to the specified namespace. 71 | func WithNamespace(namespace string) SharedInformerOption { 72 | return func(factory *sharedInformerFactory) *sharedInformerFactory { 73 | factory.namespace = namespace 74 | return factory 75 | } 76 | } 77 | 78 | // NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces. 79 | func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory { 80 | return NewSharedInformerFactoryWithOptions(client, defaultResync) 81 | } 82 | 83 | // NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. 84 | // Listers obtained via this SharedInformerFactory will be subject to the same filters 85 | // as specified here. 86 | // Deprecated: Please use NewSharedInformerFactoryWithOptions instead 87 | func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory { 88 | return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions)) 89 | } 90 | 91 | // NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options. 92 | func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory { 93 | factory := &sharedInformerFactory{ 94 | client: client, 95 | namespace: v1.NamespaceAll, 96 | defaultResync: defaultResync, 97 | informers: make(map[reflect.Type]cache.SharedIndexInformer), 98 | startedInformers: make(map[reflect.Type]bool), 99 | customResync: make(map[reflect.Type]time.Duration), 100 | } 101 | 102 | // Apply all options 103 | for _, opt := range options { 104 | factory = opt(factory) 105 | } 106 | 107 | return factory 108 | } 109 | 110 | // Start initializes all requested informers. 111 | func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { 112 | f.lock.Lock() 113 | defer f.lock.Unlock() 114 | 115 | for informerType, informer := range f.informers { 116 | if !f.startedInformers[informerType] { 117 | go informer.Run(stopCh) 118 | f.startedInformers[informerType] = true 119 | } 120 | } 121 | } 122 | 123 | // WaitForCacheSync waits for all started informers' cache were synced. 124 | func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { 125 | informers := func() map[reflect.Type]cache.SharedIndexInformer { 126 | f.lock.Lock() 127 | defer f.lock.Unlock() 128 | 129 | informers := map[reflect.Type]cache.SharedIndexInformer{} 130 | for informerType, informer := range f.informers { 131 | if f.startedInformers[informerType] { 132 | informers[informerType] = informer 133 | } 134 | } 135 | return informers 136 | }() 137 | 138 | res := map[reflect.Type]bool{} 139 | for informType, informer := range informers { 140 | res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) 141 | } 142 | return res 143 | } 144 | 145 | // InternalInformerFor returns the SharedIndexInformer for obj using an internal 146 | // client. 147 | func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { 148 | f.lock.Lock() 149 | defer f.lock.Unlock() 150 | 151 | informerType := reflect.TypeOf(obj) 152 | informer, exists := f.informers[informerType] 153 | if exists { 154 | return informer 155 | } 156 | 157 | resyncPeriod, exists := f.customResync[informerType] 158 | if !exists { 159 | resyncPeriod = f.defaultResync 160 | } 161 | 162 | informer = newFunc(f.client, resyncPeriod) 163 | f.informers[informerType] = informer 164 | 165 | return informer 166 | } 167 | 168 | // SharedInformerFactory provides shared informers for resources in all known 169 | // API group versions. 170 | type SharedInformerFactory interface { 171 | internalinterfaces.SharedInformerFactory 172 | ForResource(resource schema.GroupVersionResource) (GenericInformer, error) 173 | WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool 174 | 175 | Tappcontroller() tappcontroller.Interface 176 | } 177 | 178 | func (f *sharedInformerFactory) Tappcontroller() tappcontroller.Interface { 179 | return tappcontroller.New(f, f.namespace, f.tweakListOptions) 180 | } 181 | -------------------------------------------------------------------------------- /pkg/client/informers/externalversions/generic.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package externalversions 20 | 21 | import ( 22 | "fmt" 23 | 24 | v1 "tkestack.io/tapp/pkg/apis/tappcontroller/v1" 25 | schema "k8s.io/apimachinery/pkg/runtime/schema" 26 | cache "k8s.io/client-go/tools/cache" 27 | ) 28 | 29 | // GenericInformer is type of SharedIndexInformer which will locate and delegate to other 30 | // sharedInformers based on type 31 | type GenericInformer interface { 32 | Informer() cache.SharedIndexInformer 33 | Lister() cache.GenericLister 34 | } 35 | 36 | type genericInformer struct { 37 | informer cache.SharedIndexInformer 38 | resource schema.GroupResource 39 | } 40 | 41 | // Informer returns the SharedIndexInformer. 42 | func (f *genericInformer) Informer() cache.SharedIndexInformer { 43 | return f.informer 44 | } 45 | 46 | // Lister returns the GenericLister. 47 | func (f *genericInformer) Lister() cache.GenericLister { 48 | return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) 49 | } 50 | 51 | // ForResource gives generic access to a shared informer of the matching type 52 | // TODO extend this to unknown resources with a client pool 53 | func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { 54 | switch resource { 55 | // Group=tappcontroller.k8s.io, Version=v1 56 | case v1.SchemeGroupVersion.WithResource("tapps"): 57 | return &genericInformer{resource: resource.GroupResource(), informer: f.Tappcontroller().V1().TApps().Informer()}, nil 58 | 59 | } 60 | 61 | return nil, fmt.Errorf("no informer found for %v", resource) 62 | } 63 | -------------------------------------------------------------------------------- /pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package internalinterfaces 20 | 21 | import ( 22 | time "time" 23 | 24 | versioned "tkestack.io/tapp/pkg/client/clientset/versioned" 25 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 | runtime "k8s.io/apimachinery/pkg/runtime" 27 | cache "k8s.io/client-go/tools/cache" 28 | ) 29 | 30 | // NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer. 31 | type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer 32 | 33 | // SharedInformerFactory a small interface to allow for adding an informer without an import cycle 34 | type SharedInformerFactory interface { 35 | Start(stopCh <-chan struct{}) 36 | InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer 37 | } 38 | 39 | // TweakListOptionsFunc is a function that transforms a v1.ListOptions. 40 | type TweakListOptionsFunc func(*v1.ListOptions) 41 | -------------------------------------------------------------------------------- /pkg/client/informers/externalversions/tappcontroller/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package tappcontroller 20 | 21 | import ( 22 | internalinterfaces "tkestack.io/tapp/pkg/client/informers/externalversions/internalinterfaces" 23 | v1 "tkestack.io/tapp/pkg/client/informers/externalversions/tappcontroller/v1" 24 | ) 25 | 26 | // Interface provides access to each of this group's versions. 27 | type Interface interface { 28 | // V1 provides access to shared informers for resources in V1. 29 | V1() v1.Interface 30 | } 31 | 32 | type group struct { 33 | factory internalinterfaces.SharedInformerFactory 34 | namespace string 35 | tweakListOptions internalinterfaces.TweakListOptionsFunc 36 | } 37 | 38 | // New returns a new Interface. 39 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 40 | return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 41 | } 42 | 43 | // V1 returns a new v1.Interface. 44 | func (g *group) V1() v1.Interface { 45 | return v1.New(g.factory, g.namespace, g.tweakListOptions) 46 | } 47 | -------------------------------------------------------------------------------- /pkg/client/informers/externalversions/tappcontroller/v1/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package v1 20 | 21 | import ( 22 | internalinterfaces "tkestack.io/tapp/pkg/client/informers/externalversions/internalinterfaces" 23 | ) 24 | 25 | // Interface provides access to all the informers in this group version. 26 | type Interface interface { 27 | // TApps returns a TAppInformer. 28 | TApps() TAppInformer 29 | } 30 | 31 | type version struct { 32 | factory internalinterfaces.SharedInformerFactory 33 | namespace string 34 | tweakListOptions internalinterfaces.TweakListOptionsFunc 35 | } 36 | 37 | // New returns a new Interface. 38 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 39 | return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 40 | } 41 | 42 | // TApps returns a TAppInformer. 43 | func (v *version) TApps() TAppInformer { 44 | return &tAppInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} 45 | } 46 | -------------------------------------------------------------------------------- /pkg/client/informers/externalversions/tappcontroller/v1/tapp.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package v1 20 | 21 | import ( 22 | time "time" 23 | 24 | tappcontrollerv1 "tkestack.io/tapp/pkg/apis/tappcontroller/v1" 25 | versioned "tkestack.io/tapp/pkg/client/clientset/versioned" 26 | internalinterfaces "tkestack.io/tapp/pkg/client/informers/externalversions/internalinterfaces" 27 | v1 "tkestack.io/tapp/pkg/client/listers/tappcontroller/v1" 28 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 | runtime "k8s.io/apimachinery/pkg/runtime" 30 | watch "k8s.io/apimachinery/pkg/watch" 31 | cache "k8s.io/client-go/tools/cache" 32 | ) 33 | 34 | // TAppInformer provides access to a shared informer and lister for 35 | // TApps. 36 | type TAppInformer interface { 37 | Informer() cache.SharedIndexInformer 38 | Lister() v1.TAppLister 39 | } 40 | 41 | type tAppInformer struct { 42 | factory internalinterfaces.SharedInformerFactory 43 | tweakListOptions internalinterfaces.TweakListOptionsFunc 44 | namespace string 45 | } 46 | 47 | // NewTAppInformer constructs a new informer for TApp type. 48 | // Always prefer using an informer factory to get a shared informer instead of getting an independent 49 | // one. This reduces memory footprint and number of connections to the server. 50 | func NewTAppInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { 51 | return NewFilteredTAppInformer(client, namespace, resyncPeriod, indexers, nil) 52 | } 53 | 54 | // NewFilteredTAppInformer constructs a new informer for TApp type. 55 | // Always prefer using an informer factory to get a shared informer instead of getting an independent 56 | // one. This reduces memory footprint and number of connections to the server. 57 | func NewFilteredTAppInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { 58 | return cache.NewSharedIndexInformer( 59 | &cache.ListWatch{ 60 | ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { 61 | if tweakListOptions != nil { 62 | tweakListOptions(&options) 63 | } 64 | return client.TappcontrollerV1().TApps(namespace).List(options) 65 | }, 66 | WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { 67 | if tweakListOptions != nil { 68 | tweakListOptions(&options) 69 | } 70 | return client.TappcontrollerV1().TApps(namespace).Watch(options) 71 | }, 72 | }, 73 | &tappcontrollerv1.TApp{}, 74 | resyncPeriod, 75 | indexers, 76 | ) 77 | } 78 | 79 | func (f *tAppInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { 80 | return NewFilteredTAppInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) 81 | } 82 | 83 | func (f *tAppInformer) Informer() cache.SharedIndexInformer { 84 | return f.factory.InformerFor(&tappcontrollerv1.TApp{}, f.defaultInformer) 85 | } 86 | 87 | func (f *tAppInformer) Lister() v1.TAppLister { 88 | return v1.NewTAppLister(f.Informer().GetIndexer()) 89 | } 90 | -------------------------------------------------------------------------------- /pkg/client/listers/tappcontroller/v1/expansion_generated.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by lister-gen. DO NOT EDIT. 18 | 19 | package v1 20 | 21 | // TAppListerExpansion allows custom methods to be added to 22 | // TAppLister. 23 | type TAppListerExpansion interface{} 24 | 25 | // TAppNamespaceListerExpansion allows custom methods to be added to 26 | // TAppNamespaceLister. 27 | type TAppNamespaceListerExpansion interface{} 28 | -------------------------------------------------------------------------------- /pkg/client/listers/tappcontroller/v1/tapp.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by lister-gen. DO NOT EDIT. 18 | 19 | package v1 20 | 21 | import ( 22 | v1 "tkestack.io/tapp/pkg/apis/tappcontroller/v1" 23 | "k8s.io/apimachinery/pkg/api/errors" 24 | "k8s.io/apimachinery/pkg/labels" 25 | "k8s.io/client-go/tools/cache" 26 | ) 27 | 28 | // TAppLister helps list TApps. 29 | type TAppLister interface { 30 | // List lists all TApps in the indexer. 31 | List(selector labels.Selector) (ret []*v1.TApp, err error) 32 | // TApps returns an object that can list and get TApps. 33 | TApps(namespace string) TAppNamespaceLister 34 | TAppListerExpansion 35 | } 36 | 37 | // tAppLister implements the TAppLister interface. 38 | type tAppLister struct { 39 | indexer cache.Indexer 40 | } 41 | 42 | // NewTAppLister returns a new TAppLister. 43 | func NewTAppLister(indexer cache.Indexer) TAppLister { 44 | return &tAppLister{indexer: indexer} 45 | } 46 | 47 | // List lists all TApps in the indexer. 48 | func (s *tAppLister) List(selector labels.Selector) (ret []*v1.TApp, err error) { 49 | err = cache.ListAll(s.indexer, selector, func(m interface{}) { 50 | ret = append(ret, m.(*v1.TApp)) 51 | }) 52 | return ret, err 53 | } 54 | 55 | // TApps returns an object that can list and get TApps. 56 | func (s *tAppLister) TApps(namespace string) TAppNamespaceLister { 57 | return tAppNamespaceLister{indexer: s.indexer, namespace: namespace} 58 | } 59 | 60 | // TAppNamespaceLister helps list and get TApps. 61 | type TAppNamespaceLister interface { 62 | // List lists all TApps in the indexer for a given namespace. 63 | List(selector labels.Selector) (ret []*v1.TApp, err error) 64 | // Get retrieves the TApp from the indexer for a given namespace and name. 65 | Get(name string) (*v1.TApp, error) 66 | TAppNamespaceListerExpansion 67 | } 68 | 69 | // tAppNamespaceLister implements the TAppNamespaceLister 70 | // interface. 71 | type tAppNamespaceLister struct { 72 | indexer cache.Indexer 73 | namespace string 74 | } 75 | 76 | // List lists all TApps in the indexer for a given namespace. 77 | func (s tAppNamespaceLister) List(selector labels.Selector) (ret []*v1.TApp, err error) { 78 | err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { 79 | ret = append(ret, m.(*v1.TApp)) 80 | }) 81 | return ret, err 82 | } 83 | 84 | // Get retrieves the TApp from the indexer for a given namespace and name. 85 | func (s tAppNamespaceLister) Get(name string) (*v1.TApp, error) { 86 | obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) 87 | if err != nil { 88 | return nil, err 89 | } 90 | if !exists { 91 | return nil, errors.NewNotFound(v1.Resource("tapp"), name) 92 | } 93 | return obj.(*v1.TApp), nil 94 | } 95 | -------------------------------------------------------------------------------- /pkg/hash/hash.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack available. 3 | * 4 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 7 | * this file except in compliance with the License. You may obtain a copy of the 8 | * License at 9 | * 10 | * https://opensource.org/licenses/Apache-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations under the License. 16 | */ 17 | 18 | package hash 19 | 20 | import ( 21 | "fmt" 22 | "hash/fnv" 23 | 24 | corev1 "k8s.io/api/core/v1" 25 | hashutil "k8s.io/kubernetes/pkg/util/hash" 26 | ) 27 | 28 | const ( 29 | // TemplateHashKey is a key for storing PodTemplateSpec's hash value in labels. 30 | // It will be used to check whether pod's PodTemplateSpec has changed, if yes, 31 | // we need recreate or do in-place update for the pod according to the value of UniqHash. 32 | TemplateHashKey = "tapp_template_hash_key" 33 | // UniqHashKey is a key for storing hash value of PodTemplateSpec(without container images) in labels. 34 | // It will will be used to check whether pod's PodTemplateSpec hash changed and only container images 35 | // changed, if yes, we will do in place update for the pod. 36 | UniqHashKey = "tapp_uniq_hash_key" 37 | ) 38 | 39 | // TappHashInterface is used for generate and verify hash for tapp. 40 | type TappHashInterface interface { 41 | // SetTemplateHash sets PodTemplateSpec's hash value into template's labels, 42 | // returns true if needs set and is set, otherwise false 43 | SetTemplateHash(template *corev1.PodTemplateSpec) bool 44 | // GetTemplateHash returns PodTemplateSpec's hash value, the values is stored in labels. 45 | GetTemplateHash(labels map[string]string) string 46 | // SetUniqHash sets hash value of PodTemplateSpec(without container images) into template's labels, 47 | // returns true if needs set and is set, otherwise false 48 | SetUniqHash(template *corev1.PodTemplateSpec) bool 49 | // GetUniqHash returns hash value of PodTemplateSpec(without container images), the values is stored in labels. 50 | GetUniqHash(labels map[string]string) string 51 | // HashLabels returns labels key that stores TemplateHash and UniqHash 52 | HashLabels() []string 53 | } 54 | 55 | func NewTappHash() TappHashInterface { 56 | return &defaultTappHash{} 57 | } 58 | 59 | type defaultTappHash struct{} 60 | 61 | func (th *defaultTappHash) SetTemplateHash(template *corev1.PodTemplateSpec) bool { 62 | expected := generateTemplateHash(template) 63 | hash := th.GetTemplateHash(template.Labels) 64 | if hash != expected { 65 | if template.Labels == nil { 66 | template.Labels = make(map[string]string) 67 | } 68 | template.Labels[TemplateHashKey] = expected 69 | return true 70 | } else { 71 | return false 72 | } 73 | } 74 | 75 | func (th *defaultTappHash) GetTemplateHash(labels map[string]string) string { 76 | return labels[TemplateHashKey] 77 | } 78 | 79 | func (th *defaultTappHash) SetUniqHash(template *corev1.PodTemplateSpec) bool { 80 | expected := generateUniqHash(*template) 81 | hash := th.GetUniqHash(template.Labels) 82 | if hash != expected { 83 | if template.Labels == nil { 84 | template.Labels = make(map[string]string) 85 | } 86 | template.Labels[UniqHashKey] = expected 87 | return true 88 | } else { 89 | return false 90 | } 91 | } 92 | 93 | func (th *defaultTappHash) GetUniqHash(labels map[string]string) string { 94 | return labels[UniqHashKey] 95 | } 96 | 97 | func (th *defaultTappHash) HashLabels() []string { 98 | return []string{TemplateHashKey, UniqHashKey} 99 | } 100 | 101 | func generateHash(template interface{}) uint64 { 102 | hasher := fnv.New64() 103 | hashutil.DeepHashObject(hasher, template) 104 | return hasher.Sum64() 105 | } 106 | 107 | func generateTemplateHash(template *corev1.PodTemplateSpec) string { 108 | meta := template.ObjectMeta.DeepCopy() 109 | delete(meta.Labels, TemplateHashKey) 110 | delete(meta.Labels, UniqHashKey) 111 | return fmt.Sprintf("%d", generateHash(corev1.PodTemplateSpec{ 112 | ObjectMeta: *meta, 113 | Spec: template.Spec, 114 | })) 115 | } 116 | 117 | func generateUniqHash(template corev1.PodTemplateSpec) string { 118 | if template.Spec.InitContainers != nil { 119 | var newContainers []corev1.Container 120 | for _, container := range template.Spec.InitContainers { 121 | container.Image = "" 122 | newContainers = append(newContainers, container) 123 | } 124 | template.Spec.InitContainers = newContainers 125 | } 126 | 127 | var newContainers []corev1.Container 128 | for _, container := range template.Spec.Containers { 129 | container.Image = "" 130 | newContainers = append(newContainers, container) 131 | } 132 | template.Spec.Containers = newContainers 133 | 134 | return fmt.Sprintf("%d", generateHash(template.Spec)) 135 | } 136 | -------------------------------------------------------------------------------- /pkg/hash/hash_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack available. 3 | * 4 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 7 | * this file except in compliance with the License. You may obtain a copy of the 8 | * License at 9 | * 10 | * https://opensource.org/licenses/Apache-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations under the License. 16 | */ 17 | 18 | package hash 19 | 20 | import ( 21 | "testing" 22 | 23 | corev1 "k8s.io/api/core/v1" 24 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 | ) 26 | 27 | func TestSetTemplateHash(t *testing.T) { 28 | h := NewTappHash() 29 | 30 | template := createPodTemplate() 31 | expectedTemplateHash := generateTemplateHash(&template) 32 | h.SetTemplateHash(&template) 33 | realHash := h.GetTemplateHash(template.Labels) 34 | if expectedTemplateHash != realHash { 35 | t.Errorf("Failed to set template h") 36 | } 37 | } 38 | 39 | func TestSetUniqHash(t *testing.T) { 40 | h := NewTappHash() 41 | 42 | template := createPodTemplate() 43 | expectedUniqHash := generateUniqHash(template) 44 | h.SetUniqHash(&template) 45 | realHash := h.GetUniqHash(template.Labels) 46 | if expectedUniqHash != realHash { 47 | t.Errorf("Failed to set uniq h") 48 | } 49 | } 50 | 51 | func createPodTemplate() corev1.PodTemplateSpec { 52 | return corev1.PodTemplateSpec{ 53 | ObjectMeta: metav1.ObjectMeta{ 54 | Labels: map[string]string{"test": "hello"}, 55 | Annotations: map[string]string{}, 56 | }, 57 | Spec: corev1.PodSpec{ 58 | RestartPolicy: corev1.RestartPolicyOnFailure, 59 | DNSPolicy: corev1.DNSClusterFirst, 60 | Containers: []corev1.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}}, 61 | }, 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /pkg/tapp/crd.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack available. 3 | * 4 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 7 | * this file except in compliance with the License. You may obtain a copy of the 8 | * License at 9 | * 10 | * https://opensource.org/licenses/Apache-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations under the License. 16 | */ 17 | 18 | package tapp 19 | 20 | import ( 21 | "reflect" 22 | 23 | "tkestack.io/tapp/pkg/apis/tappcontroller" 24 | 25 | extensionsobj "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" 26 | apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" 27 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 | "k8s.io/klog" 29 | ) 30 | 31 | var scaleLabelSelector = ".status.scaleLabelSelector" 32 | 33 | var CRD = &extensionsobj.CustomResourceDefinition{ 34 | ObjectMeta: metav1.ObjectMeta{ 35 | Name: "tapps." + tappcontroller.GroupName, 36 | }, 37 | TypeMeta: metav1.TypeMeta{ 38 | Kind: "CustomResourceDefinition", 39 | APIVersion: "apiextensions.k8s.io/v1beta1", 40 | }, 41 | Spec: extensionsobj.CustomResourceDefinitionSpec{ 42 | Group: tappcontroller.GroupName, 43 | Version: "v1", 44 | Scope: extensionsobj.ResourceScope("Namespaced"), 45 | Names: extensionsobj.CustomResourceDefinitionNames{ 46 | Plural: "tapps", 47 | Singular: "tapp", 48 | Kind: "TApp", 49 | ListKind: "TAppList", 50 | }, 51 | Subresources: &extensionsobj.CustomResourceSubresources{ 52 | Status: &extensionsobj.CustomResourceSubresourceStatus{}, 53 | Scale: &extensionsobj.CustomResourceSubresourceScale{ 54 | SpecReplicasPath: ".spec.replicas", 55 | StatusReplicasPath: ".status.replicas", 56 | LabelSelectorPath: &scaleLabelSelector, 57 | }, 58 | }, 59 | }, 60 | } 61 | 62 | // EnsureCRDCreated tries to create/update CRD, returns (true, nil) if succeeding, otherwise returns (false, nil). 63 | // 'err' should always be nil, because it is used by wait.PollUntil(), and it will exit if it is not nil. 64 | func EnsureCRDCreated(client apiextensionsclient.Interface) (created bool, err error) { 65 | crdClient := client.ApiextensionsV1beta1().CustomResourceDefinitions() 66 | presetCRD, err := crdClient.Get(CRD.Name, metav1.GetOptions{}) 67 | if err == nil { 68 | if reflect.DeepEqual(presetCRD.Spec, CRD.Spec) { 69 | klog.V(1).Infof("CRD %s already exists", CRD.Name) 70 | } else { 71 | klog.V(3).Infof("Update CRD %s: %+v -> %+v", CRD.Name, presetCRD.Spec, CRD.Spec) 72 | newCRD := CRD 73 | newCRD.ResourceVersion = presetCRD.ResourceVersion 74 | // Update CRD 75 | if _, err := crdClient.Update(newCRD); err != nil { 76 | klog.Errorf("Error update CRD %s: %v", CRD.Name, err) 77 | return false, nil 78 | } 79 | klog.V(1).Infof("Update CRD %s successfully.", CRD.Name) 80 | } 81 | } else { 82 | // If not exist, create a new one 83 | if _, err := crdClient.Create(CRD); err != nil { 84 | klog.Errorf("Error creating CRD %s: %v", CRD.Name, err) 85 | return false, nil 86 | } 87 | klog.V(1).Infof("Create CRD %s successfully.", CRD.Name) 88 | } 89 | 90 | return true, nil 91 | } 92 | -------------------------------------------------------------------------------- /pkg/tapp/fakes.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack available. 3 | * 4 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 7 | * this file except in compliance with the License. You may obtain a copy of the 8 | * License at 9 | * 10 | * https://opensource.org/licenses/Apache-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations under the License. 16 | */ 17 | 18 | package tapp 19 | 20 | import ( 21 | "fmt" 22 | "sync" 23 | "time" 24 | 25 | corev1 "k8s.io/api/core/v1" 26 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 | "k8s.io/client-go/kubernetes" 28 | "k8s.io/client-go/tools/record" 29 | ) 30 | 31 | type FakeKubeClient struct { 32 | kubeclient *kubernetes.Interface 33 | } 34 | 35 | func (client *FakeKubeClient) Apps() { 36 | 37 | } 38 | 39 | func newFakeInstanceClient() *fakeInstanceClient { 40 | return &fakeInstanceClient{ 41 | Instances: map[string]*Instance{}, 42 | recorder: &record.FakeRecorder{}, 43 | InstanceHealthChecker: &defaultInstanceHealthChecker{}, 44 | } 45 | } 46 | 47 | type fakeInstanceClient struct { 48 | Instances map[string]*Instance 49 | InstancesCreated, InstancesDeleted, InstanceForceDeleted, InstancesUpdated int 50 | recorder record.EventRecorder 51 | InstanceHealthChecker 52 | sync.Mutex 53 | } 54 | 55 | // Delete fakes Instance client deletion. 56 | func (f *fakeInstanceClient) Delete(p *Instance, options *metav1.DeleteOptions) error { 57 | f.Lock() 58 | defer f.Unlock() 59 | if _, ok := f.Instances[p.id]; ok { 60 | delete(f.Instances, p.id) 61 | f.recorder.Eventf(p.parent, corev1.EventTypeNormal, "SuccessfulDelete", "Instance: %v", p.pod.Name) 62 | } else { 63 | return fmt.Errorf("failed to delete: instance %v doesn't exist", p.id) 64 | } 65 | if options == nil || (options.GracePeriodSeconds != nil && *options.GracePeriodSeconds != 0) { 66 | f.InstancesDeleted++ 67 | } else { 68 | f.InstanceForceDeleted++ 69 | } 70 | return nil 71 | } 72 | 73 | // Get fakes getting Instances. 74 | func (f *fakeInstanceClient) Get(p *Instance) (*Instance, bool, error) { 75 | f.Lock() 76 | defer f.Unlock() 77 | if instance, ok := f.Instances[p.id]; ok { 78 | return instance, true, nil 79 | } 80 | return nil, false, nil 81 | } 82 | 83 | // Create fakes Instance creation. 84 | func (f *fakeInstanceClient) Create(p *Instance) error { 85 | f.Lock() 86 | defer f.Unlock() 87 | if _, ok := f.Instances[p.id]; ok { 88 | return fmt.Errorf("failedl to create: instance %v already exists", p.id) 89 | } 90 | f.recorder.Eventf(p.parent, corev1.EventTypeNormal, "SuccessfulCreate", "Instance: %v", p.pod.Name) 91 | pod := p.pod 92 | pod.Status.Phase = corev1.PodRunning 93 | pod.Status.Conditions = make([]corev1.PodCondition, 1) 94 | pod.Status.Conditions[0] = corev1.PodCondition{ 95 | Type: corev1.PodReady, 96 | Status: corev1.ConditionTrue, 97 | } 98 | f.Instances[p.id] = p 99 | f.InstancesCreated++ 100 | return nil 101 | } 102 | 103 | // Update fakes Instance updates. 104 | func (f *fakeInstanceClient) Update(expected, wanted *Instance) error { 105 | f.Lock() 106 | defer f.Unlock() 107 | if _, ok := f.Instances[wanted.id]; !ok { 108 | return fmt.Errorf("failed to update: Instance %v not found", wanted.id) 109 | } 110 | f.Instances[wanted.id] = wanted 111 | f.InstancesUpdated++ 112 | return nil 113 | } 114 | 115 | func (f *fakeInstanceClient) getPodList() []*corev1.Pod { 116 | f.Lock() 117 | defer f.Unlock() 118 | p := []*corev1.Pod{} 119 | for i, Instance := range f.Instances { 120 | if Instance.pod == nil { 121 | continue 122 | } 123 | p = append(p, f.Instances[i].pod) 124 | } 125 | return p 126 | } 127 | 128 | // Delete fakes Instance client deletion. 129 | func (f *fakeInstanceClient) DeleteInstance(id string) error { 130 | f.Lock() 131 | defer f.Unlock() 132 | if _, ok := f.Instances[id]; ok { 133 | delete(f.Instances, id) 134 | } else { 135 | return fmt.Errorf("delete failed: instance %v doesn't exist", id) 136 | } 137 | return nil 138 | } 139 | 140 | func (f *fakeInstanceClient) setDeletionTimestamp(id string, t time.Time) error { 141 | f.Lock() 142 | defer f.Unlock() 143 | if _, ok := f.Instances[id]; !ok { 144 | return fmt.Errorf("instance %v not found", id) 145 | } 146 | f.Instances[id].pod.DeletionTimestamp = &metav1.Time{Time: t} 147 | return nil 148 | } 149 | 150 | func (f *fakeInstanceClient) setPodStatus(id string, status corev1.PodPhase) error { 151 | f.Lock() 152 | defer f.Unlock() 153 | if _, ok := f.Instances[id]; !ok { 154 | return fmt.Errorf("instance %v not found", id) 155 | } 156 | f.Instances[id].pod.Status.Phase = status 157 | return nil 158 | } 159 | 160 | func (f *fakeInstanceClient) setPodReason(id string, reason string) error { 161 | f.Lock() 162 | defer f.Unlock() 163 | if _, ok := f.Instances[id]; !ok { 164 | return fmt.Errorf("instance %v not found", id) 165 | } 166 | f.Instances[id].pod.Status.Reason = reason 167 | return nil 168 | } 169 | -------------------------------------------------------------------------------- /pkg/tapp/identity_mappers.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack available. 3 | * 4 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 7 | * this file except in compliance with the License. You may obtain a copy of the 8 | * License at 9 | * 10 | * https://opensource.org/licenses/Apache-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations under the License. 16 | */ 17 | 18 | package tapp 19 | 20 | import ( 21 | "crypto/md5" 22 | "fmt" 23 | 24 | tappv1 "tkestack.io/tapp/pkg/apis/tappcontroller/v1" 25 | 26 | corev1 "k8s.io/api/core/v1" 27 | ) 28 | 29 | // identityMapper is an interface for assigning identities to a instance. 30 | // All existing identity mappers just append "-(index)" to the tapp name to 31 | // generate a unique identity. 32 | // There's a more elegant way to achieve this mapping, but we're 33 | // taking the simplest route till we have data on whether users will need 34 | // more customization. 35 | // Note that running a single identity mapper is not guaranteed to give 36 | // your instance a unique identity. You must run them all. Order doesn't matter. 37 | type identityMapper interface { 38 | // SetIdentity takes an id and assigns the given instance an identity based 39 | // on the tapp spec. The is must be unique amongst members of the 40 | // tapp. 41 | SetIdentity(id string, pod *corev1.Pod) 42 | 43 | // Identity returns the identity of the instance. 44 | Identity(pod *corev1.Pod) string 45 | } 46 | 47 | func newIdentityMappers(tapp *tappv1.TApp) []identityMapper { 48 | return []identityMapper{ 49 | &NameIdentityMapper{tapp}, 50 | } 51 | } 52 | 53 | // NameIdentityMapper assigns names to instances. 54 | // It also puts the instance in the same namespace as the parent. 55 | type NameIdentityMapper struct { 56 | tapp *tappv1.TApp 57 | } 58 | 59 | // SetIdentity sets the instance namespace, name, hostname and subdomain. 60 | func (n *NameIdentityMapper) SetIdentity(id string, pod *corev1.Pod) { 61 | pod.Name = fmt.Sprintf("%v-%v", n.tapp.Name, id) 62 | pod.Namespace = n.tapp.Namespace 63 | pod.Labels[tappv1.TAppInstanceKey] = id 64 | 65 | if n.tapp.Spec.ServiceName != "" { 66 | pod.Spec.Hostname = pod.Name 67 | pod.Spec.Subdomain = n.tapp.Spec.ServiceName 68 | } 69 | 70 | return 71 | } 72 | 73 | // Identity returns the name identity of the instance. 74 | func (n *NameIdentityMapper) Identity(instance *corev1.Pod) string { 75 | return n.String(instance) 76 | } 77 | 78 | // String is a string function for the name identity of the instance. 79 | func (n *NameIdentityMapper) String(instance *corev1.Pod) string { 80 | return fmt.Sprintf("%v/%v", instance.Namespace, instance.Name) 81 | } 82 | 83 | // identityHash computes a hash of the instance by running all the above identity 84 | // mappers. 85 | func identityHash(tapp *tappv1.TApp, pod *corev1.Pod) string { 86 | id := "" 87 | for _, idMapper := range newIdentityMappers(tapp) { 88 | id += idMapper.Identity(pod) 89 | } 90 | return fmt.Sprintf("%x", md5.Sum([]byte(id))) 91 | } 92 | -------------------------------------------------------------------------------- /pkg/tapp/identity_mappers_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack available. 3 | * 4 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 7 | * this file except in compliance with the License. You may obtain a copy of the 8 | * License at 9 | * 10 | * https://opensource.org/licenses/Apache-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations under the License. 16 | */ 17 | 18 | package tapp 19 | 20 | import ( 21 | "fmt" 22 | "testing" 23 | 24 | tappv1 "tkestack.io/tapp/pkg/apis/tappcontroller/v1" 25 | "tkestack.io/tapp/pkg/testutil" 26 | ) 27 | 28 | func TestInstanceIDName(t *testing.T) { 29 | tapp := testutil.CreateValidTApp(2) 30 | for i := 0; i < int(tapp.Spec.Replicas); i++ { 31 | instanceName := fmt.Sprintf("%v-%d", tapp.Name, i) 32 | instance, err := newInstance(tapp, fmt.Sprintf("%d", i)) 33 | if err != nil { 34 | t.Fatalf("Failed to generate instance %v", err) 35 | } 36 | pod := instance.pod 37 | if pod.Name != instanceName || pod.Namespace != tapp.Namespace { 38 | t.Errorf("Wrong name identity, expected %v, real:%v", pod.Name, instanceName) 39 | } 40 | 41 | id, ok := pod.Labels[tappv1.TAppInstanceKey] 42 | instanceId := fmt.Sprintf("%d", i) 43 | if !ok || id != instanceId { 44 | t.Errorf("test on pod.lables[%s] failed, expected:%s, real:%s", tappv1.TAppInstanceKey, instanceId, id) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /pkg/tapp/instance_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack available. 3 | * 4 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 7 | * this file except in compliance with the License. You may obtain a copy of the 8 | * License at 9 | * 10 | * https://opensource.org/licenses/Apache-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations under the License. 16 | */ 17 | 18 | package tapp 19 | 20 | import ( 21 | "testing" 22 | 23 | "tkestack.io/tapp/pkg/hash" 24 | "tkestack.io/tapp/pkg/testutil" 25 | 26 | corev1 "k8s.io/api/core/v1" 27 | ) 28 | 29 | func TestMergePod(t *testing.T) { 30 | tapp := testutil.CreateValidTApp(1) 31 | instance, err := newInstance(tapp, "0") 32 | if err != nil { 33 | t.Errorf("new instance failed,error:%+v", err) 34 | } 35 | 36 | pod := instance.pod 37 | 38 | newPod := pod.DeepCopy() 39 | newPod.Spec.Containers[0].Image = "image.new" 40 | newPod.Spec.Containers[0].Name = "name.new" 41 | newPod.Labels[hash.TemplateHashKey] = "tappHashKey.new" 42 | mergePod(pod, newPod) 43 | if pod.Spec.Containers[0].Image != newPod.Spec.Containers[0].Image { 44 | t.Errorf("pod image not updated") 45 | } 46 | if pod.Spec.Containers[0].Name == newPod.Spec.Containers[0].Name { 47 | t.Errorf("pod name updated") 48 | } 49 | if pod.Labels[hash.TemplateHashKey] != newPod.Labels[hash.TemplateHashKey] { 50 | t.Errorf("TAppHashKey not updated") 51 | } 52 | 53 | // Check the case that new pod has added some containers 54 | pod.Spec.Containers = append(pod.Spec.Containers, corev1.Container{Image: "test"}) 55 | mergePod(pod, newPod) 56 | if len(pod.Spec.Containers) != 2 { 57 | t.Errorf("Expected containers number is 2, got: %d", len(pod.Spec.Containers)) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /pkg/tapp/tapp_utils.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack available. 3 | * 4 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 7 | * this file except in compliance with the License. You may obtain a copy of the 8 | * License at 9 | * 10 | * https://opensource.org/licenses/Apache-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations under the License. 16 | */ 17 | 18 | package tapp 19 | 20 | import ( 21 | "fmt" 22 | 23 | v1 "tkestack.io/tapp/pkg/apis/tappcontroller/v1" 24 | 25 | corev1 "k8s.io/api/core/v1" 26 | "k8s.io/client-go/kubernetes" 27 | client "k8s.io/client-go/kubernetes/typed/core/v1" 28 | ) 29 | 30 | // overlappingTApps sorts a list of TApps by creation timestamp, using their names as a tie breaker. 31 | // Generally used to tie break between TApps that have overlapping selectors. 32 | type overlappingTApps []*v1.TApp 33 | 34 | func (o overlappingTApps) Len() int { return len(o) } 35 | func (o overlappingTApps) Swap(i, j int) { o[i], o[j] = o[j], o[i] } 36 | 37 | func (o overlappingTApps) Less(i, j int) bool { 38 | if o[i].CreationTimestamp.Equal(&o[j].CreationTimestamp) { 39 | return o[i].Name < o[j].Name 40 | } 41 | return o[i].CreationTimestamp.Before(&o[j].CreationTimestamp) 42 | } 43 | 44 | // podClient returns the given podClient for the given kubeClient/ns. 45 | func podClient(kubeClient kubernetes.Interface, ns string) client.PodInterface { 46 | return kubeClient.CoreV1().Pods(ns) 47 | } 48 | 49 | // pvcClient returns the given pvcClient for the given kubeClient/ns. 50 | func pvcClient(kubeClient kubernetes.Interface, ns string) client.PersistentVolumeClaimInterface { 51 | return kubeClient.CoreV1().PersistentVolumeClaims(ns) 52 | } 53 | 54 | func getPodTemplate(spec *v1.TAppSpec, id string) (*corev1.PodTemplateSpec, error) { 55 | templateName := getPodTemplateName(spec.Templates, id, spec.DefaultTemplateName) 56 | 57 | if templateName == v1.DefaultTemplateName { 58 | return &spec.Template, nil 59 | } else if template, ok := spec.TemplatePool[templateName]; ok { 60 | return &template, nil 61 | } else { 62 | return nil, fmt.Errorf("template not found in templatePool") 63 | } 64 | } 65 | 66 | func getPodIndex(pod *corev1.Pod) (string, error) { 67 | if key, ok := pod.Labels[v1.TAppInstanceKey]; ok { 68 | return key, nil 69 | } 70 | return "", fmt.Errorf("pod: %s without %s", pod.Name, v1.TAppInstanceKey) 71 | } 72 | 73 | func isPodInActive(pod *corev1.Pod) bool { 74 | return isPodDying(pod) || pod.Status.Phase != corev1.PodRunning 75 | } 76 | 77 | func isPodDying(pod *corev1.Pod) bool { 78 | return pod != nil && pod.DeletionTimestamp != nil 79 | } 80 | 81 | func isPodCompleted(pod *corev1.Pod) bool { 82 | if pod.Status.Phase == corev1.PodFailed || pod.Status.Phase == corev1.PodSucceeded { 83 | return true 84 | } 85 | return false 86 | } 87 | 88 | func getPodFullName(pod *corev1.Pod) string { 89 | return pod.Namespace + "/" + pod.Name 90 | } 91 | 92 | func isPodFailed(pod *corev1.Pod) bool { 93 | if pod.Status.Phase == corev1.PodFailed { 94 | return true 95 | } 96 | return false 97 | } 98 | 99 | func isTAppFinished(tapp *v1.TApp) bool { 100 | appStatus := tapp.Status.AppStatus 101 | if appStatus == v1.AppFailed || appStatus == v1.AppSucc || appStatus == v1.AppKilled { 102 | return true 103 | } 104 | return false 105 | } 106 | 107 | func isInstanceAlive(status v1.InstanceStatus) bool { 108 | switch status { 109 | case v1.InstanceNotCreated: 110 | return true 111 | case v1.InstancePending: 112 | return true 113 | case v1.InstanceRunning: 114 | return true 115 | case v1.InstanceKilling: 116 | return true 117 | case v1.InstanceUnknown: 118 | return true 119 | case v1.InstancePodFailed: 120 | return true 121 | case v1.InstancePodSucc: 122 | return true 123 | } 124 | return false 125 | } 126 | 127 | func getRollingTemplateKey(tapp *v1.TApp) string { 128 | if len(tapp.Spec.UpdateStrategy.Template) != 0 { 129 | return tapp.Spec.UpdateStrategy.Template 130 | } else { 131 | return tapp.Spec.DefaultTemplateName 132 | } 133 | } 134 | 135 | func shouldRollUpdate(tapp *v1.TApp, updates []*Instance) bool { 136 | template := getRollingTemplateKey(tapp) 137 | if len(template) == 0 { 138 | return false 139 | } 140 | 141 | for _, update := range updates { 142 | if getPodTemplateName(tapp.Spec.Templates, update.id, tapp.Spec.DefaultTemplateName) == template { 143 | return true 144 | } 145 | } 146 | return false 147 | } 148 | 149 | func extractInstanceId(instances []*Instance) []string { 150 | a := []string{} 151 | for _, instance := range instances { 152 | a = append(a, instance.id) 153 | } 154 | return a 155 | } 156 | 157 | // getPodTemplateName returns template name for pod whose id is podId, default is 'DefaultTemplateName' 158 | func getPodTemplateName(templates map[string]string, podId string, defaultTemplateName string) string { 159 | if name, exist := templates[podId]; !exist { 160 | return defaultTemplateName 161 | } else { 162 | return name 163 | } 164 | } 165 | 166 | // GetPodCondition extracts the provided condition from the given status and returns that. 167 | // Returns nil and -1 if the condition is not present, and the index of the located condition. 168 | func GetPodCondition(status *corev1.PodStatus, conditionType corev1.PodConditionType) (int, *corev1.PodCondition) { 169 | if status == nil { 170 | return -1, nil 171 | } 172 | for i := range status.Conditions { 173 | if status.Conditions[i].Type == conditionType { 174 | return i, &status.Conditions[i] 175 | } 176 | } 177 | return -1, nil 178 | } 179 | -------------------------------------------------------------------------------- /pkg/testutil/utils.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack available. 3 | * 4 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 7 | * this file except in compliance with the License. You may obtain a copy of the 8 | * License at 9 | * 10 | * https://opensource.org/licenses/Apache-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations under the License. 16 | */ 17 | 18 | package testutil 19 | 20 | import ( 21 | "fmt" 22 | "strconv" 23 | 24 | v1 "tkestack.io/tapp/pkg/apis/tappcontroller/v1" 25 | "tkestack.io/tapp/pkg/hash" 26 | "tkestack.io/tapp/pkg/util" 27 | 28 | corev1 "k8s.io/api/core/v1" 29 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 | ) 31 | 32 | const ( 33 | FakeTAppName = "fake-tapp-name" 34 | FakeLabelKey = "fake_tapp_label_key" 35 | FakeLabelValue = "fake_tapp_label_value" 36 | ) 37 | 38 | var tappHash = hash.NewTappHash() 39 | 40 | func AddPodTemplate(tapp *v1.TApp, templateName string, template *corev1.PodTemplateSpec) error { 41 | template = template.DeepCopy() 42 | tappHash.SetTemplateHash(template) 43 | tappHash.SetUniqHash(template) 44 | 45 | if tapp.Spec.TemplatePool == nil { 46 | tapp.Spec.TemplatePool = make(map[string]corev1.PodTemplateSpec) 47 | } 48 | tapp.Spec.TemplatePool[templateName] = *template 49 | 50 | if tappHash.GetTemplateHash(tapp.Spec.Template.Labels) != tappHash.GetTemplateHash(template.Labels) { 51 | tapp.Spec.Template = *template 52 | } 53 | 54 | return nil 55 | } 56 | 57 | func RampUp(tapp *v1.TApp, replica uint, templateName string) error { 58 | if replica <= uint(tapp.Spec.Replicas) { 59 | return fmt.Errorf("replica:%d should great than tapp.Spec.Replicas:%d", replica, tapp.Spec.Replicas) 60 | } 61 | 62 | if tapp.Spec.Templates == nil { 63 | tapp.Spec.Templates = make(map[string]string) 64 | } 65 | 66 | for i := int(tapp.Spec.Replicas); i < int(replica); i++ { 67 | id := strconv.Itoa(i) 68 | tapp.Spec.Templates[id] = templateName 69 | } 70 | tapp.Spec.Replicas = int32(replica) 71 | return nil 72 | } 73 | 74 | func ShrinkDown(tapp *v1.TApp, replica uint) error { 75 | if replica >= uint(tapp.Spec.Replicas) { 76 | return fmt.Errorf("replica:%d should less than tapp.Spec.Replicas:%d", replica, tapp.Spec.Replicas) 77 | } 78 | 79 | tapp.Spec.Replicas = int32(replica) 80 | 81 | for i := int(replica); i < int(tapp.Spec.Replicas); i++ { 82 | id := strconv.Itoa(i) 83 | delete(tapp.Spec.Templates, id) 84 | if tapp.Spec.Statuses != nil { 85 | delete(tapp.Spec.Statuses, id) 86 | } 87 | } 88 | 89 | return nil 90 | } 91 | 92 | func UpdateInstanceTemplate(tapp *v1.TApp, instanceId string, templateId string) error { 93 | if tapp.Spec.Templates == nil { 94 | tapp.Spec.Templates = map[string]string{} 95 | } 96 | // other validate is done in corev1server v1#validate.go 97 | tapp.Spec.Templates[instanceId] = templateId 98 | return nil 99 | } 100 | 101 | func KillInstance(tapp *v1.TApp, instanceId string) { 102 | if tapp.Spec.Statuses == nil { 103 | tapp.Spec.Statuses = map[string]v1.InstanceStatus{} 104 | } 105 | tapp.Spec.Statuses[instanceId] = v1.InstanceKilled 106 | } 107 | 108 | func KillAllInstance(tapp *v1.TApp) { 109 | if tapp.Spec.Statuses == nil { 110 | tapp.Spec.Statuses = map[string]v1.InstanceStatus{} 111 | } 112 | for i := 0; i < int(tapp.Spec.Replicas); i++ { 113 | id := strconv.Itoa(i) 114 | if _, ok := tapp.Spec.Statuses[id]; !ok { 115 | tapp.Spec.Statuses[id] = v1.InstanceKilled 116 | } 117 | } 118 | } 119 | 120 | // 1. delete corresponding pod from apiserver 121 | // 2. call restartInstance 122 | // return true if tapp is updated 123 | func RestartInstance(tapp *v1.TApp, instanceId string) bool { 124 | if tapp.Spec.Statuses != nil { 125 | if status, ok := tapp.Spec.Statuses[instanceId]; ok { 126 | if status == v1.InstanceKilled { 127 | delete(tapp.Spec.Statuses, instanceId) 128 | } 129 | } 130 | } 131 | // Delete instance status 132 | delete(tapp.Status.Statuses, instanceId) 133 | 134 | return true 135 | } 136 | 137 | func CreateValidPodTemplate() *corev1.PodTemplateSpec { 138 | validLabels := map[string]string{FakeLabelKey: FakeLabelValue} 139 | return &corev1.PodTemplateSpec{ 140 | ObjectMeta: metav1.ObjectMeta{ 141 | Labels: validLabels, 142 | Annotations: map[string]string{}, 143 | }, 144 | Spec: corev1.PodSpec{ 145 | RestartPolicy: corev1.RestartPolicyOnFailure, 146 | DNSPolicy: corev1.DNSClusterFirst, 147 | Containers: []corev1.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}}, 148 | }, 149 | } 150 | } 151 | 152 | func CreateRawTApp(replica int) *v1.TApp { 153 | validTAppSpec := v1.TAppSpec{ 154 | Replicas: int32(replica), 155 | TemplatePool: map[string]corev1.PodTemplateSpec{}, 156 | Statuses: map[string]v1.InstanceStatus{}, 157 | Templates: map[string]string{}, 158 | } 159 | 160 | return &v1.TApp{ 161 | TypeMeta: metav1.TypeMeta{Kind: "TApp", APIVersion: "apps.tkestack.io/v1"}, 162 | ObjectMeta: metav1.ObjectMeta{Name: FakeTAppName, Namespace: corev1.NamespaceDefault, ResourceVersion: "0"}, 163 | Spec: validTAppSpec, 164 | Status: v1.TAppStatus{Statuses: map[string]v1.InstanceStatus{}}, 165 | } 166 | } 167 | 168 | type PodTemplateCreater func() *corev1.PodTemplateSpec 169 | 170 | func CreateTAppWithTemplateCreater(replica int, creater PodTemplateCreater) *v1.TApp { 171 | tapp := CreateRawTApp(replica) 172 | template := creater() 173 | testTemplate := "testTemplate" 174 | if err := AddPodTemplate(tapp, testTemplate, template); err != nil { 175 | panic(err) 176 | } 177 | 178 | tapp.Spec.Selector = util.GenerateTAppSelector(tapp.Spec.Template.Labels) 179 | 180 | for i := 0; i < int(tapp.Spec.Replicas); i++ { 181 | id := strconv.Itoa(i) 182 | tapp.Spec.Templates[id] = testTemplate 183 | } 184 | return tapp 185 | } 186 | 187 | func CreateValidTApp(replica int) *v1.TApp { 188 | return CreateTAppWithTemplateCreater(replica, CreateValidPodTemplate) 189 | } 190 | -------------------------------------------------------------------------------- /pkg/util/utils.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack available. 3 | * 4 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 7 | * this file except in compliance with the License. You may obtain a copy of the 8 | * License at 9 | * 10 | * https://opensource.org/licenses/Apache-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations under the License. 16 | */ 17 | 18 | package util 19 | 20 | import ( 21 | "fmt" 22 | 23 | v1 "tkestack.io/tapp/pkg/apis/tappcontroller/v1" 24 | "tkestack.io/tapp/pkg/hash" 25 | 26 | corev1 "k8s.io/api/core/v1" 27 | "k8s.io/apimachinery/pkg/api/meta" 28 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 | "k8s.io/apimachinery/pkg/labels" 30 | "k8s.io/apimachinery/pkg/runtime" 31 | ) 32 | 33 | func GetTAppFullName(tapp *v1.TApp) string { 34 | return tapp.Namespace + "/" + tapp.Name 35 | } 36 | 37 | func GenerateTAppSelector(labels map[string]string) *metav1.LabelSelector { 38 | // copy labels to tapp labels, tappHashKey is not supposed to be selector 39 | tappLabels := make(map[string]string, len(labels)) 40 | for k, v := range labels { 41 | tappLabels[k] = v 42 | } 43 | delete(tappLabels, hash.TemplateHashKey) 44 | delete(tappLabels, hash.UniqHashKey) 45 | 46 | return &metav1.LabelSelector{ 47 | MatchLabels: tappLabels, 48 | } 49 | } 50 | 51 | func GetPodFromTemplate(template *corev1.PodTemplateSpec, parentObject runtime.Object, 52 | controllerRef *metav1.OwnerReference) (*corev1.Pod, error) { 53 | desiredLabels := getPodsLabelSet(template) 54 | desiredFinalizers := getPodsFinalizers(template) 55 | desiredAnnotations := getPodsAnnotationSet(template) 56 | accessor, err := meta.Accessor(parentObject) 57 | if err != nil { 58 | return nil, fmt.Errorf("parentObject does not have ObjectMeta, %v", err) 59 | } 60 | prefix := getPodsPrefix(accessor.GetName()) 61 | 62 | pod := &corev1.Pod{ 63 | ObjectMeta: metav1.ObjectMeta{ 64 | Labels: desiredLabels, 65 | Annotations: desiredAnnotations, 66 | GenerateName: prefix, 67 | Finalizers: desiredFinalizers, 68 | }, 69 | } 70 | if controllerRef != nil { 71 | pod.OwnerReferences = append(pod.OwnerReferences, *controllerRef) 72 | } 73 | pod.Spec = *template.Spec.DeepCopy() 74 | return pod, nil 75 | } 76 | 77 | func getPodsLabelSet(template *corev1.PodTemplateSpec) labels.Set { 78 | desiredLabels := make(labels.Set) 79 | for k, v := range template.Labels { 80 | desiredLabels[k] = v 81 | } 82 | return desiredLabels 83 | } 84 | 85 | func getPodsFinalizers(template *corev1.PodTemplateSpec) []string { 86 | desiredFinalizers := make([]string, len(template.Finalizers)) 87 | copy(desiredFinalizers, template.Finalizers) 88 | return desiredFinalizers 89 | } 90 | 91 | func getPodsAnnotationSet(template *corev1.PodTemplateSpec) labels.Set { 92 | desiredAnnotations := make(labels.Set) 93 | for k, v := range template.Annotations { 94 | desiredAnnotations[k] = v 95 | } 96 | return desiredAnnotations 97 | } 98 | 99 | func getPodsPrefix(controllerName string) string { 100 | // use the dash (if the name isn't too long) to make the pod name a bit prettier 101 | prefix := fmt.Sprintf("%s-", controllerName) 102 | return prefix 103 | } 104 | -------------------------------------------------------------------------------- /pkg/version/.gitattributes: -------------------------------------------------------------------------------- 1 | base.go export-subst 2 | -------------------------------------------------------------------------------- /pkg/version/base.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack available. 3 | * 4 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 7 | * this file except in compliance with the License. You may obtain a copy of the 8 | * License at 9 | * 10 | * https://opensource.org/licenses/Apache-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations under the License. 16 | */ 17 | 18 | package version 19 | 20 | // Base version information. 21 | // 22 | // This is the fallback data used when version information from git is not 23 | // provided via go ldflags. It provides an approximation of the Apiswitch 24 | // version for ad-hoc builds (e.g. `go build`) that cannot get the version 25 | // information from git. 26 | // 27 | // If you are looking at these fields in the git tree, they look 28 | // strange. They are modified on the fly by the build process. The 29 | // in-tree values are dummy values used for "git archive", which also 30 | // works for GitHub tar downloads. 31 | var ( 32 | rpmPKGName string = "Not build from rpm package" // rpm package name, if install by rpm 33 | gitBranch string = "Not a git repo" // branch of git 34 | gitCommit string = "8ad6ffff993bd95323da8e1852fa3edf2d8f198c" // sha1 from git, output of $(git rev-parse HEAD) 35 | gitTreeState string = "Not a git tree" // state of git tree, either "clean" or "dirty" 36 | buildDate string = "1970-01-01T00:00:00Z" // build date in ISO8601 format 37 | ) 38 | -------------------------------------------------------------------------------- /pkg/version/verflag/verflag.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack available. 3 | * 4 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 7 | * this file except in compliance with the License. You may obtain a copy of the 8 | * License at 9 | * 10 | * https://opensource.org/licenses/Apache-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations under the License. 16 | */ 17 | 18 | package verflag 19 | 20 | import ( 21 | "fmt" 22 | "os" 23 | "strconv" 24 | 25 | "tkestack.io/tapp/pkg/version" 26 | 27 | flag "github.com/spf13/pflag" 28 | ) 29 | 30 | type versionValue int 31 | 32 | const ( 33 | VersionFalse versionValue = 0 34 | VersionTrue versionValue = 1 35 | VersionRaw versionValue = 2 36 | ) 37 | 38 | const strRawVersion string = "raw" 39 | 40 | func (v *versionValue) IsBoolFlag() bool { 41 | return true 42 | } 43 | 44 | func (v *versionValue) Get() interface{} { 45 | return versionValue(*v) 46 | } 47 | 48 | func (v *versionValue) Set(s string) error { 49 | if s == strRawVersion { 50 | *v = VersionRaw 51 | return nil 52 | } 53 | boolVal, err := strconv.ParseBool(s) 54 | if boolVal { 55 | *v = VersionTrue 56 | } else { 57 | *v = VersionFalse 58 | } 59 | return err 60 | } 61 | 62 | func (v *versionValue) String() string { 63 | if *v == VersionRaw { 64 | return strRawVersion 65 | } 66 | return fmt.Sprintf("%v", bool(*v == VersionTrue)) 67 | } 68 | 69 | // The type of the flag as required by the pflag.Value interface 70 | func (v *versionValue) Type() string { 71 | return "version" 72 | } 73 | 74 | func VersionVar(p *versionValue, name string, value versionValue, usage string) { 75 | *p = value 76 | flag.Var(p, name, usage) 77 | // "--version" will be treated as "--version=true" 78 | flag.Lookup(name).NoOptDefVal = "true" 79 | } 80 | 81 | func Version(name string, value versionValue, usage string) *versionValue { 82 | p := new(versionValue) 83 | VersionVar(p, name, value, usage) 84 | return p 85 | } 86 | 87 | var ( 88 | versionFlag = Version("version", VersionFalse, "Print version information and quit") 89 | ) 90 | 91 | // PrintAndExitIfRequested will check if the -version flag was passed 92 | // and, if so, print the version and exit. 93 | func PrintAndExitIfRequested() { 94 | if *versionFlag == VersionRaw { 95 | fmt.Printf("%#v\n", version.Get()) 96 | os.Exit(0) 97 | } else if *versionFlag == VersionTrue { 98 | fmt.Printf("TApp-controller %s\n", version.Get()) 99 | os.Exit(0) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /pkg/version/version.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making TKEStack available. 3 | * 4 | * Copyright (C) 2012-2019 Tencent. All Rights Reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 7 | * this file except in compliance with the License. You may obtain a copy of the 8 | * License at 9 | * 10 | * https://opensource.org/licenses/Apache-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | * WARRANTIES OF ANY KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations under the License. 16 | */ 17 | 18 | package version 19 | 20 | import ( 21 | "fmt" 22 | "runtime" 23 | ) 24 | 25 | // Info contains versioning information. 26 | // TODO: Add []string of api versions supported? It's still unclear 27 | // how we'll want to distribute that information. 28 | type Info struct { 29 | RpmPKGName string `json:"rpmPKGName"` 30 | GitBranch string `json:"gitBranch"` 31 | GitCommit string `json:"gitCommit"` 32 | GitTreeState string `json:"gitTreeState"` 33 | BuildDate string `json:"buildDate"` 34 | GoVersion string `json:"goVersion"` 35 | Compiler string `json:"compiler"` 36 | Platform string `json:"platform"` 37 | } 38 | 39 | // String returns info as a human-friendly version string. 40 | func (info Info) String() string { 41 | return info.GitBranch + "-" + info.GitCommit 42 | } 43 | 44 | // Get returns the overall codebase version. It's for detecting 45 | // what code a binary was built from. 46 | func Get() Info { 47 | // These variables typically come from -ldflags settings and in 48 | // their absence fallback to the settings in pkg/version/base.go 49 | return Info{ 50 | RpmPKGName: rpmPKGName, 51 | GitBranch: gitBranch, 52 | GitCommit: gitCommit, 53 | GitTreeState: gitTreeState, 54 | BuildDate: buildDate, 55 | GoVersion: runtime.Version(), 56 | Compiler: runtime.Compiler, 57 | Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH), 58 | } 59 | } 60 | --------------------------------------------------------------------------------