├── .gitignore ├── docs └── image │ └── weixin.png ├── deploy ├── joylive-injector │ ├── index.yaml │ ├── templates │ │ ├── secret.yaml │ │ ├── service.yaml │ │ ├── NOTES.txt │ │ ├── configmap.yaml │ │ ├── servicemonitor.yaml │ │ ├── mutatingwebhook.yaml │ │ ├── rbac.yaml │ │ ├── _helpers.tpl │ │ ├── deployment.yaml │ │ └── otel.yaml │ ├── config │ │ ├── injector.yaml │ │ ├── bootstrap.properties │ │ └── logback.xml │ ├── .helmignore │ ├── README-zh.md │ ├── Chart.yaml │ ├── README.md │ └── crds │ │ └── injector.joylive.io_agentversions.yaml ├── packages │ ├── joylive-injector-1.1.0.tgz │ ├── joylive-injector-1.2.0.tgz │ ├── joylive-injector-1.3.0.tgz │ ├── joylive-injector-1.3.1.tgz │ ├── joylive-injector-1.3.2.tgz │ ├── joylive-injector-1.3.3.tgz │ ├── joylive-injector-1.3.4.tgz │ ├── joylive-injector-1.3.5.tgz │ ├── joylive-injector-1.3.6.tgz │ ├── joylive-injector-1.3.7.tgz │ └── joylive-injector-1.3.8.tgz ├── cfssl │ ├── create-ssl.sh │ ├── dac-gencert.json │ ├── dac-ca-csr.json │ ├── create-secret.sh │ ├── dac-csr.json │ ├── dac.csr │ ├── dac-key.pem │ ├── dac-ca.csr │ ├── dac.pem │ ├── dac-ca.pem │ └── dac-ca-key.pem ├── examples │ ├── injector_v1_agentversion.yaml │ └── joylive-injector-rules.yaml └── Dockerfile ├── .vscode └── launch.json ├── LocalBuild.dockerfile ├── .github ├── PULL_REQUEST_TEMPLATE.md ├── workflows │ ├── build.yml │ ├── docker-build.yml │ └── docker-build-release.yml └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── hack ├── boilerplate.go.txt └── update-codegen.sh ├── pkg ├── apm │ ├── types.go │ └── sgm.go ├── route │ ├── route_health.go │ ├── route_available.go │ └── router.go ├── log │ ├── tee.go │ ├── option.go │ ├── rotate.go │ └── log.go ├── config │ ├── injector.go │ └── config.go ├── watcher │ ├── init.go │ ├── av_watcher.go │ └── cm_watcher.go ├── resource │ ├── control_test.go │ ├── control.go │ └── resource.go └── admission │ └── admission.go ├── community ├── members.md └── membership.md ├── client-go ├── clientset │ └── versioned │ │ ├── fake │ │ ├── doc.go │ │ ├── register.go │ │ └── clientset_generated.go │ │ ├── typed │ │ └── injector │ │ │ └── v1 │ │ │ ├── generated_expansion.go │ │ │ ├── doc.go │ │ │ ├── fake │ │ │ ├── doc.go │ │ │ ├── fake_injector_client.go │ │ │ └── fake_agentversion.go │ │ │ └── injector_client.go │ │ ├── scheme │ │ ├── doc.go │ │ └── register.go │ │ └── clientset.go ├── apis │ └── injector │ │ └── v1 │ │ ├── doc.go │ │ ├── zz_generated.defaults.go │ │ ├── register.go │ │ ├── agentversion_types.go │ │ └── zz_generated.deepcopy.go ├── listers │ └── injector │ │ └── v1 │ │ ├── expansion_generated.go │ │ └── agentversion.go ├── applyconfiguration │ ├── utils.go │ ├── internal │ │ └── internal.go │ └── injector │ │ └── v1 │ │ └── agentversionspec.go └── informers │ └── externalversions │ ├── internalinterfaces │ └── factory_interfaces.go │ ├── injector │ ├── interface.go │ └── v1 │ │ ├── interface.go │ │ └── agentversion.go │ ├── generic.go │ └── factory.go ├── InnerBuild.dockerfile ├── Makefile ├── Dockerfile ├── README-zh.md ├── CONTRIBUTING.md ├── README.md ├── CODE_OF_CONDUCT.md ├── go.mod └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | bin 3 | -------------------------------------------------------------------------------- /docs/image/weixin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jd-opensource/joylive-injector/HEAD/docs/image/weixin.png -------------------------------------------------------------------------------- /deploy/joylive-injector/index.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | entries: {} 3 | generated: "2024-04-24T19:27:32.303201+08:00" 4 | -------------------------------------------------------------------------------- /deploy/packages/joylive-injector-1.1.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jd-opensource/joylive-injector/HEAD/deploy/packages/joylive-injector-1.1.0.tgz -------------------------------------------------------------------------------- /deploy/packages/joylive-injector-1.2.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jd-opensource/joylive-injector/HEAD/deploy/packages/joylive-injector-1.2.0.tgz -------------------------------------------------------------------------------- /deploy/packages/joylive-injector-1.3.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jd-opensource/joylive-injector/HEAD/deploy/packages/joylive-injector-1.3.0.tgz -------------------------------------------------------------------------------- /deploy/packages/joylive-injector-1.3.1.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jd-opensource/joylive-injector/HEAD/deploy/packages/joylive-injector-1.3.1.tgz -------------------------------------------------------------------------------- /deploy/packages/joylive-injector-1.3.2.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jd-opensource/joylive-injector/HEAD/deploy/packages/joylive-injector-1.3.2.tgz -------------------------------------------------------------------------------- /deploy/packages/joylive-injector-1.3.3.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jd-opensource/joylive-injector/HEAD/deploy/packages/joylive-injector-1.3.3.tgz -------------------------------------------------------------------------------- /deploy/packages/joylive-injector-1.3.4.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jd-opensource/joylive-injector/HEAD/deploy/packages/joylive-injector-1.3.4.tgz -------------------------------------------------------------------------------- /deploy/packages/joylive-injector-1.3.5.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jd-opensource/joylive-injector/HEAD/deploy/packages/joylive-injector-1.3.5.tgz -------------------------------------------------------------------------------- /deploy/packages/joylive-injector-1.3.6.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jd-opensource/joylive-injector/HEAD/deploy/packages/joylive-injector-1.3.6.tgz -------------------------------------------------------------------------------- /deploy/packages/joylive-injector-1.3.7.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jd-opensource/joylive-injector/HEAD/deploy/packages/joylive-injector-1.3.7.tgz -------------------------------------------------------------------------------- /deploy/packages/joylive-injector-1.3.8.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jd-opensource/joylive-injector/HEAD/deploy/packages/joylive-injector-1.3.8.tgz -------------------------------------------------------------------------------- /deploy/cfssl/create-ssl.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ex 4 | 5 | rm -f *.pem *.csr 6 | 7 | cfssl gencert --initca=true dac-ca-csr.json | cfssljson --bare dac-ca 8 | cfssl gencert --ca dac-ca.pem --ca-key dac-ca-key.pem --config dac-gencert.json dac-csr.json | cfssljson --bare dac 9 | -------------------------------------------------------------------------------- /deploy/joylive-injector/templates/secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | type: Opaque 3 | kind: Secret 4 | metadata: 5 | name: dynamic-admission-control-certs 6 | namespace: {{ .Values.namespace }} 7 | data: 8 | dac-key.pem: {{ .Values.caKeyBundle }} 9 | dac.pem: {{ .Values.caPubBundle }} 10 | 11 | -------------------------------------------------------------------------------- /deploy/cfssl/dac-gencert.json: -------------------------------------------------------------------------------- 1 | { 2 | "signing": { 3 | "default": { 4 | "usages": [ 5 | "signing", 6 | "digital signature", 7 | "key encipherment", 8 | "server auth", 9 | "client auth" 10 | ], 11 | "expiry": "87600h" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /deploy/joylive-injector/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "joylive-injector.name" . }} 5 | namespace: {{ .Values.namespace }} 6 | labels: 7 | svc: {{ include "joylive-injector.name" . }} 8 | spec: 9 | ports: 10 | - port: 443 11 | targetPort: 443 12 | selector: 13 | app: {{ include "joylive-injector.name" . }} -------------------------------------------------------------------------------- /deploy/joylive-injector/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | Thank you for installing {{ .Chart.Name }}. {{ .Chart.Name }} has been installed successfully. 2 | For usage, please visit: https://github.com/jd-opensource/joylive-injector 3 | 4 | Your release is named {{ .Release.Name }}. 5 | 6 | To learn more about the release, try: 7 | 8 | $ helm status {{ .Release.Name }} 9 | $ helm get all {{ .Release.Name }} 10 | 11 | -------------------------------------------------------------------------------- /deploy/examples/injector_v1_agentversion.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: injector.joylive.io/v1 2 | kind: AgentVersion 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: joylive-injector 6 | app.kubernetes.io/managed-by: joylive-injector 7 | name: agentversion-1.0.0-e979a2d-amd64 8 | namespace: joylive 9 | spec: 10 | version: 1.0.0-e979a2d-AMD64 11 | configMapName: joylive-agent-config-1.0.0-e979a2d-amd64 12 | enable: true -------------------------------------------------------------------------------- /deploy/joylive-injector/config/injector.yaml: -------------------------------------------------------------------------------- 1 | # Define the version, environment variables, and other information of joylive-injector injecting joylive-agent 2 | agent: 3 | image: {{ .Values.agent.image.repository }} 4 | version: {{ .Values.agent.image.tag }} 5 | envs: 6 | {{- with .Values.agent.envs }} 7 | {{- toYaml . | nindent 4 }} 8 | {{- else }} 9 | JAVA_TOOL_OPTIONS: -javaagent:/joylive/live.jar 10 | {{- end }} -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch Main", 6 | "type": "go", 7 | "request": "launch", 8 | "mode": "auto", 9 | "program": "${workspaceFolder}/main.go", 10 | "args": [ 11 | "--match-label=k3=v3;k4=v4;", 12 | "--listen=:8443" 13 | ] 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /LocalBuild.dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | 3 | ARG TZ="Asia/Shanghai" 4 | 5 | ENV TZ ${TZ} 6 | ENV LANG en_US.UTF-8 7 | ENV LC_ALL en_US.UTF-8 8 | ENV LANGUAGE en_US:en 9 | 10 | RUN set -ex \ 11 | && apk add bash tzdata ca-certificates \ 12 | && ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime \ 13 | && echo ${TZ} > /etc/timezone \ 14 | && rm -rf /var/cache/apk/* 15 | 16 | ADD bin/joylive-injector /opt/ 17 | 18 | ENTRYPOINT ["/opt/joylive-injector"] 19 | -------------------------------------------------------------------------------- /deploy/cfssl/dac-ca-csr.json: -------------------------------------------------------------------------------- 1 | { 2 | "CN": "Dynamic Admission Control CA", 3 | "key": { 4 | "algo": "rsa", 5 | "size": 4096 6 | }, 7 | "names": [ 8 | { 9 | "O": "Dynamic Admission Control", 10 | "OU": "Dynamic Admission Control Security", 11 | "L": "Beijing", 12 | "ST": "Beijing", 13 | "C": "CN" 14 | } 15 | ], 16 | "ca": { 17 | "expiry": "87600h" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /deploy/joylive-injector/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## What is the purpose of the change? 2 | 3 | 4 | ## Checklist 5 | - [x] Make sure there is a [GitHub_issue](https://github.com/jd-opensource/joylive-injector/issues) field for the change. 6 | - [x] Write a pull request description that is detailed enough to understand what the pull request does, how, and why. 7 | - [x] Write necessary unit-test to verify your logic correction. 8 | - [x] Make sure gitHub actions can pass. [Why the workflow is failing and how to fix it?](../CONTRIBUTING.md) -------------------------------------------------------------------------------- /deploy/joylive-injector/templates/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ .Values.configMapName }} 5 | namespace: {{ .Values.namespace }} 6 | labels: {{ include "joylive-injector.selectorLabels" . | nindent 4 }} 7 | data: 8 | {{- $root := . }} 9 | {{- range $path, $_ := $root.Files.Glob "config/*" }} 10 | {{- $fileName := base $path }} 11 | {{ $fileName }}: | 12 | {{- $fileContent := $root.Files.Get $path }} 13 | {{- $parsedContent := tpl $fileContent $root }} 14 | {{ $parsedContent | indent 4 -}} 15 | {{- end }} -------------------------------------------------------------------------------- /hack/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 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 | -------------------------------------------------------------------------------- /deploy/cfssl/create-secret.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ex 4 | 5 | #kubectl create secret generic \ 6 | # dynamic-admission-control-certs \ 7 | # --namespace joylive \ 8 | # --from-file=dac.pem \ 9 | # --from-file=dac-key.pem 10 | 11 | CA_BUNDLE=$(cat dac-ca.pem | base64 | tr -d '\n') 12 | sed -i '' "s@\${CA_BUNDLE}@${CA_BUNDLE}@g" ../*/*.yaml 13 | 14 | CA_KEY_BUNDLE=$(cat dac-key.pem | base64 | tr -d '\n') 15 | sed -i '' "s@\${CA_KEY_BUNDLE}@${CA_KEY_BUNDLE}@g" ../*/*.yaml 16 | 17 | CA_PUB_BUNDLE=$(cat dac.pem | base64 | tr -d '\n') 18 | sed -i '' "s@\${CA_PUB_BUNDLE}@${CA_PUB_BUNDLE}@g" ../*/*.yaml -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: Go build 5 | 6 | on: 7 | push: 8 | branches: [ "main" ] 9 | pull_request: 10 | branches: [ "main" ] 11 | 12 | jobs: 13 | 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v4 21 | with: 22 | go-version: '1.22.3' 23 | 24 | - name: Build 25 | run: go build -v -a -o bin/joylive-injector main.go 26 | -------------------------------------------------------------------------------- /pkg/apm/types.go: -------------------------------------------------------------------------------- 1 | package apm 2 | 3 | import ( 4 | "context" 5 | v1 "k8s.io/api/apps/v1" 6 | "sync" 7 | ) 8 | 9 | type Appender interface { 10 | Modify(ctx context.Context, target *v1.Deployment) (bool, error) 11 | } 12 | 13 | // AppenderTypes is a factory holder for any AppenderType 14 | var AppenderTypes = make(map[string]Appender) 15 | 16 | var mux sync.Mutex 17 | 18 | // RegisterAppenderType Inject the Appender factory to automatically generate Appender objects. 19 | func RegisterAppenderType(key string, appender Appender) { 20 | mux.Lock() 21 | defer mux.Unlock() 22 | if _, ok := AppenderTypes[key]; !ok { 23 | AppenderTypes[key] = appender 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /community/members.md: -------------------------------------------------------------------------------- 1 | See [membership.md](./membership.md) for the definition of these community contributor roles. 2 | 3 | ### Maintainers 4 | 5 | (As of November, 25, 2024) 6 | 7 | | Name | Github ID | Email | 8 | |-------------|------------|---------------------| 9 | | Xiaofeng He | hexiaofeng | | 10 | | Zhiguo Chen | chenzhiguo | | 11 | 12 | ### Committers 13 | 14 | | Name | Github ID | Email | 15 | |-------------|-------------|--------------------------| 16 | | Zhaoyan Wu | YanRyan | <527421522@qq.com> | 17 | | Zhichao Xue | zhichao.xue | | 18 | 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[Bug]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /pkg/route/route_health.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | func init() { 8 | RegisterHandler(HandleFunc{ 9 | Path: "/healthz", 10 | Method: http.MethodGet, 11 | Func: ok, 12 | }) 13 | RegisterHandler(HandleFunc{ 14 | Path: "/livez", 15 | Method: http.MethodGet, 16 | Func: ok, 17 | }) 18 | RegisterHandler(HandleFunc{ 19 | Path: "/readyz", 20 | Method: http.MethodGet, 21 | Func: ok, 22 | }) 23 | RegisterHandler(HandleFunc{ 24 | Path: "/health", 25 | Method: http.MethodGet, 26 | Func: ok, 27 | }) 28 | } 29 | 30 | func ok(w http.ResponseWriter, r *http.Request) { 31 | w.WriteHeader(http.StatusOK) 32 | _, _ = w.Write([]byte("ok")) 33 | } 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[Feature]" 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 | -------------------------------------------------------------------------------- /pkg/route/route_available.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "sort" 7 | ) 8 | 9 | func init() { 10 | RegisterHandler(HandleFunc{ 11 | Path: "/", 12 | Method: http.MethodGet, 13 | Func: available, 14 | }) 15 | RegisterHandler(HandleFunc{ 16 | Path: "/available", 17 | Method: http.MethodGet, 18 | Func: available, 19 | }) 20 | } 21 | 22 | func available(w http.ResponseWriter, _ *http.Request) { 23 | _, _ = fmt.Fprint(w, "## AvailableRoutes\n\n") 24 | keys := make([]string, 0, len(funcMap)) 25 | for k := range funcMap { 26 | keys = append(keys, k) 27 | } 28 | sort.Strings(keys) 29 | for _, k := range keys { 30 | _, _ = fmt.Fprintln(w, "=> "+k) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /deploy/cfssl/dac-csr.json: -------------------------------------------------------------------------------- 1 | { 2 | "key": { 3 | "algo": "rsa", 4 | "size": 2048 5 | }, 6 | "names": [ 7 | { 8 | "O": "Dynamic Admission Control", 9 | "OU": "Dynamic Admission Control Security", 10 | "L": "Beijing", 11 | "ST": "Beijing", 12 | "C": "CN" 13 | } 14 | ], 15 | "CN": "Dynamic Admission Control", 16 | "hosts": [ 17 | "127.0.0.1", 18 | "localhost", 19 | "joylive-injector", 20 | "joylive-injector.joylive", 21 | "joylive-injector.joylive.svc", 22 | "validating-webhook", 23 | "validating-webhook.kube-addons", 24 | "validating-webhook.kube-addons.svc" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /client-go/clientset/versioned/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 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 | -------------------------------------------------------------------------------- /deploy/joylive-injector/templates/servicemonitor.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: monitoring.coreos.com/v1 2 | kind: ServiceMonitor 3 | metadata: 4 | annotations: 5 | meta.helm.sh/release-name: brolly 6 | meta.helm.sh/release-namespace: brolly 7 | labels: 8 | app.kubernetes.io/instance: brolly 9 | app.kubernetes.io/managed-by: Helm 10 | app.kubernetes.io/name: monitor 11 | app.kubernetes.io/system: brolly 12 | integration: tpaas-jmsf-otel 13 | inputType: integrations 14 | release: brolly 15 | name: tpaas-jmsf-otel-slave 16 | namespace: brolly 17 | spec: 18 | endpoints: 19 | - port: prometheus 20 | interval: 5s 21 | namespaceSelector: 22 | any: true 23 | selector: 24 | matchLabels: 25 | app: opentelemetry 26 | component: otel-collector -------------------------------------------------------------------------------- /client-go/clientset/versioned/typed/injector/v1/generated_expansion.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 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 AgentVersionExpansion interface{} 22 | -------------------------------------------------------------------------------- /client-go/clientset/versioned/typed/injector/v1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 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 | -------------------------------------------------------------------------------- /client-go/clientset/versioned/typed/injector/v1/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 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 | -------------------------------------------------------------------------------- /client-go/clientset/versioned/scheme/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 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 | -------------------------------------------------------------------------------- /client-go/apis/injector/v1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // +k8s:deepcopy-gen=package 18 | // +k8s:defaulter-gen=TypeMeta 19 | // +groupName=injector.joylive.io 20 | 21 | package v1 // import "github.com/jd-opensource/joylive-injector/client-go/apis/injector/v1" 22 | -------------------------------------------------------------------------------- /pkg/log/tee.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "io" 5 | 6 | "go.uber.org/zap" 7 | "go.uber.org/zap/zapcore" 8 | ) 9 | 10 | type LevelEnablerFunc func(Level) bool 11 | 12 | type TeeOption struct { 13 | Out io.Writer 14 | LevelEnablerFunc 15 | } 16 | 17 | // NewTee writes multiple outputs based on log level 18 | // https://pkg.go.dev/go.uber.org/zap#example-package-AdvancedConfiguration 19 | func NewTee(tees []TeeOption, opts ...Option) *Logger { 20 | var cores []zapcore.Core 21 | for _, tee := range tees { 22 | cfg := zap.NewProductionEncoderConfig() 23 | cfg.EncodeTime = zapcore.RFC3339TimeEncoder 24 | core := zapcore.NewCore( 25 | zapcore.NewJSONEncoder(cfg), 26 | zapcore.AddSync(tee.Out), 27 | zap.LevelEnablerFunc(tee.LevelEnablerFunc), 28 | ) 29 | cores = append(cores, core) 30 | } 31 | return &Logger{l: zap.New(zapcore.NewTee(cores...), opts...)} 32 | } 33 | -------------------------------------------------------------------------------- /InnerBuild.dockerfile: -------------------------------------------------------------------------------- 1 | ARG BUILD_IMAGE=golang:alpine 2 | ARG RUNTIME_IMAGE=alpine 3 | 4 | FROM ${BUILD_IMAGE} AS builder 5 | 6 | WORKDIR /workspace 7 | 8 | COPY . . 9 | 10 | ENV GO111MODULE=on 11 | ENV GOPROXY http://jdos-goproxy.jdcloud.com,direct 12 | ENV GOPRIVATE "" 13 | 14 | ARG APP=joylive-injector 15 | ARG RELEASE_TAG=$(VERSION) 16 | 17 | RUN go env 18 | 19 | # Build the application 20 | RUN go build -ldflags "-w -s -X main.VERSION=${RELEASE_TAG}" -o ./${APP} . 21 | 22 | FROM ${RUNTIME_IMAGE} 23 | 24 | ARG TZ="Asia/Shanghai" 25 | 26 | ENV TZ ${TZ} 27 | ENV LANG en_US.UTF-8 28 | ENV LC_ALL en_US.UTF-8 29 | ENV LANGUAGE en_US:en 30 | 31 | RUN set -ex \ 32 | && ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime \ 33 | && echo ${TZ} > /etc/timezone \ 34 | && rm -rf /var/cache/apk/* 35 | 36 | COPY --from=builder /workspace/joylive-injector /joylive-injector 37 | 38 | ENTRYPOINT ["/joylive-injector"] 39 | -------------------------------------------------------------------------------- /pkg/config/injector.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "github.com/jd-opensource/joylive-injector/pkg/log" 5 | "go.uber.org/zap" 6 | "gopkg.in/yaml.v3" 7 | ) 8 | 9 | type AgentInjectorConfig struct { 10 | AgentConfig Agent `yaml:"agent"` 11 | } 12 | 13 | type Agent struct { 14 | Image string `yaml:"image"` 15 | Version string `yaml:"version"` 16 | Envs map[string]string `json:"envs"` 17 | } 18 | 19 | func GetAgentInjectConfig(yamlData string) (*AgentInjectorConfig, error) { 20 | var config AgentInjectorConfig 21 | err := yaml.Unmarshal([]byte(yamlData), &config) 22 | if err != nil { 23 | log.Error("Error parsing YAML", zap.Error(err)) 24 | return nil, err 25 | } 26 | return &config, nil 27 | } 28 | 29 | type InjectorRule struct { 30 | MatchLabels map[string]string `yaml:"matchLabels"` 31 | Envs map[string]string `yaml:"envs"` 32 | Labels map[string]string `yaml:"labels"` 33 | } 34 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | repo ?= hub.jdcloud.com/jmsf 2 | version:=1.2.4-$(shell git rev-parse --short HEAD) 3 | 4 | ifeq (,$(shell go env GOBIN)) 5 | GOBIN=$(shell go env GOPATH)/bin 6 | else 7 | GOBIN=$(shell go env GOBIN) 8 | endif 9 | 10 | all: gen-client build image push build-charts-crs 11 | 12 | all-image: build image push 13 | 14 | gen-client: 15 | hack/update-codegen.sh 16 | 17 | build: 18 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o bin/joylive-injector main.go 19 | 20 | image: 21 | DOCKER_SCAN_SUGGEST=false 22 | docker build --platform linux/amd64 -t $(repo)/joylive-injector:$(version)-amd64 -f LocalBuild.dockerfile . 23 | 24 | build-image: 25 | DOCKER_SCAN_SUGGEST=false 26 | docker build -t $(repo)/joylive-injector:$(version) . 27 | 28 | push: 29 | docker push $(repo)/joylive-injector:$(version)-amd64 30 | 31 | build-charts-crs: 32 | helm template joylive-injector deploy/joylive-injector --include-crds > deploy/all-cr.yaml 33 | helm package deploy/joylive-injector --destination deploy/packages 34 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine AS builder 2 | 3 | ENV SRC_PATH ${GOPATH}/src/joylive-injector 4 | 5 | WORKDIR ${SRC_PATH} 6 | 7 | COPY . . 8 | 9 | RUN set -ex \ 10 | && apk add git \ 11 | && export BUILD_VERSION=$(cat version) \ 12 | && export BUILD_DATE=$(date "+%F %T") \ 13 | && export COMMIT_SHA1=$(git rev-parse HEAD) \ 14 | && go mod tidy \ 15 | && go install -trimpath -ldflags \ 16 | "-X 'main.version=${BUILD_VERSION}' \ 17 | -X 'main.buildDate=${BUILD_DATE}' \ 18 | -X 'main.commitID=${COMMIT_SHA1}' \ 19 | -w -s" 20 | 21 | FROM alpine 22 | 23 | ARG TZ="Asia/Shanghai" 24 | 25 | ENV TZ ${TZ} 26 | ENV LANG en_US.UTF-8 27 | ENV LC_ALL en_US.UTF-8 28 | ENV LANGUAGE en_US:en 29 | 30 | RUN set -ex \ 31 | && apk add bash tzdata ca-certificates \ 32 | && ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime \ 33 | && echo ${TZ} > /etc/timezone \ 34 | && rm -rf /var/cache/apk/* 35 | 36 | COPY --from=builder /go/bin/joylive-injector /joylive-injector 37 | 38 | ENTRYPOINT ["/joylive-injector"] 39 | -------------------------------------------------------------------------------- /client-go/listers/injector/v1/expansion_generated.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 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 | // AgentVersionListerExpansion allows custom methods to be added to 22 | // AgentVersionLister. 23 | type AgentVersionListerExpansion interface{} 24 | 25 | // AgentVersionNamespaceListerExpansion allows custom methods to be added to 26 | // AgentVersionNamespaceLister. 27 | type AgentVersionNamespaceListerExpansion interface{} 28 | -------------------------------------------------------------------------------- /deploy/joylive-injector/README-zh.md: -------------------------------------------------------------------------------- 1 | # joylive-injector 2 | 3 | [![GitHub repo](https://img.shields.io/badge/GitHub-repo-blue)](https://github.com/jd-opensource/joylive-injector) 4 | [![GitHub release](https://img.shields.io/github/release/jd-opensource/joylive-injector.svg)](https://github.com/jd-opensource/joylive-injector/releases) 5 | [![Slack Status](https://img.shields.io/badge/slack-join_chat-white.svg?logo=slack&style=social)](https://joylivehq.slack.com) 6 | 7 | [English](./README.md) | 简体中文 8 | 9 | ## 介绍 10 | 这是一个针对kubernetes的动态准入控制webhook,它可以用于修改`kubernete`资源。 11 | 此程序监视`deployments`的CREATE、UPDATE、DELETE事件和`pods`的CREATE事件,并为`POD`添加initContainer、默认增加环境变量`JAVA_TOOL_OPTIONS`、挂载configmap、修改主容器的卷装载等操作。 12 | 13 | ## 特性 14 | - 支持自动将`joylive-agent`注入Java应用的Pod。 15 | - 支持多版本`joylive-agent`与对应配置管理。 16 | - 支持注入指定版本`joylive-agent`及对应配置。 17 | 18 | ## 使用方式 19 | 因证书签名已按照命名空间为`joylive`预生成,所以须指定安装到对应命名空间。 执行命令: 20 | ```bash 21 | helm repo add joylive https://jd-opensource.github.io/joylive-helm-charts 22 | helm repo update 23 | kubectl create namespace joylive 24 | helm install joylive-injector joylive/joylive-injector -n joylive 25 | ``` 26 | -------------------------------------------------------------------------------- /client-go/apis/injector/v1/zz_generated.defaults.go: -------------------------------------------------------------------------------- 1 | //go:build !ignore_autogenerated 2 | // +build !ignore_autogenerated 3 | 4 | /* 5 | Copyright 2024. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | */ 19 | 20 | // Code generated by defaulter-gen. DO NOT EDIT. 21 | 22 | package v1 23 | 24 | import ( 25 | runtime "k8s.io/apimachinery/pkg/runtime" 26 | ) 27 | 28 | // RegisterDefaults adds defaulters functions to the given scheme. 29 | // Public to allow building arbitrary schemes. 30 | // All generated defaulters are covering - they call all nested defaulters. 31 | func RegisterDefaults(scheme *runtime.Scheme) error { 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /deploy/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG TPAAS_BUILD_IMAGE 2 | ARG TPAAS_RUNTIME_IMAGE 3 | 4 | FROM ${TPAAS_BUILD_IMAGE} as builder 5 | 6 | ENV SRC_PATH ${GOPATH}/src/joylive-injector 7 | 8 | WORKDIR ${SRC_PATH} 9 | 10 | COPY . . 11 | 12 | RUN set -ex \ 13 | #&& yum update -y \ 14 | && yum install -y gcc git \ 15 | && export BUILD_VERSION=$(cat version) \ 16 | && export BUILD_DATE=$(date "+%F %T") \ 17 | && export COMMIT_SHA1=$(git rev-parse HEAD) \ 18 | && go mod tidy \ 19 | && go install -trimpath -ldflags \ 20 | "-X 'main.version=${BUILD_VERSION}' \ 21 | -X 'main.buildDate=${BUILD_DATE}' \ 22 | -X 'main.commitID=${COMMIT_SHA1}' \ 23 | -w -s" 24 | 25 | FROM ${TPAAS_RUNTIME_IMAGE} 26 | 27 | ARG TZ="Asia/Shanghai" 28 | 29 | ENV TZ ${TZ} 30 | ENV LANG en_US.UTF-8 31 | ENV LC_ALL en_US.UTF-8 32 | ENV LANGUAGE en_US:en 33 | 34 | RUN set -ex \ 35 | #&& yum update -y \ 36 | && yum install -y tzdata ca-certificates \ 37 | && ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime \ 38 | && echo ${TZ} > /etc/timezone \ 39 | && yum clean all 40 | 41 | COPY --from=builder /root/go/bin/joylive-injector /joylive-injector 42 | 43 | ENTRYPOINT ["/joylive-injector"] 44 | -------------------------------------------------------------------------------- /.github/workflows/docker-build.yml: -------------------------------------------------------------------------------- 1 | name: Build and Push Docker Image to GHCR 2 | 3 | on: 4 | workflow_dispatch: 5 | branches: 6 | - main 7 | push: 8 | branches: 9 | - main 10 | paths: 11 | - "**.go" 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout code 18 | uses: actions/checkout@v4 19 | 20 | - name: Set up QEMU 21 | uses: docker/setup-qemu-action@v3 22 | 23 | - name: Set up Docker Buildx 24 | uses: docker/setup-buildx-action@v3 25 | 26 | - name: Login to GitHub Container Registry 27 | uses: docker/login-action@v3 28 | with: 29 | registry: ghcr.io 30 | username: ${{ github.actor }} 31 | password: ${{ secrets.GITHUB_TOKEN }} 32 | 33 | - name: Build and push Docker image 34 | uses: docker/build-push-action@v5 35 | with: 36 | context: . 37 | push: true 38 | tags: | 39 | ghcr.io/${{ github.repository_owner }}/joylive-injector:latest 40 | ghcr.io/${{ github.repository_owner }}/joylive-injector:${{ github.sha }} 41 | platforms: linux/amd64,linux/arm64 42 | 43 | - name: Logout from GitHub Container Registry 44 | run: docker logout ghcr.io 45 | -------------------------------------------------------------------------------- /pkg/watcher/init.go: -------------------------------------------------------------------------------- 1 | package watcher 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/jd-opensource/joylive-injector/pkg/config" 7 | "github.com/jd-opensource/joylive-injector/pkg/log" 8 | "github.com/jd-opensource/joylive-injector/pkg/resource" 9 | "go.uber.org/zap" 10 | ) 11 | 12 | func init() { 13 | var wg sync.WaitGroup 14 | var fatalErr error 15 | var once sync.Once 16 | 17 | wg.Add(2) 18 | 19 | rs := resource.GetResource() 20 | namespace := config.GetNamespace() 21 | 22 | go func() { 23 | defer wg.Done() 24 | cmWatcher := NewConfigMapWatcher(rs.ClientSet) 25 | err := cmWatcher.Start() 26 | if err != nil { 27 | once.Do(func() { 28 | fatalErr = err 29 | }) 30 | return 31 | } 32 | err = cmWatcher.InitConfigMap(namespace) 33 | if err != nil { 34 | once.Do(func() { 35 | fatalErr = err 36 | }) 37 | } 38 | }() 39 | 40 | go func() { 41 | defer wg.Done() 42 | avWatcher := NewAgentVersionWatcher(rs.RestConfig) 43 | err := avWatcher.Start() 44 | if err != nil { 45 | once.Do(func() { 46 | fatalErr = err 47 | }) 48 | return 49 | } 50 | err = avWatcher.InitAgentVersion(namespace) 51 | if err != nil { 52 | once.Do(func() { 53 | fatalErr = err 54 | }) 55 | } 56 | }() 57 | 58 | wg.Wait() 59 | if fatalErr != nil { 60 | log.Fatal("watcher init error", zap.Error(fatalErr)) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /client-go/clientset/versioned/typed/injector/v1/fake/fake_injector_client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | v1 "github.com/jd-opensource/joylive-injector/client-go/clientset/versioned/typed/injector/v1" 23 | rest "k8s.io/client-go/rest" 24 | testing "k8s.io/client-go/testing" 25 | ) 26 | 27 | type FakeInjectorV1 struct { 28 | *testing.Fake 29 | } 30 | 31 | func (c *FakeInjectorV1) AgentVersions(namespace string) v1.AgentVersionInterface { 32 | return &FakeAgentVersions{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 *FakeInjectorV1) RESTClient() rest.Interface { 38 | var ret *rest.RESTClient 39 | return ret 40 | } 41 | -------------------------------------------------------------------------------- /deploy/joylive-injector/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: joylive-injector 3 | description: A Helm chart for webhook automatically injected by JoyLive Agent 4 | 5 | # A chart can be either an 'application' or a 'library' chart. 6 | # 7 | # Application charts are a collection of templates that can be packaged into versioned archives 8 | # to be deployed. 9 | # 10 | # Library charts provide useful utilities or functions for the chart developer. They're included as 11 | # a dependency of application charts to inject those utilities and functions into the rendering 12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed. 13 | type: application 14 | 15 | # This is the chart version. This version number should be incremented each time you make changes 16 | # to the chart and its templates, including the app version. 17 | # Versions are expected to follow Semantic Versioning (https://semver.org/) 18 | version: 1.3.8 19 | 20 | keywords: 21 | - joylive 22 | 23 | home: https://github.com/jd-opensource/joylive-injector 24 | 25 | # This is the version number of the application being deployed. This version number should be 26 | # incremented each time you make changes to the application. Versions are not expected to 27 | # follow Semantic Versioning. They should reflect the version the application is using. 28 | # It is recommended to use it with quotes. 29 | appVersion: "v1.2.4" 30 | -------------------------------------------------------------------------------- /.github/workflows/docker-build-release.yml: -------------------------------------------------------------------------------- 1 | name: Build and Push Docker Image to GHCR on Release 2 | 3 | on: 4 | workflow_dispatch: 5 | branches: 6 | 7 | push: 8 | tags: 9 | - 'v*' 10 | 11 | release: 12 | types: 13 | - created 14 | 15 | jobs: 16 | build: 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - name: Checkout code 21 | uses: actions/checkout@v4 22 | 23 | - name: Set up QEMU 24 | uses: docker/setup-qemu-action@v3 25 | 26 | - name: Set up Docker Buildx 27 | uses: docker/setup-buildx-action@v3 28 | 29 | - name: Login to GitHub Container Registry 30 | uses: docker/login-action@v3 31 | with: 32 | registry: ghcr.io 33 | username: ${{ github.actor }} 34 | password: ${{ secrets.GITHUB_TOKEN }} 35 | 36 | - name: Extract release tag 37 | id: extract_tag 38 | run: echo "RELEASE_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV 39 | 40 | - name: Build and push Docker image 41 | uses: docker/build-push-action@v5 42 | with: 43 | context: . 44 | push: true 45 | tags: | 46 | ghcr.io/${{ github.repository_owner }}/joylive-injector:${{ env.RELEASE_TAG }} 47 | platforms: linux/amd64,linux/arm64/v8 48 | 49 | - name: Logout from GitHub Container Registry 50 | run: docker logout ghcr.io 51 | -------------------------------------------------------------------------------- /hack/update-codegen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2017 The Kubernetes Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -o errexit 18 | set -o nounset 19 | set -o pipefail 20 | 21 | SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. 22 | CODEGEN_PKG=$(go env GOPATH)/pkg/mod/k8s.io/code-generator@$(go list -m k8s.io/code-generator | awk '{print $2}') 23 | 24 | source "${CODEGEN_PKG}/kube_codegen.sh" 25 | 26 | THIS_PKG="github.com/jd-opensource/joylive-injector" 27 | 28 | kube::codegen::gen_helpers \ 29 | --boilerplate "${SCRIPT_ROOT}/hack/boilerplate.go.txt" \ 30 | "${SCRIPT_ROOT}" 31 | 32 | kube::codegen::gen_client \ 33 | --with-watch \ 34 | --with-applyconfig \ 35 | --output-dir "${SCRIPT_ROOT}/client-go" \ 36 | --output-pkg "${THIS_PKG}/client-go" \ 37 | --boilerplate "${SCRIPT_ROOT}/hack/boilerplate.go.txt" \ 38 | "${SCRIPT_ROOT}/client-go/apis" 39 | -------------------------------------------------------------------------------- /deploy/cfssl/dac.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIDwjCCAqoCAQAwgaYxCzAJBgNVBAYTAkNOMRAwDgYDVQQIEwdCZWlqaW5nMRAw 3 | DgYDVQQHEwdCZWlqaW5nMSIwIAYDVQQKExlEeW5hbWljIEFkbWlzc2lvbiBDb250 4 | cm9sMSswKQYDVQQLEyJEeW5hbWljIEFkbWlzc2lvbiBDb250cm9sIFNlY3VyaXR5 5 | MSIwIAYDVQQDExlEeW5hbWljIEFkbWlzc2lvbiBDb250cm9sMIIBIjANBgkqhkiG 6 | 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsr/gUdrKen9H4f+ciSyBwfe5JVj/XoWc8K6T 7 | 3yV4cOwJgMlkO0/nVUdJvmeDAuHJPCRxk1+qbv+owEUvJZT44CXuA/Tz8l2DVD3m 8 | gDd4ZczAgCsXX8IUu3MKSwHP7RHXmMoL2bjF8Vti3ot+S4LdpZKaRirUO8r4uTCz 9 | wKmU2/6qJi5suSZkzqPE9Vv/llpP7GhHo0rrq59u1SGIo9oQ//5V/ovHZnbJAUOO 10 | hNXVkF0ywClCp9Dt72B5RwCKpeD5deNmIKVE9xSV30G0w0SVtWLXB7wVEqNtNhj0 11 | W1AH82pcPf8gHKdpNLYn7HmgNMihT4jyONiJBOdhcZTepCTwKQIDAQABoIHVMIHS 12 | BgkqhkiG9w0BCQ4xgcQwgcEwgb4GA1UdEQSBtjCBs4IJbG9jYWxob3N0ghBqb3ls 13 | aXZlLWluamVjdG9yghhqb3lsaXZlLWluamVjdG9yLmpveWxpdmWCHGpveWxpdmUt 14 | aW5qZWN0b3Iuam95bGl2ZS5zdmOCEnZhbGlkYXRpbmctd2ViaG9va4IedmFsaWRh 15 | dGluZy13ZWJob29rLmt1YmUtYWRkb25zgiJ2YWxpZGF0aW5nLXdlYmhvb2sua3Vi 16 | ZS1hZGRvbnMuc3ZjhwR/AAABMA0GCSqGSIb3DQEBCwUAA4IBAQCgl6iKHyCAzrpn 17 | ODEn0g9cz0wTPl0B4hZJS3nGNbU265nvCBW1Il3NQGe5Zlf/g/tlta579Dobw34J 18 | J/VKT0P0IqCGBdkdW3m1sMkVyA/FJ6lCahj0hZrXKLIyAiquOdAk7utfO1z4ftUl 19 | s+fhja15oZ/eupzU9fuX7/qAbLVgzDTVTj5xImk2Pek+1QHKx+g7vUxqq6+Ij2zT 20 | aL/K3hsM5cqF4p/5olwsAKWC1C6u9K4wjHQyxRLr62mQ1JibuAliGRPuRCO2yLlu 21 | ptzDOcgbcqnAE70xjatYHg9/ZlNczfT2nWFqEpdNGutCjSg4nkysOEnlr550DbS8 22 | ACfhz+Sw 23 | -----END CERTIFICATE REQUEST----- 24 | -------------------------------------------------------------------------------- /deploy/examples/joylive-injector-rules.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: joylive-injector-rules 5 | namespace: joylive 6 | labels: 7 | app: joylive-injector 8 | data: 9 | default: | 10 | matchLabels: 11 | envs: 12 | APPLICATION_LOCATION_CLUSTER: beijing01-slave 13 | labels: 14 | 15 | rule1: | 16 | matchLabels: 17 | app: nginx 18 | env: prod 19 | envs: 20 | LOG_LEVEL: info 21 | TIMEZONE: UTC 22 | labels: 23 | team: ops 24 | tier: frontend 25 | 26 | rule2: | 27 | matchLabels: 28 | app: joylive-demo-springboot2021-provider 29 | envs: 30 | APPLICATION_LOCATION_LANE: cn-gray 31 | APPLICATION_SERVICE_NAMESPACE: deno-sys 32 | CONFIG_REGISTRY_ADDRESS_FIRST: 11.50.139.254:30520?grpc.port=31520 33 | REGISTRY_NAMESPACE: deno-sys 34 | CONFIG_LANE_ENABLED: "true" 35 | CONFIG_FLOW_CONTROL_ENABLED: "true" 36 | APPLICATION_NAME: deno-sys-service-provider 37 | APPLICATION_SERVICE_GROUP: default 38 | APPLICATION_SERVICE_NAME: service-provider 39 | LIVE_LOG_APPENDER: CONSOLE_APPENDER 40 | CONFIG_REGISTRY_ENABLED: "true" 41 | REGISTRY_EXTERNAL_HTTP_PORT: "30520" 42 | REGISTRY_EXTERNAL_GRPC_PORT: "31520" 43 | REGISTRY_EXTERNAL_HOST: 11.50.139.254 44 | REGISTRY_USERNAME: nacos 45 | REGISTRY_PASSWORD: nacos 46 | x-live-enabled: "true" 47 | labels: 48 | team: dev 49 | project: alpha 50 | -------------------------------------------------------------------------------- /client-go/applyconfiguration/utils.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 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 applyconfiguration-gen. DO NOT EDIT. 18 | 19 | package applyconfiguration 20 | 21 | import ( 22 | v1 "github.com/jd-opensource/joylive-injector/client-go/apis/injector/v1" 23 | injectorv1 "github.com/jd-opensource/joylive-injector/client-go/applyconfiguration/injector/v1" 24 | schema "k8s.io/apimachinery/pkg/runtime/schema" 25 | ) 26 | 27 | // ForKind returns an apply configuration type for the given GroupVersionKind, or nil if no 28 | // apply configuration type exists for the given GroupVersionKind. 29 | func ForKind(kind schema.GroupVersionKind) interface{} { 30 | switch kind { 31 | // Group=injector.joylive.io, Version=v1 32 | case v1.SchemeGroupVersion.WithKind("AgentVersion"): 33 | return &injectorv1.AgentVersionApplyConfiguration{} 34 | case v1.SchemeGroupVersion.WithKind("AgentVersionSpec"): 35 | return &injectorv1.AgentVersionSpecApplyConfiguration{} 36 | 37 | } 38 | return nil 39 | } 40 | -------------------------------------------------------------------------------- /pkg/apm/sgm.go: -------------------------------------------------------------------------------- 1 | package apm 2 | 3 | import ( 4 | "context" 5 | "github.com/jd-opensource/joylive-injector/pkg/config" 6 | "k8s.io/api/apps/v1" 7 | ) 8 | 9 | type SgmAppender struct { 10 | } 11 | 12 | // SgmAppender implements the Appender interface 13 | var _ Appender = &SgmAppender{} 14 | 15 | // init registers the SgmAppender with the Appender factory 16 | func init() { 17 | RegisterAppenderType("sgm", &SgmAppender{}) 18 | } 19 | 20 | // Modify modifies the Deployment object by adding specific labels to its template 21 | func (s *SgmAppender) Modify(ctx context.Context, target *v1.Deployment) (bool, error) { 22 | added := false 23 | if _, ok := target.Spec.Template.Labels["sgm.jd.com/app"]; !ok { 24 | target.Spec.Template.Labels["sgm.jd.com/app"] = target.Labels[config.ApplicationLabel] 25 | added = true 26 | } 27 | if _, ok := target.Spec.Template.Labels["sgm.jd.com/group"]; !ok { 28 | target.Spec.Template.Labels["sgm.jd.com/group"] = target.Labels[config.ServiceGroupLabel] 29 | added = true 30 | } 31 | if _, ok := target.Spec.Template.Labels["sgm.jd.com/probe-inject"]; !ok { 32 | target.Spec.Template.Labels["sgm.jd.com/probe-inject"] = "true" 33 | added = true 34 | } 35 | if _, ok := target.Spec.Template.Labels["sgm.jd.com/sink"]; !ok { 36 | target.Spec.Template.Labels["sgm.jd.com/sink"] = "Http" 37 | added = true 38 | } 39 | if _, ok := target.Spec.Template.Labels["sgm.jd.com/tenant"]; !ok { 40 | target.Spec.Template.Labels["sgm.jd.com/tenant"] = target.Labels[config.TenantLabel] 41 | added = true 42 | } 43 | return added, nil 44 | } 45 | -------------------------------------------------------------------------------- /deploy/joylive-injector/README.md: -------------------------------------------------------------------------------- 1 | # joylive-injector 2 | 3 | [![GitHub repo](https://img.shields.io/badge/GitHub-repo-blue)](https://github.com/jd-opensource/joylive-injector) 4 | [![GitHub release](https://img.shields.io/github/release/jd-opensource/joylive-injector.svg)](https://github.com/jd-opensource/joylive-injector/releases) 5 | [![Slack Status](https://img.shields.io/badge/slack-join_chat-white.svg?logo=slack&style=social)](https://joylivehq.slack.com) 6 | 7 | English | [简体中文](./README-zh.md) 8 | 9 | ## Description 10 | This is a dynamic admission control webhook for kubernetes, it can be used to mutate kubernetes resources. 11 | This program monitors the `CREATE`, `UPDATE`, `DELETE` events for `deployments` and the `CREATE` events for `pods` and adds the initContainer for `Pod` , adds the environment variable `JAVA_TOOL_OPTIONS` by default, mounts the configmap, modifies the volume load for the main container, and so on. 12 | 13 | ## Features 14 | - Supports automatically injecting `joylive-agent` into Pods of Java applications. 15 | - Supports multi-version `joylive-agent` and corresponding configuration management. 16 | - Support injection of specified version `joylive-agent` and corresponding configuration. 17 | 18 | ## Used 19 | Since the certificate signature has been pre-generated according to the namespace `joylive`, it is necessary to specify installation to the corresponding namespace. Execute the command: 20 | ```bash 21 | helm repo add joylive https://jd-opensource.github.io/joylive-helm-charts 22 | helm repo update 23 | kubectl create namespace joylive 24 | helm install joylive-injector joylive/joylive-injector -n joylive 25 | ``` 26 | -------------------------------------------------------------------------------- /client-go/informers/externalversions/internalinterfaces/factory_interfaces.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package internalinterfaces 20 | 21 | import ( 22 | time "time" 23 | 24 | versioned "github.com/jd-opensource/joylive-injector/client-go/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 | -------------------------------------------------------------------------------- /deploy/joylive-injector/templates/mutatingwebhook.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: admissionregistration.k8s.io/v1 2 | kind: MutatingWebhookConfiguration 3 | metadata: 4 | name: {{ include "joylive-injector.name" . }} 5 | webhooks: 6 | - name: "joylive-injector-deployment.joylive.io" 7 | rules: 8 | - apiGroups: [ "apps" ] 9 | apiVersions: [ "v1","v1beta1" ] 10 | operations: [ "CREATE","UPDATE","DELETE" ] 11 | resources: [ "deployments" ] 12 | scope: "Namespaced" 13 | clientConfig: 14 | service: 15 | name: {{ include "joylive-injector.name" . }} 16 | namespace: {{ .Values.namespace }} 17 | path: /mutating/injection-deploy 18 | port: 443 19 | caBundle: {{ .Values.caBundle }} 20 | admissionReviewVersions: [ "v1", "v1beta1" ] 21 | sideEffects: None 22 | timeoutSeconds: 5 23 | failurePolicy: Fail 24 | objectSelector: 25 | matchLabels: {{ include "joylive-injector.matchLabels" . | nindent 8 }} 26 | - name: "joylive-injector-pod.joylive.io" 27 | rules: 28 | - apiGroups: [ "" ] 29 | apiVersions: [ "v1","v1beta1" ] 30 | operations: [ "CREATE" ] 31 | resources: [ "pods" ] 32 | scope: "Namespaced" 33 | clientConfig: 34 | service: 35 | name: {{ include "joylive-injector.name" . }} 36 | namespace: {{ .Values.namespace }} 37 | path: /mutating/injection-pod 38 | port: 443 39 | caBundle: {{ .Values.caBundle }} 40 | admissionReviewVersions: [ "v1", "v1beta1" ] 41 | sideEffects: None 42 | timeoutSeconds: 5 43 | failurePolicy: Fail 44 | objectSelector: 45 | matchLabels: {{ include "joylive-injector.matchLabels" . | nindent 8 }} -------------------------------------------------------------------------------- /pkg/resource/control_test.go: -------------------------------------------------------------------------------- 1 | package resource 2 | 3 | import ( 4 | "fmt" 5 | "github.com/jd-opensource/joylive-injector/pkg/config" 6 | "github.com/stretchr/testify/assert" 7 | "net/http" 8 | "net/http/httptest" 9 | "testing" 10 | ) 11 | 12 | func TestGetApplicationEnvironments(t *testing.T) { 13 | // 模拟 HTTP 服务器 14 | mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 15 | assert.Equal(t, "/v1/ns/test-namespace/application/test-application/environments", r.URL.Path) 16 | w.WriteHeader(http.StatusOK) 17 | w.Write([]byte(`{ 18 | "data": { 19 | "ENV_VAR1": "value1", 20 | "ENV_VAR2": "value2" 21 | } 22 | }`)) 23 | })) 24 | defer mockServer.Close() 25 | 26 | // 替换配置中的 ControlPlaneUrl 27 | originalURL := config.ControlPlaneUrl 28 | config.ControlPlaneUrl = mockServer.URL 29 | defer func() { config.ControlPlaneUrl = originalURL }() 30 | 31 | // 设置测试数据 32 | labels := map[string]string{ 33 | config.ServiceSpaceLabel: "test-namespace", 34 | config.ApplicationLabel: "test-application", 35 | } 36 | 37 | // 调用被测试函数 38 | data, err := GetApplicationEnvironments(labels) 39 | 40 | // 验证结果 41 | assert.NoError(t, err) 42 | assert.Equal(t, map[string]string{ 43 | "ENV_VAR1": "value1", 44 | "ENV_VAR2": "value2", 45 | }, data) 46 | } 47 | 48 | func TestGetApplicationEnvironments2(t *testing.T) { 49 | config.ControlPlaneUrl = "http://localhost:8000/v1" 50 | 51 | // 设置测试数据 52 | labels := map[string]string{ 53 | config.ServiceSpaceLabel: "test-namespace", 54 | config.ApplicationLabel: "test-application", 55 | } 56 | 57 | // 调用被测试函数 58 | data, err := GetApplicationEnvironments(labels) 59 | 60 | // 验证结果 61 | assert.NoError(t, err) 62 | fmt.Print(data) 63 | } 64 | -------------------------------------------------------------------------------- /client-go/applyconfiguration/internal/internal.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 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 applyconfiguration-gen. DO NOT EDIT. 18 | 19 | package internal 20 | 21 | import ( 22 | "fmt" 23 | "sync" 24 | 25 | typed "sigs.k8s.io/structured-merge-diff/v4/typed" 26 | ) 27 | 28 | func Parser() *typed.Parser { 29 | parserOnce.Do(func() { 30 | var err error 31 | parser, err = typed.NewParser(schemaYAML) 32 | if err != nil { 33 | panic(fmt.Sprintf("Failed to parse schema: %v", err)) 34 | } 35 | }) 36 | return parser 37 | } 38 | 39 | var parserOnce sync.Once 40 | var parser *typed.Parser 41 | var schemaYAML = typed.YAMLObject(`types: 42 | - name: __untyped_atomic_ 43 | scalar: untyped 44 | list: 45 | elementType: 46 | namedType: __untyped_atomic_ 47 | elementRelationship: atomic 48 | map: 49 | elementType: 50 | namedType: __untyped_atomic_ 51 | elementRelationship: atomic 52 | - name: __untyped_deduced_ 53 | scalar: untyped 54 | list: 55 | elementType: 56 | namedType: __untyped_atomic_ 57 | elementRelationship: atomic 58 | map: 59 | elementType: 60 | namedType: __untyped_deduced_ 61 | elementRelationship: separable 62 | `) 63 | -------------------------------------------------------------------------------- /deploy/cfssl/dac-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAsr/gUdrKen9H4f+ciSyBwfe5JVj/XoWc8K6T3yV4cOwJgMlk 3 | O0/nVUdJvmeDAuHJPCRxk1+qbv+owEUvJZT44CXuA/Tz8l2DVD3mgDd4ZczAgCsX 4 | X8IUu3MKSwHP7RHXmMoL2bjF8Vti3ot+S4LdpZKaRirUO8r4uTCzwKmU2/6qJi5s 5 | uSZkzqPE9Vv/llpP7GhHo0rrq59u1SGIo9oQ//5V/ovHZnbJAUOOhNXVkF0ywClC 6 | p9Dt72B5RwCKpeD5deNmIKVE9xSV30G0w0SVtWLXB7wVEqNtNhj0W1AH82pcPf8g 7 | HKdpNLYn7HmgNMihT4jyONiJBOdhcZTepCTwKQIDAQABAoIBAEbuKP+SSIKve0DN 8 | A7rTNHJZ8NhHJp7Fem9vdzI3VkHJCERYpjN7E5sdfL/Xp38/RXFeLMuETMdbIAWb 9 | 7oeFQ/T+WCKt0xe7z99f6NW8LUSIKWMytkXDaCKyFcWJ5I54ZAs3Mdiln7aUwvj4 10 | dZdVSMrpD9sEJ8/KTUR/dPTg1jctf0lalyFMiHCd0GL3qDpaDZxnO/1x0+wWXNsM 11 | ckg8KP8zQCDxkMPcs2Z/Cri1KOkdyPtX5ft+qfjuO0C0CWduVCnGkNsKiP/MqtPw 12 | GjI3biz51VGFixVDNqAaz/Af9Hn5Jvwj4B1x80EnaYWwgfcozL5xGkS8rSimE6qc 13 | l0TkImECgYEAzlYvYwabiUBshbUwSNK4N/nA6FwI9G/qQ2Ocdp/r5ZNfP9gZhKjj 14 | Ra12ErxebX9+HizvjdfUGMt+YHj0D54rtEbUPC7NRoEuIeY+xYBK3lgmLONcm/C0 15 | y125UhHNWfWpvY52N1CUiW/NZWe9f7DFFpq+cBM1j+i53HpXWulfCL0CgYEA3cXb 16 | tpiyGRGH28zR2OIMSiq3WtJ9yI3rxjb5qvuJ/HShxUpmMenCxVksTrhh+GTpYEh7 17 | ZC1mUypgzjfqtwss/tuso9ru4/UgNfNF7kYrJfxaeBAQ/eYeKNnWBBs+ylKXNQI8 18 | EMUfkNmJEhXpgr3AIcIZ+xw3cIXjka1jknsWyd0CgYB1Oj398tkXJ4UAiip+lUmY 19 | c8sHuJJV3eeolcVlGAK8EfiBm1MULafCBIUaq+6yI0cR+dG4MrELzUFlqhJvdRu7 20 | dqpJ7wUtU2NUWy26o+LlZAt8YUhSSHVPPVZ2rCqRO6NOUMwVLTDWzLBTNpdRHqs6 21 | 5nvfzOoXcs911gQfYItfuQKBgD6uPHI4aCXzsX1rP2hYQ3b26cnOsmRr0Fa+lQRB 22 | gfeLk2lqpMW4tycGFCLPtEj7ZIalFdLzjfc1hBTk8v73Nql1ty68o0ZksszszDgT 23 | NwDOeVdz2wmX77BGGVohxTZz7265CfcHb+uFwFsPPGeKb/JapPM3P2gbP32eDwWI 24 | qqE1AoGBAIU/wHELLY9WCLPG6CsKo6UHBD6EAvsdNFL77roJExpwSLLl7BWbNOuI 25 | /M3sQp001rXrTuF8VWGUbvJUmI4pfz8Q2Rdn8E24rQAhKeVoxt8GWISq4J/zRTI5 26 | jbb65MivqCuGjpkimLjKcwoFSJEjLOgMnCH1OGxomJ7sCkLcYlYA 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /client-go/informers/externalversions/injector/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 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 injector 20 | 21 | import ( 22 | v1 "github.com/jd-opensource/joylive-injector/client-go/informers/externalversions/injector/v1" 23 | internalinterfaces "github.com/jd-opensource/joylive-injector/client-go/informers/externalversions/internalinterfaces" 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 | -------------------------------------------------------------------------------- /client-go/informers/externalversions/injector/v1/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package v1 20 | 21 | import ( 22 | internalinterfaces "github.com/jd-opensource/joylive-injector/client-go/informers/externalversions/internalinterfaces" 23 | ) 24 | 25 | // Interface provides access to all the informers in this group version. 26 | type Interface interface { 27 | // AgentVersions returns a AgentVersionInformer. 28 | AgentVersions() AgentVersionInformer 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 | // AgentVersions returns a AgentVersionInformer. 43 | func (v *version) AgentVersions() AgentVersionInformer { 44 | return &agentVersionInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} 45 | } 46 | -------------------------------------------------------------------------------- /deploy/joylive-injector/templates/rbac.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: {{ include "joylive-injector.name" . }} 5 | rules: 6 | - apiGroups: 7 | - "" 8 | resources: 9 | - pods 10 | - events 11 | - configmaps 12 | - namespaces 13 | - nodes 14 | verbs: 15 | - "*" 16 | - apiGroups: 17 | - apps 18 | resources: 19 | - deployments 20 | - daemonsets 21 | - replicasets 22 | - statefulsets 23 | verbs: 24 | - "*" 25 | - apiGroups: 26 | - autoscaling 27 | resources: 28 | - '*' 29 | verbs: 30 | - '*' 31 | - apiGroups: 32 | - injector.joylive.io 33 | resources: 34 | - agentversions 35 | verbs: 36 | - create 37 | - delete 38 | - get 39 | - list 40 | - patch 41 | - update 42 | - watch 43 | - apiGroups: 44 | - injector.joylive.io 45 | resources: 46 | - agentversions/finalizers 47 | verbs: 48 | - update 49 | - apiGroups: 50 | - injector.joylive.io 51 | resources: 52 | - agentversions/status 53 | verbs: 54 | - get 55 | - patch 56 | - update 57 | --- 58 | apiVersion: v1 59 | kind: ServiceAccount 60 | metadata: 61 | name: {{ include "joylive-injector.name" . }} 62 | namespace: {{ .Values.namespace }} 63 | --- 64 | kind: ClusterRoleBinding 65 | apiVersion: rbac.authorization.k8s.io/v1 66 | metadata: 67 | name: {{ include "joylive-injector.name" . }} 68 | namespace: {{ .Values.namespace }} 69 | subjects: 70 | - kind: ServiceAccount 71 | name: {{ include "joylive-injector.name" . }} 72 | namespace: {{ .Values.namespace }} 73 | roleRef: 74 | apiGroup: rbac.authorization.k8s.io 75 | kind: ClusterRole 76 | name: {{ include "joylive-injector.name" . }} -------------------------------------------------------------------------------- /deploy/cfssl/dac-ca.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIFETCCAvkCAQAwgakxCzAJBgNVBAYTAkNOMRAwDgYDVQQIEwdCZWlqaW5nMRAw 3 | DgYDVQQHEwdCZWlqaW5nMSIwIAYDVQQKExlEeW5hbWljIEFkbWlzc2lvbiBDb250 4 | cm9sMSswKQYDVQQLEyJEeW5hbWljIEFkbWlzc2lvbiBDb250cm9sIFNlY3VyaXR5 5 | MSUwIwYDVQQDExxEeW5hbWljIEFkbWlzc2lvbiBDb250cm9sIENBMIICIjANBgkq 6 | hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAzCE+gej+RANWfu48QBqyG/GQQPTUyQGW 7 | ocduHCLxYEk5KH/SMTvlljVhDF9lD4ErNt46OyBps90bLnZX2JGxktWfqS9h8EF7 8 | 5u2qOPqhc17tvJ051kofqfeP9W/mKnvHi2PDjMg2OLl0IXnFk2NT1mPWzmvZA/KV 9 | AW3Bpb+8H7v6AGhC5+/+C5RTB2xZMQnRgWwwfDwQO7xBitpGDez/Xjg7BfrfbCFz 10 | GOA2oG5SnMX78N8sMTBnFib/3fXnDCY8EWlkLihniqx67yKZFRbPDg/B8cjZ2+tV 11 | sjNEI6UBas4/5u2HcnY+F4etpl3vCJdsGlJTziuycxm747Tnf/lCfzOx35RJdRYj 12 | hBUQjMZp4aiKBgpj2Lmu/MWnjADczedmUeYbBPA2Pex0R5DsSxT28OtWDdKtdxbI 13 | 0/dHhOjYl9zOS32GlFXJHSP3W0GKSHUa1Jxn1gqDi3c+MCDUFEO1UY5R2qeQqcvd 14 | uHqwz0twiM7uTMrIYguI3UCdEGqr3pwGiADHnG/OZH0taMASmufVMJXb/N6f36eD 15 | q4zD8iin0XMK8YkN4UJfAJaIEyMuEMjRwRaHORU7wVJjBDUcpr+kSvdX038vTcNm 16 | f/N58C1sRmJyDCba7imjjA7D8te+wRugeITG8rDCN8EjAQUTHIA995d7q+DMR5u8 17 | e+DSWWD7G+sCAwEAAaAiMCAGCSqGSIb3DQEJDjETMBEwDwYDVR0TAQH/BAUwAwEB 18 | /zANBgkqhkiG9w0BAQ0FAAOCAgEAKnKH9qDgRr1Y0GrVIOyhZMScOC5njZ5M8nMF 19 | VbHfjOJdOg5c7OtlK+GXI2Vnd+hK2SwC+Xvwxc+wyvF/Y3nNooVsbYrb1c+GFNDA 20 | U3yaiad3dmjNrWZ2YtPnva5n52qBZ9kRIDrIfbeE1cr4uPf8WvqySAL+C01xLrPI 21 | YdWlGRq3Z73U1bhrG6i3lR85GMpl3EaBooo3K3uba+EdvEqDoYSFcrWqkQZec5hs 22 | 2v3V6YprLKLKMC4hjGuSj1eAVwpJcpTwcdZSwrqkxm6EJh0+U52SvRVQhkSCi9R5 23 | PbzdBvjhz7IXjaMhsew4qGkXXQ6ReWg0BPoPI7iqNWrB+wy5QNh2mlyK8RNBCX94 24 | 3PhmKLOwZztN5rkikKC9dFrhAaQlnIEdEYQBVlEMtqD14CF8zGSunEe8MwZHEJuT 25 | YnRceIn5CcWQiTZtypW42wahCbhYwRMvCuSwvijrEUpa/lNcXuS7sUVCCSakOyDG 26 | U/Qn6jpHtfwdwv2F998tO002AfkwMefM3poQxHwyhj5+sOeWO/Zmzcim4FtCzdYc 27 | FaYRNr2PXIQcdyuU94MBARxmhjQPK3ZpMrJDdA1mRiVdtFYmw3cFni4QRrdg2Se5 28 | HJ+/fJItAIRyrU4V/59koJwZqlUO7vWBnpTlP8DfXgE5oyCfL6hSC7Rz1ImOUNy1 29 | KF/hdwA= 30 | -----END CERTIFICATE REQUEST----- 31 | -------------------------------------------------------------------------------- /pkg/log/option.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import "go.uber.org/zap" 4 | 5 | type Option = zap.Option 6 | 7 | var ( 8 | WrapCore = zap.WrapCore 9 | Hooks = zap.Hooks 10 | Fields = zap.Fields 11 | ErrorOutput = zap.ErrorOutput 12 | Development = zap.Development 13 | AddCaller = zap.AddCaller 14 | WithCaller = zap.WithCaller 15 | AddCallerSkip = zap.AddCallerSkip 16 | AddStacktrace = zap.AddStacktrace 17 | IncreaseLevel = zap.IncreaseLevel 18 | WithFatalHook = zap.WithFatalHook 19 | WithClock = zap.WithClock 20 | ) 21 | 22 | var ( 23 | Skip = zap.Skip 24 | Binary = zap.Binary 25 | Bool = zap.Bool 26 | Boolp = zap.Boolp 27 | ByteString = zap.ByteString 28 | Complex128 = zap.Complex128 29 | Complex128p = zap.Complex128p 30 | Complex64 = zap.Complex64 31 | Complex64p = zap.Complex64p 32 | Float64 = zap.Float64 33 | Float64p = zap.Float64p 34 | Float32 = zap.Float32 35 | Float32p = zap.Float32p 36 | Int = zap.Int 37 | Intp = zap.Intp 38 | Int64 = zap.Int64 39 | Int64p = zap.Int64p 40 | Int32 = zap.Int32 41 | Int32p = zap.Int32p 42 | Int16 = zap.Int16 43 | Int16p = zap.Int16p 44 | Int8 = zap.Int8 45 | Int8p = zap.Int8p 46 | String = zap.String 47 | Stringp = zap.Stringp 48 | Uint = zap.Uint 49 | Uintp = zap.Uintp 50 | Uint64 = zap.Uint64 51 | Uint64p = zap.Uint64p 52 | Uint32 = zap.Uint32 53 | Uint32p = zap.Uint32p 54 | Uint16 = zap.Uint16 55 | Uint16p = zap.Uint16p 56 | Uint8 = zap.Uint8 57 | Uint8p = zap.Uint8p 58 | Uintptr = zap.Uintptr 59 | Uintptrp = zap.Uintptrp 60 | Reflect = zap.Reflect 61 | Namespace = zap.Namespace 62 | Stringer = zap.Stringer 63 | Time = zap.Time 64 | Timep = zap.Timep 65 | Stack = zap.Stack 66 | StackSkip = zap.StackSkip 67 | Duration = zap.Duration 68 | Durationp = zap.Durationp 69 | Object = zap.Object 70 | Inline = zap.Inline 71 | Any = zap.Any 72 | ) 73 | -------------------------------------------------------------------------------- /client-go/clientset/versioned/fake/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 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 | injectorv1 "github.com/jd-opensource/joylive-injector/client-go/apis/injector/v1" 23 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 | runtime "k8s.io/apimachinery/pkg/runtime" 25 | schema "k8s.io/apimachinery/pkg/runtime/schema" 26 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 27 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 28 | ) 29 | 30 | var scheme = runtime.NewScheme() 31 | var codecs = serializer.NewCodecFactory(scheme) 32 | 33 | var localSchemeBuilder = runtime.SchemeBuilder{ 34 | injectorv1.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 | -------------------------------------------------------------------------------- /client-go/clientset/versioned/scheme/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 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 | injectorv1 "github.com/jd-opensource/joylive-injector/client-go/apis/injector/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 | injectorv1.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 | -------------------------------------------------------------------------------- /client-go/apis/injector/v1/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1 18 | 19 | import ( 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | "k8s.io/apimachinery/pkg/runtime" 22 | "k8s.io/apimachinery/pkg/runtime/schema" 23 | ) 24 | 25 | var SchemeGroupVersion = schema.GroupVersion{Group: "injector.joylive.io", Version: "v1"} 26 | 27 | var ( 28 | // TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api. 29 | // localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes. 30 | SchemeBuilder runtime.SchemeBuilder 31 | localSchemeBuilder = &SchemeBuilder 32 | AddToScheme = localSchemeBuilder.AddToScheme 33 | ) 34 | 35 | func init() { 36 | // We only register manually written functions here. The registration of the 37 | // generated functions takes place in the generated files. The separation 38 | // makes the code compile even when the generated files are missing. 39 | localSchemeBuilder.Register(addKnownTypes) 40 | } 41 | 42 | // Resource takes an unqualified resource and returns a Group qualified GroupResource 43 | func Resource(resource string) schema.GroupResource { 44 | return SchemeGroupVersion.WithResource(resource).GroupResource() 45 | } 46 | 47 | // Adds the list of known types to the given scheme. 48 | func addKnownTypes(scheme *runtime.Scheme) error { 49 | scheme.AddKnownTypes(SchemeGroupVersion, 50 | &AgentVersion{}, 51 | &AgentVersionList{}, 52 | ) 53 | 54 | scheme.AddKnownTypes(SchemeGroupVersion, 55 | &metav1.Status{}, 56 | ) 57 | metav1.AddToGroupVersion(scheme, SchemeGroupVersion) 58 | return nil 59 | } 60 | -------------------------------------------------------------------------------- /client-go/apis/injector/v1/agentversion_types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1 18 | 19 | import ( 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | ) 22 | 23 | // AgentVersionSpec defines the desired state of AgentVersion 24 | type AgentVersionSpec struct { 25 | // Version of JoyLive Agent release 26 | Version string `json:"version"` 27 | // ConfigMapName record storage version configuration file 28 | ConfigMapName string `json:"configMapName,omitempty"` 29 | // Enable indicates whether this version is enabled 30 | Enable bool `json:"enable"` 31 | } 32 | 33 | // AgentVersionStatus defines the observed state of AgentVersion 34 | type AgentVersionStatus struct { 35 | } 36 | 37 | // +genclient 38 | // +k8s:openapi-gen=true 39 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 40 | // +kubebuilder:object:root=true 41 | // +kubebuilder:subresource:status 42 | // +kubebuilder:storageversion 43 | 44 | // AgentVersion is the Schema for the agentversions API 45 | type AgentVersion struct { 46 | metav1.TypeMeta `json:",inline"` 47 | metav1.ObjectMeta `json:"metadata,omitempty"` 48 | 49 | Spec AgentVersionSpec `json:"spec,omitempty"` 50 | Status AgentVersionStatus `json:"status,omitempty"` 51 | } 52 | 53 | // +kubebuilder:object:root=true 54 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 55 | 56 | // AgentVersionList contains a list of AgentVersion 57 | type AgentVersionList struct { 58 | metav1.TypeMeta `json:",inline"` 59 | metav1.ListMeta `json:"metadata,omitempty"` 60 | Items []AgentVersion `json:"items"` 61 | } 62 | 63 | func init() { 64 | //SchemeBuilder.Register(&AgentVersion{}, &AgentVersionList{}) 65 | } 66 | -------------------------------------------------------------------------------- /client-go/informers/externalversions/generic.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package externalversions 20 | 21 | import ( 22 | "fmt" 23 | 24 | v1 "github.com/jd-opensource/joylive-injector/client-go/apis/injector/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=injector.joylive.io, Version=v1 56 | case v1.SchemeGroupVersion.WithResource("agentversions"): 57 | return &genericInformer{resource: resource.GroupResource(), informer: f.Injector().V1().AgentVersions().Informer()}, nil 58 | 59 | } 60 | 61 | return nil, fmt.Errorf("no informer found for %v", resource) 62 | } 63 | -------------------------------------------------------------------------------- /deploy/cfssl/dac.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIGIzCCBAugAwIBAgIUFIkaLtWhoBOVglNOXBPaDuAR9r0wDQYJKoZIhvcNAQEN 3 | BQAwgakxCzAJBgNVBAYTAkNOMRAwDgYDVQQIEwdCZWlqaW5nMRAwDgYDVQQHEwdC 4 | ZWlqaW5nMSIwIAYDVQQKExlEeW5hbWljIEFkbWlzc2lvbiBDb250cm9sMSswKQYD 5 | VQQLEyJEeW5hbWljIEFkbWlzc2lvbiBDb250cm9sIFNlY3VyaXR5MSUwIwYDVQQD 6 | ExxEeW5hbWljIEFkbWlzc2lvbiBDb250cm9sIENBMB4XDTI0MDYxMjA4MzEwMFoX 7 | DTM0MDYxMDA4MzEwMFowgaYxCzAJBgNVBAYTAkNOMRAwDgYDVQQIEwdCZWlqaW5n 8 | MRAwDgYDVQQHEwdCZWlqaW5nMSIwIAYDVQQKExlEeW5hbWljIEFkbWlzc2lvbiBD 9 | b250cm9sMSswKQYDVQQLEyJEeW5hbWljIEFkbWlzc2lvbiBDb250cm9sIFNlY3Vy 10 | aXR5MSIwIAYDVQQDExlEeW5hbWljIEFkbWlzc2lvbiBDb250cm9sMIIBIjANBgkq 11 | hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsr/gUdrKen9H4f+ciSyBwfe5JVj/XoWc 12 | 8K6T3yV4cOwJgMlkO0/nVUdJvmeDAuHJPCRxk1+qbv+owEUvJZT44CXuA/Tz8l2D 13 | VD3mgDd4ZczAgCsXX8IUu3MKSwHP7RHXmMoL2bjF8Vti3ot+S4LdpZKaRirUO8r4 14 | uTCzwKmU2/6qJi5suSZkzqPE9Vv/llpP7GhHo0rrq59u1SGIo9oQ//5V/ovHZnbJ 15 | AUOOhNXVkF0ywClCp9Dt72B5RwCKpeD5deNmIKVE9xSV30G0w0SVtWLXB7wVEqNt 16 | Nhj0W1AH82pcPf8gHKdpNLYn7HmgNMihT4jyONiJBOdhcZTepCTwKQIDAQABo4IB 17 | QjCCAT4wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEF 18 | BQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQyutpIo6AiaimaMYOmiBhVnOcY 19 | RTAfBgNVHSMEGDAWgBQcUJY+ldtLuyIehBdEyu/ta2R/ujCBvgYDVR0RBIG2MIGz 20 | gglsb2NhbGhvc3SCEGpveWxpdmUtaW5qZWN0b3KCGGpveWxpdmUtaW5qZWN0b3Iu 21 | am95bGl2ZYIcam95bGl2ZS1pbmplY3Rvci5qb3lsaXZlLnN2Y4ISdmFsaWRhdGlu 22 | Zy13ZWJob29rgh52YWxpZGF0aW5nLXdlYmhvb2sua3ViZS1hZGRvbnOCInZhbGlk 23 | YXRpbmctd2ViaG9vay5rdWJlLWFkZG9ucy5zdmOHBH8AAAEwDQYJKoZIhvcNAQEN 24 | BQADggIBAGnGlr6claf5OYf/Kb0mJVILDdHaqm9GCywQyr++t5IxqgwOP/YyvVgR 25 | 9qPlexrhQGcXLA57rr/VIliQa7q1EU8d9JdWzouvKQJcaYuoVXGTZiJDYQRfT8Rc 26 | vuNht5E8U92a9s7gCMq39dm2q1wi6FUpSGelV4InnlDWaV+jjchFxrOv2cbHAiXj 27 | BzspYH3rlf9CoyHfGG4Cx++xMb1IPoZW1thqtfxjeShIbyqvYvVz2eF1oEcyjDhY 28 | fxQ34F6c5m3PZfi6zgOVKJsSfOtvzYaD/g3INZhkxklMkr2PUPLQsYBJ/7FCI39u 29 | y5ga4CDsFt5W4/Q5NFEePCUAISYtf4JhpJ0bRUVf6cPiXzfpmWmPbhSe4d6BWUSq 30 | RBqkQaJBnoalzamIhbezNEgPO1eK+y1sdQVaLrkzQIEFoXsy2WRitQSjP5ja/dPf 31 | 9hIyZ9yonrs2mYQjj0n3yCFQdd7xWfw5XcDVRqutvuz7yN/Dn7f/mymV6FGIRwgh 32 | VI/1qH6u7yIOYc3QUGcr2gWwXZRUZmSMoVaaU9M80U2OEz3l82GcV2A0bZRCm5RW 33 | Azlw5mz4FUK/nSgJ3B15gxmKVSYKg2CMQfeqy8cp4nMtJddhr8NIMNdaKPmI6S4+ 34 | NIYyEszqhVPLk314ysquCfRQn7j2vGChOUHUh+EO8UoVWCeS5GTQ 35 | -----END CERTIFICATE----- 36 | -------------------------------------------------------------------------------- /deploy/cfssl/dac-ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIGJDCCBAygAwIBAgIUSulET1XhjJNcx2AtUBkvmJPA44IwDQYJKoZIhvcNAQEN 3 | BQAwgakxCzAJBgNVBAYTAkNOMRAwDgYDVQQIEwdCZWlqaW5nMRAwDgYDVQQHEwdC 4 | ZWlqaW5nMSIwIAYDVQQKExlEeW5hbWljIEFkbWlzc2lvbiBDb250cm9sMSswKQYD 5 | VQQLEyJEeW5hbWljIEFkbWlzc2lvbiBDb250cm9sIFNlY3VyaXR5MSUwIwYDVQQD 6 | ExxEeW5hbWljIEFkbWlzc2lvbiBDb250cm9sIENBMB4XDTI0MDYxMjA4MzEwMFoX 7 | DTM0MDYxMDA4MzEwMFowgakxCzAJBgNVBAYTAkNOMRAwDgYDVQQIEwdCZWlqaW5n 8 | MRAwDgYDVQQHEwdCZWlqaW5nMSIwIAYDVQQKExlEeW5hbWljIEFkbWlzc2lvbiBD 9 | b250cm9sMSswKQYDVQQLEyJEeW5hbWljIEFkbWlzc2lvbiBDb250cm9sIFNlY3Vy 10 | aXR5MSUwIwYDVQQDExxEeW5hbWljIEFkbWlzc2lvbiBDb250cm9sIENBMIICIjAN 11 | BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAzCE+gej+RANWfu48QBqyG/GQQPTU 12 | yQGWocduHCLxYEk5KH/SMTvlljVhDF9lD4ErNt46OyBps90bLnZX2JGxktWfqS9h 13 | 8EF75u2qOPqhc17tvJ051kofqfeP9W/mKnvHi2PDjMg2OLl0IXnFk2NT1mPWzmvZ 14 | A/KVAW3Bpb+8H7v6AGhC5+/+C5RTB2xZMQnRgWwwfDwQO7xBitpGDez/Xjg7Bfrf 15 | bCFzGOA2oG5SnMX78N8sMTBnFib/3fXnDCY8EWlkLihniqx67yKZFRbPDg/B8cjZ 16 | 2+tVsjNEI6UBas4/5u2HcnY+F4etpl3vCJdsGlJTziuycxm747Tnf/lCfzOx35RJ 17 | dRYjhBUQjMZp4aiKBgpj2Lmu/MWnjADczedmUeYbBPA2Pex0R5DsSxT28OtWDdKt 18 | dxbI0/dHhOjYl9zOS32GlFXJHSP3W0GKSHUa1Jxn1gqDi3c+MCDUFEO1UY5R2qeQ 19 | qcvduHqwz0twiM7uTMrIYguI3UCdEGqr3pwGiADHnG/OZH0taMASmufVMJXb/N6f 20 | 36eDq4zD8iin0XMK8YkN4UJfAJaIEyMuEMjRwRaHORU7wVJjBDUcpr+kSvdX038v 21 | TcNmf/N58C1sRmJyDCba7imjjA7D8te+wRugeITG8rDCN8EjAQUTHIA995d7q+DM 22 | R5u8e+DSWWD7G+sCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF 23 | MAMBAf8wHQYDVR0OBBYEFBxQlj6V20u7Ih6EF0TK7+1rZH+6MA0GCSqGSIb3DQEB 24 | DQUAA4ICAQAeKaUrmS1gNyztw35kqsaOlXetzBpPYHsaxk5GWRcGIPIFfwmvg6JP 25 | mjtb/27cAWcEwA6eAJEbl66oHsyNYCdrfayH43MtcYSiEwEXBKbaSwAUgWf/BzSZ 26 | ADkwZ0J8mfWJt74MALyt2NzUriR2JrhhKjxMbL7jLftIhfrDbu6rWoaT5fLQALHH 27 | ky/ysZfRXqRu1rSvWokUTcm5pb0YYcDO8ik8cN5VBfXCQb6/RHUR269CivYXSKG/ 28 | Sv7BNJfrEtSu4WMrkMgVj46foAJb0aJtXs/+to0cX9ZHna1tv8YWgbRqbOFraJN8 29 | QOLsmqnybMSr7gwJ4CpzsuL5hxLKYfz3AbezNlqqb76dlMfbP8o2Q/YHMzmKbdZg 30 | Kbsy0sJaAdKTFF+G9nbZAlB7Wtdk3K91F4dqQJNS6rePJTOPIfZj1fKsvytfwv3s 31 | Ob3rckOy16YqUdKl5PyK24F5Y1taBZwOC387qqjZE/I4hGtXEeZyi50ZjVKUdfHB 32 | sXsUsrcmCxvqubD1t+TvRej4pi2FKzmLXQQMuI59IXte7wRaLCTCMYWopfGyvAt4 33 | 3XGJn++6xQUxWMjqHLmGrAaMSVaZv//wsW0EjrltUOqr96Bd5ye8QWFgeWmDRUoR 34 | V2VYehQzUNOSZ7EYyNMrLeYrWqpRyx1h4MWi3znW5Pj4FcLhZMgtnQ== 35 | -----END CERTIFICATE----- 36 | -------------------------------------------------------------------------------- /deploy/joylive-injector/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* 2 | Expand the name of the chart. 3 | */}} 4 | {{- define "joylive-injector.name" -}} 5 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 6 | {{- end }} 7 | 8 | {{/* 9 | Create a default fully qualified app name. 10 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 11 | If release name contains chart name it will be used as a full name. 12 | */}} 13 | {{- define "joylive-injector.fullname" -}} 14 | {{- if .Values.fullnameOverride }} 15 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 16 | {{- else }} 17 | {{- $name := default .Chart.Name .Values.nameOverride }} 18 | {{- if contains $name .Release.Name }} 19 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 20 | {{- else }} 21 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 22 | {{- end }} 23 | {{- end }} 24 | {{- end }} 25 | 26 | {{/* 27 | Create chart name and version as used by the chart label. 28 | */}} 29 | {{- define "joylive-injector.chart" -}} 30 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 31 | {{- end }} 32 | 33 | {{/* 34 | Common labels 35 | */}} 36 | {{- define "joylive-injector.labels" -}} 37 | helm.sh/chart: {{ include "joylive-injector.chart" . }} 38 | {{ include "joylive-injector.selectorLabels" . }} 39 | {{- if .Chart.AppVersion }} 40 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 41 | {{- end }} 42 | app.kubernetes.io/managed-by: {{ .Release.Service }} 43 | {{- end }} 44 | 45 | {{/* 46 | Selector labels 47 | */}} 48 | {{- define "joylive-injector.selectorLabels" -}} 49 | app.kubernetes.io/name: {{ include "joylive-injector.name" . }} 50 | app.kubernetes.io/instance: {{ .Release.Name }} 51 | app: {{ .Chart.Name }} 52 | {{- end }} 53 | 54 | {{/* 55 | Match labels 56 | */}} 57 | {{- define "joylive-injector.matchLabels" -}} 58 | {{ .Values.matchLabels.matchKey}} : "{{ .Values.matchLabels.matchValue }}" 59 | {{- end }} 60 | 61 | {{/* 62 | Create the name of the service account to use 63 | */}} 64 | {{- define "joylive-injector.serviceAccountName" -}} 65 | {{- if .Values.serviceAccount.create }} 66 | {{- default (include "joylive-injector.fullname" .) .Values.serviceAccount.name }} 67 | {{- else }} 68 | {{- default "default" .Values.serviceAccount.name }} 69 | {{- end }} 70 | {{- end }} 71 | -------------------------------------------------------------------------------- /pkg/log/rotate.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | rotatelogs "github.com/lestrrat-go/file-rotatelogs" 5 | lumberjack "gopkg.in/natefinch/lumberjack.v2" 6 | "io" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | type RotateConfig struct { 12 | // shared configuration 13 | Filename string // Full file name 14 | MaxAge int // Maximum number of days to keep old log files 15 | 16 | // Configuration by time rotation 17 | RotationTime time.Duration // Log file rotation time 18 | 19 | // Rotate configuration by size 20 | MaxSize int // Maximum log file size (MB) 21 | MaxBackups int // Maximum number of log files to keep 22 | Compress bool // Whether to compress and archive log files 23 | LocalTime bool // Whether to use local time, default UTC time 24 | } 25 | 26 | // NewProductionRotateByTime Create an io.Writer that rotates by time 27 | func NewProductionRotateByTime(filename string) io.Writer { 28 | return NewRotateByTime(NewProductionRotateConfig(filename)) 29 | } 30 | 31 | // NewProductionRotateBySize Create an io.Writer that rotates by size 32 | func NewProductionRotateBySize(filename string) io.Writer { 33 | return NewRotateBySize(NewProductionRotateConfig(filename)) 34 | } 35 | 36 | func NewProductionRotateConfig(filename string) *RotateConfig { 37 | return &RotateConfig{ 38 | Filename: filename, 39 | MaxAge: 30, // Logs retained for 30 days 40 | RotationTime: time.Hour * 24, // Rotates every 24 hours 41 | MaxSize: 100, // 100M 42 | MaxBackups: 100, 43 | Compress: true, 44 | LocalTime: false, 45 | } 46 | } 47 | 48 | func NewRotateByTime(cfg *RotateConfig) io.Writer { 49 | opts := []rotatelogs.Option{ 50 | rotatelogs.WithMaxAge(time.Duration(cfg.MaxAge) * time.Hour * 24), 51 | rotatelogs.WithRotationTime(cfg.RotationTime), 52 | rotatelogs.WithLinkName(cfg.Filename), 53 | } 54 | if !cfg.LocalTime { 55 | rotatelogs.WithClock(rotatelogs.UTC) 56 | } 57 | filename := strings.SplitN(cfg.Filename, ".", 2) 58 | l, _ := rotatelogs.New( 59 | filename[0]+".%Y-%m-%d-%H-%M-%S."+filename[1], 60 | opts..., 61 | ) 62 | return l 63 | } 64 | 65 | func NewRotateBySize(cfg *RotateConfig) io.Writer { 66 | return &lumberjack.Logger{ 67 | Filename: cfg.Filename, 68 | MaxSize: cfg.MaxSize, 69 | MaxAge: cfg.MaxAge, 70 | MaxBackups: cfg.MaxBackups, 71 | LocalTime: cfg.LocalTime, 72 | Compress: cfg.Compress, 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /README-zh.md: -------------------------------------------------------------------------------- 1 | # joylive-injector 2 | 3 | [![GitHub repo](https://img.shields.io/badge/GitHub-repo-blue)](https://github.com/jd-opensource/joylive-injector) 4 | [![GitHub release](https://img.shields.io/github/release/jd-opensource/joylive-injector.svg)](https://github.com/jd-opensource/joylive-injector/releases) 5 | [![Slack Status](https://img.shields.io/badge/slack-join_chat-white.svg?logo=slack&style=social)](https://joylivehq.slack.com) 6 | 7 | pic 8 | 9 | [English](./README.md) | 简体中文 10 | 11 | ## 介绍 12 | 这是一个针对kubernetes的动态准入控制webhook,它可以用于修改`kubernete`资源。 13 | 此程序监视`deployments`的CREATE、UPDATE、DELETE事件和`pods`的CREATE事件,并为`POD`添加initContainer、默认增加环境变量`JAVA_TOOL_OPTIONS`、挂载configmap、修改主容器的卷装载等操作。 14 | 15 | ## 特性 16 | - 支持自动将`joylive-agent`注入Java应用的Pod。 17 | - 支持多版本`joylive-agent`与对应配置管理。 18 | - 支持注入指定版本`joylive-agent`及对应配置。 19 | 20 | ## 使用方式 21 | ### 完全模式 22 | - 在要部署的环境中安装 CFSSL(用于签名,验证和捆绑TLS证书的HTTP API工具) 23 | ```bash 24 | wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 25 | wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 26 | wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 27 | mv cfssl-certinfo_linux-amd64 /usr/local/bin/cfssl-certinfo 28 | mv cfssl_linux-amd64 /usr/local/bin/cfssl 29 | mv cfssljson_linux-amd64 /usr/local/bin/cfssljson 30 | chmod +x /usr/local/bin/cfssl-certinfo /usr/local/bin/cfssl /usr/local/bin/cfssljson 31 | ``` 32 | - 拷贝deploy目录下的`cfssl`和`joylive-injector`到要部署的环境 33 | - `cfssl/dac-csr.json`中的namespace目前填写的是`joylive`,需要根据实际情况修改 34 | - 执行`joylive-injector/deploy/cfssl`目录下的`create-secret.sh`脚本生成secret,若`joylive-injector`包与`cfssl`在同一目录下,可自动替换`caBundle`, `caKeyBundle` 和 `caPubBundle`字段的值 35 | - 若`caBundle`,`caKeyBundle` 和 `caPubBundle`的值未替换,需要手动替换chart包中的`value.yaml`中的`caBundle`,`caKeyBundle` 和 `caPubBundle`字段得值,使用`cat dac-ca.pem | base64 | tr -d '\n'` 作为 `caBundle`, `cat dac-key.pem | base64 | tr -d '\n'` 作为 `caKeyBundle`, `cat dac.pem | base64 | tr -d '\n'` 作为 `caPubBundle` 生成的内容替换 36 | - 执行`helm install joylive-injector ./joylive-injector -n joylive`安装webhook 37 | - chart包中的`value.yaml`中配置按需修改 38 | 39 | ### 简单模式 40 | 因证书签名已按照命名空间为`joylive`预生成,所以须指定安装到对应命名空间。 执行命令: 41 | ```bash 42 | helm repo add joylive https://jd-opensource.github.io/joylive-helm-charts 43 | kubectl create namespace joylive 44 | helm install joylive-injector joylive/joylive-injector -n joylive 45 | ``` 46 | 47 | ## 成员 48 | 49 | 感谢各位贡献者![名单](./community/members.md) -------------------------------------------------------------------------------- /deploy/joylive-injector/crds/injector.joylive.io_agentversions.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: v0.15.0 7 | name: agentversions.injector.joylive.io 8 | spec: 9 | group: injector.joylive.io 10 | names: 11 | kind: AgentVersion 12 | listKind: AgentVersionList 13 | plural: agentversions 14 | singular: agentversion 15 | scope: Namespaced 16 | versions: 17 | - name: v1 18 | schema: 19 | openAPIV3Schema: 20 | description: AgentVersion is the Schema for the agentversions API 21 | properties: 22 | apiVersion: 23 | description: |- 24 | APIVersion defines the versioned schema of this representation of an object. 25 | Servers should convert recognized schemas to the latest internal value, and 26 | may reject unrecognized values. 27 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 28 | type: string 29 | kind: 30 | description: |- 31 | Kind is a string value representing the REST resource this object represents. 32 | Servers may infer this from the endpoint the client submits requests to. 33 | Cannot be updated. 34 | In CamelCase. 35 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 36 | type: string 37 | metadata: 38 | type: object 39 | spec: 40 | description: AgentVersionSpec defines the desired state of AgentVersion 41 | properties: 42 | configMapName: 43 | description: ConfigMapName record storage version configuration file 44 | type: string 45 | enable: 46 | description: Enable indicates whether this version is enabled 47 | type: boolean 48 | version: 49 | description: Version of JoyLive Agent release 50 | type: string 51 | required: 52 | - enable 53 | - version 54 | type: object 55 | status: 56 | description: AgentVersionStatus defines the observed state of AgentVersion 57 | type: object 58 | type: object 59 | served: true 60 | storage: true 61 | subresources: 62 | status: {} 63 | -------------------------------------------------------------------------------- /client-go/applyconfiguration/injector/v1/agentversionspec.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 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 applyconfiguration-gen. DO NOT EDIT. 18 | 19 | package v1 20 | 21 | // AgentVersionSpecApplyConfiguration represents an declarative configuration of the AgentVersionSpec type for use 22 | // with apply. 23 | type AgentVersionSpecApplyConfiguration struct { 24 | Version *string `json:"version,omitempty"` 25 | ConfigMapName *string `json:"configMapName,omitempty"` 26 | Enable *bool `json:"enable,omitempty"` 27 | } 28 | 29 | // AgentVersionSpecApplyConfiguration constructs an declarative configuration of the AgentVersionSpec type for use with 30 | // apply. 31 | func AgentVersionSpec() *AgentVersionSpecApplyConfiguration { 32 | return &AgentVersionSpecApplyConfiguration{} 33 | } 34 | 35 | // WithVersion sets the Version field in the declarative configuration to the given value 36 | // and returns the receiver, so that objects can be built by chaining "With" function invocations. 37 | // If called multiple times, the Version field is set to the value of the last call. 38 | func (b *AgentVersionSpecApplyConfiguration) WithVersion(value string) *AgentVersionSpecApplyConfiguration { 39 | b.Version = &value 40 | return b 41 | } 42 | 43 | // WithConfigMapName sets the ConfigMapName field in the declarative configuration to the given value 44 | // and returns the receiver, so that objects can be built by chaining "With" function invocations. 45 | // If called multiple times, the ConfigMapName field is set to the value of the last call. 46 | func (b *AgentVersionSpecApplyConfiguration) WithConfigMapName(value string) *AgentVersionSpecApplyConfiguration { 47 | b.ConfigMapName = &value 48 | return b 49 | } 50 | 51 | // WithEnable sets the Enable field in the declarative configuration to the given value 52 | // and returns the receiver, so that objects can be built by chaining "With" function invocations. 53 | // If called multiple times, the Enable field is set to the value of the last call. 54 | func (b *AgentVersionSpecApplyConfiguration) WithEnable(value bool) *AgentVersionSpecApplyConfiguration { 55 | b.Enable = &value 56 | return b 57 | } 58 | -------------------------------------------------------------------------------- /pkg/route/router.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | import ( 4 | "fmt" 5 | "github.com/jd-opensource/joylive-injector/pkg/log" 6 | "net/http" 7 | "runtime/debug" 8 | "strings" 9 | "sync" 10 | 11 | "github.com/gorilla/mux" 12 | 13 | jsoniter "github.com/json-iterator/go" 14 | 15 | admissionv1 "k8s.io/api/admission/v1" 16 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 17 | ) 18 | 19 | type HandleFunc struct { 20 | Path string 21 | Method string 22 | Func func(w http.ResponseWriter, r *http.Request) 23 | } 24 | 25 | type handleFuncMap map[string]HandleFunc 26 | 27 | var funcMap = make(handleFuncMap, 10) 28 | var routerOnce sync.Once 29 | 30 | func RegisterHandler(hf HandleFunc) { 31 | if hf.Path == "" { 32 | log.Fatalf("handle func path is empty") 33 | } 34 | registeredHf, ok := funcMap[strings.ToLower(hf.Path)] 35 | if ok && registeredHf.Method == hf.Method { 36 | log.Fatalf("handle func [%s] already registered", hf.Path) 37 | } 38 | funcMap[strings.ToLower(hf.Path)] = hf 39 | } 40 | 41 | func ResponseErr(handlePath, msg string, httpCode int, w http.ResponseWriter) { 42 | log.Errorf("handle func [%s] response err: %s", handlePath, msg) 43 | review := &admissionv1.AdmissionReview{ 44 | Response: &admissionv1.AdmissionResponse{ 45 | Allowed: false, 46 | Result: &metav1.Status{ 47 | Message: msg, 48 | }, 49 | }, 50 | } 51 | bs, err := jsoniter.Marshal(review) 52 | if err != nil { 53 | log.Errorf("failed to marshal response: %v", err) 54 | w.WriteHeader(http.StatusInternalServerError) 55 | _, _ = w.Write([]byte(fmt.Sprintf("failed to marshal response: %s", err))) 56 | } 57 | 58 | w.Header().Set("Content-Type", "application/json") 59 | w.WriteHeader(httpCode) 60 | _, err = w.Write(bs) 61 | log.Debugf("write err response: %d: %v: %v", httpCode, review, err) 62 | } 63 | 64 | func loggingMiddleware() func(http.Handler) http.Handler { 65 | return func(next http.Handler) http.Handler { 66 | fn := func(w http.ResponseWriter, r *http.Request) { 67 | defer func() { 68 | if err := recover(); err != nil { 69 | w.WriteHeader(http.StatusInternalServerError) 70 | log.Errorf("err: %v, trace: %s", err, string(debug.Stack())) 71 | } 72 | }() 73 | 74 | //start := time.Now() 75 | next.ServeHTTP(w, r) 76 | //logger.Debugf("received request: %s %s %s", time.Since(start), strings.ToLower(r.Method), r.URL.EscapedPath()) 77 | } 78 | return http.HandlerFunc(fn) 79 | } 80 | } 81 | 82 | var router *mux.Router 83 | 84 | func Setup() { 85 | routerOnce.Do(func() { 86 | log.Info("setup global http router...") 87 | router = mux.NewRouter().StrictSlash(true) 88 | for p, f := range funcMap { 89 | log.Infof("setup global handle func: %s", p) 90 | router.HandleFunc(f.Path, f.Func).Methods(f.Method) 91 | } 92 | }) 93 | } 94 | 95 | func Router() http.Handler { 96 | return loggingMiddleware()(router) 97 | } 98 | -------------------------------------------------------------------------------- /pkg/resource/control.go: -------------------------------------------------------------------------------- 1 | package resource 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "strings" 9 | 10 | "github.com/jd-opensource/joylive-injector/pkg/config" 11 | ) 12 | 13 | type Response struct { 14 | Error error `json:"error,omitempty"` 15 | Data interface{} `json:"data,omitempty"` 16 | } 17 | 18 | type ApplicationEnvResponse struct { 19 | Response 20 | Data map[string]string `json:"data"` 21 | } 22 | 23 | func GetApplicationEnvironments(labels map[string]string) (map[string]string, error) { 24 | serviceSpace := labels[config.ServiceSpaceLabel] 25 | application := labels[config.ApplicationLabel] 26 | if len(serviceSpace) == 0 { 27 | serviceSpace = labels[config.JdapServiceSpaceLabel] 28 | } 29 | if len(application) == 0 { 30 | application = labels[config.JdapApplicationLabel] 31 | } 32 | envMaps := make(map[string]string) 33 | if len(serviceSpace) != 0 && len(application) != 0 { 34 | url := fmt.Sprintf( 35 | "%s/ns/%s/application/%s/environments", config.ControlPlaneUrl, serviceSpace, application, 36 | ) 37 | resp, err := http.Get(url) 38 | if err != nil { 39 | return nil, nil 40 | } 41 | defer resp.Body.Close() 42 | 43 | if resp.StatusCode != http.StatusOK { 44 | return nil, fmt.Errorf("request failed, status: %s", resp.Status) 45 | } 46 | 47 | body, err := io.ReadAll(resp.Body) 48 | if err != nil { 49 | return nil, err 50 | } 51 | 52 | var response ApplicationEnvResponse 53 | if err := json.Unmarshal(body, &response); err != nil { 54 | return nil, err 55 | } 56 | 57 | if response.Error != nil { 58 | return nil, response.Error 59 | } 60 | for key, value := range response.Data { 61 | if len(config.FilterSensitive) != 0 && config.FilterSensitive == "true" { 62 | // Filter out keys ending with "USERNAME" and "PASSWORD" to enhance security. 63 | if strings.HasSuffix(key, "USERNAME") || strings.HasSuffix(key, "PASSWORD") { 64 | continue 65 | } 66 | } 67 | envMaps[key] = value 68 | } 69 | envMaps["APPLICATION_NAME"] = application 70 | envMaps["APPLICATION_SERVICE_NAMESPACE"] = serviceSpace 71 | } 72 | 73 | if v, ok := labels[config.RegisterTypeLabel]; ok { 74 | envMaps["CONFIG_REGISTRY_ENABLED"] = v 75 | } 76 | 77 | if v, ok := labels[config.ConfigureTypeLabel]; ok { 78 | envMaps["CONFIG_CENTER_ENABLED"] = v 79 | } 80 | 81 | if group, ok := labels[config.ServiceGroupLabel]; ok { 82 | envMaps["APPLICATION_SERVICE_GROUP"] = group 83 | } else { 84 | envMaps["APPLICATION_SERVICE_GROUP"] = "default" 85 | } 86 | 87 | if service, ok := labels[config.ServiceNameLabel]; ok { 88 | envMaps["APPLICATION_SERVICE_NAME"] = service 89 | } 90 | 91 | //envMaps["APPLICATION_LOCATION_CLUSTER"] = config.ClusterId 92 | 93 | if swimlane, ok := labels[config.SwimLaneLabel]; ok { 94 | envMaps["CONFIG_LANE_ENABLED"] = "true" 95 | envMaps["APPLICATION_LOCATION_LANE"] = swimlane 96 | } 97 | 98 | return envMaps, nil 99 | } 100 | -------------------------------------------------------------------------------- /client-go/clientset/versioned/fake/clientset_generated.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | clientset "github.com/jd-opensource/joylive-injector/client-go/clientset/versioned" 23 | injectorv1 "github.com/jd-opensource/joylive-injector/client-go/clientset/versioned/typed/injector/v1" 24 | fakeinjectorv1 "github.com/jd-opensource/joylive-injector/client-go/clientset/versioned/typed/injector/v1/fake" 25 | "k8s.io/apimachinery/pkg/runtime" 26 | "k8s.io/apimachinery/pkg/watch" 27 | "k8s.io/client-go/discovery" 28 | fakediscovery "k8s.io/client-go/discovery/fake" 29 | "k8s.io/client-go/testing" 30 | ) 31 | 32 | // NewSimpleClientset returns a clientset that will respond with the provided objects. 33 | // It's backed by a very simple object tracker that processes creates, updates and deletions as-is, 34 | // without applying any validations and/or defaults. It shouldn't be considered a replacement 35 | // for a real clientset and is mostly useful in simple unit tests. 36 | func NewSimpleClientset(objects ...runtime.Object) *Clientset { 37 | o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) 38 | for _, obj := range objects { 39 | if err := o.Add(obj); err != nil { 40 | panic(err) 41 | } 42 | } 43 | 44 | cs := &Clientset{tracker: o} 45 | cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} 46 | cs.AddReactor("*", "*", testing.ObjectReaction(o)) 47 | cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { 48 | gvr := action.GetResource() 49 | ns := action.GetNamespace() 50 | watch, err := o.Watch(gvr, ns) 51 | if err != nil { 52 | return false, nil, err 53 | } 54 | return true, watch, nil 55 | }) 56 | 57 | return cs 58 | } 59 | 60 | // Clientset implements clientset.Interface. Meant to be embedded into a 61 | // struct to get a default implementation. This makes faking out just the method 62 | // you want to test easier. 63 | type Clientset struct { 64 | testing.Fake 65 | discovery *fakediscovery.FakeDiscovery 66 | tracker testing.ObjectTracker 67 | } 68 | 69 | func (c *Clientset) Discovery() discovery.DiscoveryInterface { 70 | return c.discovery 71 | } 72 | 73 | func (c *Clientset) Tracker() testing.ObjectTracker { 74 | return c.tracker 75 | } 76 | 77 | var ( 78 | _ clientset.Interface = &Clientset{} 79 | _ testing.FakeClient = &Clientset{} 80 | ) 81 | 82 | // InjectorV1 retrieves the InjectorV1Client 83 | func (c *Clientset) InjectorV1() injectorv1.InjectorV1Interface { 84 | return &fakeinjectorv1.FakeInjectorV1{Fake: &c.Fake} 85 | } 86 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to JoyLive 2 | 3 | Welcome to JoyLive! This document is a guideline about how to contribute to JoyLive. 4 | If you find something incorrect or missing, please leave comments / suggestions. 5 | 6 | ## Before you get started 7 | 8 | ### Code of Conduct 9 | 10 | Please make sure to read and observe our [Code of Conduct](./CODE_OF_CONDUCT.md). 11 | 12 | ### Setting up your development environment 13 | 14 | You should have JDK 17 or later installed in your system. 15 | 16 | ## Contributing 17 | 18 | We are always very happy to have contributions, whether for typo fix, bug fix or big new features. 19 | Please do not ever hesitate to ask a question or send a pull request. 20 | 21 | We strongly value documentation and integration with other projects. 22 | We are very glad to accept improvements for these aspects. 23 | 24 | ### GitHub workflow 25 | 26 | We use the `main` branch as the development branch, which indicates that this is a unstable branch. 27 | 28 | Here are the workflow for contributors: 29 | 30 | 1. Fork to your own 31 | 2. Clone fork to local repository 32 | 3. Create a new branch and work on it 33 | 4. Keep your branch in sync 34 | 5. Commit your changes (make sure your commit message concise) 35 | 6. Push your commits to your forked repository 36 | 7. Create a pull request 37 | 38 | Please follow [the pull request template](./.github/PULL_REQUEST_TEMPLATE.md). 39 | Please make sure the PR has a corresponding issue. 40 | 41 | After creating a PR, one or more reviewers will be assigned to the pull request. 42 | The reviewers will review the code. 43 | 44 | Before merging a PR, squash any fix review feedback, typo, merged, and rebased sorts of commits. 45 | The final commit message should be clear and concise. 46 | 47 | Thanks for contributing! 48 | 49 | ### Open an issue / PR 50 | 51 | We use [GitHub Issues](https://github.com/jd-opensource/joylive-injector/issues) and [Pull Requests](https://github.com/jd-opensource/joylive-injector/pulls) for trackers. 52 | 53 | If you find a typo in document, find a bug in code, or want new features, or want to give suggestions, 54 | you can [open an issue on GitHub](https://github.com/jd-opensource/joylive-injector/issues/new) to report it. 55 | Please follow the guideline message in the issue template. 56 | 57 | If you want to contribute, please follow the [contribution workflow](#github-workflow) and create a new pull request. 58 | If your PR contains large changes, e.g. component refactor or new components, please write detailed documents 59 | about its design and usage. 60 | 61 | Note that a single PR should not be too large. If heavy changes are required, it's better to separate the changes 62 | to a few individual PRs. 63 | 64 | ### Code review 65 | 66 | All code should be well reviewed by one or more committers. Some principles: 67 | 68 | - Readability: Important code should be well-documented. Comply with our code style. 69 | - Elegance: New functions, classes or components should be well designed. 70 | - Testability: Important code should be well-tested (high unit test coverage). 71 | 72 | ## Community 73 | 74 | ### Contact us 75 | 76 | #### Slack 77 | 78 | Our Slack room: [https://joylivehq.slack.com](https://joylivehq.slack.com). -------------------------------------------------------------------------------- /deploy/joylive-injector/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ include "joylive-injector.name" . }} 5 | namespace: {{ .Values.namespace }} 6 | labels: {{ include "joylive-injector.selectorLabels" . | nindent 4 }} 7 | spec: 8 | replicas: {{ .Values.replicas }} 9 | selector: 10 | matchLabels: 11 | app: {{ include "joylive-injector.name" . }} 12 | template: 13 | metadata: 14 | labels: 15 | app: {{ include "joylive-injector.name" . }} 16 | {{- range $key, $val := .Values.podLabels }} 17 | {{ $key }}: "{{ $val }}" 18 | {{- end }} 19 | spec: 20 | serviceAccountName: {{ include "joylive-injector.serviceAccountName" . }} 21 | affinity: 22 | podAntiAffinity: 23 | preferredDuringSchedulingIgnoredDuringExecution: 24 | - weight: 100 25 | podAffinityTerm: 26 | labelSelector: 27 | matchLabels: 28 | app: {{ include "joylive-injector.name" . }} 29 | topologyKey: "kubernetes.io/hostname" 30 | containers: 31 | - env: 32 | - name: LOG_LEVEL 33 | value: "{{ .Values.logLevel }}" 34 | - name: JOYLIVE_CONFIGMAP_NAME 35 | value: "{{ .Values.configMapName }}" 36 | - name: JOYLIVE_RULE_CONFIGMAP_NAME 37 | value: "{{ .Values.ruleConfigMapName }}" 38 | - name: JOYLIVE_NAMESPACE 39 | value: "{{ .Values.namespace }}" 40 | - name: JOYLIVE_MATCH_ENV_LABELS 41 | value: "{{ .Values.matchEnvLabels }}" 42 | - name: JOYLIVE_CONTROL_PLANE_URL 43 | value: "{{ .Values.controlPlaneUrl }}" 44 | - name: JOYLIVE_FILTER_SENSITIVE 45 | value: "{{ .Values.filterSensitive }}" 46 | - name: JOYLIVE_CLUSTER_ID 47 | value: "{{ .Values.clusterId }}" 48 | - name: JOYLIVE_MATCH_KEY 49 | value: "{{ .Values.matchLabels.matchKey}}" 50 | - name: JOYLIVE_MATCH_VALUE 51 | value: "{{ .Values.matchLabels.matchValue }}" 52 | name: joylive-injector 53 | image: {{ .Values.image.repository }}:{{ .Values.image.tag }} 54 | imagePullPolicy: {{ .Values.image.pullPolicy }} 55 | args: 56 | {{- toYaml .Values.containerArgs | nindent 12 }} 57 | livenessProbe: 58 | httpGet: 59 | scheme: HTTPS 60 | port: 443 61 | path: /livez 62 | periodSeconds: 10 63 | initialDelaySeconds: 5 64 | readinessProbe: 65 | httpGet: 66 | scheme: HTTPS 67 | port: 443 68 | path: /readyz 69 | periodSeconds: 10 70 | initialDelaySeconds: 5 71 | resources: 72 | {{- toYaml .Values.resources | nindent 12 }} 73 | volumeMounts: 74 | {{- toYaml .Values.VolumeMounts | nindent 12 }} 75 | {{- with .Values.imagePullSecrets }} 76 | imagePullSecrets: 77 | {{- toYaml . | nindent 8 }} 78 | {{- end }} 79 | volumes: 80 | {{- toYaml .Values.Volumes | nindent 8 }} 81 | -------------------------------------------------------------------------------- /deploy/cfssl/dac-ca-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIJKgIBAAKCAgEAzCE+gej+RANWfu48QBqyG/GQQPTUyQGWocduHCLxYEk5KH/S 3 | MTvlljVhDF9lD4ErNt46OyBps90bLnZX2JGxktWfqS9h8EF75u2qOPqhc17tvJ05 4 | 1kofqfeP9W/mKnvHi2PDjMg2OLl0IXnFk2NT1mPWzmvZA/KVAW3Bpb+8H7v6AGhC 5 | 5+/+C5RTB2xZMQnRgWwwfDwQO7xBitpGDez/Xjg7BfrfbCFzGOA2oG5SnMX78N8s 6 | MTBnFib/3fXnDCY8EWlkLihniqx67yKZFRbPDg/B8cjZ2+tVsjNEI6UBas4/5u2H 7 | cnY+F4etpl3vCJdsGlJTziuycxm747Tnf/lCfzOx35RJdRYjhBUQjMZp4aiKBgpj 8 | 2Lmu/MWnjADczedmUeYbBPA2Pex0R5DsSxT28OtWDdKtdxbI0/dHhOjYl9zOS32G 9 | lFXJHSP3W0GKSHUa1Jxn1gqDi3c+MCDUFEO1UY5R2qeQqcvduHqwz0twiM7uTMrI 10 | YguI3UCdEGqr3pwGiADHnG/OZH0taMASmufVMJXb/N6f36eDq4zD8iin0XMK8YkN 11 | 4UJfAJaIEyMuEMjRwRaHORU7wVJjBDUcpr+kSvdX038vTcNmf/N58C1sRmJyDCba 12 | 7imjjA7D8te+wRugeITG8rDCN8EjAQUTHIA995d7q+DMR5u8e+DSWWD7G+sCAwEA 13 | AQKCAgEAmjtgDC3rt53CA8OjGFhTTJO3B600/O7LTk7tniq0r6iDnFr/iJ2V+Wk5 14 | e2y0YgOZkCpWWwhBdVnRSMi31PU38xafPOiFGk+P+0qFO+n6hL4r6D/vTD7TL8vS 15 | JIK4qE6/1Uy6mqtHdJKC8SwdoSACX1EuIucPoFDwTpUMJFz7GgsgeKBONQp6q2sV 16 | VN2Dgm7FsgD5nVSQe2HhL7V+CtWPmTPpPLpxhzS6zwfRnmPc3YElu7gTntLQgMdU 17 | ufMq8F4PMqSrwchbe0aY2AgPHAUQyrITOskj+msUY8EiccAhkv5k/+lmVpCKl6K3 18 | DKWajqZCUpzznyoa+aZu9TxhUasWlsX+aumeSG1yZTxL0DqL8W+ho719eZ6osF+Z 19 | NieArLR+b8Wgynykn0FfIPfuSeXjbwj1SoKJA3Iffi4ckfjnLgCe7zcqMvJh9r3L 20 | gQErnrl90emEjLKZ/fc2Zzzpo8Momf8DDlBpelvDRiClbpVlcS1dJ82LCfZkgcOu 21 | DqZiuktemcGICtj9Kao7K2iRaFwqjISliyBKREswIZx3KKx+qydrX5sRVVVEdrvL 22 | aunD6eKlvcNzaiO+oicJ5VZMbaPGgw8O6XMUYyis47sXboeeLB/nErlQUk2fSsjg 23 | Ml/NDSP1wbVv4c5o47E2RmIe9jPp5W9TM9O2w0whZz47hk2e52kCggEBAOpYWe6c 24 | XZst6Xk+sNEFJL0sAo5kQo6Q4nEV8zo8R7rCEpfC3Wo5caW5hxl0HklbbcdByEpo 25 | nff9BpB1QzI+ZoZfOx7JzL7kf92zmi4NOl9mJKx7SFjiBWYMCeAi1zW4bWOOcHYH 26 | H8H3L1smPEmHAHF0FEh36q3+8SHIIujX68OH4d8km6A7Rqs/fA+zUl2pB7bTXun6 27 | djGhZmtL7dCGsaIpuy4jJBmF2S+P2Otea9xcQpUtoHkixKmtX+s6CCFVYJOfseRn 28 | FgQ8+FmfkWxSMvVkBDEbluHueTxV0WAGHY9t5v5gjmaV4bE5BUnRWti0DTi+Yv6G 29 | 8r5JCCNFBUSd+w0CggEBAN7+H4Zu5FSO17gOx3KSid+kNDiCREKmSg7JeYu2Fj01 30 | 5L0Y27LjS49use8EkM1BRXYlKM3IASUxDyunEl1RDykBgjDktjHaJlYGy+SxLyAU 31 | Pb84hlDKCaAoi+haq2R/8X3yitWHZRZ9wcqV/6ZGXxwp8ObYO0M4qQ9mgFN2QWMO 32 | DAZsVuPkekb4+X8KpDXOAEqVaUUGv6QcfWMPoUTv0gNV7HLUB8t+5FN5FvfpWdps 33 | R3m1OE71RLLc8ROmCd4mrcTeJ0/xX9rU3Vg1nHkKIhPE1kUHOx/5JjK0d2xRvzo6 34 | iClGov/nQ+ZOdUOwmSyot1oZQdZxsdjUon59hXJhVNcCggEBAJ3uUxVqze7Rv0HV 35 | zQXgqITQzxw1V2GLJQA/Yb8CdvjW5/7c9akr83pTkUfHKM4ZTt91Zs02uhcxr6XV 36 | USbgROoT7dzu9QxFJCM+cg7NHWTDir2vC+/Syv6d6F3kPRNYKmhoy0PPw0hOFN0h 37 | 5IlM8TCEoVLBdigy40D4BwbS1XHd9owSuMsAh7zLOQm/uSejza4ZvyKCh/skoch3 38 | 0FqzG62E4D/UsPR+bwMNt4vQA3LrvneY20gn9U/Apx746hfE2SREAHhEPOM4T8re 39 | rfvJXcxgzi5mQIYc2BmgYE2NcYiC8aTHX9Ck/AgavISmEcpvdWWjcUXFnnw87b4P 40 | OuCmc1UCggEAB+2M/WCP6Zmyfa641I/1KuhRmvi2qI/07hWnppnwrHhwC3EIzpFa 41 | LF1JIjBNXZ7Gc27axoYn1ESsP2cPNhYdrd2J3WckOx2AY2qWcSkDfeZ6U4QU8mZc 42 | dT87yiT/FvGdilbHLNRFeck6zJMMpcK2uu+ayvpBESDAkfWY9l6r8HOdZdUQOcXz 43 | uvY7GJMuR2NtizlIptWYlqMUqiLyHttP4iJ555M2O7vlLjqN4H6BXUygNemFePkr 44 | WhiU0ZgDncxOPUOQrjzspYEP5YTybTM5Bu7ml0mshnR7MS5NWKKHY/CUyrLiNKrq 45 | NRPxA1kR+Q5+hkuDlbYxFn5o0Ipx5iEjvQKCAQEArl+oUVwBjd4OHr0UOcOssxPs 46 | V8KJzMekopzJ3M7Js5ud/AB6nLSBxwuPn/IYrF8n0qjEl6DbIkYy53jLubO3I9S1 47 | PW8DecQH+qCOyX3S+yrtKZiBNZbAa9ySf/QmeAW70vtzCbqFnCKfpLStm+L7LxeS 48 | PRblfuqTRBIfvbFEt29op+0iCZQpGD1cq4gO5lqtCEPQVg2hivgmBtWdZEFs52jL 49 | a0H8f5R/stwByxmMOYK6mkO+W4UPGHoSLgvtvlD0obG2fyqQd1FJVLeiYh6DOYpf 50 | Qn9cbLPVDrWXRMntr0tqZ/y0Zef+E/70VA2yIbibhZ2SRbZFxufaGkMtaI5oPQ== 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # joylive-injector 2 | 3 | [![GitHub repo](https://img.shields.io/badge/GitHub-repo-blue)](https://github.com/jd-opensource/joylive-injector) 4 | [![GitHub release](https://img.shields.io/github/release/jd-opensource/joylive-injector.svg)](https://github.com/jd-opensource/joylive-injector/releases) 5 | [![Slack Status](https://img.shields.io/badge/slack-join_chat-white.svg?logo=slack&style=social)](https://joylivehq.slack.com) 6 | 7 | pic 8 | 9 | English | [简体中文](./README-zh.md) 10 | 11 | ## Description 12 | This is a dynamic admission control webhook for kubernetes, it can be used to mutate kubernetes resources. 13 | This program monitors the `CREATE`, `UPDATE`, `DELETE` events for `deployments` and the `CREATE` events for `pods` and adds the initContainer for `Pod` , adds the environment variable `JAVA_TOOL_OPTIONS` by default, mounts the configmap, modifies the volume load for the main container, and so on. 14 | 15 | ## Features 16 | - Supports automatically injecting `joylive-agent` into Pods of Java applications. 17 | - Supports multi-version `joylive-agent` and corresponding configuration management. 18 | - Support injection of specified version `joylive-agent` and corresponding configuration. 19 | 20 | ## Used 21 | ### Full mode 22 | - Install CFSSL (HTTP API tool for signing, verifying, and bundling TLS certificates) in the environment to be deployed 23 | ```bash 24 | wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 25 | wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 26 | wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 27 | mv cfssl-certinfo_linux-amd64 /usr/local/bin/cfssl-certinfo 28 | mv cfssl_linux-amd64 /usr/local/bin/cfssl 29 | mv cfssljson_linux-amd64 /usr/local/bin/cfssljson 30 | chmod +x /usr/local/bin/cfssl-certinfo /usr/local/bin/cfssl /usr/local/bin/cfssljson 31 | ``` 32 | - Copy `cfssl` and `joylive webhook` from the deploy directory to the environment to be deployed 33 | - The namespace in `cfssl/dac-csr.json` is currently filled in as `joylive` and needs to be modified according to the actual situation 34 | - Execute the `create-secret.sh` script in the `joylive-injector/deploy/cfssl` directory to generate a secret. If the `joylive-injector` package is in the same directory as `cfssl`, it can automatically replace the value of the `caBundle`, `caKeyBundle` and `caPubBundle` field 35 | - If the value of `caBundle`, `caKeyBundle` and `caPubBundle` are not replaced, it is necessary to manually replace the value of the `caBundle`, `caKeyBundle` and `caPubBundle` field in the `value.yaml` in the chart package. Use the content generated by `cat dac-ca.pem | base64 | tr -d '\n'` as `caBundle`, `cat dac-key.pem | base64 | tr -d '\n'` as `caKeyBundle`, `cat dac.pem | base64 | tr -d '\n'` as `caPubBundle` to replace them 36 | - Execute `helm install joylive-injector ./joylive-injector -n joylive` Install webhook 37 | - Modify the configuration in the `value.yaml` section of the chart package as needed 38 | 39 | ### Simple mode 40 | Since the certificate signature has been pre-generated according to the namespace `joylive`, it is necessary to specify installation to the corresponding namespace. Execute the command: 41 | ```bash 42 | helm repo add joylive https://jd-opensource.github.io/joylive-helm-charts 43 | kubectl create namespace joylive 44 | helm install joylive-injector joylive/joylive-injector -n joylive 45 | ``` 46 | 47 | ## Member 48 | 49 | Thank you to all the contributors! [Member List](./community/members.md) -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | education, socio-economic status, nationality, personal appearance, race, 10 | religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at . All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct.html](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html) 72 | 73 | [homepage]: https://www.contributor-covenant.org -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/jd-opensource/joylive-injector 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.22.3 6 | 7 | require ( 8 | github.com/gorilla/mux v1.8.1 9 | github.com/json-iterator/go v1.1.12 10 | github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible 11 | github.com/spf13/cobra v1.8.0 12 | github.com/spf13/viper v1.18.2 13 | github.com/stretchr/testify v1.8.4 14 | go.uber.org/zap v1.27.0 15 | gomodules.xyz/jsonpatch/v2 v2.4.0 16 | gopkg.in/natefinch/lumberjack.v2 v2.2.1 17 | gopkg.in/yaml.v3 v3.0.1 18 | k8s.io/api v0.30.2 19 | k8s.io/apimachinery v0.30.2 20 | k8s.io/client-go v0.30.2 21 | sigs.k8s.io/structured-merge-diff/v4 v4.4.1 22 | ) 23 | 24 | require ( 25 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 26 | github.com/emicklei/go-restful/v3 v3.11.0 // indirect 27 | github.com/evanphx/json-patch v4.12.0+incompatible // indirect 28 | github.com/fsnotify/fsnotify v1.7.0 // indirect 29 | github.com/go-logr/logr v1.4.1 // indirect 30 | github.com/go-openapi/jsonpointer v0.19.6 // indirect 31 | github.com/go-openapi/jsonreference v0.20.2 // indirect 32 | github.com/go-openapi/swag v0.22.3 // indirect 33 | github.com/gogo/protobuf v1.3.2 // indirect 34 | github.com/golang/protobuf v1.5.4 // indirect 35 | github.com/google/gnostic-models v0.6.8 // indirect 36 | github.com/google/go-cmp v0.6.0 // indirect 37 | github.com/google/gofuzz v1.2.0 // indirect 38 | github.com/google/uuid v1.4.0 // indirect 39 | github.com/hashicorp/hcl v1.0.0 // indirect 40 | github.com/imdario/mergo v0.3.6 // indirect 41 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 42 | github.com/jonboulle/clockwork v0.4.0 // indirect 43 | github.com/josharian/intern v1.0.0 // indirect 44 | github.com/lestrrat-go/strftime v1.0.6 // indirect 45 | github.com/magiconair/properties v1.8.7 // indirect 46 | github.com/mailru/easyjson v0.7.7 // indirect 47 | github.com/mitchellh/mapstructure v1.5.0 // indirect 48 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 49 | github.com/modern-go/reflect2 v1.0.2 // indirect 50 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 51 | github.com/pelletier/go-toml/v2 v2.1.0 // indirect 52 | github.com/pkg/errors v0.9.1 // indirect 53 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 54 | github.com/sagikazarmark/locafero v0.4.0 // indirect 55 | github.com/sagikazarmark/slog-shim v0.1.0 // indirect 56 | github.com/sourcegraph/conc v0.3.0 // indirect 57 | github.com/spf13/afero v1.11.0 // indirect 58 | github.com/spf13/cast v1.6.0 // indirect 59 | github.com/spf13/pflag v1.0.5 // indirect 60 | github.com/subosito/gotenv v1.6.0 // indirect 61 | go.uber.org/multierr v1.10.0 // indirect 62 | golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect 63 | golang.org/x/net v0.23.0 // indirect 64 | golang.org/x/oauth2 v0.15.0 // indirect 65 | golang.org/x/sys v0.18.0 // indirect 66 | golang.org/x/term v0.18.0 // indirect 67 | golang.org/x/text v0.14.0 // indirect 68 | golang.org/x/time v0.5.0 // indirect 69 | google.golang.org/appengine v1.6.7 // indirect 70 | google.golang.org/protobuf v1.33.0 // indirect 71 | gopkg.in/inf.v0 v0.9.1 // indirect 72 | gopkg.in/ini.v1 v1.67.0 // indirect 73 | gopkg.in/yaml.v2 v2.4.0 // indirect 74 | k8s.io/klog/v2 v2.120.1 // indirect 75 | k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect 76 | k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect 77 | sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect 78 | sigs.k8s.io/yaml v1.3.0 // indirect 79 | ) 80 | -------------------------------------------------------------------------------- /deploy/joylive-injector/config/bootstrap.properties: -------------------------------------------------------------------------------- 1 | app.name=${APPLICATION_NAME} 2 | app.service.name=${APPLICATION_SERVICE_NAME:${CONFIG_NACOS_SERVICE:${APPLICATION_NAME}}} 3 | app.service.namespace=${APPLICATION_SERVICE_NAMESPACE} 4 | app.service.group=${APPLICATION_SERVICE_GROUP} 5 | app.service.catalog=${APPLICATION_SERVICE_CATALOG:${REGISTRY_GROUP:${CONFIG_NACOS_GROUP}}} 6 | app.service.gateway=${APPLICATION_SERVICE_GATEWAY:NONE} 7 | app.service.port=${APPLICATION_SERVICE_PORT} 8 | app.service.weight=${APPLICATION_SERVICE_WEIGHT:100} 9 | app.service.warmupDuration=${APPLICATION_SERVICE_WARMUP_DURATION} 10 | app.service.meta=${APPLICATION_SERVICE_META:} 11 | app.location.cloud=${APPLICATION_LOCATION_CLOUD:{{ .Values.agent.location.cloud }}} 12 | app.location.region=${APPLICATION_LOCATION_REGION:{{ .Values.agent.location.region }}} 13 | app.location.zone=${APPLICATION_LOCATION_ZONE:${NODE_ZONE:{{ .Values.agent.location.zone }}}} 14 | app.location.cluster=${APPLICATION_LOCATION_CLUSTER:{{ .Values.agent.location.cluster }}} 15 | app.location.liveSpaceId=${APPLICATION_LOCATION_LIVESPACE_ID:${CONFIG_LIVESPACE_ID:{{ .Values.agent.location.liveSpaceId }}}} 16 | app.location.unitRuleId=${APPLICATION_LOCATION_UNIT_RULE_ID} 17 | app.location.unit=${APPLICATION_LOCATION_UNIT:{{ .Values.agent.location.unit }}} 18 | app.location.cell=${APPLICATION_LOCATION_CELL:{{ .Values.agent.location.cell }}} 19 | app.location.laneSpaceId=${APPLICATION_LOCATION_LANESPACE_ID:{{ .Values.agent.location.laneSpaceId }}} 20 | app.location.lane=${APPLICATION_LOCATION_LANE:{{ .Values.agent.location.lane }}} 21 | agent.enhance.shutdownOnError=${CONFIG_ENHANCE_SHUTDOWN_ON_ERROR:true} 22 | agent.enhance.excludeApp=${CONFIG_ENHANCE_EXCLUDE_APPS:com.taobao.arthas.boot.Bootstrap,org.netbeans.Main,com.jdk.JDKVersionHelper,com.tongweb.srv.commons.utils.*} 23 | classloader.core.config.prefixes=yaml,yml,xml,json,properties 24 | classloader.core.parent.prefixes=com.jd.live.agent.bootstrap. 25 | classloader.core.self.prefixes=com.jd.live.agent.core.,com.jd.live.agent.governance. 26 | classloader.core.isolation.prefixes=META-INF/services/com.jd.live.agent 27 | classloader.coreImpl.parent.prefixes=com.jd.live.agent.bootstrap.,com.jd.live.agent.core.,com.jd.live.agent.governance. 28 | classloader.coreImpl.self.prefixes=com.jd.live.agent.implement.,com.jd.live.agent.shaded. 29 | classloader.plugin.parent.prefixes=com.jd.live.agent.bootstrap.,com.jd.live.agent.core.,com.jd.live.agent.governance. 30 | classloader.plugin.self.prefixes=com.jd.live.agent.plugin. 31 | classloader.essential.inclusion.prefixes=com.jd.live.agent. 32 | classloader.essential.exclusion.prefixes=com.jd.live.agent.demo. 33 | classloader.essential.bootstrap.prefixes=com.jd.live.agent.bootstrap. 34 | cipher.enabled=${CONFIG_CIPHER_ENABLED:false} 35 | cipher.cipher=${CONFIG_CIPHER_TYPE:StandardPBE} 36 | cipher.algorithm=${CONFIG_CIPHER_ALGORITHM:PBEWITHHMACSHA512ANDAES_256} 37 | cipher.provider=${CONFIG_CIPHER_PROVIDER} 38 | cipher.password=${CONFIG_CIPHER_PASSWORD:${jasypt.encryptor.password}} 39 | cipher.codec=${CONFIG_CIPHER_CODEC:base64} 40 | cipher.prefix=${CONFIG_CIPHER_PREFIX:ENC(} 41 | cipher.suffix=${CONFIG_CIPHER_SUFFIX:)} 42 | cipher.saltType=${CONFIG_CIPHER_SALT_TYPE:base64} 43 | cipher.salt=${CONFIG_CIPHER_SALT} 44 | cipher.saltSize=${CONFIG_CIPHER_SALT_SIZE:16} 45 | cipher.ivType=${CONFIG_CIPHER_IV_TYPE} 46 | cipher.iv=${CONFIG_CIPHER_IV} 47 | cipher.ivSize=${CONFIG_CIPHER_IV_SIZE:16} 48 | cipher.iterations=${CONFIG_CIPHER_ITERATIONS:1000} 49 | cipher.properties=${CONFIG_CIPHER_PROPERTIES} 50 | env.http.url=${CONFIG_ENV_HTTP_URL} 51 | env.http.parameters=${CONFIG_ENV_HTTP_PARAMETERS} 52 | -------------------------------------------------------------------------------- /client-go/clientset/versioned/typed/injector/v1/injector_client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 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 | "net/http" 23 | 24 | v1 "github.com/jd-opensource/joylive-injector/client-go/apis/injector/v1" 25 | "github.com/jd-opensource/joylive-injector/client-go/clientset/versioned/scheme" 26 | rest "k8s.io/client-go/rest" 27 | ) 28 | 29 | type InjectorV1Interface interface { 30 | RESTClient() rest.Interface 31 | AgentVersionsGetter 32 | } 33 | 34 | // InjectorV1Client is used to interact with features provided by the injector.joylive.io group. 35 | type InjectorV1Client struct { 36 | restClient rest.Interface 37 | } 38 | 39 | func (c *InjectorV1Client) AgentVersions(namespace string) AgentVersionInterface { 40 | return newAgentVersions(c, namespace) 41 | } 42 | 43 | // NewForConfig creates a new InjectorV1Client for the given config. 44 | // NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), 45 | // where httpClient was generated with rest.HTTPClientFor(c). 46 | func NewForConfig(c *rest.Config) (*InjectorV1Client, error) { 47 | config := *c 48 | if err := setConfigDefaults(&config); err != nil { 49 | return nil, err 50 | } 51 | httpClient, err := rest.HTTPClientFor(&config) 52 | if err != nil { 53 | return nil, err 54 | } 55 | return NewForConfigAndClient(&config, httpClient) 56 | } 57 | 58 | // NewForConfigAndClient creates a new InjectorV1Client for the given config and http client. 59 | // Note the http client provided takes precedence over the configured transport values. 60 | func NewForConfigAndClient(c *rest.Config, h *http.Client) (*InjectorV1Client, error) { 61 | config := *c 62 | if err := setConfigDefaults(&config); err != nil { 63 | return nil, err 64 | } 65 | client, err := rest.RESTClientForConfigAndClient(&config, h) 66 | if err != nil { 67 | return nil, err 68 | } 69 | return &InjectorV1Client{client}, nil 70 | } 71 | 72 | // NewForConfigOrDie creates a new InjectorV1Client for the given config and 73 | // panics if there is an error in the config. 74 | func NewForConfigOrDie(c *rest.Config) *InjectorV1Client { 75 | client, err := NewForConfig(c) 76 | if err != nil { 77 | panic(err) 78 | } 79 | return client 80 | } 81 | 82 | // New creates a new InjectorV1Client for the given RESTClient. 83 | func New(c rest.Interface) *InjectorV1Client { 84 | return &InjectorV1Client{c} 85 | } 86 | 87 | func setConfigDefaults(config *rest.Config) error { 88 | gv := v1.SchemeGroupVersion 89 | config.GroupVersion = &gv 90 | config.APIPath = "/apis" 91 | config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() 92 | 93 | if config.UserAgent == "" { 94 | config.UserAgent = rest.DefaultKubernetesUserAgent() 95 | } 96 | 97 | return nil 98 | } 99 | 100 | // RESTClient returns a RESTClient that is used to communicate 101 | // with API server by this client implementation. 102 | func (c *InjectorV1Client) RESTClient() rest.Interface { 103 | if c == nil { 104 | return nil 105 | } 106 | return c.restClient 107 | } 108 | -------------------------------------------------------------------------------- /client-go/apis/injector/v1/zz_generated.deepcopy.go: -------------------------------------------------------------------------------- 1 | //go:build !ignore_autogenerated 2 | // +build !ignore_autogenerated 3 | 4 | /* 5 | Copyright 2024. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | */ 19 | 20 | // Code generated by deepcopy-gen. DO NOT EDIT. 21 | 22 | package v1 23 | 24 | import ( 25 | runtime "k8s.io/apimachinery/pkg/runtime" 26 | ) 27 | 28 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 29 | func (in *AgentVersion) DeepCopyInto(out *AgentVersion) { 30 | *out = *in 31 | out.TypeMeta = in.TypeMeta 32 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 33 | out.Spec = in.Spec 34 | out.Status = in.Status 35 | return 36 | } 37 | 38 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgentVersion. 39 | func (in *AgentVersion) DeepCopy() *AgentVersion { 40 | if in == nil { 41 | return nil 42 | } 43 | out := new(AgentVersion) 44 | in.DeepCopyInto(out) 45 | return out 46 | } 47 | 48 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 49 | func (in *AgentVersion) DeepCopyObject() runtime.Object { 50 | if c := in.DeepCopy(); c != nil { 51 | return c 52 | } 53 | return nil 54 | } 55 | 56 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 57 | func (in *AgentVersionList) DeepCopyInto(out *AgentVersionList) { 58 | *out = *in 59 | out.TypeMeta = in.TypeMeta 60 | in.ListMeta.DeepCopyInto(&out.ListMeta) 61 | if in.Items != nil { 62 | in, out := &in.Items, &out.Items 63 | *out = make([]AgentVersion, len(*in)) 64 | for i := range *in { 65 | (*in)[i].DeepCopyInto(&(*out)[i]) 66 | } 67 | } 68 | return 69 | } 70 | 71 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgentVersionList. 72 | func (in *AgentVersionList) DeepCopy() *AgentVersionList { 73 | if in == nil { 74 | return nil 75 | } 76 | out := new(AgentVersionList) 77 | in.DeepCopyInto(out) 78 | return out 79 | } 80 | 81 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 82 | func (in *AgentVersionList) DeepCopyObject() runtime.Object { 83 | if c := in.DeepCopy(); c != nil { 84 | return c 85 | } 86 | return nil 87 | } 88 | 89 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 90 | func (in *AgentVersionSpec) DeepCopyInto(out *AgentVersionSpec) { 91 | *out = *in 92 | return 93 | } 94 | 95 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgentVersionSpec. 96 | func (in *AgentVersionSpec) DeepCopy() *AgentVersionSpec { 97 | if in == nil { 98 | return nil 99 | } 100 | out := new(AgentVersionSpec) 101 | in.DeepCopyInto(out) 102 | return out 103 | } 104 | 105 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 106 | func (in *AgentVersionStatus) DeepCopyInto(out *AgentVersionStatus) { 107 | *out = *in 108 | return 109 | } 110 | 111 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgentVersionStatus. 112 | func (in *AgentVersionStatus) DeepCopy() *AgentVersionStatus { 113 | if in == nil { 114 | return nil 115 | } 116 | out := new(AgentVersionStatus) 117 | in.DeepCopyInto(out) 118 | return out 119 | } 120 | -------------------------------------------------------------------------------- /client-go/listers/injector/v1/agentversion.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by lister-gen. DO NOT EDIT. 18 | 19 | package v1 20 | 21 | import ( 22 | v1 "github.com/jd-opensource/joylive-injector/client-go/apis/injector/v1" 23 | "k8s.io/apimachinery/pkg/api/errors" 24 | "k8s.io/apimachinery/pkg/labels" 25 | "k8s.io/client-go/tools/cache" 26 | ) 27 | 28 | // AgentVersionLister helps list AgentVersions. 29 | // All objects returned here must be treated as read-only. 30 | type AgentVersionLister interface { 31 | // List lists all AgentVersions in the indexer. 32 | // Objects returned here must be treated as read-only. 33 | List(selector labels.Selector) (ret []*v1.AgentVersion, err error) 34 | // AgentVersions returns an object that can list and get AgentVersions. 35 | AgentVersions(namespace string) AgentVersionNamespaceLister 36 | AgentVersionListerExpansion 37 | } 38 | 39 | // agentVersionLister implements the AgentVersionLister interface. 40 | type agentVersionLister struct { 41 | indexer cache.Indexer 42 | } 43 | 44 | // NewAgentVersionLister returns a new AgentVersionLister. 45 | func NewAgentVersionLister(indexer cache.Indexer) AgentVersionLister { 46 | return &agentVersionLister{indexer: indexer} 47 | } 48 | 49 | // List lists all AgentVersions in the indexer. 50 | func (s *agentVersionLister) List(selector labels.Selector) (ret []*v1.AgentVersion, err error) { 51 | err = cache.ListAll(s.indexer, selector, func(m interface{}) { 52 | ret = append(ret, m.(*v1.AgentVersion)) 53 | }) 54 | return ret, err 55 | } 56 | 57 | // AgentVersions returns an object that can list and get AgentVersions. 58 | func (s *agentVersionLister) AgentVersions(namespace string) AgentVersionNamespaceLister { 59 | return agentVersionNamespaceLister{indexer: s.indexer, namespace: namespace} 60 | } 61 | 62 | // AgentVersionNamespaceLister helps list and get AgentVersions. 63 | // All objects returned here must be treated as read-only. 64 | type AgentVersionNamespaceLister interface { 65 | // List lists all AgentVersions in the indexer for a given namespace. 66 | // Objects returned here must be treated as read-only. 67 | List(selector labels.Selector) (ret []*v1.AgentVersion, err error) 68 | // Get retrieves the AgentVersion from the indexer for a given namespace and name. 69 | // Objects returned here must be treated as read-only. 70 | Get(name string) (*v1.AgentVersion, error) 71 | AgentVersionNamespaceListerExpansion 72 | } 73 | 74 | // agentVersionNamespaceLister implements the AgentVersionNamespaceLister 75 | // interface. 76 | type agentVersionNamespaceLister struct { 77 | indexer cache.Indexer 78 | namespace string 79 | } 80 | 81 | // List lists all AgentVersions in the indexer for a given namespace. 82 | func (s agentVersionNamespaceLister) List(selector labels.Selector) (ret []*v1.AgentVersion, err error) { 83 | err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { 84 | ret = append(ret, m.(*v1.AgentVersion)) 85 | }) 86 | return ret, err 87 | } 88 | 89 | // Get retrieves the AgentVersion from the indexer for a given namespace and name. 90 | func (s agentVersionNamespaceLister) Get(name string) (*v1.AgentVersion, error) { 91 | obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) 92 | if err != nil { 93 | return nil, err 94 | } 95 | if !exists { 96 | return nil, errors.NewNotFound(v1.Resource("agentversion"), name) 97 | } 98 | return obj.(*v1.AgentVersion), nil 99 | } 100 | -------------------------------------------------------------------------------- /client-go/informers/externalversions/injector/v1/agentversion.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package v1 20 | 21 | import ( 22 | "context" 23 | time "time" 24 | 25 | injectorv1 "github.com/jd-opensource/joylive-injector/client-go/apis/injector/v1" 26 | versioned "github.com/jd-opensource/joylive-injector/client-go/clientset/versioned" 27 | internalinterfaces "github.com/jd-opensource/joylive-injector/client-go/informers/externalversions/internalinterfaces" 28 | v1 "github.com/jd-opensource/joylive-injector/client-go/listers/injector/v1" 29 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 | runtime "k8s.io/apimachinery/pkg/runtime" 31 | watch "k8s.io/apimachinery/pkg/watch" 32 | cache "k8s.io/client-go/tools/cache" 33 | ) 34 | 35 | // AgentVersionInformer provides access to a shared informer and lister for 36 | // AgentVersions. 37 | type AgentVersionInformer interface { 38 | Informer() cache.SharedIndexInformer 39 | Lister() v1.AgentVersionLister 40 | } 41 | 42 | type agentVersionInformer struct { 43 | factory internalinterfaces.SharedInformerFactory 44 | tweakListOptions internalinterfaces.TweakListOptionsFunc 45 | namespace string 46 | } 47 | 48 | // NewAgentVersionInformer constructs a new informer for AgentVersion type. 49 | // Always prefer using an informer factory to get a shared informer instead of getting an independent 50 | // one. This reduces memory footprint and number of connections to the server. 51 | func NewAgentVersionInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { 52 | return NewFilteredAgentVersionInformer(client, namespace, resyncPeriod, indexers, nil) 53 | } 54 | 55 | // NewFilteredAgentVersionInformer constructs a new informer for AgentVersion type. 56 | // Always prefer using an informer factory to get a shared informer instead of getting an independent 57 | // one. This reduces memory footprint and number of connections to the server. 58 | func NewFilteredAgentVersionInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { 59 | return cache.NewSharedIndexInformer( 60 | &cache.ListWatch{ 61 | ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { 62 | if tweakListOptions != nil { 63 | tweakListOptions(&options) 64 | } 65 | return client.InjectorV1().AgentVersions(namespace).List(context.TODO(), options) 66 | }, 67 | WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { 68 | if tweakListOptions != nil { 69 | tweakListOptions(&options) 70 | } 71 | return client.InjectorV1().AgentVersions(namespace).Watch(context.TODO(), options) 72 | }, 73 | }, 74 | &injectorv1.AgentVersion{}, 75 | resyncPeriod, 76 | indexers, 77 | ) 78 | } 79 | 80 | func (f *agentVersionInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { 81 | return NewFilteredAgentVersionInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) 82 | } 83 | 84 | func (f *agentVersionInformer) Informer() cache.SharedIndexInformer { 85 | return f.factory.InformerFor(&injectorv1.AgentVersion{}, f.defaultInformer) 86 | } 87 | 88 | func (f *agentVersionInformer) Lister() v1.AgentVersionLister { 89 | return v1.NewAgentVersionLister(f.Informer().GetIndexer()) 90 | } 91 | -------------------------------------------------------------------------------- /community/membership.md: -------------------------------------------------------------------------------- 1 | 2 | | Role | Responsibilities | Requirements | 3 | | -----| ---------------- | ------------ | 4 | | Contributor | Participate in the community. | Finish at least one contribution to the project in specific repos. | 5 | | Active Contributor | Actively contribute to the community. | Have 5 merged PRs or fixed major bugs.| 6 | | Committer | Accept and approve contributions. | Have a proven track record of contributions and lead major development. Nominees must be approved by a minimum of two votes (excluding the nominator's vote) from the TSC(Technical Steering Committee) members.| 7 | | Maintainer | Make and approve technical design decisions. Define milestones and releases. Nominate new SIG committers.| Highly experienced committer. Nominees must be approved by a minimum of two votes (excluding the nominator's vote) from the TSC members.| 8 | 9 | **Note:** This document is a work in progress. 10 | This doc outlines the various responsibilities of contributor roles in JoyLive. 11 | 12 | ## Contributor 13 | 14 | Everyone who contributes can become a JoyLive contributor. The members will provide mentorship and guidance when new contributors need assistance. 15 | 16 | ### How to become a Contributor? 17 | 18 | - 1 merged PR (pull request) in JoyLive's repositories 19 | 20 | ### As a Contributor, we expect you to: 21 | 22 | - Actively participate in the development of the JoyLive project 23 | - Attend community events such as meetups and hackathons 24 | - Continuously learn about JoyLive-related technologies and share your knowledge with others 25 | 26 | ### Privileges 27 | 28 | - Be recognized as a JoyLive Contributor 29 | 30 | ## Active Contributor 31 | 32 | Active contributors are contributors who have made outstanding contributions and sustained commitment to JoyLive. They actively participate in the community by contributing code, improving docs and helping others. 33 | 34 | ### How to become an Active Contributor? 35 | 36 | - Have at least 5 merged PRs or have fixed major bugs 37 | - Participate in at least 5 code reviews 38 | - Actively engage with the community by attending online or offline meetups and participating in community discussions 39 | 40 | ### Responsibilities and privileges 41 | 42 | - Join the community meeting and discussion 43 | - Mentor and guide new contributors 44 | - Be recognized as a JoyLive Active Contributor 45 | 46 | ## Committer 47 | 48 | Committers are Contributors who have earned the ability to modify ("commit") source code, documentation or other technical artifacts in a project’s repository. 49 | 50 | ### How to become a Committer? 51 | 52 | - Have a deep understanding of JoyLive's principles and future plans 53 | - Have the ability to deal with various issues that arise in the project promptly 54 | - Lead at least one major development, write and revise related documents 55 | - A contributor may become a Committer by a minimum of two votes (excluding the nominator's vote) from the TSC members. 56 | 57 | ### Responsibilities and privileges 58 | 59 | - Mentor and guide other members in the community 60 | - Ensure continued health of subproject 61 | - Be granted write access to JoyLive repos (to be specified) 62 | - Be recognized as a JoyLive Committer 63 | 64 | ## Maintainer 65 | 66 | Maintainers are a subset of Committers with additional responsibilities for driving a project’s release and serving on the TSC (optional). 67 | 68 | ### How to become a Maintainer? 69 | 70 | - In-depth understanding of JoyLive principles and a clear understanding of JoyLive's future plans 71 | - Have the ability to deal with project issues promptly 72 | - Lead project development and iterations, and steer the overall direction of the project 73 | - A Committer may become a Maintainer by a minimum of two votes (excluding the nominator's vote) from the TSC members. 74 | 75 | ### Responsibilities and privileges 76 | 77 | - Mentor and guide other members in the community 78 | - Ensure continued health of the project, such as code quality and test coverage 79 | - Make and approve technical design decisions 80 | - Define milestones and releases 81 | - Nominate new SIG committers to TSC 82 | - Be recognized as a JoyLive Maintainer -------------------------------------------------------------------------------- /pkg/resource/resource.go: -------------------------------------------------------------------------------- 1 | package resource 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "github.com/jd-opensource/joylive-injector/pkg/log" 7 | "go.uber.org/zap" 8 | corev1 "k8s.io/api/core/v1" 9 | errors2 "k8s.io/apimachinery/pkg/api/errors" 10 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 | "k8s.io/client-go/kubernetes" 12 | "k8s.io/client-go/rest" 13 | "k8s.io/client-go/tools/clientcmd" 14 | "k8s.io/client-go/util/homedir" 15 | "os" 16 | "path/filepath" 17 | "sync" 18 | ) 19 | 20 | var globalRes *Resource 21 | var initOnce sync.Once 22 | 23 | type Resource struct { 24 | RestConfig *rest.Config 25 | ClientSet *kubernetes.Clientset 26 | } 27 | 28 | func initResource() *Resource { 29 | initOnce.Do(func() { 30 | var err error 31 | globalRes = &Resource{} 32 | var kubeconfig string 33 | var config *rest.Config 34 | 35 | kubeconfig = os.Getenv("KUBECONFIG") 36 | 37 | if kubeconfig == "" { 38 | if home := homedir.HomeDir(); home != "" { 39 | kubeconfig = filepath.Join(home, ".kube", "config") 40 | } 41 | } 42 | 43 | if kubeconfig != "" { 44 | config, err = clientcmd.BuildConfigFromFlags("", kubeconfig) 45 | if err != nil { 46 | log.Warn("failed to build restConfig from kubeconfig: ", zap.Error(err)) 47 | } 48 | } 49 | 50 | if config == nil { 51 | config, err = rest.InClusterConfig() 52 | if err != nil { 53 | log.Fatalf("init k8s config error: %v", err) 54 | } 55 | } 56 | 57 | globalRes.RestConfig = config 58 | globalRes.ClientSet, err = kubernetes.NewForConfig(config) 59 | if err != nil { 60 | log.Fatalf("init k8s client error: %v", err) 61 | } 62 | }) 63 | 64 | return globalRes 65 | } 66 | 67 | func GetResource() *Resource { 68 | if globalRes == nil { 69 | return initResource() 70 | } 71 | return globalRes 72 | } 73 | 74 | func (r *Resource) CreateOrUpdateConfigMap(ctx context.Context, namespace string, configMap *corev1.ConfigMap) error { 75 | cm, err := r.ClientSet.CoreV1().ConfigMaps(namespace).Get(ctx, configMap.Name, metav1.GetOptions{}) 76 | if err != nil && !errors2.IsNotFound(err) { 77 | return err 78 | } 79 | if cm == nil || errors2.IsNotFound(err) { 80 | // create 81 | log.Debug("create configMap", zap.String("name", configMap.Name), zap.String("namespace", namespace)) 82 | _, err = r.ClientSet.CoreV1().ConfigMaps(namespace).Create(ctx, configMap, metav1.CreateOptions{}) 83 | if err != nil { 84 | return err 85 | } 86 | } else { 87 | log.Debug("update configMap", zap.String("name", configMap.Name), zap.String("namespace", namespace), zap.Any("data", configMap.Data)) 88 | cm.Data = configMap.Data 89 | _, err = r.ClientSet.CoreV1().ConfigMaps(namespace).Update(ctx, cm, metav1.UpdateOptions{}) 90 | if err != nil { 91 | return err 92 | } 93 | } 94 | return nil 95 | } 96 | 97 | func (r *Resource) DeleteConfigMap(ctx context.Context, namespace, name string) error { 98 | err := r.ClientSet.CoreV1().ConfigMaps(namespace).Delete(ctx, name, metav1.DeleteOptions{}) 99 | if err != nil { 100 | return err 101 | } 102 | return nil 103 | } 104 | 105 | func (r *Resource) GetDeploymentName(pod *corev1.Pod, namespace string) (string, error) { 106 | // Find ReplicaSet (OwnerReferences) belonging to Deployment 107 | for _, ownerReference := range pod.OwnerReferences { 108 | if ownerReference.Kind == "ReplicaSet" { 109 | // Get ReplicaSet 110 | rs, err := r.ClientSet.AppsV1().ReplicaSets(namespace).Get(context.TODO(), ownerReference.Name, metav1.GetOptions{}) 111 | if err != nil { 112 | return "", err 113 | } 114 | // Find OwnerReferences belonging to Deployment 115 | for _, rsOwnerReference := range rs.OwnerReferences { 116 | if rsOwnerReference.Kind == "Deployment" { 117 | return rsOwnerReference.Name, nil 118 | } 119 | } 120 | } 121 | } 122 | return "", errors.New("no corresponding resources found") 123 | } 124 | 125 | func (r *Resource) GetNodes() (*corev1.NodeList, error) { 126 | nodeList, err := r.ClientSet.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) 127 | if err != nil { 128 | return nil, err 129 | } 130 | return nodeList, nil 131 | } 132 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "crypto/tls" 6 | "errors" 7 | "fmt" 8 | "net/http" 9 | "os" 10 | "os/signal" 11 | "runtime" 12 | "sync" 13 | "syscall" 14 | 15 | "github.com/jd-opensource/joylive-injector/pkg/log" 16 | "go.uber.org/zap" 17 | 18 | "github.com/jd-opensource/joylive-injector/pkg/admission" 19 | _ "github.com/jd-opensource/joylive-injector/pkg/apm" 20 | _ "github.com/jd-opensource/joylive-injector/pkg/mutation" 21 | _ "github.com/jd-opensource/joylive-injector/pkg/watcher" 22 | 23 | "github.com/jd-opensource/joylive-injector/pkg/config" 24 | 25 | "github.com/jd-opensource/joylive-injector/pkg/route" 26 | "github.com/spf13/cobra" 27 | ) 28 | 29 | var ( 30 | buildDate string 31 | buildCommit string 32 | 33 | versionTpl = ` 34 | Name: joylive-injector 35 | Arch: %s 36 | BuildDate: %s 37 | BuildCommit: %s 38 | ` 39 | ) 40 | 41 | var rootCmd = &cobra.Command{ 42 | Use: "joylive-injector", 43 | Short: "JoyLive dynamic admission control tool", 44 | Version: buildCommit, 45 | Run: func(cmd *cobra.Command, args []string) { 46 | log.InitLog() 47 | admission.Setup() 48 | route.Setup() 49 | 50 | srv := &http.Server{ 51 | Handler: route.Router(), 52 | Addr: config.Addr, 53 | TLSConfig: &tls.Config{ 54 | MinVersion: tls.VersionTLS12, 55 | CipherSuites: []uint16{ 56 | tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 57 | tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 58 | tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 59 | tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 60 | tls.TLS_RSA_WITH_AES_128_GCM_SHA256, 61 | tls.TLS_RSA_WITH_AES_256_GCM_SHA384, 62 | }, 63 | }, 64 | } 65 | 66 | sigs := make(chan os.Signal) 67 | signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) 68 | 69 | go func() { 70 | var shutdownOnce sync.Once 71 | for range sigs { 72 | log.Warn("Receiving the termination signal, graceful shutdown...") 73 | shutdownOnce.Do(func() { 74 | err := srv.Shutdown(context.Background()) 75 | if err != nil { 76 | log.Error("Error:", zap.Error(err)) 77 | } 78 | }) 79 | } 80 | }() 81 | 82 | if config.Cert != "" && config.Key != "" { 83 | log.Infof("Listen TLS Server at %s", config.Addr) 84 | err := srv.ListenAndServeTLS(config.Cert, config.Key) 85 | if err != nil { 86 | if errors.Is(err, http.ErrServerClosed) { 87 | log.Info("server shutdown success.") 88 | } else { 89 | log.Error("Error:", zap.Error(err)) 90 | } 91 | } 92 | } else { 93 | log.Infof("Listen HTTP Server at %s", config.Addr) 94 | err := srv.ListenAndServe() 95 | if err != nil { 96 | if errors.Is(err, http.ErrServerClosed) { 97 | log.Info("server shutdown success.") 98 | } else { 99 | log.Error("Error:", zap.Error(err)) 100 | } 101 | } 102 | } 103 | }, 104 | } 105 | 106 | func init() { 107 | // version template 108 | rootCmd.SetVersionTemplate(fmt.Sprintf(versionTpl, runtime.GOOS+"/"+runtime.GOARCH, buildDate, buildCommit)) 109 | 110 | // webhook 111 | rootCmd.PersistentFlags().StringVarP(&config.Addr, "listen", "l", ":443", "Admission Controller listen address") 112 | rootCmd.PersistentFlags().StringVar(&config.Cert, "cert", "", "Admission Controller TLS cert") 113 | rootCmd.PersistentFlags().StringVar(&config.Key, "key", "", "Admission Controller TLS cert key") 114 | rootCmd.PersistentFlags().StringVar(&config.MatchLabels, "match-label", config.MatchLabels, "Match label") 115 | rootCmd.PersistentFlags().StringVar(&config.ControlPlaneUrl, "control-plane-url", config.ControlPlaneUrl, "Control Plane URL") 116 | rootCmd.PersistentFlags().StringVar(&config.FilterSensitive, "filter-sensitive", config.FilterSensitive, "Filter Sensitive Switch") 117 | rootCmd.PersistentFlags().StringVar(&config.ClusterId, "cluster-id", config.ClusterId, "Cluster ID") 118 | //rootCmd.PersistentFlags().StringVar(&config.KubeConfig, "kubeconfig", config.KubeConfig, "Path to the kubeconfig file") 119 | 120 | // admission config 121 | rootCmd.PersistentFlags().StringVar(&config.InitContainerName, "init-container-name", "joylive-init-container", "Init container name") 122 | } 123 | 124 | func main() { 125 | if err := rootCmd.Execute(); err != nil { 126 | log.Fatal("Start error.", zap.Error(err)) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /client-go/clientset/versioned/clientset.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package versioned 20 | 21 | import ( 22 | "fmt" 23 | "net/http" 24 | 25 | injectorv1 "github.com/jd-opensource/joylive-injector/client-go/clientset/versioned/typed/injector/v1" 26 | discovery "k8s.io/client-go/discovery" 27 | rest "k8s.io/client-go/rest" 28 | flowcontrol "k8s.io/client-go/util/flowcontrol" 29 | ) 30 | 31 | type Interface interface { 32 | Discovery() discovery.DiscoveryInterface 33 | InjectorV1() injectorv1.InjectorV1Interface 34 | } 35 | 36 | // Clientset contains the clients for groups. 37 | type Clientset struct { 38 | *discovery.DiscoveryClient 39 | injectorV1 *injectorv1.InjectorV1Client 40 | } 41 | 42 | // InjectorV1 retrieves the InjectorV1Client 43 | func (c *Clientset) InjectorV1() injectorv1.InjectorV1Interface { 44 | return c.injectorV1 45 | } 46 | 47 | // Discovery retrieves the DiscoveryClient 48 | func (c *Clientset) Discovery() discovery.DiscoveryInterface { 49 | if c == nil { 50 | return nil 51 | } 52 | return c.DiscoveryClient 53 | } 54 | 55 | // NewForConfig creates a new Clientset for the given config. 56 | // If config's RateLimiter is not set and QPS and Burst are acceptable, 57 | // NewForConfig will generate a rate-limiter in configShallowCopy. 58 | // NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), 59 | // where httpClient was generated with rest.HTTPClientFor(c). 60 | func NewForConfig(c *rest.Config) (*Clientset, error) { 61 | configShallowCopy := *c 62 | 63 | if configShallowCopy.UserAgent == "" { 64 | configShallowCopy.UserAgent = rest.DefaultKubernetesUserAgent() 65 | } 66 | 67 | // share the transport between all clients 68 | httpClient, err := rest.HTTPClientFor(&configShallowCopy) 69 | if err != nil { 70 | return nil, err 71 | } 72 | 73 | return NewForConfigAndClient(&configShallowCopy, httpClient) 74 | } 75 | 76 | // NewForConfigAndClient creates a new Clientset for the given config and http client. 77 | // Note the http client provided takes precedence over the configured transport values. 78 | // If config's RateLimiter is not set and QPS and Burst are acceptable, 79 | // NewForConfigAndClient will generate a rate-limiter in configShallowCopy. 80 | func NewForConfigAndClient(c *rest.Config, httpClient *http.Client) (*Clientset, error) { 81 | configShallowCopy := *c 82 | if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { 83 | if configShallowCopy.Burst <= 0 { 84 | return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0") 85 | } 86 | configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) 87 | } 88 | 89 | var cs Clientset 90 | var err error 91 | cs.injectorV1, err = injectorv1.NewForConfigAndClient(&configShallowCopy, httpClient) 92 | if err != nil { 93 | return nil, err 94 | } 95 | 96 | cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfigAndClient(&configShallowCopy, httpClient) 97 | if err != nil { 98 | return nil, err 99 | } 100 | return &cs, nil 101 | } 102 | 103 | // NewForConfigOrDie creates a new Clientset for the given config and 104 | // panics if there is an error in the config. 105 | func NewForConfigOrDie(c *rest.Config) *Clientset { 106 | cs, err := NewForConfig(c) 107 | if err != nil { 108 | panic(err) 109 | } 110 | return cs 111 | } 112 | 113 | // New creates a new Clientset for the given RESTClient. 114 | func New(c rest.Interface) *Clientset { 115 | var cs Clientset 116 | cs.injectorV1 = injectorv1.New(c) 117 | 118 | cs.DiscoveryClient = discovery.NewDiscoveryClient(c) 119 | return &cs 120 | } 121 | -------------------------------------------------------------------------------- /pkg/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "os" 5 | 6 | v1 "github.com/jd-opensource/joylive-injector/client-go/apis/injector/v1" 7 | "github.com/jd-opensource/joylive-injector/pkg/log" 8 | ) 9 | 10 | const ( 11 | AgentConfig = "config.yaml" 12 | InjectorConfigName = "injector.yaml" 13 | LogConfig = "logback.xml" 14 | BootConfig = "bootstrap.properties" 15 | ConfigMountPath = "/joylive/config" 16 | EmptyDirMountPath = "/joylive" 17 | InitEmptyDirMountPath = "/agent" 18 | InitContainerCmd = "/bin/sh" 19 | InitContainerArgs = "-c, cp -r /joylive/* /agent && chmod -R 777 /agent" 20 | DefaultConfigMapEnvName = "JOYLIVE_CONFIGMAP_NAME" 21 | RuleConfigMapEnvName = "JOYLIVE_RULE_CONFIGMAP_NAME" 22 | NamespaceEnvName = "JOYLIVE_NAMESPACE" 23 | MatchLabelsEnvName = "JOYLIVE_MATCH_ENV_LABELS" 24 | ControlPlaneUrlEnvName = "JOYLIVE_CONTROL_PLANE_URL" 25 | FilterSensitiveEnvName = "JOYLIVE_FILTER_SENSITIVE" 26 | ClusterIdEnvName = "JOYLIVE_CLUSTER_ID" 27 | DefaultNamespace = "joylive" 28 | AgentVersionLabel = "x-live-version" 29 | ServiceNameLabel = "jmsf.jd.com/service" 30 | ServiceGroupLabel = "jmsf.jd.com/group" 31 | ServiceSpaceLabel = "jmsf.jd.com/space" 32 | JdapServiceSpaceLabel = "jmsf.jd.com/service-space" 33 | ApplicationLabel = "jmsf.jd.com/app" 34 | EnhanceTypeLabel = "jmsf.jd.com/enhance" 35 | RegisterTypeLabel = "jmsf.jd.com/register" 36 | ConfigureTypeLabel = "jmsf.jd.com/configure" 37 | EnhanceTypeAgent = "agent" 38 | EnhanceTypeSidecar = "sidecar" 39 | SidecarEnhanceLabel = "sidecar.istio.io/inject" 40 | ApmTypeLabel = "jmsf.jd.com/apm" 41 | TenantLabel = "jmsf.jd.com/tenant" 42 | SwimLaneLabel = "jmsf.jd.com/swimlane" 43 | JdapApplicationLabel = "app.jdap.io/name" 44 | WebHookMatchKeyEnv = "JOYLIVE_MATCH_KEY" 45 | WebHookMatchValueEnv = "JOYLIVE_MATCH_VALUE" 46 | ) 47 | 48 | var ( 49 | Cert string 50 | Key string 51 | Addr string 52 | MatchLabels string 53 | ControlPlaneUrl string 54 | FilterSensitive string 55 | ClusterId string 56 | WebHookMatchKey string 57 | WebHookMatchValue string 58 | ConfigMapName = "joylive-injector-config" 59 | RuleConfigMapName = "joylive-injector-rules" 60 | ) 61 | 62 | // injection_deploy config 63 | var ( 64 | InitContainerName string 65 | // DefaultInjectorConfigMap store default injector configMap data 66 | DefaultInjectorConfigMap map[string]string 67 | // DefaultInjectorConfig define the default agent version information that can be injected into the application 68 | DefaultInjectorConfig *AgentInjectorConfig 69 | // InjectorConfigMaps key is configMap name, value is configMap data 70 | InjectorConfigMaps = map[string]map[string]string{} 71 | // InjectorAgentVersion store all versions of the agent and associated configuration information 72 | InjectorAgentVersion = map[string]v1.AgentVersionSpec{} 73 | InjectorRules = map[string]*InjectorRule{} 74 | ) 75 | 76 | func init() { 77 | // Initialize the default matchLabels from environment variables 78 | MatchLabels = os.Getenv(MatchLabelsEnvName) 79 | // Initialize the default control plane url from environment variables 80 | ControlPlaneUrl = os.Getenv(ControlPlaneUrlEnvName) 81 | // Initialize the switch for filter sensitive from environment variables 82 | FilterSensitive = os.Getenv(FilterSensitiveEnvName) 83 | // Initialize the default cluster id from environment variables 84 | ClusterId = os.Getenv(ClusterIdEnvName) 85 | WebHookMatchKey = os.Getenv(WebHookMatchKeyEnv) 86 | WebHookMatchValue = os.Getenv(WebHookMatchValueEnv) 87 | ConfigMapName = os.Getenv(DefaultConfigMapEnvName) 88 | RuleConfigMapName = os.Getenv(RuleConfigMapEnvName) 89 | } 90 | 91 | func GetNamespace() string { 92 | namespace := os.Getenv(NamespaceEnvName) 93 | if len(namespace) == 0 { 94 | namespaceBytes, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace") 95 | if err != nil { 96 | log.Warnf("Failed to read namespace file: %v", err) 97 | } 98 | namespace = string(namespaceBytes) 99 | } 100 | if len(namespace) == 0 { 101 | return DefaultNamespace 102 | } 103 | return namespace 104 | } 105 | -------------------------------------------------------------------------------- /deploy/joylive-injector/config/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 9 | 11 | 12 | 14 | 15 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | ${LIVE_LOG_CONSOLE_PATTERN} 36 | 37 | 38 | 39 | 40 | 41 | ${logger.fileName} 42 | 43 | ${logger.fileNamePattern} 44 | ${logger.maxHistory} 45 | ${logger.maxFileSize} 46 | ${logger.totalSize} 47 | 48 | 49 | 50 | ${LIVE_LOG_FILE_PATTERN} 51 | 52 | 53 | 54 | 55 | 56 | 57 | 60 | 61 | 62 | 63 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /deploy/joylive-injector/templates/otel.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: otel-collector-conf 6 | labels: 7 | app: opentelemetry 8 | component: otel-collector-conf 9 | data: 10 | otel-collector-config: | 11 | receivers: 12 | otlp: 13 | protocols: 14 | grpc: 15 | endpoint: 0.0.0.0:4317 16 | http: 17 | endpoint: 0.0.0.0:4318 18 | processors: 19 | batch: 20 | memory_limiter: 21 | # 80% of maximum memory up to 2G 22 | limit_mib: 1500 23 | # 25% of limit up to 2G 24 | spike_limit_mib: 512 25 | check_interval: 5s 26 | extensions: 27 | zpages: {} 28 | exporters: 29 | prometheus: 30 | endpoint: 0.0.0.0:8889 31 | namespace: '' 32 | service: 33 | extensions: [zpages] 34 | pipelines: 35 | metrics: 36 | receivers: [otlp] 37 | processors: [memory_limiter, batch] 38 | exporters: [prometheus] 39 | --- 40 | apiVersion: v1 41 | kind: Service 42 | metadata: 43 | name: otel-collector 44 | labels: 45 | app.kubernetes.io/managed-by: paas 46 | app.kubernetes.io/name: opentelemetry 47 | app: opentelemetry 48 | component: otel-collector 49 | spec: 50 | ports: 51 | - name: otlp-grpc # Default endpoint for OpenTelemetry gRPC receiver. 52 | port: 4317 53 | protocol: TCP 54 | targetPort: 4317 55 | - name: otlp-http # Default endpoint for OpenTelemetry HTTP receiver. 56 | port: 4318 57 | protocol: TCP 58 | targetPort: 4318 59 | - name: metrics # Default endpoint for querying metrics. 60 | port: 8888 61 | - name: prometheus 62 | port: 8889 63 | 64 | type: NodePort 65 | selector: 66 | component: otel-collector 67 | --- 68 | apiVersion: apps/v1 69 | kind: Deployment 70 | metadata: 71 | name: otel-collector 72 | labels: 73 | app: opentelemetry 74 | component: otel-collector 75 | spec: 76 | selector: 77 | matchLabels: 78 | app: opentelemetry 79 | component: otel-collector 80 | minReadySeconds: 5 81 | progressDeadlineSeconds: 120 82 | replicas: {{ .Values.otel.replicas }} 83 | template: 84 | metadata: 85 | labels: 86 | app: opentelemetry 87 | component: otel-collector 88 | {{- range $key, $val := .Values.podLabels }} 89 | {{ $key }}: "{{ $val }}" 90 | {{- end }} 91 | spec: 92 | containers: 93 | - command: 94 | - "/otelcol" 95 | - "--config=/conf/otel-collector-config.yaml" 96 | image: {{ .Values.otel.image.repository }}:{{ .Values.otel.image.tag }} 97 | name: otel-collector 98 | resources: 99 | limits: 100 | cpu: 1 101 | memory: 2Gi 102 | requests: 103 | cpu: 200m 104 | memory: 400Mi 105 | ports: 106 | - containerPort: 55679 # Default endpoint for ZPages. 107 | - containerPort: 4317 # Default endpoint for OpenTelemetry receiver. 108 | - containerPort: 14250 # Default endpoint for Jaeger gRPC receiver. 109 | - containerPort: 14268 # Default endpoint for Jaeger HTTP receiver. 110 | - containerPort: 9411 # Default endpoint for Zipkin receiver. 111 | - containerPort: 8888 # Default endpoint for querying metrics. 112 | - containerPort: 8889 # Default endpoint for Prometheus. 113 | env: 114 | - name: MY_POD_IP 115 | valueFrom: 116 | fieldRef: 117 | apiVersion: v1 118 | fieldPath: status.podIP 119 | - name: GOMEMLIMIT 120 | value: 1600MiB 121 | volumeMounts: 122 | - name: otel-collector-config-vol 123 | mountPath: /conf 124 | # - name: otel-collector-secrets 125 | # mountPath: /secrets 126 | volumes: 127 | - configMap: 128 | name: otel-collector-conf 129 | items: 130 | - key: otel-collector-config 131 | path: otel-collector-config.yaml 132 | name: otel-collector-config-vol 133 | # - secret: 134 | # name: otel-collector-secrets 135 | # items: 136 | # - key: cert.pem 137 | # path: cert.pem 138 | # - key: key.pem 139 | # path: key.pem 140 | {{- with .Values.imagePullSecrets }} 141 | imagePullSecrets: 142 | {{- toYaml . | nindent 8 }} 143 | {{- end }} -------------------------------------------------------------------------------- /pkg/admission/admission.go: -------------------------------------------------------------------------------- 1 | package admission 2 | 3 | import ( 4 | "fmt" 5 | "github.com/jd-opensource/joylive-injector/pkg/log" 6 | "io" 7 | "net/http" 8 | "strings" 9 | "sync" 10 | 11 | "k8s.io/apimachinery/pkg/runtime/serializer" 12 | 13 | jsoniter "github.com/json-iterator/go" 14 | 15 | "github.com/jd-opensource/joylive-injector/pkg/route" 16 | 17 | admissionv1 "k8s.io/api/admission/v1" 18 | "k8s.io/apimachinery/pkg/runtime" 19 | ) 20 | 21 | type AdmissionType string 22 | 23 | const ( 24 | AdmissionTypeMutating AdmissionType = "Mutating" 25 | AdmissionTypeValidating AdmissionType = "Validating" 26 | ) 27 | 28 | // AdmissionFunc defines an admission control handler 29 | type AdmissionFunc struct { 30 | Type AdmissionType 31 | Path string 32 | Func func(request *admissionv1.AdmissionRequest) (*admissionv1.AdmissionResponse, error) 33 | } 34 | 35 | // admissionFuncMap is a collection of global admission control handlers 36 | type admissionFuncMap map[string]AdmissionFunc 37 | 38 | var funcMap = make(admissionFuncMap, 10) 39 | 40 | var admissionOnce sync.Once 41 | var deserializer runtime.Decoder 42 | 43 | // Setup initialize deserializer and register admission control handlers 44 | // to the global routing handlers collection. 45 | func Setup() { 46 | admissionOnce.Do(func() { 47 | log.Info("init kube deserializer...") 48 | deserializer = serializer.NewCodecFactory(runtime.NewScheme()).UniversalDeserializer() 49 | 50 | log.Info("init admission func...") 51 | for p, af := range funcMap { 52 | log.Infof("load admission func: %s", af.Path) 53 | handlePath := strings.Replace(p, "_", "-", -1) 54 | if p != handlePath { 55 | log.Warnf("admission func handler path does not support '_', it has been automatically converted to '-'(%s => %s)", p, handlePath) 56 | } 57 | log.Infof("register admission path is: %s, origin path is %s ", handlePath, p) 58 | copyAf := af 59 | route.RegisterHandler(route.HandleFunc{ 60 | Path: handlePath, 61 | Method: http.MethodPost, 62 | Func: func(w http.ResponseWriter, r *http.Request) { 63 | defer func() { _ = r.Body.Close() }() 64 | log.Debugf("received post request: %s %s", r.Method, r.URL.Path) 65 | reqBs, err := io.ReadAll(r.Body) 66 | if err != nil { 67 | route.ResponseErr(handlePath, err.Error(), http.StatusInternalServerError, w) 68 | return 69 | } 70 | if reqBs == nil || len(reqBs) == 0 { 71 | route.ResponseErr(handlePath, "request body is empty", http.StatusBadRequest, w) 72 | return 73 | } 74 | log.Debugf("request body: %s", string(reqBs)) 75 | 76 | reqReview := admissionv1.AdmissionReview{} 77 | if _, _, err := deserializer.Decode(reqBs, nil, &reqReview); err != nil { 78 | route.ResponseErr(handlePath, fmt.Sprintf("failed to decode req: %s", err), http.StatusInternalServerError, w) 79 | return 80 | } 81 | if reqReview.Request == nil { 82 | route.ResponseErr(handlePath, "admission review request is empty", http.StatusBadRequest, w) 83 | return 84 | } 85 | 86 | resp, err := copyAf.Func(reqReview.Request) 87 | if err != nil { 88 | route.ResponseErr(handlePath, fmt.Sprintf("admission func response: %s", err), http.StatusForbidden, w) 89 | return 90 | } 91 | if resp == nil { 92 | route.ResponseErr(handlePath, "admission func response is empty", http.StatusInternalServerError, w) 93 | return 94 | } 95 | resp.UID = reqReview.Request.UID 96 | respReview := admissionv1.AdmissionReview{ 97 | TypeMeta: reqReview.TypeMeta, 98 | Response: resp, 99 | } 100 | respBs, err := jsoniter.Marshal(respReview) 101 | if err != nil { 102 | route.ResponseErr(handlePath, fmt.Sprintf("failed to marshal response: %s", err), http.StatusInternalServerError, w) 103 | log.Errorf("the expected response is: %v", respReview) 104 | return 105 | } 106 | 107 | w.Header().Set("Content-Type", "application/json") 108 | w.WriteHeader(http.StatusOK) 109 | _, err = w.Write(respBs) 110 | log.Debugf("write response: %d: %s: %v", http.StatusOK, string(respBs), err) 111 | }, 112 | }) 113 | } 114 | }) 115 | } 116 | 117 | func Register(af AdmissionFunc) { 118 | log.Infof("start to register admission func: %s", af.Path) 119 | if af.Path == "" { 120 | log.Fatalf("admission func path is empty") 121 | } 122 | 123 | if af.Type == "" { 124 | log.Fatalf("admission func type is empty") 125 | } 126 | 127 | handlePath := strings.ToLower(af.Path) 128 | if !strings.HasPrefix(handlePath, "/") { 129 | handlePath = "/" + handlePath 130 | } 131 | switch af.Type { 132 | case AdmissionTypeMutating: 133 | handlePath = "/mutating" + handlePath 134 | case AdmissionTypeValidating: 135 | handlePath = "/validating" + handlePath 136 | default: 137 | log.Fatalf("unsupported admission func type") 138 | } 139 | registeredAf, exist := funcMap[handlePath] 140 | if exist && registeredAf.Type == af.Type { 141 | log.Fatalf("admission func [%s], type: %s already registered", af.Path, af.Type) 142 | } 143 | funcMap[handlePath] = af 144 | } 145 | -------------------------------------------------------------------------------- /pkg/watcher/av_watcher.go: -------------------------------------------------------------------------------- 1 | package watcher 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | v1 "github.com/jd-opensource/joylive-injector/client-go/apis/injector/v1" 7 | clientset "github.com/jd-opensource/joylive-injector/client-go/clientset/versioned" 8 | "github.com/jd-opensource/joylive-injector/client-go/informers/externalversions" 9 | listerv1 "github.com/jd-opensource/joylive-injector/client-go/listers/injector/v1" 10 | "github.com/jd-opensource/joylive-injector/pkg/config" 11 | "github.com/jd-opensource/joylive-injector/pkg/log" 12 | "go.uber.org/zap" 13 | "gopkg.in/yaml.v3" 14 | "k8s.io/apimachinery/pkg/labels" 15 | "k8s.io/client-go/rest" 16 | "k8s.io/client-go/tools/cache" 17 | "k8s.io/client-go/util/workqueue" 18 | "reflect" 19 | "time" 20 | ) 21 | 22 | type AgentVersionWatcher struct { 23 | informerFactory externalversions.SharedInformerFactory 24 | agentVersionInformer cache.SharedIndexInformer 25 | agentVersionLister listerv1.AgentVersionLister 26 | cmQueue workqueue.RateLimitingInterface 27 | } 28 | 29 | func NewAgentVersionWatcher(client *rest.Config) *AgentVersionWatcher { 30 | cs, err := clientset.NewForConfig(client) 31 | if err != nil { 32 | log.Fatal("get clientset error", zap.Error(err)) 33 | } 34 | factory := externalversions.NewSharedInformerFactory( 35 | cs, 36 | time.Second*10, 37 | ) 38 | return &AgentVersionWatcher{ 39 | informerFactory: factory, 40 | agentVersionInformer: factory.Injector().V1().AgentVersions().Informer(), 41 | agentVersionLister: factory.Injector().V1().AgentVersions().Lister(), 42 | cmQueue: workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()), 43 | } 44 | } 45 | 46 | func (w *AgentVersionWatcher) InitAgentVersion(namespace string) error { 47 | avs, err := w.agentVersionLister.AgentVersions(namespace).List(labels.Everything()) 48 | if err != nil { 49 | log.Error("get agentVersions error", zap.String("namespace", namespace), zap.Error(err)) 50 | return err 51 | } 52 | for _, av := range avs { 53 | err = w.cacheAgentVersion(av) 54 | if err != nil { 55 | log.Error("cache agentVersion error", zap.String("avName", av.Name), zap.Error(err)) 56 | return err 57 | } 58 | } 59 | return nil 60 | } 61 | 62 | func (w *AgentVersionWatcher) Start() error { 63 | ctx := context.Background() 64 | w.informerFactory.Start(ctx.Done()) 65 | res := w.informerFactory.WaitForCacheSync(ctx.Done()) 66 | for name, synced := range res { 67 | if !synced { 68 | log.Info("cache for %s is not synced", zap.String("name", name.Name())) 69 | } 70 | } 71 | agentVersionEventHandlerFunc := func() cache.ResourceEventHandlerFuncs { 72 | return cache.ResourceEventHandlerFuncs{ 73 | AddFunc: func(obj interface{}) { 74 | w.createOrUpdateCache(obj) 75 | }, 76 | UpdateFunc: func(oldObj interface{}, newObj interface{}) { 77 | if !isAgentVersionEqual(oldObj, newObj) { 78 | w.createOrUpdateCache(newObj) 79 | } 80 | }, 81 | DeleteFunc: func(obj interface{}) { 82 | key, err := cache.MetaNamespaceKeyFunc(obj) 83 | if err != nil { 84 | log.Error("failed on configmap DeleteFunc", zap.Error(err)) 85 | return 86 | } 87 | log.Warn("ignore agentVersion deleted", zap.String("cm", key)) 88 | }, 89 | } 90 | } 91 | _, err := w.agentVersionInformer.AddEventHandler(agentVersionEventHandlerFunc()) 92 | if err != nil { 93 | return err 94 | } 95 | go w.agentVersionInformer.Run(ctx.Done()) 96 | go func() { 97 | for w.processAgentVersion() { 98 | } 99 | }() 100 | return nil 101 | } 102 | 103 | // isAgentVersionEqual compares two AgentVersion objects to see if they are equal 104 | func isAgentVersionEqual(oldObj, newObj interface{}) bool { 105 | oldConfigMap, ok1 := oldObj.(*v1.AgentVersion) 106 | newConfigMap, ok2 := newObj.(*v1.AgentVersion) 107 | if !ok1 || !ok2 { 108 | return false 109 | } 110 | return reflect.DeepEqual(oldConfigMap.Spec, newConfigMap.Spec) 111 | } 112 | 113 | func (w *AgentVersionWatcher) cacheAgentVersion(agentVersion *v1.AgentVersion) error { 114 | if agentVersion == nil { 115 | return fmt.Errorf("agentVersion not be nil") 116 | } 117 | avSpecBytes, err := yaml.Marshal(agentVersion.Spec) 118 | if err != nil { 119 | return err 120 | } 121 | avSpec := string(avSpecBytes) 122 | log.Info("Received AgentVersion update event, start updating local configuration.", zap.String("agentVersion", agentVersion.Name), 123 | zap.String("agentVersionSpec", avSpec)) 124 | config.InjectorAgentVersion[agentVersion.Spec.Version] = agentVersion.Spec 125 | return nil 126 | } 127 | 128 | func (w *AgentVersionWatcher) createOrUpdateCache(obj interface{}) { 129 | key, err := cache.MetaNamespaceKeyFunc(obj) 130 | if err != nil { 131 | log.Error("failed on createOrUpdateCache", zap.Error(err)) 132 | return 133 | } 134 | w.cmQueue.Add(key) 135 | } 136 | 137 | func (w *AgentVersionWatcher) processAgentVersion() bool { 138 | key, quit := w.cmQueue.Get() 139 | if quit { 140 | log.Info("agentVersion queue quit", zap.String("key", key.(string))) 141 | return false 142 | } 143 | defer w.cmQueue.Done(key) 144 | item, exists, err := w.agentVersionInformer.GetIndexer().GetByKey(key.(string)) 145 | if err != nil { 146 | log.Error("get agentVersion by key error", zap.String("key", key.(string)), zap.Error(err)) 147 | w.cmQueue.AddRateLimited(item) 148 | return true 149 | } 150 | if !exists { 151 | log.Info("agentVersion not exist", zap.String("key", key.(string))) 152 | return true 153 | } 154 | if agentVersion, ok := item.(*v1.AgentVersion); ok { 155 | if err := w.cacheAgentVersion(agentVersion); err != nil { 156 | log.Error("cache this agentVersion error", zap.String("key", key.(string)), zap.Error(err)) 157 | return true 158 | } 159 | } 160 | return true 161 | } 162 | -------------------------------------------------------------------------------- /pkg/log/log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "fmt" 5 | "github.com/spf13/viper" 6 | "io" 7 | "os" 8 | "strings" 9 | "time" 10 | 11 | "go.uber.org/zap" 12 | "go.uber.org/zap/zapcore" 13 | ) 14 | 15 | type Level = zapcore.Level 16 | 17 | const ( 18 | DebugLevel = zapcore.DebugLevel 19 | InfoLevel = zapcore.InfoLevel 20 | WarnLevel = zapcore.WarnLevel 21 | ErrorLevel = zapcore.ErrorLevel 22 | PanicLevel = zapcore.PanicLevel 23 | FatalLevel = zapcore.FatalLevel 24 | ) 25 | 26 | type Logger struct { 27 | l *zap.Logger 28 | ls *zap.SugaredLogger 29 | // https://pkg.go.dev/go.uber.org/zap#example-AtomicLevel 30 | al *zap.AtomicLevel 31 | } 32 | 33 | // InitLog log instance init 34 | func InitLog() { 35 | viper.AutomaticEnv() 36 | err := viper.BindEnv("log.level", "LOG_LEVEL") 37 | if err != nil { 38 | return 39 | } 40 | viper.SetDefault("log.level", "info") 41 | level := viper.GetString("log.level") 42 | logLevel := InfoLevel 43 | if "debug" == strings.ToLower(level) { 44 | logLevel = DebugLevel 45 | } 46 | if "info" == strings.ToLower(level) { 47 | logLevel = InfoLevel 48 | } 49 | if "error" == strings.ToLower(level) { 50 | logLevel = ErrorLevel 51 | } 52 | if "warn" == strings.ToLower(level) { 53 | logLevel = WarnLevel 54 | } 55 | fmt.Println("Set logLevel = " + level) 56 | var options = make([]Option, 0) 57 | logConfig := NewProductionRotateConfig("log") 58 | appName := viper.GetString("log.appName") 59 | if len(appName) > 0 { 60 | logConfig.Filename = appName 61 | } 62 | viper.SetDefault("log.development", true) 63 | development := viper.GetBool("log.development") 64 | if development { 65 | options = append(options, Development()) 66 | } 67 | debugFileName := viper.GetString("log.debugFileName") 68 | if len(debugFileName) > 0 { 69 | //modOpts = append(modOpts, setDebugFileName(debugFileName)) 70 | } 71 | infoFileName := viper.GetString("log.infoFileName") 72 | if len(infoFileName) > 0 { 73 | //modOpts = append(modOpts, setInfoFileName(infoFileName)) 74 | } 75 | errorFileName := viper.GetString("log.errorFileName") 76 | if len(errorFileName) > 0 { 77 | //modOpts = append(modOpts, setErrorFileName(errorFileName)) 78 | } 79 | maxAge := viper.GetInt("log.maxAge") 80 | if maxAge > 0 { 81 | logConfig.MaxAge = maxAge 82 | } 83 | maxBackups := viper.GetInt("log.maxBackups") 84 | if maxBackups > 0 { 85 | logConfig.MaxBackups = maxBackups 86 | } 87 | maxSize := viper.GetInt("log.maxSize") 88 | if maxSize > 0 { 89 | logConfig.MaxSize = maxSize 90 | } 91 | var iow io.Writer 92 | if !development { 93 | iow = NewRotateBySize(logConfig) 94 | } 95 | std = New(iow, logLevel, options...) 96 | defer Sync() 97 | } 98 | 99 | func New(out io.Writer, level Level, opts ...Option) *Logger { 100 | var cfg zapcore.EncoderConfig 101 | // 创建写入同步器 102 | var fileSyncer zapcore.WriteSyncer 103 | consoleSyncer := zapcore.AddSync(os.Stdout) 104 | if out == nil { 105 | out = os.Stderr 106 | cfg = zap.NewDevelopmentEncoderConfig() 107 | } else { 108 | fileSyncer = zapcore.AddSync(out) 109 | cfg = zap.NewProductionEncoderConfig() 110 | } 111 | al := zap.NewAtomicLevelAt(level) 112 | cfg.EncodeTime = zapcore.RFC3339TimeEncoder 113 | // 创建zap核心 114 | var core zapcore.Core 115 | if fileSyncer == nil { 116 | core = zapcore.NewCore( 117 | zapcore.NewConsoleEncoder(cfg), 118 | consoleSyncer, 119 | al, 120 | ) 121 | } else { 122 | core = zapcore.NewTee( 123 | zapcore.NewCore( 124 | zapcore.NewConsoleEncoder(cfg), 125 | consoleSyncer, 126 | al, 127 | ), 128 | zapcore.NewCore( 129 | zapcore.NewJSONEncoder(cfg), 130 | fileSyncer, 131 | al, 132 | ), 133 | ) 134 | } 135 | return &Logger{ 136 | l: zap.New(core, opts...), 137 | ls: zap.New(core, opts...).Sugar(), 138 | al: &al, 139 | } 140 | } 141 | 142 | // SetLevel dynamically changes the log level 143 | // Invalid for Loggers created using NewTee, because NewTee is intended to be based on different log levels 144 | // For multiple zap.Core created, the log levels of multiple zap.Core should not be unified through SetLevel. 145 | func (l *Logger) SetLevel(level Level) { 146 | if l.al != nil { 147 | l.al.SetLevel(level) 148 | } 149 | } 150 | 151 | type Field = zap.Field 152 | 153 | func (l *Logger) Debug(msg string, fields ...Field) { 154 | l.l.Debug(msg, fields...) 155 | } 156 | 157 | func (l *Logger) Debugf(template string, args ...interface{}) { 158 | l.ls.Debugf(template, args...) 159 | } 160 | 161 | func (l *Logger) Info(msg string, fields ...Field) { 162 | l.l.Info(msg, fields...) 163 | } 164 | 165 | func (l *Logger) Infof(template string, args ...interface{}) { 166 | l.ls.Infof(template, args...) 167 | } 168 | 169 | func (l *Logger) Warn(msg string, fields ...Field) { 170 | l.l.Warn(msg, fields...) 171 | } 172 | 173 | func (l *Logger) Warnf(template string, args ...interface{}) { 174 | l.ls.Warnf(template, args...) 175 | } 176 | 177 | func (l *Logger) Error(msg string, fields ...Field) { 178 | l.l.Error(msg, fields...) 179 | } 180 | 181 | func (l *Logger) Errorf(template string, args ...interface{}) { 182 | l.ls.Errorf(template, args...) 183 | } 184 | 185 | func (l *Logger) Panic(msg string, fields ...Field) { 186 | l.l.Panic(msg, fields...) 187 | } 188 | 189 | func (l *Logger) Panicf(template string, args ...interface{}) { 190 | l.ls.Panicf(template, args...) 191 | } 192 | 193 | func (l *Logger) Fatal(msg string, fields ...Field) { 194 | l.l.Fatal(msg, fields...) 195 | } 196 | 197 | func (l *Logger) Fatalf(template string, args ...interface{}) { 198 | l.ls.Fatalf(template, args...) 199 | } 200 | 201 | func (l *Logger) Sync() error { 202 | return l.l.Sync() 203 | } 204 | 205 | func (l *Logger) SyncSugar() error { 206 | return l.ls.Sync() 207 | } 208 | 209 | func timeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { 210 | enc.AppendString(t.Format("2006-01-02 15:04:05")) 211 | } 212 | 213 | func timeUnixNano(t time.Time, enc zapcore.PrimitiveArrayEncoder) { 214 | enc.AppendInt64(t.UnixNano() / 1e6) 215 | } 216 | 217 | var std = New(os.Stderr, InfoLevel) 218 | 219 | func Default() *Logger { return std } 220 | func ReplaceDefault(l *Logger) { std = l } 221 | 222 | func SetLevel(level Level) { std.SetLevel(level) } 223 | 224 | func Debug(msg string, fields ...Field) { std.Debug(msg, fields...) } 225 | func Debugf(template string, args ...interface{}) { std.Debugf(template, args...) } 226 | 227 | func Info(msg string, fields ...Field) { std.Info(msg, fields...) } 228 | func Infof(template string, args ...interface{}) { std.Infof(template, args...) } 229 | 230 | func Warn(msg string, fields ...Field) { std.Warn(msg, fields...) } 231 | func Warnf(template string, args ...interface{}) { std.Warnf(template, args...) } 232 | 233 | func Error(msg string, fields ...Field) { std.Error(msg, fields...) } 234 | func Errorf(template string, args ...interface{}) { std.Errorf(template, args...) } 235 | 236 | func Panic(msg string, fields ...Field) { std.Panic(msg, fields...) } 237 | func Panicf(template string, args ...interface{}) { std.Panicf(template, args...) } 238 | 239 | func Fatal(msg string, fields ...Field) { std.Fatal(msg, fields...) } 240 | func Fatalf(template string, args ...interface{}) { std.Fatalf(template, args...) } 241 | 242 | func Sync() error { 243 | err := std.Sync() 244 | if err != nil { 245 | return err 246 | } 247 | return std.SyncSugar() 248 | } 249 | -------------------------------------------------------------------------------- /pkg/watcher/cm_watcher.go: -------------------------------------------------------------------------------- 1 | package watcher 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/jd-opensource/joylive-injector/pkg/config" 7 | "github.com/jd-opensource/joylive-injector/pkg/log" 8 | "go.uber.org/zap" 9 | "gopkg.in/yaml.v3" 10 | v1 "k8s.io/api/core/v1" 11 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 | "k8s.io/apimachinery/pkg/labels" 13 | "k8s.io/client-go/informers" 14 | "k8s.io/client-go/kubernetes" 15 | listerv1 "k8s.io/client-go/listers/core/v1" 16 | "k8s.io/client-go/tools/cache" 17 | "k8s.io/client-go/util/workqueue" 18 | "reflect" 19 | "time" 20 | ) 21 | 22 | type ConfigMapWatcher struct { 23 | informerFactory informers.SharedInformerFactory 24 | configMapInformer cache.SharedIndexInformer 25 | configMapLister listerv1.ConfigMapLister 26 | cmQueue workqueue.RateLimitingInterface 27 | } 28 | 29 | func NewConfigMapWatcher(kubeClient kubernetes.Interface) *ConfigMapWatcher { 30 | factory := informers.NewSharedInformerFactoryWithOptions( 31 | kubeClient, 32 | time.Second*10, 33 | informers.WithNamespace(config.GetNamespace()), 34 | informers.WithTweakListOptions(func(options *metav1.ListOptions) { 35 | options.LabelSelector = labels.SelectorFromSet(map[string]string{ 36 | "app": "joylive-injector", 37 | }).String() 38 | }), 39 | ) 40 | return &ConfigMapWatcher{ 41 | informerFactory: factory, 42 | configMapInformer: factory.Core().V1().ConfigMaps().Informer(), 43 | configMapLister: factory.Core().V1().ConfigMaps().Lister(), 44 | cmQueue: workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()), 45 | } 46 | } 47 | 48 | func (w *ConfigMapWatcher) InitConfigMap(namespace string) error { 49 | cms, err := w.configMapLister.ConfigMaps(namespace).List(labels.Everything()) 50 | if err != nil { 51 | log.Error("get configMap error", zap.String("namespace", namespace), zap.Error(err)) 52 | return err 53 | } 54 | for _, cm := range cms { 55 | err = w.cacheConfigMap(cm) 56 | if err != nil { 57 | log.Error("cache configMap error", zap.String("cmName", cm.Name), zap.Error(err)) 58 | return err 59 | } 60 | } 61 | return nil 62 | } 63 | 64 | func (w *ConfigMapWatcher) Start() error { 65 | ctx := context.Background() 66 | w.informerFactory.Start(ctx.Done()) 67 | res := w.informerFactory.WaitForCacheSync(ctx.Done()) 68 | for name, synced := range res { 69 | if !synced { 70 | log.Info("cache for %s is not synced", zap.String("name", name.Name())) 71 | } 72 | } 73 | configMapEventHandlerFunc := func() cache.ResourceEventHandlerFuncs { 74 | return cache.ResourceEventHandlerFuncs{ 75 | AddFunc: func(obj interface{}) { 76 | w.createOrUpdateCache(obj) 77 | }, 78 | UpdateFunc: func(oldObj interface{}, newObj interface{}) { 79 | if !isConfigMapEqual(oldObj, newObj) { 80 | w.createOrUpdateCache(newObj) 81 | } 82 | }, 83 | DeleteFunc: func(obj interface{}) { 84 | key, err := cache.MetaNamespaceKeyFunc(obj) 85 | if err != nil { 86 | log.Error("failed on configmap DeleteFunc", zap.Error(err)) 87 | return 88 | } 89 | // 解析出 name 90 | var name string 91 | //var namespace string 92 | if cm, ok := obj.(*v1.ConfigMap); ok { 93 | name = cm.Name 94 | //namespace = cm.Namespace 95 | } else if tombstone, ok := obj.(cache.DeletedFinalStateUnknown); ok { 96 | if cm, ok := tombstone.Obj.(*v1.ConfigMap); ok { 97 | name = cm.Name 98 | //namespace = cm.Namespace 99 | } 100 | } 101 | 102 | if name == config.RuleConfigMapName { 103 | config.InjectorRules = map[string]*config.InjectorRule{} 104 | log.Info("injector rules configmap deleted, cleared InjectorRules", zap.String("cm", key)) 105 | } 106 | log.Warn("configMap deleted", zap.String("cm", key)) 107 | }, 108 | } 109 | } 110 | _, err := w.configMapInformer.AddEventHandler(configMapEventHandlerFunc()) 111 | if err != nil { 112 | return err 113 | } 114 | go w.configMapInformer.Run(ctx.Done()) 115 | go func() { 116 | for w.processConfigMap() { 117 | } 118 | }() 119 | return nil 120 | } 121 | 122 | // isConfigMapEqual compares two ConfigMap objects to see if they are equal 123 | func isConfigMapEqual(oldObj, newObj interface{}) bool { 124 | oldConfigMap, ok1 := oldObj.(*v1.ConfigMap) 125 | newConfigMap, ok2 := newObj.(*v1.ConfigMap) 126 | if !ok1 || !ok2 { 127 | return false 128 | } 129 | return reflect.DeepEqual(oldConfigMap.Data, newConfigMap.Data) && reflect.DeepEqual(oldConfigMap.BinaryData, newConfigMap.BinaryData) 130 | } 131 | 132 | func (w *ConfigMapWatcher) cacheConfigMap(configMap *v1.ConfigMap) error { 133 | if configMap == nil || configMap.Data == nil { 134 | return fmt.Errorf("config map not be nil") 135 | } 136 | cmDataBytes, err := yaml.Marshal(configMap.Data) 137 | if err != nil { 138 | return err 139 | } 140 | cmDataString := string(cmDataBytes) 141 | log.Info("Received ConfigMap update event, start updating local configuration.", zap.String("cm", configMap.Name), 142 | zap.String("data", cmDataString)) 143 | 144 | switch configMap.Name { 145 | case config.ConfigMapName: 146 | config.DefaultInjectorConfigMap = make(map[string]string, len(configMap.Data)) 147 | for k, v := range configMap.Data { 148 | config.DefaultInjectorConfigMap[k] = v 149 | } 150 | if data, ok := configMap.Data[config.InjectorConfigName]; ok { 151 | c, err := config.GetAgentInjectConfig(data) 152 | if err != nil { 153 | return fmt.Errorf("parse injector config error: %w", err) 154 | } 155 | config.DefaultInjectorConfig = c 156 | delete(config.DefaultInjectorConfigMap, config.InjectorConfigName) 157 | } 158 | case config.RuleConfigMapName: 159 | rules := make(map[string]*config.InjectorRule, len(configMap.Data)) 160 | for key, value := range configMap.Data { 161 | rule := &config.InjectorRule{} 162 | if err := yaml.Unmarshal([]byte(value), rule); err != nil { 163 | return fmt.Errorf("unmarshal rule %s error: %w", key, err) 164 | } 165 | rules[key] = rule 166 | } 167 | config.InjectorRules = rules 168 | default: 169 | if config.InjectorConfigMaps == nil { 170 | config.InjectorConfigMaps = make(map[string]map[string]string) 171 | } 172 | config.InjectorConfigMaps[configMap.Name] = configMap.Data 173 | } 174 | return nil 175 | } 176 | 177 | func (w *ConfigMapWatcher) createOrUpdateCache(obj interface{}) { 178 | key, err := cache.MetaNamespaceKeyFunc(obj) 179 | if err != nil { 180 | log.Error("failed on createOrUpdateCache", zap.Error(err)) 181 | return 182 | } 183 | w.cmQueue.Add(key) 184 | } 185 | 186 | func (w *ConfigMapWatcher) processConfigMap() bool { 187 | key, quit := w.cmQueue.Get() 188 | if quit { 189 | log.Info("configMap queue quit", zap.String("key", key.(string))) 190 | return false 191 | } 192 | defer w.cmQueue.Done(key) 193 | item, exists, err := w.configMapInformer.GetIndexer().GetByKey(key.(string)) 194 | if err != nil { 195 | log.Error("get configMap by key error", zap.String("key", key.(string)), zap.Error(err)) 196 | w.cmQueue.AddRateLimited(key) 197 | return true 198 | } 199 | 200 | if !exists { 201 | log.Info("configMap not exist", zap.String("key", key.(string))) 202 | return true 203 | } 204 | if configMap, ok := item.(*v1.ConfigMap); ok { 205 | if err := w.cacheConfigMap(configMap); err != nil { 206 | log.Error("cache this configMap error", zap.String("key", key.(string)), zap.Error(err)) 207 | return true 208 | } 209 | } 210 | return true 211 | } 212 | -------------------------------------------------------------------------------- /client-go/clientset/versioned/typed/injector/v1/fake/fake_agentversion.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | "context" 23 | json "encoding/json" 24 | "fmt" 25 | 26 | v1 "github.com/jd-opensource/joylive-injector/client-go/apis/injector/v1" 27 | injectorv1 "github.com/jd-opensource/joylive-injector/client-go/applyconfiguration/injector/v1" 28 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 | labels "k8s.io/apimachinery/pkg/labels" 30 | types "k8s.io/apimachinery/pkg/types" 31 | watch "k8s.io/apimachinery/pkg/watch" 32 | testing "k8s.io/client-go/testing" 33 | ) 34 | 35 | // FakeAgentVersions implements AgentVersionInterface 36 | type FakeAgentVersions struct { 37 | Fake *FakeInjectorV1 38 | ns string 39 | } 40 | 41 | var agentversionsResource = v1.SchemeGroupVersion.WithResource("agentversions") 42 | 43 | var agentversionsKind = v1.SchemeGroupVersion.WithKind("AgentVersion") 44 | 45 | // Get takes name of the agentVersion, and returns the corresponding agentVersion object, and an error if there is any. 46 | func (c *FakeAgentVersions) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.AgentVersion, err error) { 47 | obj, err := c.Fake. 48 | Invokes(testing.NewGetAction(agentversionsResource, c.ns, name), &v1.AgentVersion{}) 49 | 50 | if obj == nil { 51 | return nil, err 52 | } 53 | return obj.(*v1.AgentVersion), err 54 | } 55 | 56 | // List takes label and field selectors, and returns the list of AgentVersions that match those selectors. 57 | func (c *FakeAgentVersions) List(ctx context.Context, opts metav1.ListOptions) (result *v1.AgentVersionList, err error) { 58 | obj, err := c.Fake. 59 | Invokes(testing.NewListAction(agentversionsResource, agentversionsKind, c.ns, opts), &v1.AgentVersionList{}) 60 | 61 | if obj == nil { 62 | return nil, err 63 | } 64 | 65 | label, _, _ := testing.ExtractFromListOptions(opts) 66 | if label == nil { 67 | label = labels.Everything() 68 | } 69 | list := &v1.AgentVersionList{ListMeta: obj.(*v1.AgentVersionList).ListMeta} 70 | for _, item := range obj.(*v1.AgentVersionList).Items { 71 | if label.Matches(labels.Set(item.Labels)) { 72 | list.Items = append(list.Items, item) 73 | } 74 | } 75 | return list, err 76 | } 77 | 78 | // Watch returns a watch.Interface that watches the requested agentVersions. 79 | func (c *FakeAgentVersions) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { 80 | return c.Fake. 81 | InvokesWatch(testing.NewWatchAction(agentversionsResource, c.ns, opts)) 82 | 83 | } 84 | 85 | // Create takes the representation of a agentVersion and creates it. Returns the server's representation of the agentVersion, and an error, if there is any. 86 | func (c *FakeAgentVersions) Create(ctx context.Context, agentVersion *v1.AgentVersion, opts metav1.CreateOptions) (result *v1.AgentVersion, err error) { 87 | obj, err := c.Fake. 88 | Invokes(testing.NewCreateAction(agentversionsResource, c.ns, agentVersion), &v1.AgentVersion{}) 89 | 90 | if obj == nil { 91 | return nil, err 92 | } 93 | return obj.(*v1.AgentVersion), err 94 | } 95 | 96 | // Update takes the representation of a agentVersion and updates it. Returns the server's representation of the agentVersion, and an error, if there is any. 97 | func (c *FakeAgentVersions) Update(ctx context.Context, agentVersion *v1.AgentVersion, opts metav1.UpdateOptions) (result *v1.AgentVersion, err error) { 98 | obj, err := c.Fake. 99 | Invokes(testing.NewUpdateAction(agentversionsResource, c.ns, agentVersion), &v1.AgentVersion{}) 100 | 101 | if obj == nil { 102 | return nil, err 103 | } 104 | return obj.(*v1.AgentVersion), err 105 | } 106 | 107 | // UpdateStatus was generated because the type contains a Status member. 108 | // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). 109 | func (c *FakeAgentVersions) UpdateStatus(ctx context.Context, agentVersion *v1.AgentVersion, opts metav1.UpdateOptions) (*v1.AgentVersion, error) { 110 | obj, err := c.Fake. 111 | Invokes(testing.NewUpdateSubresourceAction(agentversionsResource, "status", c.ns, agentVersion), &v1.AgentVersion{}) 112 | 113 | if obj == nil { 114 | return nil, err 115 | } 116 | return obj.(*v1.AgentVersion), err 117 | } 118 | 119 | // Delete takes name of the agentVersion and deletes it. Returns an error if one occurs. 120 | func (c *FakeAgentVersions) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { 121 | _, err := c.Fake. 122 | Invokes(testing.NewDeleteActionWithOptions(agentversionsResource, c.ns, name, opts), &v1.AgentVersion{}) 123 | 124 | return err 125 | } 126 | 127 | // DeleteCollection deletes a collection of objects. 128 | func (c *FakeAgentVersions) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { 129 | action := testing.NewDeleteCollectionAction(agentversionsResource, c.ns, listOpts) 130 | 131 | _, err := c.Fake.Invokes(action, &v1.AgentVersionList{}) 132 | return err 133 | } 134 | 135 | // Patch applies the patch and returns the patched agentVersion. 136 | func (c *FakeAgentVersions) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.AgentVersion, err error) { 137 | obj, err := c.Fake. 138 | Invokes(testing.NewPatchSubresourceAction(agentversionsResource, c.ns, name, pt, data, subresources...), &v1.AgentVersion{}) 139 | 140 | if obj == nil { 141 | return nil, err 142 | } 143 | return obj.(*v1.AgentVersion), err 144 | } 145 | 146 | // Apply takes the given apply declarative configuration, applies it and returns the applied agentVersion. 147 | func (c *FakeAgentVersions) Apply(ctx context.Context, agentVersion *injectorv1.AgentVersionApplyConfiguration, opts metav1.ApplyOptions) (result *v1.AgentVersion, err error) { 148 | if agentVersion == nil { 149 | return nil, fmt.Errorf("agentVersion provided to Apply must not be nil") 150 | } 151 | data, err := json.Marshal(agentVersion) 152 | if err != nil { 153 | return nil, err 154 | } 155 | name := agentVersion.Name 156 | if name == nil { 157 | return nil, fmt.Errorf("agentVersion.Name must be provided to Apply") 158 | } 159 | obj, err := c.Fake. 160 | Invokes(testing.NewPatchSubresourceAction(agentversionsResource, c.ns, *name, types.ApplyPatchType, data), &v1.AgentVersion{}) 161 | 162 | if obj == nil { 163 | return nil, err 164 | } 165 | return obj.(*v1.AgentVersion), err 166 | } 167 | 168 | // ApplyStatus was generated because the type contains a Status member. 169 | // Add a +genclient:noStatus comment above the type to avoid generating ApplyStatus(). 170 | func (c *FakeAgentVersions) ApplyStatus(ctx context.Context, agentVersion *injectorv1.AgentVersionApplyConfiguration, opts metav1.ApplyOptions) (result *v1.AgentVersion, err error) { 171 | if agentVersion == nil { 172 | return nil, fmt.Errorf("agentVersion provided to Apply must not be nil") 173 | } 174 | data, err := json.Marshal(agentVersion) 175 | if err != nil { 176 | return nil, err 177 | } 178 | name := agentVersion.Name 179 | if name == nil { 180 | return nil, fmt.Errorf("agentVersion.Name must be provided to Apply") 181 | } 182 | obj, err := c.Fake. 183 | Invokes(testing.NewPatchSubresourceAction(agentversionsResource, c.ns, *name, types.ApplyPatchType, data, "status"), &v1.AgentVersion{}) 184 | 185 | if obj == nil { 186 | return nil, err 187 | } 188 | return obj.(*v1.AgentVersion), err 189 | } 190 | -------------------------------------------------------------------------------- /client-go/informers/externalversions/factory.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package externalversions 20 | 21 | import ( 22 | reflect "reflect" 23 | sync "sync" 24 | time "time" 25 | 26 | versioned "github.com/jd-opensource/joylive-injector/client-go/clientset/versioned" 27 | injector "github.com/jd-opensource/joylive-injector/client-go/informers/externalversions/injector" 28 | internalinterfaces "github.com/jd-opensource/joylive-injector/client-go/informers/externalversions/internalinterfaces" 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 | transform cache.TransformFunc 46 | 47 | informers map[reflect.Type]cache.SharedIndexInformer 48 | // startedInformers is used for tracking which informers have been started. 49 | // This allows Start() to be called multiple times safely. 50 | startedInformers map[reflect.Type]bool 51 | // wg tracks how many goroutines were started. 52 | wg sync.WaitGroup 53 | // shuttingDown is true when Shutdown has been called. It may still be running 54 | // because it needs to wait for goroutines. 55 | shuttingDown bool 56 | } 57 | 58 | // WithCustomResyncConfig sets a custom resync period for the specified informer types. 59 | func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption { 60 | return func(factory *sharedInformerFactory) *sharedInformerFactory { 61 | for k, v := range resyncConfig { 62 | factory.customResync[reflect.TypeOf(k)] = v 63 | } 64 | return factory 65 | } 66 | } 67 | 68 | // WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory. 69 | func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption { 70 | return func(factory *sharedInformerFactory) *sharedInformerFactory { 71 | factory.tweakListOptions = tweakListOptions 72 | return factory 73 | } 74 | } 75 | 76 | // WithNamespace limits the SharedInformerFactory to the specified namespace. 77 | func WithNamespace(namespace string) SharedInformerOption { 78 | return func(factory *sharedInformerFactory) *sharedInformerFactory { 79 | factory.namespace = namespace 80 | return factory 81 | } 82 | } 83 | 84 | // WithTransform sets a transform on all informers. 85 | func WithTransform(transform cache.TransformFunc) SharedInformerOption { 86 | return func(factory *sharedInformerFactory) *sharedInformerFactory { 87 | factory.transform = transform 88 | return factory 89 | } 90 | } 91 | 92 | // NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces. 93 | func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory { 94 | return NewSharedInformerFactoryWithOptions(client, defaultResync) 95 | } 96 | 97 | // NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. 98 | // Listers obtained via this SharedInformerFactory will be subject to the same filters 99 | // as specified here. 100 | // Deprecated: Please use NewSharedInformerFactoryWithOptions instead 101 | func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory { 102 | return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions)) 103 | } 104 | 105 | // NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options. 106 | func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory { 107 | factory := &sharedInformerFactory{ 108 | client: client, 109 | namespace: v1.NamespaceAll, 110 | defaultResync: defaultResync, 111 | informers: make(map[reflect.Type]cache.SharedIndexInformer), 112 | startedInformers: make(map[reflect.Type]bool), 113 | customResync: make(map[reflect.Type]time.Duration), 114 | } 115 | 116 | // Apply all options 117 | for _, opt := range options { 118 | factory = opt(factory) 119 | } 120 | 121 | return factory 122 | } 123 | 124 | func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { 125 | f.lock.Lock() 126 | defer f.lock.Unlock() 127 | 128 | if f.shuttingDown { 129 | return 130 | } 131 | 132 | for informerType, informer := range f.informers { 133 | if !f.startedInformers[informerType] { 134 | f.wg.Add(1) 135 | // We need a new variable in each loop iteration, 136 | // otherwise the goroutine would use the loop variable 137 | // and that keeps changing. 138 | informer := informer 139 | go func() { 140 | defer f.wg.Done() 141 | informer.Run(stopCh) 142 | }() 143 | f.startedInformers[informerType] = true 144 | } 145 | } 146 | } 147 | 148 | func (f *sharedInformerFactory) Shutdown() { 149 | f.lock.Lock() 150 | f.shuttingDown = true 151 | f.lock.Unlock() 152 | 153 | // Will return immediately if there is nothing to wait for. 154 | f.wg.Wait() 155 | } 156 | 157 | func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { 158 | informers := func() map[reflect.Type]cache.SharedIndexInformer { 159 | f.lock.Lock() 160 | defer f.lock.Unlock() 161 | 162 | informers := map[reflect.Type]cache.SharedIndexInformer{} 163 | for informerType, informer := range f.informers { 164 | if f.startedInformers[informerType] { 165 | informers[informerType] = informer 166 | } 167 | } 168 | return informers 169 | }() 170 | 171 | res := map[reflect.Type]bool{} 172 | for informType, informer := range informers { 173 | res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) 174 | } 175 | return res 176 | } 177 | 178 | // InformerFor returns the SharedIndexInformer for obj using an internal 179 | // client. 180 | func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { 181 | f.lock.Lock() 182 | defer f.lock.Unlock() 183 | 184 | informerType := reflect.TypeOf(obj) 185 | informer, exists := f.informers[informerType] 186 | if exists { 187 | return informer 188 | } 189 | 190 | resyncPeriod, exists := f.customResync[informerType] 191 | if !exists { 192 | resyncPeriod = f.defaultResync 193 | } 194 | 195 | informer = newFunc(f.client, resyncPeriod) 196 | informer.SetTransform(f.transform) 197 | f.informers[informerType] = informer 198 | 199 | return informer 200 | } 201 | 202 | // SharedInformerFactory provides shared informers for resources in all known 203 | // API group versions. 204 | // 205 | // It is typically used like this: 206 | // 207 | // ctx, cancel := context.Background() 208 | // defer cancel() 209 | // factory := NewSharedInformerFactory(client, resyncPeriod) 210 | // defer factory.WaitForStop() // Returns immediately if nothing was started. 211 | // genericInformer := factory.ForResource(resource) 212 | // typedInformer := factory.SomeAPIGroup().V1().SomeType() 213 | // factory.Start(ctx.Done()) // Start processing these informers. 214 | // synced := factory.WaitForCacheSync(ctx.Done()) 215 | // for v, ok := range synced { 216 | // if !ok { 217 | // fmt.Fprintf(os.Stderr, "caches failed to sync: %v", v) 218 | // return 219 | // } 220 | // } 221 | // 222 | // // Creating informers can also be created after Start, but then 223 | // // Start must be called again: 224 | // anotherGenericInformer := factory.ForResource(resource) 225 | // factory.Start(ctx.Done()) 226 | type SharedInformerFactory interface { 227 | internalinterfaces.SharedInformerFactory 228 | 229 | // Start initializes all requested informers. They are handled in goroutines 230 | // which run until the stop channel gets closed. 231 | Start(stopCh <-chan struct{}) 232 | 233 | // Shutdown marks a factory as shutting down. At that point no new 234 | // informers can be started anymore and Start will return without 235 | // doing anything. 236 | // 237 | // In addition, Shutdown blocks until all goroutines have terminated. For that 238 | // to happen, the close channel(s) that they were started with must be closed, 239 | // either before Shutdown gets called or while it is waiting. 240 | // 241 | // Shutdown may be called multiple times, even concurrently. All such calls will 242 | // block until all goroutines have terminated. 243 | Shutdown() 244 | 245 | // WaitForCacheSync blocks until all started informers' caches were synced 246 | // or the stop channel gets closed. 247 | WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool 248 | 249 | // ForResource gives generic access to a shared informer of the matching type. 250 | ForResource(resource schema.GroupVersionResource) (GenericInformer, error) 251 | 252 | // InformerFor returns the SharedIndexInformer for obj using an internal 253 | // client. 254 | InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer 255 | 256 | Injector() injector.Interface 257 | } 258 | 259 | func (f *sharedInformerFactory) Injector() injector.Interface { 260 | return injector.New(f, f.namespace, f.tweakListOptions) 261 | } 262 | --------------------------------------------------------------------------------